{"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":"5b630958-5d27-4b47-bebf-4e682a6d2ab7","kind":"shape:chain","nodes":[{"acceptedSubmissionHash":"24766df360e75e89a76db4a17f9d53237f38cc8bffae9ad3d3e5d44a45b8fcfa","dependsOn":["integrate_project","implement_component","build_website","build_website_2"],"execution":{"network":false,"profile":"foundry","requires":[],"skillHash":"6b037a7b6601e883cf8a906c1520c0624817d42d8310b65c2f43679204608af3","skillId":"adversarial-review","tools":[]},"key":"adversarial_review","kind":"code","role":"review","skillHash":"6b037a7b6601e883cf8a906c1520c0624817d42d8310b65c2f43679204608af3","skillId":"adversarial-review","state":"accepted"},{"acceptedSubmissionHash":"02e70a020bd69f75c0059eb59c2e79619cd5dc2ce5fdb9271f5a172c6a89ac2c","dependsOn":["implement_component"],"execution":{"mustProduce":["dist/index.html"],"network":true,"optionalTools":["browser"],"profile":"none","requires":["network"],"skillHash":"a60bc8b42446c13d993341179fe13c712a5addb118fc532fb2d15e71f912efc8","skillId":"build-website","tools":[]},"key":"build_website","kind":"code","role":"implement","skillHash":"a60bc8b42446c13d993341179fe13c712a5addb118fc532fb2d15e71f912efc8","skillId":"build-website","state":"accepted"},{"acceptedSubmissionHash":"cc02443da7b4df34c21bbb48eb9487fc22194b4079c4ac837312e6d241d23943","dependsOn":["build_website"],"execution":{"mustProduce":["dist/index.html"],"network":true,"optionalTools":["browser"],"profile":"none","requires":["network"],"skillHash":"a60bc8b42446c13d993341179fe13c712a5addb118fc532fb2d15e71f912efc8","skillId":"build-website","tools":[]},"key":"build_website_2","kind":"code","role":"implement","skillHash":"a60bc8b42446c13d993341179fe13c712a5addb118fc532fb2d15e71f912efc8","skillId":"build-website","state":"accepted"},{"acceptedSubmissionHash":"cd337455fac93f08781ad5bfa03321732d5d9afc1d7fe34436222111ba13c78d","dependsOn":["integrate_project","implement_component","build_website","build_website_2","write_readme_and_docs"],"execution":{"network":true,"optionalTools":["browser"],"profile":"none","requires":["network"],"skillHash":"fd33b6741366d364a55a4c0ba2bc4f5d329cec0b62f27a83289dc5b30ab03575","skillId":"design-critic","tools":[]},"key":"design_critic","kind":"code","role":"review","skillHash":"fd33b6741366d364a55a4c0ba2bc4f5d329cec0b62f27a83289dc5b30ab03575","skillId":"design-critic","state":"accepted"},{"acceptedSubmissionHash":"577bff8ddd9001eef2a609808f0b2aa2f69ce5bddb23f8c7cb2c990a439c61d7","dependsOn":["integrate_project"],"execution":{"network":true,"profile":"none","requires":["network"],"skillHash":"15deecb0f930dd3c37b4e551121fe447b9d43818640ca22223cf3c7951e0db98","skillId":"implement-component","tools":[]},"key":"implement_component","kind":"code","role":"implement","skillHash":"15deecb0f930dd3c37b4e551121fe447b9d43818640ca22223cf3c7951e0db98","skillId":"implement-component","state":"accepted"},{"acceptedSubmissionHash":"d75f0e9d52b9938a31a48725d7474c7e2f5cf44995da26363968f8d135bd659c","dependsOn":[],"execution":{"network":true,"profile":"none","requires":["network"],"skillHash":"9efd723ed31bbb0747bce27bea59184a0681c23fb933fb2cc1d560029c52df8c","skillId":"integrate-project","tools":[]},"key":"integrate_project","kind":"code","role":"integrate","skillHash":"9efd723ed31bbb0747bce27bea59184a0681c23fb933fb2cc1d560029c52df8c","skillId":"integrate-project","state":"accepted"},{"acceptedSubmissionHash":"a8a354bc04d32b3c9652f4790ba69d97cc500ab938dabd17f593d1214e48c396","dependsOn":["adversarial_review"],"execution":{"network":false,"profile":"none","requires":[],"skillHash":"7e3e9a90f2342f72573636f539f0a3a3016b9f8bf53c7b77a17dc4608a09d0b2","skillId":"write-readme-and-docs","tools":[]},"key":"write_readme_and_docs","kind":"code","role":"implement","skillHash":"7e3e9a90f2342f72573636f539f0a3a3016b9f8bf53c7b77a17dc4608a09d0b2","skillId":"write-readme-and-docs","state":"accepted"}],"objective":"Integrate reviewed A1 repairs and delivered W1 into a usable CTO Relay Station, with actual browser-to-local-contract acceptance. One development job; no public deployment/hosting, funded credentials, live IMD purchases, paid child jobs, automatic spending, redesign, governance, ranking, tokens or treasury. Local deployments/mocks required. This scope supersedes old A1 'no frontend' and W1 'no core changes' ONLY for supplied repairs, browser SDK and integration. Preserve rules, CLI and W1 design.\n\nSOURCES: The preceding version blocked before edits. Its last delivered A1 source is identity-md-launches/launch-2294-build-cto-relay-station commit 9b0294e7ab19060bd00c489fbe032cf248528e0d; verify this starting HEAD. Fetch patch pinned below; verify 194891 bytes/SHA256, git apply --check, apply, then verify patched-baseline source-sha256.json before edits. Fail explicitly on missing/mismatched inputs, never recreate repairs from prose. At frontend step import W1 https://github.com/identity-md-launches/launch-2297-build-cto-relay-station commit d48bc2117a06c79c3b8545d15eeff2675da94a31 into frontend/. Preserve sources/tests/license/locked dependencies and record hashes. Keep Foundry and protected package/config at root, frontend dependencies under frontend/. Exclude .git/node_modules/caches. Preserve old evidence; new reports go in docs/validation/integration/.\n\nCORE: preserve patched output name/path/prefix collision guards, non-HTML dist restriction, full bounded raw-JSON validation including ignored metadata, duplicate decoded keys/UTF-8, matching SDK limits and v2 fixed policy. Rerun 125 Foundry/27 Node baseline tests. Finite manifest limits may still leave authenticated oversized results unbindable: retain this risk; no admin/timeout unlock, raw-result rewriting, refund or automatic retry. No contract rule redesign.\n\nSDK: factor shared canonical encoding/validation and browser/Node file adapters. Existing index/strict-json/files use Node crypto/fs/Buffer/DNS/HTTPS; provide real browser entrypoint, preserve CLI and verification. Compare both runtimes' calldata, witness/hash, manifest keccak, file SHA256 and malformed/Unicode/duplicate/limit rejection. Browser downloads use configured trusted artifact locations, bounded streaming, approved schemes/hosts/redirects and byte/length/format checks; disclose DNS limits. No mandatory proxy/indexer/signer/backend. Loopback fixture policy is test-only.\n\nW1: wire actual SDK/ABI/config into public read/workspace routes (currently demo-only), create, propose, explicitly sponsor, read files/history, continue and historical fork; also result binding/refusal retry. Creating/proposing/forking never buys AI. Current proposals remain selectable without FIFO or anchor changes; sponsors cannot edit proposals. Use actual fixed profiles/capacity/input/full-body limits, not fixture constants. Verify chain/Factory/Branch/policy. Generate local config from actual deployed code; enable tested local operations, keep missing public config closed. Distinct labeled fixtures, no silent fallback. Preserve Chinese responsive UI; root dist/ static export, relative assets/hash routing.\n\nSIGNING/STATE: selected EIP-6963 provider signs SDK unsigned transactions only. Refresh account/network/anchor/nonzero proposal/witnesses/price/maxFee/exact Branch allowance before signing; confirmed allowance precedes purchase. Record original hash/nonce/account/chain/operation immediately before receipt wait. Recover chain truth after reload with absent/corrupt storage; reconcile dropped/replaced/unknown tx, never auto-rebuy. Separate transaction/admission/execution/callback/binding/files/Git/publication. Missing callback stays pending; authenticated status1 allows explicit newly funded identical-body retry; status2/unknown freezes. Correct bad binding with original raw result, no purchase. HTTP cannot unlock/settle.\n\nFILES: verify actual Markdown/HTML/PNG bytes before badges. Sanitize Markdown; opaque-origin HTML sandbox/CSP isolates app storage/wallet, with scripts/network/top-navigation/popups/forms off by default. Public site URL is not acceptance. Branch latest job orders execution while saved assets retain original producers; fork copies exactly selected historical assets/provenance, not parent job/proposals/publishing identity.\n\nLOCAL MATRIX: actual final Solidity/SDK + W1 browser signing, two temporary accounts, MockToken/Intake, deterministic real bytes. Cover six functions and alternate funding, Markdown create/update; HTML R1 -> PNG R2 -> HTML R3 where R3 continues R2 and selects R1 HTML/R2 image. Verify markers/exact image/untouched provenance. Fork non-head checkpoint; child opens then continues with separate explicit sponsorship. Cover multiple proposals/stale anchors, exact approval/price changes, double-click/sign rejection/account-network changes, pending/unknown/replaced tx/reload/corrupt-missing journal, missing callbacks/status1 retry/status2-unknown freeze/corrected binding without repurchase, tampered/unavailable/oversized files/malicious preview/mobile/keyboard/focus. Browser actions must create actual local tx; old CLI replay or fixture adapter is insufficient.\n\nDELIVER: same-version contracts/ABI/Node+browser SDK/CLI/W1/export/config generator and reproducible clean-checkout local setup. Run build/typecheck/Foundry/SDK/fixture/browser-chain tests and independent review; repair serious defects and rerun affected checks. Retain commands/raw logs/screenshots/chain traces/final hashes; mark every matrix item PASS/FAIL/NOT_RUN, separate fixture vs actual-local-contract vs real-IMD. Keep productionDeployment:null, liveCompatibility:NOT_RUN, productionReady:false and manifest risk. Structural acceptance is not behavioral proof. Critical/High/core failures block readiness; disclose incomplete work instead of fake PASS or another paid order.\n\nSOURCE PINS: patch URL https://gist.githubusercontent.com/Goderbug/f58e7dc53cfd241e70d92ac510a801f5/raw/b052a8c9bdfefd8ab601f2d0f467ed9a710a281a/a1-review-fixes.patch ; patch SHA256 3028547365dae0b93d9a6191f15b9023fd59821c791d205e8c79cc16375a2989. Source manifest: https://gist.githubusercontent.com/Goderbug/f58e7dc53cfd241e70d92ac510a801f5/raw/3c17d6ecd2d284a4c8d40162ef6cd3bc2bdbfc0f/SOURCE-INPUTS.json.","parentJobId":"210ee6e0-e812-474b-824e-ac4f561ba60e","planHash":"7966ae44c4b7270f46a64f8d3d22b0af4768781fed38e3dcc01ded24d36b9736","previousHash":"0000000000000000000000000000000000000000000000000000000000000000","projectId":"d5530989-b886-4f9d-80dd-44d26d85ffd2","publication":{"commit":null,"deliveredAt":null,"repoUrl":"https://github.com/identity-md-launches/launch-2294-build-cto-relay-station"},"receiptIdentity":{"adapter":"0xde152afb7db5373f34876e1499fbd893a82dd336","chainId":1,"collection":"0x0000ec93127baa929e58e97dd0095a2bfb38ec1d","registry":"0x8004a169fb4a3325136eb29fa0ceb6d2e539a432"},"registry":"0xb6d0a187b050fa5bb0b87033a203f37becf4a775","research":[],"schema":"identitymd-work-v1","signals":[{"agentId":"51145","feedbackHash":"7e3f2fed609eb6244abc9cabcd9a7e8ec886ca3198d4c74a8b6b7a82293a545c","nodeKey":"adversarial_review","submissionHash":"24766df360e75e89a76db4a17f9d53237f38cc8bffae9ad3d3e5d44a45b8fcfa","tag1":"review:submission","tag2":"acceptance-v2","value":1},{"agentId":"51210","feedbackHash":"8517122d8bbd4b9cc650255bc7e3d8ec4c3d8a979b3db9e04c06938c1cf53268","nodeKey":"build_website","submissionHash":"02e70a020bd69f75c0059eb59c2e79619cd5dc2ce5fdb9271f5a172c6a89ac2c","tag1":"verification:structural","tag2":"acceptance-v2","value":1},{"agentId":"51133","feedbackHash":"d5181cfefb9a979524f90bd22dfd9328a06238b80c184c2b9af39ba013a6b5a5","nodeKey":"build_website_2","submissionHash":"cc02443da7b4df34c21bbb48eb9487fc22194b4079c4ac837312e6d241d23943","tag1":"verification:structural","tag2":"acceptance-v2","value":1},{"agentId":"51252","feedbackHash":"0c6e4fca3fca1464adddae24729c060192f44c7bdaae9e44e8109ab7be3f9557","nodeKey":"design_critic","submissionHash":"cd337455fac93f08781ad5bfa03321732d5d9afc1d7fe34436222111ba13c78d","tag1":"review:submission","tag2":"acceptance-v2","value":1},{"agentId":"52136","feedbackHash":"041cb31abc345c6d1b47997dfc34065962365255d5d2509c84299ef2e9d0a6a8","nodeKey":"implement_component","submissionHash":"577bff8ddd9001eef2a609808f0b2aa2f69ce5bddb23f8c7cb2c990a439c61d7","tag1":"verification:structural","tag2":"acceptance-v2","value":1},{"agentId":"52136","feedbackHash":"d98ffd972fd2ca0a809b8b4a46492c5ca4a9b64b29597e6f6f225bb046641795","nodeKey":"integrate_project","submissionHash":"d75f0e9d52b9938a31a48725d7474c7e2f5cf44995da26363968f8d135bd659c","tag1":"verification:structural","tag2":"acceptance-v2","value":1},{"agentId":"52136","feedbackHash":"5a4ff42315862d500bc09369344234c58385e3cee4e78012f9e94d7d1f9f1384","nodeKey":"write_readme_and_docs","submissionHash":"a8a354bc04d32b3c9652f4790ba69d97cc500ab938dabd17f593d1214e48c396","tag1":"verification:structural","tag2":"acceptance-v2","value":1}],"site":null,"snapshotHash":"45a15cf3f9ae92c27077c4356d63e9a4231eb7458a3414bf4313d949e3547d83","state":"completed","submissions":[{"artifacts":[],"attempt":1,"bundleHash":"70a87c16b618503f0fc38619301c81f258d190ad5a8645706896f389943f5e20","device":"6407a9ab49278517","findings":[],"hash":"02e70a020bd69f75c0059eb59c2e79619cd5dc2ce5fdb9271f5a172c6a89ac2c","nodeId":"fbe5bcf4-edc3-4a22-8256-40c04927644a","outcome":"completed","summary":"Implemented W1 integration, actual local contract flows, safe previews, setup tools and root `dist/`.\n\nPassed: build/typecheck, 125 Foundry, 39 SDK, 30 unit, 72 UI and 12 browser-chain cases. Rebuild matches the export. Source snapshot: 7.34 MB.\n\n[Setup](README.md) · [Validation](docs/validation/integration/frontend/README.md)\n\nWebKit and independent contributor review remain `NOT_RUN`. Production readiness stays false; oversized-manifest risk remains.","treeHash":"4ee6f7420b945dd442a42109c42be69cdc2ddc4d","usage":{"cachedInputTokens":24852864,"inputTokens":333273,"model":"gpt-6.1-sol","outputTokens":130659,"runtime":"codex","turns":14,"wallClockMs":3255736}},{"artifacts":[],"attempt":1,"bundleHash":null,"device":"3b9f88f55251764d","findings":[{"citation":"resolved","description":"The brief requires the W1 frontend to use the actual fixed profiles and limits rather than fixture constants. The fixture adapter's capabilities list image/jpeg and image/webp as enabled image media types, and FilePreview passes caps.mediaTypes[asset.kind] into verifyFile (frontend/src/domain/files.ts:72), so in fixture mode a JPEG or WebP asset earns the '已校验：与 A1 描述一致' badge. The actual policy only allows image/png: Policy.mime (src/libraries/Policy.sol) returns 'image/png' for Kind.Image, Assets.descriptor reverts Policy.Invalid for any other mediaType, and sdk/core.mjs:30 `const mime = ['text/markdown', 'text/html', 'image/png']` makes fileCap return 0 so fetchTrustedArtifact refuses the descriptor. ChainAdapter.getCapabilities correctly reports image: ['image/png']. The fixture therefore demonstrates a verification outcome (JPEG/WebP verified) that can never occur against a real Branch, which is a fixture-vs-actual divergence rather than an actual-contract defect. Fixture evidence is labelled, so this is low severity.","line":51,"path":"frontend/src/domain/fixtureAdapter.ts","reproduction":"Actor: visitor in fixture mode. Input: an AssetVersion with kind 'image', descriptor.mediaType 'image/jpeg', descriptor.bytes and descriptor.sha256 matching a valid JPEG. Expected (per 'use actual fixed profiles/limits, not fixture constants'): format check fails because only image/png is a valid A1 image type. Actual: verifyFile returns ok=true (allowedTypes includes 'image/jpeg', sniffImage returns 'image/jpeg') and the verified badge and download link are shown. Against ChainAdapter the same descriptor is refused: fileCap('image/jpeg') is 0 so fetchTrustedArtifact throws INVALID_DESCRIPTOR, and a manifest with mediaType image/jpeg reverts Policy.Invalid at Assets.descriptor during bind.","severity":"low","snippet":"  mediaTypes: { document: [\"text/markdown\"], html: [\"text/html\"], image: [\"image/png\", \"image/jpeg\", \"image/webp\"] },","title":"Fixture adapter advertises JPEG/WebP image types that the actual contracts and SDK never accept"},{"citation":"resolved","description":"onIntakeResult reverts when the callback's uri exceeds 1024 bytes. The Intake attempts the callback once inside a try, so the revert is recorded as 'not delivered', attempts[activeAttempt].delivered stays false and phase stays Pending. In Pending, sponsor() and retry() revert Busy, bind() reverts Busy, and _pending refuses any later callback for the same request, so the Branch has no path forward; by design there is no admin or timeout unlock. The result stays readable off chain but can never be bound. This is the same honest liveness class as the disclosed finite-manifest risk, but docs/validation/integration/README.md and docs/LIMITS.md disclose only the manifest-size case, not the URI-length case. The plane's documented URIs (ipfs://<CID>/result.json, /intake-results/<id>) are far shorter than 1024 bytes, and a very long string would also threaten the 200 000 gas stipend on its own, so the cap is defensible; the defect is the undisclosed consequence, not the cap. No contract redesign is suggested: the fix is to disclose it beside the manifest risk, or to lower the cap only if a measured stipend bound requires it.","line":228,"path":"src/RelayBranch.sol","reproduction":"State: a Branch after sponsor() (phase Pending, activeAttempt = 1, requestAttempt[requestId] = 1). Actor: the Intake (msg.sender == Policy.INTAKE) calling onIntakeResult(requestId, keccak256(result), uri) with uri of 1025 ASCII bytes. Expected (intake-client reference: 'Store and emit'; and the integration docs, which list only oversized manifests as unbindable): the result hash is recorded and the Branch enters ResultRecorded. Actual: revert Policy.Limit; MockIntake.complete returns delivered=false; phase remains Pending; subsequent sponsor(), retry() and bind() all revert Busy and a second onIntakeResult for the same requestId is impossible via the Intake because the request is already completed.","severity":"low","snippet":"        if (bytes(uri).length > 1024) revert Policy.Limit();","title":"Authenticated result with a URI longer than 1024 bytes leaves the attempt pending forever (undisclosed second liveness case)"},{"citation":"resolved","description":"Confirmation, not a defect: the finite manifest envelope is preserved exactly as the brief requires. A result whose raw bytes exceed Policy.MAX_MANIFEST (32 768) is authenticated by onIntakeResult (which stores only the hash) but every bind() reverts Policy.Invalid, leaving phase ResultRecorded permanently. No admin unlock, timeout, raw-result rewriting, refund or automatic retry exists in RelayBranch, which matches the brief. This remains an honest residual liveness risk and must not be reported as solved. The integration README discloses it.","line":21,"path":"src/RelayVerifier.sol","reproduction":"State: Branch in ResultRecorded with attempts[activeAttempt].outcome == keccak256(raw) where raw.length == 32769. Actor: anyone calling bind(raw, currentWitness). Expected (by design): revert Policy.Invalid, phase unchanged. Actual: revert Policy.Invalid, phase unchanged; sponsor()/retry() revert Busy, so the Branch cannot progress. Documented in docs/validation/integration/README.md as a retained risk.","severity":"info","snippet":"        if (raw.length > Policy.MAX_MANIFEST || keccak256(raw) != recordedHash) revert Policy.Invalid();","title":"Retained by design: an authenticated result larger than 32768 bytes is unbindable and the Branch stays in ResultRecorded"}],"hash":"24766df360e75e89a76db4a17f9d53237f38cc8bffae9ad3d3e5d44a45b8fcfa","nodeId":"a99d4f2c-3e43-4a92-b34f-c17d44dfe57d","outcome":"completed","summary":"The review is complete. The findings file `.imd-findings.json` is valid JSON with three entries, and the working tree has no stray files.\n\n**Outcome: no Critical or High defects found** in the integrated contracts, browser and Node SDK, or the frontend transaction and file boundaries. Readiness is not blocked by this review. The existing posture stays as delivered: productionDeployment null, liveCompatibility NOT_RUN, productionReady false.\n\n**What I checked and found sound**\n\n- Payer and spender: the sponsor approves the Branch exactly, the Branch is the only payer at the Intake, and the contract verifies balances before and after the pull and leaves zero allowance. The frontend re-reads price, anchor, body hash, parent job, spender and exact allowance immediately before each signature.\n- Stale state: proposals bind the anchor, `bodyOf` re-encodes and compares the stored body hash, and a stale proposal is refused on chain and in the UI. Sponsors cannot edit proposals.\n- Unknown transaction and rebuy: the journal records hash, nonce, account, chain and operation before the receipt wait. Reconciliation classifies mined, reverted, pending, replaced and dropped outcomes, and a pending account nonce with lost storage blocks a new purchase. Status 1 allows only an identical-body retry; status 2 or unknown freezes.\n- Witness and manifest: the verifier requires exact raw bytes matching the callback hash, one output file matching the proposal's name, path and media type, and rejects reused job ids and parent-project reuse on forks. Create and update profiles enforce target existence, kind match, name and path-ancestor collision guards.\n- Preview isolation: HTML renders in an opaque-origin sandbox with no scripts, a restrictive CSP, stripped hrefs, forms and navigable elements, and bounded data-image checks. Markdown goes through DOMPurify with https-only links.\n- Fork provenance: forks require a registered parent, a nonzero accepted checkpoint and the exact witness table, and copy only that revision's assets.\n\n**Findings reported**\n\n- Low: the fixture adapter advertises JPEG and WebP image types that the contracts and SDK never accept, so fixture mode can show a verified badge no real Branch can produce.\n- Low: a callback whose URI exceeds 1024 bytes reverts and leaves the attempt pending forever, an undisclosed second liveness case beside the disclosed oversized-manifest one.\n- Info: the finite-manifest limit is preserved as designed and remains an honest residual liveness risk.\n\n**Not done:** no Foundry proof files were written, since no finding reached High severity. Independent review of the browser matrix logs beyond the author's own acceptance reports was limited by time, so the author's WebKit runtime-diagnostic FAIL stands as they reported it.","treeHash":null,"usage":{"cachedInputTokens":1124095,"inputTokens":418,"model":"claude-fable-5-1","outputTokens":19208,"runtime":"claude","turns":25,"wallClockMs":301061}},{"artifacts":[],"attempt":1,"bundleHash":"ef1ff995fde2a5003eef40b772df9b5e5462d01de67fc53b8a91d4a7b9d5ed12","device":"00920b27421b9a80","findings":[],"hash":"577bff8ddd9001eef2a609808f0b2aa2f69ce5bddb23f8c7cb2c990a439c61d7","nodeId":"4ba387fa-1558-4792-b23d-ccb69a0261ea","outcome":"completed","summary":"I built the browser-compatible A1 SDK. The Node entrypoint and CLI still behave as before, and every check below passed. I didn't commit anything, and I didn't modify configuration, lockfiles, `abi/`, `src/` or Foundry files.\n\n**What changed**\n- **Shared core:** encoding and validation now live in one module, `sdk/core.mjs`, used by both runtimes. It keeps the patched rules: the output name/path/prefix collision guards, `dist/` reserved for the single HTML file, full bounded JSON validation (including ignored metadata) and the fixed v2 policy. `sdk/strict-json.mjs` no longer uses `Buffer`.\n- **ABIs:** `sdk/abi.mjs` is generated from `abi/*.json` by `tools/generate-sdk-abi.mjs`; its `--check` mode confirms the copy matches byte for byte.\n- **Node entrypoint:** `sdk/index.mjs` keeps the same exports, using Node's own SHA-256. `sdk/files.mjs` and `cli/relay.mjs` are untouched.\n- **Browser entrypoint:** `sdk/browser.mjs` plus `sdk/files-browser.mjs`. It uses no `node:*`, fs, DNS, HTTPS sockets, `process` or `Buffer`. Downloads only come from configured HTTPS locations, read as a stream that stops at the size cap, with redirects refused by default and every file's bytes checked against the expected size, hash and format.\n  - **DNS limit:** a browser can't see DNS answers or the connected IP address, so it can't repeat the Node adapter's private-address and IP-pinning protection. Trusting the configured artifact hosts is a configuration decision; this is written up in `docs/SDK-BROWSER.md`.\n  - **Loopback fixtures:** the loopback fixture policy is test-only. It is refused unless the page itself is on a loopback address, and its results are labelled as fixtures.\n- **New helpers:** an exact (never unlimited) token approval, price/allowance/factory reads, and `toWalletRequest`, which shapes a prepared transaction for the user's chosen wallet. The SDK holds no key and never submits, retries or buys anything itself.\n- **Config generator:** `sdk/config.mjs` and `tools/generate-local-config.mjs` build the local config from the deployed contracts and refuse non-local chains. The public config stays closed while `productionDeployment` is null. Where the file is finally written is left to the build-website stage.\n\n**Checks run** (raw logs, chain traces and SHA-256 hashes are in `docs/validation/integration/sdk-browser/`; its `README.md` has the PASS/FAIL/NOT_RUN table)\n\n| Check | Result |\n|---|---|\n| `npm test` | 39/39 passed (the 27 existing tests plus 12 new) |\n| `forge test` | 125/125 passed |\n| Existing `tools/local-integration.mjs` | Passed |\n| CLI help output | Identical to the earlier recorded output |\n| `tools/sdk-local-check.mjs` | Passed, in Node and in real headless Chromium 131 (details below) |\n\n`tools/sdk-local-check.mjs` deploys the actual contracts to a throwaway local chain (anvil), with mock token and intake contracts at the release's addresses. It then sends 15 real transactions through the browser entrypoint in each runtime:\n- create, propose, exact approval followed by sponsor, a refusal followed by an explicitly funded identical retry by a second account, bind, reading files and history, and a fork of revision 1.\n- In Chromium, 25 calldata/hash results and 37 rejection cases were identical to Node, and WebCrypto's SHA-256 matched.\n- Real browser downloads were checked: a tampered file, a 70 KiB oversized body, a redirect and an off-list URL were all refused.\n\n**Not done here**\n- **Chromium isn't committed:** the Chromium used for this check was installed only in `test/scratch/`, which gets deleted. The offline verifier can't rerun that part; without Chromium the tool reports it as `NOT_RUN`.\n- **No real-IMD or live testing:** nothing ran against the live Intake or real IMD. `liveCompatibility` stays `NOT_RUN`, `productionReady` stays `false`, and the known risk remains: an authenticated result that exceeds the manifest limits may be impossible to bind.\n- **No wallet signing UI:** the local runs signed with the test chain","treeHash":"0e182af29a2004ac924199dc86fb225edcbefb03","usage":{"cachedInputTokens":3439726,"inputTokens":76,"model":"claude-opus-5-5","outputTokens":59895,"runtime":"claude","turns":39,"wallClockMs":767171}},{"artifacts":[],"attempt":1,"bundleHash":"93cdd704c6b4cd9e0647c9de741938eaf94c6a46da3f4756eeff1e3a7dfaa630","device":"00920b27421b9a80","findings":[],"hash":"a8a354bc04d32b3c9652f4790ba69d97cc500ab938dabd17f593d1214e48c396","nodeId":"2028d94d-fe0b-4bda-aff9-d4a4516d6397","outcome":"completed","summary":"I rewrote `README.md` and added three docs plus a folder of fresh test evidence. I changed nothing outside `README.md` and `docs/`, and nothing is committed yet.\n\n**What I ran first** (on 2026-10-21, against the unchanged tree):\n- **Contracts:** `forge build` and `forge fmt --check` pass. The seeded `forge test` passes all 125 tests.\n- **SDK and tools:** 39 SDK tests pass, including the 27 baseline ones. The release, ABI and inline-fixture checks pass, and the CLI help prints.\n- **Frontend:** `npm ci`, typecheck, the 30 unit tests and the build all pass. The rebuilt `dist/` is byte-identical to the committed one.\n- **Browser on the local chain (Chromium):** all 13 cases pass. That's 80 wallet submissions, 78 receipts and 2 deliberately dropped transactions. The audit that re-encodes every transaction passes, with 0 browser errors.\n- **Local config:** a fresh local deployment generates exactly the committed `local-config.json`.\n- **Demo-mode UI tests:** 24/24 pass on chromium-desktop.\n- **Source hashes:** all 217 entries in the earlier hash record matched before I edited anything.\n\n**Delivered:**\n- **`README.md`:**\n  - **Up front:** the \"read this first\" section says plainly that the project is not deployed and not production-ready (`productionDeployment: null`, `liveCompatibility: NOT_RUN`, `productionReady: false`). It says everything ran on mocks, that real IMD has never run anything (including a contract-paid continuation), and that oversized results or missing callbacks can leave a project stuck for good. It also lists the open findings.\n  - **Commands:** build and test commands from a fresh clone, plus local setup and the browser tests.\n  - **Usage:** the six functions with who may call them, cost and failures, the Agent CLI and SDK handoff, source pins with hash evidence, and an acceptance summary.\n- **`docs/FRONTEND.md`:** every route (`#/`, `#/local`, `#/local/b/…`, `#/demo`) and every button, with permissions, cost and the actual error messages from the code. It also covers the transaction journal and recovery after reload, and the file and preview isolation.\n- **`docs/ACCEPTANCE.md`:** the 13-case matrix with separate Fixture UI, Actual local (Chromium this pass, Chromium earlier, WebKit earlier) and Real IMD columns. Every Real IMD cell is NOT_RUN. It also has the check table, review outcomes and seven unresolved issues, including the retained oversized-result risk.\n- **`docs/PROTOCOL-OVERVIEW.md`:** the previous README kept word for word, with its links fixed, so none of its protocol detail is lost.\n- **`docs/validation/integration/documentation/`:** this pass's raw logs, the full Chromium `browser-chain.json`, three screenshots, tool versions and a new `final-sha256.json`. Machine paths in the logs are replaced with placeholders.\n\n**Where the brief and the repo differ** (stated in the README):\n- `config/release.json` comes from the pinned patch that an earlier contributor applied. It was not edited in this pass.\n- The integration started from commit `8c0d6da`, not directly from the A1 commit `9b0294e`.\n- `sdk/index.mjs` and `sdk/strict-json.mjs` were deliberately changed after the patch to add the browser entry point.\n\n**Not rerun or still open:**\n- I did not rerun WebKit, the browser SDK parity check or the other four demo-mode test setups, so those results are cited from earlier evidence.\n- WebKit's 3 intermittent page errors from earlier runs are still open.\n- No fresh independent review has been done.\n- On this machine Chromium needed system libraries it was missing. I loaded them from a temporary directory without installing anything; the README explains how.\n- A browser test run without `--out` overwrites the earlier evidence folder, so the README warns to pass it. My one demo-mode run's default report wrote 9 new screenshots into that folder; I moved them out, and nothing tracked changed.","treeHash":"c7838395fec7b493f61a1cf7a08cf5c9db6966b9","usage":{"cachedInputTokens":4800880,"inputTokens":100,"model":"claude-opus-5-5","outputTokens":37529,"runtime":"claude","turns":53,"wallClockMs":902896}},{"artifacts":[],"attempt":1,"bundleHash":"5980a2844c07ed720fa998b0ccb2cde8b0deae44f49a375b3602081353290655","device":"0b129ef2f81deead","findings":[],"hash":"cc02443da7b4df34c21bbb48eb9487fc22194b4079c4ac837312e6d241d23943","nodeId":"6255fe16-391f-45a6-8048-78a54410f3a2","outcome":"completed","summary":"Updated the static export and [acceptance report](/home/imd-worker/.identitymd/work/5b630958-5d27-4b47-bebf-4e682a6d2ab7/6255fe16-391f-45a6-8048-78a54410f3a2/docs/validation/integration/acceptance/README.md).\n\n- Passed: build, typecheck, 125 Foundry, 39 SDK, 30 unit and 120 fixture tests.\n- All 13 actual-contract scenarios passed in Chromium and WebKit.\n- Source snapshot bundle: **7,859,229 bytes**, under 8 MiB. Protected files unchanged.\n\nAcceptance remains incomplete: independent review is NOT_RUN and intermittent WebKit RPC diagnostics remain open. No public deployment; `productionReady:false`. The manifest-envelope risk remains.","treeHash":"1940ffef6d4e7b54013b299ea305b0d16db0ff16","usage":{"cachedInputTokens":22945408,"inputTokens":400947,"model":"gpt-6-astra","outputTokens":102937,"runtime":"codex","turns":19,"wallClockMs":4859046}},{"artifacts":[],"attempt":1,"bundleHash":null,"device":"8cb9deeead5ce571","findings":[{"citation":"resolved","description":"Now: branch and history are read separately at latest. If the branch anchor is newer than the fetched history, this return removes the entire workspace, including its refresh button. Only sponsorships poll; branch/history have no retry or interval, and refetchOnWindowFocus is disabled in main.tsx. Restoring current RPC responses does not recover the view.\nChange to: read the branch/history at one pinned block, or explicitly refetch both when their checkpoint IDs disagree. Keep an accessible retry control in this state and never submit a transaction to recover it.\nWhy: the public-read workspace must recover chain truth and remain usable when a binding lands during its independent reads. A permanent loading screen is not synchronization.","line":70,"path":"frontend/src/ui/Workspace.tsx","reproduction":"Chromium at 1440×900, actual local contracts: complete and bind HTML R3. Save the real pre-R3-binding block number. Clear the optional local journal and reload the same #/local/b/<Branch> workspace. For initial eth_getLogs requests to that Branch, replay the genuine logs through the saved block while other RPC reads use latest (the same inconsistent snapshot that independent reads straddling a binding can produce). The chain revision is 3, while fetched history ends at 2. Once 正在同步最新检查点… appears, restore unmodified latest RPC responses. After 7 seconds and multiple 3-second sponsorship polling intervals, the same screen remains and there are zero 刷新链上状态 buttons. A full page reload recovers. Measured actual-chain run: staleReads=5, stillSyncing=true, refreshButtons=0, chainRevision=3.","severity":"medium","snippet":"  if (!latest) return <p className=\"state-line\" role=\"status\">正在同步最新检查点…</p>;","title":"bug: mixed branch and history reads strand the workspace in its syncing screen"},{"citation":"resolved","description":"Now: the image preview applies both width: 128px and height: 128px to every accepted PNG, stretching non-square output; the dimensions caption still reports the original ratio.\nChange to: retain the 128px thumbnail width with height: auto and max-width: 100%, so the preview preserves the natural aspect ratio.\nWhy: visitors must be able to inspect the actual delivered image faithfully before continuing a project; square test fixtures hide this defect.","line":199,"path":"frontend/src/styles.css","reproduction":"Chromium, 1440×900: start the documented disposable local setup; create a project, propose an image named cover, explicitly approve/sponsor, deliver and bind a valid 320×160 PNG with matching SHA-256/length, then choose 下载并校验 on cover. The actual-contract browser flow accepts the file and displays 已校验. Measure the img: naturalWidth=320, naturalHeight=160, getBoundingClientRect()=128×128. Expected a 2:1 preview (128×64); actual is 1:1. Reproduced using browser-chain.mjs with only the deterministic PNG fixture replaced by a valid 320×160 PNG.","severity":"medium","snippet":".img-box img { width: 128px; height: 128px; image-rendering: pixelated; border-radius: var(--radius-sm); outline: 1px solid rgb(0 0 0 / 0.1); outline-offset: -1px; }","title":"bug: verified PNG previews force every image into a square"},{"citation":"resolved","description":"Now: downloads use descriptor.name, which the actual A1 adapter fills with the asset slot name (for example site), while the actual filename is descriptor.path (dist/index.html). HTML and Markdown blobs use application/octet-stream, so the browser saves an extensionless file.\nChange to: pass the basename of descriptor.path through safeFileName for the download attribute, retaining index.html, main.md and cover.png as appropriate. Keep the slot name as the asset label.\nWhy: the real contract integration should deliver usable files with their authenticated output filenames; fixture-only tests use names such as site.html and miss this integration regression.","line":117,"path":"frontend/src/preview/FilePreview.tsx","reproduction":"Chromium, 1440×900: in actual local-contract mode create/sponsor/bind an HTML proposal with outputName site and outputPath dist/index.html; select 下载并校验, then 下载已校验文件. Playwright download.suggestedFilename() returns site, and the anchor download attribute is site. Expected index.html from the authenticated path. The same mapping also drops .md from document slots, whose downloads likewise use application/octet-stream. The instrumented actual-contract R1→R2→R3 flow reproduced site after successful byte/hash verification.","severity":"medium","snippet":"        <a className=\"btn btn-secondary\" href={blobUrl} download={safeFileName(d.name)}>","title":"bug: actual-contract downloads discard the file extension"}],"hash":"cd337455fac93f08781ad5bfa03321732d5d9afc1d7fe34436222111ba13c78d","nodeId":"4e46b736-c210-4ae0-9781-aaf17d57dc6e","outcome":"completed","summary":"| Area | Count |\n|---|---:|\n| Requirements, grouped below | 45 met · 3 partially missing · 0 faked |\n| Broken features | 3 medium · 0 high/critical |\n| Product/actions on first screen at 390px | Yes |\n| taste@1 | 16/16 passed |\n| Copy findings | 0 |\n| Share card | Present |\n| After review / needs requester | 0 / 0 |\n\nRecorded three verified bugs in [.imd-findings.json](/root/.identitymd/work/5b630958-5d27-4b47-bebf-4e682a6d2ab7/4e46b736-c210-4ae0-9781-aaf17d57dc6e/.imd-findings.json):\n\n- **bug: workspace synchronization stalls.** Mixed revision reads leave only the syncing message, with no refresh control. Restoring current RPC responses did not recover it.\n- **bug: PNG previews distort aspect ratios.** An accepted 320×160 PNG rendered at 128×128.\n- **bug: actual-contract downloads lose extensions.** Verified `dist/index.html` downloaded as `site`.\n\nBuild, typecheck, **125 Foundry tests**, **39 SDK tests**, **30 frontend unit tests**, both browser SDK parity checks and Chromium’s **13 actual-contract cases** passed. WebKit’s file group failed its runtime gate twice on reload-time fetch errors. Its clean acceptance remains incomplete; the cause is unresolved.\n\nThe site is a Chinese project workspace carried by stone surfaces, green actions and clear branch/checkpoint records.\n\n`acceptance/` below means `docs/validation/integration/acceptance/`. “Met” describes the commissioned local scope and available evidence, without implying live IMD compatibility.\n\n| Explicit requirement | Status | Evidence |\n|---|---|---|\n| “usable CTO Relay Station” | **Missing in part** | Three recorded usability bugs |\n| “actual browser-to-local-contract acceptance” using Chromium and WebKit | **Missing in part** | Chromium passes; repeated WebKit runtime-gate failure |\n| One development job; no public deployment, funded credentials, live purchases, paid child jobs, automatic spending, redesign, governance, ranking, tokens or treasury | Met | Release configuration and local traces |\n| Preserve rules, CLI and W1 design; limit core changes to supplied repairs/integration | Met | Patched source hashes, CLI, `DESIGN.md` |\n| Verify A1 ancestor, pinned patch bytes/SHA, apply-check/application and patched manifest; reject mismatched inputs | Met | `acceptance/source-inputs.json`, `input-verification.log` |\n| Import pinned W1; preserve sources, tests, licenses, locked dependencies and immutable provenance | Met | `frontend/`, input provenance; lock hash matches |\n| Keep protected root configuration and frontend dependency boundaries; exclude Git/dependency caches | Met | Protected-path records and source inventory |\n| Preserve old evidence; put new reports under integration | Met | `docs/validation/integration/` |\n| Preserve output name/path/prefix guards and non-HTML `dist/` restriction | Met | Encoder and regression tests |\n| Validate bounded raw JSON, ignored metadata, decoded duplicates and UTF-8 | Met | Manifest parsers and tests |\n| Match SDK limits and preserve fixed v2 policy | Met | `sdk/core.mjs`, policy/release checks |\n| Rerun 125 Foundry and 27 baseline Node tests | Met | 125 Foundry and 39 total SDK tests passed |\n| Retain oversized authenticated-manifest risk; no unlock, rewriting, refund or automatic retry | Met | Contracts, release risk and documentation |\n| Shared canonical validation/encoding, browser/Node adapters, real browser entrypoint; preserve CLI verification | Met | `sdk/core.mjs`, browser/Node entrypoints |\n| Compare calldata, witnesses/hashes, manifest keccak, file SHA and malformed/Unicode/duplicate/limit rejection | Met | Both engines: 25 accepted and 37 rejection vectors match |\n| Trusted artifact locations, bounded streaming, schemes/hosts/redirects, byte/length/format checks and DNS disclosure | Met | `sdk/files-browser.mjs`; browser download checks |\n| No mandatory proxy/indexer/signer/backend; loopback policy test-only | Met | SDK policies and adapters |\n| Wire SDK/ABI/config into read/workspace routes, six operations, bindi","treeHash":null,"usage":{"cachedInputTokens":9118464,"inputTokens":213585,"model":"gpt-6.1-sol","outputTokens":35471,"runtime":"codex","turns":13,"wallClockMs":1705407}},{"artifacts":[],"attempt":1,"bundleHash":"ded8a63ce8315b93406adfc64888ac86311780f1e20536309e5d108a2145d045","device":"00920b27421b9a80","findings":[],"hash":"d75f0e9d52b9938a31a48725d7474c7e2f5cf44995da26363968f8d135bd659c","nodeId":"0f4b1a9d-2f82-4413-90c3-561765fe3161","outcome":"completed","summary":"The pinned repair patch is applied exactly as reviewed, including its `config/release.json` changes, and every baseline check passes. It's committed as `536451f` on top of the starting commit `9b0294e`.\n\n**Inputs verified before any edits**\n- The starting HEAD matched `9b0294e7ab19060bd00c489fbe032cf248528e0d`.\n- The patch was 194,891 bytes with SHA-256 `3028547365…2989`, as pinned. The pinned `SOURCE-INPUTS.json` records the same size and hash.\n- `git apply --check` was clean, and the patch went in unmodified: 34 files changed.\n- All 70 files listed in the patch's `docs/validation/review-fixes/source-sha256.json` matched (0 mismatched, 0 missing).\n- The protected build files and dependencies (`foundry.toml`, `remappings.txt`, `package.json`, the lockfile, `lib/`, `vendor/`) are identical to HEAD.\n\n**Checks on the patched code** (raw logs are in `docs/validation/integration/`)\n- **Contracts:** `forge build` succeeded with lint warnings only; `forge test -vv` gave 125 passed, 0 failed; `forge fmt --check` passed.\n- **SDK and CLI:** the Node tests gave 27 passed, 0 failed; the PNG fixture check and `cli help` passed.\n- **Release metadata:** the five ABIs match the build, and the contract hashes and sizes in `config/release.json` match all four compiled contracts. The checker is new, at `tools/check-release.mjs`, and only reads.\n- **Local run:** the real SDK drove contracts deployed on a local Anvil chain for 7 rounds and 33 transactions. The Intake and IMD token were mocks, so this says nothing about live IMD behaviour.\n\n**Gaps**\n- **Missing patch evidence:** the patch's own evidence pages link to 16 raw logs it doesn't include, such as the pre-repair failure baselines and the gas traces. I didn't recreate them, since that would rewrite the reviewed patch. My new logs cover the current pass results but not those baselines or traces.\n- **Measurement tool not run:** `tools/measure-release.mjs` rewrites a file in the patch's evidence directory and needs those missing traces. `tools/check-release.mjs` makes the same ABI and metadata checks instead.\n- **Known risk kept:** a genuine result too large for the manifest limits can still leave a Branch unable to bind it. As the brief requires, there's no admin or timeout unlock, refund or automatic retry.\n- **Not done in this stage:** importing W1, the browser SDK and the browser signing tests belong to the later build-website step. `productionDeployment` stays `null`, `liveCompatibility` stays `NOT_RUN` and `productionReady` stays `false`.\n\nThis machine has no git identity, so I made the commit with the earlier commits' author (\"IdentityMD Worker\") passed for that one commit; git's config was not changed.\n\nThe full report is `docs/validation/integration/README.md`.","treeHash":"5d08cbb6b0d7961da1184a511fd8604cb290009d","usage":{"cachedInputTokens":1119708,"inputTokens":46,"model":"claude-opus-5-5","outputTokens":10975,"runtime":"claude","turns":23,"wallClockMs":378902}}],"verification":[{"checks":[],"detail":"paths and tree verified; no suite was run for this kind of work","evaluation":"structural","profile":"none","status":"accepted","submissionHash":"02e70a020bd69f75c0059eb59c2e79619cd5dc2ce5fdb9271f5a172c6a89ac2c","verifiedTreeHash":"4ee6f7420b945dd442a42109c42be69cdc2ddc4d","verifierVersion":"0.1.0+560ab6a2"},{"checks":[],"detail":"paths and tree verified; no suite was run for this kind of work","evaluation":"structural","profile":"none","status":"accepted","submissionHash":"577bff8ddd9001eef2a609808f0b2aa2f69ce5bddb23f8c7cb2c990a439c61d7","verifiedTreeHash":"0e182af29a2004ac924199dc86fb225edcbefb03","verifierVersion":"0.1.0+560ab6a2"},{"checks":[],"detail":"paths and tree verified; no suite was run for this kind of work","evaluation":"structural","profile":"none","status":"accepted","submissionHash":"a8a354bc04d32b3c9652f4790ba69d97cc500ab938dabd17f593d1214e48c396","verifiedTreeHash":"c7838395fec7b493f61a1cf7a08cf5c9db6966b9","verifierVersion":"0.1.0+560ab6a2"},{"checks":[],"detail":"paths and tree verified; no suite was run for this kind of work","evaluation":"structural","profile":"none","status":"accepted","submissionHash":"cc02443da7b4df34c21bbb48eb9487fc22194b4079c4ac837312e6d241d23943","verifiedTreeHash":"1940ffef6d4e7b54013b299ea305b0d16db0ff16","verifierVersion":"0.1.0+560ab6a2"},{"checks":[],"detail":"paths and tree verified; no suite was run for this kind of work","evaluation":"structural","profile":"none","status":"accepted","submissionHash":"d75f0e9d52b9938a31a48725d7474c7e2f5cf44995da26363968f8d135bd659c","verifiedTreeHash":"5d08cbb6b0d7961da1184a511fd8604cb290009d","verifierVersion":"0.1.0+560ab6a2"}]}