Fibretrace Monet docs/Walkthroughs and notes/2026-07-16 Code-resolved open questions

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/main for 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). Complements persona-case-matrix.md; cross-check file:line there.

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_direction enum ('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) PRESENTOwnerDashboard.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 PRESENTadminLoginRequired 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→regulator redirect, 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.