Validation Results

Validation results

Control Plan and inspection workflows 1 to 4. Checked against our architecture, design file and documents. Every section is open. Click any row to see what was tested, what failed and where. Flagged items are listed separately at the bottom.

37% passed overall · 59 tests · 22 pass · 10 partly · 23 fail · 4 not measured

Sections

Eight sections. The first four are the review points. The last four are extras.

1
ArchitectureIs it built the way our architecture says?
4 pass3 fail
RulesResultDetails
Frontend calls only the gateway, never an RPC or the databasePass
RuleThe screens may talk to one gateway file only. They must never call the database or an RPC directly.
ResultWe scanned 249 frontend files. None breaks this.
Only Workers talk to the database, only through named functionsPass
RuleOnly the Workers reach the database, and only by calling a named function.
ResultOne place in the code does it, and all 103 functions it calls exist. No raw SQL.
Business logic lives in the Workers; RPCs only read and write rowsFail
RuleThe business rules (who can approve, which status can change, limits) are written in the Workers. The database functions only save and read data.
ResultThe opposite. The rules are in the database: 393 checks in SQL, against 413 lines of rules in the Worker, which only check the request.
Each Worker domain has a rules module that decides the business rulesFail
RuleEach area (plans, tasks, schedules...) has a rules file in the Worker that holds its business rules.
ResultThe rules files exist, but they only check the request (which fields can change, the upload type). The real rules are in the database.
Rules written in two places are kept equal by testsFail
RuleIf a rule is written twice (screen and database), a test proves both give the same answer.
ResultOnly frequency reading has such a test. Reading pass or fail, the specification reader and the allowed actions have none.
Every side effect goes through the outboxPass
RuleEmails, documents and other side effects start only from a saved event, never directly.
ResultEvery change saves an audit row and an event in the same step. The tests confirm it.
Tenant keys, forced row-level security, frozen versionsPass
RuleEach customer’s data is kept apart, rows are locked to the customer, and an approved version cannot change.
Result1,165 database tests pass, and 7 of 7 attempts to get around it were refused.
2
DesignDoes it follow our design file and component structure?
4 pass2 partly2 fail1 not measured
RulesResultDetails
Design tokens match the design filePass
RuleColours and sizes match the design file.
ResultThe token file holds the design file’s values (brand blue, navy, greys).
Colours come from named tokens, not typed inFail
RuleScreens use named colours from one place, not typed colour codes.
Result70 typed colour codes are scattered through the screen files.
Every feature has a Container, a Presenter and a ViewPartly
RuleEach feature has three parts: a Container (gets the data), a Presenter (the logic) and a View (what is shown).
Result62 views, 44 presenters, 27 containers. 2 of 19 feature folders (lifecycle, revision) have no Container.
Views are display onlyPass
RuleA View only displays. It never fetches data.
ResultNo View fetches data or calls the gateway.
Presenters are plain logicPartly
RuleA Presenter holds the screen logic.
Result40 of 44 presenters are React hooks. The guide does not forbid it, but it is not the plain-logic style.
Solid blue list table header, Inter font, no inline stylesPass
RuleTables have a solid blue header, text is in Inter, and no styles are written inline.
ResultAll three are met.
Deviations from the design file are recordedPass
RuleAny difference from the design file is written down with a reason.
ResultRecorded: red for delete buttons, tinted badges, no logo.
Collapsible sidebar (56 px rail, Ctrl+B)Fail
RuleThe sidebar collapses to a narrow icon bar, and Ctrl+B toggles it.
ResultNot built.
Each screen matched to a design frameNot measured
RuleEach screen is tied to a frame in the design file.
ResultThe tracking table is empty, so it cannot be judged. Screens were not compared by eye.
3
FunctionalityDoes it do what our documents say?
2 pass1 partly4 fail
RulesResultDetails
Workflows 1 to 4 run end to end through the real APIPass
RuleWorkflows 1 to 4 work from start to finish through the real system.
Result181 and 112 checks ran through the real API and database. None failed on the repeat run.
Import keeps raw text and cell; ambiguous values are never guessedPass
RuleImported Excel values keep their original text and cell, and unclear values are never guessed into numbers.
Result121 parser tests and the import tests on the Venturi workbook pass.
Workflow 1 step 5: validate before submitFail
RuleA plan with missing or unclear data cannot be submitted or approved.
ResultOnly 1 rule exists, so incomplete plans can still be approved.
Workflow 5: NC trigger and reaction (C1, C2, C3, MRB)Fail
RuleA failed inspection starts a reaction: stop, contain, correct, decide what to do with the parts.
ResultNot built. A failed result is recorded, but nothing reacts to it.
PDF and Excel output, evidence packsFail
RuleThe system produces PDF and Excel documents and evidence packs.
ResultNot built. The document service is a placeholder.
PFMEA linking, gauge checks beyond the calibration dateFail
RulePFMEA is linked to the plan, and gauges are checked for more than their calibration date.
ResultNot built.
Functional requirements covered (41)Partly
RuleAll 41 requirements in the functional requirements document are built.
Result15 built, 13 partly built, 13 not built.
4
Code qualityIs the code clean and tested?
4 pass6 partly5 fail
RulesResultDetails
TypeScript strict, zero lint warnings, locked installPass
RuleThe code has no type errors and no lint warnings, and installs from the locked versions.
Result0 errors and 0 warnings.
No any, ts-ignore, TODO, service_role, hard-coded IDs, inline secretsPass
RuleNo shortcuts in the code: no loose types, skipped checks, TODO notes, hard-coded IDs or secrets.
ResultNone found.
File size limit (400 code lines)Pass
RuleA file holds at most 400 lines of code.
ResultEnforced by the lint check, and it passes.
Code formattingFail
RuleAll files follow one code style.
Result2 files are not formatted. 659 more are flagged only because of Windows line endings.
Unit and database testsPartly
RuleAll tests pass.
Result2,671 of 2,679 pass. The 8 failures come from Windows file paths and line endings. 22 database tests also fail on real Postgres because of how the tests are run.
Tests can failPass
RuleRemoving a safety rule must make a test fail.
Result106 rules were removed on purpose. 103 were caught, 3 change nothing, and none were real gaps.
Dependencies free of high and critical issuesFail
RuleLibraries have no high or critical known security issues.
Resultreact-router 6.30.6 has 2 moderate issues. vitest 3.2.7 (development only) has 2 critical.
Database list queries page before doing heavy workFail
RuleA list query picks the page of rows first, then does the heavy work only for those rows.
ResultThe checking-aids list builds the full record for every gauge in the plant, then keeps 200. Fine for a few gauges, slow for thousands.
Database lists have a size limitFail
RuleEvery database list is paged, so one answer never carries every row.
ResultThe plant gauge list has no limit and returns every gauge at once. The process flow list was not checked.
Screen lists let users reach every itemFail
RuleA list screen lets the user reach every item, with page controls or load more.
ResultTasks, schedules, production and inspection plans load 200 items and have no page controls. Item 201 onward cannot be reached. Only the plans list pages.
Queue producers exist for every queue events can usePartly
RuleEvery queue that an event can be routed to is connected to the Worker that sends to it.
ResultOnly the events queue is connected. All events use it today, so nothing fails yet. Notification and document queues are not connected, and the notification routing list is empty.
Database functions list their columns (no SELECT *)Partly
RuleDatabase functions name the columns they read instead of using SELECT *.
Result154 uses of SELECT *. Safe when filling one whole row variable, risky when filling separate variables. The ban comes from the Calispec checklist, not from our CLAUDE.md.
Row locks are taken in a fixed orderPartly
RuleWhen a function locks several rows, it locks them in a fixed order so two users cannot block each other.
ResultThe 3 multi-row lock loops we found are ordered. The other 54 lock statements were not reviewed. The NC code named in the audit is not in this repo.
Bulk save keeps valid rows when one row is stalePartly
RuleSaving many rows saves the valid ones even if one row is out of date.
ResultIt is all or nothing by design. If one row is stale nothing is saved, but every row is reported with its conflict and changed fields.
Part number and revision are upper-cased as typedPartly
RuleFree-text codes such as part number and revision are turned into capitals as the user types.
ResultThe create-plan form does not do it. The rule is written for Standards Room forms, and it is not clear that Control Plan counts.
5
SecurityCan one customer reach another customer’s data?
3 pass1 partly2 fail1 not measured
RulesResultDetails
Direct database bypass attemptsPass
RuleA browser that skips the API and calls the database directly is refused.
Result7 of 7 attempts were refused.
Secrets in the repo and its historyPass
RuleNo keys or passwords anywhere in the code history.
ResultNone found in all 262 commits.
Every callable database function checks caller, tenant and plantPass
RuleEvery database function the API calls checks who is asking, which customer and which plant.
Result139 functions checked. None left unguarded.
Task tables writable only through named functionsPartly
RuleTask data can be changed only through the named functions.
ResultThe API role still has direct write rights, held back only by a hidden setting. The API cannot reach that setting.
Production login and token proof on hosted SupabaseFail
RuleUsers sign in through a real login system, and the database token works on the hosted setup.
ResultNo production login system yet. The token has not been tried on hosted Supabase.
Customer separation does not trust the APIFail
RuleThe database does not simply trust the customer that the API names.
ResultIt does trust it. All customers share one database.
Security headers (CSP) on the appNot measured
RuleThe app sends security headers.
ResultNo headers setting found. Needs a check on a hosted setup.
6
TraceabilityIs each requirement tied to a test?
1 pass2 fail
RulesResultDetails
Acceptance criteria have a passing proof recordedFail
RuleEach acceptance criterion is marked passing, with the test that proves it.
Result9 criteria: 0 passing, 1 partly, 6 pending, 2 deferred.
The proving test files named in the table existFail
RuleThe test files named in the acceptance table exist.
ResultThey do not. The e2e folder holds only a README, and Playwright is not installed.
Appendix D tests (positive, negative, cross-tenant, bypass, immutability, separation of duties)Pass
RuleEach slice has six standard tests: positive, negative, cross-customer, bypass, frozen version, separation of duties.
Result65 of 86 database test files carry the tag.
7
AccessibilityCan everyone use the screens?
3 pass1 fail1 not measured
RulesResultDetails
Accessibility lint rules activePass
RuleThe code is checked for accessibility mistakes automatically.
ResultThe check is on, with 405 aria and role attributes and 23 focus styles in the app.
Automated accessibility testFail
RuleAn automated test checks the screens for accessibility problems.
ResultNone in the repo.
Dates and numbers through the shared hooksPass
RuleDates and numbers are formatted by shared helpers, not typed into screens.
ResultNo direct date or number formatting inside the screens.
Empty values show an em dashPass
RuleAn empty value shows a dash.
ResultUsed in 42 places.
Contrast and keyboard use on real screensNot measured
RuleText is easy to read and every action works with the keyboard.
ResultNot measured. Needs a run in a browser.
8
OperationsIs it fast, deployed and watched?
1 pass4 fail1 not measured
RulesResultDetails
Performance budgets measured (DDD p.56)Fail
RuleSpeed targets are measured, for example opening a 500-line plan in under 3 seconds.
ResultThe targets are written but never measured. The test data generator is missing.
Plan approval timeFail
RuleApproving a plan finishes inside the database time limit (8 seconds).
ResultOnce it took 7.8 seconds and was cancelled.
CI/CD pipelineFail
RuleEvery change is built and tested automatically before it can be merged.
ResultNo pipeline. The workflows folder holds only a README.
Workers deployed and reachableFail
RuleThe Workers run on Cloudflare.
ResultNot deployed. They ran only on this machine.
Correlation ID and audit trailPass
RuleEach request has an ID, and every change is logged.
ResultBoth are in place.
Rate limits and real queue behaviourNot measured
RuleToo many calls are refused, and queues retry failed messages.
ResultNot measured. Dev limits are raised and queues are simulated.

