{"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":"12e0525d-ca39-4d8b-adb8-ed9d6099d575","kind":"audit","nodes":[{"acceptedSubmissionHash":"3338a94ad4e8256884dab30c8d586d89b6a6c4376d7ceaecb7c405deef3ff694","dependsOn":[],"execution":{"network":false,"profile":"foundry","requires":[],"skillHash":"bc5bfc8455464381f2db18badb2b55cf8bec1e535c019349a0378fa990741171","skillId":"audit-specialist","subagents":3,"tools":[]},"key":"audit_economics","kind":"code","role":"review","skillHash":"bc5bfc8455464381f2db18badb2b55cf8bec1e535c019349a0378fa990741171","skillId":"audit-specialist","state":"accepted"},{"acceptedSubmissionHash":"5e445b6c6c1de19a04f0906d71662cff11e648a5bcd076e568e605645aff054e","dependsOn":[],"execution":{"network":false,"profile":"foundry","requires":[],"skillHash":"bc5bfc8455464381f2db18badb2b55cf8bec1e535c019349a0378fa990741171","skillId":"audit-specialist","subagents":3,"tools":[]},"key":"audit_flow","kind":"code","role":"review","skillHash":"bc5bfc8455464381f2db18badb2b55cf8bec1e535c019349a0378fa990741171","skillId":"audit-specialist","state":"accepted"},{"acceptedSubmissionHash":"cd568964646b315feae067d4b72bc2e6d6779827494bd7611a7742d22c195dd1","dependsOn":["audit_math","audit_permissions","audit_economics","audit_flow"],"execution":{"network":false,"profile":"foundry","requires":[],"skillHash":"b89279670f5a00f5d26286edee00c97dd158b997031350a01cafe6e0329ad227","skillId":"audit-judge","subagents":4,"tools":[]},"key":"audit_judge","kind":"code","role":"review","skillHash":"b89279670f5a00f5d26286edee00c97dd158b997031350a01cafe6e0329ad227","skillId":"audit-judge","state":"accepted"},{"acceptedSubmissionHash":"b3d6406128c748919f687fe6e0d15f44d1e893fb1f0a371e654710fde9ebd21f","dependsOn":[],"execution":{"network":false,"profile":"foundry","requires":[],"skillHash":"bc5bfc8455464381f2db18badb2b55cf8bec1e535c019349a0378fa990741171","skillId":"audit-specialist","subagents":3,"tools":[]},"key":"audit_math","kind":"code","role":"review","skillHash":"bc5bfc8455464381f2db18badb2b55cf8bec1e535c019349a0378fa990741171","skillId":"audit-specialist","state":"accepted"},{"acceptedSubmissionHash":"89cef645585a7a411ee6a34a3743135ed9031fcbd53ba528cf896b836072e51c","dependsOn":[],"execution":{"network":false,"profile":"foundry","requires":[],"skillHash":"bc5bfc8455464381f2db18badb2b55cf8bec1e535c019349a0378fa990741171","skillId":"audit-specialist","subagents":3,"tools":[]},"key":"audit_permissions","kind":"code","role":"review","skillHash":"bc5bfc8455464381f2db18badb2b55cf8bec1e535c019349a0378fa990741171","skillId":"audit-specialist","state":"accepted"}],"objective":"Review Submission14/ only (read Submission14/AUDIT_REQUEST.md first): World source 64396ae, live as Worker 40c6477f. It answers the previous audit e4a57f91 on Submission13 (1 Medium, 6 Low, 1 Info). No Solidity changed.\n\n1. GOLD is now credited only at finality: the fast-credit route and its minute tick were removed (worker/app.ts, server/world-gold-api.ts). Can any route, cron or admin path still credit a top-up before the finalized indexer marks the order paid, or create a world_gold_fast_credits row? Is anything left in server/world-gold-fast.ts reachable?\n2. I-8 fix in src/world/economyWallet.ts intendedEffect: exactly one event of the intended kind for this sender from the intended contract. Any false accept or false reject for approve, pay, deposit or withdraw through an EIP-7702 / MetaMask Delegation Manager batch?\n3. src/world/pendingGoldCredit.ts: a display-only \"+N crediting\" note until the ledger books the order. Can it be treated as credit, show for a payment never sent, survive a reverted or replaced payment, leak across wallets, or poll without bound?\n4. scripts/deploy.mjs re-checks git status, HEAD and gitignored .env* after the build: any path that uploads something other than the recorded commit?\n\nReport severity, reproduction and fix. Not in scope: 3D/rendering, media, Celestao, AIMarket.","parentJobId":null,"planHash":"4b80b10b8820661bfe03399aed341a3d353fd7a34ec4cb4c8d9298e4d1036811","previousHash":"0000000000000000000000000000000000000000000000000000000000000000","projectId":"12e0525d-ca39-4d8b-adb8-ed9d6099d575","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":"52116","feedbackHash":"f951ba10889b8607ee42834b8ded5391d37eff4f099dcf258622494c44374f9d","nodeKey":"audit_economics","submissionHash":"3338a94ad4e8256884dab30c8d586d89b6a6c4376d7ceaecb7c405deef3ff694","tag1":"review:submission","tag2":"acceptance-v2","value":1},{"agentId":"52210","feedbackHash":"c035321423ba1e2c105696565aa0d6210a70306e394bc03095d284e01603d61e","nodeKey":"audit_flow","submissionHash":"5e445b6c6c1de19a04f0906d71662cff11e648a5bcd076e568e605645aff054e","tag1":"review:submission","tag2":"acceptance-v2","value":1},{"agentId":"53934","feedbackHash":"c487422b3d1a5eed6f41e33cacc404a59cb8215eb63c57938f53c27dc6dfc872","nodeKey":"audit_judge","submissionHash":"cd568964646b315feae067d4b72bc2e6d6779827494bd7611a7742d22c195dd1","tag1":"review:submission","tag2":"acceptance-v2","value":1},{"agentId":"52396","feedbackHash":"f728b2f9c3ffea1a705db891308b04d43fc1146ff4833c530138aff1db5d75ae","nodeKey":"audit_math","submissionHash":"b3d6406128c748919f687fe6e0d15f44d1e893fb1f0a371e654710fde9ebd21f","tag1":"review:submission","tag2":"acceptance-v2","value":1},{"agentId":"52600","feedbackHash":"a54fdc82c6b79af1d985cbab4fd3205a4eaa9e6cf076cf5845942e1158bd2f53","nodeKey":"audit_permissions","submissionHash":"89cef645585a7a411ee6a34a3743135ed9031fcbd53ba528cf896b836072e51c","tag1":"review:submission","tag2":"acceptance-v2","value":1}],"site":null,"snapshotHash":"7b06cb432b804236882282fdd1455efa5f07253d8bd142641b9de6a9242d78ab","state":"completed","submissions":[{"artifacts":[],"attempt":1,"bundleHash":null,"device":"6d0f746f68a99ddf","findings":[{"citation":"resolved","description":"pendingGoldCredit.ts promises that \"a payment reverted on chain or replaced by another transaction\" drops its note at once (paymentFailed, line 37: step 'execute' and phase 'replaced', or phase 'error' with TRANSACTION_REVERTED). That holds only for a direct EOA transaction (`same` true: record.to is the payment contract), where a status 0x0 receipt sets phase 'error'/TRANSACTION_REVERTED immediately. For the owner's actual wallet (MetaMask smart account routed through the Delegation Manager, record.to = 0xdb9b...7db3, the only configuration used for real transactions), `same` is false, so a status 0x0 receipt makes `done` false and finishReceipt falls into the finalization branch (lines 238-243): eth_getBlockByNumber('finalized') is below the receipt's block for the next ~13-15 minutes, so TRANSACTION_NOT_FINALIZED is thrown, action() -> fail() sets phase 'unknown' with error 'TRANSACTION_NOT_FINALIZED', and paymentFailed() is false. The note created at phase 'pending' (line 205, before the receipt) therefore survives: the top bar shows \"+1,000 crediting, expected by HH:MM\" for a payment that sent no EMBEO, WalletBalanceHud polls /api/gold/state every 30 s per open tab, and the panel tells the user 'do not pay again'. The note only goes when the user presses 'Check original transaction' after finality (then phase 'replaced', so the revert is reported as TRANSACTION_REPLACED, a code goldPurchaseError has no copy for, 'Unable to complete ... Reference: TRANSACTION_REPLACED') or after PENDING_CREDIT_MAX_MS (2 h). The nonce and sender of the mined record are already bound to this intent at line 229 (record.nonce===tx.nonce, record.from===tx.from), so a status 0x0 receipt for the wallet-returned hash is this intent's own reverted transaction whatever it was routed through; nothing else can have run under that nonce. A second input with the same symptom: the user cancels the payment in MetaMask (replacement with the same nonce, the original hash never mines); waitReceipt ends after 40 x 3 s in phase 'unknown'/PENDING_TRANSACTION, paymentFailed() is false, and the note stays the full 2 h with 240 polls per tab, since GoldPurchase only ever calls checkReceipt() without a replacement hash. Fix: in finishReceipt treat `tx.hash===candidate && r.status==='0x0'` (own nonce, own sender, mined) as the intent's reverted transaction regardless of routing, i.e. compute `const reverted=r.status==='0x0'&&tx.hash?.toLowerCase()===candidate.toLowerCase()` and let `done=same||reverted||(...)` so the state becomes 'error'/TRANSACTION_REVERTED at once and the note is dropped; for the cancelled case, also drop the note in WorldGoldPanel when the controller reaches 'unknown' with PENDING_TRANSACTION after the receipt wait, or make paymentFailed() cover it, and give goldPurchaseError a TRANSACTION_REPLACED copy.","line":234,"path":"Submission14/source/src/world/economyWallet.ts","reproduction":"State: controller after confirm() of a pay plan for order O (gold 1000), tx.version 2, tx.to = paymentContract, wallet returned hash H (phase 'pending' set at line 205, so WorldGoldPanel line 82 called trackPendingCredit(wallet, O, H, '1000')). Provider answers: eth_getTransactionReceipt(H) = {transactionHash:H, status:'0x0', from:EOA, to:'0xdb9b1e94b5b69df7e401ddbede43491141047db3', blockHash:B, blockNumber:'0x190f3c0', logs:[]}; eth_getTransactionByHash(H) = {hash:H, from:EOA, to:'0xdb9b...7db3', input:'0x...', value:'0x0', chainId:'0x1', nonce:tx.nonce, blockHash:B, blockNumber:'0x190f3c0'}; eth_getBlockByNumber('finalized') = {number:'0x190f380', hash:...} (below the receipt's block). Expected (per pendingGoldCredit.ts line 36-37 and the direct-EOA behaviour at line 251): phase 'error', error 'TRANSACTION_REVERTED', paymentFailed() true, dropPendingCredit(wallet, O) runs, pendingGold(wallet) = 0n. Actual: `same` is false (record.to is the Delegation Manager), `done` is false, line 240 throws TRANSACTION_NOT_FINALIZED, fail() sets phase 'unknown', error 'TRANSACTION_NOT_FINALIZED'; paymentFailed() returns false; pendingGold(wallet) stays 1000n, the HUD shows '+1,000 crediting' and polls every 30 s; this persists until a manual check after finality (then 'replaced'/TRANSACTION_REPLACED) or 2 hours. Second input: the same send, then the user cancels in MetaMask so eth_getTransactionReceipt(H) stays null for all 40 attempts: phase 'unknown', error 'PENDING_TRANSACTION', paymentFailed() false, note and 30 s polling persist for 2 hours.","severity":"low","snippet":"  const done=same||tx.version===2&&r.status==='0x1'&&tx.hash?.toLowerCase()===candidate.toLowerCase()&&intendedEffect(tx,(receipt as {logs?:unknown}).logs,this.config);","title":"A reverted smart-account-routed top-up payment keeps the \"+N crediting\" note (and polling) for 15 minutes to 2 hours instead of dropping it at once"},{"citation":"resolved","description":"The post-build re-check (lines 147-150) runs before `wrangler deploy` (line 159). vite build has already produced dist/, but the Worker module is not built yet: wrangler bundles `main` (worker/index.ts and everything under server/ and src/ it imports) from the working tree during the deploy command itself, after the second check. The script then hashes dist/ again (`unchangedDuringDeploy`) but never re-runs `git status`/`git rev-parse` after wrangler returns, and the manifest's `dirty` is computed only from the two earlier readings. So a tracked file changed between line 147 and wrangler's bundling (an editor save, a test watcher, a stash pop, the same class of event as the 40e97f4 incident, now in a window of seconds instead of minutes) is uploaded as the Worker while the record says commit X, dirty:false, exit code 0. The header comment ('the record's commit is exactly what shipped') and the Submission14 evidence (deploy records cited as proof that Worker 40c6477f is source 64396ae) rely on this guarantee for the Worker bundle, which only holds for dist/. Fix (minimal, preserves the design): after `run([...wrangler deploy...])` read `git rev-parse HEAD` and `git status --porcelain --untracked-files=normal` a third time and envFiles(ROOT) again; if anything differs from the start, set manifest.dirty=true, record the porcelain text, and make deploymentOutcome report verification 'failed' (exit 1) so the operator knows the uploaded version must be rolled back or re-deployed. Stronger: bundle first with `wrangler deploy --dry-run --outdir <tmp>` (no upload), re-check the tree, then upload that exact bundle with `wrangler deploy --no-bundle` pointing at <tmp>/index.js, so the bytes hashed in the record are the bytes checked against the commit.","line":147,"path":"Submission14/source/scripts/deploy.mjs","reproduction":"Throwaway git repo with Submission14/source/scripts/deploy.mjs copied to scripts/, package.json {type:module, scripts.test:'node --test tests/*.test.mjs'}, a passing tests/a.test.mjs, tracked worker/app.ts = `export default {fetch(){return new Response('committed');}}`, tracked src/runtime.ts = 'committed', .gitignore 'deploy-records/ node_modules/ dist/ .env*', package-lock.json '{}', wrangler.jsonc '{}', public/_headers, and fake node_modules exactly as tests/deploy.test.mjs builds them, except that node_modules/wrangler/bin/wrangler.js first writes worker/app.ts = `export default {fetch(){return new Response('EDITED AFTER THE CHECK');}}` and src/runtime.ts = 'edited after the check', then writes <outdir>/index.js from worker/app.ts (as the real wrangler bundles main from the tree at that moment) and prints 'Current Version ID: 2a5bf0b7-1c2d-4e5f-8a9b-0c1d2e3f4a5b'. Commit everything, run `node scripts/deploy.mjs` (real mode). Expected: refusal or an exit code 1 with the record marked dirty, as for the 'edited' variant in tests/deploy.test.mjs. Actual (run on 2026-10-27 with Node 24.21.0): exit code 0; outcome line {\"deployment\":\"uploaded\",\"verification\":\"passed\",\"recordPersistence\":\"persisted\",\"exitCode\":0}; deploy-records/<time>-983c044/manifest.json has commit 983c044..., dirty:false, wrangler.exitCode 0; deploy-records/<time>-983c044/worker/index.js contains 'EDITED AFTER THE CHECK' while `git show 983c044:worker/app.ts` contains 'committed'; `git status --porcelain` after the run shows ' M src/runtime.ts' and ' M worker/app.ts'.","severity":"low","snippet":"  const commitNow=git('rev-parse','HEAD'),porcelainNow=git('status','--porcelain','--untracked-files=normal');","title":"deploy.mjs: the Worker bundle is built by wrangler after the last tree/HEAD check and nothing is re-checked after the upload, so an edit made during `wrangler deploy` ships under the recorded commit w"},{"citation":"resolved","description":"The record hashes package-lock.json (manifest.config) and reads tool versions from node_modules/*/package.json, but nothing checks that node_modules matches the lockfile: no `npm ci`, no `npm ls`/integrity check. node_modules is gitignored, so `git status --porcelain --untracked-files=normal` cannot see it, and vite, wrangler (esbuild) and every runtime dependency bundled into dist/ and worker/index.js (react, three, viem helpers, ...) are read from it. A locally patched or stale dependency therefore ships under the recorded commit with dirty:false and tests 'passed', the same class of gap as the .env* re-check closed in 64396ae. Fix: make the first step `npm ci --ignore-scripts` (or `npm ci` with the project's usual scripts) so node_modules is rebuilt from the hashed lockfile before tests/tsc/vite/wrangler, and record its exit code in the manifest; or at least fail when `npm ls --package-lock-only --json` reports an invalid tree.","line":62,"path":"Submission14/source/scripts/deploy.mjs","reproduction":"Same fixture as tests/deploy.test.mjs's 'clean' variant (tracked src/runtime.ts, fake node_modules/vite/bin/vite.js writing dist/index.html 'abc'). Before running the deploy, change the gitignored node_modules/vite/bin/vite.js so it writes dist/index.html 'patched dependency output' (in the real project: edit any file under node_modules/react/ or node_modules/three/ that vite bundles). Run `node scripts/deploy.mjs`. Expected: refusal, or a record that says the build inputs differ from the lockfile. Actual: `git status --porcelain --untracked-files=normal` is empty (node_modules is ignored), both checks pass, exit code 0, manifest commit = HEAD, dirty:false, envFiles [], and dist/index.html (hashed into the record and uploaded by wrangler as a static asset) contains 'patched dependency output', which no file at the recorded commit produces.","severity":"info","snippet":"  return [...skipTests?[]:[['npm test',testArgs(root)]],['tsc',['node_modules/typescript/bin/tsc','--noEmit']],['vite build',['node_modules/vite/bin/vite.js','build']]];","title":"deploy.mjs: the build tools and every bundled dependency come from gitignored node_modules, which is never verified against the hashed package-lock.json"}],"hash":"3338a94ad4e8256884dab30c8d586d89b6a6c4376d7ceaecb7c405deef3ff694","nodeId":"9f5e9bd1-9549-4631-af4b-4822f7968d12","outcome":"completed","summary":"The review is complete. The findings file holds two Low and one Info finding, each with path, line and snippet verified against the tree. No file in the repository was changed.\n\n**Outcome by question**\n\n1. **Finality-only GOLD: clean.** The only writers of a top-up credit are `credit()` in world-gold.ts (reconcile cron, the per-order reconcile route, and the admin repair-topup command), and every one of them requires `economy_orders.status='paid'`, which only the finalized indexer sets. `fastCreditTopup` and `maintainFastGold` are imported by nothing in the Worker any more. What is still reachable in world-gold-fast.ts is `fastGoldReady` and `fastSettleStatements`, and both are no-ops without a `credited` row. No code path left can insert a `world_gold_fast_credits` row: the 0021 insert trigger needs state `credited`, and only the removed route built that insert.\n\n2. **intendedEffect: no false accept or reject found.** For approve, pay, deposit and withdraw the filter counts events of the intended kind from the intended contract for this sender, then demands exactly one carrying this call's arguments. The sender index is correct for each event. Approval events a token emits on transferFrom, Delegation Manager events, and another sender's events of the same kind are all filtered out correctly. The owner's six real routed receipts pass under this rule for the right reasons.\n\n3. **Pending note: one Low.** A reverted payment routed through the MetaMask smart account keeps the \"+N crediting\" note and its 30 second polling for 15 minutes to 2 hours, because a status 0x0 receipt for a routed transaction is sent down the finalization branch and ends in phase `unknown`, which `paymentFailed` does not cover. A payment cancelled in MetaMask has the same symptom. The note cannot be treated as credit, cannot leak across wallets, and polling is bounded at 2 hours per tab.\n\n4. **Deploy tool: one Low, one Info.** The Worker bundle is built by wrangler after the last tree and HEAD check, and nothing is re-checked after the upload. I ran the script in a throwaway repository where the fake wrangler edits worker/app.ts after the check: exit code 0, record says `dirty:false` at the start commit, uploaded bundle contains the edit. The Info notes that gitignored node_modules is never verified against the hashed lockfile.\n\n**Scope and limits**\n\n- I read every Submission14 source file, the cumulative diff, and the unchanged Submission13 server files it depends on. `server/economy.ts` and `economy-rpc.ts` are not in the public copy, so Q1 is clean only as far as the published code shows.\n- The pending-note finding is traced through the controller code, not executed, since the wallet test harness depends on modules outside the submission.\n- No Solidity changed, so no Foundry proof applies.","treeHash":null,"usage":{"cachedInputTokens":2389784,"inputTokens":514,"model":"claude-fable-5-1","outputTokens":36086,"runtime":"claude","turns":40,"wallClockMs":546312}},{"artifacts":[],"attempt":1,"bundleHash":null,"device":"dff6c0d3de4aa913","findings":[{"citation":"resolved","description":"The re-check added in 64396ae (lines 147-150: HEAD, `git status`, `.env*`) happens before `wrangler deploy` (line 159). wrangler does not upload a prebuilt artifact: it runs esbuild over `worker/index.ts`, `server/*.ts` and the shared `src/` modules at upload time and reads `dist/` for the static assets. Nothing re-reads `git status`/HEAD after wrangler returns; the only post-upload verification is `distAfter` (line 162), which covers dist/ and not the Worker source wrangler just bundled. A tracked-file edit (or a commit) that lands between line 147 and wrangler's read is therefore uploaded, while manifest.json records `commit` = the start HEAD and `dirty:false`, and the outcome is `uploaded / passed / persisted`, exit 0. This is the 40e97f4 class of incident with the window moved from the test run to the upload step. Fix: after wrangler returns, re-run `git rev-parse HEAD` and `git status --porcelain`; if either differs from the start, set `dirty:true` and a `movedDuringUpload` field in the manifest and exit 1 (the upload cannot be undone, but the record must not say clean). Better: run `wrangler deploy --dry-run --outdir <tmp>/prebundle` immediately after the post-build re-check, and after the real upload compare `prebundle/index.js` to `worker/index.js` by SHA-256 (the header comment states `index.js` is byte-identical across runs of the same commit); report a mismatch like `dist.unchangedDuringDeploy`. Also include the list of files wrangler bundled (its metafile) in the record.","line":159,"path":"Submission14/source/scripts/deploy.mjs","reproduction":"Throwaway git repo with the real scripts/deploy.mjs and synthetic node_modules/{typescript,vite,wrangler} (no network). wrangler.js writes `worker/app.ts` = \"export const committed=false; // edited during wrangler deploy\" and then bundles the file it just edited into --outdir/index.js (mirrors wrangler bundling source at upload time). Committed worker/app.ts = \"export const committed=true;\". `node scripts/deploy.mjs` (real mode): expected: refusal \"nothing was uploaded\" or a record marked dirty / exit 1. Actual (run on 2026-10-27): exit 0; outcome {\"deployment\":\"uploaded\",\"verification\":\"passed\",\"recordPersistence\":\"persisted\",\"exitCode\":0}; manifest.commit = start HEAD 2ea974c, manifest.dirty = false; deploy-records/*/worker/index.js contains \"committed=false ... edited during wrangler deploy\"; `git status --porcelain` after the run: \" M worker/app.ts\". The project's own tests/deploy.test.mjs variants (edited, head-moved, env-added) all inject the edit during `vite build`, so none covers this window.","severity":"low","snippet":"  const w=run(['node_modules/wrangler/bin/wrangler.js','deploy',...(dryRun?['--dry-run']:[]),'--outdir',join(tmp,'worker')],","title":"deploy.mjs: the post-build tree/HEAD re-check runs before wrangler bundles the Worker source, so an edit landing during `wrangler deploy` ships under a clean record"},{"citation":"resolved","description":"For the wallet's own hash, `done` is `same || (... && r.status==='0x1' && intendedEffect(...))`. A direct EOA transaction gives `same=true`, so a status-0x0 receipt ends immediately as phase 'error' / TRANSACTION_REVERTED. For an EIP-7702 / MetaMask Delegation Manager routed transaction `same` is false (to/input differ) and the status-0x1 guard makes `done` false even though the receipt is for the wallet-returned hash, same nonce, same sender: a definitive revert. Line 238 then demands finality (`TRANSACTION_NOT_FINALIZED`, thrown out of waitReceipt, so the controller lands in phase 'unknown' with that code) and, once final, line 251 maps `!done` to phase 'replaced' / TRANSACTION_REPLACED. The user never sees the TRANSACTION_REVERTED copy (\"Transaction reverted. Check the original transaction; network fees may still apply\"); both codes shown fall through to the generic \"Reference: ...\" line. Secondary effect on Q3: `paymentFailed()` is false during the whole 'unknown' window, so the \"+N crediting\" note created at broadcast stays on the top bar and purchase page for a payment that already failed, until the user manually re-checks after finality (or 2 h). Fix: in finishReceipt, when `tx.hash===candidate && r.status==='0x0'` treat the receipt as the original transaction's revert regardless of routing (the wallet-returned hash with the recorded nonce reverted: no effect can exist), i.e. `const reverted=tx.version===2&&r.status==='0x0'&&tx.hash?.toLowerCase()===candidate.toLowerCase(); const done=same||reverted||...`, keeping the finality requirement only for pasted/replacement hashes.","line":234,"path":"Submission14/source/src/world/economyWallet.ts","reproduction":"Harness over the real economyWallet.ts with a stub economyClient (same fixture shape as tests/economy-wallet-recovery.test.mjs). Plan: pay orderId 0x77..77, amount 1000. Wallet returns HASH. Receipt for HASH: from=A, to=DelegationManager 0xdd..dd, status \"0x0\", logs [], blockNumber 0x20; eth_getTransactionByHash matches (nonce 0x8, chainId 0x1). Case 1 (control, direct tx to=PAY, input=pay calldata, status 0x0): state {phase:\"error\",error:\"TRANSACTION_REVERTED\",paymentFailed:true}. Case 2 (routed, finalized block 0x1f < 0x20): after confirm(): {phase:\"unknown\",error:\"TRANSACTION_NOT_FINALIZED\",paymentFailed:false}; checkReceipt() again: unchanged. Case 3 (routed, finalized >= 0x20): {phase:\"replaced\",error:\"TRANSACTION_REPLACED\"}. Expected for cases 2 and 3: phase \"error\", error \"TRANSACTION_REVERTED\" at the first receipt, as in case 1.","severity":"low","snippet":"  const done=same||tx.version===2&&r.status==='0x1'&&tx.hash?.toLowerCase()===candidate.toLowerCase()&&intendedEffect(tx,(receipt as {logs?:unknown}).logs,this.config);","title":"economyWallet.finishReceipt: a smart-account routed payment that REVERTED (status 0x0) is never classified as reverted; it is held as TRANSACTION_NOT_FINALIZED for ~15 minutes and then reported as TRA"},{"citation":"resolved","description":"The note is created the moment the wallet returns the payment hash (phase 'pending', step 'execute'). It is removed only by settlePendingCredits (ledger credited), by the 2 h window, or by dropPendingCredit when paymentFailed() is true, i.e. phase 'replaced' or 'error'+TRANSACTION_REVERTED. The controller reaches 'replaced' only when finishReceipt sees a mined receipt for some candidate hash with the intent's nonce; for a MetaMask \"Cancel\" / \"Speed up\" (same nonce, different hash) the receipt for the wallet-returned hash is null forever, so waitReceipt ends in 'unknown' / PENDING_TRANSACTION after 40 x 3 s, and every later \"Check original transaction\" press repeats that. GoldPurchase.tsx wires `check` to `controller.checkReceipt()` with no hash argument and offers no field for the replacement hash; `clearUnknownAfterWalletCheck()` returns at once for a v2 intent that has a hash. Result: the HUD shows \"+1,000 crediting\", the purchase page shows \"Crediting: +1,000 GOLD, expected by HH:MM\", and the 30 s ledger poll runs, for a payment that will never be credited, until PENDING_CREDIT_MAX_MS (2 h); a reload keeps it (localStorage) and the message tells the user not to pay again. Fix options, smallest first: (a) in WorldGoldPanel, when the wallet is in 'unknown'/PENDING_TRANSACTION for the paying order, read `eth_getTransactionCount(from,'latest')` and, if it exceeds the stored intent nonce while the receipt for the hash is still null, call dropPendingCredit (the nonce was consumed by another transaction); (b) give the GOLD purchase page the same \"paste the replacement hash\" entry the economy pane has so checkReceipt(hash) can reach 'replaced'; (c) at minimum shorten PENDING_CREDIT_MAX_MS for an order whose quote has expired and whose status is still 'pending'.","line":37,"path":"Submission14/source/src/world/pendingGoldCredit.ts","reproduction":"Harness over the real economyWallet.ts + pendingGoldCredit.ts (stub economyClient). Plan: pay orderId 0x77..77, 1000 GOLD; wallet returns HASH; a subscriber calls trackPendingCredit(A,orderId,paymentHash(state),\"1000\") when paymentHash() first becomes non-null (what WorldGoldPanel's effect does). eth_getTransactionReceipt(HASH) returns null on every poll (the user cancelled in MetaMask; the cancel transaction consumed nonce 0x8). Observed: after confirm(): {phase:\"unknown\",error:\"PENDING_TRANSACTION\",paymentFailed:false}; after checkReceipt(): same; after clearUnknownAfterWalletCheck(): same, intent still stored; pendingGold(A) = 1000 at creation, 1000 at +1 h 59 min, 0 only at +2 h. Expected: once the wallet hash can no longer be mined (nonce consumed by another transaction), the note is dropped and the purchase page stops saying the GOLD is being credited.","severity":"low","snippet":"export function paymentFailed(wallet:{phase:string;step:string|null;error:string|null}){return wallet.step==='execute'&&(wallet.phase==='replaced'||wallet.phase==='error'&&wallet.error==='TRANSACTION_REVERTED');}","title":"pendingGoldCredit: a payment cancelled or replaced in the wallet (same-nonce replacement) never reaches a \"failed\" phase in the GOLD purchase flow, so \"+N crediting\" stays on the top bar for up to 2 h"},{"citation":"resolved","description":"`git status --porcelain --untracked-files=normal` ignores gitignored paths, and the only ignored input the script re-checks is `.env*`. The build and the Worker bundle are produced by `node_modules/{typescript,vite,wrangler}` and whatever other packages they resolve; the manifest records the three tools' `version` fields and the hash of package-lock.json, but not that the installed tree equals the lock (e.g. `npm ci` was not run, a package was patched or installed with --no-save). The header's claim \"the record's commit is exactly what shipped\" then holds for the sources only. Fix: run `npm ci --ignore-scripts` (or at least `npm ls --package-lock-only` / verify `node_modules/.package-lock.json` hash equals package-lock.json's resolved tree) as the first build step on a real deploy, and record the result in the manifest.","line":169,"path":"Submission14/source/scripts/deploy.mjs","reproduction":"State: operator machine where node_modules/vite/dist/node/chunks/*.js (or any transitive dependency) differs from the package-lock.json resolution, e.g. after `npm install <pkg> --no-save` or a local patch; package-lock.json unchanged and the tree clean. `node scripts/deploy.mjs`: all gates pass, manifest.dirty=false, manifest.config lists the unchanged lock hash, tools.vite shows the same version; the uploaded dist/ and worker/index.js are not reproducible from the recorded commit. Expected: a gate or a recorded verification that node_modules matches the lock file.","severity":"info","snippet":"    config:[file('wrangler.jsonc'),file('package-lock.json'),file('public/_headers')],","title":"deploy.mjs: the record names package-lock.json but nothing verifies that node_modules (which produces dist/ and the Worker bundle) matches it"}],"hash":"5e445b6c6c1de19a04f0906d71662cff11e648a5bcd076e568e605645aff054e","nodeId":"46cf057b-6a02-4cd3-bdde-2fbde01d1c40","outcome":"completed","summary":"Review of Submission14 is complete. `.imd-findings.json` holds three Low findings and one Info note, each with a concrete reproduction that I executed locally. No file in the tree was modified; the harnesses ran in /tmp.\n\n**Answers to the four questions**\n\n1. **Finality-only GOLD: confirmed clean.** The only writer of `world_gold_fast_credits` is `fastCreditTopup`, which no route, cron or admin command imports any more. The remaining `fastSettleStatements` only moves an existing `credited` row to `settled`, and `repair-topup` and `reconcile` both require the order to be `paid` by the finalized indexer. One side note, not filed as a finding because the state is unreachable: the debt collector left with the tick, while the debt-blocking triggers stay. A `world_gold_fast_debts` row with amount above zero would now block that wallet's spending permanently with no code path to repay it.\n\n2. **I-8 `intendedEffect`: no false accept or false reject found** for approve, pay, deposit or withdraw. The sender filter, exact-one count and argument checks hold for a delegated batch. The one defect nearby is in `finishReceipt`, not in `intendedEffect`: a **routed payment that reverted** is never classified as reverted. It sits in `unknown / TRANSACTION_NOT_FINALIZED` for the finality wait and then ends as `replaced / TRANSACTION_REPLACED`. A direct EOA revert ends immediately as `TRANSACTION_REVERTED`. The user never sees the \"transaction reverted\" copy.\n\n3. **Pending note: display-only, no entitlement, no cross-wallet leak, polling bounded** by the two-hour window. It does survive two failed-payment cases. A **same-nonce cancel or speed-up in MetaMask** leaves the controller in `unknown / PENDING_TRANSACTION` forever, since the purchase page offers no replacement-hash input and `clearUnknownAfterWalletCheck` is a no-op for a hashed intent. The \"+1,000 crediting\" note then stays on the top bar for the full two hours. The routed-revert case above has the same effect until the user re-checks after finality.\n\n4. **Deploy: one path remains.** The post-build re-check precedes `wrangler deploy`, and wrangler bundles the Worker source itself at upload time. A tracked-file edit landing during the upload step ships, while the record says `dirty:false` with commit equal to HEAD and exit 0. Reproduced with the real script in a throwaway repo. The suggested fix is a pre-bundle dry run compared by hash against the uploaded `index.js`, plus a status re-check after wrangler returns. The Info note covers unverified `node_modules` drift.\n\n**Coverage.** All 17 files in `Submission14/source`, the four patches, the evidence files, and the unchanged `world-gold-fast.ts`, `world-gold.ts` and migration 0021 from Submission13 were read. Both wallet-controller findings were executed against the real `economyWallet.ts` and `pendingGoldCredit.ts` with a stubbed `economyClient`. No Solidity changed, so no Foundry proof files apply.","treeHash":null,"usage":{"cachedInputTokens":1901540,"inputTokens":354,"model":"claude-fable-5-1","outputTokens":37939,"runtime":"claude","turns":36,"wallClockMs":554909}},{"artifacts":[],"attempt":1,"bundleHash":null,"device":"fa4b91aed45c94fa","findings":[{"citation":"resolved","description":"AUDIT_REQUEST.md states the note is dropped \"on reverted/replaced\". The drop effect at WorldGoldPanel.tsx:83 runs only when `payingOrder` is non-null, and `payingOrder` (line 80) is derived from `wallet.plan`. The plan exists only in the controller instance that sent the payment. After a page reload, a panel close/reopen, or a sign-in refresh that recreates the controller (line 75: `setController(null)` ... `c.restorePending()`), `adoptPending` (economyWallet.ts:91) restores the intent with `plan:null`. When that restored intent is then checked and ends as 'replaced' (TRANSACTION_REPLACED) or 'error'/TRANSACTION_REVERTED, `paymentFailed(wallet)` is true but `payingOrder` is null, so `dropPendingCredit` never runs. `settlePendingCredits` (pendingGoldCredit.ts:43) removes a note only when the ledger credits the order or the entry is older than PENDING_CREDIT_MAX_MS (2 h). A reverted/replaced payment's order stays 'pending' forever, so the top bar keeps showing \"+N 入帳中\" and WalletBalanceHud (line 22-26) keeps reading /api/gold/state every 30 s (about 240 requests) until the 2 h window ends. The same note also survives the full finality wait for a smart-account routed payment that reverted even without a reload: with `same` false and `r.status==='0x0'`, `done` is false (economyWallet.ts:234), finishReceipt throws TRANSACTION_NOT_FINALIZED until the block is finalized (line 240), the phase is 'unknown' for about 15 minutes and `paymentFailed` stays false. Display-only (purchaseStage/pendingGold never grant anything), hence Low. Fix: drop by the stored payment hash rather than by the live plan. The note already stores `hash`; in WorldGoldPanel add an effect `if(data&&failed&&wallet.hash)for(const p of readPendingCredits(data.wallet))if(p.hash.toLowerCase()===wallet.hash.toLowerCase())dropPendingCredit(data.wallet,p.orderId)` (or expose the pay orderId from the restored intent: word 0 of tx.data when the selector is d0ee4de0). Independently, let settlePendingCredits also drop a note whose order has passed expires_ms+finality margin while still 'pending', so a note never outlives an order that can no longer be credited.","line":83,"path":"Submission14/source/src/world/WorldGoldPanel.tsx","reproduction":"State: wallet W has a top-up quote O (gold 1000), approved, then 'Confirm EMBEO payment'. The wallet returns hash H; phase 'pending', step 'execute' -> trackPendingCredit(W,O,H,'1000') is written to localStorage key ember-pending-gold-v1:W. Steps: (1) before the receipt is read, reload the page (or close and reopen the GOLD panel); the new controller calls restorePending -> adoptPending: phase 'unknown', hash H, plan null. (2) Press \"Check original transaction\". The receipt for H has status 0x0 (reverted pay, e.g. the order was already paid from another tab), or H was replaced by a same-nonce cancellation and the finalized block confirms it: finishReceipt sets phase 'error'/TRANSACTION_REVERTED or 'replaced'/TRANSACTION_REPLACED. Expected (AUDIT_REQUEST.md section 2 and 3: \"drop on reverted/replaced\"): the note for O is removed and the top bar stops polling. Actual: paymentFailed(wallet) is true but payingOrder is null (wallet.plan is null after adoptPending), so the effect at WorldGoldPanel.tsx:83 never calls dropPendingCredit; pendingGold(W) stays 1000n, the HUD shows \"+1,000 入帳中\" and usePendingGold keeps calling /api/gold/state every 30 s until atMs+2h. Verified with the module in isolation: paymentFailed({phase:'replaced',step:'execute',error:'TRANSACTION_REPLACED'}) === true while the panel's payingOrder expression evaluates to null for plan null. Variant without reload: routed payment (receipt.to = Delegation Manager) with status 0x0 and finalized block < receipt block -> TRANSACTION_NOT_FINALIZED, phase 'unknown', note kept for the whole finality wait.","severity":"low","snippet":" useEffect(()=>{if(data&&payingOrder&&failed)dropPendingCredit(data.wallet,payingOrder);},[data,payingOrder,failed]);","title":"Pending \"+N crediting\" note outlives a reverted or replaced payment after a reload or panel reopen (drop is keyed on in-memory wallet.plan)"},{"citation":"resolved","description":"The post-build re-check (lines 147-150) runs `git rev-parse HEAD`, `git status --porcelain` and the .env* scan, then the script validates record directories, creates the .partial directory, hashes dist/, and only then starts `wrangler deploy` (line 159). wrangler bundles worker/index.ts and everything it imports (server/*.ts, src/world/*.ts) with its own esbuild at that moment, from the working tree. So the Worker bundle is produced from a tree state that is never compared with git: a change to any Worker source made after line 147 and before wrangler's bundling is uploaded, and no `git status`/HEAD check runs after wrangler returns. The dist/ \"unchangedDuringDeploy\" check (line 171) covers only the static assets, not the Worker bundle's inputs. The record then says commit=<start>, dirty:false, deployment 'uploaded', exitCode 0, exactly the kind of record the 8161565/64396ae change was meant to make impossible. The window is seconds wide (vs minutes for the 40e97f4 incident) and needs a concurrent local write (editor autosave, a git checkout/pull in another terminal, a watcher), so Low. Fix: (a) after wrangler returns, run the same `changedRefusal(commit, git('rev-parse','HEAD'), git('status','--porcelain','--untracked-files=normal'), dryRun)` and `envRefusal` once more, store the result in the manifest (e.g. `tree.unchangedDuringDeploy`) and feed it into deploymentOutcome so exitCode is 1 and the record is marked when it fails; (b) better, bundle first and upload a bundle that was checked: `wrangler deploy --dry-run --outdir <tmp>/worker`, then re-check git, then upload that exact bundle with `wrangler deploy --no-bundle <tmp>/worker/index.js` (or, if --no-bundle cannot be used with this config, at least verify after the upload that the bundle hash equals a fresh dry-run bundle hash taken after the final git check).","line":159,"path":"Submission14/source/scripts/deploy.mjs","reproduction":"Reproduced with the same synthetic harness tests/deploy.test.mjs uses (throwaway git repo, fake tsc/vite/wrangler, Node 24.21, git 2.43, no network). Fixture: tracked src/runtime.ts = \"committed\"; node_modules/wrangler/bin/wrangler.js writes src/runtime.ts = \"edited after the post-build re-check, before bundling\" and then emits <outdir>/index.js containing the current src/runtime.ts (what a real wrangler does when it bundles the Worker from the tree). Command: `node scripts/deploy.mjs` (real mode, not --dry-run). Expected: the run is refused like the 'edited' and 'head-moved' variants (\"nothing was uploaded\"), exit 1, no record. Actual: exit 0; stdout ends with {\"kind\":\"imd-world deploy outcome\",\"deployment\":\"uploaded\",\"verification\":\"passed\",\"recordPersistence\":\"persisted\",\"exitCode\":0,...}; manifest.json: mode \"deploy\", commit 71e52d9... (the start commit), dirty:false, dist.unchangedDuringDeploy:true; deploy-records/<name>/worker/index.js contains \"bundle of: edited after the post-build re-check, before bundling\"; `git status --porcelain` after the run prints \" M src/runtime.ts\" and `git show HEAD:src/runtime.ts` prints \"committed\". The uploaded Worker is not the recorded commit and the record does not say so.","severity":"low","snippet":"  const w=run(['node_modules/wrangler/bin/wrangler.js','deploy',...(dryRun?['--dry-run']:[]),'--outdir',join(tmp,'worker')],","title":"deploy.mjs re-checks the tree before wrangler reads the Worker sources; an edit between the re-check and bundling, or during upload, ships under the recorded commit with dirty:false"},{"citation":"resolved","description":"The display-window test is `now-v.atMs<PENDING_CREDIT_MAX_MS`. For an entry whose atMs is later than now the difference is negative, so the entry is always kept. atMs is written from Date.now() at payment time; if the device clock was ahead then and is corrected afterwards (NTP sync, timezone fix, a VM resume), the note and the WalletBalanceHud polling (every 30 s while pendingGold>0) continue for 2 h plus the skew. An entry with a very large atMs (a corrupt or hand-edited localStorage value) never expires at all: \"+N 入帳中\" is shown and /api/gold/state is polled every 30 s indefinitely on that browser. The unit test \"the note never outlives the display window\" only checks atMs <= now. No entitlement is involved (display only), so Info. Fix: treat a future atMs as invalid or clamp it: `Number.isSafeInteger(v.atMs)&&v.atMs<=now+60_000&&now-v.atMs<PENDING_CREDIT_MAX_MS` (a small forward tolerance for ordinary skew), and have settlePendingCredits rewrite the list so such entries are removed from storage.","line":22,"path":"Submission14/source/src/world/pendingGoldCredit.ts","reproduction":"Run against the module itself (Node 24, the three files pendingGoldCredit.ts, goldPurchaseRules.ts, worldGoldClient.ts copied to a temp dir, localStorage stubbed with a Map): const NOW=1_793_000_000_000,H=3_600_000; trackPendingCredit(W,ID,TX,'1000',NOW+3*H) (device clock 3 h ahead at payment). Then pendingGold(W,NOW+PENDING_CREDIT_MAX_MS) -> 1000n (expected 0n: the window is over in real time); pendingGold(W,NOW+5*H-60_000) -> 1000n; settlePendingCredits(W,[],NOW+PENDING_CREDIT_MAX_MS) -> false and the stored list still has 1 entry. With the stored entry's atMs set to Number.MAX_SAFE_INTEGER, pendingGold(W,NOW+10*365*24*H) -> 1000n (never expires). Output observed: \"at real NOW+2h (display window over): 1000 expected 0\", \"atMs=MAX_SAFE_INTEGER, 10 years later: 1000 expected 0\".","severity":"info","snippet":" return stored(address).filter((p):p is PendingCredit=>{const v=p as PendingCredit;return !!v&&/^0x[0-9a-f]{64}$/.test(v.orderId)&&/^[1-9][0-9]*$/.test(v.gold)&&typeof v.hash==='string'&&Number.isSafeInteger(v.atMs)&&now-v.atMs<PENDING_CREDIT_MAX_MS;});","title":"readPendingCredits keeps a note whose atMs is in the future, so a clock correction (or a corrupt entry) makes the note and its 30 s polling outlive the 2 h display window"},{"citation":"resolved","description":"For the wallet's own hash, a non-routed transaction (`same` true) is judged immediately: status 0x0 gives phase 'error' with TRANSACTION_REVERTED (\"鏈上交易失敗\"). For a routed transaction (MetaMask Delegation Manager, receipt.to != tx.to) with status 0x0, `done` is false because the intendedEffect shortcut requires `r.status==='0x1'`, so the receipt falls into the replacement path: it first throws TRANSACTION_NOT_FINALIZED (line 240) until the block is finalized, leaving the phase 'unknown' for about 13-15 minutes, and then ends as phase 'replaced' with TRANSACTION_REPLACED. goldPurchaseError has no entry for TRANSACTION_REPLACED, so the player sees the generic \"Unable to complete. Refresh the original order before retrying. Reference: TRANSACTION_REPLACED\" instead of the dedicated TRANSACTION_REVERTED copy (\"Transaction reverted ... network fees may still apply\"). The verdict (not confirmed) is correct, so this is not a false accept, but it is a misclassified and delayed reject for exactly the wallet type the owner uses, and it keeps the \"+N crediting\" note alive for the whole wait (paymentFailed is false while the phase is 'unknown'). tests/economy-wallet-recovery.test.mjs encodes this ('reverted' -> TRANSACTION_REPLACED) with a fixture that is already finalized, so the 15 minute 'unknown' stretch is not exercised. Fix: treat the wallet-returned hash with a failed receipt the same way in both routings: `const reverted=tx.version===2&&tx.hash?.toLowerCase()===candidate.toLowerCase()&&r.status==='0x0'`; when `reverted`, skip the finality block and set phase 'error', error TRANSACTION_REVERTED (nothing succeeded in that transaction, and the nonce is consumed exactly as in the non-routed reverted case the code already accepts without finality).","line":234,"path":"Submission14/source/src/world/economyWallet.ts","reproduction":"Fixture as in tests/economy-wallet-recovery.test.mjs: prepare(payPlan()); confirm() -> wallet returns HASH; candidate(routed([], {status:'0x0'})) with the fixture's finalized flag set to false (finalized block 0x1f < receipt block 0x20); checkReceipt(). Expected: phase 'error', error TRANSACTION_REVERTED, as for the non-routed reverted case. Actual: fail() with TRANSACTION_NOT_FINALIZED -> phase 'unknown'. Set finalized=true and checkReceipt() again: phase 'replaced', error TRANSACTION_REPLACED (the test file asserts this value for its 'reverted' case at line 86-87). During the first part the pending note for the order is kept (paymentFailed requires phase 'replaced' or 'error').","severity":"info","snippet":"  const done=same||tx.version===2&&r.status==='0x1'&&tx.hash?.toLowerCase()===candidate.toLowerCase()&&intendedEffect(tx,(receipt as {logs?:unknown}).logs,this.config);","title":"A smart-account routed payment that reverted on chain is reported as TRANSACTION_REPLACED after a ~15 minute TRANSACTION_NOT_FINALIZED wait, instead of TRANSACTION_REVERTED at once"}],"hash":"89cef645585a7a411ee6a34a3743135ed9031fcbd53ba528cf896b836072e51c","nodeId":"4f35c08d-b941-4d56-93e4-11f7c995357a","outcome":"completed","summary":"Review of Submission14 is complete. The findings file at the repository root holds 4 findings: 2 Low, 2 Info. No Critical or High. No Solidity changed, so no Foundry proofs apply. No repository file was modified.\n\n**Question 1, finality-only GOLD: clean.** The fast-credit route regex is gone from `server/world-gold-api.ts`, so the path falls to the 404 branch before any budget, session or chain read. The Worker no longer imports `world-gold-fast.ts`, and the minute tick runs only the finalized indexer, `reconcileWorldGold` and delivery jobs. Every remaining credit path requires `economy_orders.status='paid'`, which only the finalized indexer writes. That holds for the cron, the per-order reconcile route and both admin repairs. Of `world-gold-fast.ts`, only `fastGoldReady` and `fastSettleStatements` are reachable, and the settle statements are no-ops without a `credited` row. Nothing left can insert a `world_gold_fast_credits` row, and the 0021 insert trigger would also need a pending order. The L-5 receipt parser is unreachable from production. One limit: `server/economy.ts` was not supplied in either submission, so I could not re-read the `/api/economy/*` routes.\n\n**Question 2, intendedEffect: no false accept or reject found.** The sender topic position, the argument words and the exactly-one filter are right for all four kinds, and the exact-one rule does what the request says. One Info finding: a routed payment that reverted on chain is held as TRANSACTION_NOT_FINALIZED for the finality wait, then labelled TRANSACTION_REPLACED, while the same reverted receipt without routing is reported TRANSACTION_REVERTED at once.\n\n**Question 3, pending note: one Low, one Info.**\n- **Low.** The drop of a reverted or replaced payment's note is keyed on the in-memory `wallet.plan`. After a reload or panel reopen the restored intent has no plan, so the note is never dropped and the top bar shows \"+N crediting\" and polls the ledger every 30 seconds for the full 2 hours. Fix: drop by the stored payment hash.\n- **Info.** An entry with a future `atMs` never expires, so a clock correction extends the note and polling, and a corrupt entry keeps them forever. Reproduced against the module itself.\n\n**Question 4, deploy tool: one Low, reproduced.** The post-build re-check runs before wrangler bundles the Worker from the working tree, and nothing re-checks git after the upload. Using the project's own synthetic harness, a tracked source edited just before bundling was uploaded with exit code 0, a record naming the start commit, and `dirty:false`. Fix: re-check the tree after wrangler returns and record it in the outcome, or bundle first, re-check, then upload that exact bundle.\n\nNot covered: 3D, media, Celestao and AIMarket per scope, and the unsupplied server files named above.","treeHash":null,"usage":{"cachedInputTokens":2829152,"inputTokens":546,"model":"claude-fable-5-1","outputTokens":36477,"runtime":"claude","turns":43,"wallClockMs":547122}},{"artifacts":[],"attempt":1,"bundleHash":null,"device":"a11137177fc72d8b","findings":[{"citation":"resolved","description":"finishReceipt() only treats a routed (EIP-7702 / MetaMask Delegation Manager) transaction as `done` when r.status==='0x1' and intendedEffect() matches. A routed receipt with status 0x0 therefore falls into the `!done` branch: it first requires the block to be finalized (TRANSACTION_NOT_FINALIZED for the ~13 minutes after inclusion, phase 'unknown'), and once final it is classified as phase 'replaced' / TRANSACTION_REPLACED. The 'error' / TRANSACTION_REVERTED branch at line 251 is reachable only when `same` is true, i.e. only for a plain EOA whose mined `to`/input equal the request. The owner's production wallet is a MetaMask smart account (all six real receipts are routed), so for the deployed configuration the \"reverted payment drops its note at once\" fix in 64396ae does not apply: pendingGoldCredit.paymentFailed() is false for phase 'unknown' and for error TRANSACTION_NOT_FINALIZED, the note created at phase 'pending' stays, purchaseStage() maps phase 'unknown' to 'pending', and GoldPurchase renders `Crediting: +1,000 GOLD, expected by HH:MM (about 15 minutes)` next to an error alert for a payment that sent no EMBEO and will never be credited. The HUD's 30 s poll can only settle a credited order, so nothing else removes the note until PENDING_CREDIT_MAX_MS (2 h) unless the user presses \"Check original transaction\" again after finality. Boundary: receipt `status` 0x0 on the routed branch; assumption: a wallet-returned hash whose receipt reverted is recognised as reverted; actual: it is treated as a possible replacement and waits for finality. Fix: when `tx.hash` equals the candidate (the wallet's own returned hash, nonce already matched against the record) and r.status==='0x0', classify it as reverted regardless of routing, e.g. `const reverted=tx.version===2&&tx.hash?.toLowerCase()===candidate.toLowerCase()&&r.status==='0x0'; const done=same||reverted||...` and keep `phase:'error',error:'TRANSACTION_REVERTED'` for it; add a test with a routed candidate `{to:DELEGATION,status:'0x0',logs:[]}` expecting TRANSACTION_REVERTED without a finality wait.","line":234,"path":"Submission14/source/src/world/economyWallet.ts","reproduction":"Fixture as in tests/economy-wallet-recovery.test.mjs with receiptAttempts:1 and finalized block 0x1f < receipt block 0x20. prepare(payPlan()) ; confirm() -> wallet returns HASH, state passes through {phase:'pending',step:'execute',hash:HASH} so paymentHash() returns HASH and the panel calls trackPendingCredit(wallet,OID,HASH,'1000'). Candidate receipt/record: {hash:HASH,from:A,to:DELEGATION_MANAGER,input:'0xcef6d209'+64 zeros,nonce:'0x8',status:'0x0',logs:[]} (a reverted routed pay). checkReceipt() -> expected: phase 'error', error 'TRANSACTION_REVERTED', paymentFailed()=true, note dropped. Actual (run with node 24 on copies of economyWallet.ts/economyClient.ts/pendingGoldCredit.ts): phase 'unknown', error 'TRANSACTION_NOT_FINALIZED', paymentFailed()=false, pendingGold(A)=1000n (note still shown). With the finalized block advanced to 0x20 and checkReceipt() again: phase 'replaced', error 'TRANSACTION_REPLACED' (wrong code; no 'reverted' copy shown). Control: the same candidate with to/input equal to the request (EOA) gives phase 'error', TRANSACTION_REVERTED immediately.","severity":"low","snippet":"  const done=same||tx.version===2&&r.status==='0x1'&&tx.hash?.toLowerCase()===candidate.toLowerCase()&&intendedEffect(tx,(receipt as {logs?:unknown}).logs,this.config);","title":"Smart-account routed payment that reverts is reported as NOT_FINALIZED then REPLACED, never REVERTED, so the \"+N crediting\" note survives and the purchase page shows a credit ETA for a failed payment"},{"citation":"resolved","description":"The order whose note should be dropped is derived from `wallet.plan` (line 80) and the drop effect at line 83 runs only when `payingOrder` is non-null. EconomyWalletController.adoptPending() (used by restorePending() when the panel is opened again or the page reloads, and when another tab's intent is adopted) sets `plan:null` while keeping `hash` and `step:'execute'`. A player who pays, closes the panel or reloads (the very case the localStorage note was built for), then reopens and presses \"Check original transaction\" on a payment that reverted or was cancelled reaches phase 'error'/TRANSACTION_REVERTED or 'replaced'/TRANSACTION_REPLACED with plan null: paymentFailed() is true but payingOrder is null, so the \"+N crediting\" note stays in the top bar and in GoldPurchase (stage 'pending' for phase 'unknown' before the check; after the check the page shows the error while the HUD still shows +N crediting) until PENDING_CREDIT_MAX_MS. Nothing else removes it: the HUD's 30 s poll (settlePendingCredits) only drops notes whose order the ledger credited, and an unpaid order is never credited. Boundary: the restore path's `plan:null` × the drop condition. Fix: drop by hash instead of by plan: the note already stores `hash`; when paymentFailed(wallet) and wallet.hash is set, remove every note of data.wallet whose `hash` equals wallet.hash (and keep the plan-based drop as a second key), or persist the orderId in the stored Pending intent and read it back in adoptPending.","line":80,"path":"Submission14/source/src/world/WorldGoldPanel.tsx","reproduction":"Same fixture as the recovery tests (node 24, copies of the modules): c1=new EconomyWalletController(deps); prepare(payPlan()); confirm() -> HASH returned, note tracked by the panel; c1.dispose() (panel closed / reload). c2=new EconomyWalletController(same deps/store); c2.restorePending() -> state {phase:'unknown',plan:null,hash:HASH}. Candidate: EOA receipt {hash:HASH,to:PAY,input:<pay calldata>,nonce:'0x8',status:'0x0'}. c2.checkReceipt() -> state {phase:'error',error:'TRANSACTION_REVERTED',plan:null}. In WorldGoldPanel: payingOrder = wallet.plan?.kind==='pay' ? ... : null = null, so the effect at line 83 `if(data&&payingOrder&&failed)dropPendingCredit(...)` does not run. Expected: the note for OID is removed when the revert is confirmed; actual: pendingGold(wallet) stays 1000n until 2 h after atMs. The same holds for phase 'replaced' (cancelled payment) after a reopen.","severity":"low","snippet":" const payingOrder=wallet.plan?.kind==='pay'?wallet.plan.payment.orderId:null,payingHash=paymentHash(wallet),failed=paymentFailed(wallet);","title":"Reverted or replaced payment's note is dropped only while the sending panel instance is mounted: after a reload or panel reopen wallet.plan is null, so dropPendingCredit() is never called and the note"},{"citation":"resolved","description":"The post-build re-check (git status, HEAD, `.env*` file names) covers files. `runStep` and `run` spawn tsc, `vite build` and `wrangler deploy` with `{...process.env}`. Vite's loadEnv() exposes every process.env key beginning with VITE_ to the client bundle as import.meta.env.VITE_* (prioritised over .env files), so `VITE_API_BASE=https://evil.example npm run deploy` on a clean tree at a recorded commit uploads a dist/ whose behaviour the commit does not describe, while manifest.json says dirty:false, envFiles:[] and tests:'passed'. Likewise vite and wrangler bundle whatever is in the gitignored node_modules/ (a locally edited dependency file, or a tree installed from a different lockfile) and the manifest only hashes package-lock.json, which does not prove node_modules matches it. Both are paths that upload something other than the recorded commit, which is the property the tool is meant to guarantee (header comment lines 10-13). Operator-side only (no remote attacker), hence low. Fix: (1) refuse a real deploy when any process.env key matches /^VITE_/ (and record the names on a dry run, as envFiles does), and spawn the build with an explicit minimal environment rather than the inherited one; (2) record `npm ls --json` / `node_modules/.package-lock.json` hash or run `npm ci` as the first step and refuse if it changes the tree; at minimum write the list of VITE_ keys and the node_modules lock hash into the manifest so the record is honest.","line":67,"path":"Submission14/source/scripts/deploy.mjs","reproduction":"On a clean checkout at HEAD with a source file reading import.meta.env.VITE_X (or any Vite-built file: vite also injects import.meta.env.MODE etc.), run `VITE_X=injected node scripts/deploy.mjs --dry-run --skip-tests`. Expected: the deploy refuses, or the manifest records the extra input. Actual: the run passes (`dirty:false`, `envFiles:[]`), dist/ contains the string `injected`, and the SHA256SUMS / manifest attribute the bundle to the commit alone. Second input: edit one line in node_modules/<dep>/dist/index.js, run the same command: git status is clean, the manifest's config hashes (wrangler.jsonc, package-lock.json, public/_headers) are unchanged, and the altered code is in dist/ and worker/index.js.","severity":"low","snippet":"  const env={...process.env};delete env.NODE_TEST_CONTEXT;","title":"Deploy provenance misses two build inputs the record does not name: VITE_* variables in the deploy shell's environment and the local node_modules tree are baked into dist/ and the Worker bundle while "},{"citation":"resolved","description":"readPendingCredits keeps an entry while `now-v.atMs<PENDING_CREDIT_MAX_MS`. When atMs is in the future (the device clock was ahead at payment time and later corrected, e.g. by NTP or a manual fix; Date.now() is wall-clock and not monotonic), the difference is negative, so the entry is kept until the clock passes atMs+2h, which can be arbitrarily far away. While it is kept, WalletBalanceHud.usePendingGold polls /api/gold/state every 30 s in every open tab (bounded in the design only by the 2 h window) and GoldPurchase shows `expected by <atMs+15min>`, a time in the future that never arrives. Display only, operator-independent, and the note is still dropped as soon as the ledger credits the order, so info. Boundary x precision seam: an elapsed-time bound evaluated on a signed difference without a lower bound. Fix: also require `v.atMs<=now+SKEW` (e.g. 5 min) in the filter, or clamp atMs to now when tracking and when reading.","line":22,"path":"Submission14/source/src/world/pendingGoldCredit.ts","reproduction":"trackPendingCredit(W,ID,TX,'1000',NOW+365*24*3600_000) (what trackPendingCredit stores when Date.now() is a year ahead), then after the clock is corrected: pendingGold(W,NOW+PENDING_CREDIT_MAX_MS+1) -> expected 0n (past the 2 h window), actual 1000n; readPendingCredits(W,NOW).length -> 1. Run on a copy of pendingGoldCredit.ts with node 24: prints `future-stamped note visible at NOW+2h+1ms: true`.","severity":"info","snippet":" return stored(address).filter((p):p is PendingCredit=>{const v=p as PendingCredit;return !!v&&/^0x[0-9a-f]{64}$/.test(v.orderId)&&/^[1-9][0-9]*$/.test(v.gold)&&typeof v.hash==='string'&&Number.isSafeInteger(v.atMs)&&now-v.atMs<PENDING_CREDIT_MAX_MS;});","title":"Display-window bound uses a signed difference: a note stamped later than the current clock (atMs > now) never ages out, so the 2 h display and 30 s polling bound does not hold after a backward clock c"}],"hash":"b3d6406128c748919f687fe6e0d15f44d1e893fb1f0a371e654710fde9ebd21f","nodeId":"597f0907-cd58-4ab6-a674-493ce8bf38d8","outcome":"completed","summary":"Review complete. `.imd-findings.json` holds four findings, three Low and one Info. No Solidity changed, so none carries a Foundry proof; each was reproduced with Node 24 on scratch copies of the modules (under `test/scratch/`, not kept).\n\n**Findings**\n\n- **Low, routed revert misclassified** (`Submission14/source/src/world/economyWallet.ts:234`). A smart-account routed payment with receipt status 0x0 is never `done`, so it waits for finality (TRANSACTION_NOT_FINALIZED, phase unknown) and is then called TRANSACTION_REPLACED. The TRANSACTION_REVERTED branch is reachable only for a plain EOA. Since the owner's wallet is a MetaMask smart account, the \"reverted payment drops its note at once\" fix does not apply in production: the \"+N crediting\" note and a credit ETA stay on screen for a payment that sent nothing. Fix: treat the wallet's own hash with status 0x0 as reverted regardless of routing.\n- **Low, note drop depends on wallet.plan** (`Submission14/source/src/world/WorldGoldPanel.tsx:80`). After a reload or panel reopen, `adoptPending` restores the intent with `plan:null`. A revert or cancellation confirmed afterwards sets `paymentFailed` true but `payingOrder` is null, so the note is never dropped and lives the full 2 hours. The HUD poll can only settle credited orders. Fix: drop by the stored `hash` as well as by plan.\n- **Low, unrecorded build inputs** (`Submission14/source/scripts/deploy.mjs:67`). The build inherits the deploy shell's environment, and Vite exposes any `VITE_*` variable to the bundle, so a clean tree can ship behaviour the recorded commit does not describe while the manifest says dirty:false, envFiles:[]. The gitignored node_modules tree is likewise bundled with only package-lock.json hashed. Fix: refuse or record `VITE_*` keys and the installed-tree lock hash.\n- **Info, signed elapsed-time bound** (`Submission14/source/src/world/pendingGoldCredit.ts:22`). `now-atMs<2h` keeps a future-stamped note (clock corrected backwards) indefinitely, so the display and the 30 s polling outlive the intended bound. Fix: also require `atMs<=now+skew`.\n\n**Checked and clean**\n\n- Fast credit: within the provided files the only INSERT into world_gold_fast_credits is in `fastCreditTopup`, which nothing imports except tests. `repair-topup` and `credit()` require status paid, and the only status writer seen derives from finalized chain events. The SQL insert trigger also demands a pending top-up. I could not inspect server files absent from both submissions, so this is coverage of the public copy only.\n- `intendedEffect`: approve, pay, deposit and withdraw were probed with uppercase hex, zero and max amounts, extra topics, wrong data lengths, wrong contract, truncated calldata, foreign-sender duplicates and non-array logs. No false accept or false reject found.\n- Pending note keying is per lowercase session address and the server snapshot wallet, with no cross-wallet leak found. Polling is bounded by the 2 hour window except for the Info case above.","treeHash":null,"usage":{"cachedInputTokens":3302900,"inputTokens":546,"model":"claude-fable-5-1","outputTokens":43285,"runtime":"claude","turns":48,"wallClockMs":705675}},{"artifacts":[],"attempt":1,"bundleHash":null,"device":"5dd3af20ad7ac0c2","findings":[{"citation":"resolved","description":"finishReceipt() treats the wallet-returned hash as settled (\"done\") only when `same` is true (a direct EOA transaction whose mined to/input equal the request) or when the routed receipt has status 0x1 and intendedEffect() matches. A routed receipt (MetaMask Delegation Manager / EIP-7702, the only wallet configuration the owner uses for real transactions, AUDIT_REQUEST.md section 5) with status 0x0 therefore makes `done` false although it is the intent's own transaction: the hash is the one the wallet returned, and line 229 has already bound the mined record to the intent's nonce and sender, so nothing else can have run under that nonce. The `!done` branch at line 238 then demands finality: eth_getBlockByNumber('finalized') is below the receipt's block for about 13 to 15 minutes, so line 240 throws TRANSACTION_NOT_FINALIZED, fail() sets phase 'unknown', and once final line 251 maps `!done` to phase 'replaced' / TRANSACTION_REPLACED. The 'error' / TRANSACTION_REVERTED outcome at line 251 is reachable only for the direct-EOA case. Consequences: (1) the player never sees the TRANSACTION_REVERTED copy; TRANSACTION_REPLACED has no entry in goldPurchaseError, so the generic \"Reference: TRANSACTION_REPLACED\" line is shown for a revert; (2) pendingGoldCredit.paymentFailed() is false for phase 'unknown', so the note created at phase 'pending' (WorldGoldPanel.tsx line 81-82) stays: the top bar shows \"+1,000 crediting\", GoldPurchase shows \"Crediting: +1,000 GOLD, expected by HH:MM\" with purchaseStage 'pending', WalletBalanceHud polls /api/gold/state every 30 s, and the page says \"do not pay again\", for a payment that sent no EMBEO and will never be credited. The note goes only when the user presses \"Check original transaction\" again after finality (then as 'replaced') or after PENDING_CREDIT_MAX_MS (2 h). tests/economy-wallet-recovery.test.mjs line 84-87 encodes the wrong code ('reverted' -> TRANSACTION_REPLACED) with an already-finalized fixture, so the 'unknown' stretch is not exercised. Verdict safety is not affected (no false accept): this is a misclassified and delayed reject. Fix (minimal, keeps the finality rule for pasted and replacement hashes): in finishReceipt compute `const reverted=tx.version===2&&r.status==='0x0'&&tx.hash?.toLowerCase()===candidate.toLowerCase();` and use `const done=same||reverted||(...)`; the existing line 251 then yields phase 'error' / TRANSACTION_REVERTED at once (the nonce was consumed by this very transaction, exactly as in the direct-EOA case the code already judges without finality). Update the 'reverted' case of the recovery test to expect TRANSACTION_REVERTED with finalized=false, and give goldPurchaseError a TRANSACTION_REPLACED copy. Merged from four specialist reports (economics, permissions, math, flow) of the same mechanism.","line":234,"path":"Submission14/source/src/world/economyWallet.ts","reproduction":"Harness: copies of economyWallet.ts and pendingGoldCredit.ts with the fixture of tests/economy-wallet-recovery.test.mjs (Node 24.21, receiptAttempts 1, finalized block 0x1f < receipt block 0x20), plus the WorldGoldPanel effects (trackPendingCredit when paymentHash() is non-null; dropPendingCredit when payingOrder && paymentFailed()). Steps: prepare(payPlan()) for order OID gold 1000; confirm() -> wallet returns HASH, state passes {phase:'pending',step:'execute',hash:HASH} so the note is tracked; candidate = routed receipt {hash:HASH, to:DELEGATION_MANAGER, input:'0xcef6d209'+64 zeros, nonce:'0x8', status:'0x0', logs:[]}; checkReceipt(). Expected (as for the direct-EOA control): phase 'error', error 'TRANSACTION_REVERTED', paymentFailed()=true, pendingGold(A)=0n. Actual: {phase:'unknown', error:'TRANSACTION_NOT_FINALIZED', paymentFailed:false} and pendingGold(A)=1000n; a second checkReceipt() gives the same; with the finalized block advanced to 0x20 checkReceipt() gives {phase:'replaced', error:'TRANSACTION_REPLACED'} and only then pendingGold(A)=0n. Control run in the same harness, direct EOA candidate {to:PAY, input:<pay calldata>, status:'0x0'} with finalized=false: {phase:'error', error:'TRANSACTION_REVERTED', paymentFailed:true}, pendingGold 0n immediately.","severity":"low","snippet":"  const done=same||tx.version===2&&r.status==='0x1'&&tx.hash?.toLowerCase()===candidate.toLowerCase()&&intendedEffect(tx,(receipt as {logs?:unknown}).logs,this.config);","title":"Smart-account routed payment that reverted (status 0x0) is held as TRANSACTION_NOT_FINALIZED for the finality wait and then reported as TRANSACTION_REPLACED, never TRANSACTION_REVERTED; the \"+N credit"},{"citation":"resolved","description":"The order whose note must be dropped is derived at line 80 from `wallet.plan`, and the drop effect at line 83 runs only when `payingOrder` is non-null. The plan exists only in the controller instance that sent the payment. WorldGoldPanel recreates the controller on every identity change and on panel reopen (line 75: new EconomyWalletController, then restorePending()), and EconomyWalletController.adoptPending() (economyWallet.ts line 91) restores the intent with `plan:null` while keeping hash and step 'execute'. A player who pays, closes the panel or reloads (the case the localStorage note was built for, line 76-77), reopens and presses \"Check original transaction\" on a payment that reverted or was cancelled reaches phase 'error'/TRANSACTION_REVERTED or 'replaced'/TRANSACTION_REPLACED with plan null: paymentFailed(wallet) is true but payingOrder is null, so dropPendingCredit never runs. Nothing else removes the note: settlePendingCredits (pendingGoldCredit.ts line 43) drops only notes whose order the ledger credited, and an unpaid order is never credited, so the top bar shows \"+N crediting\" and WalletBalanceHud polls /api/gold/state every 30 s per tab until atMs + PENDING_CREDIT_MAX_MS (2 h). Display only (purchaseStage and pendingGold grant nothing), hence Low. Fix: key the drop on the stored note's hash, which the note already carries: when paymentFailed(wallet) && wallet.hash, remove every note of data.wallet whose hash equals wallet.hash (case-insensitive), keeping the plan-based drop as well; alternatively read the order id back from the restored intent (word 0 of tx.data when the selector is d0ee4de0). Independently, let settlePendingCredits drop a note whose order is past expires_ms plus a finality margin and still 'pending'. Merged from the permissions and math specialists.","line":83,"path":"Submission14/source/src/world/WorldGoldPanel.tsx","reproduction":"Harness over copies of economyWallet.ts and pendingGoldCredit.ts (fixture of tests/economy-wallet-recovery.test.mjs, shared store and lock between two controllers). c1.prepare(payPlan()); c1.confirm() -> HASH; the panel effect tracked the note (pendingGold(A)=1000n); c1.dispose() (panel closed / page reloaded). c2 = new controller on the same store; c2.restorePending() -> state {phase:'unknown', error:'PENDING_TRANSACTION', step:'execute', plan:null, hash:HASH}. Candidate: direct receipt for HASH {to:PAY, input:<pay calldata>, nonce:'0x8', status:'0x0'}; c2.checkReceipt() -> state {phase:'error', error:'TRANSACTION_REVERTED', plan:null}, paymentFailed(state)=true, stored intent cleared (store.size 0). The panel expression `wallet.plan?.kind==='pay'?wallet.plan.payment.orderId:null` evaluates to null, so the line-83 effect never calls dropPendingCredit. Expected: the note for OID is removed when the revert is confirmed; actual: pendingGold(A) stays 1000n. Same result for the replaced case: c2.checkReceipt(OTHER) with a finalized same-nonce record {hash:OTHER, to:A, input:'0x', nonce:'0x8'} -> {phase:'replaced', error:'TRANSACTION_REPLACED', plan:null}, payingOrder null, pendingGold(A) 1000n.","severity":"low","snippet":" useEffect(()=>{if(data&&payingOrder&&failed)dropPendingCredit(data.wallet,payingOrder);},[data,payingOrder,failed]);","title":"The \"+N crediting\" note is dropped only through the in-memory plan: after a reload or panel reopen (restorePending -> plan null) a reverted or replaced payment keeps its note until the 2 h window ends"},{"citation":"resolved","description":"paymentFailed() needs phase 'replaced' or 'error'+TRANSACTION_REVERTED. The controller reaches 'replaced' only when finishReceipt sees a mined receipt for some candidate hash carrying the intent's nonce. When the user cancels or speeds up the payment in MetaMask, the wallet-returned hash never mines: eth_getTransactionReceipt(HASH) stays null, waitReceipt ends after 40 x 3 s in phase 'unknown' / PENDING_TRANSACTION, and every later press of \"Check original transaction\" repeats that. The GOLD purchase page wires that button to controller.checkReceipt() with no hash argument (WorldGoldPanel.tsx line 105, GoldPurchase.tsx line 42) and has no field for the replacement hash (unlike the economy pane), and clearUnknownAfterWalletCheck() returns at once for a v2 intent with a hash. So the note created at broadcast (phase 'pending') is never dropped: the HUD shows \"+1,000 crediting\", GoldPurchase shows \"Crediting: +1,000 GOLD, expected by HH:MM\" (purchaseStage 'pending' for phase 'unknown') and tells the user not to pay again, and WalletBalanceHud polls /api/gold/state every 30 s per open tab (about 240 requests) until PENDING_CREDIT_MAX_MS. This is the \"replaced\" case AUDIT_REQUEST.md section 2 says the note is dropped on, and it is not covered by tests/pending-gold-credit.test.mjs. Display only, Low. Fix options, smallest first: (a) when the wallet sits in 'unknown'/PENDING_TRANSACTION with a hash whose receipt is null, read eth_getTransactionCount(from,'latest'); if it exceeds the stored intent nonce, the nonce was consumed by another transaction and the panel can call dropPendingCredit (and the controller can move to 'replaced' once the finalized count also exceeds it); (b) give the GOLD purchase page the replacement-hash entry the economy pane has, so checkReceipt(hash) can reach 'replaced'; (c) at minimum let settlePendingCredits drop a note whose order has passed expires_ms plus a finality margin while still 'pending'. Reported by the flow specialist; also the second input of the economics report.","line":37,"path":"Submission14/source/src/world/pendingGoldCredit.ts","reproduction":"Harness over copies of economyWallet.ts and pendingGoldCredit.ts with the panel effects (fixture of tests/economy-wallet-recovery.test.mjs). prepare(payPlan()); confirm() -> wallet returns HASH and the note is tracked (pendingGold(A)=1000n); eth_getTransactionReceipt(HASH) answers null on every poll (the user cancelled in MetaMask; the cancel transaction consumed nonce 0x8). Observed after confirm(): {phase:'unknown', error:'PENDING_TRANSACTION', step:'execute', paymentFailed:false}; after checkReceipt(): unchanged; after clearUnknownAfterWalletCheck(): unchanged, stored intent still present (store.size 1); pendingGold(A) = 1000n at NOW + 1 h 59 min 59.999 s and 0n only at NOW + 2 h. Expected: once the wallet hash can no longer be mined (nonce consumed by another transaction), the note is dropped and the purchase page stops saying the GOLD is being credited.","severity":"low","snippet":"export function paymentFailed(wallet:{phase:string;step:string|null;error:string|null}){return wallet.step==='execute'&&(wallet.phase==='replaced'||wallet.phase==='error'&&wallet.error==='TRANSACTION_REVERTED');}","title":"A payment cancelled or replaced in the wallet (same nonce, other hash) never reaches a failed phase from the GOLD purchase page, so the \"+N crediting\" note and the 30 s polling last the full 2 h"},{"citation":"resolved","description":"The post-build re-check (lines 147-150: HEAD, git status --porcelain, .env*) runs before `wrangler deploy` (line 159). wrangler does not upload a prebuilt artifact: wrangler.jsonc names `main: worker/index.ts`, and wrangler bundles it and everything it imports (worker/app.ts, server/*.ts, the shared src/world modules) with esbuild from the working tree at upload time. Nothing re-reads git status or HEAD after wrangler returns; the only post-upload verification is distAfter (line 162), which covers dist/ (static assets) and not the Worker bundle's inputs, and the manifest's `dirty` (line 167) is computed only from the two earlier readings. A tracked-file edit (editor autosave, a test watcher, a checkout or stash pop in another terminal: the same class of event as the 40e97f4 incident, now in a window of seconds rather than minutes) that lands after line 147 and before wrangler reads the tree is uploaded as the Worker, while manifest.json says commit = start HEAD, dirty:false, and the outcome is uploaded / passed / persisted with exit 0. The header comment (lines 10-11: \"the record's commit is exactly what shipped\") and the Submission14 evidence that Worker 40c6477f is source 64396ae rely on this guarantee, which holds only for dist/. tests/deploy.test.mjs's edited / head-moved / env-added variants all inject the change during vite build, so none covers this window. Fix (minimal, keeps the design): after `run([...wrangler deploy...])` read `git rev-parse HEAD`, `git status --porcelain --untracked-files=normal` and envFiles(ROOT) a third time; if any differs from the start, set manifest.dirty=true, record the porcelain text (e.g. tree.changedDuringUpload) and feed it into deploymentOutcome so verification is 'failed' and exitCode 1 (the upload cannot be undone, but the record must not say clean). Stronger: bundle first with `wrangler deploy --dry-run --outdir <tmp>`, re-check the tree, then upload that exact bundle with `wrangler deploy --no-bundle`, or at least compare the uploaded worker/index.js hash with a fresh dry-run bundle hash taken after the final git check (the header states index.js is byte-identical across runs of the same commit). Merged from the economics, permissions and flow specialists.","line":159,"path":"Submission14/source/scripts/deploy.mjs","reproduction":"Throwaway git repo (Node 24.21, git 2.43, no network) with Submission14/source/scripts/deploy.mjs copied to scripts/, package.json {type:module, scripts.test:'node --test tests/*.test.mjs'}, a passing tests/a.test.mjs, tracked worker/app.ts = \"export default {fetch(){return new Response('committed');}}\", tracked src/runtime.ts = 'committed', .gitignore 'deploy-records/ node_modules/ dist/ .env*', package-lock.json '{}', wrangler.jsonc '{}', public/_headers, and the same fake node_modules/{typescript,vite,wrangler} as tests/deploy.test.mjs, except that node_modules/wrangler/bin/wrangler.js first writes worker/app.ts = \"...Response('EDITED AFTER THE POST-BUILD CHECK')...\" and src/runtime.ts = 'edited after the check', then writes <outdir>/index.js from the current worker/app.ts (what the real wrangler does when it bundles main from the tree) and prints 'Current Version ID: 2a5bf0b7-...'. Commit everything; run `node scripts/deploy.mjs` (real mode). Expected: refusal (\"nothing was uploaded\") or exit 1 with the record marked dirty, as for the 'edited' and 'head-moved' variants of tests/deploy.test.mjs. Actual (run 2026-10-27): exit code 0; outcome line {\"deployment\":\"uploaded\",\"verification\":\"passed\",\"recordPersistence\":\"persisted\",\"exitCode\":0}; manifest.json has commit = start HEAD 0907577..., dirty:false, wrangler.exitCode 0, dist.unchangedDuringDeploy true, envFiles []; deploy-records/<name>/worker/index.js contains \"EDITED AFTER THE POST-BUILD CHECK\" while `git show HEAD:worker/app.ts` prints 'committed'; `git status --porcelain` after the run prints \" M src/runtime.ts\" and \" M worker/app.ts\".","severity":"low","snippet":"  const w=run(['node_modules/wrangler/bin/wrangler.js','deploy',...(dryRun?['--dry-run']:[]),'--outdir',join(tmp,'worker')],","title":"deploy.mjs re-checks the tree and HEAD before wrangler bundles the Worker sources; an edit landing between the re-check and wrangler's esbuild pass ships under the recorded commit with dirty:false and"},{"citation":"resolved","description":"The provenance record covers the git tree (start and post-build), .env* file names, and the hashes of wrangler.jsonc, package-lock.json and public/_headers; the build and bundle themselves are produced from gitignored inputs the record does not verify. (1) node_modules: tsc, vite and wrangler are run from node_modules/* (buildSteps line 62, run line 159) and bundle every dependency from it (react, three, viem helpers into dist/; the Worker's dependencies into worker/index.js). The manifest reads the three tools' version fields (pkgVersion, line 136) and hashes package-lock.json, but nothing runs `npm ci` or `npm ls` or compares node_modules/.package-lock.json with the lockfile, and `git status --porcelain --untracked-files=normal` cannot see an ignored directory. A stale tree (pull without npm ci), a package installed with --no-save, or a locally patched dependency file therefore ships under the recorded commit with dirty:false and tests 'passed', the same class of gap as the .env* re-check closed in 64396ae. (2) Environment: runStep (line 67) and run (line 132) spawn every step with `{...process.env}`; Vite's loadEnv exposes every process.env key starting with VITE_ to the client bundle as import.meta.env.VITE_* with priority over .env files, so `VITE_X=... npm run deploy` on a clean tree changes dist/ while manifest.json says dirty:false and envFiles []. The public source tree shows no import.meta.env.VITE_* read today, so (2) is a latent path that becomes live with the first such read; (1) is live now. Operator-side only (no remote attacker), hence Low. Fix: make the first real-deploy step `npm ci --ignore-scripts` (or refuse when `npm ls --package-lock-only --json` reports an invalid tree) and record its exit code in the manifest; spawn the build with an explicit minimal environment (PATH, HOME, the WRANGLER_* keys) rather than the inherited one, refusing a real deploy when any process.env key matches /^VITE_/ and listing such keys on a dry run as envFiles does. Merged from the economics, math and flow specialists.","line":67,"path":"Submission14/source/scripts/deploy.mjs","reproduction":"Same throwaway repo as the wrangler-window reproduction (fake node_modules as in tests/deploy.test.mjs, tracked src/runtime.ts). (1) node_modules: edit the gitignored node_modules/vite/bin/vite.js so it writes dist/index.html 'patched dependency output' (in the real project: any file under node_modules/react/ or node_modules/three/ that vite bundles); `git status --porcelain --untracked-files=normal` is empty; `node scripts/deploy.mjs` passes both checks with exit 0, manifest commit = HEAD, dirty:false, config hashes unchanged, and dist/index.html (hashed into the record and uploaded as a static asset) contains output no file at the recorded commit produces. (2) Environment: the fake vite appends process.env.VITE_X to dist/index.html; `VITE_X=injected node scripts/deploy.mjs --dry-run --skip-tests` (run 2026-10-27) ends with {\"deployment\":\"dry-run-passed\",\"verification\":\"passed\",\"recordPersistence\":\"persisted\",\"exitCode\":0}, dist/index.html = 'abcinjected', and manifest.json {dirty:false, envFiles:[]}. Expected in both: a refusal, or a record that names the input that differed from the commit.","severity":"low","snippet":"  const env={...process.env};delete env.NODE_TEST_CONTEXT;","title":"deploy.mjs never verifies the gitignored build inputs it hashes nothing for: node_modules is not checked against the recorded package-lock.json, and VITE_* variables of the deploy shell reach vite unr"},{"citation":"resolved","description":"The display-window test is `now-v.atMs<PENDING_CREDIT_MAX_MS`, a signed difference with no lower bound. atMs is written from Date.now() at payment time (trackPendingCredit, line 30); Date.now() is wall-clock and not monotonic. If the device clock was ahead at payment time and is corrected afterwards (NTP sync, timezone fix, VM resume), the difference is negative and the entry is kept until the corrected clock passes atMs + 2 h, which can be arbitrarily far away; an entry with a very large atMs (corrupt or hand-edited localStorage) never expires at all. While kept, WalletBalanceHud.usePendingGold polls /api/gold/state every 30 s in every open tab (the design bounds this only by the 2 h window) and GoldPurchase shows \"expected by <atMs+15 min>\", a time that never arrives. The unit test \"the note never outlives the display window\" checks only atMs <= now. Display only and still dropped as soon as the ledger credits the order, so Info. Fix: also require `v.atMs<=now+SKEW` (e.g. 5 min) in the filter, or clamp atMs to now in trackPendingCredit and when reading, and have settlePendingCredits rewrite the list so such entries leave storage. Merged from the permissions and math specialists.","line":22,"path":"Submission14/source/src/world/pendingGoldCredit.ts","reproduction":"Node 24, pendingGoldCredit.ts with localStorage stubbed by a Map: const NOW=Date.UTC(2026,9,16), H=3_600_000; trackPendingCredit(W,ID,TX,'1000',NOW+3*H) (device clock 3 h ahead at payment); then, after the clock is corrected: pendingGold(W,NOW+PENDING_CREDIT_MAX_MS) -> 1000n (expected 0n: the 2 h window is over in real time); settlePendingCredits(W,[],NOW+PENDING_CREDIT_MAX_MS) -> false and readPendingCredits(W,NOW).length -> 1 (the stored entry is not removed). With the stored entry's atMs set to Number.MAX_SAFE_INTEGER: pendingGold(W,NOW+10*365*24*H) -> 1000n (never expires). Observed output: \"visible at real NOW+2h: 1000n settle removes? false stored 1\" and \"MAX_SAFE_INTEGER atMs, 10 years later: 1000n\".","severity":"info","snippet":" return stored(address).filter((p):p is PendingCredit=>{const v=p as PendingCredit;return !!v&&/^0x[0-9a-f]{64}$/.test(v.orderId)&&/^[1-9][0-9]*$/.test(v.gold)&&typeof v.hash==='string'&&Number.isSafeInteger(v.atMs)&&now-v.atMs<PENDING_CREDIT_MAX_MS;});","title":"readPendingCredits keeps a note whose atMs is in the future, so after a backward clock correction (or a corrupt entry) the note and its 30 s polling outlive the 2 h display window, or never expire"}],"hash":"cd568964646b315feae067d4b72bc2e6d6779827494bd7611a7742d22c195dd1","nodeId":"a51672da-59f3-42f3-bb84-8f7873b951eb","outcome":"completed","summary":"The review is complete. The findings file at the repository root holds 6 findings, every one reproduced against the Submission14 code, merged from the 15 specialist reports. No repository file was changed; the harnesses lived under /tmp.\n\n**What reproduced (5 Low, 1 Info)**\n\n- **Routed revert misclassified** (economyWallet.ts line 234). A Delegation Manager payment with receipt status 0x0 is not treated as done, so it waits for finality as TRANSACTION_NOT_FINALIZED and then ends as TRANSACTION_REPLACED, never TRANSACTION_REVERTED. The crediting note survives the whole wait. Harness confirmed the direct-EOA control reverts at once while the routed case sits in phase unknown. Four specialist reports merged.\n- **Note drop keyed on the in-memory plan** (WorldGoldPanel.tsx line 83). After a reload or panel reopen, restorePending sets plan to null, so a confirmed revert or replacement never calls dropPendingCredit. Harness showed paymentFailed true, payingOrder null, pending gold still 1000. Two reports merged.\n- **Wallet cancel never reaches a failed phase** (pendingGoldCredit.ts line 37). The purchase page only checks the original hash and offers no replacement-hash entry, so a cancelled payment stays unknown and the note lasts the full 2 hours. Harness confirmed the note at 1 h 59 min and 0 only at 2 h.\n- **Deploy window before wrangler bundles** (deploy.mjs line 159). The post-build re-check runs before wrangler's esbuild pass reads the tree. A throwaway repo whose fake wrangler edits worker/app.ts before bundling shipped the edit under the start commit with dirty false and exit 0. Three reports merged.\n- **Unverified gitignored build inputs** (deploy.mjs line 67). node_modules is never checked against the hashed lockfile, and VITE_ variables pass through to the build unrecorded. Reproduced with a patched fake vite and with a VITE_X dry run. Three reports merged. The VITE_ path is latent today since the public sources read no such variable.\n- **Future atMs never ages out** (pendingGoldCredit.ts line 22, Info). A note stamped ahead of the corrected clock stays visible past the 2 h window, and a MAX_SAFE_INTEGER stamp never expires. Two reports merged.\n\n**What came out clean**\n\n- Question 1: the Worker no longer imports the fast-credit module, the route answers 404, and the only insert into the fast-credit table lives in a function nothing reachable calls. The remaining settle statements only update an existing credited row. Coverage is limited to the 17 supplied files plus the unchanged Submission13 copies; the full private server tree is not in the public snapshot.\n- Question 2: no false accept found in intendedEffect for approve, pay, deposit or withdraw. The one defect is the misclassified reject above, which is safe but wrong and slow.\n\nNothing carries a Foundry proof: no Solidity changed and no finding is High or Critical.","treeHash":null,"usage":{"cachedInputTokens":2566119,"inputTokens":546,"model":"claude-fable-5-1","outputTokens":31037,"runtime":"claude","turns":34,"wallClockMs":412724}}],"verification":[]}