Repository navigation
feat: the box decides its own wakefulness (#380) - #381
Conversation
Workroom no longer manages a provider's sleep or billing. It owns two things only, both because it causes them: an open app never holds an idle box awake, and a box never sleeps under running work (the heartbeat, #257). The agent pushes each change of verdict to every connection that has asked for `status` (Status service version 2), so nothing polls: the app's 10 s poll crossed eth0 often enough to vote its own box BUSY, and a connected boxd box never slept. Once the verdict has been IDLE for 30 s, the agent closes every connection at least that old that carries no forwarded connection, unless a pane is attached anywhere: a pane's ssh holds a box awake whatever happens, and a local container's credential relay rides the service connection. The app asks once per connection and follows the pushes, ordering a connection's readings by the agent's clock. A connection that ends on an IDLE box of a host that sleeps marks the host released, so the status sweep does not ssh back in and hold the box awake; selecting or opening its workroom, a click, a pane attaching, a forward in use or any new connection forgets it. A pane attaching connects a host with no service connection, waking it only when released. A remote host's port forward outlives its connection and reconnects when used; it never starts a container the user stopped. Removed: the awake ceiling everywhere (its prompt, `keep`, `settings`, the kept settings file, the `--awake-*` flags and variables, the three preferences, the Settings row, the prompt cards), the app's status poll and let-go, `disconnect(_:unless:)`, the boxd idle-window read and its badge warning, and this Mac's own badge, whose agent runs no wakefulness service. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ness (#380) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…lness (#380) An idle box's agent lets go of the attached app, the app takes that as idle and stops reaching it, and a kept forward and a selection reconnect it; a busy box keeps the app's connection and logs every tick of its job. Whether an idle box then sleeps is its provider's: a fresh boxd box was still running 300 s after its agent let go, with nothing of Workroom's holding it, so that is no longer asserted. A failure reports the verdicts the app saw. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Drives real connections through handle_connection: a pane spares every connection, an in-flight forward spares its own, and an unused one is closed and its peer sees EOF. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A busy box is asked for its status again every 60 s: the agent pushes only on a change, which a stalled or crashed wakefulness service never makes, so the badge kept showing a box kept awake while its heartbeat had stopped. Only while BUSY, so an idle box is never held awake. The Linux push test causes a verdict change after it subscribes; the service's first push usually came before it did, and the test failed 10 of 10 times run alone. The push decision is one helper the service and its test both call. Forwards' reconnect is a plain value, now that nothing is read late. Coverage for the pushed-change stream ending with its connection and for a forward's live connection. Stale comments and superseded design-doc bullets fixed. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Nothing "never polls" any more: an idle box is never asked again, a busy one is once a minute. Two more #254 bullets marked superseded, and the routing tests say the agent lets go, not the app. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Codex Review SummaryThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 35c9612e04
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
There was a problem hiding this comment.
🟡 Changes recommended
Status push thread-creation failure can permanently stop wakefulness protection and idle release.
1 open finding
What changed in this PR
Moves wakefulness ownership into wr-agent: verdicts are pushed to clients, idle connections are released agent-side, and awake-ceiling behavior is removed.
Changes:
- Adds pushed status events and agent-side idle connection release.
- Reconnects remote port forwards on demand.
- Removes awake-ceiling settings, prompts, polling, and related tests/UI.
| File | Description |
|---|---|
vcs/crates/wr-agent/tests/status_service.rs |
Tests status v2 and pushed verdicts. |
vcs/crates/wr-agent/tests/let_go.rs |
Tests idle connection release. |
vcs/crates/wr-agent/src/wakefulness/tests.rs |
Replaces ceiling tests with push/release tests. |
vcs/crates/wr-agent/src/wakefulness/heartbeat.rs |
Drives heartbeats directly from verdicts. |
vcs/crates/wr-agent/src/wakefulness.rs |
Implements status v2, pushes, and idle release. |
vcs/crates/wr-agent/src/serve.rs |
Registers and selectively closes connections. |
vcs/crates/wr-agent/src/main.rs |
Removes awake-ceiling CLI options. |
vcs/crates/wr-agent/src/forward.rs |
Reports active forwarded connections. |
TODOS.md |
Updates wakefulness follow-up work. |
macapp/WorkroomAppUITests/WakefulnessPromptUITests.swift |
Removes obsolete prompt UI tests. |
macapp/WorkroomAppTests/WakefulnessBadgeTests.swift |
Removes polling-specific badge test. |
macapp/WorkroomAppTests/Session/AgentBootstrapTests.swift |
Removes ceiling-setting tests. |
macapp/WorkroomAppTests/RemoteWorkroomRoutingTests.swift |
Updates released-host routing tests. |
macapp/WorkroomAppTests/RemoteWorkroomDeletionTests.swift |
Updates host-watch deletion coverage. |
macapp/WorkroomAppTests/RemoteHostsTests.swift |
Tests agent-released host behavior. |
macapp/WorkroomAppTests/RemoteHostIntegrationTests.swift |
Watches pushed host status. |
macapp/WorkroomAppTests/HostConnectionManagerTests.swift |
Removes app-side let-go coverage. |
macapp/WorkroomAppTests/BoxdIntegrationTests.swift |
Tests idle release and busy protection. |
macapp/WorkroomAppTests/BoxdHostDriverTests.swift |
Removes idle-window parsing tests. |
macapp/WorkroomAppTests/AgentPortForwardingTests.swift |
Tests reconnecting forwards. |
macapp/WorkroomAppTests/AgentFileIntegrationTests.swift |
Updates fake status events to v2. |
macapp/WorkroomApp/WorkroomApp.swift |
Removes prompt-watch lifecycle hooks. |
macapp/WorkroomApp/Views/ToastStack.swift |
Removes awake-ceiling prompt UI. |
macapp/WorkroomApp/Views/SettingsView.swift |
Removes awake-ceiling settings. |
macapp/WorkroomApp/Views/ProjectSidebar.swift |
Uses push-driven remote badges. |
macapp/WorkroomApp/Views/ChangesPanel.swift |
Simplifies wakefulness badges and forward status. |
macapp/WorkroomApp/Core/WorkroomStatus.swift |
Updates idle-state terminology. |
macapp/WorkroomApp/Core/UITestFixture.swift |
Removes prompt fixture support. |
macapp/WorkroomApp/Core/Session/PersistentSessionService.swift |
Reconnects hosts when panes attach. |
macapp/WorkroomApp/Core/Session/AgentBootstrap.swift |
Removes ceiling-setting synchronization. |
macapp/WorkroomApp/Core/RepositoryLocation.swift |
Updates released-host errors. |
macapp/WorkroomApp/Core/RemoteWorkrooms.swift |
Replaces app let-go with release tracking. |
macapp/WorkroomApp/Core/RemoteProvisioning.swift |
Simplifies agent bootstrap. |
macapp/WorkroomApp/Core/LocalAgentVCS.swift |
Removes local wakefulness connection modes. |
macapp/WorkroomApp/Core/HostDriver.swift |
Documents sleep-related traits. |
macapp/WorkroomApp/Core/HostConnectionManager.swift |
Removes conditional disconnect API. |
macapp/WorkroomApp/Core/DefaultsKeys.swift |
Removes awake-ceiling preferences. |
macapp/WorkroomApp/Core/BoxdHostDriver.swift |
Removes idle-window querying. |
macapp/WorkroomApp/Core/AgentVCSConnection.swift |
Decodes pushed status changes. |
macapp/WorkroomApp/Core/AgentPortForwarding.swift |
Preserves forwards across reconnection. |
macapp/AGENTS.md |
Documents agent-side idle release. |
docs/designs/remote-workrooms.md |
Records the #380 architecture. |
🧠 Review effort: Balanced
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
A new listener is pushed the current status on the next tick, whatever the box is doing; the push test waited for the verdict to change, which on a busy CI runner it never did. A refused push thread no longer panics the wakefulness service. At most 64 clients wait on a forward's reconnect, and removing the forward closes them: a burst against a slow wake held descriptors without bound. A deleted host's forwards go with it, since its connection no longer drops them. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 553479d355
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
The old test passed with no forward registered. It now adds a real forward under a host and checks that forgetHost drops the model, stops the forward and closes its listener. Confirmed red with the stop loop removed. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: c2cd8af235
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
The ceiling, `keep`, the prompt event and the agent's settings are gone, so the older design-doc passages that describe them say so. TODOS drops the flaky crossing-`keep` test entry (the test went with `keep`) and trims the kept-settings entry to its one remaining item. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: efc646d934
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
The owner decided an open app holding a box awake is the user's concern: deleting the workroom frees it. The let-go also closed local containers' connections, which never sleep, taking their git credential relay with them (Codex review on #381). The connection registry, LET_GO_GRACE_S, Forwards::carrying and their tests go; serve.rs is back to master apart from the removed awake-ceiling settings. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
With the agent no longer letting go of idle connections, the app's `RemoteHosts.released` marker, the `.idle` routing error and row state, and the badge's "Workroom doesn't hold it awake" text have nothing left to describe. A background read still never wakes a box boxd says is asleep (#356). The let-go's live boxd tests and unit tests go with it; the design doc, macapp/AGENTS.md and TODOS say so. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 40e34d81f5
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
The owner decided an open app holding a box awake is the user's concern: deleting the workroom frees it. The let-go also closed local containers' connections, which never sleep, taking their git credential relay with them (Codex review on #381). The connection registry, LET_GO_GRACE_S, Forwards::carrying and their tests go; serve.rs is back to master apart from the removed awake-ceiling settings. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

Fixes #380.
After merge, close #371's items 6 (rest), 8, 9, 13 and 17. They are edge cases of the app-side wakefulness logic this PR removes. Item 18 (#380 itself) closes with them.
Release note
Summary
The agent reports (wr-agent). The wakefulness service pushes status changes to listeners on the Status service (v2), and a new listener gets the current status on the next tick. The awake ceiling, its
--awake-*flags and thesettingsrequest are gone. Push threads usethread::Builder::spawn, so a refused thread no longer panics the service. Nothing lets go of an idle connection. A first version had the agent close idle connections, but Codex found it also dropped local containers' connections and their git credential relay. The owner then decided an open app holding a box awake is the user's concern (delete the workroom to free it), so the let-go was removed.The app follows (macapp).
WakefulnessModelasks for status once per connection, then follows pushes. It re-asks every 60 s only while BUSY. The app-side let-go (boxd-only on master), its "idle, not connected" row state, the status poll, the ceiling prompt, the idle-window warnings and the setting are removed. A background read still never wakes a box boxd says is asleep (#356). Port forwards outlive their connection and reconnect on use (PortForward.Reconnect), since a connection still drops when the network does or the box sleeps. Deleting a remote workroom forgets its forwards (PortForwardingModel.forgetHost).Docs.
docs/designs/remote-workrooms.mdhas an "As built (#380)" section with the design and the decisions behind it.Follow-up #382 (after merge). The owner decided that keeping a box awake under running work is the user's concern too. #382 removes the heartbeat, the BUSY/IDLE classifier, the Status service and the badge. Its let-go replacement and the dead-pane case are moot now that there is no let-go.
Test Coverage
Tests written:
AgentWakefulnessTests.testTheChangesStreamEndsWithItsConnection, and wr-agent unit tests for push-on-change, the pushed status reply and the new-listener push. (The let-go's tests,tests/let_go.rsamong them, went with the let-go.) Extended:PortForwardingModelTests' reconnect test, andtestForgettingAHostForgetsItsForwards(confirmed red withforgetHost's stop loop removed).Coverage: 76% value-weighted (79% including 1 weakly covered path). 6 paths are intentionally uncovered, per the owner (D2): they belong to the classifier and heartbeat that #382 removes.
Test value: 2 tests written, 0 rejected by the authoring gate, 2 existing tests extended, 1 path weakly covered.
Pre-Landing Review
Three passes, plus a Claude and Codex adversarial review.
forgetHosttest. Fixed in c2cd8af.Exploratory QA
QA smoke 001–004 ran against the app and agent. 003 (the Linux push test) was flaky and was fixed in 553479d. On Linux the fix passed 10/10 under both busy and quiet load, and the mutant without the new-listener push failed 3/3 quiet. 005 and 006 were revalidated and pass.
Live boxd acceptance ran on the let-go version (billed, 2026-10-08, both PASS). Both tests went with the let-go: what they checked no longer exists, and whether a box sleeps is the provider's and the user's concern, deliberately not tested.
Not run, by owner decision: the #257 heartbeat live test (
testTheHeartbeatKeepsABusyBoxAwakeAndABoxWithoutOneSleeps). The heartbeat's behaviour is unchanged, and #382 removes it.Design Review
UI changes are removals only: the setting, the ceiling prompt and the warning texts. No new UI. Design review was skipped.
Eval Results
No prompt-related files changed, so evals were skipped.
Greptile Review
Greptile triage: UNAVAILABLE (Greptile is not installed on this repository). Codex and Copilot review instead:
Plan Completion
Plan: issue #380. 23 items: 15 done, 6 changed, 1 partial (
is_busyis not pinned by a test), 0 not done, 1 unverifiable (the #371 closes, which are listed above to do after merge).Verification Results
Step 8.1: the live heartbeat check is NOT RUN, and its risk was accepted by the owner. The live let-go and busy-box checks passed on the let-go version and were removed with it. Rust and app suites: PASS (see Test plan).
TODOS
No TODO items completed in this PR. "Harden the agent's kept settings" is trimmed to its one remaining item, because the
settingsrequest it covered is removed. The P0 entry for the flaky crossing-keeptest is dropped, because the test went withkeep. "A crashed wakefulness thread hides the badge" loses its let-go sentence.Documentation
Status: updated. 3 factual doc edits, no blockers.
Scope: branch
box-decides-wakefulness(#380, auditdocs-381-1791501403) against base d8bbe27. Audited committed, staged and unstaged content (TODOS.md is the only unstaged file; there are no new files). Discovered 17 docs. Fully read: the three edited design docs,macapp/AGENTS.md, and the TODOS.md diff. remote-workrooms.md was read by section. The rest were grepped for the removed surface (awake, ceiling, wakefulness, keep, let go, heartbeat, Ask before a busy machine) with no hits.Documentation health:
docs/designs/remote-workrooms.md: Updated. Added "gone since The box decides its own wakefulness: agent-side let-go, pushed verdicts, no app-side wakefulness management #380" notes to the stale Phase 2 service description (ceiling,keepand prompt event), the Far-side lifecycle shim: keep a busy box awake through the provider (boxd first) #257 follow-up list that still named "Still owed: the ceiling prompt", the "unanswered prompt" meaning of "busy but not kept awake", and the "changed setting reaches the agent on next connect" limit. The "As built (The box decides its own wakefulness: agent-side let-go, pushed verdicts, no app-side wakefulness management #380)" section matches the code (STATUS_SERVICE_VERSION 2, LET_GO_GRACE_S 30 s, 60 s busy re-ask;RemoteHosts.released, port-forwardreconnectand the removed symbols were checked).docs/designs/oq19-wakefulness-measurements.md: Updated. OQ22 (the awake ceiling) marked superseded by The box decides its own wakefulness: agent-side let-go, pushed verdicts, no app-side wakefulness management #380, decision kept for the record.docs/designs/oq8-cross-machine-reattach.md: Updated. Three notes thatkeepand the keep-awake test are gone (Open Question 2, the implementation note, a success criterion).macapp/AGENTS.md: Current. The branch already updated the boxd paragraph.TODOS.md: Reviewed, not edited (protected). The branch's edits are consistent with The box decides its own wakefulness: agent-side let-go, pushed verdicts, no app-side wakefulness management #380. Debt: the P0 itemAgentWakefulnessTests.testAReplyCrossingAKeepDoesNotResurrectTheCard flakesnames a test that no longer exists and should be dropped or retitled by the parent.Coverage and debt: The removed surface (ceiling, settings request, prompt cards, Settings row, app-side let-go) has no user-facing doc outside the design docs, so nothing else was stale. New behavior (pushed status verdicts, agent-side let-go, forwards that survive idle disconnects) has reference and explanation coverage in "As built (#380)". It has no how-to or tutorial coverage, and none is owed because wakefulness is the user's concern. Release notes for the removal are the parent's. No architecture diagrams exist, so no diagram drift. Minor prose debt: the Known limits paragraph in remote-workrooms.md (near line 1799) repeats the ceiling-prompt sentence awkwardly.
Test plan
cargo test --manifest-path vcs/Cargo.toml -p wr-agenton macOS: all pass after the let-go removal (37e9be4)vcs/scripts/test-linux.sh: all pass, including the Linux push test, with no warningsmake app-test: 3209 passed, 0 failed, 59 skipped, on 40e34d8's contentmake app-lintandcargo fmt --check: clean🤖 Generated with Claude Code