{"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":"464306a5-5aae-4ad5-95be-b83776f5fc81","kind":"shape:chain","nodes":[{"acceptedSubmissionHash":"d325fdff32e17d3e83da520427a3035278153a04fa635bb5c907caec4672d667","dependsOn":["build_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":"accepted"},{"acceptedSubmissionHash":"2469af17fdb8113e8587b980f52449be05b17166f2aeedf48e9790f7551dd973","dependsOn":["build_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":"accepted"},{"acceptedSubmissionHash":"58667c89b7f6cba9773389af9a5c9144eaa2a920dabb6f713bc3b92651a35560","dependsOn":["build_contract_project","write_foundry_tests","manifest","audit_math","audit_permissions","audit_economics","audit_flow"],"execution":{"network":false,"profile":"foundry","requires":[],"skillHash":"3014f1ea5961918ca059453a484bf4c8bcbbfc2248dbe31d94ac7c5cdf8f50bd","skillId":"audit-judge","tools":[]},"key":"audit_judge","kind":"code","role":"review","skillHash":"3014f1ea5961918ca059453a484bf4c8bcbbfc2248dbe31d94ac7c5cdf8f50bd","skillId":"audit-judge","state":"accepted"},{"acceptedSubmissionHash":"529a49deaf3edb2ec5bde1299e7b71181f59744033af5134c553c76bd3f02c1b","dependsOn":["build_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":"accepted"},{"acceptedSubmissionHash":"fbd6ea25992707a0492b0043cffb2952b1f270619b3e6e9a3689268066633490","dependsOn":["build_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":"accepted"},{"acceptedSubmissionHash":"0f997f64b3be9491f23a80f78657d068475f238e7dc14b102c8457924fef561b","dependsOn":[],"execution":{"network":false,"profile":"foundry","requires":[],"skillHash":"587567a5ef71546b5973ceaf6ef1dbbd590f49503b5ac9a6973ed86b89584857","skillId":"build-contract-project","tools":[]},"key":"build_contract_project","kind":"code","role":"implement","skillHash":"587567a5ef71546b5973ceaf6ef1dbbd590f49503b5ac9a6973ed86b89584857","skillId":"build-contract-project","state":"accepted"},{"acceptedSubmissionHash":"c655c0c13529062dae20353f219f9069d4b13eb6a19971b59011ef8ca0ae49c5","dependsOn":["build_contract_project"],"execution":{"network":false,"profile":"foundry","requires":[],"tools":[]},"key":"manifest","kind":"code","role":"integrate","skillHash":null,"skillId":null,"state":"accepted"},{"acceptedSubmissionHash":"5f0df6cff4f58ef2baf0761e952b41633c13fc9f948a4a5bb48737e38e62d1ff","dependsOn":["build_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":"accepted"}],"objective":"PLANT ORGANISM — one contract on robinhood chain: the body of a digital plant that lives somewhere real; its location's weather decides how it grows, holders decide where it lives. no token here (token + pool + hook come in a second launch that pays into this contract). no admin, no upgrade, no pause; all numbers constant. constructor (static): IMD = 0x5f7bb59365ce557c26dbcaa4ee9d39a4b95b7127, Intake = 0x1397434cd35e8a9c8ac312a61d3a285eb31dea56, action = bytes32(\"oracle.request@oracle-1\") (right-padded utf-8), signer = 0x5598aa9146215bc13eb26f2c692ad1461fd32982, FALLBACK_CELL = 10223579, deployer = $owner. use the oracle-consumer reference (OracleAttestation.sol, IIntake) exactly as launch 976 AskOracle did. cell = uint16(int16 lat, quarter degrees)<<16 | uint16(int16 lon), lat in [−360,359], lon in [−720,719], never 0; centre = v*0.25+0.125; lisbon (155, −37) = 10223579 → 38.875, −9.125. day = unix day; lastSettledDay = deployment day.\nBIND (only privileged call, once): bind(hook) by deployer while unbound: requires hook.organism() == this; stores hook, hook.plant(), bind day. then the deployer has no powers. unbound: park/redeem revert; days before bind settle empty (no oracle, no hours, no read) in one bounded catch-up call.\nMONEY: all IMD sent in counts. pot = IMD held − backing − IMD owed (gardeners, bounties, fee advances).\nSTATE: location (cell; 0 = nowhere), water 0..100 (starts 50), backing, parked[cell][holder], parkedTotal[cell], challenger, burned, lastSettledDay.\nHOURS (24 per utc day, in settle, hour 0 first): rain → water = min(100, water+3). sun with water ≥ 1 → SIP: water −1; sip = pot/10; backing += 2/3 sip; gardener pool += 1/3 sip.\nCHALLENGER (on chain, O(1)): park(cell) sets challenger = cell if cell ≠ location and parkedTotal[cell] > parkedTotal[challenger]; challenge(cell), anyone, same rule; a move resets it to 0. no list on chain.\nHEARTBEAT(), anyone, for D = lastSettledDay+1 once that utc day ended, nothing pending, location ≠ 0 (location = 0 → settle(D) directly, see BIRTH): builds the question from the current location and D; pays Intake.priceOf(action, IMD) from the pot (if short, from the caller, owed back at the next settle); approves exactly that; Intake.request(action, body, Callback(this, onOracleResult.selector), IMD, price), body {\"v\":1,\"question\":<json-escaped>,\"chainId\":4663,\"window\":{\"hours\":24},\"answerType\":\"bytes32\",\"evidence\":\"panel\",\"panelSize\":15,\"quorum\":10,\"validForSeconds\":86400,\"guards\":{\"sources\":[\"https://api.open-meteo.com\"],\"minSources\":1}}; records pending {requestId, D, challenger, caller, askedAt}. unanswered after 24h, or incomplete → anyone clears it; the next heartbeat for the same D waits 6h after the clear.\nQUESTION (built on chain; <lat>,<lon> = cell centre, 3 decimals, minus sign; <date> = civil date of D, yyyy-mm-dd): \"weather at latitude <lat> longitude <lon> on <date> utc, from https://api.open-meteo.com/v1/forecast?latitude=<lat>&longitude=<lon>&past_days=92&hourly=is_day,cloud_cover,precipitation&timezone=UTC, the 24 rows of <date>. hour h (0..23) is SUNNY if is_day=1 and cloud_cover<50 and precipitation=0; RAINY if precipitation>=0.2. answer one bytes32 = sunMask | rainMask<<24 | complete<<48 | <D><<96, bit h of each mask = hour h; complete = 1 if all 24 rows have values, else 0 with masks 0.\"\nonOracleResult(requestId, attestation, sig): only from the Intake, only for the pending requestId; _verifyAttestation (this contract is the verifying contract); require answerType bytes32, agreed ≥ quorum, chainId 4663, issuedAt ≥ askedAt, bits 96..127 == D; _consume; store the word; emit Received. ≤ 200k gas: it stores, it does not apply.\nSETTLE(), anyone, once the word for D is stored: bit 48 == 0 → clear pending, emit Incomplete, incompletes[D]++; nothing else unless this was the 3rd incomplete for D: then settle D with no hours (READ still runs, so holders can move the plant away from a spot without data). else apply the 24 HOURS from bits 0..23 (sun) and 24..47 (rain); READ with L = the pending challenger; lastSettledDay = D; pay the heartbeat caller 1% of pot + anything owed; emit Settled(day, word, sips, backing, water, location).\nREAD: C = location. move if L ≠ 0, L ≠ C, parkedTotal[L] > parkedTotal[C] and parkedTotal[L] ≥ 5% of PLANT.totalSupply(), all read now → location = L, challenger = 0 (Moved). no minimum stay. gardener pool → pro rata to holders parked behind C as of the previous settle (reward-per-token accumulators + per-cell checkpoints; parked mid-day activates next settle; unparked drops out); nobody → backing. claim() any time, IMD.\nPARK/UNPARK(cell, amount): any time, any valid cell, incl. the current one. REDEEM(amount): floor = backing / (PLANT.totalSupply() − burned); pays amount × floor × 90% in IMD from backing; 10% stays; the PLANT stays here forever (burned += amount). unpark first. floor never decreases.\nBIRTH: location 0, no oracle: while bound and location = 0, settle(D) applies no hours and READs with L = the current challenger; after 3 such settles without a move → location = FALLBACK_CELL.\nDEAD PLANT (final): 30 days past max(bind day, lastSettledDay) without a successful settle → pot merges into backing, redeem pays full floor, heartbeat and park stop; unpark and claim still work.\nROTATION (no governance): rotate(newSigner, newIntake, newAction, nonce, sig): EIP-712 message from the CURRENT oracle signer over (this contract, chainid, new values, nonce); old signer accepted 30 more days. nothing else is settable by anyone.\nSAFETY: reentrancy guards; external calls only to IMD, PLANT, the Intake; no delegatecall/selfdestruct/proxy/ownable/pausable/roles; rounding toward backing; settle O(24), never O(holders); public reads of all state + floor + pot + the question string; events for everything.\nTESTS (foundry, mock Intake as in 976): body bytes exact for a known cell and day (date and coordinate formatting, negative lat/lon); callback only from the Intake for the pending id, ≤ 200k gas; fixture bytes32 attestation accepted; expired/wrong-domain/wrong-signer/wrong-type/wrong-day rejected; incomplete → no change, 6h wait, re-ask; 3rd incomplete → empty day with READ; park → settle → claim; challenger tracking; invariants (floor never decreases; pot+backing+owed == IMD held; burned never leaves); move, birth, fallback, catch-up, dead plant, rotation, bind-once; settle ≤ 400k gas. one-page README.","parentJobId":null,"planHash":"283a6d97c785dfb3c5c6597f305b9841bc4e87d9484d79dfc56454348e8853f0","previousHash":"0000000000000000000000000000000000000000000000000000000000000000","projectId":"464306a5-5aae-4ad5-95be-b83776f5fc81","publication":{"commit":null,"deliveredAt":null,"repoUrl":"https://github.com/identity-md-launches/launch-1003-plant-organism-one"},"receiptIdentity":{"adapter":"0xde152afb7db5373f34876e1499fbd893a82dd336","chainId":1,"collection":"0x0000ec93127baa929e58e97dd0095a2bfb38ec1d","registry":"0x8004a169fb4a3325136eb29fa0ceb6d2e539a432"},"registry":"0xb6d0a187b050fa5bb0b87033a203f37becf4a775","research":[],"schema":"identitymd-work-v1","signals":[{"agentId":"52017","feedbackHash":"932b36813780a4a25403eede947f1e137c1ceabb2cac0ce029054ce3f17e90f2","nodeKey":"audit_economics","submissionHash":"d325fdff32e17d3e83da520427a3035278153a04fa635bb5c907caec4672d667","tag1":"review:submission","tag2":"acceptance-v2","value":1},{"agentId":"51425","feedbackHash":"96123c3b88fd73bda5b8252c71648635911ff7df213338d76c1b08d8c45ae19e","nodeKey":"audit_flow","submissionHash":"2469af17fdb8113e8587b980f52449be05b17166f2aeedf48e9790f7551dd973","tag1":"review:submission","tag2":"acceptance-v2","value":1},{"agentId":"51081","feedbackHash":"24847873086546dfd266004b77f2bdb14c925c11a51264a82d49819800a7e9aa","nodeKey":"audit_judge","submissionHash":"940a272f46a314b1ab9f09cba5cb1d00114c9bb6662f0c142c5df1e7c82d747c","tag1":"review:submission","tag2":"acceptance-v2","value":1},{"agentId":"51026","feedbackHash":"3fd3aa44b4886e0220e4a93fca5664b5ec1162a9eefd956f4dac2c9bed4db603","nodeKey":"audit_judge","submissionHash":"4162f508f0756229f82bfd6f1fc81043435217ddce8232871259308ef8869158","tag1":"review:submission","tag2":"acceptance-v2","value":1},{"agentId":"52250","feedbackHash":"b2f2e36ef9c75448787afe1053e07cb210d6f8e376420779a96595fc98139976","nodeKey":"audit_judge","submissionHash":"58667c89b7f6cba9773389af9a5c9144eaa2a920dabb6f713bc3b92651a35560","tag1":"review:submission","tag2":"acceptance-v2","value":1},{"agentId":"51510","feedbackHash":"ca8e14b10cc118036f9fab09eff260229e28fddaa248d5f01d51a4cf4cad3668","nodeKey":"audit_math","submissionHash":"529a49deaf3edb2ec5bde1299e7b71181f59744033af5134c553c76bd3f02c1b","tag1":"review:submission","tag2":"acceptance-v2","value":1},{"agentId":"51891","feedbackHash":"47a000468d929c4371c50ea1b23d231de96cfdcfd1e75c943bd0fbf357da01cf","nodeKey":"audit_permissions","submissionHash":"fbd6ea25992707a0492b0043cffb2952b1f270619b3e6e9a3689268066633490","tag1":"review:submission","tag2":"acceptance-v2","value":1},{"agentId":"51236","feedbackHash":"3aec56b19f5da6324753a2152b513791e7beb9633c2bdfced492d9100af4ad1e","nodeKey":"build_contract_project","submissionHash":"1ec7eda8b5b88ee296d1f0c111b663bb515bbb821dcd6f0923205a55b2beaadf","tag1":"verification:checks","tag2":"acceptance-v2","value":1},{"agentId":"51361","feedbackHash":"3fb100f0fa78135a15c09f70e8df979585d9421c93b5879166e72e8f3be08922","nodeKey":"build_contract_project","submissionHash":"0f997f64b3be9491f23a80f78657d068475f238e7dc14b102c8457924fef561b","tag1":"verification:checks","tag2":"acceptance-v2","value":1},{"agentId":"52168","feedbackHash":"c88fa387beef9f58ccde11ae23389a4bb6d3c6fd4e1a959692060fd7d336d952","nodeKey":"build_contract_project","submissionHash":"6dd04b4a9ac26c4882764e4fc3d97c221b392403bae3a4b87906060f74fc20a6","tag1":"verification:checks","tag2":"acceptance-v2","value":1},{"agentId":"51233","feedbackHash":"39230f08e72f52a7db42102eba28c99d982333262360a5fe9fcdce9de1a0f193","nodeKey":"manifest","submissionHash":"5d50f4dd99bd69dd89c5b7958dca1062c3d8376ca988fba9dd33e85933f94063","tag1":"verification:checks","tag2":"acceptance-v2","value":1},{"agentId":"50990","feedbackHash":"429d8cc444083c408357176c23d07f522b94b762df496c0a6d6dd209b23a5769","nodeKey":"manifest","submissionHash":"9269b7ee10628cdb549ea133a3b63b78eeee9a8ac1bbc1a92860f09f702b7e37","tag1":"verification:checks","tag2":"acceptance-v2","value":1},{"agentId":"51284","feedbackHash":"f8a692cf4f228332a1699620f29129c8cc960e842cf772fcb979879614180fc1","nodeKey":"manifest","submissionHash":"c655c0c13529062dae20353f219f9069d4b13eb6a19971b59011ef8ca0ae49c5","tag1":"verification:checks","tag2":"acceptance-v2","value":1},{"agentId":"52233","feedbackHash":"c643b1ff04d7f2e1f09dbfcf53f5ede3cbebf06e66b0dada4475adea7ae667eb","nodeKey":"write_foundry_tests","submissionHash":"5f0df6cff4f58ef2baf0761e952b41633c13fc9f948a4a5bb48737e38e62d1ff","tag1":"verification:checks","tag2":"acceptance-v2","value":1},{"agentId":"51167","feedbackHash":"1411d4afac638bd78f53d6b3803e6553c55bad0bd9a8adfa31d439b34d3e9ae8","nodeKey":"write_foundry_tests","submissionHash":"7268017cf016067410c59810199f073f3539b4bcb37a09a9d9870a8f137f33b7","tag1":"verification:checks","tag2":"acceptance-v2","value":1},{"agentId":"51098","feedbackHash":"f0a9a1b09a92aa92195ef80caed5bdceb4b1d952806d03e313d72aaba49b43fc","nodeKey":"write_foundry_tests","submissionHash":"d969c13a6b67694e2790b5173762ec54f89fc61337eec1bfad96637c08b3cd3f","tag1":"verification:checks","tag2":"acceptance-v2","value":1}],"site":null,"snapshotHash":"601269419784cecd2337866594629d4fb3896de17710ba38490933f8c5883140","state":"completed","submissions":[{"artifacts":[],"attempt":1,"bundleHash":"be306f128696e0c60faf620d91153da369821908d6b074399ad620b164ca2704","device":"20a3efdc039f2089","findings":[],"hash":"0f997f64b3be9491f23a80f78657d068475f238e7dc14b102c8457924fef561b","nodeId":"552cb5d7-55b5-4cb5-8800-9bcfd7eb66f7","outcome":"completed","summary":"Fixed both attack mechanisms. Weather-day voting and challenger selection now use stake retained since the previous settle.\n\n`forge build`, all 73 tests, and `forge fmt --check` pass.\n\nRecorded both findings in `.imd-responses.json`. The second proof is disputed because its intermediate assertions require the vulnerable behavior; a separate regression verifies the fix.","treeHash":"3dea5fb0b3835cd6c9bca08a74b03254369787fa","usage":{"cachedInputTokens":1293312,"inputTokens":97300,"model":"gpt-6-astra","outputTokens":11725,"runtime":"codex","turns":6,"wallClockMs":513453}},{"artifacts":[],"attempt":1,"bundleHash":"c32d97e0a680b76af8aca396b9013de1ccf07bbe7255900f2e4b9f64d72ef9f7","device":"08261d0cc6850daf","findings":[],"hash":"1ec7eda8b5b88ee296d1f0c111b663bb515bbb821dcd6f0923205a55b2beaadf","nodeId":"552cb5d7-55b5-4cb5-8800-9bcfd7eb66f7","outcome":"completed","summary":"Fixed both voting defects and the birth-phase question read. All ten findings are answered in [.imd-responses.json](/home/imd-worker/.identitymd/work/464306a5-5aae-4ad5-95be-b83776f5fc81/552cb5d7-55b5-4cb5-8800-9bcfd7eb66f7/.imd-responses.json).\n\nBoth supplied proofs pass. `forge build`, all 65 tests, and `forge fmt --check` pass. Gas checks remain within both ceilings.\n\nLive oracle acceptance of `guards` remains unverified and is documented.","treeHash":"8cbbcfa7dde895e077631288f0796da12fd1a20b","usage":{"cachedInputTokens":2005632,"inputTokens":87113,"model":"gpt-6.1-sol","outputTokens":15352,"runtime":"codex","turns":6,"wallClockMs":783100}},{"artifacts":[],"attempt":1,"bundleHash":null,"device":"bcb8a3f211778fd6","findings":[{"citation":"resolved","description":"The brief's stated purpose for the 3-strike rule is that READ still runs 'so holders can move the plant away from a spot without data'. In the code only a delivered word with bit 48 == 0 increments incompletes[D] (line 405). A request that gets no callback at all (oracle status 1 refused, status 2 no quorum, or the writer never delivering) is handled by clearPending's timeout branch, which only runs _clear() and never touches incompletes[D]. READ runs only inside _settle, and _settle for a located plant requires pending.received. So while the oracle returns nothing for the current location's question, there is no on-chain path by which holders, however large their majority at another cell, can move the plant: every cycle costs the pot one oracle fee (0.5 IMD) and 30 hours (24h timeout + 6h retry), and after 30 days past lastSettledDay isDead() becomes true and the plant is final. The implementation follows the brief's letter (only 'incomplete' counts) but defeats the brief's stated intent for the no-data case; the two kinds of no-data should probably share the strike counter, or an unanswered clear should count as a strike.","line":360,"path":"src/PlantOrganism.sol","reproduction":"Bound plant, location = LISBON (10223579) after birth, alice parked 100e18 at LISBON. bob parks 2000e18 (of 3000e18 supply) at cell (156<<16|65500) so challenger = that cell (>5% supply, > parkedTotal[LISBON]). Loop: warp to end of day lastSettledDay+1, heartbeat() (succeeds, 0.5 IMD leaves the pot), no oracle delivery, warp +24h, clearPending() (NotTimedOut before 24h, then succeeds, retryAt = now+6h), warp to retryAt, repeat. Observed after 23 cycles: isDead() == true, location still LISBON, incompletes[lastSettledDay+1] == 0, 11.5 IMD of pot spent on fees, bob's majority never produced a move. Expected per the brief's READ-on-no-data intent: after three failed cycles for D the plant settles D empty with READ and moves to bob's cell. Scratch test test/scratch/Probe.t.sol::test_unansweredNeverEscapes reproduced this (passes, i.e. asserts the trapped end state).","severity":"low","snippet":"            if (block.timestamp < uint256(pending.askedAt) + ORACLE_TIMEOUT) revert NotTimedOut();\n            _clear();","title":"Unanswered oracle requests never count toward the 3-incomplete escape, so a location the oracle cannot answer traps the plant until it dies"},{"citation":"resolved","description":"The brief requires 'public reads of all state + floor + pot + the question string'. question() forwards location to WeatherQuestion.question, whose first statement is validate(cell), and validate reverts InvalidCell for cell == 0. Between bind and the first move or the FALLBACK_CELL assignment (up to three settled days, plus any time before anyone calls settle) location is 0, so the view reverts instead of returning a string. Any dashboard, indexer or keeper that reads question() to display or pre-check the next request fails during birth. Returning an empty string (or the fallback cell's question) when location == 0 would satisfy the read requirement.","line":229,"path":"src/PlantOrganism.sol","reproduction":"Deploy PlantOrganism with the brief's constants, bind a hook whose plant() has nonzero supply, do not settle. Call question() with no arguments. Actual: revert WeatherQuestion.InvalidCell(). Expected: a string. Also reverts after one or two birth settles with no mover (location still 0). Scratch test test/scratch/Probe.t.sol::test_questionViewRevertsDuringBirth reproduces with vm.expectRevert(WeatherQuestion.InvalidCell.selector).","severity":"low","snippet":"        return question(location, lastSettledDay + 1);","title":"question() public read reverts for the whole birth phase (location == 0)"},{"citation":"resolved","description":"By design 'all IMD sent in counts' and park/redeem revert while unbound. There is no other IMD exit while hook == address(0): claim() pays only credits/advances which cannot exist before bind, settle() only catches up days. If the deployer ($owner) never calls bind, or the second launch never produces a hook whose organism() == this and plant() has nonzero supply, every IMD transferred to the contract is stranded forever. Not a code defect against the brief (bind is the single intended privilege) but a material dependency on the deployer that the launch notes should state.","line":562,"path":"src/PlantOrganism.sol","reproduction":"Deploy; transfer 1000 IMD to the organism; never call bind. redeem(1) reverts Unbound, park reverts Unbound, claim() transfers nothing, settle() only advances lastSettledDay. Balance stays 1000 IMD with no function able to move it.","severity":"info","snippet":"        if (hook == address(0)) revert Unbound();","title":"Trust assumption: IMD donated before bind is irrecoverable unless the deployer binds"},{"citation":"resolved","description":"rotate() accepts any newIntake with code and any newAction, authenticated only by the current oracle signer. After such a rotation heartbeat() calls newIntake.priceOf, which can return any value, and then approves and pays that amount to newIntake; when price exceeds the pot the caller's own IMD is pulled as an advance (line 294). The brief asks for exactly this rotation and names the signer as trusted, so this is a documented power, not a bypass; it is recorded here because the signer's key compromise converts directly into loss of the pot and of keepers' approved balances, and no other party (holders, deployer) can veto or delay a rotation.","line":621,"path":"src/PlantOrganism.sol","reproduction":"Signer signs Rotate(this, 4663, newSigner, Evil, action, nonce). rotate(...) succeeds. Evil.priceOf returns 1e24; Evil.request pulls it. Keeper calls heartbeat() with a max IMD approval: spendablePot and up to 1e24 of the keeper's IMD are transferred to Evil in one call.","severity":"info","snippet":"        intake = IIntake(newIntake);","title":"Trust assumption: the oracle signer can rotate intake to an arbitrary contract and thereby spend the whole pot and any approving keeper's IMD through heartbeat"}],"hash":"2469af17fdb8113e8587b980f52449be05b17166f2aeedf48e9790f7551dd973","nodeId":"51d1b271-2e2b-4ac5-b6eb-7060c06c4e13","outcome":"completed","summary":"Review complete. Findings are in `.imd-findings.json` at the repository root, with all 15 verifier entry points covered.\n\n**Outcome.** The control flow of `PlantOrganism` holds up well under the Execution Trace, Periphery and First Principles checks. I found no critical, high or medium defects. Two low findings and two trust-assumption notes are recorded.\n\n**Findings**\n- **Low: unanswered requests never reach the 3-strike escape.** Only a delivered word with the complete bit clear increments `incompletes`. A request that gets no callback is just cleared on timeout. While the oracle returns nothing for the current location, holders cannot move the plant no matter their majority. Reproduced: a 2000-vs-100 majority stayed stuck through 23 heartbeat cycles, 11.5 IMD of fees left the pot, and the plant died with its location unchanged. This follows the brief's letter but defeats its stated intent that READ lets holders leave a spot without data.\n- **Low: `question()` reverts during birth.** The no-argument view forwards `location`, and the library rejects cell 0, so the required public read fails until the first move or fallback.\n- **Info: IMD donated before bind is stranded** unless the deployer binds. No exit exists while unbound.\n- **Info: the oracle signer can rotate the intake to any contract** and spend the pot and keepers' approved IMD via `heartbeat`. Designed power, recorded as a trust assumption.\n\n**Verified as holding**\n- Heartbeat, callback, clear and settle interleavings, including rotation mid-flight, death mid-flight, and the received-incomplete and 3rd-incomplete paths.\n- Bind and catch-up day arithmetic for same-day, late and never-bind cases, and the death clock around them.\n- Reward accumulator epoch rolling and activation checkpoints, fee-advance seniority, floor monotonicity, redeem rounding.\n- `WeatherQuestion` body text, coordinate and date formatting. `OracleAttestation.sol` is byte-identical to the reference modulo whitespace.\n- Cold callback gas through the grace-signer path measured at 99k, well under the 200k stipend.\n\n**Static-analysis leads** were all checked and none promoted: the heartbeat reentrancy lines are guarded and the token is fixed, the date arithmetic is the standard civil-from-days algorithm, and the uninitialized locals are intentional.\n\n**Not reached.** No area in scope was left unreached. Scratch tests were removed after use. The tree is unchanged apart from `.imd-findings.json`.","treeHash":null,"usage":{"cachedInputTokens":1938774,"inputTokens":450,"model":"claude-fable-5-1","outputTokens":68170,"runtime":"claude","turns":37,"wallClockMs":936919}},{"artifacts":[],"attempt":1,"bundleHash":null,"device":"fd744d20ac30e31f","findings":[],"hash":"2a33cebd24681147555b8e8132561fca7101c38940cfb551cdcd8906861acbb2","nodeId":"bed004b9-308e-4f3c-addb-39747a5599a1","outcome":"failed","summary":"the task produced no changes; the agent's last message was: `launch.json` already matches the approved constructor and passes schema and compiled ABI checks. No changes were needed.\n\n`forge build` succeeded with existing lint warnings. `forge test` passed all 65 tests. The working tree is unchanged.","treeHash":null,"usage":{"cachedInputTokens":282240,"inputTokens":42787,"model":"gpt-6-astra","outputTokens":2984,"runtime":"codex","turns":3,"wallClockMs":195614}},{"artifacts":[],"attempt":2,"bundleHash":null,"device":"56b13350bba65fd7","findings":[{"citation":"resolved","description":"_read (lines 452-462) decides a move from parkedTotal[candidate] and parkedTotal[current] at the instant settle() runs, settle() is callable by anyone once the day's word is stored, and park/unpark (lines 236, 252) have no minimum stay. Stake that exists only inside the settle transaction therefore counts in full: park(candidate, X) -> settle() -> unpark(candidate, X) relocates the plant with PLANT that is borrowed (Uniswap v4 flash accounting from the second launch's pool) or simply held for one block. The economic damage goes beyond the location: the following day's gardener pool is paid by _distribute to cellRewards[newCell].active, i.e. only to stake that was already parked at the destination before the move, while honest holders who park there afterwards are queued until the settle after next. An attacker who pre-parks 1 wei at an empty cell (free to become challenger whenever challenger == 0, which is the state after every move) therefore receives 100% of the next day's pool (1/3 of all sips, about 30% of the pot on a fully sunny day) and can repeat the manoeuvre every day with a fresh cell, so the gardener share is systematically paid to a one-block visitor instead of the holders gardening the plant. The inverse (park into the current location inside settle, then unpark) vetoes a legitimate move at no cost. Reported by audit_economics (medium) and audit_permissions (high); merged. The brief says 'all read now' and 'no minimum stay', so the minimal fix changes that wording and is a scope decision for the author: evaluate both sides of the comparison and the 5% threshold on stake active as of the previous settle (cellRewards[c].active after _rollCell, which the reward accumulator already maintains), or require the candidate's parkedTotal at settle to be no lower than when it was snapshotted at heartbeat.","line":455,"path":"src/PlantOrganism.sol","proof":"// SPDX-License-Identifier: MIT\npragma solidity 0.8.26;\n\nimport {Test} from \"forge-std/Test.sol\";\nimport {PlantOrganism} from \"src/PlantOrganism.sol\";\nimport {OracleAttestation} from \"src/OracleAttestation.sol\";\nimport {IERC20} from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport {IIntake} from \"src/interfaces/IIntake.sol\";\n\ncontract FMToken is IERC20 {\n    uint256 public totalSupply;\n    mapping(address => uint256) public balanceOf;\n    mapping(address => mapping(address => uint256)) public allowance;\n\n    function mint(address to, uint256 amount) external {\n        balanceOf[to] += amount;\n        totalSupply += amount;\n    }\n\n    function approve(address spender, uint256 amount) external returns (bool) {\n        allowance[msg.sender][spender] = amount;\n        return true;\n    }\n\n    function transfer(address to, uint256 amount) external returns (bool) {\n        balanceOf[msg.sender] -= amount;\n        balanceOf[to] += amount;\n        return true;\n    }\n\n    function transferFrom(address from, address to, uint256 amount) external returns (bool) {\n        allowance[from][msg.sender] -= amount;\n        balanceOf[from] -= amount;\n        balanceOf[to] += amount;\n        return true;\n    }\n}\n\ncontract FMHook {\n    address public immutable organism;\n    address public immutable plant;\n\n    constructor(address o, address p) {\n        organism = o;\n        plant = p;\n    }\n}\n\ncontract FMIntake is IIntake {\n    uint256 public sequence;\n    bytes32 public lastId;\n    Callback public callback;\n\n    function priceOf(bytes32, address) external pure returns (uint256) {\n        return 0.5 ether;\n    }\n\n    function request(bytes32, bytes calldata, Callback calldata cb, address asset, uint256 amount)\n        external\n        payable\n        returns (bytes32 id)\n    {\n        require(IERC20(asset).transferFrom(msg.sender, address(this), amount));\n        callback = cb;\n        id = keccak256(abi.encode(address(this), ++sequence));\n        lastId = id;\n    }\n\n    function deliver(bytes32 id, OracleAttestation.Attestation calldata a, bytes calldata sig)\n        external\n        returns (bool ok)\n    {\n        (ok,) = callback.target.call{gas: 200000}(abi.encodeWithSelector(callback.selector, id, a, sig));\n    }\n}\n\n/// @dev Stands in for a flash borrower: the PLANT that casts the vote is parked and unparked inside one call.\ncontract Relocator {\n    PlantOrganism immutable organism;\n\n    constructor(PlantOrganism o, IERC20 p) {\n        organism = o;\n        p.approve(address(o), type(uint256).max);\n    }\n\n    function dustPark(uint32 cell) external {\n        organism.park(cell, 1);\n    }\n\n    function relocate(uint32 cell, uint256 amount) external {\n        organism.park(cell, amount);\n        organism.settle();\n        organism.unpark(cell, amount);\n    }\n\n    function claim() external {\n        organism.claim();\n    }\n}\n\ncontract FlashMoveTest is Test {\n    uint256 constant KEY = 0x59c6995e998f97a5a0044966f0945389dc9e86dae88c7a8412f4603b6b78690d;\n    uint32 constant LISBON = 10223579;\n    uint32 constant OTHER = (156 << 16) | 65500;\n    uint32 constant START = 20000;\n    address alice = address(0xa11ce);\n    address keeper = address(0xbee);\n    FMToken imd;\n    FMToken plant;\n    FMIntake intake;\n    PlantOrganism organism;\n    Relocator eve;\n    uint256 serial;\n\n    function setUp() public {\n        vm.chainId(4663);\n        vm.warp(uint256(START) * 1 days + 123);\n        imd = new FMToken();\n        plant = new FMToken();\n        intake = new FMIntake();\n        organism = new PlantOrganism(\n            address(imd), address(intake), bytes32(\"oracle.request@oracle-1\"), vm.addr(KEY), LISBON, address(this)\n        );\n        plant.mint(alice, 900 ether);\n        eve = new Relocator(organism, plant);\n        plant.mint(address(eve), 100 ether + 1); // 1 wei of lasting stake plus capital used only inside relocate()\n        organism.bind(address(new FMHook(address(organism), address(plant))));\n        imd.mint(address(organism), 1000 ether);\n        vm.prank(alice);\n        plant.approve(address(organism), type(uint256).max);\n        // Birth: alice is the only gardener, at Lisbon.\n        vm.prank(alice);\n        organism.park(LISBON, 100 ether);\n        _nextEnded();\n        organism.settle();\n        assertEq(organism.location(), LISBON);\n        // Eve's 1 wei at OTHER becomes the standing challenger and is reward-active one settle later.\n        eve.dustPark(OTHER);\n        assertEq(organism.challenger(), OTHER);\n        _askAndDeliver(0);\n        organism.settle(); // no move: 1 wei is below alice's 100 ether\n        assertEq(organism.location(), LISBON);\n    }\n\n    function _nextEnded() internal {\n        uint256 time = uint256(organism.lastSettledDay() + 2) * 1 days;\n        if (vm.getBlockTimestamp() < time) vm.warp(time);\n    }\n\n    function _askAndDeliver(uint24 sun) internal {\n        _nextEnded();\n        vm.prank(keeper);\n        bytes32 id = organism.heartbeat();\n        OracleAttestation.Attestation memory a;\n        a.requestId = keccak256(abi.encode(\"uuid\", ++serial));\n        a.chainId = 4663;\n        a.answerType = 2;\n        a.answer = abi.encode(bytes32(uint256(sun) | uint256(1) << 48 | uint256(organism.lastSettledDay() + 1) << 96));\n        a.panelSize = 15;\n        a.quorum = 10;\n        a.agreed = 12;\n        a.issuedAt = uint64(vm.getBlockTimestamp());\n        a.expiresAt = uint64(vm.getBlockTimestamp() + 1 days);\n        (uint8 v, bytes32 r, bytes32 s) = vm.sign(KEY, organism.attestationDigest(a));\n        assertTrue(intake.deliver(id, a, abi.encodePacked(r, s, v)));\n    }\n\n    /// Runs park -> settle -> unpark in one transaction. A fix may make that sequence revert or make the\n    /// settle inside it not move the plant; either way the plant must still be at Lisbon afterwards.\n    function _attemptAtomicRelocation() internal {\n        uint32 before = organism.lastSettledDay();\n        try eve.relocate(OTHER, 100 ether) {} catch {}\n        if (organism.lastSettledDay() == before) organism.settle();\n    }\n\n    /// The vote that moves the plant is cast and withdrawn inside one transaction.\n    function test_atomicParkSettleUnparkMustNotMoveThePlant() public {\n        _askAndDeliver(0); // the day's word is stored; settle() is open to anyone\n        uint256 eveBefore = plant.balanceOf(address(eve));\n        _attemptAtomicRelocation();\n        assertEq(plant.balanceOf(address(eve)), eveBefore, \"capital fully withdrawn\");\n        assertEq(organism.parkedTotal(OTHER), 1, \"only the dust remains at OTHER\");\n        assertEq(organism.location(), LISBON, \"plant moved by capital that left in the same transaction\");\n    }\n\n    /// After the zero-commitment move, the next sunny day's whole gardener pool goes to 1 wei of PLANT and\n    /// the only real gardener (alice, 100 ether) earns nothing.\n    function test_nextPoolMustGoToTheRealGardenerNotTheDustStake() public {\n        _askAndDeliver(0);\n        _attemptAtomicRelocation();\n        _askAndDeliver(0xffffff); // a sunny day\n        organism.settle();\n        eve.claim();\n        vm.prank(alice);\n        organism.claim();\n        emit log_named_uint(\"IMD paid to the 1 wei stake\", imd.balanceOf(address(eve)));\n        emit log_named_uint(\"IMD paid to alice's 100 ether stake\", imd.balanceOf(alice));\n        assertEq(imd.balanceOf(address(eve)), 0, \"dust stake captured the pool\");\n        assertGt(imd.balanceOf(alice), 0, \"the committed gardener earned nothing\");\n    }\n}","reproduction":"Bound plant at Lisbon (10223579), PLANT supply 1000e18, 1000 IMD in the pot; alice has 100e18 active at Lisbon; eve has 1 wei parked at OTHER = (156<<16)|65500 since two settles ago (so it is reward-active) and holds 100e18 liquid PLANT. The day's word is delivered, settle() not yet called. Eve sends one transaction: park(OTHER, 100e18); settle(); unpark(OTHER, 100e18). Expected: location stays 10223579 because no committed stake backs OTHER. Actual: location == 10289116 (OTHER), parkedTotal[OTHER] is back to 1 wei, eve's PLANT balance unchanged. Next day with 24 sunny hours: the pool is 300184772098882854196 wei, all credited to eve's 1 wei; alice's 100e18 stake receives 0. Both tests in the attached proof fail on this code with exactly those values: forge test --match-path test/scratch/FlashMove.t.sol -vv.","severity":"high","snippet":"            candidate != 0 && candidate != current && parkedTotal[candidate] > parkedTotal[current]","title":"Relocation vote reads live parkedTotal at a permissionless settle: PLANT held for one transaction moves the plant, and a pre-parked 1 wei position then captures the whole next-day gardener pool"},{"citation":"resolved","description":"_challenge (line 277) overwrites challenger whenever parkedTotal[cell] > parkedTotal[challenger] using live balances; heartbeat (line 312) copies that value into pending.challenger; _settle (line 401) READs only p.challenger and never the live challenger that anyone can repair afterwards with challenge(). heartbeat is permissionless and pays its caller the bounty, so the caller may shape the snapshot: park into an unused cell with more PLANT than the current challenger, call heartbeat, unpark in the same transaction. pending.challenger then names a cell with parkedTotal == 0, _read fails for it at settle, and the holders' candidate, which satisfies every rule in the brief, is not evaluated that day. Repeating this each day blocks relocation indefinitely for gas plus a one-block PLANT borrow; on the day after a move it costs 1 wei because challenger is 0. Distinct root cause from finding 1 (the snapshot at heartbeat rather than the read at settle) and it survives a fix of finding 1 that only changes _read, because _challenge itself uses live parkedTotal. Minimal fix: in _settle, if the snapshot candidate fails _read, also try the live challenger (still O(1)); and/or have _challenge compare active stake after _rollCell so transient stake cannot become the challenger.","line":312,"path":"src/PlantOrganism.sol","proof":"// SPDX-License-Identifier: MIT\npragma solidity 0.8.26;\n\nimport {Test} from \"forge-std/Test.sol\";\ninterface IERC20Like { function transferFrom(address from, address to, uint256 amount) external returns (bool); }\nimport {PlantOrganism} from \"src/PlantOrganism.sol\";\nimport {OracleAttestation} from \"src/OracleAttestation.sol\";\nimport {IIntake} from \"src/interfaces/IIntake.sol\";\n\ncontract ProofToken2 {\n    uint256 public totalSupply;\n    mapping(address => uint256) public balanceOf;\n    mapping(address => mapping(address => uint256)) public allowance;\n\n    function mint(address to, uint256 amount) external {\n        balanceOf[to] += amount;\n        totalSupply += amount;\n    }\n\n    function approve(address spender, uint256 amount) external returns (bool) {\n        allowance[msg.sender][spender] = amount;\n        return true;\n    }\n\n    function transfer(address to, uint256 amount) external returns (bool) {\n        balanceOf[msg.sender] -= amount;\n        balanceOf[to] += amount;\n        return true;\n    }\n\n    function transferFrom(address from, address to, uint256 amount) external returns (bool) {\n        allowance[from][msg.sender] -= amount;\n        balanceOf[from] -= amount;\n        balanceOf[to] += amount;\n        return true;\n    }\n}\n\ncontract ProofHook2 {\n    address public immutable organism;\n    address public immutable plant;\n\n    constructor(address organism_, address plant_) {\n        organism = organism_;\n        plant = plant_;\n    }\n}\n\ncontract ProofIntake2 is IIntake {\n    uint256 public sequence;\n    bytes32 public lastId;\n    Callback public callback;\n\n    function priceOf(bytes32, address) external pure returns (uint256) {\n        return 0.5 ether;\n    }\n\n    function request(bytes32, bytes calldata, Callback calldata cb, address asset, uint256 amount)\n        external\n        payable\n        returns (bytes32 id)\n    {\n        require(IERC20Like(asset).transferFrom(msg.sender, address(this), amount));\n        callback = cb;\n        id = keccak256(abi.encode(address(this), ++sequence));\n        lastId = id;\n    }\n\n    function deliver(bytes32 id, OracleAttestation.Attestation calldata a, bytes calldata sig) external returns (bool ok) {\n        (ok,) = callback.target.call{gas: 200000}(abi.encodeWithSelector(callback.selector, id, a, sig));\n    }\n}\n\n/// @notice The heartbeat caller fixes the day's only move candidate. Parking into an empty cell,\n/// asking, and unparking in one transaction points the candidate at a cell with no stake, so the\n/// holders' real candidate (above 5% of supply and above the location) is never read that day.\ncontract SnapshotEmptyCellTest is Test {\n    uint256 internal constant KEY = 0x59c6995e998f97a5a0044966f0945389dc9e86dae88c7a8412f4603b6b78690d;\n    uint32 internal constant LISBON = 10223579;\n    uint32 internal constant PORTO = (164 << 16) | uint32(uint16(int16(-35)));\n    uint32 internal constant DECOY = (100 << 16) | uint32(uint16(int16(-20)));\n    uint32 internal constant START = 20000;\n\n    address internal alice = address(0xa11ce);\n    address internal bob = address(0xb0b);\n    address internal mallory = address(0xbad);\n    address internal lender = address(0x9001);\n\n    ProofToken2 internal imd;\n    ProofToken2 internal plant;\n    ProofIntake2 internal intake;\n    PlantOrganism internal organism;\n    uint256 internal serial;\n\n    function setUp() public {\n        vm.chainId(4663);\n        vm.warp(uint256(START) * 1 days + 123);\n        imd = new ProofToken2();\n        plant = new ProofToken2();\n        intake = new ProofIntake2();\n        plant.mint(alice, 300 ether);\n        plant.mint(bob, 300 ether);\n        plant.mint(lender, 400 ether);\n        organism = new PlantOrganism(\n            address(imd), address(intake), bytes32(\"oracle.request@oracle-1\"), vm.addr(KEY), LISBON, address(this)\n        );\n        organism.bind(address(new ProofHook2(address(organism), address(plant))));\n        imd.mint(address(organism), 1000 ether);\n        vm.prank(alice);\n        plant.approve(address(organism), type(uint256).max);\n        vm.prank(bob);\n        plant.approve(address(organism), type(uint256).max);\n        vm.prank(mallory);\n        plant.approve(address(organism), type(uint256).max);\n    }\n\n    function _nextEnded() internal {\n        uint256 time = uint256(organism.lastSettledDay() + 2) * 1 days;\n        if (vm.getBlockTimestamp() < time) vm.warp(time);\n    }\n\n    function _deliver() internal {\n        OracleAttestation.Attestation memory a;\n        a.requestId = keccak256(abi.encode(\"uuid\", ++serial));\n        a.chainId = 4663;\n        a.questionHash = keccak256(\"doc\");\n        a.answerType = 2;\n        a.answer = abi.encode(bytes32(uint256(1) | (uint256(1) << 48) | (uint256(organism.lastSettledDay() + 1) << 96)));\n        a.panelJobId = keccak256(\"panel\");\n        a.panelSize = 15;\n        a.quorum = 10;\n        a.agreed = 12;\n        a.issuedAt = uint64(vm.getBlockTimestamp());\n        a.expiresAt = uint64(vm.getBlockTimestamp() + 1 days);\n        (uint8 v, bytes32 r, bytes32 s) = vm.sign(KEY, organism.attestationDigest(a));\n        assertTrue(intake.deliver(intake.lastId(), a, abi.encodePacked(r, s, v)), \"callback failed\");\n    }\n\n    function test_emptyCellSnapshotBlocksTheHoldersMove() public {\n        vm.prank(alice);\n        organism.park(LISBON, 100 ether);\n        _nextEnded();\n        organism.settle(); // birth: Lisbon\n        assertEq(organism.location(), LISBON);\n\n        // Bob wants Porto: 200 parked there, above Lisbon's 100 and above 5% of the 1000 supply.\n        vm.prank(bob);\n        organism.park(PORTO, 200 ether);\n        assertEq(organism.challenger(), PORTO);\n\n        // Day ends. In one transaction mallory borrows 201 PLANT, parks into a decoy cell (now the\n        // challenger), asks the oracle (candidate snapshotted = DECOY), unparks and repays.\n        _nextEnded();\n        vm.prank(lender);\n        plant.transfer(mallory, 201 ether);\n        vm.startPrank(mallory);\n        organism.park(DECOY, 201 ether);\n        organism.heartbeat();\n        organism.unpark(DECOY, 201 ether);\n        plant.transfer(lender, 201 ether);\n        vm.stopPrank();\n        (, , uint32 snapshot,,,,,,) = organism.pending();\n        assertEq(snapshot, DECOY);\n        assertEq(organism.parkedTotal(DECOY), 0);\n\n        // Anyone repairs the live challenger, but the day's READ only looks at the snapshot.\n        organism.challenge(PORTO);\n        assertEq(organism.challenger(), PORTO);\n\n        _deliver();\n        organism.settle();\n        // Expected: Porto (200 > 100, >= 50). Actual: Lisbon; the day's vote was spent on an empty cell.\n        assertEq(organism.location(), PORTO, \"holders' candidate was never read\");\n    }\n}","reproduction":"PLANT supply 1000e18 (alice 300, bob 300, lender 400), organism bound, 1000 IMD pot. alice parks 100e18 at Lisbon; settle -> birth move to Lisbon. bob parks 200e18 at PORTO = (164<<16)|uint16(-35) = 10813405 -> challenger = PORTO (200 > 100 and 200e18 >= ceil(1000e18/20)). Day ends. In one transaction mallory receives 201e18 PLANT, calls park(DECOY=(100<<16)|uint16(-20), 201e18) -> challenger = DECOY, heartbeat() -> pending.challenger = DECOY, unpark(DECOY, 201e18), returns the PLANT; parkedTotal[DECOY] == 0. Anyone calls challenge(PORTO) -> live challenger = PORTO, snapshot unchanged. Oracle delivers a complete word; settle(). Expected: location == 10813405 (PORTO). Actual: location == 10223579 (Lisbon); the day's vote is lost. The attached proof fails on this code with 'holders' candidate was never read: 10223579 != 10813405': forge test --match-path test/scratch/SnapshotEmptyCell.t.sol.","severity":"medium","snippet":"            Pending(id, day, challenger, msg.sender, uint64(block.timestamp), address(intake), true, false, bytes32(0));","title":"heartbeat() freezes the live challenger as the day's only move candidate: park -> heartbeat -> unpark in one transaction points it at an empty cell and the holders' real candidate is never read that d"},{"citation":"resolved","description":"The brief requires public reads of all state including the question string. question() forwards location to WeatherQuestion.question, whose first statement validate(cell) reverts InvalidCell for cell == 0 (src/WeatherQuestion.sol line 14). Between bind and the first move or the FALLBACK_CELL assignment (up to three settled days plus any time before anyone settles) and for the whole pre-bind period, the view reverts instead of returning a string, so any dashboard, indexer or keeper that reads it during birth fails. Reported by audit_math and audit_flow; merged. Nothing is lost on chain. Fix: return \"\" (or the FALLBACK_CELL question) when location == 0.","line":229,"path":"src/PlantOrganism.sol","reproduction":"Deploy PlantOrganism with the brief's constants, bind a hook whose plant() has nonzero supply, do not settle. Call question(). Expected: a string. Actual: revert WeatherQuestion.InvalidCell(). test/scratch/Judge.t.sol::test_questionRevertsAtBirth passes with vm.expectRevert(WeatherQuestion.InvalidCell.selector).","severity":"low","snippet":"        return question(location, lastSettledDay + 1);","title":"question() public read reverts for the whole birth phase (location == 0)"},{"citation":"resolved","description":"Only a delivered word with bit 48 == 0 increments incompletes[day] (line 405). A request that gets no callback (status 1 refused, status 2 no quorum, writer never delivering) is handled by clearPending's timeout branch, which only runs _clear() and never touches incompletes. READ for a located plant runs only inside _settle, and _settle requires pending.received. So while the oracle returns nothing for the current location's question there is no on-chain path by which holders, however large their majority elsewhere, can move the plant; each cycle costs the pot one oracle fee plus 30 hours (24h timeout + 6h retry), and 30 days after lastSettledDay the plant is dead. The brief's letter counts only 'incomplete', but its stated purpose for the three-strike rule is that holders can move the plant away from a spot without data, which this no-data case defeats. Reported by audit_flow. Fix: count a timed-out clear as a strike for its day (or share the counter), so the third one settles the day empty with READ.","line":361,"path":"src/PlantOrganism.sol","reproduction":"Bound plant at Lisbon after birth, alice 100e18 at Lisbon; bob parks 300e18 of 1000e18 supply at (156<<16)|65500 -> challenger set and above both thresholds. Loop: warp to the end of day lastSettledDay+1 (and past retryAt), heartbeat() (0.5 IMD leaves the pot), no delivery, warp +24h, clearPending(). After 22 cycles isDead() == true, location still 10223579, incompletes[lastSettledDay+1] == 0, and settle() reverts DeadPlant. Expected per the brief's READ-on-no-data intent: after three failed cycles the day settles empty with READ and the plant moves to bob's cell. test/scratch/Judge.t.sol::test_unansweredNeverCountsAsStrike reproduces the trapped end state.","severity":"low","snippet":"            _clear();","title":"Unanswered oracle requests never count toward the three-incomplete escape, so a location the oracle does not answer traps the plant until it dies"},{"citation":"resolved","description":"The Rotate struct binds organism, chain id, new values and nonce but no expiry, and rotate (line 605) is relayable by anyone. Once the oracle signer has produced a signature it cannot withdraw it except by consuming the nonce with a different rotation. A relayer or anyone holding the payload can submit it at any later time, pointing the organism at an intake or signer the oracle no longer wants and starting the 30-day death clock until the new signer rotates again. Impact is bounded because the signer authorised the values and the new signer can rotate back. Reported by audit_permissions. Fix: add a deadline field to ROTATE_TYPEHASH/rotationDigest and require block.timestamp <= deadline in rotate.","line":29,"path":"src/PlantOrganism.sol","reproduction":"At t0 the current signer signs rotationDigest(vm.addr(555), <intake>, action, 0). Nobody submits it. At t0 + 400 days anyone calls rotate(vm.addr(555), <intake>, action, 0, sig). Expected: a 400-day-old authorisation is refused. Actual: accepted; oracleSigner() == vm.addr(555). test/scratch/Judge.t.sol::test_rotationSignatureNeverExpires passes on this code.","severity":"low","snippet":"        \"Rotate(address organism,uint256 chainId,address newSigner,address newIntake,bytes32 newAction,uint256 nonce)\"","title":"Rotation signatures carry no deadline: a signed rotate() message withheld by its relayer stays valid until the nonce is consumed"},{"citation":"resolved","description":"The 5% threshold is taken from PLANT.totalSupply(), which never shrinks because redeemed PLANT stays in the organism forever (burned), while floor() uses totalSupply() - burned. Once burned > 95% of supply, ceilDiv(totalSupply, 20) exceeds all PLANT in circulation and no candidate cell can satisfy _read even if every remaining holder parks there. The plant is pinned to its last cell permanently; if that cell dries out, no sip ever happens again, yet each settle still credits 1% of the pot to the heartbeat caller, so the only remaining outflow from the pot is the keeper bounty and the holders' only exit is redeem at 90% of a frozen floor. The brief says literally '5% of PLANT.totalSupply()', so this may be intended; it is reported because the end state contradicts 'holders decide where it lives'. Reported by audit_economics. Alternative: measure 5% against totalSupply() - burned, the base the floor uses.","line":456,"path":"src/PlantOrganism.sol","reproduction":"Supply 1000e18 (alice 600, bob 300, carol 100), plant born at Lisbon with one settled weather day. alice unparks and redeems 600e18, bob redeems 300e18, carol redeems 60e18 -> burned == 960e18, 40e18 circulating, parkedTotal[LISBON] == 0. carol parks all 40e18 at (156<<16)|65500 -> challenger set. Next weather day settles. Expected: a move (every circulating token voted for the candidate against an empty Lisbon). Actual: location stays 10223579 because 40e18 < ceilDiv(1000e18, 20) = 50e18, permanently. test/scratch/Judge.t.sol::test_stuckAfterNinetyFivePercentRedeemed passes on this code.","severity":"low","snippet":"                && parkedTotal[candidate] >= Math.ceilDiv(PLANT.totalSupply(), 20)","title":"Relocation threshold counts burned PLANT: once more than 95% of supply is redeemed the plant can never move again"},{"citation":"resolved","description":"heartbeat pays whatever priceOf returns with no ceiling, approves it (line 301) and pulls any shortfall above spendablePot from the caller as a fee advance (line 294). Two privileged actors set that number: the Intake owner by repricing, and the oracle signer through rotate(), which accepts any address with code as the new intake (line 610). A rotated-in intake whose priceOf returns spendablePot() + callerApproval receives the entire pot and the caller's approved IMD in one heartbeat; backing, gardener credits and bounties are not reachable this way. The brief names the signer as the rotation authority and asks for no admin, so this is a documented power rather than a bypass. Reported by audit_economics, audit_permissions and audit_flow; merged. Mitigations within the design: a constant maximum acceptable price in heartbeat (skip the day above it), and keepers approving only the advance they intend to make rather than type(uint256).max.","line":290,"path":"src/PlantOrganism.sol","reproduction":"Signer S signs Rotate(organism, 4663, S2, M, action, 0) where M.priceOf returns 10000e18 and M.request pulls exactly that. Anyone relays rotate(S2, M, action, 0, sig). A keeper with an unlimited IMD approval calls heartbeat(): price 10000e18 > spendablePot(), the keeper's IMD is pulled for the difference and the whole amount is transferred to M; feeAdvances[keeper] records a debt the pot may never repay. No unprivileged amplifier exists.","severity":"info","snippet":"        uint256 price = intake.priceOf(action, address(IMD));","title":"Trust assumption: no cap on the oracle price, so a repriced Intake or a signer-rotated intake can take the whole spendable pot plus the heartbeat caller's open IMD approval in one heartbeat"},{"citation":"resolved","description":"The brief mandates the guards object, but the pinned oracle-consumer skill says the body is strict PaidOracleInput ('an unknown key refuses the whole request', status 1, price spent, no callback) and lists v, question, chainId, window, answerType, evidence, panelSize, quorum, validForSeconds, definitions, allowAmbiguous, deliver and consumer, never guards; it also says the reference wins where it and the brief disagree on the protocol. If guards is not accepted, every heartbeat spends 0.5 IMD, is never answered, times out after 24h, waits 6h and repeats; lastSettledDay never advances and isDead() becomes true at bindDay + 30 (about 12 IMD spent). Not reproducible offline (the mock accepts any body); a pre-launch verification item. Reported by audit_economics.","line":79,"path":"src/WeatherQuestion.sol","reproduction":"Not reproducible offline. Verification: call Intake.request(action, requestBody(10223579, <ended day>), Callback(this, onOracleResult.selector), IMD, 0.5e18) once on Robinhood Chain and confirm status 0 and a callback; a status-1 refusal confirms the defect. On-chain consequence if refused: heartbeat -> no callback -> clearPending after 24h -> RetryLater for 6h -> repeat; isDead() at bindDay + 30.","severity":"info","snippet":"                '\",\"chainId\":4663,\"window\":{\"hours\":24},\"answerType\":\"bytes32\",\"evidence\":\"panel\",\"panelSize\":15,\"quorum\":10,\"validForSeconds\":86400,\"guards\":{\"sources\":[\"https://api.open-meteo.com\"],\"minSources\":1}}'","title":"Request body carries a 'guards' key the oracle-consumer reference does not list for PaidOracleInput; if the live door refuses it, no day ever settles and the plant dies at bindDay + 30"},{"citation":"resolved","description":"Settlement is strictly sequential (_settle targets lastSettledDay + 1, src/PlantOrganism.sol line 383) and the question pins past_days=92, so for any day more than 92 days old a faithful panel answers complete = 0. The only way forward is three incompletes (3 x 0.5 IMD, two 6h waits, about 15h) per biological day, each paying a 1% bounty on an empty day. Reachable while alive because one settle every 29 days resets the death clock while the backlog grows. Costs are bounded (the backlog shrinks about 9h per calendar day) and the brief fixes both the question text and the sequential rule; an operational cliff the README should state (keep the backlog under 92 days). Reported by audit_economics.","line":64,"path":"src/WeatherQuestion.sol","reproduction":"State: bound, location != 0, lastSettledDay = today - 100, alive (last settle 29 days ago). heartbeat() asks about a date 99 days old; the pinned URL has no rows for it; the panel returns complete = 0; settle() counts incompletes[D] = 1, clears, retryAt = +6h; twice more; the third settle advances one day with no hours and credits 1% of the pot to the caller. Repeat for every backlog day.","severity":"info","snippet":"            \"&past_days=92&hourly=is_day,cloud_cover,precipitation&timezone=UTC, the 24 rows of \",","title":"Days older than the 92-day source window can only be settled through three paid incompletes each"},{"citation":"resolved","description":"By design all IMD sent in counts and park/redeem revert while unbound. There is no other IMD exit while hook == address(0): claim() pays only credits/advances which cannot exist before bind, settle() only catches up days. If the deployer never calls bind, or the second launch never produces a hook whose organism() == this with a nonzero-supply plant(), every IMD transferred to the contract is stranded forever. Not a defect against the brief (bind is the single intended privilege) but a dependency on the deployer the launch notes should state. Reported by audit_flow.","line":562,"path":"src/PlantOrganism.sol","reproduction":"Deploy; transfer 1000 IMD to the organism; never call bind. redeem(1) reverts Unbound, park reverts Unbound, claim() transfers nothing, settle() only advances lastSettledDay. Balance stays 1000 IMD with no function able to move it.","severity":"info","snippet":"        if (hook == address(0)) revert Unbound();","title":"Trust assumption: IMD donated before bind is irrecoverable unless the deployer binds"}],"hash":"4162f508f0756229f82bfd6f1fc81043435217ddce8232871259308ef8869158","nodeId":"218594ac-f382-4dbc-83d5-a2316d05422b","outcome":"completed","summary":"Review complete. The findings file is written with 10 findings and an 18-row coverage record, and every snippet was checked against the tree.\n\n**Kept and reproduced**\n\n- **High: flash relocation and pool capture.** The economics and permissions reports describe one root cause, so I merged them at high. The specialist proof fails on this code exactly as stated: park, settle and unpark in one transaction move the plant, and a pre-parked 1 wei position then collects the whole next sunny day's gardener pool (about 300 IMD in the fixture) while the real gardener gets nothing. The brief's \"all read now\" wording means the fix is a scope decision, which the finding says.\n- **Medium: heartbeat snapshot points the day's only candidate at an emptied cell.** Distinct mechanism from the first finding and survives a fix limited to the read at settle. Proof fails here as stated.\n- **Low, merged where duplicated:** the question view reverts during birth, timed-out requests never count as a strike so an unanswered location traps the plant until death, rotation signatures never expire, and burned PLANT keeps counting in the 5% move threshold. I confirmed each with a scratch test under test/scratch/Judge.t.sol.\n- **Info:** uncapped oracle price under rotation (three specialists, merged as a trust assumption), the unlisted `guards` body key as a pre-launch verification item, the 92-day source window, and pre-bind donations depending on the deployer binding.\n\n**Dropped**\n\nThe two \"tautological conservation invariant\" findings overstate their claim. The identity in PlantInvariant.t.sol is indeed trivially true, but real solvency is asserted in PlantStateMachine.t.sol at line 271 with a nonzero fee, keeper advances, incomplete answers, timeouts and death. That leaves only a cosmetic duplication, not a coverage gap.\n\n**My own pass** found nothing beyond the specialists. I traced the reward accumulator's lazy activation and reserve rounding, the signed pot and fee-advance seniority, the death merge, the callback's temporary signer swap, and the question and date formatting, and all hold. The full suite passes 73 tests.","treeHash":null,"usage":{"cachedInputTokens":1443585,"inputTokens":418,"model":"claude-fable-5-1","outputTokens":59928,"runtime":"claude","turns":27,"wallClockMs":931637}},{"artifacts":[],"attempt":1,"bundleHash":null,"device":"8d428b115b0ebd64","findings":[{"citation":"resolved","description":"The brief requires public reads of all state including 'the question string'. While the plant is bound but unborn (location == 0, up to three birth settles, plus the whole pre-bind period), question() forwards location = 0 into WeatherQuestion.question, whose validate(cell) reverts with InvalidCell because cell == 0 is reserved. Every caller (front end, keeper, indexer) that reads the question gets a revert instead of a string during the birth phase. Boundary agent: sentinel value 0 for 'nowhere' is handled by heartbeat (NoRequest) but not by the view. Nothing is lost on chain; this is a broken read guarantee only.","line":228,"path":"src/PlantOrganism.sol","reproduction":"Deploy PlantOrganism with any valid constructor, bind(hook). Do not settle. Call organism.question(). Expected: a string (or an empty string / explicit 'no location' error) per 'public reads of ... the question string'. Actual: revert WeatherQuestion.InvalidCell(). Scratch test test_questionViewRevertsAtBirth in test/scratch/Probe.t.sol passes with vm.expectRevert(WeatherQuestion.InvalidCell.selector). Minimal fix: return \"\" when location == 0 (or return question(FALLBACK_CELL, ...) only if the brief wants a preview).","severity":"low","snippet":"    function question() external view returns (string memory) {\n        return question(location, lastSettledDay + 1);\n    }","title":"question() public read reverts for the whole birth phase (location == 0)"},{"citation":"resolved","description":"pot() is defined in src/PlantOrganism.sol:203-208 as int256(held) - int256(backing + owed()). Therefore pot() + backing + owed() == held holds for any values of backing, gardenerPoints, creditTotal and feeAdvanceTotal, and the assertion (also _conservation() in test/PlantOrganism.t.sol:136-142 and the invariant_conservationAndPermanentBurnCustody check at line 156) cannot fail on any accounting bug. The README advertises this identity as 'the required conservation identity', so the suite's main money check is vacuous. The meaningful property the code relies on in _pay (credit + advance <= held) and in redeem (backing -= payout never underflows the token balance) is backing + gardenerReserve() + creditTotal <= held, equivalently pot() >= -feeAdvanceTotal, which is asserted nowhere. The invariant handler also fixes the oracle price at 0 (no fee advance path), never reaches death, incompletes, timeouts or rotation, so the fee-advance repayment arithmetic in _pay and the pot-merge at death run only in a handful of unit tests.","line":41,"path":"test/PlantInvariant.t.sol","reproduction":"In a test, corrupt backing to an insolvent value: vm.store(address(organism), bytes32(uint256(9)), bytes32(uint256(5000 ether))) with IMD balance 1000e18. Then assertEq(organism.pot() + int256(organism.backing()) + int256(organism.owed()), int256(imd.balanceOf(address(organism)))) still PASSES (pot() is -4000e18), while the real solvency check assertGe(organism.pot() + int256(organism.feeAdvanceTotal()), 0) FAILS. See test_conservationIdentityHoldsEvenWithCorruptedBacking in test/scratch/Probe.t.sol. Expected: the suite's conservation check should fail on corrupted accounting. Actual: it passes. Fix: assert backing + gardenerReserve() + creditTotal <= IMD.balanceOf(organism) (and pot() >= 0 whenever feeAdvanceTotal == 0) in _conservation() and in the invariant; give the handler a nonzero oracle price and an occasional incomplete/timeout/death step.","severity":"low","snippet":"        assertEq(\n            organism.pot() + int256(organism.backing()) + int256(organism.owed()),\n            int256(imd.balanceOf(address(organism)))\n        );","title":"Conservation 'invariant' pot()+backing+owed == held is a tautology; solvency (backing+reserves <= held) is never tested"}],"hash":"529a49deaf3edb2ec5bde1299e7b71181f59744033af5134c553c76bd3f02c1b","nodeId":"04ea0664-4cd5-499e-a903-372732dda5b1","outcome":"completed","summary":"The review is complete. `.imd-findings.json` holds two low-severity findings and a 21-row coverage record; no files outside `test/scratch/` and the findings file were changed.\n\n**What I found**\n\n- **Low: `question()` reverts during the birth phase.** While `location == 0`, the public view forwards cell 0 into the validator and reverts with `InvalidCell`, so the brief's \"public read of the question string\" is unavailable until the plant is born. Reproduced in a scratch test.\n- **Low: the suite's conservation invariant is a tautology.** `pot()` is defined as held minus backing minus owed, so `pot + backing + owed == held` cannot fail on any accounting bug. I corrupted `backing` to an insolvent value via `vm.store` and the suite's check still passed. The real solvency property, backing plus reserves at most equal to the token balance, is asserted nowhere, and the invariant handler never exercises fee advances, death, incompletes or rotation.\n\n**What holds, with evidence**\n\n- Reward-per-token with epoch checkpoints matches an independent model exactly across 400 random 40-step sequences of park, unpark, move, challenge and claim, including per-holder credits and active/queued totals.\n- Sip and pool split, ceil-reserve minus floor-credit, and all dust flows round toward backing with no underflow path.\n- Floor is non-decreasing under redeem rounding and across the death transition, including with a negative pot from an unfunded fee advance.\n- Civil-date formatting matches an independent year/month walk over 2000 fuzzed days in the full supported range. The slither divide-before-multiply lines are the Hinnant algorithm itself.\n- Death, timeout, retry and 5% threshold boundaries are exact to the second and the wei.\n\n**Not verifiable here, outside my area**\n\nThe request body carries a `guards` key that the oracle-consumer reference does not list among accepted `PaidOracleInput` keys. The reference says an unknown key refuses the whole request with the price spent. If that key is not accepted by the live Intake, every heartbeat is refused and the plant dies after 30 days. The key is mandated by the brief itself, so I left it out of the findings and flag it here for the judge.","treeHash":null,"usage":{"cachedInputTokens":2593229,"inputTokens":578,"model":"claude-fable-5-1","outputTokens":67848,"runtime":"claude","turns":39,"wallClockMs":1033523}},{"artifacts":[],"attempt":1,"bundleHash":null,"device":"fc87c05048aca47d","findings":[{"citation":"resolved","description":"The revision moved weather-day votes to stake retained since the previous settle, but votingStake() still returns live parkedTotal while location == 0, and birth settles need no oracle word, so anyone can call settle() the instant a birth day ends. A visitor who borrows PLANT for one transaction (park -> settle -> unpark all but 1 wei) wins the birth READ against every holder who parked, and because _distribute pays the next day's pool to cellRewards[location].active, the 1 wei left behind is the only active stake at the new cell and receives 100% of the first weather day's pool (about 30% of the pot on a sunny day) while the committed holders receive nothing. This is the same mechanism the author fixed for weather days (round-1 finding 07ebfac46f95), left open for the up-to-three birth settles; it is bounded to the birth window and to whatever IMD is in the pot by then, hence medium. The holders can challenge the old cell back the next day, but the first day's pool is already paid to the visitor. Fix within the design: use the same epoch-based measure for birth (stake queued before the previous settle, so the first birth settle is a maturation day and the second decides), or require a park to survive one settle before it can be unparked.","line":455,"path":"src/PlantOrganism.sol","proof":"// SPDX-License-Identifier: MIT\npragma solidity 0.8.26;\n\nimport {Test} from \"forge-std/Test.sol\";\nimport {IERC20} from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport {PlantOrganism} from \"src/PlantOrganism.sol\";\nimport {OracleAttestation} from \"src/OracleAttestation.sol\";\nimport {IIntake} from \"src/interfaces/IIntake.sol\";\n\ncontract Token is IERC20 {\n    uint256 public totalSupply;\n    mapping(address => uint256) public balanceOf;\n    mapping(address => mapping(address => uint256)) public allowance;\n\n    function mint(address to, uint256 amount) external {\n        balanceOf[to] += amount;\n        totalSupply += amount;\n    }\n\n    function approve(address spender, uint256 amount) external returns (bool) {\n        allowance[msg.sender][spender] = amount;\n        return true;\n    }\n\n    function transfer(address to, uint256 amount) external returns (bool) {\n        balanceOf[msg.sender] -= amount;\n        balanceOf[to] += amount;\n        return true;\n    }\n\n    function transferFrom(address from, address to, uint256 amount) external returns (bool) {\n        allowance[from][msg.sender] -= amount;\n        balanceOf[from] -= amount;\n        balanceOf[to] += amount;\n        return true;\n    }\n}\n\ncontract Hook {\n    address public immutable organism;\n    address public immutable plant;\n\n    constructor(address o, address p) {\n        organism = o;\n        plant = p;\n    }\n}\n\ncontract Intake is IIntake {\n    uint256 public sequence;\n    bytes32 public lastId;\n    Callback public callback;\n\n    function priceOf(bytes32, address) external pure returns (uint256) {\n        return 0.5 ether;\n    }\n\n    function request(bytes32, bytes calldata, Callback calldata cb, address asset, uint256 amount)\n        external\n        payable\n        returns (bytes32 id)\n    {\n        require(IERC20(asset).transferFrom(msg.sender, address(this), amount));\n        callback = cb;\n        id = keccak256(abi.encode(address(this), ++sequence));\n        lastId = id;\n    }\n\n    function deliver(bytes32 id, OracleAttestation.Attestation calldata a, bytes calldata sig) external returns (bool ok) {\n        (ok,) = callback.target.call{gas: 200000}(abi.encodeWithSelector(callback.selector, id, a, sig));\n    }\n}\n\n/// @dev Stands in for a flash borrower: borrows, parks, settles the birth day, unparks all but 1 wei, repays.\ncontract Visitor {\n    PlantOrganism internal immutable organism;\n    Token internal immutable plant;\n    address internal immutable lender;\n\n    constructor(PlantOrganism o, Token p, address l) {\n        organism = o;\n        plant = p;\n        lender = l;\n        plant.approve(address(o), type(uint256).max);\n    }\n\n    function flashBirth(uint32 cell, uint256 amount) external {\n        plant.transferFrom(lender, address(this), amount);\n        organism.park(cell, amount);\n        organism.settle();\n        organism.unpark(cell, amount - 1);\n        plant.transfer(lender, amount - 1);\n    }\n\n    function claim() external {\n        organism.claim();\n    }\n}\n\ncontract BirthFlashMoveTest is Test {\n    uint256 internal constant KEY = 0x59c6995e998f97a5a0044966f0945389dc9e86dae88c7a8412f4603b6b78690d;\n    uint32 internal constant LISBON = 10223579;\n    uint32 internal constant OTHER = (156 << 16) | 65500;\n    uint32 internal constant START = 20000;\n    bytes32 internal constant ACTION = bytes32(\"oracle.request@oracle-1\");\n    address internal alice = address(0xa11ce);\n    address internal bob = address(0xb0b);\n    address internal lender = address(0x1e4d);\n    Token internal imd;\n    Token internal plant;\n    Intake internal intake;\n    PlantOrganism internal organism;\n    Visitor internal eve;\n    uint256 internal serial;\n\n    function setUp() public {\n        vm.chainId(4663);\n        vm.warp(uint256(START) * 1 days + 123);\n        imd = new Token();\n        plant = new Token();\n        intake = new Intake();\n        plant.mint(alice, 100 ether);\n        plant.mint(bob, 100 ether);\n        plant.mint(lender, 800 ether);\n        organism = new PlantOrganism(address(imd), address(intake), ACTION, vm.addr(KEY), LISBON, address(this));\n        organism.bind(address(new Hook(address(organism), address(plant))));\n        imd.mint(address(organism), 1000 ether);\n        eve = new Visitor(organism, plant, lender);\n        vm.prank(lender);\n        plant.approve(address(eve), type(uint256).max);\n        vm.prank(alice);\n        plant.approve(address(organism), type(uint256).max);\n        vm.prank(bob);\n        plant.approve(address(organism), type(uint256).max);\n        vm.prank(alice);\n        organism.park(LISBON, 100 ether);\n        vm.prank(bob);\n        organism.park(LISBON, 100 ether);\n        assertEq(organism.challenger(), LISBON);\n    }\n\n    function _flash() internal {\n        vm.warp(uint256(START + 2) * 1 days);\n        uint256 lenderBefore = plant.balanceOf(lender);\n        eve.flashBirth(OTHER, 201 ether);\n        assertEq(plant.balanceOf(lender), lenderBefore - 1, \"lender repaid all but one wei\");\n        assertEq(organism.parkedTotal(OTHER), 1);\n    }\n\n    function _deliverSunny() internal {\n        OracleAttestation.Attestation memory a;\n        a.requestId = keccak256(abi.encode(\"oracle uuid\", ++serial));\n        a.chainId = 4663;\n        a.questionHash = keccak256(\"resolved\");\n        a.answerType = 2;\n        a.answer = abi.encode(\n            bytes32(uint256(0xffffff) | (uint256(1) << 48) | uint256(organism.lastSettledDay() + 1) << 96)\n        );\n        a.panelJobId = keccak256(\"panel\");\n        a.panelSize = 15;\n        a.quorum = 10;\n        a.agreed = 12;\n        a.issuedAt = uint64(vm.getBlockTimestamp());\n        a.expiresAt = uint64(vm.getBlockTimestamp() + 1 days);\n        (uint8 v, bytes32 r, bytes32 s) = vm.sign(KEY, organism.attestationDigest(a));\n        assertTrue(intake.deliver(intake.lastId(), a, abi.encodePacked(r, s, v)), \"callback failed\");\n    }\n\n    /// Expected: stake present only inside the settle transaction does not decide where the plant is born.\n    function test_oneTransactionVisitorDecidesBirth() public {\n        _flash();\n        assertNotEq(organism.location(), OTHER, \"a one-transaction visitor decided the birth location\");\n    }\n\n    /// Expected: the first weather day's gardener pool goes to the holders who parked, not to the visitor's 1 wei.\n    function test_oneTransactionVisitorTakesFirstDayPool() public {\n        _flash();\n        if (organism.location() != OTHER) return;\n        vm.warp(uint256(START + 3) * 1 days);\n        organism.heartbeat();\n        _deliverSunny();\n        organism.settle();\n        uint256 before = imd.balanceOf(address(eve));\n        eve.claim();\n        vm.prank(alice);\n        organism.claim();\n        emit log_named_uint(\"visitor's 1 wei received\", imd.balanceOf(address(eve)) - before);\n        emit log_named_uint(\"alice's 100e18 received\", imd.balanceOf(alice));\n        assertEq(imd.balanceOf(address(eve)), before, \"visitor's 1 wei captured the first-day gardener pool\");\n    }\n}","reproduction":"Bound plant, pot 1000 IMD, PLANT supply 1000e18. alice and bob each park 100e18 at Lisbon (10223579) before any settle; challenger == Lisbon. Day ends. eve (a contract) in one transaction: transferFrom(lender, 201e18); park(OTHER = 10289116, 201e18); settle(); unpark(OTHER, 201e18 - 1); repay. Expected: location != OTHER (no committed stake backs it). Actual: location == 10289116, parkedTotal[OTHER] == 1, lender repaid all but 1 wei. Next day, 24 sunny hours delivered and settled: eve.claim() pays 306591146714888642126 wei of IMD to the 1 wei; alice.claim() pays 0 to her 100e18. Both tests in the proof fail on this code: forge test --match-path test/scratch/BirthFlashMove.t.sol -vv.","severity":"medium","snippet":"        if (location == 0) return parkedTotal[cell];","title":"Birth settle still reads live stake: PLANT held for one transaction decides where the plant is born and its leftover 1 wei takes the whole first-day gardener pool"},{"citation":"resolved","description":"Only a delivered word with bit 48 == 0 increments incompletes[D] (line 406). A request that gets no callback (refused body, no quorum, writer outage) is cleared by the timeout branch, which never touches incompletes, and READ only runs inside _settle, which for a located plant requires pending.received. So while the oracle returns nothing for the current cell's question, no majority at any other cell can move the plant: each cycle burns 0.5 IMD and 30 hours, and after 30 days the plant is dead with its last location. The brief's letter counts only 'incomplete', and the README documents this, but the brief's stated purpose of READ on the third strike ('so holders can move the plant away from a spot without data') is defeated for the no-answer case. Counting a timed-out clear as a strike (or sharing the counter) keeps settle O(1).","line":361,"path":"src/PlantOrganism.sol","reproduction":"Suite fixture: birth to Lisbon with alice 100e18; bob parks 300e18 at OTHER (10289116), one weather day settles so it matures; challenge(OTHER) -> challenger == OTHER, votingStake 300e18 > 100e18 and >= 50e18. Loop: warp to day end, heartbeat() (0.5 IMD leaves), no delivery, warp +24h, clearPending(), warp +6h. After 22 cycles isDead() == true, location == 10223579, incompletes[lastSettledDay+1] == 0, 11.5 IMD of pot spent. Expected per the brief's intent: a settle with READ after three failures moving the plant to OTHER. Scratch test test/scratch/Unanswered.t.sol::test_unansweredNeverEscapes asserts the trapped end state and passes on this code.","severity":"low","snippet":"            if (block.timestamp < uint256(pending.askedAt) + ORACLE_TIMEOUT) revert NotTimedOut();\n            _clear();","title":"Unanswered oracle requests never count toward the three-strike escape, so a location the oracle will not answer traps the plant until it dies"},{"citation":"resolved","description":"The Rotate message binds organism, chain id, new values and nonce but no expiry, and rotate() is relayable by anyone. Once the oracle signer has produced a signature it cannot withdraw it except by consuming the nonce with another rotation. A relayer can hold the payload indefinitely and submit it when the named intake or signer are no longer wanted, pointing the organism at a deprecated intake and starting the 30-day death clock until the new signer rotates again. The brief lists the fields without a deadline, so this is signature hygiene (matches the eth-security checklist's replay/expiry item), not a loss; adding a deadline field to the struct and requiring block.timestamp <= deadline is a minimal change.","line":28,"path":"src/PlantOrganism.sol","reproduction":"At t0 the current signer signs rotationDigest(vm.addr(555), <any contract with code>, ACTION, 0). Nobody submits it. At t0 + 400 days anyone calls rotate(vm.addr(555), that intake, ACTION, 0, sig). Expected: refused as stale. Actual: accepted; intake() and oracleSigner() change (test/scratch/Round3.t.sol::test_rotateSignatureHasNoExpiry passes on this code).","severity":"low","snippet":"    bytes32 public constant ROTATE_TYPEHASH = keccak256(\n        \"Rotate(address organism,uint256 chainId,address newSigner,address newIntake,bytes32 newAction,uint256 nonce)\"\n    );","title":"Rotation signatures carry no deadline: a withheld rotate() message stays valid until its nonce is consumed"},{"citation":"resolved","description":"Confirmed fixed. votingStake() now counts only stake whose deposit epoch is older than the current epoch (matured across a successful settle) capped by the live balance, and unpark removes active stake immediately. A deposit made in the heartbeat transaction is queued at the current epoch and counts for nothing at settle; a matured stake that is withdrawn and redeposited is queued again and loses its power for the epoch. Two consecutive settles of a located plant always have a heartbeat and an oracle callback between them, so the minimum holding time is one oracle cycle, not one transaction.","line":459,"path":"src/PlantOrganism.sol","reproduction":"Re-ran the round-2 sequence on the revised code (test/scratch/Round3.t.sol::test_settled1_heartbeatFlashNoLongerMoves): eve has 1 wei matured at OTHER, loans 100e18 around heartbeat() and again around settle(). Result: location stays 10223579, votingStake(OTHER) == 1, and after a fully sunny day eve.claim() adds nothing beyond her 1% heartbeat bounty. The author's test/PlantVotingCommitment.t.sol covers the same sequence plus a loan spanning the previous settle, redeposits and a fuzzed partial withdrawal.","severity":"info","snippet":"        uint256 active = c.active + (c.epoch < epoch ? c.queued : 0);\n        return Math.min(active, parkedTotal[cell]);","title":"Settled (fixed): round-2 finding e2d7397d, heartbeat-instant snapshot let park -> heartbeat -> unpark then park -> settle -> unpark move the plant"},{"citation":"resolved","description":"Confirmed fixed. _challenge now compares the same committed votingStake READ uses, so a deposit made in the same transaction has zero power and cannot replace an eligible challenger at heartbeat or at settle; the captured-candidate fallback to the live challenger remains. The author's dispute of the attached proof is correct: its lines that require a fresh deposit to become challenger immediately demand the live-balance behaviour the finding asked to remove, so the proof now fails for that reason at setup (0 != 10813405), not because the candidate is hidden.","line":278,"path":"src/PlantOrganism.sol","reproduction":"Adapted sequence on the revised code (test/scratch/Round3.t.sol::test_settled2_reArmedDecoyNoLongerHidesCandidate): PORTO (10813405) has 200e18 matured, Lisbon 100e18; mallory loans 201e18 around heartbeat() into DECOY and 201e18 around settle() into DECOY2. pending.challenger == PORTO after the heartbeat, votingStake(DECOY) == 0, and settle moves the plant to 10813405. The old proof copied to test/scratch/ReDecoy.t.sol fails only at its assertion that a fresh park sets challenger.","severity":"info","snippet":"        if (cell != location && votingStake(cell) > votingStake(challenger)) {","title":"Settled (fixed): round-2 finding 8a915778, transient decoy challenger at heartbeat and settle hid the holders' candidate"},{"citation":"resolved","description":"Not a flash issue any more and arguably within 'holders decide': whoever holds more matured stake can shape the vote. Recorded so the author knows the fallback has a bound: with two decoy cells each matured above the honest candidate, the griefer makes one the captured candidate at heartbeat, withdraws it, lets the honest side repair, then in the settle transaction challenges with the second decoy, withdraws it and settles, so neither READ sees power. It costs more capital than simply winning the vote, loses maturity for the next day, and the honest candidate wins the following day, so no revision is asked.","line":417,"path":"src/PlantOrganism.sol","reproduction":"test/scratch/Round3.t.sol::test_residual_maturedDoubleDecoyGriefsOneDay: Lisbon 100e18, PORTO 200e18, eve 201e18 at DECOY and 202e18 at DECOY2, all matured by one settle. challenge(DECOY) -> heartbeat; eve unparks DECOY; challenge(PORTO) repairs; word delivered; eve in one tx: challenge(DECOY2), unpark(DECOY2, 202e18), settle(). location stays 10223579 that day; the next weather day moves to 10813405.","severity":"info","snippet":"        if (!_read(candidate) && challenger != candidate) _read(challenger);","title":"Residual: a griefer holding more than twice the candidate's matured stake across one oracle cycle can still deny that day's move with two matured decoys"},{"citation":"resolved","description":"The 5% threshold uses PLANT.totalSupply(), which never shrinks because redeemed PLANT stays in this contract, while floor() uses totalSupply() - burned. Once burned exceeds 95% no candidate can satisfy READ even if every remaining holder parks there, and the only pot outflow left at a rainless cell is the 1% keeper bounty per settle. The brief says '5% of PLANT.totalSupply()' literally and the README documents the end state, so this is a design note (the economics specialist's low), not a defect against the brief; measuring against totalSupply() - burned would keep 'holders decide' alive for the remaining holders.","line":469,"path":"src/PlantOrganism.sol","reproduction":"Suite fixture (supply 1000e18), plant at Lisbon. alice redeems 600e18, bob 300e18, carol 60e18 -> burned 960e18, 40e18 outstanding. carol parks 40e18 at OTHER, matures, challenge(OTHER). Expected (all remaining holders vote OTHER against an empty Lisbon): a move. Actual: 40e18 < ceilDiv(1000e18, 20) = 50e18, no move, permanently; each settle still credits 1% of the pot to the heartbeat caller.","severity":"info","snippet":"                && candidateStake >= Math.ceilDiv(PLANT.totalSupply(), 20)","title":"Relocation threshold counts burned PLANT: after more than 95% of supply is redeemed the plant can never move again"},{"citation":"resolved","description":"Merged from the economics, permissions and flow specialists. rotate() accepts any newIntake with code, authenticated only by the current signer, and heartbeat() approves and pays the returned price with no ceiling, pulling any shortfall from the caller as an advance. A compromised signer key (or a repriced genuine Intake, whose price is owner-settable) can therefore drain the spendable pot in one heartbeat; backing, gardener credits and bounties are excluded by spendablePot(). The brief names the signer as the sole rotation authority, so this is a documented privileged power, not a bypass; a constant maximum acceptable price in heartbeat would bound it to one day's cap, and keepers should approve only the advance they intend to make.","line":291,"path":"src/PlantOrganism.sol","reproduction":"Signer signs Rotate(organism, 4663, S2, Evil, action, 0) where Evil.priceOf returns 1000e18 and Evil.request pulls it. Anyone relays rotate(); on the next ended day anyone calls heartbeat(): price <= spendablePot(), forceApprove(Evil, 1000e18), request pulls 1000e18, the exact-transfer check passes, pending is recorded. Pot 0, Evil holds 1000 IMD. If price exceeds the pot, the caller's IMD covers the rest as a feeAdvance the pot may never repay.","severity":"info","snippet":"        uint256 price = intake.priceOf(action, address(IMD));","title":"Trust assumption: the oracle signer can rotate intake to any contract and heartbeat pays whatever priceOf returns, up to the whole spendable pot plus the caller's open approval"},{"citation":"resolved","description":"The brief mandates the guards object, but the pinned reference says the body is strict ('an unknown key refuses the whole request', status 1, price spent, no callback) and its key list never mentions guards; it also says the reference wins where the brief disagrees on the protocol. If the live door refuses guards, every heartbeat spends 0.5 IMD and is never answered, each request times out after 24h and waits 6h, no day ever settles and the plant is dead 30 days after bind. This cannot be reproduced against the mock and is an item for the launch operator: send one request with this exact body against the live Intake on 4663 before relying on it, or confirm the schema version that accepts guards.","line":79,"path":"src/WeatherQuestion.sol","reproduction":"Not reproducible offline. On chain: Intake.request(action, requestBody(10223579, <an ended day>), Callback(this, onOracleResult.selector), IMD, 0.5e18); status 0 and a callback confirm acceptance, status 1 confirms the defect, whose on-chain consequence is heartbeat -> no callback -> clearPending after 24h -> RetryLater 6h -> repeat until isDead() at bindDay + 30.","severity":"info","snippet":"                '\",\"chainId\":4663,\"window\":{\"hours\":24},\"answerType\":\"bytes32\",\"evidence\":\"panel\",\"panelSize\":15,\"quorum\":10,\"validForSeconds\":86400,\"guards\":{\"sources\":[\"https://api.open-meteo.com\"],\"minSources\":1}}'","title":"Pre-launch verification: the request body carries a 'guards' key the oracle-consumer reference does not list for PaidOracleInput"},{"citation":"resolved","description":"Settlement is strictly sequential and the pinned URL has no rows older than 92 days, so once the backlog exceeds 92 days each further day costs three requests (1.5 IMD), two 6h waits and a 1% bounty on an empty day. Reachable while alive because one settle every 29 days resets the death clock. The brief fixes both the question text and the sequential rule, so this is an operational note for the README (keep the backlog under 92 days), not a code defect.","line":64,"path":"src/WeatherQuestion.sol","reproduction":"State: bound, located, lastSettledDay = today - 100, last settle 29 days ago. heartbeat() asks about a date 99 days old; a faithful panel returns complete = 0; settle() counts incompletes[D] = 1 and clears with retryAt = +6h; twice more; the third settle advances one empty day and pays 1% of the pot. Repeat per backlog day.","severity":"info","snippet":"            \"&past_days=92&hourly=is_day,cloud_cover,precipitation&timezone=UTC, the 24 rows of \",","title":"Operational: days older than the 92-day source window can only settle through three paid incompletes each"},{"citation":"resolved","description":"By design all IMD sent in counts and park/redeem revert while unbound; claim() can pay only credits and advances, which cannot exist before bind, and settle() only catches up days. If the deployer never calls bind, or the second launch never yields a hook whose organism() == this with a non-zero-supply PLANT, every IMD transferred before then has no exit. Not a defect against the brief (bind is the single intended privilege); the README already states it.","line":190,"path":"src/PlantOrganism.sol","reproduction":"Deploy; transfer 1000 IMD to the organism; never bind. redeem(1) and park(cell, 1) revert Unbound, claim() transfers nothing, settle() only advances lastSettledDay; the balance stays with no function able to move it.","severity":"info","snippet":"        if (hook == address(0)) revert Unbound();","title":"Trust assumption: IMD sent before bind is stranded unless the deployer binds"}],"hash":"58667c89b7f6cba9773389af9a5c9144eaa2a920dabb6f713bc3b92651a35560","nodeId":"218594ac-f382-4dbc-83d5-a2316d05422b","outcome":"completed","summary":"Review complete. The findings file is written at `.imd-findings.json` with 11 entries and 18 coverage rows. Both findings from my last round are settled as fixed. One new medium finding carries a proof.\n\n**Settled findings**\n\n- **Round-2 high (heartbeat-instant snapshot): fixed.** Voting power is now stake matured across a successful settle, capped by the live balance, and withdrawal removes it immediately. I re-ran my sequence with a matured 1 wei and loans around both heartbeat and settle: the plant stays at Lisbon and the visitor earns nothing beyond the heartbeat bounty.\n- **Round-2 medium (re-armed decoy challenger): fixed.** The challenger comparison uses the same committed measure, so a same-transaction deposit has zero power at nomination. The author's dispute of my attached proof is correct: it fails at setup because it demands the live-balance behaviour the finding asked to remove. My adapted sequence with a matured Porto candidate moves there despite decoys at both instants.\n\n**New finding, medium, with proof**\n\nThe birth phase still reads live stake. While the location is zero, birth settles need no oracle word, so anyone can park borrowed PLANT, settle, and unpark in one transaction to decide where the plant is born. The 1 wei left behind is the only active stake at the new cell, and the first weather day's pool goes entirely to it. The proof at `test/scratch/BirthFlashMove.t.sol` fails on this code with these values:\n\n| | IMD received on the first sunny day |\n|---|---|\n| visitor's 1 wei | 306.59 |\n| alice's 100e18 | 0 |\n\nThis is the mechanism fixed for weather days, bounded to the birth window and whatever the pot holds by then.\n\n**Kept from the specialists**\n\n- Low: a never-answered request clears without counting a strike. I reproduced the trap: 22 cycles, 11.5 IMD spent, plant dead at Lisbon with a matured majority never read.\n- Low: rotation signatures have no deadline. A 400-day-old signature is accepted.\n- Info: residual two-decoy griefing needing over twice the candidate's matured stake, the burned-supply 5% threshold, the signer rotation trust assumption merged from three specialists, the unverified `guards` body key, the 92-day backlog cliff, and pre-bind donations.\n\n**Dropped**\n\n- The tautological conservation check is now accompanied by a real solvency assertion in the new state-machine suite with non-zero price, advances, incompletes, timeouts and death.\n- The question view no longer reverts at birth.\n- The economics and permissions flash-move and decoy reports are the fixed findings above.\n\nStatic-analysis leads on heartbeat and bind reentrancy, the strict equality in payment, and the civil-date arithmetic all hold. I checked the date formatting against nine reference dates including leap and century boundaries. The working tree is clean apart from the findings file and my scratch tests.","treeHash":null,"usage":{"cachedInputTokens":1983397,"inputTokens":642,"model":"claude-fable-5-1","outputTokens":37090,"runtime":"claude","turns":21,"wallClockMs":618463}},{"artifacts":[],"attempt":2,"bundleHash":"9beb79abb6025d53bb089a65426bf1fbced7117119d1b1799c04456ab26a8ad6","device":"15b1d692225b9db2","findings":[],"hash":"5d50f4dd99bd69dd89c5b7958dca1062c3d8376ca988fba9dd33e85933f94063","nodeId":"bed004b9-308e-4f3c-addb-39747a5599a1","outcome":"completed","summary":"Updated only [launch.json](/root/.identitymd/work/464306a5-5aae-4ad5-95be-b83776f5fc81/bed004b9-308e-4f3c-addb-39747a5599a1/launch.json), clarifying constructor types, initial state, and `$owner` binding authority. Approved constructor values remain unchanged.\n\nSchema and compiled ABI checks passed. `forge build` succeeded with existing lint warnings; `forge test` passed all 65 tests.","treeHash":"e6b90cf920223f1a5dd72104935d50ede02202fc","usage":{"cachedInputTokens":267136,"inputTokens":42409,"model":"gpt-6-astra","outputTokens":4409,"runtime":"codex","turns":4,"wallClockMs":182570}},{"artifacts":[],"attempt":1,"bundleHash":"735cb70c10beb8524876479a7a3d9efc2eab6d38c218773271c671125a17cf04","device":"0da932e4a0b9bd4a","findings":[],"hash":"5f0df6cff4f58ef2baf0761e952b41633c13fc9f948a4a5bb48737e38e62d1ff","nodeId":"2f38f19e-efa5-428b-8ad9-b7f740ed0e0b","outcome":"completed","summary":"Updated stale voting tests, added withdrawal and retry regressions, and strengthened independent voting invariants. Only `test/**` changed.\n\nValidation passed: `forge build` and all 102 tests, including 32,768 invariant calls with zero reverts.","treeHash":"83c6e5419937cf57233bd04598f65045b3316ebb","usage":{"cachedInputTokens":2504704,"inputTokens":131788,"model":"gpt-6-astra","outputTokens":10487,"runtime":"codex","turns":7,"wallClockMs":594854}},{"artifacts":[],"attempt":1,"bundleHash":"b5f7fef6074166f8a5faaaa8476cdaa1770e5781b44283828ea8f47811d11269","device":"55ad97181c96c1ba","findings":[],"hash":"6dd04b4a9ac26c4882764e4fc3d97c221b392403bae3a4b87906060f74fc20a6","nodeId":"552cb5d7-55b5-4cb5-8800-9bcfd7eb66f7","outcome":"completed","summary":"Implemented the contract, deployment manifest, vendored dependencies, tests, and README.\n\nVerified:\n\n- `forge build` and `forge fmt --check` pass.\n- All 50 tests pass, including 8,192 invariant operations.\n- Callback: ~94k gas; cold settlement with movement: ~257k gas.\n\nREADME documents deployment, binding, keeper duties, fee-advance liquidity limits, and historical-cell claims.","treeHash":"a35192460fa5d2106bf01cfc3d8c43804d1be4af","usage":{"cachedInputTokens":3430016,"inputTokens":141185,"model":"gpt-6-astra","outputTokens":48601,"runtime":"codex","turns":9,"wallClockMs":1643544}},{"artifacts":[],"attempt":1,"bundleHash":"49bdf1c09e133be4d525598155f55fdbfa2639858488401a6c0e34247b303c76","device":"2c736d2f8963f8a5","findings":[],"hash":"7268017cf016067410c59810199f073f3539b4bcb37a09a9d9870a8f137f33b7","nodeId":"2f38f19e-efa5-428b-8ad9-b7f740ed0e0b","outcome":"completed","summary":"Added voting regressions, strengthened randomized snapshot invariants, enforced the 400k settlement gas limit, and updated the test README.\n\n`forge build --offline` passes. `forge test --offline`: **91 passed, 0 failed, 0 skipped**.\n\nOnly `test/**` changed. No confirmed new defects found.","treeHash":"1f52f4c6a49f9d18c4a0353c7f67b87fd555eea3","usage":{"cachedInputTokens":1483392,"inputTokens":66290,"model":"gpt-6-astra","outputTokens":5306,"runtime":"codex","turns":6,"wallClockMs":362923}},{"artifacts":[],"attempt":1,"bundleHash":"897d722d1431011dbe6d0a78dfaf7e844acc15822b5b225fa606a5837ede441b","device":"1f805d33d69c340e","findings":[],"hash":"9269b7ee10628cdb549ea133a3b63b78eeee9a8ac1bbc1a92860f09f702b7e37","nodeId":"bed004b9-308e-4f3c-addb-39747a5599a1","outcome":"completed","summary":"Updated [launch.json](/root/.identitymd/work/464306a5-5aae-4ad5-95be-b83776f5fc81/bed004b9-308e-4f3c-addb-39747a5599a1/launch.json) with clarified deployment notes, preserving the six correct constructor arguments.\n\nValidated the supplied schema and compiled ABI. `forge build` passed with lint warnings; `forge test` passed all 50 tests.\n\nOnly `launch.json` changed.","treeHash":"dc255e25e5de004934829874e541689355ebce22","usage":{"cachedInputTokens":403584,"inputTokens":37593,"model":"gpt-6-astra","outputTokens":3917,"runtime":"codex","turns":4,"wallClockMs":176213}},{"artifacts":[],"attempt":1,"bundleHash":null,"device":"2c968e88904ec22b","findings":[{"citation":"resolved","description":"Settles finding 07ebfac46f95 (round 1, high): the fix does not hold. The revision replaces the live read at settle with votingStake(cell) = min(parkedTotal at heartbeat, parkedTotal now) (lines 474-478), where the heartbeat value is recorded by _snapshotVote when heartbeat() runs (lines 325-327) or when the first park/unpark touches the cell during the request (lines 251, 267). But heartbeat() is permissionless, is paid a 1% bounty, and runs the instant the UTC day ends, and park/unpark still have no minimum stay. The snapshot is therefore not a commitment: the attacker makes the heartbeat instant one in which the borrowed stake is parked (park(X, A); heartbeat(); unpark(X, A) in one transaction, so voteSnapshots[X] = {round, A}), holds nothing while the oracle answers, and when the word is stored sends park(X, A); settle(); unpark(X, A). Inside that settle, votingStake(X) = min(A, A) = A: the full borrowed amount counts, _read moves the plant to X, and the stake leaves in the same transaction. The author's regression tests (test/PlantVotingRevision.t.sol) only exercise deposits made after an honest heartbeat, which the cap does block; none lets the attacker call heartbeat. The economic consequence is unchanged from round 1: _distribute pays the next day's pool to cellRewards[X].active, i.e. only to stake already active at X before the move, so the attacker's pre-parked 1 wei (free to become challenger whenever challenger == 0, the state after every move) receives 100% of the next day's gardener pool while the holders who were gardening receive nothing, repeatable every day with a fresh cell. The inverse (park into the current cell at heartbeat and again at settle) vetoes a legitimate move the same way. Fix that keeps settle O(1): define voting power as stake present continuously since the previous settle rather than since the heartbeat, i.e. take the lazy per-cell snapshot keyed by epoch at the first park/unpark after each settle (or simply use cellRewards[c].active after _rollCell, which the reward accumulator already maintains) and cap it by the live balance, for both sides of the comparison and the 5% threshold. This is the same scope decision as in round 1 (the brief's 'all read now' / 'no minimum stay'), which the author has already taken by introducing a cutoff; the cutoff merely needs to be one the attacker cannot choose.","line":325,"path":"src/PlantOrganism.sol","reproduction":"Bound plant at Lisbon (10223579), PLANT supply 1000e18, 1000 IMD donated; alice has 100e18 active at Lisbon; eve (a contract standing in for a flash borrower) has 1 wei parked at OTHER = (156<<16)|65500 = 10289116 since two settles ago and 100e18 liquid PLANT. UTC day ends. Transaction 1 by eve: park(OTHER, 100e18); heartbeat(); unpark(OTHER, 100e18). Afterwards parkedTotal[OTHER] == 1, eve's PLANT balance is back to 100e18, voteSnapshots[OTHER].amount == 100e18+1. The oracle delivers a complete word (any weather). Transaction 2 by eve: park(OTHER, 100e18); settle(); unpark(OTHER, 100e18). Expected: location stays 10223579 because no committed stake backs OTHER (the README states 'later deposits' are excluded from the vote). Actual: location == 10289116, parkedTotal[OTHER] == 1, eve's PLANT balance unchanged. Next day with 24 sunny hours: eve.claim() pays 310074822098882854196 wei of IMD to the 1 wei stake and alice's 100e18 receives 0. Both tests in the proof fail on this code with exactly those values: forge test --match-path test/scratch/HeartbeatFlashMove.t.sol -vv. The round-1 proof (Proof_07ebfac46f95) now passes because its attacker never calls heartbeat.","severity":"high","snippet":"        ++voteRound;\n        _snapshotVote(location);\n        _snapshotVote(challenger);","title":"Not fixed: the relocation snapshot is taken at the permissionless heartbeat instant, so park -> heartbeat -> unpark followed by park -> settle -> unpark of the same amount in the same cell still moves"},{"citation":"resolved","description":"Settles finding 7a54012d9763 (round 1, medium): the fix covers only the proof's exact sequence. The revision makes _settle try the live challenger when the captured candidate fails READ (line 431), which repairs the case where someone calls challenge(PORTO) after the decoy and nobody touches the challenger again. But _challenge (line 289) still compares live parkedTotal, and park/unpark have no minimum stay, so the attacker who parked, heartbeated and unparked at the day boundary (pending.challenger = DECOY, votingStake(DECOY) = min(201e18, 0) = 0) simply repeats the trick around settle: once the word is stored, park(DECOY2, 201e18) sets challenger = DECOY2 (201e18 > parkedTotal[PORTO] = 200e18, live), then settle() runs _read(DECOY) -> 0 stake, falls back to _read(DECOY2) -> votingStake = min(snapshot taken by park = 0, 201e18) = 0, no move; then unpark(DECOY2, 201e18). Nobody can repair in between because challenge(PORTO) fails while 201e18 sits in DECOY2 (200e18 > 201e18 is false), and if the attacker bundles park -> settle -> unpark in one transaction there is no 'between' at all. The holders' candidate PORTO, which satisfies every rule in the brief (200e18 > 100e18 at Lisbon, >= 50e18 = 5% of supply, present at heartbeat so votingStake(PORTO) == 200e18), is never evaluated, and the attacker held PLANT only inside two transactions. Distinct from finding 1 (which uses the snapshot to gain power; this one uses zero-power decoys to deny a read) and it survives a fix of finding 1 that only changes what _read measures. Fix: make _challenge compare committed stake (the same measure _read uses, e.g. cellRewards[cell].active after _rollCell, or votingStake) instead of live parkedTotal, so a transient park cannot become the challenger at heartbeat or at settle; then the existing fallback suffices.","line":431,"path":"src/PlantOrganism.sol","proof":"// SPDX-License-Identifier: MIT\npragma solidity 0.8.26;\n\nimport {Test} from \"forge-std/Test.sol\";\nimport {PlantOrganism} from \"src/PlantOrganism.sol\";\nimport {OracleAttestation} from \"src/OracleAttestation.sol\";\nimport {IIntake} from \"src/interfaces/IIntake.sol\";\n\ninterface IERC20Like {\n    function transferFrom(address from, address to, uint256 amount) external returns (bool);\n}\n\ncontract ProofToken3 {\n    uint256 public totalSupply;\n    mapping(address => uint256) public balanceOf;\n    mapping(address => mapping(address => uint256)) public allowance;\n\n    function mint(address to, uint256 amount) external {\n        balanceOf[to] += amount;\n        totalSupply += amount;\n    }\n\n    function approve(address spender, uint256 amount) external returns (bool) {\n        allowance[msg.sender][spender] = amount;\n        return true;\n    }\n\n    function transfer(address to, uint256 amount) external returns (bool) {\n        balanceOf[msg.sender] -= amount;\n        balanceOf[to] += amount;\n        return true;\n    }\n\n    function transferFrom(address from, address to, uint256 amount) external returns (bool) {\n        allowance[from][msg.sender] -= amount;\n        balanceOf[from] -= amount;\n        balanceOf[to] += amount;\n        return true;\n    }\n}\n\ncontract ProofHook3 {\n    address public immutable organism;\n    address public immutable plant;\n\n    constructor(address organism_, address plant_) {\n        organism = organism_;\n        plant = plant_;\n    }\n}\n\ncontract ProofIntake3 is IIntake {\n    uint256 public sequence;\n    bytes32 public lastId;\n    Callback public callback;\n\n    function priceOf(bytes32, address) external pure returns (uint256) {\n        return 0.5 ether;\n    }\n\n    function request(bytes32, bytes calldata, Callback calldata cb, address asset, uint256 amount)\n        external\n        payable\n        returns (bytes32 id)\n    {\n        require(IERC20Like(asset).transferFrom(msg.sender, address(this), amount));\n        callback = cb;\n        id = keccak256(abi.encode(address(this), ++sequence));\n        lastId = id;\n    }\n\n    function deliver(bytes32 id, OracleAttestation.Attestation calldata a, bytes calldata sig)\n        external\n        returns (bool ok)\n    {\n        (ok,) = callback.target.call{gas: 200000}(abi.encodeWithSelector(callback.selector, id, a, sig));\n    }\n}\n\n/// @notice The live-challenger fallback added in the revision is itself chosen by live parkedTotal\n/// (_challenge), so the heartbeat decoy can be re-armed in the settle transaction: park into the\n/// decoy again (live challenger == snapshot candidate again, so no fallback runs), settle, unpark.\n/// The holders' candidate is never read, with PLANT held only inside two transactions.\ncontract ReDecoyTest is Test {\n    uint256 internal constant KEY = 0x59c6995e998f97a5a0044966f0945389dc9e86dae88c7a8412f4603b6b78690d;\n    uint32 internal constant LISBON = 10223579;\n    uint32 internal constant PORTO = (164 << 16) | uint32(uint16(int16(-35)));\n    uint32 internal constant DECOY = (100 << 16) | uint32(uint16(int16(-20)));\n    uint32 internal constant START = 20000;\n\n    address internal alice = address(0xa11ce);\n    address internal bob = address(0xb0b);\n    address internal mallory = address(0xbad);\n    address internal lender = address(0x9001);\n\n    ProofToken3 internal imd;\n    ProofToken3 internal plant;\n    ProofIntake3 internal intake;\n    PlantOrganism internal organism;\n    uint256 internal serial;\n\n    function setUp() public {\n        vm.chainId(4663);\n        vm.warp(uint256(START) * 1 days + 123);\n        imd = new ProofToken3();\n        plant = new ProofToken3();\n        intake = new ProofIntake3();\n        plant.mint(alice, 300 ether);\n        plant.mint(bob, 300 ether);\n        plant.mint(lender, 400 ether);\n        organism = new PlantOrganism(\n            address(imd), address(intake), bytes32(\"oracle.request@oracle-1\"), vm.addr(KEY), LISBON, address(this)\n        );\n        organism.bind(address(new ProofHook3(address(organism), address(plant))));\n        imd.mint(address(organism), 1000 ether);\n        vm.prank(alice);\n        plant.approve(address(organism), type(uint256).max);\n        vm.prank(bob);\n        plant.approve(address(organism), type(uint256).max);\n        vm.prank(mallory);\n        plant.approve(address(organism), type(uint256).max);\n    }\n\n    function _nextEnded() internal {\n        uint256 time = uint256(organism.lastSettledDay() + 2) * 1 days;\n        if (vm.getBlockTimestamp() < time) vm.warp(time);\n    }\n\n    function _deliver() internal {\n        OracleAttestation.Attestation memory a;\n        a.requestId = keccak256(abi.encode(\"uuid\", ++serial));\n        a.chainId = 4663;\n        a.questionHash = keccak256(\"doc\");\n        a.answerType = 2;\n        a.answer =\n            abi.encode(bytes32(uint256(1) | (uint256(1) << 48) | (uint256(organism.lastSettledDay() + 1) << 96)));\n        a.panelJobId = keccak256(\"panel\");\n        a.panelSize = 15;\n        a.quorum = 10;\n        a.agreed = 12;\n        a.issuedAt = uint64(vm.getBlockTimestamp());\n        a.expiresAt = uint64(vm.getBlockTimestamp() + 1 days);\n        (uint8 v, bytes32 r, bytes32 s) = vm.sign(KEY, organism.attestationDigest(a));\n        assertTrue(intake.deliver(intake.lastId(), a, abi.encodePacked(r, s, v)), \"callback failed\");\n    }\n\n    function test_reArmedDecoyAtSettleStillBlocksTheHoldersMove() public {\n        vm.prank(alice);\n        organism.park(LISBON, 100 ether);\n        _nextEnded();\n        organism.settle(); // birth: Lisbon\n        assertEq(organism.location(), LISBON);\n\n        // Bob wants Porto: 200 parked there, above Lisbon's 100 and above 5% of the 1000 supply.\n        vm.prank(bob);\n        organism.park(PORTO, 200 ether);\n        assertEq(organism.challenger(), PORTO);\n\n        // Day ends. Transaction 1: borrow 201 PLANT, park into DECOY, heartbeat, unpark, repay.\n        _nextEnded();\n        vm.prank(lender);\n        plant.transfer(mallory, 201 ether);\n        vm.startPrank(mallory);\n        organism.park(DECOY, 201 ether);\n        organism.heartbeat();\n        organism.unpark(DECOY, 201 ether);\n        plant.transfer(lender, 201 ether);\n        vm.stopPrank();\n        (,, uint32 snapshot,,,,,,) = organism.pending();\n        assertEq(snapshot, DECOY);\n        assertEq(organism.parkedTotal(DECOY), 0);\n        assertEq(organism.votingStake(DECOY), 0);\n\n        // Anyone repairs the live challenger; the revision's fallback would now read Porto.\n        organism.challenge(PORTO);\n        assertEq(organism.challenger(), PORTO);\n\n        _deliver();\n\n        // Transaction 2, once the word is stored: borrow again, re-arm a fresh decoy as the live\n        // challenger (201 > 200, live balances), settle, unpark, repay. votingStake(decoy2) is 0\n        // because its snapshot (taken by park) is 0, so the fallback READ fails; Porto is never evaluated.\n        uint32 decoy2 = (101 << 16) | uint32(uint16(int16(-21)));\n        vm.prank(lender);\n        plant.transfer(mallory, 201 ether);\n        vm.startPrank(mallory);\n        organism.park(decoy2, 201 ether);\n        assertEq(organism.challenger(), decoy2);\n        assertEq(organism.votingStake(decoy2), 0);\n        organism.settle();\n        organism.unpark(decoy2, 201 ether);\n        plant.transfer(lender, 201 ether);\n        vm.stopPrank();\n\n        assertEq(plant.balanceOf(mallory), 0, \"mallory kept no PLANT\");\n        assertEq(organism.parkedTotal(decoy2), 0);\n        assertEq(organism.votingStake(PORTO), 200 ether, \"Porto's stake was there at heartbeat\");\n        // Expected: Porto (200 > 100, >= 50). Actual: Lisbon; the day's vote was spent on decoys twice.\n        assertEq(organism.location(), PORTO, \"holders' candidate was never read\");\n    }\n\n}","reproduction":"PLANT supply 1000e18 (alice 300, bob 300, lender 400), organism bound, 1000 IMD pot. alice parks 100e18 at Lisbon; settle -> birth move to Lisbon. bob parks 200e18 at PORTO = (164<<16)|uint16(-35) = 10813405 -> challenger = PORTO. Day ends. Transaction 1 by mallory with 201e18 borrowed from lender: park(DECOY = (100<<16)|uint16(-20), 201e18); heartbeat(); unpark(DECOY, 201e18); return the PLANT -> pending.challenger == DECOY, parkedTotal[DECOY] == 0, votingStake(DECOY) == 0. Anyone calls challenge(PORTO) -> challenger == PORTO. Oracle delivers a complete word. Transaction 2 by mallory with 201e18 borrowed again: park(DECOY2 = (101<<16)|uint16(-21), 201e18) -> challenger == DECOY2, votingStake(DECOY2) == 0; settle(); unpark(DECOY2, 201e18); return the PLANT. Expected: location == 10813405 (PORTO; votingStake(PORTO) == 200e18 at that settle). Actual: location == 10223579 (Lisbon), mallory's PLANT balance 0, parkedTotal[DECOY2] == 0. The proof fails on this code with 'holders' candidate was never read: 10223579 != 10813405': forge test --match-path test/scratch/ReDecoy.t.sol. The round-1 proof (Proof_7a54012d9763) now passes because its attacker does not re-arm the decoy at settle.","severity":"medium","snippet":"        if (!_read(candidate) && challenger != candidate) _read(challenger);","title":"Not fixed: the live-challenger fallback is itself chosen by live parkedTotal, so the decoy that froze pending.challenger can be re-armed inside the settle transaction and the holders' candidate is sti"}],"hash":"940a272f46a314b1ab9f09cba5cb1d00114c9bb6662f0c142c5df1e7c82d747c","nodeId":"218594ac-f382-4dbc-83d5-a2316d05422b","outcome":"completed","summary":"Review complete. The findings are in `.imd-findings.json` with two findings, both carrying a proof, and coverage rows for all 15 entry points.\n\n**Outcome: both round-1 blocking findings reproduce on the revised code in a new form, so neither fix holds.** The two attached round-1 proofs now pass, but only because their attackers never call heartbeat.\n\n- **High, not fixed (line 325):** the vote cutoff the author introduced is the heartbeat instant, and heartbeat is permissionless. An attacker parks borrowed PLANT, calls heartbeat, and unparks in one transaction, so the snapshot records the borrowed amount. When the word arrives they park the same amount into the same cell, settle, and unpark. Inside that settle the voting stake is the full borrowed amount, the plant moves, and a pre-parked 1 wei takes the whole next-day gardener pool. The proof moves the plant to cell 10289116 and pays 310 IMD to the 1 wei stake while the committed gardener receives nothing. Suggested fix: measure stake present since the previous settle, which the reward accumulator already tracks as active stake.\n- **Medium, not fixed (line 431):** the new fallback reads the live challenger, but the challenger is still chosen from live balances. The decoy that froze the captured candidate can be re-armed at settle with a fresh empty-snapshot cell, so the holders' candidate at Porto is never read. The proof ends at Lisbon instead of Porto with the attacker holding no PLANT.\n\n**Settled as fixed or accepted:** the empty question string at birth is verified. The disputed items on unanswered strikes, the rotation deadline and the 95% threshold follow the brief's explicit wording and were dropped. The 92-day window note was wrong, since a living plant cannot be more than 30 days behind. The specialists' tautological-conservation note is answered by the new state-machine suite's solvency assertion. The project's own suite passes with 91 tests.\n\nScratch proofs stay under `test/scratch/`, which is not kept. Nothing else in the tree changed.","treeHash":null,"usage":{"cachedInputTokens":1837493,"inputTokens":610,"model":"claude-fable-5-1","outputTokens":31528,"runtime":"claude","turns":23,"wallClockMs":569699}},{"artifacts":[],"attempt":1,"bundleHash":"402b5bf5952f6510eafabe2d77b8ef4be41622c39885131841181be76f920a4e","device":"2fe9efd0790ef9c3","findings":[],"hash":"c655c0c13529062dae20353f219f9069d4b13eb6a19971b59011ef8ca0ae49c5","nodeId":"bed004b9-308e-4f3c-addb-39747a5599a1","outcome":"completed","summary":"Updated [launch.json](/home/worker/.identitymd/work/464306a5-5aae-4ad5-95be-b83776f5fc81/bed004b9-308e-4f3c-addb-39747a5599a1/launch.json) with precise signer-rotation and pending-callback notes.\n\nSchema and constructor ABI checks passed. `forge build` succeeded with existing lint warnings; `forge test` passed all 73 tests.\n\nOnly `launch.json` changed.","treeHash":"7c4c63efd1f6297947bd2c8057fd349277857cda","usage":{"cachedInputTokens":549760,"inputTokens":47144,"model":"gpt-6-astra","outputTokens":7456,"runtime":"codex","turns":5,"wallClockMs":331805}},{"artifacts":[],"attempt":1,"bundleHash":null,"device":"9df7d5d52e83c572","findings":[{"citation":"resolved","description":"_read() compares parkedTotal[candidate] against parkedTotal[current] and the 5% threshold using live balances at the moment settle() runs, and unpark() imposes no delay after park(). settle() is callable by anyone once the day's word is stored. So the whole relocation requirement can be met by PLANT that is parked and withdrawn inside the same transaction as settle(): the vote is a flash vote (the governance threat profile's 'voting power measured at current balance, not a snapshot'). Anyone who momentarily holds, or flash-borrows from the second-launch pool, more PLANT than the current cell's total and at least 5% of supply decides where the plant lives at zero capital cost. The brief says 'all read now' and 'no minimum stay', but the economic consequence goes further than relocating: because the gardener pool is paid to the stake that was already active at the destination, an attacker who pre-parked 1 wei at the destination cell receives 100% of the next day's pool (1/3 of all sips) while the committed gardeners of the old cell receive nothing for that day. The inverse works too: park at the current cell inside the settle transaction to veto a legitimate move, then unpark. Who loses: the gardeners active at the old location (one day's pool each time, repeatable every day they do not win the cell back). Who gains: the mover, with no stake left behind. Fix that keeps 'holders decide where it lives': evaluate the move on stake that was already active as of the previous settle (cellRewards[cell].active after _rollCell, which the reward accumulator already maintains) for both sides of the comparison and the 5% threshold, or make unpark wait one settle after park. Either changes the brief's 'all read now' wording and is a scope decision for the author.","line":455,"path":"src/PlantOrganism.sol","proof":"// SPDX-License-Identifier: MIT\npragma solidity 0.8.26;\n\nimport {Test} from \"forge-std/Test.sol\";\nimport {PlantOrganism} from \"src/PlantOrganism.sol\";\nimport {OracleAttestation} from \"src/OracleAttestation.sol\";\nimport {IERC20} from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport {IIntake} from \"src/interfaces/IIntake.sol\";\n\ncontract FMToken is IERC20 {\n    uint256 public totalSupply;\n    mapping(address => uint256) public balanceOf;\n    mapping(address => mapping(address => uint256)) public allowance;\n\n    function mint(address to, uint256 amount) external {\n        balanceOf[to] += amount;\n        totalSupply += amount;\n    }\n\n    function approve(address spender, uint256 amount) external returns (bool) {\n        allowance[msg.sender][spender] = amount;\n        return true;\n    }\n\n    function transfer(address to, uint256 amount) external returns (bool) {\n        balanceOf[msg.sender] -= amount;\n        balanceOf[to] += amount;\n        return true;\n    }\n\n    function transferFrom(address from, address to, uint256 amount) external returns (bool) {\n        allowance[from][msg.sender] -= amount;\n        balanceOf[from] -= amount;\n        balanceOf[to] += amount;\n        return true;\n    }\n}\n\ncontract FMHook {\n    address public immutable organism;\n    address public immutable plant;\n\n    constructor(address o, address p) {\n        organism = o;\n        plant = p;\n    }\n}\n\ncontract FMIntake is IIntake {\n    uint256 public sequence;\n    bytes32 public lastId;\n    Callback public callback;\n\n    function priceOf(bytes32, address) external pure returns (uint256) {\n        return 0.5 ether;\n    }\n\n    function request(bytes32, bytes calldata, Callback calldata cb, address asset, uint256 amount)\n        external\n        payable\n        returns (bytes32 id)\n    {\n        require(IERC20(asset).transferFrom(msg.sender, address(this), amount));\n        callback = cb;\n        id = keccak256(abi.encode(address(this), ++sequence));\n        lastId = id;\n    }\n\n    function deliver(bytes32 id, OracleAttestation.Attestation calldata a, bytes calldata sig)\n        external\n        returns (bool ok)\n    {\n        (ok,) = callback.target.call{gas: 200000}(abi.encodeWithSelector(callback.selector, id, a, sig));\n    }\n}\n\n/// @dev Stands in for a flash borrower: the PLANT that casts the vote is parked and unparked inside one call.\ncontract Relocator {\n    PlantOrganism immutable organism;\n\n    constructor(PlantOrganism o, IERC20 p) {\n        organism = o;\n        p.approve(address(o), type(uint256).max);\n    }\n\n    function dustPark(uint32 cell) external {\n        organism.park(cell, 1);\n    }\n\n    function relocate(uint32 cell, uint256 amount) external {\n        organism.park(cell, amount);\n        organism.settle();\n        organism.unpark(cell, amount);\n    }\n\n    function claim() external {\n        organism.claim();\n    }\n}\n\ncontract FlashMoveTest is Test {\n    uint256 constant KEY = 0x59c6995e998f97a5a0044966f0945389dc9e86dae88c7a8412f4603b6b78690d;\n    uint32 constant LISBON = 10223579;\n    uint32 constant OTHER = (156 << 16) | 65500;\n    uint32 constant START = 20000;\n    address alice = address(0xa11ce);\n    address keeper = address(0xbee);\n    FMToken imd;\n    FMToken plant;\n    FMIntake intake;\n    PlantOrganism organism;\n    Relocator eve;\n    uint256 serial;\n\n    function setUp() public {\n        vm.chainId(4663);\n        vm.warp(uint256(START) * 1 days + 123);\n        imd = new FMToken();\n        plant = new FMToken();\n        intake = new FMIntake();\n        organism = new PlantOrganism(\n            address(imd), address(intake), bytes32(\"oracle.request@oracle-1\"), vm.addr(KEY), LISBON, address(this)\n        );\n        plant.mint(alice, 900 ether);\n        eve = new Relocator(organism, plant);\n        plant.mint(address(eve), 100 ether + 1); // 1 wei of lasting stake plus capital used only inside relocate()\n        organism.bind(address(new FMHook(address(organism), address(plant))));\n        imd.mint(address(organism), 1000 ether);\n        vm.prank(alice);\n        plant.approve(address(organism), type(uint256).max);\n        // Birth: alice is the only gardener, at Lisbon.\n        vm.prank(alice);\n        organism.park(LISBON, 100 ether);\n        _nextEnded();\n        organism.settle();\n        assertEq(organism.location(), LISBON);\n        // Eve's 1 wei at OTHER becomes the standing challenger and is reward-active one settle later.\n        eve.dustPark(OTHER);\n        assertEq(organism.challenger(), OTHER);\n        _askAndDeliver(0);\n        organism.settle(); // no move: 1 wei is below alice's 100 ether\n        assertEq(organism.location(), LISBON);\n    }\n\n    function _nextEnded() internal {\n        uint256 time = uint256(organism.lastSettledDay() + 2) * 1 days;\n        if (vm.getBlockTimestamp() < time) vm.warp(time);\n    }\n\n    function _askAndDeliver(uint24 sun) internal {\n        _nextEnded();\n        vm.prank(keeper);\n        bytes32 id = organism.heartbeat();\n        OracleAttestation.Attestation memory a;\n        a.requestId = keccak256(abi.encode(\"uuid\", ++serial));\n        a.chainId = 4663;\n        a.answerType = 2;\n        a.answer = abi.encode(bytes32(uint256(sun) | uint256(1) << 48 | uint256(organism.lastSettledDay() + 1) << 96));\n        a.panelSize = 15;\n        a.quorum = 10;\n        a.agreed = 12;\n        a.issuedAt = uint64(vm.getBlockTimestamp());\n        a.expiresAt = uint64(vm.getBlockTimestamp() + 1 days);\n        (uint8 v, bytes32 r, bytes32 s) = vm.sign(KEY, organism.attestationDigest(a));\n        assertTrue(intake.deliver(id, a, abi.encodePacked(r, s, v)));\n    }\n\n    /// Runs park -> settle -> unpark in one transaction. A fix may make that sequence revert or make the\n    /// settle inside it not move the plant; either way the plant must still be at Lisbon afterwards.\n    function _attemptAtomicRelocation() internal {\n        uint32 before = organism.lastSettledDay();\n        try eve.relocate(OTHER, 100 ether) {} catch {}\n        if (organism.lastSettledDay() == before) organism.settle();\n    }\n\n    /// The vote that moves the plant is cast and withdrawn inside one transaction.\n    function test_atomicParkSettleUnparkMustNotMoveThePlant() public {\n        _askAndDeliver(0); // the day's word is stored; settle() is open to anyone\n        uint256 eveBefore = plant.balanceOf(address(eve));\n        _attemptAtomicRelocation();\n        assertEq(plant.balanceOf(address(eve)), eveBefore, \"capital fully withdrawn\");\n        assertEq(organism.parkedTotal(OTHER), 1, \"only the dust remains at OTHER\");\n        assertEq(organism.location(), LISBON, \"plant moved by capital that left in the same transaction\");\n    }\n\n    /// After the zero-commitment move, the next sunny day's whole gardener pool goes to 1 wei of PLANT and\n    /// the only real gardener (alice, 100 ether) earns nothing.\n    function test_nextPoolMustGoToTheRealGardenerNotTheDustStake() public {\n        _askAndDeliver(0);\n        _attemptAtomicRelocation();\n        _askAndDeliver(0xffffff); // a sunny day\n        organism.settle();\n        eve.claim();\n        vm.prank(alice);\n        organism.claim();\n        emit log_named_uint(\"IMD paid to the 1 wei stake\", imd.balanceOf(address(eve)));\n        emit log_named_uint(\"IMD paid to alice's 100 ether stake\", imd.balanceOf(alice));\n        assertEq(imd.balanceOf(address(eve)), 0, \"dust stake captured the pool\");\n        assertGt(imd.balanceOf(alice), 0, \"the committed gardener earned nothing\");\n    }\n}","reproduction":"State: bound plant at Lisbon, alice has 100e18 PLANT active at Lisbon (supply 1000e18, pot ~979 IMD), eve has 1 wei parked at cell OTHER (the standing challenger) since two settles ago and holds 100e18 PLANT liquid (or borrows it). Day D's word is delivered by the oracle, settle() not yet called. Eve sends one transaction: park(OTHER, 100e18); settle(); unpark(OTHER, 100e18). Expected (holders decide where it lives): the plant stays at Lisbon because no committed stake backs OTHER. Actual: location becomes OTHER (10289116) while parkedTotal[OTHER] is back to 1 wei and eve's PLANT balance is unchanged. Next day (24 sunny hours at OTHER): the pool is 300184772098882854196 wei (~300 IMD), all credited to eve's 1 wei; alice's 100e18 stake receives 0. Both tests in test/scratch/FlashMove.t.sol fail on this code with exactly those values: run `forge test --match-path test/scratch/FlashMove.t.sol -vv`.","severity":"medium","snippet":"            candidate != 0 && candidate != current && parkedTotal[candidate] > parkedTotal[current]\n                && parkedTotal[candidate] >= Math.ceilDiv(PLANT.totalSupply(), 20)","title":"Relocation vote has no commitment: park -> settle -> unpark in one transaction moves the plant, and a dust stake then takes the whole next-day gardener pool"},{"citation":"resolved","description":"pot() is defined in src/PlantOrganism.sol:203-208 as int256(held) - int256(backing + owed()), so pot() + backing + owed() == held is true for every possible value of backing, creditTotal, gardenerPoints and feeAdvanceTotal. The README and both test files present this as 'the required conservation identity', but it cannot fail and therefore proves nothing about the accounting. The property that actually protects holders, backing + gardenerReserve() + creditTotal <= IMD.balanceOf(organism) (every senior claim is payable), is not asserted anywhere. In addition the invariant handler sets intake.setPrice(0) (test/PlantInvariant.t.sol:25), delivers only complete words and warps exactly one day per step, so the negative-pot / fee-advance / senior-repayment code in heartbeat() and _pay(), the incomplete and timeout paths and the death merge are never reached by the stateful fuzz. I ran a scratch handler with price 0.5 IMD, keeper advances, random incomplete words, timeouts, donations and death asserting the solvency property and floor monotonicity (64 runs x 160 calls, 13 advances / 2 incompletes / 6 timeouts / 1 death in the sampled run) and found no violation, so this is a test-coverage defect, not a code defect.","line":156,"path":"test/PlantInvariant.t.sol","reproduction":"Deploy and bind as in the suite, mint 1000e18 IMD to the organism, then vm.store(organism, bytes32(uint256(9)), bytes32(uint256(5000e18))) (slot 9 is backing). Now backing == 5000e18 while the contract holds 1000e18: floor() reports 5e18 per PLANT, redeem would revert for any meaningful amount, i.e. the contract is insolvent. The suite's assertion pot() + backing + owed() == balanceOf still passes (pot() is -4000e18), whereas backing + gardenerReserve() + creditTotal > balanceOf is true. test/scratch/Tautology.t.sol::test_conservationAssertionCannotDetectCorruptedBacking demonstrates both statements and passes on this code. Fix: assert backing + gardenerReserve() + creditTotal <= held and, when pot() < 0, -pot() <= feeAdvanceTotal, and give the invariant handler a non-zero price with approved keepers, incomplete/timeout steps and a death step.","severity":"low","snippet":"        assertEq(o.pot() + int256(o.backing()) + int256(o.owed()), int256(handler.imd().balanceOf(address(o))));","title":"The suite's 'conservation identity' is a tautology; real solvency is never asserted and the stateful fuzz disables fee advances, incomplete words and death"},{"citation":"resolved","description":"The 5% threshold is taken from PLANT.totalSupply(), which never shrinks because redeemed PLANT stays in this contract forever (burned), while the floor uses totalSupply() - burned. Once burned > 95% of supply, ceilDiv(totalSupply, 20) exceeds all PLANT still in circulation, so no candidate cell can ever satisfy _read() even if every remaining holder parks there. 'Holders decide where it lives' becomes unsatisfiable for the remaining holders, and the plant is pinned to its last cell. If that cell has run its water down to 0 (no rain), no sip ever happens again, yet every settle still credits 1% of the pot to the heartbeat caller, so from then on the only outflow from the pot is the keeper bounty (about 26% of the pot per 30 days) and the remaining holders' only exit is redeem at 90% of a frozen floor. The brief literally says '5% of PLANT.totalSupply()', so this may be intended; it is reported because the end state contradicts the brief's own 'holders decide where it lives' and leaks the pot to keepers rather than to backing or gardeners. Alternative: measure 5% against totalSupply() - burned, the same base the floor uses.","line":456,"path":"src/PlantOrganism.sol","reproduction":"Suite fixture (supply 1000e18: alice 600, bob 300, carol 100), plant born at Lisbon. alice unparks and redeems 600e18, bob redeems 300e18, carol redeems 60e18 -> burned = 960e18, 40e18 in circulation, parkedTotal[LISBON] == 0. carol parks all 40e18 at OTHER; challenger == OTHER. Next weather day settles: expected (all holders voted for OTHER against an empty Lisbon) a move; actual location stays LISBON because 40e18 < ceilDiv(1000e18, 20) = 50e18, and this is permanent. Each further settle still lowers spendablePot() by the 1% bounty. test/scratch/StuckAfterRedeem.t.sol::test_relocationImpossibleAfterNinetyFivePercentRedeemed passes on this code.","severity":"low","snippet":"                && parkedTotal[candidate] >= Math.ceilDiv(PLANT.totalSupply(), 20)","title":"Relocation threshold counts burned PLANT: after more than 95% of supply is redeemed the plant can never move again, while keepers keep taking 1% of the pot per day"},{"citation":"resolved","description":"heartbeat() pays whatever priceOf() returns, with no ceiling, and pulls the shortfall from the caller. Two privileged actors can set that number: the Intake owner (protocol) by repricing, and the oracle signer through rotate(), which may point intake at any contract with code. A rotated-in intake returning price = spendablePot() + callerApproval receives the entire pot and drains the caller's approval in a single heartbeat; backing and gardener credits are protected because spendablePot() excludes them. This is documented behaviour of a trusted role (rotation is in the brief) and is listed as a trust assumption, not a defect: the oracle key is a single point of failure, a compromised key can rotate once and permanently lock out the honest operator (rotate requires the current signer), and keepers should approve only the advance they intend to make rather than type(uint256).max as the test fixture does.","line":290,"path":"src/PlantOrganism.sol","reproduction":"Signer S signs Rotate(organism, 4663, S2, M, action, 0) where M is a contract whose priceOf returns 10_000e18 and whose request pulls exactly that. Anyone relays rotate(S2, M, action, 0, sig). A keeper with an unlimited IMD approval calls heartbeat(): price 10_000e18 > spendablePot(), the keeper's IMD is pulled for the difference and the whole amount is transferred to M; feeAdvances[keeper] records a debt the pot may never repay. No unprivileged amplifier exists, hence informational.","severity":"info","snippet":"        uint256 price = intake.priceOf(action, address(IMD));\n        uint256 available = spendablePot();\n        if (price > available) {\n            uint256 advance = price - available;\n            _pullExact(IMD, msg.sender, advance);","title":"No cap on the oracle price: a repriced or rotated Intake can take the whole spendable pot plus the heartbeat caller's open IMD approval in one heartbeat (trust assumption)"},{"citation":"resolved","description":"The brief mandates the guards object, but the pinned oracle-consumer skill says the body is the strict PaidOracleInput ('an unknown key refuses the whole request', status 1, price spent, no callback) and lists v, question, chainId, window, answerType, evidence, panelSize, quorum, validForSeconds, definitions, allowAmbiguous and deliver, never guards. The skill also says that where the brief and the reference disagree on the protocol, the reference wins. If guards is not accepted by the live door, every heartbeat spends 0.5 IMD and is never answered, each request must time out (24h) and wait 6h, no day ever settles, and the plant is dead 30 days after bind having burned about 12 IMD. This cannot be reproduced locally (the mock accepts any body) and is a pre-launch verification item: submit one request with this exact body against the live door, or confirm the schema version that introduced guards.","line":79,"path":"src/WeatherQuestion.sol","reproduction":"Not reproducible offline. Verification: call Intake.request(action, requestBody(10223579, <ended day>), callback, IMD, 0.5e18) once on Robinhood Chain and confirm status 0 and a callback; a status-1 refusal confirms the defect. On-chain consequence if refused: heartbeat -> no callback -> clearPending after 24h -> RetryLater for 6h -> repeat; lastSettledDay never advances; isDead() at bindDay + 30.","severity":"info","snippet":"                '\",\"chainId\":4663,\"window\":{\"hours\":24},\"answerType\":\"bytes32\",\"evidence\":\"panel\",\"panelSize\":15,\"quorum\":10,\"validForSeconds\":86400,\"guards\":{\"sources\":[\"https://api.open-meteo.com\"],\"minSources\":1}}'","title":"Request body carries a 'guards' key that the oracle-consumer reference does not list for PaidOracleInput; a strict door would refuse every request and the plant would die unanswered"},{"citation":"resolved","description":"Settlement is strictly sequential (_settle() always targets lastSettledDay + 1, src/PlantOrganism.sol:383) and the question pins the forecast endpoint with past_days=92, so for any day more than 92 days old a faithful panel answers complete = 0. The only way forward is three incompletes (3 x 0.5 IMD, two 6h waits, about 15h) per biological day, each paying a 1% bounty on an empty day. The state is reachable while alive because one settle every 29 days resets the death clock while the backlog grows by 28 days a month. Costs are bounded (the backlog shrinks about 9h per calendar day) and the brief fixes both the question text and the sequential rule, so this is informational: an operational cliff the README should state (keep the backlog under 92 days) rather than a code defect.","line":64,"path":"src/WeatherQuestion.sol","reproduction":"State: bound, location != 0, lastSettledDay = today - 100, alive (last settle 29 days ago). heartbeat() asks about a date 99 days old; the pinned URL has no rows for it; the panel returns complete = 0; settle() counts incompletes[D] = 1, clears, retryAt = +6h; twice more; the third settle advances one day with no hours and credits 1% of the pot to the caller. Repeat for every backlog day.","severity":"info","snippet":"            \"&past_days=92&hourly=is_day,cloud_cover,precipitation&timezone=UTC, the 24 rows of \",","title":"Days older than the 92-day source window can only be settled through three paid incompletes each, so a plant that falls far behind pays 1.5 IMD plus a 1% bounty per empty day"}],"hash":"d325fdff32e17d3e83da520427a3035278153a04fa635bb5c907caec4672d667","nodeId":"df2ff5e9-326f-4091-bee0-5f1ed6d8eff8","outcome":"completed","summary":"The review is complete. `.imd-findings.json` holds 6 findings and a 20-row coverage record (all 15 entry points answered, none unreached). Git status is clean apart from the review outputs, which are ignored.\n\n**What I found in the assigned area (Economic Security, Invariant, Flow Gap)**\n\n- **Medium: the relocation vote has no commitment.** `_read` compares live `parkedTotal` at settle time and `unpark` has no delay, so one transaction can park, settle, and unpark. Reproduced: with alice's 100 PLANT committed at Lisbon, an attacker moved the plant with capital that left in the same transaction, and a pre-parked 1 wei stake then collected the entire next-day gardener pool of about 300 IMD while alice got nothing. The proof test in `test/scratch/FlashMove.t.sol` fails on this code with those exact values. The brief's \"all read now\" permits this, so the fix is a scope decision, and I named two options that keep \"holders decide where it lives\".\n- **Low: the suite's conservation identity is a tautology.** `pot()` is defined as held minus backing minus owed, so the assertion holds for any backing value. I showed that with backing corrupted to five times the IMD held, the suite's check still passes. Real solvency is never asserted, and the stateful fuzz sets the oracle price to zero, so fee advances, incompletes, timeouts and death are never fuzzed.\n- **Low: the 5% move threshold counts burned PLANT.** After more than 95% of supply is redeemed, no remaining holder can ever move the plant, while the keeper bounty keeps draining 1% of the pot per day. Reproduced with the suite's fixture.\n- **Info:** no cap on the oracle price (a repriced or rotated Intake can take the spendable pot and the caller's open approval), the undocumented `guards` body key that a strict door would refuse, and the 92-day source window that makes a deep backlog cost three paid incompletes per day.\n\n**What holds.** I traced every writer of backing, credits, gardener points and fee advances and ran a scratch stateful fuzz with a nonzero price, keeper advances, incomplete words, timeouts, donations and death. The real solvency invariant, floor monotonicity, burned custody and the reward accumulator all held. The callback stays under the 200k stipend on both the current-signer and grace-signer paths (94k and 99k cold).\n\n**Not verifiable here.** Whether the live Intake accepts the `guards` key, and whether the second-launch PLANT keeps a fixed supply, which the floor guarantee depends on.","treeHash":null,"usage":{"cachedInputTokens":5153314,"inputTokens":898,"model":"claude-fable-5-1","outputTokens":115617,"runtime":"claude","turns":54,"wallClockMs":1979725}},{"artifacts":[],"attempt":1,"bundleHash":"fd90d57a604d385cc6d52cecd7eec6f61024ee0467b83f3c65e28e3ecdcad525","device":"e04b1c0f30c2612f","findings":[],"hash":"d969c13a6b67694e2790b5173762ec54f89fc61337eec1bfad96637c08b3cd3f","nodeId":"2f38f19e-efa5-428b-8ad9-b7f740ed0e0b","outcome":"completed","summary":"Added 23 tests covering failure paths, stateful accounting invariants, and enforced gas limits, plus a one-page test README.\n\nAll 73 tests pass with two fuzz seeds. Offline build and formatting checks pass. Changes are confined to `test/`. No reproducible implementation defects found.","treeHash":"3da9752c4d5bff2d6f274a85d28ae555bb04798a","usage":{"cachedInputTokens":3032960,"inputTokens":133114,"model":"gpt-6-astra","outputTokens":21609,"runtime":"codex","turns":8,"wallClockMs":556886}},{"artifacts":[],"attempt":1,"bundleHash":null,"device":"934ef01306a3cc89","findings":[{"citation":"resolved","description":"Seam: access x asymmetry x economics. Three things are each correct alone and exploitable together. (1) `settle()` (line 373) and `heartbeat()` (line 283) are callable by anyone, so the caller chooses the exact instant at which the move is evaluated. (2) `_read` (lines 452-462) compares `parkedTotal[candidate]` against `parkedTotal[current]` and the 5% threshold using the balances at that instant, while `park` (236) and `unpark` (252) have no minimum stay, so stake that exists only inside one transaction counts fully. (3) The gardener pool for the following day is distributed by `_distribute` to `cellRewards[location].active`, i.e. whoever already had stake in the new cell before the move. After every move (and after the birth fallback) `challenger` is reset to 0 (lines 459, 419), so `_challenge` (line 277) accepts any cell with 1 wei parked as the next candidate for free; `heartbeat` then snapshots it into `pending.challenger` (line 312). A non-holder therefore needs PLANT only for the one block in which it calls `park` -> `settle` -> `unpark` (a Uniswap v4 flash-accounting take/settle from the launch pool, or any whale lending to itself), and afterwards owns 100% of `cellRewards[newCell].active` with its 1 wei. The brief's guarantee \"holders decide where it lives\" and \"gardener pool -> pro rata to holders parked behind C\" both fail: a one-block visitor decides the location, and the pool (1/3 of all sips, ~30% of the pot on a fully sunny day) is paid to that visitor instead of the holders who were gardening. The same primitive lets the visitor defend the current cell against a legitimate move (park into `location` -> settle -> unpark). Minimal fix preserving the design: count only stake that was parked before the day's heartbeat toward the move (e.g. roll the candidate and current cells and compare `cellRewards[c].active`, which already encodes \"parked as of the previous settle\"), or require `parkedTotal[candidate]` at settle to be no lower than it was when the candidate was snapshotted at heartbeat; either way stake created in the settle transaction no longer moves the plant.","line":455,"path":"src/PlantOrganism.sol","reproduction":"Supply 1000 PLANT (alice 600, bob 300, pool 100-1 wei, mallory 1 wei), organism bound, 1000 IMD in the pot. 1) alice and bob park 100 each at LISBON; settle -> birth move to LISBON, challenger = 0. 2) mallory parks 1 wei at DESERT (cell (100<<16)|uint16(-20)); `_challenge`: 1 > parkedTotal[0] = 0 -> challenger = DESERT. 3) Next UTC day: keeper calls heartbeat() -> pending.challenger = DESERT; oracle delivers a complete all-sunny word. 4) In ONE transaction mallory receives 201 PLANT from the pool, calls park(DESERT, 201e18), settle(), unpark(DESERT, 201e18) and returns the 201 PLANT. `_read`: parkedTotal[DESERT] = 201e18+1 > parkedTotal[LISBON] = 200e18 and >= ceil(1000e18/20) = 50e18 -> location = DESERT. mallory's PLANT balance after the block: 0. Expected: location stays LISBON (no holder moved it). Actual: location = 6619116 (DESERT). 5) Next day, full sun at DESERT: settle -> _distribute(DESERT, pool) with cellRewards[DESERT].active = 1 wei (mallory). mallory.claim([DESERT]) pays 24.057 IMD; alice (100e18 parked at LISBON all along) receives 0 for that day. Expected: the pool goes to holders parked behind the location holders chose; actual: all of it to the one-block visitor. Proof: test/scratch/TransientStakeMove.t.sol, both tests fail on this code (`transient stake moved the plant: 6619116 != 10223579`, `a one-block visitor captured the gardener pool: 24057756140295982821 != 0`).","severity":"high","snippet":"        if (\n            candidate != 0 && candidate != current && parkedTotal[candidate] > parkedTotal[current]\n                && parkedTotal[candidate] >= Math.ceilDiv(PLANT.totalSupply(), 20)\n        ) {","title":"READ samples live parkedTotal with no minimum stay and settle() is permissionless: PLANT held for one transaction moves the plant and a 1 wei position then captures the whole gardener pool"},{"citation":"resolved","description":"Seam: access x asymmetry. `_challenge` (line 277) overwrites `challenger` whenever `parkedTotal[cell] > parkedTotal[challenger]` using live balances, `heartbeat` (line 312) freezes that value into `pending.challenger`, and `_settle` (line 401) READs only `p.challenger`, never the live `challenger` that anyone can repair afterwards with `challenge()`. Since `heartbeat` is permissionless and pays its caller the 1% bounty, the caller is free (even rewarded) to shape the snapshot. A caller that parks into an unused cell with more PLANT than the current challenger, calls heartbeat, and unparks in the same transaction leaves `pending.challenger` pointing at a cell with `parkedTotal == 0`. At settle `_read(DECOY)` fails and the holders' candidate, which satisfies every rule in the brief (above the location, above 5% of supply), is not evaluated that day. Repeating this every day blocks relocation indefinitely at the cost of gas plus borrowing PLANT for one block; on the day right after a move it costs nothing at all because `challenger` is 0 and 1 wei suffices to set the decoy. The brief says \"holders decide where it lives\"; here the heartbeat caller decides whether they get to. This survives a fix of finding 1 (active-only stake for the move) because `_challenge` itself uses live `parkedTotal`. Minimal fix: in `_settle`, if the snapshot candidate fails `_read`, also try the live `challenger` (still O(1)); and/or have `_challenge` compare `cellRewards[cell].active` after `_rollCell` so transient stake cannot become the challenger.","line":312,"path":"src/PlantOrganism.sol","proof":"// SPDX-License-Identifier: MIT\npragma solidity 0.8.26;\n\nimport {Test} from \"forge-std/Test.sol\";\ninterface IERC20Like { function transferFrom(address from, address to, uint256 amount) external returns (bool); }\nimport {PlantOrganism} from \"src/PlantOrganism.sol\";\nimport {OracleAttestation} from \"src/OracleAttestation.sol\";\nimport {IIntake} from \"src/interfaces/IIntake.sol\";\n\ncontract ProofToken2 {\n    uint256 public totalSupply;\n    mapping(address => uint256) public balanceOf;\n    mapping(address => mapping(address => uint256)) public allowance;\n\n    function mint(address to, uint256 amount) external {\n        balanceOf[to] += amount;\n        totalSupply += amount;\n    }\n\n    function approve(address spender, uint256 amount) external returns (bool) {\n        allowance[msg.sender][spender] = amount;\n        return true;\n    }\n\n    function transfer(address to, uint256 amount) external returns (bool) {\n        balanceOf[msg.sender] -= amount;\n        balanceOf[to] += amount;\n        return true;\n    }\n\n    function transferFrom(address from, address to, uint256 amount) external returns (bool) {\n        allowance[from][msg.sender] -= amount;\n        balanceOf[from] -= amount;\n        balanceOf[to] += amount;\n        return true;\n    }\n}\n\ncontract ProofHook2 {\n    address public immutable organism;\n    address public immutable plant;\n\n    constructor(address organism_, address plant_) {\n        organism = organism_;\n        plant = plant_;\n    }\n}\n\ncontract ProofIntake2 is IIntake {\n    uint256 public sequence;\n    bytes32 public lastId;\n    Callback public callback;\n\n    function priceOf(bytes32, address) external pure returns (uint256) {\n        return 0.5 ether;\n    }\n\n    function request(bytes32, bytes calldata, Callback calldata cb, address asset, uint256 amount)\n        external\n        payable\n        returns (bytes32 id)\n    {\n        require(IERC20Like(asset).transferFrom(msg.sender, address(this), amount));\n        callback = cb;\n        id = keccak256(abi.encode(address(this), ++sequence));\n        lastId = id;\n    }\n\n    function deliver(bytes32 id, OracleAttestation.Attestation calldata a, bytes calldata sig) external returns (bool ok) {\n        (ok,) = callback.target.call{gas: 200000}(abi.encodeWithSelector(callback.selector, id, a, sig));\n    }\n}\n\n/// @notice The heartbeat caller fixes the day's only move candidate. Parking into an empty cell,\n/// asking, and unparking in one transaction points the candidate at a cell with no stake, so the\n/// holders' real candidate (above 5% of supply and above the location) is never read that day.\ncontract SnapshotEmptyCellTest is Test {\n    uint256 internal constant KEY = 0x59c6995e998f97a5a0044966f0945389dc9e86dae88c7a8412f4603b6b78690d;\n    uint32 internal constant LISBON = 10223579;\n    uint32 internal constant PORTO = (164 << 16) | uint32(uint16(int16(-35)));\n    uint32 internal constant DECOY = (100 << 16) | uint32(uint16(int16(-20)));\n    uint32 internal constant START = 20000;\n\n    address internal alice = address(0xa11ce);\n    address internal bob = address(0xb0b);\n    address internal mallory = address(0xbad);\n    address internal lender = address(0x9001);\n\n    ProofToken2 internal imd;\n    ProofToken2 internal plant;\n    ProofIntake2 internal intake;\n    PlantOrganism internal organism;\n    uint256 internal serial;\n\n    function setUp() public {\n        vm.chainId(4663);\n        vm.warp(uint256(START) * 1 days + 123);\n        imd = new ProofToken2();\n        plant = new ProofToken2();\n        intake = new ProofIntake2();\n        plant.mint(alice, 300 ether);\n        plant.mint(bob, 300 ether);\n        plant.mint(lender, 400 ether);\n        organism = new PlantOrganism(\n            address(imd), address(intake), bytes32(\"oracle.request@oracle-1\"), vm.addr(KEY), LISBON, address(this)\n        );\n        organism.bind(address(new ProofHook2(address(organism), address(plant))));\n        imd.mint(address(organism), 1000 ether);\n        vm.prank(alice);\n        plant.approve(address(organism), type(uint256).max);\n        vm.prank(bob);\n        plant.approve(address(organism), type(uint256).max);\n        vm.prank(mallory);\n        plant.approve(address(organism), type(uint256).max);\n    }\n\n    function _nextEnded() internal {\n        uint256 time = uint256(organism.lastSettledDay() + 2) * 1 days;\n        if (vm.getBlockTimestamp() < time) vm.warp(time);\n    }\n\n    function _deliver() internal {\n        OracleAttestation.Attestation memory a;\n        a.requestId = keccak256(abi.encode(\"uuid\", ++serial));\n        a.chainId = 4663;\n        a.questionHash = keccak256(\"doc\");\n        a.answerType = 2;\n        a.answer = abi.encode(bytes32(uint256(1) | (uint256(1) << 48) | (uint256(organism.lastSettledDay() + 1) << 96)));\n        a.panelJobId = keccak256(\"panel\");\n        a.panelSize = 15;\n        a.quorum = 10;\n        a.agreed = 12;\n        a.issuedAt = uint64(vm.getBlockTimestamp());\n        a.expiresAt = uint64(vm.getBlockTimestamp() + 1 days);\n        (uint8 v, bytes32 r, bytes32 s) = vm.sign(KEY, organism.attestationDigest(a));\n        assertTrue(intake.deliver(intake.lastId(), a, abi.encodePacked(r, s, v)), \"callback failed\");\n    }\n\n    function test_emptyCellSnapshotBlocksTheHoldersMove() public {\n        vm.prank(alice);\n        organism.park(LISBON, 100 ether);\n        _nextEnded();\n        organism.settle(); // birth: Lisbon\n        assertEq(organism.location(), LISBON);\n\n        // Bob wants Porto: 200 parked there, above Lisbon's 100 and above 5% of the 1000 supply.\n        vm.prank(bob);\n        organism.park(PORTO, 200 ether);\n        assertEq(organism.challenger(), PORTO);\n\n        // Day ends. In one transaction mallory borrows 201 PLANT, parks into a decoy cell (now the\n        // challenger), asks the oracle (candidate snapshotted = DECOY), unparks and repays.\n        _nextEnded();\n        vm.prank(lender);\n        plant.transfer(mallory, 201 ether);\n        vm.startPrank(mallory);\n        organism.park(DECOY, 201 ether);\n        organism.heartbeat();\n        organism.unpark(DECOY, 201 ether);\n        plant.transfer(lender, 201 ether);\n        vm.stopPrank();\n        (, , uint32 snapshot,,,,,,) = organism.pending();\n        assertEq(snapshot, DECOY);\n        assertEq(organism.parkedTotal(DECOY), 0);\n\n        // Anyone repairs the live challenger, but the day's READ only looks at the snapshot.\n        organism.challenge(PORTO);\n        assertEq(organism.challenger(), PORTO);\n\n        _deliver();\n        organism.settle();\n        // Expected: Porto (200 > 100, >= 50). Actual: Lisbon; the day's vote was spent on an empty cell.\n        assertEq(organism.location(), PORTO, \"holders' candidate was never read\");\n    }\n}","reproduction":"Supply 1000 PLANT (alice 300, bob 300, lender 400), organism bound, 1000 IMD pot. 1) alice parks 100 at LISBON; settle -> birth move to LISBON. 2) bob parks 200 at PORTO ((164<<16)|uint16(-35)) -> challenger = PORTO (200 > 100, and 200e18 >= 50e18 so a READ would move). 3) UTC day ends. In ONE transaction mallory receives 201 PLANT from lender, calls park(DECOY, 201e18) -> challenger = DECOY (201 > 200), heartbeat() -> pending.challenger = DECOY, unpark(DECOY, 201e18), returns the PLANT. Now parkedTotal[DECOY] == 0 and pending.challenger == DECOY. 4) Anyone calls challenge(PORTO) -> live challenger = PORTO again, but the pending snapshot is unchanged. 5) Oracle delivers a complete word; settle(). `_read(DECOY)`: parkedTotal[DECOY] = 0 > 100e18 is false -> no move. Expected: location = PORTO (10813405). Actual: location = LISBON (10223579); the day's vote is lost and bob must wait another day, where the same transaction blocks him again. Proof: test/scratch/SnapshotEmptyCell.t.sol fails with `holders' candidate was never read: 10223579 != 10813405`.","severity":"medium","snippet":"        pending =\n            Pending(id, day, challenger, msg.sender, uint64(block.timestamp), address(intake), true, false, bytes32(0));","title":"pending.challenger is the only candidate READ for the day and is fixed by whoever calls heartbeat(); a park->heartbeat->unpark in one transaction points it at an empty cell and silently discards the h"},{"citation":"resolved","description":"The `Rotate` struct binds organism, chain id, new values and nonce, but no deadline. `rotate` is relayable by anyone (line 605), so once the oracle signer has produced a signature it cannot withdraw it except by consuming the nonce with a different rotation. A relayer (or anyone who obtained the signed payload) can hold the message for any length of time and submit it when the named intake/signer are no longer the ones the oracle wants, pointing the organism at a deprecated intake and starting the 30-day death clock until the (new) signer rotates again. Impact is bounded because the signer authorised the values and the new signer can rotate back, so this is a signature-hygiene defect rather than a loss. Minimal fix: add a `deadline` field to `ROTATE_TYPEHASH`/`rotationDigest` and require `block.timestamp <= deadline` in `rotate`.","line":601,"path":"src/PlantOrganism.sol","reproduction":"At t0 the current signer signs rotationDigest(newSigner = vm.addr(555), newIntake = <some intake>, ACTION, nonce = 0). Nobody submits it. At t0 + 400 days anyone calls rotate(vm.addr(555), <that intake>, ACTION, 0, sig). Expected: a 400-day-old authorisation is refused. Actual: accepted; `intake()` and `oracleSigner()` change (scratch test test/scratch/RotateNoExpiry.t.sol passes on this code, demonstrating acceptance).","severity":"low","snippet":"        return _hashTypedDataV4(\n            keccak256(abi.encode(ROTATE_TYPEHASH, address(this), block.chainid, newSigner, newIntake, newAction, nonce))\n        );","title":"Rotation signatures carry no expiry: a signed rotate() message withheld by its relayer stays valid until the nonce is consumed"},{"citation":"resolved","description":"Documented as a privileged-power risk, not a bypass: the brief makes the oracle signer the only authority over `rotate`, and `rotate` (line 621) accepts any address with code as the new intake with no further check. `heartbeat` then approves and pays `intake.priceOf(action, IMD)` with no upper bound (line 290, line 301), accepting any amount up to `spendablePot()` from the pot and anything above that from the caller's own IMD as an advance. A signer who rotates to a contract whose `priceOf` returns `spendablePot()` and whose `request` pulls exactly that amount receives the whole pot on the next heartbeat (the `NonExactTransfer` check on line 310 is satisfied because exactly `price` moved). Backing, gardener credits and bounties are not reachable this way. The same signer already controls weather truth (and therefore sips), so this is an escalation from 'direct the pot into backing/gardeners' to 'take the pot', available for 30 days to an old key after rotation only for attestations, not for rotation. Also note that even the genuine Intake's owner can raise `priceOf` arbitrarily (the oracle reference says price is owner-settable) and the contract has no cap. If the author wants to narrow this, a constant maximum acceptable price in `heartbeat` (reverting or skipping the day above it) preserves the design and bounds the exposure to one day's cap.","line":290,"path":"src/PlantOrganism.sol","reproduction":"State: pot = 1000 IMD, plant alive. Oracle signer (or holder of its key) signs rotate(newSigner, evilIntake, action, nonce). evilIntake.priceOf returns 1000e18 and its request() calls IMD.transferFrom(organism, evil, 1000e18). Anyone relays rotate; anyone calls heartbeat() on the next ended day: `price = 1000e18 <= spendablePot()`, forceApprove(evil, 1000e18), request pulls 1000e18, balance check passes, pending recorded. Result: pot 0, evil holds 1000 IMD. Expected under a least-privilege design: a bounded per-request price. Actual: unbounded. Classified info because the actor is the trusted role named by the brief.","severity":"info","snippet":"        uint256 price = intake.priceOf(action, address(IMD));","title":"Trust assumption: the oracle signer can point `intake` at any contract and heartbeat pays whatever priceOf returns, so a compromised or malicious signer can redirect the entire spendable pot in one he"}],"hash":"fbd6ea25992707a0492b0043cffb2952b1f270619b3e6e9a3689268066633490","nodeId":"00d161b3-ba1b-4aa0-8548-cfd30215dc53","outcome":"completed","summary":"Review complete. The findings file is at the repository root, every quoted snippet matches the tree, and no tracked file was changed.\n\n**Outcome.** Within my area (Access Control, Trust Gap, Asymmetry) the permission model itself is tight: the deployer's only power is a one-shot `bind`, the callback is Intake-only and store-only, rotation is nonce- and domain-bound to the current signer, and every other entry point is deliberately permissionless. The defects I found live at the seam the Trust Gap guide describes: permissionless callers choosing the instant at which live, no-minimum-stay balances are read.\n\n**Findings written to `.imd-findings.json`:**\n\n1. **High, with proof.** The move READ compares live `parkedTotal` at the moment `settle()` is called, and park/unpark have no minimum stay. PLANT borrowed for one transaction (park, settle, unpark) moves the plant anywhere, and because `challenger` resets to 0 after every move, 1 wei in any cell makes it the next candidate for free. The one-block visitor then owns 100% of the new cell's active stake and takes the entire gardener pool the next day. The proof shows the plant moved to the desert and 24 IMD going to a 1 wei position while real gardeners got 0.\n2. **Medium, with proof.** `heartbeat()` freezes the only candidate the day will read. A park, heartbeat, unpark in one transaction points it at an empty cell, so the holders' legitimate candidate is never evaluated that day. Repeatable daily and survives a fix of finding 1.\n3. **Low.** Rotation signatures have no deadline. A withheld signature was accepted 400 days later in a scratch test.\n4. **Info, trust assumption.** The oracle signer can rotate to any intake and `heartbeat` pays an unbounded `priceOf`, so a compromised signer can redirect the whole spendable pot in one heartbeat.\n\n**Coverage.** All 15 entry points have rows (10 hold, 5 carry findings), plus three invariant rows. I also measured the grace-signer callback cold at 99k gas, which the existing suite does not assert, and it is comfortably inside the stipend. I did not go deep on question formatting, date arithmetic, or reward-accumulator rounding, which belong to other specialists.","treeHash":null,"usage":{"cachedInputTokens":2218406,"inputTokens":514,"model":"claude-fable-5-1","outputTokens":62828,"runtime":"claude","turns":36,"wallClockMs":942272}}],"verification":[{"checks":[{"durationMs":32662,"exitCode":0,"name":"build","output":"Compiling 54 files with Solc 0.8.26\nSolc 0.8.26 finished in 32.52s\nCompiler run successful!\nwarning[unsafe-typecast]: typecast can truncate values\n   ╭▸ src/WeatherQuestion.sol:12:21\n   │\n12 │         int16 lat = int16(uint16(cell >> 16));\n   │                     ━━━━━━━━━━━━━━━━━━━━━━━━━\n   │\n   ├ note: consider disabling this lint if you're certain the cast is safe\n   │       \n   │       // casting to 'int16' is safe because [explain why]\n   │       // forge-lint: disable-next-line(unsafe-typecast)\n   │       \n   │       \n   ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n   ╭▸ src/WeatherQuestion.sol:12:27\n   │\n12 │         int16 lat = int16(uint16(cell >> 16));\n   │                           ━━━━━━━━━━━━━━━━━━\n   │\n   ├ note: consider disabling this lint if you're certain the cast is safe\n   │       \n   │       // casting to 'uint16' is safe because [explain why]\n   │       // forge-lint: disable-next-line(unsafe-typecast)\n   │       \n   │       \n   ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n   ╭▸ src/WeatherQuestion.sol:13:21\n   │\n13 │         int16 lon = int16(uint16(cell));\n   │                     ━━━━━━━━━━━━━━━━━━━\n   │\n   ├ note: consider disabling this lint if you're certain the cast is safe\n   │       \n   │       // casting to 'int16' is safe because [explain why]\n   │       // forge-lint: disable-next-line(unsafe-typecast)\n   │       \n   │       \n   ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n   ╭▸ src/WeatherQuestion.sol:13:27\n   │\n13 │         int16 lon = int16(uint16(cell));\n   │                           ━━━━━━━━━━━━\n   │\n   ├ note: consider disabling this lint if you're certain the cast is safe\n   │       \n   │       // casting to 'uint16' is safe because [explain why]\n   │       // forge-lint: disable-next-line(unsafe-typecast)\n   │       \n   │       \n   ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n   ╭▸ src/WeatherQuestion.sol:19:29\n   │\n19 │         uint256 magnitude = uint256(milli < 0 ? -milli : milli);\n   │                             ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n   │\n   ├ note: consider disabling this lint if you're certain the cast is safe\n   │       \n   │       // casting to 'uint256' is safe because [explain why]\n   │       // forge-lint: disable-next-line(unsafe-typecast)\n   │       \n   │       \n   ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n   ╭▸ src/WeatherQuestion.sol:26:29\n   │\n26 │             b[--i] = bytes1(uint8(48 + n % 10));\n   │                             ━━━━━━━━━━━━━━━━━━\n   │\n   ├ note: consider disabling this lint if you're certain the cast is safe\n   │       \n   │       // casting to 'uint8' is safe because [explain why]\n   │       // forge-lint: disable-next-line(unsafe-typecast)\n   │       \n   │       \n   ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[divide-before-multiply]: division before multiplication may lose precision\n   ╭▸ src/WeatherQuestion.sol:39:27\n   │\n39 │         uint256 y = yoe + era * 400;\n   │                           ━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/divide-before-multiply\n\nwarning[divide-before-multiply]: division before multiplication may lose precision\n   ╭▸ src/WeatherQuestion.sol:40:30\n   │\n40 │         uint256 doy = doe - (365 * yoe + yoe / 4 - yoe / 100);\n   │                              ━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/divide-before-multiply\n\nwarning[divide-before-multiply]: division before multiplication may lose precision\n   ╭▸ src/WeatherQuestion.sol:42:28\n   │\n42 │         uint256 d = doy - (153 * mp + 2) / 5 + 1;\n   │                            ━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/divide-before-multiply\n\nwarning[unsafe-typecast]: typecast can truncate values\n   ╭▸ src/WeatherQuestion.sol:50:40\n   │\n50 │         string memory lat = coordinate(int16(uint16(cell >> 16)));\n   │                                        ━━━━━━━━━━━━━━━━━━━━━━━━━\n   │\n   ├ note: consider disabling this lint if you're certain the cast is safe\n   │       \n   │       // casting to 'int16' is safe because [explain why]\n   │       // forge-lint: disable-next-line(unsafe-typecast)\n   │       \n   │       \n   ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n   ╭▸ src/WeatherQuestion.sol:50:46\n   │\n50 │         string memory lat = coordinate(int16(uint16(cell >> 16)));\n   │                                              ━━━━━━━━━━━━━━━━━━\n   │\n   ├ note: consider disabling this lint if you're certain the cast is safe\n   │       \n   │       // casting to 'uint16' is safe because [explain why]\n   │       // forge-lint: disable-next-line(unsafe-typecast)\n   │       \n   │       \n   ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n   ╭▸ src/WeatherQuestion.sol:51:40\n   │\n51 │         string memory lon = coordinate(int16(uint16(cell)));\n   │                                        ━━━━━━━━━━━━━━━━━━━\n   │\n   ├ note: consider disabling this lint if you're certain the cast is safe\n   │       \n   │       // casting to 'int16' is safe because [explain why]\n   │       // forge-lint: disable-next-line(unsafe-typecast)\n   │       \n   │       \n   ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n   ╭▸ src/WeatherQuestion.sol:51:46\n   │\n51 │         string memory lon = coordinate(int16(uint16(cell)));\n   │                                              ━━━━━━━━━━━━\n   │\n   ├ note: consider disabling this lint if you're certain the cast is safe\n   │       \n   │       // casting to 'uint16' is safe because [explain why]\n   │       // forge-lint: disable-next-line(unsafe-typecast)\n   │       \n   │       \n   ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/PlantOrganism.sol:167:16\n    │\n167 │         return uint32(block.timestamp / 1 days);\n    │                ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint32' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[missing-zero-check]: address parameter is used in a state write or value transfer without a zero-address check\n    ╭▸ src/PlantOrganism.sol:170:19\n    │\n170 │     function bind(address hook_) external nonReentrant {\n    │                   ━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/missing-zero-check\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/PlantOrganism.sol:206:25\n    │\n206 │         require(held <= uint256(type(int256).max) && reserved <= uint256(type(int256).max));\n    │                         ━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/PlantOrganism.sol:206:66\n    │\n206 │         require(held <= uint256(type(int256).max) && reserved <= uint256(type(int256).max));\n    │                                                                  ━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/PlantOrganism.sol:207:16\n    │\n207 │         return int256(held) - int256(reserved);\n    │                ━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'int256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/PlantOrganism.sol:207:31\n    │\n207 │         return int256(held) - int256(reserved);\n    │                               ━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'int256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/PlantOrganism.sol:212:24\n    │\n212 │         return p > 0 ? uint256(p) : 0;\n    │                        ━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/PlantOrganism.sol:525:13\n    │\n525 │             emit Checkpointed(holder, cell, credit);\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/PlantOrganism.sol:280:13\n    │\n280 │             emit Challenged(cell);\n    │             ━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/PlantOrganism.sol:250:9\n    │\n250 │         emit Parked(msg.sender, cell, amount);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/PlantOrganism.sol:268:9\n    │\n268 │         emit Unparked(msg.sender, cell, amount);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[reentrancy-no-eth]: external call can be reentered before `pending` is updated\n    ╭▸ src/PlantOrganism.sol:303:14\n    │\n303 │           id = intake.request(\n    │ ┏━━━━━━━━━━━━━━┛\n304 │ ┃             action,\n305 │ ┃             requestBody(location, day),\n306 │ ┃             IIntake.Callback(address(this), this.onOracleResult.selector),\n307 │ ┃             address(IMD),\n308 │ ┃             price\n309 │ ┃         );\n    │ ┗━━━━━━━━━┛\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-no-eth\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/PlantOrganism.sol:290:13\n    │\n290 │         if (block.timestamp < retryAt) revert RetryLater();\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/PlantOrganism.sol:299:13\n    │\n299 │             emit Advanced(msg.sender, advance);\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/PlantOrganism.sol:314:9\n    │\n314 │         emit Asked(id, day, location, challenger, msg.sender, price);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[incorrect-strict-equality]: dangerous strict equality check on an externally-influenced value\n    ╭▸ src/PlantOrganism.sol:311:13\n    │\n311 │         if (IMD.balanceOf(address(this)) != beforeBalance - price) revert NonExactTransfer();\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/incorrect-strict-equality\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/PlantOrganism.sol:313:54\n    │\n313 │             Pending(id, day, challenger, msg.sender, uint64(block.timestamp), address(intake), true, false, bytes32(0));\n    │                                                      ━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint64' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/PlantOrganism.sol:331:20\n    │\n331 │                 && block.timestamp < signerAcceptedUntil[recovered]\n    │                    ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/PlantOrganism.sol:352:9\n    │\n352 │         emit Received(requestId, a.requestId, word);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/PlantOrganism.sol:342:13\n    │\n342 │         if (uint32(w >> 96) != p.day) revert WrongDay();\n    │             ━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint32' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/PlantOrganism.sol:361:17\n    │\n361 │             if (block.timestamp < uint256(pending.askedAt) + ORACLE_TIMEOUT) revert NotTimedOut();\n    │                 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[uninitialized-local]: local variable is read before being initialized\n    ╭▸ src/PlantOrganism.sol:433:27\n    │\n433 │         emit Settled(day, word, sips, backing, water, location);\n    │                           ━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/uninitialized-local\n\nwarning[uninitialized-local]: local variable is read before being initialized\n    ╭▸ src/PlantOrganism.sol:433:33\n    │\n433 │         emit Settled(day, word, sips, backing, water, location);\n    │                                 ━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/uninitialized-local\n\nwarning[uninitialized-local]: local variable is read before being initialized\n    ╭▸ src/PlantOrganism.sol:416:30\n    │\n416 │         _distribute(oldCell, pool);\n    │                              ━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/uninitialized-local\n\nwarning[uninitialized-local]: local variable is read before being initialized\n    ╭▸ src/PlantOrganism.sol:426:13\n    │\n426 │         if (caller != address(0)) {\n    │             ━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/uninitialized-local\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/PlantOrganism.sol:389:13\n    │\n389 │             emit EmptyCatchUp(day, end);\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/PlantOrganism.sol:407:17\n    │\n407 │                 emit Incomplete(day, count);\n    │                 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/PlantOrganism.sol:371:9\n    │\n371 │         emit Cleared(id, day, retryAt);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/PlantOrganism.sol:503:9\n    │\n503 │         emit Rewarded(cell, pool, reserved);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/PlantOrganism.sol:473:13\n    │\n473 │             emit Moved(current, candidate);\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/PlantOrganism.sol:421:13\n    │\n421 │             emit Moved(0, location);\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/PlantOrganism.sol:430:13\n    │\n430 │             emit Bounty(caller, bounty);\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/PlantOrganism.sol:565:13\n    │\n565 │             emit Paid(holder, credit, advance);\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/PlantOrganism.sol:571:13\n    │\n571 │             emit PaymentDeferred(holder, credit + advance);\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/PlantOrganism.sol:433:9\n    │\n433 │         emit Settled(day, word, sips, backing, water, location);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/PlantOrganism.sol:387:61\n    │\n387 │             uint32 end = hook == address(0) ? today() - 1 : uint32(Math.min(today() - 1, bindDay - 1));\n    │                                                             ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint32' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/PlantOrganism.sol:450:17\n    │\n450 │         water = uint8(wet);\n    │                 ━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint8' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[uninitialized-local]: local variable is read before being initialized\n    ╭▸ src/PlantOrganism.sol:559:22\n    │\n559 │         if (credit + advance == 0) return;\n    │                      ━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/uninitialized-local\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/PlantOrganism.sol:588:9\n    │\n588 │         emit Redeemed(msg.sender, amount, payout, dead);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/PlantOrganism.sol:603:13\n    │\n603 │             emit Died(today(), merged);\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/PlantOrganism.sol:605:13\n    │\n605 │             emit PotMerged(merged);\n    │             ━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/OracleAttestation.sol:182:9\n    │\n182 │         emit OracleSignerSet(signer);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/PlantOrganism.sol:637:9\n    │\n637 │         emit Rotated(oldSigner, newSigner, newIntake, newAction, nonce);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[incorrect-strict-equality]: dangerous strict equality check on an externally-influenced value\n    ╭▸ src/PlantOrganism.sol:643:13\n    │\n643 │         if (token.balanceOf(address(this)) != beforeBalance + amount) revert NonExactTransfer();\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/incorrect-strict-equality\n\n","passed":true},{"durationMs":1648,"exitCode":0,"name":"test","output":"No files changed, compilation skipped\n\nRan 3 tests for test/WeatherQuestion.t.sol:WeatherQuestionTest\n[PASS] test_coordinatesAtEdgesAndNegativeZeroFraction() (gas: 40309)\n[PASS] test_exactLisbonBodyAndKnownDate() (gas: 15004)\n[PASS] test_exactNegativeLatitudeAndLongitudeBody() (gas: 15401)\nSuite result: ok. 3 passed; 0 failed; 0 skipped; finished in 533.11µs (375.68µs CPU time)\n\nRan 4 tests for test/OracleConsumerConformance.t.sol:OracleConsumerConformanceTest\n[PASS] test_acceptsOriginalProtocolSignatureInCanonicalVerifier() (gas: 117518)\n[PASS] test_bytes32WeatherFixtureAcceptedUnderStipend() (gas: 1711910)\n[PASS] test_callbackSelectorIsCanonical() (gas: 3543)\n[PASS] test_digestMatchesTheProtocol() (gas: 13139)\nSuite result: ok. 4 passed; 0 failed; 0 skipped; finished in 2.10ms (1.01ms CPU time)\n\nRan 11 tests for test/PlantOrganism.t.sol:PlantLifecycleTest\n[PASS] test_bindIsOnceAndOnlyDeployerWithMatchingHook() (gas: 325519)\n[PASS] test_birthFallbackAfterExactlyThreeEmptyDays() (gas: 314602)\n[PASS] test_birthMovesAtFivePercentWithoutMinimumStay() (gas: 660654)\n[PASS] test_cellBoundsAndZeroRejected() (gas: 609707)\n[PASS] test_challengerOnlyStrictlyLargerAndCanBeRepaired() (gas: 908869)\n[PASS] test_deathIsFinalAtThirtyDaysAndKeepsExits() (gas: 2607311)\n[PASS] test_deathUsesBindDayAfterLongUnboundPeriod() (gas: 285365)\n[PASS] test_moveUsesPendingCandidateAndRemainingCommittedBalances() (gas: 3190015)\n[PASS] test_noEarlyOrOutOfOrderSettleAndHeartbeat() (gas: 153139)\n[PASS] test_preBindDaysSkipInOneCallWithoutRead() (gas: 645106)\n[PASS] test_unboundCatchUpIsBoundedAndDoesNotRunBirth() (gas: 157524)\nSuite result: ok. 11 passed; 0 failed; 0 skipped; finished in 5.87ms (4.03ms CPU time)\n\nRan 7 tests for test/PlantAssumptions.t.sol:PlantAssumptionsTest\n[PASS] test_explicitZeroCellQuestionRemainsInvalid() (gas: 25324)\n[PASS] test_oldRotationSignatureAccepted() (gas: 887620)\n[PASS] test_oldSourceWindowBacklogIsAlreadyDead() (gas: 401886)\n[PASS] test_preBindDonationsHaveNoExit() (gas: 196001)\n[PASS] test_redeemedSupplyStillCountsInThreshold() (gas: 3095372)\n[PASS] test_rotatedIntakeCanPriceAbovePotAndPullAdvance() (gas: 2308589)\n[PASS] test_timeoutsDoNotCountAndEventuallyDie() (gas: 8459564)\nSuite result: ok. 7 passed; 0 failed; 0 skipped; finished in 6.04ms (7.41ms CPU time)\n\nRan 4 tests for test/PlantOrganism.t.sol:PlantRotationTest\n[PASS] test_oldSignerExpiresAtThirtyDaysWhileNewSignerKeepsPlantAlive() (gas: 19607309)\n[PASS] test_onlyCurrentSignerCanRotateNonceCannotReplay() (gas: 1150507)\n[PASS] test_pendingIntakeAndOldSignerWorkAfterRotation() (gas: 3791569)\n[PASS] test_rotationDomainBindsAllFieldsAndContract() (gas: 974532)\nSuite result: ok. 4 passed; 0 failed; 0 skipped; finished in 14.33ms (16.62ms CPU time)\n\nRan 4 tests for test/GasAndDeployment.t.sol:GasAndDeploymentTest\n[PASS] test_coldCallbackAndMaxHoursMoveSettleGas() (gas: 2897618)\nLogs:\n  cold callback gas: 94192\n  cold settle, 24 sips and move: 264013\n\n[PASS] test_coldMaxHoursWithRepairedCandidateSettleGas() (gas: 3013335)\nLogs:\n  cold settle, 24 sips and repaired candidate: 272198\n\n[PASS] test_requestedStaticConstructorAndForbiddenOpcodes() (gas: 8026701)\n[PASS] test_thirdIncompleteMoveAndAdvancePaymentGas() (gas: 3924579)\nLogs:\n  cold third incomplete with move and advance repayment: 202030\n\nSuite result: ok. 4 passed; 0 failed; 0 skipped; finished in 25.21ms (44.04ms CPU time)\n\nRan 7 tests for test/PlantVotingRevision.t.sol:PlantVotingRevisionTest\n[PASS] test_atomicCurrentCellTopUpCannotVetoCommittedCandidate() (gas: 2367527)\n[PASS] test_atomicDestinationTopUpCannotMoveOrCaptureRewards() (gas: 3846488)\n[PASS] test_emptyCapturedCandidateFallsBackToRepairedLiveCandidate() (gas: 2598482)\n[PASS] test_fallbackCannotUseNewPostHeartbeatStake() (gas: 3095191)\n[PASS] test_questionIsEmptyBeforeBindAndDuringBirth() (gas: 411374)\n[PASS] test_thirdIncompleteAlsoUsesRepairedCandidate() (gas: 4181498)\n[PASS] test_withdrawalRemovesPowerAndReaskCannotRestoreIt() (gas: 3678603)\nSuite result: ok. 7 passed; 0 failed; 0 skipped; finished in 44.83ms (14.03ms CPU time)\n\nRan 12 tests for test/PlantOrganism.t.sol:PlantRewardsTest\n[PASS] testFuzz_floorConservationAndBurnCustody(uint96,uint96,uint24) (runs: 256, μ: 2333818, ~: 2348070)\nLogs:\n  Bound result 23880\n\n[PASS] testFuzz_hourlyAccountingMatchesIndependentModel(uint24,uint24) (runs: 256, μ: 1798855, ~: 1803699)\n[PASS] test_departuresDropOutAndReparkingCannotCaptureDay() (gas: 4107169)\n[PASS] test_finalRedemptionDoesNotDivideByZeroOrReleaseBurnedPlant() (gas: 2449577)\n[PASS] test_gardenerDustRoundsToBackingAndAccountsCloseExactly() (gas: 5259461)\n[PASS] test_hourZeroFirstRainCapAndSunRequiresWater() (gas: 4841001)\n[PASS] test_noGardenersSendsEntireSipToBacking() (gas: 1548055)\n[PASS] test_nonExactTokenTransfersRejected() (gas: 274277)\n[PASS] test_oldCellKeepsItsRewardsAfterMoveAndLazyActivation() (gas: 5794681)\n[PASS] test_parkSettleClaimProRataAndMidDayJoinsWaitOneSettle() (gas: 3184396)\n[PASS] test_redeemRetainsPlantAndTenPercentAndFloorMonotonic() (gas: 2200068)\n[PASS] test_reentrancyFromTokensAndIntakeBlocked() (gas: 2288899)\nSuite result: ok. 12 passed; 0 failed; 0 skipped; finished in 53.93ms (124.10ms CPU time)\n\nRan 12 tests for test/PlantOrganism.t.sol:PlantOracleTest\n[PASS] test_blockedKeeperDoesNotBlockSettleAndCanClaimLater() (gas: 2081432)\n[PASS] test_callbackSenderIdAndReplay() (gas: 2209347)\n[PASS] test_callbackStoresOnlyAndFitsStipend() (gas: 1790328)\nLogs:\n  callback gas including mock encoding: 94192\n  settle gas: 301570\n\n[PASS] test_clearIncompleteUsesSameCounterAndCannotDiscardComplete() (gas: 2192207)\n[PASS] test_exactApprovalAndIntakeBody() (gas: 1419428)\n[PASS] test_feeAdvanceTracksDeficitThenRepaidAtSettle() (gas: 2142778)\n[PASS] test_incompleteHasNoBiologicalChangesThenThirdReads() (gas: 3416039)\n[PASS] test_intakeMustPullExactPrice() (gas: 1260208)\n[PASS] test_rejectsExpiredWrongDomainSignerTypeDayChainAndPanel() (gas: 2520660)\n[PASS] test_rejectsPreAskFutureTamperedAndMalformedAttestations() (gas: 2262051)\n[PASS] test_timeoutClearsAndWaitsSixHoursToReaskSameDay() (gas: 2210846)\n[PASS] test_unfundedAdvanceDoesNotSpendBackingOrBlockSettle() (gas: 2748338)\nSuite result: ok. 12 passed; 0 failed; 0 skipped; finished in 53.97ms (21.15ms CPU time)\n\nRan 8 tests for test/PlantVotingCommitment.t.sol:PlantVotingCommitmentTest\n[PASS] testFuzz_withdrawalAndRedepositCannotIncreasePower(uint96,uint96) (runs: 256, μ: 2575927, ~: 2576513)\nLogs:\n  Bound result 6594\n  Bound result 5798\n\n[PASS] test_borrowingAtBothHeartbeatAndSettleCannotCaptureGardenersPool() (gas: 4607864)\n[PASS] test_borrowingAtBothHeartbeatAndSettleCannotMove() (gas: 3607963)\n[PASS] test_borrowingAtBothHeartbeatAndSettleCannotVeto() (gas: 2518219)\n[PASS] test_loanAcrossPreviousSettleLosesPowerOnWithdrawal() (gas: 3138794)\n[PASS] test_rearmedDecoyCannotHideHoldersCandidate() (gas: 2717594)\n[PASS] test_thresholdUsesCommittedStakeAndNewDepositsMatureAtSettle() (gas: 3542686)\n[PASS] test_timeoutAndReaskCannotRestoreWithdrawnPower() (gas: 2783428)\nSuite result: ok. 8 passed; 0 failed; 0 skipped; finished in 66.28ms (81.10ms CPU time)\n\nRan 1 test for test/PlantInvariant.t.sol:PlantInvariantTest\n[PASS] invariant_conservationAndPermanentBurnCustody() (runs: 128, calls: 8192, reverts: 0)\n\n╭--------------+---------------+-------+---------+----------╮\n| Contract     | Selector      | Calls | Reverts | Discards |\n+===========================================================+\n| PlantHandler | challenge     | 1052  | 0       | 0        |\n|--------------+---------------+-------+---------+----------|\n| PlantHandler | checkpointAll | 975   | 0       | 0        |\n|--------------+---------------+-------+---------+----------|\n| PlantHandler | claim         | 1007  | 0       | 0        |\n|--------------+---------------+-------+---------+----------|\n| PlantHandler | fund          | 1014  | 0       | 0        |\n|--------------+---------------+-------+---------+----------|\n| PlantHandler | park          | 1018  | 0       | 0        |\n|--------------+---------------+-------+---------+----------|\n| PlantHandler | redeem        | 1043  | 0       | 0        |\n|--------------+---------------+-------+---------+----------|\n| PlantHandler | unpark        | 1020  | 0       | 0        |\n|--------------+---------------+-------+---------+----------|\n| PlantHandler | weather       | 1063  | 0       | 0        |\n╰--------------+---------------+-------+---------+----------╯\n\nLogs:\n  Bound result 5681791129045538706\n  Bound result 2177\n  Bound result 2191165144\n  Bound result 16533\n  Bound result 2388\n  Bound result 2585\n  Bound result 9716\n  Bound result 937339850076086215098\n  Bound result 8258\n  Bound result 1359182280\n  Bound result 5130\n  Bound result 217675612662455767\n  Bound result 937557525688748687397\n  Bound result 4420\n  Bound result 3802\n  Bound result 55939973348894314216\n  Bound result 2462\n  Bound result 1816\n\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 1.56s (1.55s CPU time)\n\nRan 11 test suites in 1.56s (1.83s CPU time): 73 tests passed, 0 failed, 0 skipped (73 total tests)\n","passed":true},{"durationMs":50,"exitCode":0,"name":"source-index","output":"{\"v\":1,\"entryPoints\":[\"PlantOrganism.bind(address)\",\"PlantOrganism.challenge(uint32)\",\"PlantOrganism.checkpoint(uint32,address)\",\"PlantOrganism.claim()\",\"PlantOrganism.claim(uint32[])\",\"PlantOrganism.clearPending()\",\"PlantOrganism.die()\",\"PlantOrganism.heartbeat()\",\"PlantOrganism.onOracleResult(bytes32,(bytes32,uint256,bytes32,uint8,bytes,uint256,uint64,uint64,bytes32,bytes32,uint16,uint16,uint16,uint64,uint64),bytes)\",\"PlantOrganism.park(uint32,uint256)\",\"PlantOrganism.redeem(uint256)\",\"PlantOrganism.rotate(address,address,bytes32,uint256,bytes)\",\"PlantOrganism.settle()\",\"PlantOrganism.settle(uint32)\",\"PlantOrganism.unpark(uint32,uint256)\"],\"files\":{\".gitignore\":3,\"DEPENDENCIES.md\":7,\"README.md\":30,\"foundry.toml\":19,\"launch.json\":17,\"src/OracleAttestation.sol\":221,\"src/PlantOrganism.sol\":645,\"src/WeatherQuestion.sol\":83,\"src/interfaces/IIntake.sol\":14,\"test/GasAndDeployment.t.sol\":113,\"test/OracleConsumerConformance.t.sol\":126,\"test/PlantAssumptions.t.sol\":86,\"test/PlantInvariant.t.sol\":166,\"test/PlantOrganism.t.sol\":919,\"test/PlantVotingCommitment.t.sol\":196,\"test/PlantVotingRevision.t.sol\":115,\"test/WeatherQuestion.t.sol\":36,\"test/fixtures/weather.json\":11,\"test/mocks/Mocks.sol\":137},\"excluded\":[\"lib/\",\"node_modules/\"],\"truncated\":false}","passed":true},{"durationMs":4244,"exitCode":0,"name":"slither","output":"[high/medium] reentrancy-balance at src/PlantOrganism.sol:284: Reentrancy in PlantOrganism.heartbeat() (src/PlantOrganism.sol#284-315):\n[medium/medium] divide-before-multiply at src/WeatherQuestion.sol:33: WeatherQuestion.date(uint32) (src/WeatherQuestion.sol#33-46) performs a multiplication on the result of a division:\n[medium/medium] divide-before-multiply at src/WeatherQuestion.sol:33: WeatherQuestion.date(uint32) (src/WeatherQuestion.sol#33-46) performs a multiplication on the result of a division:\n[medium/medium] divide-before-multiply at src/WeatherQuestion.sol:33: WeatherQuestion.date(uint32) (src/WeatherQuestion.sol#33-46) performs a multiplication on the result of a division:\n[medium/high] incorrect-equality at src/PlantOrganism.sol:550: PlantOrganism._pay(address) (src/PlantOrganism.sol#550-573) uses a dangerous strict equality:\n[medium/medium] reentrancy-no-eth at src/PlantOrganism.sol:284: Reentrancy in PlantOrganism.heartbeat() (src/PlantOrganism.sol#284-315):\n[medium/medium] uninitialized-local at src/PlantOrganism.sol:395: PlantOrganism._settle().caller (src/PlantOrganism.sol#395) is a local variable never initialized\n[medium/medium] uninitialized-local at src/PlantOrganism.sol:552: PlantOrganism._pay(address).advance (src/PlantOrganism.sol#552) is a local variable never initialized\n[medium/medium] uninitialized-local at src/PlantOrganism.sol:394: PlantOrganism._settle().word (src/PlantOrganism.sol#394) is a local variable never initialized\n[medium/medium] uninitialized-local at src/PlantOrganism.sol:397: PlantOrganism._settle().sips (src/PlantOrganism.sol#397) is a local variable never initialized\n[medium/medium] uninitialized-local at src/PlantOrganism.sol:396: PlantOrganism._settle().pool (src/PlantOrganism.sol#396) is a local variable never initialized\n[medium/medium] unused-return at src/PlantOrganism.sol:619: PlantOrganism.rotate(address,address,bytes32,uint256,bytes) (src/PlantOrganism.sol#619-638) ignores return value by (recovered,err,None) = ECDSA.tryRecoverCalldata(rotationDigest(newSigner,newIntake,newAction,nonce),sig) (src/PlantOrganism.sol#628-629)\n[medium/medium] unused-return at src/PlantOrganism.sol:317: PlantOrganism.onOracleResult(bytes32,OracleAttestation.Attestation,bytes) (src/PlantOrganism.sol#317-353) ignores return value by (recovered,err,None) = ECDSA.tryRecoverCalldata(attestationDigest(a),sig) (src/PlantOrganism.sol#328)\n[low/medium] timestamp at src/PlantOrganism.sol:317: PlantOrganism.onOracleResult(bytes32,OracleAttestation.Attestation,bytes) (src/PlantOrganism.sol#317-353) uses timestamp for comparisons\n[low/medium] timestamp at src/PlantOrganism.sol:383: PlantOrganism._settle() (src/PlantOrganism.sol#383-434) uses timestamp for comparisons\n[low/medium] timestamp at src/PlantOrganism.sol:284: PlantOrganism.heartbeat() (src/PlantOrganism.sol#284-315) uses timestamp for comparisons\n[low/medium] timestamp at src/PlantOrganism.sol:185: PlantOrganism.isDead() (src/PlantOrganism.sol#185-187) uses timestamp for comparisons\n[low/medium] timestamp at src/PlantOrganism.sol:355: PlantOrganism.clearPending() (src/PlantOrganism.sol#355-364) uses timestamp for comparisons\n[low/medium] timestamp at src/OracleAttestation.sol:157: OracleAttestationConsumer._verifyAttestation(OracleAttestation.Attestation,bytes) (src/OracleAttestation.sol#157-165) uses timestamp for comparisons","passed":true},{"durationMs":562,"exitCode":0,"name":"aderyn","output":"[high] reentrancy-state-change at src/PlantOrganism.sol:173: Reentrancy: State change after external call (8 places)\n[low] costly-loop at src/PlantOrganism.sol:544: Costly operations inside loop\n[low] empty-require-revert at src/PlantOrganism.sol:206: Empty `require()` / `revert()` Statement\n[low] internal-function-used-once at src/OracleAttestation.sol:195: Internal Function Used Only Once (4 places)\n[low] literal-instead-of-constant at src/PlantOrganism.sol:337: Literal Instead of Constant (39 places)\n[low] local-variable-shadowing at src/PlantOrganism.sol:62: Local Variable Shadows State Variable (4 places)\n[low] uninitialized-local-variable at src/PlantOrganism.sol:439: Uninitialized Local Variable (2 places)","passed":true}],"detail":"all checks passed","evaluation":"checks","profile":"foundry","status":"accepted","submissionHash":"0f997f64b3be9491f23a80f78657d068475f238e7dc14b102c8457924fef561b","verifiedTreeHash":"3dea5fb0b3835cd6c9bca08a74b03254369787fa","verifierVersion":"0.1.0+ad90ce4c"},{"checks":[{"durationMs":28188,"exitCode":0,"name":"build","output":"Compiling 53 files with Solc 0.8.26\nSolc 0.8.26 finished in 28.04s\nCompiler run successful!\nwarning[unsafe-typecast]: typecast can truncate values\n   ╭▸ src/WeatherQuestion.sol:12:21\n   │\n12 │         int16 lat = int16(uint16(cell >> 16));\n   │                     ━━━━━━━━━━━━━━━━━━━━━━━━━\n   │\n   ├ note: consider disabling this lint if you're certain the cast is safe\n   │       \n   │       // casting to 'int16' is safe because [explain why]\n   │       // forge-lint: disable-next-line(unsafe-typecast)\n   │       \n   │       \n   ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n   ╭▸ src/WeatherQuestion.sol:12:27\n   │\n12 │         int16 lat = int16(uint16(cell >> 16));\n   │                           ━━━━━━━━━━━━━━━━━━\n   │\n   ├ note: consider disabling this lint if you're certain the cast is safe\n   │       \n   │       // casting to 'uint16' is safe because [explain why]\n   │       // forge-lint: disable-next-line(unsafe-typecast)\n   │       \n   │       \n   ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n   ╭▸ src/WeatherQuestion.sol:13:21\n   │\n13 │         int16 lon = int16(uint16(cell));\n   │                     ━━━━━━━━━━━━━━━━━━━\n   │\n   ├ note: consider disabling this lint if you're certain the cast is safe\n   │       \n   │       // casting to 'int16' is safe because [explain why]\n   │       // forge-lint: disable-next-line(unsafe-typecast)\n   │       \n   │       \n   ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n   ╭▸ src/WeatherQuestion.sol:13:27\n   │\n13 │         int16 lon = int16(uint16(cell));\n   │                           ━━━━━━━━━━━━\n   │\n   ├ note: consider disabling this lint if you're certain the cast is safe\n   │       \n   │       // casting to 'uint16' is safe because [explain why]\n   │       // forge-lint: disable-next-line(unsafe-typecast)\n   │       \n   │       \n   ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n   ╭▸ src/WeatherQuestion.sol:19:29\n   │\n19 │         uint256 magnitude = uint256(milli < 0 ? -milli : milli);\n   │                             ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n   │\n   ├ note: consider disabling this lint if you're certain the cast is safe\n   │       \n   │       // casting to 'uint256' is safe because [explain why]\n   │       // forge-lint: disable-next-line(unsafe-typecast)\n   │       \n   │       \n   ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n   ╭▸ src/WeatherQuestion.sol:26:29\n   │\n26 │             b[--i] = bytes1(uint8(48 + n % 10));\n   │                             ━━━━━━━━━━━━━━━━━━\n   │\n   ├ note: consider disabling this lint if you're certain the cast is safe\n   │       \n   │       // casting to 'uint8' is safe because [explain why]\n   │       // forge-lint: disable-next-line(unsafe-typecast)\n   │       \n   │       \n   ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[divide-before-multiply]: division before multiplication may lose precision\n   ╭▸ src/WeatherQuestion.sol:39:27\n   │\n39 │         uint256 y = yoe + era * 400;\n   │                           ━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/divide-before-multiply\n\nwarning[divide-before-multiply]: division before multiplication may lose precision\n   ╭▸ src/WeatherQuestion.sol:40:30\n   │\n40 │         uint256 doy = doe - (365 * yoe + yoe / 4 - yoe / 100);\n   │                              ━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/divide-before-multiply\n\nwarning[divide-before-multiply]: division before multiplication may lose precision\n   ╭▸ src/WeatherQuestion.sol:42:28\n   │\n42 │         uint256 d = doy - (153 * mp + 2) / 5 + 1;\n   │                            ━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/divide-before-multiply\n\nwarning[unsafe-typecast]: typecast can truncate values\n   ╭▸ src/WeatherQuestion.sol:50:40\n   │\n50 │         string memory lat = coordinate(int16(uint16(cell >> 16)));\n   │                                        ━━━━━━━━━━━━━━━━━━━━━━━━━\n   │\n   ├ note: consider disabling this lint if you're certain the cast is safe\n   │       \n   │       // casting to 'int16' is safe because [explain why]\n   │       // forge-lint: disable-next-line(unsafe-typecast)\n   │       \n   │       \n   ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n   ╭▸ src/WeatherQuestion.sol:50:46\n   │\n50 │         string memory lat = coordinate(int16(uint16(cell >> 16)));\n   │                                              ━━━━━━━━━━━━━━━━━━\n   │\n   ├ note: consider disabling this lint if you're certain the cast is safe\n   │       \n   │       // casting to 'uint16' is safe because [explain why]\n   │       // forge-lint: disable-next-line(unsafe-typecast)\n   │       \n   │       \n   ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n   ╭▸ src/WeatherQuestion.sol:51:40\n   │\n51 │         string memory lon = coordinate(int16(uint16(cell)));\n   │                                        ━━━━━━━━━━━━━━━━━━━\n   │\n   ├ note: consider disabling this lint if you're certain the cast is safe\n   │       \n   │       // casting to 'int16' is safe because [explain why]\n   │       // forge-lint: disable-next-line(unsafe-typecast)\n   │       \n   │       \n   ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n   ╭▸ src/WeatherQuestion.sol:51:46\n   │\n51 │         string memory lon = coordinate(int16(uint16(cell)));\n   │                                              ━━━━━━━━━━━━\n   │\n   ├ note: consider disabling this lint if you're certain the cast is safe\n   │       \n   │       // casting to 'uint16' is safe because [explain why]\n   │       // forge-lint: disable-next-line(unsafe-typecast)\n   │       \n   │       \n   ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/PlantOrganism.sol:176:16\n    │\n176 │         return uint32(block.timestamp / 1 days);\n    │                ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint32' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[missing-zero-check]: address parameter is used in a state write or value transfer without a zero-address check\n    ╭▸ src/PlantOrganism.sol:179:19\n    │\n179 │     function bind(address hook_) external nonReentrant {\n    │                   ━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/missing-zero-check\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/PlantOrganism.sol:215:25\n    │\n215 │         require(held <= uint256(type(int256).max) && reserved <= uint256(type(int256).max));\n    │                         ━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/PlantOrganism.sol:215:66\n    │\n215 │         require(held <= uint256(type(int256).max) && reserved <= uint256(type(int256).max));\n    │                                                                  ━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/PlantOrganism.sol:216:16\n    │\n216 │         return int256(held) - int256(reserved);\n    │                ━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'int256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/PlantOrganism.sol:216:31\n    │\n216 │         return int256(held) - int256(reserved);\n    │                               ━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'int256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/PlantOrganism.sol:221:24\n    │\n221 │         return p > 0 ? uint256(p) : 0;\n    │                        ━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/PlantOrganism.sol:543:13\n    │\n543 │             emit Checkpointed(holder, cell, credit);\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/PlantOrganism.sol:291:13\n    │\n291 │             emit Challenged(cell);\n    │             ━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/PlantOrganism.sol:260:9\n    │\n260 │         emit Parked(msg.sender, cell, amount);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/PlantOrganism.sol:279:9\n    │\n279 │         emit Unparked(msg.sender, cell, amount);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[reentrancy-no-eth]: external call can be reentered before `pending` is updated\n    ╭▸ src/PlantOrganism.sol:314:14\n    │\n314 │           id = intake.request(\n    │ ┏━━━━━━━━━━━━━━┛\n315 │ ┃             action,\n316 │ ┃             requestBody(location, day),\n317 │ ┃             IIntake.Callback(address(this), this.onOracleResult.selector),\n318 │ ┃             address(IMD),\n319 │ ┃             price\n320 │ ┃         );\n    │ ┗━━━━━━━━━┛\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-no-eth\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/PlantOrganism.sol:301:13\n    │\n301 │         if (block.timestamp < retryAt) revert RetryLater();\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/PlantOrganism.sol:310:13\n    │\n310 │             emit Advanced(msg.sender, advance);\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/PlantOrganism.sol:328:9\n    │\n328 │         emit Asked(id, day, location, challenger, msg.sender, price);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[incorrect-strict-equality]: dangerous strict equality check on an externally-influenced value\n    ╭▸ src/PlantOrganism.sol:322:13\n    │\n322 │         if (IMD.balanceOf(address(this)) != beforeBalance - price) revert NonExactTransfer();\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/incorrect-strict-equality\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/PlantOrganism.sol:324:54\n    │\n324 │             Pending(id, day, challenger, msg.sender, uint64(block.timestamp), address(intake), true, false, bytes32(0));\n    │                                                      ━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint64' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/PlantOrganism.sol:345:20\n    │\n345 │                 && block.timestamp < signerAcceptedUntil[recovered]\n    │                    ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/PlantOrganism.sol:366:9\n    │\n366 │         emit Received(requestId, a.requestId, word);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/PlantOrganism.sol:356:13\n    │\n356 │         if (uint32(w >> 96) != p.day) revert WrongDay();\n    │             ━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint32' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/PlantOrganism.sol:375:17\n    │\n375 │             if (block.timestamp < uint256(pending.askedAt) + ORACLE_TIMEOUT) revert NotTimedOut();\n    │                 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[uninitialized-local]: local variable is read before being initialized\n    ╭▸ src/PlantOrganism.sol:440:13\n    │\n440 │         if (caller != address(0)) {\n    │             ━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/uninitialized-local\n\nwarning[uninitialized-local]: local variable is read before being initialized\n    ╭▸ src/PlantOrganism.sol:447:33\n    │\n447 │         emit Settled(day, word, sips, backing, water, location);\n    │                                 ━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/uninitialized-local\n\nwarning[uninitialized-local]: local variable is read before being initialized\n    ╭▸ src/PlantOrganism.sol:430:30\n    │\n430 │         _distribute(oldCell, pool);\n    │                              ━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/uninitialized-local\n\nwarning[uninitialized-local]: local variable is read before being initialized\n    ╭▸ src/PlantOrganism.sol:447:27\n    │\n447 │         emit Settled(day, word, sips, backing, water, location);\n    │                           ━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/uninitialized-local\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/PlantOrganism.sol:403:13\n    │\n403 │             emit EmptyCatchUp(day, end);\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/PlantOrganism.sol:421:17\n    │\n421 │                 emit Incomplete(day, count);\n    │                 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/PlantOrganism.sol:385:9\n    │\n385 │         emit Cleared(id, day, retryAt);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/PlantOrganism.sol:521:9\n    │\n521 │         emit Rewarded(cell, pool, reserved);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/PlantOrganism.sol:491:13\n    │\n491 │             emit Moved(current, candidate);\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/PlantOrganism.sol:435:13\n    │\n435 │             emit Moved(0, location);\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/PlantOrganism.sol:444:13\n    │\n444 │             emit Bounty(caller, bounty);\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/PlantOrganism.sol:583:13\n    │\n583 │             emit Paid(holder, credit, advance);\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/PlantOrganism.sol:589:13\n    │\n589 │             emit PaymentDeferred(holder, credit + advance);\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/PlantOrganism.sol:447:9\n    │\n447 │         emit Settled(day, word, sips, backing, water, location);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/PlantOrganism.sol:401:61\n    │\n401 │             uint32 end = hook == address(0) ? today() - 1 : uint32(Math.min(today() - 1, bindDay - 1));\n    │                                                             ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint32' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/PlantOrganism.sol:464:17\n    │\n464 │         water = uint8(wet);\n    │                 ━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint8' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[uninitialized-local]: local variable is read before being initialized\n    ╭▸ src/PlantOrganism.sol:577:22\n    │\n577 │         if (credit + advance == 0) return;\n    │                      ━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/uninitialized-local\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/PlantOrganism.sol:606:9\n    │\n606 │         emit Redeemed(msg.sender, amount, payout, dead);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/PlantOrganism.sol:621:13\n    │\n621 │             emit Died(today(), merged);\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/PlantOrganism.sol:623:13\n    │\n623 │             emit PotMerged(merged);\n    │             ━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/OracleAttestation.sol:182:9\n    │\n182 │         emit OracleSignerSet(signer);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/PlantOrganism.sol:655:9\n    │\n655 │         emit Rotated(oldSigner, newSigner, newIntake, newAction, nonce);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[incorrect-strict-equality]: dangerous strict equality check on an externally-influenced value\n    ╭▸ src/PlantOrganism.sol:661:13\n    │\n661 │         if (token.balanceOf(address(this)) != beforeBalance + amount) revert NonExactTransfer();\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/incorrect-strict-equality\n\n","passed":true},{"durationMs":1715,"exitCode":0,"name":"test","output":"No files changed, compilation skipped\n\nRan 4 tests for test/OracleConsumerConformance.t.sol:OracleConsumerConformanceTest\n[PASS] test_acceptsOriginalProtocolSignatureInCanonicalVerifier() (gas: 117518)\n[PASS] test_bytes32WeatherFixtureAcceptedUnderStipend() (gas: 1809143)\n[PASS] test_callbackSelectorIsCanonical() (gas: 3543)\n[PASS] test_digestMatchesTheProtocol() (gas: 13139)\nSuite result: ok. 4 passed; 0 failed; 0 skipped; finished in 3.28ms (1.65ms CPU time)\n\nRan 3 tests for test/WeatherQuestion.t.sol:WeatherQuestionTest\n[PASS] test_coordinatesAtEdgesAndNegativeZeroFraction() (gas: 40309)\n[PASS] test_exactLisbonBodyAndKnownDate() (gas: 15004)\n[PASS] test_exactNegativeLatitudeAndLongitudeBody() (gas: 15401)\nSuite result: ok. 3 passed; 0 failed; 0 skipped; finished in 5.79ms (529.66µs CPU time)\n\nRan 7 tests for test/PlantVotingRevision.t.sol:PlantVotingRevisionTest\n[PASS] test_atomicCurrentCellTopUpCannotVetoCommittedCandidate() (gas: 2334978)\n[PASS] test_atomicDestinationTopUpCannotMoveOrCaptureRewards() (gas: 4006719)\n[PASS] test_emptyCapturedCandidateFallsBackToRepairedLiveCandidate() (gas: 2456859)\n[PASS] test_fallbackCannotUseNewPostHeartbeatStake() (gas: 3004109)\n[PASS] test_questionIsEmptyBeforeBindAndDuringBirth() (gas: 412963)\n[PASS] test_thirdIncompleteAlsoUsesRepairedCandidate() (gas: 4089852)\n[PASS] test_withdrawalCapsSnapshotAndReaskUsesNewBalances() (gas: 2875579)\nSuite result: ok. 7 passed; 0 failed; 0 skipped; finished in 6.73ms (15.58ms CPU time)\n\nRan 4 tests for test/GasAndDeployment.t.sol:GasAndDeploymentTest\n[PASS] test_coldCallbackAndMaxHoursMoveSettleGas() (gas: 2720230)\nLogs:\n  cold callback gas: 94192\n  cold settle, 24 sips and move: 267586\n\n[PASS] test_coldMaxHoursWithRepairedCandidateSettleGas() (gas: 2762265)\nLogs:\n  cold settle, 24 sips and repaired candidate: 321416\n\n[PASS] test_requestedStaticConstructorAndForbiddenOpcodes() (gas: 8172999)\n[PASS] test_thirdIncompleteMoveAndAdvancePaymentGas() (gas: 3862256)\nLogs:\n  cold third incomplete with move and advance repayment: 255502\n\nSuite result: ok. 4 passed; 0 failed; 0 skipped; finished in 21.01ms (31.75ms CPU time)\n\nRan 4 tests for test/PlantOrganism.t.sol:PlantRotationTest\n[PASS] test_oldSignerExpiresAtThirtyDaysWhileNewSignerKeepsPlantAlive() (gas: 20452846)\n[PASS] test_onlyCurrentSignerCanRotateNonceCannotReplay() (gas: 1150859)\n[PASS] test_pendingIntakeAndOldSignerWorkAfterRotation() (gas: 3919007)\n[PASS] test_rotationDomainBindsAllFieldsAndContract() (gas: 974730)\nSuite result: ok. 4 passed; 0 failed; 0 skipped; finished in 25.16ms (29.05ms CPU time)\n\nRan 7 tests for test/PlantAssumptions.t.sol:PlantAssumptionsTest\n[PASS] test_explicitZeroCellQuestionRemainsInvalid() (gas: 25346)\n[PASS] test_oldRotationSignatureAccepted() (gas: 887686)\n[PASS] test_oldSourceWindowBacklogIsAlreadyDead() (gas: 403497)\n[PASS] test_preBindDonationsHaveNoExit() (gas: 196023)\n[PASS] test_redeemedSupplyStillCountsInThreshold() (gas: 3259070)\n[PASS] test_rotatedIntakeCanPriceAbovePotAndPullAdvance() (gas: 2405888)\n[PASS] test_timeoutsDoNotCountAndEventuallyDie() (gas: 9110386)\nSuite result: ok. 7 passed; 0 failed; 0 skipped; finished in 65.34ms (16.18ms CPU time)\n\nRan 11 tests for test/PlantOrganism.t.sol:PlantLifecycleTest\n[PASS] test_bindIsOnceAndOnlyDeployerWithMatchingHook() (gas: 325629)\n[PASS] test_birthFallbackAfterExactlyThreeEmptyDays() (gas: 313927)\n[PASS] test_birthMovesAtFivePercentWithoutMinimumStay() (gas: 663854)\n[PASS] test_cellBoundsAndZeroRejected() (gas: 613601)\n[PASS] test_challengerOnlyStrictlyLargerAndCanBeRepaired() (gas: 916047)\n[PASS] test_deathIsFinalAtThirtyDaysAndKeepsExits() (gas: 2709414)\n[PASS] test_deathUsesBindDayAfterLongUnboundPeriod() (gas: 285431)\n[PASS] test_moveUsesPendingCandidateButLiveBalances() (gas: 3186979)\n[PASS] test_noEarlyOrOutOfOrderSettleAndHeartbeat() (gas: 153161)\n[PASS] test_preBindDaysSkipInOneCallWithoutRead() (gas: 646783)\n[PASS] test_unboundCatchUpIsBoundedAndDoesNotRunBirth() (gas: 157656)\nSuite result: ok. 11 passed; 0 failed; 0 skipped; finished in 65.33ms (8.32ms CPU time)\n\nRan 12 tests for test/PlantOrganism.t.sol:PlantOracleTest\n[PASS] test_blockedKeeperDoesNotBlockSettleAndCanClaimLater() (gas: 2181699)\n[PASS] test_callbackSenderIdAndReplay() (gas: 2333839)\n[PASS] test_callbackStoresOnlyAndFitsStipend() (gas: 1890573)\nLogs:\n  callback gas including mock encoding: 94192\n  settle gas: 305143\n\n[PASS] test_clearIncompleteUsesSameCounterAndCannotDiscardComplete() (gas: 2316720)\n[PASS] test_exactApprovalAndIntakeBody() (gas: 1516617)\n[PASS] test_feeAdvanceTracksDeficitThenRepaidAtSettle() (gas: 2244748)\n[PASS] test_incompleteHasNoBiologicalChangesThenThirdReads() (gas: 3414172)\n[PASS] test_intakeMustPullExactPrice() (gas: 1261831)\n[PASS] test_rejectsExpiredWrongDomainSignerTypeDayChainAndPanel() (gas: 2617959)\n[PASS] test_rejectsPreAskFutureTamperedAndMalformedAttestations() (gas: 2359372)\n[PASS] test_timeoutClearsAndWaitsSixHoursToReaskSameDay() (gas: 2335382)\n[PASS] test_unfundedAdvanceDoesNotSpendBackingOrBlockSettle() (gas: 2874622)\nSuite result: ok. 12 passed; 0 failed; 0 skipped; finished in 73.32ms (28.40ms CPU time)\n\nRan 12 tests for test/PlantOrganism.t.sol:PlantRewardsTest\n[PASS] testFuzz_floorConservationAndBurnCustody(uint96,uint96,uint24) (runs: 256, μ: 2440192, ~: 2449990)\nLogs:\n  Bound result 250983920246099140921\n\n[PASS] testFuzz_hourlyAccountingMatchesIndependentModel(uint24,uint24) (runs: 256, μ: 1901482, ~: 1903788)\n[PASS] test_departuresDropOutAndReparkingCannotCaptureDay() (gas: 4273483)\n[PASS] test_finalRedemptionDoesNotDivideByZeroOrReleaseBurnedPlant() (gas: 2551540)\n[PASS] test_gardenerDustRoundsToBackingAndAccountsCloseExactly() (gas: 5477167)\n[PASS] test_hourZeroFirstRainCapAndSunRequiresWater() (gas: 5074645)\n[PASS] test_noGardenersSendsEntireSipToBacking() (gas: 1625914)\n[PASS] test_nonExactTokenTransfersRejected() (gas: 276495)\n[PASS] test_oldCellKeepsItsRewardsAfterMoveAndLazyActivation() (gas: 5233256)\n[PASS] test_parkSettleClaimProRataAndMidDayJoinsWaitOneSettle() (gas: 3313977)\n[PASS] test_redeemRetainsPlantAndTenPercentAndFloorMonotonic() (gas: 2302008)\n[PASS] test_reentrancyFromTokensAndIntakeBlocked() (gas: 2389035)\nSuite result: ok. 12 passed; 0 failed; 0 skipped; finished in 73.33ms (156.01ms CPU time)\n\nRan 1 test for test/PlantInvariant.t.sol:PlantInvariantTest\n[PASS] invariant_conservationAndPermanentBurnCustody() (runs: 128, calls: 8192, reverts: 0)\n\n╭--------------+---------------+-------+---------+----------╮\n| Contract     | Selector      | Calls | Reverts | Discards |\n+===========================================================+\n| PlantHandler | challenge     | 1060  | 0       | 0        |\n|--------------+---------------+-------+---------+----------|\n| PlantHandler | checkpointAll | 985   | 0       | 0        |\n|--------------+---------------+-------+---------+----------|\n| PlantHandler | claim         | 995   | 0       | 0        |\n|--------------+---------------+-------+---------+----------|\n| PlantHandler | fund          | 1031  | 0       | 0        |\n|--------------+---------------+-------+---------+----------|\n| PlantHandler | park          | 1038  | 0       | 0        |\n|--------------+---------------+-------+---------+----------|\n| PlantHandler | redeem        | 1050  | 0       | 0        |\n|--------------+---------------+-------+---------+----------|\n| PlantHandler | unpark        | 985   | 0       | 0        |\n|--------------+---------------+-------+---------+----------|\n| PlantHandler | weather       | 1048  | 0       | 0        |\n╰--------------+---------------+-------+---------+----------╯\n\nLogs:\n  Bound result 20876\n  Bound result 13076\n  Bound result 15959\n  Bound result 12632\n  Bound result 10613\n  Bound result 10354650\n  Bound result 2960\n  Bound result 261\n  Bound result 9621\n  Bound result 3974976807\n  Bound result 11044\n  Bound result 292\n  Bound result 19070\n  Bound result 3351\n  Bound result 95\n  Bound result 5942\n\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 1.61s (1.61s CPU time)\n\nRan 10 test suites in 1.61s (1.95s CPU time): 65 tests passed, 0 failed, 0 skipped (65 total tests)\n","passed":true},{"durationMs":40,"exitCode":0,"name":"source-index","output":"{\"v\":1,\"entryPoints\":[\"PlantOrganism.bind(address)\",\"PlantOrganism.challenge(uint32)\",\"PlantOrganism.checkpoint(uint32,address)\",\"PlantOrganism.claim()\",\"PlantOrganism.claim(uint32[])\",\"PlantOrganism.clearPending()\",\"PlantOrganism.die()\",\"PlantOrganism.heartbeat()\",\"PlantOrganism.onOracleResult(bytes32,(bytes32,uint256,bytes32,uint8,bytes,uint256,uint64,uint64,bytes32,bytes32,uint16,uint16,uint16,uint64,uint64),bytes)\",\"PlantOrganism.park(uint32,uint256)\",\"PlantOrganism.redeem(uint256)\",\"PlantOrganism.rotate(address,address,bytes32,uint256,bytes)\",\"PlantOrganism.settle()\",\"PlantOrganism.settle(uint32)\",\"PlantOrganism.unpark(uint32,uint256)\"],\"files\":{\".gitignore\":3,\"DEPENDENCIES.md\":7,\"README.md\":30,\"foundry.toml\":19,\"launch.json\":17,\"src/OracleAttestation.sol\":221,\"src/PlantOrganism.sol\":663,\"src/WeatherQuestion.sol\":83,\"src/interfaces/IIntake.sol\":14,\"test/GasAndDeployment.t.sol\":115,\"test/OracleConsumerConformance.t.sol\":126,\"test/PlantAssumptions.t.sol\":86,\"test/PlantInvariant.t.sol\":166,\"test/PlantOrganism.t.sol\":909,\"test/PlantVotingRevision.t.sol\":112,\"test/WeatherQuestion.t.sol\":36,\"test/fixtures/weather.json\":11,\"test/mocks/Mocks.sol\":137},\"excluded\":[\"lib/\",\"node_modules/\"],\"truncated\":false}","passed":true},{"durationMs":4407,"exitCode":0,"name":"slither","output":"[high/medium] reentrancy-balance at src/PlantOrganism.sol:295: Reentrancy in PlantOrganism.heartbeat() (src/PlantOrganism.sol#295-329):\n[medium/medium] divide-before-multiply at src/WeatherQuestion.sol:33: WeatherQuestion.date(uint32) (src/WeatherQuestion.sol#33-46) performs a multiplication on the result of a division:\n[medium/medium] divide-before-multiply at src/WeatherQuestion.sol:33: WeatherQuestion.date(uint32) (src/WeatherQuestion.sol#33-46) performs a multiplication on the result of a division:\n[medium/medium] divide-before-multiply at src/WeatherQuestion.sol:33: WeatherQuestion.date(uint32) (src/WeatherQuestion.sol#33-46) performs a multiplication on the result of a division:\n[medium/high] incorrect-equality at src/PlantOrganism.sol:568: PlantOrganism._pay(address) (src/PlantOrganism.sol#568-591) uses a dangerous strict equality:\n[medium/medium] reentrancy-no-eth at src/PlantOrganism.sol:295: Reentrancy in PlantOrganism.heartbeat() (src/PlantOrganism.sol#295-329):\n[medium/medium] uninitialized-local at src/PlantOrganism.sol:409: PlantOrganism._settle().caller (src/PlantOrganism.sol#409) is a local variable never initialized\n[medium/medium] uninitialized-local at src/PlantOrganism.sol:570: PlantOrganism._pay(address).advance (src/PlantOrganism.sol#570) is a local variable never initialized\n[medium/medium] uninitialized-local at src/PlantOrganism.sol:408: PlantOrganism._settle().word (src/PlantOrganism.sol#408) is a local variable never initialized\n[medium/medium] uninitialized-local at src/PlantOrganism.sol:411: PlantOrganism._settle().sips (src/PlantOrganism.sol#411) is a local variable never initialized\n[medium/medium] uninitialized-local at src/PlantOrganism.sol:410: PlantOrganism._settle().pool (src/PlantOrganism.sol#410) is a local variable never initialized\n[medium/medium] unused-return at src/PlantOrganism.sol:637: PlantOrganism.rotate(address,address,bytes32,uint256,bytes) (src/PlantOrganism.sol#637-656) ignores return value by (recovered,err,None) = ECDSA.tryRecoverCalldata(rotationDigest(newSigner,newIntake,newAction,nonce),sig) (src/PlantOrganism.sol#646-647)\n[medium/medium] unused-return at src/PlantOrganism.sol:331: PlantOrganism.onOracleResult(bytes32,OracleAttestation.Attestation,bytes) (src/PlantOrganism.sol#331-367) ignores return value by (recovered,err,None) = ECDSA.tryRecoverCalldata(attestationDigest(a),sig) (src/PlantOrganism.sol#342)\n[low/medium] reentrancy-benign at src/PlantOrganism.sol:295: Reentrancy in PlantOrganism.heartbeat() (src/PlantOrganism.sol#295-329):\n[low/medium] timestamp at src/PlantOrganism.sol:397: PlantOrganism._settle() (src/PlantOrganism.sol#397-448) uses timestamp for comparisons\n[low/medium] timestamp at src/PlantOrganism.sol:295: PlantOrganism.heartbeat() (src/PlantOrganism.sol#295-329) uses timestamp for comparisons\n[low/medium] timestamp at src/PlantOrganism.sol:369: PlantOrganism.clearPending() (src/PlantOrganism.sol#369-378) uses timestamp for comparisons\n[low/medium] timestamp at src/PlantOrganism.sol:467: PlantOrganism._snapshotVote(uint32) (src/PlantOrganism.sol#467-471) uses timestamp for comparisons\n[low/medium] timestamp at src/PlantOrganism.sol:331: PlantOrganism.onOracleResult(bytes32,OracleAttestation.Attestation,bytes) (src/PlantOrganism.sol#331-367) uses timestamp for comparisons\n[low/medium] timestamp at src/PlantOrganism.sol:194: PlantOrganism.isDead() (src/PlantOrganism.sol#194-196) uses timestamp for comparisons\n[low/medium] timestamp at src/OracleAttestation.sol:157: OracleAttestationConsumer._verifyAttestation(OracleAttestation.Attestation,bytes) (src/OracleAttestation.sol#157-165) uses timestamp for comparisons","passed":true},{"durationMs":583,"exitCode":0,"name":"aderyn","output":"[high] reentrancy-state-change at src/PlantOrganism.sol:182: Reentrancy: State change after external call (8 places)\n[low] costly-loop at src/PlantOrganism.sol:562: Costly operations inside loop\n[low] empty-require-revert at src/PlantOrganism.sol:215: Empty `require()` / `revert()` Statement\n[low] internal-function-used-once at src/OracleAttestation.sol:195: Internal Function Used Only Once (4 places)\n[low] literal-instead-of-constant at src/PlantOrganism.sol:351: Literal Instead of Constant (39 places)\n[low] local-variable-shadowing at src/PlantOrganism.sol:71: Local Variable Shadows State Variable (4 places)\n[low] uninitialized-local-variable at src/PlantOrganism.sol:453: Uninitialized Local Variable (2 places)","passed":true},{"durationMs":6819,"exitCode":0,"name":"proof 07ebfac46f95","output":"Compiling 47 files with Solc 0.8.26\nSolc 0.8.26 finished in 6.31s\nCompiler run successful!\n\nRan 2 tests for test/imd-proof-4aa18028/Proof_07ebfac46f95.t.sol:FlashMoveTest\n[PASS] test_atomicParkSettleUnparkMustNotMoveThePlant() (gas: 675254)\n[PASS] test_nextPoolMustGoToTheRealGardenerNotTheDustStake() (gas: 1548652)\nLogs:\n  IMD paid to the 1 wei stake: 0\n  IMD paid to alice's 100 ether stake: 300184772098882854100\n\nSuite result: ok. 2 passed; 0 failed; 0 skipped; finished in 2.41ms (1.54ms CPU time)\n\nRan 1 test suite in 3.03ms (2.41ms CPU time): 2 tests passed, 0 failed, 0 skipped (2 total tests)\n","passed":true},{"durationMs":6274,"exitCode":0,"name":"proof 7a54012d9763","output":"2026-10-08T06:27:33.246153Z ERROR foundry_compilers_artifacts_solc::sources: error=\"/tmp/imd-verify-Ir64yp/repo/test/imd-proof-4aa18028/Proof_07ebfac46f95.t.sol\": No such file or directory (os error 2)\nCompiling 1 files with Solc 0.8.26\nSolc 0.8.26 finished in 5.76s\nCompiler run successful!\n\nRan 1 test for test/imd-proof-ff353c68/Proof_7a54012d9763.t.sol:SnapshotEmptyCellTest\n[PASS] test_emptyCellSnapshotBlocksTheHoldersMove() (gas: 1884564)\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 1.52ms (800.89µs CPU time)\n\nRan 1 test suite in 2.95ms (1.52ms CPU time): 1 tests passed, 0 failed, 0 skipped (1 total tests)\n","passed":true}],"detail":"all checks passed","evaluation":"checks","profile":"foundry","status":"accepted","submissionHash":"1ec7eda8b5b88ee296d1f0c111b663bb515bbb821dcd6f0923205a55b2beaadf","verifiedTreeHash":"8cbbcfa7dde895e077631288f0796da12fd1a20b","verifierVersion":"0.1.0+ad90ce4c"},{"checks":[{"durationMs":26457,"exitCode":0,"name":"build","output":"Compiling 53 files with Solc 0.8.26\nSolc 0.8.26 finished in 26.33s\nCompiler run successful!\nwarning[unsafe-typecast]: typecast can truncate values\n   ╭▸ src/WeatherQuestion.sol:12:21\n   │\n12 │         int16 lat = int16(uint16(cell >> 16));\n   │                     ━━━━━━━━━━━━━━━━━━━━━━━━━\n   │\n   ├ note: consider disabling this lint if you're certain the cast is safe\n   │       \n   │       // casting to 'int16' is safe because [explain why]\n   │       // forge-lint: disable-next-line(unsafe-typecast)\n   │       \n   │       \n   ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n   ╭▸ src/WeatherQuestion.sol:12:27\n   │\n12 │         int16 lat = int16(uint16(cell >> 16));\n   │                           ━━━━━━━━━━━━━━━━━━\n   │\n   ├ note: consider disabling this lint if you're certain the cast is safe\n   │       \n   │       // casting to 'uint16' is safe because [explain why]\n   │       // forge-lint: disable-next-line(unsafe-typecast)\n   │       \n   │       \n   ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n   ╭▸ src/WeatherQuestion.sol:13:21\n   │\n13 │         int16 lon = int16(uint16(cell));\n   │                     ━━━━━━━━━━━━━━━━━━━\n   │\n   ├ note: consider disabling this lint if you're certain the cast is safe\n   │       \n   │       // casting to 'int16' is safe because [explain why]\n   │       // forge-lint: disable-next-line(unsafe-typecast)\n   │       \n   │       \n   ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n   ╭▸ src/WeatherQuestion.sol:13:27\n   │\n13 │         int16 lon = int16(uint16(cell));\n   │                           ━━━━━━━━━━━━\n   │\n   ├ note: consider disabling this lint if you're certain the cast is safe\n   │       \n   │       // casting to 'uint16' is safe because [explain why]\n   │       // forge-lint: disable-next-line(unsafe-typecast)\n   │       \n   │       \n   ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n   ╭▸ src/WeatherQuestion.sol:19:29\n   │\n19 │         uint256 magnitude = uint256(milli < 0 ? -milli : milli);\n   │                             ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n   │\n   ├ note: consider disabling this lint if you're certain the cast is safe\n   │       \n   │       // casting to 'uint256' is safe because [explain why]\n   │       // forge-lint: disable-next-line(unsafe-typecast)\n   │       \n   │       \n   ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n   ╭▸ src/WeatherQuestion.sol:26:29\n   │\n26 │             b[--i] = bytes1(uint8(48 + n % 10));\n   │                             ━━━━━━━━━━━━━━━━━━\n   │\n   ├ note: consider disabling this lint if you're certain the cast is safe\n   │       \n   │       // casting to 'uint8' is safe because [explain why]\n   │       // forge-lint: disable-next-line(unsafe-typecast)\n   │       \n   │       \n   ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[divide-before-multiply]: division before multiplication may lose precision\n   ╭▸ src/WeatherQuestion.sol:39:27\n   │\n39 │         uint256 y = yoe + era * 400;\n   │                           ━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/divide-before-multiply\n\nwarning[divide-before-multiply]: division before multiplication may lose precision\n   ╭▸ src/WeatherQuestion.sol:40:30\n   │\n40 │         uint256 doy = doe - (365 * yoe + yoe / 4 - yoe / 100);\n   │                              ━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/divide-before-multiply\n\nwarning[divide-before-multiply]: division before multiplication may lose precision\n   ╭▸ src/WeatherQuestion.sol:42:28\n   │\n42 │         uint256 d = doy - (153 * mp + 2) / 5 + 1;\n   │                            ━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/divide-before-multiply\n\nwarning[unsafe-typecast]: typecast can truncate values\n   ╭▸ src/WeatherQuestion.sol:50:40\n   │\n50 │         string memory lat = coordinate(int16(uint16(cell >> 16)));\n   │                                        ━━━━━━━━━━━━━━━━━━━━━━━━━\n   │\n   ├ note: consider disabling this lint if you're certain the cast is safe\n   │       \n   │       // casting to 'int16' is safe because [explain why]\n   │       // forge-lint: disable-next-line(unsafe-typecast)\n   │       \n   │       \n   ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n   ╭▸ src/WeatherQuestion.sol:50:46\n   │\n50 │         string memory lat = coordinate(int16(uint16(cell >> 16)));\n   │                                              ━━━━━━━━━━━━━━━━━━\n   │\n   ├ note: consider disabling this lint if you're certain the cast is safe\n   │       \n   │       // casting to 'uint16' is safe because [explain why]\n   │       // forge-lint: disable-next-line(unsafe-typecast)\n   │       \n   │       \n   ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n   ╭▸ src/WeatherQuestion.sol:51:40\n   │\n51 │         string memory lon = coordinate(int16(uint16(cell)));\n   │                                        ━━━━━━━━━━━━━━━━━━━\n   │\n   ├ note: consider disabling this lint if you're certain the cast is safe\n   │       \n   │       // casting to 'int16' is safe because [explain why]\n   │       // forge-lint: disable-next-line(unsafe-typecast)\n   │       \n   │       \n   ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n   ╭▸ src/WeatherQuestion.sol:51:46\n   │\n51 │         string memory lon = coordinate(int16(uint16(cell)));\n   │                                              ━━━━━━━━━━━━\n   │\n   ├ note: consider disabling this lint if you're certain the cast is safe\n   │       \n   │       // casting to 'uint16' is safe because [explain why]\n   │       // forge-lint: disable-next-line(unsafe-typecast)\n   │       \n   │       \n   ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/PlantOrganism.sol:176:16\n    │\n176 │         return uint32(block.timestamp / 1 days);\n    │                ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint32' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[missing-zero-check]: address parameter is used in a state write or value transfer without a zero-address check\n    ╭▸ src/PlantOrganism.sol:179:19\n    │\n179 │     function bind(address hook_) external nonReentrant {\n    │                   ━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/missing-zero-check\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/PlantOrganism.sol:215:25\n    │\n215 │         require(held <= uint256(type(int256).max) && reserved <= uint256(type(int256).max));\n    │                         ━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/PlantOrganism.sol:215:66\n    │\n215 │         require(held <= uint256(type(int256).max) && reserved <= uint256(type(int256).max));\n    │                                                                  ━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/PlantOrganism.sol:216:16\n    │\n216 │         return int256(held) - int256(reserved);\n    │                ━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'int256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/PlantOrganism.sol:216:31\n    │\n216 │         return int256(held) - int256(reserved);\n    │                               ━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'int256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/PlantOrganism.sol:221:24\n    │\n221 │         return p > 0 ? uint256(p) : 0;\n    │                        ━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/PlantOrganism.sol:543:13\n    │\n543 │             emit Checkpointed(holder, cell, credit);\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/PlantOrganism.sol:291:13\n    │\n291 │             emit Challenged(cell);\n    │             ━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/PlantOrganism.sol:260:9\n    │\n260 │         emit Parked(msg.sender, cell, amount);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/PlantOrganism.sol:279:9\n    │\n279 │         emit Unparked(msg.sender, cell, amount);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[reentrancy-no-eth]: external call can be reentered before `pending` is updated\n    ╭▸ src/PlantOrganism.sol:314:14\n    │\n314 │           id = intake.request(\n    │ ┏━━━━━━━━━━━━━━┛\n315 │ ┃             action,\n316 │ ┃             requestBody(location, day),\n317 │ ┃             IIntake.Callback(address(this), this.onOracleResult.selector),\n318 │ ┃             address(IMD),\n319 │ ┃             price\n320 │ ┃         );\n    │ ┗━━━━━━━━━┛\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-no-eth\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/PlantOrganism.sol:301:13\n    │\n301 │         if (block.timestamp < retryAt) revert RetryLater();\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/PlantOrganism.sol:310:13\n    │\n310 │             emit Advanced(msg.sender, advance);\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/PlantOrganism.sol:328:9\n    │\n328 │         emit Asked(id, day, location, challenger, msg.sender, price);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[incorrect-strict-equality]: dangerous strict equality check on an externally-influenced value\n    ╭▸ src/PlantOrganism.sol:322:13\n    │\n322 │         if (IMD.balanceOf(address(this)) != beforeBalance - price) revert NonExactTransfer();\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/incorrect-strict-equality\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/PlantOrganism.sol:324:54\n    │\n324 │             Pending(id, day, challenger, msg.sender, uint64(block.timestamp), address(intake), true, false, bytes32(0));\n    │                                                      ━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint64' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/PlantOrganism.sol:345:20\n    │\n345 │                 && block.timestamp < signerAcceptedUntil[recovered]\n    │                    ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/PlantOrganism.sol:366:9\n    │\n366 │         emit Received(requestId, a.requestId, word);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/PlantOrganism.sol:356:13\n    │\n356 │         if (uint32(w >> 96) != p.day) revert WrongDay();\n    │             ━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint32' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/PlantOrganism.sol:375:17\n    │\n375 │             if (block.timestamp < uint256(pending.askedAt) + ORACLE_TIMEOUT) revert NotTimedOut();\n    │                 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[uninitialized-local]: local variable is read before being initialized\n    ╭▸ src/PlantOrganism.sol:447:33\n    │\n447 │         emit Settled(day, word, sips, backing, water, location);\n    │                                 ━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/uninitialized-local\n\nwarning[uninitialized-local]: local variable is read before being initialized\n    ╭▸ src/PlantOrganism.sol:430:30\n    │\n430 │         _distribute(oldCell, pool);\n    │                              ━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/uninitialized-local\n\nwarning[uninitialized-local]: local variable is read before being initialized\n    ╭▸ src/PlantOrganism.sol:447:27\n    │\n447 │         emit Settled(day, word, sips, backing, water, location);\n    │                           ━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/uninitialized-local\n\nwarning[uninitialized-local]: local variable is read before being initialized\n    ╭▸ src/PlantOrganism.sol:440:13\n    │\n440 │         if (caller != address(0)) {\n    │             ━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/uninitialized-local\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/PlantOrganism.sol:403:13\n    │\n403 │             emit EmptyCatchUp(day, end);\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/PlantOrganism.sol:421:17\n    │\n421 │                 emit Incomplete(day, count);\n    │                 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/PlantOrganism.sol:385:9\n    │\n385 │         emit Cleared(id, day, retryAt);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/PlantOrganism.sol:521:9\n    │\n521 │         emit Rewarded(cell, pool, reserved);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/PlantOrganism.sol:491:13\n    │\n491 │             emit Moved(current, candidate);\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/PlantOrganism.sol:435:13\n    │\n435 │             emit Moved(0, location);\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/PlantOrganism.sol:444:13\n    │\n444 │             emit Bounty(caller, bounty);\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/PlantOrganism.sol:583:13\n    │\n583 │             emit Paid(holder, credit, advance);\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/PlantOrganism.sol:589:13\n    │\n589 │             emit PaymentDeferred(holder, credit + advance);\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/PlantOrganism.sol:447:9\n    │\n447 │         emit Settled(day, word, sips, backing, water, location);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/PlantOrganism.sol:401:61\n    │\n401 │             uint32 end = hook == address(0) ? today() - 1 : uint32(Math.min(today() - 1, bindDay - 1));\n    │                                                             ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint32' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/PlantOrganism.sol:464:17\n    │\n464 │         water = uint8(wet);\n    │                 ━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint8' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[uninitialized-local]: local variable is read before being initialized\n    ╭▸ src/PlantOrganism.sol:577:22\n    │\n577 │         if (credit + advance == 0) return;\n    │                      ━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/uninitialized-local\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/PlantOrganism.sol:606:9\n    │\n606 │         emit Redeemed(msg.sender, amount, payout, dead);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/PlantOrganism.sol:621:13\n    │\n621 │             emit Died(today(), merged);\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/PlantOrganism.sol:623:13\n    │\n623 │             emit PotMerged(merged);\n    │             ━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/OracleAttestation.sol:182:9\n    │\n182 │         emit OracleSignerSet(signer);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/PlantOrganism.sol:655:9\n    │\n655 │         emit Rotated(oldSigner, newSigner, newIntake, newAction, nonce);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[incorrect-strict-equality]: dangerous strict equality check on an externally-influenced value\n    ╭▸ src/PlantOrganism.sol:661:13\n    │\n661 │         if (token.balanceOf(address(this)) != beforeBalance + amount) revert NonExactTransfer();\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/incorrect-strict-equality\n\n","passed":true},{"durationMs":1620,"exitCode":0,"name":"test","output":"No files changed, compilation skipped\n\nRan 3 tests for test/WeatherQuestion.t.sol:WeatherQuestionTest\n[PASS] test_coordinatesAtEdgesAndNegativeZeroFraction() (gas: 40309)\n[PASS] test_exactLisbonBodyAndKnownDate() (gas: 15004)\n[PASS] test_exactNegativeLatitudeAndLongitudeBody() (gas: 15401)\nSuite result: ok. 3 passed; 0 failed; 0 skipped; finished in 307.30µs (194.99µs CPU time)\n\nRan 4 tests for test/OracleConsumerConformance.t.sol:OracleConsumerConformanceTest\n[PASS] test_acceptsOriginalProtocolSignatureInCanonicalVerifier() (gas: 117518)\n[PASS] test_bytes32WeatherFixtureAcceptedUnderStipend() (gas: 1809143)\n[PASS] test_callbackSelectorIsCanonical() (gas: 3543)\n[PASS] test_digestMatchesTheProtocol() (gas: 13139)\nSuite result: ok. 4 passed; 0 failed; 0 skipped; finished in 1.66ms (794.80µs CPU time)\n\nRan 7 tests for test/PlantVotingRevision.t.sol:PlantVotingRevisionTest\n[PASS] test_atomicCurrentCellTopUpCannotVetoCommittedCandidate() (gas: 2334978)\n[PASS] test_atomicDestinationTopUpCannotMoveOrCaptureRewards() (gas: 4006719)\n[PASS] test_emptyCapturedCandidateFallsBackToRepairedLiveCandidate() (gas: 2456859)\n[PASS] test_fallbackCannotUseNewPostHeartbeatStake() (gas: 3004109)\n[PASS] test_questionIsEmptyBeforeBindAndDuringBirth() (gas: 412963)\n[PASS] test_thirdIncompleteAlsoUsesRepairedCandidate() (gas: 4089852)\n[PASS] test_withdrawalCapsSnapshotAndReaskUsesNewBalances() (gas: 2875579)\nSuite result: ok. 7 passed; 0 failed; 0 skipped; finished in 4.96ms (14.29ms CPU time)\n\nRan 12 tests for test/PlantOrganism.t.sol:PlantOracleTest\n[PASS] test_blockedKeeperDoesNotBlockSettleAndCanClaimLater() (gas: 2181699)\n[PASS] test_callbackSenderIdAndReplay() (gas: 2333839)\n[PASS] test_callbackStoresOnlyAndFitsStipend() (gas: 1890573)\nLogs:\n  callback gas including mock encoding: 94192\n  settle gas: 305143\n\n[PASS] test_clearIncompleteUsesSameCounterAndCannotDiscardComplete() (gas: 2316720)\n[PASS] test_exactApprovalAndIntakeBody() (gas: 1516617)\n[PASS] test_feeAdvanceTracksDeficitThenRepaidAtSettle() (gas: 2244748)\n[PASS] test_incompleteHasNoBiologicalChangesThenThirdReads() (gas: 3414172)\n[PASS] test_intakeMustPullExactPrice() (gas: 1261831)\n[PASS] test_rejectsExpiredWrongDomainSignerTypeDayChainAndPanel() (gas: 2617959)\n[PASS] test_rejectsPreAskFutureTamperedAndMalformedAttestations() (gas: 2359372)\n[PASS] test_timeoutClearsAndWaitsSixHoursToReaskSameDay() (gas: 2335382)\n[PASS] test_unfundedAdvanceDoesNotSpendBackingOrBlockSettle() (gas: 2874622)\nSuite result: ok. 12 passed; 0 failed; 0 skipped; finished in 5.20ms (17.78ms CPU time)\n\nRan 7 tests for test/PlantAssumptions.t.sol:PlantAssumptionsTest\n[PASS] test_explicitZeroCellQuestionRemainsInvalid() (gas: 25346)\n[PASS] test_oldRotationSignatureAccepted() (gas: 887686)\n[PASS] test_oldSourceWindowBacklogIsAlreadyDead() (gas: 403497)\n[PASS] test_preBindDonationsHaveNoExit() (gas: 196023)\n[PASS] test_redeemedSupplyStillCountsInThreshold() (gas: 3259070)\n[PASS] test_rotatedIntakeCanPriceAbovePotAndPullAdvance() (gas: 2405888)\n[PASS] test_timeoutsDoNotCountAndEventuallyDie() (gas: 9110386)\nSuite result: ok. 7 passed; 0 failed; 0 skipped; finished in 6.43ms (9.95ms CPU time)\n\nRan 4 tests for test/GasAndDeployment.t.sol:GasAndDeploymentTest\n[PASS] test_coldCallbackAndMaxHoursMoveSettleGas() (gas: 2720230)\nLogs:\n  cold callback gas: 94192\n  cold settle, 24 sips and move: 267586\n\n[PASS] test_coldMaxHoursWithRepairedCandidateSettleGas() (gas: 2762265)\nLogs:\n  cold settle, 24 sips and repaired candidate: 321416\n\n[PASS] test_requestedStaticConstructorAndForbiddenOpcodes() (gas: 8172999)\n[PASS] test_thirdIncompleteMoveAndAdvancePaymentGas() (gas: 3862256)\nLogs:\n  cold third incomplete with move and advance repayment: 255502\n\nSuite result: ok. 4 passed; 0 failed; 0 skipped; finished in 13.09ms (21.27ms CPU time)\n\nRan 4 tests for test/PlantOrganism.t.sol:PlantRotationTest\n[PASS] test_oldSignerExpiresAtThirtyDaysWhileNewSignerKeepsPlantAlive() (gas: 20452846)\n[PASS] test_onlyCurrentSignerCanRotateNonceCannotReplay() (gas: 1150859)\n[PASS] test_pendingIntakeAndOldSignerWorkAfterRotation() (gas: 3919007)\n[PASS] test_rotationDomainBindsAllFieldsAndContract() (gas: 974730)\nSuite result: ok. 4 passed; 0 failed; 0 skipped; finished in 13.98ms (16.97ms CPU time)\n\nRan 11 tests for test/PlantOrganism.t.sol:PlantLifecycleTest\n[PASS] test_bindIsOnceAndOnlyDeployerWithMatchingHook() (gas: 325629)\n[PASS] test_birthFallbackAfterExactlyThreeEmptyDays() (gas: 313927)\n[PASS] test_birthMovesAtFivePercentWithoutMinimumStay() (gas: 663854)\n[PASS] test_cellBoundsAndZeroRejected() (gas: 613601)\n[PASS] test_challengerOnlyStrictlyLargerAndCanBeRepaired() (gas: 916047)\n[PASS] test_deathIsFinalAtThirtyDaysAndKeepsExits() (gas: 2709414)\n[PASS] test_deathUsesBindDayAfterLongUnboundPeriod() (gas: 285431)\n[PASS] test_moveUsesPendingCandidateButLiveBalances() (gas: 3186979)\n[PASS] test_noEarlyOrOutOfOrderSettleAndHeartbeat() (gas: 153161)\n[PASS] test_preBindDaysSkipInOneCallWithoutRead() (gas: 646783)\n[PASS] test_unboundCatchUpIsBoundedAndDoesNotRunBirth() (gas: 157656)\nSuite result: ok. 11 passed; 0 failed; 0 skipped; finished in 43.18ms (7.15ms CPU time)\n\nRan 12 tests for test/PlantOrganism.t.sol:PlantRewardsTest\n[PASS] testFuzz_floorConservationAndBurnCustody(uint96,uint96,uint24) (runs: 256, μ: 2440303, ~: 2449990)\nLogs:\n  Bound result 293620695856222465371\n\n[PASS] testFuzz_hourlyAccountingMatchesIndependentModel(uint24,uint24) (runs: 256, μ: 1901405, ~: 1904083)\n[PASS] test_departuresDropOutAndReparkingCannotCaptureDay() (gas: 4273483)\n[PASS] test_finalRedemptionDoesNotDivideByZeroOrReleaseBurnedPlant() (gas: 2551540)\n[PASS] test_gardenerDustRoundsToBackingAndAccountsCloseExactly() (gas: 5477167)\n[PASS] test_hourZeroFirstRainCapAndSunRequiresWater() (gas: 5074645)\n[PASS] test_noGardenersSendsEntireSipToBacking() (gas: 1625914)\n[PASS] test_nonExactTokenTransfersRejected() (gas: 276495)\n[PASS] test_oldCellKeepsItsRewardsAfterMoveAndLazyActivation() (gas: 5233256)\n[PASS] test_parkSettleClaimProRataAndMidDayJoinsWaitOneSettle() (gas: 3313977)\n[PASS] test_redeemRetainsPlantAndTenPercentAndFloorMonotonic() (gas: 2302008)\n[PASS] test_reentrancyFromTokensAndIntakeBlocked() (gas: 2389035)\nSuite result: ok. 12 passed; 0 failed; 0 skipped; finished in 53.95ms (118.18ms CPU time)\n\nRan 1 test for test/PlantInvariant.t.sol:PlantInvariantTest\n[PASS] invariant_conservationAndPermanentBurnCustody() (runs: 128, calls: 8192, reverts: 0)\n\n╭--------------+---------------+-------+---------+----------╮\n| Contract     | Selector      | Calls | Reverts | Discards |\n+===========================================================+\n| PlantHandler | challenge     | 1004  | 0       | 0        |\n|--------------+---------------+-------+---------+----------|\n| PlantHandler | checkpointAll | 1042  | 0       | 0        |\n|--------------+---------------+-------+---------+----------|\n| PlantHandler | claim         | 970   | 0       | 0        |\n|--------------+---------------+-------+---------+----------|\n| PlantHandler | fund          | 1025  | 0       | 0        |\n|--------------+---------------+-------+---------+----------|\n| PlantHandler | park          | 1011  | 0       | 0        |\n|--------------+---------------+-------+---------+----------|\n| PlantHandler | redeem        | 1033  | 0       | 0        |\n|--------------+---------------+-------+---------+----------|\n| PlantHandler | unpark        | 1040  | 0       | 0        |\n|--------------+---------------+-------+---------+----------|\n| PlantHandler | weather       | 1067  | 0       | 0        |\n╰--------------+---------------+-------+---------+----------╯\n\nLogs:\n  Bound result 5019\n  Bound result 3707953491\n  Bound result 7101\n  Bound result 7338\n  Bound result 3851\n  Bound result 2044958676\n  Bound result 52\n  Bound result 2044959793\n  Bound result 1093\n  Bound result 1870\n  Bound result 456\n  Bound result 3376\n  Bound result 1689\n  Bound result 2568\n  Bound result 570\n  Bound result 228\n  Bound result 3199980195240\n  Bound result 864\n  Bound result 20915\n  Bound result 392296852068271574969\n\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 1.54s (1.54s CPU time)\n\nRan 10 test suites in 1.54s (1.68s CPU time): 65 tests passed, 0 failed, 0 skipped (65 total tests)\n","passed":true},{"durationMs":40,"exitCode":0,"name":"source-index","output":"{\"v\":1,\"entryPoints\":[\"PlantOrganism.bind(address)\",\"PlantOrganism.challenge(uint32)\",\"PlantOrganism.checkpoint(uint32,address)\",\"PlantOrganism.claim()\",\"PlantOrganism.claim(uint32[])\",\"PlantOrganism.clearPending()\",\"PlantOrganism.die()\",\"PlantOrganism.heartbeat()\",\"PlantOrganism.onOracleResult(bytes32,(bytes32,uint256,bytes32,uint8,bytes,uint256,uint64,uint64,bytes32,bytes32,uint16,uint16,uint16,uint64,uint64),bytes)\",\"PlantOrganism.park(uint32,uint256)\",\"PlantOrganism.redeem(uint256)\",\"PlantOrganism.rotate(address,address,bytes32,uint256,bytes)\",\"PlantOrganism.settle()\",\"PlantOrganism.settle(uint32)\",\"PlantOrganism.unpark(uint32,uint256)\"],\"files\":{\".gitignore\":3,\"DEPENDENCIES.md\":7,\"README.md\":30,\"foundry.toml\":19,\"launch.json\":17,\"src/OracleAttestation.sol\":221,\"src/PlantOrganism.sol\":663,\"src/WeatherQuestion.sol\":83,\"src/interfaces/IIntake.sol\":14,\"test/GasAndDeployment.t.sol\":115,\"test/OracleConsumerConformance.t.sol\":126,\"test/PlantAssumptions.t.sol\":86,\"test/PlantInvariant.t.sol\":166,\"test/PlantOrganism.t.sol\":909,\"test/PlantVotingRevision.t.sol\":112,\"test/WeatherQuestion.t.sol\":36,\"test/fixtures/weather.json\":11,\"test/mocks/Mocks.sol\":137},\"excluded\":[\"lib/\",\"node_modules/\"],\"truncated\":false}","passed":true}],"detail":"all checks passed","evaluation":"checks","profile":"foundry","status":"accepted","submissionHash":"5d50f4dd99bd69dd89c5b7958dca1062c3d8376ca988fba9dd33e85933f94063","verifiedTreeHash":"e6b90cf920223f1a5dd72104935d50ede02202fc","verifierVersion":"0.1.0+ad90ce4c"},{"checks":[{"durationMs":42735,"exitCode":0,"name":"build","output":"Compiling 56 files with Solc 0.8.26\nSolc 0.8.26 finished in 42.60s\nCompiler run successful!\nwarning[unsafe-typecast]: typecast can truncate values\n   ╭▸ src/WeatherQuestion.sol:12:21\n   │\n12 │         int16 lat = int16(uint16(cell >> 16));\n   │                     ━━━━━━━━━━━━━━━━━━━━━━━━━\n   │\n   ├ note: consider disabling this lint if you're certain the cast is safe\n   │       \n   │       // casting to 'int16' is safe because [explain why]\n   │       // forge-lint: disable-next-line(unsafe-typecast)\n   │       \n   │       \n   ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n   ╭▸ src/WeatherQuestion.sol:12:27\n   │\n12 │         int16 lat = int16(uint16(cell >> 16));\n   │                           ━━━━━━━━━━━━━━━━━━\n   │\n   ├ note: consider disabling this lint if you're certain the cast is safe\n   │       \n   │       // casting to 'uint16' is safe because [explain why]\n   │       // forge-lint: disable-next-line(unsafe-typecast)\n   │       \n   │       \n   ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n   ╭▸ src/WeatherQuestion.sol:13:21\n   │\n13 │         int16 lon = int16(uint16(cell));\n   │                     ━━━━━━━━━━━━━━━━━━━\n   │\n   ├ note: consider disabling this lint if you're certain the cast is safe\n   │       \n   │       // casting to 'int16' is safe because [explain why]\n   │       // forge-lint: disable-next-line(unsafe-typecast)\n   │       \n   │       \n   ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n   ╭▸ src/WeatherQuestion.sol:13:27\n   │\n13 │         int16 lon = int16(uint16(cell));\n   │                           ━━━━━━━━━━━━\n   │\n   ├ note: consider disabling this lint if you're certain the cast is safe\n   │       \n   │       // casting to 'uint16' is safe because [explain why]\n   │       // forge-lint: disable-next-line(unsafe-typecast)\n   │       \n   │       \n   ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n   ╭▸ src/WeatherQuestion.sol:19:29\n   │\n19 │         uint256 magnitude = uint256(milli < 0 ? -milli : milli);\n   │                             ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n   │\n   ├ note: consider disabling this lint if you're certain the cast is safe\n   │       \n   │       // casting to 'uint256' is safe because [explain why]\n   │       // forge-lint: disable-next-line(unsafe-typecast)\n   │       \n   │       \n   ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n   ╭▸ src/WeatherQuestion.sol:26:29\n   │\n26 │             b[--i] = bytes1(uint8(48 + n % 10));\n   │                             ━━━━━━━━━━━━━━━━━━\n   │\n   ├ note: consider disabling this lint if you're certain the cast is safe\n   │       \n   │       // casting to 'uint8' is safe because [explain why]\n   │       // forge-lint: disable-next-line(unsafe-typecast)\n   │       \n   │       \n   ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[divide-before-multiply]: division before multiplication may lose precision\n   ╭▸ src/WeatherQuestion.sol:39:27\n   │\n39 │         uint256 y = yoe + era * 400;\n   │                           ━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/divide-before-multiply\n\nwarning[divide-before-multiply]: division before multiplication may lose precision\n   ╭▸ src/WeatherQuestion.sol:40:30\n   │\n40 │         uint256 doy = doe - (365 * yoe + yoe / 4 - yoe / 100);\n   │                              ━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/divide-before-multiply\n\nwarning[divide-before-multiply]: division before multiplication may lose precision\n   ╭▸ src/WeatherQuestion.sol:42:28\n   │\n42 │         uint256 d = doy - (153 * mp + 2) / 5 + 1;\n   │                            ━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/divide-before-multiply\n\nwarning[unsafe-typecast]: typecast can truncate values\n   ╭▸ src/WeatherQuestion.sol:50:40\n   │\n50 │         string memory lat = coordinate(int16(uint16(cell >> 16)));\n   │                                        ━━━━━━━━━━━━━━━━━━━━━━━━━\n   │\n   ├ note: consider disabling this lint if you're certain the cast is safe\n   │       \n   │       // casting to 'int16' is safe because [explain why]\n   │       // forge-lint: disable-next-line(unsafe-typecast)\n   │       \n   │       \n   ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n   ╭▸ src/WeatherQuestion.sol:50:46\n   │\n50 │         string memory lat = coordinate(int16(uint16(cell >> 16)));\n   │                                              ━━━━━━━━━━━━━━━━━━\n   │\n   ├ note: consider disabling this lint if you're certain the cast is safe\n   │       \n   │       // casting to 'uint16' is safe because [explain why]\n   │       // forge-lint: disable-next-line(unsafe-typecast)\n   │       \n   │       \n   ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n   ╭▸ src/WeatherQuestion.sol:51:40\n   │\n51 │         string memory lon = coordinate(int16(uint16(cell)));\n   │                                        ━━━━━━━━━━━━━━━━━━━\n   │\n   ├ note: consider disabling this lint if you're certain the cast is safe\n   │       \n   │       // casting to 'int16' is safe because [explain why]\n   │       // forge-lint: disable-next-line(unsafe-typecast)\n   │       \n   │       \n   ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n   ╭▸ src/WeatherQuestion.sol:51:46\n   │\n51 │         string memory lon = coordinate(int16(uint16(cell)));\n   │                                              ━━━━━━━━━━━━\n   │\n   ├ note: consider disabling this lint if you're certain the cast is safe\n   │       \n   │       // casting to 'uint16' is safe because [explain why]\n   │       // forge-lint: disable-next-line(unsafe-typecast)\n   │       \n   │       \n   ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/PlantOrganism.sol:167:16\n    │\n167 │         return uint32(block.timestamp / 1 days);\n    │                ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint32' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[missing-zero-check]: address parameter is used in a state write or value transfer without a zero-address check\n    ╭▸ src/PlantOrganism.sol:170:19\n    │\n170 │     function bind(address hook_) external nonReentrant {\n    │                   ━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/missing-zero-check\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/PlantOrganism.sol:206:25\n    │\n206 │         require(held <= uint256(type(int256).max) && reserved <= uint256(type(int256).max));\n    │                         ━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/PlantOrganism.sol:206:66\n    │\n206 │         require(held <= uint256(type(int256).max) && reserved <= uint256(type(int256).max));\n    │                                                                  ━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/PlantOrganism.sol:207:16\n    │\n207 │         return int256(held) - int256(reserved);\n    │                ━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'int256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/PlantOrganism.sol:207:31\n    │\n207 │         return int256(held) - int256(reserved);\n    │                               ━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'int256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/PlantOrganism.sol:212:24\n    │\n212 │         return p > 0 ? uint256(p) : 0;\n    │                        ━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/PlantOrganism.sol:525:13\n    │\n525 │             emit Checkpointed(holder, cell, credit);\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/PlantOrganism.sol:280:13\n    │\n280 │             emit Challenged(cell);\n    │             ━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/PlantOrganism.sol:250:9\n    │\n250 │         emit Parked(msg.sender, cell, amount);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/PlantOrganism.sol:268:9\n    │\n268 │         emit Unparked(msg.sender, cell, amount);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[reentrancy-no-eth]: external call can be reentered before `pending` is updated\n    ╭▸ src/PlantOrganism.sol:303:14\n    │\n303 │           id = intake.request(\n    │ ┏━━━━━━━━━━━━━━┛\n304 │ ┃             action,\n305 │ ┃             requestBody(location, day),\n306 │ ┃             IIntake.Callback(address(this), this.onOracleResult.selector),\n307 │ ┃             address(IMD),\n308 │ ┃             price\n309 │ ┃         );\n    │ ┗━━━━━━━━━┛\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-no-eth\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/PlantOrganism.sol:290:13\n    │\n290 │         if (block.timestamp < retryAt) revert RetryLater();\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/PlantOrganism.sol:299:13\n    │\n299 │             emit Advanced(msg.sender, advance);\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/PlantOrganism.sol:314:9\n    │\n314 │         emit Asked(id, day, location, challenger, msg.sender, price);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[incorrect-strict-equality]: dangerous strict equality check on an externally-influenced value\n    ╭▸ src/PlantOrganism.sol:311:13\n    │\n311 │         if (IMD.balanceOf(address(this)) != beforeBalance - price) revert NonExactTransfer();\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/incorrect-strict-equality\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/PlantOrganism.sol:313:54\n    │\n313 │             Pending(id, day, challenger, msg.sender, uint64(block.timestamp), address(intake), true, false, bytes32(0));\n    │                                                      ━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint64' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/PlantOrganism.sol:331:20\n    │\n331 │                 && block.timestamp < signerAcceptedUntil[recovered]\n    │                    ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/PlantOrganism.sol:352:9\n    │\n352 │         emit Received(requestId, a.requestId, word);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/PlantOrganism.sol:342:13\n    │\n342 │         if (uint32(w >> 96) != p.day) revert WrongDay();\n    │             ━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint32' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/PlantOrganism.sol:361:17\n    │\n361 │             if (block.timestamp < uint256(pending.askedAt) + ORACLE_TIMEOUT) revert NotTimedOut();\n    │                 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[uninitialized-local]: local variable is read before being initialized\n    ╭▸ src/PlantOrganism.sol:416:30\n    │\n416 │         _distribute(oldCell, pool);\n    │                              ━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/uninitialized-local\n\nwarning[uninitialized-local]: local variable is read before being initialized\n    ╭▸ src/PlantOrganism.sol:433:27\n    │\n433 │         emit Settled(day, word, sips, backing, water, location);\n    │                           ━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/uninitialized-local\n\nwarning[uninitialized-local]: local variable is read before being initialized\n    ╭▸ src/PlantOrganism.sol:433:33\n    │\n433 │         emit Settled(day, word, sips, backing, water, location);\n    │                                 ━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/uninitialized-local\n\nwarning[uninitialized-local]: local variable is read before being initialized\n    ╭▸ src/PlantOrganism.sol:426:13\n    │\n426 │         if (caller != address(0)) {\n    │             ━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/uninitialized-local\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/PlantOrganism.sol:389:13\n    │\n389 │             emit EmptyCatchUp(day, end);\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/PlantOrganism.sol:407:17\n    │\n407 │                 emit Incomplete(day, count);\n    │                 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/PlantOrganism.sol:371:9\n    │\n371 │         emit Cleared(id, day, retryAt);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/PlantOrganism.sol:503:9\n    │\n503 │         emit Rewarded(cell, pool, reserved);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/PlantOrganism.sol:473:13\n    │\n473 │             emit Moved(current, candidate);\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/PlantOrganism.sol:421:13\n    │\n421 │             emit Moved(0, location);\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/PlantOrganism.sol:430:13\n    │\n430 │             emit Bounty(caller, bounty);\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/PlantOrganism.sol:565:13\n    │\n565 │             emit Paid(holder, credit, advance);\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/PlantOrganism.sol:571:13\n    │\n571 │             emit PaymentDeferred(holder, credit + advance);\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/PlantOrganism.sol:433:9\n    │\n433 │         emit Settled(day, word, sips, backing, water, location);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/PlantOrganism.sol:387:61\n    │\n387 │             uint32 end = hook == address(0) ? today() - 1 : uint32(Math.min(today() - 1, bindDay - 1));\n    │                                                             ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint32' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/PlantOrganism.sol:450:17\n    │\n450 │         water = uint8(wet);\n    │                 ━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint8' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[uninitialized-local]: local variable is read before being initialized\n    ╭▸ src/PlantOrganism.sol:559:22\n    │\n559 │         if (credit + advance == 0) return;\n    │                      ━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/uninitialized-local\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/PlantOrganism.sol:588:9\n    │\n588 │         emit Redeemed(msg.sender, amount, payout, dead);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/PlantOrganism.sol:603:13\n    │\n603 │             emit Died(today(), merged);\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/PlantOrganism.sol:605:13\n    │\n605 │             emit PotMerged(merged);\n    │             ━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/OracleAttestation.sol:182:9\n    │\n182 │         emit OracleSignerSet(signer);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/PlantOrganism.sol:637:9\n    │\n637 │         emit Rotated(oldSigner, newSigner, newIntake, newAction, nonce);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[incorrect-strict-equality]: dangerous strict equality check on an externally-influenced value\n    ╭▸ src/PlantOrganism.sol:643:13\n    │\n643 │         if (token.balanceOf(address(this)) != beforeBalance + amount) revert NonExactTransfer();\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/incorrect-strict-equality\n\n","passed":true},{"durationMs":8340,"exitCode":0,"name":"test","output":"No files changed, compilation skipped\n\nRan 3 tests for test/WeatherQuestion.t.sol:WeatherQuestionTest\n[PASS] test_coordinatesAtEdgesAndNegativeZeroFraction() (gas: 40309)\n[PASS] test_exactLisbonBodyAndKnownDate() (gas: 15004)\n[PASS] test_exactNegativeLatitudeAndLongitudeBody() (gas: 15401)\nSuite result: ok. 3 passed; 0 failed; 0 skipped; finished in 239.40µs (307.03µs CPU time)\n\nRan 4 tests for test/OracleConsumerConformance.t.sol:OracleConsumerConformanceTest\n[PASS] test_acceptsOriginalProtocolSignatureInCanonicalVerifier() (gas: 117518)\n[PASS] test_bytes32WeatherFixtureAcceptedUnderStipend() (gas: 1711342)\n[PASS] test_callbackSelectorIsCanonical() (gas: 3543)\n[PASS] test_digestMatchesTheProtocol() (gas: 13139)\nSuite result: ok. 4 passed; 0 failed; 0 skipped; finished in 1.76ms (704.67µs CPU time)\n\nRan 4 tests for test/PlantOrganism.t.sol:PlantRotationTest\n[PASS] test_oldSignerExpiresAtThirtyDaysWhileNewSignerKeepsPlantAlive() (gas: 19607309)\n[PASS] test_onlyCurrentSignerCanRotateNonceCannotReplay() (gas: 1150507)\n[PASS] test_pendingIntakeAndOldSignerWorkAfterRotation() (gas: 3791569)\n[PASS] test_rotationDomainBindsAllFieldsAndContract() (gas: 974532)\nSuite result: ok. 4 passed; 0 failed; 0 skipped; finished in 11.99ms (14.54ms CPU time)\n\nRan 11 tests for test/PlantOrganism.t.sol:PlantLifecycleTest\n[PASS] test_bindIsOnceAndOnlyDeployerWithMatchingHook() (gas: 325519)\n[PASS] test_birthFallbackAfterExactlyThreeEmptyDays() (gas: 314602)\n[PASS] test_birthMovesAtFivePercentWithoutMinimumStay() (gas: 660654)\n[PASS] test_cellBoundsAndZeroRejected() (gas: 609707)\n[PASS] test_challengerOnlyStrictlyLargerAndCanBeRepaired() (gas: 908869)\n[PASS] test_deathIsFinalAtThirtyDaysAndKeepsExits() (gas: 2607311)\n[PASS] test_deathUsesBindDayAfterLongUnboundPeriod() (gas: 285365)\n[PASS] test_moveUsesPendingCandidateAndRemainingCommittedBalances() (gas: 3190015)\n[PASS] test_noEarlyOrOutOfOrderSettleAndHeartbeat() (gas: 153139)\n[PASS] test_preBindDaysSkipInOneCallWithoutRead() (gas: 645106)\n[PASS] test_unboundCatchUpIsBoundedAndDoesNotRunBirth() (gas: 157524)\nSuite result: ok. 11 passed; 0 failed; 0 skipped; finished in 147.09ms (7.59ms CPU time)\n\nRan 12 tests for test/PlantOrganism.t.sol:PlantOracleTest\n[PASS] test_blockedKeeperDoesNotBlockSettleAndCanClaimLater() (gas: 2081462)\n[PASS] test_callbackSenderIdAndReplay() (gas: 2209467)\n[PASS] test_callbackStoresOnlyAndFitsStipend() (gas: 1788195)\nLogs:\n  callback gas including mock encoding: 94192\n\n[PASS] test_clearIncompleteUsesSameCounterAndCannotDiscardComplete() (gas: 2192267)\n[PASS] test_exactApprovalAndIntakeBody() (gas: 1419488)\n[PASS] test_feeAdvanceTracksDeficitThenRepaidAtSettle() (gas: 2142808)\n[PASS] test_incompleteHasNoBiologicalChangesThenThirdReads() (gas: 3416129)\n[PASS] test_intakeMustPullExactPrice() (gas: 1260208)\n[PASS] test_rejectsExpiredWrongDomainSignerTypeDayChainAndPanel() (gas: 2520990)\n[PASS] test_rejectsPreAskFutureTamperedAndMalformedAttestations() (gas: 2262291)\n[PASS] test_timeoutClearsAndWaitsSixHoursToReaskSameDay() (gas: 2210906)\n[PASS] test_unfundedAdvanceDoesNotSpendBackingOrBlockSettle() (gas: 2748398)\nSuite result: ok. 12 passed; 0 failed; 0 skipped; finished in 262.82ms (17.22ms CPU time)\n\nRan 5 tests for test/GasAndDeployment.t.sol:GasAndDeploymentTest\n[PASS] test_coldCallbackAndMaxHoursMoveSettleGas() (gas: 2895397)\nLogs:\n  cold callback gas: 94192\n\n[PASS] test_coldMaxHoursWithRepairedCandidateSettleGas() (gas: 3011387)\n[PASS] test_requestedStaticConstructorAndForbiddenOpcodes() (gas: 8026701)\n[PASS] test_settlementFitsStipendWithManyEligibleHolders() (gas: 38325240)\n[PASS] test_thirdIncompleteMoveAndAdvancePaymentGas() (gas: 3922619)\nSuite result: ok. 5 passed; 0 failed; 0 skipped; finished in 273.03ms (45.42ms CPU time)\n\nRan 8 tests for test/PlantVotingCommitment.t.sol:PlantVotingCommitmentTest\n[PASS] testFuzz_withdrawalAndRedepositCannotIncreasePower(uint96,uint96) (runs: 256, μ: 2575918, ~: 2576537)\nLogs:\n  Bound result 8465\n  Bound result 23906\n\n[PASS] test_borrowingAtBothHeartbeatAndSettleCannotCaptureGardenersPool() (gas: 4607864)\n[PASS] test_borrowingAtBothHeartbeatAndSettleCannotMove() (gas: 3607963)\n[PASS] test_borrowingAtBothHeartbeatAndSettleCannotVeto() (gas: 2518219)\n[PASS] test_loanAcrossPreviousSettleLosesPowerOnWithdrawal() (gas: 3138794)\n[PASS] test_rearmedDecoyCannotHideHoldersCandidate() (gas: 2717594)\n[PASS] test_thresholdUsesCommittedStakeAndNewDepositsMatureAtSettle() (gas: 3542686)\n[PASS] test_timeoutAndReaskCannotRestoreWithdrawnPower() (gas: 2783428)\nSuite result: ok. 8 passed; 0 failed; 0 skipped; finished in 272.94ms (286.78ms CPU time)\n\nRan 21 tests for test/PlantAdversarial.t.sol:PlantAdversarialTest\n[PASS] testFuzz_parkUnparkRoundTripCannotEarnWithoutSettlement(uint96,uint8) (runs: 1000, μ: 2388002, ~: 1955265)\nLogs:\n  Bound result 1919910060\n  Bound result 20\n\n[PASS] testFuzz_reservedWeatherBitsRejectedWithoutMutatingPending(uint8) (runs: 1000, μ: 1429351, ~: 1429342)\n[PASS] testFuzz_signedMalformedPanelsAreRejected(uint8) (runs: 1000, μ: 1426006, ~: 1425875)\n[PASS] test_answerMustBeExactlyOneWordIncludingTrailingData() (gas: 1573646)\n[PASS] test_bindRejectsNonContractWrongAndEmptyTokens() (gas: 1568748)\n[PASS] test_callbackAndSettlementEventsDescribeTheAppliedResult() (gas: 1675309)\n[PASS] test_consumedOracleUuidCannotBeReusedForSameDayRetry() (gas: 2262425)\n[PASS] test_deathClearsPendingAndPreservesGardenersAndFeeDebt() (gas: 2843784)\n[PASS] test_deferredAdvanceAndBountyRemainClaimableExactlyOnce() (gas: 2214832)\n[PASS] test_depositAfterAskMustMatureBeforeItCanChallenge() (gas: 2578253)\n[PASS] test_exactClockDriftToleranceAccepted() (gas: 1422939)\n[PASS] test_moveRewardsDepartingCellAndNeverIncomingCellForSameDay() (gas: 3449472)\n[PASS] test_moveThresholdRoundsUpWhenSupplyNotDivisibleByTwenty() (gas: 690617)\n[PASS] test_rejectedResultCanBeCorrectedWithoutBurningRequest() (gas: 1728599)\n[PASS] test_rotatedIntakeCannotAnswerPreviousIntakesRequest() (gas: 3452324)\n[PASS] test_thirdIncompleteReadsCandidateFromLatestRequest() (gas: 3715877)\n[PASS] test_timeoutExactSecondAndOldResultCannotPoisonReask() (gas: 1884932)\n[PASS] test_twoKeepersAdvancesSurviveTimeoutAndOnlyUnlockOnSettlement() (gas: 2623445)\n[PASS] test_unparkAfterCallbackDropsOutBeforeHoursAndKeepsPreviousReward() (gas: 2623843)\n[PASS] test_validityWindowBoundariesAndAskedAt() (gas: 1848640)\n[PASS] test_zeroAmountsAndOverdrawCannotChangeCustody() (gas: 595930)\nSuite result: ok. 21 passed; 0 failed; 0 skipped; finished in 273.05ms (816.04ms CPU time)\n\nRan 12 tests for test/PlantOrganism.t.sol:PlantRewardsTest\n[PASS] testFuzz_floorConservationAndBurnCustody(uint96,uint96,uint24) (runs: 1000, μ: 2336210, ~: 2348028)\nLogs:\n  Bound result 5048256185\n\n[PASS] testFuzz_hourlyAccountingMatchesIndependentModel(uint24,uint24) (runs: 1000, μ: 1800932, ~: 1803743)\n[PASS] test_departuresDropOutAndReparkingCannotCaptureDay() (gas: 4107169)\n[PASS] test_finalRedemptionDoesNotDivideByZeroOrReleaseBurnedPlant() (gas: 2449577)\n[PASS] test_gardenerDustRoundsToBackingAndAccountsCloseExactly() (gas: 5259461)\n[PASS] test_hourZeroFirstRainCapAndSunRequiresWater() (gas: 4841001)\n[PASS] test_noGardenersSendsEntireSipToBacking() (gas: 1548055)\n[PASS] test_nonExactTokenTransfersRejected() (gas: 274277)\n[PASS] test_oldCellKeepsItsRewardsAfterMoveAndLazyActivation() (gas: 5794681)\n[PASS] test_parkSettleClaimProRataAndMidDayJoinsWaitOneSettle() (gas: 3184396)\n[PASS] test_redeemRetainsPlantAndTenPercentAndFloorMonotonic() (gas: 2200068)\n[PASS] test_reentrancyFromTokensAndIntakeBlocked() (gas: 2288899)\nSuite result: ok. 12 passed; 0 failed; 0 skipped; finished in 273.11ms (546.21ms CPU time)\n\nRan 7 tests for test/PlantAssumptions.t.sol:PlantAssumptionsTest\n[PASS] test_explicitZeroCellQuestionRemainsInvalid() (gas: 25324)\n[PASS] test_oldRotationSignatureAccepted() (gas: 887620)\n[PASS] test_oldSourceWindowBacklogIsAlreadyDead() (gas: 401886)\n[PASS] test_preBindDonationsHaveNoExit() (gas: 196001)\n[PASS] test_redeemedSupplyStillCountsInThreshold() (gas: 3777707)\n[PASS] test_rotatedIntakeCanPriceAbovePotAndPullAdvance() (gas: 2308589)\n[PASS] test_timeoutsDoNotCountAndEventuallyDie() (gas: 8667659)\nSuite result: ok. 7 passed; 0 failed; 0 skipped; finished in 273.15ms (7.38ms CPU time)\n\nRan 13 tests for test/PlantVotingRevision.t.sol:PlantVotingRevisionTest\n[PASS] testFuzz_repeatedTopUpsCannotIncreaseCommittedStake(uint96) (runs: 1000, μ: 3066096, ~: 3065992)\nLogs:\n  Bound result 18991\n\n[PASS] testFuzz_withdrawalUsesOwnQueuedStakeBeforeCommitment(uint96) (runs: 1000, μ: 2472318, ~: 2472890)\nLogs:\n  Bound result 238638432481119841779\n\n[PASS] test_atomicCurrentCellTopUpCannotVetoCommittedCandidate() (gas: 2367496)\n[PASS] test_atomicDestinationTopUpCannotMoveOrCaptureRewards() (gas: 3846415)\n[PASS] test_currentCellWithdrawalBreaksTieForCommittedCandidate() (gas: 2234283)\n[PASS] test_emptyCapturedCandidateFallsBackToRepairedLiveCandidate() (gas: 2598453)\n[PASS] test_fallbackCannotUseNewPostHeartbeatStake() (gas: 3095203)\n[PASS] test_questionIsEmptyBeforeBindAndDuringBirth() (gas: 411465)\n[PASS] test_replacementHolderCannotInheritWithdrawnVotingPower() (gas: 3329707)\n[PASS] test_thirdIncompleteAlsoUsesRepairedCandidate() (gas: 4181413)\n[PASS] test_thirdIncompleteReadsBeforePromotingFreshDeposits() (gas: 4119122)\n[PASS] test_timeoutRetryCannotMatureRequestOrCooldownDeposits() (gas: 3510713)\n[PASS] test_withdrawalRemovesPowerAndReaskCannotRestoreIt() (gas: 3678490)\nSuite result: ok. 13 passed; 0 failed; 0 skipped; finished in 273.04ms (560.50ms CPU time)\n\nRan 1 test for test/PlantInvariant.t.sol:PlantInvariantTest\n[PASS] invariant_conservationAndPermanentBurnCustody() (runs: 128, calls: 8192, reverts: 0)\n\n╭--------------+---------------+-------+---------+----------╮\n| Contract     | Selector      | Calls | Reverts | Discards |\n+===========================================================+\n| PlantHandler | challenge     | 1062  | 0       | 0        |\n|--------------+---------------+-------+---------+----------|\n| PlantHandler | checkpointAll | 995   | 0       | 0        |\n|--------------+---------------+-------+---------+----------|\n| PlantHandler | claim         | 1053  | 0       | 0        |\n|--------------+---------------+-------+---------+----------|\n| PlantHandler | fund          | 1032  | 0       | 0        |\n|--------------+---------------+-------+---------+----------|\n| PlantHandler | park          | 1065  | 0       | 0        |\n|--------------+---------------+-------+---------+----------|\n| PlantHandler | redeem        | 991   | 0       | 0        |\n|--------------+---------------+-------+---------+----------|\n| PlantHandler | unpark        | 1021  | 0       | 0        |\n|--------------+---------------+-------+---------+----------|\n| PlantHandler | weather       | 973   | 0       | 0        |\n╰--------------+---------------+-------+---------+----------╯\n\nLogs:\n  Bound result 6481\n  Bound result 5283\n  Bound result 8283\n  Bound result 719467\n  Bound result 5819\n  Bound result 8124\n  Bound result 3353\n  Bound result 5628\n  Bound result 14754\n  Bound result 16164\n  Bound result 914\n  Bound result 213463962154504527072\n  Bound result 8145\n  Bound result 1094\n  Bound result 116389448\n  Bound result 638\n  Bound result 519560370\n  Bound result 15156\n\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 1.61s (1.61s CPU time)\n\nRan 1 test for test/PlantStateMachine.t.sol:PlantStateMachineInvariantTest\n[PASS] invariant_independentCustodyAndSolvency() (runs: 256, calls: 24576, reverts: 0)\n\n╭--------------------------+---------------+-------+---------+----------╮\n| Contract                 | Selector      | Calls | Reverts | Discards |\n+=======================================================================+\n| PlantStateMachineHandler | answer        | 2046  | 0       | 0        |\n|--------------------------+---------------+-------+---------+----------|\n| PlantStateMachineHandler | challenge     | 2004  | 0       | 0        |\n|--------------------------+---------------+-------+---------+----------|\n| PlantStateMachineHandler | claim         | 2004  | 0       | 0        |\n|--------------------------+---------------+-------+---------+----------|\n| PlantStateMachineHandler | clear         | 2080  | 0       | 0        |\n|--------------------------+---------------+-------+---------+----------|\n| PlantStateMachineHandler | donate        | 2111  | 0       | 0        |\n|--------------------------+---------------+-------+---------+----------|\n| PlantStateMachineHandler | elapse        | 2044  | 0       | 0        |\n|--------------------------+---------------+-------+---------+----------|\n| PlantStateMachineHandler | park          | 2027  | 0       | 0        |\n|--------------------------+---------------+-------+---------+----------|\n| PlantStateMachineHandler | redeem        | 2049  | 0       | 0        |\n|--------------------------+---------------+-------+---------+----------|\n| PlantStateMachineHandler | request       | 2106  | 0       | 0        |\n|--------------------------+---------------+-------+---------+----------|\n| PlantStateMachineHandler | settle        | 2094  | 0       | 0        |\n|--------------------------+---------------+-------+---------+----------|\n| PlantStateMachineHandler | transferPlant | 1999  | 0       | 0        |\n|--------------------------+---------------+-------+---------+----------|\n| PlantStateMachineHandler | unpark        | 2012  | 0       | 0        |\n╰--------------------------+---------------+-------+---------+----------╯\n\nLogs:\n  Bound result 3000000000000000000\n  Bound result 200000000000000000000\n  Bound result 109757219298253544884\n  Bound result 643884375054\n  Bound result 10140\n  Bound result 75531\n  Bound result 16361\n  Bound result 8746\n  Bound result 58815\n  Bound result 206997742589\n  Bound result 95684\n  Bound result 10326\n  Bound result 10101\n  Bound result 11044\n  Bound result 6203\n  Bound result 695\n  Bound result 2266\n  Bound result 6673\n  Bound result 16710\n  Bound result 4771\n  Bound result 43\n  Bound result 36524\n  Bound result 26827\n  Bound result 9891\n  Bound result 10497\n  Bound result 6981\n  Bound result 4751\n  Bound result 18798\n  Bound result 8375\n  Bound result 17536\n  Bound result 8930\n  Bound result 71759626513944166723\n  Bound result 23486\n  Bound result 651033139523906533214\n  Bound result 8431\n  Bound result 1359182280\n  Bound result 9720\n  Bound result 10456\n  Bound result 707793353679303198935\n  Bound result 15752\n  Bound result 13854\n  Bound result 446850514594418610792\n  Bound result 4623009587538\n  Bound result 1022079\n  Bound result 224354594715689886940\n  Bound result 3072453408173938335\n  Bound result 23\n  Bound result 13976\n  Bound result 18969\n  Bound result 199999995376990432382\n  Bound result 224354594715689886940\n  Bound result 23486\n  Bound result 10456\n  Bound result 239209640969483004787\n  Bound result 292206645676812393653\n  Bound result 553149486256463480672\n\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 8.26s (8.25s CPU time)\n\nRan 13 test suites in 8.26s (11.93s CPU time): 102 tests passed, 0 failed, 0 skipped (102 total tests)\n","passed":true},{"durationMs":47,"exitCode":0,"name":"source-index","output":"{\"v\":1,\"entryPoints\":[\"PlantOrganism.bind(address)\",\"PlantOrganism.challenge(uint32)\",\"PlantOrganism.checkpoint(uint32,address)\",\"PlantOrganism.claim()\",\"PlantOrganism.claim(uint32[])\",\"PlantOrganism.clearPending()\",\"PlantOrganism.die()\",\"PlantOrganism.heartbeat()\",\"PlantOrganism.onOracleResult(bytes32,(bytes32,uint256,bytes32,uint8,bytes,uint256,uint64,uint64,bytes32,bytes32,uint16,uint16,uint16,uint64,uint64),bytes)\",\"PlantOrganism.park(uint32,uint256)\",\"PlantOrganism.redeem(uint256)\",\"PlantOrganism.rotate(address,address,bytes32,uint256,bytes)\",\"PlantOrganism.settle()\",\"PlantOrganism.settle(uint32)\",\"PlantOrganism.unpark(uint32,uint256)\"],\"files\":{\".gitignore\":3,\"DEPENDENCIES.md\":7,\"README.md\":30,\"foundry.toml\":19,\"launch.json\":17,\"src/OracleAttestation.sol\":221,\"src/PlantOrganism.sol\":645,\"src/WeatherQuestion.sol\":83,\"src/interfaces/IIntake.sol\":14,\"test/GasAndDeployment.t.sol\":123,\"test/OracleConsumerConformance.t.sol\":125,\"test/PlantAdversarial.t.sol\":420,\"test/PlantAssumptions.t.sol\":89,\"test/PlantInvariant.t.sol\":166,\"test/PlantOrganism.t.sol\":922,\"test/PlantStateMachine.t.sol\":394,\"test/PlantVotingCommitment.t.sol\":196,\"test/PlantVotingRevision.t.sol\":226,\"test/README.md\":55,\"test/WeatherQuestion.t.sol\":36,\"test/fixtures/weather.json\":11,\"test/mocks/Mocks.sol\":137},\"excluded\":[\"lib/\",\"node_modules/\"],\"truncated\":false}","passed":true}],"detail":"all checks passed","evaluation":"checks","profile":"foundry","status":"accepted","submissionHash":"5f0df6cff4f58ef2baf0761e952b41633c13fc9f948a4a5bb48737e38e62d1ff","verifiedTreeHash":"83c6e5419937cf57233bd04598f65045b3316ebb","verifierVersion":"0.1.0+ad90ce4c"},{"checks":[{"durationMs":26602,"exitCode":0,"name":"build","output":"Compiling 51 files with Solc 0.8.26\nSolc 0.8.26 finished in 26.43s\nCompiler run successful!\nwarning[unsafe-typecast]: typecast can truncate values\n   ╭▸ src/WeatherQuestion.sol:12:21\n   │\n12 │         int16 lat = int16(uint16(cell >> 16));\n   │                     ━━━━━━━━━━━━━━━━━━━━━━━━━\n   │\n   ├ note: consider disabling this lint if you're certain the cast is safe\n   │       \n   │       // casting to 'int16' is safe because [explain why]\n   │       // forge-lint: disable-next-line(unsafe-typecast)\n   │       \n   │       \n   ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n   ╭▸ src/WeatherQuestion.sol:12:27\n   │\n12 │         int16 lat = int16(uint16(cell >> 16));\n   │                           ━━━━━━━━━━━━━━━━━━\n   │\n   ├ note: consider disabling this lint if you're certain the cast is safe\n   │       \n   │       // casting to 'uint16' is safe because [explain why]\n   │       // forge-lint: disable-next-line(unsafe-typecast)\n   │       \n   │       \n   ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n   ╭▸ src/WeatherQuestion.sol:13:21\n   │\n13 │         int16 lon = int16(uint16(cell));\n   │                     ━━━━━━━━━━━━━━━━━━━\n   │\n   ├ note: consider disabling this lint if you're certain the cast is safe\n   │       \n   │       // casting to 'int16' is safe because [explain why]\n   │       // forge-lint: disable-next-line(unsafe-typecast)\n   │       \n   │       \n   ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n   ╭▸ src/WeatherQuestion.sol:13:27\n   │\n13 │         int16 lon = int16(uint16(cell));\n   │                           ━━━━━━━━━━━━\n   │\n   ├ note: consider disabling this lint if you're certain the cast is safe\n   │       \n   │       // casting to 'uint16' is safe because [explain why]\n   │       // forge-lint: disable-next-line(unsafe-typecast)\n   │       \n   │       \n   ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n   ╭▸ src/WeatherQuestion.sol:19:29\n   │\n19 │         uint256 magnitude = uint256(milli < 0 ? -milli : milli);\n   │                             ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n   │\n   ├ note: consider disabling this lint if you're certain the cast is safe\n   │       \n   │       // casting to 'uint256' is safe because [explain why]\n   │       // forge-lint: disable-next-line(unsafe-typecast)\n   │       \n   │       \n   ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n   ╭▸ src/WeatherQuestion.sol:26:29\n   │\n26 │             b[--i] = bytes1(uint8(48 + n % 10));\n   │                             ━━━━━━━━━━━━━━━━━━\n   │\n   ├ note: consider disabling this lint if you're certain the cast is safe\n   │       \n   │       // casting to 'uint8' is safe because [explain why]\n   │       // forge-lint: disable-next-line(unsafe-typecast)\n   │       \n   │       \n   ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[divide-before-multiply]: division before multiplication may lose precision\n   ╭▸ src/WeatherQuestion.sol:39:27\n   │\n39 │         uint256 y = yoe + era * 400;\n   │                           ━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/divide-before-multiply\n\nwarning[divide-before-multiply]: division before multiplication may lose precision\n   ╭▸ src/WeatherQuestion.sol:40:30\n   │\n40 │         uint256 doy = doe - (365 * yoe + yoe / 4 - yoe / 100);\n   │                              ━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/divide-before-multiply\n\nwarning[divide-before-multiply]: division before multiplication may lose precision\n   ╭▸ src/WeatherQuestion.sol:42:28\n   │\n42 │         uint256 d = doy - (153 * mp + 2) / 5 + 1;\n   │                            ━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/divide-before-multiply\n\nwarning[unsafe-typecast]: typecast can truncate values\n   ╭▸ src/WeatherQuestion.sol:50:40\n   │\n50 │         string memory lat = coordinate(int16(uint16(cell >> 16)));\n   │                                        ━━━━━━━━━━━━━━━━━━━━━━━━━\n   │\n   ├ note: consider disabling this lint if you're certain the cast is safe\n   │       \n   │       // casting to 'int16' is safe because [explain why]\n   │       // forge-lint: disable-next-line(unsafe-typecast)\n   │       \n   │       \n   ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n   ╭▸ src/WeatherQuestion.sol:50:46\n   │\n50 │         string memory lat = coordinate(int16(uint16(cell >> 16)));\n   │                                              ━━━━━━━━━━━━━━━━━━\n   │\n   ├ note: consider disabling this lint if you're certain the cast is safe\n   │       \n   │       // casting to 'uint16' is safe because [explain why]\n   │       // forge-lint: disable-next-line(unsafe-typecast)\n   │       \n   │       \n   ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n   ╭▸ src/WeatherQuestion.sol:51:40\n   │\n51 │         string memory lon = coordinate(int16(uint16(cell)));\n   │                                        ━━━━━━━━━━━━━━━━━━━\n   │\n   ├ note: consider disabling this lint if you're certain the cast is safe\n   │       \n   │       // casting to 'int16' is safe because [explain why]\n   │       // forge-lint: disable-next-line(unsafe-typecast)\n   │       \n   │       \n   ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n   ╭▸ src/WeatherQuestion.sol:51:46\n   │\n51 │         string memory lon = coordinate(int16(uint16(cell)));\n   │                                              ━━━━━━━━━━━━\n   │\n   ├ note: consider disabling this lint if you're certain the cast is safe\n   │       \n   │       // casting to 'uint16' is safe because [explain why]\n   │       // forge-lint: disable-next-line(unsafe-typecast)\n   │       \n   │       \n   ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/PlantOrganism.sol:167:16\n    │\n167 │         return uint32(block.timestamp / 1 days);\n    │                ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint32' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[missing-zero-check]: address parameter is used in a state write or value transfer without a zero-address check\n    ╭▸ src/PlantOrganism.sol:170:19\n    │\n170 │     function bind(address hook_) external nonReentrant {\n    │                   ━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/missing-zero-check\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/PlantOrganism.sol:206:25\n    │\n206 │         require(held <= uint256(type(int256).max) && reserved <= uint256(type(int256).max));\n    │                         ━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/PlantOrganism.sol:206:66\n    │\n206 │         require(held <= uint256(type(int256).max) && reserved <= uint256(type(int256).max));\n    │                                                                  ━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/PlantOrganism.sol:207:16\n    │\n207 │         return int256(held) - int256(reserved);\n    │                ━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'int256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/PlantOrganism.sol:207:31\n    │\n207 │         return int256(held) - int256(reserved);\n    │                               ━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'int256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/PlantOrganism.sol:212:24\n    │\n212 │         return p > 0 ? uint256(p) : 0;\n    │                        ━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/PlantOrganism.sol:511:13\n    │\n511 │             emit Checkpointed(holder, cell, credit);\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/PlantOrganism.sol:279:13\n    │\n279 │             emit Challenged(cell);\n    │             ━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/PlantOrganism.sol:249:9\n    │\n249 │         emit Parked(msg.sender, cell, amount);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/PlantOrganism.sol:267:9\n    │\n267 │         emit Unparked(msg.sender, cell, amount);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[reentrancy-no-eth]: external call can be reentered before `pending` is updated\n    ╭▸ src/PlantOrganism.sol:302:14\n    │\n302 │           id = intake.request(\n    │ ┏━━━━━━━━━━━━━━┛\n303 │ ┃             action,\n304 │ ┃             requestBody(location, day),\n305 │ ┃             IIntake.Callback(address(this), this.onOracleResult.selector),\n306 │ ┃             address(IMD),\n307 │ ┃             price\n308 │ ┃         );\n    │ ┗━━━━━━━━━┛\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-no-eth\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/PlantOrganism.sol:289:13\n    │\n289 │         if (block.timestamp < retryAt) revert RetryLater();\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/PlantOrganism.sol:298:13\n    │\n298 │             emit Advanced(msg.sender, advance);\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/PlantOrganism.sol:313:9\n    │\n313 │         emit Asked(id, day, location, challenger, msg.sender, price);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[incorrect-strict-equality]: dangerous strict equality check on an externally-influenced value\n    ╭▸ src/PlantOrganism.sol:310:13\n    │\n310 │         if (IMD.balanceOf(address(this)) != beforeBalance - price) revert NonExactTransfer();\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/incorrect-strict-equality\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/PlantOrganism.sol:312:54\n    │\n312 │             Pending(id, day, challenger, msg.sender, uint64(block.timestamp), address(intake), true, false, bytes32(0));\n    │                                                      ━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint64' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/PlantOrganism.sol:330:20\n    │\n330 │                 && block.timestamp < signerAcceptedUntil[recovered]\n    │                    ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/PlantOrganism.sol:351:9\n    │\n351 │         emit Received(requestId, a.requestId, word);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/PlantOrganism.sol:341:13\n    │\n341 │         if (uint32(w >> 96) != p.day) revert WrongDay();\n    │             ━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint32' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/PlantOrganism.sol:360:17\n    │\n360 │             if (block.timestamp < uint256(pending.askedAt) + ORACLE_TIMEOUT) revert NotTimedOut();\n    │                 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[uninitialized-local]: local variable is read before being initialized\n    ╭▸ src/PlantOrganism.sol:432:33\n    │\n432 │         emit Settled(day, word, sips, backing, water, location);\n    │                                 ━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/uninitialized-local\n\nwarning[uninitialized-local]: local variable is read before being initialized\n    ╭▸ src/PlantOrganism.sol:415:30\n    │\n415 │         _distribute(oldCell, pool);\n    │                              ━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/uninitialized-local\n\nwarning[uninitialized-local]: local variable is read before being initialized\n    ╭▸ src/PlantOrganism.sol:425:13\n    │\n425 │         if (caller != address(0)) {\n    │             ━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/uninitialized-local\n\nwarning[uninitialized-local]: local variable is read before being initialized\n    ╭▸ src/PlantOrganism.sol:432:27\n    │\n432 │         emit Settled(day, word, sips, backing, water, location);\n    │                           ━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/uninitialized-local\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/PlantOrganism.sol:388:13\n    │\n388 │             emit EmptyCatchUp(day, end);\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/PlantOrganism.sol:406:17\n    │\n406 │                 emit Incomplete(day, count);\n    │                 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/PlantOrganism.sol:370:9\n    │\n370 │         emit Cleared(id, day, retryAt);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/PlantOrganism.sol:489:9\n    │\n489 │         emit Rewarded(cell, pool, reserved);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/PlantOrganism.sol:460:13\n    │\n460 │             emit Moved(current, candidate);\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/PlantOrganism.sol:420:13\n    │\n420 │             emit Moved(0, location);\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/PlantOrganism.sol:429:13\n    │\n429 │             emit Bounty(caller, bounty);\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/PlantOrganism.sol:551:13\n    │\n551 │             emit Paid(holder, credit, advance);\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/PlantOrganism.sol:557:13\n    │\n557 │             emit PaymentDeferred(holder, credit + advance);\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/PlantOrganism.sol:432:9\n    │\n432 │         emit Settled(day, word, sips, backing, water, location);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/PlantOrganism.sol:386:61\n    │\n386 │             uint32 end = hook == address(0) ? today() - 1 : uint32(Math.min(today() - 1, bindDay - 1));\n    │                                                             ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint32' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/PlantOrganism.sol:449:17\n    │\n449 │         water = uint8(wet);\n    │                 ━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint8' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[uninitialized-local]: local variable is read before being initialized\n    ╭▸ src/PlantOrganism.sol:545:22\n    │\n545 │         if (credit + advance == 0) return;\n    │                      ━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/uninitialized-local\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/PlantOrganism.sol:574:9\n    │\n574 │         emit Redeemed(msg.sender, amount, payout, dead);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/PlantOrganism.sol:589:13\n    │\n589 │             emit Died(today(), merged);\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/PlantOrganism.sol:591:13\n    │\n591 │             emit PotMerged(merged);\n    │             ━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/OracleAttestation.sol:182:9\n    │\n182 │         emit OracleSignerSet(signer);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/PlantOrganism.sol:623:9\n    │\n623 │         emit Rotated(oldSigner, newSigner, newIntake, newAction, nonce);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[incorrect-strict-equality]: dangerous strict equality check on an externally-influenced value\n    ╭▸ src/PlantOrganism.sol:629:13\n    │\n629 │         if (token.balanceOf(address(this)) != beforeBalance + amount) revert NonExactTransfer();\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/incorrect-strict-equality\n\n","passed":true},{"durationMs":1731,"exitCode":0,"name":"test","output":"No files changed, compilation skipped\n\nRan 3 tests for test/WeatherQuestion.t.sol:WeatherQuestionTest\n[PASS] test_coordinatesAtEdgesAndNegativeZeroFraction() (gas: 40309)\n[PASS] test_exactLisbonBodyAndKnownDate() (gas: 15004)\n[PASS] test_exactNegativeLatitudeAndLongitudeBody() (gas: 15401)\nSuite result: ok. 3 passed; 0 failed; 0 skipped; finished in 391.20µs (509.71µs CPU time)\n\nRan 4 tests for test/OracleConsumerConformance.t.sol:OracleConsumerConformanceTest\n[PASS] test_acceptsOriginalProtocolSignatureInCanonicalVerifier() (gas: 117518)\n[PASS] test_bytes32WeatherFixtureAcceptedUnderStipend() (gas: 1711401)\n[PASS] test_callbackSelectorIsCanonical() (gas: 3543)\n[PASS] test_digestMatchesTheProtocol() (gas: 13139)\nSuite result: ok. 4 passed; 0 failed; 0 skipped; finished in 2.87ms (1.77ms CPU time)\n\nRan 11 tests for test/PlantOrganism.t.sol:PlantLifecycleTest\n[PASS] test_bindIsOnceAndOnlyDeployerWithMatchingHook() (gas: 325519)\n[PASS] test_birthFallbackAfterExactlyThreeEmptyDays() (gas: 305969)\n[PASS] test_birthMovesAtFivePercentWithoutMinimumStay() (gas: 659503)\n[PASS] test_cellBoundsAndZeroRejected() (gas: 609033)\n[PASS] test_challengerOnlyStrictlyLargerAndCanBeRepaired() (gas: 907339)\n[PASS] test_deathIsFinalAtThirtyDaysAndKeepsExits() (gas: 2597183)\n[PASS] test_deathUsesBindDayAfterLongUnboundPeriod() (gas: 285387)\n[PASS] test_moveUsesPendingCandidateButLiveBalances() (gas: 2969440)\n[PASS] test_noEarlyOrOutOfOrderSettleAndHeartbeat() (gas: 153141)\n[PASS] test_preBindDaysSkipInOneCallWithoutRead() (gas: 644624)\n[PASS] test_unboundCatchUpIsBoundedAndDoesNotRunBirth() (gas: 157507)\nSuite result: ok. 11 passed; 0 failed; 0 skipped; finished in 6.09ms (11.37ms CPU time)\n\nRan 4 tests for test/PlantOrganism.t.sol:PlantRotationTest\n[PASS] test_oldSignerExpiresAtThirtyDaysWhileNewSignerKeepsPlantAlive() (gas: 19271508)\n[PASS] test_onlyCurrentSignerCanRotateNonceCannotReplay() (gas: 1150397)\n[PASS] test_pendingIntakeAndOldSignerWorkAfterRotation() (gas: 3771780)\n[PASS] test_rotationDomainBindsAllFieldsAndContract() (gas: 974444)\nSuite result: ok. 4 passed; 0 failed; 0 skipped; finished in 20.28ms (22.60ms CPU time)\n\nRan 3 tests for test/GasAndDeployment.t.sol:GasAndDeploymentTest\n[PASS] test_coldCallbackAndMaxHoursMoveSettleGas() (gas: 2574374)\nLogs:\n  cold callback gas: 94192\n  cold settle, 24 sips and move: 256652\n\n[PASS] test_requestedStaticConstructorAndForbiddenOpcodes() (gas: 7928021)\n[PASS] test_thirdIncompleteMoveAndAdvancePaymentGas() (gas: 3665334)\nLogs:\n  cold third incomplete with move and advance repayment: 244632\n\nSuite result: ok. 3 passed; 0 failed; 0 skipped; finished in 20.79ms (24.81ms CPU time)\n\nRan 12 tests for test/PlantOrganism.t.sol:PlantOracleTest\n[PASS] test_blockedKeeperDoesNotBlockSettleAndCanClaimLater() (gas: 2071228)\n[PASS] test_callbackSenderIdAndReplay() (gas: 2199209)\n[PASS] test_callbackStoresOnlyAndFitsStipend() (gas: 1780168)\nLogs:\n  callback gas including mock encoding: 94192\n  settle gas: 289534\n\n[PASS] test_clearIncompleteUsesSameCounterAndCannotDiscardComplete() (gas: 2182068)\n[PASS] test_exactApprovalAndIntakeBody() (gas: 1418919)\n[PASS] test_feeAdvanceTracksDeficitThenRepaidAtSettle() (gas: 2134321)\n[PASS] test_incompleteHasNoBiologicalChangesThenThirdReads() (gas: 3236176)\n[PASS] test_intakeMustPullExactPrice() (gas: 1259699)\n[PASS] test_rejectsExpiredWrongDomainSignerTypeDayChainAndPanel() (gas: 2520151)\n[PASS] test_rejectsPreAskFutureTamperedAndMalformedAttestations() (gas: 2261542)\n[PASS] test_timeoutClearsAndWaitsSixHoursToReaskSameDay() (gas: 2200708)\n[PASS] test_unfundedAdvanceDoesNotSpendBackingOrBlockSettle() (gas: 2724601)\nSuite result: ok. 12 passed; 0 failed; 0 skipped; finished in 43.25ms (20.00ms CPU time)\n\nRan 12 tests for test/PlantOrganism.t.sol:PlantRewardsTest\n[PASS] testFuzz_floorConservationAndBurnCustody(uint96,uint96,uint24) (runs: 256, μ: 2328197, ~: 2337890)\nLogs:\n  Bound result 138682730577283944089\n\n[PASS] testFuzz_hourlyAccountingMatchesIndependentModel(uint24,uint24) (runs: 256, μ: 1790152, ~: 1793559)\n[PASS] test_departuresDropOutAndReparkingCannotCaptureDay() (gas: 4076157)\n[PASS] test_finalRedemptionDoesNotDivideByZeroOrReleaseBurnedPlant() (gas: 2439439)\n[PASS] test_gardenerDustRoundsToBackingAndAccountsCloseExactly() (gas: 5209191)\n[PASS] test_hourZeroFirstRainCapAndSunRequiresWater() (gas: 4779386)\n[PASS] test_noGardenersSendsEntireSipToBacking() (gas: 1529791)\n[PASS] test_nonExactTokenTransfersRejected() (gas: 274277)\n[PASS] test_oldCellKeepsItsRewardsAfterMoveAndLazyActivation() (gas: 4941414)\n[PASS] test_parkSettleClaimProRataAndMidDayJoinsWaitOneSettle() (gas: 3164621)\n[PASS] test_redeemRetainsPlantAndTenPercentAndFloorMonotonic() (gas: 2189930)\n[PASS] test_reentrancyFromTokensAndIntakeBlocked() (gas: 2278761)\nSuite result: ok. 12 passed; 0 failed; 0 skipped; finished in 71.77ms (145.42ms CPU time)\n\nRan 1 test for test/PlantInvariant.t.sol:PlantInvariantTest\n[PASS] invariant_conservationAndPermanentBurnCustody() (runs: 128, calls: 8192, reverts: 0)\n\n╭--------------+---------------+-------+---------+----------╮\n| Contract     | Selector      | Calls | Reverts | Discards |\n+===========================================================+\n| PlantHandler | challenge     | 1065  | 0       | 0        |\n|--------------+---------------+-------+---------+----------|\n| PlantHandler | checkpointAll | 1058  | 0       | 0        |\n|--------------+---------------+-------+---------+----------|\n| PlantHandler | claim         | 1022  | 0       | 0        |\n|--------------+---------------+-------+---------+----------|\n| PlantHandler | fund          | 1008  | 0       | 0        |\n|--------------+---------------+-------+---------+----------|\n| PlantHandler | park          | 1066  | 0       | 0        |\n|--------------+---------------+-------+---------+----------|\n| PlantHandler | redeem        | 1028  | 0       | 0        |\n|--------------+---------------+-------+---------+----------|\n| PlantHandler | unpark        | 975   | 0       | 0        |\n|--------------+---------------+-------+---------+----------|\n| PlantHandler | weather       | 970   | 0       | 0        |\n╰--------------+---------------+-------+---------+----------╯\n\nLogs:\n  Bound result 4159\n  Bound result 514264337923053876093\n  Bound result 2513700414\n  Bound result 9621\n  Bound result 157042771797158535973\n  Bound result 432977322435\n  Bound result 5428\n  Bound result 11344\n  Bound result 1051522198954\n  Bound result 8282\n  Bound result 1852\n  Bound result 9620\n  Bound result 126145142\n  Bound result 11716\n  Bound result 661966299497965372224\n  Bound result 9935\n\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 1.64s (1.64s CPU time)\n\nRan 8 test suites in 1.64s (1.80s CPU time): 50 tests passed, 0 failed, 0 skipped (50 total tests)\n","passed":true},{"durationMs":36,"exitCode":0,"name":"source-index","output":"{\"v\":1,\"entryPoints\":[\"PlantOrganism.bind(address)\",\"PlantOrganism.challenge(uint32)\",\"PlantOrganism.checkpoint(uint32,address)\",\"PlantOrganism.claim()\",\"PlantOrganism.claim(uint32[])\",\"PlantOrganism.clearPending()\",\"PlantOrganism.die()\",\"PlantOrganism.heartbeat()\",\"PlantOrganism.onOracleResult(bytes32,(bytes32,uint256,bytes32,uint8,bytes,uint256,uint64,uint64,bytes32,bytes32,uint16,uint16,uint16,uint64,uint64),bytes)\",\"PlantOrganism.park(uint32,uint256)\",\"PlantOrganism.redeem(uint256)\",\"PlantOrganism.rotate(address,address,bytes32,uint256,bytes)\",\"PlantOrganism.settle()\",\"PlantOrganism.settle(uint32)\",\"PlantOrganism.unpark(uint32,uint256)\"],\"files\":{\".gitignore\":3,\"DEPENDENCIES.md\":7,\"README.md\":30,\"foundry.toml\":19,\"launch.json\":17,\"src/OracleAttestation.sol\":221,\"src/PlantOrganism.sol\":631,\"src/WeatherQuestion.sol\":83,\"src/interfaces/IIntake.sol\":14,\"test/GasAndDeployment.t.sol\":95,\"test/OracleConsumerConformance.t.sol\":126,\"test/PlantInvariant.t.sol\":166,\"test/PlantOrganism.t.sol\":909,\"test/WeatherQuestion.t.sol\":36,\"test/fixtures/weather.json\":11,\"test/mocks/Mocks.sol\":137},\"excluded\":[\"lib/\",\"node_modules/\"],\"truncated\":false}","passed":true},{"durationMs":4403,"exitCode":0,"name":"slither","output":"[high/medium] reentrancy-balance at src/PlantOrganism.sol:283: Reentrancy in PlantOrganism.heartbeat() (src/PlantOrganism.sol#283-314):\n[medium/medium] divide-before-multiply at src/WeatherQuestion.sol:33: WeatherQuestion.date(uint32) (src/WeatherQuestion.sol#33-46) performs a multiplication on the result of a division:\n[medium/medium] divide-before-multiply at src/WeatherQuestion.sol:33: WeatherQuestion.date(uint32) (src/WeatherQuestion.sol#33-46) performs a multiplication on the result of a division:\n[medium/medium] divide-before-multiply at src/WeatherQuestion.sol:33: WeatherQuestion.date(uint32) (src/WeatherQuestion.sol#33-46) performs a multiplication on the result of a division:\n[medium/high] incorrect-equality at src/PlantOrganism.sol:536: PlantOrganism._pay(address) (src/PlantOrganism.sol#536-559) uses a dangerous strict equality:\n[medium/medium] reentrancy-no-eth at src/PlantOrganism.sol:283: Reentrancy in PlantOrganism.heartbeat() (src/PlantOrganism.sol#283-314):\n[medium/medium] uninitialized-local at src/PlantOrganism.sol:394: PlantOrganism._settle().caller (src/PlantOrganism.sol#394) is a local variable never initialized\n[medium/medium] uninitialized-local at src/PlantOrganism.sol:538: PlantOrganism._pay(address).advance (src/PlantOrganism.sol#538) is a local variable never initialized\n[medium/medium] uninitialized-local at src/PlantOrganism.sol:393: PlantOrganism._settle().word (src/PlantOrganism.sol#393) is a local variable never initialized\n[medium/medium] uninitialized-local at src/PlantOrganism.sol:396: PlantOrganism._settle().sips (src/PlantOrganism.sol#396) is a local variable never initialized\n[medium/medium] uninitialized-local at src/PlantOrganism.sol:395: PlantOrganism._settle().pool (src/PlantOrganism.sol#395) is a local variable never initialized\n[medium/medium] unused-return at src/PlantOrganism.sol:316: PlantOrganism.onOracleResult(bytes32,OracleAttestation.Attestation,bytes) (src/PlantOrganism.sol#316-352) ignores return value by (recovered,err,None) = ECDSA.tryRecoverCalldata(attestationDigest(a),sig) (src/PlantOrganism.sol#327)\n[medium/medium] unused-return at src/PlantOrganism.sol:605: PlantOrganism.rotate(address,address,bytes32,uint256,bytes) (src/PlantOrganism.sol#605-624) ignores return value by (recovered,err,None) = ECDSA.tryRecoverCalldata(rotationDigest(newSigner,newIntake,newAction,nonce),sig) (src/PlantOrganism.sol#614-615)\n[low/medium] timestamp at src/PlantOrganism.sol:354: PlantOrganism.clearPending() (src/PlantOrganism.sol#354-363) uses timestamp for comparisons\n[low/medium] timestamp at src/PlantOrganism.sol:316: PlantOrganism.onOracleResult(bytes32,OracleAttestation.Attestation,bytes) (src/PlantOrganism.sol#316-352) uses timestamp for comparisons\n[low/medium] timestamp at src/PlantOrganism.sol:185: PlantOrganism.isDead() (src/PlantOrganism.sol#185-187) uses timestamp for comparisons\n[low/medium] timestamp at src/PlantOrganism.sol:283: PlantOrganism.heartbeat() (src/PlantOrganism.sol#283-314) uses timestamp for comparisons\n[low/medium] timestamp at src/OracleAttestation.sol:157: OracleAttestationConsumer._verifyAttestation(OracleAttestation.Attestation,bytes) (src/OracleAttestation.sol#157-165) uses timestamp for comparisons\n[low/medium] timestamp at src/PlantOrganism.sol:382: PlantOrganism._settle() (src/PlantOrganism.sol#382-433) uses timestamp for comparisons","passed":true},{"durationMs":498,"exitCode":0,"name":"aderyn","output":"[high] reentrancy-state-change at src/PlantOrganism.sol:173: Reentrancy: State change after external call (8 places)\n[low] costly-loop at src/PlantOrganism.sol:530: Costly operations inside loop\n[low] empty-require-revert at src/PlantOrganism.sol:206: Empty `require()` / `revert()` Statement\n[low] internal-function-used-once at src/OracleAttestation.sol:195: Internal Function Used Only Once (4 places)\n[low] literal-instead-of-constant at src/PlantOrganism.sol:336: Literal Instead of Constant (39 places)\n[low] local-variable-shadowing at src/PlantOrganism.sol:62: Local Variable Shadows State Variable (4 places)\n[low] uninitialized-local-variable at src/PlantOrganism.sol:438: Uninitialized Local Variable (2 places)","passed":true}],"detail":"all checks passed","evaluation":"checks","profile":"foundry","status":"accepted","submissionHash":"6dd04b4a9ac26c4882764e4fc3d97c221b392403bae3a4b87906060f74fc20a6","verifiedTreeHash":"a35192460fa5d2106bf01cfc3d8c43804d1be4af","verifierVersion":"0.1.0+ad90ce4c"},{"checks":[{"durationMs":35682,"exitCode":0,"name":"build","output":"Compiling 55 files with Solc 0.8.26\nSolc 0.8.26 finished in 35.55s\nCompiler run successful!\nwarning[unsafe-typecast]: typecast can truncate values\n   ╭▸ src/WeatherQuestion.sol:12:21\n   │\n12 │         int16 lat = int16(uint16(cell >> 16));\n   │                     ━━━━━━━━━━━━━━━━━━━━━━━━━\n   │\n   ├ note: consider disabling this lint if you're certain the cast is safe\n   │       \n   │       // casting to 'int16' is safe because [explain why]\n   │       // forge-lint: disable-next-line(unsafe-typecast)\n   │       \n   │       \n   ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n   ╭▸ src/WeatherQuestion.sol:12:27\n   │\n12 │         int16 lat = int16(uint16(cell >> 16));\n   │                           ━━━━━━━━━━━━━━━━━━\n   │\n   ├ note: consider disabling this lint if you're certain the cast is safe\n   │       \n   │       // casting to 'uint16' is safe because [explain why]\n   │       // forge-lint: disable-next-line(unsafe-typecast)\n   │       \n   │       \n   ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n   ╭▸ src/WeatherQuestion.sol:13:21\n   │\n13 │         int16 lon = int16(uint16(cell));\n   │                     ━━━━━━━━━━━━━━━━━━━\n   │\n   ├ note: consider disabling this lint if you're certain the cast is safe\n   │       \n   │       // casting to 'int16' is safe because [explain why]\n   │       // forge-lint: disable-next-line(unsafe-typecast)\n   │       \n   │       \n   ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n   ╭▸ src/WeatherQuestion.sol:13:27\n   │\n13 │         int16 lon = int16(uint16(cell));\n   │                           ━━━━━━━━━━━━\n   │\n   ├ note: consider disabling this lint if you're certain the cast is safe\n   │       \n   │       // casting to 'uint16' is safe because [explain why]\n   │       // forge-lint: disable-next-line(unsafe-typecast)\n   │       \n   │       \n   ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n   ╭▸ src/WeatherQuestion.sol:19:29\n   │\n19 │         uint256 magnitude = uint256(milli < 0 ? -milli : milli);\n   │                             ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n   │\n   ├ note: consider disabling this lint if you're certain the cast is safe\n   │       \n   │       // casting to 'uint256' is safe because [explain why]\n   │       // forge-lint: disable-next-line(unsafe-typecast)\n   │       \n   │       \n   ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n   ╭▸ src/WeatherQuestion.sol:26:29\n   │\n26 │             b[--i] = bytes1(uint8(48 + n % 10));\n   │                             ━━━━━━━━━━━━━━━━━━\n   │\n   ├ note: consider disabling this lint if you're certain the cast is safe\n   │       \n   │       // casting to 'uint8' is safe because [explain why]\n   │       // forge-lint: disable-next-line(unsafe-typecast)\n   │       \n   │       \n   ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[divide-before-multiply]: division before multiplication may lose precision\n   ╭▸ src/WeatherQuestion.sol:39:27\n   │\n39 │         uint256 y = yoe + era * 400;\n   │                           ━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/divide-before-multiply\n\nwarning[divide-before-multiply]: division before multiplication may lose precision\n   ╭▸ src/WeatherQuestion.sol:40:30\n   │\n40 │         uint256 doy = doe - (365 * yoe + yoe / 4 - yoe / 100);\n   │                              ━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/divide-before-multiply\n\nwarning[divide-before-multiply]: division before multiplication may lose precision\n   ╭▸ src/WeatherQuestion.sol:42:28\n   │\n42 │         uint256 d = doy - (153 * mp + 2) / 5 + 1;\n   │                            ━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/divide-before-multiply\n\nwarning[unsafe-typecast]: typecast can truncate values\n   ╭▸ src/WeatherQuestion.sol:50:40\n   │\n50 │         string memory lat = coordinate(int16(uint16(cell >> 16)));\n   │                                        ━━━━━━━━━━━━━━━━━━━━━━━━━\n   │\n   ├ note: consider disabling this lint if you're certain the cast is safe\n   │       \n   │       // casting to 'int16' is safe because [explain why]\n   │       // forge-lint: disable-next-line(unsafe-typecast)\n   │       \n   │       \n   ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n   ╭▸ src/WeatherQuestion.sol:50:46\n   │\n50 │         string memory lat = coordinate(int16(uint16(cell >> 16)));\n   │                                              ━━━━━━━━━━━━━━━━━━\n   │\n   ├ note: consider disabling this lint if you're certain the cast is safe\n   │       \n   │       // casting to 'uint16' is safe because [explain why]\n   │       // forge-lint: disable-next-line(unsafe-typecast)\n   │       \n   │       \n   ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n   ╭▸ src/WeatherQuestion.sol:51:40\n   │\n51 │         string memory lon = coordinate(int16(uint16(cell)));\n   │                                        ━━━━━━━━━━━━━━━━━━━\n   │\n   ├ note: consider disabling this lint if you're certain the cast is safe\n   │       \n   │       // casting to 'int16' is safe because [explain why]\n   │       // forge-lint: disable-next-line(unsafe-typecast)\n   │       \n   │       \n   ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n   ╭▸ src/WeatherQuestion.sol:51:46\n   │\n51 │         string memory lon = coordinate(int16(uint16(cell)));\n   │                                              ━━━━━━━━━━━━\n   │\n   ├ note: consider disabling this lint if you're certain the cast is safe\n   │       \n   │       // casting to 'uint16' is safe because [explain why]\n   │       // forge-lint: disable-next-line(unsafe-typecast)\n   │       \n   │       \n   ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/PlantOrganism.sol:176:16\n    │\n176 │         return uint32(block.timestamp / 1 days);\n    │                ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint32' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[missing-zero-check]: address parameter is used in a state write or value transfer without a zero-address check\n    ╭▸ src/PlantOrganism.sol:179:19\n    │\n179 │     function bind(address hook_) external nonReentrant {\n    │                   ━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/missing-zero-check\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/PlantOrganism.sol:215:25\n    │\n215 │         require(held <= uint256(type(int256).max) && reserved <= uint256(type(int256).max));\n    │                         ━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/PlantOrganism.sol:215:66\n    │\n215 │         require(held <= uint256(type(int256).max) && reserved <= uint256(type(int256).max));\n    │                                                                  ━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/PlantOrganism.sol:216:16\n    │\n216 │         return int256(held) - int256(reserved);\n    │                ━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'int256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/PlantOrganism.sol:216:31\n    │\n216 │         return int256(held) - int256(reserved);\n    │                               ━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'int256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/PlantOrganism.sol:221:24\n    │\n221 │         return p > 0 ? uint256(p) : 0;\n    │                        ━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/PlantOrganism.sol:543:13\n    │\n543 │             emit Checkpointed(holder, cell, credit);\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/PlantOrganism.sol:291:13\n    │\n291 │             emit Challenged(cell);\n    │             ━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/PlantOrganism.sol:260:9\n    │\n260 │         emit Parked(msg.sender, cell, amount);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/PlantOrganism.sol:279:9\n    │\n279 │         emit Unparked(msg.sender, cell, amount);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[reentrancy-no-eth]: external call can be reentered before `pending` is updated\n    ╭▸ src/PlantOrganism.sol:314:14\n    │\n314 │           id = intake.request(\n    │ ┏━━━━━━━━━━━━━━┛\n315 │ ┃             action,\n316 │ ┃             requestBody(location, day),\n317 │ ┃             IIntake.Callback(address(this), this.onOracleResult.selector),\n318 │ ┃             address(IMD),\n319 │ ┃             price\n320 │ ┃         );\n    │ ┗━━━━━━━━━┛\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-no-eth\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/PlantOrganism.sol:301:13\n    │\n301 │         if (block.timestamp < retryAt) revert RetryLater();\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/PlantOrganism.sol:310:13\n    │\n310 │             emit Advanced(msg.sender, advance);\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/PlantOrganism.sol:328:9\n    │\n328 │         emit Asked(id, day, location, challenger, msg.sender, price);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[incorrect-strict-equality]: dangerous strict equality check on an externally-influenced value\n    ╭▸ src/PlantOrganism.sol:322:13\n    │\n322 │         if (IMD.balanceOf(address(this)) != beforeBalance - price) revert NonExactTransfer();\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/incorrect-strict-equality\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/PlantOrganism.sol:324:54\n    │\n324 │             Pending(id, day, challenger, msg.sender, uint64(block.timestamp), address(intake), true, false, bytes32(0));\n    │                                                      ━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint64' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/PlantOrganism.sol:345:20\n    │\n345 │                 && block.timestamp < signerAcceptedUntil[recovered]\n    │                    ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/PlantOrganism.sol:366:9\n    │\n366 │         emit Received(requestId, a.requestId, word);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/PlantOrganism.sol:356:13\n    │\n356 │         if (uint32(w >> 96) != p.day) revert WrongDay();\n    │             ━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint32' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/PlantOrganism.sol:375:17\n    │\n375 │             if (block.timestamp < uint256(pending.askedAt) + ORACLE_TIMEOUT) revert NotTimedOut();\n    │                 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[uninitialized-local]: local variable is read before being initialized\n    ╭▸ src/PlantOrganism.sol:440:13\n    │\n440 │         if (caller != address(0)) {\n    │             ━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/uninitialized-local\n\nwarning[uninitialized-local]: local variable is read before being initialized\n    ╭▸ src/PlantOrganism.sol:447:27\n    │\n447 │         emit Settled(day, word, sips, backing, water, location);\n    │                           ━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/uninitialized-local\n\nwarning[uninitialized-local]: local variable is read before being initialized\n    ╭▸ src/PlantOrganism.sol:447:33\n    │\n447 │         emit Settled(day, word, sips, backing, water, location);\n    │                                 ━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/uninitialized-local\n\nwarning[uninitialized-local]: local variable is read before being initialized\n    ╭▸ src/PlantOrganism.sol:430:30\n    │\n430 │         _distribute(oldCell, pool);\n    │                              ━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/uninitialized-local\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/PlantOrganism.sol:403:13\n    │\n403 │             emit EmptyCatchUp(day, end);\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/PlantOrganism.sol:421:17\n    │\n421 │                 emit Incomplete(day, count);\n    │                 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/PlantOrganism.sol:385:9\n    │\n385 │         emit Cleared(id, day, retryAt);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/PlantOrganism.sol:521:9\n    │\n521 │         emit Rewarded(cell, pool, reserved);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/PlantOrganism.sol:491:13\n    │\n491 │             emit Moved(current, candidate);\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/PlantOrganism.sol:435:13\n    │\n435 │             emit Moved(0, location);\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/PlantOrganism.sol:444:13\n    │\n444 │             emit Bounty(caller, bounty);\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/PlantOrganism.sol:583:13\n    │\n583 │             emit Paid(holder, credit, advance);\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/PlantOrganism.sol:589:13\n    │\n589 │             emit PaymentDeferred(holder, credit + advance);\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/PlantOrganism.sol:447:9\n    │\n447 │         emit Settled(day, word, sips, backing, water, location);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/PlantOrganism.sol:401:61\n    │\n401 │             uint32 end = hook == address(0) ? today() - 1 : uint32(Math.min(today() - 1, bindDay - 1));\n    │                                                             ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint32' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/PlantOrganism.sol:464:17\n    │\n464 │         water = uint8(wet);\n    │                 ━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint8' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[uninitialized-local]: local variable is read before being initialized\n    ╭▸ src/PlantOrganism.sol:577:22\n    │\n577 │         if (credit + advance == 0) return;\n    │                      ━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/uninitialized-local\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/PlantOrganism.sol:606:9\n    │\n606 │         emit Redeemed(msg.sender, amount, payout, dead);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/PlantOrganism.sol:621:13\n    │\n621 │             emit Died(today(), merged);\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/PlantOrganism.sol:623:13\n    │\n623 │             emit PotMerged(merged);\n    │             ━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/OracleAttestation.sol:182:9\n    │\n182 │         emit OracleSignerSet(signer);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/PlantOrganism.sol:655:9\n    │\n655 │         emit Rotated(oldSigner, newSigner, newIntake, newAction, nonce);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[incorrect-strict-equality]: dangerous strict equality check on an externally-influenced value\n    ╭▸ src/PlantOrganism.sol:661:13\n    │\n661 │         if (token.balanceOf(address(this)) != beforeBalance + amount) revert NonExactTransfer();\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/incorrect-strict-equality\n\n","passed":true},{"durationMs":8417,"exitCode":0,"name":"test","output":"No files changed, compilation skipped\n\nRan 3 tests for test/WeatherQuestion.t.sol:WeatherQuestionTest\n[PASS] test_coordinatesAtEdgesAndNegativeZeroFraction() (gas: 40309)\n[PASS] test_exactLisbonBodyAndKnownDate() (gas: 15004)\n[PASS] test_exactNegativeLatitudeAndLongitudeBody() (gas: 15401)\nSuite result: ok. 3 passed; 0 failed; 0 skipped; finished in 343.73µs (198.80µs CPU time)\n\nRan 4 tests for test/OracleConsumerConformance.t.sol:OracleConsumerConformanceTest\n[PASS] test_acceptsOriginalProtocolSignatureInCanonicalVerifier() (gas: 117518)\n[PASS] test_bytes32WeatherFixtureAcceptedUnderStipend() (gas: 1808575)\n[PASS] test_callbackSelectorIsCanonical() (gas: 3543)\n[PASS] test_digestMatchesTheProtocol() (gas: 13139)\nSuite result: ok. 4 passed; 0 failed; 0 skipped; finished in 2.48ms (1.49ms CPU time)\n\nRan 4 tests for test/PlantOrganism.t.sol:PlantRotationTest\n[PASS] test_oldSignerExpiresAtThirtyDaysWhileNewSignerKeepsPlantAlive() (gas: 20452846)\n[PASS] test_onlyCurrentSignerCanRotateNonceCannotReplay() (gas: 1150859)\n[PASS] test_pendingIntakeAndOldSignerWorkAfterRotation() (gas: 3919007)\n[PASS] test_rotationDomainBindsAllFieldsAndContract() (gas: 974730)\nSuite result: ok. 4 passed; 0 failed; 0 skipped; finished in 17.78ms (21.27ms CPU time)\n\nRan 7 tests for test/PlantAssumptions.t.sol:PlantAssumptionsTest\n[PASS] test_explicitZeroCellQuestionRemainsInvalid() (gas: 25346)\n[PASS] test_oldRotationSignatureAccepted() (gas: 887686)\n[PASS] test_oldSourceWindowBacklogIsAlreadyDead() (gas: 403497)\n[PASS] test_preBindDonationsHaveNoExit() (gas: 196023)\n[PASS] test_redeemedSupplyStillCountsInThreshold() (gas: 3259070)\n[PASS] test_rotatedIntakeCanPriceAbovePotAndPullAdvance() (gas: 2405888)\n[PASS] test_timeoutsDoNotCountAndEventuallyDie() (gas: 9110386)\nSuite result: ok. 7 passed; 0 failed; 0 skipped; finished in 170.88ms (11.04ms CPU time)\n\nRan 11 tests for test/PlantOrganism.t.sol:PlantLifecycleTest\n[PASS] test_bindIsOnceAndOnlyDeployerWithMatchingHook() (gas: 325629)\n[PASS] test_birthFallbackAfterExactlyThreeEmptyDays() (gas: 313927)\n[PASS] test_birthMovesAtFivePercentWithoutMinimumStay() (gas: 663854)\n[PASS] test_cellBoundsAndZeroRejected() (gas: 613601)\n[PASS] test_challengerOnlyStrictlyLargerAndCanBeRepaired() (gas: 916047)\n[PASS] test_deathIsFinalAtThirtyDaysAndKeepsExits() (gas: 2709414)\n[PASS] test_deathUsesBindDayAfterLongUnboundPeriod() (gas: 285431)\n[PASS] test_moveUsesPendingCandidateButLiveBalances() (gas: 3186979)\n[PASS] test_noEarlyOrOutOfOrderSettleAndHeartbeat() (gas: 153161)\n[PASS] test_preBindDaysSkipInOneCallWithoutRead() (gas: 646783)\n[PASS] test_unboundCatchUpIsBoundedAndDoesNotRunBirth() (gas: 157656)\nSuite result: ok. 11 passed; 0 failed; 0 skipped; finished in 197.80ms (4.05ms CPU time)\n\nRan 12 tests for test/PlantOrganism.t.sol:PlantOracleTest\n[PASS] test_blockedKeeperDoesNotBlockSettleAndCanClaimLater() (gas: 2181729)\n[PASS] test_callbackSenderIdAndReplay() (gas: 2333959)\n[PASS] test_callbackStoresOnlyAndFitsStipend() (gas: 1888440)\nLogs:\n  callback gas including mock encoding: 94192\n\n[PASS] test_clearIncompleteUsesSameCounterAndCannotDiscardComplete() (gas: 2316780)\n[PASS] test_exactApprovalAndIntakeBody() (gas: 1516677)\n[PASS] test_feeAdvanceTracksDeficitThenRepaidAtSettle() (gas: 2244778)\n[PASS] test_incompleteHasNoBiologicalChangesThenThirdReads() (gas: 3414262)\n[PASS] test_intakeMustPullExactPrice() (gas: 1261831)\n[PASS] test_rejectsExpiredWrongDomainSignerTypeDayChainAndPanel() (gas: 2618289)\n[PASS] test_rejectsPreAskFutureTamperedAndMalformedAttestations() (gas: 2359612)\n[PASS] test_timeoutClearsAndWaitsSixHoursToReaskSameDay() (gas: 2335442)\n[PASS] test_unfundedAdvanceDoesNotSpendBackingOrBlockSettle() (gas: 2874682)\nSuite result: ok. 12 passed; 0 failed; 0 skipped; finished in 200.58ms (17.42ms CPU time)\n\nRan 21 tests for test/PlantAdversarial.t.sol:PlantAdversarialTest\n[PASS] testFuzz_parkUnparkRoundTripCannotEarnWithoutSettlement(uint96,uint8) (runs: 1000, μ: 2519556, ~: 2274052)\nLogs:\n  Bound result 24415184750\n  Bound result 1\n\n[PASS] testFuzz_reservedWeatherBitsRejectedWithoutMutatingPending(uint8) (runs: 1000, μ: 1526604, ~: 1526597)\n[PASS] testFuzz_signedMalformedPanelsAreRejected(uint8) (runs: 1000, μ: 1523271, ~: 1523130)\n[PASS] test_answerMustBeExactlyOneWordIncludingTrailingData() (gas: 1670945)\n[PASS] test_bindRejectsNonContractWrongAndEmptyTokens() (gas: 1569012)\n[PASS] test_callbackAndSettlementEventsDescribeTheAppliedResult() (gas: 1775401)\n[PASS] test_challengerAddedAfterAskWaitsForFollowingRequest() (gas: 2685669)\n[PASS] test_consumedOracleUuidCannotBeReusedForSameDayRetry() (gas: 2386982)\n[PASS] test_deathClearsPendingAndPreservesGardenersAndFeeDebt() (gas: 2970169)\n[PASS] test_deferredAdvanceAndBountyRemainClaimableExactlyOnce() (gas: 2315012)\n[PASS] test_exactClockDriftToleranceAccepted() (gas: 1520150)\n[PASS] test_moveRewardsDepartingCellAndNeverIncomingCellForSameDay() (gas: 3440806)\n[PASS] test_moveThresholdRoundsUpWhenSupplyNotDivisibleByTwenty() (gas: 693795)\n[PASS] test_rejectedResultCanBeCorrectedWithoutBurningRequest() (gas: 1825942)\n[PASS] test_rotatedIntakeCannotAnswerPreviousIntakesRequest() (gas: 3576881)\n[PASS] test_thirdIncompleteReadsCandidateFromLatestRequest() (gas: 3533833)\n[PASS] test_timeoutExactSecondAndOldResultCannotPoisonReask() (gas: 2006587)\n[PASS] test_twoKeepersAdvancesSurviveTimeoutAndOnlyUnlockOnSettlement() (gas: 2750906)\n[PASS] test_unparkAfterCallbackDropsOutBeforeHoursAndKeepsPreviousReward() (gas: 2756477)\n[PASS] test_validityWindowBoundariesAndAskedAt() (gas: 1945983)\n[PASS] test_zeroAmountsAndOverdrawCannotChangeCustody() (gas: 597607)\nSuite result: ok. 21 passed; 0 failed; 0 skipped; finished in 214.56ms (596.40ms CPU time)\n\nRan 5 tests for test/GasAndDeployment.t.sol:GasAndDeploymentTest\n[PASS] test_coldCallbackAndMaxHoursMoveSettleGas() (gas: 2717979)\nLogs:\n  cold callback gas: 94192\n\n[PASS] test_coldMaxHoursWithRepairedCandidateSettleGas() (gas: 2760310)\n[PASS] test_requestedStaticConstructorAndForbiddenOpcodes() (gas: 8172999)\n[PASS] test_settlementFitsStipendWithManyEligibleHolders() (gas: 38734276)\n[PASS] test_thirdIncompleteMoveAndAdvancePaymentGas() (gas: 3860292)\nSuite result: ok. 5 passed; 0 failed; 0 skipped; finished in 222.51ms (58.30ms CPU time)\n\nRan 12 tests for test/PlantOrganism.t.sol:PlantRewardsTest\n[PASS] testFuzz_floorConservationAndBurnCustody(uint96,uint96,uint24) (runs: 1000, μ: 2438752, ~: 2449990)\nLogs:\n  Bound result 6577836339573122129\n\n[PASS] testFuzz_hourlyAccountingMatchesIndependentModel(uint24,uint24) (runs: 1000, μ: 1901224, ~: 1903793)\n[PASS] test_departuresDropOutAndReparkingCannotCaptureDay() (gas: 4273483)\n[PASS] test_finalRedemptionDoesNotDivideByZeroOrReleaseBurnedPlant() (gas: 2551540)\n[PASS] test_gardenerDustRoundsToBackingAndAccountsCloseExactly() (gas: 5477167)\n[PASS] test_hourZeroFirstRainCapAndSunRequiresWater() (gas: 5074645)\n[PASS] test_noGardenersSendsEntireSipToBacking() (gas: 1625914)\n[PASS] test_nonExactTokenTransfersRejected() (gas: 276495)\n[PASS] test_oldCellKeepsItsRewardsAfterMoveAndLazyActivation() (gas: 5233256)\n[PASS] test_parkSettleClaimProRataAndMidDayJoinsWaitOneSettle() (gas: 3313977)\n[PASS] test_redeemRetainsPlantAndTenPercentAndFloorMonotonic() (gas: 2302008)\n[PASS] test_reentrancyFromTokensAndIntakeBlocked() (gas: 2389035)\nSuite result: ok. 12 passed; 0 failed; 0 skipped; finished in 222.55ms (425.31ms CPU time)\n\nRan 10 tests for test/PlantVotingRevision.t.sol:PlantVotingRevisionTest\n[PASS] testFuzz_repeatedTopUpsCannotOverwriteHeartbeatSnapshot(uint96) (runs: 1000, μ: 2965285, ~: 2965230)\nLogs:\n  Bound result 49644\n\n[PASS] test_atomicCurrentCellTopUpCannotVetoCommittedCandidate() (gas: 2334965)\n[PASS] test_atomicDestinationTopUpCannotMoveOrCaptureRewards() (gas: 4006711)\n[PASS] test_currentCellWithdrawalBreaksTieForCommittedCandidate() (gas: 2222173)\n[PASS] test_emptyCapturedCandidateFallsBackToRepairedLiveCandidate() (gas: 2456846)\n[PASS] test_fallbackCannotUseNewPostHeartbeatStake() (gas: 3004197)\n[PASS] test_questionIsEmptyBeforeBindAndDuringBirth() (gas: 413007)\n[PASS] test_thirdIncompleteAlsoUsesRepairedCandidate() (gas: 4089918)\n[PASS] test_timeoutRetryRefreshesSnapshotIncludingCooldownDeposits() (gas: 2733636)\n[PASS] test_withdrawalCapsSnapshotAndReaskUsesNewBalances() (gas: 2875588)\nSuite result: ok. 10 passed; 0 failed; 0 skipped; finished in 222.56ms (231.68ms CPU time)\n\nRan 1 test for test/PlantInvariant.t.sol:PlantInvariantTest\n[PASS] invariant_conservationAndPermanentBurnCustody() (runs: 128, calls: 8192, reverts: 0)\n\n╭--------------+---------------+-------+---------+----------╮\n| Contract     | Selector      | Calls | Reverts | Discards |\n+===========================================================+\n| PlantHandler | challenge     | 1042  | 0       | 0        |\n|--------------+---------------+-------+---------+----------|\n| PlantHandler | checkpointAll | 1054  | 0       | 0        |\n|--------------+---------------+-------+---------+----------|\n| PlantHandler | claim         | 1088  | 0       | 0        |\n|--------------+---------------+-------+---------+----------|\n| PlantHandler | fund          | 937   | 0       | 0        |\n|--------------+---------------+-------+---------+----------|\n| PlantHandler | park          | 1027  | 0       | 0        |\n|--------------+---------------+-------+---------+----------|\n| PlantHandler | redeem        | 997   | 0       | 0        |\n|--------------+---------------+-------+---------+----------|\n| PlantHandler | unpark        | 1034  | 0       | 0        |\n|--------------+---------------+-------+---------+----------|\n| PlantHandler | weather       | 1013  | 0       | 0        |\n╰--------------+---------------+-------+---------+----------╯\n\nLogs:\n  Bound result 5685\n  Bound result 7533\n  Bound result 1\n  Bound result 17728\n  Bound result 6343\n  Bound result 1980\n  Bound result 545911651617525069851\n  Bound result 247488552\n  Bound result 1600787201\n  Bound result 157\n  Bound result 5813\n  Bound result 16752\n  Bound result 2332\n  Bound result 10359\n  Bound result 6141\n  Bound result 612195468\n  Bound result 9444\n  Bound result 16524\n\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 1.57s (1.57s CPU time)\n\nRan 1 test for test/PlantStateMachine.t.sol:PlantStateMachineInvariantTest\n[PASS] invariant_independentCustodyAndSolvency() (runs: 256, calls: 24576, reverts: 0)\n\n╭--------------------------+---------------+-------+---------+----------╮\n| Contract                 | Selector      | Calls | Reverts | Discards |\n+=======================================================================+\n| PlantStateMachineHandler | answer        | 2008  | 0       | 0        |\n|--------------------------+---------------+-------+---------+----------|\n| PlantStateMachineHandler | challenge     | 1978  | 0       | 0        |\n|--------------------------+---------------+-------+---------+----------|\n| PlantStateMachineHandler | claim         | 2045  | 0       | 0        |\n|--------------------------+---------------+-------+---------+----------|\n| PlantStateMachineHandler | clear         | 2028  | 0       | 0        |\n|--------------------------+---------------+-------+---------+----------|\n| PlantStateMachineHandler | donate        | 2024  | 0       | 0        |\n|--------------------------+---------------+-------+---------+----------|\n| PlantStateMachineHandler | elapse        | 2110  | 0       | 0        |\n|--------------------------+---------------+-------+---------+----------|\n| PlantStateMachineHandler | park          | 2074  | 0       | 0        |\n|--------------------------+---------------+-------+---------+----------|\n| PlantStateMachineHandler | redeem        | 2038  | 0       | 0        |\n|--------------------------+---------------+-------+---------+----------|\n| PlantStateMachineHandler | request       | 2112  | 0       | 0        |\n|--------------------------+---------------+-------+---------+----------|\n| PlantStateMachineHandler | settle        | 2089  | 0       | 0        |\n|--------------------------+---------------+-------+---------+----------|\n| PlantStateMachineHandler | transferPlant | 2064  | 0       | 0        |\n|--------------------------+---------------+-------+---------+----------|\n| PlantStateMachineHandler | unpark        | 2006  | 0       | 0        |\n╰--------------------------+---------------+-------+---------+----------╯\n\nLogs:\n  Bound result 3000000000000000000\n  Bound result 200000000000000000000\n  Bound result 1\n  Bound result 12738\n  Bound result 8051\n  Bound result 4216\n  Bound result 1879405646820326248\n  Bound result 20249\n  Bound result 1202855478688867895\n  Bound result 16531\n  Bound result 1\n  Bound result 54037\n  Bound result 8\n  Bound result 1076\n  Bound result 612\n  Bound result 2054\n  Bound result 12591\n  Bound result 1878\n  Bound result 190038944\n  Bound result 153\n  Bound result 383815178707915270129\n  Bound result 4058770804952167\n  Bound result 4409\n  Bound result 4411\n  Bound result 14943\n  Bound result 301\n  Bound result 14852\n  Bound result 13577\n  Bound result 733787075573431945074\n  Bound result 50000\n  Bound result 19250\n  Bound result 2848\n  Bound result 8948\n  Bound result 2037\n  Bound result 34614\n  Bound result 23475675207151218588\n  Bound result 2276\n  Bound result 801959566643904776134\n  Bound result 226557936332470367883\n  Bound result 733787075573431965323\n  Bound result 2848\n  Bound result 1000000000000000017547\n  Bound result 616188880062889649507\n  Bound result 198036374585290278535\n\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 8.34s (8.33s CPU time)\n\nRan 12 test suites in 8.34s (11.38s CPU time): 91 tests passed, 0 failed, 0 skipped (91 total tests)\n","passed":true},{"durationMs":49,"exitCode":0,"name":"source-index","output":"{\"v\":1,\"entryPoints\":[\"PlantOrganism.bind(address)\",\"PlantOrganism.challenge(uint32)\",\"PlantOrganism.checkpoint(uint32,address)\",\"PlantOrganism.claim()\",\"PlantOrganism.claim(uint32[])\",\"PlantOrganism.clearPending()\",\"PlantOrganism.die()\",\"PlantOrganism.heartbeat()\",\"PlantOrganism.onOracleResult(bytes32,(bytes32,uint256,bytes32,uint8,bytes,uint256,uint64,uint64,bytes32,bytes32,uint16,uint16,uint16,uint64,uint64),bytes)\",\"PlantOrganism.park(uint32,uint256)\",\"PlantOrganism.redeem(uint256)\",\"PlantOrganism.rotate(address,address,bytes32,uint256,bytes)\",\"PlantOrganism.settle()\",\"PlantOrganism.settle(uint32)\",\"PlantOrganism.unpark(uint32,uint256)\"],\"files\":{\".gitignore\":3,\"DEPENDENCIES.md\":7,\"README.md\":30,\"foundry.toml\":19,\"launch.json\":17,\"src/OracleAttestation.sol\":221,\"src/PlantOrganism.sol\":663,\"src/WeatherQuestion.sol\":83,\"src/interfaces/IIntake.sol\":14,\"test/GasAndDeployment.t.sol\":125,\"test/OracleConsumerConformance.t.sol\":125,\"test/PlantAdversarial.t.sol\":417,\"test/PlantAssumptions.t.sol\":86,\"test/PlantInvariant.t.sol\":166,\"test/PlantOrganism.t.sol\":912,\"test/PlantStateMachine.t.sol\":380,\"test/PlantVotingRevision.t.sol\":162,\"test/README.md\":52,\"test/WeatherQuestion.t.sol\":36,\"test/fixtures/weather.json\":11,\"test/mocks/Mocks.sol\":137},\"excluded\":[\"lib/\",\"node_modules/\"],\"truncated\":false}","passed":true}],"detail":"all checks passed","evaluation":"checks","profile":"foundry","status":"accepted","submissionHash":"7268017cf016067410c59810199f073f3539b4bcb37a09a9d9870a8f137f33b7","verifiedTreeHash":"1f52f4c6a49f9d18c4a0353c7f67b87fd555eea3","verifierVersion":"0.1.0+ad90ce4c"},{"checks":[{"durationMs":23123,"exitCode":0,"name":"build","output":"Compiling 51 files with Solc 0.8.26\nSolc 0.8.26 finished in 22.99s\nCompiler run successful!\nwarning[unsafe-typecast]: typecast can truncate values\n   ╭▸ src/WeatherQuestion.sol:12:21\n   │\n12 │         int16 lat = int16(uint16(cell >> 16));\n   │                     ━━━━━━━━━━━━━━━━━━━━━━━━━\n   │\n   ├ note: consider disabling this lint if you're certain the cast is safe\n   │       \n   │       // casting to 'int16' is safe because [explain why]\n   │       // forge-lint: disable-next-line(unsafe-typecast)\n   │       \n   │       \n   ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n   ╭▸ src/WeatherQuestion.sol:12:27\n   │\n12 │         int16 lat = int16(uint16(cell >> 16));\n   │                           ━━━━━━━━━━━━━━━━━━\n   │\n   ├ note: consider disabling this lint if you're certain the cast is safe\n   │       \n   │       // casting to 'uint16' is safe because [explain why]\n   │       // forge-lint: disable-next-line(unsafe-typecast)\n   │       \n   │       \n   ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n   ╭▸ src/WeatherQuestion.sol:13:21\n   │\n13 │         int16 lon = int16(uint16(cell));\n   │                     ━━━━━━━━━━━━━━━━━━━\n   │\n   ├ note: consider disabling this lint if you're certain the cast is safe\n   │       \n   │       // casting to 'int16' is safe because [explain why]\n   │       // forge-lint: disable-next-line(unsafe-typecast)\n   │       \n   │       \n   ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n   ╭▸ src/WeatherQuestion.sol:13:27\n   │\n13 │         int16 lon = int16(uint16(cell));\n   │                           ━━━━━━━━━━━━\n   │\n   ├ note: consider disabling this lint if you're certain the cast is safe\n   │       \n   │       // casting to 'uint16' is safe because [explain why]\n   │       // forge-lint: disable-next-line(unsafe-typecast)\n   │       \n   │       \n   ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n   ╭▸ src/WeatherQuestion.sol:19:29\n   │\n19 │         uint256 magnitude = uint256(milli < 0 ? -milli : milli);\n   │                             ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n   │\n   ├ note: consider disabling this lint if you're certain the cast is safe\n   │       \n   │       // casting to 'uint256' is safe because [explain why]\n   │       // forge-lint: disable-next-line(unsafe-typecast)\n   │       \n   │       \n   ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n   ╭▸ src/WeatherQuestion.sol:26:29\n   │\n26 │             b[--i] = bytes1(uint8(48 + n % 10));\n   │                             ━━━━━━━━━━━━━━━━━━\n   │\n   ├ note: consider disabling this lint if you're certain the cast is safe\n   │       \n   │       // casting to 'uint8' is safe because [explain why]\n   │       // forge-lint: disable-next-line(unsafe-typecast)\n   │       \n   │       \n   ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[divide-before-multiply]: division before multiplication may lose precision\n   ╭▸ src/WeatherQuestion.sol:39:27\n   │\n39 │         uint256 y = yoe + era * 400;\n   │                           ━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/divide-before-multiply\n\nwarning[divide-before-multiply]: division before multiplication may lose precision\n   ╭▸ src/WeatherQuestion.sol:40:30\n   │\n40 │         uint256 doy = doe - (365 * yoe + yoe / 4 - yoe / 100);\n   │                              ━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/divide-before-multiply\n\nwarning[divide-before-multiply]: division before multiplication may lose precision\n   ╭▸ src/WeatherQuestion.sol:42:28\n   │\n42 │         uint256 d = doy - (153 * mp + 2) / 5 + 1;\n   │                            ━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/divide-before-multiply\n\nwarning[unsafe-typecast]: typecast can truncate values\n   ╭▸ src/WeatherQuestion.sol:50:40\n   │\n50 │         string memory lat = coordinate(int16(uint16(cell >> 16)));\n   │                                        ━━━━━━━━━━━━━━━━━━━━━━━━━\n   │\n   ├ note: consider disabling this lint if you're certain the cast is safe\n   │       \n   │       // casting to 'int16' is safe because [explain why]\n   │       // forge-lint: disable-next-line(unsafe-typecast)\n   │       \n   │       \n   ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n   ╭▸ src/WeatherQuestion.sol:50:46\n   │\n50 │         string memory lat = coordinate(int16(uint16(cell >> 16)));\n   │                                              ━━━━━━━━━━━━━━━━━━\n   │\n   ├ note: consider disabling this lint if you're certain the cast is safe\n   │       \n   │       // casting to 'uint16' is safe because [explain why]\n   │       // forge-lint: disable-next-line(unsafe-typecast)\n   │       \n   │       \n   ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n   ╭▸ src/WeatherQuestion.sol:51:40\n   │\n51 │         string memory lon = coordinate(int16(uint16(cell)));\n   │                                        ━━━━━━━━━━━━━━━━━━━\n   │\n   ├ note: consider disabling this lint if you're certain the cast is safe\n   │       \n   │       // casting to 'int16' is safe because [explain why]\n   │       // forge-lint: disable-next-line(unsafe-typecast)\n   │       \n   │       \n   ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n   ╭▸ src/WeatherQuestion.sol:51:46\n   │\n51 │         string memory lon = coordinate(int16(uint16(cell)));\n   │                                              ━━━━━━━━━━━━\n   │\n   ├ note: consider disabling this lint if you're certain the cast is safe\n   │       \n   │       // casting to 'uint16' is safe because [explain why]\n   │       // forge-lint: disable-next-line(unsafe-typecast)\n   │       \n   │       \n   ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/PlantOrganism.sol:167:16\n    │\n167 │         return uint32(block.timestamp / 1 days);\n    │                ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint32' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[missing-zero-check]: address parameter is used in a state write or value transfer without a zero-address check\n    ╭▸ src/PlantOrganism.sol:170:19\n    │\n170 │     function bind(address hook_) external nonReentrant {\n    │                   ━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/missing-zero-check\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/PlantOrganism.sol:206:25\n    │\n206 │         require(held <= uint256(type(int256).max) && reserved <= uint256(type(int256).max));\n    │                         ━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/PlantOrganism.sol:206:66\n    │\n206 │         require(held <= uint256(type(int256).max) && reserved <= uint256(type(int256).max));\n    │                                                                  ━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/PlantOrganism.sol:207:16\n    │\n207 │         return int256(held) - int256(reserved);\n    │                ━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'int256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/PlantOrganism.sol:207:31\n    │\n207 │         return int256(held) - int256(reserved);\n    │                               ━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'int256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/PlantOrganism.sol:212:24\n    │\n212 │         return p > 0 ? uint256(p) : 0;\n    │                        ━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/PlantOrganism.sol:511:13\n    │\n511 │             emit Checkpointed(holder, cell, credit);\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/PlantOrganism.sol:279:13\n    │\n279 │             emit Challenged(cell);\n    │             ━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/PlantOrganism.sol:249:9\n    │\n249 │         emit Parked(msg.sender, cell, amount);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/PlantOrganism.sol:267:9\n    │\n267 │         emit Unparked(msg.sender, cell, amount);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[reentrancy-no-eth]: external call can be reentered before `pending` is updated\n    ╭▸ src/PlantOrganism.sol:302:14\n    │\n302 │           id = intake.request(\n    │ ┏━━━━━━━━━━━━━━┛\n303 │ ┃             action,\n304 │ ┃             requestBody(location, day),\n305 │ ┃             IIntake.Callback(address(this), this.onOracleResult.selector),\n306 │ ┃             address(IMD),\n307 │ ┃             price\n308 │ ┃         );\n    │ ┗━━━━━━━━━┛\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-no-eth\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/PlantOrganism.sol:289:13\n    │\n289 │         if (block.timestamp < retryAt) revert RetryLater();\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/PlantOrganism.sol:298:13\n    │\n298 │             emit Advanced(msg.sender, advance);\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/PlantOrganism.sol:313:9\n    │\n313 │         emit Asked(id, day, location, challenger, msg.sender, price);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[incorrect-strict-equality]: dangerous strict equality check on an externally-influenced value\n    ╭▸ src/PlantOrganism.sol:310:13\n    │\n310 │         if (IMD.balanceOf(address(this)) != beforeBalance - price) revert NonExactTransfer();\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/incorrect-strict-equality\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/PlantOrganism.sol:312:54\n    │\n312 │             Pending(id, day, challenger, msg.sender, uint64(block.timestamp), address(intake), true, false, bytes32(0));\n    │                                                      ━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint64' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/PlantOrganism.sol:330:20\n    │\n330 │                 && block.timestamp < signerAcceptedUntil[recovered]\n    │                    ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/PlantOrganism.sol:351:9\n    │\n351 │         emit Received(requestId, a.requestId, word);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/PlantOrganism.sol:341:13\n    │\n341 │         if (uint32(w >> 96) != p.day) revert WrongDay();\n    │             ━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint32' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/PlantOrganism.sol:360:17\n    │\n360 │             if (block.timestamp < uint256(pending.askedAt) + ORACLE_TIMEOUT) revert NotTimedOut();\n    │                 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[uninitialized-local]: local variable is read before being initialized\n    ╭▸ src/PlantOrganism.sol:415:30\n    │\n415 │         _distribute(oldCell, pool);\n    │                              ━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/uninitialized-local\n\nwarning[uninitialized-local]: local variable is read before being initialized\n    ╭▸ src/PlantOrganism.sol:425:13\n    │\n425 │         if (caller != address(0)) {\n    │             ━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/uninitialized-local\n\nwarning[uninitialized-local]: local variable is read before being initialized\n    ╭▸ src/PlantOrganism.sol:432:27\n    │\n432 │         emit Settled(day, word, sips, backing, water, location);\n    │                           ━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/uninitialized-local\n\nwarning[uninitialized-local]: local variable is read before being initialized\n    ╭▸ src/PlantOrganism.sol:432:33\n    │\n432 │         emit Settled(day, word, sips, backing, water, location);\n    │                                 ━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/uninitialized-local\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/PlantOrganism.sol:388:13\n    │\n388 │             emit EmptyCatchUp(day, end);\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/PlantOrganism.sol:406:17\n    │\n406 │                 emit Incomplete(day, count);\n    │                 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/PlantOrganism.sol:370:9\n    │\n370 │         emit Cleared(id, day, retryAt);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/PlantOrganism.sol:489:9\n    │\n489 │         emit Rewarded(cell, pool, reserved);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/PlantOrganism.sol:460:13\n    │\n460 │             emit Moved(current, candidate);\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/PlantOrganism.sol:420:13\n    │\n420 │             emit Moved(0, location);\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/PlantOrganism.sol:429:13\n    │\n429 │             emit Bounty(caller, bounty);\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/PlantOrganism.sol:551:13\n    │\n551 │             emit Paid(holder, credit, advance);\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/PlantOrganism.sol:557:13\n    │\n557 │             emit PaymentDeferred(holder, credit + advance);\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/PlantOrganism.sol:432:9\n    │\n432 │         emit Settled(day, word, sips, backing, water, location);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/PlantOrganism.sol:386:61\n    │\n386 │             uint32 end = hook == address(0) ? today() - 1 : uint32(Math.min(today() - 1, bindDay - 1));\n    │                                                             ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint32' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/PlantOrganism.sol:449:17\n    │\n449 │         water = uint8(wet);\n    │                 ━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint8' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[uninitialized-local]: local variable is read before being initialized\n    ╭▸ src/PlantOrganism.sol:545:22\n    │\n545 │         if (credit + advance == 0) return;\n    │                      ━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/uninitialized-local\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/PlantOrganism.sol:574:9\n    │\n574 │         emit Redeemed(msg.sender, amount, payout, dead);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/PlantOrganism.sol:589:13\n    │\n589 │             emit Died(today(), merged);\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/PlantOrganism.sol:591:13\n    │\n591 │             emit PotMerged(merged);\n    │             ━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/OracleAttestation.sol:182:9\n    │\n182 │         emit OracleSignerSet(signer);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/PlantOrganism.sol:623:9\n    │\n623 │         emit Rotated(oldSigner, newSigner, newIntake, newAction, nonce);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[incorrect-strict-equality]: dangerous strict equality check on an externally-influenced value\n    ╭▸ src/PlantOrganism.sol:629:13\n    │\n629 │         if (token.balanceOf(address(this)) != beforeBalance + amount) revert NonExactTransfer();\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/incorrect-strict-equality\n\n","passed":true},{"durationMs":1714,"exitCode":0,"name":"test","output":"No files changed, compilation skipped\n\nRan 3 tests for test/WeatherQuestion.t.sol:WeatherQuestionTest\n[PASS] test_coordinatesAtEdgesAndNegativeZeroFraction() (gas: 40309)\n[PASS] test_exactLisbonBodyAndKnownDate() (gas: 15004)\n[PASS] test_exactNegativeLatitudeAndLongitudeBody() (gas: 15401)\nSuite result: ok. 3 passed; 0 failed; 0 skipped; finished in 388.24µs (430.24µs CPU time)\n\nRan 4 tests for test/OracleConsumerConformance.t.sol:OracleConsumerConformanceTest\n[PASS] test_acceptsOriginalProtocolSignatureInCanonicalVerifier() (gas: 117518)\n[PASS] test_bytes32WeatherFixtureAcceptedUnderStipend() (gas: 1711401)\n[PASS] test_callbackSelectorIsCanonical() (gas: 3543)\n[PASS] test_digestMatchesTheProtocol() (gas: 13139)\nSuite result: ok. 4 passed; 0 failed; 0 skipped; finished in 2.57ms (1.50ms CPU time)\n\nRan 4 tests for test/PlantOrganism.t.sol:PlantRotationTest\n[PASS] test_oldSignerExpiresAtThirtyDaysWhileNewSignerKeepsPlantAlive() (gas: 19271508)\n[PASS] test_onlyCurrentSignerCanRotateNonceCannotReplay() (gas: 1150397)\n[PASS] test_pendingIntakeAndOldSignerWorkAfterRotation() (gas: 3771780)\n[PASS] test_rotationDomainBindsAllFieldsAndContract() (gas: 974444)\nSuite result: ok. 4 passed; 0 failed; 0 skipped; finished in 12.13ms (15.05ms CPU time)\n\nRan 3 tests for test/GasAndDeployment.t.sol:GasAndDeploymentTest\n[PASS] test_coldCallbackAndMaxHoursMoveSettleGas() (gas: 2574374)\nLogs:\n  cold callback gas: 94192\n  cold settle, 24 sips and move: 256652\n\n[PASS] test_requestedStaticConstructorAndForbiddenOpcodes() (gas: 7928021)\n[PASS] test_thirdIncompleteMoveAndAdvancePaymentGas() (gas: 3665334)\nLogs:\n  cold third incomplete with move and advance repayment: 244632\n\nSuite result: ok. 3 passed; 0 failed; 0 skipped; finished in 23.57ms (24.40ms CPU time)\n\nRan 11 tests for test/PlantOrganism.t.sol:PlantLifecycleTest\n[PASS] test_bindIsOnceAndOnlyDeployerWithMatchingHook() (gas: 325519)\n[PASS] test_birthFallbackAfterExactlyThreeEmptyDays() (gas: 305969)\n[PASS] test_birthMovesAtFivePercentWithoutMinimumStay() (gas: 659503)\n[PASS] test_cellBoundsAndZeroRejected() (gas: 609033)\n[PASS] test_challengerOnlyStrictlyLargerAndCanBeRepaired() (gas: 907339)\n[PASS] test_deathIsFinalAtThirtyDaysAndKeepsExits() (gas: 2597183)\n[PASS] test_deathUsesBindDayAfterLongUnboundPeriod() (gas: 285387)\n[PASS] test_moveUsesPendingCandidateButLiveBalances() (gas: 2969440)\n[PASS] test_noEarlyOrOutOfOrderSettleAndHeartbeat() (gas: 153141)\n[PASS] test_preBindDaysSkipInOneCallWithoutRead() (gas: 644624)\n[PASS] test_unboundCatchUpIsBoundedAndDoesNotRunBirth() (gas: 157507)\nSuite result: ok. 11 passed; 0 failed; 0 skipped; finished in 40.48ms (6.90ms CPU time)\n\nRan 12 tests for test/PlantOrganism.t.sol:PlantOracleTest\n[PASS] test_blockedKeeperDoesNotBlockSettleAndCanClaimLater() (gas: 2071228)\n[PASS] test_callbackSenderIdAndReplay() (gas: 2199209)\n[PASS] test_callbackStoresOnlyAndFitsStipend() (gas: 1780168)\nLogs:\n  callback gas including mock encoding: 94192\n  settle gas: 289534\n\n[PASS] test_clearIncompleteUsesSameCounterAndCannotDiscardComplete() (gas: 2182068)\n[PASS] test_exactApprovalAndIntakeBody() (gas: 1418919)\n[PASS] test_feeAdvanceTracksDeficitThenRepaidAtSettle() (gas: 2134321)\n[PASS] test_incompleteHasNoBiologicalChangesThenThirdReads() (gas: 3236176)\n[PASS] test_intakeMustPullExactPrice() (gas: 1259699)\n[PASS] test_rejectsExpiredWrongDomainSignerTypeDayChainAndPanel() (gas: 2520151)\n[PASS] test_rejectsPreAskFutureTamperedAndMalformedAttestations() (gas: 2261542)\n[PASS] test_timeoutClearsAndWaitsSixHoursToReaskSameDay() (gas: 2200708)\n[PASS] test_unfundedAdvanceDoesNotSpendBackingOrBlockSettle() (gas: 2724601)\nSuite result: ok. 12 passed; 0 failed; 0 skipped; finished in 54.44ms (19.17ms CPU time)\n\nRan 12 tests for test/PlantOrganism.t.sol:PlantRewardsTest\n[PASS] testFuzz_floorConservationAndBurnCustody(uint96,uint96,uint24) (runs: 256, μ: 2323921, ~: 2337878)\nLogs:\n  Bound result 3007\n\n[PASS] testFuzz_hourlyAccountingMatchesIndependentModel(uint24,uint24) (runs: 256, μ: 1791316, ~: 1793568)\n[PASS] test_departuresDropOutAndReparkingCannotCaptureDay() (gas: 4076157)\n[PASS] test_finalRedemptionDoesNotDivideByZeroOrReleaseBurnedPlant() (gas: 2439439)\n[PASS] test_gardenerDustRoundsToBackingAndAccountsCloseExactly() (gas: 5209191)\n[PASS] test_hourZeroFirstRainCapAndSunRequiresWater() (gas: 4779386)\n[PASS] test_noGardenersSendsEntireSipToBacking() (gas: 1529791)\n[PASS] test_nonExactTokenTransfersRejected() (gas: 274277)\n[PASS] test_oldCellKeepsItsRewardsAfterMoveAndLazyActivation() (gas: 4941414)\n[PASS] test_parkSettleClaimProRataAndMidDayJoinsWaitOneSettle() (gas: 3164621)\n[PASS] test_redeemRetainsPlantAndTenPercentAndFloorMonotonic() (gas: 2189930)\n[PASS] test_reentrancyFromTokensAndIntakeBlocked() (gas: 2278761)\nSuite result: ok. 12 passed; 0 failed; 0 skipped; finished in 65.39ms (148.86ms CPU time)\n\nRan 1 test for test/PlantInvariant.t.sol:PlantInvariantTest\n[PASS] invariant_conservationAndPermanentBurnCustody() (runs: 128, calls: 8192, reverts: 0)\n\n╭--------------+---------------+-------+---------+----------╮\n| Contract     | Selector      | Calls | Reverts | Discards |\n+===========================================================+\n| PlantHandler | challenge     | 1018  | 0       | 0        |\n|--------------+---------------+-------+---------+----------|\n| PlantHandler | checkpointAll | 1045  | 0       | 0        |\n|--------------+---------------+-------+---------+----------|\n| PlantHandler | claim         | 1013  | 0       | 0        |\n|--------------+---------------+-------+---------+----------|\n| PlantHandler | fund          | 1016  | 0       | 0        |\n|--------------+---------------+-------+---------+----------|\n| PlantHandler | park          | 1050  | 0       | 0        |\n|--------------+---------------+-------+---------+----------|\n| PlantHandler | redeem        | 1008  | 0       | 0        |\n|--------------+---------------+-------+---------+----------|\n| PlantHandler | unpark        | 1005  | 0       | 0        |\n|--------------+---------------+-------+---------+----------|\n| PlantHandler | weather       | 1037  | 0       | 0        |\n╰--------------+---------------+-------+---------+----------╯\n\nLogs:\n  Bound result 6850\n  Bound result 3255\n  Bound result 1114\n  Bound result 19090\n  Bound result 926\n  Bound result 18895\n  Bound result 158665975059971614762\n  Bound result 165465347153265606619\n  Bound result 5262\n  Bound result 1862231153\n  Bound result 3\n  Bound result 6491\n  Bound result 5329\n  Bound result 21601\n  Bound result 8428\n  Bound result 659926548971588282423\n\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 1.63s (1.63s CPU time)\n\nRan 8 test suites in 1.63s (1.83s CPU time): 50 tests passed, 0 failed, 0 skipped (50 total tests)\n","passed":true},{"durationMs":37,"exitCode":0,"name":"source-index","output":"{\"v\":1,\"entryPoints\":[\"PlantOrganism.bind(address)\",\"PlantOrganism.challenge(uint32)\",\"PlantOrganism.checkpoint(uint32,address)\",\"PlantOrganism.claim()\",\"PlantOrganism.claim(uint32[])\",\"PlantOrganism.clearPending()\",\"PlantOrganism.die()\",\"PlantOrganism.heartbeat()\",\"PlantOrganism.onOracleResult(bytes32,(bytes32,uint256,bytes32,uint8,bytes,uint256,uint64,uint64,bytes32,bytes32,uint16,uint16,uint16,uint64,uint64),bytes)\",\"PlantOrganism.park(uint32,uint256)\",\"PlantOrganism.redeem(uint256)\",\"PlantOrganism.rotate(address,address,bytes32,uint256,bytes)\",\"PlantOrganism.settle()\",\"PlantOrganism.settle(uint32)\",\"PlantOrganism.unpark(uint32,uint256)\"],\"files\":{\".gitignore\":3,\"DEPENDENCIES.md\":7,\"README.md\":30,\"foundry.toml\":19,\"launch.json\":17,\"src/OracleAttestation.sol\":221,\"src/PlantOrganism.sol\":631,\"src/WeatherQuestion.sol\":83,\"src/interfaces/IIntake.sol\":14,\"test/GasAndDeployment.t.sol\":95,\"test/OracleConsumerConformance.t.sol\":126,\"test/PlantInvariant.t.sol\":166,\"test/PlantOrganism.t.sol\":909,\"test/WeatherQuestion.t.sol\":36,\"test/fixtures/weather.json\":11,\"test/mocks/Mocks.sol\":137},\"excluded\":[\"lib/\",\"node_modules/\"],\"truncated\":false}","passed":true}],"detail":"all checks passed","evaluation":"checks","profile":"foundry","status":"accepted","submissionHash":"9269b7ee10628cdb549ea133a3b63b78eeee9a8ac1bbc1a92860f09f702b7e37","verifiedTreeHash":"dc255e25e5de004934829874e541689355ebce22","verifierVersion":"0.1.0+ad90ce4c"},{"checks":[{"durationMs":35150,"exitCode":0,"name":"build","output":"Compiling 54 files with Solc 0.8.26\nSolc 0.8.26 finished in 35.00s\nCompiler run successful!\nwarning[unsafe-typecast]: typecast can truncate values\n   ╭▸ src/WeatherQuestion.sol:12:21\n   │\n12 │         int16 lat = int16(uint16(cell >> 16));\n   │                     ━━━━━━━━━━━━━━━━━━━━━━━━━\n   │\n   ├ note: consider disabling this lint if you're certain the cast is safe\n   │       \n   │       // casting to 'int16' is safe because [explain why]\n   │       // forge-lint: disable-next-line(unsafe-typecast)\n   │       \n   │       \n   ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n   ╭▸ src/WeatherQuestion.sol:12:27\n   │\n12 │         int16 lat = int16(uint16(cell >> 16));\n   │                           ━━━━━━━━━━━━━━━━━━\n   │\n   ├ note: consider disabling this lint if you're certain the cast is safe\n   │       \n   │       // casting to 'uint16' is safe because [explain why]\n   │       // forge-lint: disable-next-line(unsafe-typecast)\n   │       \n   │       \n   ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n   ╭▸ src/WeatherQuestion.sol:13:21\n   │\n13 │         int16 lon = int16(uint16(cell));\n   │                     ━━━━━━━━━━━━━━━━━━━\n   │\n   ├ note: consider disabling this lint if you're certain the cast is safe\n   │       \n   │       // casting to 'int16' is safe because [explain why]\n   │       // forge-lint: disable-next-line(unsafe-typecast)\n   │       \n   │       \n   ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n   ╭▸ src/WeatherQuestion.sol:13:27\n   │\n13 │         int16 lon = int16(uint16(cell));\n   │                           ━━━━━━━━━━━━\n   │\n   ├ note: consider disabling this lint if you're certain the cast is safe\n   │       \n   │       // casting to 'uint16' is safe because [explain why]\n   │       // forge-lint: disable-next-line(unsafe-typecast)\n   │       \n   │       \n   ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n   ╭▸ src/WeatherQuestion.sol:19:29\n   │\n19 │         uint256 magnitude = uint256(milli < 0 ? -milli : milli);\n   │                             ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n   │\n   ├ note: consider disabling this lint if you're certain the cast is safe\n   │       \n   │       // casting to 'uint256' is safe because [explain why]\n   │       // forge-lint: disable-next-line(unsafe-typecast)\n   │       \n   │       \n   ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n   ╭▸ src/WeatherQuestion.sol:26:29\n   │\n26 │             b[--i] = bytes1(uint8(48 + n % 10));\n   │                             ━━━━━━━━━━━━━━━━━━\n   │\n   ├ note: consider disabling this lint if you're certain the cast is safe\n   │       \n   │       // casting to 'uint8' is safe because [explain why]\n   │       // forge-lint: disable-next-line(unsafe-typecast)\n   │       \n   │       \n   ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[divide-before-multiply]: division before multiplication may lose precision\n   ╭▸ src/WeatherQuestion.sol:39:27\n   │\n39 │         uint256 y = yoe + era * 400;\n   │                           ━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/divide-before-multiply\n\nwarning[divide-before-multiply]: division before multiplication may lose precision\n   ╭▸ src/WeatherQuestion.sol:40:30\n   │\n40 │         uint256 doy = doe - (365 * yoe + yoe / 4 - yoe / 100);\n   │                              ━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/divide-before-multiply\n\nwarning[divide-before-multiply]: division before multiplication may lose precision\n   ╭▸ src/WeatherQuestion.sol:42:28\n   │\n42 │         uint256 d = doy - (153 * mp + 2) / 5 + 1;\n   │                            ━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/divide-before-multiply\n\nwarning[unsafe-typecast]: typecast can truncate values\n   ╭▸ src/WeatherQuestion.sol:50:40\n   │\n50 │         string memory lat = coordinate(int16(uint16(cell >> 16)));\n   │                                        ━━━━━━━━━━━━━━━━━━━━━━━━━\n   │\n   ├ note: consider disabling this lint if you're certain the cast is safe\n   │       \n   │       // casting to 'int16' is safe because [explain why]\n   │       // forge-lint: disable-next-line(unsafe-typecast)\n   │       \n   │       \n   ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n   ╭▸ src/WeatherQuestion.sol:50:46\n   │\n50 │         string memory lat = coordinate(int16(uint16(cell >> 16)));\n   │                                              ━━━━━━━━━━━━━━━━━━\n   │\n   ├ note: consider disabling this lint if you're certain the cast is safe\n   │       \n   │       // casting to 'uint16' is safe because [explain why]\n   │       // forge-lint: disable-next-line(unsafe-typecast)\n   │       \n   │       \n   ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n   ╭▸ src/WeatherQuestion.sol:51:40\n   │\n51 │         string memory lon = coordinate(int16(uint16(cell)));\n   │                                        ━━━━━━━━━━━━━━━━━━━\n   │\n   ├ note: consider disabling this lint if you're certain the cast is safe\n   │       \n   │       // casting to 'int16' is safe because [explain why]\n   │       // forge-lint: disable-next-line(unsafe-typecast)\n   │       \n   │       \n   ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n   ╭▸ src/WeatherQuestion.sol:51:46\n   │\n51 │         string memory lon = coordinate(int16(uint16(cell)));\n   │                                              ━━━━━━━━━━━━\n   │\n   ├ note: consider disabling this lint if you're certain the cast is safe\n   │       \n   │       // casting to 'uint16' is safe because [explain why]\n   │       // forge-lint: disable-next-line(unsafe-typecast)\n   │       \n   │       \n   ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/PlantOrganism.sol:167:16\n    │\n167 │         return uint32(block.timestamp / 1 days);\n    │                ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint32' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[missing-zero-check]: address parameter is used in a state write or value transfer without a zero-address check\n    ╭▸ src/PlantOrganism.sol:170:19\n    │\n170 │     function bind(address hook_) external nonReentrant {\n    │                   ━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/missing-zero-check\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/PlantOrganism.sol:206:25\n    │\n206 │         require(held <= uint256(type(int256).max) && reserved <= uint256(type(int256).max));\n    │                         ━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/PlantOrganism.sol:206:66\n    │\n206 │         require(held <= uint256(type(int256).max) && reserved <= uint256(type(int256).max));\n    │                                                                  ━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/PlantOrganism.sol:207:16\n    │\n207 │         return int256(held) - int256(reserved);\n    │                ━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'int256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/PlantOrganism.sol:207:31\n    │\n207 │         return int256(held) - int256(reserved);\n    │                               ━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'int256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/PlantOrganism.sol:212:24\n    │\n212 │         return p > 0 ? uint256(p) : 0;\n    │                        ━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/PlantOrganism.sol:525:13\n    │\n525 │             emit Checkpointed(holder, cell, credit);\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/PlantOrganism.sol:280:13\n    │\n280 │             emit Challenged(cell);\n    │             ━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/PlantOrganism.sol:250:9\n    │\n250 │         emit Parked(msg.sender, cell, amount);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/PlantOrganism.sol:268:9\n    │\n268 │         emit Unparked(msg.sender, cell, amount);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[reentrancy-no-eth]: external call can be reentered before `pending` is updated\n    ╭▸ src/PlantOrganism.sol:303:14\n    │\n303 │           id = intake.request(\n    │ ┏━━━━━━━━━━━━━━┛\n304 │ ┃             action,\n305 │ ┃             requestBody(location, day),\n306 │ ┃             IIntake.Callback(address(this), this.onOracleResult.selector),\n307 │ ┃             address(IMD),\n308 │ ┃             price\n309 │ ┃         );\n    │ ┗━━━━━━━━━┛\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-no-eth\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/PlantOrganism.sol:290:13\n    │\n290 │         if (block.timestamp < retryAt) revert RetryLater();\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/PlantOrganism.sol:299:13\n    │\n299 │             emit Advanced(msg.sender, advance);\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/PlantOrganism.sol:314:9\n    │\n314 │         emit Asked(id, day, location, challenger, msg.sender, price);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[incorrect-strict-equality]: dangerous strict equality check on an externally-influenced value\n    ╭▸ src/PlantOrganism.sol:311:13\n    │\n311 │         if (IMD.balanceOf(address(this)) != beforeBalance - price) revert NonExactTransfer();\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/incorrect-strict-equality\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/PlantOrganism.sol:313:54\n    │\n313 │             Pending(id, day, challenger, msg.sender, uint64(block.timestamp), address(intake), true, false, bytes32(0));\n    │                                                      ━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint64' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/PlantOrganism.sol:331:20\n    │\n331 │                 && block.timestamp < signerAcceptedUntil[recovered]\n    │                    ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/PlantOrganism.sol:352:9\n    │\n352 │         emit Received(requestId, a.requestId, word);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/PlantOrganism.sol:342:13\n    │\n342 │         if (uint32(w >> 96) != p.day) revert WrongDay();\n    │             ━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint32' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/PlantOrganism.sol:361:17\n    │\n361 │             if (block.timestamp < uint256(pending.askedAt) + ORACLE_TIMEOUT) revert NotTimedOut();\n    │                 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[uninitialized-local]: local variable is read before being initialized\n    ╭▸ src/PlantOrganism.sol:416:30\n    │\n416 │         _distribute(oldCell, pool);\n    │                              ━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/uninitialized-local\n\nwarning[uninitialized-local]: local variable is read before being initialized\n    ╭▸ src/PlantOrganism.sol:433:33\n    │\n433 │         emit Settled(day, word, sips, backing, water, location);\n    │                                 ━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/uninitialized-local\n\nwarning[uninitialized-local]: local variable is read before being initialized\n    ╭▸ src/PlantOrganism.sol:433:27\n    │\n433 │         emit Settled(day, word, sips, backing, water, location);\n    │                           ━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/uninitialized-local\n\nwarning[uninitialized-local]: local variable is read before being initialized\n    ╭▸ src/PlantOrganism.sol:426:13\n    │\n426 │         if (caller != address(0)) {\n    │             ━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/uninitialized-local\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/PlantOrganism.sol:389:13\n    │\n389 │             emit EmptyCatchUp(day, end);\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/PlantOrganism.sol:407:17\n    │\n407 │                 emit Incomplete(day, count);\n    │                 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/PlantOrganism.sol:371:9\n    │\n371 │         emit Cleared(id, day, retryAt);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/PlantOrganism.sol:503:9\n    │\n503 │         emit Rewarded(cell, pool, reserved);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/PlantOrganism.sol:473:13\n    │\n473 │             emit Moved(current, candidate);\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/PlantOrganism.sol:421:13\n    │\n421 │             emit Moved(0, location);\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/PlantOrganism.sol:430:13\n    │\n430 │             emit Bounty(caller, bounty);\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/PlantOrganism.sol:565:13\n    │\n565 │             emit Paid(holder, credit, advance);\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/PlantOrganism.sol:571:13\n    │\n571 │             emit PaymentDeferred(holder, credit + advance);\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/PlantOrganism.sol:433:9\n    │\n433 │         emit Settled(day, word, sips, backing, water, location);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/PlantOrganism.sol:387:61\n    │\n387 │             uint32 end = hook == address(0) ? today() - 1 : uint32(Math.min(today() - 1, bindDay - 1));\n    │                                                             ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint32' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/PlantOrganism.sol:450:17\n    │\n450 │         water = uint8(wet);\n    │                 ━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint8' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[uninitialized-local]: local variable is read before being initialized\n    ╭▸ src/PlantOrganism.sol:559:22\n    │\n559 │         if (credit + advance == 0) return;\n    │                      ━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/uninitialized-local\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/PlantOrganism.sol:588:9\n    │\n588 │         emit Redeemed(msg.sender, amount, payout, dead);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/PlantOrganism.sol:603:13\n    │\n603 │             emit Died(today(), merged);\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/PlantOrganism.sol:605:13\n    │\n605 │             emit PotMerged(merged);\n    │             ━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/OracleAttestation.sol:182:9\n    │\n182 │         emit OracleSignerSet(signer);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/PlantOrganism.sol:637:9\n    │\n637 │         emit Rotated(oldSigner, newSigner, newIntake, newAction, nonce);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[incorrect-strict-equality]: dangerous strict equality check on an externally-influenced value\n    ╭▸ src/PlantOrganism.sol:643:13\n    │\n643 │         if (token.balanceOf(address(this)) != beforeBalance + amount) revert NonExactTransfer();\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/incorrect-strict-equality\n\n","passed":true},{"durationMs":1783,"exitCode":0,"name":"test","output":"No files changed, compilation skipped\n\nRan 3 tests for test/WeatherQuestion.t.sol:WeatherQuestionTest\n[PASS] test_coordinatesAtEdgesAndNegativeZeroFraction() (gas: 40309)\n[PASS] test_exactLisbonBodyAndKnownDate() (gas: 15004)\n[PASS] test_exactNegativeLatitudeAndLongitudeBody() (gas: 15401)\nSuite result: ok. 3 passed; 0 failed; 0 skipped; finished in 466.90µs (507.62µs CPU time)\n\nRan 7 tests for test/PlantVotingRevision.t.sol:PlantVotingRevisionTest\n[PASS] test_atomicCurrentCellTopUpCannotVetoCommittedCandidate() (gas: 2367527)\n[PASS] test_atomicDestinationTopUpCannotMoveOrCaptureRewards() (gas: 3846488)\n[PASS] test_emptyCapturedCandidateFallsBackToRepairedLiveCandidate() (gas: 2598482)\n[PASS] test_fallbackCannotUseNewPostHeartbeatStake() (gas: 3095191)\n[PASS] test_questionIsEmptyBeforeBindAndDuringBirth() (gas: 411374)\n[PASS] test_thirdIncompleteAlsoUsesRepairedCandidate() (gas: 4181498)\n[PASS] test_withdrawalRemovesPowerAndReaskCannotRestoreIt() (gas: 3678603)\nSuite result: ok. 7 passed; 0 failed; 0 skipped; finished in 5.77ms (10.14ms CPU time)\n\nRan 4 tests for test/OracleConsumerConformance.t.sol:OracleConsumerConformanceTest\n[PASS] test_acceptsOriginalProtocolSignatureInCanonicalVerifier() (gas: 117518)\n[PASS] test_bytes32WeatherFixtureAcceptedUnderStipend() (gas: 1711910)\n[PASS] test_callbackSelectorIsCanonical() (gas: 3543)\n[PASS] test_digestMatchesTheProtocol() (gas: 13139)\nSuite result: ok. 4 passed; 0 failed; 0 skipped; finished in 7.77ms (1.50ms CPU time)\n\nRan 4 tests for test/PlantOrganism.t.sol:PlantRotationTest\n[PASS] test_oldSignerExpiresAtThirtyDaysWhileNewSignerKeepsPlantAlive() (gas: 19607309)\n[PASS] test_onlyCurrentSignerCanRotateNonceCannotReplay() (gas: 1150507)\n[PASS] test_pendingIntakeAndOldSignerWorkAfterRotation() (gas: 3791569)\n[PASS] test_rotationDomainBindsAllFieldsAndContract() (gas: 974532)\nSuite result: ok. 4 passed; 0 failed; 0 skipped; finished in 12.59ms (15.39ms CPU time)\n\nRan 8 tests for test/PlantVotingCommitment.t.sol:PlantVotingCommitmentTest\n[PASS] testFuzz_withdrawalAndRedepositCannotIncreasePower(uint96,uint96) (runs: 256, μ: 2576086, ~: 2576513)\nLogs:\n  Bound result 1892896118556\n  Bound result 50725546375218271850\n\n[PASS] test_borrowingAtBothHeartbeatAndSettleCannotCaptureGardenersPool() (gas: 4607864)\n[PASS] test_borrowingAtBothHeartbeatAndSettleCannotMove() (gas: 3607963)\n[PASS] test_borrowingAtBothHeartbeatAndSettleCannotVeto() (gas: 2518219)\n[PASS] test_loanAcrossPreviousSettleLosesPowerOnWithdrawal() (gas: 3138794)\n[PASS] test_rearmedDecoyCannotHideHoldersCandidate() (gas: 2717594)\n[PASS] test_thresholdUsesCommittedStakeAndNewDepositsMatureAtSettle() (gas: 3542686)\n[PASS] test_timeoutAndReaskCannotRestoreWithdrawnPower() (gas: 2783428)\nSuite result: ok. 8 passed; 0 failed; 0 skipped; finished in 53.91ms (66.49ms CPU time)\n\nRan 11 tests for test/PlantOrganism.t.sol:PlantLifecycleTest\n[PASS] test_bindIsOnceAndOnlyDeployerWithMatchingHook() (gas: 325519)\n[PASS] test_birthFallbackAfterExactlyThreeEmptyDays() (gas: 314602)\n[PASS] test_birthMovesAtFivePercentWithoutMinimumStay() (gas: 660654)\n[PASS] test_cellBoundsAndZeroRejected() (gas: 609707)\n[PASS] test_challengerOnlyStrictlyLargerAndCanBeRepaired() (gas: 908869)\n[PASS] test_deathIsFinalAtThirtyDaysAndKeepsExits() (gas: 2607311)\n[PASS] test_deathUsesBindDayAfterLongUnboundPeriod() (gas: 285365)\n[PASS] test_moveUsesPendingCandidateAndRemainingCommittedBalances() (gas: 3190015)\n[PASS] test_noEarlyOrOutOfOrderSettleAndHeartbeat() (gas: 153139)\n[PASS] test_preBindDaysSkipInOneCallWithoutRead() (gas: 645106)\n[PASS] test_unboundCatchUpIsBoundedAndDoesNotRunBirth() (gas: 157524)\nSuite result: ok. 11 passed; 0 failed; 0 skipped; finished in 54.17ms (5.54ms CPU time)\n\nRan 7 tests for test/PlantAssumptions.t.sol:PlantAssumptionsTest\n[PASS] test_explicitZeroCellQuestionRemainsInvalid() (gas: 25324)\n[PASS] test_oldRotationSignatureAccepted() (gas: 887620)\n[PASS] test_oldSourceWindowBacklogIsAlreadyDead() (gas: 401886)\n[PASS] test_preBindDonationsHaveNoExit() (gas: 196001)\n[PASS] test_redeemedSupplyStillCountsInThreshold() (gas: 3095372)\n[PASS] test_rotatedIntakeCanPriceAbovePotAndPullAdvance() (gas: 2308589)\n[PASS] test_timeoutsDoNotCountAndEventuallyDie() (gas: 8459564)\nSuite result: ok. 7 passed; 0 failed; 0 skipped; finished in 54.31ms (7.66ms CPU time)\n\nRan 12 tests for test/PlantOrganism.t.sol:PlantOracleTest\n[PASS] test_blockedKeeperDoesNotBlockSettleAndCanClaimLater() (gas: 2081432)\n[PASS] test_callbackSenderIdAndReplay() (gas: 2209347)\n[PASS] test_callbackStoresOnlyAndFitsStipend() (gas: 1790328)\nLogs:\n  callback gas including mock encoding: 94192\n  settle gas: 301570\n\n[PASS] test_clearIncompleteUsesSameCounterAndCannotDiscardComplete() (gas: 2192207)\n[PASS] test_exactApprovalAndIntakeBody() (gas: 1419428)\n[PASS] test_feeAdvanceTracksDeficitThenRepaidAtSettle() (gas: 2142778)\n[PASS] test_incompleteHasNoBiologicalChangesThenThirdReads() (gas: 3416039)\n[PASS] test_intakeMustPullExactPrice() (gas: 1260208)\n[PASS] test_rejectsExpiredWrongDomainSignerTypeDayChainAndPanel() (gas: 2520660)\n[PASS] test_rejectsPreAskFutureTamperedAndMalformedAttestations() (gas: 2262051)\n[PASS] test_timeoutClearsAndWaitsSixHoursToReaskSameDay() (gas: 2210846)\n[PASS] test_unfundedAdvanceDoesNotSpendBackingOrBlockSettle() (gas: 2748338)\nSuite result: ok. 12 passed; 0 failed; 0 skipped; finished in 54.45ms (17.90ms CPU time)\n\nRan 4 tests for test/GasAndDeployment.t.sol:GasAndDeploymentTest\n[PASS] test_coldCallbackAndMaxHoursMoveSettleGas() (gas: 2897618)\nLogs:\n  cold callback gas: 94192\n  cold settle, 24 sips and move: 264013\n\n[PASS] test_coldMaxHoursWithRepairedCandidateSettleGas() (gas: 3013335)\nLogs:\n  cold settle, 24 sips and repaired candidate: 272198\n\n[PASS] test_requestedStaticConstructorAndForbiddenOpcodes() (gas: 8026701)\n[PASS] test_thirdIncompleteMoveAndAdvancePaymentGas() (gas: 3924579)\nLogs:\n  cold third incomplete with move and advance repayment: 202030\n\nSuite result: ok. 4 passed; 0 failed; 0 skipped; finished in 58.22ms (24.96ms CPU time)\n\nRan 12 tests for test/PlantOrganism.t.sol:PlantRewardsTest\n[PASS] testFuzz_floorConservationAndBurnCustody(uint96,uint96,uint24) (runs: 256, μ: 2335917, ~: 2347961)\nLogs:\n  Bound result 20621\n\n[PASS] testFuzz_hourlyAccountingMatchesIndependentModel(uint24,uint24) (runs: 256, μ: 1800183, ~: 1803646)\n[PASS] test_departuresDropOutAndReparkingCannotCaptureDay() (gas: 4107169)\n[PASS] test_finalRedemptionDoesNotDivideByZeroOrReleaseBurnedPlant() (gas: 2449577)\n[PASS] test_gardenerDustRoundsToBackingAndAccountsCloseExactly() (gas: 5259461)\n[PASS] test_hourZeroFirstRainCapAndSunRequiresWater() (gas: 4841001)\n[PASS] test_noGardenersSendsEntireSipToBacking() (gas: 1548055)\n[PASS] test_nonExactTokenTransfersRejected() (gas: 274277)\n[PASS] test_oldCellKeepsItsRewardsAfterMoveAndLazyActivation() (gas: 5794681)\n[PASS] test_parkSettleClaimProRataAndMidDayJoinsWaitOneSettle() (gas: 3184396)\n[PASS] test_redeemRetainsPlantAndTenPercentAndFloorMonotonic() (gas: 2200068)\n[PASS] test_reentrancyFromTokensAndIntakeBlocked() (gas: 2288899)\nSuite result: ok. 12 passed; 0 failed; 0 skipped; finished in 76.65ms (151.31ms CPU time)\n\nRan 1 test for test/PlantInvariant.t.sol:PlantInvariantTest\n[PASS] invariant_conservationAndPermanentBurnCustody() (runs: 128, calls: 8192, reverts: 0)\n\n╭--------------+---------------+-------+---------+----------╮\n| Contract     | Selector      | Calls | Reverts | Discards |\n+===========================================================+\n| PlantHandler | challenge     | 1055  | 0       | 0        |\n|--------------+---------------+-------+---------+----------|\n| PlantHandler | checkpointAll | 1001  | 0       | 0        |\n|--------------+---------------+-------+---------+----------|\n| PlantHandler | claim         | 1009  | 0       | 0        |\n|--------------+---------------+-------+---------+----------|\n| PlantHandler | fund          | 1032  | 0       | 0        |\n|--------------+---------------+-------+---------+----------|\n| PlantHandler | park          | 1014  | 0       | 0        |\n|--------------+---------------+-------+---------+----------|\n| PlantHandler | redeem        | 1014  | 0       | 0        |\n|--------------+---------------+-------+---------+----------|\n| PlantHandler | unpark        | 1045  | 0       | 0        |\n|--------------+---------------+-------+---------+----------|\n| PlantHandler | weather       | 1022  | 0       | 0        |\n╰--------------+---------------+-------+---------+----------╯\n\nLogs:\n  Bound result 1664\n  Bound result 17496\n  Bound result 16706\n  Bound result 17324810955\n  Bound result 261\n  Bound result 1979\n  Bound result 2758\n  Bound result 405\n  Bound result 9878\n  Bound result 6305\n  Bound result 3118\n  Bound result 33924073662563232\n\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 1.69s (1.68s CPU time)\n\nRan 11 test suites in 1.69s (2.06s CPU time): 73 tests passed, 0 failed, 0 skipped (73 total tests)\n","passed":true},{"durationMs":56,"exitCode":0,"name":"source-index","output":"{\"v\":1,\"entryPoints\":[\"PlantOrganism.bind(address)\",\"PlantOrganism.challenge(uint32)\",\"PlantOrganism.checkpoint(uint32,address)\",\"PlantOrganism.claim()\",\"PlantOrganism.claim(uint32[])\",\"PlantOrganism.clearPending()\",\"PlantOrganism.die()\",\"PlantOrganism.heartbeat()\",\"PlantOrganism.onOracleResult(bytes32,(bytes32,uint256,bytes32,uint8,bytes,uint256,uint64,uint64,bytes32,bytes32,uint16,uint16,uint16,uint64,uint64),bytes)\",\"PlantOrganism.park(uint32,uint256)\",\"PlantOrganism.redeem(uint256)\",\"PlantOrganism.rotate(address,address,bytes32,uint256,bytes)\",\"PlantOrganism.settle()\",\"PlantOrganism.settle(uint32)\",\"PlantOrganism.unpark(uint32,uint256)\"],\"files\":{\".gitignore\":3,\"DEPENDENCIES.md\":7,\"README.md\":30,\"foundry.toml\":19,\"launch.json\":17,\"src/OracleAttestation.sol\":221,\"src/PlantOrganism.sol\":645,\"src/WeatherQuestion.sol\":83,\"src/interfaces/IIntake.sol\":14,\"test/GasAndDeployment.t.sol\":113,\"test/OracleConsumerConformance.t.sol\":126,\"test/PlantAssumptions.t.sol\":86,\"test/PlantInvariant.t.sol\":166,\"test/PlantOrganism.t.sol\":919,\"test/PlantVotingCommitment.t.sol\":196,\"test/PlantVotingRevision.t.sol\":115,\"test/WeatherQuestion.t.sol\":36,\"test/fixtures/weather.json\":11,\"test/mocks/Mocks.sol\":137},\"excluded\":[\"lib/\",\"node_modules/\"],\"truncated\":false}","passed":true}],"detail":"all checks passed","evaluation":"checks","profile":"foundry","status":"accepted","submissionHash":"c655c0c13529062dae20353f219f9069d4b13eb6a19971b59011ef8ca0ae49c5","verifiedTreeHash":"7c4c63efd1f6297947bd2c8057fd349277857cda","verifierVersion":"0.1.0+ad90ce4c"},{"checks":[{"durationMs":30115,"exitCode":0,"name":"build","output":"Compiling 53 files with Solc 0.8.26\nSolc 0.8.26 finished in 29.99s\nCompiler run successful!\nwarning[unsafe-typecast]: typecast can truncate values\n   ╭▸ src/WeatherQuestion.sol:12:21\n   │\n12 │         int16 lat = int16(uint16(cell >> 16));\n   │                     ━━━━━━━━━━━━━━━━━━━━━━━━━\n   │\n   ├ note: consider disabling this lint if you're certain the cast is safe\n   │       \n   │       // casting to 'int16' is safe because [explain why]\n   │       // forge-lint: disable-next-line(unsafe-typecast)\n   │       \n   │       \n   ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n   ╭▸ src/WeatherQuestion.sol:12:27\n   │\n12 │         int16 lat = int16(uint16(cell >> 16));\n   │                           ━━━━━━━━━━━━━━━━━━\n   │\n   ├ note: consider disabling this lint if you're certain the cast is safe\n   │       \n   │       // casting to 'uint16' is safe because [explain why]\n   │       // forge-lint: disable-next-line(unsafe-typecast)\n   │       \n   │       \n   ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n   ╭▸ src/WeatherQuestion.sol:13:21\n   │\n13 │         int16 lon = int16(uint16(cell));\n   │                     ━━━━━━━━━━━━━━━━━━━\n   │\n   ├ note: consider disabling this lint if you're certain the cast is safe\n   │       \n   │       // casting to 'int16' is safe because [explain why]\n   │       // forge-lint: disable-next-line(unsafe-typecast)\n   │       \n   │       \n   ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n   ╭▸ src/WeatherQuestion.sol:13:27\n   │\n13 │         int16 lon = int16(uint16(cell));\n   │                           ━━━━━━━━━━━━\n   │\n   ├ note: consider disabling this lint if you're certain the cast is safe\n   │       \n   │       // casting to 'uint16' is safe because [explain why]\n   │       // forge-lint: disable-next-line(unsafe-typecast)\n   │       \n   │       \n   ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n   ╭▸ src/WeatherQuestion.sol:19:29\n   │\n19 │         uint256 magnitude = uint256(milli < 0 ? -milli : milli);\n   │                             ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n   │\n   ├ note: consider disabling this lint if you're certain the cast is safe\n   │       \n   │       // casting to 'uint256' is safe because [explain why]\n   │       // forge-lint: disable-next-line(unsafe-typecast)\n   │       \n   │       \n   ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n   ╭▸ src/WeatherQuestion.sol:26:29\n   │\n26 │             b[--i] = bytes1(uint8(48 + n % 10));\n   │                             ━━━━━━━━━━━━━━━━━━\n   │\n   ├ note: consider disabling this lint if you're certain the cast is safe\n   │       \n   │       // casting to 'uint8' is safe because [explain why]\n   │       // forge-lint: disable-next-line(unsafe-typecast)\n   │       \n   │       \n   ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[divide-before-multiply]: division before multiplication may lose precision\n   ╭▸ src/WeatherQuestion.sol:39:27\n   │\n39 │         uint256 y = yoe + era * 400;\n   │                           ━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/divide-before-multiply\n\nwarning[divide-before-multiply]: division before multiplication may lose precision\n   ╭▸ src/WeatherQuestion.sol:40:30\n   │\n40 │         uint256 doy = doe - (365 * yoe + yoe / 4 - yoe / 100);\n   │                              ━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/divide-before-multiply\n\nwarning[divide-before-multiply]: division before multiplication may lose precision\n   ╭▸ src/WeatherQuestion.sol:42:28\n   │\n42 │         uint256 d = doy - (153 * mp + 2) / 5 + 1;\n   │                            ━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/divide-before-multiply\n\nwarning[unsafe-typecast]: typecast can truncate values\n   ╭▸ src/WeatherQuestion.sol:50:40\n   │\n50 │         string memory lat = coordinate(int16(uint16(cell >> 16)));\n   │                                        ━━━━━━━━━━━━━━━━━━━━━━━━━\n   │\n   ├ note: consider disabling this lint if you're certain the cast is safe\n   │       \n   │       // casting to 'int16' is safe because [explain why]\n   │       // forge-lint: disable-next-line(unsafe-typecast)\n   │       \n   │       \n   ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n   ╭▸ src/WeatherQuestion.sol:50:46\n   │\n50 │         string memory lat = coordinate(int16(uint16(cell >> 16)));\n   │                                              ━━━━━━━━━━━━━━━━━━\n   │\n   ├ note: consider disabling this lint if you're certain the cast is safe\n   │       \n   │       // casting to 'uint16' is safe because [explain why]\n   │       // forge-lint: disable-next-line(unsafe-typecast)\n   │       \n   │       \n   ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n   ╭▸ src/WeatherQuestion.sol:51:40\n   │\n51 │         string memory lon = coordinate(int16(uint16(cell)));\n   │                                        ━━━━━━━━━━━━━━━━━━━\n   │\n   ├ note: consider disabling this lint if you're certain the cast is safe\n   │       \n   │       // casting to 'int16' is safe because [explain why]\n   │       // forge-lint: disable-next-line(unsafe-typecast)\n   │       \n   │       \n   ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n   ╭▸ src/WeatherQuestion.sol:51:46\n   │\n51 │         string memory lon = coordinate(int16(uint16(cell)));\n   │                                              ━━━━━━━━━━━━\n   │\n   ├ note: consider disabling this lint if you're certain the cast is safe\n   │       \n   │       // casting to 'uint16' is safe because [explain why]\n   │       // forge-lint: disable-next-line(unsafe-typecast)\n   │       \n   │       \n   ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/PlantOrganism.sol:167:16\n    │\n167 │         return uint32(block.timestamp / 1 days);\n    │                ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint32' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[missing-zero-check]: address parameter is used in a state write or value transfer without a zero-address check\n    ╭▸ src/PlantOrganism.sol:170:19\n    │\n170 │     function bind(address hook_) external nonReentrant {\n    │                   ━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/missing-zero-check\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/PlantOrganism.sol:206:25\n    │\n206 │         require(held <= uint256(type(int256).max) && reserved <= uint256(type(int256).max));\n    │                         ━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/PlantOrganism.sol:206:66\n    │\n206 │         require(held <= uint256(type(int256).max) && reserved <= uint256(type(int256).max));\n    │                                                                  ━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/PlantOrganism.sol:207:16\n    │\n207 │         return int256(held) - int256(reserved);\n    │                ━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'int256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/PlantOrganism.sol:207:31\n    │\n207 │         return int256(held) - int256(reserved);\n    │                               ━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'int256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/PlantOrganism.sol:212:24\n    │\n212 │         return p > 0 ? uint256(p) : 0;\n    │                        ━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/PlantOrganism.sol:511:13\n    │\n511 │             emit Checkpointed(holder, cell, credit);\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/PlantOrganism.sol:279:13\n    │\n279 │             emit Challenged(cell);\n    │             ━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/PlantOrganism.sol:249:9\n    │\n249 │         emit Parked(msg.sender, cell, amount);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/PlantOrganism.sol:267:9\n    │\n267 │         emit Unparked(msg.sender, cell, amount);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[reentrancy-no-eth]: external call can be reentered before `pending` is updated\n    ╭▸ src/PlantOrganism.sol:302:14\n    │\n302 │           id = intake.request(\n    │ ┏━━━━━━━━━━━━━━┛\n303 │ ┃             action,\n304 │ ┃             requestBody(location, day),\n305 │ ┃             IIntake.Callback(address(this), this.onOracleResult.selector),\n306 │ ┃             address(IMD),\n307 │ ┃             price\n308 │ ┃         );\n    │ ┗━━━━━━━━━┛\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-no-eth\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/PlantOrganism.sol:289:13\n    │\n289 │         if (block.timestamp < retryAt) revert RetryLater();\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/PlantOrganism.sol:298:13\n    │\n298 │             emit Advanced(msg.sender, advance);\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/PlantOrganism.sol:313:9\n    │\n313 │         emit Asked(id, day, location, challenger, msg.sender, price);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[incorrect-strict-equality]: dangerous strict equality check on an externally-influenced value\n    ╭▸ src/PlantOrganism.sol:310:13\n    │\n310 │         if (IMD.balanceOf(address(this)) != beforeBalance - price) revert NonExactTransfer();\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/incorrect-strict-equality\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/PlantOrganism.sol:312:54\n    │\n312 │             Pending(id, day, challenger, msg.sender, uint64(block.timestamp), address(intake), true, false, bytes32(0));\n    │                                                      ━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint64' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/PlantOrganism.sol:330:20\n    │\n330 │                 && block.timestamp < signerAcceptedUntil[recovered]\n    │                    ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/PlantOrganism.sol:351:9\n    │\n351 │         emit Received(requestId, a.requestId, word);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/PlantOrganism.sol:341:13\n    │\n341 │         if (uint32(w >> 96) != p.day) revert WrongDay();\n    │             ━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint32' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/PlantOrganism.sol:360:17\n    │\n360 │             if (block.timestamp < uint256(pending.askedAt) + ORACLE_TIMEOUT) revert NotTimedOut();\n    │                 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[uninitialized-local]: local variable is read before being initialized\n    ╭▸ src/PlantOrganism.sol:432:27\n    │\n432 │         emit Settled(day, word, sips, backing, water, location);\n    │                           ━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/uninitialized-local\n\nwarning[uninitialized-local]: local variable is read before being initialized\n    ╭▸ src/PlantOrganism.sol:425:13\n    │\n425 │         if (caller != address(0)) {\n    │             ━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/uninitialized-local\n\nwarning[uninitialized-local]: local variable is read before being initialized\n    ╭▸ src/PlantOrganism.sol:415:30\n    │\n415 │         _distribute(oldCell, pool);\n    │                              ━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/uninitialized-local\n\nwarning[uninitialized-local]: local variable is read before being initialized\n    ╭▸ src/PlantOrganism.sol:432:33\n    │\n432 │         emit Settled(day, word, sips, backing, water, location);\n    │                                 ━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/uninitialized-local\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/PlantOrganism.sol:388:13\n    │\n388 │             emit EmptyCatchUp(day, end);\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/PlantOrganism.sol:406:17\n    │\n406 │                 emit Incomplete(day, count);\n    │                 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/PlantOrganism.sol:370:9\n    │\n370 │         emit Cleared(id, day, retryAt);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/PlantOrganism.sol:489:9\n    │\n489 │         emit Rewarded(cell, pool, reserved);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/PlantOrganism.sol:460:13\n    │\n460 │             emit Moved(current, candidate);\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/PlantOrganism.sol:420:13\n    │\n420 │             emit Moved(0, location);\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/PlantOrganism.sol:429:13\n    │\n429 │             emit Bounty(caller, bounty);\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/PlantOrganism.sol:551:13\n    │\n551 │             emit Paid(holder, credit, advance);\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/PlantOrganism.sol:557:13\n    │\n557 │             emit PaymentDeferred(holder, credit + advance);\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/PlantOrganism.sol:432:9\n    │\n432 │         emit Settled(day, word, sips, backing, water, location);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/PlantOrganism.sol:386:61\n    │\n386 │             uint32 end = hook == address(0) ? today() - 1 : uint32(Math.min(today() - 1, bindDay - 1));\n    │                                                             ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint32' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/PlantOrganism.sol:449:17\n    │\n449 │         water = uint8(wet);\n    │                 ━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint8' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[uninitialized-local]: local variable is read before being initialized\n    ╭▸ src/PlantOrganism.sol:545:22\n    │\n545 │         if (credit + advance == 0) return;\n    │                      ━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/uninitialized-local\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/PlantOrganism.sol:574:9\n    │\n574 │         emit Redeemed(msg.sender, amount, payout, dead);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/PlantOrganism.sol:589:13\n    │\n589 │             emit Died(today(), merged);\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/PlantOrganism.sol:591:13\n    │\n591 │             emit PotMerged(merged);\n    │             ━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/OracleAttestation.sol:182:9\n    │\n182 │         emit OracleSignerSet(signer);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/PlantOrganism.sol:623:9\n    │\n623 │         emit Rotated(oldSigner, newSigner, newIntake, newAction, nonce);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[incorrect-strict-equality]: dangerous strict equality check on an externally-influenced value\n    ╭▸ src/PlantOrganism.sol:629:13\n    │\n629 │         if (token.balanceOf(address(this)) != beforeBalance + amount) revert NonExactTransfer();\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/incorrect-strict-equality\n\n","passed":true},{"durationMs":7002,"exitCode":0,"name":"test","output":"No files changed, compilation skipped\n\nRan 3 tests for test/WeatherQuestion.t.sol:WeatherQuestionTest\n[PASS] test_coordinatesAtEdgesAndNegativeZeroFraction() (gas: 40309)\n[PASS] test_exactLisbonBodyAndKnownDate() (gas: 15004)\n[PASS] test_exactNegativeLatitudeAndLongitudeBody() (gas: 15401)\nSuite result: ok. 3 passed; 0 failed; 0 skipped; finished in 321.54µs (364.10µs CPU time)\n\nRan 4 tests for test/OracleConsumerConformance.t.sol:OracleConsumerConformanceTest\n[PASS] test_acceptsOriginalProtocolSignatureInCanonicalVerifier() (gas: 117518)\n[PASS] test_bytes32WeatherFixtureAcceptedUnderStipend() (gas: 1710833)\n[PASS] test_callbackSelectorIsCanonical() (gas: 3543)\n[PASS] test_digestMatchesTheProtocol() (gas: 13139)\nSuite result: ok. 4 passed; 0 failed; 0 skipped; finished in 2.05ms (1.18ms CPU time)\n\nRan 4 tests for test/PlantOrganism.t.sol:PlantRotationTest\n[PASS] test_oldSignerExpiresAtThirtyDaysWhileNewSignerKeepsPlantAlive() (gas: 19271508)\n[PASS] test_onlyCurrentSignerCanRotateNonceCannotReplay() (gas: 1150397)\n[PASS] test_pendingIntakeAndOldSignerWorkAfterRotation() (gas: 3771780)\n[PASS] test_rotationDomainBindsAllFieldsAndContract() (gas: 974444)\nSuite result: ok. 4 passed; 0 failed; 0 skipped; finished in 19.57ms (21.70ms CPU time)\n\nRan 11 tests for test/PlantOrganism.t.sol:PlantLifecycleTest\n[PASS] test_bindIsOnceAndOnlyDeployerWithMatchingHook() (gas: 325519)\n[PASS] test_birthFallbackAfterExactlyThreeEmptyDays() (gas: 305969)\n[PASS] test_birthMovesAtFivePercentWithoutMinimumStay() (gas: 659503)\n[PASS] test_cellBoundsAndZeroRejected() (gas: 609033)\n[PASS] test_challengerOnlyStrictlyLargerAndCanBeRepaired() (gas: 907339)\n[PASS] test_deathIsFinalAtThirtyDaysAndKeepsExits() (gas: 2597183)\n[PASS] test_deathUsesBindDayAfterLongUnboundPeriod() (gas: 285387)\n[PASS] test_moveUsesPendingCandidateButLiveBalances() (gas: 2969440)\n[PASS] test_noEarlyOrOutOfOrderSettleAndHeartbeat() (gas: 153141)\n[PASS] test_preBindDaysSkipInOneCallWithoutRead() (gas: 644624)\n[PASS] test_unboundCatchUpIsBoundedAndDoesNotRunBirth() (gas: 157507)\nSuite result: ok. 11 passed; 0 failed; 0 skipped; finished in 162.28ms (5.77ms CPU time)\n\nRan 12 tests for test/PlantOrganism.t.sol:PlantOracleTest\n[PASS] test_blockedKeeperDoesNotBlockSettleAndCanClaimLater() (gas: 2071258)\n[PASS] test_callbackSenderIdAndReplay() (gas: 2199329)\n[PASS] test_callbackStoresOnlyAndFitsStipend() (gas: 1778035)\nLogs:\n  callback gas including mock encoding: 94192\n\n[PASS] test_clearIncompleteUsesSameCounterAndCannotDiscardComplete() (gas: 2182128)\n[PASS] test_exactApprovalAndIntakeBody() (gas: 1418979)\n[PASS] test_feeAdvanceTracksDeficitThenRepaidAtSettle() (gas: 2134351)\n[PASS] test_incompleteHasNoBiologicalChangesThenThirdReads() (gas: 3236266)\n[PASS] test_intakeMustPullExactPrice() (gas: 1259699)\n[PASS] test_rejectsExpiredWrongDomainSignerTypeDayChainAndPanel() (gas: 2520481)\n[PASS] test_rejectsPreAskFutureTamperedAndMalformedAttestations() (gas: 2261782)\n[PASS] test_timeoutClearsAndWaitsSixHoursToReaskSameDay() (gas: 2200768)\n[PASS] test_unfundedAdvanceDoesNotSpendBackingOrBlockSettle() (gas: 2724661)\nSuite result: ok. 12 passed; 0 failed; 0 skipped; finished in 170.89ms (22.99ms CPU time)\n\nRan 4 tests for test/GasAndDeployment.t.sol:GasAndDeploymentTest\n[PASS] test_coldCallbackAndMaxHoursMoveSettleGas() (gas: 2572185)\nLogs:\n  cold callback gas: 94192\n\n[PASS] test_requestedStaticConstructorAndForbiddenOpcodes() (gas: 7928021)\n[PASS] test_settlementFitsStipendWithManyEligibleHolders() (gas: 38302870)\n[PASS] test_thirdIncompleteMoveAndAdvancePaymentGas() (gas: 3663999)\nSuite result: ok. 4 passed; 0 failed; 0 skipped; finished in 180.60ms (56.67ms CPU time)\n\nRan 12 tests for test/PlantOrganism.t.sol:PlantRewardsTest\n[PASS] testFuzz_floorConservationAndBurnCustody(uint96,uint96,uint24) (runs: 1000, μ: 2324955, ~: 2337878)\nLogs:\n  Bound result 439193391276\n\n[PASS] testFuzz_hourlyAccountingMatchesIndependentModel(uint24,uint24) (runs: 1000, μ: 1791413, ~: 1793707)\n[PASS] test_departuresDropOutAndReparkingCannotCaptureDay() (gas: 4076157)\n[PASS] test_finalRedemptionDoesNotDivideByZeroOrReleaseBurnedPlant() (gas: 2439439)\n[PASS] test_gardenerDustRoundsToBackingAndAccountsCloseExactly() (gas: 5209191)\n[PASS] test_hourZeroFirstRainCapAndSunRequiresWater() (gas: 4779386)\n[PASS] test_noGardenersSendsEntireSipToBacking() (gas: 1529791)\n[PASS] test_nonExactTokenTransfersRejected() (gas: 274277)\n[PASS] test_oldCellKeepsItsRewardsAfterMoveAndLazyActivation() (gas: 4941414)\n[PASS] test_parkSettleClaimProRataAndMidDayJoinsWaitOneSettle() (gas: 3164621)\n[PASS] test_redeemRetainsPlantAndTenPercentAndFloorMonotonic() (gas: 2189930)\n[PASS] test_reentrancyFromTokensAndIntakeBlocked() (gas: 2278761)\nSuite result: ok. 12 passed; 0 failed; 0 skipped; finished in 191.48ms (383.52ms CPU time)\n\nRan 21 tests for test/PlantAdversarial.t.sol:PlantAdversarialTest\n[PASS] testFuzz_parkUnparkRoundTripCannotEarnWithoutSettlement(uint96,uint8) (runs: 1000, μ: 2424177, ~: 1953660)\nLogs:\n  Bound result 10684\n  Bound result 6\n\n[PASS] testFuzz_reservedWeatherBitsRejectedWithoutMutatingPending(uint8) (runs: 1000, μ: 1428842, ~: 1428833)\n[PASS] testFuzz_signedMalformedPanelsAreRejected(uint8) (runs: 1000, μ: 1425502, ~: 1425366)\n[PASS] test_answerMustBeExactlyOneWordIncludingTrailingData() (gas: 1573137)\n[PASS] test_bindRejectsNonContractWrongAndEmptyTokens() (gas: 1568770)\n[PASS] test_callbackAndSettlementEventsDescribeTheAppliedResult() (gas: 1665193)\n[PASS] test_challengerAddedAfterAskWaitsForFollowingRequest() (gas: 2487054)\n[PASS] test_consumedOracleUuidCannotBeReusedForSameDayRetry() (gas: 2252286)\n[PASS] test_deathClearsPendingAndPreservesGardenersAndFeeDebt() (gas: 2833668)\n[PASS] test_deferredAdvanceAndBountyRemainClaimableExactlyOnce() (gas: 2204673)\n[PASS] test_exactClockDriftToleranceAccepted() (gas: 1422430)\n[PASS] test_moveRewardsDepartingCellAndNeverIncomingCellForSameDay() (gas: 3258281)\n[PASS] test_moveThresholdRoundsUpWhenSupplyNotDivisibleByTwenty() (gas: 689444)\n[PASS] test_rejectedResultCanBeCorrectedWithoutBurningRequest() (gas: 1728112)\n[PASS] test_rotatedIntakeCannotAnswerPreviousIntakesRequest() (gas: 3442164)\n[PASS] test_thirdIncompleteReadsCandidateFromLatestRequest() (gas: 3306511)\n[PASS] test_timeoutExactSecondAndOldResultCannotPoisonReask() (gas: 1884423)\n[PASS] test_twoKeepersAdvancesSurviveTimeoutAndOnlyUnlockOnSettlement() (gas: 2613002)\n[PASS] test_unparkAfterCallbackDropsOutBeforeHoursAndKeepsPreviousReward() (gas: 2604054)\n[PASS] test_validityWindowBoundariesAndAskedAt() (gas: 1848131)\n[PASS] test_zeroAmountsAndOverdrawCannotChangeCustody() (gas: 595443)\nSuite result: ok. 21 passed; 0 failed; 0 skipped; finished in 191.52ms (587.82ms CPU time)\n\nRan 1 test for test/PlantInvariant.t.sol:PlantInvariantTest\n[PASS] invariant_conservationAndPermanentBurnCustody() (runs: 128, calls: 8192, reverts: 0)\n\n╭--------------+---------------+-------+---------+----------╮\n| Contract     | Selector      | Calls | Reverts | Discards |\n+===========================================================+\n| PlantHandler | challenge     | 990   | 0       | 0        |\n|--------------+---------------+-------+---------+----------|\n| PlantHandler | checkpointAll | 1075  | 0       | 0        |\n|--------------+---------------+-------+---------+----------|\n| PlantHandler | claim         | 966   | 0       | 0        |\n|--------------+---------------+-------+---------+----------|\n| PlantHandler | fund          | 1050  | 0       | 0        |\n|--------------+---------------+-------+---------+----------|\n| PlantHandler | park          | 1024  | 0       | 0        |\n|--------------+---------------+-------+---------+----------|\n| PlantHandler | redeem        | 1049  | 0       | 0        |\n|--------------+---------------+-------+---------+----------|\n| PlantHandler | unpark        | 1006  | 0       | 0        |\n|--------------+---------------+-------+---------+----------|\n| PlantHandler | weather       | 1032  | 0       | 0        |\n╰--------------+---------------+-------+---------+----------╯\n\nLogs:\n  Bound result 7077\n  Bound result 842010790672636867173\n  Bound result 8439\n  Bound result 99999999\n  Bound result 1483\n  Bound result 48809866\n  Bound result 2366\n  Bound result 1602827397\n  Bound result 4519\n  Bound result 4908\n  Bound result 5968\n  Bound result 1222\n  Bound result 3171\n  Bound result 1575069946793070028\n  Bound result 5263\n  Bound result 6482\n\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 1.54s (1.54s CPU time)\n\nRan 1 test for test/PlantStateMachine.t.sol:PlantStateMachineInvariantTest\n[PASS] invariant_independentCustodyAndSolvency() (runs: 256, calls: 24576, reverts: 0)\n\n╭--------------------------+---------------+-------+---------+----------╮\n| Contract                 | Selector      | Calls | Reverts | Discards |\n+=======================================================================+\n| PlantStateMachineHandler | answer        | 2050  | 0       | 0        |\n|--------------------------+---------------+-------+---------+----------|\n| PlantStateMachineHandler | challenge     | 2021  | 0       | 0        |\n|--------------------------+---------------+-------+---------+----------|\n| PlantStateMachineHandler | claim         | 2085  | 0       | 0        |\n|--------------------------+---------------+-------+---------+----------|\n| PlantStateMachineHandler | clear         | 2047  | 0       | 0        |\n|--------------------------+---------------+-------+---------+----------|\n| PlantStateMachineHandler | donate        | 2045  | 0       | 0        |\n|--------------------------+---------------+-------+---------+----------|\n| PlantStateMachineHandler | elapse        | 2008  | 0       | 0        |\n|--------------------------+---------------+-------+---------+----------|\n| PlantStateMachineHandler | park          | 2099  | 0       | 0        |\n|--------------------------+---------------+-------+---------+----------|\n| PlantStateMachineHandler | redeem        | 2015  | 0       | 0        |\n|--------------------------+---------------+-------+---------+----------|\n| PlantStateMachineHandler | request       | 2052  | 0       | 0        |\n|--------------------------+---------------+-------+---------+----------|\n| PlantStateMachineHandler | settle        | 2039  | 0       | 0        |\n|--------------------------+---------------+-------+---------+----------|\n| PlantStateMachineHandler | transferPlant | 2067  | 0       | 0        |\n|--------------------------+---------------+-------+---------+----------|\n| PlantStateMachineHandler | unpark        | 2048  | 0       | 0        |\n╰--------------------------+---------------+-------+---------+----------╯\n\nLogs:\n  Bound result 3000000000000000000\n  Bound result 200000000000000000000\n  Bound result 114264337593543950333\n  Bound result 853\n  Bound result 5842\n  Bound result 1799\n  Bound result 11512\n  Bound result 11244\n  Bound result 11540\n  Bound result 23294\n  Bound result 181385881926\n  Bound result 3892\n  Bound result 82184016227149456491\n  Bound result 1980\n  Bound result 22152\n  Bound result 17289\n  Bound result 1\n  Bound result 273\n  Bound result 3314\n  Bound result 383476485063936878807\n  Bound result 3112\n  Bound result 14127\n  Bound result 49280\n  Bound result 58604\n  Bound result 2151\n  Bound result 18327\n  Bound result 1352\n  Bound result 1\n  Bound result 3056\n  Bound result 289\n  Bound result 425\n  Bound result 22900\n  Bound result 15761\n  Bound result 299524276\n  Bound result 80091487951467557383\n  Bound result 2233\n  Bound result 2010\n  Bound result 276\n  Bound result 4999\n  Bound result 4166\n  Bound result 4174\n  Bound result 7685895105581222477\n  Bound result 42\n  Bound result 3\n  Bound result 2270\n  Bound result 6195\n  Bound result 1626\n  Bound result 4802\n  Bound result 200000000000000000000\n  Bound result 9727\n  Bound result 114264337593543948047\n  Bound result 2540\n  Bound result 276\n  Bound result 7517\n  Bound result 999999999818614114529\n  Bound result 999999999999999971360\n  Bound result 999999999999999985644\n\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 6.92s (6.92s CPU time)\n\nRan 10 test suites in 6.93s (9.39s CPU time): 73 tests passed, 0 failed, 0 skipped (73 total tests)\n","passed":true},{"durationMs":35,"exitCode":0,"name":"source-index","output":"{\"v\":1,\"entryPoints\":[\"PlantOrganism.bind(address)\",\"PlantOrganism.challenge(uint32)\",\"PlantOrganism.checkpoint(uint32,address)\",\"PlantOrganism.claim()\",\"PlantOrganism.claim(uint32[])\",\"PlantOrganism.clearPending()\",\"PlantOrganism.die()\",\"PlantOrganism.heartbeat()\",\"PlantOrganism.onOracleResult(bytes32,(bytes32,uint256,bytes32,uint8,bytes,uint256,uint64,uint64,bytes32,bytes32,uint16,uint16,uint16,uint64,uint64),bytes)\",\"PlantOrganism.park(uint32,uint256)\",\"PlantOrganism.redeem(uint256)\",\"PlantOrganism.rotate(address,address,bytes32,uint256,bytes)\",\"PlantOrganism.settle()\",\"PlantOrganism.settle(uint32)\",\"PlantOrganism.unpark(uint32,uint256)\"],\"files\":{\".gitignore\":3,\"DEPENDENCIES.md\":7,\"README.md\":30,\"foundry.toml\":19,\"launch.json\":17,\"src/OracleAttestation.sol\":221,\"src/PlantOrganism.sol\":631,\"src/WeatherQuestion.sol\":83,\"src/interfaces/IIntake.sol\":14,\"test/GasAndDeployment.t.sol\":109,\"test/OracleConsumerConformance.t.sol\":125,\"test/PlantAdversarial.t.sol\":417,\"test/PlantInvariant.t.sol\":166,\"test/PlantOrganism.t.sol\":912,\"test/PlantStateMachine.t.sol\":342,\"test/README.md\":47,\"test/WeatherQuestion.t.sol\":36,\"test/fixtures/weather.json\":11,\"test/mocks/Mocks.sol\":137},\"excluded\":[\"lib/\",\"node_modules/\"],\"truncated\":false}","passed":true}],"detail":"all checks passed","evaluation":"checks","profile":"foundry","status":"accepted","submissionHash":"d969c13a6b67694e2790b5173762ec54f89fc61337eec1bfad96637c08b3cd3f","verifiedTreeHash":"3da9752c4d5bff2d6f274a85d28ae555bb04798a","verifierVersion":"0.1.0+ad90ce4c"}]}