{"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":"456ed8e0-2a58-47d6-8797-b03eb9d6424d","kind":"shape:chain","nodes":[{"acceptedSubmissionHash":"cc09f3ef884b34e2d9c995a0d09661486fd6be81b8ebc79f55c8650b84d5673a","dependsOn":["refine_project"],"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":"e8ad590f784d212c71889f32fe8d566ba8a5986e2c7315bedb3233cde040c86d","dependsOn":[],"execution":{"network":false,"profile":"none","requires":[],"skillHash":"99cccc7e3e2e1b515c66d54cc6d4bd9832d528aaf0ec0ba48c87a4182db4b7ca","skillId":"refine-project","tools":[]},"key":"refine_project","kind":"code","role":"implement","skillHash":"99cccc7e3e2e1b515c66d54cc6d4bd9832d528aaf0ec0ba48c87a4182db4b7ca","skillId":"refine-project","state":"accepted"}],"objective":"Three gaps against the live POST https://api.imd.fun/requests/check. 1 The live check also refuses \".github/workflows\" and \".github/workflows/ci.yml\" as protected_path; the library accepts them. Treat .github and anything under .github/ as protected. (\".vscode\" passes in both; keep it.) 2 The live check accepts \"./.git/config\" while the library refuses it. Make the library match the server on that path exactly (re-check it live first and record the verdict), without loosening the .git and .git/ protection. 3 launch_token: the live server blocks on it (blocker, not suggestion). Report it as an error instead of a warning, with the same heuristic. 4 scripts/generate-schemas.mjs still has the old relativePath pattern, so npm run schemas would undo the earlier \".\" and \"./src\" fix. Update it to the new pattern and add a test that regenerating the schemas leaves them unchanged. Record each new case's live verdict in the saved live fixtures. Verify against the LIVE API at https://api.imd.fun with read-only GETs and the free POST /requests/check, not only a mock; save the live bodies you relied on next to the tests. Add a CHANGELOG.md entry listing each item and what changed. Keep the existing experimental label everywhere it already appears.","parentJobId":"ace7336a-2c79-452b-89bd-8e2fe2361238","planHash":"ce50149bd9ae4b67838613a7e2a8b462c3c34cf806ce1490ff1435e03e5327f8","previousHash":"0000000000000000000000000000000000000000000000000000000000000000","projectId":"e3008b8a-268f-41d6-8f2c-491a47a02f0e","publication":{"commit":null,"deliveredAt":null,"repoUrl":"https://github.com/identity-md-launches/launch-603-build-imd-schemas-json-schema"},"receiptIdentity":{"adapter":"0xde152afb7db5373f34876e1499fbd893a82dd336","chainId":1,"collection":"0x0000ec93127baa929e58e97dd0095a2bfb38ec1d","registry":"0x8004a169fb4a3325136eb29fa0ceb6d2e539a432"},"registry":"0xb6d0a187b050fa5bb0b87033a203f37becf4a775","research":[],"schema":"identitymd-work-v1","signals":[{"agentId":"50939","feedbackHash":"77d437ec6fb0021472443f4f4b5280d54404202989c9f4e898042ac85455b3d5","nodeKey":"adversarial_review","submissionHash":"cc09f3ef884b34e2d9c995a0d09661486fd6be81b8ebc79f55c8650b84d5673a","tag1":"review:submission","tag2":"acceptance-v2","value":1},{"agentId":"51458","feedbackHash":"42688e55c7e445563010f0b3dc2f5d651971be9e519ea2a6d7b5697bef25778e","nodeKey":"refine_project","submissionHash":"e8ad590f784d212c71889f32fe8d566ba8a5986e2c7315bedb3233cde040c86d","tag1":"verification:structural","tag2":"acceptance-v2","value":1}],"site":null,"snapshotHash":"764422bdd60912b935647f7223211685dc8d773dabb0509dc0ae0709de9c2c10","state":"completed","submissions":[{"artifacts":[],"attempt":1,"bundleHash":null,"device":"72b617d4b615473a","findings":[{"citation":"resolved","description":"Item 3 turned the launch_token heuristic into an error, but the gate still only fires when body.onchain is exactly 'evm_project' or 'univ4_hook'. The schema (common-1.json $defs/onchain: anyOf [const true, launchKind]) also admits onchain: true, and the live check treats such a launch as a project launch and blocks it with launch_token. The same objective text that the library refuses for onchain: 'evm_project' passes the library unchanged for onchain: true, so the library and server disagree on a body the schema explicitly allows. dist/index.js:143 carries the same condition. (A custom_token launch with a 2,000,000,000 supply and 6 decimals was also checked live: no launch_token blocker, so the library is right to skip that kind.)","line":110,"path":"src/index.ts","reproduction":"Live POST https://api.imd.fun/requests/check on 2026-10-02 with {\"action\":\"launch.open\",\"input\":{\"onchain\":true,\"objective\":\"Launch Example Token (EXM) with a fixed supply of 1,000,000,000 tokens and 6 decimals.\"}} -> HTTP 200, blockers include {\"code\":\"launch_token\",\"detail\":\"Every launch deploys the same token: 1,000,000,000 with 18 decimals ... This request asks for a different supply or decimals, which the launch would not deploy. Take it out and check again.\"} (plus two bad_path_count blockers, see the next finding). Library: validate('launch.open', sameInput) -> valid: true, errors: [], warnings: []. Expected: errors contain code 'launch_token' as it does for onchain: 'evm_project' with the identical objective (validate('launch.open', {onchain:'evm_project', objective: same}) -> errors [{code:'launch_token', ...}]).","severity":"medium","snippet":"  if (action === 'launch.open' && ['evm_project','univ4_hook'].includes(body.onchain) && typeof body.objective === 'string' && launchTokenMismatch(body.objective)) fail('/objective','launch_token: project and hook launches use 1,000,000,000 tokens, 18 decimals and plain transfers; check the requested token terms with the server','launch_token');","title":"launch_token error is skipped for onchain: true launches that the live check blocks"},{"citation":"resolved","description":"The saved fixture test/live/check-followup-2026-10-02.json case launch_standard has two live blockers (bad_path_count at nodes impl and tests), yet the test asserts the library returns valid: true for that exact body, and line 17 asserts local('root_dot').valid === true while the saved check-2026-10-02.json case root_dot has a live bad_path_count blocker at node build (the test never compares root_dot to its live verdict). Re-running live shows the rule: when the server plans implementation steps itself (no skill and no steps: template single/impl_tests/impl_tests_review, or a bare objective; launch.open with onchain evm_project, univ4_hook or true), every planned implementation node needs a write budget, which only `contracts` supplies. Root `paths` do not count for planned nodes. Without `contracts` the live check always blocks with bad_path_count; with contracts (examples/job-template.json, or adding contracts: ['src/Example.sol'] to launch_standard) it passes. The library's bad_path_count check (src/job.ts:8-9) only counts explicit paths arrays and returns valid for all of these bodies. The CHANGELOG calls these blockers 'unrelated', but they are deterministic, reproduced on 14 bodies, and the most basic job body ({objective} alone) is affected.","line":59,"path":"test/live-check.test.cjs","reproduction":"Live POST /requests/check on 2026-10-02, all HTTP 200: {\"action\":\"job.open\",\"input\":{\"objective\":\"Build a project.\"}} -> blockers [{\"code\":\"bad_path_count\",\"detail\":\"expected between 1 and 16 allowed paths\",\"node\":\"build\"}]; {\"action\":\"job.open\",\"input\":{\"objective\":\"Build a project.\",\"paths\":[\"src\",\"test\"]}} -> same bad_path_count@build; {\"action\":\"job.open\",\"input\":{\"objective\":\"Implement an ERC-20 vesting contract with a cliff.\",\"template\":\"impl_tests\"}} -> bad_path_count@impl and bad_path_count@tests; {\"action\":\"job.open\",\"input\":{\"objective\":\"Implement an ERC-20 vesting contract with a cliff.\",\"template\":\"single\"}} -> bad_path_count@build; {\"action\":\"launch.open\",\"input\":{\"onchain\":\"evm_project\",\"objective\":\"Launch Example Token (EXM) with a fixed supply of 1,000,000,000 tokens, 18 decimals and plain transfers.\"}} -> bad_path_count@impl, bad_path_count@tests (matches the saved fixture). Control: the same template body plus \"contracts\":[\"src/Vesting.sol\"] -> blockers []; the launch body plus \"contracts\":[\"src/Example.sol\"] -> blockers []; {\"objective\":\"Build a project.\",\"skill\":\"implement-component\",\"paths\":[\"src\"]} -> blockers []. Library: validate(...) returns valid: true with no bad_path_count for every blocked body above. Expected: library reports bad_path_count (or at least does not assert valid: true in a test named after the live verdicts) for planner-driven bodies without contracts.","severity":"medium","snippet":"  assert.equal(local('launch_standard').valid, true);","title":"Template, planner-only and launch bodies without contracts validate locally but are blocked live with bad_path_count; the live test asserts the disagreement"},{"citation":"resolved","description":"The live check refuses a step whose skill declares its own write budget when it also names paths: 'step 1 (build-contract-project) declares its own budget, so the step may not also name paths'. The library's fixed-budget list has only three skills and does not include build-contract-project, so the same body is valid locally. The identical mismatch appears on launch.open (unplannable_steps) and, when build-contract-project is used as the top-level skill with root paths, the live code is unknown_skill with the same detail; unknown_skill is not in src/refusals.json either.","line":20,"path":"src/job.ts","reproduction":"Live POST /requests/check on 2026-10-02 with {\"action\":\"job.open\",\"input\":{\"objective\":\"Build a vesting project.\",\"shape\":\"chain\",\"steps\":[{\"skill\":\"build-contract-project\",\"paths\":[\"src\"]}]}} -> HTTP 200, blockers [{\"code\":\"unplannable_steps\",\"detail\":\"step 1 (build-contract-project) declares its own budget, so the step may not also name paths\"}]. Same body without paths -> blockers []. Library: validate('job.open', input) -> valid: true, errors: []. Expected: an unplannable_steps error at /steps/0/paths, as for deploy-script. Also live: {\"action\":\"launch.open\",\"input\":{\"onchain\":\"evm_project\",\"objective\":\"Launch Example Token (EXM) with a fixed supply of 1,000,000,000 tokens, 18 decimals and plain transfers.\",\"skill\":\"build-contract-project\",\"paths\":[\"src\",\"test\"]}} -> blockers [{\"code\":\"unknown_skill\",\"detail\":\"step 1 (build-contract-project) declares its own budget, so the step may not also name paths\"}]; library valid: true.","severity":"low","snippet":"    if (['gas-and-size-report','write-readme-and-docs','deploy-script'].includes(step.skill) && Object.hasOwn(step,'paths')) fail(`/steps/${i}/paths`,`${step.skill} steps must not name paths`,'unplannable_steps');","title":"build-contract-project steps that name paths validate locally but are refused live with unplannable_steps"},{"citation":"resolved","description":"Re-checked live: ./.git/config is still accepted (blockers []), so the carve-out matches the brief. But the server's actual rule is that it does not normalise a leading ./ or an inner /./ segment at all, so ./.git, ./.git/HEAD, ./.git/hooks/pre-commit, ././.git/config, .git/./config, ./.github, ./.github/workflows/ci.yml, ./foundry.toml and ./lib/x.sol are all accepted live while the library refuses each with protected_path. The brief asked to match only './.git/config' exactly and not to loosen .git protection, so the stricter library is a deliberate scope decision and is the safer side; this is recorded as a scope question, not a request to loosen. Two concrete contradictions should however be known: test/semantics.test.cjs:32 asserts '././.git/config' and './.git/hooks/pre-commit' are protected_path, and CHANGELOG.md says other spellings 'retain their previous protection', while the live server accepts both. The single-string exception also means the library's verdict on ./.git/config no longer follows any general rule a user could predict.","line":14,"path":"src/job.ts","reproduction":"Live POST /requests/check on 2026-10-02, body {\"action\":\"job.open\",\"input\":{\"objective\":\"Implement a component.\",\"shape\":\"chain\",\"steps\":[{\"skill\":\"implement-component\",\"paths\":[P]}]}}: P = \"./.git/config\" -> blockers [] (library valid: true, agree). P = \"./.git/hooks/pre-commit\", \"././.git/config\", \"./.git/HEAD\", \"./.git\", \".git/./config\", \"./.github\", \"./.github/workflows/ci.yml\", \"./foundry.toml\", \"./lib/x.sol\" -> each HTTP 200, blockers [] live; library -> errors [{code:'protected_path', path:'/steps/0/paths/0'}] for every one. Controls that agree on both sides: \".git\", \".git/\", \".git/config\", \".github\", \".github/\", \".github/workflows\", \".github/workflows/ci.yml\" -> protected_path live and locally; \".vscode\", \".githubx\", \"src/.github\" -> accepted on both.","severity":"low","snippet":"      if ((path !== './.git/config' && (normalized === '.git' || normalized.startsWith('.git/'))) || normalized === '.github' || normalized.startsWith('.github/') || normalized === 'foundry.toml' || normalized === 'lib' || normalized.startsWith('lib/')) fail(`${location}/${index}`,`protected path: ${path}; .git, .github, foundry.toml and lib are reserved`,'protected_path');","title":"Live check accepts every ./-prefixed protected spelling, not only ./.git/config; the library refuses nine such inputs and its tests assert the opposite of the live verdict for two"},{"citation":"resolved","description":"Item 3 asked for the existing heuristic to become an error, which was done. Since the error now decides valid:false, the gaps in the heuristic are now the only reason a launch body can be valid locally and blocked live under this code. The live launch_token message lists 'no fees, limits, pausing or minting' and the server blocks objectives that ask for a per-transfer fee phrased without 'transfer fee'/'fee on transfers', for owner minting, for pausable transfers and for a maximum wallet limit; the library detects none of these. The brief said to keep the same heuristic, so this is reported as an observation with concrete inputs for a later scope decision rather than a defect in the requested change. The three brief cases (2,000,000,000 supply, 6 decimals, 2% transfer tax) were re-run live and still return launch_token; the library errors on all three and warns on none, as required.","line":170,"path":"src/index.ts","reproduction":"Live POST /requests/check on 2026-10-02 with {\"action\":\"launch.open\",\"input\":{\"onchain\":\"evm_project\",\"objective\":O}}: O = \"Launch Example Token (EXM) with a 1% fee charged on each transfer.\" -> launch_token ('asks for rules on transfers'); O = \"Launch Example Token (EXM); the owner can mint more tokens later.\" -> launch_token ('asks for minting or burning after launch'); O = \"Launch Example Token (EXM) with pausable transfers.\" -> launch_token; O = \"Launch Example Token (EXM) with a maximum wallet limit of 2% of supply.\" -> launch_token. Library: validate('launch.open', ...) -> no launch_token error or warning for any of the four. Controls that agree: \"Launch Example Token (EXM) with no transfer tax and 18 decimals.\" -> no launch_token on either side; \"Launch Example Token (EXM) that burns 1% of every transfer.\" -> no launch_token on either side.","severity":"low","snippet":"function launchTokenMismatch(objective: string): boolean {","title":"launch_token heuristic misses four kinds of live launch_token blockers (fee per transfer, minting, pausing, wallet limits)"},{"citation":"resolved","description":"A launch.open with template impl_tests is accepted by the library but refused live with code launch_requires_review ('project and custom token launches require a final independent review step after the manifest'). Neither launch_requires_review nor unknown_skill (see the build-contract-project finding) appears in src/refusals.json or docs/refusals.md, which present themselves as the known refusal codes. Outside the four requested items; recorded so the catalog can be extended.","line":75,"path":"src/refusals.json","reproduction":"Live POST /requests/check on 2026-10-02 with {\"action\":\"launch.open\",\"input\":{\"onchain\":\"evm_project\",\"objective\":\"Launch Example Token (EXM) with a fixed supply of 1,000,000,000 tokens, 18 decimals and plain transfers.\",\"template\":\"impl_tests\"}} -> HTTP 200, blockers [{\"code\":\"launch_requires_review\",\"detail\":\"project and custom token launches require a final independent review step after the manifest\"}]. Same body with template impl_tests_review and contracts [\"src/Example.sol\"] -> blockers []. Library: valid: true for both; knownRefusals contains no launch_requires_review entry (node -e \"console.log(require('./src/refusals.json').some(r=>r.code==='launch_requires_review'))\" prints false).","severity":"info","snippet":"    \"code\": \"launch_token\",","title":"Live launch rule launch_requires_review (template launches need a final review) is not mirrored and its code is absent from the refusal catalog"}],"hash":"cc09f3ef884b34e2d9c995a0d09661486fd6be81b8ebc79f55c8650b84d5673a","nodeId":"d9526533-61d8-4e80-83b3-61967c3b3567","outcome":"completed","summary":"Review complete. The findings file is at `.imd-findings.json` in the repository root and no other file was changed. All six snippets were verified against their lines.\n\n**What I confirmed live** (free POST to `/requests/check` on 2026-10-02, read-only otherwise):\n\n- The four requested items agree between library and server. `.github`, `.github/workflows` and `.github/workflows/ci.yml` are refused on both sides, `.vscode` passes on both, `./.git/config` passes on both, and the three launch_token bodies are blocked live and now error locally with no warning.\n- Regenerating the schemas in a temporary copy produces byte-identical files, and `npm test` passes all 413 tests with dist in sync with src.\n- All 17 shipped examples were also run live. Their only live blockers are remote facts the README already excludes (missing repo, placeholder artifact hashes, token name and symbol).\n\n**Findings, ranked by severity:**\n\n1. **Medium. launch_token gate skips `onchain: true`.** The schema admits `onchain: true`, and the server blocks such a launch with launch_token for \"6 decimals\", but the library only fires for `evm_project` and `univ4_hook`. Anchored at src/index.ts:110.\n2. **Medium. Planner-driven bodies without `contracts` are blocked live but valid locally, and the test asserts the disagreement.** A bare `{objective}` job, any template job, and the fixture's own `launch_standard` body all get bad_path_count live. Root `paths` do not help, `contracts` does. test/live-check.test.cjs:59 asserts the library accepts a body whose saved live response blocks it, and line 17 never compares `root_dot` with its live verdict.\n3. **Low. `build-contract-project` steps with paths** are refused live with unplannable_steps but accepted locally. src/job.ts:20.\n4. **Low. The server does not normalise `./` at all.** Nine more spellings such as `./.git/HEAD` and `./.github` pass live while the library refuses them. This is the brief's explicit design choice, so it is recorded as a scope question, but test/semantics.test.cjs asserts the opposite of the live verdict for two of them.\n5. **Low. Heuristic gaps now decide validity.** Fee-per-transfer, owner minting, pausable transfers and wallet limits are blocked live and missed locally. The brief said \"same heuristic\", so this is an observation.\n6. **Info. `launch_requires_review` and `unknown_skill`** are live codes absent from the refusal catalog.\n\n**Not done, deliberately:** I did not save the new live bodies beside the tests, because this review may not change any file. Each finding's reproduction carries the request and the live response verbatim. No Foundry proofs apply since there is no Solidity in scope.","treeHash":null,"usage":{"cachedInputTokens":1039996,"inputTokens":418,"model":"claude-fable-5-1","outputTokens":32987,"runtime":"claude","turns":25,"wallClockMs":444198}},{"artifacts":[],"attempt":1,"bundleHash":"be9286801dc539e29ab60e95b9ee35a36bbae6cc62834ec447726fa6477d2595","device":"0d3a91a6089edc68","findings":[],"hash":"e8ad590f784d212c71889f32fe8d566ba8a5986e2c7315bedb3233cde040c86d","nodeId":"f6cb1878-b9e0-4788-b280-a13fbd963a67","outcome":"completed","summary":"Fixed all four items and confirmed the reported verdicts against the live API.\n\n- `.github` paths now produce `protected_path`; `.vscode` still passes.\n- Exactly `./.git/config` passes; other Git protection remains.\n- `launch_token` now reports an error using the unchanged heuristic.\n- Schema regeneration preserves the corrected path pattern, with a byte-for-byte regression test.\n\nSaved live responses, updated the changelog, and preserved experimental labels.\n\nVerification: all 413 tests pass; schema regeneration is unchanged; `git diff --check` passes.","treeHash":"ffadc61ae4a66da404c193d196c0a50d1427c3e4","usage":{"cachedInputTokens":267904,"inputTokens":41961,"model":null,"outputTokens":3358,"runtime":"codex","turns":3,"wallClockMs":170652}}],"verification":[{"checks":[],"detail":"paths and tree verified; no suite was run for this kind of work","evaluation":"structural","profile":"none","status":"accepted","submissionHash":"e8ad590f784d212c71889f32fe8d566ba8a5986e2c7315bedb3233cde040c86d","verifiedTreeHash":"ffadc61ae4a66da404c193d196c0a50d1427c3e4","verifierVersion":"0.1.0+b537d296"}]}