Fibretrace Monet docs/Knowledge base/Persona: Tier 1 Manufacturer

Persona 04 — Tier 1 Manufacturer

Confidence markers and citation shorthand per README.md.

Who they are

The final manufacturer (cut-make-trim; demo: ACME Apparel, Vietnam) — the company that receives a brand’s nomination, physically handles the traced fibre, scans it at its facilities, and declares how much traced fibre went into the order. Malcolm’s key allocation rule lives here: the manufacturer, not the brand, sets the claimed tonnage, because only they know actual fibre used; the PO itself is a money/product figure. STATED (BL:wt2:38; flow-explained rule 5).

Tier 2 in the demo (ACME TIER2) renders the identical experience — Tier 1 and Tier 2 share one manufacturer persona, differing only by data. VERIFIED observation (BL:gui-walkthrough README case 9).

A good day: scans from the floor appear in Scan History, the partner brand’s nomination arrives, they link sessions, allocate per line, propose the claim, and the brand confirms without variance questions.

What makes them this persona

isManufacturer = hasIncomingPoShares — the company has at least one non-archived purchase order addressed to it (purchase_orders.partner_company_id = companyId AND archived = false). VERIFIED (L:src/hooks/usePersonaContext.ts:32,40-41). This is worth pausing on: manufacturer-ness is not a company type, a tier, or a registration — it is having been sent a nomination. A factory with no incoming nominations falls to the generic “scanner” persona (see persona-06-additional-roles.md). VERIFIED (usePersona fallback).

Every screen they touch

