Skip to content

Promote Develop: Table-Cell Prose Checks, Block-Comment Closers, and Session Prompt Rules - #3667

Merged
ptr727 merged 13 commits into
mainfrom
develop
Oct 30, 2026
Merged

ptr727 merged 13 commits into
mainfrom
develop

Conversation

@ptr727

@ptr727 ptr727 commented Oct 30, 2026 •

Copy link
Copy Markdown
Owner

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

  • #3640 Pins the validator's uvx ruff to a Dependabot-managed manifest.
  • #3642 Counts Copilot findings under the unresolved bullet and the file table.
  • #3644 Aligns the split comment copies and drops the stale counts.
  • #3646 Sends a command meant for the session's input line in a plain message, never in a blocking prompt.
  • #3648 Opens the attended session by cloning a hub checkout of the session's own.
  • #3650 Reads a held package as installed in both Linux host scripts.
  • #3652 States the PowerShell -Help and -h form in the command-line argument rules.
  • #3654 Counts sentences in Markdown table cells for the sentence-length check.
  • #3658 Splits a table row only at an unescaped pipe in the semicolon rule.
  • #3660 Finds table cell boundaries before blanking code spans and quotations.
  • #3662 Names a stale job record on a completed run in pr_review.py.
  • #3665 Keeps a line holding code after a block comment's closer out of comment-added. Its squash commit on develop carries the pull request's original title, which names a heredoc reading. That reading was withdrawn before the merge on decision Decide How the Prose Gate's Heredoc Half of #1664 Proceeds #3666, so a workflow run: heredoc body still reads as code.
  • #3669 Counts each [Pn] marker in a Copilot Findings cell, so a second finding in one cell is no longer dropped from triage. It answers one of the two findings Copilot raised on this promotion. The other is declined with a parser probe in its thread.

Closes #3616
Closes #3610
Closes #3622
Closes #3638
Closes #3604
Closes #3511
Closes #3602
Closes #3456
Closes #3655
Closes #3657
Closes #3362
Closes #1664

🤖 Generated with Claude Code

ptr727 and others added 12 commits October 29, 2026 22:35
## Summary

Implements #3616 as the maintainer decided on it: a Dependabot-managed
ruff pin with no fleet canary.

- **The manifest.** A new `python-tool-pins` composite action carries
`requirements.txt` with `ruff==0.17.3`, the current PyPI release and the
version the hub's own code already passes. Its one step reads that pin
from `$GITHUB_ACTION_PATH` and fails, naming the file, unless exactly
one `ruff==<version>` line is there. It does so without `mapfile`, so
the test also runs under macOS's stock bash 3.2.
- **The validator.** `validate-task.yml` reaches the action as
`$/.github/actions/python-tool-pins`, so it resolves at the reusable
workflow's own commit. A carrier calling the workflow at a hub pin
therefore runs that pin's ruff rather than anything in its own tree.
Both lint-only `uvx` calls, `ruff check` and `ruff format --check`, now
run `uvx "ruff@$RUFF_VERSION"`. A declared pip directory whose venv
installs ruff, and a uv directory, keep their own versions as before.
- **Dependabot.** The hub's `.github/dependabot.yml` gains a `pip` pair,
main and develop, on `/.github/actions/python-tool-pins`, so the next
ruff release opens a hub pull request. The hub's own CI calls the
workflow locally, so that bump is linted against the hub's code before
it merges. It then reaches carriers at a named hub release.
- **Hub-local runs.** The hub's `.husky/pre-commit`, the `OPERATIONS.md`
local block, and its own `.vscode/tasks.json` ruff tasks run `uvx
--with-requirements .github/actions/python-tool-pins/requirements.txt
ruff ...`. That constrains ruff to the pin, so a local run here still
matches CI. A probe with a manifest pinning `ruff==0.15.22` resolved
0.15.22 that way.
- **Docs and spec.** `spec/project-types.json` `python.scripts.uvx`, the
python-codestyle skill (`SKILL.md` and `references/profiles.md`), and
`docs/reusable-workflows.md` now say CI's uvx ruff runs the hub's pin.
mypy, pyright, and coverage still run `@latest`. A carrier's local tools
(the catalog's VS Code and pre-commit snippets, the skill's local
commands) still run ruff `@latest`, since #3616 covers the validator
only. The skill now names the skew that leaves, and says it ends when
the caller's hub pin moves to a release carrying the new version.

The change adds comment lines, so it carries the `comments` label.

Closes on promotion: #3616

## Verification

