Skip to content

feat(source-control): explicit-autopilot merge-rung exception with fresh-agent resolution - #1405

Merged
kyle-sexton merged 22 commits into
mainfrom
chore/1309-babysit-loop-merge-c3-autonomous
Jul 26, 2026
Merged

feat(source-control): explicit-autopilot merge-rung exception with fresh-agent resolution#1405
kyle-sexton merged 22 commits into
mainfrom
chore/1309-babysit-loop-merge-c3-autonomous

Conversation

@kyle-sexton

@kyle-sexton kyle-sexton commented Jul 25, 2026

Copy link
Copy Markdown
Contributor

Summary

  • Adds one named, explicit paired-argument exception to the babysit-loop merge rung: an invocation whose own argument line types both the autopilot tier keyword and the dedicated raise argument --merge c3-this-run (each never inherited, never defaulted, never config-supplied, never model-composed on the caller's behalf) widens that single invocation's merge authority up to C3, in a repository that has already adopted the loop-lane baseline. The raise token exists for this exception alone, so its presence cannot be a legacy leftover: autopilot alone stays merge-inert beyond the tracked rung, exactly as before this change, so saved invocations and templates that already pass the tier keyword acquire no new authority.
  • Blocked PRs (needs-human label, open machine-authored finding, contradictory or unresolved bot review thread) get a fresh frontier-tier subagent — sharing no context with whatever produced or previously reviewed the PR — dispatched to resolve the blocker through babysit-prs's existing guarded-mutation path, before the deterministic merge gate runs.
  • C4-structural and C5-untrusted-provenance stay unconditionally human-merge. No rung, seam config, or invocation argument — including this one — ever reaches them, per the autonomy matrix's own promotion contract ("never promotes — human merge always; no evidence predicate exists for these cells"). C5 is derived from the PR's own provenance via two executable, fail-closed snapshot tests: a fork test (isCrossRepository / headRepositoryOwner) and a trust test on the provider-computed authorAssociation (anything other than OWNER/MEMBER is external — including a same-repository outside collaborator), never from the class stamped on the item it closes.
  • The rung partition's verdict is pinned to the head SHA it examined. The merge-capable invocation carries the partitioned head as its merge gate's --expected-head; any worker push — an ordinary CI or review-finding fix, not only the pre-escalation resolver's — moves the head off the pin, the gate's head-match refusal blocks the merge deterministically, and the lane re-partitions the post-push head before any merge-capable re-invocation. babysit-prs carries the matching named "Lane-pinned merge authorization" exception in reference/safety.md.
  • A safer merge argument still wins and is mutually exclusive with the raise by grammar: every --merge value other than c3-this-run only ever selects a lower rung, so autopilot --merge human-only merges nothing. Human blocking feedback and merge conflicts stay outside the dispatch entirely.
  • Bumps docs/conventions/loop-lane/README.md to 3.0.0 (a change to the autonomy-ladder invariant is a major bump per its own changelog rule) and updates config-resolution.md / babysit-loop/SKILL.md / babysit-prs/SKILL.md in lockstep. source-control plugin bumps to 0.32.0.

Reviewer gate — closed by operator decision

An earlier revision of this body held the PR from autonomous merge while #1388 — the operator decision on whether C3 autonomous merge may exist at all — was open.

  • decision: raise babysit_loop_merge to c3-autonomous, or leave it — the guardrail matrix has no C3 auto-merge cell #1388 is closed as decided (operator decision, 2026-07-25): the tracked seam stays at c2-mechanical. No standing C3 raise lands; the recorded reopening path is contract-first — amend the guardrail matrix to define a C3 promotion cell, then flip the seam, never the reverse. This PR makes no seam edit.
  • The operator explicitly approved landing this PR. What lands is the per-invocation, explicit paired-argument exception only: authorized each time by a human-typed autopilot + --merge c3-this-run pair, persisting nothing, ratifying no standing rung, and never reaching C4/C5.
  • The review thread on docs/conventions/loop-lane/README.md that was deliberately held open carries the closure record and is resolved on that operator decision.
  • An independent fresh-context verifier audited the final diff and this body before merge, per the operator's landing instruction. Its first pass found the widening keyed to the pre-existing merge-inert autopilot keyword alone (legacy invocations, template expansion, and model-composed launches could activate it without a human authorizing a merge raise); the paired dedicated raise token above is the fix, and the two trigger phrases that invited model composition ("drain hard", "merge everything you can in autopilot") are removed from the skill description.

Test plan

  • python plugins/source-control/skills/babysit-prs/scripts/tests/test_skill_contract.py — 23/23 pass at the final head.
  • python plugins/source-control/skills/babysit-prs/scripts/tests/test_guards.py — pass at the final head.
  • scripts/check-changed-skills.sh origin/main and scripts/check-changelog-parity.sh --check-bump origin/main — pass at the final head.
  • markdownlint-cli2 clean on every changed markdown file.
  • origin/main merged in (manifest + changelog conflicts composed, not overwritten).
  • Human decision on decision: raise babysit_loop_merge to c3-autonomous, or leave it — the guardrail matrix has no C3 auto-merge cell #1388 — closed as decided 2026-07-25 (seam stays c2-mechanical), with the operator's explicit approval to land this PR's per-invocation exception.

Related

No linked issue — this PR deliberately closes nothing.

…esh-agent resolution

Adds one named, explicit-argument exception to the babysit-loop merge rung:
typing the literal `autopilot` tier argument widens that single invocation's
merge authority up to C3, dispatching a fresh independent frontier-tier agent
(sharing no context with the PR's authoring/reviewing sessions) to resolve
needs-human labels, security findings, and contradictory review threads
through babysit-prs's existing guarded-mutation path before the deterministic
gate runs. C4-structural and C5-untrusted-provenance stay unconditionally
human-merge — no rung, seam, or argument, including this one, ever reaches
them, per the autonomy matrix's own promotion contract.

Closes #1309

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Kb58mL6JnfEos5zrhgn4GD

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 3896f0443b

ℹ️ 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".

Comment thread plugins/source-control/skills/babysit-loop/SKILL.md Outdated
Comment thread plugins/source-control/skills/babysit-loop/SKILL.md Outdated
Comment thread plugins/source-control/skills/babysit-loop/SKILL.md Outdated
Resolve the loop-lane CHANGELOG collision: main landed 2.0.0 (single-account
gap reframing + second re-derivation trigger), so this branch's autonomy-ladder
entry renumbers to 3.0.0 and stacks above it. Both entries retained.
…inheritance paths

Addresses the three review threads on #1405.

Version and delivery (P1): plugin.json 0.26.9 -> 0.27.0 with a matching
CHANGELOG entry — per docs/MIGRATION-PLAYBOOK.md the manifest version is the
update cache key, so an unbumped plugin never delivers the exception to an
installed consumer. Confirmed against the current plugins reference fetched this
session (https://code.claude.com/docs/en/plugins-reference): "Setting this pins
the plugin to that version string, so users only receive updates when you bump
it."

Marketplace description (P2): the plugin.json description no longer claims
merge-rung raises bind only from the team-tracked layer — it now says STANDING
raises do and names the one exception plus the C4/C5 floor. README.md
regenerated with scripts/generate-catalog.mjs (the catalog block is generated,
CI-checked, never hand-edited). docs/conventions/config-cascade/README.md
carried the same stale claim and is corrected too.

Eval alignment (P2): babysit-loop eval 1 graded the exact invocation this change
widens as "stays c2-mechanical", so running it would have rejected the intended
behavior. It now distinguishes the two argument-shaped raises on that line — the
typed autopilot tier keyword widens this one invocation to C3; the --merge
full-autonomy dimension argument is still ignored — and asserts the persist-
nothing and C4/C5-floor properties. New eval 4 covers the safety half that had
no coverage: a tier resolved from babysit_loop_tier or babysit_default_tier
rather than typed does NOT widen the merge dimension.

Inheritance paths closed: the exception's "never inherited, never defaulted"
clause named only babysit_loop_tier. config-resolution.md and babysit-loop
SKILL.md now state that no config layer or key supplies the keyword, calling out
babysit_default_tier explicitly as a babysit-prs userConfig scalar that is not a
loop-lane key.

babysit-prs SKILL.md was 4 lines over the 500-line hard cap after the merge; the
security/P1 exception detail moves to reference/safety.md's new "Security/P1
escalation: the one named exception" subsection, where it is also stated as a
contract on how the lane dispatches rather than a claim the dispatch presents
about itself.
source-control landed 0.26.10 on main (worker cwd anchoring, #1299) while this
branch bumped to 0.27.0 for the merge-rung exception. Kept 0.27.0 — a minor bump
still leads 0.26.10 — with this branch's description, and stacked both changelog
entries newest-first.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 028c668f1c

ℹ️ 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".

Comment thread docs/conventions/loop-lane/README.md Outdated
Comment thread plugins/source-control/skills/babysit-loop/SKILL.md Outdated
Comment thread plugins/source-control/skills/babysit-loop/SKILL.md Outdated
source-control reached 0.26.11 on main (in_reply_to_id projection, #587). Kept
this branch's 0.27.0 and description; main did not touch the description text,
only the version. Changelog entries stacked newest-first.
@claude

claude Bot commented Jul 25, 2026

Copy link
Copy Markdown

Claude encountered an error after 2s —— View job


I'll analyze this and get back to you.

@claude

claude Bot commented Jul 25, 2026

Copy link
Copy Markdown

Claude encountered an error after 2s —— View job


I'll analyze this and get back to you.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: b4826ad861

ℹ️ 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".

Comment thread plugins/source-control/skills/babysit-loop/SKILL.md Outdated
Comment thread plugins/source-control/skills/babysit-loop/SKILL.md Outdated
…eption

An explicitly argued lower rung is now applied after the autopilot widening, so
`autopilot --merge human-only` merges nothing; the resolution order is stated
identically in the convention, the skill's rung partition, and config-resolution.

C5 becomes a provenance test on the PR itself rather than a lookup of the
close-linked item's stamp, so a fork PR closing an internally classified C2/C3
item stays outside the exception.

Human blocking feedback and merge conflicts are named as outside the
pre-escalation dispatch: the former stays a stop-and-ask condition, the latter
routes to the merge-only conflict path, never a rebase.
Composes both sides of the source-control plugin manifest and changelog:
main's 0.28.0-0.30.0 entries keep their history, and this branch's
merge-rung exception entry renumbers to 0.31.0 on top of them. The
manifest description keeps this branch's exception clause at the new
version.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: fab489e049

ℹ️ 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".

Comment thread plugins/source-control/skills/babysit-loop/SKILL.md Outdated
Comment thread plugins/source-control/skills/babysit-loop/SKILL.md Outdated
main took 0.31.0 for the argparse-abbreviation hardening while this branch
held that number, so this branch's merge-rung exception entry moves to
0.32.0 above it. Both changelog sections are kept; the manifest keeps this
branch's description at the new version.
@claude

claude Bot commented Jul 26, 2026

Copy link
Copy Markdown

Claude encountered an error after 2s —— View job


I'll analyze this and get back to you.

@claude

claude Bot commented Jul 26, 2026

Copy link
Copy Markdown

Claude encountered an error after 2s —— View job


I'll analyze this and get back to you.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: dcdacf741f

ℹ️ 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".

Comment thread plugins/source-control/skills/babysit-loop/SKILL.md Outdated
Comment thread plugins/source-control/skills/babysit-loop/SKILL.md Outdated
…d cap

The exception pointer added here pushed the file to exactly 500 lines, which
skill-quality-gate fails. Tightened to a single pointer sentence; the
independence and scope conditions are stated in full in safety.md's
"Security/P1 escalation" section, which owns them.
main took 0.31.1 for the extra-bot-logins fix; this branch's minor stays
0.32.0 above it and both changelog sections are kept. main also grew
babysit-prs/SKILL.md to the 499-line ceiling, so the security/P1 exception
pointer folds into the existing sentence rather than adding a line —
safety.md still carries the full scope and independence conditions.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 884afa1228

ℹ️ 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".

Comment thread plugins/source-control/skills/babysit-loop/SKILL.md Outdated
main reached 0.31.3 while this branch held 0.32.0; both changelog sections
are kept and this branch's minor stays above main's patches.
@claude

claude Bot commented Jul 26, 2026

Copy link
Copy Markdown

Claude encountered an error after 2s —— View job


I'll analyze this and get back to you.

@claude

claude Bot commented Jul 26, 2026

Copy link
Copy Markdown

Claude encountered an error after 2s —— View job


I'll analyze this and get back to you.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 50d2f77523

ℹ️ 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".

Comment thread plugins/source-control/skills/babysit-loop/SKILL.md Outdated
…g exception

The C4/C5 floor now reads the pull request rather than the linked item's
stamp on both axes: C5 from the code's cross-repository provenance, and C4
from the diff's blast radius, with a class/diff mismatch failing closed to
escalation. The author-login-versus-watched-owners test is gone — that key
is a repository-owner allowlist, so it would have classified every
internally authored PR on an org-owned repository as C5.

The pre-escalation dispatch gains three limits: it fires only on a
machine-escalated needs-human item, never on the role label alone, so an
operator-parked item stays the attended queue's; it holds the PR's own
worker lease for its duration, because the guarded-mutation wrappers pin
comment state without conferring concurrency ownership; and its capability
tier resolves through the convention's tier binding instead of a fable/opus
family alias that rots across model generations.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: fc0a5a2664

ℹ️ 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".

Comment thread plugins/source-control/skills/babysit-loop/SKILL.md Outdated
Comment thread plugins/source-control/skills/babysit-loop/SKILL.md Outdated
main reached 0.31.5; both changelog sections are kept and this branch's
minor stays 0.32.0 above main's patches.
@claude

claude Bot commented Jul 26, 2026

Copy link
Copy Markdown

Claude finished @kyle-sexton's task in 5m 28s —— View job


PR Review — feat(source-control): explicit-autopilot merge-rung exception with fresh-agent resolution

  • Load tools and set up review
  • Read changed files (babysit-loop SKILL.md, loop-lane README, config-resolution, safety.md, evals)
  • Analyze governance / autonomy matrix alignment
  • Review two open Codex P1/P2 findings (worktree isolation, loop wakeup persistence)
  • Check correctness, security, and CLAUDE.md alignment
  • Post final review

Overview

The internal consistency of the exception — its constraint language, safety guards, and four-step resolution order — is thorough and well-constructed. The responses to earlier Codex rounds addressed real defects (C5 provenance, operator-parked distinction, lease acquisition, tier resolution, safer-argument ordering). Three issues remain open, one of them unaddressed in any PR round.


Finding 1 — prompts/loops/loop-lane-prompts.md not updated (correctness; not addressed)

This is the Codex P2 finding from commit 884afa1228 that received no response and no commit. The file is not in this PR's changed-files set, so the stale text survives the merge.

prompts/loops/loop-lane-prompts.md:19–21:

`{{TIER}}` widens discovery, fixing, threads, drafts, barriers, and
escalation. It never raises merge authority — that binds only from the
target repo's tracked config (below).

prompts/loops/loop-lane-prompts.md:479–482:

**Standing authorization.** Autonomous lane. Advance PRs, fix
branch-owned CI and review failures, resolve outdated bot threads, and
merge within whatever rung the repository's tracked config resolves.

Both statements are now false when {{TIER}} is autopilot. A user who copies the merge-lane template with {{TIER}}=autopilot receives seeded instructions that tell the lane the tier never raises merge authority. The lane will follow that instruction over the SKILL.md, and eligible C3 PRs will be withheld in exactly the invocation the exception is meant to cover.

There is also a repo-specific note at lines 758–764 that says autopilot does not touch merge authority and cites #1388 and the operator's decision — that section correctly reflects the current-repo governance state and should stay, but it should not be the only place the exception is mentioned. The generic template text at lines 19–21 needs a matching carve-out for the named exception.

Fix this →


Finding 2 — "Fixing findings" scope creates ambiguous code-mutation path (correctness)

plugins/source-control/skills/babysit-loop/SKILL.md:311–312:

resolves through babysit-prs's own guarded-mutation path (never a raw mutation outside that skill's
wrappers), replying to and resolving threads or fixing findings as the blocker requires.

The dispatch's three blocker classes are: a machine-escalated needs-human item, an open machine-authored finding, and a contradictory bot review thread. For the first and third, resolution means thread mutations — covered by the guarded wrappers. For the second ("open finding"), it can mean a code edit + commit + push, which needs the isolated-worktree lifecycle (babysit-prs/reference/safety.md:28–105, assigned-worktree head assertion, refspec push), not just the thread-mutation wrappers.

The phrase "fixing findings as the blocker requires" is broad enough to include a code change. If the dispatch attempts one, it does so without a worktree, without a HEAD assertion, and without the refspec-push discipline. The lease prevents concurrent ownership but not the code-path unsafe behavior.

The dispatch description should either state explicitly that code-edit mutations are not in scope for this pre-escalation dispatch (they fall to the subsequent normal babysit-prs invocation), or route them through the full worktree lifecycle. The former is probably the right shape — the dispatch resolves the review-layer blocker, and the subsequent babysit-prs invocation handles any code work. Codex raised this as P1 on commit fc0a5a2664.

Fix this →


Finding 3 — Loop wakeup persistence contradicts ScheduleWakeup behavior (documentation)

plugins/source-control/skills/babysit-loop/SKILL.md:475–480:

If you want the next cycle to carry the same merge authority, type `autopilot` again; a bare
`babysit_loop_tier: worker` config value with no `autopilot` argument reverts to the seam rung
immediately.

/loop re-invokes via ScheduleWakeup, which preserves the original prompt verbatim (loop-lane convention §2; claude-ops/skills/lanes/SKILL.md:35–39). For a session started as /loop /source-control:babysit-loop owner/repo autopilot, every wakeup re-fires /source-control:babysit-loop owner/repo autopilotautopilot is present on every cycle without the user typing it again.

"Type autopilot again" is technically accurate in that ScheduleWakeup does exactly that, but an agent reading this in context of the surrounding "single-invocation, non-standing" framing could reasonably interpret it as requiring fresh user input between cycles. That interpretation would cause it to downgrade the merge rung after the first cycle of a standing loop, while the babysit_loop_tier: worker example reinforces the wrong mental model.

The clarification is straightforward: state that ScheduleWakeup re-fires the original invocation intact, so the widening is active for every cycle of the same /loop run, and reverts only if the next /loop invocation omits autopilot. Codex raised this as P2 on fc0a5a2664.


Finding 4 — Governance conflict (deliberately open, no action needed from review)

The unresolved review thread on docs/conventions/loop-lane/README.md:71 is correct: guardrails.md sets C3 merge policy to a flat human merge with no promotion clause, and work-classes.md has no C3 auto-merge promotable cell. The PR author verified this against the files and left the thread open deliberately, with full explanation, pending #1388. That is the right call. Nothing in this review changes the governance status.


What is well-done

  • The four-step resolution order (tracked rung → autopilot raise → explicit lower rung → C4/C5 ceiling) is stated identically in SKILL.md, loop-lane README §1, and config-resolution.md. Cross-file consistency here is a specific improvement over earlier rounds.
  • C4/C5 derivation from the PR itself (not the linked item's stamp) is correct and matches work-classes.md's "the bundle assigns the class". The explicit prohibition against testing author login against babysit_watched_owners prevents the defect that Codex caught.
  • Operator-parked vs. machine-escalated distinction in the Escalation section closes a real gap — dispatching on the label alone would have crossed lane authority boundaries.
  • Eval 4 covers the inherited-vs-typed distinction with concrete cases (C3 PR, C4 PR, fork PR), making the safety boundary machine-testable.
  • The safety.md named-exception block is scoped narrowly (one dispatch path, fresh context, through wrappers, never anything else). The boundaries are precise.

Summary

Three issues warrant attention before the governance question (Finding 4) is even reached: prompts/loops/loop-lane-prompts.md was not updated (Finding 1 — correctness, high), the "fixing findings" scope is ambiguous about code mutations (Finding 2 — moderate), and the wakeup-persistence framing could cause a standing loop to downgrade its rung mid-run (Finding 3 — moderate). Finding 1 is the most directly unsafe: a template-launched autopilot lane will receive instructions that explicitly say the tier never raises merge authority, negating the exception for its most natural entry point.

@claude

claude Bot commented Jul 26, 2026

Copy link
Copy Markdown

Claude finished @kyle-sexton's task in 3m 56s —— View job


Security Review — PR #1405

  • Load tools
  • Fetch PR diff
  • Read changed files
  • Analyze for security findings
  • Post findings

This is a policy-document PR — every changed file is Markdown or JSON evals. No executable code, shell scripts, or workflow files are modified. The security surface is the behavioral contract the documents define for AI agents: authorization logic, access-control invariants, and the scope of autonomous action.

Three findings follow, ordered by severity.


FINDING 1 — IMPORTANT | Confidence: HIGH

Unconditional security/P1 escalation rule converted to a conditional one

Location: plugins/source-control/skills/babysit-prs/SKILL.md#L237-L239 and plugins/source-control/skills/babysit-prs/reference/safety.md#L305-L326

Before this PR, the babysit-prs agent-judgment rule was unconditional: "Escalate a security/P1 thread instead of resolving it, even in autopilot."

After this PR, it reads: "even in autopilot — one named exception, scoped in safety.md." That exception, new in safety.md, permits the explicit-autopilot pre-escalation resolver to attempt to resolve (dismiss or address) a thread that the agent has classified as security/P1. The safeguards are natural-language only: "fresh context," "through wrappers," and "escalates if uncertain."

Attack path: A review bot or crafted PR review comment containing a security-flagged finding can also embed adversarial instructions (prompt injection). The pre-escalation resolver is briefed with the blocker content, has no prior context to be skeptical, and is explicitly instructed to resolve it. A resolver manipulated into marking the thread "action": "resolved" (via the guarded wrapper) clears the blocker before the deterministic gate runs — which then treats the security finding as addressed and proceeds to merge. The guarded wrappers prevent unauthorized mutations but do not filter what the resolver decides counts as "addressed."

The previous rule was unconditional precisely because the cost of a false dismissal on a security finding exceeds any benefit from automation. Converting it to conditional on a model-enforced "freshness" and "uncertainty" check is a meaningful security regression, regardless of how that check is worded.


FINDING 2 — IMPORTANT | Confidence: MEDIUM

Machine-escalation marker is not bound to a trusted identity

Location: plugins/source-control/skills/babysit-loop/SKILL.md#L289-L300

The dispatch condition for the pre-escalation resolver distinguishes machine-escalated needs-human items (dispatch fires) from operator-parked ones (dispatch never fires, escalate) using a sentinel comment:


The SKILL.md describes this as "a machine-marked escalation comment" but does not specify that verification of "machine-escalated" checks the comment's author against a known-trusted identity (lane identity, bot login). The check as described is purely content-based: does this issue have a comment whose first line matches the sentinel?

Attack path: Any user with comment access to the linked GitHub issue (which on most repos is any repo collaborator, and on public repos, any authenticated user) can post a comment beginning with that sentinel. An operator who manually parked an item with needs-human — because it requires a product decision or manual QA — would see the pre-escalation resolver dispatch on it anyway once the sentinel is present, causing the lane to answer an operator-owned question with an agent.

Confidence is MEDIUM because there may be identity verification in the marker-reading implementation not shown in these policy files. The SKILL.md does not describe such a check, and the added dispatch logic cites the sentinel grammar as the discriminator without qualifying it. If no identity check exists, this becomes a straightforward privilege-escalation from commenter to autonomous-merge authority.


FINDING 3 — IMPORTANT | Confidence: HIGH

C3 autonomous merge introduced in direct conflict with the governing guardrails matrix

Location: docs/conventions/loop-lane/README.md#L39-L71 and plugins/source-control/skills/babysit-loop/SKILL.md#L196-L208

The PR adds a path by which typing autopilot as an invocation argument grants C3 autonomous-merge authority for that run. The PR body (and an open unresolved review thread on README.md:71) both confirm that plugins/autonomy/reference/guardrails.md's autonomy matrix sets the C3 merge policy to a flat human merge with no promotion path — the promotable-cell table in work-classes.md has a C3 row that advances a verification knob, not a merge-authority one; no C3 auto-merge cell exists.

This is a policy-level authorization bypass: the mechanism is live and invocable now, while the operator decision on whether C3 autonomous merge should exist at all is unresolved (open issue #1388, labelled needs-human + priority: high, with RECOMMENDED option to leave merge authority unchanged). Merging this PR pre-commits that decision.

The PR body carries an explicit reviewer gate about this, which the PR author deliberately left open. That gate is correct. This finding records it at the security-review layer: the gap between the behavior this PR ships and the governing matrix it has not amended is a real authorization inconsistency, not just a documentation concern.


No findings related to GitHub Actions patterns, injection into shell commands, credential handling, or dependency pins — none of those surfaces changed in this PR.

…line hard cap

The skill-quality gate fails at >= 500 lines; babysit-loop had grown to 537
and babysit-prs sat exactly at 500. Condensed by pointing instead of
restating: the exception's token mechanics now cite config-resolution.md as
their single home, the widening section folds its safer-argument paragraph
in and hands the dispatch contract to the Escalation section, and verbose
passages are reflowed without dropping any normative statement. Also fixes
three bare reference/... cites introduced by the condensation to full
babysit-prs/reference/... paths so the internal-ref check resolves them,
and trims the frontmatter description under the 1536-char listing cap.
@claude

claude Bot commented Jul 26, 2026

Copy link
Copy Markdown

Claude encountered an error after 0s —— View job


I'll analyze this and get back to you.

@claude

claude Bot commented Jul 26, 2026

Copy link
Copy Markdown

Claude encountered an error after 0s —— View job


I'll analyze this and get back to you.

@github-actions

github-actions Bot commented Jul 26, 2026

Copy link
Copy Markdown

Warning

Automated review did not complete — this is an infrastructure failure, not a review verdict.

Treat any Claude comment on this PR (including a placeholder like "I'll analyze this and get back to you") as incomplete, not "no findings."

Re-running the job, or pushing a new commit, will retry the review.

@github-actions

github-actions Bot commented Jul 26, 2026

Copy link
Copy Markdown

Warning

Automated security review did not complete — this is an infrastructure failure, not a review verdict.

Treat any Claude comment on this PR (including a placeholder like "I'll analyze this and get back to you") as incomplete, not "no findings."

Re-running the job, or pushing a new commit, will retry the review.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 94164e40ec

ℹ️ 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".

Comment thread plugins/source-control/skills/babysit-loop/SKILL.md
Comment thread plugins/source-control/skills/babysit-loop/SKILL.md
…-trust to #1525

Three review findings showed paths around the rung partition's guarantees,
each fixed at the partition itself:

- Operator-parked items: a PR whose close-linked item wears the human-gated
  role label without the machine escalation marker is now withheld from the
  merge-capable set (routed to safe), not merely spared the resolver
  dispatch - the base merge gate never inspects the linked item's labels.
- Human-blocked PRs: a human CHANGES_REQUESTED review, explicit blocking
  language, or an unresolved inline human thread likewise withholds the PR
  from the merge-capable set, because a merge-capable tier's own runbook
  widens thread scope to human threads.
- Worker-tier head pin: the lane pin now rides the invocation brief at
  every merge-capable tier - babysit-prs's worker-mode step 7 gains the
  same lane-pinned no-re-pin exception Autopilot step 3 carries, and
  safety.md's section says worker and autopilot alike.

The canonical merge-lane prompt's dispatch-model passage gains the explicit
frontier exception for the pre-escalation resolver, so the template's
review-work model binding no longer contradicts the frontier requirement.

The bot-author trust finding (repository-owned App bots classified C5 by
the trust test) is deferred to #1525: the misclassification is conservative
- it only escalates org-automation PRs a C2 rung could merge - and the fix
needs a reviewed internal-bot trust surface on the tracked seam, which
would widen the scope the operator ratified for this change.
@claude

claude Bot commented Jul 26, 2026

Copy link
Copy Markdown

Claude encountered an error after 0s —— View job


I'll analyze this and get back to you.

@claude

claude Bot commented Jul 26, 2026

Copy link
Copy Markdown

Claude encountered an error after 0s —— View job


I'll analyze this and get back to you.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: c56eec1e09

ℹ️ 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".

Comment thread plugins/source-control/skills/babysit-loop/SKILL.md
…rs; restore description guard

The Subagents section routed every dedicated blocker through the Merge
Conflict Resolution contract's no-push rule, contradicting Escalation's
requirement that a code-change blocker runs the full per-PR worker
lifecycle with its own commit and refspec push. The section now
distinguishes them: conflict workers never push (the dispatching context
does); every other blocker worker, the pre-escalation resolver included,
lands its own push.

The frontmatter description's raise-token parenthetical regains the
"never inferred from a drain/merge phrasing" guard the cap condensation
dropped - the description is the model-routing surface, exactly where a
phrasing-only match must not read as an implied raise. 499/500 lines,
1470/1536 description chars.
@claude

claude Bot commented Jul 26, 2026

Copy link
Copy Markdown

Claude encountered an error after 1s —— View job


I'll analyze this and get back to you.

@claude

claude Bot commented Jul 26, 2026

Copy link
Copy Markdown

Claude encountered an error after 1s —— View job


I'll analyze this and get back to you.

@kyle-sexton
kyle-sexton merged commit d857551 into main Jul 26, 2026
29 checks passed
@kyle-sexton
kyle-sexton deleted the chore/1309-babysit-loop-merge-c3-autonomous branch July 26, 2026 09:06

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: e74d9d5f27

ℹ️ 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".

Comment on lines +293 to +296
**Pre-escalation resolution attempt, explicit-`autopilot` only.** Before a merge-eligible (C1-C3)
PR is escalated for a **machine-escalated** `needs-human` item, an open machine-authored finding, or
a contradictory/unresolved **bot** review thread, and only when this invocation's own argument line
typed both the literal `autopilot` tier argument and `--merge c3-this-run` (the widening pair

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Apply dimension floors before dispatching the resolver

For an invocation such as autopilot --merge c3-this-run --thread-resolution safe, this condition dispatches solely because the paired raise tokens are present, and the resolver can mutate bot threads before the tier-flooring at lines 254–265 is applied to the normal invocation. This bypasses the explicit safer override: reference/config-resolution.md:114-122 gives invocation arguments precedence, while babysit-prs/SKILL.md:98-104 defines safe thread resolution as disabled. Compute the effective dimension floors before this dispatch and skip thread mutation when the resolved thread-resolution dimension forbids it.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This reply was generated by AI.

Addressed in #1848 (commit 1f70ec8b), which lands the fix this finding asked for.

This finding was stranded — it posted after #1405 merged, so the thread-resolution gate never saw it. It was recovered by the stranded-findings sweep, re-verified against origin/main, and triaged as item 4 of #1786.

The fix: babysit-loop/SKILL.md's dimension-flooring rule was scoped only to "Before invoking" the babysit-prs tier, and reference/pre-escalation-dispatch.md contained no occurrence of dimension at all. Resolving review threads is an exercise of autonomy dimension 3, so the flooring rule now binds every capability the cycle exercises rather than only the tier keyword it passes on: a floored thread-resolution dimension withholds the dispatch outright and the PR escalates, reported as override-constrained — never dispatched and then narratively told not to resolve. The reference doc gains a gating section stated ahead of everything else in the file. New eval 6 covers exactly the autopilot --merge c3-this-run --thread-resolution safe shape this finding named.

Comment thread plugins/source-control/skills/babysit-loop/SKILL.md
kyle-sexton added a commit that referenced this pull request Jul 26, 2026
…#1539)

## Summary

`plugins/source-control/bin/source-control-babysit-merge` exists to add
exactly one refusal on
top of `babysit_merge.py`: it rejects `--allow-unpinned-head` so that no
unattended,
allow-rule-covered invocation can merge an unvetted head. The guard's
own comment says it "must not
depend on the interpreter behind it" — but an `=value` spelling of the
flag did exactly that.

**Reproduced on `origin/main` before touching anything:**

```console
$ bash bin/source-control-babysit-merge owner/repo#1 --allow-unpinned-head
source-control-babysit-merge: --allow-unpinned-head is not permitted through the wrapper (--allow-unpinned-head or a prefix of it).
Invoke babysit_merge.py directly for interactive unpinned use.
RC=2                             # the WRAPPER refused

$ bash bin/source-control-babysit-merge owner/repo#1 --allow-unpinned-head=true
usage: babysit_merge.py ...
babysit_merge.py: error: argument --allow-unpinned-head: ignored explicit argument 'true'
RC=2                             # argparse refused — the wrapper let it through
```

Both exit 2, but for different reasons. The guard is `[[ "$arg" == --a*
&& "--allow-unpinned-head"
== "$arg"* ]]` — a test that `$arg` is a *prefix* of the flag.
`--allow-unpinned-head=true` is not a
prefix of `--allow-unpinned-head` (the `=true` tail breaks the
comparison), so the wrapper's own
filter never fires; the CLI happens to reject it today only because the
flag is `argparse
store_true`, which refuses an explicit value. The refusal is real, but
incidental — the moment the
guarded flag (or an equivalent guarded flag) accepts a value, this same
test stops refusing
anything while looking identical, and nothing fails loudly to say so.

### Fix

Strip a `--flag=value` tail (`stem="${arg%%=*}"`) before the prefix
comparison, so the guard tests
the option name rather than the raw argument. No exact spelling that
previously refused changed
behavior — the stem of an already-refused argument is unchanged, and a
stemmed sibling flag
(`--allow-dependency`, `--allow-unprotected`,
`--allowed-owners=<value>`) still isn't a prefix of
`--allow-unpinned-head`, so it still reaches the CLI unmolested.

## Test plan

- [x] **Red first.** Wrote the `check_wrapper_refusal` rows in
`engine.test.sh` and the two new
`guard_contract.py` rows against unmodified `origin/main` and confirmed
they fail: 3 bash rows
FAIL (`--allow-unpinned-head=true`, `--allow-unpinned=1`,
`--allow-unpinned-hea=1` all reach
argparse instead of the wrapper) and 2 Python `test_every_refusal_row`
subtests FAIL the same
      way.
- [x] **Assertions check the wrapper's own refusal *text*, not exit 2.**
Exit 2 is overloaded
between the wrapper's refusal and argparse's own usage/rejection errors
on this path, so an
exit-code-only assertion would have passed before and after this fix for
different reasons
(exactly the trap `--allow-unpinned-head=true` is). `engine.test.sh`
gained a
`check_wrapper_refusal` helper that greps stderr for the wrapper's
refusal text;
`guard_contract.py`'s existing framework already asserts no JSON
envelope was emitted (proof
      Python never ran) for `bash-wrapper`-attributed rows.
- [x] **New engine.test.sh rows:** `--allow-unpinned-head=true`,
`--allow-unpinned=1`,
`--allow-unpinned-hea=1` (all refused by the wrapper) plus
no-over-refusal rows for
`--allow-dependency`, `--allow-unprotected`, and
`--allowed-owners=owner` (all still reach the
fail-closed CLI, verified via an out-of-scope-owner exit 3 so no network
call is needed).
- [x] **New guard_contract.py rows:**
`merge.equals-value-unpinned-head-refused-by-wrapper` and
`merge.equals-value-abbreviated-unpinned-head-refused-by-wrapper`, each
citing #1522. Also
      updated `wrapper_denies()` (used by the doc/parser cross-check
`test_every_wrapper_refusal_row_reaches_the_denial_table`) to strip the
same `=value` tail
before its own prefix check, so the two new rows don't fail that
self-consistency test for an
unrelated reason. Regenerated `reference/guard-contract.md` via `python
      tests/guard_contract.py --emit`.
- [x] **Green after the fix:** all `engine.test.sh` wrapper rows PASS;
full `unittest discover`
suite (442 tests, includes both new rows and the existing 5
unpinned-head-family rows) OK;
      `ruff check` clean; `shellcheck -x` on the wrapper clean.
- [x] **CHECK-SKILL babysit-prs: PASS** via
`scripts/check-changed-skills.sh origin/main` (trigger
phrases preserved, `engine.test.sh` passes). Note: this machine's
default `python3`/`python` on
PATH resolve to a broken local interpreter install unrelated to this
change (a `dataclasses`
import failure); the gate was run with a working Python 3.11+
interpreter on PATH, and the
identical failure reproduces on unmodified `origin/main` too, confirming
it's pre-existing and
      environmental, not a regression.
- [x] Repo gates run locally: `scripts/check-changelog-parity.sh
--check` and `--check-bump
origin/main` ✔; `scripts/validate-plugins.sh` ✔; `markdownlint-cli2` on
the touched docs clean.
- [x] Version bumped `0.32.0` → `0.32.1` with the matching CHANGELOG
entry.

## Related

Closes #1522. Same failure family as #1371 (fixed by #1354, which made
the guard prefix-aware) —
this closes the one spelling that fix's prefix comparison still missed.
Found during independent
verification of #1405, per #1522's own description; not introduced by
that PR.

---

*This was generated by AI during work-loop execution.*

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------

Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
kyle-sexton added a commit that referenced this pull request Jul 31, 2026
…1318/#1320/#1405

Five review findings landed on those PRs after each merged, so the
thread-resolution gate never saw them. All five re-verified live against
origin/main before fixing.

claude-config / audit-pass run-contract:
- Lock reclamation gains a liveness conjunct where the platform exposes no
  process start identity: the lock now records the holder's run id, and the
  holder's lease -- classified by the contract's existing two-sided test --
  decides. A live lease defers the reclaim instead of losing the lock, so
  assertion 3.1 holds for a run that legitimately exceeds 30 minutes. Same
  remedy shape as claude-ops' restart-consumer (#1759/#1760), whose deferral
  needs a hard ceiling only because its holder publishes no lease. Assertions
  3.12/3.13, eval 27.
- The matching table gains an anchor-collision clause, tested ahead of the
  anchor comparison and routed to the existing OLD CLOSED, NEW OPENED
  disposition, so 1.10a's "no suppression carries forward" is reachable
  rather than contradicted by the SAME, UNCHANGED row. Assertion 4.7,
  eval 28.

loop-lane prompts and convention:
- The prompts file stripped the discipline preamble from every dispatch
  brief and asserted that dispatched subagents inherit the root sweep's
  posture, which fresh-context subagents cannot. Reconciled toward the
  normative owner doc: every dispatch brief carries the preamble; the
  narrow exemption is sweep-all's own audit forks. The preamble does not
  recurse -- at a subagent's conversation start that skill reports its
  posture digest with no audit fan-out. The owner doc drops its hand-copied
  discipline enumeration, which its own no-enumeration rule forbade.

source-control / babysit-loop:
- The pre-escalation resolution dispatch is gated on the resolved
  thread-resolution dimension: dimension flooring binds every capability
  the cycle exercises, not only the tier keyword handed to babysit-prs, so
  --thread-resolution safe withholds the dispatch instead of narrating at
  it. Eval 6.
- The dispatch names --independent-resolver as its mode, maps the D7.5
  ledger onto that mode's validated evidence flags, and names the two
  thread shapes it refuses (multi-finding, severity-flagged), which
  escalate. As written the implied --autonomous could never clear the
  current non-outdated thread class the dispatch exists for. Eval 7.

Refs #1786

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FVoZoMYXqf8ZVbQYixPVPW
kyle-sexton added a commit that referenced this pull request Jul 31, 2026
…1318/#1320/#1405

Five review findings landed on those PRs after each merged, so the
thread-resolution gate never saw them. All five re-verified live against
origin/main before fixing.

claude-config / audit-pass run-contract:
- Lock reclamation gains a liveness conjunct where the platform exposes no
  process start identity: the lock now records the holder's run id, and the
  holder's lease -- classified by the contract's existing two-sided test --
  decides. A live lease defers the reclaim instead of losing the lock, so
  assertion 3.1 holds for a run that legitimately exceeds 30 minutes. Same
  remedy shape as claude-ops' restart-consumer (#1759/#1760), whose deferral
  needs a hard ceiling only because its holder publishes no lease. Assertions
  3.12/3.13, eval 27.
- The matching table gains an anchor-collision clause, tested ahead of the
  anchor comparison and routed to the existing OLD CLOSED, NEW OPENED
  disposition, so 1.10a's "no suppression carries forward" is reachable
  rather than contradicted by the SAME, UNCHANGED row. Assertion 4.7,
  eval 28.

loop-lane prompts and convention:
- The prompts file stripped the discipline preamble from every dispatch
  brief and asserted that dispatched subagents inherit the root sweep's
  posture, which fresh-context subagents cannot. Reconciled toward the
  normative owner doc: every dispatch brief carries the preamble; the
  narrow exemption is sweep-all's own audit forks. The preamble does not
  recurse -- at a subagent's conversation start that skill reports its
  posture digest with no audit fan-out. The owner doc drops its hand-copied
  discipline enumeration, which its own no-enumeration rule forbade.

source-control / babysit-loop:
- The pre-escalation resolution dispatch is gated on the resolved
  thread-resolution dimension: dimension flooring binds every capability
  the cycle exercises, not only the tier keyword handed to babysit-prs, so
  --thread-resolution safe withholds the dispatch instead of narrating at
  it. Eval 6.
- The dispatch names --independent-resolver as its mode, maps the D7.5
  ledger onto that mode's validated evidence flags, and names the two
  thread shapes it refuses (multi-finding, severity-flagged), which
  escalate. As written the implied --autonomous could never clear the
  current non-outdated thread class the dispatch exists for. Eval 7.

Refs #1786

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FVoZoMYXqf8ZVbQYixPVPW
kyle-sexton added a commit that referenced this pull request Aug 3, 2026
…1318/#1320/#1405 (#1848)

## Summary

Closes the five stranded review findings triaged in #1786 — findings
that landed on #1318, #1320,
and #1405 *after* each merged, so the thread-resolution gate never saw
them. Each was re-verified
against `origin/main` before being fixed; none was refuted, and none was
already fixed.

**1 — Lock reclamation had no liveness conjunct where the platform
exposes no process start
identity (P1).**
`claude-config/skills/audit-pass/reference/run-contract.md` said *"age
alone
reclaims"* there, and mentioned the lease heartbeat only as prose no
reclamation test consulted — so
a `--fix` run legitimately exceeding 30 minutes lost its lock to a
second run, against the
contract's own assertion 3.1 (*"exactly one proceeds"*). The lock now
records the holder's **run
id**, and reclamation reads that holder's lease and applies the
contract's *existing* two-sided
liveness test as the second conjunct: a stale, `released`, missing, or
unreadable lease reclaims; a
**live** lease refuses exactly as it would inside the 30-minute window.
Same remedy shape as
`claude-ops`' restart-consumer (#1759/#1760) — cited by its actual
heading phrase, *"age alone never
reclaims"* — with the one deliberate divergence stated at the site:
restart-consumer needs a hard
24-hour deferral ceiling because its holder publishes no lease; a
lease-bearing run needs none,
because a dead holder stops heartbeating within five minutes. New
assertions 3.12 / 3.13, new eval
27.

**2 — The `SAME, UNCHANGED` table row had no anchor-collision exception
(P2).** A collided site's
anchor is by construction *unchanged* (the occurrence discriminator
digests the heading path), so a
previously-suppressed excerpt that later gained an identical duplicate
matched row 1 exactly and
silently re-suppressed itself — contradicting assertion 1.10a's *"no
suppression carries forward"*.
Collision is now tested **ahead of** the anchor comparison in every row
and routes to the existing
`OLD CLOSED, NEW OPENED` disposition (entry stale per 4.2, finding
unsuppressed, collision named
with its occurrence count), reusing the section's established
fail-closed answer rather than adding
a fifth disposition. New assertion 4.7, new eval 28.

**3 — The prompts file stripped the discipline preamble from every
dispatch brief (P2).**
`prompts/loops/loop-lane-prompts.md` forbade the sweep *"inside a
dispatch brief"* and claimed
dispatched subagents *"inherit the posture the root sweep already set"*
— which a fresh,
non-inherited context cannot — directly contradicting the normative
owner doc
(`docs/conventions/loop-lane/README.md`, "Subagent discipline
preamble"). Reconciled **toward the
owner doc**, as the issue's acceptance criterion required: every
dispatch brief carries the
preamble; the one recursion still forbidden is narrowed to `sweep-all`'s
own audit forks. Verified
against the skill itself — at a subagent's conversation start
`sweep-all` reports its cheap posture
digest and runs no audit fan-out, and its preflight degrades to that
digest when the fan-out cannot
inherit a conversation (a fresh subagent never can), so the preamble
costs one skill read per
dispatch and does not recurse. The owner doc also drops its hand-copied
enumeration of the
individual disciplines, which its own no-enumeration rule forbade.

**4 — The resolver dispatch ignored per-dimension overrides (P1).** The
*"Dimension overrides bind
by tier flooring"* rule in `babysit-loop/SKILL.md` was scoped only to
*"Before invoking"* the
babysit-prs tier, and `reference/pre-escalation-dispatch.md` contained
no occurrence of `dimension`
at all — so `autopilot --merge c3-this-run --thread-resolution safe`
still dispatched a fresh
subagent to mutate bot threads the operator's own argument had just
denied, against
`config-resolution.md`'s *"invocation arguments win"*. Resolving review
threads **is** an exercise
of autonomy dimension 3, so the flooring rule now binds every capability
the cycle exercises, not
only the tier keyword it passes on: a floored thread-resolution
dimension withholds the dispatch
outright and the PR escalates, reported as override-constrained — never
a dispatch made and then
narratively told not to resolve. New eval 6.

**5 — The dispatch named no resolver mode, and the implied one could not
work (P2).** As written,
*"the full per-PR worker lifecycle"* implied `--autonomous`, which
hard-refuses any thread not
already `isOutdated` before its own push — precisely the current,
non-outdated bot thread D7.5
routes to this dispatch. The mode is now stated as
**`--independent-resolver`**, with the D7.5
ledger mapped onto that mode's machine-validated evidence flags, and the
two thread shapes it
refuses named where the dispatch meets them (multi-finding,
severity-flagged — both escalate). The
worker-lifecycle sentence is scoped to how a *code change* is made
rather than to mode selection.
New eval 7.

**Issue AC item 4 — checked before any work started.** `9df80ba5aa` is
**not** an ancestor of
`origin/main`, but `--independent-resolver` **is** present on
`origin/main`
(`babysit-prs/scripts/babysit_resolve_thread.py`), so the mode landed by
another route and finding 5
is not done twice. Every flag, constraint, and refusal this PR asserts
about that mode was verified
against the script by an independent reader: flag spellings, the
`--disposition` requirement, the
single-pinned-`--thread-id` requirement, the `--autonomous` /
`--include-human` /
`--allow-unpinned-thread` conflict set, and the `isOutdated` refusal.

**Already fixed — recorded so nobody re-checks it.** The sixth triaged
item, #1320:475 (the
unbounded babysit-loop launch command), was fixed by `d8575516cd` in
#1405; the generic command
carries `{{MERGE}}` and both remaining invocation examples carry an
explicit merge cap. No change
here.

**Not in this PR.** The issue's last acceptance criterion — resolving
the five threads on #1318 /
#1320 / #1405 — can only happen once these fixes land, so it is a
post-merge follow-up on #1786's
own thread rather than a change in this diff.

## Test plan

Every surface here is documentation and contract prose, so the
executable regression net is the
repo's own gates plus the skills' eval sets. All run locally against
this branch:

- `python3 scripts/check-contract-clause-coverage.py` — **passed** (4
canonical surfaces, 14 tagged
restatements, 10 pointing surfaces). It initially **failed** on this
branch, catching an untagged
restatement of `D7.5-thread-eligibility` the new resolver-mode section
had introduced; reduced to
  a pointer, which the gate's own guidance calls the stronger fix.
- `scripts/check-changed-skills.sh origin/main` — **PASS** for the
changed skills, 0 errors
(`audit-pass` 2 warnings, `babysit-loop` 1 warning; all pre-existing
soft-target line-count and
fresh-eyes notes). `babysit-loop/SKILL.md` is 499/500 lines, inside the
hard cap — the base itself
is at 499, so the net-new prose was reflowed and the dimension-flooring
detail pushed down to
`reference/pre-escalation-dispatch.md`, which already owns it, rather
than restated twice.
`babysit-prs` reports one failure, `engine.test.sh`, which is
**environment drift and not this
branch**: it fails identically on the unmodified base with 82 ruff
findings, and under CI's pinned
`ruff==0.15.22` (run via `uvx`) the same tree reports `All checks
passed!`. Local ruff is 0.16.0,
  and CI agrees with the pin — the `ci` lane is green.
- `scripts/check-changelog-parity.sh` `--check`, `--check-order`,
`--check-bump origin/main` — all
**passed**; both bumped plugins carry a matching entry and every
changelog reads newest-first with
  no duplicate versions.
- `scripts/validate-plugins.sh` — **passed** (all plugin manifests and
the catalog).
- Eval sets validated against
`plugins/skill-quality/reference/evals.schema.json` — **passed**.
- `scripts/check-orphaned-fixtures.sh --check` and
`scripts/check-contract-slice-prune.sh --check` /
  `--check-diff origin/main` — **passed**.
- `markdownlint-cli2` over every changed tree — **0 issues**.
- `typos --config _typos.toml` — **clean**.

New regression coverage added with the fixes: `audit-pass` evals **27**
(a live lease refuses the
lock past the age bound; a stale, missing, or unreadable one reclaims)
and **28** (an anchor
collision never carries a suppression forward); `babysit-loop` evals
**6** (`--thread-resolution
safe` withholds the resolver dispatch entirely) and **7** (the dispatch
runs
`--independent-resolver`, and refuses a multi-finding thread).

Two defects were found in the in-flight work and fixed before this PR
opened, both by checking the
artifact rather than the claim: a **duplicate assertion id `4.6`** in
run-contract.md's §4 table
(the new row now appends as `4.7`, matching the table's append-at-end
convention), and a
**fabricated section citation** — restart-consumer.md was cited as *"the
lock reclaims"*, a phrase
that does not appear in it; corrected to its actual bolded phrase *"age
alone never reclaims"*, and
the accompanying claim that a live holder *"never loses the lock"*
corrected, since restart-consumer
does impose a hard 24-hour ceiling.

## Round 2 — review findings, and a rebase

The second commit (`e6b91b4c`) addresses three review findings from the
first round; each carries a
per-thread reply with its disposition. All three were valid.

- **P1, run-contract.** A lock persisted by an earlier plugin version
carries no run id, so the new
lease-locating conjunct could not be constructed for it — and treating
that unlocatable lease as
stale fell straight back to age-only reclamation, the exact failure this
change removes, at the
moment it is most likely (an operator who just upgraded and is
re-running). Fixed by establishing
the conjunct the other way round for such a lock: enumerate every lease
under `runs/<state-key>/`,
and any one live by the same two-sided test defers the reclaim.
Over-deferral on a live read-only
lease is stated as the deliberate fail-closed direction and is bounded
by that lease's own
  release/staleness. **New assertion 3.14.**
- **P2, `babysit-prs/reference/safety.md`.** The section titled
*"Security/P1 escalation: the one
named exception"* cast this resolver as that exception, citing the
loop-lane convention's §1 — but
§1's exception widens the **merge rung** for a single run and never
touches severity, while the
wrapper refuses a severity-flagged thread in every unattended mode. The
documented exception was
unreachable, and it contradicted the refusal this PR had just made
explicit. Retitled and
reframed: the bright line has no exception, the typed pair unlocks the
dispatch *path*.
  `babysit-prs/SKILL.md`'s one-line restatement corrected to match.
- **P2, `babysit-loop/SKILL.md`.** The surface that actually builds
dispatch briefs still hand-copied
the discipline plugin's membership inline — the enumeration this PR had
just removed from the owner
  doc. Now points at the sweep skill, which resolves its own membership.

**Rebased onto current `main`.** The branch was several commits behind,
and the round-2 push had
triggered only the `pull_request_target` workflows, leaving the full
`ci` suite unrun against the new
code; rebasing forced a clean re-trigger. Two upstream commits had
touched the same two files this PR
edits (`babysit-loop/SKILL.md`, `babysit-prs/reference/safety.md`) —
both auto-merged, and the merged
result was re-read rather than assumed. Version bumps re-derived against
the moved base:
`source-control` 0.42.4 → **0.44.1**; `claude-config` stays **0.16.1**.

Every gate above was re-run against the rebased head, and all **32 PR
checks are green**, `ci`
included.

## Related

Fixes #1786

Refs #1318
Refs #1320
Refs #1405
Refs #1777

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
kyle-sexton added a commit that referenced this pull request Aug 6, 2026
…sit-loop C5 trust test (#1830)

## Summary

Resolves the reserved design decision on #1525 (decision comment with
delegated-authority statement, chosen design, rejected alternatives, and
cross-vendor review:
#1525 (comment))
and implements it, which also fixes #1520's misclassification of
repository-owned automation identities.

- **New team-tracked loop-lane key
`babysit_loop_trusted_internal_bot_logins`** (`config-resolution.md`:
key-table row plus a dedicated "the C5 trust test's one reviewed
widening" section). A flat Markdown bullet list of exact GitHub App bot
logins; honored **only** from the TARGET repository's team-tracked
`.claude/source-control.md` (default branch when the checkout differs),
so every trust grant is a recorded, reviewable, versioned config change;
appearances in the user-global layer, local overlay, or invocation
arguments are ignored and reported; unset/unreadable/malformed **fails
closed** to the empty set.
- **The babysit-loop C5 trust test gains a second positive arm**
(`babysit-loop/SKILL.md` rung partition): `authorAssociation`
`OWNER`/`MEMBER`, or a structural bot (`[bot]` suffix / provider `Bot`
type) whose login matches a listed entry. Neither arm positively passing
is C5, fail closed, exactly as before. The fork test stays independent
(a listed bot authoring from a cross-repository head is still C5), and
the key is consumed by the trust test and nowhere else.
- **Composition with the dependency-manager hold:** independent, and the
hold wins on intersection — the trust key affects classification only; a
login also in the built-in dependabot/renovate set or
`babysit_extra_dependency_manager_logins` is still never merged
autonomously. `babysit_watched_owners` remains never a trusted-author
list (per the acceptance criteria, the key does not reuse it).
- **Convention + changelog in lockstep:** loop-lane convention README's
C4/C5 floor paragraph now admits the tracked-seam internal-bot
attestation; convention CHANGELOG bumped to 7.0.0 (major, with
bump-ambiguity rationale per the convention's own rule); plugin
CHANGELOG 0.43.0 + `plugin.json` bump.
- **Eval added** (`babysit-loop/evals/evals.json` id 6) covering the
bot-author cases: listed same-repo bot (not C5), listed bot from a fork
(still C5), unlisted bot (C5), listed dependency manager (not C5 but
held from merge), overlay-only listing (ignored; C5).

To keep `babysit-loop/SKILL.md` under the 500-line hard cap, the
trust-test bullet drops the parenthetical `babysit-prs` "Scope
resolution" citation for the watched-owners rule — the rule itself, with
its rationale, remains stated in the SKILL, `config-resolution.md`, and
the convention.

## Test plan

- `scripts/check-changed-skills.sh origin/main` — PASS (babysit-loop;
499/500 lines, description trigger phrases preserved).
- `scripts/check-changelog-parity.sh --check`, `--check-bump
origin/main`, `--check-order` — all PASS (71 changelogs, version/entry
parity, newest-first order).
- `markdownlint-cli2` on all five changed markdown files — 0 errors.
- `evals.json` validated against
`plugins/skill-quality/reference/evals.schema.json` — valid.
- `typos` and `editorconfig-checker` on the changed paths — clean.

## Related

- Fixes #1525
- Fixes #1520
- Deferred from PR #1405 review (Codex thread "Preserve trust for
repository-owned bot authors").
- Concurrency note: authored on a branch based directly on `origin/main`
while sibling sessions work #1660 and #1641 on their own branches. Those
issues target `babysit-prs/reference/safety.md` and `orchestration.md`
(non-convergence tripwire durability; worker-tier disproved-thread
retirement) — this diff touches neither file, so no overlap with the
regions they describe is expected; the shared
`plugins/source-control/CHANGELOG.md` head and `plugin.json` version are
the one likely merge-order conflict point (version renumber on whichever
lands second).
- This session may not merge, and does not remove `needs-human`: merge
and decision ratification stay with a human reviewer.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

https://claude.ai/code/session_01RhS3T7ShwJgKTrvk2Mvd3C

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant