FibreTrace QC Test Script — Analysis
Source: .local/qc-test-script/fibretrace-qc-test-script.docx (v1.0, from Malcolm, 2026-08-19). Condensed cases: test-cases.md (same folder). Line references X:<n> cite extracted.txt.
Citation shorthand. L:<path>:<line> = the Lovable clone fibre-trace-studio-8b5c03d8 read at ref origin/main = ffab85ca, 2026-08-19 (the clone’s checked-out working tree is 4,264 commits behind that ref — every Lovable citation below was verified against the ref via git show/git grep, not the stale files on disk). M:<path>:<line> = fibretrace-monet/apps/dashboard. Memory/business-logic citations use full paths.
TL;DR
- The script targets the live Lovable MVP, not monet: 30 of 31 distinct routes exist at Lovable
origin/main; monet is missing 10 of them outright and stubs the data layer on 14 more. Running this script against monet would produce false bug reports on essentially every case. - The one route that exists nowhere (Lovable, monet, or canvas) is
/insights(R-13), and the Insights module toggle (A-08) does not exist either — the admin module registry has exactly 5 modules, none named Insights (L:src/pages/admin/AdminCompanies.tsx:81). - The document does not settle the two known open questions: it uses both CU labels itself (“Claim Unit / credit unit”, X:6, X:276), and it mandates “Nominations (not Purchase Order)” wording (X:276) while still titling R-01 “Create a nomination (PO)” and routing everything through
/purchase-orders— the table-rename question stays open. - Malcolm’s fresh-accounts requirement is achievable self-serve for users/companies (real signup + admin approval flow exists at HEAD), but 7 of the 11 object types in the env checklist are FT-Admin-gated (programme, programme registration, reservation, license, scanner, SDU, product categories). The QC account-switcher tab would remove login friction but does not remove the admin-gated object creation — that is the actual bottleneck.
a) Which app is under test
Verdict: the live Lovable MVP (fibre-trace-studio), full stop. Not monet’s apps/dashboard, and not “partly both”.
Evidence:
- Route census. Of the 31 distinct routes referenced by the 50 cases, 30 exist at Lovable
origin/main(full matrix in section b). The only miss is/insights(R-13), which exists in no codebase we can inspect: at Lovableorigin/mainthe only hit for “insight” is an unrelated local constant (L:src/components/EmptyProgrammeHealthCard.tsx:51); monet has no route or page; a case-insensitive sweep offrontend-canvas(.local/frontend-canvas/repo/src/) returns zero hits. R-13 tests a feature that has not been built anywhere yet (unverified whether it is in-flight inside Lovable today). - Admin-route freshness. The script references
/admin/programs(notprogrammes),/admin/reservation-requests,/admin/overrides,/admin/claim-verifications,/admin/settings— all of which only came into existence in the recent Lovable window (.local/lovable/sync-20260819/port-plan.md:176records theAdminOverrides/AdminReservationRequestsadditions and theadmin/programmes→admin/programsrename). Atorigin/main,/admin/programsis canonical and old/admin/programmesis a redirect (L:src/App.tsx:215-216). Monet still routes the OLD name only (M:src/App.tsx:168-169) and has no reservation-requests, signup-requests, overrides, claim-verifications, or admin-settings routes at all. Whoever wrote the script was looking at the live Lovable app as of this week. - Business-rule fingerprints. The script’s rules are implemented only in Lovable: the 18-month reservation default (
L:src/pages/admin/AdminProgrammes.tsx:110,209,247), the negative-adjustment ledger (“Use a negative value to subtract. 1,000 CU = 1 MT”,L:src/pages/admin/AdminProgrammes.tsx:1463), the obfuscation setting keyobfuscate_verification_idsdefaulting to ON (L:src/hooks/useObfuscateVerificationIds.ts:4, toggle atL:src/pages/admin/AdminSettings.tsx:169), and the product-category CU rate model (L:src/pages/admin/AdminProductCategories.tsx:144). The real Fibretrace REST backend has none of these concepts — “real backend has noexpires_atconcept anywhere (confirmed 2026-08-18)” (.local/lovable/sync-20260819/port-plan.md:79). - Context fit. Malcolm’s message is about the MVP used for demos, the demo modal, and a possible “QC modal account-switcher tab” — the demo modal (
DemoNavBar) exists only in the Lovable app (L:src/components/DemoNavBar.tsx:15-28).
Note on a premise: the working assumption that monet has no admin surface is not literally true — monet routes a 13-page /admin block (M:src/App.tsx:167-184) and 4 of 5 owner pages (M:src/App.tsx:151-154). But those pages are an early, now-stale port: they lag Lovable’s admin surface by 5+ screens, and their data tables are almost all no-op stubs (section b). The practical conclusion is the same — admin-route tests cannot be run against monet.
b) Route coverage matrix
Monet data-layer ground truth: M:src/lib/apiAdapter.ts registers 50 tables in ALL_TABLES (:109-128); only 12 have real mappers (company_modules:142, company_api_access:154, app_settings:161, company_users:263, company_partners:304, company_details:367, scanners:440, audits:462, facilities:533, invoices:577, companies:614, collections:647); the other 38 fall through to the no-op stub returning []. Every write through the fake client throws MutationNotImplementedError regardless of table (M:src/lib/apiAdapter.ts:835-858) — writes are only “supported” where a page bypasses the adapter for a real RTK Query mutation (e.g. facility creation). The 2026-08-19 port-plan’s API resolution confirms the window: EXISTS=0, 9 GAP, 3 PARTIAL (.local/lovable/sync-20260819/port-plan.md:4,69-189), and state.json carries 10 known_unported units — the whole PO/claims/programmes domain has been BE-blocked since June.
| Route | QC cases | Lovable origin/main |
Monet router | Monet data layer | Run on monet? |
|---|---|---|---|---|---|
/ (home) |
M-07, R-12 | L:src/App.tsx:165 |
M:src/App.tsx:126 |
company_modules/app_settings wired; all PO/claim KPIs stubbed | PARTIAL — KPIs empty for a known BE reason |
/signup, /signup/status |
A-10 | L:src/App.tsx:151-152 |
ABSENT | — | NO — route absent |
/admin/programs |
A-01, A-02 | L:src/App.tsx:215 (old programmes redirects, :216) |
ABSENT as named (only stale /admin/programmes, M:src/App.tsx:168-169) |
fibre_programmes stub (apiAdapter.ts:178-183), programme_reservations stub (:233) |
NO |
/admin/reservation-requests |
A-03, A-04, R-04 | L:src/App.tsx:222 |
ABSENT | reservation_requests not even in ALL_TABLES |
NO |
/admin/companies |
A-05…A-09 | L:src/App.tsx:219 |
M:src/App.tsx:170 |
companies/company_details/company_modules wired; programme_participants/programme_producers stubs (:230-231); writes throw |
PARTIAL — lists render, registrations impossible |
/admin/signup-requests |
A-10 | L:src/App.tsx:221 |
ABSENT | signup_requests not in ALL_TABLES |
NO |
/admin/facilities |
A-11 | L:src/App.tsx:225 |
M:src/App.tsx:173 |
facilities WIRED (:533) |
READS yes |
/admin/scanners |
A-11 | L:src/App.tsx:224 |
M:src/App.tsx:172 |
scanners WIRED (:440); scanner_facility_assignments stub (:381) |
PARTIAL |
/admin/licensing |
A-12 | L:src/App.tsx:226 |
M:src/App.tsx:174 |
facility_licenses stub (:237) |
NO — empty shell |
/admin/overrides |
A-13 | L:src/App.tsx:223 |
ABSENT | — | NO |
/admin/claim-verifications |
A-14 | L:src/App.tsx:234 |
ABSENT (only /admin/verifications, M:src/App.tsx:179) |
claim_verifications not in ALL_TABLES; verifications/sessions stubs marked “BE-blocked” (:200-209) |
NO |
/admin/settings |
A-15 | L:src/App.tsx:244 |
ABSENT — no AdminSettings.tsx in monet at all |
— | NO |
/admin/fibre-production |
P-05 (expected) | L:src/App.tsx:232 |
M:src/App.tsx:180 |
production_records stub (:234) |
NO — empty shell |
/owner |
O-01, A-09 | L:src/App.tsx:190 |
NOT ROUTED — OwnerDashboard imported (M:src/App.tsx:73) but never given a <Route>; dead code |
— | NO |
/owner/certificates |
O-02 | L:src/App.tsx:192 |
M:src/App.tsx:153 |
program_certificates(+assignments) stubs |
NO — shell |
/owner/documents |
O-02 | L:src/App.tsx:191 |
M:src/App.tsx:154 |
program_documents(+assignments) stubs |
NO — shell |
/owner/program-uptake |
O-03 | L:src/App.tsx:194 |
M:src/App.tsx:152 |
stubs | NO — shell |
/owner/program-record |
O-03 | L:src/App.tsx:193 |
M:src/App.tsx:151 |
stubs | NO — shell |
/sdu |
P-01, P-02 | L:src/App.tsx:184 |
M:src/App.tsx:143 |
sliver_delivery_units (:236) / sdu_activity_logs stubs |
NO — shell |
/fibre-creation |
P-03…P-06 | L:src/App.tsx:183 |
M:src/App.tsx:142 |
production_records (:234) / pigment_orders (:228) stubs |
NO — shell |
/facilities |
M-01 | L:src/App.tsx:185 |
M:src/App.tsx:144 |
facilities WIRED (:533) |
YES (closest to runnable) |
/facilities/add |
M-01 | L:src/App.tsx:140 |
M:src/App.tsx:111 |
real facilitiesApi mutation hooks (bypasses adapter) |
YES |
/scanners |
M-02, A-11 | L:src/App.tsx:179 |
M:src/App.tsx:141 |
scanners WIRED (:440) |
READS yes |
/verifications (Scan History) |
M-03 | L:src/App.tsx:177 |
M:src/App.tsx:138 |
verifications/sessions stubs, “BE-blocked” (:200-209) |
NO — shell |
/partners |
M-04 | L:src/App.tsx:188 (old /settings/partners redirects, :189) |
ABSENT — monet has only /settings/partners (M:src/App.tsx:161) |
company_partners WIRED (:304) |
PARTIAL — feature exists at the old path |
/link-to-po |
M-05, M-06 | L:src/App.tsx:176 |
M:src/App.tsx:134 |
purchase_orders/purchase_order_verifications/purchase_order_evidence_packs all stubs |
NO — shell |
/purchase-orders |
R-01, R-02 | L:src/App.tsx:173 (+:id detail :174) |
M:src/App.tsx:132 |
purchase_orders stub |
NO — shell |
/claim-position |
R-03, R-04, R-05, A-02 | L:src/App.tsx:170-171 (incl. :id) |
M:src/App.tsx:129 (no :id; separate flat /claim-position-list, :130) |
fibre_programmes/programme_reservations stubs |
NO — shell |
/ready-to-claim |
R-06, R-07 | L:src/App.tsx:175 |
M:src/App.tsx:133 |
same PO/claim stubs | NO — shell |
/claims |
R-08…R-11 | L:src/App.tsx:172 |
M:src/App.tsx:131 |
production_claims stub (:235) |
NO — shell |
/insights |
R-13 | ABSENT (only word-hit L:src/components/EmptyProgrammeHealthCard.tsx:51) |
ABSENT | — | NO — exists nowhere |
Tally (31 routes): Lovable origin/main has 30/31 (missing: /insights). Monet: 7 routed with at least partially wired data (/, /admin/companies, /admin/facilities, /admin/scanners, /facilities, /facilities/add, /scanners), 14 routed but empty-state shells (all owner pages, /sdu, /fibre-creation, /verifications, /link-to-po, /purchase-orders, /claim-position, /ready-to-claim, /claims, /admin/licensing, /admin/fibre-production), 10 absent (/signup(+status), /admin/programs-as-named, /admin/reservation-requests, /admin/signup-requests, /admin/overrides, /admin/claim-verifications, /admin/settings, /owner, /partners, /insights).
Practical consequence: zero of the 50 cases can pass end-to-end on monet today — every write path either throws MutationNotImplementedError or is toast-guarded (“backend pending”), except facility creation. If anyone runs this script against monet, the failures on all A/O/P cases, M-03…M-07, and R-01…R-13 are known BE gaps, not bugs — filing them would duplicate .local/backend-specs/integration-audit/ and the port-plan beSpecForGap blocks. Only M-01 (facilities) and read-only parts of M-02/A-11 exercise real monet wiring.
c) Business rules captured
Cross-checked against .local/business-logic/, ~/.claude/memory/fibretrace/, and the Lovable sync records (research by a read-only agent; quotes spot-checked against the files).
| # | Rule (X:6-11) | Status vs recorded knowledge |
|---|---|---|
| 1 | 1 CU = 1 kg; CU = qty × net weight × blend % | ALREADY RECORDED — but the formula as stated MISMATCHES the app. Recorded: “CU/piece = baseline_net_weight_kg × blend% × loss_multiplier (1000 CU = 1 MT)” (~/.claude/memory/fibretrace/reference_claim_units_product_categories_fibretrace-monet.md:23). The app itself says the same: “A CU is one kilogram of raw marked fibre: baseline weight x blend ratio x internal loss multiplier” (L:src/pages/admin/AdminProductCategories.tsx:144), and the loss multiplier is deliberately internal-only (...md:29). The QC rule omits loss_multiplier — testers computing expected CU by qty × weight × blend will get numbers that don’t match the app wherever loss_multiplier ≠ 1. “1 CU = 1 kg” itself is consistent everywhere. |
| 2 | Blend % from admin product-category config, never hard-coded | ALREADY RECORDED, matches (reference_claim_units_product_categories_fibretrace-monet.md:29: product_categories.default_blend_pct, admin CRUD in AdminProductCategories.tsx). |
| 3 | Fibre Producer only via programme registration; SDU/EWR = feature gate, not persona | Resolves a recorded fork. The old hook contradicts it: isProducer = company_modules("sdu").enabled OR ≥1 programme_producers row (.local/business-logic/persona-case-matrix.md:20, usePersona.ts:34). The newer hook matches exactly: isProducer = ≥1 programme_producers row (persona-case-matrix.md:31, usePersonaContext.ts:36). The rule effectively declares usePersonaContext canonical and marks the old SDU-OR clause as a bug to retire — worth confirming with Malcolm (P-01 is precisely the test for this). |
| 4 | IDs/GUIDs obfuscated by default, unmask when claim-linked | ALREADY RECORDED, matches (reference_verification_id_obfuscation_fibretrace-monet.md:27-31: visibility = NOT app_settings['obfuscate_verification_ids'] OR session_is_claim_linked, default true). Rule 4 + A-15 also narrow that memory’s open question 3 (server-side vs client-side masking): confirms the product intent; the mechanics question stays open. |
| 5 | 18-month default expiry; negative entries allowed | ALREADY RECORDED, matches closely (reference_reservation_requests_expiry_fibretrace-monet.md:3,16,26-27). Closes that memory’s open question 3 (“per-adjustment ledger including negative deltas, or single reserved_mt override?” — ...md:58): the QC script now tests the ledger model as expected behaviour, so the ledger IS the product decision. Note this is exactly port-plan unit 14’s GAP — the real BE has neither expires_at nor negative ledger entries (port-plan.md:145). |
| 6 | reserved ≥ nominated ≥ claimed must reconcile | NEW. No prior record states the three-way inequality; the closest is a two-way demand-vs-supply check in OwnerDashboard (reference_programme_owner_domain_be_gap_fibretrace-monet.md:162). Worth saving to business-logic notes as a first-class invariant (X-01, O-03, R-03, R-06 all test facets of it). |
The two known open questions:
- Q1 — CU label (“Claim Unit (CU)” vs “credit units”), open since 2026-08-18 (
.local/lovable/sync-20260818/RUN-SUMMARY.md:30, re-confirmed open insync-20260819/RUN-SUMMARY.md:104). NOT settled by this document. The script itself writes “1 CU (Claim Unit / credit unit)” (X:6), O-01 says “credit units (CU)” (X:106), and X-02’s expected wording is “Claim Unit (CU) / credit units” (X:276) — it canonises the pair, not a winner. Anyone hoping this doc closes the terminology sweep (port-plan unit 9) will be disappointed; ask on Thursday. - Q2 — Purchase Order → Nomination rename. Partially settled. X-02 mandates “Nominations (not Purchase Order)” as UI wording (X:276) — that settles the display-terminology direction. But the script itself still says “Create a nomination (PO)” (X:198), “Link your Purchase Orders > New nomination” (X:200), routes everything through
/purchase-orders, and A-07 calls the menu “Link your POs” (X:56). The open question recorded inreference_po_domain_be_gap_fibretrace-monet.md:146— whether the underlying TABLE/domain renamespurchase_orders→nominations— remains open (also flagged “enum unconfirmed post-2026-08-18 rename” inport-plan.md:136).
d) Test-data build-out (Malcolm’s fresh-accounts requirement)
All findings verified at Lovable origin/main (an earlier pass against the stale working tree wrongly concluded signup and reservation requests didn’t exist — they do at HEAD). Auth model first, because everything hangs off it: login is a company_users email lookup (ilike), the password is only checked for non-emptiness, and the 2FA code is a demo code displayed on the login screen itself (L:src/pages/Login.tsx:102-115,151). “Emails” (invites, approvals) are simulated — appended to localStorage["fibretrace_signup_emails"] (L:src/lib/signup.ts:67-70), never actually sent. So creating and entering fresh accounts is cheap; nothing requires a real inbox.
Build order and who can create what:
| Step | Object | Screen / write | Persona | Self-serve? |
|---|---|---|---|---|
| 0 | Product categories (blend %) | /admin/product-categories, product_categories CRUD (L:src/pages/admin/AdminProductCategories.tsx) |
FT Admin | ADMIN-ONLY. Missing from the script’s env checklist entirely, yet R-01/A-13/rule 2 depend on it |
| 1 | Fibre programme | /admin/programs, fibre_programmes.insert (L:src/pages/admin/AdminProgrammes.tsx:257); default_reservation_term_months default 18 (:247); no blend-% field on the form |
FT Admin | ADMIN-ONLY |
| 2 | Companies + users | Self-serve: /signup 4-step wizard inserts signup_requests incl. company_type/tier (L:src/pages/Signup.tsx:191-192) → admin approves at /admin/signup-requests, which bootstraps companies+company_details (with type/tier, :70-77)+facilities+company_users+notification in one action (L:src/pages/admin/AdminSignupRequests.tsx:63-108). Or direct: admin quick-create is name+code only (L:src/pages/admin/AdminCompanies.tsx:197-199) + user (L:src/pages/admin/AdminUsers.tsx:68). Third path: partner-CSV bulk invite creates unclaimed:true placeholder companies with an invite token, claimed later via /signup?claim=<token> (L:src/components/partners/ImportPartnersCsvModal.tsx:104-140, Signup.tsx:75-113) |
Any (signup) + FT Admin (approval) | YES, with an admin approval step. company_type lives on company_details, but per rule 3 the persona still comes from programme registration (next row) |
| 3 | Programme registration (participant / manufacturer / producer / owner) | AdminProgrammes sub-dialogs: participant (L:...AdminProgrammes.tsx:448), manufacturer (:508), producers + owner company (:262-270 — delete-then-insert enforces max one owner company per programme; the old standalone /admin/program-owners page is gone at HEAD); owner visibility flag separately via company_modules("program_owner") (L:src/pages/admin/AdminCompanies.tsx:254) |
FT Admin | ADMIN-ONLY |
| 4 | Reservation (+ negative adjustment) | Programme’s reservation panel, programme_reservations.insert; negative allowed there (“Use a negative value to subtract”, L:...AdminProgrammes.tsx:542,1463; no floor check on the manual override insert, :568,598). Retailer-side request: AddReservationModal → reservation_requests.insert (L:src/components/AddReservationModal.tsx:167-173; submit disabled for addCu <= 0, :158), reviewed at /admin/reservation-requests where approve throws if cu <= 0 (L:src/pages/admin/AdminReservationRequests.tsx:97-135, floor check :101) |
FT Admin (grant) / Tier 0 (request) | Request self-serve; grant ADMIN-ONLY. Negative entries possible ONLY via the programme panel, never through the request queue |
| 5 | Facility license, then facility | License grant: /admin/licensing (facility_licenses). Facility: self-serve /facilities/add but hard-gated on available licenses; admin path via /admin/facilities |
FT Admin (license), Tier 1 (facility) | Facility self-serve ONLY after admin grants a license |
| 6 | Scanner | Create+assign at /admin/scanners. Company-side /scanners has NO in-app request — the “request” is a mailto to support (L:src/pages/Scanners.tsx:154-165) |
FT Admin | ADMIN-ONLY (M-02’s “request” step is out-of-band) |
| 7 | SDU device | /admin/sdu, sliver_delivery_units.insert (L:src/pages/admin/AdminSDU.tsx:88). Company /sdu page cannot register devices |
FT Admin | ADMIN-ONLY |
| 8 | Scans / verifications | Demo Scan simulator only (DemoScanModalV2 insert chain: collection :570 → verification :580 → scans :656 → audits :710) — no hardware/BE ingestion in this app. M-03 explicitly allows “simulate” |
Tier 1 | YES (simulator) |
| 9 | Production records (SDU / EWR / manual) | /fibre-creation manual insert (L:src/pages/FibreCreation.tsx:441); SDU via DemoSDUModal.tsx:165; EWR via DemoEWRModal.tsx:49 (source:"ewr_demo") — EWR is simulator-only; no real EWR integration exists |
Tier 4 | YES (manual + simulators), after steps 3/5/7 |
| 10 | Nominations (POs) + partners | PO create/import at /purchase-orders (CSV import insert ImportPoCsvModal); partner invite by 3-letter code, case-insensitive ilike match, at /partners (L:src/pages/settings/SettingsPartners.tsx:160-177) |
Tier 0 / Tier 1 | YES |
| 11 | Insights module on/off | DOES NOT EXIST. Module registry is exactly billing, verified_cta, verifications_menu, scanners_menu, program_owner (L:src/pages/admin/AdminCompanies.tsx:81); no /insights route or page anywhere |
— | BLOCKED — cannot be built by anyone |
Dependency chain: 0 → 1 → 2 → 3 → {4, 5} → {6, 7} → {8, 9, 10}. Nothing needs a database seed or BE work except item 11 (feature absent) — everything else has a working create form at HEAD.
Does the QC account-switcher tab help? Partially. The current demo modal is a hardcoded list of 7 seed personas (L:src/components/DemoNavBar.tsx:15-28 — Tier 4 GIN “SUNDOWN”, Tier 4 rPET, Tier 1, Tier 2, Tier 0 WALMART, Program Owner, plus a dev-gated empty Tier 0); switchUser() re-fetches those fixed user IDs (:53-62) and cannot see newly created companies. So yes — a switcher tab that lists arbitrary/fresh accounts would remove the logout → email → demo-2FA cycle per persona hop, and it is the only switching aid besides admin Impersonate (L:src/pages/admin/AdminUsers.tsx:83-84,180), which already exists and works today. But switching is not the real friction: object creation is. Steps 0-1, 3-7 are all FT-Admin-gated, so either every tester gets FT Admin access (the /admin block is gated by an adminLoginRequired setting, L:src/components/AdminLayout.tsx:60-69), or one person pre-builds the env per the chain above. The switcher solves neither the admin-gated spine nor the two feature absences (Insights, override-add).
One direct conflict with Malcolm’s rule: R-13 says “Insights module (Walmart only in test data)” (X:258) — but WALMART is precisely one of the demo-modal companies testers are forbidden to touch, and no admin toggle exists to enable Insights for a fresh company. As written, R-13 is doubly impossible: the feature doesn’t exist, and its only named data home is a banned account.
e) Risks and gaps in the script itself
Internal contradictions:
- A-01 vs rule 2 (blend %). A-01 step 2 says set “default blend %” on the programme and expects “no blank blend %” (X:25-26), but rule 2 (X:7) says blend % lives in product-category config — and the programme create form at HEAD has no blend field at all (insert payload: name/description/status/dates/pigment/fibre_type/country/heatmap/logo,
L:src/pages/admin/AdminProgrammes.tsx:257region;default_reservation_term_monthsis the only numeric default,:247). A-01 will “fail” on a field that is by-design elsewhere. - Rule 1 omits the loss multiplier the app itself applies (
L:src/pages/admin/AdminProductCategories.tsx:144; memoryreference_claim_units_product_categories...md:23). Any tester hand-computing CU = qty × weight × blend will file false mismatches for categories with loss_multiplier ≠ 1 (and the multiplier is internal-only, so they can’t even see it). - X-02 vs the script’s own vocabulary. X-02 demands “Nominations (not Purchase Order)” and “Verification Records (not Claims)” (X:276), yet the script itself says “Create a nomination (PO)” (X:198), “Link your Purchase Orders” (X:200), “Link your POs” (X:56), and routes
/purchase-ordersand/claims. If URLs count as “wording”, X-02 can never pass; define scope = visible copy only. - A-04 contradicts the implementation at the route it names. A-04 expects a negative CU entry to be “accepted and saved… no validation error blocking the minus sign” at
/admin/reservation-requests(X:39-41) — but that queue actively rejects non-positive values: the approve path throws ifcu <= 0(L:src/pages/admin/AdminReservationRequests.tsx:101) and the retailer request modal disables submit foraddCu <= 0(L:src/components/AddReservationModal.tsx:158). The only negative-capable surface is the programme’s reservation panel at/admin/programs(“Use a negative value to subtract”,L:src/pages/admin/AdminProgrammes.tsx:542,588,1463). As written, A-04 will fail at the named route by design — either the route is a mislabel or the queue is intended to gain negative-entry support.
Cases testing things that don’t exist (would fail for a known reason, not a bug):
- A-08 + R-13 (Insights) — no module toggle, no route, anywhere (section d, item 11). Plus the Walmart conflict above.
- A-13 (overrides) —
/admin/overridesat HEAD is a read-only calibration/audit report (reads PO lines vs category defaults, computes deltas, exportsrate-and-quantity-overrides.csv; zero.insert(calls in the file,L:src/pages/admin/AdminOverrides.tsx:48-84,161-175). Step 1 “Add a rate/quantity override” has no UI on that page; overrides appear to arise from editing blend/qty on a nomination line. Either the step means “edit a nomination line and observe it on /admin/overrides”, or the add-UI is still to be built. - P-06 (production limits) — no allocation-limit validation found in
FibreCreation.tsxat HEAD (only.limit(1)query calls; no “exceeds allocation” guard located — unverified, may exist server-side, but nothing client-side names a limit). Expect this to fail as written. - M-02 (request a scanner) — the in-app “request” is a mailto to support (
L:src/pages/Scanners.tsx:154-165); the expected “request CTA” exists, but its delivery is out-of-band email, so “Admin assigns it” (step 2) can’t be triggered by the request itself.
Observability gaps:
- Invite/approval emails are simulated into
localStorage["fibretrace_signup_emails"](L:src/lib/signup.ts:67-70). A-09’s “Owner user receives an invite” and A-10’s approval email are only observable via that demo log/toasts — the pass criterion should say so. - X-05 (persona isolation) — access control is client-side demo auth:
/adminis gated only when theadminLoginRequiredsetting is on (L:src/components/AdminLayout.tsx:60-69), the login accepts any password value, and the 2FA code is printed on the login page (L:src/pages/Login.tsx:151). “Access denied” expectations need the auth settings stated as a precondition, and “no other company’s data is ever visible” is a data-scoping check in a client-side-filtered app — deep verification is out of QC’s reach here. - Companion CSV missing. The doc tells testers to record results in
fibretrace-qc-test-script.csv(X:4), which was not part of what we received (.local/qc-test-script/holds only the docx).
Unstated dependencies / ordering:
- The prescribed section order is broken by M-05. “Run the sections in order” (X:4), but M-05 (section 4) requires “a shared nomination line” that only R-01 (section 5) creates (X:180 vs X:200). Minimum viable order: A → R-01/R-02 (retailer creates+shares nominations) → M-01…M-07 → rest of R → O → X. Similarly O-03 and X-01 need claims from R-07/R-08 to exist.
- Product-category setup is a hidden precondition (R-01, A-13, rule 2 depend on
/admin/product-categories; no A-case creates categories and the env checklist omits them). - Unlabeled case-to-case couplings inside sections: A-02 needs A-07’s participant; A-03 needs R-04’s request (admin section runs before the retailer section that generates requests — A-03 needs at least one pre-staged request or must be revisited after R-04); A-14 needs claims (R-07/R-08) and scans (M-03); O-01’s “over-commitment red” needs nominations exceeding capacity, i.e. deliberate over-raising in R-01.
Parallelisation: after the FT Admin spine (A-01…A-02, A-05…A-12) and product categories are built, the Producer chain (P-01…P-06) and the Manufacturer+Retailer chain (R-01→M-01…M-07→R-03…R-12) are independent of each other and can run in parallel on the same programme; O and X run last (they reconcile the others’ outputs). Two testers can also parallelise fully by building two independent programmes.
f) Proposed split + questions for Thursday
Assumption: testing happens on the live Lovable MVP with fresh nia-/tester-marked data per the build order in (d); one person holds FT Admin.
| Who | Sections | Cases | Rough effort |
|---|---|---|---|
| Tester A (holds FT Admin) | Env build (d) + Section 1 + X-05/X-06 | A-01…A-15 (A-08/A-13 expected-blocked), 2 X cases | 0.5 day env + 1 day testing |
| Tester B | Section 3 + Section 4 | P-01…P-06, M-01…M-07 (needs R-01 from Tester C first) | 1 day |
| Tester C | Section 5 + X-01…X-04 | R-01…R-13 (R-13 expected-blocked), 4 X cases | 1.5 days |
| Whoever frees first | Section 2 (after claims exist) | O-01…O-03 | 0.5 day |
Total ≈ 4.5 tester-days per full pass, minus ~3 cases currently untestable (A-08, A-13, R-13). Binh Le as coordinator/reviewer fits Tester A (admin spine) since that role sequences everyone else.
Questions for Malcolm (each tied to a finding above):
- (e5/d11) Insights: no
/insightsroute and no Insights module toggle exist at Lovable HEAD — are A-08/R-13 testing something you’re about to build, or should they be dropped from this round? And R-13 names Walmart — a demo-modal company we’re told not to touch. Which rule wins? - (e6)
/admin/overridesis a read-only calibration report with no “add override” form — is A-13’s intended trigger “edit blend/qty on a nomination line, then verify it appears here”? If so we’ll reword the step. - (e1/e13) Where does blend % actually get set — the programme form has no blend field; only
/admin/product-categoriescarriesdefault_blend_pct. Should A-01 drop that step, and should the env checklist gain “product categories configured” as step 0? - (e2) Rule 1 omits the internal loss multiplier that the app applies (
CU = baseline weight × blend × loss multiplierper the admin UI’s own description). For QC math, should testers assume loss_multiplier = 1 categories only, or get the multiplier values? - (c/Q1) The script uses both “Claim Unit” and “credit unit” — X-02 asks us to verify “consistent wording”, so which label is canonical? (Open since 2026-08-18; blocks the terminology sweep too.)
- (e12) “Run the sections in order” breaks at M-05, which needs a nomination shared by R-01 (section 5). Confirm the intended order is A → R-01/R-02 → M → rest of R → O → X.
- (e9) Invites/approval emails only land in the in-app demo email log (localStorage), not a real inbox — is checking that log the accepted pass criterion for A-09/A-10?
- (d) Env build is FT-Admin-gated at 7 of 11 steps (programme, registrations, reservations, licenses, scanners, SDUs, product categories). Do all testers get admin access, or does one person pre-build? Related: the switcher tab you offered helps persona-hopping (fresh accounts aren’t in the demo modal, and admin Impersonate already exists) — but it won’t remove the admin-gated creation. Still want it?
- (e11) The companion tracking CSV (
fibretrace-qc-test-script.csv) wasn’t attached — please share it so results can be recorded per release. - (e10) For X-05, what auth settings should be ON during QC (
adminLoginRequiredetc.)? With the demo login (any password, on-screen 2FA code), “access denied” only holds if those settings are set. - (e4) A-04 expects a negative CU entry to be accepted at
/admin/reservation-requests, but that queue rejectscu <= 0on approve — negative adjustments only exist on the programme’s reservation panel. Which surface should A-04 target, and is the queue meant to accept negatives at all?
Method note: three read-only research agents fanned out (route matrix, business-logic cross-check, test-data chain); every load-bearing claim above was re-verified against origin/main of the Lovable clone by the synthesising session, because the clone’s working tree is 4,264 commits behind the ref — two agent conclusions (no signup flow; no reservation-request flow) were stale-tree artefacts and are corrected here. The Lovable clone was not modified: all reads used git show/git grep against the fetched ref.