Repository navigation
[C] #514 slice 11: Egg Operations contract and the opaque stock seam #853
Description
Activity
- addedsliceThin vertical work itemThin vertical work itemarea:apiAPI/endpoint layerAPI/endpoint layerpriority:tier4Deferred or speculativeDeferred or speculativesize:LSeveral days; wide blast radius or unresolved scopeSeveral days; wide blast radius or unresolved scopeepic-514Modular monolith architecture (#514)Modular monolith architecture (#514)
on Sep 14, 2026 - changed the title
[-]#514 slice 11: Egg Operations contract and the opaque stock seam[/-][+][C] #514 slice 11: Egg Operations contract and the opaque stock seam[/+]on Sep 14, 2026 - addedtrack:C#514 Track C — moves production code; UNSCHEDULED, needs authorisation#514 Track C — moves production code; UNSCHEDULED, needs authorisation
on Sep 14, 2026 Audit 2026-09-26, against
mainat e47288cBody left as written.
Atomicity premises verified.
SubmitDailyEntryHandlercreates lots and opening ledger movements inline and saves once.AdjustDailyEntryHandlerandVoidDailyEntryHandlereach wrap their body in oneExecuteInTransactionAsyncwith exactly one audit write. The "one transaction, one save" claim the whole slice rests on is intact.Correction: there is a fourth egg-lot writer, and the issue's framing never mentions it.
RecordEggLotMovementHandler(Features/EggLots/RecordEggLotMovement) has its own transaction and its own audit row, and is the writer behindDiscard,InternalUseandReconciliationmovements. It shipped in #464 on 2026-08-08, so this is a pre-existing gap in the description rather than drift.AGENTS.mdalready lists all four writers under the egg loop; this slice's scope section should match it. Confirmed no new writer since: the fiveIEggLotRepositorycall sites are unchanged.New since the issue: the per-grade low-stock floor (#950).
EggGradeFloorPolicybelongs to Egg Operations and is grade-management authorization, the same shape as the rest of the grade catalog, so it belongs in this contract. It also reads Farm's role model throughDomain.Accounts.Roles, which is why the Egg Operations to Farm edge grew from one symbol to two. That is a read, not a write, so the atomicity story is unaffected, but the contract now carries a second Farm-role dependency alongside the existing provisioning one.Scope gap in the "opaque stock seam". The seam as described covers FIFO reserve and restore. Commerce also reads the grade catalog for product and order-line mapping (
CreateProductHandler,UpdateProductHandler,AddOrderItemHandler) and, since #912, reads grade display names for sales responses. Neither is a stock operation, so neither is covered by a reserve-and-restore seam. Name them explicitly or they become compatibility exceptions by default.Sizing unchanged at L. The added items are incremental, and the expensive part, proving the invariant tests pass unedited after extraction, has not grown.
Amendment from #852 (PR #1021). That PR ships part of this issue's scope. The body above is left as written.
Shipped:
- "Calls Flock Management's mortality port inside the same ambient transaction." Submit, Adjust and Void now call
IMortalityLedger.AppendAsync. The port adds the movement to the caller's unit of work and never saves. MortalityLedgerAtomicityTestsproves the rollback. The Submit test turns red when the port saves early. The Void test turns red once itsExecuteInTransactionAsynctransaction is also removed.- All four daily-entry handlers read the flock through
IFlockLookup(FlockDetails.CanRecordProductionOn), notIFlockRepository. DailyEntryEndpointsreads display names throughIFlockLookup.
Remains for this issue:
IEggOperationsModuleitself, the opaque stock seam for Commerce, and moving the daily-entry, grade and stock endpoints and the lock-sweep job onto the contract. The Egg Operations to Flock Management edge staysW. These handlers take nothing else from Flock Management, so this slice does not need to touch their flock calls again.- "Calls Flock Management's mortality port inside the same ambient transaction." Submit, Adjust and Void now call
Remainder note (PR #1028). PR #1028 closes this issue, but it does not ship all of it.
Shipped:
IEggOperationsModulecovers daily entries, grades, stock, lots and the lot ledger.- The
IEggGradeLookupandIDailyEntryLookupread ports are in place for peers. - The daily-entry, grade and stock endpoints, both seeders and the lock sweep all call the contract.
- The egg lot lock order is pinned by
EggLotLockOrderTestsandEggLotLockSqlTests.
Moved to #854 by the owner (2026-10-02): the opaque stock seam for Commerce. Confirm and void still call
IEggLotRepository,IEggInventoryMovementRepositoryandIEggGradeRepository. Commerce's grade reads move with them. The properties any seam must keep are listed in the #854 amendment.Corrected from the body:
SubmitDailyEntryHandlerdoes write an audit row (DailyEntry.Submit, since #494). #1028 keeps every handler's audit rows unchanged.
Important
Track C is unscheduled. These slices move production code, and the 2026-08 design's own status
line still governs: "proposed architecture; no production refactor is authorized by this
document." Filed so the analysis is tracked work rather than a comment, not as a commitment to
run it.
The 2026-09-14 re-plan
recommends gating Track C on evidence from Tracks A and B: if the #842 ratchet fires repeatedly in
real pull requests, these become worth their cost; if it stays quiet, they do not. Track C is
57–95 focused engineering days and produces no user-visible change.
Read the re-plan before picking any of these up. Several assumptions in the 2026-08 plan are
corrected there.
Daily capture, egg grades, egg lots and the egg ledger behind one contract. The largest invariant
cluster in the system.
Why these four are one module, not four
Submission and correction prove they share invariants. Submitting a daily entry creates lots and
opening ledger movements and optionally appends mortality, then saves once. Adjust and void
reverse it with compensating rows under the client's aggregate version. Splitting grades, lots and
the ledger into peers would fracture a transaction that is currently atomic, which is the one thing
this whole design refuses to do.
Scope
IEggOperationsModule— submit, adjust, void, plus stock reads.never lots. Commerce must not learn what a lot is.
Done when
Every one of these passes unedited — if a test needs editing, the extraction changed behaviour:
(ProductionDate, Id);Audit behaviour must not change.
AdjustDailyEntryHandlerandVoidDailyEntryHandlerwrite auditrows today;
SubmitDailyEntryHandlerdoes not. Adding one would be a product change this slicedoes not authorise.
Explicitly not in this slice
Commerce. No event performs an authoritative write — read-model publishes happen after commit, never
before, and never carry stock creation.