engine+cli: wheel-on-wheel push batch — WHEEL_WORKSPACE, idle-parking, agent-sender test, ctx-clear - #23
Merged
Merged
Conversation
…idle parking, (c) agent-sender coverage, (d) ctx clear (a) The engine already provisions a worktree from `workspaces` — proven in production this morning, a 4.1 MB shared store and a 7.1 MB worktree. What was missing was a way to NAME it: cwd was always right, but a script that has cd-ed cannot recover the path and there was no variable to ask. WHEEL_WORKSPACE is exported only when a workspace was really materialised, so it never names the fallback directory. (b) Idle parking, §3c#14, the reason this project exists rather than YOKE. idle_timeout_secs had a field, an accessor, a default, a Parked status and a test named after the behaviour — and the engine never read it. Measured before this: an agent an hour past its turn still held a 162 MB claude process; six would hold a gigabyte. Park is `stop` that KEEPS session_id, so the next message resumes the same session — the half already proven when the first wake came back on the id it went to sleep with. The timer re-checks under the slot lock, so a message arriving as it fires either restarts the agent or finds work queued and leaves it alone. (c) Agent→agent was covered at the CLI plane and envelope rendering in wheel-core, but nothing asserted the two together: every test watching bytes reach a child's stdin used a `user` sender. That is the producer a multi-agent board runs on. (d) `wheel ctx clear` had an engine route since M1 and a promise in every agent's preamble, and no dispatch arm — so it answered "unknown command" to a command we told agents to use. Adds the arm, the usage line, and an invariant that every advertised verb has an arm. Each mutation-checked. The (d) invariant needed two attempts: the first matched the verb anywhere after `match cmd`, so it passed with the arm deliberately removed — it was finding the word in an error message. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RqRiYvAcUKDYCHb4HDP8Jg
`let _ = child.kill()` discarded the result, so a failed kill left a live process while the board reported Parked — the saving claimed and not made, and invisible. kill_on_drop still reaps it, so the process does die; what was missing was anyone hearing that the direct kill did not. SCOPE NOTE, disclosed rather than folded in quietly: ADVERSARY found this in `park`, and the identical swallow was in `stop` and `clear_context` too. All three are fixed here. It is log-only with no behavioural change, and fixing one instance of a class while leaving two is how the same finding gets rediscovered as 050. Objecting to the extra two lines is reasonable and I will split them out if either reviewer prefers. Found because it was risk #2 of the three I gave the reviewers — which is the argument for handing an adversary your own list of where you think it breaks. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RqRiYvAcUKDYCHb4HDP8Jg
…never initialises BUG-040, found by QA reading the code before running anything: a timer is armed per turn and none is cancelled, so an OLD timer outlives the turn that armed it. Two turns 250s apart under a 300s timeout leaves the first firing 50s after the second, parking an agent that was active moments ago — safe, since it resumes, but it silently shortens the hot window the operator configured. Fixed QA's way rather than mine: park compares last_activity to now and REPORTS the remainder instead of parking, and the timer task loops on what it reports. No cancellation state, self-correcting however many stale timers are in flight, and it closes the timer-accumulation M2 in the same change. Reporting rather than re-arming inside park also keeps park non-recursive — a recursive async fn cannot be proven Send, which is how the first attempt failed to compile. Elapsed time comes from sqlite's julianday, the same way every other age in this engine is measured, rather than adding a date library to a crate that has done without one. Second fix, same area: a start that passed --resume and never reached `init` now clears the session. Nothing cleared it before, so every later start re-passed the same dead id and failed identically — an agent that could never recover without a manual clear. Parking makes this matter, because resume used to be rare and is now the normal path back from idle. Dropping a session we might have kept costs context once; keeping a dead one costs every start from here on. Both mutation-checked: parking anyway fails the first, not clearing fails the second. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RqRiYvAcUKDYCHb4HDP8Jg
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
The wheel-on-wheel push batch — the prerequisites that let the cloud board self-develop safely, landed as one deploy.
What's in it
WHEEL_WORKSPACE— the engine exports each agent's provisioned worktree path (/data/projects/<p>/ws/<agent>/wheel) into the child env. Provisioning (A8/A9 worktree off the shared object store) was already live and proven in production; this only names it. Paired with the doc/ctx change telling agents to work there instead of cloning.session_idso the next message resumes the same session.idle_timeout_secs=0opts out (stays hot).every_verb_in_usage_has_a_dispatch_arm), mutation-verified.Review (all independent, run not inspected)
--resume <pre-park id>read from the child's argv, resumes same session, no message loss,timeout=0hot); (d) mutation-checked (removed the arm, watched it fail naming the verb);cargo test --workspaceexit 0, 58 suites.session_idNone before park) was attributed to a test stub not emittingsystem/init, not the engine.Follow-up (not in this batch)
🤖 Generated with Claude Code
https://claude.ai/code/session_01BTHr6CTMA4NNuLKd2PS4h4