Flagged

33 items failed or only partly passed: 13 high, 15 medium, 5 low. High means it blocks production, breaks the Workers-first rule, or hides or slows real data.

High 13 flags

FlagOwnerDetailsWhat to do
Business logic lives in the Workers; RPCs only read and write rowsArchitectureDecision, then rework · V-12
RuleThe business rules (who can approve, which status can change, limits) are written in the Workers. The database functions only save and read data.
ResultThe opposite. The rules are in the database: 393 checks in SQL, against 413 lines of rules in the Worker, which only check the request.
Decide where rules live. If the Workers must hold them, the live-data rules move out of SQL: most of about 22,200 lines and the 1,165 database tests are affected. If SQL stays, record that decision in docs/decisions.
Each Worker domain has a rules module that decides the business rulesArchitectureFollows the decision above · V-12
RuleEach area (plans, tasks, schedules...) has a rules file in the Worker that holds its business rules.
ResultThe rules files exist, but they only check the request (which fields can change, the upload type). The real rules are in the database.
The rule files hold only request rules today. They grow only if the decision above moves rules into the Workers.
Workflow 1 step 5: validate before submitFunctionalityLater slice
RuleA plan with missing or unclear data cannot be submitted or approved.
ResultOnly 1 rule exists, so incomplete plans can still be approved.
Build the validation engine and its rules, so incomplete plans cannot be approved.
Database list queries page before doing heavy workCode qualityDeveloper
RuleA list query picks the page of rows first, then does the heavy work only for those rows.
ResultThe checking-aids list builds the full record for every gauge in the plant, then keeps 200. Fine for a few gauges, slow for thousands.
Filter and page the gauge ids first, then build the full record only for that page. The "anomalous" filter needs the computed record, so it has to be handled separately.
Database lists have a size limitCode qualityDeveloper
RuleEvery database list is paged, so one answer never carries every row.
ResultThe plant gauge list has no limit and returns every gauge at once. The process flow list was not checked.
Add page parameters to get_cp_plant_gauges, and check get_cp_process_flow.
Screen lists let users reach every itemCode qualityDeveloper
RuleA list screen lets the user reach every item, with page controls or load more.
ResultTasks, schedules, production and inspection plans load 200 items and have no page controls. Item 201 onward cannot be reached. Only the plans list pages.
Add page controls (or load more) to the task, schedule, production and inspection plan lists.
Queue producers exist for every queue events can useCode qualityDeveloper
RuleEvery queue that an event can be routed to is connected to the Worker that sends to it.
ResultOnly the events queue is connected. All events use it today, so nothing fails yet. Notification and document queues are not connected, and the notification routing list is empty.
Connect the notification and document queues in producersFrom and fill NOTIFICATION_ROUTES before any event routes to them.
Row locks are taken in a fixed orderCode qualityDeveloper
RuleWhen a function locks several rows, it locks them in a fixed order so two users cannot block each other.
ResultThe 3 multi-row lock loops we found are ordered. The other 54 lock statements were not reviewed. The NC code named in the audit is not in this repo.
Review the other FOR UPDATE statements for a fixed order, and re-check once the NC migrations are in this repo.
Part number and revision are upper-cased as typedCode qualityDecision
RuleFree-text codes such as part number and revision are turned into capitals as the user types.
ResultThe create-plan form does not do it. The rule is written for Standards Room forms, and it is not clear that Control Plan counts.
Decide whether Control Plan forms must upper-case free text. If yes, upper-case the part number and revision fields.
Production login and token proof on hosted SupabaseSecurityDecision, then developer · V-01, V-02
RuleUsers sign in through a real login system, and the database token works on the hosted setup.
ResultNo production login system yet. The token has not been tried on hosted Supabase.
Choose the identity provider. Prove the token on hosted dev with a dedicated rotating key.
Customer separation does not trust the APISecurityCTO decision · V-04
RuleThe database does not simply trust the customer that the API names.
ResultIt does trust it. All customers share one database.
Record written acceptance of the shared-database design.
CI/CD pipelineOperationsDeveloper · V-03
RuleEvery change is built and tested automatically before it can be merged.
ResultNo pipeline. The workflows folder holds only a README.
Build the pull-request pipeline, then deploy and smoke test.
Workers deployed and reachableOperationsDeveloper and owner · T18
RuleThe Workers run on Cloudflare.
ResultNot deployed. They ran only on this machine.
Set up hosted dev and deploy the four Workers.

