Skip to content

Promote Develop: Merge a Gate-Met Pull Request Through pr_review.py and Add a backlog-burndown Away Mode - #3736

Merged
ptr727 merged 3 commits into
mainfrom
develop
Nov 1, 2026
Merged

ptr727 merged 3 commits into
mainfrom
develop

Conversation

@ptr727

@ptr727 ptr727 commented Nov 1, 2026 •

Copy link
Copy Markdown
Owner

Promotes the changes merged into develop since promotion #3715 to main.

  • #3704: squash-merges a gate-met feature pull request through pr_review.py merge, and the host-safety kit now owns its autoMode entries.

  • #3735 adds an away mode to backlog-burndown:

    • Parallel file-claimed rounds with no maintainer present, run by a lean orchestrator.
    • A group meeting a question parks behind a decision issue instead of asking.
    • Merges go only as far as the develop, main or release scope named at invocation.
    • The end report names each held-back issue's real reason.

    It also adds the attended decision-triage skill, which researches and asks every open question at once, and retires unattended-handoff.

  • #3737 reads a pull request that reads back closed after pr_review.py merge's merge call as a refusal that merged nothing, rather than as an unconfirmed merge. It answers this promotion's first review round.

After the release, the host-safety installer re-run that #3704 needs is still the maintainer's, per its handoff #3734. That handoff's step 3 names an unattended-handoff worker. That skill is retired here, so the verification runs from a backlog-burndown worker instead.

Fixes #3375

🤖 Generated with Claude Code

ptr727 and others added 2 commits October 31, 2026 14:57
…e Kit's autoMode Entries (#3704)

## Why

