Skip to content

Project Plan: Item Manufacturer Field

Implements the Item Manufacturer field (PDEV-664) as a BusinessAffiliate MANUFACTURER role, reusing the supplier machinery by generalizing SupplierReferenceBusinessRoleReference (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:

  1. 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.
  2. Green — implement until the unit tests pass.
  3. Integration/E2E — add ContainerizedPostgres integration tests (backend) or mock-mode Playwright (frontend) as the task warrants.
  4. 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.

RepositoryRoleBranchCHANGELOG model
operationsUpstream (backend + printing)jmpicnic/pdev-664-manufacturer-fieldDirect-edit CHANGELOG.md
arda-frontend-appDownstream (frontend)jmpicnic/pdev-664-manufacturer-fieldPR-body ## CHANGELOG (migrating)
documentationProject docsjmpicnic/pdev-664-manufacturer-fieldPR-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.

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): role is a per-slot parameter, not a persisted/serialized field. The supplier_ref slot is always VENDOR, the manufacturer_ref slot always MANUFACTURER; the component factory injects the constant on materialization. No role column, 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).

#TaskRepositoryDepends OnAcceptance Criteria
T-01Execute PDEV-875: rename SupplierReferenceBusinessRoleReference, SupplierReferenceComponentBusinessRoleReferenceComponent, factory + ~46 imports; thread role as a per-slot parameter to the factory/resolver/lookup; ItemSupply.supplier slot bound to VENDORoperationsExisting 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-02Add MANUFACTURER to BusinessRoleTypeoperationsT-01Enum value present; generic lookup accepts role=MANUFACTURER; no migration needed (free varchar)
T-03Add Item.manufacturer: BusinessRoleReference.Value? + migration (manufacturer_ref_* full column set) + businessRoleReferenceComponent("manufacturer_ref")operationsT-02Component round-trip test green; item persists/reads manufacturer incl. empty; migration applies cleanly, no backfill
T-04Role-parameterized resolver + STRICT/PROPAGATE mode (default PROPAGATE; keep retired guard)operationsT-03STRICT 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-05Listener for MANUFACTURER role + findItemsByManufacturerRole cross query + item projection refreshoperationsT-04Role rename updates linked items’ manufacturer.name; retire marks reference retired; deleted/retired items skipped; no cascade loop
T-06Item create/update API: resolution-mode (STRICT/PROPAGATE) threaded to the resolver inside the write transaction; optional, defaults to PROPAGATE (non-breaking — DQ-002)operationsT-04Absent 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-07Thread manufacturer name into print payloads — ItemPrinter / ItemPrintingService (label, breadcrumb) and KanbanCardPrinter (card/Kanban)operationsT-06Card/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-manufacturers route handler under src/app/api/arda/** — thin route.ts, import 'server-only', no Redux/client-fetcher imports.
  • Shared (BFF + SPA): route payload types and the BusinessRoleReferenceDTO in src/types/**; pure normalizeManufacturer in src/lib/shared/**. No server-only/client-only deps — safe in both bundles.
  • SPA-exclusive (client): lookupManufacturers (ardaClient), lookupManufacturersAsOptions (lookupAdapters), the manufacturerLinks slice (src/store/**), the typeahead + form wiring.
#TaskRepositoryDepends OnAcceptance Criteria
T-08Types (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 routearda-frontend-appT-07Modules 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-09Ordering-Details “Item Manufacturer” typeahead in ItemFormPanel (under Order method)arda-frontend-appT-08Suggests existing, adds new, clears; persists on submit; matches Type typeahead behavior
T-10Catalog Manufacturer filter + optional sortable column (itemsSlice filter/column-visibility)arda-frontend-appT-09Filter 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.

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.

#TaskRepositoryDepends OnAcceptance Criteria
T-11Open the operations PR (closes PDEV-875; refs PDEV-664); run pr-stewardoperationsT-05, T-07CI green; CHANGELOG present; threads resolved
T-12Open the arda-frontend-app PR (refs PDEV-664) only after operations is deployed AND verified in the target partition(s); run pr-stewardarda-frontend-appT-10, T-11Operations deployed + verified per partitionGET /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-13After 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 ticketT-11Tickets 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.

Entry criteria: feature merged.

#TaskRepositoryDepends OnAcceptance Criteria
T-14Update project docs to completed status, promote per the project lifecycle; open the documentation PR (PR-body CHANGELOG); make pr-checks greendocumentationT-11, T-12Docs reflect shipped design; links valid; smoke tests pass

PlantUML diagram

Single-agent, sequential — no persona split. The implementing agent reads the mandatory coding skills per repo before starting each phase.

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.

  • PDEV-1107 — make resolution-mode required — 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