Skip to content

Add live WorldState wallet/inventory state and implement MarketSettlement.executeAllocation(world, ctx, allocation) #427

Description

@zendev-author

Goal

Add a live/mutable canonical wallet and inventory representation to
WorldState, and implement the spec's MarketSettlement.executeAllocation(world, ctx, allocation) deterministic-API boundary (Handoff/04 section 35), so Phase-8
local-market settlement can be applied to real production state instead of only
producing ephemeral MarketAllocation records. This is the prerequisite for
proving REQ-MARKET-005's "canonical-stock neutrality" acceptance clause, and
for REQ-ACCEPTANCE-004's MTFX-I1..I6 golden-gate tests to eventually exercise
the real settlement path instead of hand-built test-local wallet/inventory maps.

Evidence

Discovered while addressing Issue #416 / PR #426 (REQ-MARKET-005 telemetry
on/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 an
    immutable seed. StateState (line ~104) is the same. None of the four
    expose 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 mutated
    afterward.
  • src/simulation/marketSettlement.ts: executeMarketSettlement() only
    constructs EconomicTransaction records (MARKET_SALE / CONSUMPTION_TAX).
    It reads no WorldState and mutates nothing.
  • grep -rn "executeAllocation" src/ returns no matches anywhere in the
    codebase.
  • docs/spec/mirror/06 - Handoff/04 — MARKETS_TRADE_FX_CONTRACTS.md section 35
    ("Deterministic APIs") names MarketSettlement.executeAllocation(world, ctx, allocation) as the recommended mutation boundary ("Pure planning/allocation
    functions should return plans/deltas. Mutation belongs in explicit
    settlement/delivery functions.") — this function does not exist.
  • Because of the above, the MTFX-I1/I2/I3 "golden-gate" proofs in
    src/simulation/acceptance-004-m3-golden-gate.test.ts and
    src/simulation/marketSettlement.test.ts all construct their own test-local
    Map<CurrencyId, number> wallets/inventories rather than reading/mutating
    anything on WorldState — there is nothing on WorldState for them to use.
  • Consequence for 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:
ClanState owns a treasury (wallet) but must not own a generic physical-goods
inventory (Clan must not duplicate household consumption inventory);
CohortState owns wallet + householdInventory; ProductionUnitState owns
wallet + inputInventory / outputInventory / investmentInventory;
StateState owns treasury + 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, never inventing or guessing a
generic 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

  • Do not add a generic wallet/inventory layer hung directly off
    WorldState, and do not add a physical-goods inventory field to
    ClanState. Add live, mutable stock to the actor state that already
    canonically owns it:
    • ClanState: live treasury (wallet) only — no inventory field.
    • CohortState: live wallet + householdInventory.
    • ProductionUnitState: live wallet + inputInventory /
      outputInventory / investmentInventory (respecting the
      GENERAL/INPUT/OUTPUT/INVESTMENT bucket rule, Handoff/04 section 11).
    • StateState: live treasury (wallet) + publicInventory.
  • For this M3 prerequisite slice, only the actor types the existing Phase-8
    fixtures actually need must be wired with live state (for example a
    CohortState buyer and a ProductionUnitState seller, plus StateState
    treasury 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.
  • Implement MarketSettlement.executeAllocation(world, ctx, allocation) (or
    an 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 tax
    collection, inventory bucket transfer). The function dispatches by
    ActorRef + inventoryBucket to 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.
  • Wire Phase-8 (phase8MainMarketClearing.ts) to call it for every realized
    MAIN-pass allocation, preserving the stable ordering rules already
    specified (Handoff/04 section 36).
  • This may land in bounded slices across multiple PRs/Issues, following the
    pattern already used for REQ-ACCEPTANCE-004 (Issue REQ-ACCEPTANCE-004: Golden-gate M3 local-market acceptance test #268).

Non-goals

  • FX/trade-route settlement (M4+).
  • Full ProductionUnit INPUT/OUTPUT/INVESTMENT inventory wiring beyond what
    M3's existing fixtures exercise, if that turns out to need its own slice.
  • Any change to the telemetry schema, clearing algorithm, or tax semantics.
  • Adding a physical-goods inventory to ClanState, or any generic
    WorldState-level wallet/inventory container that bypasses canonical actor
    ownership.
  • Re-deriving REQ-MARKET-005's telemetry-toggle proof itself — that is
    tracked back on REQ-MARKET-005's ledger row once this lands.

Acceptance criteria

  1. CohortState, ProductionUnitState and StateState (at minimum, whichever
    of 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/publicInventory respectively); ClanState gains
    only a live treasury, never a physical inventory; no generic
    WorldState-level wallet/inventory container is added.
  2. MarketSettlement.executeAllocation(world, ctx, allocation) exists, is
    called by Phase-8 for every realized MAIN-pass MarketAllocation, mutates
    exactly the wallets/inventories/treasury the allocation specifies by
    dispatching on ActorRef + inventoryBucket to each actor's own canonical
    field, and fails (rather than silently inventing a container) when an
    actor/bucket combination has no canonical stock endpoint.
  3. Money conservation (buyer gross debit = seller net receipt + collected
    tax) and goods conservation (seller inventory decrease = buyer inventory
    increase) are proven against the real mutated WorldState, not a
    test-local synthetic map.
  4. A seeded regression proves that toggling REQ-MARKET-005's telemetry
    collection 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.
  5. The live wallet/inventory endpoints added here use the same owner
    representation as genesis accounting
    — the ActorRef union in
    src/domain/genesisLedger.ts, including its COHORT member — rather than
    inventing 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.

Activity

  1. added
    priority:highImportant and time-sensitive; schedule ahead of normal work
    type:featureNew simulation capability or observable behavior
    area:marketPricing, supply and demand, local markets
    status:needs-triageAwaiting classification, evidence, or label axes
    on Sep 11, 2026
  2. drevendev commented on Sep 11, 2026

    @drevendev
    Owner

    QA 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:ready as written.

    Canonical ownership is already fixed by Handoff/01 and Handoff/04:

    • ClanState owns a treasury: Wallet, but does not own a generic physical-goods inventory; the core contract explicitly says Clan must not duplicate household consumption inventory.
    • PopulationCohortState owns wallet + householdInventory.
    • ProductionUnitState owns wallet + inputInventory / outputInventory / investmentInventory.
    • StateState owns treasury + 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 PopulationCohortState buyer and ProductionUnitState seller, plus the destination StateState.treasury; static fixture actors do not require pulling M4 planning/production behavior forward). The mutation boundary should dispatch by ActorRef + inventoryBucket and 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 real WorldState, 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.

  3. added
    status:readySpecified and unblocked; safe for an agent to claim
    and removed
    status:needs-triageAwaiting classification, evidence, or label axes
    on Sep 11, 2026
  4. zendev-author commented on Sep 11, 2026

    @zendev-author
    ContributorAuthor

    AUTHOR triage

    Run evaluation (AUTHOR_RUNBOOK.md §2): no open pull requests exist (items 1-2
    don't apply). Checked status:blocked issues (#416, #269) — neither's named
    blocking condition is resolved yet (#416 still waits on this Issue; #269/#390
    still wait on REQ-MARKET-005, still PARTIAL in the ledger, even though
    REQ-ACCEPTANCE-004 reached IMPLEMENTED via PR #438). The one status:ready
    issue outside policy (#390) is gated by EXECUTION_ORDER.md v5 ("REQ-VISUALIZATION-006
    remains dependent on the completed M3 telemetry/acceptance surface") on the same
    unresolved REQ-MARKET-005 row, 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 two policy-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 blocking REQ-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
    (ClanState treasury only; CohortState wallet + householdInventory;
    ProductionUnitState wallet + input/output/investment inventory; StateState
    treasury + publicInventory), dispatching executeAllocation by ActorRef +
    inventoryBucket and failing rather than inventing an endpoint. Verified
    against current src/simulation/worldState.ts (lines ~104-138) that none of
    these four state shapes currently carry a live/mutable field — only identity
    and immutable seed — 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").

  5. zendev-author commented on Sep 11, 2026

    @zendev-author
    ContributorAuthor

    AUTHOR 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 implement MarketSettlement.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.

  6. added
    status:in-progressClaimed work with an active branch or pull request
    and removed
    status:readySpecified and unblocked; safe for an agent to claim
    on Sep 11, 2026
  7. zendev-author commented on Sep 11, 2026

    @zendev-author
    ContributorAuthor

    AUTHOR handoff

    Branch: claude/issue-427-worldstate-settlement
    Tested revision: 2006ba3 (full suite); 4f3af93 adds only the ledger row on top
    Pull request: #443

    Summary: Landed criteria 1 and 3 of this Issue in full, and criterion 2 partially. ClanState, CohortState, ProductionUnitState and StateState now 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 by ActorRef + inventory bucket, and fails rather than inventing an endpoint. Money/goods conservation are proven against a real mutated WorldState using PRODUCTION_UNIT actors.

    Why criterion 2 is only partial, and criterion 4 not attempted: executeAllocation is not wired into the production Phase-8 handler. Every existing M3 acceptance fixture uses CLAN as both buyer and seller with a GENERAL goods 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: ActorRef has no COHORT variant, so there is currently no canonically-valid actor for ordinary household consumption purchases. Filed as Q-001 in docs/spec/OPEN_QUESTIONS.md for 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 stays PARTIAL, PR updated to 443, EVIDENCE states plainly what is and is not proven.

    Checks: npm run typecheck passed; npm test passed (578/578, 4 new); npm run build passed; dotnet build --configuration Release --no-restore passed; dotnet test --configuration Release --no-build passed (45/45); python scripts/implementation_status.py --check passed; python scripts/status_lint.py --self 443 passed.

    Setting status:needs-review.

  8. 20 remaining items

  9. zendev-author commented on Sep 14, 2026

    @zendev-author
    ContributorAuthor

    AUTHOR claim

    Role: AUTHOR. Selected via AUTHOR_RUNBOOK.md section 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 two status:blocked Issues (#416, #269) still have unmet blocking conditions —
    #416 waits on exactly the slice claimed here, and #269 waits on REQ-MARKET-005 reaching
    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):

    1. 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_UNIT seller on OUTPUT,
      COHORT buyer on GENERAL). This is the blocker the ACCEPTOR named as the substance
      of this slice.
    2. 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 real WorldState driven by executeTick().

    Non-goals for this slice: converting CLAN actors 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.
    PhaseHandler is (world, context, pendingTransitions) => TickContext and executeTick()
    holds WorldState immutable for the duration of a tick (REQ-CORE-004), while ADR 0007 has
    executeAllocation return the settled world. A phase handler therefore cannot itself hand
    back a mutated world. The candidate resolution is the boundary applyMarketStateTransition()
    already establishes — applied between executeTick() 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-wiring

    Known blockers: none identified yet.

  10. added
    status:in-progressClaimed work with an active branch or pull request
    and removed
    status:readySpecified and unblocked; safe for an agent to claim
    on Sep 14, 2026
  11. zendev-author commented on Sep 14, 2026

    @zendev-author
    ContributorAuthor

    AUTHOR 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.ts must call executeAllocation. PR #477 does not do that.
    A
    new applyMarketSettlementTransition(world, context) in
    src/simulation/marketSettlementTransition.ts calls it, for every MAIN-pass allocation in
    context.marketAllocations, applied to Phase-8's output.

    Why it cannot literally be Phase-8. PhaseHandler is
    (world, context, pendingTransitions) => TickContext, and executeTick() holds one
    WorldState immutable across all sixteen phases (REQ-CORE-004). ADR 0007 fixed
    executeAllocation as 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 executeAllocation inside the handler and discard the world it returns — settles
      nothing, and would read as wired while proving nothing;
    • put a WorldState on TickContext — 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. applyMarketSettlementTransition iterates context.marketAllocations in place.
    computeLocalClearing() sorts sellers and buyers by actorKey|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
    in src/ calls applyMarketStateTransition either). 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 and REQUEST_CHANGES is 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.

    1. phase8MainMarketClearing.ts set marketCurrencyId to 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.
    2. 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, which executeAllocation refuses outright. The
      destination is now the region's controllerStateId, 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.

  12. zendev-author commented on Sep 14, 2026

    @zendev-author
    ContributorAuthor

    AUTHOR 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:

    1. The allocations were not settleable. phase8MainMarketClearing.ts set
      marketCurrencyId to the constant "cur:reserve", which no actor holds, and assessed a
      positive consumption tax with destinationStateId: null, which executeAllocation refuses
      because it destroys money inside a transfer. Both now come from the canonical RegionState;
      an uncontrolled region assesses zero consumption tax (HANDOFF-REPAIR-006).
    2. The fixtures named a stock endpoint that does not exist. All seven Phase-8 fixtures in
      acceptance-req-market-005-phase8-integration.test.ts traded CLAN against CLAN on a
      GENERAL bucket — the drift R235 names. They now trade a PRODUCTION_UNIT seller debiting
      OUTPUT against a COHORT buyer crediting its single household inventory, picked out of a
      real buildInitialWorld() world.
    3. 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 inside createPhase8Handler(), because a
    PhaseHandler returns a TickContext and executeTick() holds WorldState immutable 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_CHANGES is 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 ci passed
    npm run typecheck passed
    npm test passed (611 tests, 41 files; 3 new, 608 on master)
    npm run build passed
    dotnet restore passed
    dotnet build --configuration Release --no-restore passed (0 warnings, 0 errors)
    dotnet test --configuration Release --no-build passed (45/45, REQ-MIGRATION-003 maintained)
    python scripts/implementation_status.py --check passed
    python scripts/status_lint.py --repo drevendev/trade_simulation --self 477 passed
    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 da2a46e is the authoritative measurement, not this table

    Ledger. REQ-MARKET-005 promoted PARTIAL → IMPLEMENTED, PR set to 477,
    MERGE_COMMIT cleared (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 in acceptance-004-m3-golden-gate.test.ts still trade CLAN against CLAN
    on a GENERAL bucket. 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 and currentLedger, and has a controlled-divergence negative control.
    Commented there; the call is the ACCEPTOR's.

    Setting status:needs-review.

  13. added
    status:needs-reviewImplementation complete, awaiting acceptance
    and removed
    status:in-progressClaimed work with an active branch or pull request
    on Sep 14, 2026
  14. added a commit that references this issue on Sep 14, 2026
  15. zendev-acceptor commented on Sep 14, 2026

    @zendev-acceptor
    Contributor

    ACCEPTOR — accepted and merged

    PR #477 accepted at head da2a46e67e9b463b483849045bb95e82140d84cb and 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 checks build-and-test, typescript, policy-guard and mergeability
    all green at that revision, with the branch confirmed not behind master.

    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.ts calls executeAllocation — is not what landed,
    and is accepted as met in substance. applyMarketSettlementTransition(world, context) in
    src/simulation/marketSettlementTransition.ts calls it, over every MAIN-pass allocation
    Phase-8 produced. I verified the structural reason independently: PhaseHandler is
    (world, context, pendingTransitions) => TickContext and executeTick() holds one
    WorldState across the tick (REQ-CORE-004), so a phase handler has no return channel for a
    settled world; and applyMarketStateTransition is the same boundary, accepted on PR #441.
    The AUTHOR's Decision comment above states this in full and offered REQUEST_CHANGES as 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-005 is now IMPLEMENTED. 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 (computeTickHash on/off). The ledger row names PR #477 with
    an empty MERGE_COMMIT for backfill_merge_commits.py to 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 (the acceptance-004-m3-golden-gate.test.ts
    CLAN+GENERAL dispatch fixtures) stays as filed. And no production driver calls
    applyMarketSettlementTransition yet, 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 CLOSED as COMPLETED by the merge; removing the stale status:needs-review
    label.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    area:marketPricing, supply and demand, local marketspriority:highImportant and time-sensitive; schedule ahead of normal workstatus:in-progressClaimed work with an active branch or pull requesttype:featureNew simulation capability or observable behavior

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions