{"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":"df50e1ba-1df3-4cfc-8d6d-5180523699d8","kind":"audit","nodes":[{"acceptedSubmissionHash":"dabd44365295f5a169290f4f57d96825bad61c0a41942c9903dc3f0065587ae6","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":"60c93ecc599ced18c9faaa6155865ed2751e7bb240d104b0231dff85dfdc26f8","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":"626f7aa749734e9cc79cdec6c1d79c4233806a8cdf424c59cdd09f644162f4f7","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":"e5c28e5a4b931dcaae25a3dcb22f83a2817a408de1327fca85e0eccf8fb1839b","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":"be962ac2627c2a51d2e2fd9b0b06916ef28193accb78f2f00024332b39a8ebce","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 exact containing commit 7611e2bfd519a9491eaa4be3595ae854636ba0f9 (World source fe920ae748155d7d7ed9aaa98ebb6f9fb2c3a81f). Read AUDIT_OBJECTIVE.en.txt, REQUIREMENTS.md, SCOPE.md, AUDIT_FINDINGS.md and world-source/docs/economy/AUDIT4_REMEDIATION.md. Previous job658db620/publicca2ed8a1/producte8afd02f reported 1 Medium,4 Low,1 Info and does not cover these new bytes. Source/local tests only; no signing,payment,deployment,publication or automatic fixes.\n\nRe-audit C01: retained bootstrap staged prefixes must survive C1->C2->C3 recovery. Resolve an ancestor sentinel using its immutable bootstrap-predecessor receipt, not its empty last_hash. Verify canonical registry, source/target/domain, finalized boundary/time and transitive lineage. Preserve prefix and journals, positive shortfall once, replay/rollback/future settlement. Corrupt hash/domain/from/missing receipt must fail with zero writes. Explicit canonical source input may identify its own pre-0013 namespace without a registry row; never infer an unrelated unregistered namespace. A compatibility test caught this during local review.\n\nC03/C04: another vault's reviewed retired progress and healthy/halted pending prefix must not block this vault. Recognize foreign retired data only through coherent receipts,lineage and current binding; a retired label is insufficient. Limit pending semantic checks to the relevant staking domain but retain the ALL-financial-table atomic snapshot guard. Foreign concurrent changes must still invalidate stale plans and roll back everything. Do not delete evidence or bypass related pending proofs.\n\nC05: an accepted empty same-vault sync can remain preserved without blocking recovery. Require a complete absence of economic footprint: events,positions/checkpoints,days/progress/pages,journal references,binding,pending/staged,orders/payment cases,recoveries/lineage. Pending unpaid orders also count. Check existing FK relationships for challenges,fulfillment,case actions and adjustments; do not infer emptiness from events alone. Any real/ambiguous economic history still fails. Keep original registry,ingestions and chain state immutable.\n\nC02: deterministic 128-row snapshots replace whole-table 512KiB values. Inside ONE atomic batch compare all table counts AND every complete page value; test append/delete/update/reorder across pages and concurrent spend/indexer changes. UTF8-bound bulk chunks preserve GLOBAL block/log ordering before splitting. All limits apply simultaneously:5000events,366days,16ancestors,64statements,512KiB/page or bound value,4MiB both snapshots,100guard parameters. 5000 is an upper cap, not guaranteed recovery capacity. Global journals/orders/cases count too. Read-only preflight uses the identical complete planner, reports actual table/page bytes,guard params,statements or a specific capacity failure. CLI exit0 ready/2 blocked,unchanged input hashes,no writable DB copy/no migration batch. Validate 600-existing/601-rebuilt events,exact512KiB/+1,total4MiB and realistic global growth; no deletion,raised constants or non-atomic multi-batch workaround. Actual production CPU/D1 scale remains a separate unproved gate; oversize recovery needs another reviewed atomic plan.\n\nC06: actual bilingual pane distinguishes pending-fulfillment from exceptions for pending-only,exception-only,mixed lists. Correct finalized payment says delivery pending/do not pay again; no blanket no-service heading or exception disposition for pending rows. Preserve no automatic refund/resend,unknown-send protections and real delivery semantics.\n\nRetain original A/B scope and adversarial controls. Chain1 canonical EMBEO0x2b82cdeb8477415d541799abb0cc48044c8ea5f3,18decimals,100000EMBEO/whole share,separate deposits,1 integer wallet-bound nontransferable/nontradeable GOLD per share held throughout complete UTC[00:00,next00:00),after finalized indexing. Midday deposits start next full day. Withdraw anytime; only withdrawn shares forfeit that UTC day,earlier GOLD/survivors retained. No trading pair,conversion or cashout. Principal separate from immutable treasury0x1C651928150DADDDA9C2C040a9D4901d862f8eC4. Names/oldSIWE grant no money authority.\n\nReview custody/reentrancy/exact token deltas,official pinned compiler/artifact projection,token/code/supply/immutables; complete finalized headers/receipts/logs,cursor/staging atomicity,UTC bigint paged settlement and durable halt. Recovery derives entitlement independently from events and subtracts EARNED journals plus prior adjustments,never spendable balance/carry; positive-only corrections,overissuance refusal,exact old proofs,controlled indexer-rebuilt input,atomic receipt/binding/lineage/import/history closure,retired-config final-write guards. Preserve pending immutable quotes and exact late same-slot EMBEO. No player event upload,privileged credit/refund route,remote D1 command or automatic config switch. Same chain/token/vault/paymentContract/treasury,earlier deploymentBlock only.\n\nPayment facts,physical chain/paymentContract/tx/log dedup,cases,cursor and fulfillment commit together. Exact pending payments remain deferred until owner/lineage fulfillment; private pending view cannot claim delivery. Wrong amount/payer/config/currency,unknown/extra payments grant no extra service; a later correct payment fulfills once and stays linked. Disposition is append-only metadata,no money/refund power. CLI refuses unresolved owner fulfillment. Duplicate exact logs are defensive input; actual EmbeoPayments rejects repeated payer/order/amount.\n\nRetain origin/session/final-SQL authorization,canonical GOLD personal_sign binding all authority/time fields,atomic debit/journal/challenge/fulfillment,completed signed retries after expiry paid once while expired NEW spending fails,clock lowerbounds,opaque immutable orders/explicit quote renewal. At every await recheck account/chain/provider/session/halt; exact approval and separate confirmation,persisted nonce intent/WebLocks,no unknown-send resend,finalized replacement proof,strict JSON and private error/case views.\n\nReport severity,paths/lines,prerequisites,reproducible positive/negative controls and gaps. Node24 native EDR/SQLite runner has19 selected financial files; partial public source is separate from full private World. Accepted exact-source review,physical wallet extension nonce/replacement,actual Workers CPU/D1/large receipt/recovery capacity,RPC completeness,mainnet token plus deployed new-contract runtime/immutables,catalogue and backup/migration/config/postflight remain independent release gates. Nothing is authorized to go live by this audit.","parentJobId":null,"planHash":"2188a5ec5deec64fb201a0853bbce4b5f303ac56486e8b48ad4cb9c58db3f3c4","previousHash":"0000000000000000000000000000000000000000000000000000000000000000","projectId":"df50e1ba-1df3-4cfc-8d6d-5180523699d8","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":"52120","feedbackHash":"9da245b1c6e55561e6c24ce990d2c98e8d693a8e4c6af30f4142394661d16a1b","nodeKey":"audit_economics","submissionHash":"dabd44365295f5a169290f4f57d96825bad61c0a41942c9903dc3f0065587ae6","tag1":"review:submission","tag2":"acceptance-v2","value":1},{"agentId":"51423","feedbackHash":"2fa4fcaaf11bc7428f6d810af91cd043a95a20b19567c7451e5325e06a013bbe","nodeKey":"audit_flow","submissionHash":"60c93ecc599ced18c9faaa6155865ed2751e7bb240d104b0231dff85dfdc26f8","tag1":"review:submission","tag2":"acceptance-v2","value":1},{"agentId":"52257","feedbackHash":"775d8713d8a59400466e47363b1bda84c4fa0122df03a3286df3221efa249edf","nodeKey":"audit_judge","submissionHash":"626f7aa749734e9cc79cdec6c1d79c4233806a8cdf424c59cdd09f644162f4f7","tag1":"review:submission","tag2":"acceptance-v2","value":1},{"agentId":"52251","feedbackHash":"f84391ec037f0e69b639e04f175a841fb8d1b66facd2e6bec7183b55eb0be630","nodeKey":"audit_math","submissionHash":"e5c28e5a4b931dcaae25a3dcb22f83a2817a408de1327fca85e0eccf8fb1839b","tag1":"review:submission","tag2":"acceptance-v2","value":1},{"agentId":"52323","feedbackHash":"f6bf1baec37891006b9993266d4cc70facf6edbc749b46268cd94497d4ec8748","nodeKey":"audit_permissions","submissionHash":"be962ac2627c2a51d2e2fd9b0b06916ef28193accb78f2f00024332b39a8ebce","tag1":"review:submission","tag2":"acceptance-v2","value":1}],"site":null,"snapshotHash":"57c5d912da7dcb13a2ea5ae491c63eb6190673a2e59c6b49877c849cbc3827eb","state":"completed","submissions":[{"artifacts":[],"attempt":1,"bundleHash":null,"device":"56e50117311155be","findings":[],"hash":"60c93ecc599ced18c9faaa6155865ed2751e7bb240d104b0231dff85dfdc26f8","nodeId":"f29a0da2-ad70-4bff-8247-1876f9416f9f","outcome":"completed","summary":"Contract suite passes as well (30 cases on the pinned 0.8.37 compiler and in-process EDR). I have no substantiated defect to add, so the findings file stays as the empty list.\n\n**Result: no reproducible defect found in the Audit4 bytes at commit 7611e2b.** `.imd-findings.json` holds `{\"findings\":[]}`.\n\n**What I reviewed in depth**\n- `world-source/server/economy-recovery.ts` end to end: C01 ancestor receipt resolution (`verifiedReceipt`, `validAncestor`, `retainedPending`), the bootstrap predecessor path, canonical registry re-derivation, transitive lineage insertion, positive-only shortfall against ancestor journals plus prior adjustments, overissuance refusal, and plan-hash replay.\n- C02 paged snapshot guard: the 128-row `ORDER BY row LIMIT/OFFSET` pages plus per-table counts cover the complete sorted content of all 23 financial tables, so append, delete, update and reorder across pages all change a bound page or count inside the one batch. A false guard inserts `accepted=0`, which the `CHECK(accepted=1)` on `economy_recoveries` rejects and rolls back the whole batch. Global block/log sort happens before UTF-8 chunking, and chunks are consecutive. All caps are enforced through one `capacity()` path that the read-only preflight shares.\n- C03/C04: foreign-domain pending blocks are excluded only from semantic checks; foreign retired progress needs a verified receipt chain to the current binding. C05: `emptySync` covers positions, checkpoints, events, days, progress, pages, orders, pending and staged rows, bindings, cases, journal references, lineage and recoveries, with challenges, fulfillments and case actions reachable only through those FK parents.\n- C06 pane text and the client's strict case parsing, the ledger's retired-config guards (`lineage ancestor_id` checks in ingest, staging, orders, challenges and GOLD debit), settlement resumption after the recovered horizon, and the three Solidity contracts (exact-delta transfers, `nonReentrant`, immutable treasury, per payer/order/amount replay key).\n\n**Local execution**\n- Selected financial suite: 19 files, all pass after installing both pinned locks with scripts disabled. The three contract-runtime files failed only until the contracts lock was installed.\n- Portable contract tests: 30 pass, including the audit-order-amount and preflight files.\n- Audit4 recovery regressions: 23 pass, including the C1→C2→C3 prefix, corrupt receipt hash/domain/from/missing negatives, exact 512 KiB and plus-one bounds, 4 MiB global growth, appended-page rollback and the CLI preflight ready/blocked exits.\n\n**Leads examined and rejected**\n- A same-vault empty sync whose claimed-empty range contains rebuilt events is accepted, but the planner already accepts extra rebuilt events inside the source's own synced range, and no row from that config is ever imported or credited, so this is consistent design rather than a defect.\n- Non-capacity planner failures make the preflight CLI exit 1 with a FAIL receipt, which matches the documented ready/blocked split for capacity only.\n\n**Gaps that remain outside this review**\n- No real D1 or Workers execution, so the `pragma_table_info` call, statement-length and batch limits on production D1, and actual CPU time stay unproved, as the remediation note already states.\n- Slither and fuzzing did not run. Mainnet token runtime, deployed immutables, wallet extension nonce behavior and RPC completeness remain the independent release gates listed in SCOPE.md.","treeHash":null,"usage":{"cachedInputTokens":1334431,"inputTokens":386,"model":"claude-fable-5-1","outputTokens":35221,"runtime":"claude","turns":32,"wallClockMs":600825}},{"artifacts":[],"attempt":1,"bundleHash":null,"device":"22c9b760d0a15149","findings":[{"citation":"resolved","description":"The C-05 tolerance for an accepted empty same-vault sync only checks that the stray config's economy_chain_state row has a well-formed last_hash, last_block >= its deploymentBlock, a non-negative through_ms and only accepted ingestions. It never cross-checks that finalized claim against the rebuilt boundary the plan imports. Two contradictory cases are therefore treated as 'provably empty': (A) the stray asserts block 101 finalized with hash H(777777) while the rebuilt asserts block 101 with hash H(101) (two finalized identities for one height of one vault); (B) the stray asserts an accepted EMPTY range 98..105 while the rebuilt carries stake events at blocks 99 and 100 inside that range. Both are ambiguous finalized evidence about the same chainId:token:vault domain, which the objective says must still fail ('Any real/ambiguous economic history still fails'; requirement 12 requires any header at an announced finalized height to match the complete finalized identity). The planner proceeds, writes binding/lineage/adjustments and leaves the contradictory row in place instead of failing closed with RECOVERY_AMBIGUOUS_HISTORY. Impact is bounded: no GOLD is mis-issued by this alone and the stray cannot settle afterwards (ECONOMY_CONFIG_CONFLICT), but the 'provably empty' gate accepts evidence that disproves the input being imported, so a forked or corrupt indexer run is hidden from operator review. Fix: in emptySync (or at the call site on line 163) require coherence with the rebuilt boundary b: fail when state.last_block===b.last_block && state.last_hash!==b.last_hash; fail when integer(state.last_block)>integer(b.last_block) unless explicitly documented as an unverifiable-beyond-horizon exception; and fail when any rebuilt economy_chain_events row has block_number between BigInt(c.deploymentBlock) and integer(state.last_block). The existing positive fixture (stray at deploymentBlock 150 beyond the boundary) would need that exception or a last_block<=b.last_block constraint. Reproduced by the judge against commit 7611e2b with Node 24 and tests/d1-sqlite.mjs; merged from the audit_economics specialist finding (reproduction matched exactly).","line":104,"path":"world-source/server/economy-recovery.ts","reproduction":"Node 24, world-source/, helpers identical to tests/economy-audit4-recovery.test.mjs (cfg/stake/bad/ingest/complete). (1) c=cfg({deploymentBlock:'100'}), next=cfg({deploymentBlock:'99'}), stray=cfg({deploymentBlock:'98'}), same chainId/token/stakingContract/paymentContract/treasury. (2) ingest(db,c,[stake(2,100,T)],100,101,T+DAY); complete(db,c); ingest(db,c,[bad()]) rejects ECONOMY_HALTED. Rebuilt: ingest(shadow,next,[stake(1,99,T,2),stake(2,100,T)],99,101,T+DAY); complete(shadow,next) -> shadow economy_chain_state last_block 101, last_hash H(101), events at blocks 99,100. (3A) ingestEconomyEvents(db,stray,{fromBlock:'98',toBlock:'101',toBlockHash:H(777777),throughTimestamp:T+DAY,events:[]}) -> stray state last_block 101, last_hash 0x...bde31. (4) prepareEconomyRecovery(db,shadow,c,next,{nowMs:T+DAY+1001}) then applyEconomyRecovery with the plan hash. Expected: reject RECOVERY_AMBIGUOUS_HISTORY, zero writes. Actual (observed): plan accepted totalDelta '2', batch applied, SUM(economy_gold_accounts.balance)=3, stray row retained unchanged. (3B) instead ingest(db,stray,[],98,105,T+DAY+9000) (accepted empty 98..105 while rebuilt has stakes at 99,100). Expected: reject. Actual (observed): accepted and applied, sum 3. Positive controls: the shipped C-05 negatives (event/order/progress/pending) still reject; after apply, settleEconomy(db,stray,...) throws ECONOMY_CONFIG_CONFLICT, bounding impact.","severity":"low","snippet":"  /^0x[0-9a-f]{64}$/.test(state.last_hash!)&&integer(state.last_block)>=BigInt(c.deploymentBlock)&&clock(state.through_ms)>=0&&ingestions.length>0&&ingestions.every(r=>r.accepted==='1');","title":"C-05 emptySync accepts same-vault sync metadata that contradicts the rebuilt finalized chain (same height/different hash, or empty range covering rebuilt events)"},{"citation":"resolved","description":"The same-domain ambiguity check iterates only configs present in economy_config_registry ('known'). Migration 0013 creates the registry without backfilling rows for configs that existed before it, and the registry is only populated when a config next writes through economyConfigStatement. A pre-0013 same-vault config that ingested real stake events and was then abandoned therefore has economy_chain_state, economy_ingestions, economy_chain_events, economy_positions and economy_position_balances rows but no registry row, and line 163 never evaluates it. Day-progress rows of unknown configs are caught at lines 164-167, so settled (journaled) history is still blocked and no double issuance was found; but unsettled real events for the same vault pass as clean. In the reproduction the stray's accepted event is a stake at block 98, which is BELOW the rebuilt deploymentBlock 99: prior accepted evidence proves the rebuilt input is incomplete for this vault, yet the plan is accepted and applied, leaving position 80 permanently uncredited and the stray's evidence unreviewed. With the registry row present the identical state is rejected (RECOVERY_AMBIGUOUS_HISTORY), so the outcome depends only on whether a registry row happens to exist. The objective says explicit source input may identify its own pre-0013 namespace and the planner must not INFER an unrelated unregistered namespace; it does not say unregistered rows carrying real economic footprint may be ignored ('Any real/ambiguous economic history still fails'). Fix (preserving the no-inference rule): before line 163, fail with RECOVERY_AMBIGUOUS_HISTORY when any row of economy_chain_events/economy_positions/economy_position_balances/economy_orders/economy_payment_cases (and utc-reward journal references) names a config_id that is neither in known nor in ancestors, since its domain cannot be proven to differ from domain(from); a pre-0013 export must then be registered (or reviewed) explicitly before recovery. Related to the C-05 coherence finding but a distinct mechanism (registry scoping vs. metadata coherence).","line":163,"path":"world-source/server/economy-recovery.ts","reproduction":"Node 24, world-source/, same helpers as tests/economy-audit4-recovery.test.mjs. (1) c=cfg({deploymentBlock:'100'}), next=cfg({deploymentBlock:'99'}), stray=cfg({deploymentBlock:'98'}) same vault. (2) ingest(db,c,[stake(2,100,T)],100,101,T+DAY); complete(db,c); ingest(db,c,[bad()]) rejects ECONOMY_HALTED. Rebuilt: ingest(shadow,next,[stake(1,99,T,2),stake(2,100,T)],99,101,T+DAY); complete(shadow,next). (3) ingest(db,stray,[stake(80,98,T)],98,98,T) -> one real stake event row under stray.id. (4) Simulate the pre-0013 export shape exactly as the shipped compatibility test does: db.raw.exec('DROP TRIGGER economy_registry_no_delete'); DELETE FROM economy_config_registry WHERE config_id=stray.id. (5) prepareEconomyRecovery(db,shadow,c,next,{nowMs:T+DAY+1001}) then apply. Expected: RECOVERY_AMBIGUOUS_HISTORY, zero writes (a same-vault config with a real event is economic footprint). Actual (observed): plan ACCEPTED totalDelta '2', applied, SUM(balance)=3, stray event rows left unreviewed. Control (observed): with the registry row left in place the same state is rejected with RECOVERY_AMBIGUOUS_HISTORY.","severity":"low","snippet":" for(const c of known.values())if(domain(c)===domain(from)&&!ancestors.has(c.id)&&!emptySync(old,c))return fail('RECOVERY_AMBIGUOUS_HISTORY');","title":"Unregistered same-vault config with real stake events is invisible to the RECOVERY_AMBIGUOUS_HISTORY gate (registry-scoped iteration; migration 0013 does not backfill)"},{"citation":"resolved","description":"Every stake event costs rows on BOTH snapshot sides (original and rebuilt): economy_chain_events (whose payload column duplicates the row), economy_positions and economy_position_balances. planBytes (4,194,304 bytes for both snapshots together, accumulated in snapshot() at line 31 and charged per page) therefore saturates at roughly 1,450-1,500 single-day stake events with no withdrawals, payments, orders or journals, and the events:5000 cap at line 48 can never be the binding limit. Real histories (withdrawals add an event and a balance row on both sides; payments add events and cases; settlement adds journals and accounts) lower the ceiling further. Measured by the judge with the shipped planner and the d1-sqlite adapter (40 rotating wallets, one deposit day, one-day horizon, original halted with INVALID_POSITION_WITHDRAWAL like the Audit4 fixture): 600 events ready at 1,734,875 bytes / 42 guard params / 15 statements; 1,450 ready at 4,144,366 bytes / 63 guard params; 1,500 blocked planBytes actual 4,264,643 on economy_chain_events/rebuilt; 2,500 blocked planBytes 4,194,792 on economy_position_balances/rebuilt. REQUIREMENTS.md and AUDIT4_REMEDIATION.md do say 5000 is 'an upper cap, not guaranteed capacity', but neither states that the byte cap removes about 70% of that range even in the best case, so an operator sizing 'another reviewed atomic plan' from the 5000 figure will design work the planner can never execute. No funds are at risk: the refusal is explicit, read-only and leaves both inputs unchanged (verified by dump comparison). Fix options that preserve the design: document the measured effective ceiling (about 1,400 minimal-footprint stake events, fewer with withdrawals) next to the 5000 figure and in the preflight output; or stop charging rebuilt-side pages against planBytes (they are hashed into the plan identity but never bound into the guard statement, which only binds original pages at line 254), roughly doubling the usable range while keeping the 512 KiB, 100-parameter and 64-statement guards. Raising planBytes blindly is not recommended without D1 evidence. Merged from the audit_math specialist finding; the judge's measurements agree within 0.1%.","line":12,"path":"world-source/server/economy-recovery.ts","reproduction":"Node 24, world-source/, openD1() from tests/d1-sqlite.mjs for both databases. Config c (deploymentBlock 100) and next (deploymentBlock 99), same vault/token/payment/treasury. Ingest N stake events (shares '1', amount 1e23, all timestamps T=2026-10-01T00:00Z, 40 rotating wallets, log indices 0..) in chunks of 900 per block starting at block 100 (ingestEconomyEvents caps a batch at 1000 events), then an empty block with throughTimestamp T+DAY; settle to completion; halt the original with an invalid withdrawal (ECONOMY_HALTED). Rebuilt: same plus stake(1,99,T,2) from block 99. Call preflightEconomyRecovery(db,shadow,c,next,{nowMs:T+DAY+1001}). Expected from the documented caps: N=1500 and N=2500 (both below events:5000) are evaluated against the events cap. Actual (observed): N=600 -> ready, snapshotBytes 1,734,875; N=1450 -> ready, snapshotBytes 4,144,366; N=1500 -> blocked {metric:'planBytes',actual:4264643,limit:4194304,table:'economy_chain_events',side:'rebuilt'}; N=2500 -> blocked {metric:'planBytes',actual:4194792,limit:4194304,table:'economy_position_balances',side:'rebuilt'}. Both input dumps unchanged in every run. The events:5000 cap is never the reported failure for any N.","severity":"low","snippet":"export const RECOVERY_LIMITS=Object.freeze({events:5000,days:366,ancestors:16,statements:64,boundValueBytes:524288,planBytes:4194304,snapshotPageRows:128,guardParameters:100});","title":"4 MiB combined-snapshot cap makes the documented 5000-event recovery cap unreachable; effective ceiling is about 1,450 single-day stake events"}],"hash":"626f7aa749734e9cc79cdec6c1d79c4233806a8cdf424c59cdd09f644162f4f7","nodeId":"e103fbb0-89e0-4591-aa57-3c0067282235","outcome":"completed","summary":"The review is complete and `.imd-findings.json` holds three substantiated Low findings, all reproduced against commit 7611e2b with the shipped planner on Node 24 using the project's own `node:sqlite` D1 adapter.\n\n**Specialist findings judged**\n\n- **audit_economics C-05 (kept, Low).** Reproduced exactly. A same-vault stray config whose chain state claims block 101 with a different hash than the rebuilt boundary, or an accepted empty range 98..105 while the rebuilt has stakes at 99 and 100, still passes `emptySync` at `world-source/server/economy-recovery.ts:104`. The plan is accepted, applied, and the contradictory row is left in place. Impact is bounded because the stray cannot settle afterwards.\n- **audit_math capacity (kept, Low).** Reproduced within 0.1% of the specialist's numbers. With minimal single-day stake histories the 4 MiB combined snapshot cap blocks at 1,500 events while 1,450 is ready, so the events:5000 cap at line 12 can never bind. The docs disclaim 5000 as capacity but do not state the effective ceiling. No funds at risk, inputs unchanged.\n- audit_flow and audit_permissions reported nothing.\n\n**Additional finding from my own pass (Low)**\n\n- The ambiguity gate at line 163 iterates only registry rows, and migration 0013 does not backfill the registry. A pre-0013 same-vault config holding a real stake event at block 98, below the rebuilt deployment block, is accepted and applied, leaving that position permanently uncredited. The identical state with the registry row present is rejected. This is a distinct mechanism from the C-05 coherence gap, so it is reported separately.\n\n**Verification run**\n\n| Suite | Result |\n| --- | --- |\n| economy-audit4-recovery, pane, and other TS/D1 files | 279 pass |\n| forwarder, journey, payment-timing, ABI selector test | 4 fail, environment only |\n\nThe four failures come from my external copy lacking the sibling `contracts/` directory and the Hardhat/EDR runtime. They are not product defects.\n\n**Gaps not covered here.** Solidity contract tests were not rerun, since the EDR runtime was not installed and the contracts are unchanged from the previously accepted audit. Production Workers CPU and D1 capacity, real wallet behavior, RPC completeness, and mainnet runtime immutables remain separate release gates as the objective states. No repository file other than `.imd-findings.json` was touched; all scratch scripts live outside the tree.","treeHash":null,"usage":{"cachedInputTokens":1864167,"inputTokens":642,"model":"claude-fable-5-1","outputTokens":30451,"runtime":"claude","turns":33,"wallClockMs":391917}},{"artifacts":[],"attempt":1,"bundleHash":null,"device":"3a720671678111bc","findings":[],"hash":"be962ac2627c2a51d2e2fd9b0b06916ef28193accb78f2f00024332b39a8ebce","nodeId":"8deab161-cf5b-4f6e-a53f-4aeb3e441a83","outcome":"completed","summary":"The review is complete. The findings file at `.imd-findings.json` holds `{\"findings\":[]}`: I could not substantiate any defect in the six Audit4 dispositions or the retained controls, and I changed no repository files.\n\n**What I reviewed**\n\n- `world-source/server/economy-recovery.ts` in full, plus `economy-reconciliation.ts`, `economy-staging.ts`, the relevant parts of `economy-ledger.ts`, migrations 0010 to 0013, `scripts/economy-recovery.mjs`, `EconomyPane.tsx`, `economyClient.ts`, and the audit4 and pane test files.\n- Both production Solidity contracts. They are unchanged, tiny, ownerless, use `nonReentrant` and checked arithmetic, and the payments contract keys replay on payer, order and exact amount as required.\n\n**How I checked it**\n\n- Installed the exact world-source lock and ran the pinned suites. Result: 52 of 52 pass.\n- Wrote nine adversarial probes outside the repository and ran them against the real migrations in Node SQLite. All nine passed, meaning the guards behaved as specified:\n  - C01: a three-hop C1→C2→C3→C4 chain kept the C1 staged prefix intact with the sentinel two hops back, credited the shortfall once, and a forged sentinel parent hash failed with `RECOVERY_NOT_CLEAN` and zero writes.\n  - C02: an in-place delete plus insert inside a full 128-row page at batch time rolled back everything; a table with exactly 256 rows produced three pages including the empty guard page and applied cleanly; the plan hash was deterministic across two preparations.\n  - C03/C04: a foreign vault that was retired and whose successor then held a healthy pending prefix did not block or change during recovery.\n  - C05: a bootstrap-only stray same-vault chain state was tolerated and preserved, while a stray with only a pending unpaid GOLD order was refused as footprint.\n  - A late exact EMBEO payment for a pre-halt pending order was fulfilled once by recovery, with one fulfillment row and no double fulfillment on replay.\n- One diagnostic lead was disproved by a 17-hop lineage probe: preflight correctly returned `blocked` with the ancestors metric at 17 over 16 rather than throwing an ambiguity error.\n\n**Coverage limits**\n\n- Everything ran on synthetic Node SQLite fixtures, not Workers or D1, so production CPU, D1 statement size and bound-parameter behaviour remain unproved exactly as the scope documents say.\n- I did not run the Hardhat contract suite or a Foundry build; the repository has no Foundry layout and the contracts are unchanged from the previously accepted review.\n- A clean result here is not proof of absence of defects. The independent release gates listed in SCOPE.md still stand.","treeHash":null,"usage":{"cachedInputTokens":1630683,"inputTokens":450,"model":"claude-fable-5-1","outputTokens":37106,"runtime":"claude","turns":34,"wallClockMs":622735}},{"artifacts":[],"attempt":1,"bundleHash":null,"device":"a5c5e95a2ed07117","findings":[{"citation":"resolved","description":"The C-05 tolerance for an 'accepted empty same-vault sync' only checks that the stray config's economy_chain_state row has a well-formed hash, a last_block at or after its deploymentBlock and at least one accepted ingestion. It never cross-checks that metadata against the rebuilt finalized chain the plan is about to import: (a) a stray row whose last_block equals the rebuilt boundary block but carries a DIFFERENT last_hash (two finalized identities for the same height of the same vault) is still treated as provably empty; (b) a stray row whose accepted empty ingestion range covers blocks in which the rebuilt indexer recorded stake events (the stray asserts 'no events in 98..105' while the rebuilt DB has stakes at 99 and 100) is still treated as provably empty. Both are contradictory finalized evidence about the same chainId:token:vault domain, i.e. ambiguous history per the objective ('Any real/ambiguous economic history still fails'; rule 12 requires any header at an announced finalized height to match the complete finalized identity). The planner proceeds, writes the binding/lineage/adjustments and leaves the contradictory row in place rather than failing closed with RECOVERY_AMBIGUOUS_HISTORY. No GOLD is mis-issued by this alone (the stray cannot settle afterwards: ECONOMY_CONFIG_CONFLICT), so severity is Low; the defect is that the 'provably empty' gate accepts evidence that disproves the input it is importing, hiding a forked/corrupt indexer run from the operator review. Fix: in emptySync (or at the call site on line 163) require coherence with the rebuilt boundary b: if integer(state.last_block)>=integer(b.last_block) then the stray's ingested range must not overlap any rebuilt event block without matching hash evidence; specifically fail when state.last_block===b.last_block&&state.last_hash!==b.last_hash, when state.last_block>b.last_block (an unverifiable claim beyond the reviewed horizon), and when any rebuilt economy_chain_events row has block_number between BigInt(c.deploymentBlock) and integer(state.last_block) (the stray claims an empty range that the rebuilt proof contradicts). The existing C-05 positive fixture (stray at deploymentBlock 150 beyond the boundary with through_ms later than b.through_ms) would then also need the stray's last_block<=b.last_block, or an explicit documented exception for ranges strictly after the horizon.","line":104,"path":"world-source/server/economy-recovery.ts","reproduction":"Node 24, world-source/, using tests/d1-sqlite.mjs and the same helpers as tests/economy-audit4-recovery.test.mjs. (1) c=cfg({deploymentBlock:'100'}), next=cfg({deploymentBlock:'99'}), stray=cfg({deploymentBlock:'98'}) (same chainId/token/stakingContract/paymentContract/treasury). (2) ingest(db,c,[stake(2,100,T)],100,101,T+DAY); complete(db,c); ingest(db,c,[bad()]) rejects with ECONOMY_HALTED (INVALID_POSITION_WITHDRAWAL bootstrap-style halt at 102). (3) Stray contradiction A: ingestEconomyEvents(db,stray,{fromBlock:'98',toBlock:'101',toBlockHash:H(777777),throughTimestamp:T+DAY,events:[]}) so economy_chain_state[stray]={last_block:101,last_hash:H(777777)} while the rebuilt shadow (ingest(shadow,next,[stake(1,99,T,2),stake(2,100,T)],99,101,T+DAY); complete(shadow,next)) has last_block 101,last_hash H(101). (4) prepareEconomyRecovery(db,shadow,c,next,{nowMs:T+DAY+1001}) then applyEconomyRecovery with the plan hash. Expected: rejection with RECOVERY_AMBIGUOUS_HISTORY and zero writes, because two accepted finalized identities exist for block 101 of the same vault. Actual: plan accepted, totalDelta '2', batch applied, SUM(economy_gold_accounts.balance)=3, stray row retained unchanged. Contradiction B: instead ingest(db,stray,[],98,105,T+DAY+9000) (accepted empty range 98..105 while the rebuilt has stakes at 99 and 100). Expected: rejection. Actual: plan accepted and applied (sum 3). Positive control: the existing C-05 'event'/'order'/'progress'/'pending' negatives still reject, and the stray cannot settle afterwards (settleEconomy throws ECONOMY_CONFIG_CONFLICT), which bounds the impact.","severity":"low","snippet":"  /^0x[0-9a-f]{64}$/.test(state.last_hash!)&&integer(state.last_block)>=BigInt(c.deploymentBlock)&&clock(state.through_ms)>=0&&ingestions.length>0&&ingestions.every(r=>r.accepted==='1');","title":"C-05 emptySync accepts same-vault sync metadata whose finalized identity contradicts the rebuilt chain"}],"hash":"dabd44365295f5a169290f4f57d96825bad61c0a41942c9903dc3f0065587ae6","nodeId":"7cd0987c-8cee-45da-a8b3-d7617c406466","outcome":"completed","summary":"Review complete. The findings file holds one substantiated defect; everything else I probed held up.\n\n**Outcome**\n\n- **One Low finding** in the C-05 \"empty same-vault sync\" tolerance at `world-source/server/economy-recovery.ts:104`. The planner treats a same-vault stray config as provably empty without cross-checking it against the rebuilt finalized chain. Two reproduced inputs are accepted and applied when they should fail closed as ambiguous history: a stray row at the same height as the rebuilt boundary with a different finalized hash, and a stray accepted empty range covering blocks where the rebuilt indexer holds stake events. Impact is bounded because the stray still cannot settle afterwards, so no GOLD is mis-issued. The report includes exact inputs, expected versus actual, and a minimal fix.\n\n**What was verified and held**\n\n- C-01: ancestor sentinel resolved from the immutable bootstrap-predecessor receipt, registry canonicalization, domain and transitive lineage checks, prefix and journal preservation, single positive shortfall, replay, and the four corrupt-receipt negatives.\n- C-02: deterministic 128-row pages, count plus every page value inside the one atomic batch, global block/log ordering before UTF-8 chunking, simultaneous caps, read-only preflight with unchanged input hashes and no writable copy.\n- C-03/C-04: foreign retired progress needs coherent receipt, lineage and binding; pending semantic checks scoped to the domain; all 23 financial tables stay in the guard and foreign concurrent changes roll back.\n- C-05 negatives for events, unpaid orders, legacy progress and pending prefixes; registry, ingestions and chain state remain immutable.\n- C-06: bilingual pane distinguishes pending delivery from exceptions in pending-only, exception-only and mixed lists; no exception disposition on pending rows.\n- Payment facts, dedup, fulfillment scope, disposition append-only metadata, client strict JSON types, and the unchanged Solidity custody/exact-delta logic.\n\n**Test runs**\n\n| Suite | Result |\n|---|---|\n| audit4, audit3, reconciliation | 66 pass, 0 fail |\n| other selected financial files | 213 pass, 4 fail |\n\nThe four failures are environmental only: those files import the Hardhat/EDR contract runtime, which is not installed in this sandbox. No Foundry proof file was needed since no critical or high Solidity defect was found.\n\n**Gaps**\n\n- The RPC adapter and wallet controller were not re-reviewed in depth this round; they are unchanged bytes covered by the prior accepted report.\n- Actual Workers CPU/D1 capacity, mainnet runtime/immutables, provider completeness and the physical wallet extension remain the separate release gates the packet already names.","treeHash":null,"usage":{"cachedInputTokens":1769619,"inputTokens":450,"model":"claude-fable-5-1","outputTokens":36345,"runtime":"claude","turns":32,"wallClockMs":558785}},{"artifacts":[],"attempt":1,"bundleHash":null,"device":"1731fbfe0c4574fb","findings":[{"citation":"resolved","description":"Numerical-gap (boundary x invariant) between the simultaneously applied caps. Every stake event costs rows on BOTH snapshot sides (original and rebuilt) in economy_chain_events (whose payload column duplicates the whole row), economy_positions and economy_position_balances, so planBytes (4,194,304 bytes for both snapshots together, accumulated in snapshot() at line 31 and charged per page) saturates at roughly 1,450-1,500 single-day stake events with no withdrawals, no payments, no orders and no journals. The events:5000 cap at line 48 therefore never binds; any plan with more than ~1,500 stake events is refused by planBytes long before it, and real histories (withdrawals add an event row and a balance row on both sides, payments add events plus payment cases, settlement adds journals and accounts) lower the ceiling further. Measured with the shipped planner and d1-sqlite adapter (one wallet rotation over 40 wallets, one deposit day, horizon one day, original halted with INVALID_POSITION_WITHDRAWAL exactly like the Audit4 fixture): 600 events ready at 1,732,928 bytes (42 guard params); 1,000 ready at 2,864,772; 1,300 ready at 3,716,650; 1,450 ready at 4,142,420; 1,500 blocked planBytes actual 4,263,101 on economy_chain_events/rebuilt; 2,500 blocked at 4,194,406; 3,000 and 3,500 blocked at 4,245,840 and 4,237,514 (original side). The documentation (AUDIT4_REMEDIATION.md, REQUIREMENTS.md) states that 5000 is 'an upper cap, not guaranteed capacity', but it does not state that the byte cap removes about 70% of that range even in the best case, so an operator sizing a reviewed recovery from the 5000 figure will plan work that the planner can never execute. No funds are at risk: the refusal is explicit and writes nothing (verified: both input dumps unchanged). Fix options that preserve the design: either document the measured effective ceiling (about 1,400 stake events with a minimal footprint, fewer with withdrawals) next to the 5000 figure and in the preflight output, or stop charging the rebuilt-side pages against planBytes (they are hashed but never bound into the guard statement; only the original pages are bound as parameters at line 254), which roughly doubles the usable range while keeping the per-value 512 KiB, 100-parameter and 64-statement guards unchanged. Raising planBytes blindly is not recommended without D1 evidence.","line":12,"path":"world-source/server/economy-recovery.ts","reproduction":"Using world-source/tests/d1-sqlite.mjs openD1() for both databases and the shipped server modules: config c (deploymentBlock 100) and next (deploymentBlock 99), same vault/token/payment/treasury. Ingest N stake events (shares '1', amount 1e23, timestamps all T=2026-10-01T00:00Z, 40 rotating wallets) into both DBs in blocks of 900, then an empty block with throughTimestamp T+DAY, settle both to completion, halt the original with an invalid withdrawal (ECONOMY_HALTED), then call preflightEconomyRecovery(db,shadow,c,next,{nowMs:T+DAY+1001}). Expected (from the stated caps): N=1500, 2500, 3000, 3500 (all below events:5000) are at least evaluated against the events cap. Actual: N=1450 -> status 'ready', snapshotBytes 4,142,420; N=1500 -> status 'blocked', capacityFailure {metric:'planBytes',actual:4263101,limit:4194304,table:'economy_chain_events',side:'rebuilt'}; N=2500 -> blocked planBytes 4,194,406; N=3000 -> blocked planBytes 4,245,840; N=3500 -> blocked planBytes 4,237,514. The events:5000 cap is never the reported failure for any N.","severity":"low","snippet":"export const RECOVERY_LIMITS=Object.freeze({events:5000,days:366,ancestors:16,statements:64,boundValueBytes:524288,planBytes:4194304,snapshotPageRows:128,guardParameters:100});","title":"4 MiB combined-snapshot cap makes the 5000-event recovery cap unreachable; real ceiling is ~1,450 single-day stake events"}],"hash":"e5c28e5a4b931dcaae25a3dcb22f83a2817a408de1327fca85e0eccf8fb1839b","nodeId":"3e6f7b79-1a12-4475-bd39-4d51d2c73b2d","outcome":"completed","summary":"Review complete. The findings file holds one substantiated low finding; everything else I probed held up.\n\n**Outcome**\n\n- **One low finding recorded** in `.imd-findings.json`. The recovery planner's 4 MiB combined-snapshot cap binds long before the documented 5000-event cap. With the minimum possible footprint (single deposit day, no withdrawals, payments, orders or journals), the planner reports ready at 1,450 stake events and blocked at 1,500. The events cap is never the reported failure for any input. Documentation says 5000 is \"an upper cap, not guaranteed capacity\" but does not say about 70% of that range is unreachable even in the best case. Writes stay at zero and the refusal is explicit, so no funds are at risk. Suggested fixes preserve the design: publish the measured ceiling, or stop charging rebuilt-side pages against the byte budget since only original pages are bound into the guard.\n\n**What I verified and found clean**\n\n- **Settlement versus independent replay equivalence.** I fuzzed 160 random schedules of stakes and partial or full withdrawals across up to 6 days, then 55 more with up to 180 positions, 45 wallets and about 22 days so the 64-position and 32-wallet page limits were crossed repeatedly, with exact-midnight blocks included. Every plan produced zero adjustments and no overissuance refusal, so the paged settlement and the recovery replay agree at every UTC boundary.\n- **Boundary semantics.** Deposit exactly at midnight counts that day. Withdrawal exactly at next midnight keeps the completed day. Both code paths use the same inequalities.\n- **C02 accounting.** The bulk chunker's byte arithmetic is exact at the 512 KiB bound, global block/log ordering is sorted before splitting, and the guard compares every table count plus every page value inside the single batch.\n- **Solidity.** Checked arithmetic on share multiplication, exact balance deltas, supply and codehash pinning, and the payer/order/amount replay map are all sound. No overflow, rounding or sentinel path reaches value.\n- **Tests run.** All 280 selected TypeScript cases pass. The contracts package builds with the pinned compiler, the projection check passes, and all 30 contract tests pass. The contracts build rewrote one artifact's runtime-evidence block with this machine's platform. I restored it with git, and the tree is clean.\n\n**Gaps**\n\n- Capacity numbers come from the shipped planner on local SQLite, not production D1 or Workers CPU.\n- The Foundry proof format did not apply: the repo has no Foundry project and no critical or high finding arose.","treeHash":null,"usage":{"cachedInputTokens":3295254,"inputTokens":610,"model":"claude-fable-5-1","outputTokens":46917,"runtime":"claude","turns":53,"wallClockMs":849585}}],"verification":[]}