Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion .agents/skills/drive-pr/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,11 @@ promotion PR once the fix lands, is the early exit this skill exists to prevent.
obstacle to route around, and that
skill's refusal table says what each refusal means and what clears it.
3. Drive pr-review-conduct's review loop on it to the Merge Gate, disposing of every finding per
"Disposing of Every Finding" below.
"Disposing of Every Finding" below. Request no Copilot round on this feature PR. Its `wait` reads
step 2's attestation as `coverage=local` and requests none, unless a partial round or a truncated
review history is on record. Exit `49` means the head carries no attestation, so run and record a
pass over it, push, and attest. A promotion re-reviews content its feature PRs already carried,
so the hosted review runs once per change, at the promotion in step 6.
Comment thread
ptr727 marked this conversation as resolved.
Resolved
4. Merge the feature PR into
develop, `gh pr merge <number> --squash --repo <owner>/<repo>`. Never `--delete-branch` on this
call, it is run from inside the task's own worktree per step 1, where the feature branch is
Expand Down
2 changes: 1 addition & 1 deletion .claude-plugin/fleet-skills/.source-digests/drive-pr
Original file line number Diff line number Diff line change
@@ -1 +1 @@
99691b995f5e1af8
1094ede7aaef64e9
6 changes: 5 additions & 1 deletion .claude-plugin/fleet-skills/skills/drive-pr/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,11 @@ promotion PR once the fix lands, is the early exit this skill exists to prevent.
obstacle to route around, and that
skill's refusal table says what each refusal means and what clears it.
3. Drive pr-review-conduct's review loop on it to the Merge Gate, disposing of every finding per
"Disposing of Every Finding" below.
"Disposing of Every Finding" below. Request no Copilot round on this feature PR. Its `wait` reads
step 2's attestation as `coverage=local` and requests none, unless a partial round or a truncated
review history is on record. Exit `49` means the head carries no attestation, so run and record a
pass over it, push, and attest. A promotion re-reviews content its feature PRs already carried,
so the hosted review runs once per change, at the promotion in step 6.
4. Merge the feature PR into
develop, `gh pr merge <number> --squash --repo <owner>/<repo>`. Never `--delete-branch` on this
call, it is run from inside the task's own worktree per step 1, where the feature branch is
Expand Down
2 changes: 1 addition & 1 deletion .github/copilot-instructions.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ For every review:

The review automation is `scripts/pr_review.py`, run from a hub checkout. Use its `status`, `wait`, `attest`, `comment`, and `reply --resolve` commands instead of reconstructing GraphQL queries or copying review identifiers by hand. Use `comment` for a suppressed-finding answer in the pull request conversation. Its status gate verifies the current head, diff coverage, output shape, inline threads, body-only findings, and required checks.

A formal review with no findings is complete only when it covers the current head and full diff coverage of the change set that head has is stated, or read from a file table as below. The round covering the head states it, or the newest round that states it at all does and the pull request changes the same set of files at both commits, which is the only condition under which a statement carries forward. Only that newest round is consulted, so an older round whose change set does match carries nothing. Where no statement reaches the head either way, a round covering it whose own file table names exactly the changed files covers it, which is the reading a round at Balanced review effort gives, and a statement that reaches the head, stated on it or carried to it, wins over that table. Where no round covering the head carries a table of its own, the newest round that does, its table naming exactly the changed files, stands in under the bound a statement carries under, the pull request changing the same set of files at both commits. The table stands in only where no Copilot round on the pull request, on any commit, states or appears to state partial coverage, so a pull request that ever had a partial round goes to the maintainer. A round reporting partial coverage of the diff blocks the merge, and so does a refusal, a coverage statement that does not reach this head, meaning absent from every round or carried by none because the change set moved or could not be compared, with no file table or attested local pass standing in for it, an unrecognized output shape, an unresolved thread, or a body-only finding. On a pull request into a branch other than the default, a fix push after Copilot's first round is covered by an attested local pass rather than another Copilot round, `coverage=local` in the digest, while a pull request into the default branch, a promotion among them, always carries a Copilot round on its head. Re-run the loop after every fix push. Never infer review completion from `mergeStateStatus: CLEAN`.
A formal review with no findings is complete only when it covers the current head and full diff coverage of the change set that head has is stated, or read from a file table as below. The round covering the head states it, or the newest round that states it at all does and the pull request changes the same set of files at both commits, which is the only condition under which a statement carries forward. Only that newest round is consulted, so an older round whose change set does match carries nothing. Where no statement reaches the head either way, a round covering it whose own file table names exactly the changed files covers it, which is the reading a round at Balanced review effort gives, and a statement that reaches the head, stated on it or carried to it, wins over that table. Where no round covering the head carries a table of its own, the newest round that does, its table naming exactly the changed files, stands in under the bound a statement carries under, the pull request changing the same set of files at both commits. The table stands in only where no Copilot round on the pull request, on any commit, states or appears to state partial coverage, so a pull request that ever had a partial round goes to the maintainer. A round reporting partial coverage of the diff blocks the merge, and so does a refusal, a coverage statement that does not reach this head, meaning absent from every round or carried by none because the change set moved or could not be compared, with no file table or attested local pass standing in for it, an unrecognized output shape, an unresolved thread, or a body-only finding. On a pull request into a branch other than the default, an attested local pass covers the head, `coverage=local` in the digest. A partial round, a truncated review history, or a refusal on the head still blocks that reading. A pull request into the default branch, a promotion among them, always carries a Copilot round on its head. Re-run the loop after every fix push. Never infer review completion from `mergeStateStatus: CLEAN`.

