Code-resolved open questions (source-only pass)
Source: read-only fan-out (3 Explore agents, 2026-07-16) over the Lovable clone
fibre-trace-studio-8b5c03d8(.local/lovable-source/, checked-out tree =origin/mainfor app code). NO browser was used — this pass closes every open question that can be answered from source and explicitly marks the rest as needing Malcolm / team / design (or the T75 Claude-in-Chrome walkthrough). Complementspersona-case-matrix.md; cross-checkfile:linethere.
This satisfies T74 done-when part 2 — the previously-missing walkthrough segment (scanner/shipping supply-chain flow, PO → ready-to-claim → claims mechanics, evidence-pack composition + sharing) is now documented FROM SOURCE below. Part 1 (every open-question checklist resolved) is partially met: all code-resolvable items are closed here; the remainder are genuinely design-unsettled and listed under “NOT code-resolvable”.
1. Supply-chain / scanner / shipping flow (from source)
1.1 How a scan records shipping direction
sessions carries direction on TWO parallel column sets, both written on every scan:
- v2 (canonical for chain-walk):
direction text(supabase/migrations/20260519093025_...sql:1-4),partner_company_id uuid REFERENCES companies,carrier,shipping_doc_id text(...20260617214854_...sql:1-6). - legacy (canonical for list/detail display):
material_directionenum('inbound','outbound'),shipper,shipping_id,counterparty_company_id(...20260618094316_...sql:2-8). - Scan-write mirrors one into the other on insert:
DemoScanModalV2.tsx:628-639. There is NO dedicated “to-company” field beyond the counterparty (partner_company_id/counterparty_company_id) on either end.
1.2 Tier structure is derived purely from shipping pairs
Component RetailerSupplyChain.tsx, hook useRetailerSupplyChain.ts, walk lib/chainWalk.ts. Rule (chainWalk.ts:5-16): an inbound session at company A (partner_company_id=P, shipping_doc_id=D) joins an outbound session scanned at a facility of P with the SAME shipping_doc_id (case-insensitive) and partner_company_id=A. The walk starts from sessions shared with the retailer and walks backward hop-by-hop (chainWalk.ts:127-135, .eq("direction","outbound").eq("partner_company_id", companyId)). Tier labels come from company_details.supply_chain_tier (or inferred from company name) — useRetailerSupplyChain.ts:33-46,262-277. → Confirms Malcolm’s demo claim: tiers are reconstructed from shipping data on scans, no orders/audits.
1.3 Partner vs non-partner display rule — CONFIRMED (with nuance)
useRetailerSupplyChain.ts:186-196: display_name = isPartner ? company.name : "Confidential supplier"; real_name only set for partners. BUT city/country are populated identically regardless of partner status. So the matrix’s “partner=name+location / non-partner=city+country” is right in spirit: only the IDENTITY is masked (“Confidential supplier” + lock icon RetailerSupplyChain.tsx:45-54); city/country always show.
1.4 Non-partner suppliers WITH scans DO appear (not filtered)
No is_partner guard on inclusion — every company reached by the shipment join is pushed to views (useRetailerSupplyChain.ts:182-205), masked, with a “FibreTrace-connected” badge + “Invite partner” CTA (RetailerSupplyChain.tsx:68-78,301). Discrepancy to flag: Malcolm said in the demo that non-partner suppliers with scans currently DON’T appear and “I do need to check the logic on this one” — current source shows they DO appear (masked). Either the demo ran on older code or he was describing intended-not-yet-built behavior. The FINAL rule is still Malcolm’s to confirm.
1.5 “Program Owner” is derived, no hard role column
Two combined mechanisms: (a) membership row in program_owners join table (...20260606025632_...sql:7-13), checked in Home.tsx:70-77; (b) generic company_modules toggle module_name='program_owner' (default OFF), read in useCompanyModules.ts:39, gating sidebar in AppSidebar.tsx:115,136-137. No is_program_owner boolean on companies. → Confirms Part A/Part C item 1.
2. PO → claims → evidence pack (from source)
2.1 PO + “ready to claim” mechanics
Tables purchase_orders (po_status enum: draft,shared,linked,proposed,ready,partially_claimed,fully_claimed) + purchase_order_verifications (session↔PO link, session_id UNIQUE — a session belongs to at most one PO). Creation sets status='shared' (usePurchaseOrders.ts:127). “Ready to claim” is a DB trigger, NOT a UI flag: validate_po_verification() flips status to ready when a session is linked (...c3a12d9b...sql:127-131), also blocks linking to a fully-claimed PO and enforces the session’s facility company = partner_company_id.
2.2 Partial-claim fully supported
volume_mt / volume_claimed_mt tracking; trigger apply_po_evidence_pack() raises if claimed + new > total, else sets partially_claimed/fully_claimed (...c3a12d9b...sql:157-167). Client mirror in useConfirmEvidenceClaim (usePurchaseOrders.ts:466-467).
2.3 Non-PO direct-claim path — DB allows, UI steers PO-only
production_claims.purchase_order_id is nullable (...20260618105600_...sql:2), and legacy pages (ClaimPosition.tsx, MakeClaimModal.tsx, useEvidenceRequests.ts) still create claims without a PO (governed by validate_production_claim_limits() against reservations). But .lovable/memory/features/purchase-orders.md:18-19 states retailers no longer see legacy Claim-Position / Request-Evidence menu items — routed through PO menus. → PO is the ENFORCED path for retailers; non-PO remains possible DB-side. Whether legacy pages are reachable for every persona = not fully traced (needs per-persona nav gating).
2.4 pack_types — 3 DB values vs 8 UI catalogue
No CREATE TYPE enum — pack_type is plain text (...20260611175027_...sql:5). Only 3 values ever seeded: trade, sustainability, procurement (usePurchaseOrders.ts:499, mirrored Connections.tsx:38-42, PublicClaim.tsx:8-12). UI catalogue = 8 packs: risk, sustainability, procurement, retailer, assurance, regulator, dpp, supplier (lib/evidencePacks.ts:13-21,98-299). Mapping: LEGACY_PACK_REDIRECTS = { trade: "regulator" } (evidencePacks.ts:310-312); sustainability/procurement map 1:1; the other 5 catalogue packs have NO DB pack_type / public-token counterpart. → The “trade→regulator redirect” the earlier note suspected as mid-migration is confirmed real.
2.5 Sharing — public URL/QR real, webhook NOT
No real outbound webhook. /verifications/webhook/:sessionId (App.tsx:129) → WebhookPreview.tsx just queries Supabase client-side and renders JSON.stringify in a <pre> (pull-style page, not a dispatcher). The Connections “Webhook” toggle only inserts a verification_connections row (useConnections.ts:43-56); the generated URL points at /api/v1/... which does NOT exist in this repo (grep "api/v1" = only this string, no server, no fetch/axios). Public URL + QR IS real: /c/:token (App.tsx:132) → PublicClaim.tsx looks up public_verification_tokens, QR via api.qrserver.com (PublicClaim.tsx:84).
3. Part C bug triage (verified against current source)
| # | Finding | Verdict | Owner | Note |
|---|---|---|---|---|
| 3 | Owner route /owner/programme-uptake (link) vs /owner/program-uptake (route) |
PRESENT — OwnerDashboard.tsx:254 vs App.tsx:170; sidebar spelling is correct so only the card “View details” 404s |
Report to Lovable (fix upstream so it flows down); routing is dev-adjacent so monet CAN one-line-patch, but prefer upstream to avoid sync drift | Real bug |
| 4 | /admin open by default, client-only gate, plaintext creds |
PRESENT — adminLoginRequired default false (AuthSettingsContext.tsx:86), gate is a client redirect AdminLayout.tsx:59, default creds support@fibretrace.io in code + localStorage (AuthSettingsContext.tsx:36-39,65) |
Lovable-owned — the whole prototype has no real backend auth; monet uses real Supabase/JWT downstream, not the adapter | Prototype limitation, not a monet patch |
| 5 | Dead branches: DemoScanModalV2 “warning” banner + legacy Settings.tsx |
PRESENT — banner prop "warning" never passed (all 10 call sites pass "connected", :718-721); pages/Settings.tsx:9 never imported in App.tsx |
Lovable-owned cleanup | Cosmetic |
| 6 | 7 SAT error bottom-sheets absent in dashboard | CONFIRMED absent — only loose equivalents, all in DemoScanModalV2 simulator: GPS-mismatch banner (:832-838), no-facility toast (:493), subscription = data filter only (Home.tsx:178). “Wrong company” / “Lost connection” = none; “Need scanner”/“Scanner disconnected” = the dead banner (3a) |
Lovable/SAT-owned — these are SAT mobile-app UX states, dashboard only has a scan simulator | SAT-vs-Dashboard difference |
Open-question status (maps the day-file checklists)
RESOLVED from source (tick in place)
- Webhook “version” (
2026-07-09-claims-and-product-flow.md) → source has only the JSON-preview page + a connection-row insert pointing at a non-existent/api/v1; NO real outbound webhook exists. (Whether BE builds one later is a BE decision, but the page IS all that exists today.) - 3 DB pack_types vs 8 UI catalogue (same file) → fully mapped in §2.4: 3 text values, 8 catalogue packs,
trade→regulatorredirect, 5 unbacked. (Which set BE must support is still a BE call.) - Non-PO direct claims still supported? (same file) → §2.3: DB allows, UI enforces PO-only for retailers. PO is THE path now.
- partner=name+location / non-partner=city+country rule (
2026-07-08-...demo.md) → §1.3: confirmed; identity masked to “Confidential supplier”, city/country always shown. - Programme details CRUD bug fixed in Lovable yet? (same file) → §3-adjacent: PARTIALLY — owner now has logo + program-items CRUD (
OwnerProgramRecord.tsx:127-141,77-118); core fields (name/dates/fibre/country/status) read-only BY DESIGN, admin edits them (AdminProgrammes.tsx:725-836). No longer “missing” — a deliberate permission split.
PARTIAL — current source state known, FINAL rule still Malcolm’s
- Supply-chain visibility of non-partner suppliers with scans → source: they DO appear masked (§1.4); demo said they don’t. Malcolm must confirm the intended rule.
- Company-level vs facility-level tier → source is company-level today (
company_details.supply_chain_tier, §1.2); Malcolm admits the model may be wrong and BE should anticipate a possible facility-level tier.
NOT code-resolvable — needs Malcolm / team / design (or T75 Chrome walkthrough)
- Certificates: exact data model for MULTIPLE certificates per transaction + “connected sub-tiers” inheritance — Malcolm was still reworking at demo time.
- “Show partner on scan-selection screen” improvement (Jamie) — future UI, not built yet.
- Recursive mark-claimed for shared sessions + Shannon’s “which traceability solution used this evidence” marker — design unsettled.
- Legacy verification-sharing vs data-connections: does sharing fold into claim-linking / data connections — Malcolm’s final decision.
- Partial-claim listing improvements + retailer visibility of remaining PO volume — expected UI changes (mechanics themselves confirmed working, §2.2).
- Bale ID tracking / serialising scan data — Malcolm has an improvement plan; new data model likely.
- Which admin/CMS override functions are in scope for monet — scope decision (admin currently OUT of dashboard scope per
feedback_dashboard_mvp). - What Vu’s “updated APIs for Nghia to integrate” covers — get the list from the team.
Mock/stub surfaces (confirmed, all still mock)
Scanners Buy/Modify → mailto: (Scanners.tsx:104,159); OS-Hub sync → fake 900ms + comment “Simulated OS API call” (AdminOpenSupplyHub.tsx:74-75); password update → mock toast “(mock)” (SettingsAccount.tsx:79-83); webhook → JSON page (WebhookPreview.tsx). Fix via a Lovable prompt (replace mock) rather than patching only monet, to avoid sync drift.