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.

…ternal gaps

The rung partition's verdict is now pinned to the head SHA it examined,
for every push and not only the pre-escalation resolver's: the
merge-capable invocation carries the partitioned head as its merge gate's
--expected-head, an ordinary worker fix-push moves the head off the pin,
the gate's head-match refusal blocks the merge deterministically, and the
invocation reports the new head instead of re-pinning. babysit-prs gains
the matching named "Lane-pinned merge authorization" exception in
reference/safety.md; the lane reruns provenance, C4-diff, and rung on the
post-push head before any merge-capable re-invocation.

The C5 provenance test is now executable and fail-closed instead of
"another external-contribution signal": a fork test (isCrossRepository /
headRepositoryOwner) plus a trust test on the provider-computed
authorAssociation, where anything other than OWNER or MEMBER is external
- catching the outside collaborator whose base-repository branch passes
the fork test - and a missing or unreadable field is C5, never a pass.
The cycle-start snapshot enumerates the provenance fields it must carry.

Loop-lane convention README, its changelog 3.0.0 entry, and the plugin
changelog updated in lockstep.
@claude

claude Bot commented Jul 26, 2026

Copy link
Copy Markdown

Claude finished @kyle-sexton's task in 4m 8s —— View job


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

  • Load tools and set up review
  • Read current diff and changed files (HEAD: 79bde72)
  • Check which prior findings were addressed by this commit
  • Analyze for new or remaining issues
  • Post final review

This review covers the one commit added since the prior round (d8c8cf0): 79bde72 ("fix(source-control): close the post-push repartition and same-repo-external gaps"). All changed files are Markdown.

What 79bde72 resolved

Both Codex P1 findings from the prior Codex round (f71d8df9) are now closed.

1. Repartition after the normal worker's final push — resolved

