Project Plan: Item Manufacturer Field
Overview
Section titled “Overview”Implements the Item Manufacturer field (PDEV-664) as a BusinessAffiliate MANUFACTURER role,
reusing the supplier machinery by generalizing SupplierReference → BusinessRoleReference
(PDEV-875). See the Design for architecture and the Decision Log
for the resolved DQs. Single-agent, sequential execution across the existing worktrees; no
integration branch.
Task Execution Protocol (applies to every task)
Section titled “Task Execution Protocol (applies to every task)”Each task follows the TDD loop and lands as one self-contained, reviewable commit:
- Red — write the unit tests for the task’s behavior. They must compile (production
types/signatures defined; Kotlin bodies stubbed with
TODO()) and fail because the behavior is absent. - Green — implement until the unit tests pass.
- Integration/E2E — add ContainerizedPostgres integration tests (backend) or mock-mode Playwright (frontend) as the task warrants.
- Commit — a single coherent slice that compiles and passes its own tests. A slice may span several files when they are irreducible (e.g. entity field + migration + component together).
Exception (T-01): the PDEV-875 rename is a refactor — the existing suite stays green
throughout; tests-first applies only to the new role behavior it introduces.
Standards (mandatory, read before coding in each repo): kotlin-coding, unit-tests-backend,
unit-tests-infra (backend); typescript-coding, react-best-practices, ui-component,
unit-tests-frontend (frontend); general-conventions. Honor the monadic error channel
(Result/AppError, smart constructors, .unitify()), single-exit style, config-driven test
assertions.
Verification cadence: edit → compile → targeted test roots → full local gate before push. Never run the full suite per change.
Repositories
Section titled “Repositories”| Repository | Role | Branch | CHANGELOG model |
|---|---|---|---|
operations | Upstream (backend + printing) | jmpicnic/pdev-664-manufacturer-field | Direct-edit CHANGELOG.md |
arda-frontend-app | Downstream (frontend) | jmpicnic/pdev-664-manufacturer-field | PR-body ## CHANGELOG (migrating) |
documentation | Project docs | jmpicnic/pdev-664-manufacturer-field | PR-body ## CHANGELOG |
Phase labels map to the agreed BE / FE / printing / docs split. Printing’s backend payload work is folded into the operations phase (T-07) because it is operations code that must ship in the operations PR; the frontend consumes no printing change.
Phases
Section titled “Phases”Phase 1: operations — manufacturer linkage + printing
Section titled “Phase 1: operations — manufacturer linkage + printing”Entry criteria: operations worktree on origin/main; make build green.
Note (DQ-003):
roleis a per-slot parameter, not a persisted/serialized field. Thesupplier_refslot is alwaysVENDOR, themanufacturer_refslot alwaysMANUFACTURER; the component factory injects the constant on materialization. Norolecolumn, no migration, no backfill, wire JSON shape unchanged. Reads stay single-query (the reference’s denormalized columns are on the owner row; the role is a compile-time constant, never looked up).
| # | Task | Repository | Depends On | Acceptance Criteria |
|---|---|---|---|---|
| T-01 | Execute PDEV-875: rename SupplierReference → BusinessRoleReference, SupplierReferenceComponent → BusinessRoleReferenceComponent, factory + ~46 imports; thread role as a per-slot parameter to the factory/resolver/lookup; ItemSupply.supplier slot bound to VENDOR | operations | — | Existing suite green (refactor); no migration/backfill; no role column; wire JSON shape unchanged; role is a non-serialized field on the VO populated per slot (VENDOR for supplier_ref, MANUFACTURER for manufacturer_ref) with a code comment stating it is not persisted and how it is set (DQ-010a); new tests assert the slot role is carried in-memory; VENDOR path unaffected |
| T-02 | Add MANUFACTURER to BusinessRoleType | operations | T-01 | Enum value present; generic lookup accepts role=MANUFACTURER; no migration needed (free varchar) |
| T-03 | Add Item.manufacturer: BusinessRoleReference.Value? + migration (manufacturer_ref_* full column set) + businessRoleReferenceComponent("manufacturer_ref") | operations | T-02 | Component round-trip test green; item persists/reads manufacturer incl. empty; migration applies cleanly, no backfill |
| T-04 | Role-parameterized resolver + STRICT/PROPAGATE mode (default PROPAGATE; keep retired guard) | operations | T-03 | STRICT rejects non-empty unlinked ref; PROPAGATE finds/reuses/creates + honors retired guard; blank name → unlinked; retirement per-role vs per-affiliate (DQ-004): attaches MANUFACTURER to a live affiliate with a retired VENDOR role, refuses a retired affiliate |
| T-05 | Listener for MANUFACTURER role + findItemsByManufacturerRole cross query + item projection refresh | operations | T-04 | Role rename updates linked items’ manufacturer.name; retire marks reference retired; deleted/retired items skipped; no cascade loop |
| T-06 | Item create/update API: resolution-mode (STRICT/PROPAGATE) threaded to the resolver inside the write transaction; optional, defaults to PROPAGATE (non-breaking — DQ-002) | operations | T-04 | Absent mode → PROPAGATE (today’s behavior, no 400); explicit STRICT/PROPAGATE honored; resolution + item write are one transaction (rollback on failure). Hard-required deferred to PDEV-1107 |
| T-07 | Thread manufacturer name into print payloads — ItemPrinter / ItemPrintingService (label, breadcrumb) and KanbanCardPrinter (card/Kanban) | operations | T-06 | Card/Kanban payload carries item_manufacturer; label/breadcrumb carry manufacturer; empty when unset; existing print output unchanged otherwise |
Exit criteria: make build + full test gate green; CHANGELOG.md updated; no composite
includeBuild staged.
Phase 2: arda-frontend-app — item form, filter, column
Section titled “Phase 2: arda-frontend-app — item form, filter, column”Entry criteria: Phase 1 API contract finalized (T-06/T-07 merged or contract-frozen). FE uses mock mode, so it does not require a deployed backend.
Code placement — strict BFF / shared / SPA separation. Place each new module by category
(convention codified in the front-end-engineer agent profile and the clean-components skill):
- BFF-exclusive (server-only): the
lookup-manufacturersroute handler undersrc/app/api/arda/**— thinroute.ts,import 'server-only', no Redux/client-fetcher imports. - Shared (BFF + SPA): route payload types and the
BusinessRoleReferenceDTOinsrc/types/**; purenormalizeManufacturerinsrc/lib/shared/**. Noserver-only/client-onlydeps — safe in both bundles. - SPA-exclusive (client):
lookupManufacturers(ardaClient),lookupManufacturersAsOptions(lookupAdapters), themanufacturerLinksslice (src/store/**), the typeahead + form wiring.
| # | Task | Repository | Depends On | Acceptance Criteria |
|---|---|---|---|---|
| T-08 | Types (BusinessRoleReference DTO in src/types/**, manufacturer on Item/ItemFormState) + always-reference mapper (buildManufacturerField, normalizeManufacturer in src/lib/shared/**) + lookupManufacturers client + lookupManufacturersAsOptions adapter + Redux manufacturerLinks + server-only BFF pass-through route | arda-frontend-app | T-07 | Modules placed per the BFF/shared/SPA convention above; mapper round-trip (incl. empty) green; adapter maps generic lookup → {name,link}; always sends reference object (no legacy path); client always sends an explicit resolution-mode on create/update |
| T-09 | Ordering-Details “Item Manufacturer” typeahead in ItemFormPanel (under Order method) | arda-frontend-app | T-08 | Suggests existing, adds new, clears; persists on submit; matches Type typeahead behavior |
| T-10 | Catalog Manufacturer filter + optional sortable column (itemsSlice filter/column-visibility) | arda-frontend-app | T-09 | Filter narrows results; column renders + sorts; visibility persists; empty manufacturer handled |
Exit criteria: npm run build + lint + typecheck + unit tests + mock-mode E2E green; PR-body
CHANGELOG prepared.
Phase 3: Release
Section titled “Phase 3: Release”Entry criteria: Phases 1–2 exit criteria met.
Deployment dependency (verified). operations auto-deploys to all four partitions
(dev→stage→demo→prod) on merge to main; arda-frontend-app deploys via Amplify after its own
merge, with demo/prod gated behind an Extended-E2E quality gate — so the backend reaches an
environment before the frontend does. The manufacturer field is graceful either way: the backend
deserializes with ignoreUnknownKeys = true, so a frontend-ahead-of-backend window silently drops
the field (empty read-back) rather than erroring. The resolution-mode flag is likewise safe
because it is optional/defaults to PROPAGATE (DQ-002) — a backend without it, or a caller omitting
it, behaves as today. The guard below makes the ordering explicit regardless.
| # | Task | Repository | Depends On | Acceptance Criteria |
|---|---|---|---|---|
| T-11 | Open the operations PR (closes PDEV-875; refs PDEV-664); run pr-steward | operations | T-05, T-07 | CI green; CHANGELOG present; threads resolved |
| T-12 | Open the arda-frontend-app PR (refs PDEV-664) only after operations is deployed AND verified in the target partition(s); run pr-steward | arda-frontend-app | T-10, T-11 | Operations deployed + verified per partition — GET /operations/oam/version and an item round-trip confirming manufacturer persists (version endpoint alone is inconclusive: it can report the common-module version); CI green (incl. VRT); PR-body CHANGELOG; Amplify preview checked |
| T-13 | After backend merge, create the bulk-import follow-up ticket with concrete specifics (reference shape, mode contract, manufacturer_ref_* columns, PROPAGATE semantics); create the print-template testData.json follow-up ticket | — | T-11 | Tickets created and linked to PDEV-664 |
Merge ordering: operations (T-11) deployed and verified per partition before
arda-frontend-app (T-12) is merged — the frontend relies on the deployed manufacturer behavior in
non-mock environments. The failure mode is graceful (silent field drop, not rejection), so this
guard prevents a correctness/UX defect, not an outage. documentation (Phase 4) is independent.
Phase 4: documentation
Section titled “Phase 4: documentation”Entry criteria: feature merged.
| # | Task | Repository | Depends On | Acceptance Criteria |
|---|---|---|---|---|
| T-14 | Update project docs to completed status, promote per the project lifecycle; open the documentation PR (PR-body CHANGELOG); make pr-checks green | documentation | T-11, T-12 | Docs reflect shipped design; links valid; smoke tests pass |
Dependency Graph
Section titled “Dependency Graph”Personas
Section titled “Personas”Single-agent, sequential — no persona split. The implementing agent reads the mandatory coding skills per repo before starting each phase.
Worktree Strategy
Section titled “Worktree Strategy”Existing worktrees under projects/manufacturer-field-worktrees/ (all on branch
jmpicnic/pdev-664-manufacturer-field): operations, arda-frontend-app, documentation,
workbooks. No integration branch. No composite includeBuild override (no common-module change).
Push guards: stage narrowly (never git add -A); before each commit,
git diff --cached | grep -iE 'includeBuild|/Users/|-worktrees' to catch a swept-in local composite
or absolute path; run the repo’s full local gate before every push.
Cleanup (on completion): verify no uncommitted changes and all branches pushed, then remove the worktrees and local branches.
Follow-ups (out of this project)
Section titled “Follow-ups (out of this project)”- PDEV-1107 — make
resolution-moderequired — flip the flag from optional/PROPAGATE-default to required-with-no-default, after every item-write caller (FE, api-test, bulk-import) sends it explicitly and a soak period confirms none rely on the default (DQ-002). Already filed; do not start until its preconditions hold. - Print-template
testData.json+ placeholders (printing-templates repo) — ticket created in T-13. - Bulk-import round-trip of
item.manufacturer— ticket created in T-13, after backend merge, so it carries concrete specifics. - Manufacturer management screens — separate future project (the denormalized reference fields are in place to support it).
Copyright: (c) Arda Systems 2025-2026, All rights reserved
Copyright: © Arda Systems 2025-2026, All rights reserved