Repository navigation
Conversation
…ent Rules (#3803) ## Summary The .NET analyzers read the Roslyn marker `// lang=json` to classify a raw string literal. Without it, `dotnet format style --verify-no-changes --severity=info` fails with JSON002. The marker opens in lowercase, so the prose gate's `comment-case` rule reported it, and `comment-added` read it as prose. This adds `lang(?:uage)?\s*=` to the carried `exemptions.tool_directives` in `.github/actions/prose-gate/prose-gate.json`. It covers `lang=`, the `language=` spelling Rider also reads, and spaces around the `=`. A comment opening on a tool directive is not prose to either rule, so the lowercase marker passes both. `tests/test_prose_lint.py` adds three rows to the existing `test_a_directive_opening_the_body_is_still_exempt` table, one per spelling. Each fails without the JSON entry. ## Not in this pull request Issue #1657 was grouped with this one. Its accepted fix passes the staged path list to the prose gate from the Husky pre-commit hooks instead of `.`. A named file is judged even inside a `generated_trees` directory (#3443), so a staged file under `reports/` would then fail the hook. That change waits on #3443, and this pull request does not carry it. Closes on promotion: #3770 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
## Summary - `scripts/local_review.py record` takes `--report`, the file the agent-skill reviewer wrote, and no longer accepts a typed `--findings` count. The report's first line is `contentDigest: <digest>`, and each later non-blank line is one finding. `record` refuses a report that is absent or names another digest. Otherwise it stores the report's sha256 on the pass as `reportSha256` and derives the count from the report's lines. There is no dispatch-time stamp, per the recorded decision on #3699. - `status` gains a `reportPath` field, the digest-keyed path in the worktree's git directory where the reviewer writes. A record removes reports written for any other digest. The fields `pr_review.py` reads (`covered`, `receiptProblems`, `findings`) are unchanged. - The receipt format moves to version 2, so a receipt an earlier engine wrote, its pass carrying a typed count, no longer covers. - `check`'s printed remedy carries `--report` with the real path, so it still runs as printed once the reviewer has written its report. - The `local-strict-review` skill's brief has the reviewer write that report. "Running It" says to record only once the harness delivers the reviewer's completion notification, and that text in the driver's own turn is never that notification. The handback route and "Recording the Pass" carry `--report`, and on the handback route the dispatcher reads `reportPath` from `status` itself. - `scripts/README.md` describes the new `record` contract and names the uv no-cache exception at both places it promises `uvx` alone can read the action files (#3705). Closes on promotion: #3699, #3705 ## Local Review Findings The first push's local-strict-review edit budget ran out with two `introduced` findings open. Commit d2512ee fixes both, and the pass over that commit found nothing: - `introduced`, fixed: "What It Does" said the pass dispatches a read-only subagent, while the brief now has the reviewer write its report. It now says the reviewer needs an agent type that can write a file. - `introduced`, fixed: `read_report`'s `OSError` and `UnicodeDecodeError` branches only reworded a failure that `main()` already reports with exit 2, so they are gone. Filed rather than fixed here: #3804, the typed `--findings` count `canonical_review.py record` still takes. ## Verification - `python3 tests/test_local_review.py`: new cases cover an absent report, a report for another digest, a report with no digest line, the derived count and stored sha256, a header-only report, `reportPath` sitting in the git directory without moving the digest, pruning, and the removed `--findings` flag. - `tests/test_pr_review.py`, `tests/test_canonical_review.py`, and `tests/test_cli_usage.py` pass against the changed engine. - `prose_lint.py --diff origin/develop`, `ruff format --check`, `ruff check`, `repo_gate.py`, `spec/validate.py`, and `build_dist.py --check` are clean. 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
… Qodo Findings to a Dismiss Request (#3809) ## Summary Two Merge Gate gaps `pr_review.py merge` hit on PlexCleaner pull requests. **A hosted review still running holds the merge (#3789).** `merge` squash-merged a feature pull request while CodeRabbit was still reviewing its head. That happened because an `UNSTABLE` merge state let an unrequired check stop holding the wait. - `check_nodes` now records who posted each check. That is a StatusContext's `creator` login, newly read by the query, or a CheckRun's app slug. - `hosted_reviews_running` names each CodeRabbit or Qodo check on the head still settling. It matches by the poster or by a context named for the reviewer. The reviewers come from `OTHER_REVIEWERS`. It skips a reviewer whose newest output is a rate limit posted or edited since the check began, so that state still clears. An older limit the reviewer has moved past does not. The held read now carries each comment's `updatedAt` for that. - `wait` holds an attested head on such a check whatever the merge state reads. Its `CHECKS_PENDING` line then adds `(reviewing: <name>)`. - `merge`'s fresh read refuses it as `CHECKS_PENDING`, which routes `redrive`. That covers a head `wait` does not poll and a review that starts after the gate read. - A `merge` run inside the write scope that merged nothing and routes to `redrive` or the maintainer now prints `pending=` before `next=`. It names each check on the head that has neither passed nor failed, from a fresh read. So a pull request handed to the maintainer names what still runs. - A hosted status that has not registered on the head yet is not held. That second factor from #3789 needs a design choice, filed as #3806. **Qodo findings are answered with a dismiss request (#2252).** Per the maintainer's answer on #2252, `qodo_open` keeps reading Qodo's own badge. - The digest's Qodo remedy text no longer says to answer in the conversation, which never adds the badge. It says Qodo marks a fixed finding Resolved itself. For a disproved one, the `--ack` comment's body mentions `@<Qodo's login>` and asks it to dismiss that finding by its number, with the reason. The number comes from the findings comment. - `pr-review-conduct` Merge Gate item 3 and `drive-pr` step 4 say the same thing. `drive-pr` routes an `UNTHREADED_FINDINGS` line naming `qodo_open` to that answer rather than to a blocked merge. - `pr-review-conduct` item 2 states the hosted-review hold. The live check #2252 asks for is not part of this change. It confirms on a PlexCleaner pull request that a dismiss request adds the badge, which wording Qodo accepts, and which number it wants. So #2252 stays open. `scripts/README.md` is held by another group in this round, so its `pr_merge.py`, `wait`, and findings paragraphs are not updated here. #3805 tracks that. Line counts stay under every ceiling: `pr_review.py` 2171 of 2200, `pr_review_answers.py` 402 of 450, `pr_review_checks.py` 435 of 450, `pr_merge.py` 246 of 250. ## Local-review finding outcomes - `introduced`, listed on the opening push: `limited_since` dated a rate limit by the newest edit across every reviewer comment, so a summary comment edited as a review starts could clear the hold. Fixed in f38355e, which compares the edit time of the node `rate_limited_by` reads, with a test for that case. - Copilot, `[P1] Hold every unfinished hosted check`: fixed in 7eb8ee6 and 75b4dd1. The hold now reads every shape short of a pass or a failure, so a hosted check queued past the pickup grace, waiting, or a stale record still holds. Its thread was already resolved when the fix landed. ## Verification - `tests/test_pr_review.py` (550), `tests/test_pr_merge.py` (70), `tests/test_pr_review_copilot.py`, `tests/test_pr_review_size.py`, `tests/test_pr_cleanup.py`, and `tests/test_build_dist.py` pass. - New cases cover the poster each check carries and each way a hosted check matches. They also cover the rate-limit clear and an older limit that does not clear. They cover the hold on `CLEAN`, `UNSTABLE`, and `HAS_HOOKS`, and `merge`'s `CHECKS_PENDING` refusal. The `pending=` line, its unread form, and its absence on an out-of-scope refusal are covered, and so is the Qodo dismiss remedy. - ruff format and check, mypy, the prose gate, `repo_gate.py --check eol`, and `build_dist.py --check` are clean. Closes on promotion: #3789 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…edge a Verified Intent Advisory (#3811) ## Summary Each of the five issues is implemented the way the maintainer answered it on 2026-11-01. - **#1819, anchored intentRef dating.** `spec/audit.py` `hub_last_change` now takes the anchor and dates an anchored `intentRef` by the section its anchor names. It narrows each revision to that region and then runs `_last_effective_change`, the way `check_verbatim` narrows a section's history. When the anchor names no heading in the current canonical, it dates the whole file. `spec/fidelity_text.py` gains `heading_slug` and `extract_anchored`, a slug-keyed region extract that works at any heading level (both live anchors are H3). A new test checks that every anchored `intentRef` in `spec/files.json` resolves in the hub. That way a typo cannot fall back to whole-file dating without anyone noticing. - **#727, clearing a verified intent finding.** The advisory now has its own check id, `files.intent`. `intent_ref` parses an intent unit's canonical and anchor in one place. It is a repo-scope check that runs after the file stage, with `hub: false` and `suppression: { endsWithType: null }` in `spec/checks.json`. Before this, the advisory sat inside `files.presence`. A registry `driftNote` naming `(files.intent)` acknowledges a copy with a `` `<path>@<sha>` `` code span. That clears the copy until its intent canonical changes again. The note reader, `spanned_notes`, is shared with the `filenaming.scripts` exemption, so the file-naming check now goes through it too. The check reports a stale acknowledgement, meaning one the advisory no longer raises at that sha, and it also reports a note that names no such span. `AUDIT.md` section 7 describes the acknowledgement, and section 8's list of suppression notes now names it. - **#3710.** `section_key` keeps `strip().lower()`. Its docstring now says `lower()` rather than `casefold()` is intended, since fleet headings are ASCII and the two agree there. - **#3706.** `spec/validate.py` keeps the exact section-name match. A comment at the comparison says why: the declared name is the verbatim disposition label. - **#3623.** The stale `RegressionTests` clause is dropped from PlexCleaner's driftNote. PlexCleaner#1064's merge commit is on PlexCleaner `main`, which carries `RegressionTests/tests/` and passes `python-directories: RegressionTests` from both validator callers. ## Verification - The owned test files pass on the head. The full `tests/` suite passed on the first commit. - `spec/validate.py` passes. - The pre-commit gate passes: ruff format, ruff check, mypy, and the prose gate. The prose gate needs the `comments` label for two comment lines in `spec/validate.py`: the one #3706 asks for, and the existing comment that now names `intent_ref()`. - A live audit of one fleet repo on hub `23c7d784` no longer flags `.editorconfig`, `.editorconfig-checker.json`, or `.gitattributes` against the latest whole-file `GOVERNANCE.md` change. `version.json` is still flagged, but against the change that actually edited `WORKFLOW.md` "D3 - Versioning and Classification", not the whole file's latest edit. ## Local findings fixed after opening The third local pass raised three `introduced` findings once the first push's edit budget was spent. Commit 0ac4676 fixes all three under a fresh pass: - `tests/test_audit_helpers.py` now pins that `files.intent` runs after `files.presence`, the read it depends on, since `spec/validate.py`'s ordering guard covers only file checks. Commit 7bff4e5 widens that test to the whole class. Every repo check whose function reads `run.content`, `run.found`, `run.docs`, or the README through `_readme` must sit after the first baseline check. - A test now pins that `intent_trail` passes the intentRef's anchor to `hub_last_change`. - The `heading_slug` docstring now says `_` is kept as well as `-`. The fourth local pass found one more `introduced` gap: no test drove `check_files_intent` with a copy read at a declared alternative. Commit d014dcd adds that test. The fifth pass found that no test pinned `files.intent`'s suppression role, and commit b3a8d04 adds one. ## Declined local findings - `spec/audit.py` `hub_last_change` anchored narrowing no longer catches an edit to the hub's own `.gitattributes`, `.editorconfig`, `.editorconfig-checker.json` or `version.json` unless the anchored section also moves (local pass, `introduced`): declined. The maintainer's 2026-11-01 answer on #1819 chose option 1, dating the anchored section only. The decision record says option 2, adding the file's own hub canonical, "still takes the noisy doc date unless combined with option 1", and option 2 was not chosen. #727's 2026-08-15 comment shows hub-only edits to those files, such as a CRLF default or a hub-tree pin, are exactly the noise a downstream copy must not be dated by. The content-identical shortcut against the hub file at `path` stays, and it only ever clears. - `spec/fidelity_text.py` `extract_anchored`'s extent against `sections()` (local pass, `introduced`, design): declined. `sections()` is the manifest's level-two unit, which ends only at the next H2, and every verbatim section and the carry depend on that extent. An anchor names a GitHub section, which ends at the next heading of its own level or above, and that is what bounds an H3 such as "Line Endings" at all. If one primitive served both, it would either move the H2 unit's extent or lose the H3 case. So the `sections()` docstring now claims only the level-two extent. The any-level duplicate inside `scripts/build_dist.py` is #3810. - `spec/audit.py` `spanned_notes` shares only the no-span half of a code-span driftNote (local pass, `introduced`, consolidation): declined. The stale half matches differently at each site. A file-naming path is checked against what the check reports. An intent `path@sha` is checked by sha prefix against the change the advisory cites. Folding the two would mean passing the matcher in, which costs more than the few lines it saves. - `spec/validate.py` #3706 comment gives the verbatim disposition label as the reason for the exact match, but the match also covers intent-fidelity and string-form sections (local pass, `introduced`): declined. The comment states the reason the maintainer's 2026-11-01 answer on #3706 recorded, "the files.json name is the verbatim disposition label". A section's fidelity is a one-field manifest edit, and an exact rule that holds for every declared section means flipping one to verbatim never needs a spelling fix first. ## Pre-existing findings, filed - `docs/reusable-workflows.md` tracker: the PlexCleaner item is a sibling of #3623's stale claim (local pass, `pre-existing`), filed as #3813. ## Pre-existing findings, not filed - `registry/repos.json` PlexCleaner driftNote: the clause this change keeps still says "uvx scripts profile", a term the python-codestyle build-versus-lint-only split no longer uses (local pass, `pre-existing`). ## Dropped findings - `spec/fidelity_text.py` `section_span` and `extract_section` docstrings, and `scripts/build_dist.py` `heading_body`, say "case-folded" or "folded" (local pass, `pre-existing`, wording on text this change did not touch). ## Deferred - `RESYNC.md` section 5, "What a Resync Cannot Detect", still summarizes the advisory without the anchor and acknowledgement cases. `RESYNC.md` is outside this change's file claim, so it is filed as #3808 (local pass, `introduced`, deferred). - The three line-ending units name `GOVERNANCE.md#line-endings`, which is a one-paragraph pointer to the `comment-and-doc-style` Skill. Dated by that section alone, their advisory fires only when the pointer itself is reworded. Re-pointing them is a `spec/files.json` decision outside this change's claim, filed as #3812 (local pass, `introduced`, deferred). - `spec/fidelity_text.py` `extract_anchored` and `scripts/build_dist.py` `heading_body` compute the same any-level region. `scripts/build_dist.py` is outside this change's file claim, so it is filed as #3810 (local pass, `introduced`, consolidation, deferred). Closes on promotion: #1819, #727, #3710, #3706, #3623 🤖 Generated with [Claude Code](https://claude.com/claude-code) <!-- This is an auto-generated comment: release notes by coderabbit.ai --> <!-- coderabbit-ai-generated:v1 --> ## Summary by CodeRabbit * **New Features** * Added a repository check that flags outdated intent copies and supports acknowledgements tied to a cited change. * Intent checks can now focus on a named section and ignore changes limited to formatting or workflow normalization. * **Documentation** * Clarified how to record verified intent advisories and how acknowledged copies are checked. * Updated the PlexCleaner advisory to reflect that Python lockfile pinning does not apply. <!-- end of auto-generated comment: release notes by coderabbit.ai --> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…on Form (#3818) ## Summary Three small `GOVERNANCE.md` prose changes, each made the way the maintainer answered it, plus the `.gitignore` entry one of them calls for. - **#3617, the workspace-edit default.** The maintainer kept the default that treats another VS Code window as hosting no session. Both sentences stating it, in "Verification Discipline" and "Editor and Tasks", now give the reason. Another window's session state is normally unobservable, so assuming a live one would hand every such edit back to the maintainer. That reason is there so a carrier's reviewer does not raise the same finding again. `agent-conduct` includes "Verification Discipline", so `scripts/build_dist.py` regenerated it. - **#3757, the Python Environments extension.** "Editor and Tasks" now says a repository shipping Python lists `.vscode/settings.json` in `.gitignore`. The reason given is that the Python extension installs the Python Environments extension, which writes settings there (it is in `ms-python.python`'s `extensionPack`). The rule that settings live only in the `.code-workspace` file stays. Its `.vscode/` clause now speaks of tracked files, so the generated file does not contradict it. The hub ships Python, so its own `.gitignore` gains the entry. - **#3790, the aggregator's expression name.** The "Job and step `name:` suffixes" bullet now says the ruleset-bound name is the one a `pull_request` run posts. It points to `WORKFLOW.md` D1.5 for the expression form and says each string that expression yields ends in "job". The same-string sentence is scoped to that `pull_request` name too, since an expression `name:` is not literally the context string. ## Not in this change #3768 (Repository Layout) was assigned to this group, but `develop` already has every item its decision lists. #1685 added them on 2026-09-18, before #1155 was split. Its `.github/workflows/` row already scopes the reusable task workflows to the hub, and the `STANDUP.md`/`RESYNC.md`, `.agents/`, and `host-setup/` rows are all there. That issue is left open with a comment giving the evidence, so this pull request does not close it. ## Verification - `scripts/build_dist.py --check`: generated distributions are current. Only `agent-conduct` changed. - `prose_lint.py . --diff` against the merge base: clean. - `spec/validate.py`: OK. - The `tests/` unittest suite: 3332 tests, OK (7 skipped). - `local-strict-review`: three recorded passes at the `frontier` tier. The first two raised findings and each was fixed or classed. The third ran over the final content. ## Pre-existing findings, not filed - `GOVERNANCE.md` "Verification Discipline", the workspace-edit bullet, `pre-existing`: "Where it cannot tell, it asks the maintainer" can be read as covering the unobservable other-window state. The default sentence just before it already settles that case, and the "Editor and Tasks" copy scopes its question to "whether the rule binds it". The sentence predates this change, which adds only the reason clause. A resync is owed to carriers, since the sections this changes are carried. Closes on promotion: #3617, #3757, #3790 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
## Summary - `gh_graphql.request()` now raises `NullNodeError` (a `GraphQLError`) when any `nodes` list in the response holds a null. The check and `null_node()` live in the shared transport. - `pr_review.gh_graphql()` keeps its query-only re-read around the new error and ends the run naming where the null sat. A mutation is never sent twice. - Removed the per-site `isinstance(n, dict)` guards in `pr_cleanup.py` that the transport makes redundant. `handoff.py`'s label-node read, which raised `TypeError` on a null node, now reports exit 2 through its existing `GraphQLError` handling. - Tests: null node through the shared transport, the moved `null_node` helper, and `handoff.labeled` with a null label node. `pr_review.py` shrinks by one line. `pr_review_rounds.null_node` is now unused by scripts and is left for a follow-up since that file is outside this change. Closes on promotion: #3687 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Co-authored-by: Claude Sonnet 5.5 <noreply@anthropic.com>
…nch Delete, and Hold a Grown Agent-Safety Set (#3822) ## Summary This changes `backlog-burndown`'s "Cleanup Is the Orchestrator's" section, the re-verify bullet in its "Grouping and File Claims" section, and the abandon-discard exception that `git-commit-conventions` and `GOVERNANCE.md` "Git and Commit Rules" both state. - **#3786: the orchestrator does the abandon discard, per the maintainer's answer on that issue.** - A worker told to abandon its branch reports its worktree path and ends. It discards nothing. - The orchestrator waits until the worker has ended, per "Bounding the Wait on a Worker". - It then reads a live `git worktree list --porcelain` for a linked worktree whose `branch` line names the claimed branch, and runs `git -C <path> reset --hard` and `git -C <path> clean -fd` in that entry's own `worktree` path. Where no such entry exists, the group stops for the maintainer. - The matching exception in `git-commit-conventions` and the one sentence in `GOVERNANCE.md` now name the orchestrator instead of the worker. Nothing in agent-safety is edited. - **#3785: the local branch delete now matches the is-ancestor check.** - The orchestrator records the branch tip and runs the is-ancestor check against `refs/remotes/origin/develop`. Both happen before anything is removed. - The local branch is then deleted with `git update-ref -d refs/heads/<branch> <tip>`. That command refuses only if the branch has moved off the tip the check judged. - A `git config --remove-section` step drops the branch's tracking section, which `git branch -d` would have removed. - `git branch -d` judged a branch with no upstream against the base clone's `HEAD`. The fetch does not advance that `HEAD`, so a branch with no commits of its own could be refused even though deleting it discarded nothing. - **#3772: a file set that grows into `host-setup/agent-safety/` gets the agent-safety hold.** - When a worker reports that its files now reach into `host-setup/agent-safety/`, the orchestrator applies the "Ranking" hold as though it were ranking the issue now. - In an attended run, a group whose issue needing that edit has an approval recorded continues on a brief carrying the no-merge line. - Every other such group, and each one in an away-mode run, stops per "Raising a Blocked Question". An away run turns that stop into an ordinary park through the existing "Parking Instead of Asking" rule, so this adds no new parking path. Closes on promotion: #3786, #3785, #3772 ## Verification - `uv run --script scripts/build_dist.py --check`: generated skill distributions are current. - The prose gate on the diff against `origin/develop`: no violations. - `scripts/repo_gate.py --check eol`: 0 issues. - `spec/validate.py`: OK. - Local strict review: a recorded pass covers this head's content, with no findings open against this change. The pre-existing findings it raised are filed below. ## Pre-existing findings, filed - `.agents/skills/backlog-burndown/SKILL.md` "Cleanup Is the Orchestrator's", the discard path for a standalone clone (`pre-existing`). The base text already limited the discard to a linked worktree. Tracked in #3771. - `.agents/skills/backlog-burndown/SKILL.md` "Ranking" (the resuming-group sentence) and the resume paragraph in "Parking Instead of Asking" state no precedence against "Ranking"'s away-mode agent-safety hold (`pre-existing`). A worker could already park on an agent-safety edit through "Needs the Maintainer" before this change. Filed as #3825. 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…y Flag (#3823) ## Summary The Husky pre-commit hooks now hand the prose gate the paths the commit stages, rather than `.`. An unrelated untracked scratch file no longer refuses every commit (#1657, option 1 of the maintainer's answer). Naming paths would have made the gate read a staged file inside a generated tree, which a `.` scan skips. So the gate gains `--skip-generated`. It drops a named file inside a `generated_trees` directory the same way a directory scan does. It reads the carried list and the repository's own `.prose-gate.json` additions. The `pre-commit` snippet passes the flag and drops its `exclude: (^|/)reports/` copy of that list, and its README drops the sentence about mirroring it (#3443). - `.github/actions/prose-gate/prose_lint.py`, `prose-gate.json`: `--skip-generated`, applied in `discover()` to a named file. Without the flag a named file is still read wherever it sits, so `prose_lint.py reports/x.md` by hand is unchanged. - `.husky/pre-commit`, `catalog/snippets/husky/pre-commit`: `git diff --cached --name-only --diff-filter=ACMRT -z`, kept only where `[ -f ]` holds, so a submodule, a dangling symlink, or a staged-then-deleted path does not reach the gate's `error-not-a-path` refusal. The inner `sh` skips the gate on an empty list, since a gate given no path scans `.` with untracked files. It checks itself rather than relying on `xargs -r`, because GNU xargs runs once on empty input and BSD xargs does not. The list is read once on its own before the pipeline, so a failing `git diff` stops the hook rather than reading as an empty list. The catalog snippet calls `uv run --no-project python "$hub_fetch_run"` for the gate, the form the `pre-commit` snippet already uses, since xargs cannot call the `run_py` shell function. The working-tree content is still what is read, so a partially staged file is still judged on all of its edits. - `prose_lint.py` `repo_root()`: git hands a hook in a linked worktree `GIT_DIR`, and under it `git -C <dir> rev-parse --show-toplevel` reports `<dir>` itself. So the first commit through the new hook, from a worktree, was refused as spanning several repositories. The lookup now drops the location variables `checkout_provenance` already drops (`GIT_LOCATION`). A `.` scan never met this, because the hook stands at the top level. - `scripts/README.md`, `catalog/snippets/pre-commit/README.md`: the hook's scope and the generated-tree sentence now describe this. ## Verification - `tests/test_prose_lint.py`: three new cases in `TestTheScanScopeIsTheScopeReported`, on constructed data. A staged file at `reports/run/audit.md` reports its bait when named and passes under `--skip-generated`. A repository-declared `generated_trees` entry is honored while an authored file in the same run is still read. An untracked file is not read when the run names only the staged path. A test also pins one repository lookup per directory. Full suite under `tests/requirements.txt`: 3336 tests OK. - `tests/test_pre_commit_hook.py`: the fixture moves to a shared `HookCase`. Five new cases drive the hub hook through real commits beside an untracked file holding a finding. A deletion-only commit skips the gate. An edit plus a staged report under `reports/` passes, and a staged finding is refused without naming the untracked file. Those three fail against the hook that scanned `.`. A staged-then-removed path is dropped and a typechange is read, each failing once its filter is removed. The merge-commit cases still pass. - Hub hook driven through constructed commits in a throwaway repository. An untracked scratch comment, a copy of `reports/_template.md` staged at `reports/run/audit.md`, a deletion-only commit, a staged-then-deleted path, and a dangling symlink each exit 0. An authored file with a duplicated word, its name holding a space, exits 1. So do a new comment line and a symlink replaced by a regular file holding a finding. - The catalog Husky snippet driven the same way, with a local shim standing in for `hub_fetch_run.py`. An untracked note and a staged report pass, and an added comment is refused. - shellcheck and shfmt clean on both hooks. ruff format, ruff check, mypy clean. `repo_gate.py` clean. The hook comments are wanted, so this PR carries the `comments` label. ## Local review - The typechange gap is fixed: `ACMR` dropped a symlink replaced by a regular file, so `T` is kept. So is the per-path repository lookup, which now runs once per directory. The duplicated git-environment filter now shares one `git_env()` helper. And the Husky README's count of `hub_fetch_run.py` calls is dropped. - Second pass: a staged-then-removed path and a typechange each now have a hook case. Each fails when the `[ -f ]` filter or the `T` filter is dropped. `lookup_start()` now holds where a git lookup starts, for `repo_root()` and `main()`. - Declined: moving the non-file filter and the empty-list skip into the gate. The gate's `error-not-a-path` refusal deliberately refuses rather than falling back to another scan, for every caller. The `pre-commit` framework already drops a non-file through its default `types: [file]`. - Declined: xargs may split a very large commit into several gate runs. The hook still fails when any batch fails, since xargs exits 123, and each batch's scope line names its gate commit. So a split never turns into a false pass. - Third pass, edit budget spent. Declined: `catalog/snippets/hub_fetch_run.py` line 9's example `prose_lint.py . --diff HEAD` is a valid by-hand run of the fetcher, the form `scripts/README.md` documents. It makes no claim about what a hook passes, so it is left as written. - Fix pass, after the hosted review: the inner `sh` takes a literal `sh` as `$0`. So an error such as a missing uv names the shell, not `hub_fetch_run.py` or the base. `main()`'s `scan_root` fallback now calls `lookup_start()` too. - Last pass, edit budget spent. Declined: the standalone read of the staged list before the pipeline was called over-hardening, since git has loaded the index before it starts the hook. It stays. Without it a failed read silently skips the gate, and the hook is also run by hand, which its own comment allows for. The cost is one `git diff --name-only`. ## Pre-existing findings, not filed - `catalog/snippets/husky/pre-commit`: no test drives the catalog Husky snippet, only the hub's own hook (coverage, pre-existing). This change ran it by hand against constructed commits, as listed under Verification. Stand-ins replaced `hub_fetch_run.py`, `dotnet` and the formatters. - `catalog/snippets/husky/pre-commit`, `catalog/snippets/pre-commit/.pre-commit-config.yaml`: a merge commit downstream still diffs against `HEAD` rather than `MERGE_HEAD` (correctness, pre-existing). It was closed unfixed under the backlog-scope decision, so it is not refiled here. ## Dropped findings - `prose_lint.py` `discover()`: its start computation, with a `.` fallback for a path that is neither file nor directory, could call `lookup_start` (consolidation, untouched text). - `prose_lint.py` `GIT_LOCATION`: its list differs from `spec/git_env.py`'s, which the fetched gate cannot import. The list predates this change, which only moves its comprehension into `git_env()` (consolidation, untouched text). ## Deferred - `GOVERNANCE.md` line 299 still implies a plain git hook scans the tree. That file is held by another lane, so the one-clause fix is #3820. ## Pre-existing findings, filed - `prose_lint.py` `tracked_paths`, `untracked_paths`, `shallow_checkout`, `git_bytes`: an inherited `GIT_DIR` reroots a subdirectory run inside a linked-worktree hook (#3821). Closes on promotion: #1657, #3443 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
… Match the Scripts README to the Merge Gate (#3828) Read Copilot's `Open findings:` label, drop the duplicate `pr_review_rounds.null_node` (callers use `gh_graphql.null_node`), and bring three `scripts/README.md` passages in line with the code on develop (hosted-reviewer hold in `wait` exit 30, `merge`'s `CHECKS_PENDING` refusal with `pending=`, the Qodo dismiss remedy). Closes on promotion: #3824, #3817, #3805 Pre-existing findings, not filed: - scripts/pr_review_checks.py `limited_since`: a queued check with no start time reads any past rate limit as newer (pre-existing, code outside this diff). - scripts/README.md: "neither passed nor failed" mirrors the code docstrings, which also count a failed check whose newer run is unfinished (pre-existing wording, low severity). Local strict review: attested below after the push. 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Co-authored-by: Claude Sonnet 5.5 <noreply@anthropic.com>
…y Git Reads (#3831) ## Summary A hook in a linked worktree inherits `GIT_DIR`. Under `GIT_DIR` with no `GIT_WORK_TREE`, git takes the directory a command starts in as the work tree's top level. So a run naming a subdirectory, such as `prose_lint.py sub`, read `git -C sub ls-files` as though `sub/` were the root, and the top level's ignores lapsed. - The reads of the scanned tree now run under a new `tree_env()`. These are `tracked_paths`, `untracked_paths`, `shallow_checkout`, `git_bytes`, and the dead-path rule's `once_tracked`. `tree_env()` drops the location variables as `git_env()` does for `repo_root` and provenance. `once_tracked` reads the same tree through the same `git -C`, so it is the same class and moves with the rest. - `tree_env()` keeps `GIT_INDEX_FILE`. Measured on git 2.47, `git commit -a` hands the hook `index.lock`, and `git commit <paths>` hands it `next-index-*.lock`. That index is what the commit records, and the checkout's own index is not, so dropping it would make the gate read the wrong one. A plain commit in the main worktree hands the hook a relative `.git/index` with `GIT_DIR` unset, which was already inherited and is unchanged. ## Tests The tests are built from the issue's constructed repro, with temporary repositories and synthetic content only. - `test_a_hook_in_a_linked_worktree_names_a_subdirectory`: under `GIT_DIR`, an ignored file in the named subdirectory stays unread. - `test_each_scanned_tree_read_answers_for_the_directory_it_names`: with `GIT_DIR` naming a different, shallow repository, each of the five reads answers for the directory it names. Reverting any one call site alone fails this case, checked site by site. - `test_a_commit_naming_paths_keeps_the_index_it_hands_its_hook`: a real `git commit <paths>` in a linked worktree runs a pre-commit hook that lists a subdirectory. The listing holds the file the commit's index still tracks and the checkout's index dropped. It fails both when the whole environment is inherited and when `GIT_INDEX_FILE` is dropped with `GIT_DIR`. It is skipped off POSIX, as the textconv-driver case is, since the hook is written for a POSIX shell. `prose_lint.py` grows by 14 lines and no ceiling moves. ## Local review Two recorded `local-strict-review` passes. The first raised one `introduced` finding, the real-hook case running unguarded on the Windows leg, which was fixed in f47ab60. The second raised none. ## Pre-existing findings, not filed - `.github/actions/prose-gate/prose_lint.py` `GIT_LOCATION`, consolidation: its list differs from `spec/git_env.py`'s `GIT_LOCATION_VARS`, for example in `GIT_SHALLOW_FILE` and `GIT_GRAFT_FILE`. No hook sets the missing ones, and the gate cannot import `spec/`. Closes on promotion: #3821 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…r Refusal (#3829) ## Summary - Bump the two stale hub caller snippet pins, `merge-bot-pull-request.yml` (was 2.0.338) and `run-periodic-codegen-pull-request.yml` (was 2.0.352), to the 2.0.1319 release commit `320713d9`. Each snippet's `secrets:` matches the callee's `workflow_call` at that commit. The merge-bot's optional `rules`, `delete-branch` and `app-login` inputs, which the snippet's comments name, all exist there. The codegen callee dropped its optional `NINJA_API_KEY` secret, which the snippet never passed. `test-pull-request.yml`, `publish-release.yml` and `test-pull-request-smoke.yml` were already past the prose gate's non-UTF-8 fix and are unchanged. - GOVERNANCE.md "Hub-Hosted Tooling" gains the one sentence the maintainer chose on #3745: Dependabot keeps the `uses:` pin current, so the never-pinned rule for a doc gate fetched from `main` does not reach it. - State `hub_fetch_run.py`'s mixed-pair refusal where its unresolved-commit fallback is documented: GOVERNANCE.md "Running the Linters Locally" and the husky and pre-commit snippet READMEs. The fallback reads the prose gate and its data from `main` twice, refuses when the reads differ, and a re-run is the remedy. The residual per-URL CDN caching risk from #3696's review needs an immutable fetch, a design change filed as #3827 (`deferred`). Not changed: `docs/reusable-workflows.md`'s adopt-stub copies of these two pins (the "Adopting the Merge-Bot" and codegen stubs) and `catalog/snippets/workflows/README.md`'s history line still name 2.0.338 and 2.0.352. A Markdown-quoted pin is never bumped by Dependabot, which is exactly #1472's open question, so they are left for that issue rather than hand-bumped here. Closes on promotion: #1666, #3700 ## Pre-existing findings, not filed - `catalog/snippets/workflows/publish-release.yml`, `test-pull-request-smoke.yml`, `test-pull-request.yml`: these still pin 2.0.1122 and 2.0.1166, which are past the fix #1666 needed but older than 2.0.1319, and the hub's Dependabot config does not scan `catalog/snippets/workflows`. That is `pre-existing`, outside this change's file claim. The class, a snippet pin nothing keeps current, is #1472's. ## Verification - `git rev-parse '2.0.1319^{commit}'` resolves to `320713d9d491f578b5604a8d67b82478717d7b05`, the latest release per `gh release list`. - Callee `on.workflow_call` read at that commit for both snippets. - Prose gate against `origin/develop`: clean. Repo gate: every check ok, `sha-pin` resolving both new pins against GitHub. `tests/test_release_guards.py`: 33 passed. - `local-strict-review`, two recorded passes: the first raised two `introduced` wording findings in GOVERNANCE.md (a carried sentence leaning on a reason stated only in a hub-only section, and an ambiguous "That fallback"), both fixed in the second commit. The second pass raised nothing. 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…he Agent-Safety Hold Past a Park (#3832) ## Summary Gives `backlog-burndown` a disposition for a worker's standalone clone on every path that retires or removes one, and states that releasing a parked group never lifts the away-mode agent-safety hold. - **Finding the clone (#3771).** A worker choosing the `repo-worktree` standalone-clone fallback records the clone's directory name on its group's claim comments before cloning, keeping the claim opening. The brief names the temporary root, which is never posted. This is what lets the orchestrator find a dead worker's clone. - **Disposing of it (#3771, #3794).** "Cleanup Is the Orchestrator's" gains one clone paragraph. A clone holds its branch alone, so the base-clone commands do not reach it. It must be clean. Retired alone, its tip must equal the live remote tip. Removed with its branch, its tip must be on `origin/develop`. The directory is deleted per `repo-worktree`, and its recorded name is removed from the claim comments. Any failed check leaves the clone standing and stops the group. The abandoned-worker discard does not run in a clone. - **Paths that cite it.** The dead-worker path ("Bounding the Wait on a Worker"), the abandoned path, the attended resume ("Raising a Blocked Question"), and the away-mode retire step ("The Lean Orchestrator" step 2, since `git worktree remove` errors on a clone path). The stop prompt says whether a clone holds commits the remote lacks, and pushing or removing them is the maintainer's. - **Agent-safety hold (#3825).** "Ranking" now says an answered decision never lifts the away-mode hold on an edit under `host-setup/agent-safety/`, so such a group resumes only in an attended run and the report names it as held. "Parking Instead of Asking" defers to "Ranking" there. Generated copies regenerated with `scripts/build_dist.py`. Closes on promotion: #3771, #3794, #3825 ## Findings from the local passes Each finding the pass on the first push left open has an outcome. Each one the hosted round raised is answered in its thread. - Resume bullet, `introduced`: a removed or never-pushed clone could never pass the retire-alone check. Fixed in 1573113 and cb8addf: the resume waits until the remote holds the clone's branch. - Dispatch bullet, `introduced`: "That root is never posted" was false. Fixed in 1573113 by deleting the claim. - Clone paragraph, `introduced`: "strikes its recorded name". Fixed in 1573113 ("removes"). - Clone paragraph, `introduced`: the release also reached a clone retired alone, and a later run could not find a clone under a root it never named. Fixed in 873a9b7: a clone retired alone keeps its claim, and the paragraph says a later run that named another root sends the group to the maintainer. - Clone paragraph, `introduced`: the retire-alone `ls-remote` read the caller's `origin`. Fixed in dee2602 (`git -C <clone> ls-remote`). - Clone paragraph retire-alone check, stop bullet, and park step 4, `introduced`: the check needs tip equality while the prompt and park comment asked whether the remote lacked any of the branch. Fixed in 1cafaa2: all three now say whether the clone's tip equals the remote tip. ## Pre-existing findings, filed - "Cleanup Is the Orchestrator's" merged-group clone check, `pre-existing`: it requires tip equality with `headRefOid` where `repo-worktree` accepts a tip at or behind it. Filed on the unit tracker #3833. ## Deferred, filed - #3830: the "Invoking It" bullet still calls every answered parked group free to resume. It sits outside the units these issues name. 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…ame an Open Fence (#3835) ## Summary Fixes the three follow-ups #3690 records against `scripts/carry.py`'s section modes. 1. `plan_unit` now renders the insertion and the splice in memory, so `check-sections` meets every refusal `apply-sections` raises. `SectionPlan` carries the rendered `inserted` and `rewritten` lines, and `apply_plans` only writes and asserts them. The `scripts/README.md` sentence on refusals now says so. 2. An inserted section whose heading lands inside a code fence the target leaves open is refused naming that fence and its line, rather than as an absent section. Any other block that swallows the heading gets a refusal naming the insertion line. Tests assert both messages. 3. `RESYNC.md` step 1 and `scripts/README.md` now say the rename check covers text in the same file, since the scan is per file. ## Verification - `python -m unittest test_carry` from `tests/`, with `tests/requirements.txt`: 78 tests pass. - Each new test fails against the code before its fix. - `ruff check`, `ruff format --check`, and `prose_lint.py --diff origin/develop` over the changed files: clean. The pre-commit hooks passed. ## Local strict review Two rounds. Round 1 raised two findings, and round 2 raised none. - Fixed in 62e5d18: when the inserted hub region held a fence of its own, that fence closed the target's open one in the spliced text. So the refusal fell back to the generic message. `unparsed_insertion` now reads the open fence from the text ahead of the insertion, and a test covers it. - Declined: consolidate the open-fence filter with `scripts/build_dist.py:364`. Both sites already call the shared primitive, `spec/fidelity_text.py`'s `code_blocks` and its `closed` field. What is left at each site is a one-line filter with a different range meaning. There it keeps blocks that start strictly inside a heading's body, and here it keeps a block reaching the end of a prefix. Closes on promotion: #3690 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…ates Item (#3836) ## Summary - **Stub pins become placeholders.** Every hub `uses:` pin in `docs/reusable-workflows.md`'s stubs now reads `@<hub-main-commit-sha> # <release-tag>`, the shape the maintainer chose on #1472. No Dependabot scan reads a pin quoted in a document, so a literal there only went stale. - **Snippets keep a literal floor pin.** Each `catalog/snippets/workflows/` snippet is unchanged and keeps its literal minimum known-good pin, which moves when a task's interface changes. `spec/workflow_reuse.py` and the pin gate read them as canonical text. - **Adoption resolves the pin.** "Pinning" now states the rule and the two read-only `gh` commands that name the latest release and its `main` commit. The snippets README and `STANDUP.md` section 3 point there. "Byte for byte" no longer covers the pin lines. - **Tests.** The tests comparing a doc stub to its snippet substitute the placeholder through one `documented()` helper, and the merge-bot stub is now compared too. A new test holds both pin shapes, placeholders in the doc and literals in the snippets, judging every hub reference including one with no tag comment. - **PlexCleaner Stage 2 item ticked (#3813).** Checked live against PlexCleaner `main`. ptr727/PlexCleaner#1064 merged into `develop` at `84fd00e` and was promoted in ptr727/PlexCleaner#1098 at `1876563`. Both validator callers pass `python-directories: RegressionTests`. The Stage 3, 4, and 5 PlexCleaner items it also bears on are filed as #3834 rather than widened into this change. ## Verification - Full unittest suite, `spec/validate.py`, `test_audit_helpers.py`, and `spec/workflow_reuse.py --selftest` all exit 0. - The prose gate, ruff, mypy, and the eol check pass in the pre-commit hook. Closes on promotion: #1472, #3813 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…e Over It (#3839) Closes on promotion: #1592 ## Summary The `description:` frontmatter field is what makes a skill fire. Most fleet skill descriptions ran past the 1024-character Agent Skills limit, and nothing bounded them. A loader can truncate a description past that point. The part it drops is the tail, which is where these descriptions say which sibling skill wins a shared trigger. - **Gate.** `scripts/build_dist.py` parses each `.agents/skills/<name>/SKILL.md` frontmatter with PyYAML. It refuses a `description` longer than `DESCRIPTION_LIMIT` (1024): - exit 2 under `--check` - exit 1 on a regenerate, before anything is written - also refused: frontmatter that never closes, invalid YAML, and a description that is not a string - a skill with no frontmatter or no description is not measured - PyYAML is imported lazily, so `skills_install.py` still imports `build_dist` under a plain `python3` - **Trims.** Every over-limit description is trimmed to the limit or under. The longest is now `git-commit-conventions` at 1014. Each trim keeps what the skill governs, its "use this whenever" triggers, and its sibling-precedence and co-fire clauses. - Narrative the body did not already hold is moved into it. That is one new paragraph in `session-handoff` "Why This Exists". - Narrative the body already covers is deleted. - **skill-lifecycle.** Step 2 states the cap and its source constant. It also says incident and "Triggers even when" reasoning goes in the body. The Pipeline section's exit-2 list gets the new cause. - **scripts/README.md.** It names PyYAML as the second dependency exception, and adds the gate to the `build_dist.py` `--check` exit-2 list. ## Verification - `uv run --no-project --script scripts/build_dist.py --check` exits 0. Before the trims it exited 2 and listed every over-limit skill. - New `DescriptionCase` tests in `tests/test_build_dist.py`: - a description at the limit passes and one past it is refused - the limit is measured on the parsed string, not the raw lines - the exit codes, with nothing written - a skill with no description is not measured - the malformed-frontmatter refusals - importing `skills_install` does not import `yaml` - Full suite, `python -m unittest discover -s tests`: 3355 tests OK, 7 skipped. - The pre-commit hook passed: ruff format and check, mypy, the prose gate, and eol. - `local-strict-review`: three recorded passes. - The first raised 5 findings, all fixed: two dropped trigger words, one trigger widened from "a composite action under .github/actions/" to the whole tree, and two descriptions whose sentences had gone over 25 words. - The second raised 1, fixed: an over-long sentence in `backlog-burndown`. - The third was clean. - A Copilot finding on the opening head (an ungrammatical "Triggers whatever" in `upstream-contribution-workflow`) is fixed in a3b6163. Running the fix through its own `local-strict-review` swept every trimmed description for the same kind of slip. It found four more and then one: a pronoun with no clear target, a possessive, a dropped "explicitly", and two narrowings of the history-rewrite clause. The final pass at 2c9d839 is clean. 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…3838) ## Summary The `runtime-secrets` audit dimension never reported a bare `secrets/` directory. A runtime-secrets directory is ignored by design, so it holds no tracked path, and `secret_stem_findings` judged tracked paths only. The check now reads the tracked files that name such a directory instead. - **The bare directory.** `secrets.stems` reads each tracked `.gitignore` and each compose or editor config file (`compose*.yml`, `docker-compose*.yml`, `.vscode/*.json`, `.devcontainer` configs). A compose file is read by its YAML keys, for the paths compose resolves on the host. A `.gitignore` line ignoring a directory named `secrets` (`secrets/`, `/secrets`, `**/secrets/`, `secrets/*`), or a repo-relative `secrets/<name>` path, reports the bare directory with each file naming it. `.secrets` is never matched. A container mount point such as `/run/secrets/<name>` is out of scope, and so is a path that climbs out of the repository. - **A file the repository names but does not track.** Each secret file such a path names, in `secrets/` or `.secrets/`, is judged by the env and secret file rule even though the tree lacks it. A compose path resolves against its own directory, and an editor path against the workspace root. A file the tree tracks, or whose `.example` it tracks, is judged once, from the tree. - **The `.txt` rule.** A `.txt` extension on a file in a secrets directory now breaks the rule, per GOVERNANCE.md "Repo-Scoped Secrets" (a single opaque credential carries no extension). It applies to tracked and named files alike. - **A directory that tracks code is not one.** A reference into a `secrets` directory that tracks more than its own `.gitignore`, `.gitkeep`, `README.md`, templates, and files beside their templates is no signal, since that directory is not ignored. A bare directory's own tracked files report it. - **Only a marked path counts.** A path counts where a `./`, `../`, or workspace variable marks it, or where it is a compose `file:` value. A URL path, a cloud resource name, a Vault path, or a shell `$PWD` is not one. A whole-directory mount counts. A relative editor path reports the directory but judges no file name, since it depends on a task's working directory. - **A `.gitignore` line counts where it ignores the folder or its files by glob**, and an anchored one inside a code directory does not. - A named file is counted once however many files name it, and is listed with each of them. - A signal file whose read returns no text is reported as undecided rather than read as clean. The issue's constructed case is the end-to-end test in `tests/test_audit.py`. The parser and regex edges are in `tests/test_audit_helpers.py`. `spec/project-types.json` already states both the bare directory and the `.txt` file as drift, so its assert text needs no change. ## Local review findings Every `introduced` finding the local passes raised is fixed or filed, through `266f99f6`. The last pass, at `266f99f6`, raised one finding, deferred: - `spec/audit.py:543`, `introduced`: a `.gitignore` line with a character class in the folder segment, such as `[Ss]ecrets/`, is not matched. Deferred to #3842, which covers the same case-insensitive match. ## Declined findings - `spec/project-types.json:175`, `introduced`: a host-side home path such as `~/.secrets/Api_Token.txt` is not judged. Declined. `AUDIT.md` section 7's runtime-secrets bullet leaves host-side names to hand judgment: "The rest is judged by hand. ... A host-side name is judged where the repo's docs and scripts spell it out, never by reading a host." ## Pre-existing findings, filed - #3841: `Run.read_text` reads by branch name while the tree is listed at the head commit, so a push between the two turns a repository's result into one ERROR. This change adds reads through it. ## Follow-ups filed - #3842: a capitalized `Secrets/` or `.Secrets/` directory, or a `[Ss]ecrets/` ignore line, is matched by no part of the check. - #3840: a multi-file compose set, such as a devcontainer's `dockerComposeFile` list, resolves against its first file. This check resolves each file against its own directory. - #3837: `AUDIT.md` section 7's runtime-secrets bullet and the `spec/checks.json` `secrets.stems` description still describe the narrower check. Both are outside this change's files. Closes on promotion: #3719 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
|
Important
This repository does not receive automatic reviews because it has fewer than 10 stars.
|
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #3843 +/- ##
==========================================
+ Coverage 82.72% 83.30% +0.57%
==========================================
Files 29 29
Lines 9629 9937 +308
==========================================
+ Hits 7966 8278 +312
+ Misses 1663 1659 -4
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
There was a problem hiding this comment.
🟡 Changes recommended
Critical pre-commit loop bugs can hang processing and prevent the prose gate from running, and the fidelity model still contains a repository-specific reference that must be removed.
3 open issues
What changed in this PR
This promotion consolidates the develop changes into main, improving review gating, local hook scoping, fidelity auditing, and generated skill validation.
Changes:
- Holds merges while hosted review checks remain pending and improves Qodo and Copilot finding handling.
- Adds staged-path and generated-tree handling to prose hooks, plus related documentation.
- Adds intent-fidelity, secret-directory, GraphQL null-node, and skill-description validation.
Review findings:
- Remove the repository issue reference from
spec/fidelity-model.md. - Make the new staged-path pipelines failure-safe in both Husky hooks.
| File | Description |
|---|---|
| tests/test_release_guards.py | Updated as part of this pull request. |
| tests/test_pr_review_copilot.py | Updated as part of this pull request. |
| tests/test_pr_merge.py | Updated as part of this pull request. |
| tests/test_pr_cleanup.py | Updated as part of this pull request. |
| tests/test_handoff.py | Updated as part of this pull request. |
| tests/test_gh_graphql.py | Updated as part of this pull request. |
| tests/test_fidelity_text.py | Updated as part of this pull request. |
| tests/test_carry.py | Updated as part of this pull request. |
| tests/test_build_dist.py | Updated as part of this pull request. |
| tests/test_audit.py | Updated as part of this pull request. |
| STANDUP.md | Updated as part of this pull request. |
| spec/validate.py | Updated as part of this pull request. |
| spec/fidelity-model.md | Updated as part of this pull request. |
| spec/fidelity_text.py | Updated as part of this pull request. |
| spec/checks.json | Updated as part of this pull request. |
| scripts/reviewers/copilot.json | Updated as part of this pull request. |
| scripts/pr_review_rounds.py | Updated as part of this pull request. |
| scripts/pr_review_answers.py | Updated as part of this pull request. |
| scripts/pr_merge.py | Updated as part of this pull request. |
| scripts/pr_cleanup.py | Updated as part of this pull request. |
| scripts/gh_graphql.py | Updated as part of this pull request. |
| scripts/build_dist.py | Updated as part of this pull request. |
| registry/repos.json | Updated as part of this pull request. |
| catalog/snippets/workflows/run-periodic-codegen-pull-request.yml | Updated as part of this pull request. |
| catalog/snippets/workflows/README.md | Updated as part of this pull request. |
| catalog/snippets/workflows/merge-bot-pull-request.yml | Updated as part of this pull request. |
| catalog/snippets/pre-commit/README.md | Updated as part of this pull request. |
| catalog/snippets/pre-commit/.pre-commit-config.yaml | Updated as part of this pull request. |
| catalog/snippets/husky/README.md | Updated as part of this pull request. |
| catalog/snippets/husky/pre-commit | Updated as part of this pull request. |
| .husky/pre-commit | Updated as part of this pull request. |
| .gitignore | Updated as part of this pull request. |
| .github/skills/workflow-ci-contract/SKILL.md | Updated as part of this pull request. |
| .github/skills/upstream-contribution-workflow/SKILL.md | Updated as part of this pull request. |
| .github/skills/skill-lifecycle/SKILL.md | Updated as part of this pull request. |
| .github/skills/shell-codestyle/SKILL.md | Updated as part of this pull request. |
| .github/skills/session-handoff/SKILL.md | Updated as part of this pull request. |
| .github/skills/repo-worktree/SKILL.md | Updated as part of this pull request. |
| .github/skills/python-codestyle/SKILL.md | Updated as part of this pull request. |
| .github/skills/pr-review-conduct/SKILL.md | Updated as part of this pull request. |
| .github/skills/merge-and-release/SKILL.md | Updated as part of this pull request. |
| .github/skills/git-commit-conventions/SKILL.md | Updated as part of this pull request. |
| .github/skills/drive-pr/SKILL.md | Updated as part of this pull request. |
| .github/skills/dotnet-codestyle/SKILL.md | Updated as part of this pull request. |
| .github/skills/decision-triage/SKILL.md | Updated as part of this pull request. |
| .github/skills/copilot-instructions-keeper/SKILL.md | Updated as part of this pull request. |
| .github/skills/comment-and-doc-style/SKILL.md | Updated as part of this pull request. |
| .github/skills/check-this-repo/SKILL.md | Updated as part of this pull request. |
| .github/skills/carried-instruction-file-guard/SKILL.md | Updated as part of this pull request. |
| .github/skills/branching-and-release-model/SKILL.md | Updated as part of this pull request. |
| .github/skills/audit-a-repo/SKILL.md | Updated as part of this pull request. |
| .github/actions/prose-gate/prose-gate.json | Updated as part of this pull request. |
| .claude-plugin/fleet-skills/skills/workflow-ci-contract/SKILL.md | Updated as part of this pull request. |
| .claude-plugin/fleet-skills/skills/upstream-contribution-workflow/SKILL.md | Updated as part of this pull request. |
| .claude-plugin/fleet-skills/skills/skill-lifecycle/SKILL.md | Updated as part of this pull request. |
| .claude-plugin/fleet-skills/skills/shell-codestyle/SKILL.md | Updated as part of this pull request. |
| .claude-plugin/fleet-skills/skills/session-handoff/SKILL.md | Updated as part of this pull request. |
| .claude-plugin/fleet-skills/skills/repo-worktree/SKILL.md | Updated as part of this pull request. |
| .claude-plugin/fleet-skills/skills/python-codestyle/SKILL.md | Updated as part of this pull request. |
| .claude-plugin/fleet-skills/skills/pr-review-conduct/SKILL.md | Updated as part of this pull request. |
| .claude-plugin/fleet-skills/skills/merge-and-release/SKILL.md | Updated as part of this pull request. |
| .claude-plugin/fleet-skills/skills/git-commit-conventions/SKILL.md | Updated as part of this pull request. |
| .claude-plugin/fleet-skills/skills/drive-pr/SKILL.md | Updated as part of this pull request. |
| .claude-plugin/fleet-skills/skills/dotnet-codestyle/SKILL.md | Updated as part of this pull request. |
| .claude-plugin/fleet-skills/skills/decision-triage/SKILL.md | Updated as part of this pull request. |
| .claude-plugin/fleet-skills/skills/copilot-instructions-keeper/SKILL.md | Updated as part of this pull request. |
| .claude-plugin/fleet-skills/skills/comment-and-doc-style/SKILL.md | Updated as part of this pull request. |
| .claude-plugin/fleet-skills/skills/check-this-repo/SKILL.md | Updated as part of this pull request. |
| .claude-plugin/fleet-skills/skills/carried-instruction-file-guard/SKILL.md | Updated as part of this pull request. |
| .claude-plugin/fleet-skills/skills/branching-and-release-model/SKILL.md | Updated as part of this pull request. |
| .claude-plugin/fleet-skills/skills/audit-a-repo/SKILL.md | Updated as part of this pull request. |
| .claude-plugin/fleet-skills/.source-digests/workflow-ci-contract | Updated as part of this pull request. |
| .claude-plugin/fleet-skills/.source-digests/upstream-contribution-workflow | Updated as part of this pull request. |
| .claude-plugin/fleet-skills/.source-digests/skill-lifecycle | Updated as part of this pull request. |
| .claude-plugin/fleet-skills/.source-digests/shell-codestyle | Updated as part of this pull request. |
| .claude-plugin/fleet-skills/.source-digests/session-handoff | Updated as part of this pull request. |
| .claude-plugin/fleet-skills/.source-digests/repo-worktree | Updated as part of this pull request. |
| .claude-plugin/fleet-skills/.source-digests/python-codestyle | Updated as part of this pull request. |
| .claude-plugin/fleet-skills/.source-digests/pr-review-conduct | Updated as part of this pull request. |
| .claude-plugin/fleet-skills/.source-digests/merge-and-release | Updated as part of this pull request. |
| .claude-plugin/fleet-skills/.source-digests/local-strict-review | Updated as part of this pull request. |
| .claude-plugin/fleet-skills/.source-digests/git-commit-conventions | Updated as part of this pull request. |
| .claude-plugin/fleet-skills/.source-digests/drive-pr | Updated as part of this pull request. |
| .claude-plugin/fleet-skills/.source-digests/dotnet-codestyle | Updated as part of this pull request. |
| .claude-plugin/fleet-skills/.source-digests/decision-triage | Updated as part of this pull request. |
| .claude-plugin/fleet-skills/.source-digests/copilot-instructions-keeper | Updated as part of this pull request. |
| .claude-plugin/fleet-skills/.source-digests/comment-and-doc-style | Updated as part of this pull request. |
| .claude-plugin/fleet-skills/.source-digests/check-this-repo | Updated as part of this pull request. |
| .claude-plugin/fleet-skills/.source-digests/carried-instruction-file-guard | Updated as part of this pull request. |
| .claude-plugin/fleet-skills/.source-digests/branching-and-release-model | Updated as part of this pull request. |
| .claude-plugin/fleet-skills/.source-digests/backlog-burndown | Updated as part of this pull request. |
| .claude-plugin/fleet-skills/.source-digests/audit-a-repo | Updated as part of this pull request. |
| .claude-plugin/fleet-skills/.source-digests/agent-conduct | Updated as part of this pull request. |
| .agents/skills/workflow-ci-contract/SKILL.md | Updated as part of this pull request. |
| .agents/skills/upstream-contribution-workflow/SKILL.md | Updated as part of this pull request. |
| .agents/skills/skill-lifecycle/SKILL.md | Updated as part of this pull request. |
| .agents/skills/shell-codestyle/SKILL.md | Updated as part of this pull request. |
| .agents/skills/session-handoff/SKILL.md | Updated as part of this pull request. |
| .agents/skills/repo-worktree/SKILL.md | Updated as part of this pull request. |
| .agents/skills/python-codestyle/SKILL.md | Updated as part of this pull request. |
| .agents/skills/pr-review-conduct/SKILL.md | Updated as part of this pull request. |
| .agents/skills/merge-and-release/SKILL.md | Updated as part of this pull request. |
| .agents/skills/git-commit-conventions/SKILL.md | Updated as part of this pull request. |
| .agents/skills/drive-pr/SKILL.md | Updated as part of this pull request. |
| .agents/skills/dotnet-codestyle/SKILL.md | Updated as part of this pull request. |
| .agents/skills/decision-triage/SKILL.md | Updated as part of this pull request. |
| .agents/skills/copilot-instructions-keeper/SKILL.md | Updated as part of this pull request. |
| .agents/skills/comment-and-doc-style/SKILL.md | Updated as part of this pull request. |
| .agents/skills/check-this-repo/SKILL.md | Updated as part of this pull request. |
| .agents/skills/carried-instruction-file-guard/SKILL.md | Updated as part of this pull request. |
| .agents/skills/branching-and-release-model/SKILL.md | Updated as part of this pull request. |
| .agents/skills/audit-a-repo/SKILL.md | Updated as part of this pull request. |
Review effort: Lite
…f Filing Issues (#3846) ## Summary Until now, filing an issue was always an acceptable way to dispose of a finding. backlog-burndown's file claim and its prose bound ("the next issue, filed") made filing the required route for a doc describing a group's own change. This PR closes those routes. A fix that can land in the pull request already open now lands there. Filing is kept for work that really needs its own lane. - **pr-review-conduct, outcome 4** (drive-pr carries it as an include): - An `introduced` finding is never filed. - An edge case on new code is fixed where its deployment can produce the input. Otherwise it is declined under Trust Boundaries. - A `pre-existing` correctness defect in a file the diff touches is fixed in this PR, within the same two-round budget. This uses the same file grain as the class sweep, and Merge Gate item 3 excepts these defects from the canonical tracker. - Only a `pre-existing` correctness defect **in code that runs**, elsewhere, is filed. - An edge case on new code follows Trust Boundaries' whole fix-or-decline test. - The open-ended "Any other deferral is filed" catch-all is gone. The only filings left are a design finding's untouched-file sites (one issue, per "Triaging findings") and a fix belonging to another repository. - **GOVERNANCE.md, Verification Discipline** (agent-conduct carries it as an include): the doc, manifest entry, or rule sentence describing changed behavior is a sibling the change put in disagreement. It is fixed in the change, not filed. - **backlog-burndown**: - Claims predict the docs that describe each fix. - A grown claim into an unclaimed file is granted at once. A grown claim into a held file is sequenced: the requester's PR waits for the holder's merge, and parks behind a holder that is stopped. A mutual wait is adjudicated as a conflict. A file on an unclaimed branch stops for the maintainer. - The prose bound admits units the change put in disagreement. - Growth into carried canonical content or a design change under a worker below `frontier` re-tiers the group, so that work never falls to a lower tier. - Hub workers read develop's copy of any skill develop has changed. This closes the stale-installed-skill gap behind the self-written reports on #3835, #3836, and #3838. - **local-strict-review**: the reviewer brief stops asking for wording, style, or consolidation remarks on text the diff did not write. Those were dropped anyway, so this only cuts output and disposal tokens. Maintainer decisions, 2026-11-02: - Lever 1: agreed. - Lever 2: file only code that runs. - In-file pre-existing defects: fix them in the PR. - Lever 3: rejected. ## Open `introduced` findings (loop stopped by the maintainer) There were twelve recorded local passes at the `frontier` tier, raising 10, 9, 8, 6, 3, 3, 5, 4, 4, 2, 6, and 3 findings. Nearly every finding was against text an earlier round had written. Of pass 11's six findings, 1 to 5 were fixed in d70a96e. Finding 6, which asked to revive the automatic re-tier, is declined: that hand-off drew new findings in every round, so it was replaced on purpose with the existing stop for the maintainer. By the maintainer's call, the last pass's findings get no further edit round. On 2026-11-02 the maintainer accepted both as known gaps (outcome 3): 1. `introduced`, backlog-burndown "Choosing the Worker's Model Tier": the tier-stop paragraph's "claim comment is extended first" does not repeat re-verify's limit to a file no branch holds. Read alone, it could extend a claim over a file another group holds. 2. `introduced`, pr-review-conduct outcome 4, last bullet: where the other repository's fix goes to the maintainer, no upstream issue exists for the local blocker record to name. ## Pre-existing findings, not filed - `pre-existing`, local-strict-review "The Carried-Content Sweep" with pr-review-conduct outcome 4: a sweep pass's findings are on units the sweep branch has not edited yet, so they route to the per-unit tracker rather than to a fix. - `pre-existing`, pr-review-conduct outcome 1 and "Triaging findings": the untouched-file sites of a design finding are routed two ways. One route files them as one issue, and the other drops them as a consolidation on untouched text. ## Replay of the 25 issues the run filed | Category | Issues | Under these rules | | --- | --- | --- | | Adjacent doc outside the claim | #3804, #3805, #3808, #3810, #3817, #3820, #3837 | Fixed in the originating PR through the predicted or granted claim. #3810 is a consolidation and #3817 is dead code, so each is fixed if in a touched file and otherwise dropped or listed | | Old defect next to the diff | #3813, #3821, #3824, #3825, #3833, #3834, #3841, #3844 | Fixed where it sits in a touched file or unit. Still filed only if it is code that runs elsewhere, which is about #3821, #3824, and #3841. #3833 keeps its canonical tracker | | Edge case past the done-definition | #3806, #3827, #3830, #3840, #3842 | `introduced`, so fixed in the PR or declined. #3830 is a contradiction the fix wrote and is never filed | | Decision | #3812, #3814, #3815, #3826 | Unchanged, still `decision` issues | Expected result for the same run: about 7 issues filed instead of 25. Closes on promotion: #3845 ## Verification - `scripts/build_dist.py --check`: generated distributions are current. - `prose_lint.py --diff origin/develop`: clean. - `spec/validate.py`: OK. - `repo_gate.py --check eol`: 0 issues. - Docker markdownlint and cspell: 0 issues. - local-strict-review: twelve passes recorded at the `frontier` tier. The receipt covers the pushed head. 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
## Summary Fixes a finding Copilot raised on promotion PR #3843, against the description length gate #3839 added. `skill_description` in `scripts/build_dist.py` returned None ("states no description") for any frontmatter that was not a mapping. A `SKILL.md` whose frontmatter parsed to a list or a scalar was therefore never measured, and it got past the 1024-character gate. `skill-lifecycle` and `scripts/README.md` both say the gate refuses "frontmatter too malformed to measure one". The code now does the same: frontmatter that is not a mapping raises `frontmatter is not a mapping`, as unclosed and invalid frontmatter already did. Empty frontmatter still reads as stating no description. - `scripts/build_dist.py`: refuse non-mapping frontmatter, and keep returning None for empty frontmatter. - `tests/test_build_dist.py`: a refusal case for list frontmatter, and a not-measured case for empty frontmatter. ## Verification - `python -m unittest discover -s tests -p test_build_dist.py`: 99 tests OK. - `ruff check` and `ruff format --check` on both files: clean. - `scripts/build_dist.py --check`: generated distributions are current. - Every `SKILL.md` under `.agents/skills/` opens with mapping frontmatter, so no existing skill newly fails the build. - local-strict-review: two passes recorded at the `frontier` tier. The first raised one finding (no test covered the empty-frontmatter branch), fixed in ce00bf3. The second raised none. The receipt covers the pushed head. 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
There was a problem hiding this comment.
🔵 Needs a closer look
The change affects GitHub review gating, GraphQL transport, and commit hooks, while live check/reviewer behavior and hook execution remain unvalidated beyond modeled unit tests.
No open issues 🎉
1 resolved since last review
Review effort: Lite
|
Declining the
Every constituent pull request also merged with full coverage: an attested local pass, or a Copilot file table naming exactly its changed files. |
…er Tracker Items (#3849) Promotes the changes merged into `develop` since promotion #3843 to `main`. - [#3848](#3848) uploads the .NET unit-test coverage in `validate-task.yml` under a fixed `dotnet` Codecov flag, beside the Python legs' own flags, with a release-guard test pinning it. It also ticks the PlexCleaner Stage 3 and Stage 4 items in `docs/reusable-workflows.md` and adds a ticked Stage 5 item, after re-checking PlexCleaner `main`. - [#3850](#3850) answers this promotion's Copilot finding, naming the upload on both sides of that guard test's docstring. Fixes #3834 Fixes #3819 🤖 Generated with [Claude Code](https://claude.com/claude-code)



Promotes the changes merged into
developsince promotion #3797 tomain, the end of an away-mode backlog burndown.// lang=json,language=) from the prose gate's comment rules as a tool directive.local_review.py recordtake--report, the file the reviewer wrote, bind the pass to its sha256, and derive the finding count from it.statusgainsreportPath.pr_review.py mergewhile a hosted review is still running on the head, and routes a declined Qodo finding to a dismiss request.intentRefby the section its anchor names, and lets adriftNoteacknowledge a verified intent advisory.gh_graphqltransport asNullNodeError.GOVERNANCE.mdand names the aggregator's expression form.backlog-burndownorchestrator, aligns the abandoned-branch delete, and holds a grown agent-safety set.--skip-generatedso a named file inside a generated tree is skipped.Open findings:label, drops the duplicatenull_node, and matchesscripts/README.mdto the Merge Gate.GIT_DIRfrom rerooting the prose gate's subdirectory git reads.backlog-burndowna disposition for a worker's standalone clone and keeps the agent-safety hold past a park.carry.py check-sectionsrefuse whatapply-sectionswould, and name an open fence.runtime-secretsaudit dimension detect a baresecrets/directory from the tracked files that name it.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