fix(ship): carry NEXT names the push and CI wait; docs give the real diff-plus-blocking cap (GST-1346 follow-up) (#4006)

* docs: diff-plus-blocking stops at the third fix cycle; carry order matches ship Step 1 (Step 14.5)

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

* fix(ship): carry NEXT names the push and CI wait; docs give the real diff-plus-blocking cap (GST-1346 follow-up)

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

* fix(ship): carry route says push before the dry-run and that CI_CARRY: none is not a stop

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 01:00:05 +00:00
1 parent 7c918efcda
commit 6bdafb2cf5
6 files changed
+44 -16

No files matched your search

+2 -2
View File
@@ -562,7 +562,7 @@ puts in the receipt:
| Field | Meaning |
|---|---|
| `review_loop.passes` | full review passes this ship ran (1 or 2; up to 3 under `review_scope: all`) |
| `review_loop.passes` | full review passes this ship ran (1 or 2; up to 3 under `review_scope: diff-plus-blocking` or `all`) |
| `review_loop.blocking_found` | every blocker found (GST-951): the rows' blocking findings (classified, or an unclassified CRITICAL), plus each ledger CRITICAL (as collected or judged) that no row finding from the same reviewer at the same path:line accounts for (each row finding pairs with one per ledger), so an adversarial, outside-voice or not-yet-classified blocker counts and one whose newest classification is below the floor does not. An unreadable ledger, merged file or judged set, or a judged set changed since `judge`, is `REVIEW_SCOPE_LEDGER`, never a count |
| `review_loop.unrecorded` | blockers found that no row gives a disposition (fixed, deferred, noted, skipped, a gate answer); absent when 0 |
| `review_loop.unbound` | what could not bind (GST-1168), each printed as `REVIEW_SCOPE_UNBOUND: <what>: <why>`: a row with no start token and no readable time, a `pN` ledger with no review row of pass N, a row finding's `ledger_id` no ledger collected, a `Review-Finding` trailer naming nothing; absent when 0 |
@@ -638,7 +638,7 @@ difference passed a scoped review.
| Value | Pass 1 | Blocking finding in unchanged code | Pass 2+ | Cap |
|---|---|---|---|---|
| `frozen` (default) | the diff | filed as an issue, listed as deferred | the range since the previous pass, blocking only | 2 rounds (a fix-check is one) |
| `diff-plus-blocking` | the diff | fixed in this PR | the range since the previous pass, blocking only | 2 rounds (a fix-check is one) |
| `diff-plus-blocking` | the diff | fixed in this PR | the range since the previous pass, blocking only | 3 fix cycles (Step 9.4 runs the review plan's two-round cap only under `frozen`) |
| `all` | everything | fixed in this PR | the whole diff, every finding | 3 fix cycles |
`diff-plus-blocking` is the fallback for a repository where deferred issues pile
+14 -9
View File
@@ -1573,9 +1573,10 @@ The floor and the pass structure are stated once, in
[review-policy.md](review-policy.md); this section documents the knob.
`review_scope: diff-plus-blocking` is the documented fallback for a repository
where deferred issues pile up: the same passes, range and cap as `frozen`, but a
where deferred issues pile up: the same passes and range as `frozen`, but a
blocking finding in code the PR did not change is `scope: fix` in this PR
instead of `defer`. `review_scope: all` is the opt-back to the earlier loop:
instead of `defer`, and the loop stops at Step 9.4's third fixing cycle
(`CYCLES >= 3`): only `frozen` runs the review plan's two-round cap. `review_scope: all` is the opt-back to the earlier loop:
every finding is `scope: fix` on every pass, reviewers read the whole diff each
time, and only the three-cycle fix cap stops the loop. `show-effective` prints the choice as a `ship: review_scope …`
line, and the codes a refusal ends with are
@@ -1783,21 +1784,25 @@ carried receipt like any other.
**Delta /ship: eligibility, then checks, then publication.** `/ship` Step 1 runs
the carry when the branch has an open PR, in this order, and never reruns the
review loop for a carried receipt:
review loop or Step 14.5's docs audit for a carried receipt:
1. `gstack-ship-receipt carry --pr <n> --base <base> --dry-run`. `CARRY: would
1. At the pushed HEAD (carry refuses an unpushed one, `--dry-run` included),
`gstack-ship-receipt carry --pr <n> --base <base> --dry-run`. `CARRY: would
carry` is eligibility only; a refusal continues the full ship, except
`RECEIPT_CARRY_SCOPED_REVIEW`, for which `gstack-ship-preflight` prints
`NEXT: carry route`: fix what the base merge broke, commit and push, run carry
without `--dry-run` to write the packet, have one fix-check reviewer read it,
then carry with `--scoped-review <record.json>` and `--pregate`. A blocking
finding continues the full ship.
2. At that pinned HEAD: the CHANGELOG check, the size-cap check the policy
declares, and `gstack-pregate` (the remote tests stage when
`requires_remote` asks for it). A failing check, or HEAD moving, continues
the full ship from Step 2.
2. Fix what the base merge broke (the CHANGELOG, the size caps the policy
declares) and commit, `gstack-ship-release push --branch <branch-name> --early`,
`gstack-ci-carry wait --base <base>` (`CI_CARRY: none` is not a stop), then
`gstack-pregate --allow-repo-commands` at that pinned HEAD (the remote tests stage when `requires_remote` asks for it).
A failing check, or HEAD moving after the pregate, continues the full ship
from Step 2.
3. `gstack-ship-receipt carry --pr <n> --base <base> --pregate <pregate.json>`
rebinds the receipt with a pregate current for HEAD and ends the run.
rebinds the receipt with a pregate current for HEAD and ends the run with its
`GSTACK_RESULT:` line.
A receipt that records a pregate never rebinds without a current one:
`carry` without `--pregate` (or with a stale one) is
+8 -4
View File
@@ -171,7 +171,7 @@ unpushed head is `QUEUE_RECEIPT_STALE` to the queue.
The receipt's `review_loop` object, written from `gstack-review-scope summary
--since <REVIEW_SINCE>`, is the loop's machine-readable outcome: `passes`
(Step 9 full passes this ship ran, 1 or 2; up to 3 under `review_scope: all`),
(Step 9 full passes this ship ran, 1 or 2; up to 3 under `review_scope: diff-plus-blocking` or `all`),
`blocking_found` (every blocker the readers found, rows and dispatch ledgers
both, [review-policy.md](review-policy.md#what-the-receipt-records)), `deferred`
(blocking findings in unchanged code filed as issues, Step 11's late defers
@@ -252,9 +252,13 @@ missing voice never reads as converged or as an unavailable Codex CLI.
### The carry across a base merge or a test-only fix
When the parent merges the base in or fixes a test after the receipt was
written, it runs `gstack-ship-receipt carry --pr <n> --base <base> --dry-run`
instead of another /ship; on `CARRY: would carry` it runs `gstack-pregate` at HEAD
and reruns carry with `--pregate .gstack/tmp/pregate.json`. `CARRY: carried from <old> to <new> (<reason>)` plus the
written, it pushes that commit with `gstack-ship-release push --branch <branch-name> --early`
(carry refuses an unpushed HEAD, `--dry-run` included), then runs
`gstack-ship-receipt carry --pr <n> --base <base> --dry-run` instead of another
/ship; on `CARRY: would carry` it commits and pushes any fix the same way, waits with
`gstack-ci-carry wait --base <base>` (`CI_CARRY: none` is not a stop), runs `gstack-pregate --allow-repo-commands`
at HEAD and reruns carry with `--pregate .gstack/tmp/pregate.json`, the order
`gstack-ship-preflight`'s carry route prints. `CARRY: carried from <old> to <new> (<reason>)` plus the
`GSTACK_RESULT: skill=ship status=complete head=<sha> run=<dir>` line means the receipt
now names HEAD with a `carry` object holding the `git range-diff` evidence, and
`ship-run.json` gains `carried_from` and `carry_reason`; the `review_loop`