Route What they see / do
/ (HomeManufacturer) Manufacturer dashboard: linking/scan-coverage KPI tiles (facilities, scanner coverage, verification sessions), next-actions inbox; empty state all-clear card. VERIFIED (L:src/pages/HomeManufacturer.tsx:396-406; port-plan.md unit 12)
/facilities, /facilities/add Facility list and licence-gated creation (“No licenses available. Buy more licenses first.” at zero). VERIFIED (L:src/components/CreateFacility.tsx:49-53)
/scanners Scanner list per facility. The “request a scanner” CTA is a mailto to support — no in-app request object exists, so admin assignment cannot be triggered by the request itself (QC M-02 divergence). VERIFIED (L:src/pages/Scanners.tsx:154-165)
/verifications (Scan History) Sessions at their facilities: date, facility, scanner, volume, direction (inbound/outbound), shipping fields; session IDs obfuscated with Inert connection chips until claim-linked. VERIFIED (L:src/hooks/useVerifications.ts; ObfuscatedId/ConnectionStatusChip)
/link-to-po (Linking Production) Their proposal workbench, grouped: awaiting evidence pack / awaiting brand confirmation / confirmed packs; the 3-step LinkVerificationsToPoModal (select sessions, per-line allocation + claim amount, review and notify). VERIFIED (L:src/components/LinkVerificationsToPoModal.tsx; BL:gui-walkthrough README)
/partners Invite the brand (or be invited) by 3-letter company code, case-insensitive; partnership is the precondition for nominations and for declaring shipments to/from a company. VERIFIED (L:src/pages/settings/SettingsPartners.tsx:160-177; BL:demo:72)
/collections, /team, /notifications, /settings/* (incl. Certificates) Shared surfaces; company certificates (Settings > Certificates) attachable during proposal. VERIFIED routes; certificates module is BE-blocked in monet

The linking rules that bind them (the heart of this persona)

  • Selectable sessions: only sessions at their own facilities OR shared to them via session_shares, minus any session already claim-bound (production_claim_sessions) or already PO-linked (purchase_order_verifications). VERIFIED (L:src/components/LinkVerificationsToPoModal.tsx:134-193).
  • A session can only ever back one PO link — purchase_order_verifications.session_id is UNIQUE at the DB. VERIFIED (SQL 20260608132010:53). Same-company double-claiming is blocked by UNIQUE(company_id, session_id) on production_claim_sessions. VERIFIED (SQL 20260511103814:9-10).
  • The DB-side link trigger additionally requires the session’s facility to belong to the PO partner, or the session to be shared with them — the cross-company facility guard. VERIFIED (validate_po_verification, SQL 20260608132010). Note the UI mostly prevents this by filtering candidates, so QC M-06’s expected explicit error message may in practice never render; the guard manifests as absence from the picker. INFERRED from both mechanisms existing.
  • Deliberate looseness Malcolm insists on: any scan at their own facility is attachable even if it was addressed to another company — the sub-tier import case. “You can send a scan to somebody else and put that on a claim certificate”. A counterparty display on the picker is planned mitigation, not a restriction. STATED (BL:wt2:21-22).
  • Allocation: per PO line (quantity, blend %), allocated CU computed live; over-allocating a line beyond its remaining quantity blocks Next. Claim MT is capped at min(buyer’s available capacity, programme remaining) with named errors (exceedsProgramme, exceedsBuyerCapacity, programmeExhausted). VERIFIED (LinkVerificationsToPoModal:236-280,373-378).
  • Proposing writes, atomically-ish: purchase_order_verifications, a production_claims row with status proposed scoped to the RETAILER’s company (the reservation lives on the brand), production_claim_lines snapshot, production_claim_sessions bindings, PO status -> proposed. VERIFIED (L:src/hooks/usePurchaseOrders.ts:373-560,437-441).
  • A claim cannot reach proposed with zero linked verifications — DB trigger. VERIFIED (validate_claim_has_verifications, origin/main SQL 20260814070749).
  • After confirmation their linked sessions are locked/retired (is_locked = PO-linked AND pack exists; IDs unmask, chip flips to “Linked & Verified by {brand}”); rejection deletes the PO’s links and frees the sessions to re-pick. VERIFIED (L:src/hooks/useVerifications.ts:209-234; useRejectEvidenceClaim:773-834).
  • Supplier close: they can flag “no more shipments” (supplier_closed_at/note) — notifies the brand, does not close the PO. VERIFIED (usePurchaseOrders.ts:1063-1090).

CRUD abilities and gates

Create: facilities (licence-gated), scans (SAT/simulator), session shares, partner invites, claim proposals (with certificate attach), team invites. Update: allocations before propose; own facility data; supplier-close flag. Cannot: confirm or reject claims (brand-only), edit a claim after confirm (locked for both parties), reduce a PO line below its claimed quantity, create nominations (brand-side), see another company’s scans unless shared. VERIFIED (lifecycle hooks; L:src/hooks/usePurchaseOrderLines.ts:105-110; BL:wt2:30).

Dependencies

Upstream: the brand must exist as a partner and share a nomination before /link-to-po has anything actionable (the QC script’s own ordering breaks here — M-05 needs R-01 from a later section); FT Admin supplies facility licences and scanners; producers upstream supply the marked fibre their scans detect. Downstream: the brand’s Ready-to-Claim queue is fed entirely by their proposals; the programme owner’s uptake and unlinked-CU signals reflect their linking pace. VERIFIED ordering issue (.local/qc-test-script/analysis.md e12).

First-login / empty state

A manufacturer company with no incoming nominations is not yet this persona at all (falls to generic Home). Once a nomination arrives: HomeManufacturer with the all-clear card until sessions/nominations demand action; unassigned facilities show an empty state with the request CTA (mailto). VERIFIED (HomeManufacturer.tsx:396-406; Scanners.tsx:154-165).

Likely failure modes and confusions

  • Expecting an in-app scanner request that routes to admin — it is an email. VERIFIED.
  • Being unable to find a scan in the picker and reading it as a bug — usually it is already linked elsewhere (one-PO-per-session rule) or belongs to a non-shared facility. INFERRED from the three candidate filters.
  • Variance disputes: their allocation differing from PO line quantities is surfaced to the brand as “changed” rows needing review — expect questions rather than errors. VERIFIED (usePoProposalReview.ts:55-80).
  • Confusion between “the PO is fully claimed” and “the PO is closed”: closing is a separate retailer action; their supplier-close only signals intent. VERIFIED (useCloseNomination vs useSupplierCloseNomination).

Implementation state

Lovable: fully working including the Aug HIG rewrites (ReadyToClaim/Nominations counterparts). Monet: /verifications, /link-to-po are routed empty shells (sessions, purchase_orders, purchase_order_verifications, production_claims all no-op stubs; writes throw or toast “backend pending”); /partners exists only at the old /settings/partners path until the nav refactor is ported; facilities and scanners are the two genuinely wired surfaces (real /api/monet/v1 CRUD). VERIFIED (M:src/lib/apiAdapter.ts mappers; .local/qc-test-script/analysis.md route matrix; port-plan.md units 3,10-12). The whole propose/confirm domain has zero real endpoints — see 05-implementation-state.md.