fix(discipline): make sweep-all's fork stop-rule evaluable and stop claiming enforcement it lacks - #1628
Conversation
…laiming enforcement it lacks The stop-rule "if forks are unavailable, report and stop" had no detection mechanism anywhere in the plugin, so its guard could never be evaluated and the blind path was the one that ran: non-inheriting subagents fabricating ledgers, merged at step 3 and written to the working tree at step 4. - Add a two-stage preflight. Stage 1 is a zero-dispatch paired tool-schema read that deliberately never gates (the docs tie run_in_background removal to the env-var path only). Stage 2 is the decider: an inheritance-proof canary folded into the first member's real audit, verified on the main thread, failing closed. - Move the degraded mode out of setup, which a sweep never loads, into sweep-all where it executes; setup now points instead of restating. - Say plainly that forks receive the main conversation's exact tool pool, so "make NO writes" is trusted, not enforced; dispatch with isolation: "worktree" as containment and name the residual risk. - Replace the imported "roughly a dozen" wave cap with four, model the shared concurrent-subagent budgets, restore per-wave checkpointing, and define failure for the retry rule as an unproven ledger. - Flag the undocumented fork-off fallback as observed, not documented. Refs #1621, #1622, #1623 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
Claude finished @kyle-sexton's task in 2m 13s —— View job Code Review
This is a high-quality fix for three real defects. The two-stage preflight design is well-reasoned, the fresh-docs citations are present and correctly qualified, the responsibility split between Finding 1 — Canary retry: preflight and wave-cap sections give contradictory signals (clarity)
The preflight section reads as "one unproven canary result → immediate degrade." The wave-cap section reads as "unproven result → retry once → degrade on second failure." For members dispatched after a passing canary these are consistent, but for the canary itself they give opposite answers: does a first-time unproven canary degrade immediately or get one retry? The intent appears to be that the canary gets no retry (the preflight section's "never re-dispatch the batch blind" being the governing rule), and the wave-cap retry applies only to post-canary members. But a reader building from the wave-cap section alone would infer the canary gets a retry. Suggest either:
Finding 2 — Stage 1 first branch overstates what "Agent still carrying it" establishes (minor imprecision)
If fork mode were active via the server-side rollout (not the env var), the Agent tool would also lose The final "no branch is conclusive in either direction" sentence then contradicts this: if Agent carries the param, that branch is conclusive (fork mode is off). The broader claim should narrow to the other two branches. Low risk given Stage 1 never gates, but it would help a reader calibrate. Finding 3 — Eval 7's prompt cannot establish the failure condition it tests (inherent, acknowledged)
The expected output begins "When the canary returns a ledger without a verifiable inheritance proof…" — the prompt alone cannot force this condition, so any eval runner would be testing against a scenario that may not occur in the actual invocation. This is an inherent limitation of the eval format (harness state cannot be injected), and the PR description correctly acknowledges these two evals cover "paths every existing eval assumed away." Worth noting explicitly in the eval's Other notes (no action required)
|
|
Claude finished @kyle-sexton's task in 3m 47s —— View job Security Review — PR #1628
All changes are in LLM skill instruction files (Markdown) and a JSON evals definition. The security surface is prompt-level: instructions that, if wrong, cause the model to take unsafe actions — writes to the working tree, exfiltration via Bash, or acts without user authorization. I reviewed for those logic and trust-boundary issues; there are no GitHub Actions workflow changes to audit. Net direction is clearly positive. The PR adds a fail-closed preflight where none existed, adds One genuine gap remains in the canary design, plus a residual risk that is correctly disclosed but worth naming explicitly in a security review. IMPORTANT — Canary proof-question self-validation gapConfidence: PLAUSIBLE The main thread chooses the inheritance-proof question and dispatches the canary. Property 2 of the question spec says "the dispatch prompt neither contains nor paraphrases the answer." However, the skill includes no explicit instruction for the main thread to check its own dispatch message against this property before sending. The model must both formulate the question and reason that its dispatch message doesn't leak the answer — in the same context, in one step, with no backstop. This matters here more than in a typical "follow these properties" instruction because the canary is folded into the lowest-ranked member's real audit. A false canary positive has a two-part consequence: (1) the batch proceeds with non-inheriting forks, and (2) the canary fork's fabricated audit result is retained as real and carried into the step-3 dedup and step-4 corrections written to the working tree. A question that accidentally leaks its answer doesn't just allow the batch to run blind — it also seeds a fabricated audit result. A concrete failure path: the conversation is short (e.g., the first user message was "sweep all disciplines"), so the only unique conversation content is the user's opening phrase. The main thread chooses a question like "What was the first message the user sent this session?" and includes in the dispatch prompt "answer the preflight question first, then load the corrector." A non-inheriting fork can trivially reconstruct the answer from context about what triggered the skill. The main thread accepts this as verified inheritance, the canary ledger is kept, and all subsequent forks run blind. The fix is one sentence added before the dispatch instruction:
This is a self-check the main thread can execute cheaply and without a separate dispatch. Permalink: SUGGESTION — No suitable-question case is left implicitConfidence: PLAUSIBLE Property 1 requires the answer to exist only in conversation history — not in any file, CLAUDE.md, or plugin content. On a very short session (one or two turns) where all content is either in the system prompt, the CLAUDE.md hierarchy, or the dispatch message itself, there may genuinely be no question satisfying all three properties. The skill's fail-closed rule ("Absent, ambiguous, or unverifiable proof counts as NOT inherited") covers the fork's answer, but doesn't explicitly cover the case where the main thread cannot identify a question satisfying the properties in the first place. A model that tries anyway and formulates a question that violates property 1 or 2 returns to the gap above. Adding "If no question satisfying all three properties can be identified from the current conversation content, degrade immediately — treat it as a failed preflight" would close this implicitly. Permalink: Disclosed residual risk — fork Bash access is not contained by
|
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 4a8e3ed627
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
…fan-out, and preserve verified ledgers Addresses both PR review threads and a fresh-context review of the diff. - isolation: "worktree" is rejected, not shipped. A worktree is created from a commit, so isolated audit forks would not see the uncommitted work in flight — usually the thing under audit — and isolation would not bound an absolute-path write, of which inherited history is full. Replaced with a before/after working-tree comparison: verify the no-writes rule instead of claiming containment. The rejection reasoning is recorded in the skill. - Step 1 no longer re-dispatches the canary member. - A member that returns unproven mid-fan-out no longer collapses the whole pass to the posture digest, discarding checkpointed verified ledgers. Only a failed canary degrades; later failures keep the verified subset and report the rest open. - "Lowest-ranked" disambiguated to lowest discipline-batch-rank value. - Softened an over-general claim the PR's own evidence refutes: six of eight non-inheriting subagents fabricated a ledger, two refused. - Cited the CLAUDE.md-hierarchy claim the proof question depends on. - Two-modes summary, declared-delta paragraph, and the cost example brought in line with the inserted preflight. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
Claude finished @kyle-sexton's task in 3m 14s —— View job Code Review (commit
|
|
Claude finished @kyle-sexton's task in 5m 36s —— View job Security Review — PR #1628
All changes are LLM skill instruction files (Markdown) and a JSON evals definition. The security surface is prompt-level: instructions that if wrong cause the model to take unsafe actions — writes to the working tree, exfiltration via Bash, or acts without user authorization. No GitHub Actions workflow files changed. I reviewed for logic and trust-boundary issues that static analysis cannot reach; zizmor's lane (supply-chain, trigger patterns, permissions, template injection) is not re-reported here. Net direction is positive. The PR adds a fail-closed preflight where none existed, removes the false enforcement claim, and correctly discloses the residual Bash/network risk as trusted-not-enforced. The prior security review at commit IMPORTANT — Post-canary forks can lift the proof answer from conversation history (bootstrapping gap)Confidence: PLAUSIBLE The canary is dispatched first and alone. If it passes, its response — containing the stated proof answer — is recorded in the main conversation thread. Subsequent forks dispatched in waves 1, 2, etc. inherit the entire main conversation at the moment of dispatch, which by then includes the canary's exchange: the proof question and the canary's verified answer to it. A non-inheriting fork dispatched after the canary can read the canary's answer from the inherited conversation and re-state it as its own inheritance proof. The main thread verifies the fork's answer against "what this context knows" — and the canary's answer IS now in this context, so the re-stated answer appears correct. The main thread accepts it as a pass. The prior review's self-validation gap covers the canary itself (the dispatch message leaking the answer before the canary runs). This is a different path: after the canary passes, the conversation itself becomes the leak source for all subsequent forks. Concrete failure path: the observed incident had 6 of 8 forks non-inheriting within a single batch. If the canary is one of the 2 that genuinely inherits, it passes and records its proof answer. The 6 non-inheriting forks dispatched in subsequent waves read the canary's answer from conversation history, echo it, pass the main-thread check, and their fabricated audit ledgers proceed to step 3 (dedup) and step 4 (write corrections to the working tree). Permalink: Mitigations to consider (in order of containment strength):
IMPORTANT (open from prior review) — Canary dispatch self-validation gapConfidence: PLAUSIBLE — still open at HEAD The main thread selects the proof question and embeds it in the canary dispatch message. Property 2 of the question spec says "the dispatch prompt neither contains nor paraphrases the answer" — but there is no instruction for the main thread to verify compliance with property 2 before sending. A proof question whose answer can be inferred from the dispatch message's context allows the non-inheriting canary to appear to pass. The one-sentence fix from the prior review was not applied: "Before sending the canary dispatch, verify on the main thread that your chosen question's expected answer cannot be derived from the dispatch message you are about to send. If it can, choose a different question." Permalink: SUGGESTION — Non-git project leaves the write-verification path unspecifiedConfidence: PLAUSIBLE The before/after working-tree comparison uses This is lower severity than the proof-bootstrapping finding because the primary guard (instructing forks not to write) still applies; the before/after check is a verification layer, not the primary prevention. Worth a one-line addition: "In a non-git project, record file modification times or checksums for the working directory to approximate the same check; where no comparison is possible, note that the no-writes rule is unverified for this run." Permalink: Disclosed residual risk — confirmed accurateThe fork Bash/network/credential access disclosure is accurate and correctly labeled as trusted-not-enforced. No new finding here; noting for completeness that the disclosure at lines 307–314 and 318–322 covers what the before/after Summary: Two IMPORTANT findings — the bootstrapping gap (new, this review) and the self-validation gap (open from prior review, not yet fixed) — both rest on the same underlying fragility: the proof mechanism relies entirely on careful question selection, with no structural backstop when question selection fails. Both gaps compound each other: the self-validation gap allows the canary to be spoofed, and the bootstrapping gap allows every subsequent fork to be spoofed off the canary's recorded answer. Fixing one without the other leaves the preflight only half-closed. |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 58caf37c37
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
… define the non-git branch The write-verification check had the same shape of defect as the guard this PR exists to fix: a precondition that may not hold, with no stated fallback. - Move the baseline capture into the preflight, ahead of the canary dispatch. The canary is a fork with the same unconstrained tool pool as every later member, so a baseline taken before wave 1 would miss it. - Define the non-git branch explicitly: skip the comparison and report the skip, rather than omitting it silently and letting the reader assume it ran. - Say what to do when the comparison finds a difference: report it as its own finding and hold it out of the correction pass. - Cite the staged-rollout claim in setup, which was the one uncited harness statement left in that file. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
Claude finished @kyle-sexton's task in 3m 5s —— View job Code Review (commit
|
|
Claude finished @kyle-sexton's task in 3m 19s —— View job Security Review (commit
|
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 9dbae2a657
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
…lly discriminating Four findings from review, all of which made a guard look like a guard without being one — the same failure class this PR exists to fix. - The baseline now digests content, not just `git status --porcelain` entries. A fork rewriting an already-dirty file leaves the status letters unchanged, and a dirty file is exactly what a mid-session audit targets. - A tripped check now stops the pass before dedup and correction, leaves the tree untouched for the user, and reports the in-flight members' ledgers as untrusted. Previously it reported the mutation and carried on correcting on top of unauthorized edits. - No baseline available (not a git project) no longer proceeds and discloses afterwards, which protects nothing. The fan-out does not run on its own authority: report, and either degrade or take the user's explicit acceptance. - The inheritance proof adds a fourth required property: unguessable. A yes/no or a detail common to most sessions clears an equality check without proving inheritance, which is precisely the hole the preflight exists to close. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
Claude finished @kyle-sexton's task in 3m 22s —— View job Code Review (commit
|
|
Claude finished @kyle-sexton's task in 2m 32s —— View job Security Review — PR #1628 (commit
|
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: fa6d54a25e
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
…nstead of hardening it again Three consecutive review rounds each found a correct hole in the before/after working-tree digest protocol: baseline taken after the canary, status entries that do not move when an already-dirty file is rewritten, a tripped check that corrected anyway, an undefined non-git branch, no comparison at the canary-return boundary, and `?? dir/` entries `git hash-object` cannot digest. Six holes in one prose-specified protocol is the signal, not a to-do list. The mechanism was additive: #1622's commissioned remediation is that the skill stop implying the no-writes rule is enforced, and that ships here. Capture, digest, and diff are deterministic sub-work that belongs in a script the skill calls — this plugin's own script-the-deterministic-work position — reusing the repository's existing state-digest contract rather than standing up a second parallel one in prose, per reuse-or-replace. What stays: forks receive the main conversation's exact tool pool and cannot be tool-restricted, so the no-writes rule is trusted rather than enforced; a fork that wrote is untrusted output to stop on, not to correct on top of; any check is detection after the fact, never prevention. Refs #1631 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
Claude finished @kyle-sexton's task in 2m 40s —— View job Code Review (commit
|
|
Claude finished @kyle-sexton's task in 3m 23s —— View job Security Review — PR #1628 (commit
|
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 30895cdcf1
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
… instead of dropping the audit A session that opens straight into a full batch over an already-dirty tree may hold no detail meeting all four proof properties, yet the working tree holds exactly the artifacts the forks should audit. Degrading there costs the user the audit they asked for, for a reason that has a cheap fix. A fork "inherits everything the main session has at the moment it spawns", so the main thread emits a fresh high-entropy value into the transcript as an ordinary tool result before the canary spawns and asks for it back. Unguessable by construction, present in no file, unavailable to a non-inheriting subagent — the proof now works at any conversation length. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
Claude finished @kyle-sexton's task in 4m 0s —— View job Code Review (commit
|
|
Claude finished @kyle-sexton's task in 5m 20s —— View job Security Review — PR #1628 (commit
|
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 61f0fcd751
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
…audits it gates Folding the canary into the first member's real audit looked free and was not. A fork inherits everything the session holds when it spawns, so that member's ledger would land in the main conversation before wave 1 and sit in every later fork's inherited context — anchoring audits that step 3's dedup requires to be independent. The skill states that independence one section later; the canary design contradicted it. The canary is now one fork that answers the proof and nothing else: no corrector, no audit, no ledger, nothing that can anchor anyone. It costs one extra fork and the skill says so plainly rather than claiming the guard is free. Also makes the failed-canary eval reproducible: it now states the failure condition in the prompt instead of depending on the operator's fork-rollout state, which in a fork-enabled session exercised the opposite path. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
Claude finished @kyle-sexton's task in 3m 32s —— View job Security Review — PR #1628 (HEAD
|
|
Claude finished @kyle-sexton's task in 3m 52s —— View job Code Review (commit
|
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 27f3fe8de9
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
…at breaks ledger independence The wave protocol contradicted the independence invariant the dedup depends on, and the four-fork cap this PR introduced made it worse: it turned a typical eight-member pass into two waves, so wave 2 inherited wave 1's findings and was anchored by them. That is the same defect as the canary ledger, generalized. The fix is a different default rather than a different number. Prefer ONE wave: dispatch every in-scope member together so no member's ledger is in the transcript when another spawns. With the never tier excluded and the situational tier gated, an in-scope set fits inside the documented parallelism budget, so one wave is usually available. Where it is not, shrink the set before splitting, and disclose the anchoring when splitting anyway — including that a retried member inherits its own discarded response. This also lands F6 more precisely than "four": the original finding was that the -deep siblings' "roughly a dozen" exceeds CLAUDE_CODE_MAX_TOOL_USE_CONCURRENCY, and the real reason their number never belonged here is that cheap fresh-context subagents carry no independence invariant to break. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
Claude finished @kyle-sexton's task in 2m 39s —— View job Claude finished reviewing commit Code Review (commit
|
|
Claude finished @kyle-sexton's task in 2m 41s —— View job Security Review — PR #1628 (commit
|
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: e54696dc86
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
…ng only applies to a split Two holes the one-wave rewrite left in the path it recommends. - The retry-anchoring disclosure was scoped to the split branch, but a retry is anchored in a single wave as well — and worse there: it spawns after every other member's ledger has landed, so it inherits all of them. Moved to the retry rule itself, with the trade named: re-running the pass clean is often worth more than one anchored ledger. - "Checkpoint after each wave" was left verbatim in a section whose first instruction is to use one wave, where there is no partial state to lose. It now attaches to the split fallback, which is the only case it serves. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
Claude finished @kyle-sexton's task in 3m 7s —— View job Code Review (commit
|
|
Claude finished @kyle-sexton's task in 2m 52s —— View job Security Review — PR #1628 (HEAD
|
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: a382666550
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
… budget The one-wave rewrite told the router to "gate the situational tier harder" when the in-scope set did not fit. That instructs it to change relevance to satisfy a budget — corrupting membership resolution and silently costing coverage on a sweep the user explicitly asked for. It also rested on a conflation of two documented limits, now corrected against the fetched pages: CLAUDE_CODE_MAX_TOOL_USE_CONCURRENCY (default 10) caps how many run at once, not how many you dispatch; CLAUDE_CODE_MAX_CONCURRENT_SUBAGENTS (default 20) is the hard spawn failure. An 11-member fully-admitted set was never over the limit that matters, so the shrink instruction was solving a problem that did not exist. Membership decides scope; concurrency decides only timing. If the session cannot dispatch the whole set right now, wait for capacity and dispatch it whole. Split only if that is impossible, and disclose the anchoring. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
Claude finished @kyle-sexton's task in 3m 29s —— View job Code Review (commit
|
|
Claude finished @kyle-sexton's task in 2m 59s —— View job Security Review — PR #1628 (HEAD
|
…sation is more anchored, not less The retry paragraph offered "re-running the whole pass clean" as the better trade against one anchored ledger. That is backwards: a rerun dispatched from this conversation inherits every ledger the first pass already landed, so it is strictly more anchored than the single retry it was supposed to improve on. There is no un-anchored rerun available in-session at all, and the skill now says that instead of implying one exists. A retry is anchored by construction; report it as such and weigh its ledger accordingly. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
Claude finished @kyle-sexton's task in 2m 53s —— View job Code Review (commit
|
|
Claude finished @kyle-sexton's task in 2m 24s —— View job Security Review — PR #1628 (HEAD
|
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: f6f330bb26
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
…etryable audit failure The step quoted the documented behavior — `Concurrent subagent limit reached` "tells Claude not to retry" — and then, four lines later, classified every errored dispatch as retryable. The two instructions contradicted each other exactly under a capacity race, and the retry would have been anchored as well, since it spawns after other ledgers land. That error is now excluded from the retry rule by name: wait for capacity and dispatch, rather than retrying into the same wall. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
Claude finished @kyle-sexton's task in 3m 33s —— View job Claude finished reviewing commit Code Review (commit
|
|
Claude finished @kyle-sexton's task in 1m 58s —— View job Security Review — PR #1628 (HEAD
|
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 84500fe2b9
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
…guess alike The unguessable property listed "an exact count" as an example of a high-entropy value. Small-domain counts — turns, files, findings — are exactly what a non-inheriting fork can hit by chance, and because every fork gets the same question and the same model, their guesses correlate: one lucky answer is not one bad ledger, it is a pattern the others can repeat. The example is removed and the correlation is stated. The instruction now points at minting the value, which is unguessable by construction, rather than hunting the history for something that looks specific. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
Claude finished @kyle-sexton's task in 2m 40s —— View job Code Review (commit
|
|
Claude finished @kyle-sexton's task in 2m 57s —— View job Security Review — PR #1628 (HEAD
|
Closes #1621, closes #1622, closes #1623.
discipline0.9.0 → 0.10.0. Prose and evals only — no frontmatter change, no manifest surfacebeyond the version bump.
The defect
sweep-allcarried a stop-rule: "if forks are unavailable, report that the inheriting auditfan-out cannot run and stop, rather than auditing blind." Nothing in the plugin defined how to
determine that forks are unavailable. A rule whose guard cannot be evaluated is inert, not
merely under-specified — so the path that actually ran was the blind one: non-inheriting subagents
fabricating ledgers from their system prompt, merged at step 3 and written to the working tree
at step 4, which instructs "correct forward … on the main thread now".
Observed, not hypothetical. A real full-batch run dispatched eight forks; all eight came back with
no inherited conversation. Two of the eight refused to invent a ledger, and that refusal is the
only reason the run did not merge eight fabrications.
What landed
A two-stage preflight, before the fan-out.
sentences pair up: fork mode "removes the
run_in_backgroundparameter from theAgenttool"(sub-agents), while
CLAUDE_CODE_DISABLE_BACKGROUND_TASKSremoves it from "Bash and subagent tools"(env-vars). The pair discriminates those two causes,
which one signal alone cannot — but the docs tie removal to the env-var path and say nothing
about the server-side rollout, so no branch is conclusive. Shipping it as a gate would create the
false negative that silently downgrades a working sweep, so it explains rather than decides.
carrying no corrector and returning no ledger. An earlier revision folded it into the first
member's real audit to make it free; review caught that this breaks the plugin's own
independence invariant — a fork inherits everything the session holds at spawn, so that member's
ledger would sit in every later fork's context and anchor its audit. The guard costs one extra
fork and the skill says so rather than disguising it. The runbook specifies the proof question's
four required properties rather than a fixed question — the answer exists only in conversation
history, the prompt neither contains nor paraphrases it, it keys on ordinary inherited material,
and it cannot be guessed. Where history supplies nothing suitable, the main thread mints a
high-entropy value into the transcript before the canary spawns, so a thin session never loses
its audit. The main thread fails closed: absent, ambiguous, or unverifiable proof counts as
not inherited.
is already established, is retried once and then reported open — the verified, checkpointed
ledgers are kept and corrected rather than discarded.
The degraded mode moves to where it executes.
setupdeclared "where it is off, only thesession-start posture digest runs" while
sweep-alldeclared "report and stop" — two contractsfor one condition, and the better one in a file a sweep never loads, so the operator got nothing at
all.
sweep-allnow owns the degraded pass;setuppoints at it instead of restating it.Stop implying the no-writes rule is enforced (#1622). Forks "skip both filters and receive the
main conversation's exact tool pool", and a fork's tools are "Same as main session" — a named
subagent can be narrowed with
tools, a fork cannot. Every audit fork holds Write/Edit/Bash and isonly asked not to use them, and the declared delta's safety argument rested on that. The skill
now says so plainly, and treats a fork that wrote as untrusted output to stop on rather than
correct on top of.
Detection of such a write is advisory here, and deliberately split to #1631. Intermediate
revisions of this PR carried a normative before/after working-tree digest protocol. It drew a
correct review finding in three consecutive rounds — baseline taken after the canary; status
entries that do not move when an already-dirty file is rewritten; a tripped check that corrected
anyway; an undefined non-git branch; no comparison at the canary-return boundary;
?? dir/entriesthat
git hash-objectcannot digest. Six holes in one prose-specified protocol is the signal, nota to-do list. The plugin's own
script-the-deterministic-workposition says that capture/digest/diff belongs in a script the skill calls, and
reuse-or-replacesays to generalize the repository'sexisting state-digest contract rather than stand up a second parallel one in prose. So the honesty
statement — the actual commissioned remediation for F3 — ships, and the mechanism is designed
properly in its own issue instead of hardened a fourth time.
isolation: "worktree"was in the first revision as containment and is rejected after review(both threads on this PR), with the reasoning recorded in the skill so it is not re-proposed. A git
worktree is created from a commit, so isolated audit forks would not see the uncommitted work in
flight — usually the very thing under audit — and isolation would not bound a write addressed by an
absolute path, of which inherited history is full. Real audit fidelity traded for partial
containment is a bad trade for a correctness skill.
Concurrency (#1623). The wave cap no longer imports the
-deepsiblings' "roughly a dozen",which is calibrated for cheap fresh-context subagents and exceeds
CLAUDE_CODE_MAX_TOOL_USE_CONCURRENCY(default 10). It is four; the sharedCLAUDE_CODE_MAX_CONCURRENT_SUBAGENTS(20) andCLAUDE_CODE_MAX_SUBAGENTS_PER_SESSION(200)budgets are modeled; and the siblings' per-wave checkpointing is restored rather than dropped by
omission.
Failure is defined. "Retry only a failed subset, once" now covers the actual exposure: a ledger
returned without verified inheritance proof is a failure, not just an errored dispatch.
Two evals for paths all six existing evals assumed away: the fork-unavailable degrade, and the
tool pool being contained rather than enforced.
What this PR refutes
The audit was commissioned with the framing "no degraded mode exists" plus a proposal to build a
three-rung fallback ladder. At plugin scope that is wrong — a degraded mode was already the
plugin's declared position, just stranded in the wrong file. The ladder is deliberately not
built; it re-creates the salience dilution the declared delta exists to prevent and would yield
audits weaker than the ones the skill halts to avoid.
Also refuted: the tier metadata format is not a defect.
discipline-batch: core # …is validYAML and a real parser yields
core; only a naive string read is defeated by the trailing comment.The defect belongs to the runbook's read instruction, tracked separately in #1624.
Fresh-docs compliance
Every harness claim above was verified this session against pages fetched today —
https://code.claude.com/docs/en/sub-agents and https://code.claude.com/docs/en/env-vars — with
the quoted text taken verbatim. Two independent retrieval routes were used after the first
summariser pass returned a false negative for variables that are in fact documented.
Flagged unverified in the shipped text, not asserted:
forktype is requested while fork mode is OFF. The skillpreviously stated this as harness fact; it appears on no current page. Now labelled
observed-not-documented, and the preflight no longer depends on it.
isolation: "worktree"on a fork. Only its acceptance by theAgent tool is documented, so after review the skill no longer makes any claim about what it
contains — it records why the option was rejected instead.
tool result was absent from a fork's inherited transcript — which is why the proof question must
key on ordinary inherited material.
Verification
scripts/check-changed-skills.sh origin/main— PASS, 0 errors. All 7 base-ref trigger phrasespreserved on
sweep-all, all 10 onsetup. The one fresh-eyes WARN onsweep-allispre-existing (confirmed by running the checker against the
origin/mainblob).scripts/check-changelog-parity.sh --check-bump origin/main— PASS.scripts/validate-plugins.sh— PASS.markdownlint-cli2overplugins/discipline/**/*.md— 0 issues.Related
Not closed by this PR:
specified as a reusable digest rather than prose.
sweep-allrunbook findings from the same audit, deliberately deferred toa follow-up because they do not touch these paragraphs: cost gate (F5), relevance-gate tie-break
(F8), membership-read spec (F9), and the remaining eval gaps (F11).
as refuted, not deferred — it would re-create the salience dilution the skill's declared
delta exists to prevent.
🤖 Generated with Claude Code