Repository navigation
Check the planning agent at startup and before planning requests (#164) - #193
Merged
Merged
Conversation
PlanningAgent.probe runs one planning invocation in planning's worker and profile, asking for the schema's own answer. PlanningCheck starts it when the server starts and again when the vendor in Settings changes; productionPlanning waits for it in `describe`, before the issue is read or anything is recorded. A failed check refuses the request with "Planning is unavailable: <reason>" for a minute, after which the next request checks again. Shutdown cancels a running check before it closes the worker. Closes #164 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… vendor (#164) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Review round 1: the probe starts synchronously again (a deferred start broke three tests and let close wait out its cap); the check gets a real request's budget, since the first job may build the image, while a request waits for it at most 30 s; the request's validate refuses a vendor chosen during the issue read until its check passed; the refusal no longer advises a retry that cannot help; tests cover close waiting, the close cap, the wait and a wrong answer. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…#164) Review round 2: assertPassed, which the server calls inside its recorded action, refuses and starts a needed check on the next turn, so no worker, ledger or owner-token write runs inside the transaction; a check another vendor's replaces is cancelled; one deadline bounds a request's whole wait; the stub answers only the check's own prompt and schema; tests for a probe that throws and a replaced check. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ng (#164) Review round 3: tests show no check starts after close and one deadline bounds a wait across a vendor change; the deferred check reads the vendor again; the refusal ends its reason with a period and says a new agent is checked at once. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
# Conflicts: # docs/implementation/plan-library-alignment.md
…heck (#164) Review round 4: a reason ending with a command is no longer followed by text; a request whose check was replaced while the vendor changed and changed back waits for the current check; the provider checks again as it invokes; the deadline test has margins a busy machine keeps. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Review round 5. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… throws (#164) Review round 6: a request waiting on a replaced check moves to the current one without waiting for the old one to stop; the deferred check reads the vendor inside its try; the refusal says 'Retry shortly' and names the configured recheck window; wider timing margins; the vendor test checks the leftovers record is gone. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…164) Review round 7: the planning check against the real Claude service runs as a live probe in the Live probes workflow; a request no longer stays attached to the check after it settles; the still-checking refusal says the first check can take minutes; the shutdown waits are documented; wider timing margins. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…#164) Review round 8: docs and comments say the planning check at startup is the process's first planning job; waiting requests no longer trigger Node's listener-leak warning; the stopping refusal carries the unavailable prefix. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…#164) Review round 9: the wrapped provider rejects instead of throwing; docs and comments say the startup check recovers only when it can sign in, holds the lock until the process ends, and builds the image beside the runner's; assertPassed after close says planning is stopping; wider timing margins. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…164) Review round 10. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ocker (#164) Review round 11. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Review round 12: a check that has not settled within a real request's budget and a minute fails and is cancelled, so the recheck applies; a reason that holds until a restart gets no recheck promise; the invoke-time check is described as defence in depth; startup failure exits only in the CLI; test names and docs say what they show. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…st the cancel (#164) Review round 13: an aborted request or Store error while waiting for the check is refused as such, never recorded as a failed GitHub read; a missing token, read only at startup, gets the restart wording; tests show a timed-out check is cancelled and stays failed if its probe answers late; durations under a minute read correctly. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…obes (#164) Review round 14: a database with hard links gets the restart wording; the restart match ignores vendor output; close also waits, within its bound, for a timed-out check's probe; a Store failure while waiting reads as a failed check, not a stopped wait; shutdown cancels with the shutdown stop; the timeout rejects before it aborts; a test pins the probe's repository, head, prompt and schema; wider recheck margin; 'recheck window' in test names. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…mits stated (#164) Review round 15: cleanup that did not settle gets the restart wording; after close the refusal says planning is stopping; Codex is told to choose another agent; the Store-error test now fails the read after a wait has begun; the docs and comments state the shared-recovery, kept-pass and shutdown-deferral limits; test names say what they show. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ort first (#164) Review round 16: a run whose cleanup did not settle keeps the recheck promise (its storage is retried, and the next check reports a real restart need), so the wording no longer depends on whether Claude printed anything; a request already aborted starts no check; docs state when a passed check is kept, the check's own unsettled cleanup, the lock after an abandoned worker and the replayed refusal. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Review round 17. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…p text (#164) Review round 18: the check passes only on {"ok": true} written once; the cleanup-wording test uses cleanupFailed's real shape; test names say 'still checking' and 'recheck window'; the docs list the Store-error replay limit. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…164) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…#164) Review round 19 nits. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…n docs (#164) Review round 20: assertPassed's refusal says the chosen agent changed and is being checked; docs say a first job whose recovery fails leaves recovery to the next, that shutdown cancels the check after the server's grace, and how a check before the runner's setup fails on a task with runner commits (kept as a recheck, since the runner's setup sets the repository later). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Review round 21: refusals for restart-only reasons say codeboost must be restarted after the cause is fixed; the still-checking refusal no longer claims every slow check is the first; the doc says shutdown cancels a check once planning requests settle or after the grace; the replaced flag, always implied by a changed current check, is gone; a test uses AgentWorker's real text. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Review round 22: a restart-only refusal says only a restart helps and to fix the cause first if the reason names one; the unreachable still-running branch is gone; the check timeout's doc says no request waits that long. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…a wait (#164) Review round 23: the restart-wording tests build each reason with the code that gives it (abandonedWorkerText is exported for that); a request whose wait ran out goes on when Settings cleared the agent meanwhile; a Store error is labelled a stopped wait only when it is the request's own abort; the null-to-Claude path is the one tested for a vendor chosen during the issue read; the abort signal's listener limit, which Node 26 never warns about, is no longer raised; the suggestion-cancel row names the check that fails before the worker. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Review round 24: the request's signal is checked at the top of each loop with no await before the race, so the second check is gone; admit's doc covers no vendor chosen; the refusal prefix's doc names its refusals as examples; the recheck window's wording reads one way. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Review round 25: the tests that switch to Codex give it the probe answer PlanningAgent.probe gives (CODEX_PLANNING_REFUSED) and expect its refusal; a doc line is rewrapped. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Review round 26: the vendor-output, unsettled-cleanup, Codex and multi-line reasons are built by planningOutput, PLANNING_FEATURE.cleanupFailed, CODEX_PLANNING_REFUSED and assertClear's wording; web/planning.ts says when the check starts with no vendor chosen; a comment names the recheck window. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… fix/164-planning-probe # Conflicts: # docs/implementation/plan-library-alignment.md
…164) Review round 27: the sign-in refusals use planningOutput's own text and the vendor-change test CODEX_PLANNING_REFUSED; checked() takes only the vendors Settings accept; the suggestion-cancel row gives the runner-repository case as one example of a check failing before recovery; comments rewrapped; abandonedWorkerText moved below the module's re-export. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Review round 28: the invoke test first shows a passed check forwards the call as it is; the refusal for a changed vendor says codeboost checks it next, which holds even if the deferred check cannot start; 'records a planning request' in the docs; the browser spec's stand-in says no vendor is chosen there; one import from runner/planning.ts. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Review round 29: one fake-clock test through productionPlanning, with nothing overridden, shows a request waits 30 s, a check fails after the planning budget and a minute, and close waits 20 s; it fails if any of them changes. The refusal comment notes the Store's 4,000-character record. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Review round 30: #check and assertPassed test for close before the covering check, so a request, its start and the wrapped invoke are refused as stopping once planning closed; a test shows it. The defaults test's close step names what close waits for. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Review round 31: admit and assertPassed test for close before they read the vendor; the after-close test covers no agent chosen too. The PR body records the live probe run on the head with #163's budget. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Review round 32: #verdict tests for close before a pass, so a check that passes after close admits nothing; tests cover a check deferred before close, start after close, and a request waiting at close whose check then passes. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…164) Review round 33: a request waiting at close is refused as stopping at once, even when its check's probe never stops; start's doc says it throws after close; web/planning.ts says the check gates a request when a vendor is chosen; a test covers validate meeting a check still running; a comment notes what a worker that never ends a job leaves for close. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…164) Review round 34. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…164) Review round 35. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
There was a problem hiding this comment.
🟢 Approval recommended
No blocking issues remain in the reviewed admission lifecycle, production wiring, or regression coverage; deferred server and UI work is explicitly tracked in #198.
No open issues 🎉
What changed in this PR
Adds a startup planning-agent check and gates planning requests on its result, addressing #164. Review-view status and server lifecycle follow-ups remain in #198.
Changes:
- Probes the configured agent through planning’s worker and read-only profile.
- Adds bounded admission waits, retries, cancellation, and shutdown handling.
- Adds regression coverage and a live Claude probe.
| File | Description |
|---|---|
web/planning.ts |
Starts the check and guards planning requests. |
test/planning-worker.test.ts |
Tests probe inputs, credentials, and exact answers. |
test/planning-production.test.ts |
Tests admission, retries, races, and shutdown. |
test/fixtures/question-worker-stub.ts |
Supplies controlled probe answers. |
test/browser/plans-production.spec.ts |
Adds the probe method to the stand-in. |
test/agent-planning.test.ts |
Tests real authentication and cleanup. |
scripts/live-probes.ts |
Registers the planning live probe. |
runner/question-leftovers.ts |
Clarifies recovery timing in comments. |
runner/question-agent.ts |
Extracts shared abandoned-worker wording. |
runner/planning.ts |
Implements the probe and admission lifecycle. |
docs/implementation/runner-lifecycle.md |
Documents recovery and shutdown timing. |
docs/implementation/planning-acceptance.md |
Documents check behavior and limitations. |
docs/implementation/plan-library-alignment.md |
Records #164 implementation and follow-up. |
docs/implementation/ci.md |
Documents the fifth live probe. |
docs/implementation/agent-isolation.md |
Adds planning-check validation evidence. |
.github/workflows/live-probes.yml |
Includes the planning live-probe test file. |
Review effort: Balanced
mchwang
added a commit
that referenced
this pull request
Oct 21, 2026
… missing-record failures (#189) Review round 53: the comment counted four probes, before #193 added agent-planning's; with its 11-minute probe and 2-minute teardown the limits total 90 minutes plus setup, so the job limit rises to 100 (pinned in vitest-config); the refused-network and refused-proxy tests say so when their shim recorded nothing. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
mchwang
added a commit
that referenced
this pull request
Oct 21, 2026
…#189) (#191) * Remove test objects made without a runner label however the test ends (#189) `withoutRunnerLabel` now labels each object it strips with a per-call test-run ID and removes everything with that ID when the test finishes, passed, failed or timed out. The owner sweep also removes containers that hold a swept volume. In agent-network, the stand-in proxy and the blocker are removed in `finally`. A pair of tests checks that a keeper left by a test that stopped early is gone. Closes #189 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Give the test-run sweep its own budget and an afterAll pass (#189) Review round 1: the onTestFinished removal gets 3 minutes, not the 10 s hook default; afterAll sweeps every test-run ID again, for a create that lands after a timed-out test's removal; the regression pair records the storage only after its checks and takes it out of afterAll's list first; one ps call finds every holder. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Remove containers that hold a swept network; record the stand-in first (#189) Review round 2: a network made without the runner label stayed when a runner-labelled proxy held it, so the sweep now removes the containers on a swept network too (looked up by name, which also finds a created container). The hook budget covers the sweep's ten calls, and the impostor's name is recorded before the stand-in exists. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Remove only exact holders of a swept network; test the holder sweep (#189) Review round 3: Docker's network filter also matches the start of another network's ID, so holders are checked by network name; a real-Docker test covers created and running holders and bystanders; hook budgets cover the sweep; label-less creates in agent-container and agent-supervisor are tracked before they are made. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Close the remaining label-less leaks the review found (#189) Review round 4: the rogue volume is made inside its try and removed after its container; the foreign network is listed again in finally; the holder test records each object before it exists; the sweep reads a partial inspect; the hook budgets cover eleven calls. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Record label-less objects first in agent-network; remove networks by ID (#189) Review round 5: the network and proxy shims record a name before they make it, and the tests remove it in finally; the sweep removes networks by full ID, reads output only from a call that exited by itself, and the holder test runs its inspect with a missing ID, as the sweep meets it; the supervisor's finally removals never throw. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Test removal by full network ID; say what the runner sweep removes (#189) Review round 6. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Remove swept containers by full ID; track the blocker before it is made (#189) Review round 7: Docker resolves a container name before an ID prefix, so the sweep lists containers and holders with --no-trunc, and the holder test keeps a container named like a holder's short ID; the blocker is tracked before its run; comment wording. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Remove the foreign-runner storage in its test; clean every storage in afterAll (#189) Review round 8: the storage made under another runner is removed in finally; afterAll removes every storage even when one fails; the killed-create network is listed by full ID; afterAll budgets have a minute over the sweep. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Read network names as JSON; name the seccomp probe; test --no-trunc (#189) Review round 9: a network name may hold spaces, so the sweep reads names and holders' networks as JSON; the label-less seccomp probe is named and tracked before it runs; the holder test keeps a container named like a swept container's short ID; budget comments state the real margin. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Never throw on output the sweep cannot parse; foreign creates inside try (#189) Review round 10: a line that is not JSON is reported and skipped; the foreign-runner network and storage are made inside their try and removed by allocation; the recovery-handle test removes by full ID through the sweep; doc wording. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Sweep the failed-allocation test by allocation; guard unreadable lines (#189) Review round 11: the foreign-runner failed-allocation test removes its objects through the sweep, by full ID and never throwing; network lines that are not a network are skipped, with a unit test; in-test sweeps have budgets for their worst case and their own warning prefix. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Budget the in-test sweeps; exact wording (#189) Review round 12. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Sweep both runners of the recovery test; keep the first failure (#189) Review round 13: the recovery test sweeps its two random runner owners after its undo steps, so what a step could not remove or never recorded goes too; the foreign-storage test's cleanup no longer replaces the body's failure; doc wording. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Check the traps are live in the holder test; report every cleanup failure (#189) Review round 14: the holder test first asserts Docker's prefix match and its name-before-prefix resolution, so its bystander checks test something; afterAll collects every cleanup step's failure; comment states which ending the it.fails pair tests. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Keep the first failure in the replaced-keeper test; exact sweep wording (#189) Review round 15 nits. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Check the partial-inspect answer; sweep the replaced keeper; budget margin (#189) Review round 16. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Budget the replaced-keeper sweep; say what the holder test's budget covers (#189) Review round 17. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * One per-file sweep in afterAll; real docker for the sweep; timed-out leftovers (#189) Review round 18: objects made without the runner label also carry this file's run ID, so afterAll removes them in one sweep within its budget; the sweep runs the real docker found at load, never a shim left on PATH; agent-network's label-less objects and the rogue volume are also removed in afterAll should their test time out; the volume test checks the wrapper's labels; cleanup warnings are logged; the fake's note names its answers. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Track the race and legacy volumes; cleanup loops inside try with real docker (#189) Review round 19. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Sweep other runners' objects when the test finishes, timeouts included (#189) Review round 20: async tests that make objects under another runner's owner or their own random one register their sweep with onTestFinished, which runs after a timeout too, so test budgets return to their old values; the shims and afterAll use the real docker with a timeout; comment wording. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Real docker in every shim; foreign-name containers swept on finish; killed-call test (#189) Review round 21: every shim calls the real docker found at load and refuses when there is none; agent-supervisor's two foreign-name containers are removed by name when their test finishes, timeouts included; a unit test shows a killed call's output is not read; one more race volume tracked; comments name every removal afterAll makes and what the finish-time sweep cannot cover. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Budget the supervisor finish hooks; name a signal kill; exact comments (#189) Review round 22. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Register late-landing agent-network objects by name; exact comments (#189) Review round 23: the three agent-network tests whose foreign object may land after their own removal register it by name for afterAll, since the record goes with the shim; the late-network test says why its network is swept; comment wording. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Track the holder test's objects for afterAll, removed by exact name (#189) Review round 24: the holder test's unlabelled objects and the swapped keeper are tracked for afterAll; afterAll removes tracked names only when an object of exactly that name exists, by its full ID, since some are named like ID prefixes; withoutRunnerLabel uses sweepWhenFinished; the finish-time sweep states what lands too late for it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Report removeByName failures; records keep every name; shim scoped (#189) Review round 25: removeByName reads JSON (names with spaces), reports a failed call other than no such object; agent-network's shims append one name per line and every name is removed; the foreign-label shim rewrites only its own allocation's create; the holder test checks the prefix-named network goes; the killed-call unit test first checks its fake and asserts the warning; comments name the networks set. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Test removeByName's exact-name guard and reports; records outside shims (#189) Review round 26: the holder test checks removeByName leaves an object whose ID starts with a now-missing tracked name; a unit test covers its no-such answer, an ID-prefix match, a refused removal and a killed inspect; only a container's name loses its leading slash; agent-network keeps its records outside the shims so afterAll reads names a late client adds; sweepWhenFinished's doc covers withoutRunnerLabel. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * removeByName treats only Docker's own missing answers as missing; tests every kind (#189) Review round 27: a failure before the daemon (an unknown context) is reported; the fake covers containers, networks and volumes, found, missing, under another name and refused; the unlabelled-create shim acts only on its own container; agent-supervisor's finish-time removals use removeByName; doc and import fixes. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Real-Docker guard check on a free network; real error text in the fake; budgets (#189) Review round 28: the holder test's removeByName check uses a network with no endpoints, so a broken guard would remove it; the fake's stderr is Docker 27.4's own; the supervisor's finish-time removals get 90 s for two 30 s calls; agent-network makes its records directory on first use; comment fixes. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Holder test checks removeByName's exact removal and silence on a gone name (#189) Review round 29: one free object of each kind is removed by exact name and asserted gone, and a second removal of the gone names reports nothing; the ID-prefix check's comment says what the names are; the test's finally removes by exact name; the unit file's header names what the daemon test covers. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * The stand-in shim acts only on an exec in a proxy (#189) Review round 30: any other exec passes through, so the shim can never run a stand-in with an empty name, which Docker would give a random, unrecorded one. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Doc: a test may sweep its own runner owner with removeLabelledObjects (#189) Review round 31. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Say what the killed-call fake leaves out (#189) Review round 32. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Sweeps keep the Docker daemon set when they loaded (#189) Review round 33: a test that times out may leave DOCKER_HOST changed (agent-supervisor points it at a missing socket until its handle settles); every call in docker-sweep.ts now restores DOCKER_HOST and DOCKER_CONTEXT as they were at load, with a unit test that fails without it; the context fake says its path is shortened. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Comment and naming fixes (#189) Review round 34: what the loaded Docker address means for each call, which stdout a failed call leaves unread, who adds the test-run label; the records directory variable is lower case, as it changes. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * The racer shim acts only on its own allocation's create (#189) Review round 35: should a race test time out with the shim on PATH, other tests' creates pass through; withoutRunnerLabel's doc says what a wrapper left on PATH does to a later test. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * removeByName's reports name the stage and the command run (#189) Review round 36: removeByName takes a when prefix, as removeLabelledObjects does, and names the command it ran (docker rm, docker container inspect). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Sweep reports name their stage; a gone holder is not a failure; loaded daemon tested (#189) Review round 37: removeLabelledObjects names the stage of each failed call and says nothing of a holder gone since the listing (Docker 27.4's No such container for it), still reporting any other failure; a test loads a fresh copy of the module with DOCKER_HOST and DOCKER_CONTEXT set and shows the sweep keeps them; unit tests cover both holder inspect answers. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Track the never-started unlabelled agent container by name; comment fixes (#189) Review round 38: Docker 27.4 removes a network a created, never started container still names, so that test's container is tracked for afterAll; the docs say which holder reads stay quiet, that the sweep reads stderr for them, and that a wrapper can stay on PATH for the rest of the file; the loaded-daemon fake says what it answers. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Label agent-network's unnamed helper containers for afterAll; track the rogue endpoint (#189) Review round 39: a helper run killed between create and start is never removed by --rm, and is no holder once its vendor network is gone, so the curl, DNS and concurrent-network helpers carry this file's run ID and afterAll sweeps it (13-minute hook for two sweeps); agent-container's rogue endpoint is tracked by name; the holder fake says the removals' stdout is not read. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Track the peer, stand-in proxy and foreign keeper by name; holder test asserts the quiet sweep on the daemon (#189) Review round 40: three label-less or foreign containers made with a 60 s client could land after their test's finally, and once their network is gone none is a holder, so each is tracked by name for afterAll; the holder test checks the daemon's own answer for a gone container and that the sweep reports no failure for it; the holder fake says what it leaves out; the unit file unstubs env after each test; agent-network removes its records before the sweeps. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Track the rogue-volume container before its create (#189) Review round 41: should the create land after its client is killed, afterAll removes the container by name before the label-less volume it mounts. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Read the holder test's sweep warnings before restoring the spy (#189) Review round 42: mockRestore clears the recorded calls, so the check that the sweep says nothing of a gone holder read an empty list and could not fail; the calls are copied first, and the test also checks the spy saw the sweep's own lines. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Storage release after a pass fails the test again; mark the invented fake answer (#189) Review round 43: in the two tests whose cleanup now warns instead of throwing, a release that fails after the test's own checks passed fails the test, as on main, while a failure above still stays the one reported; the second holder fake case says its answer is invented. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * removeByName: tested on unreadable output, quiet on a network gone before its removal (#189) Review round 44: the fake gives an inspect answer that is not JSON (reported, nothing removed, never thrown) and a network gone between inspect and removal, which Docker 27.4 answers 'network <ID> not found' and removeByName now treats as gone; the keeper comment says afterAll also removes it by name; a comment line is rewrapped. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Restore two failures main's cleanups had; report an inspect answer with no ID and name (#189) Review round 45: the foreign-name supervisor test fails again when, after its checks pass, the container's removal fails; the unsettled-allocation test checks after its sweep that nothing of the allocation is left, as main's listing did; removeByName reports a parsed answer without an ID and a name, and its doc lists what it stays quiet about; comment blocks rewrapped. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Comment fixes: what main's check was; what removeByName reads (#189) Review round 46. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Test that the real docker is found once at load and that shims refuse without one (#189) Review round 47: a unit test loads the fixture with docker on PATH, then puts another first, and checks REAL_DOCKER keeps the first; loaded with no docker, requireRealDocker refuses. A docker found through a relative PATH entry is resolved to an absolute path. The never-started agent container's comment gives the real reason it is tracked. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Test the load-time docker as the default and a relative PATH entry; client limit; messages (#189) Review round 48: the finds-docker test loads the fixture through a relative PATH entry, then puts another docker first and calls both removals with no docker given, checking only the docker found at load ran; the network listing in the killed-create test gets its 60 s client limit back; the supervisor's removal message handles a docker that could not start; the fake says what it answers. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Every Docker client call the branch adds or moves in a test has a time limit (#189) Review round 49: synchronous calls cannot be stopped by a Vitest timer, so each one the branch added, moved or rewrote (removals, inspects, the foreign creates and the ack-script run) takes CLIENT_LIMIT, 60 s with SIGKILL, as main's docker() helper did; the supervisor's removal message names a kill by signal. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Track the ack-script container by name; time limits on the moved and attached calls (#189) Review round 50: agent-supervisor's label-less ack-script container is tracked by name before its create and removed by afterAll; runner-workspace's docker helper, which now runs calls this branch moved out of a try, takes CLIENT_LIMIT; the seccomp probe and agent-network's attached helper runs are killed with SIGKILL at their limit, as an attached run forwards SIGTERM; the killed-create test's listing reports its own failure. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Name a failed foreign-keeper removal after a pass (#189) Review round 51: as on main, a docker rm of the foreign keeper that fails after the test's checks pass fails the test with Docker's own error, before the storage release's. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Workflow limits for the longer teardown; full test of the loaded daemon; keeper warning (#189) Review round 52: the Live probes job limit rises to 90 minutes, as the teardown hooks it covers now allow 24 (vitest-config pins it); the Agent isolation comment says its 45 minutes cover ordinary runs; the loaded-daemon test answers one network with a holder and one named container, so every kind of call is checked; a failed foreign-keeper removal is warned of when the body failed. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Live probes limit counts agent-planning's probe and teardown; clearer missing-record failures (#189) Review round 53: the comment counted four probes, before #193 added agent-planning's; with its 11-minute probe and 2-minute teardown the limits total 90 minutes plus setup, so the job limit rises to 100 (pinned in vitest-config); the refused-network and refused-proxy tests say so when their shim recorded nothing. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Sweep quiet on a network gone since its listing; full loaded-daemon test; hook budget (#189) Review round 54: the sweep treats Docker 27.4's 'network <ID> not found' on its network removal as gone, as removeByName does, with a test that still reports any other failure; the loaded-daemon test now lists a container, a network and a volume, each with a holder, and checks every command line; the after-sweep check's budget covers its three listings; the Agent isolation comment names the shared sweep. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Holder test checks its gone-holder shim fired; header and workflow comments (#189) Review round 55: the holder test's shim leaves a marker, and the test checks it before trusting the quiet sweep; the unit file's header says the sweep reads a network removal's stderr too; the Agent isolation comment names everything its 45 minutes cover. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Rewrap two comment blocks (#189) Review round 56. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * The loaded-daemon fake gives the volume its own holder (#189) Review round 57: the stateless fake listed the network's holder again for the volume, after the sweep had removed it, which Docker would not; the volume now has its own holder, and the test checks its removal. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Track the supervisor's foreign-name containers for afterAll; stage in unreadable-line reports (#189) Review round 58: the two foreign-name containers are tracked by name for afterAll too, should a create land after their finish-time removal; the sweep's report of a line that is not JSON names its stage. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Failure messages name the docker command; record checks first (#189) Review round 59: the killed-create listing and the supervisor's after-pass removal name the command that failed, as main's helper did; the refused-network and refused-proxy tests check their shim's record before the error, so a shim that no longer matches is reported as itself. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Exact command in the listing's failure; holder test checks every sweep line (#189) Review round 60: the killed-create listing's failure message is built from the arguments it ran; the holder test requires the gone-holder inspect's exit 1, and accepts only the sweep's own removal lines, including the holders line, so any failure or unreadable answer fails it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Report sweep lines of the wrong shape; exact commands in removeByName's reports (#189) Review round 61: the sweep reports a network listing line that is not a network and a holder line that is not an ID and networks, as removeByName reports an answer that is not an ID and a name, with tests; removeByName's failure reports name the exact command and ID. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Gone checks need Docker's missing answer; sweep checks listed IDs and names; full commands (#189) Review round 62: objectExists counts only Docker 27.4's missing answer as gone and throws on any other failure, so a killed inspect no longer passes a removal check (the export, follow-up and holder tests; runner-workspace's volume check wants 'no such volume'); the sweep names whole commands in its reports, reports and skips listed lines that are not a container ID or a volume name, and reports a holder line with no space once; agent-network's records removal cannot stop the sweeps; the keeper test's finally sweep says finally. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * agent-network's survival checks use objectExists (#189) Review round 63: a killed or failed inspect in the finally no longer reads as the foreign object being removed; Docker's missing answer counts as gone, and any other failure is rethrown, naming the command, after the body's own checks. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Every ID passed on as an argument has a checked shape (#189) Review round 64: network IDs from the listing, holder IDs and removeByName's inspected IDs must be full IDs, or are reported and skipped, so none reads as an option; an inspect line with no space is reported instead of parsed twice; fake cases for each. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * removeByName refuses a name that could read as an option; fake notes (#189) Review round 65: removeByName checks the name it passes on (a volume's against Docker's allowed shape, others for a leading dash) and reports a refusal, with fake cases; the volume-name shape needs two characters, as Docker 27.4 does; agent-network's two rewritten shims say which answers are Docker's and which are invented. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Test objectExists; gone cases reach their inspect; per-record errors in afterAll (#189) Review round 66: a unit test loads the fixture with a stand-in docker and checks objectExists is true, false only on each kind's missing answer, and throws naming the command on a killed call or an unknown context, at the loaded daemon; removeByName's gone cases assert their inspect ran; the holder fake's missing answer names the full ID; agent-network's afterAll reports a record it cannot read and goes on. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Gone checks in the touched tests use objectExists; fake and comment fixes (#189) Review round 67: the keeper-replaced, foreign-name and unlabelled-agent checks use objectExists, and the holder test calls it directly; the PR says checks in untouched tests keep main's form; the objectExists fake prints the unknown context's quotes as Docker does and says its path is shortened; the supervisor's tracked containers are described as lacking the runner label. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Every gone check in a changed test uses objectExists (#189) Review round 68: the by-ID removal and start tests, the rogue-endpoint test, the recovery test's exists helper and agent-network's late-landing check use objectExists; removeByName's refusal says the name could read as an option; the objectExists fake marks its stdout as invented. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * The proxy-start test's gone check uses objectExists; assert the raced removal ran (#189) Review round 69: the last gone check in a changed test uses objectExists; the gone-network sweep test checks its removal ran before trusting the silence; the stand-in shim's note says where stderr goes. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Shape checks before the gone checks; objectExists refuses an option-like ref (#189) Review round 70: the late-landing network and proxy-start tests check the value their shim recorded before inspecting it, as the record tests do, so a shim that recorded something else is reported as itself; objectExists refuses an empty or dash-led ref, naming it; the proxy-start test's removal takes CLIENT_LIMIT. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Trim the listing's stderr in its failure message (#189) Review round 71. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Pin every docker call's time limit; volume refusal reason; guarded record reads (#189) Review round 72: a unit test loads the fixture with spawnSync observed and checks every removal call is given 30 s and SIGKILL, and objectExists 60 s and SIGKILL, so the budgets built on them cannot lose their limit unseen; removeByName says a refused volume name is not one Docker allows; agent-network's finally blocks report a record they cannot read instead of throwing over the body's failure; the PR says what every changed gone check requires. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Time-limit test runs all thirteen calls; holder search and no-space lines tested (#189) Review round 73: the time-limit test answers so both removals make every call (13), and checks each; a holder search line that is not a container ID is reported and skipped, with a test; removeByName reports an inspect line with no space as no ID and name; the holder read's no-space line is tested. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Test the fixture's remaining guards; exact reasons in refusals and reports (#189) Review round 74: tests cover a docker that cannot start (both functions report and never throw), Docker's missing text with another exit status in objectExists, an empty name in removeByName, and a holder line that is not JSON, reported once; refusals of an empty value say so; an inspected ID that is not a full ID is reported as that; the two fakes that answer removals say their stdout is not read. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
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.
Closes #164
What changes
PlanningAgent.probe(vendor, signal)(runner/planning.ts) is the planning check. It runs one planning invocation in planning's own worker and in the same lane D profile a real request gets: read, list and search only, vendor-only egress, and a mounted schema. It runs on the review's code at the snapshot's head, with a real request's budget, because a process's first job may build the agent image. It asks for the schema's own answer,{"ok": true}, and passes only on exactly that (isPlanningProbeAnswer). So it covers:PlanningCheck(runner/planning.ts) controls admission:start()runs the check for the vendor chosen in Settings when the server starts. It runs nothing when no vendor is chosen.admit(signal)waits at most 30 s in all for the check that covers the vendor chosen now. All waiting requests share that check, and a request that stops waiting leaves it running. A check still running after the wait gets a refusal: "Planning is unavailable: codeboost is still checking the planning agent."assertPassed()runs again inside the server's recorded action, through the description'svalidate(). So a vendor chosen during the issue read never runs unchecked. Because that action runs inside the Store's transaction,assertPassedstarts no check there. It refuses, and the needed check starts on the next turn.provider.invokechecks once more when it is invoked. This is defence in depth: E3 invokes it in a microtask right after the recorded action, where Settings cannot change, but a later caller might. It rejects rather than throws, and E3 then settles the request as failed with the refusal.CLAUDE_CODE_OAUTH_TOKEN, because codeboost's environment is fixed while it runs. Codex is told to choose another agent, which the next planning request then checks. After close, the refusal says planning is stopping. It matches only codeboost's own text, never the vendor's output.close()cancels any running check and waits up to 20 s, including for a timed-out check's probe still in the worker, before the worker closes.productionPlanning(web/planning.ts) starts the check at setup.describeawaitsadmitbefore the issue is read or a planning request is recorded. The server already records aGuardRefusalas a 409 and starts no agent, soweb/server.tsis unchanged.Interpretation
web/server.tsandweb/public/*, which other work owns right now. That is follow-up Show the planning check's state in the review view #198, which also covers theweb/server.tslimits listed below.web/server.ts(Show the planning check's state in the review view #198).web/server.ts(Show the planning check's state in the review view #198).startServeris left with the check running, becausestartServerdoes not close planning when a later startup step fails. Fixing that needsweb/server.ts, so it is part of Show the planning check's state in the review view #198.Tests
test/planning-production.test.ts, "the planning check (Probe the planning profile at runner startup #164)", 39 tests. Each uses a stand-in agent whose probe passes, fails, throws or never settles. They cover:productionPlanning, with nothing overridden: a 30 s wait, a check failing after the planning budget and a minute, and a 20 s close bound;validate, checked on the next turn, refused as still checking while that check runs, then admitted;test/planning-worker.test.ts: the check runs as a planning job in planning's worker, and the stub answers only the check's prompt and schema. It fails without a sign-in, and with Codex before any lock or Docker work. A wrong answer fails it, and only the exact answer passes.test/agent-planning.test.ts(real Docker): the check against Anthropic with an invalid token fails with the vendor's 401 and leaves nothing behind.test/agent-planning.test.ts, "passes the planning check with a real sign-in (live Claude)", added toscripts/live-probes.tsand the Live probes workflow): the check's own prompt and schema, answered by the real Claude service.--max-turns 50and input budget: run 39550815257 passed all 5 live Claude probes. Later commits change onlyPlanningCheck, its tests and docs, notPlanningAgent.probeor the answer check.testandreal-dockerpassed on the same head.test/browser/plans-production.spec.ts: the lane D stand-in gets aprobeto fit the type. The spec chooses no vendor, so the check checks nothing there; it passes locally.Review record
All rounds were independent-agent reviews: a fresh general-purpose subagent with no author context, reading the full
origin/main...HEADdiff each time, never only the fixes. Each round's fixes are one commit whose message starts "Review round N" and lists what changed.UNTIL_RESTARTon codeboost's own text only; exact JSON answer; close waits for probes; follow-up #198 filed for server-side limitsDeclined, with reasons (each repeated to later rounds so they were not re-reported):
planningOutput's request wording: its sign-in and usage advice applies, and the one-turn probe cannot hit the turn limit.checked()does not close planning after each test: close waits up to 20 s for never-settling probes, and the check's timers are unref'd.Merges of main at e46cb68 (#196, #163's planning budget) resolved one table-row conflict in
plan-library-alignment.mdkeeping both rows; the review rounds after it read the merged diff.Clean independent-agent pass authorizing the Copilot request: round 36, base e46cb68, head 6888587.
Copilot review on head 6888587 (review 5596165142): approval recommended, no open issues, no inline comments.
Review-lesson audit
No new rule is needed, so no
AGENTS.mdorCLAUDE.mdchange is in this branch.🤖 Generated with Claude Code