Medium 15 flags

FlagOwnerDetailsWhat to do
Rules written in two places are kept equal by testsArchitectureDeveloper · V-10
RuleIf a rule is written twice (screen and database), a test proves both give the same answer.
ResultOnly frequency reading has such a test. Reading pass or fail, the specification reader and the allowed actions have none.
Add three parity tests (reading evaluation, specification parser, allowed-action tables).
Colours come from named tokens, not typed inDesignDeveloper
RuleScreens use named colours from one place, not typed colour codes.
Result70 typed colour codes are scattered through the screen files.
Replace the 70 hex values in components with named tokens from tokens.css.
Collapsible sidebar (56 px rail, Ctrl+B)DesignDeveloper
RuleThe sidebar collapses to a narrow icon bar, and Ctrl+B toggles it.
ResultNot built.
Build the 56 px rail and the Ctrl+B toggle from the design file.
Workflow 5: NC trigger and reaction (C1, C2, C3, MRB)FunctionalityLater slice · T26
RuleA failed inspection starts a reaction: stop, contain, correct, decide what to do with the parts.
ResultNot built. A failed result is recorded, but nothing reacts to it.
Build the failure and reaction workflow.
PDF and Excel output, evidence packsFunctionalityLater slice
RuleThe system produces PDF and Excel documents and evidence packs.
ResultNot built. The document service is a placeholder.
Build document-api rendering, the documents queue and bucket.
Functional requirements covered (41)FunctionalityLater slice
RuleAll 41 requirements in the functional requirements document are built.
Result15 built, 13 partly built, 13 not built.
Finish the 13 partly built and the 13 unbuilt requirements.
Unit and database testsCode qualityDeveloper · V-07, V-08
RuleAll tests pass.
Result2,671 of 2,679 pass. The 8 failures come from Windows file paths and line endings. 22 database tests also fail on real Postgres because of how the tests are run.
Fix Windows paths and line endings. Make the database tests safe on real Postgres.
Dependencies free of high and critical issuesCode qualityDeveloper · V-06
RuleLibraries have no high or critical known security issues.
Resultreact-router 6.30.6 has 2 moderate issues. vitest 3.2.7 (development only) has 2 critical.
Upgrade react-router to 7.18 or newer and vitest to 4.1.11 or newer.
Bulk save keeps valid rows when one row is staleCode qualityDecision
RuleSaving many rows saves the valid ones even if one row is out of date.
ResultIt is all or nothing by design. If one row is stale nothing is saved, but every row is reported with its conflict and changed fields.
Decide whether all-or-nothing is the intended behaviour for the grid save, and record it in docs/decisions.
Task tables writable only through named functionsSecurityDeveloper · V-05
RuleTask data can be changed only through the named functions.
ResultThe API role still has direct write rights, held back only by a hidden setting. The API cannot reach that setting.
Remove the direct write rights on the task tables (task T34).
Acceptance criteria have a passing proof recordedTraceabilityDeveloper
RuleEach acceptance criterion is marked passing, with the test that proves it.
Result9 criteria: 0 passing, 1 partly, 6 pending, 2 deferred.
Update the acceptance table as each criterion passes, with the proving test file.
The proving test files named in the table existTraceabilityDeveloper
RuleThe test files named in the acceptance table exist.
ResultThey do not. The e2e folder holds only a README, and Playwright is not installed.
Write the e2e specs the table names and install Playwright, or correct the table.
Automated accessibility testAccessibilityDeveloper
RuleAn automated test checks the screens for accessibility problems.
ResultNone in the repo.
Add axe-based tests for the main screens.
Performance budgets measured (DDD p.56)OperationsDeveloper
RuleSpeed targets are measured, for example opening a 500-line plan in under 3 seconds.
ResultThe targets are written but never measured. The test data generator is missing.
Write the synthetic-plan generator and measure the budgets.
Plan approval timeOperationsDeveloper · V-09
RuleApproving a plan finishes inside the database time limit (8 seconds).
ResultOnce it took 7.8 seconds and was cancelled.
Measure approval on large plans and set an explicit database time limit.

