Fibretrace Monet docs/Walkthroughs and notes/2026-07-09 Claims and product flow

Claims + end-to-end product flow (from Lovable source)

Source: Lovable clone origin/maindocs/application-overview.md, docs/database-overview.md, supabase migrations, and the claims/PO hooks+pages. Researched 2026-07-09 (T74 day 1). This RECONSTRUCTS the untranscribed 2:40–40:00 walkthrough segment (claim process: PO → ready-to-claim → claims; evidence pack; sharing). Confirm details with Malcolm in daily Q&A.

What a claim IS

Framing from docs/application-overview.md:3: “FibreTrace is a credit-based traceability platform. Physical fibre is marked with a pigment, scanned at facilities along the supply chain, and those scans are metered against a Fibre Programme so participating brands can make verifiable claims about the volume they handled.”

A production claim = the moment platform credit converts into a brand-visible, verifiable statement: “our company handled X metric tonnes of traced fibre from programme Y”. Backed by production_claims (programme_id, company_id, quantity_mt, status, purchase_order_id, proposed/confirmed/rejected_* fields).

  • Lifecycle: status IN ('proposed','confirmed','rejected') (migration 20260618105531). PO-linked claims: manufacturer proposes → brand confirms or rejects (rejected can be revised + re-proposed). Legacy non-PO claims default straight to confirmed.
  • The claim gatevalidate_production_claim_limits (migration 20260421164336:78-134): quantity must be > 0, AND ≤ company’s remaining reservation (reserved − already claimed), AND ≤ programme’s unclaimed produced volume (SUM(production_records.weight_kg)/1000 where excluded=false − total claimed). “Before it passes the gate, no claim is created.”
  • Sessions backing a claim: production_claim_sessions (claim_id ↔ session_id, unique per company+session — a session is claimable once per company; matches the dashboard copy “Claimed once — retired from the pool, never re-claimable”).

Key supporting concepts

  • Reservation / claim position (programme_reservations): a brand’s PRE-allocated right to claim volume from a programme; capacity-gated by validate_programme_reservation_capacity (SUM(reservations) ≤ SUM(programme_producers.ceiling_mt)). UI: Claim Position pages.
  • Chain of custody: every shipment = TWO paired sessions rows joined by shipping_doc_id (case-insensitive) — outbound at sender (partner_company_id=recipient), inbound at recipient (partner_company_id=sender). src/lib/chainWalk.ts walkSupplyChain BFS (depth cap 12) walks these joins; session_shares (Tier 1 → brand) is the anchor.
  • PO ↔ sessions link: purchase_order_verifications (session_id UNIQUE). Manual via LinkToPo page, or automatic via trigger auto_associate_session_to_po (session’s purchase_order text matches exactly one active non-fully-claimed PO of the facility’s company). validate_po_verification enforces the session’s facility belongs to the PO’s partner company and bumps PO → ready.
  • Evidence pack (DB): purchase_order_evidence_packs (purchase_order_id, claim_id, volume_mt, is_partial) — created ONLY on claim confirm; trigger apply_po_evidence_pack enforces pack volume ≤ PO remaining volume and flips PO to partially_claimed/fully_claimed.
  • Evidence pack (UI catalogue): src/lib/evidencePacks.ts — 8 pack keys: risk, sustainability, procurement, retailer, assurance, regulator, dpp, supplier (each with sections, audience, CTAs: export pdf/csv/json, share, copy public link, request evidence). DB seeds only 3 token pack_types on confirm: trade, sustainability, procurement; LEGACY_PACK_REDIRECTS = { trade: "regulator" }. Rendered at /claims/:claimId/evidence/:packType.
  • Public verification: public_verification_tokens (token, session_id | claim_id + pack_type, enabled; unique per claim+pack). Routes: /v/:token = one session public page (session code, fibre, facility, company, date, scan count, claim status); /c/:token = one claim+pack public page (programme, quantity_mt, pack label, issuer); /verifications/webhook/:sessionId = raw JSON view (page, NOT a real outbound webhook — no HTTP push exists; SharePackModal “Send” is a toast-only mock). QR code generated client-side from the page URL via api.qrserver.com. Tokens can be enabled/disabled per session or per claim+pack (useConnections.ts:160-220).