- `tests/test_release_guards.py` gains
`test_tool_pins_read_exactly_one_ruff_pin`. It runs the action's step
against the hub's own manifest and against constructed manifests: one
with a comment, no pin, a range, and two pins. It also gains
`test_tool_pins_manifest_is_dependabot_tracked_on_both_branches`. The
existing `test_ruff_prefers_a_declared_pip_directory_venv` now expects
`uvx ruff@<pin>`.
- The full unittest suite ran 3105 tests, OK (skipped=7).
- `uvx ruff@0.17.3 check .` and `format --check .` are clean on the hub.
mypy, `repo_gate.py` (composite-actions included), `spec/validate.py`,
`build_dist.py --check`, actionlint, and the prose gate with
`--allow-comments` are clean too.
- The full unittest suite ran again on the final head: 3105 tests, OK
(skipped=7). The hub's own pre-commit hook ran the pinned ruff on each
commit.
- Recorded `local-strict-review` passes ran before each push.
- The first raised two `introduced` findings:
`docs/reusable-workflows.md` and the python-codestyle `SKILL.md` still
described the fallback as `ruff@latest`. Both are fixed in ee4ed5b.
- The second raised four, all fixed in 7180e2f. They were the `mapfile`
read under bash 3.2, a hand-rolled `step_script`, the hub's hook and
`OPERATIONS.md` claiming to match CI while running `@latest`, and the
skill naming the wrong release as the end of the skew.
- The third raised the redundant `skipUnless(shutil.which("bash"))`
decorator, which Copilot raised too. It is fixed in 853ba38.
- The passes on the fix push raised two more, both fixed. One was the
hub's own VS Code ruff tasks still running `@latest`, fixed in b248df8.
The other was the spec and profile text then misdescribing the hub, plus
the hook's and `OPERATIONS.md`'s separate sed readers, fixed in
633642c.
  - The last pass raised one design finding, declined below on evidence.

## Declined findings

- `.github/actions/python-tool-pins/action.yml` read step, `introduced`,
design: the finding says the multi-line half of the one-pin check guards
a failure that is already loud, since a second unkeyed line in
`$GITHUB_OUTPUT` fails the step. Declined. It is one more condition in
the `if` the empty-pin check needs anyway, and the finding itself says
that check stays. Its error names the manifest and the expected
`ruff==<version>` shape, which a runner refusal of a malformed output
line does not. The `two pins` subtest of
`test_tool_pins_read_exactly_one_ruff_pin` pins that message. The
finding's alternative, `uvx --with-requirements` in the validator, would
float silently to latest if the manifest lost its ruff line. So it would
need the same presence check.

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

---------

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

## Summary

A Copilot `ccr-overview-v2` round can open on "Approval recommended" and
state "No open issues". Its narrative can still name findings in a
nested "Review findings remain unresolved:" list and in the file table's
Findings column. The reader counted none of them. So the digest printed
`CLEAN VERDICT HEADLINE` for a round it said counts no finding, and
`wait` exited 0 with nothing pointing at the findings.

- The nested label is now a findings-list spelling in
`scripts/reviewers/copilot.json`.
- The file table's Findings column is read through a new
`table_findings` vocabulary key. One `table_cells` reader replaces
`table_paths`, so the paths and the column come from a single walk.
- A new `Reading.listed` field holds the larger of the list count and
the filled Findings cells, since both name the same findings.
`unthreaded` takes it into account, so `FINDINGS WITH NO THREAD (2)`
prints for the body in #3610.
- `Reading.open` stays the count the round states. So `overview=T/M` and
its `?` reading keep their meaning for the Merge Gate.
- A clean round whose list or table names a finding no longer prints
`CLEAN VERDICT HEADLINE`. A flagging round in the same shape still exits
50 as it did on `develop`.
- No exit code changes. The documented contract keeps `FINDINGS WITH NO
THREAD` advisory.

Closes on promotion: #3610

## Verification

- `tests/test_pr_review_copilot.py` and `tests/test_pr_review.py` gain
constructed cases for the #3610 shape, the list alone, the column alone,
a stated count beside the lists, and a flagging verdict that still exits
50. Each new case fails against the base reader.
- The whole suite passes (`python -m unittest discover -s tests`),
`tests/test_pr_review_size.py` included.
- ruff format, ruff check, mypy, the prose gate, `build_dist.py
--check`, and the eol gate pass.

## Local pass findings

The first push's spent edit budget left four `introduced` findings open.
The next push fixed each:

- `scripts/pr_review_digest.py` dropped the headline under a flagging
verdict carrying a findings list. Fixed in 872b4ed, which also added
the test assertion for it.
- `scripts/pr_review.py` `--help` said the headline alone names what the
round flags. Fixed in 872b4ed.
- Lines this change edited ran past the wrap width. Rewrapped in
872b4ed.
- The flagging wording named only the findings list, while `listed` can
come from the file table alone. The pr-review-conduct skill still called
the headline the one place. Fixed in dd213c6.

A later pass raised one more `introduced` finding, also fixed:

