You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Commit eafe2be
Browse filesBrowse the repository at this point in the historyBrowse files
authored
Probe task storage path identity at runner startup (#162) (#180)
* Probe checkout path identity at runner startup
Runner startup now probes case sensitivity and Unicode normalization on
the filesystem that holds the task checkouts and refuses to start when
the configured pathIdentity disagrees. The demo uses the same shared
probe.
* Probe task storage, not the host folder, for path identity (#162)
The agent edits, and the runner inspects, a tmpfs volume in the Docker
daemon's Linux kernel. The host attempts folder holds only the staging
clone. On a macOS host the earlier probe reported APFS's folding and
refused the setting that is correct for task storage.
Startup now runs the probe with the built image, in a hardened --rm
container on an anonymous volume made with task storage's own tmpfs
options, and refuses on a mismatch. The host probe stays for the demo.
The staging clone's own case collapse is #179.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Narrow the task storage claim and test the probe script on the host
Independent review of #180: the runner's host checkouts (the staging
clone and the rebase worktree) are not task storage, so the comment and
docs now say only what the probe covers, and point at #179. The probe
script takes its directory, so a test runs it on a host folder and
compares it with the host probe: a script that stopped measuring fails
on a host that folds names. A test comment no longer claims an order
the test does not check.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Remove a failed path identity probe by name
Independent review of #180, round 2: --rm removes the probe only once
its container has started. A client killed between create and start
(its deadline, on a slow daemon) left an unnamed container and its
volume that nothing would ever remove. The probe now names its
container and, on any failure, removes it and its volume by that name,
asking through the settle window when the client did not finish. If
that does not settle, the error names the container to remove.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Confirm a failed probe's removal by inspect, as Docker answers
Independent review of #180, round 3: `docker rm --force` exits 0 for a
name that does not exist (Docker 27), so the first removal ended the
settle window and a create that landed after the killed client stayed
behind. Removal is now confirmed by `container inspect` reporting the
name absent after the window. A docker that never started sent nothing,
so it is neither removed nor waited for. The test's fake docker now
answers rm and inspect as Docker does, and can land a late create.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Test the probe's retries, its give-up and an any-host negative control
Independent review of #180, round 4. The script is now run on a folder
that already holds CASE and the NFD name, which must read as folded on
any host, so a script that printed a constant fails on Linux CI too.
The cleanup's retry while Docker refuses a removal, its give-up with
the name to remove, and a client refused before it started each have a
test; a give-up after Docker still reports the container says so. The
comment no longer claims every failed probe is removed: a create that
lands after the window, or a runner that dies first, can leave one.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Pin the path identity probe's volume options and hardening in a test
Independent review of #180, round 5: any Linux volume answers
`true none`, so only the probe's arguments show it measures a volume
made like task storage, in a container hardened like the keeper. A
fake-docker test now checks the exact run arguments.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Confirm a failed probe's removal by inspect whatever rm answers
Independent review of #180, round 6: older Docker CLIs exit non-zero
for `rm --force` on a missing name, and the cleanup then never asked
inspect, so it waited out its whole window and said a container may
remain when none did. Every removal is now followed by an inspect, as
allocation's own removal does.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Point the probe's leftover-container note at #181
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Require test fakes to answer as the real tool does
Review lesson from #180: a fake `docker` answered `rm --force` on a
missing name with exit 1, while Docker 27 exits 0, so a cleanup that
trusted that exit code passed its tests while it would leak against
the real daemon. Added to the mirrored "Review readiness" section of
AGENTS.md and CLAUDE.md in the same commit.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Hold this branch's fakes to the new rule
The probe fake names the Docker version its answers were checked
against and gives the older CLI's rm answer for a missing name; the
CLI test's fake says it models only a successful startup's calls.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Word the fakes rule by the versions the code may meet
No repository document defines a supported Docker, git or gh range, so
"supported versions" left a fake's completeness undecidable. Changed in
both mirrored files.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Make the fakes answer exactly as Docker 27.4.0 does
The probe fake's inspect of a missing name now prints [] on stdout and
its refused run uses Docker's own wording; the CLI test's fake names
the version its success-path answers were checked against.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Give the fakes Docker's full answers and list every modelled call
Independent review of #180, round 13: a refused run's stderr has a
second line on Docker 27.4.0, now in the fake and asserted in full; the
CLI test's fake lists the network listing and build among its calls.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Name every call the fakes answer, image inspect and build included
Independent review of #180, round 14.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Bound the probe cleanup per call, test volume removal, document the step
Independent review of #180, round 15. Each cleanup call now gets the
time left before giving up (at least 1 s), so a daemon that stops
answering cannot stretch the bound, and the give-up reports the last
answer Docker gave. A real-Docker test shows a never-started container
made with the probe's exact mount loses its volume to the cleanup's own
rm. The runner lifecycle doc and README name the startup check.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Start no cleanup call after giving up is due, and report its real cause
Independent review of #180, round 16. After the first ask, the probe
cleanup checks the give-up time before each call, so a daemon that
stops answering partway adds at most one call's floor and stop grace.
On give-up it reports the latest outcome of either call (an unanswered
rm included), or that Docker still reported the container. The fake no
longer has states Docker never answers: a removal in progress applies
only to a container that exists, and a hanging daemon starts at a set
time.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Simplify the probe cleanup's give-up, and settle unless the daemon refused
Independent review of #180, round 17. One helper now makes every
cleanup call and checks the give-up time before each, and the give-up
says only that Docker did not confirm the container is gone, with
Docker's last outcome as the cause. A client-side exit 125 (a lost
connection after the create was sent) no longer skips the settle
window: only a refusal from the daemon itself does. The fake notes
which rm and inspect output the code reads.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Accept either last answer in the give-up test; use Docker's real 125 text
Independent review of #180, round 18: when giving up falls due during
the rm, the last answer is the rm's refusal, not the inspect, so the
test accepts both. The unreachable-daemon fake prints what Docker
27.4.0 prints. The cleanup comment names the 10 s cap on each call.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Test the probe's strict parse; model an unreachable daemon fully
Independent review of #180, round 19: a probe that exits 0 with output
other than one identity line is refused, now under test. The unreachable
fake answers rm and inspect as Docker 27.4.0 does without a daemon, and
the old-CLI note cites the 20.10.0 release notes it rests on.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Pin the script the probe container runs, and cite the probe's evidence
Independent review of #180, round 20: the argv test now checks the -c
argument is the measuring script itself, so a constant printed in its
place fails; the plan-library alignment row cites the probe's code and
tests.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Test each field the probe reports, and the 10 s cap per cleanup call
Independent review of #180, round 21: every fake answered tmpfs's
true none, so a probe that ignored the container's answer passed; the
fake can now answer any identity and each field is checked on its own.
A hung-daemon test shows the first cleanup call is cut at 10 s.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Test the probe's 1 s floor, the parse's start anchor and its writes
Independent review of #180, round 22: a slow daemon with almost no
time left is still heard; output with a line before the identity is
refused; the script's directory listing is checked on any host; and
the tests that need a create to land assert that it did, with a 2 s
client deadline to keep that margin under load.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Land the late create after the first inspect, and assert that order
Independent review of #180, round 23: the late-create fake landed
before the first rm, so the test never exercised a create that lands
after the cleanup has found nothing. It now lands after the first
inspect, and the test checks that the first rm found nothing and a
later one removed it.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Assert the absent-then-silent order, and say what the old-CLI fake leaves unchecked
Independent review of #180, round 24.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Judge the probe cleanup's settle window by when each inspect was asked
Independent review of #180, round 25: an absent answer to an inspect
asked inside the window, arriving after it while a create lands, was
taken as settled and left the container. The window now uses the time
the inspect was asked; a fake with a slow, mid-landing inspect tests it.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Assert the slow inspect answered, and test the default 30 s give-up
Independent review of #180, round 26.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Pin the default give-up from both sides
Independent review of #180, round 27: the default-bound test accepted
up to 50 s, so a longer settle window or give-up passed; it now allows
3 s over the 40 s it states.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Pin the cleanup call deadline's floor and cap exactly
Independent review of #180, round 28: timing tests caught only some
changes to the 1 s floor and 10 s cap. The deadline is now a small
exported function with exact values tested in both directions; the
timing tests still show the cleanup uses it.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Name and pin the probe cleanup's settle and give-up values; reattach a doc
Independent review of #180, round 29: changes under about 3 s to the
default give-up or the settle window passed the timing test. Both are
now named exports with their exact values tested; the timing test
shows they are the ones in effect. The removeProbeContainer doc sits
above its function again.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Check that the probe's settle window starts when the client is stopped
Independent review of #180, round 30.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Test the never-started check's synchronous spawn failure (E2BIG)
Independent review of #180, round 32.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Scope the real-Docker volume checks to the probe's own volumes
Independent review of #180, round 33: both tests compared every volume
on the daemon, so another run on it could fail them. They now count
only volumes with the probe's tmpfs options, or the container's own
volume by name. The per-call deadline comment states the tolerance the
timing tests give.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* State the real-Docker probe test's one-probe-per-daemon precondition
Independent review of #180, round 34.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Say which constant script each script test catches, and where
Independent review of #180, round 36.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Word the fakes rule by the parts of an answer the code reads
Independent review of #180, round 37: as written, the rule required a
fake to reproduce output the code never reads (a removed container's
name, full inspect JSON, build logs). Changed in both mirrored files.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Pin the probe cleanup's give-up start and the settle window's end
Independent review of #180, round 37: giving up counted from the start
of docker run, or a 15 s window, passed every test. The give-up test
now needs the client deadline, the window and the cleanup time; the
late-create test bounds the window from above.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* State the cleanup bound's pause and the unconfirmed volume removal
Independent review of #180, round 38. The overshoot bound includes one
250 ms pause. The volume's removal is Docker's and is not confirmed;
the maintainer accepted that as part of #181.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Seed one name per field in the script tests; assert the busy refusals
Independent review of #180, round 42: with CASE and the NFD name seeded
together, a script whose case check read the NFD name (or the reverse)
passed everywhere; one seeded name per field catches it on Linux CI.
The busy test now shows both refusals were answered before a later rm
removed the container.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Tighten the hung-daemon test so a floor near 4 s fails
Independent review of #180, round 43: a 3.7 s per-call floor at the call
site passed under the 4 s ceiling; the ceiling is now 2.5 s and the
stated floor tolerance roughly 0.4-2.5 s.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: eastagiletracker <310448263+eastagiletracker@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Copy file name to clipboardExpand all lines: AGENTS.md
+1Lines changed: 1 addition & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -59,6 +59,7 @@ A race regression must assert that the interleaving it names actually happened (
59
59
- Each self-review and independent-agent review pass examines the full current PR diff against the base, including changed tests, configuration and documentation, and rereads every changed function in full. Do not review only the fixes or lines changed since the previous round. Code unchanged since the first commit of the PR still gets reviewed in every pass.
60
60
- Treat every behavioural claim the change makes, in code comments, the PR body or docs (for example "pauses every 1,000 entries", "settles only after exit", "never throws", "bounded by N seconds"), as something to verify. Trace each claim through every path that can break it, including nested loops, callbacks, error paths and early returns, and give it a test that fails if the claim is false.
61
61
- A test's setup must leave the state the production path would: if production never runs a step (such as a commit that refreshes Git's index), the test must not run it before the behaviour under test either.
62
+
- A fake of an external tool (a stand-in `docker`, `git` or `gh` on `PATH`, or a stubbed client) must give, for every call the code reads, each part of the answer the code reads (exit status, stdout, stderr) as the real tool gives it, error cases included. Where it leaves out output the code does not read, say so beside the fake. Check those answers against the real tool and name the version checked beside the fake. Where versions the code may meet answer differently (for example `docker rm --force` on a missing name), the code must not depend on that answer: confirm the outcome with a call whose answer is stable, and give the fake each version's answer.
62
63
- Independent-agent review is mandatory before every Copilot review request, including the first request and every re-request, for all changes, including documentation-only changes. The author's self-review is not a substitute. Use a reviewer that did not implement the change and does not share the author's working context: `/codex review`, a separate review agent, or `/code-review` at `high` effort or above.
63
64
- Repeat independent-agent review -> address findings -> re-run relevant validation -> independent-agent review until a complete pass on the latest head reports no new issues and no earlier valid findings remain unresolved. A pass that found issues is not clean merely because they were fixed afterward; the fixes require another independent full-diff pass. Do not stop after a fixed number of rounds.
64
65
- Only after that independent loop is clean may Copilot review be requested on the same reviewed and validated head. If Copilot finds issues, including summary-only concerns, address them and re-run relevant validation, then repeat the independent-agent review/fix loop until clean before requesting another Copilot review. Repeat this sequence until Copilot also completes a review with no new issues and no earlier valid findings remain unresolved. Never request Copilot in parallel with an unfinished independent-review cycle.
Copy file name to clipboardExpand all lines: CLAUDE.md
+1Lines changed: 1 addition & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -29,6 +29,7 @@ In QA mode, flag any code that doesn't match DESIGN.md.
29
29
- Each self-review and independent-agent review pass examines the full current PR diff against the base, including changed tests, configuration and documentation, and rereads every changed function in full. Do not review only the fixes or lines changed since the previous round. Code unchanged since the first commit of the PR still gets reviewed in every pass.
30
30
- Treat every behavioural claim the change makes, in code comments, the PR body or docs (for example "pauses every 1,000 entries", "settles only after exit", "never throws", "bounded by N seconds"), as something to verify. Trace each claim through every path that can break it, including nested loops, callbacks, error paths and early returns, and give it a test that fails if the claim is false.
31
31
- A test's setup must leave the state the production path would: if production never runs a step (such as a commit that refreshes Git's index), the test must not run it before the behaviour under test either.
32
+
- A fake of an external tool (a stand-in `docker`, `git` or `gh` on `PATH`, or a stubbed client) must give, for every call the code reads, each part of the answer the code reads (exit status, stdout, stderr) as the real tool gives it, error cases included. Where it leaves out output the code does not read, say so beside the fake. Check those answers against the real tool and name the version checked beside the fake. Where versions the code may meet answer differently (for example `docker rm --force` on a missing name), the code must not depend on that answer: confirm the outcome with a call whose answer is stable, and give the fake each version's answer.
32
33
- Independent-agent review is mandatory before every Copilot review request, including the first request and every re-request, for all changes, including documentation-only changes. The author's self-review is not a substitute. Use a reviewer that did not implement the change and does not share the author's working context: `/codex review`, a separate review agent, or `/code-review` at `high` effort or above.
33
34
- Repeat independent-agent review -> address findings -> re-run relevant validation -> independent-agent review until a complete pass on the latest head reports no new issues and no earlier valid findings remain unresolved. A pass that found issues is not clean merely because they were fixed afterward; the fixes require another independent full-diff pass. Do not stop after a fixed number of rounds.
34
35
- Only after that independent loop is clean may Copilot review be requested on the same reviewed and validated head. If Copilot finds issues, including summary-only concerns, address them and re-run relevant validation, then repeat the independent-agent review/fix loop until clean before requesting another Copilot review. Repeat this sequence until Copilot also completes a review with no new issues and no earlier valid findings remain unresolved. Never request Copilot in parallel with an unfinished independent-review cycle.
Copy file name to clipboardExpand all lines: README.md
+1-1Lines changed: 1 addition & 1 deletion
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -51,7 +51,7 @@ A configuration with a `github` block may also add a `runner` block. The runner
51
51
-`diagnosticsCapBytes`: the size retention trims that directory back to. It is a target, not a hard limit: the file just saved is always kept, even when it alone passes the cap. The default is 256 MiB.
The runner needs Docker and `CLAUDE_CODE_OAUTH_TOKEN`. At start, before the server opens, codeboost recovers what an earlier run left and builds the agent image; a recovery it cannot finish safely stops startup with what to do. `POST /api/runner` with `start` or `resume` runs the plan; the review screen has no runner buttons yet. A task paused for an out-of-scope change stays paused until the plan is amended, every changed path is declared, the remaining items validate against the runner's audited tree, and a person sends `approve-continuation` with the current `expectedStateVersion`, `expectedReviewVersion`, and an action ID. Then `resume` starts at the first unfinished item. `GET /api/runner` reports the checkpoint and whether the continuation is approved. When a run has completed every item, codeboost pushes the task head to a `codeboost/…` branch with your `gh` credentials and opens a ready pull request into `baseBranch`; a task that needs a person gets a draft pull request with its problems. Before merge, `prepare-merge` refreshes the published PR's exact base/head, rebases onto a moved base, refreshes attribution and approvals, and runs allowed `cmd:` checks in a credential-free read-only container. The check result counts only for that exact head and command list. A publish that was refused (for example, the branch holds a commit codeboost did not make) is retried with the `publish` action, and `GET /api/runner` shows the last outcome under `publish`. When a task is cancelled, codeboost closes the open pull requests it opened for it (the branch is kept). A pull request a person moved is left open and reported, and `close-pull-requests` retries a close that failed. Merge PR then merges that pull request, so `github.pullRequest` can be left out; if it is set, it must name the task's pull request, or merging is blocked. Demos never publish.
54
+
The runner needs Docker and `CLAUDE_CODE_OAUTH_TOKEN`. At start, before the server opens, codeboost recovers what an earlier run left and builds the agent image; a recovery it cannot finish safely stops startup with what to do. It then checks `pathIdentity` against the task storage the agent edits, a Linux tmpfs (`caseSensitive: true`, `unicodeNormalization: "none"`), and refuses to start when they differ. `POST /api/runner` with `start` or `resume` runs the plan; the review screen has no runner buttons yet. A task paused for an out-of-scope change stays paused until the plan is amended, every changed path is declared, the remaining items validate against the runner's audited tree, and a person sends `approve-continuation` with the current `expectedStateVersion`, `expectedReviewVersion`, and an action ID. Then `resume` starts at the first unfinished item. `GET /api/runner` reports the checkpoint and whether the continuation is approved. When a run has completed every item, codeboost pushes the task head to a `codeboost/…` branch with your `gh` credentials and opens a ready pull request into `baseBranch`; a task that needs a person gets a draft pull request with its problems. Before merge, `prepare-merge` refreshes the published PR's exact base/head, rebases onto a moved base, refreshes attribution and approvals, and runs allowed `cmd:` checks in a credential-free read-only container. The check result counts only for that exact head and command list. A publish that was refused (for example, the branch holds a commit codeboost did not make) is retried with the `publish` action, and `GET /api/runner` shows the last outcome under `publish`. When a task is cancelled, codeboost closes the open pull requests it opened for it (the branch is kept). A pull request a person moved is left open and reported, and `close-pull-requests` retries a close that failed. Merge PR then merges that pull request, so `github.pullRequest` can be left out; if it is set, it must name the task's pull request, or merging is blocked. Demos never publish.
0 commit comments