Visitar URL original
Promote Develop: Gate the Required Check Name, Answer Unthreaded Findings With an Acknowledgment Record, and Mark Direct-Push Closures by ptr727 · Pull Request #3797 · ptr727/ProjectTemplate · GitHub
Skip to content

Promote Develop: Gate the Required Check Name, Answer Unthreaded Findings With an Acknowledgment Record, and Mark Direct-Push Closures - #3797

Merged
ptr727 merged 6 commits into
mainfrom
develop
Nov 2, 2026
Merged

ptr727 merged 6 commits into
mainfrom
develop

Conversation

@ptr727

@ptr727 ptr727 commented Nov 2, 2026 •

Copy link
Copy Markdown
Owner

Promotes the changes merged into develop since promotion #3778 to main, the second round of a backlog burndown.

  • #3784 has the write guard read a push option attached to -o as that option's value, as git does, rather than as more short flags. It also walks a short-option cluster carrying a digit, such as -4f.
  • #3796 fixes five pull-request-gate and release-task defects:
    • Each aggregator's name: gives the ruleset-bound required check name only on a pull_request run, so a dispatch run cannot mask it. spec/audit.py check_interface reads that expression.
    • The stubs drop the pull_request base-branch filter, and "Adopting the Gates" states the task's preconditions.
    • A re-run release takes the Latest flag only where its version sorts at or above the current Latest release.
    • The lint job runs its Python steps under the first python-versions entry.
    • dotnet format and dotnet test run per tracked workspace where the root holds no solution or project.
  • #3793 has pr_review.py merge read an acknowledgment record, posted with comment --ack KEY=OUTCOME, for findings no thread carries and for a verdict naming no counted finding. Every merge run ends on one next= line the skills follow, and the digest reads the excluded-files refusal as refusal=EXCLUDED.
  • #3792 has a direct push to develop on an operational-model repository name the issues it fixes in its commit message's on-promotion line. backlog-burndown assembles the promotion body from the pull request that merged each commit, falling back to a direct push's own message. Its claim lookup matches only claim comments, and git-commit-conventions names the abandon-discard exception.
  • #3798 has has_prose read an inline token's text children outside code, so an inline-code span alone no longer counts as prose. It answers this promotion's first review round.
  • #3799 has check_interface read each job by its key on a workflow a non-PR event triggers. A job whose off-PR name is the required check gets its own DRIFT. It answers this promotion's first review round.

Downstream effect

From #3796: once this reaches main, every downstream test-pull-request.yml whose aggregator carries a literal name: shows check_interface DRIFT. It shows that until its next resync. A resync clears it by replacing the aggregator's name: line with the expression. No ruleset changes, since the context: a pull request run posts is unchanged.

Fixes #3720
Fixes #3334
Fixes #1516
Fixes #3487
Fixes #1485
Fixes #1938
Fixes #3718
Fixes #3724
Fixes #3777
Fixes #3773
Fixes #1276
Fixes #1307
Fixes #3780

🤖 Generated with Claude Code

ptr727 and others added 4 commits November 1, 2026 15:22
)

## Summary

`_push_targets` in the write guard read an all-letter short-option word
as a cluster of flags anywhere in it, so a push option attached to `-o`
was read as more flags. When the word ended in `o`, the next word was
also consumed as the option's value. So `git push -ofoo origin develop`
read `f` as `--force`, consumed `origin`, and judged the push as a bare
push of the current branch. It was allowed while git pushed `develop`.

