{"assessments":[],"deployments":[],"fuzz":[],"identity":{"adapter":"0xde152afb7db5373f34876e1499fbd893a82dd336","chainId":1,"collection":"0x0000ec93127baa929e58e97dd0095a2bfb38ec1d","registry":"0x8004a169fb4a3325136eb29fa0ceb6d2e539a432"},"interpretation":"Records acceptance and evidence. Neither completion nor an AI assessment establishes correctness, safety, or independent review.","jobId":"17fb18b0-3f81-4545-aabe-fe3316b73e64","kind":"audit","nodes":[{"acceptedSubmissionHash":"82d32cb1988164a9ad063de57f84d617229ff2042e93e2c821bba02d78d7c12a","dependsOn":[],"execution":{"network":false,"profile":"foundry","requires":[],"skillHash":"e5ac2cb1fd91a56aa40b16487fc230c48de0d317c8266140331dd3219bb40a85","skillId":"audit-specialist","tools":[]},"key":"audit_economics","kind":"code","role":"review","skillHash":"e5ac2cb1fd91a56aa40b16487fc230c48de0d317c8266140331dd3219bb40a85","skillId":"audit-specialist","state":"accepted"},{"acceptedSubmissionHash":"f9d69239cf2ee0df915a60d1f8c83d6f43e8f31c2776c3aac9b4e6af338d40e1","dependsOn":[],"execution":{"network":false,"profile":"foundry","requires":[],"skillHash":"e5ac2cb1fd91a56aa40b16487fc230c48de0d317c8266140331dd3219bb40a85","skillId":"audit-specialist","tools":[]},"key":"audit_flow","kind":"code","role":"review","skillHash":"e5ac2cb1fd91a56aa40b16487fc230c48de0d317c8266140331dd3219bb40a85","skillId":"audit-specialist","state":"accepted"},{"acceptedSubmissionHash":"47a6a2add16ca9bc3fb1a17095355c0ce86bfaeb29bc9d176e833f9a443cf1e1","dependsOn":["audit_math","audit_permissions","audit_economics","audit_flow"],"execution":{"network":false,"profile":"foundry","requires":[],"skillHash":"af3aa01159bbf354f621cafa5c0006f6169e0938a8b015be508b7377dbf165bc","skillId":"audit-judge","tools":[]},"key":"audit_judge","kind":"code","role":"review","skillHash":"af3aa01159bbf354f621cafa5c0006f6169e0938a8b015be508b7377dbf165bc","skillId":"audit-judge","state":"accepted"},{"acceptedSubmissionHash":"4283edaeb3f121ddd3e2313e7789901c40d32102b65c913af2f7f861b36575e4","dependsOn":[],"execution":{"network":false,"profile":"foundry","requires":[],"skillHash":"e5ac2cb1fd91a56aa40b16487fc230c48de0d317c8266140331dd3219bb40a85","skillId":"audit-specialist","tools":[]},"key":"audit_math","kind":"code","role":"review","skillHash":"e5ac2cb1fd91a56aa40b16487fc230c48de0d317c8266140331dd3219bb40a85","skillId":"audit-specialist","state":"accepted"},{"acceptedSubmissionHash":"30639f949de0e4ce17860fa5fa3a17e9d7f1024e9aae0cfc051a652d14c00152","dependsOn":[],"execution":{"network":false,"profile":"foundry","requires":[],"skillHash":"e5ac2cb1fd91a56aa40b16487fc230c48de0d317c8266140331dd3219bb40a85","skillId":"audit-specialist","tools":[]},"key":"audit_permissions","kind":"code","role":"review","skillHash":"e5ac2cb1fd91a56aa40b16487fc230c48de0d317c8266140331dd3219bb40a85","skillId":"audit-specialist","state":"accepted"}],"objective":"Audit BOTH Solidity and TypeScript/D1/client at the exact immutable containing Git commit 5a9e8f74a4b8f6c9cedf7e661d3c683c5af311c0 (World source 6732aa3f75971a3ecb371dfaf9a7adcff77b7cb2). Read AUDIT_OBJECTIVE.en.txt, REQUIREMENTS.md, SCOPE.md and AUDIT_FINDINGS.md for the complete requirements and previous findings; do not limit this to Solidity or the new files. Prior accepted job592da0ae-6f65-4f4d-9636-b10cb6d9d71b/public143ac7e0 did not review this source. Source review/local tests only: no signing, payments, deployment, service reset or automatic fixes.\n\nRules remain chain1 canonical EMBEO0x2b82cdeb8477415d541799abb0cc48044c8ea5f3,18decimals,100000EMBEO/whole share, separate deposits,1wallet-bound nontransferable/nontradeable GOLD per share retained throughout complete UTC[00:00,next00:00), after finalized indexing. Midday deposit first qualifies next full day. Withdraw anytime; only withdrawn shares lose that UTC day, survivors/prior GOLD stay; no inherited deposit eligibility, conversion or cashout. Principal is separate from immutable treasury0x1C651928150DADDDA9C2C040a9D4901d862f8eC4. Editable names and old SIWE grant no money authority.\n\nPrevious fixes still require review: unsafe reward config repins cannot reissue historical GOLD; matching signed completed GOLD retries after expiry return paid once with all original authority/time bounds, while expired NEW spending fails; exact payer/order/amount payment replay key and paid(address,bytes32,uint256) getter; wrong manual transfers remain irreversible without entitlement/refund; expired pending quote returns ORDER_EXPIRED with explicit new-request renewal, immutable old/late paid history retained.\n\nHistorical GOLD recovery and payment reconciliation are now included: migrations0013, server/economy-recovery.ts, server/economy-reconciliation.ts, the local-only scripts/economy-recovery.mjs, the full RECONCILIATION.md runbook and executable regressions. Treat this new scope as needing independent review, not as covered by the old accepted Audit or the previous b46 local tests.\n\nReview the independent event-derived UTC entitlement model, comparison against EARNED journals plus prior adjustments (never spendable balance), positive-only corrections, overissuance refusal, exact retained old event proofs, same finalized boundary, settled old/new days, all-financial-table SQL snapshot guard, atomic receipt/binding/lineage/import/day closure/adjustments and exact-hash replay. Preserve every original journal and immutable quote; only valid finalized events may complete pending quotes. Exercise concurrent spend/indexer changes, rollback/retry, second historical rebuild and another legitimate vault. Normal settlement must resume after the recovered horizon and never reissue historical days. Retired configs must fail final-write/indexer/page guards; independent onchain principal withdrawal remains unchanged.\n\nThe controlled rebuilt input must originate from the existing finalized receipt-verifying indexer, not a player event upload. Local copies are not independent chain consensus proof. The internal module and offline SQLite tool add no HTTP route, privileged wallet, arbitrary credit parameter, RPC credential, remote D1 command, refund or automatic deployment. Review read-only input hashes and fresh side-by-side output, the private migration batch and the explicit reviewed-plan boundary. Actual production batch execution and config switch require separate review/authorization. Same chain/token/vault/paymentContract/treasury only; earlier deploymentBlock only. Payment-contract replacement is rejected, not silently migrated. Engineering caps:5000events,366days,16ancestors,64statements,512KiB per bound snapshot/value,4MiB snapshots total. Larger plans fail before writes; local throughput does not close production capacity.\n\nReview immutable payment facts/classification and chain/paymentContract/tx/log dedup in the SAME event/cursor/fulfillment transaction. Incomplete staging has no case/economic effect. Wrong amount/payer/config/currency, unknown orders and extra payments give no extra entitlement. A correct later exact payment fulfills once and stays linked to the original exception, not falsely marked refunded. Legacy evidence is rebuilt256/page with bounded operator-copy tooling. Disposition only appends a note or acknowledged-no-entitlement; it cannot move money, change an order, claim a refund or confer treasury control. Players see only their own latest50cases; another user's quote/result is withheld and GOLD prices are not displayed as EMBEO. Review strict client types and actual pane correction/case labels, unknown-send protections and original auth authority.\n\nRetain full original scope: custody/reentrancy/exact token deltas and code/supply/immutable binding; official pinned compiler and artifact projection; contiguous finalized headers/receipts/logs, staging/cursor atomicity, UTC bigint pages and halt; same-origin/session/final-write authority, canonical GOLD personal_sign and atomic debit/journal/fulfillment; opaque immutable orders and late same-slot EMBEO; account/chain/provider/session guards at every await, exact approval and separate confirmation, persisted nonce intent/WebLocks, no unknown-send resend, finalized replacement proof, strict JSON types and clock lowerbounds. See full objective for every negative control and stated trust limit.\n\nReport severity, exact paths/lines, prerequisites, reproducible adversarial/negative controls and scope gaps. The explicit sixteen-file financial runner uses pinned Node24/native EDR, not the old Node22 partial reproduction. Distinguish the complete private World gate from this selected partial public source. Both-language coverage, accepted exact-source report, real wallet extension nonce/replacement, actual Workers CPU/D1/large receipt capacity, provider completeness, mainnet token plus deployed new-contract runtime/immutables, catalogue and production migration/config/postflight remain independent release gates. No new source publication or live deployment is implied by preparing this packet.","parentJobId":null,"planHash":"79f90350a9bb0e3f8a727419666b59794f5e50c25c4c292f3f8f039419eb6c71","previousHash":"0000000000000000000000000000000000000000000000000000000000000000","projectId":"17fb18b0-3f81-4545-aabe-fe3316b73e64","publication":{"commit":null,"deliveredAt":null,"repoUrl":null},"receiptIdentity":{"adapter":"0xde152afb7db5373f34876e1499fbd893a82dd336","chainId":1,"collection":"0x0000ec93127baa929e58e97dd0095a2bfb38ec1d","registry":"0x8004a169fb4a3325136eb29fa0ceb6d2e539a432"},"registry":"0xb6d0a187b050fa5bb0b87033a203f37becf4a775","research":[],"schema":"identitymd-work-v1","signals":[{"agentId":"52161","feedbackHash":"bad32935b2739515962b8ffd281163458798b2b8de86098fe120fcbb7c7b09b1","nodeKey":"audit_economics","submissionHash":"82d32cb1988164a9ad063de57f84d617229ff2042e93e2c821bba02d78d7c12a","tag1":"review:submission","tag2":"acceptance-v2","value":1},{"agentId":"52255","feedbackHash":"6b17102329063763adc7aadc4a007ec9b86ddb907432c08a81327b5048f9f130","nodeKey":"audit_flow","submissionHash":"f9d69239cf2ee0df915a60d1f8c83d6f43e8f31c2776c3aac9b4e6af338d40e1","tag1":"review:submission","tag2":"acceptance-v2","value":1},{"agentId":"52011","feedbackHash":"a2688f0dc986b49a759404eef6fa298e12c809219299ea061206e417da8a824e","nodeKey":"audit_judge","submissionHash":"47a6a2add16ca9bc3fb1a17095355c0ce86bfaeb29bc9d176e833f9a443cf1e1","tag1":"review:submission","tag2":"acceptance-v2","value":1},{"agentId":"52660","feedbackHash":"5175fabb4ed7eed99c20f92af7d5a89fe60b95ae23605b95b71d09139aa9f22f","nodeKey":"audit_math","submissionHash":"4283edaeb3f121ddd3e2313e7789901c40d32102b65c913af2f7f861b36575e4","tag1":"review:submission","tag2":"acceptance-v2","value":1},{"agentId":"51317","feedbackHash":"2e50cb0d0e620c6362270a7e69557cf56276ba120c2fe6a402842340edff2d84","nodeKey":"audit_permissions","submissionHash":"30639f949de0e4ce17860fa5fa3a17e9d7f1024e9aae0cfc051a652d14c00152","tag1":"review:submission","tag2":"acceptance-v2","value":1}],"site":null,"snapshotHash":"7d3f95b90d85ae46f57210c998701a093ffed3d5c8e23ad0e833b8b119a7a3a2","state":"completed","submissions":[{"artifacts":[],"attempt":1,"bundleHash":null,"device":"0476c44a80aa9574","findings":[{"citation":"resolved","description":"The 0010 trigger economy_event_withdraw (migrations/0010_world_economy.sql:79-84) RAISEs ECONOMY_INVALID_WITHDRAWAL when a withdraw event names a position the indexer has no row for. A position staked in a block below config.deploymentBlock is exactly such a row (this is the very situation the new 0013 recovery scope exists for). When its owner later calls EmbeoStakingVault.withdraw (which succeeds on-chain because the contract itself checks ownership), the finalized Withdrawn log is decoded and handed to ingestEconomyEvents, the whole block batch aborts, the catch at line 69 rethrows the generic SQLite error, and synchronize() rejects. Nothing calls haltEconomyChain, so economy_chain_state.halted_reason stays NULL and every later cron run fails identically on the same block: a permanent, silent cursor stall. Asymmetry: an inconsistency the design treats as a durable halt (changed finalized hash) is surfaced to operators and players through chain.haltedReason, but this one is not; the pane shows only an ageing 'Chain data through' value, canSpend stays true, and players keep being quoted EMBEO orders that can never be fulfilled because no later block is ever indexed. Concretely, an exact-amount Paid event in the stalled block (or any later block) never completes its pending quote and never produces an economy_payment_cases row, so the 'immutable payment facts' guarantee is also broken for that payer. Staging (economy-staging.ts:54-55 INSERT..SELECT from staged rows) fires the same trigger and stalls the same way. Prerequisite: operator pinned deploymentBlock above the real first stake, then any such early staker withdraws (or partially withdraws) after the pin. Fix: in ingestEconomyEvents/stageEconomyBlock, detect ECONOMY_INVALID_WITHDRAWAL and call haltEconomyChain(db,config,'UNKNOWN_POSITION_WITHDRAWAL') (durable, visible, recoverable via the 0013 path) rather than rethrowing a generic error; or make the trigger tolerant of a withdraw for an unknown position by recording the event without touching positions, so the block, its cursor and its payment fulfillment still commit.","line":69,"path":"world-source/server/economy-ledger.ts","reproduction":"Node 24, world-source/: openD1() with all migrations; config deploymentBlock='100'. ingest {fromBlock:'100',toBlock:'101',events:[stake(position 2, block 100, T)]}. createEconomyOrder(currency EMBEO) -> order O pending. ingest {fromBlock:'102',toBlock:'102',toBlockHash:H(102),throughTimestamp:T+DAY+1000,events:[withdraw(position 1 (never staked within range), block 102, 1 share), payment(O.id, exact amount, block 102, logIndex 1)]}. Expected: block 102 indexes (position 1 is a legitimate on-chain position) OR the chain is halted with a durable reason. Actual: ingestEconomyEvents rejects with ECONOMY_INVALID_WITHDRAWAL on every attempt, economyChainState = {lastBlock:'101', haltedReason:null}, order O stays 'pending', economyWalletSnapshot(...).paymentCases is empty. Probe output: 'state after { lastBlock: 101, haltedReason: null } / order status pending cases 0'.","severity":"medium","snippet":"  catch(error){if(await bind(db,'SELECT id FROM economy_ingestions WHERE id=?',ingestionId).first())return {replayed:true,eventCount:events.length};throw error;}","title":"Valid finalized withdrawal of a position staked before the pinned deploymentBlock stalls ingestion forever with no halt reason; the same block's exact EMBEO payment never fulfills and no payment case "},{"citation":"resolved","description":"economy_payment_cases is deduplicated on (chain_id,payment_contract,tx_hash,log_index) and every row is immutable (0013 triggers economy_payment_case_no_change/no_delete). defineEconomyConfig allows two live configs to share one paymentContract while having different stakingContracts, and the shipped regression ('separate deployed vaults can independently earn into the same wallet', and the recovery test's `separate` config) uses exactly that shape. Both indexers filter eth_getLogs on their own staking+payment addresses, so both ingest the same Paid log. Whichever config's ingestion commits first writes the only case row. If the vault that does NOT own the order (no lineage link) ingests the block first, the CASE expression at line 17 classifies the payment 'wrong-config'. When the owning config then ingests the block, its UPDATE economy_orders fulfills the quote correctly, but the NOT EXISTS at line 22 skips the evidence row, so the sole, immutable record of this payment says 'wrong-config'. economyPaymentCases filters classification<>'matched', so the player sees their fully successful payment listed under 'Payment reconciliation' with the pane text 'These payments grant no additional service... Correct payment confirmed; original exception retained · Needs review · Disposition pending' forever; reconcileEconomyPayments cannot correct it (NOT EXISTS again) and recordEconomyPaymentDisposition can only append. No funds are affected. Fix: scope the dedup to the order's config lineage (or dedup per config_id and let economyPaymentCases prefer the owning config's row), or classify in the owning config's ingestion by re-evaluating when the order is fulfilled by this event (paid_event_id=e.id => 'matched' should win over an earlier foreign 'wrong-config' row).","line":22,"path":"world-source/server/economy-reconciliation.ts","reproduction":"Node 24, world-source/: openD1(); A=cfg(staking 0x11..), C=cfg({stakingContract:'0x'+'4'.repeat(40)}) (same paymentContract 0x22..). Ingest blocks 100-101 for both. createEconomyOrder(db,A,EMBEO) -> O. pay=payment(O.id, exact amount, block 102). ingestEconomyEvents(db,C,{102..102,events:[pay]}) then ingestEconomyEvents(db,A,{102..102,events:[pay]}). Expected: O paid and either no visible case or a 'matched' case. Actual: O.status='paid' but economyWalletSnapshot(db,A,W).paymentCases = [{classification:'wrong-config', expectedAmount: exact amount, correctPaymentEvent: A's event id, disposition:'unreviewed'}]; SELECT from economy_payment_cases shows a single row with config_id=C and classification 'wrong-config' that can never be changed.","severity":"low","snippet":" AND NOT EXISTS(SELECT 1 FROM economy_payment_cases p WHERE p.chain_id=r.chain_id AND p.payment_contract=r.payment_contract AND p.tx_hash=e.tx_hash AND p.log_index=e.log_index)","title":"Payment-case dedup across configs sharing one payment contract lets the other vault's indexer permanently classify a correct, fulfilled payment as 'wrong-config'"}],"hash":"30639f949de0e4ce17860fa5fa3a17e9d7f1024e9aae0cfc051a652d14c00152","nodeId":"2f80bbdb-5de6-442c-9c02-06b9198bc915","outcome":"completed","summary":"The review is complete and the findings file holds the two substantiated defects.\n\n**Scope covered.** I read both languages at the pinned commit: the three Solidity contracts, the full server economy boundary (`economy.ts`, ledger, staging, RPC indexer, config, the new `economy-recovery.ts` and `economy-reconciliation.ts`, migrations 0010 to 0013, the offline recovery script), the SIWE session and account routes in `auth.ts`, and the browser side (`economyClient.ts`, `economyWallet.ts`, `EconomyPane.tsx`, `wallet.ts`). I ran my own probes plus the shipped reconciliation, audit-regression, ledger and API suites on pinned Node 24 against the real migrations. All 49 shipped cases pass. No Solidity defect was found, so no Foundry proof was written. The tree is unchanged apart from the findings file.\n\n**Findings recorded in `.imd-findings.json`:**\n\n- **Medium, `economy-ledger.ts:69`.** A finalized, on-chain-valid withdrawal of a position staked before the pinned deploymentBlock trips the 0010 withdraw trigger, the block batch aborts, and the generic error is rethrown instead of a durable halt. The cursor stalls silently forever with `haltedReason` null, the pane keeps quoting EMBEO orders, and an exact payment in that or any later block never fulfills and never gets a payment-case row. Reproduced: cursor stays at block 101, order stays pending, zero cases. Staging folds hit the same trigger. Prerequisite is a deploymentBlock pinned above the first real stake, which is the exact state the new recovery scope exists for.\n\n- **Low, `economy-reconciliation.ts:22`.** Payment cases dedup on chain, payment contract, tx and log across configs. Two configs may share one payment contract, as the shipped regressions do. If the vault that does not own the order indexes the block first, it writes an immutable `wrong-config` case, and the owning config's later correct fulfillment cannot replace it. The player then sees a successful payment listed permanently under reconciliation as needing review. No money moves.\n\n**Confirmed sound in the assigned areas, for the record.** Config lineage retires old configs at order, challenge, debit, ingestion, staging and settlement guards. Recovery compares event-derived entitlement to earned journals plus prior adjustments only, refuses overissuance, requires exact old proofs and the same finalized boundary, and the all-table snapshot guard runs inside the batch. Disposition is append-only with no money authority. The money routes require same origin, a live EOA ECDSA session re-checked at the final SQL write, and closed JSON schemas. The client persists a nonce-bound intent under a Web Lock before every prompt and treats a changed payload as a replacement, not success.\n\n**Release gates this review cannot close.** Mainnet EMBEO runtime and supply readback, deployed contract immutables, real wallet extension behaviour, Workers CPU and D1 capacity, provider log completeness, the production catalogue and the production migration batch remain outside this source-only review.","treeHash":null,"usage":{"cachedInputTokens":1658104,"inputTokens":354,"model":"claude-fable-5-1","outputTokens":40621,"runtime":"claude","turns":43,"wallClockMs":624650}},{"artifacts":[],"attempt":1,"bundleHash":null,"device":"1ec9f666356a7ad7","findings":[{"citation":"resolved","description":"Both arms of the conditional assign the same value, so the expression is equivalent to previousLog=e.logIndex. The apparent intent was to reset the per-block log cursor when the block number changes. No money or ordering defect follows: the only consumer is the guard on line 45, which compares logIndex against previousLog solely under block===previousBlock, so a stale value carried across a block boundary is never read. This is a correctness-of-intent / readability note under the Boundary guide (early-return and cursor-reset paths), not a reachable failure. No failing input exists; the batch [{block 100, logIndex 7},{block 101, logIndex 0}] is accepted today and is also accepted with the intended reset, and [{block 100, logIndex 7},{block 100, logIndex 7}] is rejected in both versions.","line":47,"path":"world-source/server/economy-ledger.ts","reproduction":"Input: ingestEconomyEvents with events ordered (block 100, logIndex 7) then (block 101, logIndex 0). Expected: accepted (new block may restart log indices). Actual: accepted. Input: (block 100, logIndex 7) then (block 100, logIndex 7). Expected: EVENT_ORDER_OR_RANGE. Actual: EVENT_ORDER_OR_RANGE. The ternary therefore has no observable effect; simplify to previousLog=e.logIndex or reset to -1 on a new block to match the evident intent.","severity":"info","snippet":"    previousLog=block===previousBlock?e.logIndex:e.logIndex;previousBlock=block;previousTime=e.timestamp;previousHash=e.blockHash;","title":"Dead ternary in ingestion ordering loop: previousLog is never reset at a block boundary"},{"citation":"resolved","description":"horizon is defined on line 75 as Math.floor(clock(a.through_ms)/DAY)*DAY, so through_ms < horizon+DAY always holds. Every rebuilt event is additionally required on line 87 to satisfy clock(e.at_ms)<=clock(b.through_ms) with b.through_ms===a.through_ms (line 73), and every old event is a byte-identical proof of a rebuilt event (lines 93-95). Hence time<=through_ms<horizon+DAY for every event that reaches replay(), and the upper bound branch is dead. The effective horizon bound that matters, events after horizon only affecting the unsettled partial day, is enforced by the day loop on lines 58-61 (day<horizon). This is a Numerical-Gap note (a boundary check written against the wrong edge: it should either be time>through_ms, which is already enforced upstream, or be removed). No overissuance or refusal results from it; the differential fuzz described in the reproduction never triggered it.","line":43,"path":"world-source/server/economy-recovery.ts","reproduction":"State: original DB through_ms=T (any value), rebuilt DB through_ms=T. Any event with at_ms in (T, horizon+DAY] is already rejected at line 87 (RECOVERY_EVENT_CONFLICT) before replay runs, and any event with at_ms<=T satisfies at_ms<horizon+DAY. Expected: the guard on line 43 contributes a rejection for some input. Actual: no input reaches it with time>horizon+DAY. Verified by a differential fuzz (950 random stake/withdraw histories across 2-6 UTC days, with midnight-exact deposits, midnight-exact and partial withdrawals, same-block multi-events and equal block timestamps) in which settleEconomy journals, an independent brute-force end-of-day-balance model, and prepareEconomyRecovery adjustments (expected empty for an identical rebuild with an earlier deploymentBlock) all agreed; the dead branch was never the deciding check.","severity":"info","snippet":"  if(time<priorTime||time>horizon+DAY||seen.has(identity))return fail('RECOVERY_EVENT_CONFLICT');priorTime=time;seen.add(identity);","title":"Unreachable time guard in recovery replay: time>horizon+DAY can never be true for a validated rebuilt event"}],"hash":"4283edaeb3f121ddd3e2313e7789901c40d32102b65c913af2f7f861b36575e4","nodeId":"16c446e7-e992-4660-83a3-5c7511b497ef","outcome":"completed","summary":"Review complete. The findings file at the repository root holds two informational notes and no defects of severity low or above.\n\n**Outcome**\n\nNo arithmetic, rounding, boundary or numerical-seam defect was found in either language at commit 5a9e8f7. The findings file `.imd-findings.json` contains two informational notes, each a dead or unreachable branch with no failing input:\n\n- `world-source/server/economy-ledger.ts:47` has a ternary whose both arms assign the same value. The ordering guard never reads the stale value across a block boundary, so behaviour is unchanged.\n- `world-source/server/economy-recovery.ts:43` rejects events later than horizon plus one day. Every event reaching that function is already bounded by the stored through-timestamp, so the branch cannot fire.\n\n**What was checked and how**\n\n- **Solidity.** Vault deposit and withdraw multiply shares by the share size under checked arithmetic, settle state before the token call and verify exact balance deltas and unchanged total supply. The payments contract keys replay by payer, order and exact amount. The EMBEO fixture ABI has no burn or mint, so the fixed-supply check cannot be used to freeze principal. Client selectors for paid, pay, positions and SHARE_SIZE match cast output.\n- **Settlement math.** A differential fuzz of about 950 random histories across two to six UTC days, including midnight-exact deposits, midnight-exact and partial withdrawals, same-block multi-events and equal block timestamps, found full agreement between the live settlement journals, an independent brute-force end-of-day model, and the recovery replay run against an identical rebuild with an earlier deployment block. Recovery produced zero adjustments in every case.\n- **Integer boundaries.** GOLD debits at the signed 64-bit maximum succeed exactly and insufficient balances fail with no journal write. Balance overflow and over-int64 journal deltas are rejected by the SQL CHECK constraints rather than wrapping. Wei formatting on the client is exact.\n- **Existing suites.** The settlement, ledger and reconciliation test files pass on Linux Node 24 with the pinned lock.\n\n**Scope gaps to note**\n\n- The contract test runner rewrites tracked artifact files, so it was not executed here. Solidity was reviewed by reading plus selector checks only, and no Foundry proof files were written because no high or critical finding exists.\n- Production D1 and Workers capacity, mainnet token readback, real wallet behaviour and the private full-World gate remain the release gates the objective already lists.","treeHash":null,"usage":{"cachedInputTokens":3550780,"inputTokens":674,"model":"claude-fable-5-1","outputTokens":54065,"runtime":"claude","turns":38,"wallClockMs":810841}},{"artifacts":[],"attempt":1,"bundleHash":null,"device":"a406deaac63a93b0","findings":[{"citation":"resolved","description":"The migration 0010 trigger economy_event_withdraw (world-source/migrations/0010_world_economy.sql:79-84) RAISEs ECONOMY_INVALID_WITHDRAWAL when a withdraw event names a position for which this config has no economy_positions row. A position staked in a block below config.deploymentBlock is exactly such a row, and the new 0013 earlier-deploymentBlock recovery scope exists because that operator state is anticipated. EmbeoStakingVault.withdraw succeeds on-chain (the contract checks ownership itself), the finalized Withdrawn log is decoded by economy-rpc.ts and passed to ingestEconomyEvents, the whole block batch aborts, and the catch at line 69 rethrows the raw SQLite error. Nothing calls haltEconomyChain, so economy_chain_state.halted_reason stays NULL and every later synchronize() run rejects on the same block: a permanent, silent cursor stall. The design surfaces a changed finalized hash as a durable FINALIZED_REORG halt visible through chain.haltedReason, but this inconsistency is not surfaced at all: the pane only shows an ageing 'Chain data through' value, players keep being quoted EMBEO orders, and an exact-amount Paid event in the stalled block (or any later block) never completes its pending quote and never produces an economy_payment_cases row, so the immutable payment-facts guarantee fails for that payer. stageEconomyBlock's INSERT..SELECT fold (economy-staging.ts:54-55) fires the same trigger and stalls the same way. Prerequisite: operator pinned deploymentBlock above the real first stake, then any such early staker withdraws (fully or partially) after the pin. Fix: in ingestEconomyEvents/stageEconomyBlock detect ECONOMY_INVALID_WITHDRAWAL and call haltEconomyChain(db,config,'UNKNOWN_POSITION_WITHDRAWAL') so the state is durable, visible and recoverable via the 0013 path; or make the trigger tolerant of a withdraw for an unknown position (record the event without touching positions) so the block, cursor, payment fulfillment and payment case still commit. Merged from audit_permissions.","line":69,"path":"world-source/server/economy-ledger.ts","reproduction":"Node 24, world-source/ with node_modules installed: openD1() over all migrations, c=defineEconomyConfig({...deploymentBlock:'100'}). ingestEconomyEvents(db,c,{fromBlock:'100',toBlock:'101',toBlockHash:H(101),throughTimestamp:T+DAY,events:[stake(position 2, block 100, T)]}). order=createEconomyOrder(db,c,{currency:'EMBEO',...}). Then twice: ingestEconomyEvents(db,c,{fromBlock:'102',toBlock:'102',toBlockHash:H(102),throughTimestamp:T+DAY+1000,events:[withdraw(position 1, never staked in range, 1 share, block 102, logIndex 0), payment(order.id, exact 1e18, block 102, logIndex 1)]}). Expected: block 102 indexes (position 1 is a legitimate on-chain position) OR economyChainState().haltedReason is a durable code. Actual (observed): both attempts reject with 'Error: ECONOMY_INVALID_WITHDRAWAL'; economyChainState = {lastBlock:'101', haltedReason:null}; economy_orders.status for the order stays 'pending'; economyWalletSnapshot(db,c,W).paymentCases is empty. Probe output: 'attempt 0 error: Error: ECONOMY_INVALID_WITHDRAWAL / attempt 1 error: Error: ECONOMY_INVALID_WITHDRAWAL / state after { lastBlock: 101, haltedReason: null } / order status pending cases 0'.","severity":"medium","snippet":"  catch(error){if(await bind(db,'SELECT id FROM economy_ingestions WHERE id=?',ingestionId).first())return {replayed:true,eventCount:events.length};throw error;}","title":"Finalized withdrawal of a position staked before the pinned deploymentBlock stalls ingestion forever without a durable halt reason; the same block's exact EMBEO payment never fulfills and no payment c"},{"citation":"resolved","description":"economyPaymentEvidenceStatement writes one immutable economy_payment_cases row per physical Paid log (UNIQUE(chain_id,payment_contract,tx_hash,log_index) in migrations/0013_economy_reconciliation.sql:59, plus the NOT EXISTS guard at line 22), but the classification CASE at lines 16-19 is evaluated from the perspective of whichever config's ingestion commits first. defineEconomyConfig (economy-config.ts:33) permits two live configs that differ only in stakingContract while sharing chain/token/paymentContract/treasury; this is exactly the 'another legitimate vault' shape the objective asks to exercise and that tests/economy-reconciliation.test.mjs:43 uses (`separate`). Both indexers filter eth_getLogs on their own staking plus the shared payment address, so both ingest the same Paid log. If the config that does not own the order (no lineage link) ingests the block first, line 17 classifies the payment 'wrong-config' and inserts the only row. When the owning config ingests, its UPDATE economy_orders fulfills the quote correctly, but the evidence INSERT is skipped by the dedup, so the sole immutable record of a fully successful payment says 'wrong-config'. Triggers economy_payment_case_no_change/no_delete make it permanent; reconcileEconomyPayments uses the same NOT EXISTS and cannot repair it; recordEconomyPaymentDisposition can only append and will accept 'acknowledged-no-entitlement' for a payment that did deliver the service. economyPaymentCases returns it (classification<>'matched') with correctPaymentEvent pointing at the owning config's event id, so the payer's pane lists their own fulfilling payment under payment reconciliation as 'Disposition pending', and the operator case export counts it as an unreviewed exception. The outcome depends on cron/isolate ordering between the two configs' sync flights, so the evidence table is nondeterministic. No funds move and fulfillment stays exactly-once; this is an integrity defect of the new immutable reconciliation evidence. Fix: classify against the physical event rather than the ingesting namespace (treat as 'matched' when the order owned by any config is fulfilled by this tx_hash:log_index), or let only the order-owning config or its lineage write the case for an order it owns, or reject configs that share a paymentContract in defineEconomyConfig. Merged from audit_permissions, audit_flow and audit_economics (three reports of the same mechanism at lines 17/22).","line":22,"path":"world-source/server/economy-reconciliation.ts","reproduction":"Node 24, world-source/: openD1(); X=defineEconomyConfig({stakingContract:0x11..11,paymentContract:0x22..22,deploymentBlock:'100',...}); Y=same but stakingContract 0x44..44. order=createEconomyOrder(db,X,{wallet:W,requestId:'order-under-x-0000001',serviceId:'svc',currency:'EMBEO',nowMs:T+DAY}). e={kind:'payment',transactionHash:0x..0bcc,logIndex:0,blockNumber:'102',blockHash:H(102),timestamp:T+DAY+1000,wallet:W,orderId:order.id,amount:'1000000000000000000',token:EMBEO,receiver:TREASURY}. ingestEconomyEvents(db,Y,{fromBlock:'100',toBlock:'102',toBlockHash:H(102),throughTimestamp:T+DAY+1000,events:[e]}) then ingestEconomyEvents(db,X,{same batch}). Expected: order paid under X and no non-matched payment case for W (or a 'matched' row). Actual (observed): economy_orders.status='paid', paid_event_id='<X.id>:0x..0bcc:0', one fulfillment row, but economy_payment_cases holds exactly one row with config_id=<Y.id>, event_id='<Y.id>:0x..0bcc:0', classification='wrong-config'; economyWalletSnapshot(db,X,W).paymentCases=[{classification:'wrong-config',expectedAmount:'1000000000000000000',actualAmount:'1000000000000000000',correctPaymentEvent:'<X.id>:0x..0bcc:0',disposition:'unreviewed'}]; reconcileEconomyPayments(db,X) returns {remaining:0} and leaves the row 'wrong-config'. Reversing the order (X ingests first) yields one 'matched' row and an empty player case list, demonstrating order dependence.","severity":"medium","snippet":" AND NOT EXISTS(SELECT 1 FROM economy_payment_cases p WHERE p.chain_id=r.chain_id AND p.payment_contract=r.payment_contract AND p.tx_hash=e.tx_hash AND p.log_index=e.log_index)","title":"Payment-case classification is per ingesting config but dedup is per payment contract: when another live config sharing the EmbeoPayments contract ingests first, the player's own fulfilling exact paym"},{"citation":"resolved","description":"Both arms of the conditional assign the same value, so the expression equals previousLog=e.logIndex. The evident intent was to reset the per-block log cursor when the block number changes. No ordering or money defect follows: the only consumer is the guard on line 45, which compares logIndex against previousLog only under block===previousBlock, so a stale value carried across a block boundary is never read. Readability/intent note only. Fix: write previousLog=e.logIndex, or reset to -1 on a new block to match the intent. From audit_math; confirmed by reading and by exercising the guard.","line":47,"path":"world-source/server/economy-ledger.ts","reproduction":"Input: ingestEconomyEvents with events (block 100, logIndex 7) then (block 101, logIndex 0). Expected: accepted. Actual: accepted. Input: (block 100, logIndex 7) then (block 100, logIndex 7). Expected: EVENT_ORDER_OR_RANGE. Actual: EVENT_ORDER_OR_RANGE. The ternary has no observable effect in either version.","severity":"info","snippet":"    previousLog=block===previousBlock?e.logIndex:e.logIndex;previousBlock=block;previousTime=e.timestamp;previousHash=e.blockHash;","title":"Dead ternary in ingestion ordering loop: both arms assign e.logIndex, so previousLog is never reset at a block boundary"},{"citation":"resolved","description":"horizon is Math.floor(clock(a.through_ms)/DAY)*DAY (line 75), so through_ms<horizon+DAY always. Every rebuilt event must satisfy clock(e.at_ms)<=clock(b.through_ms) (line 87) with b.through_ms===a.through_ms (line 73), and every old event is a byte-identical proof of a rebuilt event (lines 93-95). Hence time<=through_ms<horizon+DAY for every event that reaches replay(), and the branch is dead. The bound that matters, events after horizon affecting only the unsettled partial day, is enforced by the day loop (lines 58-61, day<horizon). No overissuance or wrong refusal results. Fix: remove the dead comparison or make it time>through_ms with the horizon passed explicitly. From audit_math; confirmed by reading.","line":43,"path":"world-source/server/economy-recovery.ts","reproduction":"State: original DB through_ms=T, rebuilt DB through_ms=T. Any event with at_ms>T is rejected at line 87 (RECOVERY_EVENT_CONFLICT) before replay() runs; any event with at_ms<=T satisfies at_ms<horizon+DAY. Expected: the line 43 upper bound rejects some input. Actual: no input reaches it with time>horizon+DAY. The fixture tests (tests/economy-reconciliation.test.mjs) and the earlier differential fuzz never hit this branch.","severity":"info","snippet":"  if(time<priorTime||time>horizon+DAY||seen.has(identity))return fail('RECOVERY_EVENT_CONFLICT');priorTime=time;seen.add(identity);","title":"Unreachable upper time bound in recovery replay: time>horizon+DAY can never hold for an event that passed the build() validation"}],"hash":"47a6a2add16ca9bc3fb1a17095355c0ce86bfaeb29bc9d176e833f9a443cf1e1","nodeId":"bab633ff-e13e-4470-850d-7387a115763d","outcome":"completed","summary":"The review is complete. `.imd-findings.json` holds four substantiated findings, all reproduced against the code at commit 5a9e8f7 with the project's own SQLite-backed D1 adapter on Node 24.21 after installing the exact world-source lock. The project's reconciliation and ledger suites also pass here, 27 of 27.\n\n**Kept, merged and verified**\n\n- **Medium, unknown-position withdrawal stall** at `world-source/server/economy-ledger.ts:69`. A finalized Withdrawn log for a position staked before the pinned deploymentBlock trips the 0010 trigger, the batch aborts, and nothing calls the halt path. My probe confirmed two identical attempts both reject, the cursor stays at block 101 with a null halt reason, the exact EMBEO payment in the same block stays pending, and no payment case is written. Prerequisite is an operator pin above the first real stake, which is the very state the new recovery scope anticipates.\n- **Medium, shared payment-contract case classification** at `world-source/server/economy-reconciliation.ts:22`. Three specialists reported the same mechanism at lines 17 and 22. I merged them into one finding. The probe shows that when the non-owning vault config ingests first, the order is still paid once, but the sole immutable case row says wrong-config, the payer's snapshot lists their own fulfilling payment as an exception, and reconciliation cannot repair it. Reversing the ingestion order yields a matched row, confirming order dependence. I rated it medium because the evidence is immutable, nondeterministic and can lead an operator to record no-entitlement for a delivered service, though no funds move.\n- **Info, dead ternary** at `economy-ledger.ts:47` and **info, unreachable time bound** at `economy-recovery.ts:43`. Both confirmed by reading and by exercising the guards. Neither has an observable effect.\n\n**Dropped**\n\n- The plan-hash note about any player write between plan and apply aborting the batch. The runbook in RECONCILIATION.md already instructs operators to pause money flows before switching, so it is documented fail-closed design, not a defect.\n\n**Scope notes**\n\n- No Foundry project exists in this packet, and no Solidity finding reached high or critical, so no proof tests were written. The three contracts were read and show no new defect beyond prior accepted reports.\n- Gateway wiring for the cron sync and the number of live configs in production are in the private World source, so the second finding's prerequisite of two configs sharing one payment contract on one database could not be confirmed or ruled out from this tree.","treeHash":null,"usage":{"cachedInputTokens":1101209,"inputTokens":354,"model":"claude-fable-5-1","outputTokens":19257,"runtime":"claude","turns":31,"wallClockMs":329839}},{"artifacts":[],"attempt":1,"bundleHash":null,"device":"ca075d17c94a854b","findings":[{"citation":"resolved","description":"defineEconomyConfig (economy-config.ts:33) only requires the four addresses of ONE config to differ, so two staking configs (different stakingContract, same chain/token/paymentContract/treasury) are legal and the 'another legitimate vault' scenario in the objective is exactly this shape. Both configs index the same Paid log. economyPaymentEvidenceStatement runs in each config's ingestion batch, but the row is keyed physically (UNIQUE(chain_id,payment_contract,tx_hash,log_index) and the NOT EXISTS at line 22), so only the FIRST ingesting config writes the immutable economy_payment_cases row. The classification at line 17 compares the order's config with the ingesting config and labels the payment 'wrong-config' even though the same event, folded by the order's own config, sets the order to paid and creates exactly one fulfilment. Because economy_payment_cases rows are immutable (migration 0013 triggers) and deduplicated, the later correct fold cannot repair the row. Money effect: none (one fulfilment, no extra entitlement, no refund). Evidence effect: the player's own wallet snapshot (economyPaymentCases, classification<>'matched') and EconomyPane.tsx:99 permanently show the fulfilled payment as an exception ('Correct payment confirmed; original exception retained' / 'Disposition pending'), and the operator case export counts it as an unreviewed exception. Fix: classify against the physical event rather than the ingesting namespace, e.g. treat as 'matched' when EXISTS an order o with o.id=e.order_id AND o.wallet=e.wallet AND o.amount=e.amount AND o.currency='EMBEO' AND o.paid_event_id ends with ':'||e.tx_hash||':'||e.log_index (any config namespace), or defer case creation to the config that owns the order (o.config_id or its lineage), and document that two live configs must not share a payment contract.","line":17,"path":"world-source/server/economy-reconciliation.ts","reproduction":"Node (node:sqlite + viem present), from world-source/: import {openD1} from './tests/d1-sqlite.mjs'; a=cfg({stakingContract:'0x11..'}), b=cfg({stakingContract:'0x44..'}) (same paymentContract 0x22.., same token/treasury). order=createEconomyOrder(db,b,{wallet:W,requestId:'order-0000000000002',serviceId:'svc',currency:'EMBEO',nowMs:T+DAY}). e=Paid event {orderId:order.id,wallet:W,amount:order.amount,block 102}. ingestEconomyEvents(db,a,{100..102,[e]}) then ingestEconomyEvents(db,b,{100..102,[e]}). Expected: order paid once AND no exception case for W (or a 'matched' case). Actual (observed): economy_orders.status='paid', paid_event_id='<b.id>:0x..bcc:0' (one fulfilment), but economyPaymentCases(db,W) returns one row {classification:'wrong-config', expectedAmount:'1000000000000000000', correctPaymentEvent:'<b.id>:0x..bcc:0', disposition:'unreviewed'} whose event_id is in a's namespace; it is immutable and never reclassified. Probe script used: /tmp/imdscratch/probe-misc.test.mjs test 2 (logs order status and cases).","severity":"low","snippet":" WHEN o.config_id<>e.config_id AND NOT EXISTS(SELECT 1 FROM economy_config_lineage l WHERE l.config_id=e.config_id AND l.ancestor_id=o.config_id) THEN 'wrong-config'","title":"Payment-case classification is decided by whichever config ingests a shared payment-contract event first; a payment that fulfils the player's own order under a second legitimate vault config is perman"},{"citation":"resolved","description":"This is the intended fail-closed design and RECONCILIATION.md tells the operator to pause new money flows before the switch, so it is recorded as a verified trust/operational limit rather than a defect. All 23 TABLES (including economy_gold_challenges and economy_orders) are serialized into the plan hash and compared again in the first statement of the batch (line 127). Any ordinary player request that writes a row (order quote, GOLD challenge) between `plan` and `stage/apply` changes the hash. The operator must therefore take the Worker offline (or block /api/economy/* ) for the whole plan→review→apply window; a live site makes the plan unreproducible indefinitely. No credential, route, or money authority is affected.","line":162,"path":"world-source/server/economy-recovery.ts","reproduction":"Fixture as in tests/economy-reconciliation.test.mjs fixture(); plan=prepareEconomyRecovery(db,rebuilt,from,to,{nowMs:T+DAY+10}); then createEconomyGoldChallenge(db,from,{wallet:W,orderId:<pending GOLD order>,requestId:'chal-00000000000001',origin:'https://imdember.com',nowMs:T+DAY+2}) (no debit, no journal change); then applyEconomyRecovery(db,rebuilt,from,to,{nowMs:T+DAY+10,expectedPlanHash:plan.hash}). Expected per runbook: rejection. Observed: throws RECOVERY_PLAN_CHANGED, no economy_recoveries row, no writes (probe /tmp/imdscratch/probe-misc.test.mjs test 1).","severity":"info","snippet":" const built=await build(db,rebuilt,from,to,nowMs);if(built.plan.hash!==expectedPlanHash)return fail('RECOVERY_PLAN_CHANGED');","title":"Recovery plan hash and in-batch snapshot guard cover economy_gold_challenges and economy_orders, so any player action between plan and apply (even a GOLD challenge request that debits nothing) aborts "}],"hash":"82d32cb1988164a9ad063de57f84d617229ff2042e93e2c821bba02d78d7c12a","nodeId":"674972d3-05ea-4e20-938e-14e7c3e6fb52","outcome":"completed","summary":"Review complete. The findings file is written at the repository root and both snippets validate against the tree. No repository files were modified.\n\n**Outcome**\n\nOne low and one informational finding. No critical or high defects were substantiated in either language, so no Foundry proof file was warranted.\n\n- **Low, `world-source/server/economy-reconciliation.ts:17`.** When two legitimate configs share the payment contract (only the vault differs, which the config validator allows), the first config to ingest a Paid log writes the immutable case row and labels it `wrong-config`. The order's own config then fulfils the order once, but the dedup key blocks any correction. The player's own pane permanently shows a fulfilled payment as an unreviewed exception. No money or entitlement effect. Reproduced with a two-config ingest probe.\n- **Info, `world-source/server/economy-recovery.ts:162`.** Any player write between plan and apply, including a GOLD challenge request that debits nothing, changes the plan hash and aborts the batch. This is the intended fail-closed design and the runbook says to pause money flows, so it is recorded as a verified operational limit.\n\n**What was checked and held**\n\n- Independent event replay versus live settlement: a randomized probe over 12 histories, 3 wallets, 6 UTC days with midnight-exact and midday deposits and partial and full withdrawals. Recovery credits equalled the rebuilt settlement's earned GOLD minus the old config's issuance for every wallet, later settlement matched exactly, and no historical day was reissued.\n- Comparison against earned journals plus prior adjustments, overissuance refusal, old-proof retention, same finalized boundary, snapshot guard, atomic batch and exact-hash replay, retired-config guards on orders, ingestion, staging and settlement pages, and the binding trigger monotonicity under chained recoveries.\n- Payment facts, dedup, wrong amount, wrong payer, unknown order, late exact payment, disposition immutability, per-player case privacy, and the pane's GOLD versus EMBEO labelling.\n- Solidity custody, exact deltas, reentrancy and replay keys. The mainnet EMBEO token was read on chain: fixed supply of 1e27, 18 decimals, no EIP-1967 slot, and no mint, burn, owner, pause or upgrade selectors, so the fixed-token guard matches the real token.\n- Existing suites: 10 of 12 reconciliation cases and 188 of 193 other financial cases passed in a scratch copy. The 7 failures were all environment only (Node 24 gate in the CLI, missing contracts EDR runtime, Web Locks), not source defects.\n\n**Scope gaps that remain open**\n\n- The CLI tool and the EDR contract suites could not be executed here (Node 22 host, no contracts dependency closure), so the Node 24 sixteen-file runner remains an independent gate.\n- Workers CPU and D1 capacity, provider log completeness, real wallet nonce and replacement behaviour, production catalogue and migration execution were not exercisable locally, as the objective already states.","treeHash":null,"usage":{"cachedInputTokens":2569876,"inputTokens":482,"model":"claude-fable-5-1","outputTokens":47367,"runtime":"claude","turns":46,"wallClockMs":696661}},{"artifacts":[],"attempt":1,"bundleHash":null,"device":"e06554fd2816f9d7","findings":[{"citation":"resolved","description":"economyPaymentEvidenceStatement records one immutable economy_payment_cases row per (chain_id,payment_contract,tx_hash,log_index) (UNIQUE in migrations/0013_economy_reconciliation.sql:59, NOT EXISTS guard at economy-reconciliation.ts:22), but the classification is computed from the perspective of whichever config's ingestion runs first. Two legitimate configs that share the same EmbeoPayments contract but differ in stakingContract (exactly the 'another legitimate vault' shape used in tests/economy-reconciliation.test.mjs:43) both subscribe to the same Paid logs (economy-rpc.ts eth_getLogs address:[stakingContract,paymentContract]). If config Y ingests the block before config X, the Paid event for an order quoted under X is classified 'wrong-config' under Y's event id and inserted. When X then ingests the same block, its UPDATE economy_orders correctly fulfils the order (status='paid', paid_event_id=X:tx:log) but the evidence INSERT is skipped by the dedup NOT EXISTS, so the only immutable record of that payment says 'wrong-config'. Triggers economy_payment_case_no_change/no_delete make this permanent. economyPaymentCases() returns it to the payer (classification<>'matched') with correctPaymentEvent pointing at the X event id, so EconomyPane.tsx:99 shows the player's own fulfilling payment as a reconciliation exception ('Correct payment confirmed; original exception retained' / 'Disposition pending'), and recordEconomyPaymentDisposition will accept an 'acknowledged-no-entitlement' disposition for a payment that did deliver the service. The result depends on cron/isolate ordering between the two configs' syncEconomyChain flights, so the evidence table is non-deterministic. No funds move and fulfilment stays exactly-once; this is an integrity defect of the new immutable reconciliation evidence. Fix: scope the dedup key to the config perspective that owns the order (e.g. classify only when the order's config is the ingesting config or its lineage, and otherwise skip insertion for events whose order_id belongs to a different live config), or key economy_payment_cases by event_id only and let the owning config insert its own 'matched' row; alternatively reject configs that share a paymentContract at defineEconomyConfig.","line":17,"path":"world-source/server/economy-reconciliation.ts","reproduction":"Node 24, node:sqlite via tests/d1-sqlite.mjs, real migrations. X=defineEconomyConfig({stakingContract:0x11..11,paymentContract:0x22..22,deploymentBlock:'100',...}); Y=same but stakingContract 0x44..44. order=createEconomyOrder(db,X,{wallet:W,requestId:'order-under-x-000001',serviceId:'svc',currency:'EMBEO',nowMs:T+DAY}). e={kind:'payment',transactionHash:0x..0bcc,logIndex:0,blockNumber:'102',blockHash:H(102),timestamp:T+DAY+1000,wallet:W,orderId:order.id,amount:'1000000000000000000',token:EMBEO,receiver:TREASURY}. ingestEconomyEvents(db,Y,{fromBlock:'100',toBlock:'102',toBlockHash:H(102),throughTimestamp:T+DAY+1000,events:[e]}) then ingestEconomyEvents(db,X,{same batch}). Expected: order paid under X and no non-matched payment case for W. Actual (observed): economy_orders.status='paid', paid_event_id='<X.id>:0x..0bcc:0', 1 fulfillment, but economy_payment_cases holds exactly one row event_id='<Y.id>:0x..0bcc:0' classification='wrong-config', and economyWalletSnapshot(db,X,W).paymentCases=[{classification:'wrong-config',expectedAmount:'1000000000000000000',actualAmount:'1000000000000000000',correctPaymentEvent:'<X.id>:0x..0bcc:0',disposition:'unreviewed'}]. Reversing the ingestion order (X first) yields a 'matched' row and an empty player case list, demonstrating order dependence.","severity":"medium","snippet":" WHEN o.config_id<>e.config_id AND NOT EXISTS(SELECT 1 FROM economy_config_lineage l WHERE l.config_id=e.config_id AND l.ancestor_id=o.config_id) THEN 'wrong-config'","title":"Payment-case classification is per-config while dedup is per payment contract: a fulfilling exact payment is immutably recorded as a 'wrong-config' exception when another live config sharing the payme"}],"hash":"f9d69239cf2ee0df915a60d1f8c83d6f43e8f31c2776c3aac9b4e6af338d40e1","nodeId":"6d412423-a976-4297-8d4c-afdbb0444c6a","outcome":"completed","summary":"The review is complete. Findings are written to `.imd-findings.json` at the repository root, and the tree is otherwise unchanged. Git status shows only that file.\n\n**Result: one substantiated finding, medium severity, in the new reconciliation scope.**\n\n**Finding (medium).** Payment-case evidence is deduplicated per payment contract but classified per config. Two live configs that share one EmbeoPayments contract but differ in staking vault both subscribe to the same Paid logs. If the config that does not own the order indexes the block first, the exact fulfilling payment is immutably recorded as a `wrong-config` exception. The owning config then fulfils the order correctly but its own evidence insert is skipped by the dedup guard. The player sees their successful payment listed as a reconciliation exception, and an operator can record a no-entitlement disposition against a payment that delivered the service. Reversing the ingestion order gives a `matched` row, so the immutable evidence table is order dependent. No funds move and fulfilment stays exactly once. The reproduction in the findings file ran on Node 24 with the real migrations and the packet's own SQLite D1 shim, and the anchored snippet at `world-source/server/economy-reconciliation.ts:17` is verified against the tree.\n\n**What I checked and found sound.**\n- Solidity: custody, exact balance deltas, reentrancy guards, immutable treasury, token code-hash and supply binding, payer/order/amount replay key. No reachable defect, so no Foundry proof was warranted.\n- Ledger: finalized range contiguity, staging atomicity, order scope through lineage, GOLD challenge and debit atomicity, persisted clock lower bounds, completed-replay after expiry, expired-pending `ORDER_EXPIRED`.\n- Recovery: the event-replay entitlement model agrees with settlement at every UTC boundary I traced, including midnight deposits, same-day withdrawals and midnight closures. Overissuance refusal, old-proof retention, snapshot guard, lineage and binding updates, retired-config guards and second-rebuild accounting behave as specified.\n- Client: strict snapshot and payment-case parsing, GOLD prices never rendered as EMBEO, context guards at every await, persisted nonce intent and lock handling.\n\n**Scope gaps.** I did not run the sixteen-file financial runner end to end, the EDR contract tests, or the pane tests, since React and EDR are not installed in this environment. I ran three suites on Node 24 in a temp copy, all passing. Production D1 capacity, mainnet token readback and real wallet behaviour remain release gates as the objective states.","treeHash":null,"usage":{"cachedInputTokens":2623625,"inputTokens":610,"model":"claude-fable-5-1","outputTokens":36679,"runtime":"claude","turns":29,"wallClockMs":504016}}],"verification":[]}