Repository navigation
[C] #514 slice 13: General Inventory contract and the flock-eligibility read #855
Description
Amended 2026-10-02 (#852, PR #1021): the three usage handlers now read flock eligibility through
IFlockLookup, with the feed-usage order unchanged;IInventoryModuleremains. See this comment.
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.
Inventory items, lots, movements, feed usage and water usage behind a contract.
Scope
IInventoryModuleover purchase, adjust, consume, feed usage and water usage.- Calls Flock Management's eligibility read, not its entities or repositories. The risk this
slice carries is importingFlockinstead of asking a question about it. - Calls Farm for the currency snapshot.
The ordering detail that must not move
RecordFeedUsage locks the inventory item first, then — still inside that same transaction —
evaluates flock eligibility, then reads lots. The check must stay inside the transaction and after
the item lock.
Moving it to a precondition before the transaction opens would widen the race, not close it.
Today's shape deliberately shrinks a concurrent archive/deplete race to the commit itself, and leaves
the flock row unlocked on purpose: a same-day deplete racing a same-day feed is business-valid either
way, because the birds ate before they left.
Done when
Unedited and green: backdated FIFO consumption, insufficient-stock rollback, concurrent lot
consumption, flock eligibility behaviour, currency handling, tenant and Version tests, API and SPA
contracts. One inventory transaction retained. EF model digest identical.
- Change map in the PR (owner, 2026-10-01). The PR carries a before/after diagram of who calls whom, in the shape of the refactor(finance): put Finance behind an IFinanceModule contract #1010 and refactor(insights): read reports, exports and audit provenance through an Insights facade #1012 change maps: the adapters and module types before and after, any ledger edges added or removed, and the end state of any file another open slice also changes. Put it in the PR body or a PR comment as a fenced text block.
Explicitly not in this slice
Flock internals. Egg stock — that is Egg Operations' ledger and a different FIFO.
Activity
[-]#514 slice 13: General Inventory contract and the flock-eligibility read[/-][+][C] #514 slice 13: General Inventory contract and the flock-eligibility read[/+]mforce commentedon Sep 26, 2026
Audit 2026-09-26, against
mainat e47288cNo change needed. The ordering this slice says must not move is intact in
RecordFeedUsageHandler: the inventory item is locked first (:67), flock eligibility is evaluated next (:89), and the FIFO lots are read last (:107), all inside the same transaction.One detail worth carrying into the contract design. There are also two flock reads before the transaction opens: the flock-scope guard at
:41and a flock lookup for the link at:55. So the Inventory contract needs a flock-eligibility read in two positions, pre-transaction and in-transaction, not one. The slice's prose describes only the in-transaction one, which is the one with the ordering constraint, but an implementer reading only that will miss the other two call sites.Census unchanged: 32 files, 5 tables, three outgoing edges (Egg Operations, Farm, Flock Management, all
R), zero incoming.mforce commentedon Oct 2, 2026
Amendment from #850 (PR #1013)
What shipped.
module-ledger.jsonnow has acompatibilityExceptionssection.CompatibilityExceptionRealTreeTestsfails CI on any read of a contracted module's tables that is neither allowed by structure nor registered. One row names this issue as its deletion trigger:Cluckwork.Infrastructure.Repositories.CurrencyBoundRowProbe.AnyAsyncreaches Finance throughdb.Expenses. Its owner isFarm, because the currency lock is Farm's rule.What this issue now owns. The probe also reads Commerce (
SalesOrders,Payments,Products) and General Inventory (InventoryLots,FeedUsages,InventoryItems) tables. It can move behind module contracts only once the last of those modules has one, and this slice is that one. When this slice declaresowners.GeneralInventory.contract, the guard applies to General Inventory's tables too: the probe's General Inventory reads, and any other Platform reader of those tables, become rows this slice must register or remove. Deleting the probe'sdb.Expensesread makes the existing row stale, so remove the row in the same PR. The rule is indocs/decisions/850-compatibility-exceptions.md.mforce commentedon Oct 2, 2026
Note from the Farm contract slice (#851, PR #1015): the locked currency snapshot did not move behind
IFarmModule. Following the 2026-09-26 audit on #851,IAccountRepositoryandDomain.Accountsstay the stable account seam that peer modules call directly. So "Calls Farm for the currency snapshot" here means callingIAccountRepository.GetCurrentSharedLockedAsyncinside the write transaction, as today (#162FOR SHARE). It does not mean a Farm contract method. Rationale:docs/decisions/851-farm-contract.md.mforce commentedon Oct 2, 2026
Amendment from #852 (PR #1021). That PR ships part of this issue's scope. The body above is left as written.
Shipped:
RecordFeedUsageHandler,RecordWaterUsageHandlerandUpdateWaterUsageHandlernow takeIFlockLookupand read aFlockDetailscopy withCanRecordProductionOn(date).InventoryEndpoints.ListFeedUsageandWaterUsageEndpoints.ListWaterUsageread display names throughIFlockLookup.Remains for this issue:
IInventoryModule, the Farm currency snapshot, andCurrencyBoundRowProbe(a #850 row withdeleteWhen: #855). The unedited-test acceptance criteria are unchanged.test(eggs): make the lot lock-order pin deterministic, pin the SQL text