Skip to content

Tenant policy and existing Trade writer ownership

  • Status: ACCEPTED
  • Date: 2026-09-05
  • Authority: the supplied multi-tenant review and the direct instructions proceed, then keep going.
  • Review pass: current source/caller inventory before implementation, separate from implementation verification.
  • Snapshot: local qa/HEAD 2b6079a4856ea16348bddb3fac76dd622981c9d8; local master 2a04f5db18775ccfd95618da26e2f71fde8eb05a; no remote refresh or delivery.

Problem and evidence

Fulfillment creation repeats profile-based prerequisites in UI and HTTP. The current PATCH path has no equivalent prerequisite rule; preserve that distinction rather than introducing new denials. TradeDataAccess infers execution contracts from MySQL binding. trade.ts still owns 2,215 lines, including Fulfillment, receipt/allocation and Scale writes. Packing combines layout and rate permission; its customer profile and numeric Booking field have no production readers. Warehouse-label role logic already exists in usesForkliftOperationalRolePolicy at the compatibility adapter.

Options considered

  1. Keep HTTP/UI branches and only rename variables: zero file/export cost, but duplicated behavior and DB-derived execution authority remain. Rejected for the supplied change pain.
  2. Reuse policy and existing domain contracts; move concrete writers inside existing trade/, preserving SQL and transaction semantics. Selected: immediate consumers are Web composition, Fulfillment and existing adapter callers. No new package or dependency is needed.
  3. Add workflow/repository frameworks or separate services: introduces unused boundaries and distributed failure modes. Rejected; existing modules and transactional adapters suffice.

Decision and bounded structural budget

Use typed profile defaults plus optional stored values for the existing outbound prerequisite variants. Fulfillment owns prerequisite judgment and its execution port; HTTP maps errors and composes trusted policy. Keep current PATCH behavior. UI consumes resolved policy from the public session, with presentation profile usage left intact where it only owns columns, terms or list defaults.

Move actual Fulfillment, Inventory and Scale writer bodies and their private helpers into semantic adapter files. Keep shared SQL utilities internal and keep existing public adapter names explicit. The receipt transaction remains one unit; placement remains a post-commit effect with existing retry and readback. No SQL, lock/CAS, tenant filter, ledger or recovery behavior is changed by extraction.

Packing uses semantic layout and independently resolved rate-edit policy. Remove unused customer-name exports and numeric public Booking identity after migrating current test/production consumers. Preserve persisted source edits and generated document artifacts; no stored data is rewritten. Reuse the existing numeric-role compatibility decoder; expose its semantic result to UI/commands.

Classifications: domain contracts CORE_MODULE; concrete SQL owners STABLE_ADAPTER; numeric-role decoding COMPATIBILITY_INTERNAL. No tenant/customer source fork.

Budget: zero packages/dependencies/new Product surfaces/architecture exceptions. At most four new internal adapter files are justified by removing real writer bodies from the original 2,215-line owner; no forwarding wrappers. Root exports may add the directly consumed Fulfillment port, prerequisite judgment/create command and typed policy helpers; remove unused Packing exports. Measure final deltas.

Verification and consequences

Preserve source-function bodies mechanically for the adapter move, then typecheck and execute existing contract and fault tests. New behavior checks cover both profiles, stored policy overrides, partial module composition for a third fixture tenant, denied actions, and Packing layout/rate independence. Fixture checks do not certify live DB isolation, concurrent transactions, issued-document history or operator acceptance. Test-tenant data creation requires separately authorized disposable runtime state.

The deliberate cost is more internal adapter files; revisit if the same write responsibility remains in multiple owners or production lines grow without moving an existing responsibility. No new generic Unit of Work is needed: reuse the existing transaction function and preserve each current commit boundary.


Source: new-system/docs/decisions/0024-tenant-policy-and-trade-writers.md — ctc repository · This page is an English translation of the source document. Identifiers, file paths and code symbols are unchanged.