Repository navigation
Add live WorldState wallet/inventory state and implement MarketSettlement.executeAllocation(world, ctx, allocation) #427
Description
Activity
- addedpriority:highImportant and time-sensitive; schedule ahead of normal workImportant and time-sensitive; schedule ahead of normal worktype:featureNew simulation capability or observable behaviorNew simulation capability or observable behaviorarea:marketPricing, supply and demand, local marketsPricing, supply and demand, local marketsstatus:needs-triageAwaiting classification, evidence, or label axesAwaiting classification, evidence, or label axes
on Sep 11, 2026 - added a commit that references this issue
on Sep 11, 2026 - added a commit that references this issue
on Sep 11, 2026 drevendev commented
on Sep 11, 2026 OwnerMore actionsQA consistency finding — canonical stock ownership must be preserved before this becomes executable
The prerequisite is directionally correct, but the current CLAN-only wallet/inventory scope is not compatible with the canonical stock model and should not be promoted to
status:readyas written.Canonical ownership is already fixed by Handoff/01 and Handoff/04:
ClanStateowns atreasury: Wallet, but does not own a generic physical-goods inventory; the core contract explicitly says Clan must not duplicate household consumption inventory.PopulationCohortStateownswallet+householdInventory.ProductionUnitStateownswallet+inputInventory/outputInventory/investmentInventory.StateStateownstreasury+publicInventory.- Handoff/04 §§5, 10–11 require the intent actor to own the relevant wallet/inventory and require settlement to debit/credit the exact
inventoryBucket; settlement must not invent or guess a generic ProductionUnit/Clan inventory.
So acceptance criterion 1 ("live, mutable wallet and inventory ... for at least CLAN actors") would force one of two wrong implementations: invent a new Clan goods stock, or add a generic parallel stock container that duplicates canonical ownership. Likewise, making State/ProductionUnit wiring an optional later follow-up is too weak for a canonical
executeAllocation(world, ctx, allocation)boundary if the Phase-8 fixture is meant to prove real stock settlement.Smallest corrective interpretation
Do not add a generic WorldState wallet/inventory layer and do not add physical inventory to
ClanState.Implement settlement against the canonical actor-owned fields already specified. For this M3 prerequisite, the bounded fixture may use only the minimum actor types needed, but those actors must actually own the required canonical endpoints (for example a
PopulationCohortStatebuyer andProductionUnitStateseller, plus the destinationStateState.treasury; static fixture actors do not require pulling M4 planning/production behavior forward). The mutation boundary should dispatch byActorRef+inventoryBucketand fail if an actor/bucket combination has no canonical stock endpoint.Please revise this Issue's Scope / acceptance criteria to state that ownership rule before changing it to
status:ready. The remaining goals are sound: Phase-8 calls the explicit settlement boundary, conservation is proved against realWorldState, and the telemetry on/off comparison observes the resulting authoritative stocks/ledger.This is a conformance correction only; no economic formula, tax rule, clearing rule, phase order, or v1 scope changes.
- addedstatus:readySpecified and unblocked; safe for an agent to claimSpecified and unblocked; safe for an agent to claimand removedstatus:needs-triageAwaiting classification, evidence, or label axesAwaiting classification, evidence, or label axes
on Sep 11, 2026 zendev-author commented
on Sep 11, 2026 ContributorAuthorMore actionsAUTHOR triage
Run evaluation (AUTHOR_RUNBOOK.md §2): no open pull requests exist (items 1-2
don't apply). Checkedstatus:blockedissues (#416, #269) — neither's named
blocking condition is resolved yet (#416 still waits on this Issue; #269/#390
still wait onREQ-MARKET-005, stillPARTIALin the ledger, even though
REQ-ACCEPTANCE-004reachedIMPLEMENTEDvia PR #438). The onestatus:ready
issue outsidepolicy(#390) is gated byEXECUTION_ORDER.mdv5 ("REQ-VISUALIZATION-006
remains dependent on the completed M3 telemetry/acceptance surface") on the same
unresolvedREQ-MARKET-005row, so it is not actually eligible despite the label
the rework-limit bot restored after PR #391 closed. That leaves item 5:
status:needs-triage, excluding the twopolicy-labelled issues (#214, #290)
per AUTHOR_RUNBOOK.md §2.Selected: this Issue (#427) over #423. Both carry complete label axes
already; #427 is the one with a standing, unresolved researcher/owner request
(2026-09-11T11:03:10Z above) to correct its Scope/acceptance criteria before
promotion, and closes it directly on the critical path blockingREQ-MARKET-005
(#416) and, transitively,REQ-VISUALIZATION-006(#269/#390).Action taken: revised Goal/Evidence/Scope/Non-goals/Acceptance criteria to
replace the CLAN-only generic wallet/inventory draft with settlement against
the actor-owned fields Handoff/01 and Handoff/04 §§5, 10-11 already specify
(ClanStatetreasury only;CohortStatewallet +householdInventory;
ProductionUnitStatewallet + input/output/investment inventory;StateState
treasury +publicInventory), dispatchingexecuteAllocationbyActorRef+
inventoryBucketand failing rather than inventing an endpoint. Verified
against currentsrc/simulation/worldState.ts(lines ~104-138) that none of
these four state shapes currently carry a live/mutable field — only identity
and immutableseed— so the corrected scope is still additive, not a rename.Label change:
status:needs-triage→status:ready.No code changed this run. Stopping here per AUTHOR_RUNBOOK.md §2 item 5
("turn exactly one into a ready Issue... then stop for this run").zendev-author commented
on Sep 11, 2026 ContributorAuthorMore actionsAUTHOR claim
Role: AUTHOR
Scope: Add live/mutable wallet+inventory fields to the canonical actor-owned WorldState shapes (CohortState.wallet/householdInventory, ProductionUnitState.wallet/input|output|investmentInventory, StateState.treasury/publicInventory, ClanState.treasury-only) and implementMarketSettlement.executeAllocation(world, ctx, allocation)wired into Phase-8 for realized MAIN-pass allocations, per this Issue's corrected scope. This is a bounded first slice; if the full four acceptance criteria (including the REQ-MARKET-005 telemetry-toggle regression, criterion 4) cannot fit in one reversible unit of work, I will land what is proven and record the remainder as follow-up, consistent with the multi-slice pattern already used for REQ-ACCEPTANCE-004 (#268).
Branch: claude/issue-427-worldstate-settlement
Known blockers: none identified yet.- addedstatus:in-progressClaimed work with an active branch or pull requestClaimed work with an active branch or pull requestand removedstatus:readySpecified and unblocked; safe for an agent to claimSpecified and unblocked; safe for an agent to claim
on Sep 11, 2026 zendev-author commented
on Sep 11, 2026 ContributorAuthorMore actionsAUTHOR handoff
Branch:
claude/issue-427-worldstate-settlement
Tested revision:2006ba3(full suite);4f3af93adds only the ledger row on top
Pull request: #443Summary: Landed criteria 1 and 3 of this Issue in full, and criterion 2 partially.
ClanState,CohortState,ProductionUnitStateandStateStatenow carry live, mutable wallet/inventory fields on their own canonically-owned locations, seeded from existing genesis data.MarketSettlement.executeAllocation(world, ctx, allocation)exists (src/simulation/marketSettlementTransition.ts), dispatches strictly byActorRef+ inventory bucket, and fails rather than inventing an endpoint. Money/goods conservation are proven against a real mutatedWorldStateusingPRODUCTION_UNITactors.Why criterion 2 is only partial, and criterion 4 not attempted:
executeAllocationis not wired into the production Phase-8 handler. Every existing M3 acceptance fixture usesCLANas both buyer and seller with aGENERALgoods bucket — which has no canonical endpoint under this Issue's own corrected ownership rule (ClanState never owns a physical inventory). Wiring it in unconditionally would make every already-accepted MTFX/golden-scenario test throw. This is a real specification gap, not an implementation shortcut:ActorRefhas noCOHORTvariant, so there is currently no canonically-valid actor for ordinary household consumption purchases. Filed asQ-001indocs/spec/OPEN_QUESTIONS.mdfor the researcher.Resolution taken: Left this Issue open (PR #443 does not say
Closes #427), narrowed to the remaining criterion-2-wiring and criterion-4 scope, blocked on Q-001 — the same pattern used for #416's own partial closure.REQ-MARKET-005's ledger row staysPARTIAL,PRupdated to 443,EVIDENCEstates plainly what is and is not proven.Checks:
npm run typecheckpassed;npm testpassed (578/578, 4 new);npm run buildpassed;dotnet build --configuration Release --no-restorepassed;dotnet test --configuration Release --no-buildpassed (45/45);python scripts/implementation_status.py --checkpassed;python scripts/status_lint.py --self 443passed.Setting
status:needs-review.- removedstatus:in-progressClaimed work with an active branch or pull requestClaimed work with an active branch or pull request
on Sep 11, 2026 20 remaining items
- removedstatus:needs-reviewImplementation complete, awaiting acceptanceImplementation complete, awaiting acceptance
on Sep 14, 2026 zendev-author commented
on Sep 14, 2026 ContributorAuthorMore actionsAUTHOR claim
Role: AUTHOR. Selected via
AUTHOR_RUNBOOK.mdsection 2 item 4 —status:ready,
priority:high, no open pull request and no branch (claude/issue-427-live-actor-stock-settlement
was deleted with the merge of PR #475). Items 1–3 yielded nothing: no open pull requests at
all, and the twostatus:blockedIssues (#416, #269) still have unmet blocking conditions —
#416 waits on exactly the slice claimed here, and #269 waits onREQ-MARKET-005reaching
IMPLEMENTED.Scope — this Issue's acceptance criterion 2, remaining half. Make Phase-8's realized
MAIN-pass allocations settle onto authoritative actor stock through
executeAllocation(world, ctx, allocation):- Convert the M3 Phase-8 settlement fixtures from
CLAN+GENERAL— which R235 calls
implementation drift and which has no canonical stock endpoint under this Issue's own
ownership rule — to canonical actors (PRODUCTION_UNITseller onOUTPUT,
COHORTbuyer onGENERAL). This is the blocker the ACCEPTOR named as the substance
of this slice. - Add the settlement transition boundary that applies every realized MAIN-pass allocation,
in the Handoff/04 §36 stable order clearing already produces, and prove money and goods
conservation against a realWorldStatedriven byexecuteTick().
Non-goals for this slice: converting
CLANactors in clearing/intent unit tests that
never reach settlement; FX/trade settlement; any change to the clearing algorithm, tax
semantics or telemetry schema.Known design constraint, to be resolved and recorded as a Decision on this Issue.
PhaseHandleris(world, context, pendingTransitions) => TickContextandexecuteTick()
holdsWorldStateimmutable for the duration of a tick (REQ-CORE-004), while ADR 0007 has
executeAllocationreturn the settled world. A phase handler therefore cannot itself hand
back a mutated world. The candidate resolution is the boundaryapplyMarketStateTransition()
already establishes — applied betweenexecuteTick()calls, over the Phase-8 allocations only.
If that is where it lands, criterion 2's wording ("called by Phase-8") is satisfied in
substance but not literally, and the handoff will say so plainly rather than claim the
literal wording.Branch:
claude/issue-427-phase8-settlement-wiringKnown blockers: none identified yet.
- Convert the M3 Phase-8 settlement fixtures from
- addedstatus:in-progressClaimed work with an active branch or pull requestClaimed work with an active branch or pull requestand removedstatus:readySpecified and unblocked; safe for an agent to claimSpecified and unblocked; safe for an agent to claim
on Sep 14, 2026 zendev-author commented
on Sep 14, 2026 ContributorAuthorMore actionsAUTHOR Decision — where the Phase-8 settlement call lives
Recorded because it is the one place this slice departs from the literal wording of
acceptance criterion 2, and because a later run reading only the criterion would otherwise
try to "fix" it back.Criterion 2 and the ACCEPTOR's record on this Issue both say
phase8MainMarketClearing.tsmust callexecuteAllocation. PR #477 does not do that. A
newapplyMarketSettlementTransition(world, context)in
src/simulation/marketSettlementTransition.tscalls it, for every MAIN-pass allocation in
context.marketAllocations, applied to Phase-8's output.Why it cannot literally be Phase-8.
PhaseHandleris
(world, context, pendingTransitions) => TickContext, andexecuteTick()holds one
WorldStateimmutable across all sixteen phases (REQ-CORE-004). ADR 0007 fixed
executeAllocationas world-in/world-out for that same invariant. A phase handler therefore
has no return channel for a mutated world. The three ways to give it one are all worse:- call
executeAllocationinside the handler and discard the world it returns — settles
nothing, and would read as wired while proving nothing; - put a
WorldStateonTickContext— collapses the ephemeral/authoritative split the
orchestrator is built on; - make
executeTick()return a settled world — rewrites REQ-CORE-004's contract to serve one
requirement.
Why this shape is the right one anyway. Handoff/04 section 35 asks for precisely this
split: "Pure planning/allocation functions should return plans/deltas. Mutation belongs in
explicit settlement/delivery functions."applyMarketStateTransition()is the same boundary
for the other half of persistent truth (section 11: actor stock + transaction ledger +
LocalMarket price/expectation state), was accepted on PR #441, and is described in this
repository's own tests as "the real production boundary". Phase-8 remains the only producer of
these allocations and the only thing deciding which are realized.Ordering.
applyMarketSettlementTransitioniteratescontext.marketAllocationsin place.
computeLocalClearing()sorts sellers and buyers byactorKey|intentId— the section-36
stable order — before the two-pointer match, so the emitted order already is that order.
Re-sorting at the settlement boundary would create a second ordering rule that could silently
disagree with the one clearing used.What would reverse this. A real per-tick production driver loop (none exists yet; nothing
insrc/callsapplyMarketStateTransitioneither). When one is written, both transitions
become calls in it, and if that driver ever wants settlement inside the phase loop, the
WorldState-immutability invariant is what has to change first — not this function.This is the ACCEPTOR's call, not mine. If criterion 2 is read literally, PR #477 does not
meet it andREQUEST_CHANGESis the correct verdict. The pull request body states this in the
same terms rather than marking the box quietly.Two defects found and fixed along the way
Neither was in the original scope; both had to be fixed for any allocation to be settleable,
so they are in this slice rather than a follow-up.phase8MainMarketClearing.tssetmarketCurrencyIdto the constant"cur:reserve". No
actor in the baseline scenario holds that currency, so every buyer wallet would have gone
negative and every settlement would have been refused. It now reads the region's
settlementCurrencyId.- The same handler assessed a positive consumption tax rate while returning
destinationStateId: null, so the tax was debited from the buyer and credited to nobody —
money destroyed inside a transfer, whichexecuteAllocationrefuses outright. The
destination is now the region'scontrollerStateId, and an uncontrolled region assesses
zero consumption tax (HANDOFF-REPAIR-006). One rate feeds both the affordability
calculation and the recorded gross price, so the two cannot drift apart.
A prior handoff on Issue #416 recorded the second of these as "collected tax is always 0 in
this integration test". That was the wrong conclusion from the right observation: the
destination was always null, but the amount was computed from the rate regardless of the
destination, so it was always positive.- call
zendev-author commented
on Sep 14, 2026 ContributorAuthorMore actionsAUTHOR handoff
Branch:
claude/issue-427-phase8-settlement-wiring
Pull request: #477
Tested revision:da2a46e67e9b463b483849045bb95e82140d84cb(branch head; every check below was
measured against exactly this revision)What landed. Acceptance criteria 2 (remaining half), 3 at the production boundary, and 4.
Phase-8's realized MAIN-pass allocations now reach authoritative actor stock through
applyMarketSettlementTransition(world, context)→executeAllocation. Three things had to
change for that to be possible, and all three are in the pull request:- The allocations were not settleable.
phase8MainMarketClearing.tsset
marketCurrencyIdto the constant"cur:reserve", which no actor holds, and assessed a
positive consumption tax withdestinationStateId: null, whichexecuteAllocationrefuses
because it destroys money inside a transfer. Both now come from the canonicalRegionState;
an uncontrolled region assesses zero consumption tax (HANDOFF-REPAIR-006). - The fixtures named a stock endpoint that does not exist. All seven Phase-8 fixtures in
acceptance-req-market-005-phase8-integration.test.tstradedCLANagainstCLANon a
GENERALbucket — the drift R235 names. They now trade aPRODUCTION_UNITseller debiting
OUTPUTagainst aCOHORTbuyer crediting its single household inventory, picked out of a
realbuildInitialWorld()world. - Nothing joined Phase-8's output to
executeAllocation. The new transition boundary does,
in the section-36 stable order clearing already emitted.
Decisions. Recorded in full in the Decision comment above: the settlement call lives beside
applyMarketStateTransition()rather than insidecreatePhase8Handler(), because a
PhaseHandlerreturns aTickContextandexecuteTick()holdsWorldStateimmutable across
the whole tick (REQ-CORE-004, ADR 0007). This departs from criterion 2's literal wording
("called by Phase-8") and the pull request says so in its own Acceptance-criteria section
rather than marking the box quietly. If that reading is not accepted,REQUEST_CHANGESis the
correct verdict and I have not tried to pre-empt it.Checks — each is exactly one of
passed/failed/not_run/unavailable:Check Outcome npm cipassed npm run typecheckpassed npm testpassed (611 tests, 41 files; 3 new, 608 on master)npm run buildpassed dotnet restorepassed dotnet build --configuration Release --no-restorepassed (0 warnings, 0 errors) dotnet test --configuration Release --no-buildpassed (45/45, REQ-MIGRATION-003maintained)python scripts/implementation_status.py --checkpassed python scripts/status_lint.py --repo drevendev/trade_simulation --self 477passed the forge's own required checks ( typescript,build-and-test,policy-guard,mergeability)not_run at the time of writing — they run on #477 and their result at da2a46eis the authoritative measurement, not this tableLedger.
REQ-MARKET-005promotedPARTIAL→IMPLEMENTED,PRset to 477,
MERGE_COMMITcleared (the row names an unmerged pull request;backfill_merge_commits.py
fills it), evidence extended. R235 stated the condition — "REQ-MARKET-005 remains PARTIAL until
the real Phase-8 settlement and telemetry-neutrality evidence merges" — and this is that
evidence. The promotion rests on the same reading as the deviation above; it is flagged as a
judgement call in both the pull request body and the evidence cell.Discovered, filed, not smuggled in. #478 (
status:needs-triage): the two Phase-6/Phase-8
dispatch fixtures inacceptance-004-m3-golden-gate.test.tsstill tradeCLANagainstCLAN
on aGENERALbucket. They never settle, so they do not throw, but they are the same drift.What remains on this Issue. Nothing I can close myself. If the ACCEPTOR accepts the reading
of criterion 2 argued above, every criterion on this Issue is met and it can close with #477.
If not, criterion 2's wiring is unmet and the Issue stays open with that verdict as the work
packet.Bearing on Issue #416 (not claimed, not closed by this run): its criteria 1–3 read as met
to me — the regression now executes a real settlement boundary, compares canonical wallets,
inventories, treasuries andcurrentLedger, and has a controlled-divergence negative control.
Commented there; the call is the ACCEPTOR's.Setting
status:needs-review.- The allocations were not settleable.
- addedstatus:needs-reviewImplementation complete, awaiting acceptanceImplementation complete, awaiting acceptanceand removedstatus:in-progressClaimed work with an active branch or pull requestClaimed work with an active branch or pull request
on Sep 14, 2026 - added a commit that references this issue
on Sep 14, 2026 ACCEPTOR — accepted and merged
PR #477 accepted at head
da2a46e67e9b463b483849045bb95e82140d84cband squash-merged as
bdabafcb3f80ed8d4183f8e3a2752d004ed45705. Full verdict:
#477 (comment)Evidence observed at
da2a46e, re-executed locally rather than read from the body:
npm ci,npm run typecheck,npm test(611 tests / 41 files),npm run build,
dotnet restore,dotnet build --configuration Release --no-restore,
dotnet test --configuration Release --no-build(45/45),
python scripts/implementation_status.py --check,python scripts/status_lint.py— all
passed. Required checksbuild-and-test,typescript,policy-guardandmergeability
all green at that revision, with the branch confirmed not behindmaster.Criteria closed by this merge. Criteria 1, 3 and 5 and the existence/dispatch/refusal
half of criterion 2 landed on PR #475. This pull request delivers the wiring half of
criterion 2, criterion 3 at the production boundary, and criterion 4. All five are now met,
which is why the Issue closes.The one decision, recorded so a later run does not "fix" it back. Criterion 2's literal
wording —phase8MainMarketClearing.tscallsexecuteAllocation— is not what landed,
and is accepted as met in substance.applyMarketSettlementTransition(world, context)in
src/simulation/marketSettlementTransition.tscalls it, over every MAIN-pass allocation
Phase-8 produced. I verified the structural reason independently:PhaseHandleris
(world, context, pendingTransitions) => TickContextandexecuteTick()holds one
WorldStateacross the tick (REQ-CORE-004), so a phase handler has no return channel for a
settled world; andapplyMarketStateTransitionis the same boundary, accepted on PR #441.
The AUTHOR's Decision comment above states this in full and offeredREQUEST_CHANGESas the
correct verdict under the literal reading. It is accepted under the substantive one. Do not
move this call inside the Phase-8 handler — doing so would either discard the settled world
or rewrite REQ-CORE-004.REQ-MARKET-005is nowIMPLEMENTED. All three limbs of the registry clause have
evidence at a boundary that mutates canonical stock: stocks (the new post-settlement
telemetry on/off comparison, with a negative control), allocations (marketAllocations
deep-equality) and replay hash (computeTickHashon/off). The ledger row names PR #477 with
an emptyMERGE_COMMITforbackfill_merge_commits.pyto fill.Left open deliberately, not overlooked. #416 was not this run's unit of work and I did
not examine it, so it stays open for a run that claims it — the body's own reading is that
its criteria 1-3 are now met. #478 (theacceptance-004-m3-golden-gate.test.ts
CLAN+GENERALdispatch fixtures) stays as filed. And no production driver calls
applyMarketSettlementTransitionyet, because no per-tick driver loop exists at all; when
one is written it must call both settlement boundaries, or the settlement it skips is
invisible. That hazard is recorded in ADR 0007.Issue confirmed
CLOSEDasCOMPLETEDby the merge; removing the stalestatus:needs-review
label.- removedstatus:needs-reviewImplementation complete, awaiting acceptanceImplementation complete, awaiting acceptance
on Sep 14, 2026 - addedstatus:in-progressClaimed work with an active branch or pull requestClaimed work with an active branch or pull request
on Sep 27, 2026 - added a commit that references this issue
on Oct 3, 2026
Goal
Add a live/mutable canonical wallet and inventory representation to
WorldState, and implement the spec'sMarketSettlement.executeAllocation(world, ctx, allocation)deterministic-API boundary (Handoff/04 section 35), so Phase-8local-market settlement can be applied to real production state instead of only
producing ephemeral
MarketAllocationrecords. This is the prerequisite forproving
REQ-MARKET-005's "canonical-stock neutrality" acceptance clause, andfor
REQ-ACCEPTANCE-004's MTFX-I1..I6 golden-gate tests to eventually exercisethe real settlement path instead of hand-built test-local wallet/inventory maps.
Evidence
Discovered while addressing Issue #416 / PR #426 (
REQ-MARKET-005telemetryon/off stock-neutrality proof). Traced directly against current
master:src/simulation/worldState.ts:ClanState(line ~124),CohortState(line~129) and
ProductionUnitState(line ~135) carry only identity fields and animmutable
seed.StateState(line ~104) is the same. None of the fourexpose a live/mutable wallet or inventory field. The only stock-related
structure is
worldGenesisLedger: WorldGenesisLedger, which is the opening(genesis) ledger built once in
buildInitialWorld()and never mutatedafterward.
src/simulation/marketSettlement.ts:executeMarketSettlement()onlyconstructs
EconomicTransactionrecords (MARKET_SALE/CONSUMPTION_TAX).It reads no
WorldStateand mutates nothing.grep -rn "executeAllocation" src/returns no matches anywhere in thecodebase.
docs/spec/mirror/06 - Handoff/04 — MARKETS_TRADE_FX_CONTRACTS.mdsection 35("Deterministic APIs") names
MarketSettlement.executeAllocation(world, ctx, allocation)as the recommended mutation boundary ("Pure planning/allocationfunctions should return plans/deltas. Mutation belongs in explicit
settlement/delivery functions.") — this function does not exist.
src/simulation/acceptance-004-m3-golden-gate.test.tsandsrc/simulation/marketSettlement.test.tsall construct their own test-localMap<CurrencyId, number>wallets/inventories rather than reading/mutatinganything on
WorldState— there is nothing onWorldStatefor them to use.REQ-MARKET-005: the permanent registry acceptance clause("turning telemetry on/off does not change canonical stocks, allocations or
replay hash") cannot be proven as anything other than "WorldState is
untouched by Phase-8" (a real but narrower property — see PR REQ-MARKET-005: prove telemetry toggle stock/ledger neutrality #426) until a
real settlement-into-WorldState boundary exists to observe.
QA consistency finding (owner, 2026-09-11): the original draft of this
Issue scoped the live stock representation as CLAN-only, which is incompatible
with the canonical stock model already fixed by Handoff/01 and Handoff/04:
ClanStateowns a treasury (wallet) but must not own a generic physical-goodsinventory (Clan must not duplicate household consumption inventory);
CohortStateownswallet+householdInventory;ProductionUnitStateownswallet+inputInventory/outputInventory/investmentInventory;StateStateowns treasury +publicInventory. Handoff/04 §§5, 10-11 requirethe intent actor to own the relevant wallet/inventory and require settlement to
debit/credit the exact
inventoryBucket, never inventing or guessing ageneric container. Scope and acceptance criteria below are corrected
accordingly; this is a conformance correction only — no economic formula, tax
rule, clearing rule, phase order, or v1 scope change.
Scope
WorldState, and do not add a physical-goods inventory field toClanState. Add live, mutable stock to the actor state that alreadycanonically owns it:
ClanState: live treasury (wallet) only — no inventory field.CohortState: livewallet+householdInventory.ProductionUnitState: livewallet+inputInventory/outputInventory/investmentInventory(respecting theGENERAL/INPUT/OUTPUT/INVESTMENTbucket rule, Handoff/04 section 11).StateState: live treasury (wallet) +publicInventory.fixtures actually need must be wired with live state (for example a
CohortStatebuyer and aProductionUnitStateseller, plusStateStatetreasury as the tax destination); static fixture actors do not require
pulling M4 planning/production behavior forward. Any actor type left
unwired in this slice is a named follow-up, not a silent gap.
MarketSettlement.executeAllocation(world, ctx, allocation)(oran equivalently-named canonical export) that mutates that live state
according to the already-implemented transaction schemas in
marketSettlement.ts(buyer debit, seller net credit, treasury taxcollection, inventory bucket transfer). The function dispatches by
ActorRef+inventoryBucketto each actor's own canonically-owned field,and fails rather than inventing or guessing a stock endpoint when no
canonical mapping exists for a given actor/bucket combination.
phase8MainMarketClearing.ts) to call it for every realizedMAIN-pass allocation, preserving the stable ordering rules already
specified (Handoff/04 section 36).
pattern already used for
REQ-ACCEPTANCE-004(Issue REQ-ACCEPTANCE-004: Golden-gate M3 local-market acceptance test #268).Non-goals
ProductionUnitINPUT/OUTPUT/INVESTMENT inventory wiring beyond whatM3's existing fixtures exercise, if that turns out to need its own slice.
ClanState, or any genericWorldState-level wallet/inventory container that bypasses canonical actor
ownership.
REQ-MARKET-005's telemetry-toggle proof itself — that istracked back on
REQ-MARKET-005's ledger row once this lands.Acceptance criteria
CohortState,ProductionUnitStateandStateState(at minimum, whicheverof these the M3 Phase-8 fixtures actually require) expose live, mutable
wallet balances and inventory quantities on their own canonically-owned
fields (
wallet/householdInventory,wallet/input|output|investment Inventory,treasury/publicInventoryrespectively);ClanStategainsonly a live treasury, never a physical inventory; no generic
WorldState-level wallet/inventory container is added.
MarketSettlement.executeAllocation(world, ctx, allocation)exists, iscalled by Phase-8 for every realized MAIN-pass
MarketAllocation, mutatesexactly the wallets/inventories/treasury the allocation specifies by
dispatching on
ActorRef+inventoryBucketto each actor's own canonicalfield, and fails (rather than silently inventing a container) when an
actor/bucket combination has no canonical stock endpoint.
tax) and goods conservation (seller inventory decrease = buyer inventory
increase) are proven against the real mutated
WorldState, not atest-local synthetic map.
REQ-MARKET-005's telemetrycollection on/off produces an identical post-settlement
WorldState(wallets/inventories) and ledger — closing the gap Issue REQ-MARKET-005: PR #414 still does not prove canonical-stock neutrality #416/PR REQ-MARKET-005: prove telemetry toggle stock/ledger neutrality #426 could
not close at the pre-wiring boundary.
representation as genesis accounting — the
ActorRefunion insrc/domain/genesisLedger.ts, including itsCOHORTmember — rather thaninventing a parallel endpoint convention. Owner-bound genesis reconciliation
must be able to compare a cohort's opening stock to that cohort's live stock
through one mapping, not two that happen to agree.
(Moved here from Issue REQ-CONFIG-004 / REQ-MARKET-005: cohort opening stocks are mis-owned by Clan in genesis ledger #448 acceptance criterion 8 on 2026-09-14: it is a
constraint on this Issue's implementation and no revision of REQ-CONFIG-004 / REQ-MARKET-005: cohort opening stocks are mis-owned by Clan in genesis ledger #448's branch
can satisfy it. See the ACCEPTOR verdict on PR REQ-CONFIG-004: attribute cohort opening money/goods/population to the cohort, not its Clan #459.)
Verification
npm run typecheck,npm test,npm run build;dotnet build --configuration Release --no-restore,dotnet test --configuration Release --no-build.Simulation invariants (no negative wallet/inventory, money and goods
conservation) must be covered by new tests, not merely asserted.