Review effort is user-controlled. The automation observes `Lite`, `Balanced`, or `Max`, including an inherited `Default (<level>)`, and never selects or changes the setting. Effort does not determine coverage or completion. A request can complete without a `copilot_work_started` event, so absence of that event is not a stalled-review verdict. When `wait` returns `PENDING` with `requested=yes`, report the state and rerun `wait` for another bounded interval by default, reading that field as acceptance of the request rather than as delivery of a round. Do not clear the request on that first timeout, because it may still be active. Where a second bounded wait times out as well, read the pending set, clear it only where no human or team reviewer is requested alongside the bot, and rerun `wait`, which then has nothing outstanding to defer to and requests afresh, or polls and says so on its own auto-request line where it finds no reviewer node id to request with. The clear replaces that set rather than adding to it and nothing restores a request it drops, so a stall on a pull request that has a human or team reviewer requested goes to the maintainer, and so does one still pending after the wait that follows a clear. That clear is a recovery step the script does not implement, and `docs/pr-reviewer-reference.md`, in the hub checkout the script is run from, carries it. This recovery replaces only the review request and never changes the effort setting. A `wait` ending `REQUEST_NOT_RECORDED`, exit 48, is a different state and takes none of this recovery: the request returned success and left neither a pending reviewer nor a review-request event, which is how an exhausted Copilot allowance has shown itself, so clearing and requesting again does not clear it and it goes to the maintainer.

Expand Down
6 changes: 5 additions & 1 deletion .github/skills/drive-pr/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,11 @@ promotion PR once the fix lands, is the early exit this skill exists to prevent.
obstacle to route around, and that
skill's refusal table says what each refusal means and what clears it.
3. Drive pr-review-conduct's review loop on it to the Merge Gate, disposing of every finding per
"Disposing of Every Finding" below.
"Disposing of Every Finding" below. Request no Copilot round on this feature PR. Its `wait` reads
step 2's attestation as `coverage=local` and requests none, unless a partial round or a truncated
review history is on record. Exit `49` means the head carries no attestation, so run and record a
pass over it, push, and attest. A promotion re-reviews content its feature PRs already carried,
so the hosted review runs once per change, at the promotion in step 6.
4. Merge the feature PR into
develop, `gh pr merge <number> --squash --repo <owner>/<repo>`. Never `--delete-branch` on this
call, it is run from inside the task's own worktree per step 1, where the feature branch is
Expand Down
2 changes: 1 addition & 1 deletion TODO.md
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,7 @@ One pull request deciding what the `hugo` type says about a theme, which it decl
- **Settled** - Three details the intake predicted are wrong against what Blog runs, so planning from the prediction encodes requirements the repo does not meet: the theme is vendored with no recorded upstream ref rather than a Dependabot-tracked submodule, the generator is pinned by version and hash rather than run at latest, and the deploy is a separate dispatch rather than a tag cut last after the live check.
- **Settled** - What held is that the deploy is a publish, the type is named for the generator with the generic checks phrased so they do not name it, and the URL parity gate asserting a floor on the golden list length before comparing is the check of record.
- **Settled** - Promoting the generator-agnostic `hugo` checks to a shared type when a second generator arrives is a registry edit by construction, per [`spec/type-model.md`][type-model] "Generators".
- **Settled** - The `copilot_code_review` rule in both ruleset payloads gates no merge today, because gated Copilot review is an invite-only beta, which deserves a sentence near the merge gate so no repo reads the rule as the enforcement and relaxes the manual discipline holding the line.
- **Settled** - The `copilot_code_review` rule gates no merge today, because gated Copilot review is an invite-only beta, which deserves a sentence near the merge gate so no repo reads the rule as the enforcement and relaxes the manual discipline holding the line.

### Locally Required Secrets

Expand Down
2 changes: 1 addition & 1 deletion docs/pr-reviewer-reference.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ The consequence a review loop actually needs: **a private repository has Copilot

The repository already has first-class status, wait, comment, reply, resolution, coverage, and output-shape handling in `scripts/pr_review.py`.

