Fix resident autonomy, Skipper coordination, and Cowork contracts - #46
Conversation
📝 WalkthroughWalkthroughThis change adds resident-scoped status handling, user-authorized Skipper workflows, durable Cowork output contracts, Apple guest-network recovery, expanded skill metadata, and removal of retired demo-host references. ChangesAgent coordination and Cowork contracts
Apple guest-network recovery
Retired host and public documentation cleanup
Estimated code review effort: 5 (Critical) | ~120 minutes Sequence Diagram(s)sequenceDiagram
participant RequestingUser
participant AgentRuntime
participant CoworkContract
participant Workspace
RequestingUser->>AgentRuntime: start scoped agent work
AgentRuntime->>CoworkContract: derive durable surface contract
AgentRuntime->>Workspace: snapshot and modify workspace
AgentRuntime->>CoworkContract: validate new outputs
CoworkContract->>Workspace: remove incompatible files and synchronize artifacts
sequenceDiagram
participant AppleMachine
participant FleetReconciliation
participant VmnetServices
participant GuestCommand
AppleMachine->>FleetReconciliation: report failed guest network
FleetReconciliation->>VmnetServices: recover shared networking
FleetReconciliation->>AppleMachine: restart and verify machine
AppleMachine->>GuestCommand: run command with restored network
Possibly related PRs
🚥 Pre-merge checks | ✅ 3 | ❌ 2❌ Failed checks (2 warnings)
✅ Passed checks (3 passed)
✨ Finishing Touches 💡 1📝 Generate docstrings 💡
🧪 Generate unit tests (beta)
Comment |
There was a problem hiding this comment.
Actionable comments posted: 4
🧹 Nitpick comments (4)
test/brief-regressions-browser.mjs (1)
130-152: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick winScope the working-row assertions to the target channel.
The selectors at Line 139 and Line 142 count every desktop sidebar row. The test also uses a separate
mainChanneland does not reset other resident statuses. An unrelated working resident can make these assertions fail even whenapplyAgentStatusEventis correct. Query the target channel row or reset all fixture residents toreadybefore asserting.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@test/brief-regressions-browser.mjs` around lines 130 - 152, Scope the working-row queries in the sidebarAgentIdentity evaluation to the target channel represented by target.id, rather than counting all desktop sidebar rows. Update both the dots count and workingRows selection to use that channel’s sidebar row, preserving the existing assertions for Skipper rejection and resident acceptance without relying on unrelated resident statuses.test/site.mjs (1)
46-46: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick winRestore regression coverage for retired-host routing.
This assertion checks only manual text. It cannot detect an HTTP redirect for the retired host, and it does not check a hostname token. Add a request-level no-redirect check and explicit content checks for the retired references.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@test/site.mjs` at line 46, Extend the regression coverage around the existing manual assertion to make an HTTP request to the retired host with redirects disabled, asserting that no redirect response is returned and the response contains the expected retired-host reference. Retain explicit content checks for both the hostname token and the existing retired sandbox references, using the test’s established request utilities.test/channel-computers.mjs (1)
67-71: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winConsider adding coverage for the fleet-reconciliation repair path.
This test exercises the boot-time repair path (
ensureChannelComputerRunning, channel-computers.ts Line 846). The new deferral logic inreconcileOne(channel-computers.ts Lines 1534-1540), which skips repair whilecanStopChannelComputerreturns false, has no visible direct test coverage in this diff. Consider adding a scenario that simulates an active obligation on an Apple channel and asserts thatreconcileChannelComputersdefers rather than forcing a stop.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@test/channel-computers.mjs` around lines 67 - 71, Add a direct fleet-reconciliation test alongside the existing Apple VM repair coverage that simulates an active obligation causing canStopChannelComputer to return false, then invokes reconcileChannelComputers and verifies repair is deferred without forcing the computer to stop. Keep the existing boot-time ensureChannelComputerRunning assertions unchanged.src/server/channel-computers.ts (1)
125-126: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick winKey the Apple network repair throttle to the active incident.
appleNetworkRepairAtsuppressesapple(["system", "start"])for 30 seconds across all Apple machines. An independent failure can therefore receive onlymachine stopandmachine run. The fake container clears.network-downonly duringsystem start, so this path can throw"channel computer network remained unavailable after automatic repair".Cross-file test-order coupling is not present because the channel-computer test runs in its own test process.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/server/channel-computers.ts` around lines 125 - 126, Update the Apple network repair throttle represented by appleNetworkRepairAt so it is scoped to the active incident or machine rather than shared across all Apple machines; ensure a new independent failure can invoke apple(["system", "start"]) even when another machine was repaired within the 30-second window, while preserving deduplication for repeated repairs of the same incident.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@src/server/bots.ts`:
- Around line 1887-1889: Update the authorization-tool name list in the Skipper
error branch near the result handling conditional to include schedule_workflow,
list_workflows, and set_workflow_status, so unauthorized calls use the existing
authority-boundary error message instead of the generic unavailable-tool
message.
- Around line 1813-1814: Replace the duplicated host-or-personal-owner
authorization expression in the tool-name branch with the existing
skipperControlAuthorized helper defined near line 190, while preserving the
agent kind and allowed tool checks.
In `@src/server/channel-computers.ts`:
- Around line 588-623: Update repairAppleGuestNetwork and its caller
reconcileOne to use an atomic channel-scoped repair/admission gate held through
machine stop, restart, and health verification. Acquire the gate before checking
terminal sessions or active work, count the current repair operation when
evaluating competing commands, and prevent runChannelCommand or
openChannelTerminal from admitting new work until repair completes; do not rely
on a standalone re-check after work can begin.
- Around line 1534-1540: Move the shared active-work safety check into
repairAppleGuestNetwork so it runs before restarting the Apple guest network or
stopping the machine, while preserving ensureChannelComputerRunning’s
caller-specific allowance for its own command or turn. Remove the duplicated
caller-side check only if the centralized API can retain that distinction.
Ensure reconciliation deferrals are treated as retryable: do not increment
errors, mark provision_status as error, record failed activity, or expose
last_error.
---
Nitpick comments:
In `@src/server/channel-computers.ts`:
- Around line 125-126: Update the Apple network repair throttle represented by
appleNetworkRepairAt so it is scoped to the active incident or machine rather
than shared across all Apple machines; ensure a new independent failure can
invoke apple(["system", "start"]) even when another machine was repaired within
the 30-second window, while preserving deduplication for repeated repairs of the
same incident.
In `@test/brief-regressions-browser.mjs`:
- Around line 130-152: Scope the working-row queries in the sidebarAgentIdentity
evaluation to the target channel represented by target.id, rather than counting
all desktop sidebar rows. Update both the dots count and workingRows selection
to use that channel’s sidebar row, preserving the existing assertions for
Skipper rejection and resident acceptance without relying on unrelated resident
statuses.
In `@test/channel-computers.mjs`:
- Around line 67-71: Add a direct fleet-reconciliation test alongside the
existing Apple VM repair coverage that simulates an active obligation causing
canStopChannelComputer to return false, then invokes reconcileChannelComputers
and verifies repair is deferred without forcing the computer to stop. Keep the
existing boot-time ensureChannelComputerRunning assertions unchanged.
In `@test/site.mjs`:
- Line 46: Extend the regression coverage around the existing manual assertion
to make an HTTP request to the retired host with redirects disabled, asserting
that no redirect response is returned and the response contains the expected
retired-host reference. Retain explicit content checks for both the hostname
token and the existing retired sandbox references, using the test’s established
request utilities.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro Plus
Run ID: 2e0bf148-6976-4b00-804c-276f0a4b9d89
📒 Files selected for processing (23)
CHANGELOG.mddesktop/workspace-target.cjsdocs/GOVERNANCE.mddocs/VISION.mddocs/release-notes-template.mdpublic/index.htmlscripts/autonomy-benchmark.mjssite/content.mjssite/manual.htmlsite/server.mjssrc/client/app.tssrc/server/bots.tssrc/server/channel-computers.tssrc/server/cowork-contract.tssrc/server/index.tssrc/server/skills.tstest/autonomy-platform.mjstest/brief-regressions-browser.mjstest/channel-computers.mjstest/desktop.mjstest/fake-container.mjstest/native-world.mjstest/site.mjs
💤 Files with no reviewable changes (4)
- site/manual.html
- test/desktop.mjs
- docs/VISION.md
- site/server.mjs
| } else if (["list_channels", "inspect_channel", "archive_channel", "restore_channel", "delete_channel", "inspect_fleet", "care_for_channel_computer", "list_obligations", "schedule_workflow", "list_workflows", "set_workflow_status"].includes(name) | ||
| && agent?.kind === "skipper" && (hostAuthorized || Boolean(q1("SELECT 1 FROM channels WHERE id=? AND personal_main_owner_id=?", channelId, requestUserId)))) { |
There was a problem hiding this comment.
📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick win
Reuse skipperControlAuthorized instead of duplicating the authorization check.
This condition repeats the same logic already defined in skipperControlAuthorized (Line 190). Duplicated authorization logic can drift out of sync if one copy is updated and the other is not. Call the shared helper here instead.
♻️ Proposed fix
- } else if (["list_channels", "inspect_channel", "archive_channel", "restore_channel", "delete_channel", "inspect_fleet", "care_for_channel_computer", "list_obligations", "schedule_workflow", "list_workflows", "set_workflow_status"].includes(name)
- && agent?.kind === "skipper" && (hostAuthorized || Boolean(q1("SELECT 1 FROM channels WHERE id=? AND personal_main_owner_id=?", channelId, requestUserId)))) {
+ } else if (["list_channels", "inspect_channel", "archive_channel", "restore_channel", "delete_channel", "inspect_fleet", "care_for_channel_computer", "list_obligations", "schedule_workflow", "list_workflows", "set_workflow_status"].includes(name)
+ && agent?.kind === "skipper" && skipperControlAuthorized(channelId, requestUserId, hostAuthorized)) {
result = String(await executeSkipperControlTool(name, args, requestUserId, hostAuthorized));📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| } else if (["list_channels", "inspect_channel", "archive_channel", "restore_channel", "delete_channel", "inspect_fleet", "care_for_channel_computer", "list_obligations", "schedule_workflow", "list_workflows", "set_workflow_status"].includes(name) | |
| && agent?.kind === "skipper" && (hostAuthorized || Boolean(q1("SELECT 1 FROM channels WHERE id=? AND personal_main_owner_id=?", channelId, requestUserId)))) { | |
| } else if (["list_channels", "inspect_channel", "archive_channel", "restore_channel", "delete_channel", "inspect_fleet", "care_for_channel_computer", "list_obligations", "schedule_workflow", "list_workflows", "set_workflow_status"].includes(name) | |
| && agent?.kind === "skipper" && skipperControlAuthorized(channelId, requestUserId, hostAuthorized)) { |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@src/server/bots.ts` around lines 1813 - 1814, Replace the duplicated
host-or-personal-owner authorization expression in the tool-name branch with the
existing skipperControlAuthorized helper defined near line 190, while preserving
the agent kind and allowed tool checks.
| else result = agent?.kind === "skipper" && ["create_channel", "list_channels", "inspect_channel", "archive_channel", "restore_channel", "delete_channel", "inspect_fleet", "care_for_channel_computer", "list_obligations"].includes(name) | ||
| ? `Error: this user is not authorized to use ${name} in this channel.` | ||
| : `Error: tool ${name} is not available.`; |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
Add the workflow tool names to the "not authorized" error branch.
schedule_workflow, list_workflows, and set_workflow_status are gated by skipperControlAuthorized (Line 1813-1814), the same as create_channel and the other listed tools. This list at Line 1888 does not include them. An unauthorized Skipper call to one of these three tools falls through to the generic Error: tool ${name} is not available. instead of the accurate authority-boundary message. This contradicts the prompt instruction at Line 221 to explain the authority boundary rather than imply the capability is missing.
🐛 Proposed fix
- else result = agent?.kind === "skipper" && ["create_channel", "list_channels", "inspect_channel", "archive_channel", "restore_channel", "delete_channel", "inspect_fleet", "care_for_channel_computer", "list_obligations"].includes(name)
+ else result = agent?.kind === "skipper" && ["create_channel", "list_channels", "inspect_channel", "archive_channel", "restore_channel", "delete_channel", "inspect_fleet", "care_for_channel_computer", "list_obligations", "schedule_workflow", "list_workflows", "set_workflow_status"].includes(name)
? `Error: this user is not authorized to use ${name} in this channel.`
: `Error: tool ${name} is not available.`;📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| else result = agent?.kind === "skipper" && ["create_channel", "list_channels", "inspect_channel", "archive_channel", "restore_channel", "delete_channel", "inspect_fleet", "care_for_channel_computer", "list_obligations"].includes(name) | |
| ? `Error: this user is not authorized to use ${name} in this channel.` | |
| : `Error: tool ${name} is not available.`; | |
| else result = agent?.kind === "skipper" && ["create_channel", "list_channels", "inspect_channel", "archive_channel", "restore_channel", "delete_channel", "inspect_fleet", "care_for_channel_computer", "list_obligations", "schedule_workflow", "list_workflows", "set_workflow_status"].includes(name) | |
| ? `Error: this user is not authorized to use ${name} in this channel.` | |
| : `Error: tool ${name} is not available.`; |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@src/server/bots.ts` around lines 1887 - 1889, Update the authorization-tool
name list in the Skipper error branch near the result handling conditional to
include schedule_workflow, list_workflows, and set_workflow_status, so
unauthorized calls use the existing authority-boundary error message instead of
the generic unavailable-tool message.
| async function repairAppleGuestNetwork(computer: ChannelComputer): Promise<void> { | ||
| if (computer.backend !== "apple") return; | ||
| if (!appleNetworkRepair) { | ||
| appleNetworkRepair = (async () => { | ||
| // Avoid repeatedly bouncing shared vmnet while several machines detect | ||
| // the same fleet-wide outage during one reconciliation pass. | ||
| if (now() - appleNetworkRepairAt > 30_000) { | ||
| if (platform() === "darwin") { | ||
| const label = `gui/${process.getuid?.() ?? 501}/com.apple.container.container-network-vmnet.default`; | ||
| const kicked = await spawnCollected("/bin/launchctl", ["kickstart", "-k", label], { timeoutMs: 30_000 }); | ||
| if (kicked.code !== 0) throw new Error(kicked.stderr.toString("utf8").trim() || "Apple shared VM network service could not restart"); | ||
| } | ||
| const started = await apple(["system", "start"], { timeoutMs: 90_000 }); | ||
| if (started.code !== 0) throw new Error(started.stderr.toString("utf8").trim() || "Apple container services could not restart"); | ||
| appleNetworkRepairAt = now(); | ||
| } | ||
| })().finally(() => { appleNetworkRepair = null; }); | ||
| } | ||
| await appleNetworkRepair; | ||
| const competingCommands = Number(q1(`SELECT COUNT(*) n FROM channel_computer_obligations | ||
| WHERE channel_id=? AND kind='command' AND status='active'`, computer.channel_id)?.n || 0); | ||
| const runningTurns = Number(q1("SELECT COUNT(*) n FROM agent_turns WHERE channel_id=? AND state='running'", computer.channel_id)?.n || 0); | ||
| if ([...terminalSessions.values()].some((session) => session.channelId === computer.channel_id) | ||
| || competingCommands > 1 || runningTurns > 1) { | ||
| throw new Error("resident computer network is unavailable; automatic repair is waiting for concurrent work to finish"); | ||
| } | ||
| const stopped = await apple(["machine", "stop", computer.machine_id], { timeoutMs: 90_000 }); | ||
| if (stopped.code !== 0 && !/not running|stopped/i.test(Buffer.concat([stopped.stderr, stopped.stdout]).toString("utf8"))) { | ||
| throw new Error(stopped.stderr.toString("utf8").trim() || "network repair could not stop the channel computer"); | ||
| } | ||
| const restarted = await apple(["machine", "run", "-n", computer.machine_id, "--", ...guestWords("/bin/sh", "-lc", APPLE_GUEST_NETWORK_CHECK)], { timeoutMs: 90_000 }); | ||
| if (restarted.code !== 0 || !await appleGuestNetworkHealthy(computer)) throw new Error("channel computer network remained unavailable after automatic repair"); | ||
| run("UPDATE channel_computers SET observed_state='running',provision_status='ready',last_health=?,last_error='',updated=? WHERE channel_id=?", now(), now(), computer.channel_id); | ||
| recordComputerActivity(computer.channel_id, "Recovered the resident computer's network without replacing its Linux disk.", "complete"); | ||
| } | ||
|
|
There was a problem hiding this comment.
🩺 Stability & Availability | 🟠 Major | 🏗️ Heavy lift
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Description: Confirm channel-lock coverage around command dispatch relative to repairAppleGuestNetwork's stop/restart.
ast-grep run --pattern 'withChannelLock($_, $_)' --lang typescript src/server/channel-computers.ts
rg -n 'function runChannelCommand|kind=.command.|status=.active.' src/server/channel-computers.ts -C5Repository: gitcommit90/1Helm
Length of output: 21994
🏁 Script executed:
#!/bin/bash
# Inspect the lock implementation, the complete command/turn dispatch paths, and reconcileOne callers.
rg -n 'withChannelLock|runChannelCommand|agent_turns|reconcileOne|repairAppleGuestNetwork|ensureChannelComputerRunning' src/server -g '*.ts' -C4
printf '\n--- channel-computers.ts 560-630 ---\n'
sed -n '560,630p' src/server/channel-computers.ts
printf '\n--- channel-computers.ts 800-875 ---\n'
sed -n '800,875p' src/server/channel-computers.ts
printf '\n--- channel-computers.ts 930-1015 ---\n'
sed -n '930,1015p' src/server/channel-computers.ts
printf '\n--- reconcile region ---\n'
sed -n '1460,1575p' src/server/channel-computers.tsRepository: gitcommit90/1Helm
Length of output: 50373
🏁 Script executed:
#!/bin/bash
# Read only the exact sections needed to determine whether the lock covers command execution
# and whether reconcileOne acquires the same lock before repairAppleGuestNetwork.
printf '%s\n' '--- lock implementation ---'
sed -n '118,142p' src/server/channel-computers.ts
printf '%s\n' '--- command lifecycle ---'
sed -n '935,957p' src/server/channel-computers.ts
printf '%s\n' '--- terminal lifecycle ---'
sed -n '957,1042p' src/server/channel-computers.ts
printf '%s\n' '--- turn admission and execution ---'
sed -n '1320,1365p' src/server/bots.ts
sed -n '1418,1475p' src/server/bots.ts
printf '%s\n' '--- reconcile and repair callers ---'
sed -n '1490,1562p' src/server/channel-computers.tsRepository: gitcommit90/1Helm
Length of output: 19953
Serialize network repair with channel work before machine stop. reconcileOne calls repairAppleGuestNetwork without withChannelLock. The lock in ensureChannelComputerRunning ends before runChannelCommand starts isolated or spawnCollected, and before openChannelTerminal creates its PTY. New work can therefore start after the checks, while competingCommands > 1 still permits one active command. Use an atomic repair/admission gate that accounts for the current command; a standalone re-check is insufficient.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@src/server/channel-computers.ts` around lines 588 - 623, Update
repairAppleGuestNetwork and its caller reconcileOne to use an atomic
channel-scoped repair/admission gate held through machine stop, restart, and
health verification. Acquire the gate before checking terminal sessions or
active work, count the current repair operation when evaluating competing
commands, and prevent runChannelCommand or openChannelTerminal from admitting
new work until repair completes; do not rely on a standalone re-check after work
can begin.
| if (computer.backend === "apple" && !await appleGuestNetworkHealthy(computer)) { | ||
| if (!canStopChannelComputer(computer.channel_id)) { | ||
| throw new Error("resident computer network is unavailable; automatic repair is waiting for its active work to stop"); | ||
| } | ||
| await repairAppleGuestNetwork(computer); | ||
| computer = channelComputer(computer.channel_id)!; | ||
| } |
There was a problem hiding this comment.
🩺 Stability & Availability | 🟠 Major | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Description: Check how canStopChannelComputer's criteria compare to repairAppleGuestNetwork's inline checks, and whether last_error is user-facing.
rg -n 'function canStopChannelComputer' -A 15 src/server/channel-computers.ts
rg -n 'last_error' src/client/app.ts src/server/channel-computers.ts -C3Repository: gitcommit90/1Helm
Length of output: 19097
🏁 Script executed:
#!/bin/bash
set -e
printf '%s\n' '--- repairAppleGuestNetwork and callers ---'
sed -n '580,625p' src/server/channel-computers.ts
sed -n '830,865p' src/server/channel-computers.ts
sed -n '1518,1555p' src/server/channel-computers.ts
printf '%s\n' '--- reconciliation and error handling ---'
rg -n 'reconcileOne|reconcileChannelComputers|recordComputerError|ensureChannelComputerRunning' src/server/channel-computers.ts -C 12
printf '%s\n' '--- all last_error consumers ---'
rg -n 'last_error|errors' src --glob '!src/server/channel-computers.ts' -C 3
printf '%s\n' '--- relevant type and API serialization ---'
sed -n '1,215p' src/server/channel-computers.tsRepository: gitcommit90/1Helm
Length of output: 50373
🏁 Script executed:
#!/bin/bash
set -e
python3 - <<'PY'
from pathlib import Path
p = Path("src/server/channel-computers.ts")
s = p.read_text()
checks = {
"canStopChannelComputer definition": "export function canStopChannelComputer",
"repairAppleGuestNetwork definition": "async function repairAppleGuestNetwork",
"reconciliation catch": "recordComputerError",
"deferred error text": "automatic repair is waiting for its active work to stop",
}
for label, needle in checks.items():
print(f"{label}: {s.count(needle)} occurrence(s)")
for needle in checks:
pass
# Print compact enclosing function ranges without executing repository code.
for name in ("canStopChannelComputer", "repairAppleGuestNetwork", "reconcileOne", "reconcileChannelComputers"):
pos = s.find(name)
print(f"{name}: first occurrence at line {s.count(chr(10), 0, pos)+1 if pos >= 0 else 'not found'}")
PYRepository: gitcommit90/1Helm
Length of output: 542
🏁 Script executed:
#!/bin/bash
set -e
python3 - <<'PY'
from pathlib import Path
s = Path("src/server/channel-computers.ts").read_text()
# Standalone behavioral model of the two predicates as implemented.
def can_stop(terminal_sessions, resident_obligations, working_agents):
return not terminal_sessions and not resident_obligations and not working_agents
def repair_allows(terminal_sessions, competing_commands, running_turns):
return not terminal_sessions and competing_commands <= 1 and running_turns <= 1
cases = [
("one active command and one running turn", False, True, True, 1, 1),
("two active commands", False, True, False, 2, 1),
("one working agent", False, False, True, 0, 0),
("terminal session", True, False, False, 0, 0),
]
for name, terminal, obligation, agent, commands, turns in cases:
left = can_stop(terminal, obligation, agent)
right = repair_allows(terminal, commands, turns)
print(f"{name}: canStop={left}, repairAllows={right}, same={left == right}")
# Confirm the shared-runtime restart occurs before repair's local safety check.
repair = s[s.index("async function repairAppleGuestNetwork"):s.index("const isolatedBackend")]
restart = repair.index('await apple(["system", "start"]')
safety = repair.index("const competingCommands")
print("repair restart precedes safety check:", restart < safety)
# Confirm reconciliation converts the deferral into a recorded error.
reconcile = s[s.index("async function reconcileOne"):s.index("export function startChannelComputerReconciler")]
print("deferral thrown:", "automatic repair is waiting for its active work to stop" in reconcile)
print("reconcile catch calls recordComputerError:", "catch (error) { errors++; recordComputerError(row.channel_id, error); }" in reconcile)
PY
printf '%s\n' '--- precise client exposure ---'
sed -n '1148,1172p' src/client/channel.tsRepository: gitcommit90/1Helm
Length of output: 3667
Move the safety check before the shared Apple network restart.
ensureChannelComputerRunning intentionally allows its own command or turn, while reconciliation rejects active work. Preserve this caller-specific allowance, but centralize the common safety logic.
repairAppleGuestNetwork currently restarts the shared network before checking active work. Check safety before restarting the shared service and before stopping the machine.
Treat reconciliation deferrals as retryable. They currently increment errors, set provision_status='error', record failed activity, and expose last_error in the channel UI.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@src/server/channel-computers.ts` around lines 1534 - 1540, Move the shared
active-work safety check into repairAppleGuestNetwork so it runs before
restarting the Apple guest network or stopping the machine, while preserving
ensureChannelComputerRunning’s caller-specific allowance for its own command or
turn. Remove the duplicated caller-side check only if the centralized API can
retain that distinction. Ensure reconciliation deferrals are treated as
retryable: do not increment errors, mark provision_status as error, record
failed activity, or expose last_error.
Outcome
Repairs the combined 0.0.32 candidate regression set before native packaging:
Verification so far
npm run typechecknpm run buildnpm run test:brief-browser(53/53)node --test test/autonomy-platform.mjs test/channel-surfaces.mjs test/channel-computers.mjs(28/28)node --test test/site.mjs test/desktop.mjsgit diff --checkBroader native-world/full-suite verification is continuing against this pushed commit while CI runs.
Summary by CodeRabbit
New Features
Bug Fixes
Documentation