The walk now stops at the first `o`, as git does. The rest of the word
is the push option's value, and the next word is consumed only when
nothing follows the `o`. A cluster carrying a digit, such as `-4f` (`-4`
is git push's IPv4 option), is walked too, where before it was skipped
and its `f` missed. A word whose flag part carries a run-time character,
such as `-$o`, consumes no next word, as before.

## Changes

- `host-setup/agent-safety/claude/gh-write-guard.py`: the short-cluster
branch of `_push_targets`, plus self-test rows for `-ofoo`, `-o foo`,
`-fofoo`, `-ofoo` to a `main` whose rules allow an update, `-4f`, and
`-uo ci.skip`, a cluster ending in `o` that takes the next word as its
value.
- `host-setup/agent-safety/README.md`: requirement 4 states how a
short-option cluster and its push option are read.

## Verification

- `gh-write-guard.py --selftest` passes. The same rows run against the
guard from `origin/develop` fail on `-ofoo origin develop`, `-fofoo
origin main`, and `-4f origin main`, each allowed there. A mutant that
never consumes the next word fails the `-uo ci.skip` row.
- `tests/test_guard_size.py` and
`host-setup/agent-safety/claude/test_install.py` pass. The guard is 2208
lines against its 2500 ceiling.

## Pre-existing findings, filed

- `host-setup/agent-safety/claude/gh-write-guard.py` `_skips_hooks`, the
`clean` reader in `_primary_checkout_verdict`, and the `git branch`
rename check, the same cluster-reading class on commit, clean, and
branch, plus the shared-helper consolidation across all three sites
(`pre-existing`, correctness): #3783.

Closes on promotion: #3720

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

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
… Workspaces (#3796)

## Summary

Fixes five pull-request-gate and release-task defects, each the way its
recorded maintainer answer chose.

- **#3334: a dispatch run can mask a pull request's required check.**
Each aggregator's `name:` is now an expression that gives the
ruleset-bound `Check pull request workflow status job` only when
`github.event_name == 'pull_request'`. Any other run reports `Check
workflow status job`. That applies to both catalog stubs, the docs
example, and the hub's own `test-pull-request.yml`. `spec/audit.py`
`check_interface` now accepts that expression as naming the required
check. It reports a literal name in a workflow that any other event also
triggers. WORKFLOW.md D1.5 and `spec/fidelity-model.md` state the rule.
The ruleset `context:` does not change, so no live ruleset needs
editing.
- **#1516: adoption preconditions and the base-branch filter.** The
`pull_request` base-branch filter is gone from both stubs, the docs
example, and the hub's own stub. A retarget fires `edited`, so the
filter left a retargeted stacked pull request with no run. "Adopting the
Gates" now says what the task expects of the repo it gates: a mypy
target (`files`), one type checker with the `validate` hook as the route
to a second, and a `pull_request` trigger for the prose gate. The
operational walkthrough in WORKFLOW.md section 6 and the
`branch.operational.prtriggers` assertion in `spec/project-types.json`
are updated to match.
- **#3487: a re-run release takes the Latest flag.** A new `Decide
latest release step` passes `make_latest`. A main release takes the flag
only where its version sorts at or above the current Latest release's
tag. A prerelease never takes it. A 404 (no Latest release yet) lets the
release take it, and any other read failure fails the step with a named
cause.
- **#1485: the lint job's pinned interpreter.** The `lint` job's Python
steps now run under the first `python-versions` entry. A new `Resolve
lint Python version step` reads it with a named-cause refusal. The Sync,
Lint, Format, and Type check steps each set `UV_PYTHON` to that version,
so a `uv.lock` whose floor is above 3.13 syncs. setup-uv stays at 3.13,
the floor the hub's own uv scripts declare, so the prose and repo gates
still run under it. The pip path's separate `UV_PYTHON` override and its
check are removed, since these four steps now carry the interpreter.
- **#1938: `dotnet format` and `dotnet test` at a root with no
workspace.** Both steps keep the bare call where the root tracks a
`.sln`, `.slnx`, or `.csproj`. Otherwise the style step runs once per
tracked solution, or once per `.csproj` where the tree has no solution.
The test step runs once per tracked `*Tests*.csproj`, the glob its own
`hashFiles` guard uses. It names no solution, because `dotnet test
--project` on a solution or project holding no test project exits 1 ("No
test projects were found"), measured on SDK 10 under the testing
platform with the coverage flags.

Closes on promotion: #3334, #1516, #3487, #1485, #1938

## Verification

- The full unittest suite, `spec/audit.py --selftest`,
`spec/validate.py`, `scripts/build_dist.py --check`, ruff format and
check, and mypy all pass locally.
- `docker_lint.py` passes for actionlint, markdownlint, cspell, and
editorconfig-checker.
- New tests run each changed step's own script against stand-in tools:
- The `make_latest` decision covers newer, older, numeric-order,
refresh, `v`-prefixed, 404, prerelease, and 502 cases.
  - The lint interpreter resolution covers good and malformed values.
- The per-workspace .NET steps cover a root solution, solutions below
the root, and projects with no solution, in real git repos. A check also
confirms the resolver text is the same in both jobs.
  - A test confirms no shipped stub or docs block filters a base branch.
- The `check_interface` cases cover the expression form, a literal name
under a dispatch trigger, the list-form `on:`, an expression that names
the check on every event, and one gated on the wrong event. A test also
confirms every shipped stub meets the carried contract in
`spec/files.json`.
- `dotnet format style <solution|project>` was measured on SDK 10
against a subdirectory-only tree, and so was `dotnet test --project` on
a solution and on a project that hold no test project.

## Downstream effect

**Every downstream `test-pull-request.yml` whose aggregator still has
the literal name will show `check_interface` DRIFT until its next
resync.** The audit now reports a literal name in any stub that
`workflow_dispatch` or `push` also triggers, and that is every adopted
stub today. A resync clears it by replacing the aggregator's `name:`
line with the expression from the snippet. No ruleset changes, since the
`context:` string a pull request run posts is unchanged.

## Introduced findings from the local passes, and their outcomes

- `tests/test_release_guards.py`
`test_lint_runs_under_the_first_named_interpreter`, introduced: Git Bash
collapses the doubled backslash in the jq filter in a `bash -c`
argument. Fixed in f6fd08a, which runs the script from a file.
- `.github/workflows/validate-task.yml` `Run unit tests step`,
introduced: a solution holding no test project fails `dotnet test`.
Fixed in 4ecb00d, which names test projects instead.
- `.github/workflows/validate-task.yml` `Run unit tests step`,
introduced: in a tree with no root workspace, a non-test helper project
whose name contains `Tests` is passed to `dotnet test --project`, which
exits 1. Declined under GOVERNANCE.md "Trust Boundaries and Hardening
Effort". The layout is the maintainer's own and the failure is loud
rather than silent. The step's own `hashFiles('**/*Tests*.csproj')`
guard already treats that name as a test project.
- `spec/audit.py` `check_interface`, introduced: the function re-parsed
the text to read `on:`. Fixed in f6fd08a, which reads the composed
tree.
- `spec/audit.py` `PR_ONLY_NAME`, introduced: only the shipped operand
order is accepted. Declined: the carried snippet's form is what a resync
delivers, and recognizing every boolean rewrite answers no requirement.
f6fd08a makes the DRIFT message name both accepted forms.
- `WORKFLOW.md` D1.6, introduced: the per-workspace `--project` form was
not mentioned. Fixed in f6fd08a and 4ecb00d.
- The lint job's comment and the docs said the whole job runs under the
first interpreter, although only its Python steps do. Fixed in 4ecb00d.
- `.github/workflows/validate-task.yml` `Check C# style step`,
introduced: where the root holds no workspace and the tree tracks a
solution, a `.csproj` no tracked solution references is not
style-checked. Declined: the #1938 maintainer answer chose "once per
tracked .sln/.slnx, else per .csproj", and this is that rule's stated
behavior.

## Pre-existing findings, filed

- `.github/workflows/validate-task.yml` `Check C# style step` and `Run
unit tests step`, correctness: a root holding more than one solution or
project keeps the bare call, which fails with MSB1011. Filed as #3795.

## Out-of-claim findings, filed

- `GOVERNANCE.md:275`, contract drift: the job-name bullet describes the
aggregator name as a literal and does not mention the expression form.
That file is held by another change. Filed as #3790.
- `.github/workflows/validate-task.yml` `Check C# style step` and `Run
unit tests step`, design: the `dotnet_workspaces` resolver is copied
into both jobs. A test asserts the two copies are identical. Moving it
into a composite action is filed as #3791.
- The `python-codestyle` profiles do not name the mypy `files` target
that #1516 item 1 documents in "Adopting the Gates". Filed as #3787.

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

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
… merge Reads (#3793)

## Summary

`pr_review.py merge` now reads an acknowledgment record for findings no
thread carries. It also ends every run on one `next=` line, and the
skills follow that field instead of listing every status line. The
digest gives the excluded-files refusal its own `refusal=EXCLUDED`
value.

### Acknowledgment record (#3718, and the verdict case from #3724)

- New library module `scripts/pr_review_answers.py`. It makes no call
and imports neither the gate nor a transport. It gives each answerable
item a key. That is a SHA-256 prefix over the item's kind, the round or
comment raising it, and the item's text. The items are:
- each `suppressed`, `previously_missed`, and `cr_outside_diff` section
on every round
  - each open Qodo entry
  - the head round's `FINDINGS WITH NO THREAD` block
  - each `REVIEWER OUTPUT NOTED` part
  - a verdict naming no counted finding

One key covers every finding a collapsed section or block holds. A key
holding a finding still awaiting the maintainer takes no `--ack`. A Qodo
entry's key covers its title and text, since Qodo edits one comment in
place. A reviewer login drift gets no key and takes no answer. `merge`
refuses it as `REVIEWER_LOGIN_DRIFT` for the maintainer, since that
login's rounds stay unread while the drift stands.
- `pr_review.py comment --ack KEY=OUTCOME`, repeatable, posts the
written outcomes with one `<!-- fleet-finding-ack: key=<key>
outcome=<outcome> -->` line per item. Some bodies post nothing and exit
`69`: one with no prose outside code (for `--fingerprint` too), one
naming a key no item carries, and one with an outcome the item does not
take. Both acknowledgment kinds count only where markdown-it finds prose
outside code.
- The outcomes are `fixed`, `declined`, `filed`, `listed`, and
`dropped`, after `pr-review-conduct`'s verdicts. `Awaiting maintainer`
has no outcome, so a value call stays the maintainer's.
- A record counts only when it is all of these:
  - from an owner, member, or collaborator
  - in a comment that is not hidden
  - written beside outcomes in prose, not just the marker
  - outside code
- `merge` clears a count only once every item under it carries an
outcome. A partial answer, a new round raising the same text (new key),
a `?` count, or a cut window still refuses.
- A verdict naming no counted finding
(`VERDICT_NAMES_UNCOUNTED_FINDINGS`, exit `50`) clears once its key is
acknowledged. That is the case that blocked #3764 and #3765. It stays
fail-closed:
  - An unanswered verdict still exits `50`.
  - `--ack` refuses `fixed` for a verdict, since a fix moves the head.
- A headline naming a concrete failing input is a real finding under
outcome 1, so the driver fixes it and pushes.
- After a fix push, a head hold that was acknowledged turns into noted
output. That output now reads as answered by its own round's shape
acknowledgment. `shape_hold` takes the commit to read for this. A
refusal's parts never read as answered this way. The shape
acknowledgment and the finding acknowledgment now read a written outcome
the same way, through `answering_comments`.
- `report_verdict` moved from `pr_review.py` into the new module. That
keeps `pr_review.py` under its ceiling (2196 lines before, 2166 now).
The gate-item reading moved out of `pr_merge.py` as well (227 lines
now). No ceiling was raised. The new module has its own ceiling, 450,
and sits in the readers' import contract test (384 lines).

### `next=` route (#3724)

- `merge` passes its output through and ends on
`next=done|cleanup|readback|redrive|retry|maintainer`. One table in
`pr_review_answers.py` decides it from each `status=` line, `wait`'s
gate lines included. A line the table does not name goes to the
maintainer.
- A run naming several routes takes the first of `readback`, `cleanup`,
`redrive`, `retry`, and `maintainer`, so what the seat can clear goes
first. The skill keeps the one-rerun bound.
- A generic `REVIEW_IS_A_REFUSAL` routes to the maintainer, since only a
file-count refusal has a remedy this seat can apply, and the line does
not say which kind it is.
- A test pins every status line `pr_merge.py`, `pr_review.py`, and
`pr_review_answers.py` print to its route.
- `drive-pr` step 4 now follows `next=` and drops its per-status list.
`scripts/README.md` "`pr_merge.py`" carries the route table.
- The Qodo open item: an open Qodo entry is now answered by `--ack` and
needs no `/review` re-review for `merge` to read. The untracked
re-review wait therefore no longer gates a merge.

### Excluded-files refusal kind (#3777)

- The digest prints `refusal=EXCLUDED` for the excluded-files refusal,
apart from `refusal=YES`.
- `scripts/README.md` now lists `excluded` among the adapter's refusal
kinds. The `wait` exit-`41` paragraph now covers the `49`/`0`/`41` split
for that kind and the earlier-head quota or error refusal under an
attested later head. The `coverage=` paragraph already described the
lineless-file allowance (from #3702), so it is unchanged.

### Skills

- `pr-review-conduct` Merge Gate item 3 (verdict) and item 4 (noted
parts), and "Answering a suppressed finding" (the `--ack` outcome
mapping), agree with the code. The generated trees are regenerated.

## Verification

- `tests/test_pr_review.py`, `tests/test_pr_merge.py`,
`tests/test_pr_review_copilot.py`, and `tests/test_pr_review_size.py`
pass, as do ruff, mypy, the prose gate (with the `comments` label), and
`build_dist.py --check`.
- New cases cover:
  - the clearing of each count
- an untrusted, hidden, marker-only, unknown-outcome, or code-quoted
record not clearing
  - a partial answer still refusing
  - per-round keys
  - shape-acknowledged noted parts
- a verdict clearing as `declined` but not as `fixed` or from an
outsider
  - `comment --ack` posting the marker, and refusing with `69`
  - `refusal=EXCLUDED`
- each route, through `merge` and as a table pinned to the status lines
in the sources

Closes on promotion: #3718, #3724, #3777

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

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…m Gaps (#3792)

## Summary

- **Direct-push closing line (#1307, #1276).** Per the maintainer's
answer on #3751, `GOVERNANCE.md` "Operational Repositories" and
`branching-and-release-model` "Operational repositories (the complete
delta)" now state that a direct push to `develop` on an
operational-model repository names the issues it fixes in an inert
on-promotion closing line in its commit message, and that the promotion
body takes no closing keyword for a push without one.
`git-commit-conventions` points to it.
- **backlog-burndown "The Promotion Boundary" (#1276, #1307).** Removes
the falsified single-round rationale. The operational paragraph no
longer uses "the two hops" before they are defined. Step 1 is qualified
to the operational model, and "the only case with nothing to promote" is
gone. "Assembling the Promotion Body" now reads, for each commit in the
range, the pull request that merged it into develop. A commit no pull
request merged is a direct push, and its own message's line is read. A
failed read stops the assembly. The freeze-amend sentence no longer
presumes a fix carries an issue. "Ranking" points at that read.
- **Claim lookup (#3780).** A claim comment now opens with `**Backlog
burndown claim`, and the lookup matches only that opening. This drops
released claims and any other comment that names the branch.
- **A false `git branch -d` claim deleted.** "Cleanup Is the
Orchestrator's" said `git branch -d` refuses rather than discards where
the branch check was wrong. That is false for a branch with an upstream,
which `-d` checks against that upstream.
- **Abandon-discard exception (#3773).** `GOVERNANCE.md` "Git and Commit
Rules" and `git-commit-conventions` name it beside the post-squash `git
branch -D` exception.

#1415 is not in this change. It has no recorded maintainer answer, and
it would add a rule nobody requested.

#3771 and #3772 are not in this change either. Each local review round
over their text raised new findings against the text the previous round
wrote, so both were taken out and return to the ranking.

Closes on promotion: #3773, #1276, #1307, #3780

## Local review

`local-strict-review` passes ran at the `frontier` tier over every push.
Each pass's findings on this diff's text were fixed in the following
commits, several by deleting the text that drew them: the
standalone-clone and agent-safety re-verify text, and a restated copy of
the direct-push rule. The final pass, recorded on head `df7082d2`,
raised no correctness finding. Its two remaining findings are wording
and consolidation preferences on text this change wrote. Each is
declined under `fleet-code-review` "Review the Change"'s own rule to
omit preferences:

- `.agents/skills/backlog-burndown/SKILL.md` "The Promotion Boundary"
operational paragraph (`style`): it restates the direct-push rule beside
its pointer to `GOVERNANCE.md` "Operational Repositories". The paragraph
says what the run reads, and no reading of it leads to a wrong action.
- `GOVERNANCE.md` "Git and Commit Rules" and `git-commit-conventions`
(`style`): "states the grant" is defined only in `backlog-burndown`,
which both name. The reviewer judged it clarity only, with the same
permitted action either way.

### Pre-existing findings, filed

- `.agents/skills/backlog-burndown/SKILL.md` abandoned-group removal,
`git branch -d` judged against `HEAD` rather than `origin/develop`
(`pre-existing`): #3785
- `.agents/skills/backlog-burndown/SKILL.md` and
`git-commit-conventions`, the discard grant arriving as an agent message
a Claude Code worker may refuse (`pre-existing`): #3786
- `.agents/skills/backlog-burndown/SKILL.md` "Grouping and File Claims",
a feature PR body's closing keyword copied into the squash commit under
the `PR_BODY` setting (`pre-existing`): #3788
- `.agents/skills/backlog-burndown/SKILL.md` "Raising a Blocked
Question" and away-mode parking, no resume disposition for a stopped
group's standalone clone (`pre-existing`): #3794

🤖 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 2, 2026 01:02
@coderabbitai

coderabbitai Bot commented Nov 2, 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 2, 2026 •

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 82.72%. Comparing base (27d1ca4) to head (23c7d78).
⚠️ Report is 458 commits behind head on main.

Additional details and impacted files
@@            Coverage Diff             @@
##             main    #3797      +/-   ##
==========================================
+ Coverage   82.34%   82.72%   +0.38%     
==========================================
  Files          28       29       +1     
  Lines        9454     9629     +175     
==========================================
+ Hits         7785     7966     +181     
+ Misses       1669     1663       -6     
Flag Coverage Δ
python-3.13 82.72% <100.00%> (+0.38%) ⬆️

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

The change still has unresolved correctness and test-blocking issues, including false acknowledgment detection, incomplete pull-request gating validation, masking of unprotected jobs, and an unmapped verdict status.

4 open issues
What changed in this PR

This pull request promotes the second backlog-burndown round from develop to main, improving workflow gating, release classification, review acknowledgments, direct-push handling, and related documentation and generated skills.

Changes:

  • Restricts required-check naming to pull-request runs and updates workflow adoption contracts.
  • Improves release Latest selection, lint interpreter selection, and per-workspace .NET validation.
  • Adds acknowledgment records, deterministic merge routing, and excluded-file refusal reporting.
  • Updates write-guard parsing, direct-push closure conventions, promotion assembly, claim lookup, and generated skill distributions.
  • Review findings remain in spec/audit.py, scripts/pr_review_copilot.py, .github/workflows/validate-task.yml, and tests/test_pr_merge.py, including critical and moderate correctness issues.

This pull request requires changes.

File Description
WORKFLOW.md Updates workflow, required-check, coverage, and operational trigger contracts.
tests/​test_pr_review.py Tests acknowledgment records, verdict handling, and refusal output.
tests/​test_pr_review_size.py Adds the acknowledgment module size ceiling.
tests/​test_pr_merge.py Tests acknowledgment clearing and merge-route selection; contains a route-table finding.
tests/​test_audit_helpers.py Tests workflow interface expressions and shipped stubs.
spec/​project-types.json Updates operational pull-request trigger requirements.
spec/​fidelity-model.md Documents pull-request-only required-check naming.
spec/​audit.py Validates conditional required-check expressions; contains two critical findings.
scripts/​pr_review.py Adds acknowledgment CLI handling and verdict integration.
scripts/​pr_review_rounds.py Supports historical acknowledgment shapes and round identifiers.
scripts/​pr_review_digest.py Displays acknowledgment keys and excluded-file refusal state.
scripts/​pr_review_copilot.py Validates prose in acknowledgment bodies; contains a moderate finding.
scripts/​pr_merge.py Uses acknowledgment-aware gate results and emits next= routes.
host-setup/​agent-safety/​README.md Documents short push-option cluster parsing.
host-setup/​agent-safety/​claude/​gh-write-guard.py Corrects attached push-option and short-cluster parsing.
GOVERNANCE.md Documents direct-push issue closures and discard exceptions.
docs/​reusable-workflows.md Updates workflow triggers, adoption prerequisites, check names, and interpreter guidance.
catalog/​snippets/​workflows/​test-pull-request.yml Removes the base filter and gates the required name by event.
catalog/​snippets/​workflows/​test-pull-request-smoke.yml Removes the base filter and gates the required name by event.
.github/​workflows/​validate-task.yml Adds lint interpreter resolution and per-workspace .NET execution; contains a nit about duplicated workspace resolution.
.github/​workflows/​test-pull-request.yml Removes the base filter and applies event-specific aggregator names.
.github/​workflows/​build-release-task.yml Computes and passes the version-aware make_latest value.
.github/​skills/​workflow-ci-contract/​references/​d-guarantees.md Updates generated workflow-contract guidance.
.github/​skills/​pr-review-conduct/​SKILL.md Updates generated acknowledgment and verdict guidance.
.github/​skills/​git-commit-conventions/​SKILL.md Updates generated commit and discard guidance.
.github/​skills/​drive-pr/​SKILL.md Updates generated merge-route guidance.
.github/​skills/​branching-and-release-model/​SKILL.md Updates generated operational repository guidance.
.github/​skills/​backlog-burndown/​SKILL.md Updates generated promotion and claim guidance.
.claude-plugin/​fleet-skills/​skills/​workflow-ci-contract/​references/​d-guarantees.md Updates the Claude workflow-contract distribution.
.claude-plugin/​fleet-skills/​skills/​pr-review-conduct/​SKILL.md Updates the Claude acknowledgment guidance.
.claude-plugin/​fleet-skills/​skills/​git-commit-conventions/​SKILL.md Updates the Claude commit guidance.
.claude-plugin/​fleet-skills/​skills/​drive-pr/​SKILL.md Updates the Claude merge-route guidance.
.claude-plugin/​fleet-skills/​skills/​branching-and-release-model/​SKILL.md Updates the Claude branch-model guidance.
.claude-plugin/​fleet-skills/​skills/​backlog-burndown/​SKILL.md Updates the Claude backlog guidance.
.claude-plugin/​fleet-skills/​.source-digests/​workflow-ci-contract Updates the generated workflow-contract digest.
.claude-plugin/​fleet-skills/​.source-digests/​pr-review-conduct Updates the generated review digest.
.claude-plugin/​fleet-skills/​.source-digests/​git-commit-conventions Updates the generated commit digest.
.claude-plugin/​fleet-skills/​.source-digests/​drive-pr Updates the generated drive digest.
.claude-plugin/​fleet-skills/​.source-digests/​branching-and-release-model Updates the generated branching digest.
.claude-plugin/​fleet-skills/​.source-digests/​backlog-burndown Updates the generated backlog digest.
.agents/​skills/​workflow-ci-contract/​references/​d-guarantees.md Updates the canonical workflow-contract source.
.agents/​skills/​pr-review-conduct/​SKILL.md Updates canonical acknowledgment guidance.
.agents/​skills/​git-commit-conventions/​SKILL.md Updates canonical commit and discard guidance.
.agents/​skills/​drive-pr/​SKILL.md Updates canonical merge-route guidance.
.agents/​skills/​branching-and-release-model/​SKILL.md Updates canonical operational repository guidance.
.agents/​skills/​backlog-burndown/​SKILL.md Updates canonical backlog promotion guidance.

Review effort: Lite

Comment thread spec/audit.py Resolved
Comment thread spec/audit.py Resolved
Comment thread tests/test_pr_merge.py Resolved
Comment thread scripts/pr_review_copilot.py Resolved
ptr727 and others added 2 commits November 1, 2026 18:48
## Summary

`has_prose` counted any markdown `inline` token with content as prose.
So a body holding only an inline code span, such as `` `Fixed in abc`
``, an empty HTML tag pair, or a code-only heading, list item, quote,
table cell, or link text read as prose. A fenced block, an indented
block, and an HTML comment already read as none. `comment --ack` and
`--fingerprint` could therefore attach a marker to a body with nothing
written outside code, and `answering_comments` counted such a body as an
answer.

- `inline_text(token)` takes `code=False`, which keeps only an inline
token's `text` children, with breaks read as spaces.
- `has_prose` reads through `inline_text(token, code=False)`.
- The reviewer refusal-opening check, a sibling reading the first inline
token's raw content, reads through it too. It stops matching a phrase
quoted in a code span, and starts matching one split by emphasis, an
entity, or a line break.
- The coverage-prose count kept its own copy of the same child walk, and
now calls `inline_text` instead.

Tests cover each code-only and HTML-only shape returning no prose, text
beside code or HTML returning prose, a code-quoted refusal phrase
reading as no refusal, and a refusal split by emphasis still reading as
one. Against the pre-fix module, the eight inline code and HTML shapes
and both refusal cases failed, while the fenced, indented, comment, and
text-beside-code cases passed as before.

Closes on promotion: #3718

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

---------

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

## Summary

In `spec/audit.py`, `check_interface` skipped the D1.5 every-event
finding for the whole workflow once any one job used the
pull_request-only name expression. A second job could still post the
required check on a non-PR run, and nothing flagged it.

The check now reads each job's `name:` together with its job key. A
job's name off a pull_request run is its literal name, or the `||`
fallback string when the name uses the pull_request-only expression.
Some workflows are triggered by events other than `pull_request`. In
those, every job whose off-PR name equals the required check gets its
own DRIFT, and the finding names that job. The other jobs no longer
affect it.

The existing missing-name DRIFT keeps its develop behaviour. It covers
names that are neither a literal nor the pull_request-only expression,
and it still flags an expression whose two branches are both the
required name on a pull_request-only workflow. It now runs alongside the
per-job check rather than ahead of it.

- **A job falling back to the required name, with no gated job.** It
reports both findings, and both are true. The PR run never posts the
check, and a non-PR run does.
- **An expression whose two branches are both the required name, on a
dispatch-triggered workflow.** It reports only its per-job finding. That
job does post the check on the PR run, so the missing-name finding would
be false there.

Repro on a constructed stub triggered by `pull_request` and
`workflow_dispatch`:

- **A literal sibling.** One job is named by the D1.5 expression, and a
second carries the literal `Check pull request workflow status job`.
This returned `[]` before the change. It now returns one DRIFT naming
the second job.
- **A fallback sibling.** The second job is named `${{ github.event_name
== 'pull_request' && 'Other job' || 'Check pull request workflow status
job' }}`. This also returned `[]` before the change. It now returns one
DRIFT naming that job.

Unchanged: a gated job alone raises no finding. A literal name on a
pull_request-only workflow raises no finding either.

## Tests

New `INTERFACE_CASES` rows in `tests/test_audit_helpers.py`:

- A literal sibling beside a gated job.
- A fallback sibling beside a gated job.
- Two literal jobs, one finding each.
- A fallback-only job, both findings.
- An every-event expression on a dispatch-triggered workflow, one
finding.
- A fallback job beside an every-event expression naming another check,
both findings.

Two new tests assert which job each finding names, and that the dispatch
every-event case reports its per-job finding rather than the generic
one. Run against develop's `spec/audit.py`, every new row fails except
the dispatch every-event row. That row keeps develop's count, so its
message test is what pins it. The last row also fails if the suppression
stops checking for the required name.

`spec/audit.py --selftest` and the full `OPERATIONS.md` "Local
Verification" block pass.

Closes on promotion: #3334

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

---------

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

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

Approval should be deferred because the broad changes to merge-gate reading, release publication, merge routing, and direct-push rules still require live GitHub/API and rerun-release validation beyond the offline review.

No open issues 🎉

4 resolved since last review

Review effort: Lite

@ptr727

ptr727 commented Nov 2, 2026

Copy link
Copy Markdown
Owner Author

Answer to the verdict headline of round 2 on head 23c7d78, which counts 0/0 open findings.

Approval should be deferred because the broad changes to merge-gate reading, release publication, merge routing, and direct-push rules still require live GitHub/API and rerun-release validation beyond the offline review.

No change needed. The headline names no defect and no failing input, only validation that cannot happen before this promotion or that has already happened:

Each constituent pull request had full coverage at its merged head. #3796, #3793, #3792, and #3799 had an attested local pass. #3784 and #3798 had a Copilot round whose table named the changed files. Whether to merge stays the maintainer's call under Merge Gate item 5.

@ptr727
ptr727 merged commit 320713d into main Nov 2, 2026
14 checks passed
ptr727 added a commit that referenced this pull request Nov 3, 2026
…rge on a Running Hosted Review, and Gate Skill Description Length (#3843)

Promotes the changes merged into `develop` since promotion #3797 to
`main`, the end of an away-mode backlog burndown.

- [#3803](#3803) exempts
the Roslyn embedded-language marker (`// lang=json`, `language=`) from
the prose gate's comment rules as a tool directive.
- [#3807](#3807) has
`local_review.py record` take `--report`, the file the reviewer wrote,
bind the pass to its sha256, and derive the finding count from it.
`status` gains `reportPath`.
- [#3809](#3809) holds
`pr_review.py merge` while a hosted review is still running on the head,
and routes a declined Qodo finding to a dismiss request.
- [#3811](#3811) dates an
anchored `intentRef` by the section its anchor names, and lets a
`driftNote` acknowledge a verified intent advisory.
- [#3816](#3816) moves the
null GraphQL node check into the shared `gh_graphql` transport as
`NullNodeError`.
- [#3818](#3818) explains
the workspace-edit default in `GOVERNANCE.md` and names the aggregator's
expression form.
- [#3822](#3822) moves the
abandon discard to the `backlog-burndown` orchestrator, aligns the
abandoned-branch delete, and holds a grown agent-safety set.
- [#3823](#3823) scopes
the Husky prose gate to staged paths and adds `--skip-generated` so a
named file inside a generated tree is skipped.
- [#3828](#3828) reads
Copilot's `Open findings:` label, drops the duplicate `null_node`, and
matches `scripts/README.md` to the Merge Gate.
- [#3829](#3829) bumps the
two stale hub caller snippet pins and documents the fallback's
mixed-pair refusal.
- [#3831](#3831) keeps an
inherited `GIT_DIR` from rerooting the prose gate's subdirectory git
reads.
- [#3832](#3832) gives
`backlog-burndown` a disposition for a worker's standalone clone and
keeps the agent-safety hold past a park.
- [#3835](#3835) makes
`carry.py check-sections` refuse what `apply-sections` would, and name
an open fence.
- [#3836](#3836) pins the
reusable-workflow stubs by placeholder and ticks the PlexCleaner gates
item.
- [#3838](#3838) has the
`runtime-secrets` audit dimension detect a bare `secrets/` directory
from the tracked files that name it.
- [#3839](#3839) gates
skill descriptions at the 1024-character limit and trims every one over
it.
- [#3846](#3846) fixes an
adjacent doc, an in-file pre-existing defect, and an edge case on new
code in the open pull request instead of filing an issue for each, and
keeps filing for code that runs elsewhere.
- [#3847](#3847) refuses
skill frontmatter that parses to a list or a scalar, so the description
length gate cannot be bypassed.

Fixes #3770
Fixes #3699
Fixes #3705
Fixes #3789
Fixes #1819
Fixes #727
Fixes #3710
Fixes #3706
Fixes #3623
Fixes #3687
Fixes #3617
Fixes #3757
Fixes #3790
Fixes #3786
Fixes #3785
Fixes #3772
Fixes #1657
Fixes #3443
Fixes #3824
Fixes #3817
Fixes #3805
Fixes #1666
Fixes #3700
Fixes #3821
Fixes #3771
Fixes #3794
Fixes #3825
Fixes #3690
Fixes #1472
Fixes #3813
Fixes #3719
Fixes #1592
Fixes #3845

🤖 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

2 participants