Skip to content

feat(source-control): independent-resolver mode for babysit_resolve_thread.py (evidence-gated, non-outdated bot threads) #1632

Description

@kyle-sexton

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.

Measured across two real babysit runs:

PR Threads resolved Still not isOutdated
#1594 15 6
#1615 5 1

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:
    1. fixed — a fix commit SHA, which must exist on the PR head branch;
    2. deferred — a tracker item id for the filed follow-up, which must exist and be open;
    3. 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". Note scripts/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.

Related

🤖 Agent-authored (autonomous babysit lane). Evidence measured on #1594 and #1615.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

agent-readyFully specified and briefed; eligible for autonomous pickup from the frontier.priority: highSignificant impact, or blocks an imminent release; staff this cycle.

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions