Repository navigation
fix: issue #1 review findings - #3
Merged
Merged
Conversation
boxd_ensure rebuilt an open work's record from a short list of fields, so a coordinator that ensures each work at the start of a turn, such as after a resize, lost the files moved in and out and the last run's outcome. A drop then refused an output a harvest had already brought back, and status no longer showed a failed run. Ensure now starts from the record it loaded and clears only the create intent. Refs #1
GET /api/session/<id>/message lists newest first, 50 a page, on 2.2.3 and 2.2.6. The helper walked that page oldest first and stopped at the first idle message, so a session with several runs reported an earlier run's error, or none: a credential failure after a completed run read as GUEST_RUN_FAILED with no error. The helper now asks for ?type=idle&order=desc&limit=1, one message, the last run's. The fake guest server lists messages the way the real one does, and each finished run ends with its own idle message. Refs #1
A coded error's tool result was { error, next }: the sentence that said what
happened went only to the model's text, so a Code Mode program or a script saw a
bare TIMEOUT with no step and no cause. The result is now { error, message, next }.
A timeout names its command and machine, such as the guest's phase on
oc-<work id>, instead of "boxd machine". A GUEST_FAILED message carries the
helper's error and its detail, redacted, or the last line the machine wrote when
the helper answered nothing.
docs/scripting.md said a plugin code arrives as the RPC error failed; it arrives
as the method's output, and only the four RPC codes are errors.
Refs #1
The helper lives in the machine's /tmp, which a reboot or a resize clears, and the plugin only remembered that it had copied it once in this process. Every guest call then failed with GUEST_FAILED until OpenCode restarted. A call that gets no JSON back now lists /tmp with ls -1a. Only when the listing answered, includes ".", and lacks the helper's folder is the helper copied again and the call run once more: Python never started the action, so nothing runs twice. A crash, or a boxd CLI that failed (auth, network, a 503), keeps the call's own error with no copy and no retry. The probe is plain words, so it reads the same whether machine exec runs it directly or through a shell. The fake CLI now runs the copy the plugin uploaded and answers like Python when it is missing, so the host tests can clear /tmp as a reboot does. Refs #1
A guest call that times out is followed by one probe of the machine, which reads /proc and the server's port and never talks to OpenCode. A machine under a tenth of its memory or over four load per CPU answers GUEST_UNRESPONSIVE with the facts, and its one next step is to tell the user. A machine with nothing on the OpenCode port answers GUEST_SERVER_DOWN, whose next step is boxd_job. A machine that looks well keeps TIMEOUT, with the facts; a probe that does not answer, or answers with a line that is not JSON, keeps the plain timeout. Refs #1
boxd_job gave busy or idle and an outcome, so a session that stopped on a 401 and one that finished its task both read as idle and completed, and a coordinator had to read a report file on every machine by hand. On a running machine boxd_job now adds lastText, the newest assistant message's text parts, redacted and cut to 300 characters on the laptop; lastActivityAt, the latest of the session's and that message's times; and pending, the user items in the session's inbox. Each is read from the guest's own session with no model call, and left out when the guest does not answer it. A message of another kind than the one asked for is never read as an answer. boxd_submit reads /api/session/active before the prompt and answers queued: true when the session was running, so the prompt waits in its inbox for that run. That read is best-effort: a server that refuses it still takes the prompt. Refs #1
Ten machines were found hibernated with nothing to say whether boxd_sleep or the idle bound had done it. boxd reports no hibernation time or reason, so the plugin says what it knows. boxd_sleep records sleptAt in the ledger, and every wake or create the plugin makes clears it. boxd_job's asleep answer and each work in status carry asleepBy: boxd_sleep with sleptAt, or idle_or_cli for the 30-minute idle bound or the boxd CLI. The status text says the same beside the machine's state. Refs #1
…ks and credentials The skill said directory moves the guest's folder, which is always the plugin's own work folder on the machine, and that boxd_job answers busy, idle or missing, when it also answers empty, starting, asleep and gone. It now says directory is the laptop folder, names every state, and says what proof fresh, stale and missing mean. It adds what a coordinator needed: drive many works from one Code Mode loop, since there is no batch tool; where a guest's credentials come from, that a short-lived token stops every guest once nothing renews it, and that a prompt should tell the session to stop and report a 401 rather than start an interactive sign-in; and how to look at a slow guest with the boxd CLI, read-only. The README's known limits and the golden-image page say the same about short-lived tokens. Refs #1
The boxd CLI that runs inside a boxd machine takes --vm <name> where the laptop CLI takes the machine as an argument, so the plugin's commands failed one by one and the awake bound ended as AWAKE_UNBOUND. Every machine call now first reads boxd --help once per binary, which changes nothing, and answers BOXD_MISSING when it is the in-VM CLI: this plugin drives remote machines from the laptop's CLI. A CLI that cannot answer --help is not refused; its next command reports its own failure. Refs #1
The laptop CLI's help has not been read, so one phrase alone could be its own wording, and a false match would block every tool with BOXD_MISSING. The check now requires both "boxd inside a machine" and "from inside the VM". Refs #1
The helper runs on the Linux guest and reads /proc, which macOS has not, so the test that runs it on the runner failed on both macOS jobs. It now runs on Linux only. Refs #1
This was referenced Nov 12, 2026
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.
Refs #1. Each commit closes the issue #1 items named here; the items that need an owner decision are left open.
fix(fleet): keep the whole ledger record when ensure runs again— found while working on Agent feedback from 17 concurrent works: fleet status in one call, guest health, credentials, and a helper lost on reboot #1:boxd_ensureon an open work dropped the moved files and the last run's outcome.fix(guest): read the newest idle message for a run's outcome— found while working on Agent feedback from 17 concurrent works: fleet status in one call, guest health, credentials, and a helper lost on reboot #1: a failed run after a completed one reported no error.fix(tools): keep what happened in a coded error's result— bug 2, "the tool result drops the message": the result is now{ error, message, next }, and a timeout names its step and machine.fix(guest): copy the helper again when a reboot cleared it— bug 1: the helper is copied again, and the call run once, only when/tmpclearly lost its folder. A boxd CLI failure keeps its own error.feat(guest): say why a guest that times out cannot answer— bug 2, the probe:GUEST_UNRESPONSIVEwith free memory, load and the largest processes, andGUEST_SERVER_DOWNwhen the port is closed.feat(job): say what the guest session last did, and when a prompt waits—boxd_jobgetslastText,lastActivityAtandpending;boxd_submitgetsqueued.feat(job): say who put a machine to sleep— "say why a machine hibernated":asleepBy, andsleptAtfor the plugin's own sleeps. When boxd or the idle bound hibernated it, the time is boxd's to report.docs(skill): correct directory and the job states, and cover many works and credentials— "explainproof: "missing"", "batch submit" (a Code Mode loop), the credentials and 401 guidance, and the read-only diagnosis commands.feat(boxd): refuse the in-VM boxd CLI instead of ending as AWAKE_UNBOUND— found while working on Agent feedback from 17 concurrent works: fleet status in one call, guest health, credentials, and a helper lost on reboot #1: the in-VM CLI takes--vm, so the plugin now answersBOXD_MISSINGon it.fix(boxd): refuse the in-VM CLI only when its help carries both phrases— narrows the check above, so one phrase in the laptop CLI's help cannot block every tool.Not in this PR, left for the owner: live fields per work in
boxd_status,tail: n, one result shape for errors,modelon submit, a size onboxd_ensure, a bounded wait, a credential view inboxd_status, and renamingproof. Theboxd env set --secretstdin, token refresh and update-banner asks are for boxd.