{"workflow":null,"planning":null,"id":"d38487ec-5ad5-443b-8377-fe311797d775","state":"blocked","template":"shape:chain","objective":"Revision 1 of the IMDO flywheel project: apply the audit judge's findings 1-3 from the parent job, nothing else. Start from the accepted tree (commit 8f2430e9, the manifest node's result). Every file not named below must stay byte-identical; foundry.toml, lib/ and remappings.txt are not to be touched.\n\nFINDING 1 (medium, src/ImdoTreasury.sol, the checkpoint refresh in _executeImd after a successful buy). Today the checkpoint is overwritten with the post-swap slot0 sqrt-price with no bound. Replace it with a bounded refresh: let floorNow = checkpoint * CHECKPOINT_DECAY / (CHECKPOINT_DECAY + age) (the same floor quoteMinOut enforced in this call; factor it into one private view so both use it); read post = slot0 sqrt-price after the swap; set the new checkpoint to clamp(post, floorNow, floorNow * (BPS + MAX_CHECKPOINT_RISE_BPS) / BPS) where MAX_CHECKPOINT_RISE_BPS is a new public constant = 200 (sqrt-price bps, about 4% in price), capping the ceiling at TickMath.MAX_SQRT_PRICE; set checkpointAt = block.timestamp; emit a new event CheckpointRefreshed(uint160 postSwapSqrtPriceX96, uint160 checkpointSqrtPriceX96). Result: a sandwiched buy can never lower the next floor below what the 7-day decay allows, and a dust buy at a pushed price can raise the floor at most one bounded step, so a pinned floor decays under spot within about an hour instead of days. quoteMinOut's formula (max of spot and decayed checkpoint, fee-adjusted, 300 bps slippage) is unchanged. Add test/unit/CheckpointRefresh.t.sol containing the judge's own proof test (local PoolManager, ETH/IMD fee 10000 spacing 200 at tick 54000 seeded full range with 200 ETH, one 1e18 staker, treasury with the manifest literals; 20 sandwiched rounds; assert the checkpoint stays >= 95% of the market sqrt-price) plus an upward case: push the sqrt-price to 1.5x market by selling IMD, fund the treasury 3 gwei, warp 600 s, process(), assert the checkpoint <= market * (BPS + MAX_CHECKPOINT_RISE_BPS) / BPS, buy back to market, then fund 0.01 ETH and process() every 600 s and assert the IMD leg buys again within 6 calls. Both tests must fail on the parent's code and pass on the revised code.\n\nFINDING 3 (low, script/DeployImdo.s.sol preflight). The claim is created in a later transaction than the staking that bakes in its predicted address, so a launch timestamp that has passed by the broadcast block reverts the claim creation and burns the address. Add public constant MIN_LAUNCH_LEAD = 1 hours and make preflight revert InvalidConfiguration when c.launch < block.timestamp + MIN_LAUNCH_LEAD. Add a test in test/unit/ImdoDeploy.t.sol: launch == now and launch == now + MIN_LAUNCH_LEAD - 1 both revert before any creation (deployer nonce unchanged); launch == now + MIN_LAUNCH_LEAD deploys and staking.claimContract() == address(claim). docs/DEPLOYMENT.md: the launch row says at least 1 hour after the simulation, and the transaction-order paragraph says staking and claim must be consecutive deployer transactions and that a reverted claim creation burns the predicted address (token and staking must then be redeployed).\n\nFINDING 2 (low, disclosure only, no code change). launch.json wires $owner as the ImdoStaking claim caller and stakeFor restarts the beneficiary's 24-hour lock, so that address could call stakeFor(staker, 1) once a day and keep any staker's principal locked. State this plainly as a trust assumption in README.md (the manifest section and the 'Who can call what' row for the claim address) and in the launch.json notes, and state that the manual deployment avoids it because ImdoClaim is the only stakeFor caller and stakes only for msg.sender. Do not change ImdoStaking.\n\nREADME.md's checkpoint paragraph must describe the bounded refresh (floor on the low side, MAX_CHECKPOINT_RISE_BPS on the high side, the CheckpointRefreshed event). Keep the README's existing rules: credits ADAM, lists each change and who can call what, never says yield, APY, returns, investment, carbon offset or net zero, does not claim an audit. forge build, forge fmt --check and the full default suite (the parent's 98 tests plus the new ones) must pass offline. Findings 4-7 (informational) need no change.","blockedReason":"node refine_project: runtime_error","createdAt":"2026-10-07T01:25:40.230Z","updatedAt":"2026-10-07T01:27:21.082Z","paidBy":"0x28aa88fb640ef295a41b2759fe240c5c7578c2db","parentJobId":"948f8b1b-a4bd-4689-a346-2536b218e486","project":{"id":"948f8b1b-a4bd-4689-a346-2536b218e486","head":"d38487ec-5ad5-443b-8377-fe311797d775","running":null,"versions":[{"jobId":"948f8b1b-a4bd-4689-a346-2536b218e486","workflowId":null,"objective":"Token name: IMD Offsets. Token symbol: IMDO. Total supply 1,000,000,000 with 18 decimals. Chain id 11155111, paired with ETH. Ethereum mainnet is the real target, deployed manually from this repo.\n\nBASE. The workspace is the ADAM repo (MIT). Fork it: keep its layout, vendored lib/, foundry.toml unchanged (bytecode_hash \"none\"; README quotes it), errors, guards, pull payments, tests. Rename Adam* to Imdo*, drop @custom:x tags, delete AdamSplitOracle and every PNKSTR/IMDSTR path. README credits ADAM, lists each change and who can call what, never says yield, APY, returns, investment, carbon offset or net zero. Do not claim an audit.\n\nPURPOSE. Trades on one hooked ETH/IMDO pool pay a fee in ETH: it buys IMD for IMDO stakers, funds REGEN bought off-chain for stakers, funds retirement of ecological credits, and operations.\n\nCONTRACTS (exact).\n1. IMDOToken = LaunchToken; constructor() no args; name \"IMD Offsets\", symbol \"IMDO\".\n2. ImdoHook = AdamHook, behaviour unchanged; constructor(IPoolManager,address token,address treasury,address owner). Not in the manifest.\n3. ImdoStaking = AdamDistributorV2 reduced. constructor(address imdo,address imd,address manager,address claim,address regenSafe). One reward token (IMD) plus an ETH credit under key address(0). Keep stake, unstake, claim, exit, claimReward, earned, the 24h lock reset by every stake, stakeFor only by claim, the 7-day backlog gated at 10,000,000 staked, fixed exclusions. Remove directDistribution, claimIMDSTR, swaps, unlockCallback. Add notifyRegen() external payable: anyone, msg.value > 0, credits stakers under address(0) exactly as ADAM's non-direct notifyIMDSTR. regenCreditOf(address) view = lifetime credit, never reduced by claim, exit or unstake. withdrawRegen(uint256): only regenSafe, pays regenSafe, cumulative withdrawals <= cumulative notifyRegen ETH. claim/exit never pay ETH. Events RegenNotified(address indexed from,uint256 amount,uint256 distributed), RegenWithdrawn(uint256 amount).\n4. ImdoTreasury = AdamTreasuryV2 reduced. FLAT constructor(address staking,address opsWallet,address offsetsSafe,address regenSafe,address manager,address imd,uint24 imdFee,uint24 imdTickSpacing,address imdHooks,uint256 maxEthPerBuy,uint16 slippageBps,uint32 cooldown,uint256 regenCap,uint256 regenCapMin,uint256 regenCapMax). process(): anyone, cooldown, keeper bounty 50 bps of newly processed ETH as V2. The net splits by constants: OPS_BPS 1000 to opsOwed (payOps(), only opsWallet); OFFSETS_BPS 2500 to offsetsOwed (payOffsets(), only offsetsSafe); REGEN_BPS 2500 to the REGEN leg; the remaining 4000 to the IMD leg. IMD leg = ADAM's IMD buy leg (cap, checkpoint/spot min-out, halving retries; no rerouting). REGEN leg: at most regenCap wei accrue per epoch (block.timestamp / 7 days); the excess joins the IMD leg in the same call; accrued ETH goes to staking.notifyRegen{value}; a failed call stays pending for retry and never blocks process(). setRegenCap(uint256): only regenSafe, within [regenCapMin, regenCapMax]; the ONLY setter. No owner, upgrade or other withdrawal.\n5. ImdoClaim = NFTClaim changed. constructor(address imdo,address staking,address seatNFT,uint256 seatSize,bytes32 holderRoot,uint256 launch). SEAT_ALLOCATION 100,000,000e18 over seat ids [0, seatSize); HOLDER_ALLOCATION 10,000,000e18. Vesting and deadline as ADAM (10 daily tranches, launch + 39 days, burnUnclaimed). claimSeat(uint256[] ids,bool stake): caller must be ownerOf(id); sets seatClaimedBy[id] = caller and emits SeatClaimed(uint256 indexed id,address indexed owner) whenever it changes; pays vested tranches; the entitlement follows the NFT. claimHolder(uint256 total,bytes32[] proof,bool stake): leaf keccak256(bytes.concat(keccak256(abi.encode(msg.sender,total)))), OpenZeppelin MerkleProof, same tranches, per-address claimed; reverts when holderRoot is zero. stake = true calls staking.stakeFor(msg.sender, amount).\n\nMANIFEST. launch.json kind evm_project, token IMDOToken, contracts in order ImdoStaking then ImdoTreasury, FLAT constructorArgs (5 and 15 items), each $token, $owner, $contract:ImdoStaking or a literal. Later contracts cannot be referenced: use $owner for claim, regenSafe, offsetsSafe, opsWallet and say so in notes. Literals: manager 0x000000000004444c5dc75cB358380D2e3dE08A90, IMD 0xD34a99Bc0f67aE1bbd63C660e6d0b0dd03E263B7, 10000, 200, hooks zero, 1 ETH, 300, 600, cap 0.5 ETH, min 0.05, max 5. ImdoHook and ImdoClaim are deployed only by the script.\n\nSCRIPT. script/DeployImdo.s.sol, manual signer, no env or keys, all addresses, opening tick and holderRoot as arguments: token, staking (claim address predicted as DeployAdamExtension does), claim, treasury, mined hook, initialize, one single-sided position of 890,000,000 IMDO with its LP NFT sent to dead, fund claim with 110,000,000, start the two-step hook ownership transfer. Seat NFT 0x0000eC93127BAA929E58E97dd0095A2BFb38ec1D, seatSize 2000.\n\nTESTS (by another worker, from this text). Adapt ADAM's suites; add: exact split and bounty; cap, overflow, epoch roll; setRegenCap bounds and auth; notifyRegen credit incl. no stake; regenCreditOf monotonic; withdrawRegen auth and bound; failed notifyRegen retried; seat claim after NFT transfer; holder proof, double claim, zero root; stakeFor lock; script test; mainnet-fork IMD buy; invariants: staking ETH >= notified - withdrawn; treasury ETH = owed + pending.","baseCommit":"785edda603f9ed41364e08d46ecea440bcc0d6e2","state":"blocked","createdAt":"2026-10-06T19:10:35.248Z"},{"jobId":"d38487ec-5ad5-443b-8377-fe311797d775","workflowId":null,"objective":"Revision 1 of the IMDO flywheel project: apply the audit judge's findings 1-3 from the parent job, nothing else. Start from the accepted tree (commit 8f2430e9, the manifest node's result). Every file not named below must stay byte-identical; foundry.toml, lib/ and remappings.txt are not to be touched.\n\nFINDING 1 (medium, src/ImdoTreasury.sol, the checkpoint refresh in _executeImd after a successful buy). Today the checkpoint is overwritten with the post-swap slot0 sqrt-price with no bound. Replace it with a bounded refresh: let floorNow = checkpoint * CHECKPOINT_DECAY / (CHECKPOINT_DECAY + age) (the same floor quoteMinOut enforced in this call; factor it into one private view so both use it); read post = slot0 sqrt-price after the swap; set the new checkpoint to clamp(post, floorNow, floorNow * (BPS + MAX_CHECKPOINT_RISE_BPS) / BPS) where MAX_CHECKPOINT_RISE_BPS is a new public constant = 200 (sqrt-price bps, about 4% in price), capping the ceiling at TickMath.MAX_SQRT_PRICE; set checkpointAt = block.timestamp; emit a new event CheckpointRefreshed(uint160 postSwapSqrtPriceX96, uint160 checkpointSqrtPriceX96). Result: a sandwiched buy can never lower the next floor below what the 7-day decay allows, and a dust buy at a pushed price can raise the floor at most one bounded step, so a pinned floor decays under spot within about an hour instead of days. quoteMinOut's formula (max of spot and decayed checkpoint, fee-adjusted, 300 bps slippage) is unchanged. Add test/unit/CheckpointRefresh.t.sol containing the judge's own proof test (local PoolManager, ETH/IMD fee 10000 spacing 200 at tick 54000 seeded full range with 200 ETH, one 1e18 staker, treasury with the manifest literals; 20 sandwiched rounds; assert the checkpoint stays >= 95% of the market sqrt-price) plus an upward case: push the sqrt-price to 1.5x market by selling IMD, fund the treasury 3 gwei, warp 600 s, process(), assert the checkpoint <= market * (BPS + MAX_CHECKPOINT_RISE_BPS) / BPS, buy back to market, then fund 0.01 ETH and process() every 600 s and assert the IMD leg buys again within 6 calls. Both tests must fail on the parent's code and pass on the revised code.\n\nFINDING 3 (low, script/DeployImdo.s.sol preflight). The claim is created in a later transaction than the staking that bakes in its predicted address, so a launch timestamp that has passed by the broadcast block reverts the claim creation and burns the address. Add public constant MIN_LAUNCH_LEAD = 1 hours and make preflight revert InvalidConfiguration when c.launch < block.timestamp + MIN_LAUNCH_LEAD. Add a test in test/unit/ImdoDeploy.t.sol: launch == now and launch == now + MIN_LAUNCH_LEAD - 1 both revert before any creation (deployer nonce unchanged); launch == now + MIN_LAUNCH_LEAD deploys and staking.claimContract() == address(claim). docs/DEPLOYMENT.md: the launch row says at least 1 hour after the simulation, and the transaction-order paragraph says staking and claim must be consecutive deployer transactions and that a reverted claim creation burns the predicted address (token and staking must then be redeployed).\n\nFINDING 2 (low, disclosure only, no code change). launch.json wires $owner as the ImdoStaking claim caller and stakeFor restarts the beneficiary's 24-hour lock, so that address could call stakeFor(staker, 1) once a day and keep any staker's principal locked. State this plainly as a trust assumption in README.md (the manifest section and the 'Who can call what' row for the claim address) and in the launch.json notes, and state that the manual deployment avoids it because ImdoClaim is the only stakeFor caller and stakes only for msg.sender. Do not change ImdoStaking.\n\nREADME.md's checkpoint paragraph must describe the bounded refresh (floor on the low side, MAX_CHECKPOINT_RISE_BPS on the high side, the CheckpointRefreshed event). Keep the README's existing rules: credits ADAM, lists each change and who can call what, never says yield, APY, returns, investment, carbon offset or net zero, does not claim an audit. forge build, forge fmt --check and the full default suite (the parent's 98 tests plus the new ones) must pass offline. Findings 4-7 (informational) need no change.","baseCommit":"785edda603f9ed41364e08d46ecea440bcc0d6e2","state":"blocked","createdAt":"2026-10-07T01:25:40.230Z"}]},"deliver":true,"host":false,"site":null,"launch":{"requested":false,"kind":null,"id":null,"status":null,"chainId":null},"oracleRequestId":null,"delivery":null,"media":null,"nodes":[{"key":"adversarial_review","role":"review","state":"waiting","attempt":0,"revisions":0,"judgeRevisions":0,"dependsOn":["refine_project"],"allowedPaths":[],"failureReason":null,"dispatchNote":null,"dispatchNoteAt":null,"updatedAt":"2026-10-07T01:25:40.230Z","verdict":null,"seat":null,"live":null},{"key":"refine_project","role":"implement","state":"failed","attempt":3,"revisions":0,"judgeRevisions":0,"dependsOn":[],"allowedPaths":["src/ImdoTreasury.sol","script/DeployImdo.s.sol","README.md","docs/DEPLOYMENT.md","launch.json","test/**"],"failureReason":"runtime_error","dispatchNote":null,"dispatchNoteAt":null,"updatedAt":"2026-10-07T01:27:21.082Z","verdict":null,"seat":null,"live":null}],"reviews":[{"status":"sent","chainId":1,"txHash":"0x4d22e0e82aa984d68a75a481bd146b140a5ed93e6cff8bb3ec81b574dc826a6f","blockNumber":26137219,"sentAt":"2026-10-07T01:28:05.044Z","entries":[]}]}