A review is requested by that script rather than by hand. `wait` requests one on the current head where nothing it reads already settles the round, nothing is outstanding, and the head is not a fix push an attested local pass covers, so an accepted request that is never picked up is a state it cannot clear for itself. Clearing the request set is what leaves the next `wait` nothing to defer to. A `wait` ending `REQUEST_NOT_RECORDED`, exit 48, is a different state and takes none of this recovery: the request returned success and left neither a pending reviewer nor a review-request event, which is how an exhausted Copilot allowance has shown itself, so clearing and requesting again does not clear it and it goes to the maintainer. Nothing in the commands below is the script, so none of its scope refusals reaches them, and the owner in the target is checked by whoever runs them:
A review is requested by that script rather than by hand. `wait` requests one on the current head where nothing it reads already settles the round and nothing is outstanding. An accepted request that is never picked up is therefore a state it cannot clear for itself. Into a branch other than the default, `wait` requests none unless a partial round or a truncated review history is on record. An attested local pass covers that head instead. Clearing the request set is what leaves the next `wait` nothing to defer to. A `wait` ending `REQUEST_NOT_RECORDED`, exit 48, is a different state and takes none of this recovery: the request returned success and left neither a pending reviewer nor a review-request event, which is how an exhausted Copilot allowance has shown itself, so clearing and requesting again does not clear it and it goes to the maintainer. Nothing in the commands below is the script, so none of its scope refusals reaches them, and the owner in the target is checked by whoever runs them:

```sh
PR_NODE=$(gh pr view "<N>" --repo "<owner>/<repo>" --json id --jq '.id')
Expand Down
4 changes: 2 additions & 2 deletions repo-config/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,9 @@ Hub-only repository and branch configuration held as committed files, kept out o
Two workflow models share `main.json` but differ on `develop` (registry `workflowModel`, default `release`):

- **`release`** (`develop.json`): `develop` requires squash merges with linear history and a PR, the feature-branch pipeline.
- **`operational`** (`operational/develop.json`): `develop` takes **direct signed pushes**, carrying only `deletion`, `non_fast_forward`, and `required_signatures`, with no PR, no status-check, and no Copilot review rule. CI runs on the push as advisory feedback. Read the dropped rules as an allowance rather than a prohibition, since a PR into `develop` remains legal and the lint workflow triggers on it, with its result reported and not required (a required check here would gate the direct push as well). This is for live-service config repos that edit `develop` directly and promote a known-good snapshot to `main` via an occasional PR (see [GOVERNANCE.md "Branching Model"][governance-branching-model]).
- **`operational`** (`operational/develop.json`): `develop` takes **direct signed pushes**, carrying only `deletion`, `non_fast_forward`, and `required_signatures`, with no PR and no status-check rule. CI runs on the push as advisory feedback. Read the dropped rules as an allowance rather than a prohibition, since a PR into `develop` remains legal and the lint workflow triggers on it, with its result reported and not required (a required check here would gate the direct push as well). This is for live-service config repos that edit `develop` directly and promote a known-good snapshot to `main` via an occasional PR (see [GOVERNANCE.md "Branching Model"][governance-branching-model]).

`main` (both models) requires merge-commit merges (no linear-history rule), signed commits, a passing `Check pull request workflow status job`, resolved review threads, and Copilot review, and blocks force-pushes and deletion, so a `develop -> main` promotion is always gated even when `develop` takes direct commits. The Copilot review rule in `develop.json` and `main.json` reviews a pull request when it opens and not on each push or while it is a draft, since a later round is requested deliberately per [GOVERNANCE.md "PR Review Etiquette"][governance-pr-review-etiquette] rather than spent on every push. Every ruleset intentionally leaves "Require branches to be up to date before merging" **off**, per [GOVERNANCE.md "Branching Model"][governance-branching-model].
`main` (both models) requires merge-commit merges (no linear-history rule), signed commits, a passing `Check pull request workflow status job`, resolved review threads, and Copilot review, and blocks force-pushes and deletion, so a `develop -> main` promotion is always gated even when `develop` takes direct commits. The Copilot review rule sits in `main.json` alone. A promotion re-reviews content its feature pull requests already carried, so the hosted review runs once per change, at the `develop -> main` promotion. A feature pull request carries the attested local pass and CI instead (Decision ptr727, 2026-10-23, on #3391, item 4). That rule reviews a pull request when it opens, not on each push or in draft. A later round is requested deliberately per [GOVERNANCE.md "PR Review Etiquette"][governance-pr-review-etiquette] rather than spent on every push. Every ruleset intentionally leaves "Require branches to be up to date before merging" **off**, per [GOVERNANCE.md "Branching Model"][governance-branching-model].

The result is **exactly two rulesets named `develop` and `main`**, and the names are load-bearing (`GOVERNANCE.md` and the workflows reference them). Only the `develop` *content* varies by model. The required check binds by name and only turns green after the repo's PR workflow runs once.

Expand Down
7 changes: 0 additions & 7 deletions repo-config/develop.json
Original file line number Diff line number Diff line change
Expand Up @@ -49,13 +49,6 @@
"strict_required_status_checks_policy": false
},
"type": "required_status_checks"
},
{
"parameters": {
"review_draft_pull_requests": false,
"review_on_push": false
},
"type": "copilot_code_review"
}
],
"target": "branch"
Expand Down
Loading
Loading