fix(ship-receipt): a docs audit after the review no longer forces another review (GST-1429) (#4034)

* fix(ship-receipt): a docs audit after the review no longer voids the review_exclude binding (GST-1429)

Step 14.5's docs audit runs after the review. When it edits, adds or removes a
file under review_exclude_paths, HEAD's selection digest moves and receipt
write refused the reviewed digest. write --docs-post <audit-id>-post.json now
binds the reviewed digest when it is HEAD's selection with the files under a
pattern that changed since the snapshot's HEAD reset to it, each named by the
snapshot with the content HEAD holds and excluded or kept only because code
names it, and every other file selected alike. Anything else still refuses.

The ship driver's Step 19 write omitted --review-exclude, so gbrain #8627
wrote recorded=none; it now passes it, and the refusal says when the flag was
left out.

Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>

* test(review-exclude): keep the GST-1429 in-process receipt read off the real state root

The #8627-shape test called receiptReviewAtHead in-process with the runner's
GSTACK_STATE_ROOT, so its gate scan wrote gate-diff and slug-cache entries
under the real ~/.gstack and CI rejected free-suite shard 9
(FREE_SUBSHARD_REJECTED, run 42017628509). It now points GSTACK_STATE_ROOT at
the fixture like the docs-only gate-tagged test does.

Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>

* fix: pre-landing review fixes

The docs-audit exception reset every changed file under review_exclude_paths
to the post snapshot's HEAD, so a docs edit the review read while it was still
uncommitted was reverted and write refused a normal /ship run. --docs-pre
<Prepare snapshot> now narrows the reset to the files the audit changed, back
to their Prepare content. Snapshot hashes of working-tree bytes also compare
against HEAD's content as it checks out, so autocrlf or a clean filter no
longer reads as an edit after the audit.

Review-Finding: s11/outside-voice#1 reader=outside-voice
Review-Finding: s11/outside-voice#2 reader=outside-voice
Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>

* chore: changelog and TODOS

gstack: ok 1.96.0.0 for=ship
Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>

---------

Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
This commit is contained in:
Garry Tanandcapy-ai[bot] authored and GitHub committed 2026-11-05 12:25:45 +00:00
1 parent b406820980
commit 467be16ab2
11 files changed
+220 -14

No files matched your search

+1
View File
@@ -1476,6 +1476,7 @@ recorded by accident, but it does not read the review ledger: a write given the
the reviewers read that selection.
The queue makes one exception (GST-1408): a receipt with no `review_exclude`, written before the base gained the key, still binds when no file the head changes matches any current pattern, excluded or kept, because its reviewers read the same full diff; a recorded digest that differs, a matched file and an unreadable base policy still refuse.
Carry makes the same exception in both directions (GST-1415): when the digests differ but neither the receipt's recorded selection (no `review_exclude`, or one whose list hash is the empty selection's and whose digest is built from it) nor HEAD's holds a file the PR changed, the receipt carries and records HEAD's digest, so a pattern the base gains, adds, removes or reverts does not void it; a selection that holds a PR file, excluded or kept, and an unreadable base policy still refuse.
Write makes one exception for Step 14.5's docs audit, which runs after the review (GST-1429): with `--docs-post <audit-id>-post.json` it accepts the reviewed digest when that digest is HEAD's selection with every file under a pattern that changed since the snapshot's HEAD reset to it, each such file is uncommitted in the snapshot with the content HEAD holds, each is excluded or kept only because code names it, and every other file is excluded or kept alike on both sides. With `--docs-pre <Prepare snapshot>` only the files whose content changed between the audit's two snapshots reset, to their Prepare content, so uncommitted docs the reviewers read stay as they are; content compares as git stores it or as it checks out (autocrlf, filters). It prints `REVIEW_EXCLUDE_DOCS_AUDIT:` and records HEAD's digest. Any other change to the selection, an audit file the exclusion keeps for another reason, an edit after the snapshot and a missing or unreadable snapshot still refuse.
**Adopting it.** (1) Open a PR on the base that adds the patterns and
`min_gstack_version` at least `REVIEW_EXCLUDE_KEY_VERSION`, and ship it like any
+5
View File
@@ -3496,6 +3496,11 @@ the field without a review. Do not do that. Rerun
run `gstack-review-dispatch verify --ledger <ledger>`, which prints
`REVIEW_EXCLUDE_RECORD: digest=<d>` from the briefs the readers got, then
`gstack-ship-receipt write --pr <n> --review-exclude <d> --body <body>`.
`recorded=none` with a `no --review-exclude was given` note means the write left the flag out: pass the digest
`verify` printed. When the only change since the review is Step 14.5's docs audit editing, adding or
removing files under a pattern, pass that run's `--docs-post <audit-id>-post.json` and its Prepare snapshot as
`--docs-pre` as well instead of rerunning the review; the refusal's `the docs-audit exception does not apply:` clause says why when it cannot
([ship-policy.md](ship-policy.md#review-exclude-paths), GST-1429).
The queue's check skips a receipt with `recorded=none` (written before the base gained
`review_exclude_paths`) when no file the head changes matches any current pattern, so