Agents driving work under `drive-pr`, `backlog-burndown`, and
`unattended-handoff` reach a feature pull request whose gate is met.
Then Claude Code's auto-mode classifier denies the hand-typed `gh pr
merge --squash` as "Merge Without Review". This happens most often
inside a subagent, which sees neither the review nor the maintainer's
go-ahead. The agent then hands the maintainer a command, sometimes while
a prompt is open and the command cannot run.

`pr_review.py cleanup` already shows the fix pattern. It runs its checks
in code under the installer's `pr_review.py` allow rule, and auto mode
completes it. This change gives the develop merge the same shape.
Authorization to merge stays with the skills. The code guarantees that
the merge is into `develop`, at the gated head, inside the registry
scope, and with the Merge Gate's mechanically readable items met. A
develop -> main promotion merge is unchanged and stays the maintainer's.

## What Changes

- **`pr_review.py merge NUMBER --repo OWNER/NAME [--cleanup --checkout
DIR]`** (new `scripts/pr_merge.py`) merges only when all of these hold:
  - `wait`'s gate exits 0, with no review request.
- A fresh read of the digest's field tuples shows coverage, zero open
threads, every no-thread count zero, and `mergeStateStatus` CLEAN. A
missing or unreadable count fails closed.
- The base is `develop`, the head is neither a fork nor a permanent or
protected branch, and the target is inside the registry scope.
- The merge is pinned with `--match-head-commit` and confirmed by
reading it back. A refused merge (exit 1) is told apart from an
unconfirmed one (exit 2).
  - It shares the target query and reader with `pr_cleanup.py`.
- **Skills.**
- `drive-pr` step 4, `backlog-burndown`, and `unattended-handoff` route
develop merges through `merge`.
  - Clearable refusals go back to the review loop.
  - `MERGE_NOT_CONFIRMED` is read back before anything is reported.
  - A harness refusal of the call itself follows the refused-write rule.
- **GOVERNANCE "Repository Boundaries and Write Safety".** The
refused-write rule now names a gate-checked subcommand the maintainer's
installer allows as a route sanctioned in advance. It is never reached
for after a hand-typed form of the same write was refused.
- **Host-safety installer (Claude Code).**
- The kit now owns tagged (`[agent-safety] `) entries in `settings.json`
`autoMode`.
- It never removes, reorders, or rewrites an untagged entry, so each
host's own rules survive.
- It keeps `$defaults` in any list it creates, and its entries join the
stamp digest so `--report` shows them stale.
- Until now `autoMode` was unmanaged. Claude Code reads it only from
user or managed settings, so one repository's content had drifted in
host-wide.

## Local Review Findings and Their Outcomes

Each push carried a recorded local strict pass, with up to two edit
rounds per push. Every finding those passes raised was either fixed in a
later commit on this branch or has one of the outcomes below.

- **Deferred to #3724** (`merge` prints its own next step, so the skills
stop listing status lines): a `CLOSED` read-back route, a merge landing
during the gate's own read, step 3 against step 4 on reporting a peer
merge, and the `BEHIND`/`DIRTY`/`UNKNOWN` clearing routes.
- **Deferred to #3718** (an acknowledgment record): `merge` stays strict
on answered earlier-round findings and noted output, per the
maintainer's decision.
- **Deferred, design reuse:** `ready()` repeats `cleanup_check`'s
refusal list. `auto_mode_misshape` and `report()`'s autoMode walk could
share `main()`'s shape table and `tagged_auto_mode`. `read_settings`
sits beside `registration_problems`. The `--repo` parse is a third copy,
and the other two copies predate this branch.
- **Declined:** a `__main__` guard in `pr_merge.py`, since
`test_cli_usage` then requires a full entry point and the module is
reached only through `pr_review.py merge`. The existing allow rule
approving `merge` before the installer re-runs, since the maintainer
installed it, merging is the maintainer's act, and `--report` reads
stale until the re-run. The non-string guard in `tagged()`, which an
earlier round asked for. The attested pass's own finding count, which is
a PR-body finding the driver already disposes of.
- **Open premise, settled by this PR's own merge:** whether auto mode
keeps the `Bash(uv run --no-project --script scripts/pr_review.py:*)`
allow rule. This pull request is merged with `pr_review.py merge`, which
is the live test.

## Verification

- `tests/`: `test_pr_merge test_pr_review test_pr_review_size
test_pr_cleanup test_cli_usage test_build_dist` pass, 790 tests.
- `host-setup/agent-safety/claude/test_install.py` passes, 182 tests.
- ruff 0.17.3 format and check, mypy, `build_dist.py --check`, and the
prose gate (`--allow-comments`, the `comments` label) all pass.
- Not exercised live yet: `merge` against a real pull request, and a
classifier run with the new `autoMode` entries. Both follow the release
and the installer run.

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

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…3735)

## Why

An unattended run often ended saying it needed the maintainer when
nothing did. The picker had a single reply, `NONE`, for every kind of
skip, and the run's final message routed that to the attended session as
if a decision were waiting. Its exclusions stacked on each other:
attended tracks, issues any handoff names, burndown claims, and an
all-or-nothing "outcome determined" test. It also ran one worker per
round. `backlog-burndown`, which works parallel file-claimed groups,
proved more effective at running unattended.

## What

- **`backlog-burndown` gains an away mode.**
  - It keeps the same rounds and the same file claims.
- A lean orchestrator holds only what a planner subagent and the workers
return.
- A group meeting a question parks behind a `decision` issue instead of
asking. The park is recorded in its claim comment, and the decision is
named on every pull request it blocks.
- Merges go only as far as the `develop`, `main` or `release` scope
named at invocation.
- The end report names every held-back issue under the reason that
actually holds it back, and counts only real questions as needing the
maintainer.
- It brings over the parts of the retired loop it still needs: the
transient dispatch retry, the one-run-per-repository rule, the
write-access comment filter, the blocked-promotion hold and the standing
promotion grant.
- Ranking now also holds back an issue already fixed on develop until a
promotion closes it. This applies to attended runs as well.
- **New `decision-triage` skill (attended).**
- Classifiers sort the open issues into fixable alone, question, split,
or held for another reason.
- Researchers return a recommended answer per question, and each
question is filed as a `decision` issue.
- The whole queue is then put to the maintainer at once, and the answers
are applied.
- It owns the "Needs the Maintainer" test, which `backlog-burndown`
carries as a generated include. It carries `GOVERNANCE.md`
"Communicating with the User" as an include rather than paraphrasing it.
- **`unattended-handoff` is retired.** References in `AGENTS.md`,
`session-handoff`, `merge-and-release`, `pr-review-conduct`,
`local-strict-review` and `repo-config/README.md` are swept. Open
`auto-*` lanes an earlier loop left are picked up by the away mode.

Closes on promotion: #3375

## Local review

All `local-strict-review` passes ran at the `frontier` tier, over the
whole branch diff each time.

- Before the first push, three passes raised 15, then 3, then 1
introduced findings. The first two rounds fixed theirs in 62e8ce6 and
cb52db2.
- The 1 left open at the first push was the promotion hold keyed on any
claim release. It is fixed in 0990687.
- The follow-up passes raised two more on that fix: a no-merge answer
holding the promotion forever, and a dropped answer leaving its issue
re-ranked. They are fixed in 706460d and 24603cc.
- The pass on 24603cc raised nothing.
- Copilot's round on cb52db2 opened 3 threads. One was already fixed.
Two were real: outsider issue text steering an away run, and
operational-model direct pushes missing from the fixed set. They are
fixed in af29ce3.
- Passes on that fix raised 4, then 1. They are fixed in 6f0dac6 and
c42e20e. The pass on c42e20e raised nothing.

### Open introduced findings

None.

## Related open issues the retirement may overtake

None is closed here, since closing on judgment is the maintainer's call.
Each targets the retired skill:
- #1853 is partly covered by "Trusting a Comment".
- #2342 is covered by the away-mode review-handback step.
- #2378: the picker it names no longer exists.
- #2183: lessons are filed as `decision` issues.
- #2433: the agent-safety rule is now in "Needs the Maintainer".

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

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Copilot AI lite review requested due to automatic review settings November 1, 2026 05:01
@coderabbitai

coderabbitai Bot commented Nov 1, 2026 •

Copy link
Copy Markdown

Important

  • Trigger review

This repository does not receive automatic reviews because it has fewer than 10 stars.

Run configuration
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

Share

@codecov

codecov Bot commented Nov 1, 2026 •

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 98.46939% with 3 lines in your changes missing coverage. Please review.
✅ Project coverage is 82.31%. Comparing base (5ad2acf) to head (4b7c352).
⚠️ Report is 455 commits behind head on main.

Files with missing lines Patch % Lines
scripts/pr_merge.py 98.70% 2 Missing ⚠️
scripts/pr_cleanup.py 95.45% 1 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #3736      +/-   ##
==========================================
+ Coverage   82.02%   82.31%   +0.28%     
==========================================
  Files          27       28       +1     
  Lines        9275     9438     +163     
==========================================
+ Hits         7608     7769     +161     
- Misses       1667     1669       +2     
Flag Coverage Δ
python-3.13 82.31% <98.46%> (+0.28%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🟡 Changes recommended

Approval is blocked by an unresolved critical race that can merge into the wrong base branch, plus a read-back handling issue that misclassifies definitively refused merges.

2 open issues
What changed in this PR

This promotion moves changes from develop toward main, adding gate-checked feature merging, host-safety autoMode ownership, and backlog-burndown away mode with attended decision triage while retiring unattended-handoff.

Changes:

  • Adds pr_review.py merge and pr_merge.py with gate validation, pinned squash merges, confirmation, cleanup integration, documentation, and tests.
  • Adds host-safety ownership, preservation, validation, and reporting for tagged Claude Code autoMode entries.
  • Replaces unattended-handoff with backlog-burndown away mode and decision-triage across canonical and generated skill distributions.

Review findings:

  • scripts/pr_merge.py:90 — [P1] critical, 1 vote: the final merge-target payload is not revalidated, allowing a retargeted pull request to merge into main.
  • scripts/pr_merge.py:167 — [P1] moderate, 1 vote: requested cleanup is skipped when the pull request was already merged by a peer.
  • scripts/pr_merge.py:217 — [P2] moderate, 2 votes: a closed read-back after a failed merge is reported ambiguously instead of as a definitive refusal.

This pull request requires changes.

File Summary
tests/​test_pr_review.py Tests merge command parsing, gate polling, and non-requesting wait behavior.
tests/​test_pr_review_size.py Adds the size ceiling for pr_merge.py.
scripts/​README.md Documents the merge command and its contract.
scripts/​pr_review.py Adds merge forwarding, merge flags, and non-requesting gate waits.
scripts/​pr_review_digest.py Exposes digest fields and standardized count labels for merge validation.
scripts/​pr_merge.py Implements gate-checked squash merging, read-back confirmation, and optional cleanup.
scripts/​pr_cleanup.py Shares pull request target reads and permanent-branch checks.
repo-config/​README.md Updates parked-handoff terminology for away mode.
host-setup/​agent-safety/​claude/​test_install.py Tests tagged autoMode installation, reporting, preservation, and malformed settings.
host-setup/​agent-safety/​claude/​README.md Documents managed permissions and autoMode behavior.
host-setup/​agent-safety/​claude/​install.py Manages tagged autoMode entries and includes them in digests.
GOVERNANCE.md Defines the sanctioned gate-checked merge command shape.
AGENTS.md Routes unattended backlog work through away mode and decision triage.
.github/​skills/​session-handoff/​SKILL.md Replaces retired unattended-handoff handling with away-mode behavior.
.github/​skills/​pr-review-conduct/​SKILL.md Documents merge forwarding and away-mode promotion evidence.
.github/​skills/​merge-and-release/​SKILL.md Recognizes away-mode promotion grants.
.github/​skills/​local-strict-review/​SKILL.md Removes retired unattended-handoff references.
.github/​skills/​drive-pr/​SKILL.md Routes feature merges through pr_review.py merge.
.github/​skills/​decision-triage/​SKILL.md Adds the attended decision-triage procedure.
.github/​skills/​backlog-burndown/​SKILL.md Adds away-mode rounds, parking, promotion, and reporting rules.
.claude-plugin/​fleet-skills/​skills/​session-handoff/​SKILL.md Generated Claude Skill copy of the session-handoff updates.
.claude-plugin/​fleet-skills/​skills/​pr-review-conduct/​SKILL.md Generated Claude Skill copy of the review-conduct updates.
.claude-plugin/​fleet-skills/​skills/​merge-and-release/​SKILL.md Generated Claude Skill copy of the release updates.
.claude-plugin/​fleet-skills/​skills/​local-strict-review/​SKILL.md Generated Claude Skill copy of the review updates.
.claude-plugin/​fleet-skills/​skills/​drive-pr/​SKILL.md Generated Claude Skill copy of the merge-routing updates.
.claude-plugin/​fleet-skills/​skills/​decision-triage/​SKILL.md Generated Claude Skill copy of the new triage skill.
.claude-plugin/​fleet-skills/​.source-digests/​unattended-handoff Removes the retired skill digest.
.claude-plugin/​fleet-skills/​.source-digests/​session-handoff Updates the generated session-handoff digest.
.claude-plugin/​fleet-skills/​.source-digests/​pr-review-conduct Updates the generated review-conduct digest.
.claude-plugin/​fleet-skills/​.source-digests/​merge-and-release Updates the generated release digest.
.claude-plugin/​fleet-skills/​.source-digests/​local-strict-review Updates the generated review digest.
.claude-plugin/​fleet-skills/​.source-digests/​drive-pr Updates the generated drive-pr digest.
.claude-plugin/​fleet-skills/​.source-digests/​decision-triage Adds the new triage digest.
.claude-plugin/​fleet-skills/​.source-digests/​backlog-burndown Updates the generated backlog digest.
.claude-plugin/​fleet-skills/​.claude-plugin/​plugin.json Registers decision-triage and removes unattended-handoff.
.agents/​skills/​session-handoff/​SKILL.md Updates the canonical session-handoff skill.
.agents/​skills/​pr-review-conduct/​SKILL.md Updates the canonical review-conduct skill.
.agents/​skills/​merge-and-release/​SKILL.md Updates the canonical release skill.
.agents/​skills/​local-strict-review/​SKILL.md Removes the retired unattended-handoff reference.
.agents/​skills/​drive-pr/​SKILL.md Routes feature merges through the gate-checked command.
.agents/​skills/​decision-triage/​SKILL.md Adds the canonical decision-triage skill.
.agents/​skills/​backlog-burndown/​SKILL.md Adds canonical away-mode procedures.

Review effort: Lite

Comment thread scripts/pr_merge.py Resolved
Comment thread scripts/pr_merge.py Resolved
## Why

Copilot's round on promotion #3736 found that `pr_review.py merge`
reports a pull request read back `CLOSED` after the merge call as
`MERGE_NOT_CONFIRMED`, exit `2`, "may still land on the server". A
closed pull request cannot merge later, so a driver following `drive-pr`
step 4 was sent to read back a merge that cannot have happened.

## What

- A read-back of `CLOSED` is `status=REFUSED`, exit `1`, nothing merged,
whatever `gh` answered. The `scripts/README.md` contract says so.
- It shares the branch an `OPEN` refusal already takes, so `pr_merge.py`
stays under its size ceiling. The first push's separate branch tripped
`test_pr_review_size.py` in CI. The line now names the state it read and
what `gh` answered, or `nothing` where the call never finished.
- A test covers `gh` exit `0`, exit `1`, and a call that never finished.
It fails on the previous code, which returned exit `2` for each.

The same round's other two findings are answered on #3736 rather than
changed here:
- **Retargeted base:** `ready()` already re-checks the base on the read
after the gate, at `scripts/pr_merge.py` in `merge()`.
- **Cleanup skipped on `PEER_MERGED`:** `scripts/README.md`
"`pr_merge.py`" and `drive-pr` step 4 state this as the contract.

## Local review

Two `local-strict-review` passes ran at the `frontier` tier.
- The first raised 3 findings. A redundant base check I had added was
dropped, together with its README claim. The closed read-back test's
subtests now read only their own output.
- The second raised nothing.
- After the CI ceiling failure, the third raised 1: the shared branch
named a `gh` refusal that had not happened. The fourth raised nothing.
- The full suite of 3249 tests passes locally.

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

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
@ptr727

ptr727 commented Nov 1, 2026

Copy link
Copy Markdown
Owner Author

Answering the round on 06e45c4's findings list item with no thread (the round lists 3 findings, 2 with threads, both answered there).

scripts/pr_merge.py:167 — [P1] moderate, 1 vote: requested cleanup is skipped when the pull request was already merged by a peer.

No change needed. This is the documented contract. scripts/README.md "pr_merge.py" states: "A pull request read back merged at a head other than the gated one prints status=PEER_MERGED at exit 1. This run merged nothing, so it runs no cleanup itself." Its exit text adds that such a pull request "still owes cleanup". drive-pr step 4 routes it: "So does a status=PEER_MERGED line. For either, run the cleanup command below. Report each of these merges as one this run cannot vouch for, since the head that merged may be one the gate never read." Cleanup is left to the driver deliberately, since the merged head is one this run's gate never read.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🔵 Needs a closer look

The change introduces autonomous GitHub merges and host-wide Claude Code autoMode behavior, but the supplied tests do not verify live classifier, merge, or recovery operations, leaving critical production boundaries for maintainer validation.

No open issues 🎉

2 resolved since last review

Review effort: Lite

@ptr727

ptr727 commented Nov 1, 2026

Copy link
Copy Markdown
Owner Author

Answering the round on 4b7c352, whose verdict is needs a closer look with no counted finding (0 open, 2 resolved since the last round). Its headline is the one item it flags:

The change introduces autonomous GitHub merges and host-wide Claude Code autoMode behavior, but the supplied tests do not verify live classifier, merge, or recovery operations, leaving critical production boundaries for maintainer validation.

No change needed here, and the live validation it names is on record or tracked.

Merge Gate item 2, promotion case. coverage=none on this head, because the file table names 42 of 47 paths. Every change this promotion carries had full coverage on its own feature -> develop pull request, met at its merged head by an attested local pass (coverage=local): #3704, #3735 and #3737. Every review thread here is resolved.

@ptr727
ptr727 merged commit e5152b6 into main Nov 1, 2026
14 checks passed
ptr727 added a commit that referenced this pull request Nov 1, 2026
…Fetch Before Its History Check (#3762)

Promotes the changes merged into `develop` since promotion #3736 to
`main`.

- [#3760](#3760) improves
`decision-triage` from its first full run:
- Researchers take topic clusters, and step 3 states the researcher
count and a token estimate before dispatching.
- The researcher brief reads every comment newest last, says its output
is posted publicly, and bounds writes to a scratch directory of its own.
- The classifier can return `OVERTAKEN` after a cheap history check, and
a new step offers to close overtaken issues.
  - Coupled questions are asked in one prompt.
- A new `deferred` label marks a "not yet" answer, which the classifier
and `backlog-burndown` skip and report by count.
- A recorded maintainer approval lets an attended run implement an
agent-safety edit, with the merge still waiting on a per-PR go-ahead.
- [#3763](#3763) has the
triaging session fetch `main` and `develop` once before dispatching the
history check and the researchers, and stop if the fetch fails. The
subagents read the full remote-tracking refs and fetch nothing. It
answers this promotion's first review round.

After the release, the maintainer runs `repo-config/configure.sh apply
ptr727/ProjectTemplate` to create the `deferred` label, then labels
#781, #1464 and #2174 `deferred`, per #3760's "After Merge".

Deferred review findings: #3761 (an attended `backlog-burndown` run
applies no agent-safety hold to an unapproved issue).

Fixes #3759

🤖 Generated with [Claude Code](https://claude.com/claude-code)
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.

Let Unattended Handoff Work Unblocked Attended Lanes and Name Why It Skips

2 participants