Persona 05 — Tier 0 Retailer / Brand
Confidence markers and citation shorthand per README.md.
Who they are
The brand or retailer at the top of the chain (demo: WALMART; walkthrough example: Target, US) — the company that pays for programme capacity, orders product from Tier 1 manufacturers, and ultimately makes the public claim (“this order contains X MT of traced fibre from programme Y”) that customs, sustainability teams and consumers see. They are the buyer of everything the platform produces: capacity, evidence, certificates. STATED (BL:flow-explained.md:33; demo:81).
A good day: capacity is healthy (no over-limit warning), manufacturer proposals arrive with no variance, bulk approval clears the queue in one pass, and the resulting verification records carry clean evidence they can hand to customs via the QR certificate.
What makes them this persona
isRetailerTier0 = NOT isProducer AND company_type in (brand, retailer) AND at least one programme_participants row. VERIFIED (L:src/hooks/usePersonaContext.ts:37-39). So three facts must line up: company type (self-declared at signup/settings), programme participation (admin-registered), and not being a producer. Participation alone without the brand/retailer company type does not produce this persona. VERIFIED (same).
Every screen they touch
| Route | What they see / do |
|---|---|
| / (HomeRetailerTier0) | Tier 0 dashboard: CU KPI tiles reconciling with /claim-position and /claims, capacity modal breaking down nominated vs not-yet-nominated CU, next-actions inbox; fresh account shows the dashed “Create a nomination” placeholder. VERIFIED component (L:src/pages/HomeRetailerTier0.tsx:428-439; QC R-12 wording STATED QC:256) |
| /purchase-orders (Nominations) | Create nominations manually (multi-line: HS codes, categories, qty, net weight; blend pulled from category config; CU per line computed live) or import CSV with per-row error reporting; share to the Tier 1 partner; archive; segmented Active/Archived tabs after the Aug HIG rewrite. VERIFIED (L:src/hooks/usePurchaseOrders.ts:118-149; usePurchaseOrderLines.ts:11-24; ImportPoCsvModal) |
| /claim-position(/:id) | Program Capacity: “Total reserved” headline, reservation history (expiry dates, Expired badges, negative rows rendered as “Released”), capacity funnel, PO-raised KPI with “Near limit” at 90 percent and “Over limit” past the reservation, Add-reservation request flow with the CU estimator. VERIFIED (L:src/pages/ClaimPosition.tsx:140-207,387-398,474-478,565-586; AddReservationModal) |
| /ready-to-claim | The proposal review queue: segmented Ready/Requested/Confirmed/All, variance-vs-PO highlighting, over-capacity flag with “Deselect to fit (N of M kept)”, single confirm (ReviewClaimProposalModal with per-line up/down variance icons, closesPo detection, confirm or reject-with-reason) and bulk approval (unchanged bucket auto-selected, to-review bucket not, sequential confirm so one failure does not roll back the batch). VERIFIED (L:src/pages/ReadyToClaim.tsx:500; BulkClaimApprovalModal.tsx:88-183; ReviewClaimProposalModal.tsx:169-420) |
| /claims (Verification Records) | Their confirmed claims: CLM- references, expandable rows with Linked Evidence (PO-linked plus manually-linked verifications merged), “Add scan record” and “Add certificate” actions, evidence-pack links. VERIFIED (L:src/pages/Claims.tsx:88,934-946; ClaimLinkedVerifications.tsx:113-150) |
| /claims/:claimId/evidence/:packType | Evidence pack detail — 8 pack types (verification-record, sustainability, procurement, retailer, assurance, regulator, dpp, supplier), each with its own sections and share settings. VERIFIED (L:src/lib/evidencePacks.ts:15-24; EvidencePackDetail.tsx) |
| /supply-chain, /programs/:id/supply-chain | The reconstructed tier ladder from scan pairs: partners named with location, non-partners masked as “Confidential supplier” with city/country and an Invite CTA; BFS chain walk capped at depth 12. VERIFIED (BL:2026-07-16-code-resolved-open-questions.md:18-24; RetailerSupplyChain at origin/main) |
| /partners, /connections, /team, /notifications, /settings/* | Shared surfaces; Connections is now a claims-only participant-gated view (old webhook/CSV tabs dropped upstream). VERIFIED (memory reference_connections_claims_tabs) |
CRUD abilities and gates
Create: nominations and their lines, CSV imports, reservation requests (CU estimator; submit disabled at CU <= 0), claim confirmations (which create evidence packs and public tokens), manual claim-verification links (claim_verifications), certificates on claims, partner invites. Update: nomination lines before claiming (never below quantity_claimed), pack share toggles (public_verification_tokens.enabled). Close: nominations (requires every non-excluded line fully claimed unless allowShort). Reject: proposals with a reason (frees the manufacturer’s scans). Cannot: set the claimed tonnage (manufacturer-side), grant themselves capacity (admin approves requests), modify a confirmed claim (locked both sides; only admin revoke/void exists). VERIFIED (L:src/hooks/usePurchaseOrders.ts:640-834,1023-1059; AddReservationModal:158; BL:wt2:30).
Business rules binding specifically to them
- Blend % comes from admin product-category configuration; per-line overrides are flagged and audited on /admin/overrides. CU per line = qty x net weight x blend x loss multiplier; 1 CU = 1 kg. VERIFIED (claimUnits.ts; AdminOverrides). The QC script omits the loss multiplier — testers hand-computing will mismatch wherever it is not 1. VERIFIED discrepancy (QC:6 vs claimUnits.ts).
- Their claims are metered by the DB: a claim insert/confirm exceeding the company’s remaining unexpired reservation, or the programme’s unclaimed produced volume, raises an exception. VERIFIED (validate_production_claim_limits, SQL 20260421164336:70-129 refined by origin/main 20260816203909:59).
- Reservations expire (per-row expires_at, default 18 months); expired capacity leaves every usable figure but stays visible as history; negative rows are releases and cannot exceed the company’s active reserved capacity. VERIFIED (origin/main SQL 20260816203909, 20260818202335).
- Nominations are only softly bounded by reservations: raising POs past reserved capacity produces the Over-limit warning, not a block. The QC invariant reserved >= nominated >= claimed is therefore enforced hard only on the claimed leg. VERIFIED (ClaimPosition.tsx:565-586 warning-only; trigger enforces claims) — see
04-rules-and-invariants.mdR-20. - Claims they confirm belong to their company (production_claims.company_id = PO issuer) because the reservation lives on them. VERIFIED (usePurchaseOrders.ts:437-441).
- Verification IDs unmask for scans linked to their confirmed claims, showing their company name on the scan. VERIFIED (sessions_v/audits_v views; ConnectionStatusChip).
- Bulk approval with insufficient credits: the modal computes over-capacity groups and a greedy keep-smallest recommendation; per-PO sequential confirms mean a mid-batch failure leaves earlier confirms standing (no rollback) — QC R-07’s “no partial/duplicate claims” expectation holds for duplicates but “no partial batch” is not how the code behaves. VERIFIED (BulkClaimApprovalModal.tsx:140-183).
Dependencies
Upstream: FT Admin (participant registration, reservations, product categories), the manufacturer partner (proposals; they cannot claim without a proposal in the PO flow), producers (Activated volume bounds available-to-claim). Downstream: the programme owner’s uptake reporting, and the public — customs officers and consumers scanning the QR. A special direct path exists: the older Make-a-claim wizard let a brand claim capacity without a PO (observed live in July, still DB-legal; UI now steers to the PO flow) — whether that path should survive is an open question. STATED (BL:ux-findings-for-malcolm.md finding 6; 2026-07-16-code-resolved-open-questions.md:66).
First-login / empty state
Fresh Tier 0 account: dashed “Create a nomination” placeholder instead of KPI cards (QC R-12), all-clear inbox card, empty claim position until admin registers participation and grants a reservation. VERIFIED component/STATED wording (HomeRetailerTier0.tsx:428-439; QC:256).
Likely failure modes and confusions
- Insights (QC R-13): the module and route exist nowhere; additionally the QC’s named data home (Walmart) is a demo account testers are told not to touch. Doubly untestable. VERIFIED absence (
.local/qc-test-script/analysis.mdd11). - Reading the Over-limit warning as a block — it is advisory at nomination time; the hard stop comes later at claim time. VERIFIED.
- Expecting “Total Amount Reserved” to include expired rows — it excludes them; history still shows them. VERIFIED.
- The variance workflow surprising first-time reviewers: manufacturer-edited quantities appear as “To review” rows that are deliberately NOT auto-selected in bulk approval. VERIFIED (BulkClaimApprovalModal.tsx:88-104).
- Terminology drift mid-rename: Nominations vs Purchase Orders and CU vs credit units are both mid-sweep upstream; screens may disagree with each other until the sweeps land. VERIFIED (
port-plan.mdunits 9,11).
Implementation state
Lovable: fully working; this persona received most of the Aug HIG rewrites (ClaimPosition, ReadyToClaim, Nominations, Claims). Monet: every surface in this persona’s core loop is an empty shell — purchase_orders, purchase_order_lines, production_claims, programme_reservations, fibre_programmes, sessions are all no-op stubs; writes throw MutationNotImplementedError or toast “backend pending”. The real backend covers only the aggregate read side (claim-positions funnel), a write-only reservation enquiry, and a single-step POST /claims that does not implement the propose/confirm model at all. VERIFIED (M:src/lib/apiAdapter.ts; .local/backend-specs/fibre-program/README.md:52-76; port-plan.md units 6,10,11).