Low 5 flags

FlagOwnerDetailsWhat to do
Every feature has a Container, a Presenter and a ViewDesignDeveloper
RuleEach feature has three parts: a Container (gets the data), a Presenter (the logic) and a View (what is shown).
Result62 views, 44 presenters, 27 containers. 2 of 19 feature folders (lifecycle, revision) have no Container.
Add a Container to the lifecycle and revision folders, or change the frontend guide.
Presenters are plain logicDesignDecision
RuleA Presenter holds the screen logic.
Result40 of 44 presenters are React hooks. The guide does not forbid it, but it is not the plain-logic style.
Confirm that hook-style presenters are accepted, and write it into the frontend guide.
PFMEA linking, gauge checks beyond the calibration dateFunctionalityLater slice
RulePFMEA is linked to the plan, and gauges are checked for more than their calibration date.
ResultNot built.
Build PFMEA linking and the gauge fitness (MSA) check.
Code formattingCode qualityDeveloper · V-07
RuleAll files follow one code style.
Result2 files are not formatted. 659 more are flagged only because of Windows line endings.
Run prettier on 2 files and add .gitattributes.
Database functions list their columns (no SELECT *)Code qualityDeveloper
RuleDatabase functions name the columns they read instead of using SELECT *.
Result154 uses of SELECT *. Safe when filling one whole row variable, risky when filling separate variables. The ban comes from the Calispec checklist, not from our CLAUDE.md.
Replace select * with column lists where it fills separate variables. Decide whether the ban applies to whole row variables.

Not measured 4 items

ItemWhy
Each screen matched to a design frameDesigndocs/design-coverage.md is an empty table, so no screen is tied to a design frame.
Security headers (CSP) on the appSecurityNo security headers file found. Needs a check on a hosted environment.
Contrast and keyboard use on real screensAccessibilityNeeds a run in a browser.
Rate limits and real queue behaviourOperationsDev limits are raised and queues are simulated locally.

Checked on 15 Oct 2026 at commit 44bde70. Test suites ran on 13 Oct 2026. The code has not changed since.