Repository navigation
Remove test objects made without a runner label however the test ends (#189) - #191
Merged
Merged
Conversation
…#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>
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>
#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>
…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>
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>
…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>
…de (#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>
… 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>
…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>
…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>
…leanup # Conflicts: # test/runner-workspace.test.ts
#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>
Review round 12. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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>
…lure (#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>
…ng (#189) Review round 15 nits. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…argin (#189) Review round 16. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…overs (#189) Review round 17. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…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>
… docker (#189) Review round 19. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
#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>
…illed-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>
#189) Review round 22. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…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>
…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>
) 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>
…ms (#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>
…ts 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>
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>
…on; 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>
… 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>
…est; 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>
…mments (#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>
Review round 56. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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>
… 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>
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>
…p 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>
…'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>
… 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>
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>
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>
…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>
…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>
…ixes (#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>
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>
… 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>
…ike 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>
Review round 71. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…cord 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>
…ines 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>
…ports (#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>
There was a problem hiding this comment.
🟢 Approval recommended
No blocking issues were identified in the cleanup logic, regression coverage, or workflow changes.
No open issues 🎉
What changed in this PR
Addresses #189 by strengthening Docker test cleanup for objects missing this run’s runner label, including failure and timeout paths.
Changes:
- Adds shared label-based and exact-name cleanup with bounded Docker calls.
- Tracks test-created resources and adds cleanup regressions.
- Adjusts teardown budgets and the live-probe job timeout.
| File | Description |
|---|---|
| test/vitest-config.test.ts | Pins the updated live-probe timeout. |
| test/runner-workspace.test.ts | Adds finish-time sweeps and stricter removal checks. |
| test/fixtures/docker-sweep.ts | Adds holder cleanup, exact-name removal, and bounded calls. |
| test/docker-sweep.test.ts | Tests cleanup safeguards and Docker failure handling. |
| test/agent-supervisor.test.ts | Tracks foreign containers and bounds creation calls. |
| test/agent-network.test.ts | Records foreign objects and labels helper containers for cleanup. |
| test/agent-gate.test.ts | Increases the sweep’s teardown budget. |
| test/agent-container.test.ts | Adds resource tracking, finish-time sweeps, and leak regressions. |
| .github/workflows/live-probes.yml | Raises the job timeout to 100 minutes. |
| .github/workflows/agent-isolation.yml | Clarifies existing timeout coverage in comments. |
Review effort: Balanced
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 #189
What changes
Docker test objects without this run's runner label (
io.codeboost.runner), or with another runner's, are now removed however their test ends: pass, fail, timeout, a create that lands late, or a client killed between create and start.test/fixtures/docker-sweep.tsremoveLabelledObjects(label, value, when, docker = REAL_DOCKER)removes the containers, networks and volumes withlabel=value, and first every container that holds one of those networks or volumes, labelled or not.removeRunnerObjectsmade six calls (3 minutes), with no holder pass, ran thedockerand environment on PATH at the time of the call, and named only a call's first two arguments; the hook budgets below rise for that.removeRunnerObjects(owner, when?)is that sweep for one runner owner. The five Docker test files that called it on main still call it inafterAll; agent-planning and agent-question keep their own sweeps.removeByName(kind, name, when, docker?)removes only an object of exactly that name, by its full ID, since Docker reads a missing name as an ID prefix. It stays quiet on Docker 27.4's own missing answers, including a network gone between its inspect and its removal. It reports any other failure, naming the exact command, and any answer it cannot read, including an ID that is not a full ID. It refuses a name that could read as an option. Every value either function passes on as an argument has a checked shape, so none can read as an option.REAL_DOCKERis thedockerfound when the module loads, resolved to an absolute path. It is the default for both removals, so a shim left onPATHby a timed-out test is not called.requireRealDocker()refuses a shim when none was found.DOCKER_HOSTandDOCKER_CONTEXTas they were at load. agent-supervisor pointsDOCKER_HOSTat a missing socket until its handle settles.objectExists(kind, ref)is for the tests' checks that an object is gone. It refuses a ref that could read as an option. Only Docker's own missing answer counts as gone, and any other failure (a killed call, a daemon that cannot be reached) throws, naming the command. Every gone check in a test this branch changes now requires Docker's own missing answer (objectExists, a helper's error matched against that answer, or an empty listing by allocation); on main some counted any failed inspect as gone. Checks in tests it does not change keep main's form.CLIENT_LIMIT(60 s, then SIGKILL) is for the tests' synchronous Docker calls, which no Vitest timer can stop.TEST_RUN_LABELandTEST_FILE_LABELare labels only tests write.test/agent-container.test.tswithoutRunnerLabelrefuses a prefix that is not a create, since it puts its labels aftercreate. It labels what it makes with a per-call test-run ID and this file's run ID.sweepWhenFinishedremoves the test-run ID inonTestFinished(6-minute budget), andafterAllsweeps the file's run ID.sweepWhenFinishedat their start, and synchronous ones sweep in theirfinally.afterAllremoves the tracked names withremoveByName, containers first. It then sweeps by file label and by owner, and reports every step's failure at the end. Its limit rises from 2 to 13 minutes, for the two sweeps.requireRealDocker()rather than looking it up at test time.try, so thefinallyremoves what they made: the rogue volume (tracked by name before it is made), its container and the foreign keeper.CLIENT_LIMIT. The rogue-volume test'sfinallyremoves the container before the volume it mounts.finally, so its budget rises from 60 s to 7 minutes.finally, and a release failure after a pass fails the test there. On main it was pushed totaskFilesystems, so such a failure failedafterAll.CLIENT_LIMIT(SIGKILL; main's helper used SIGTERM), and a failed listing names its command. Itsfinallyremoval is replaced by an allocation sweep inonTestFinished.onTestFinishedhook. Main'sfinallylisted only containers and volumes.removeByName. Its budget is 8 minutes: the sweep's worst case and ordinary calls.it.failstest and its follow-up check that a keeper left by a test that stopped early is gone.test/agent-network.test.tsafterAllreads it again.execinto acodeboost-proxy-*container. On main it acted on everyexec.afterAll's runner sweep. The proxy's removal takesCLIENT_LIMIT(none on main).finallyand usesobjectExists: only Docker's missing answer counts as gone, and any other failure is reported, naming the command, after the body's own checks.run --rmcontainers carry this file's run ID, andafterAllsweeps it. A helper whose client is killed between create and start is never removed by--rm, and is no holder once its network is gone.try. TheafterAllhook's limit rises from 4 to 13 minutes.curl, DNS, peer) are killed with SIGKILL at their limit: an attacheddocker runpasses SIGTERM on to the container rather than exiting. A killed helper's container then runs on to its own short timer (15 s at most).test/agent-supervisor.test.ts: the two foreign-name creates and the ack-script run are made inside theirtry, withCLIENT_LIMIT. The two foreign-name containers are removed by name when their test finishes (90 s hooks, for two calls of 30 s) and tracked by name forafterAll, as is the ack-script container. TheafterAlllimit rises from 3 to 7 minutes.test/agent-gate.test.ts,test/runner-workspace.test.ts:afterAllhooks, which already ranremoveRunnerObjects, get budgets for the sweep's worst case: 4 to 6 minutes and 4 to 7 minutes.onTestFinished(6 minutes each), not in afinally, so the sweeps run on a timeout too.dockerhelper takesCLIENT_LIMIT.passedflag, or a check registered to run after the sweep. A failure in the test body stays the one reported.test/docker-sweep.test.ts(15 tests, fakedockers that say beside them what they answer as Docker 27.4, and what they leave out or invent):objectExists: gone only on Docker's missing answer, a throw naming the command on any other failure, at the loaded daemon;dockeras the default.test/vitest-config.test.tspins the new value.Accepted limits
finally, which always runs.withoutRunnerLabel's wrapper, if a timed-out test leaves it onPATH, also unlabels a later test's matching create, which then fails. What it makes carries the file's run ID, andafterAllremoves it.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.PATHREAL_DOCKER,removeByNamefinally, records that a late client overwrites, never-started containers that are no holder once their network is gone,DOCKER_HOSTleft changedmockRestorecleared it) and minors: cleanups that lost main's failure, fakes not matching Docker 27.4, quiet answers that hid real failurespassedflags and after-sweep checks, fakes copied from the daemon, exact missing answers, workflow limits (Live probes 75→100 min)CLIENT_LIMIT,objectExists, shape checks on every value passed on, a test for each guard and each time limitDeclined, with reasons (each repeated to later rounds so they were not re-reported):
withoutRunnerLabel's guard refusing a prefix that is not a create has no test: it is a precondition of a test-only helper inside a Docker suite file, and every caller passes a create.docker <args>even when a stand-in path is given: that argument is only for stand-ins that pass the call through or imitate it, so the command named is the one that ran.Merge of main at 79deeec (#193, #196): no conflicts; the review rounds after it read the merged diff.
Clean independent-agent pass authorizing the Copilot request: round 75, base 79deeec, head f6bf790.
Validated head f6bf790 (base 79deeec): CI
testandreal-dockerpassed on it.Copilot review on head f6bf790 (review 5602245061): approval recommended, no open issues, no inline comments.
Review-lesson audit
mockRestore, a guard with no test, a gone check that passed on a killed call): covered by "The Design system and plan format #1 Rule of E2E Tests" and "give it a test that fails if the claim is false".No new rule is needed, so no
AGENTS.mdorCLAUDE.mdchange is in this branch.🤖 Generated with Claude Code