End-to-end flow (actor → action → tables)

  1. Programme owner creates fibre_programmes + programme_producers (per-gin ceiling_mt, scans_per_mt, window) + enrolls brands as programme_participants.
  2. Tier 4 producer (gin) sprays pigment + scans → sessions at their facility; trigger auto_associate_session_to_programme writes programme_facility_scans, recomputes programme_producers.produced_mt (capped at ceiling). Manual path: production_records.
  3. Each hop Tier 3 → Tier 1: sender outbound session + recipient inbound session paired by shipping_doc_id (chain of custody).
  4. Tier 1 shares the final verification with the brand → session_shares.
  5. Brand (Tier 0) reserves programme volume → programme_reservations (capacity-gated).
  6. Brand issues purchase_orders to the manufacturer (draftshared).
  7. Manufacturer links sessions to the PO → purchase_order_verifications (manual LinkToPo or auto-associate); PO → ready.
  8. Manufacturer proposes a claim → production_claims(status='proposed'), PO → proposed, brand notified (useProposeEvidenceClaim).
  9. Brand reviews on ReadyToClaim (ReviewClaimProposalModal):
    • Confirm → claim confirmed; insert purchase_order_evidence_packs (trigger advances PO volume_claimed/status); copy PO’s sessions → production_claim_sessions; seed 3 public_verification_tokens (trade/sustainability/procurement); notify manufacturer (useConfirmEvidenceClaim).
    • Reject → claim rejected, unlink verifications, PO reverts to shared, manufacturer revises.
  10. (Non-PO path) Brand claims directly against its reservation — gated by validate_production_claim_limits.
  11. Users view packs at /claims/:claimId/evidence/:packType; anyone with link/QR verifies at /v/:token (session) or /c/:token (claim+pack), gated by token enabled.

Page → step map

  • ClaimPosition / ClaimPositionList = step 5 (reservations vs ceiling/claimed).
  • PurchaseOrders = step 6; LinkToPo (manufacturer: “Awaiting evidence pack” / “Awaiting brand confirmation” / “Confirmed”) = steps 7–8.
  • ReadyToClaim (brand: POs with status in ready/linked/proposed/partially_claimed AND volume_claimed < volume) = step 9; Claims = confirmed claim list.
  • EvidencePackDetail + SharePackModal + Connections (token toggles) = steps 11+.

UPDATE 2026-07-09 (evening): the full walkthrough capture arrived and CONFIRMS this source-derived flow end-to-end (PO → notification → partner links verifications + optional certificate → retailer confirms → claims list + packs + downloadable claim certificate with QR for customs). New concepts from Malcolm’s narration: implied chain of custody (unmatched shipping doc = unconfirmed process, still catered for), mass balance (proving fibre MOVEMENT, not bale-to-t-shirt), claim quantity = fibre component only (blended goods, per-component tax rules), partnership prerequisite for inbound scans, supply-chain page privacy (partner = name+location, non-partner = city+country only). See 2026-07-08-malcolm-walkthrough-demo.md § Middle segment.

Open questions

  • [x] Malcolm said “webhook version” in the demo — source shows only the /verifications/webhook/:sessionId JSON PAGE, no outbound webhook. Is a real webhook planned for BE, or is the page the deliverable? — RESOLVED (2026-07-16, source): NO real outbound webhook exists; the “Webhook” toggle only inserts a verification_connections row pointing at a non-existent /api/v1 path, and WebhookPreview.tsx is a pull-style JSON page. Public URL/QR (/c/:token) IS real. See 2026-07-16-code-resolved-open-questions.md §2.5. (Whether BE builds a real dispatcher later is still a BE decision.)
  • [x] 3 DB pack_types (trade/sustainability/procurement) vs 8 UI catalogue packs — which set does BE need to support? (trade→regulator redirect suggests catalogue is mid-migration.) Demo confirms procurement/sustainability/risk are “same data, different views”. — RESOLVED (2026-07-16, source): pack_type is plain text (no DB enum); 3 seeded values vs 8 UI catalogue packs; LEGACY_PACK_REDIRECTS = {trade: "regulator"}, 2 map 1:1, 5 unbacked. See §2.4. (Which set BE supports is still a BE call.)
  • [x] Certificates attach point — ANSWERED by full capture: manufacturer can attach certificates when linking verifications to a PO (so the “transaction” ≈ the PO-verification link), and programme-owner certificates also land on evidence packs. Data model for MULTIPLE certificates per transaction still being reworked by Malcolm (tracked in the 07-08 file).
  • [x] Non-PO direct claims (step 10): still a supported path in the final product, or legacy? (Demo showed ONLY the PO path; Malcolm: “to create an evidence pack… I have to have a purchase order” — suggests PO path is THE path now.) — RESOLVED (2026-07-16, source): production_claims.purchase_order_id is nullable and legacy pages still exist DB-side, but the UI removed the legacy Claim-Position / Request-Evidence menu items for retailers — PO is the ENFORCED path. See §2.3.