{"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":"2234e9a2-925b-4a9b-85a6-a7c68712da72","kind":"shape:chain","nodes":[{"acceptedSubmissionHash":null,"dependsOn":["audit_imported_code"],"execution":{"network":false,"profile":"foundry","requires":[],"skillHash":"2d7d84a1a9972f1497e5bc68a704cbf42294d08c62d2868f102ffe3b909b9b51","skillId":"adapt-contract-project","tools":[]},"key":"adapt_contract_project","kind":"code","role":"implement","skillHash":"2d7d84a1a9972f1497e5bc68a704cbf42294d08c62d2868f102ffe3b909b9b51","skillId":"adapt-contract-project","state":"failed"},{"acceptedSubmissionHash":null,"dependsOn":["adapt_contract_project"],"execution":{"network":false,"profile":"foundry","requires":[],"skillHash":"e5ac2cb1fd91a56aa40b16487fc230c48de0d317c8266140331dd3219bb40a85","skillId":"audit-specialist","tools":[]},"key":"audit_economics","kind":"code","role":"review","skillHash":"e5ac2cb1fd91a56aa40b16487fc230c48de0d317c8266140331dd3219bb40a85","skillId":"audit-specialist","state":"waiting"},{"acceptedSubmissionHash":null,"dependsOn":["adapt_contract_project"],"execution":{"network":false,"profile":"foundry","requires":[],"skillHash":"e5ac2cb1fd91a56aa40b16487fc230c48de0d317c8266140331dd3219bb40a85","skillId":"audit-specialist","tools":[]},"key":"audit_flow","kind":"code","role":"review","skillHash":"e5ac2cb1fd91a56aa40b16487fc230c48de0d317c8266140331dd3219bb40a85","skillId":"audit-specialist","state":"waiting"},{"acceptedSubmissionHash":"ff186251591641e69caefb797d0b8b5e9d0c440ba9ae61ddb16315ddfb33e68f","dependsOn":[],"execution":{"network":false,"profile":"foundry","requires":[],"skillHash":"9897597976b0e72440db7ca402d225aea20f3d37205ba53e7df2400ff80f29cc","skillId":"audit-imported-code","tools":[]},"key":"audit_imported_code","kind":"code","role":"review","skillHash":"9897597976b0e72440db7ca402d225aea20f3d37205ba53e7df2400ff80f29cc","skillId":"audit-imported-code","state":"accepted"},{"acceptedSubmissionHash":null,"dependsOn":["adapt_contract_project","write_foundry_tests","manifest","audit_math","audit_permissions","audit_economics","audit_flow"],"execution":{"network":false,"profile":"foundry","requires":[],"skillHash":"af3aa01159bbf354f621cafa5c0006f6169e0938a8b015be508b7377dbf165bc","skillId":"audit-judge","tools":[]},"key":"audit_judge","kind":"code","role":"review","skillHash":"af3aa01159bbf354f621cafa5c0006f6169e0938a8b015be508b7377dbf165bc","skillId":"audit-judge","state":"waiting"},{"acceptedSubmissionHash":null,"dependsOn":["adapt_contract_project"],"execution":{"network":false,"profile":"foundry","requires":[],"skillHash":"e5ac2cb1fd91a56aa40b16487fc230c48de0d317c8266140331dd3219bb40a85","skillId":"audit-specialist","tools":[]},"key":"audit_math","kind":"code","role":"review","skillHash":"e5ac2cb1fd91a56aa40b16487fc230c48de0d317c8266140331dd3219bb40a85","skillId":"audit-specialist","state":"waiting"},{"acceptedSubmissionHash":null,"dependsOn":["adapt_contract_project"],"execution":{"network":false,"profile":"foundry","requires":[],"skillHash":"e5ac2cb1fd91a56aa40b16487fc230c48de0d317c8266140331dd3219bb40a85","skillId":"audit-specialist","tools":[]},"key":"audit_permissions","kind":"code","role":"review","skillHash":"e5ac2cb1fd91a56aa40b16487fc230c48de0d317c8266140331dd3219bb40a85","skillId":"audit-specialist","state":"waiting"},{"acceptedSubmissionHash":null,"dependsOn":["adapt_contract_project"],"execution":{"network":false,"profile":"foundry","requires":[],"tools":[]},"key":"manifest","kind":"code","role":"integrate","skillHash":null,"skillId":null,"state":"waiting"},{"acceptedSubmissionHash":null,"dependsOn":["adapt_contract_project"],"execution":{"network":false,"profile":"foundry","requires":[],"skillHash":"73431852439ad3a343f3d2b7db1c43cd363b9f48f9bb51497374a0cf6d50b223","skillId":"write-foundry-tests","tools":[]},"key":"write_foundry_tests","kind":"code","role":"tests","skillHash":"73431852439ad3a343f3d2b7db1c43cd363b9f48f9bb51497374a0cf6d50b223","skillId":"write-foundry-tests","state":"waiting"}],"objective":"Launch PLEA test run 4 on Sepolia from this Foundry repository exactly as launch.json lists it: five contracts (PLEA, CabalGate, Seasons, PleaDistributor, PleaLaunch), deployed in order in one transaction with the constructor arguments in launch.json. This is the code your test run 3 job c20158a4 (launch #1426) accepted and deployed, plus three changes. (a) imd/acc's trader rule in PleaHook: the cashback and the buy record go to the address in the swap's hookData (abi.encode(trader)), else to the wallet the router acts for, read from the router's msgSender() with 30,000 gas (the Universal Router and v4 Router expose it), else to the swap sender only when it is a plain wallet; never a router, tx.origin, the Stacker, the IMD token, the sIMD vault (read once from the Stacker's SIMD() at deploy; zero if absent), the PoolManager or the hook. When nothing resolves the trade still goes through without cashback (the 0.5% stays in the pool share) and without a buy record, a fee surplus with no trader is burned (PLEA) or kept for the pool (IMD), and a buy while the Cabal lives still reverts RecipientRequired because the hook must deliver the PLEA to someone; the gate always names the seller. test/TraderResolution.t.sol covers it with a router that exposes msgSender() and one that does not. (b) The rebalance() keeper tip is removed (KEEPER_TIP, TIP_MIN_WORK, TIP_INTERVAL, lastTipAt, _payTip, KeeperTipPaid): rebalance deploys the whole retained reserve and pays nobody, so the wall's IMD never leaves the pool. (c) The three low findings your run-3 judge left in the deployed code are fixed: Letterbox.deliver caps the 0.35 IMD auto-sell fee at the sale, so a fall of more than half between the plea's and the delivery's hourly snapshots cannot underflow it; an appeal letter's fees may go up to the appeal's own amount instead of only 10% of the balance, so whoever could plead by letter can appeal by letter; PLEA ownership is two-step (OpenZeppelin Ownable2Step), so a season leader cannot be made the owner and stripped of a prize. (d) From the Sepolia run-3 live test: an APPEAL letter is clamped to the original plea's share of the balance (CabalGate.pleaBalance is public), because the first letter's fee sale shrinks the balance and the same amount fell into the next share band (need 40 instead of 33). test/JudgeFindings.t.sol follows the judge's 110-sale crash recipe in both pool orientations (the first buy of the next hour succeeds, so the judge's fourth, unisolated finding does not reproduce) and tests the 50% ceiling and the 20%/35% share bands. Everything else is test run 3's: the ownerless Letterbox deployed after the launch through PleaLaunch.deployLetterbox(creationCode) against the keccak256 committed in launch.json (updated here for the changed Letterbox; a test checks it), letters as raw input data or letter(text), the commands APPEAL, SELL and NAME, 20% letters in escrow, fees paid in PLEA by an exact-output swap capped at the snapshot value plus 10% (Sepolia mints the 0.5 test oracle fee), deliver by anyone for at least 95% of the snapshot value with the 0.35 IMD fee to the season pot and the 0.25 IMD runner tip, settle on every other ending, status and leaderboard text views, display names, CabalGate's Letterbox-only submitFor/appealFor/executeSellFor and plea flags, PLEA.setLetterbox and courtTake, no Laureates, a plea of 1% to 50% of the balance (or 2.5M PLEA) with share bands 18/11/5 at 20%/35%, the owner excluded from points, prizes and bonus, the six-season PLEA bonus (87/3/10 mint, 5,000,000 PLEA per season, unpaid rest burned), judging rules v2 in CabalGate.DEFINITIONS (keep the text exactly), and the behaviour-preserving gas pass (test/GasPass.t.sol). The launch transaction must fit Ethereum's 16,777,216 per-transaction gas cap with your deployer's headroom: test/LaunchManifest.t.sol measures it as the chain charges it plus your factory's overhead (12.6M, 15.4M with 22% headroom) and asserts it; keep any fix within that. Audit the imported code and deploy it unchanged; fix only what the audit proves is a real bug, and keep every number as it is (fees, timings, panel 30, quorum 17, 5-hour seasons, 33-hour dead-man, START 1792162800, credit gas 1300000, letters 20%, plea maximum 50%, share bands 20/35, tip 0.25 IMD, auto-sell fee 0.35 IMD to the season pot, 0.70 IMD minimum plea value, 95% sale minimum, fee cap +10%, 1% plea minimum, six bonus seasons of 5,000,000 PLEA, the 87/3/10 split, msgSender gas 30000, no keeper tip). If you change the Letterbox, update its code hash in launch.json. Runtime sizes: CabalGate 23,889 bytes, PleaHook 24,288 (288 under the EIP-170 limit), Letterbox 21,383: keep any fix within them. Keep PLEA.setLauncher accepting either the deploy-transaction flag or PLEA's deployment block. Keep foundry.toml as it is, including [lint] lint_on_build = false and the read-only fs_permissions for ./out and ./launch.json. test/LetterboxInvariant.t.sol checks the Letterbox's custody in both fee modes, and test/fork/ForkMainnet.t.sol (with MAINNET_RPC_URL) rehearses letters against the real IMD, sIMD, PoolManager and Intake v2 on an Ethereum fork.","parentJobId":null,"planHash":"09e1c08107aff9c7f0d3eaeb95addfde7aaae766ca09f03068d63fae5bcc230d","previousHash":"0000000000000000000000000000000000000000000000000000000000000000","projectId":"2234e9a2-925b-4a9b-85a6-a7c68712da72","publication":{"commit":null,"deliveredAt":null,"repoUrl":"https://github.com/identity-md-launches/launch-1466-launch-plea-test-run"},"receiptIdentity":{"adapter":"0xde152afb7db5373f34876e1499fbd893a82dd336","chainId":1,"collection":"0x0000ec93127baa929e58e97dd0095a2bfb38ec1d","registry":"0x8004a169fb4a3325136eb29fa0ceb6d2e539a432"},"registry":"0xb6d0a187b050fa5bb0b87033a203f37becf4a775","research":[],"schema":"identitymd-work-v1","signals":[{"agentId":"52114","feedbackHash":"14207d5e44f39cc43469e4c294f6082c43ce03c2c5aeace68a57fa3c1574d72e","nodeKey":"audit_imported_code","submissionHash":"ff186251591641e69caefb797d0b8b5e9d0c440ba9ae61ddb16315ddfb33e68f","tag1":"review:submission","tag2":"acceptance-v2","value":1}],"site":null,"snapshotHash":"80a233792420bfb2be2a643116d5aef8b56594e544164e8d59d94ac70f9a8083","state":"blocked","submissions":[{"artifacts":[],"attempt":1,"bundleHash":null,"device":"918f8261a6fd589c","findings":[{"citation":"resolved","description":"imd/acc's trader rule (change a) says a trade whose trader cannot be resolved still goes through without cashback (after the Cabal dies) or falls back to the router's msgSender() (while it lives). _resolveTrader decodes any 32-byte hookData with abi.decode(hookData, (address)); Solidity 0.8 validates the address type and reverts with empty data when the word has bits above 160 set. The revert escapes beforeSwap and the PoolManager wraps it, so the swap reverts (WrappedError from the hook with empty inner data) instead of continuing down the resolution chain. A router that forwards user-supplied hookData, or a front end that encodes the trader with a packed or mis-padded word, makes every such swap fail even though a valid fallback (msgSender(), or 'no trader' after death) exists. Fix: decode the word as uint256 and treat word > type(uint160).max like the msgSender() path already does (return zero / fall through), so the stated rule holds for every 32-byte hookData.","line":655,"path":"src/PleaHook.sol","reproduction":"State: launch fixture (test/utils/Fixture.sol), Cabal dead (warp past lastVerdictAt + DEADMAN). Call: alice -> BuyRouter.buyExactIn(5e18, abi.encode(uint256(uint160(alice)) | (1 << 200))) (BuyRouter has no msgSender()). Expected per the trader rule: the buy goes through with plain v4 accounting, trader zero, no cashback, no buy record (as test_noTraderAfterDeathBuyGoesThroughWithoutCashback shows for empty hookData). Actual: the swap reverts (WrappedError(hook, 0x575e24b4 beforeSwap, 0x, 0xa9e35b2f HookCallFailed)). While the Cabal lives the same word through MsgSenderRouter.swap(true, 5e18, dirty) also reverts instead of falling back to the router's caller. Scratch test test/scratch/Leads.t.sol: test_dirtyHookDataWordRevertsAfterDeath and test_dirtyHookDataWordRevertsWhileAliveEvenWithMsgSender both fail on this code.","severity":"low","snippet":"            trader = abi.decode(hookData, (address));","title":"A 32-byte hookData word with dirty upper bits reverts the whole swap instead of resolving to no trader"},{"citation":"resolved","description":"Change (d) clamps an appeal to the original plea's share of the balance: sameShare = floor(bal * origAmount / origBal). CabalGate._appeal then checks the amount against its 1% minimum computed on min(bal, originalBalance): least = floor(bal * 100 / 10000). When the original site plea (Letterbox.plead with an explicit amount) was exactly the gate's minimum, origAmount = floor(origBal / 100), and the fee sale shrank the balance so bal < origBal, the clamp evaluates floor(bal/100 - bal*r/(100*origBal)) with r = origBal mod 100. Whenever origBal is not a multiple of 100 and bal is, that is floor(bal/100) - 1, one wei under the gate's least, and appealFor reverts AmountTooSmall. Balances at 18 decimals are essentially uniformly distributed mod 100, so about 1 in 100 such appeals fails for no reason the writer can see or fix (the Letterbox chooses the amount; the writer can only fall back to CabalGate.appeal and pay the fees in IMD by hand). Fix: compute sameShare with FullMath.mulDivRoundingUp, or raise it to the gate's minimum for min(bal, origBal) before calling appealFor.","line":297,"path":"src/Letterbox.sol","reproduction":"State: launch fixture, whale buys 2,000 IMD of PLEA, 1 day later holds B PLEA with B mod 100 != 0 (the test sets B = b0 - b0%100 - 1 with deal; a natural balance is equally likely to satisfy it). Calls: whale -> Letterbox.plead(B/100, text) (exactly the gate's 1% minimum; pleaBalance[id] == B), oracle answers 0 (Denied), Letterbox.settle(id) returns the escrow so the balance is bal = B - feePlea < B; with bal a multiple of 100 (the test rounds it down with deal; naturally about 1 in 100 cases), whale -> Letterbox.appealAuto(id, text). Expected: the appeal opens for the same 1% share as the original. Actual: revert CabalGate.AmountTooSmall() because sameShare = bal/100 - 1 < least = bal/100. Scratch test test/scratch/Leads.t.sol: test_appealAutoOfOnePercentPleaRoundsUnderTheGateMinimum fails with AmountTooSmall() on this code.","severity":"low","snippet":"            uint256 sameShare = FullMath.mulDiv(bal, gate.getPlea(originalId).amount, origBal);","title":"The Letterbox's same-share appeal clamp floors one wei below the gate's 1% minimum, refusing appealAuto of a minimum-size site plea"}],"hash":"ff186251591641e69caefb797d0b8b5e9d0c440ba9ae61ddb16315ddfb33e68f","nodeId":"34cccda1-a8ce-4076-bd81-6bb71818ab82","outcome":"completed","summary":"The review is complete. The findings file holds two substantiated defects, both low severity, and the working tree is otherwise untouched apart from the scratch test.\n\n**Contracts read in full:** PLEA, CabalGate, Seasons, PleaDistributor, PleaLaunch, PleaHook, Letterbox, OracleAttestation, and both interfaces. Everything in `src/` was reached. Not exercised: the fork rehearsals in `test/fork/` (no RPC in this environment) and the live Sepolia Intake, Stacker, sIMD and TestIMD contracts, which remain trusted dependencies. The non-fork suite passes (387 tests, 0 failures), the manifest has only the three schema keys with the right argument counts, and the runtime sizes match the task's figures (CabalGate 23,889, PleaHook 24,288, Letterbox 21,383).\n\n**Findings, both reproduced by `test/scratch/Leads.t.sol`:**\n\n- **Low, PleaHook.sol:655.** A 32-byte hookData word with bits above 160 set makes `abi.decode(hookData, (address))` revert, so the swap fails instead of falling through to the router's `msgSender()` or to \"no trader\" after death. Repro: a buy through a router with hookData `abi.encode(uint256(uint160(alice)) | 1 << 200)` reverts in both states where the rule says it should go through.\n- **Low, Letterbox.sol:297.** The same-share appeal clamp floors, and the gate's 1% minimum floors from a larger number, so an appeal of a site plea placed at exactly the 1% minimum lands one wei under the minimum whenever the original balance is not a multiple of 100 and the shrunk balance is. `appealAuto` then reverts `AmountTooSmall`, about 1 in 100 such appeals; the writer can only fall back to a hand appeal paying IMD. Rounding up or raising the clamp to the gate's minimum fixes it.\n\n**Nothing critical or high was found.** The new trader rule, keeper-tip removal, fee cap at delivery, Ownable2Step, and appeal-share clamp behave as specified in every other path I traced: custody of escrows and tips, exact-output fee swaps, PLEA transfer gating, Seasons accounting with ERC-6909 claims, and the hook's delta settlement in both pool orientations.\n\n**Trust assumptions to carry forward:** the oracle signer and Intake (the gate binds neither `a.requestId` to the Intake id nor `questionHash` to the body, so a misrouted callback from the Intake would be accepted), the Stacker's `credit`, the PLEA owner's add-only allowlist, the distributor owner's one-time Merkle root, and the immutable hook fee beneficiary.","treeHash":null,"usage":{"cachedInputTokens":1804658,"inputTokens":386,"model":"claude-fable-5-1","outputTokens":58031,"runtime":"claude","turns":25,"wallClockMs":1225594}}],"verification":[]}