SKILL.md:242–252 — "The verdict authorizes a head SHA, not the PR" now covers all pushes: any worker push (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, and the invocation reports the new head rather than re-pinning. The lane re-runs the full partition (provenance, C4-diff, rung) on the post-push head before any fresh merge-capable invocation.

babysit-prs/reference/safety.md:339–351 gains the matching "Lane-pinned merge authorization" named exception, which records the end-of-authority rule in the invoked skill's own contract, not only the lane that dispatches it.

The cross-reference from SKILL.md Escalation (lines 358–361) ties the two-partition requirement back to this mechanism so the resolver path and the normal-worker path are stated as converging on the same enforcement surface.

2. Same-repository outside-collaborator classified as C5 — resolved

SKILL.md:224–237 — the C5 test is now two executable, fail-closed tests, either marking C5 and each failing closed when its field is missing or unreadable:

  • Fork test: isCrossRepository: true, or headRepositoryOwner differing from the base owner.
  • Trust test: authorAssociation anything other than OWNER or MEMBER — explicitly naming COLLABORATOR (the outside collaborator whose same-repository branch passes the fork test), CONTRIBUTOR, and NONE.

"Vague 'other signals' are not a test: these two fields are the executable surface" closes the "another external-contribution signal" underspecification that was the Codex P1. The provenance fields (isCrossRepository, headRepositoryOwner, authorAssociation) are now required fields of the cycle-start snapshot.

The loop-lane README §1 (84–86) is updated in lockstep with the outside-collaborator example and the fail-closed rule.


What remains open

Finding A — config-resolution.md C5 description still lags the SKILL.md (MODERATE)

config-resolution.md:164–165:

C5 from the code's own provenance — a cross-repository
head, or another external-contribution signal on the PR —

79bde72 updated SKILL.md and the convention README with the specific two-field test, but did not update this file. The SKILL.md explicitly warns "Vague 'other signals' are not a test"; that statement was made about the SKILL.md itself, but the vague language now lives here, in the plugin's authoritative config-resolution reference. An agent briefed only from this file for C5 derivation receives an underspecified test that invites the forbidden "another signal" interpretation — which is precisely what 79bde72 was meant to eliminate.

Fix this →

Finding B — No eval coverage for the authorAssociation: COLLABORATOR case (MODERATE)

The evals' eval 4 (prompt: "opened from a fork in an outside account — the head repository differs from the base") tests the isCrossRepository: true fork case. The new behavior added by 79bde72 — an outside collaborator pushing to a base-repository branch (isCrossRepository: false, authorAssociation: COLLABORATOR) — has no machine-testable assertion. The SKILL.md explicitly singles out this case as the gap the prior Codex P1 identified ("COLLABORATOR is an outside collaborator, whose push to a base-repository branch has isCrossRepository: false and so passes the fork test while being exactly the same-repository external contribution"). It's the only new behavior in 79bde72 without a corresponding eval.

Fix this →

Finding C — Governance conflict (deliberately open; no action needed)

The open review thread on docs/conventions/loop-lane/README.md:71 is correct. The operator's 2026-07-25 decision against C3 autonomous merge is on-record in prompts/loops/loop-lane-prompts.md:786–795. The PR body carries the explicit reviewer gate; this is the right posture.

Finding D — Security/P1 escalation rule conditional (carried over; not addressed)

From the prior security review: babysit-prs/SKILL.md:237–239 and safety.md:353–374. The unconditional security/P1 escalation rule now has a named exception for the explicit-autopilot pre-escalation resolver. The constraints in safety.md — fresh context, through wrappers, escalates when uncertain — are natural-language only. A crafted review comment classified as a security/P1 finding can embed adversarial instructions; the resolver has no prior skepticism and is briefed to resolve the blocker. 79bde72 does not address this finding; it is inherited from the PR's earlier commits. Whether the narrow scope sufficiently mitigates it is a judgment the operator should make explicitly when reviewing the governance question in Finding C.

Finding E — Machine-escalation sentinel not bound to author identity (medium confidence; carried over)

SKILL.md:302–306: the dispatch distinguishes machine-escalated from operator-parked needs-human items using the sentinel comment, but the SKILL.md does not require verifying the comment's author against a known-trusted identity. Any collaborator with issue-comment access can post a matching sentinel. Confidence is medium: an identity check may exist in the implementation. 79bde72 does not address this.


What is well-done in 79bde72

  • The head-SHA pinning model is the right abstraction: it converts a narrative re-partition rule into a deterministic enforcement mechanism (the gate's head-match refusal), and it applies uniformly to both the resolver dispatch and the normal worker path. No head merges under the lane that the partition didn't class-check.
  • "Report, don't re-pin" in safety.md records the end-of-authority rule in the invoked skill's own contract rather than only in the lane. A future babysit-prs invocation reading only safety.md for its lane-pinned behavior gets the same rule.
  • The authorAssociation test is specific, fail-closed, and cites the field name directly — which is the concrete fix the Codex P1 called for.
  • The snapshot field enumeration (isCrossRepository, headRepositoryOwner, authorAssociation) at step 1 closes the gap between "SKILL.md says to read these" and "cycle-start snapshot must carry these".

Summary

79bde72 cleanly closes both Codex P1 findings from the prior round. The implementation's internal consistency across all four documents — SKILL.md, safety.md, convention README, and CHANGELOG — is solid for the resolved findings. Two moderate gaps remain: config-resolution.md still uses the vague "another external-contribution signal" language the SKILL.md explicitly repudiates (Finding A), and the authorAssociation: COLLABORATOR case has no eval (Finding B). Neither is new to this commit; both are consequences of the incremental fix-in-place approach rather than a retroactive rewrite of every document at once. Findings D and E are carried over from the security review and are not addressed here. Finding C is the governing blocker and is correctly left to #1388.

@claude

claude Bot commented Jul 26, 2026

Copy link
Copy Markdown

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


Security Review — PR #1405 (HEAD: 79bde72)

  • Load tools
  • Fetch PR diff and read changed files
  • Analyze for security findings at current HEAD
  • Cross-check prior open findings against current state
  • Post final findings

This review covers HEAD 79bde72 ("close the post-push repartition and same-repo-external gaps"). The changed files are Markdown and JSON evals; the security surface is the behavioral contract these documents define for AI agents — authorization logic, access-control invariants, and the scope of autonomous merge authority.

What 79bde72 closes

Two Codex P1 findings from the f71d8df9 round are closed by this commit:

Same-repository outside-collaborator C5 gap — closed. The prior C5 test only applied a fork test (isCrossRepository / headRepositoryOwner), which passes for an outside collaborator whose branch lives in the base repository. 79bde72 adds a second, independent trust test: the PR's provider-computed authorAssociation must be OWNER or MEMBER; COLLABORATOR, CONTRIBUTOR, NONE, and every other value is external → C5. Absence of the field also fails closed to C5. The field is GitHub-server-computed and not directly user-influenceable, so the trust surface is sound. SKILL.md:224–237

Post-push repartition gap — closed. The partition verdict is now explicitly pinned to the head SHA it examined for every worker push, not only the pre-escalation resolver's. The merge-capable invocation carries the partitioned head as --expected-head; an ordinary worker fix-push moves the head off that pin, the gate refuses deterministically, and the invocation reports the new head rather than re-pinning. The lane reruns the full provenance/C4-diff/rung partition on the post-push head before any fresh merge-capable invocation. SKILL.md:243–252 and safety.md:339–352


Findings still open after 79bde72

Three findings from prior rounds are unaddressed. None were introduced by this commit.


FINDING 1 — CRITICAL | Confidence: HIGH

C3 autonomous merge mechanism ships against the governing matrix and a recorded operator decision

Locations:

The three-layer authorization gap is unchanged: (1) guardrails.md sets C3 merge policy to a flat human merge with no promotion clause; (2) work-classes.md's promotable-cell table has no C3 auto-merge cell — the only C3 promotable row advances a verification knob, not merge authority; (3) the operator explicitly decided against raising on 2026-07-25 (#1388), recorded in prompts/loops/loop-lane-prompts.md:763–779.

The SKILL.md and convention README still ship a fully live mechanism: any consumer repository using source-control:babysit-loop with autopilot typed on the invocation and babysit_loop_* keys in its tracked config can trigger C3 autonomous merges. The --merge human-only guard and the "decided against" text exist only in this repository's launch-template document, outside the plugin's own enforcement surface. A consumer who fills in {{TIER}}=autopilot and omits {{MERGE}} (or uses a different template) receives the exception with none of those guards.

The PR author has correctly flagged this, left the review thread on README.md:71 open deliberately, and carried the explicit reviewer gate in the PR body. This finding records the gap at the security-review layer for completeness: the authorization inconsistency is real and not a documentation concern.


FINDING 2 — IMPORTANT | Confidence: HIGH

Security/P1 escalation rule weakened from unconditional to conditional

Locations:

Before this PR: "Escalate a security/P1 thread instead of resolving it, even in autopilot" — unconditional. After this PR: "even in autopilot — one named exception, scoped in safety.md." The safety.md exception permits the explicit-autopilot pre-escalation resolver to attempt to dismiss or address a thread the agent has classified as security/P1.

Attack path. A review bot or crafted PR review comment can both be flagged as security/P1 and embed adversarial instructions. The pre-escalation resolver is explicitly fresh-context (no prior skepticism), briefed with the blocker content, and instructed to resolve it. The guarded-mutation wrappers enforce which API mutations are mechanically permitted; they do not filter what the resolver decides counts as "genuinely addressed." A resolver manipulated into classifying injected instructions as a legitimate address of the finding submits "action": "resolved" through the wrapper, clears the security blocker before the deterministic gate runs, and the gate proceeds to merge.

The constraints in safety.md:356–375 — fresh context, through wrappers, escalates if uncertain — are natural-language only. The unconditional rule provided no attack surface because no agent ever attempted to resolve a security/P1 finding. Converting it to conditional on model judgment restores that surface. The PR body does not address this; no commit in the round addresses it.


FINDING 3 — IMPORTANT | Confidence: MEDIUM

Machine-escalation sentinel verified by content only, not by comment author identity

Location: plugins/source-control/skills/babysit-loop/SKILL.md:302–307

The dispatch that distinguishes machine-escalated needs-human items (dispatch fires) from operator-parked ones (escalate, no dispatch) uses a sentinel comment: ``. The SKILL.md does not require that the comment author be verified against a known-trusted identity (the lane bot login or its babysit_extra_bot_logins allowlist). The check as described is purely content-based.

Attack path. Any GitHub user with comment access to a linked issue can post a comment beginning with that sentinel. An operator who manually parked an item with needs-human — because it requires a product decision, architectural judgment, or manual QA — would have the pre-escalation dispatch fire on it once any commenter posts the sentinel, routing an operator-owned question to an autonomous agent. Combined with Finding 2, a commenter who also influences the PR's review thread payload controls both the dispatch trigger and the content the resolver acts on.

Confidence is MEDIUM because an identity check in the sentinel-reading implementation (not described in any policy text visible in these files) would close this gap without changing the stated policy.


No new security issues from 79bde72

The authorAssociation trust test introduced in this commit relies on a server-computed GitHub field that is not user-influenceable, and fails closed on absence. The head-SHA pinning extension correctly extends a safety property that already existed for the resolver path to all worker pushes. Neither change regresses the security posture.

No findings on GitHub Actions patterns, credential handling, or dependency pins — none of those surfaces changed in this PR.

@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: 79bde72cff

ℹ️ 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
…raise token

An independent audit of the final diff found the widening keyed to bare
token-presence of the pre-existing autopilot tier keyword, which was
merge-inert before this change: every saved invocation, alias, or expanded
template already passing autopilot would have silently acquired C3 merge
authority, and the two trigger phrases the description added ("drain hard",
"merge everything you can in autopilot") invited a model to compose the
keyword from a request that never contained it.

The exception now fires only when the invocation's own argument line types
both the autopilot tier keyword AND the dedicated raise argument
--merge c3-this-run. The raise token exists for this exception alone, so
its presence cannot be a leftover: autopilot alone stays merge-inert
beyond the tracked rung (its pre-exception semantics), c3-this-run is not
a rung name and is invalid in any config layer, and the token is never
composed by a model on the caller's behalf. The safer-cap rule becomes
mutually exclusive with the raise by grammar - every other merge value
only ever lowers. The two inviting trigger phrases are removed.

Convention README, both changelogs, config-resolution, safety.md's
dispatch-path scoping, the prompts document, evals 1 and 4, plugin.json,
and the marketplace README updated in lockstep.
@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

Review of HEAD cf16f30 (commit: "fix(source-control): re-key the merge widening to a dedicated paired raise token").

  • Load tools and read current diff (HEAD: cf16f30)
  • Check what the new commit resolves vs. prior open findings
  • Analyze for new issues
  • Post final review

What cf16f30 resolved

The re-keying from bare autopilot to the paired autopilot + --merge c3-this-run is a significant security improvement. The prior design had a real flaw: autopilot was a pre-existing merge-inert tier keyword that widened dimensions 1–5 and 7 before this exception existed. Overloading it to also raise merge authority meant every saved invocation, alias, or template already carrying autopilot silently gained C3 authority the moment the exception was merged. The commit's own message confirms this was found by an independent audit.

The paired-token design fixes that:

  • autopilot alone stays merge-inert beyond the tracked rung, as it was before this PR.
  • c3-this-run is a token that exists only for this exception, is invalid in any config layer, and cannot be a leftover in an existing invocation.
  • Every other --merge value only lowers — making the raise and the safer cap mutually exclusive by grammar, eliminating the prior "widening first, cap second" ordering conflict.
  • The two trigger phrases removed ("drain hard", "merge everything you can in autopilot") prevented model-inference composition of the bare keyword from a drain/merge phrasing.

SKILL.md:137–142, README.md §1, and config-resolution.md are updated in lockstep. All four description points are consistent. Eval 1 and eval 4 are updated to reflect the paired requirement.

Additionally, config-resolution.md's C5 description (Finding A from the 79bde72 round) is now consistent with SKILL.md's authorAssociation test — the vague "another external-contribution signal" language is gone.


What remains open

Finding A — Bot-authored PRs classified as C5 by the trust test (Codex P1 from 79bde72, HIGH — not addressed)

SKILL.md:239–243:

Trust test: the PR's provider-computed `authorAssociation` is
anything other than `OWNER` or `MEMBER` — `COLLABORATOR` is an outside collaborator ...
`CONTRIBUTOR`, `NONE`, and every other value are likewise external.

Org-owned GitHub App bots ("configured lane bot identities" in the Codex finding, covered as C2 in work-classes.md:22–26) are not organization MEMBER or OWNER accounts and typically receive an authorAssociation other than those two values. The trust test therefore classifies every machine-authored PR — including C2-mechanical automation PRs that the autonomy matrix explicitly authorizes — as C5, excluding them from the merge-eligible set under any invocation including the explicit-autopilot widening. The finding was Codex P1 on 79bde72 and is not addressed in cf16f30.

Fix this →

Finding B — Operator-parked PRs still reach the normal autopilot invocation (Codex P1 from 79bde72, HIGH — not addressed)

SKILL.md:276–280:

a merge-eligible PR still blocked on a
machine-escalated `needs-human` item, an open finding, or a contradictory thread gets the
leased fresh-subagent resolution dispatch ... ahead of its
`/source-control:babysit-prs autopilot <owner/repo>#<N>` invocation, not instead of it.

The dispatch is correctly skipped for operator-parked items (Escalation, line 347–351), but the partition step (step 3, lines 204–230) does not exclude PRs whose close-linked item carries needs-human without the machine marker. The PR remains in the merge-eligible set — the partition tests class, do-not-merge, and C4/C5, not the needs-human role label. After the dispatch is skipped, the normal autopilot invocation runs. The Codex finding states the merge gate does not inspect labels on closing issues, so an operator-parked C3 PR that is otherwise green can merge.

The fix is in the partition step: a PR whose close-linked item carries the needs-human role label (regardless of whether the machine marker is present) should be excluded from the merge-eligible set and routed to safe. That exclusion mirrors the do-not-merge exclusion already there and would guarantee operator-owned decisions stay out of the autonomous merge path entirely.

Fix this →

Finding C — No eval for authorAssociation: COLLABORATOR same-repo case (MODERATE — not addressed)

Eval 4's PR #9 tests the fork case (isCrossRepository: true, "head repository differs from the base"). The 79bde72 commit added the trust test specifically to cover the same-repository outside-collaborator (isCrossRepository: false, authorAssociation: COLLABORATOR), calling it out explicitly in the SKILL.md as "exactly the same-repository external contribution the governing C5 definition includes." That case has no eval expectation — a conformant implementation could pass all evals while applying only the fork test and not the trust test. The Codex identified this as a gap on 79bde72 and it is not addressed in cf16f30.

Fix this →

Finding D — Security/P1 escalation rule now conditional (IMPORTANT, carried over — not addressed)

babysit-prs/SKILL.md:237–239 and safety.md:353–375. Before this PR, the security/P1 escalation rule was unconditional. After this PR, it has one named exception for the explicit-autopilot pre-escalation resolver. The cf16f30 re-keying narrows the dispatch condition to the paired token, which is a meaningful improvement (fewer invocations can reach the resolver), but the exception text in safety.md has not been narrowed to match: it still reads "when a caller typed the literal autopilot tier argument on that invocation's own line" in the original text, now updated to "when a caller typed both the literal autopilot tier argument and the dedicated raise argument --merge c3-this-run." The attack path is correspondingly narrower but not closed — a crafted review comment can still embed adversarial instructions, and the fresh-context resolver has no prior skepticism. The guards remain natural-language only.

Finding E — Governance conflict (deliberately open; no action needed)

The open thread on docs/conventions/loop-lane/README.md:71 is correctly held: guardrails.md sets C3 merge to flat human merge, work-classes.md has no C3 auto-merge promotable cell, and the operator decided on-record against raising on 2026-07-25 (#1388). The PR body carries the explicit reviewer gate; this is the right posture.


What is well-done in cf16f30

  • The paired-token design closes the silent-authority-acquisition gap. The prior autopilot-only design was a genuine security flaw; every existing saved invocation or template carrying the keyword would have silently gained C3 authority after merge. c3-this-run existing only for this exception, being invalid in any config layer, and being mutually exclusive with safer caps by grammar are all meaningful constraints.
  • Removal of inviting trigger phrases. "Drain hard" and "merge everything you can in autopilot" were direct prompts for model inference of the widening keyword from a drain request. Their removal closes that inference path.
  • Lockstep updates. Convention README §1, both changelogs, config-resolution.md, safety.md's dispatch-path scoping, loop-lane-prompts.md, evals 1 and 4, plugin.json, and the marketplace README are all updated consistently. Cross-document consistency for this redesign is good.
  • config-resolution.md C5 description finally matches SKILL.md's specific authorAssociation test — "another external-contribution signal" is gone.

Summary

cf16f30 closes the most critical gap found by the independent audit: silent C3 authority acquisition by all pre-existing autopilot invocations. Two Codex P1 findings from the 79bde72 round remain unaddressed — bot-authored PRs classified as C5 (Finding A) and operator-parked PRs still reaching the normal autopilot invocation (Finding B). The eval gap for the COLLABORATOR same-repo case (Finding C) is also open. Finding D (security/P1 escalation conditional) is carried from prior security reviews. Finding E (governance conflict) is correctly held open pending #1388 per the PR author's explicit reviewer gate.

@claude

claude Bot commented Jul 26, 2026

Copy link
Copy Markdown

Claude finished @kyle-sexton's task in 6m 48s —— View job


Security Review — PR #1405 (HEAD: cf16f30)

  • Load tools
  • Fetch PR diff and identify new commits since last security review
  • Read changed files at current HEAD
  • Cross-check prior open findings against current state
  • Analyze new commits for fresh security issues
  • Post final findings

This review covers the one commit added since the last security review (79bde72): cf16f30 ("fix(source-control): re-key the merge widening to a dedicated paired raise token"). All changed files are Markdown and JSON evals; the security surface is the behavioral contract these documents define for AI agents.

What cf16f30 closes

cf16f30 is a meaningful security improvement that closes the implicit-activation risk identified by the independent audit cited in its commit message.

Single-token activation eliminated. Before this commit, any saved invocation, alias, or expanded template that already carried autopilot (which it was before this exception existed) would have silently acquired C3 merge authority. After this commit, the raise fires only when the invocation's own argument line types both autopilot and --merge c3-this-run. autopilot alone stays merge-inert at the tracked rung — its pre-exception semantics — so existing invocations carry no new merge authority.

Trigger-phrase attack path closed. The two phrases that invited a model to compose autopilot from a user request ("drain hard", "merge everything you can in autopilot") are removed from the skill's description field. A model-routed launch "runs without it or asks."

Template activation gated. prompts/loops/loop-lane-prompts.md now uses a {{MERGE}} variable (defaulting to --merge human-only) in the copy-block command. Every value other than c3-this-run only ever lowers the rung, so a template-launched lane with any {{MERGE}} value other than the dedicated raise token cannot activate the widening.

config-resolution.md C5 description updated. The vague "another external-contribution signal" language (which SKILL.md explicitly called out as not a test) is replaced with the specific authorAssociation field, consistent with the SKILL.md and convention README.

evals.json corrected. Eval 1 now tests that autopilot alone (without --merge c3-this-run) keeps the merge dimension at c2-mechanical, closing the prior contradiction where the eval would have rejected the intended behavior.


Findings still open after cf16f30

Four findings from prior rounds are unaddressed in the current HEAD. None were introduced by this commit.


FINDING 1 — CRITICAL | Confidence: HIGH

C3 autonomous merge mechanism ships against the governing matrix and a recorded operator decision

Locations:

The three-layer authorization gap is unchanged: (1) guardrails.md sets C3 merge policy to flat human merge with no promotion clause; (2) work-classes.md's promotable-cell table has no C3 auto-merge cell; (3) the operator explicitly decided against raising on 2026-07-25 (#1388), recorded at prompts/loops/loop-lane-prompts.md:763–791.

cf16f30 correctly tightens the activation criteria (paired token, not single token), but it does not amend any of the three authoritative sources that prohibit C3 autonomous merge. The mechanism remains live and fully operative: any consumer repository using source-control:babysit-loop whose operator types both autopilot and --merge c3-this-run can trigger C3 autonomous merges. The --merge human-only guard and the "decided against" text exist only in this repository's launch-template document, outside the plugin's own enforcement surface.

The PR body's reviewer gate is correct and remains the governing blocker. This finding records the unchanged gap at the security-review layer.


FINDING 2 — IMPORTANT | Confidence: HIGH

Security/P1 escalation rule weakened from unconditional to conditional

Locations:

Before this PR, "Escalate a security/P1 thread instead of resolving it, even in autopilot" was unconditional. After this PR, safety.md carries a named exception that permits the explicit-autopilot pre-escalation resolver (active when both autopilot and --merge c3-this-run were typed) to attempt to dismiss or address a thread the agent has classified as security/P1.

Attack path. A review bot or crafted PR review comment can be both classified as a security/P1 finding and embed adversarial instructions. The pre-escalation resolver is explicitly fresh-context (no prior skepticism), briefed with the blocker content, and instructed to resolve it. The guarded-mutation wrappers enforce which API mutations are permitted; they do not filter what the resolver decides counts as "genuinely addressed." A resolver manipulated into classifying injected instructions as a legitimate resolution submits "action": "resolved" through the wrapper, clears the security blocker, and the deterministic gate then proceeds to merge.

cf16f30 raises the activation bar (paired token required instead of single token), which meaningfully reduces who can reach this path. It does not change the path's behavior once reached. The unconditional rule provided no attack surface because no agent ever attempted to resolve a security/P1 finding; this rule restores that surface for the paired-token path.


FINDING 3 — IMPORTANT | Confidence: MEDIUM

Machine-escalation sentinel verified by content only, not by comment author identity

Location: plugins/source-control/skills/babysit-loop/SKILL.md:313–317

The dispatch that distinguishes machine-escalated needs-human items (pre-escalation resolver fires) from operator-parked ones (escalate, no dispatch) uses a sentinel comment whose first line is ``. The SKILL.md does not require that the comment's author be verified against a known-trusted identity.

Attack path. Any GitHub user with comment access to a linked issue can post a matching sentinel. An operator who manually parked an item — because it requires a product decision or manual QA — would see the pre-escalation resolver dispatch fire on it once any commenter posts the sentinel, routing an operator-owned question to an autonomous agent. Combined with Finding 2, a commenter who can also craft the PR's review thread payload controls both the dispatch trigger and the content the resolver acts on.

Confidence is MEDIUM because an identity check in the sentinel-reading implementation (not described in the policy text) would close this gap.


FINDING 4 — IMPORTANT | Confidence: HIGH

Operator-parked C3 PRs remain rung-eligible and can reach the merge gate

Locations:

This is the Codex P1 from the 79bde72 round that is not addressed in cf16f30.

The rung partition (step 3) includes a C3 PR in the merge-eligible set if its close-linked item has a C3 work-class classification. It does not check whether that same item also carries the needs-human role label (operator-parked). A C3 item stamped C3 that an operator deliberately parked for a product decision or manual QA passes the partition.

Step 4 then invokes babysit-prs autopilot against the PR. The pre-escalation dispatch section correctly states that operator-parked items get "no dispatch, escalate" — but this applies to the pre-escalation resolver dispatch. The Codex P1 finding notes that babysit_merge.py:736-745 does not inspect labels on closing issues; it passes closing references only to the optional autopilot-merge-tier check. A green, branch-rule-clean PR closing an operator-parked C3 item can therefore reach and pass the merge gate.

Attack path. An operator who parks a C3 item for human review before merge (by applying the needs-human label without a machine escalation marker) still has the PR auto-merged by a c3-this-run-widened lane invocation, because the partition admits the PR and the merge gate ignores the closing item's label.

cf16f30 does not address this finding. The rung partition still omits any check for the needs-human label on the close-linked item.

Fix this →


No new security issues from cf16f30

The paired-token requirement introduced in this commit meaningfully raises the activation bar: existing invocations, aliases, and templates do not silently acquire C3 merge authority, and drain/merge phrasings cannot prompt a model to compose the raise. The c3-this-run token's "never composed by a model on the caller's behalf" guarantee remains natural-language only — mechanically, nothing prevents a model that has read these documents from composing the token if instructed — but the token's unusual name and the explicit instruction against model-supply together constitute a reasonable mitigation for this invocation argument's specific risk. No regression introduced by cf16f30 was found.

No findings on GitHub Actions patterns, credential handling, or dependency pins — none of those surfaces changed in this PR.


@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: cf16f30c99

ℹ️ 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
…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 👍 / 👎.

Comment on lines +311 to +312
**A blocker needing a code change runs the full per-PR worker lifecycle** — isolated PR worktree,
HEAD asserted at the live PR head, commit and refspec push (`babysit-prs/reference/safety.md`) — not

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Exempt the resolver from the pre-push-outdated rule

When the blocker is a current bot review thread and fixing it requires a push, routing the resolver through the regular per-PR worker lifecycle leaves the advertised path unable to clear the blocker: babysit-prs/reference/orchestration.md:754-762 permits a worker to resolve only threads that were already outdated before its push and explicitly bars resolving a thread made outdated by that push, while babysit_merge.py:689-694 refuses any merge with an unresolved thread. Give this named resolver an explicit pinned current-thread resolution carve-out, or route its addressed-thread resolution through the autopilot mechanic.

AGENTS.md reference: AGENTS.md:L27-L28

Useful? React with 👍 / 👎.

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>
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