You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Add a third mode to plugins/source-control/skills/babysit-prs/scripts/babysit_resolve_thread.py for an independent resolver: a fresh, non-merging context that resolves a bot thread on evidence of disposition, rather than on isOutdated.
Motivating evidence
--autonomous resolves only threads GitHub marks isOutdated — the one deterministic "addressed" signal it has. That guard is correct for its stated threat ("a self-resolved thread would otherwise satisfy the merge gate's 'zero unresolved threads' predicate — the actor signing its own permission slip"), but isOutdated means the referenced code moved. On a documentation or prose PR, a finding is usually fixed by rewriting elsewhere in the file, so the anchor never moves, the finding is genuinely addressed, and the guard refuses.
Both counts understate it: the script's own docstring notes that "a worker's own push flips isOutdated to true without touching a comment", so more of those threads were current at the moment they were resolved than are current now.
The consequence is that an autonomous prose lane has no legitimate route to zero unresolved threads through the sanctioned path — its only options are resolving outside the guard, staying blocked forever, or escalating every current thread to an attended session. All 20 resolves on #1594 and #1615 took the first route (raw gh api graphql), which is the gap this closes.
Design
A new mode, parallel to --autonomous, not a relaxation of it.
Caller — a fresh context that is not the merging worker and did not author the fix. Independence is what replaces isOutdated as the anti-self-certification guard: the actor resolving is not the actor whose permission slip it is.
Author scope — bot-authored threads only, reusing the existing bot classification (__typename == "Bot" / *[bot] suffix / --extra-bot-logins). Human threads stay untouchable, exactly as today. This matches the D7.5 rule in plugins/source-control/reference/review-discipline.md, which is the canonical author-conditional policy.
One at a time — a single pinned --thread-id per call, with the existing --expected-comment-count and --expected-last-updated pins enforced. Bulk resolve stays refused, as in --autonomous.
Disposition evidence, validated before resolving — the caller must pass the disposition and its evidence as arguments, and the script validates them against the thread rather than trusting the claim. Three accepted dispositions:
fixed — a fix commit SHA, which must exist on the PR head branch;
deferred — a tracker item id for the filed follow-up, which must exist and be open;
incorrect — counter-evidence, a reference the script can confirm is present in a reply on the thread.
Receipt — same JSON contract as the other modes: per-thread action, plus resolvedCount/eligibleCount. A caller must parse action, never the exit code (Gotchas, "Exit codes are not per-thread outcomes").
Fail-closed cases
Missing, unparseable, or unverifiable evidence refuses the resolve. A SHA absent from the head branch, a tracker id that does not resolve or is already closed, or counter-evidence not found on the thread are all refusals, not warnings. Refusing leaves the thread unresolved, which is the recoverable direction — a suppressed finding is not.
Why not just relax --autonomous
Because the guard is right about the merging worker. The policy decision relayed on #1614 keeps both properties: worker self-resolution stays outdated-only per the script, and non-outdated bot threads route to this independent dispatch instead. Widening --autonomous would delete the anti-self-certification property for the exact actor it was written to constrain.
Acceptance criteria
New mode implemented in babysit_resolve_thread.py, with the existing modes' behavior unchanged — --autonomous still refuses a non-outdated thread and still refuses bulk.
Human-authored threads refused in the new mode, with a test.
Each of the three dispositions has a passing-evidence test and a failing-evidence test that asserts refusal.
Evidence validation is against the world (branch/tracker/thread), never the caller's assertion.
JSON receipt shape unchanged; new refusal reasons are distinguishable action values.
scripts/engine.test.sh green (it runs the wrapper-behavior suite that guards these flags).
The wrapper plugins/source-control/bin/source-control-babysit-resolve-thread needs no new capability — it passes arguments through; confirm it does not need a new refusal rule.
Documented where the other modes are: skills/babysit-prs/SKILL.md "Guarded mutations" and skills/babysit-prs/reference/safety.md "Guarded Mutation Wrappers". Notescripts/tests/test_skill_contract.py pins the **Merge readiness** paragraph and other SKILL.md prose to the hub — read that test before editing SKILL.md, and note the file is at 499/500 lines (chore(source-control): babysit-prs/SKILL.md also sits 1 line under the 500-line hard cap #1626), so a net-positive edit needs that wall addressed first.
Add a third mode to
plugins/source-control/skills/babysit-prs/scripts/babysit_resolve_thread.pyfor an independent resolver: a fresh, non-merging context that resolves a bot thread on evidence of disposition, rather than onisOutdated.Motivating evidence
--autonomousresolves only threads GitHub marksisOutdated— the one deterministic "addressed" signal it has. That guard is correct for its stated threat ("a self-resolved thread would otherwise satisfy the merge gate's 'zero unresolved threads' predicate — the actor signing its own permission slip"), butisOutdatedmeans the referenced code moved. On a documentation or prose PR, a finding is usually fixed by rewriting elsewhere in the file, so the anchor never moves, the finding is genuinely addressed, and the guard refuses.Measured across two real babysit runs:
isOutdatedBoth counts understate it: the script's own docstring notes that "a worker's own push flips
isOutdatedtotruewithout touching a comment", so more of those threads were current at the moment they were resolved than are current now.The consequence is that an autonomous prose lane has no legitimate route to zero unresolved threads through the sanctioned path — its only options are resolving outside the guard, staying blocked forever, or escalating every current thread to an attended session. All 20 resolves on #1594 and #1615 took the first route (raw
gh api graphql), which is the gap this closes.Design
A new mode, parallel to
--autonomous, not a relaxation of it.isOutdatedas the anti-self-certification guard: the actor resolving is not the actor whose permission slip it is.__typename == "Bot"/*[bot]suffix /--extra-bot-logins). Human threads stay untouchable, exactly as today. This matches the D7.5 rule inplugins/source-control/reference/review-discipline.md, which is the canonical author-conditional policy.--thread-idper call, with the existing--expected-comment-countand--expected-last-updatedpins enforced. Bulk resolve stays refused, as in--autonomous.action, plusresolvedCount/eligibleCount. A caller must parseaction, never the exit code (Gotchas, "Exit codes are not per-thread outcomes").Fail-closed cases
Missing, unparseable, or unverifiable evidence refuses the resolve. A SHA absent from the head branch, a tracker id that does not resolve or is already closed, or counter-evidence not found on the thread are all refusals, not warnings. Refusing leaves the thread unresolved, which is the recoverable direction — a suppressed finding is not.
Why not just relax
--autonomousBecause the guard is right about the merging worker. The policy decision relayed on #1614 keeps both properties: worker self-resolution stays outdated-only per the script, and non-outdated bot threads route to this independent dispatch instead. Widening
--autonomouswould delete the anti-self-certification property for the exact actor it was written to constrain.Acceptance criteria
babysit_resolve_thread.py, with the existing modes' behavior unchanged —--autonomousstill refuses a non-outdated thread and still refuses bulk.actionvalues.scripts/engine.test.shgreen (it runs the wrapper-behavior suite that guards these flags).plugins/source-control/bin/source-control-babysit-resolve-threadneeds no new capability — it passes arguments through; confirm it does not need a new refusal rule.skills/babysit-prs/SKILL.md"Guarded mutations" andskills/babysit-prs/reference/safety.md"Guarded Mutation Wrappers". Notescripts/tests/test_skill_contract.pypins the**Merge readiness**paragraph and other SKILL.md prose to the hub — read that test before editing SKILL.md, and note the file is at 499/500 lines (chore(source-control): babysit-prs/SKILL.md also sits 1 line under the 500-line hard cap #1626), so a net-positive edit needs that wall addressed first.Related
babysit-prs/SKILL.md, which blocks documenting this mode in the hub until it is resolved.--autonomouspins; related surface, do not conflate.🤖 Agent-authored (autonomous babysit lane). Evidence measured on #1594 and #1615.