- `scripts/pr_review_copilot.py` the `Reading` docstring said `listed`
is the findings the Findings column names, while a cell counts once
however many it names. Fixed in fc8eb49, which states the possible
undercount. The pass over fc8eb49 raised no finding.

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

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Closes on promotion: #3622

Aligns the comment copies the two parallel sentence-length slices split
differently, drops the stale counts, and puts the Scripts README rollout
wording in the present tense.

- Parameter-scope, native-command, and WSL_UTF8 comments read one
wording across host-setup.
- Removed "four functions", "four scripts", "four lines", and "fourth
script" counts that did not match the code.
- Rewrote past-tense rollout wording in scripts/README.md.
docs/fleet-map.md is untouched.

The change edits comment lines, so it carries the `comments` label.

The local strict review pass raised three findings, all fixed in the
second commit.

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

---------

Co-authored-by: Claude Sonnet 5.5 <noreply@anthropic.com>
… Prompt (#3646)

## Summary

Splits the `GOVERNANCE.md` "Communicating with the User" command bullet
by where the command runs.

- The bullet opens with a placement test. A command runs outside this
session where this session's input line cannot run it or cannot hold its
effect, such as one waiting on typed input, one meant for another host,
or one setting a variable the session reads at launch. Every other one
runs at the session's input line, where that line can run a command.
- A command run outside the session stays in the question's own text, as
before.
- A command run at the session's input line, a `!` line on Claude Code,
never goes in an interactive prompt, since an open prompt blocks the
place it runs. It opens a plain message as a fenced block holding the
`!` line. Any question the turn owes follows as a numbered list, then
any report. The message asks the user to reply once it has run, the turn
ends with no prompt open, and the agent reads back the state or output
on the next message.
- The "Ask every question", "Raise work blocked on the user", and
parked-queue handoff bullets each name such a turn as an exception that
carries its questions in that message. The `session-handoff` skill's own
steps 4 and 5 and "The Parked Decision Queue" say the same.
- The `agent-conduct` and `session-handoff` include copies are
regenerated with `scripts/build_dist.py`.

Closes on promotion: #3638

## Local review findings

Every finding the earlier local passes raised was fixed in a later push.
The last pass, over the current head, raised one `introduced` finding,
declined with evidence below.

1. `introduced`, declined: the pass asked for a fourth example, a
command that never exits such as a foreground server, among the
outside-session cases. The test sentence already states the property,
"where this session's input line cannot run it or cannot hold its
effect", and a process that outlives the line's run is one the line
cannot hold. The examples after it illustrate that property rather than
closing it. A fourth example adds length without changing what the rule
decides.

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

---------

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

- `session-handoff` "The Attended Session" gains a step 0: clone the
hub's `main` into a new directory under the fleet worktree directory as
the session's first command, with the command in the step. That clone is
the checkout `$handoff` runs from, and it is pulled fast-forward before
step 6 writes.
- Step 0 says that wherever the skill was loaded from is never the
session's hub checkout, and leaves task worktrees off a base clone to
`repo-worktree`.
- "Running the Chain" now says `<hub-checkout>` is a hub checkout of the
session's own, never the maintainer's, and drops the in-hub
relative-path exception.
- `unattended-handoff` "The Seats" stops citing that removed exception,
and says a seat clones its own hub checkout on `main` the way step 0
does.
- Distributions regenerated with `scripts/build_dist.py`.

Where a private checkout lives long-term and who removes it stays
#3330's subject, closed under the decision on #3391. This change is
about when the clone is made.

Closes on promotion: #3604

## Local pass findings

The two `introduced` findings the first push listed, the orchestrator
being named as a cloning seat and Picker step 6 forbidding the picker's
own clone, are fixed in 9e0a7de. A further pass over that commit raised
none.

Each unattended round now leaves a picker clone and a worker clone under
the fleet worktree directory. Removing them is the subject #3330 named,
closed under the decision on #3391, so this change sets none.

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

---------

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

`host-setup/linux/install-tools.sh`'s `package_installed` matched dpkg's
whole `${Status}` against `install ok installed`. A held package, whose
status is `hold ok installed`, therefore read as missing and was planned
for install again. `setup-github.sh` already read a hold as installed,
so the two hub copies of the check disagreed.

- **One definition.** Both scripts now carry the same
`package_installed`, which compares `${db:Status-Status}` to
`installed`. Each Linux script stays independently fetchable, per
`host-setup/README.md` "Three Rules This Directory Follows", so the
definition is an identical copy rather than a sourced file. A new test
in `tests/test_setup_github.py` holds the two copies equal, and the
README's list of duplicated helpers now names it.
- **Held Docker conflicts.** With a hold now counted, the Docker
conflict removal would hand a held conflict to `apt-get remove -y`,
which refuses it with exit 100, while a `-s` simulation passes.
`docker_install` now reads `${db:Status-Want}` first. Where any name on
Docker's uninstall list is held, it warns with the `apt-mark unhold`
remedy and returns before removing anything, so `docker` lands in the
failed set. That covers every name on the list, `docker-doc` included,
on purpose. A held `runc` or `containerd` beside an unheld `docker.io`
would otherwise remove the distro engine and then fail the `docker-ce`
install, leaving the host with no engine.

## Verification

- `python3 -m unittest tests.test_install_tools
tests.test_setup_github`: 78 tests pass, with 1 skipped. The new tests
fail against the base branch's `install-tools.sh`.
- Run in a throwaway `debian:13` container with `less` and `runc`
installed and held. Both scripts read the held `less` as installed.
`apt_missing` does not plan either held package. `docker_install`
reports the held `runc`, returns 1, and leaves it installed. In the same
container, `apt-get remove -y` on the held `runc` exits 100 and `apt-get
remove -s` exits 0, which is why a dry run is not the evidence.
- `docker_lint.py` passes shellcheck, shfmt, editorconfig-checker,
markdownlint, and cspell. The pre-commit hook passes ruff, mypy, and the
prose gate.
- The local strict review ran three passes. The first raised a reuse
finding, fixed by moving the equality test onto the existing
`function_body` helper. The second found that leaving a held conflict
installed while still removing the others could remove the engine, fixed
by restoring refuse-before-removing. The third raised nothing.

Closes on promotion: #3511

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

---------

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

`CODESTYLE.md` "Command-Line Arguments" required every command-line
script to answer `--help` and `-h`. A PowerShell script declaring its
own parameters takes the `-Help` switch with an `h` alias instead, as
the hub's own `host-setup` scripts do, so a literal `--help` reached the
unknown-argument path. Read literally, the rule put every such script in
breach. The maintainer already chose to keep the PowerShell form, so
this changes the rule text only.

- **Bullet 1** now says a PowerShell script declaring its own parameters
answers `-Help` and `-h` and owes no answer to a literal `--help`. A
wrapper forwarding its arguments to another script answers whatever that
script answers.
- **Bullet 2** now says such a script refuses a literal `--help` like
any other unrecognized argument. The binder does that where no parameter
takes the remaining arguments. A script with such a parameter refuses a
dash-prefixed leftover itself, as `install-agents.ps1`,
`install-tools.ps1`, and `setup-wsl.ps1` already do.
- The `python-codestyle` and `shell-codestyle` Skills carry the section
as a generated include, regenerated with `scripts/build_dist.py`.

## Verification

- `uv run --no-project --script scripts/build_dist.py --check` reports
the generated distributions current.
- The `.husky/pre-commit` gate passes, prose lint included.
- Three `local-strict-review` passes ran. The first two found claims the
hub's own scripts contradicted: the remaining-arguments scripts and the
two forwarding wrappers. Both were fixed. The third found nothing.

Closes on promotion: #3602

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

## Summary

The prose gate's `sentence-length` rule read only paragraph inline
units. So a sentence in a Markdown table cell was never counted. A table
cell holds prose as a paragraph does. So each header and body cell is
now its own sentence block, held to the same cap. Two short cells on one
row never join into one sentence. A blockquoted table stays unread, as a
blockquoted paragraph already is.

- `.github/actions/prose-gate/prose_lint.py`: `extract_markdown` builds
a `paragraph` unit for each inline token under `th_open` or `td_open`,
as it already did under `paragraph_open`. `sentence-length` is the only
rule reading `paragraph` units, so no other rule starts reading cells.
- `tests/test_prose_lint.py`: new cases cover a body cell, a header
cell, and two cells on one row judged apart. The structure test drops
its table-row case, the exemption this issue ends.
- The table cells the stricter check reported in hub-authored content
are reworded under the cap. Those cells are in `AGENTS.md`,
`WORKFLOW.md` section 5, the `local-strict-review` skill,
`catalog/snippets/workflows/README.md`, `docs/host-setup.md`,
`host-setup/windows/README.md`, and `scripts/README.md`. The skill
distributions are regenerated with `scripts/build_dist.py`.

**Downstream effect.** Carriers fetch the prose gate from the hub's
`main`. Once this is promoted, a change touching a table row whose cell
holds an over-cap sentence fails the gate there. The composite action
runs diff-scoped, so only touched rows are read. That is the intended
effect of the fix.

Closes on promotion: #3456

## Verification

- The issue's repro now reports both `probe.md:5` (the cell) and
`probe.md:7` (the paragraph).
- `python3 scripts/prose_lint.py .` over the whole tree reports no
violations.
- `python -m unittest discover -s tests` passes on the final head. Its
one failure on the first commit was the old table-row exemption this
change ends.
- `scripts/build_dist.py --check` reports the generated distributions
current. Docker markdownlint, cspell, and editorconfig-checker pass, and
so do ruff check and ruff format.
- Six `local-strict-review` passes ran over the whole diff. Each of the
first five found meaning drift or an ambiguous pronoun in a reworded
cell, fixed in the commit after it. The sixth found no defect in this
change.

## Pre-existing findings, filed

- `.github/actions/prose-gate/prose_lint.py:1103`, `pre-existing`:
`rule_semicolon` splits a table row on raw `|`, so an escaped `\|`
splits a cell and raises a false semicolon finding. Filed as #3655.

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

---------

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

## Summary

The semicolon rule split a Markdown table row on every pipe, so an
escaped `\|` cut its cell in two. A comma before the escaped pipe then
no longer excused the semicolon list after it, and the rule reported
semicolons the whole cell allows.

A cell now ends only at a pipe no backslash escapes, the same escape
markdown-it-py honors when it splits a row.

## Why Not the Parser's Cell Tokens

The issue proposed reading the `th_open` and `td_open` inline tokens the
`sentence-length` rule reads. A local review pass found that loses text
the row split reads today.

- A table inside a blockquote yields no cell units, since the extractor
skips blockquotes, so its rows would be read whole.
- markdown-it-py drops any body cell past the header's column count, so
a semicolon there would never be read.

Keeping the row split and fixing only where it ends a cell closes the
issue's case and keeps both of those.

## Changes

- `.github/actions/prose-gate/prose_lint.py`: `rule_semicolon` splits a
row with `CELL_BREAK`, a pipe no backslash precedes.
- `tests/test_prose_lint.py`: an escaped pipe stays inside its cell, at
top level and inside a blockquote. The row-per-cell test now uses a real
table.
- `scripts/README.md`: states the escape rule.

## Verification

- The issue's repro now reports no `semicolon` violation, and reported
two before.
- The prose gate suite passes, and both new tests fail against the old
split.
- A whole-tree `semicolon` and `sentence-length` run is unchanged.

## Local Review

Three recorded `local-strict-review` passes. The first raised the two
regressions above, which moved the fix off the parser tokens. The second
raised one `introduced` overstatement in the docstring and README, which
was narrowed. The third raised nothing.

The pre-existing finding that the split runs after code spans and
quotations are blanked is filed as #3657.

Closes on promotion: #3655

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

---------

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

## Summary

The prose gate's Markdown rules blanked code spans and quotations across
a whole table row before any cell split. So a cell boundary inside
either one was lost.

- markdown-it-py ends a cell at an unescaped pipe inside a code span.
Blanking the span first merged those two cells, and a comma in the first
then excused a semicolon list in the second.
- An unpaired `"` in one cell, such as an inch mark, paired with a quote
in a later cell. Every cell between them was blanked, so no rule read
them.

The semicolon rule is the one the issue names. A local review pass found
the same defect class in the `dash`, `dupword`, `spelling`, and
`dead-path` rules, all in the same file, so every site moves onto one
helper here.

## Changes

- `.github/actions/prose-gate/prose_lint.py`:
- `TABLE_ROW` and `row_cells` split a row opening with a pipe,
blockquoted or not, at each pipe no backslash escapes.
- `by_cell` applies a blanker one cell at a time and rejoins the cells,
so every pipe that ends a cell stays where it was.
- `prose_lines`, `unit_texts`, and the `dead-path` code-span and link
scans each go through it. `rule_semicolon` reads the blanked row from
`prose_lines` as before.
- `tests/test_prose_lint.py`: both of the issue's cases for the
semicolon rule, a quotation inside one cell still blanked, the
unpaired-quote case for `dash`, `dupword`, and `spelling`, a code span
across cells for `dash`, and two `dead-path` cases.
- `scripts/README.md`: states that a row opening with a pipe is blanked
a cell at a time, and why.

## Verification

- The issue's repro now reports four `semicolon` violations, two on each
body row, and reported none before. The issue says five. Counting the
cells it names gives four: `e; f; g` holds two semicolons and `a; b; c`
holds two.
- The prose gate suite passes, 640 tests. Every new test fails against
the base `rule_semicolon` and blanking code.
- A whole-tree prose gate run is unchanged, with no violations before or
after.

## Local Review

Three recorded `local-strict-review` passes.

- The first raised that the fix covered only the semicolon rule while
the other rules shared the defect. Fixed by the shared helper.
- The second raised a second row check in `rule_semicolon`, a code-span
token scan in `dead-path` still reading the whole row, and docs wider
than the code for rows with no leading pipe. All three were fixed.
- The third raised one finding, that `rule_semicolon` still split a row
itself beside `row_cells`. That push's edit budget was spent, so it went
out listed as open and was fixed on the next push, which now calls
`row_cells`.
- The fourth, over that push, raised nothing.

Closes on promotion: #3657

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

---------

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

A job record can stay `IN_PROGRESS` after its workflow run has
completed. `pr_review.py status` and `wait` read it as a check still
running, so a driver waits on a record no run will update. This change
names that shape and says how to clear it.

- `check_nodes` marks a check run that is still `IN_PROGRESS` while its
suite has completed as `stale`. It also carries the run's id on every
check run.
- `check_shape` judges a stale node `STALE_RECORD` at any age. The
digest prints `CHECK RECORD STALE` with the run id and both remedies, a
re-run of that job or a new head.
- A required stale record on a `BLOCKED` merge takes exit `44`. It is
not settling, so it holds no poll.
- A stale record in an older run counts as finished for superseding. So
while a newer run of the same workflow is still going on the head, the
poll holds instead of exiting `44` on a record that run will replace.

## Changes

- `scripts/pr_review_checks.py` was at 447 lines against its 450
ceiling. So `STUCK_LINES` and `check_lines` move first into a new
reader, `scripts/pr_review_check_lines.py`, which gets its own size
ceiling and its own place in the reader import guards. Their inline
comments move into docstrings.
- The new shape is added to the `--help` text, the
`CHECKS_NOT_MERGEABLE` line, and `scripts/README.md`.
- The test fixture `check()` now gives a running job an in-progress
suite unless a case names the suite's status, since a running job in a
completed suite is the shape this change reads as stale.
- New tests cover the shape at two ages, the digest line, the `44` exit,
the poll that no longer holds, and the newer-run supersede. Each one
fails with the reader change reverted.

## Verification

- `python -m unittest discover -s tests`: 3128 tests OK (7 skipped), run
before the two review-fix commits. After them, the `test_pr_review*`
suites pass (565 tests).
- The pre-commit hook (ruff, mypy, prose gate, eol) passes on every
commit.
- A local strict review ran three rounds. Round 1 found the older-run
supersede gap and a docstring inaccuracy. Round 2 found a dangling `/`
in the superseded line. Both were fixed, and round 3 found nothing.

Closes on promotion: #3362

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

This closes the one #1664 shape that the lexer-based prose gate still
reads as a comment and that a fix can remove without trading a visible
false positive for a silent false negative.

- **A line holding code after a block comment's closer is not a
`comment-added` line.** A new `Line.trailed` flag marks it, and a later
comment on that line does not count as code. Deleting such a line, as
`comment-added` asks, would delete that code and leave the comment
unterminated. The case is covered for CSS, TypeScript, C#, JSONC, HTML,
XML and csproj.
- **A heredoc body in a workflow `run:` block stays code, as develop
reads it.** This is the answer on decision #3666. Eight recorded
`local-strict-review` passes over a hand-built reading of shell each
found new shapes it read as data where bash runs the body as code. Each
such error silently drops a real comment from `comment-added`,
`comment-wrap` and `comment-case`. Develop's reading errs only toward a
visible false positive, which the `comments` label clears. So this shape
from #1664 is recorded as accepted rather than fixed, and the heredoc
commits on this branch are withdrawn by the latest commits rather than
rewritten out of its history.
- **Measured but not reproduced, so not changed:** at develop
`b09f957c`, the TOML `"""` string, the Terraform heredoc, the
extensionless `.husky/pre-push`, and a multi-line double-quoted YAML
scalar under an ordinary key all read clean.
- `scripts/README.md` and the `comment-and-doc-style` skill state the
closer exclusion. The skill's distributions are regenerated.

## Verification

- The full suite passes, with 3123 tests run and 7 skipped.
- The closer test fails against develop, and a variant of `code_follows`
that stops at the second comment on a line fails it in all seven
languages.
- ruff, mypy, the diff-scoped and whole-tree prose gate, `build_dist.py
--check`, and the eol gate are clean.

Closes on promotion: #1664

🤖 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 October 30, 2026 17:06
@coderabbitai

coderabbitai Bot commented Oct 30, 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

Comment @coderabbitai help to get the list of available commands.

@codecov

codecov Bot commented Oct 30, 2026 •

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 81.72%. Comparing base (19548f4) to head (346f0ba).
⚠️ Report is 448 commits behind head on main.

Additional details and impacted files
@@            Coverage Diff             @@
##             main    #3667      +/-   ##
==========================================
+ Coverage   81.69%   81.72%   +0.03%     
==========================================
  Files          26       27       +1     
  Lines        9111     9128      +17     
==========================================
+ Hits         7443     7460      +17     
  Misses       1668     1668              
Flag Coverage Δ
python-3.13 81.72% <100.00%> (+0.03%) ⬆️

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

Two active P1 issues remain: Markdown table parsing mishandles even backslash parity, and review triage can undercount multiple findings in one cell.

2 open issues
What changed in this PR

This pull request promotes the changes merged into develop since promotion #3636 to main, improving validator pinning, Markdown prose checks, review-finding accounting, host setup behavior, session guidance, and synchronized documentation and skills.

Changes:

  • Pins validator Ruff execution through a Dependabot-managed requirements manifest.
  • Extends prose checks to Markdown table cells and handles block-comment closers.
  • Improves Copilot finding parsing, stale-check detection, held-package handling, and session prompt guidance.
  • Updates related tests, documentation, generated skills, and tooling configuration.
  • Three review findings remain concerning Markdown table boundaries, pipe-leading paragraph classification, and multiple findings in one Copilot table cell.
File Description
WORKFLOW.md Rewords workflow test scenarios into shorter sentences.
tests/​test_setup_github.py Tests held-package detection and identical Linux helper definitions.
tests/​test_release_guards.py Tests pinned Ruff resolution and Dependabot tracking.
tests/​test_prose_lint.py Adds table-cell and block-comment parsing coverage.
tests/​test_pr_review_size.py Adds the new check-lines module ceiling.
tests/​test_pr_review_copilot.py Tests findings-list and table-based Copilot findings.
tests/​test_install_tools.py Tests held packages and Docker conflict handling.
tests/​test_bootstrap.py Removes stale script-count wording.
spec/​project-types.json Updates the lint-only Ruff versioning contract.
scripts/​reviewers/​qodo.json Adds the optional table-findings vocabulary field.
scripts/​reviewers/​copilot.json Adds unresolved-list and findings-column parsing.
scripts/​reviewers/​coderabbit.json Adds the optional table-findings vocabulary field.
scripts/​pr_review.py Updates review and stale-check guidance.
scripts/​pr_review_rounds.py Accounts for listed findings in verdict handling.
scripts/​pr_review_digest.py Reports findings from lists and file tables.
scripts/​pr_review_copilot.py Parses Copilot finding lists and file tables.
scripts/​pr_review_checks.py Detects stale jobs in completed workflow runs.
scripts/​pr_review_check_lines.py Centralizes digest text for check states.
OPERATIONS.md Uses the pinned Ruff requirements manifest locally.
host-setup/​windows/​upgrade-host.ps1 Aligns the WSL encoding comment copy.
host-setup/​windows/​setup-wsl.ps1 Aligns script and output-stream comments.
host-setup/​windows/​README.md Updates host-tool comparison wording.
host-setup/​windows/​install-tools.ps1 Removes stale function-count wording.
host-setup/​windows/​install-agents.ps1 Clarifies the nested-scope parameter comment.
host-setup/​README.md Documents the duplicated installed-package helper.
host-setup/​menu.ps1 Clarifies parameter and compatibility comments.
host-setup/​linux/​setup-github.sh Reads dpkg's normalized installed status.
host-setup/​linux/​install-tools.sh Handles held packages before Docker conflict removal.
host-setup/​bootstrap.ps1 Removes stale script and function counts.
GOVERNANCE.md Defines where session-input commands and questions belong.
docs/​reusable-workflows.md Documents the hub-pinned Ruff fallback.
docs/​host-setup.md Updates the uv tool role description.
CODESTYLE.md Documents PowerShell help and unknown-argument behavior.
catalog/​snippets/​workflows/​README.md Splits long workflow table-cell sentences.
AGENTS.md Clarifies routed procedure and skill descriptions.
.vscode/​tasks.json Runs local Ruff through the pinned requirements manifest.
.husky/​pre-commit Aligns the hook with CI's pinned Ruff version.
.github/​workflows/​validate-task.yml Reads and applies the validator's Ruff pin.
.github/​skills/​workflow-ci-contract/​references/​test-methodology.md Regenerates workflow test-methodology prose.
.github/​skills/​unattended-handoff/​SKILL.md Regenerates unattended handoff instructions.
.github/​skills/​shell-codestyle/​SKILL.md Regenerates shell command-line rules.
.github/​skills/​python-codestyle/​SKILL.md Regenerates Python toolchain and command-line rules.
.github/​skills/​python-codestyle/​references/​profiles.md Regenerates Python profile guidance.
.github/​skills/​pr-review-conduct/​SKILL.md Regenerates review-finding handling guidance.
.github/​skills/​local-strict-review/​SKILL.md Regenerates local review guidance.
.github/​skills/​comment-and-doc-style/​SKILL.md Regenerates comment and prose rules.
.github/​skills/​agent-conduct/​SKILL.md Regenerates session communication rules.
.github/​dependabot.yml Adds daily pip updates for the Ruff manifest on both branches.
.github/​actions/​python-tool-pins/​requirements.txt Pins Ruff for validator execution.
.github/​actions/​python-tool-pins/​action.yml Reads and validates the Ruff pin.
.github/​actions/​prose-gate/​prose_lint.py Adds table-cell processing and block-comment closer handling.
.claude-plugin/​fleet-skills/​skills/​workflow-ci-contract/​references/​test-methodology.md Regenerates the Claude workflow reference.
.claude-plugin/​fleet-skills/​skills/​unattended-handoff/​SKILL.md Regenerates the Claude unattended handoff skill.
.claude-plugin/​fleet-skills/​skills/​shell-codestyle/​SKILL.md Regenerates the Claude shell skill.
.claude-plugin/​fleet-skills/​skills/​python-codestyle/​SKILL.md Regenerates the Claude Python skill.
.claude-plugin/​fleet-skills/​skills/​python-codestyle/​references/​profiles.md Regenerates the Claude Python profile reference.
.claude-plugin/​fleet-skills/​skills/​pr-review-conduct/​SKILL.md Regenerates the Claude review-conduct skill.
.claude-plugin/​fleet-skills/​skills/​local-strict-review/​SKILL.md Regenerates the Claude local-review skill.
.claude-plugin/​fleet-skills/​skills/​comment-and-doc-style/​SKILL.md Regenerates the Claude documentation skill.
.claude-plugin/​fleet-skills/​skills/​agent-conduct/​SKILL.md Regenerates the Claude agent-conduct skill.
.claude-plugin/​fleet-skills/​.source-digests/​workflow-ci-contract Updates the workflow skill digest.
.claude-plugin/​fleet-skills/​.source-digests/​unattended-handoff Updates the unattended handoff digest.
.claude-plugin/​fleet-skills/​.source-digests/​shell-codestyle Updates the shell skill digest.
.claude-plugin/​fleet-skills/​.source-digests/​session-handoff Updates the session handoff digest.
.claude-plugin/​fleet-skills/​.source-digests/​python-codestyle Updates the Python skill digest.
.claude-plugin/​fleet-skills/​.source-digests/​pr-review-conduct Updates the review-conduct digest.
.claude-plugin/​fleet-skills/​.source-digests/​local-strict-review Updates the local-review digest.
.claude-plugin/​fleet-skills/​.source-digests/​comment-and-doc-style Updates the documentation digest.
.claude-plugin/​fleet-skills/​.source-digests/​agent-conduct Updates the agent-conduct digest.
.agents/​skills/​workflow-ci-contract/​references/​test-methodology.md Updates the canonical workflow reference.
.agents/​skills/​unattended-handoff/​SKILL.md Updates the canonical unattended handoff skill.
.agents/​skills/​shell-codestyle/​SKILL.md Updates the canonical shell skill.
.agents/​skills/​python-codestyle/​SKILL.md Updates the canonical Python skill.
.agents/​skills/​python-codestyle/​references/​profiles.md Updates the canonical Python profile reference.
.agents/​skills/​pr-review-conduct/​SKILL.md Updates the canonical review-conduct skill.
.agents/​skills/​local-strict-review/​SKILL.md Updates the canonical local-review skill.
.agents/​skills/​comment-and-doc-style/​SKILL.md Updates the canonical documentation skill.
.agents/​skills/​agent-conduct/​SKILL.md Updates the canonical agent-conduct skill.

Review effort: Lite

Comment thread scripts/pr_review_copilot.py Resolved
Answers the Copilot finding raised on promotion #3667 against
`scripts/pr_review_copilot.py`, which #3642 introduced.

- **A Copilot Findings cell now counts each finding it names.** The file
table's Findings column counted one finding per filled cell, so a second
finding in the same cell went uncounted. On a clean-verdict round that
leaves it out of triage. A new per-reviewer vocabulary pattern,
`table_finding_marker`, is `\[P\d\]` for Copilot and empty for
CodeRabbit and Qodo. A filled cell counts each marker it carries, and
once where it carries none, so no count drops below what it was before.
- The `Reading` docstring states the new counting.

## Verification

- The full suite passes, with 3123 tests run and 7 skipped.
- The new subtest, a cell carrying two markers beside a cell carrying
none, counts 3. It fails against develop's adapter, which counts 2.
- ruff, mypy, the diff-scoped prose gate, and the eol gate are clean.

## Dropped findings

- `scripts/pr_review_copilot.py`: a `[Pn]` written inside a code span or
in prose about another finding also counts. The only effect is a higher
count in the digest's no-thread line, a loud failure rather than a
silent one, so it is left as is under "Trust Boundaries and Hardening
Effort".

Closes on promotion: none. The finding was raised on #3667 and has no
issue of its own.

🤖 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.

🟢 Approval recommended

No actionable review findings remain, including coverage of the updated parsing, package-handling, validation, and documentation changes.

No open issues 🎉

2 resolved since last review

Review effort: Lite

@ptr727
ptr727 merged commit 645b7a3 into main Oct 30, 2026
14 checks passed
ptr727 added a commit that referenced this pull request Oct 30, 2026
…mment Rules (#3675)

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

- [#3674](#3674) Exempts a
Python script's PEP 723 metadata block from every comment rule. The
block's lines leave the comment run before any rule reads it, and only a
closed `# /// script` block qualifies.

Closes #3634

🤖 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