mirror of
https://github.com/garrytan/gstack.git
synced 2026-10-31 14:06:50 +01:00
v1.91.307.0 ship: proportional review depth (light, standard, full) with depth-checked receipts (#3567)
* feat(ship-receipt): carry across regenerated outputs and onto a reviewed tree after a rebase
A base merge whose only product-side change is a registered output the
pregate regen check proved fresh now carries the receipt. A rebased head
carries when its tree is one the receipt covers and its product patch-id
against the current base matches (RECEIPT_CARRY_TREE_MISMATCH otherwise).
Docs: merge main into a reviewed branch, never rebase it.
Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
* fix: regenerate doctor anchors; type the head_tree assert
Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
* feat(review): proportional /ship review depth (light, standard, full)
Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
* fix: carry sets aside only outputs the regen check proved it recreates
The regen check deletes each touched output in its scratch worktree before
the command runs and records the ones recreated byte-identically as proven.
gstack-ship-receipt carry exempts only those paths, and only when the check
ran origin/<base>'s registry at the commit the carry reads. A file under an
output glob that the generator never writes stays product (pre-landing
review, #3564 pass 1).
Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
* feat(ship-receipt): record review depth, check it in the queue, structured review-loop exception (#3559)
Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
* fix: prove generated outputs in a second regen run; literal names only
The regen verdict comes from the unmodified run again, so a generator that
reads its own registered output (gen-skill-docs and
qa/templates/functional-report-template.md) passes as before; the proof is a
second run with the touched outputs deleted, and its failure only means not
proven. The carry ignores a proven path with glob syntax, since it uses the
paths as pathspec globs (pre-landing review, #3564 pass 2).
Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
* docs: receipt evidence keys and carry record fields; carry refusal fix line
Documentation audit for #3564 (/document-release, audit ship3564-docs-1).
Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
* fix: pre-landing review fixes (depth scan binding, queue scope, carry checks)
- carry refuses a --head other than the checked-out HEAD, a receipt with
gstack_version but no review_depth, and an owner exception that no longer
stands; a dirty or foreign checkout makes the write-time gate scan
unavailable (full)
- the queue binds a gate scan only to the full bound commit and recomputes
the scope signals with gstack-diff-scope <base> --head <sha>, the same
range the writer uses
- the queue checks the review-loop exception's fix-check ancestry
- a package.json version line counts as stamp-only only when it is a bare
version key
- docs and help name what review_depth is and which registry the queue reads
Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
* docs: CHANGELOG for the review fixes; defer two plan items to TODOS
Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
* docs: queue and carry refusal lists name the depth and exception codes
Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
* test: approval-classes summary-line regexes allow the receipt's depth field
Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
* fix(ship-receipt): summary line prints depth before approval
Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
* Revert "test: approval-classes summary-line regexes allow the receipt's depth field"
This reverts commit e9ec264358.
---------
Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
This commit is contained in:
1 parent
de4c694d91
commit
edf4c2e581
43 files changed
+1464
-193
No files matched your search
+86
-20
@@ -128,26 +128,75 @@ applies no edits is its exit too.
|
||||
<a id="review-depth"></a>
|
||||
## Review depth by diff class
|
||||
|
||||
`gstack-review-plan --base <base>` classifies the diff and prints the reviewers
|
||||
the next step dispatches (`REVIEW_CLASS:`, `REVIEW_PLAN:`, one `DISPATCH:` per
|
||||
reviewer with its effort, one `SKIP:` per reviewer left out, `--json`). A mixed
|
||||
diff takes its most restrictive class; a `gstack-diff-scope` error fails closed
|
||||
to code, and code with the gate scan unavailable fails closed to gate.
|
||||
`gstack-review-plan --base <base>` classifies the diff, picks a **depth** and prints
|
||||
the reviewers the next step dispatches: `REVIEW_CLASS:`, `REVIEW_DEPTH: <d>
|
||||
runtime_lines=<n> rules=<hash> reasons=<…> codes=<…>`, `GATE_SCAN: tagged=<n>|unavailable
|
||||
head=<sha>`, one `DEPTH_ERROR:` per input that failed closed, one `DISPATCH:` per reviewer
|
||||
with its effort and a shell-quoted `DIFF_CMD:` (plus `DIFF_EXCLUDE: <name> none` when it
|
||||
reads everything), one `SKIP:` per reviewer left out, and `--json` with the same fields.
|
||||
A plan that fails closed to full exits 0; a diff git cannot list exits 1.
|
||||
|
||||
| Class | What it is | Pass 1 dispatches |
|
||||
**Classes** are unchanged. A mixed diff takes its most restrictive class: **docs**
|
||||
(markdown no agent or gate reads), **tests** (test files and fixtures), **code**
|
||||
(everything else, including every `SKILL.md` and template, markdown in a skill
|
||||
directory, `*/sections/**`, `scripts/resolvers/**`, rule docs, checklists and bins) and
|
||||
**gate** (code plus auth or migration scope, a hunk the gate scan tags, a deleted code
|
||||
or test path, CI workflows, credential, state-root and the paths below).
|
||||
|
||||
**Runtime lines** are the changed lines of code-class paths, minus outputs the base
|
||||
commit's `.gstack/generated.json` registers. Docs, tests and release metadata are never
|
||||
runtime; a `.tmpl` edit plus its regenerated `SKILL.md` counts the template once.
|
||||
|
||||
| Depth | When | Pass 1 dispatches |
|
||||
|---|---|---|
|
||||
| **docs** | markdown no agent or gate reads: README, CHANGELOG, TODOS, how-to and explanation docs | the parent's core checklist only |
|
||||
| **tests** | test files and fixtures only | core plus the testing specialist |
|
||||
| **code** | everything else, including every `SKILL.md` and template, any markdown in a skill directory, `*/sections/**`, `scripts/resolvers/**`, `docs/*-policy.md`, `docs/captain.md`, `docs/unattended.md`, `docs/pregate.md`, checklists, specialists and bins | specialists by scope (as before), the native adversarial pass, the outside voice; the Red Team past 200 lines or after a CRITICAL |
|
||||
| **gate** | code plus auth or migration scope, a hunk the gate scan tags (an RH rule: a relaxed threshold, a skipped test), a deleted code file, CI workflows, credential or state-root paths | code's set, plus the Red Team always, at full effort, and no hit-rate gating |
|
||||
| **light**, runtime 0 | docs and tests only, or only registered outputs | the core checklist only; the outside voice when the diff publishes numbers |
|
||||
| **light**, runtime 1–50 | `SMALL_DIFF_LINES` or fewer runtime lines | core, one **batched** specialist (testing, maintainability and security checklists, the specialists scope selects and the Red Team prompt in one context), the native adversarial pass (kept: backtest inconclusive, GSTA-147 D5) and the outside voice on numbers |
|
||||
| **standard** | more than 50 runtime lines | the code set with every threshold (specialists at 50, simplification at 100, Red Team at 200) read on runtime lines; specialists leave docs and registered outputs out of their diff, and every specialist but Testing leaves tests out too; adversarial and outside voice read everything |
|
||||
| **full** | gate class, or any classifier error | the gate set, unchanged: every reviewer on the whole diff, the Red Team always, no hit-rate gating |
|
||||
|
||||
**Numbers keep the outside voice.** On a docs or tests diff the outside voice is
|
||||
skipped unless the diff publishes numeric claims: `gstack-review-plan` scans the
|
||||
added lines of every path for percentages, money, multipliers, durations, token
|
||||
or credit counts and score, rate or speed claims (`NUMBERS: yes`), and a
|
||||
claim always dispatches it. The claims' fingerprint (`fingerprint=`) is passed
|
||||
back as `--reviewed-numbers`; when a later fix changes a claim, only the
|
||||
outside voice reruns, on the numbers.
|
||||
A fixed blocking finding, a CRITICAL or an open blocker raises a light diff to
|
||||
standard for the next step (`step=standard`): its specialists read at least 50 lines
|
||||
and the Red Team runs, so a raise never dispatches less than light did. A fix-check
|
||||
is the same at every depth.
|
||||
|
||||
**Fail closed.** Each of these makes the depth full and is listed in `errors[]` with
|
||||
the input it could not read, a sanitized cause and the repair: a `gstack-diff-scope`
|
||||
error, a gate scan that is unavailable, a base registry that cannot be read or is
|
||||
invalid (`REGEN_REGISTRY_INVALID` or `REGEN_CONTAINMENT` in `codes=`), a numstat git
|
||||
could not produce, and a binary code-class path (it is never counted as 0 lines).
|
||||
|
||||
**The registry rule.** Only the base commit's registry counts (the merge base's
|
||||
`origin/<base>` commit for live /ship and the current base ref's commit for the queue: `origin/<base>` in `ready`, `<remote>/<base>` in `train build`), and only
|
||||
entries with a `command`, which pregate's regen check proves for that tree. An entry
|
||||
added on the branch never excludes anything; `command: null` entries, vendored code
|
||||
and lockfiles stay runtime. To adopt it, commit an entry like this on the base branch:
|
||||
|
||||
```json
|
||||
{
|
||||
"$schema": "https://github.com/garrytan/gstack/docs/pregate.md#generated-v1",
|
||||
"entries": [
|
||||
{ "id": "skill-docs", "command": "bun run gen:skill-docs --host all", "outputs": ["*/SKILL.md", "*/sections/*.md"], "inputs": ["*/SKILL.md.tmpl", "*/sections/*.tmpl"] }
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
**Gate paths for the machinery itself.** An edit to `lib/review-depth.ts`,
|
||||
`lib/review-collect.ts`, `bin/gstack-review-plan`, `lib/review-scope.ts`,
|
||||
`lib/ship-queue*.ts`, `lib/regen.ts`, `.gstack/generated.json`,
|
||||
`ship/sections/review-army.md.tmpl`, `ship/sections/adversarial.md.tmpl` or
|
||||
`scripts/resolvers/review-army.ts` is gate class: changing how much review runs gets
|
||||
the most review. `rules=` (`DEPTH_RULES`) hashes the deciding constants and patterns,
|
||||
not file bytes, so a comment edit keeps it.
|
||||
|
||||
**`--depth-floor standard|full`** (a /ship prompt flag, passed to every plan run)
|
||||
raises the depth and never lowers it.
|
||||
|
||||
**Numbers keep the outside voice.** At light depth the outside voice runs only when
|
||||
the diff publishes numeric claims: `gstack-review-plan` scans the added lines of
|
||||
prose and data files for percentages, money, multipliers, durations, token or credit
|
||||
counts and score, rate or speed claims (`NUMBERS: yes`). The claims' fingerprint
|
||||
(`fingerprint=`) is passed back as `--reviewed-numbers`; when a later fix changes a
|
||||
claim, only the outside voice reruns, on the numbers. It always reads the whole diff.
|
||||
|
||||
**The adversarial and outside voices run once per invocation** (Step 11, on the
|
||||
final tree), as the last full-pass plan lists them; after they ran, decision 2
|
||||
@@ -166,6 +215,15 @@ default. `gstack-config set review_effort_tiers off` (or `--no-tiers`) runs
|
||||
the audits and watchers at the host default too
|
||||
([model-policy.md](model-policy.md#reasoning-effort)).
|
||||
|
||||
### Behavior changes
|
||||
|
||||
- A tests-only diff (runtime 0) no longer dispatches the Testing specialist: the
|
||||
core checklist and the gate scan review it.
|
||||
- Light no longer runs a separate Red Team: the batched specialist carries its
|
||||
prompt. At runtime 0 light runs no adversarial pass either.
|
||||
- Standard reads thresholds on runtime lines, not the whole diff, so a test-heavy
|
||||
diff with few runtime lines dispatches fewer specialists.
|
||||
|
||||
## What the receipt records
|
||||
|
||||
`gstack-review-scope summary --since <REVIEW_SINCE>` reads this invocation's
|
||||
@@ -190,6 +248,13 @@ specialist stats they record). The summary line appends `fix_checks=`,
|
||||
`dispatches=` and `est_input_tokens=` only when they were recorded, so an old
|
||||
loop prints the old line.
|
||||
|
||||
Beside the counters, `write` records `review_depth` (the depth, runtime lines,
|
||||
rules hash, base and registry commits, and the gate scan bound to the head) and
|
||||
the queue refuses a receipt below the depth its head needs
|
||||
([ship-policy.md#ship-receipt](ship-policy.md#ship-receipt)). A stop the owner
|
||||
accepted after a fix-check edit goes in `review_loop_exception`, never in prose
|
||||
under `REVIEW_LOOP:`.
|
||||
|
||||
The receipt's summary line prints `passes=<n> blocking_found=<k> deferred=<m>`,
|
||||
which is what a scorecard tracks (target: a median of 1 pass per PR). A
|
||||
receipt carried across a base merge or a test-only fix
|
||||
@@ -268,9 +333,10 @@ trailers, appended }`.
|
||||
|
||||
## Where this is enforced
|
||||
|
||||
- `scripts/resolvers/review-army.ts`: the floor and `NO FINDINGS` in every specialist and Red Team prompt (rendered into `review/sections/review-army.md` and `ship/sections/review-army.md`), and the lead judgment's "On record" line that turns a [ruled class](#rulings-below-the-floor) into a note; in `/ship` every specialist and Red Team prompt it renders carries its diff base as a literal sha the parent writes in (`DIFF_BASE=<diff-base>`: the merge base on pass 1, `RANGE_BASE` from pass 2 on), because a reviewer is its own process and never sees the parent's shell variables; the fan-out batches above.
|
||||
- `ship/sections/review-army.md` Step 9: passes, `RANGE_BASE`, classify, defer, render, the cap check before any edit, the Step 11 late-defer row, scratch files under `.gstack/tmp/`, the cap decisions; `ship/sections/adversarial.md` Step 11: the outside voice reads the same range and scopes queued fixes the same way.
|
||||
- `lib/review-depth.ts`, `bin/gstack-review-plan`: the diff class, the transition table, the numeric-claim scan and each role's effort tier (`test/review-depth.test.ts`, `test/review-plan-cli.test.ts`).
|
||||
- `scripts/resolvers/review-army.ts`: the floor and `NO FINDINGS` in every specialist and Red Team prompt (rendered into `review/sections/review-army.md` and `ship/sections/review-army.md`), and the lead judgment's "On record" line that turns a [ruled class](#rulings-below-the-floor) into a note; in `/ship` every specialist and Red Team prompt it renders carries its diff base as a literal sha the parent writes in (`DIFF_BASE=<diff-base>`: the merge base on pass 1, `RANGE_BASE` from pass 2 on) beside its plan's `DIFF_CMD` excludes, because a reviewer is its own process and never sees the parent's shell variables; the fan-out batches above.
|
||||
- `ship/sections/review-army.md` Step 9: the plan's `REVIEW_DEPTH`, `GATE_SCAN` and `--depth-floor` on every run, the depth fields of the review-log row, passes, `RANGE_BASE`, classify, defer, render, the cap check before any edit, the Step 11 late-defer row, scratch files under `.gstack/tmp/`, the cap decisions; `ship/sections/adversarial.md` Step 11: the outside voice reads the same range and scopes queued fixes the same way.
|
||||
- `lib/review-depth.ts`, `bin/gstack-review-plan`: the diff class, the depth (`depthFor`, `DEPTH_RULES`, `depthOfCommitInput`), each dispatch's `exclude` pathspecs and `DIFF_CMD`, the transition table, the numeric-claim scan and each role's effort tier (`test/review-depth.test.ts`, `test/review-plan-cli.test.ts`).
|
||||
- `lib/review-collect.ts`: `collectCommit` (git objects only, from any checkout) and `collectLive` (the working tree), per-path lines and the base commit's registry (`test/review-collect.test.ts`).
|
||||
- `lib/review-scope.ts`, `bin/gstack-review-scope`: `classify`, `defer`, `render`, `summary` (with `fix_checks`, `dispatches`, `est_input_tokens`); refusals are `REVIEW_SCOPE_INPUT` and `REVIEW_SCOPE_RANGE` ([troubleshooting.md](troubleshooting.md#review-scope-input)).
|
||||
- `lib/ship-policy.ts`: the `review_scope` key and its `ship: review_scope …` consequence line.
|
||||
- `review/checklist.md`: `prereg-with-results` under Gate Integrity and `test-keyboard-count-loop` under Pass 2; `bin/gstack-coauthor-trailers` for the contributor-credit trailers of a port.
|
||||
|
||||
+59
-7
@@ -221,8 +221,11 @@ It refuses (`QUEUE_NOT_READY`) when the working tree has tracked edits, HEAD
|
||||
is not the PR's pushed head, CI at that exact head is not green, or the pregate
|
||||
result is not a current pass, and (`QUEUE_RECEIPT_MISSING`,
|
||||
`QUEUE_RECEIPT_STALE`) when the PR body carries no /ship receipt for that
|
||||
head: /ship runs before every READY ([details](#ship-before-ready)). Send the
|
||||
block as printed.
|
||||
head: /ship runs before every READY ([details](#ship-before-ready)). It also
|
||||
refuses `QUEUE_RECEIPT_DEPTH_RAISED` when the receipt's review depth is below
|
||||
what the head needs and `QUEUE_RECEIPT_EXCEPTION_INVALID` when an owner
|
||||
exception the receipt records does not stand ([the ship receipt](#ship-receipt)).
|
||||
Send the block as printed.
|
||||
|
||||
**2. Lane behind another PR: gate ahead.** Gate the tree the queue will have
|
||||
once the predecessor lands, while it is still open:
|
||||
@@ -370,12 +373,17 @@ slot. The queue enforces this at every step from the PR body alone:
|
||||
- `ready` refuses with `QUEUE_RECEIPT_MISSING` when the body has no valid
|
||||
receipt and with `QUEUE_RECEIPT_STALE` when the receipt's `head` is not the
|
||||
head being READY'd (or its `pr` is another PR); the informational
|
||||
`SHIP_RECEIPT:` line still prints. There is no flag around it.
|
||||
`SHIP_RECEIPT:` line still prints. It refuses `QUEUE_RECEIPT_DEPTH_RAISED`
|
||||
(with a `REVIEW_DEPTH:` line) when the receipt records a review depth below
|
||||
what the head needs, and `QUEUE_RECEIPT_EXCEPTION_INVALID` when an owner
|
||||
exception it records does not stand ([the ship receipt](#ship-receipt)).
|
||||
There is no flag around it.
|
||||
- `train build` refuses a pin whose PR has no current receipt with the same
|
||||
codes, naming the PR, before anything is written, pushed or dispatched,
|
||||
`--dry-run` included.
|
||||
- `verify --train` refuses the slot with the same codes before printing
|
||||
`MERGE_CONDITION`.
|
||||
- `verify --train` refuses the slot with `QUEUE_RECEIPT_MISSING` or
|
||||
`QUEUE_RECEIPT_STALE` before printing `MERGE_CONDITION`; it does not
|
||||
recompute the review depth.
|
||||
|
||||
**The stamp keeps the pin's receipt.** The slot's stamp commit changes only
|
||||
stamp files (and, from slot 2 on, merges main into the pin), so no /ship runs
|
||||
@@ -478,6 +486,9 @@ its record. Refusals, all before any spend: `QUEUE_TRAIN_UNTRUSTED` (a fork
|
||||
head), `QUEUE_TRAIN_MOVED` (a head moved from its pin),
|
||||
`QUEUE_RECEIPT_MISSING` and `QUEUE_RECEIPT_STALE` (the PR body has no /ship
|
||||
receipt for the pin; see [/ship before READY](#ship-before-ready)),
|
||||
`QUEUE_RECEIPT_DEPTH_RAISED` and `QUEUE_RECEIPT_EXCEPTION_INVALID` (the
|
||||
receipt's review depth is below what the pin needs, or an owner exception it
|
||||
records does not stand; see [the ship receipt](#ship-receipt)),
|
||||
`QUEUE_NOT_READY` (no READY receipt at that head), `QUEUE_TRAIN_CONFLICT` (the printed
|
||||
`QUEUE_TRAIN_CONFLICT: train <id> stopped at #<pr> (conflicts: <files>) after
|
||||
merging #<a>,#<b>; nothing dispatched, no branch pushed; fix: …` line names
|
||||
@@ -777,8 +788,9 @@ written by the pr-body section of `/ship` after the body is composed.
|
||||
|
||||
The ```` ```gstack-ship-receipt ```` block is the handoff record: identifiers
|
||||
first (`pr`, `head`, `version`, `base`), then the evidence (`gate`, `ci`,
|
||||
`head_tree`, `product_patch_id`, `pregate`, `issues`, `approval`, `docs`,
|
||||
`review_loop`, `carry`), validated
|
||||
`head_tree`, `product_patch_id`, `gstack_version`, `review_depth`, `pregate`,
|
||||
`issues`, `approval`, `docs`, `review_loop`, `review_loop_exception`, `carry`),
|
||||
validated
|
||||
against the `ship-receipt` schema (`gstack-artifact schema ship-receipt`) and
|
||||
read back by `gstack-ship-receipt read`, `result`, `carry` and the queue.
|
||||
|
||||
@@ -813,6 +825,46 @@ unreadable base policy is `POLICY_SOURCE_UNAVAILABLE`); one
|
||||
(`absent/<max> over` for a path the tree lacks), exit 1 on any `over`, whose
|
||||
repair is trimming the file, never raising the cap in the same PR.
|
||||
|
||||
**`review_depth` and `gstack_version`.** `write` records the gstack that
|
||||
wrote the receipt and the review depth at the checked-out head
|
||||
([review-policy.md#review-depth](review-policy.md#review-depth)):
|
||||
`{ depth, class, runtime_lines, rules, base, registry, gate_scan: { tagged, head }, reasons }`,
|
||||
computed from git objects for the range against `origin/<base>`
|
||||
(`gstack-diff-scope <base> --head <sha>` for the scope signals), with the gate
|
||||
scan run on that head. `--head` must be the checked-out HEAD
|
||||
(`RECEIPT_INVALID` otherwise), so the record and `head_tree` describe the
|
||||
tree the review read; tracked edits in the checkout make the scan unavailable,
|
||||
which records `full`. `gstack-ship-queue ready` and `train build` recompute
|
||||
the depth the head needs from the same range and scope and print `REVIEW_DEPTH: receipt=<d> required=<d>
|
||||
runtime_lines=<n> class=<c> reasons=<…> inputs=base@<sha>,registry@<sha>,gate_scan@<sha|missing>`;
|
||||
a recorded gate scan counts only when its `head` is the full bound commit,
|
||||
otherwise the scan is unavailable and the head needs `full`. A receipt below the required
|
||||
depth, or one whose writer records `gstack_version` but no `review_depth`, is
|
||||
[`QUEUE_RECEIPT_DEPTH_RAISED`](troubleshooting.md#queue-receipt-depth-raised):
|
||||
the receipt is left unchanged and the fix is /ship on that head. A receipt with
|
||||
neither field predates depth receipts and is accepted as legacy. A carry
|
||||
recomputes the depth at the new head, rebinds the gate scan, and refuses when
|
||||
the new head needs a deeper review than the receipt records (a test change that
|
||||
skips a test raises the diff to gate class), when `--head` is not the
|
||||
checked-out HEAD, when the receipt records `gstack_version` without
|
||||
`review_depth`, or when an owner exception no longer stands, each as
|
||||
[`RECEIPT_CARRY_REFUSED`](troubleshooting.md#receipt-carry-refused).
|
||||
|
||||
**`review_loop_exception`.** The owner's acceptance of a stop after a
|
||||
fix-check edit under frozen scope (#3559), from `write --review-loop-exception
|
||||
<json|@file>`: `{ accepted_by, date, via, reason, fix_check: { head, findings[, files] } }`.
|
||||
It needs `--review-loop` with `fix_checks` of 1 or more, a `fix_check.head`
|
||||
that is the receipt head or its ancestor, and, with `--body`, the line
|
||||
`Review-loop exception: accepted by <owner> <date> via <ref>: <reason> (fix-check at <sha12>, <n> findings)`
|
||||
word for word; otherwise
|
||||
[`REVIEW_LOOP_EXCEPTION_INVALID`](troubleshooting.md#review-loop-exception-invalid)
|
||||
and nothing is written. The queue checks both owner exceptions the same way
|
||||
before READY and train build: the record must parse, the body must still
|
||||
carry its line, and the fix-check head must be the bound head or its ancestor
|
||||
(after a carry across rewritten history the old fix-check commit is off the
|
||||
branch, so that check is skipped), or the receipt is
|
||||
[`QUEUE_RECEIPT_EXCEPTION_INVALID`](troubleshooting.md#queue-receipt-exception-invalid).
|
||||
|
||||
**Prose against the receipt.** `gstack-ship-receipt read --pr <n> --check-prose`
|
||||
(or `--body <file>`) compares every counter the body quotes with the receipt's
|
||||
integers under `review_loop`, `gate` and `ci`: `passes=2`, `passes: 2` and
|
||||
|
||||
+61
-5
@@ -2900,10 +2900,12 @@ for an older PR, write it once by hand with the same command.
|
||||
### `RECEIPT_INVALID`
|
||||
|
||||
**Meaning.** The block exists but does not validate against the `ship-receipt`
|
||||
schema (`gstack-artifact schema ship-receipt`).
|
||||
schema (`gstack-artifact schema ship-receipt`). `gstack-ship-receipt write`
|
||||
also refuses with this code, and writes nothing, when `--head` is not the
|
||||
checked-out HEAD: the receipt binds the tree the review read.
|
||||
|
||||
**Fix.** Regenerate it with `gstack-ship-receipt write`; never hand-edit the
|
||||
block.
|
||||
block. For the `--head` refusal, check out that commit and write there.
|
||||
<a id="execution-unknown"></a>
|
||||
### `EXECUTION_UNKNOWN`
|
||||
|
||||
@@ -4434,6 +4436,39 @@ written for the pinned head is the binding one and `verify --train` accepts it.
|
||||
`gstack-ship-queue ready --pr <n> --screen|--no-screen`. A lane never stamps
|
||||
to fix this: the stamp comes only when the slot is called.
|
||||
|
||||
<a id="queue-receipt-depth-raised"></a>
|
||||
### `QUEUE_RECEIPT_DEPTH_RAISED`
|
||||
|
||||
**Meaning.** `ready` or `train build` recomputed the review depth the head
|
||||
needs from git (the range against the base ref, `origin/<base>` in `ready`
|
||||
and `<remote>/<base>` in `train build`, the base commit's
|
||||
generated-output registry, and the receipt's gate scan when it names this
|
||||
head) and the receipt's `review_depth.depth` is below it, or the receipt comes
|
||||
from a gstack that records `gstack_version` but carries no `review_depth`. The
|
||||
`REVIEW_DEPTH:` line prints the receipt's depth, the required depth, runtime
|
||||
lines, the class, the reasons and the inputs (`base@`, `registry@`,
|
||||
`gate_scan@` or `missing`). A gate scan recorded for another head counts as
|
||||
missing, which makes the head need `full`. The receipt is unchanged.
|
||||
|
||||
**Fix.** Run /ship on the head the READY names; it reviews at the depth
|
||||
`gstack-review-plan` plans for that head and writes a receipt that records it.
|
||||
Never edit `review_depth` by hand ([review-policy.md](review-policy.md#review-depth)).
|
||||
|
||||
<a id="queue-receipt-exception-invalid"></a>
|
||||
### `QUEUE_RECEIPT_EXCEPTION_INVALID`
|
||||
|
||||
**Meaning.** The receipt records an owner exception (`docs.exception` or
|
||||
`review_loop_exception`) that the queue cannot accept: the record does not
|
||||
parse, a review-loop exception names no fix-check (`review_loop.fix_checks`
|
||||
is 0 or missing), its `fix_check.head` is neither the bound head nor an
|
||||
ancestor of it (not checked after a carry across rewritten history), or the PR
|
||||
body no longer carries the exception's line word for word
|
||||
(`Documentation exception: …` or `Review-loop exception: …`).
|
||||
|
||||
**Fix.** Restore the line in the PR body exactly as `write` printed it, or run
|
||||
/ship again and record the owner's acceptance with `--docs` or
|
||||
`--review-loop-exception` ([ship-policy.md](ship-policy.md#ship-receipt)).
|
||||
|
||||
<a id="queue-screen-stale-ref"></a>
|
||||
### `QUEUE_SCREEN_STALE_REF`
|
||||
|
||||
@@ -4699,13 +4734,34 @@ untracked changes (a carry certifies a commit, never a tree that differs from
|
||||
it), the receipt's head left HEAD's history and the receipt records no tree to
|
||||
compare (a receipt written before `head_tree`, whose head is not in this
|
||||
clone), or git could not compare the two product diffs or read the working
|
||||
tree's status. The receipt in the body is unchanged and still names the old
|
||||
head, so `gstack-ship-queue ready` refuses `QUEUE_RECEIPT_STALE`.
|
||||
tree's status. It also refuses when an owner exception the receipt records
|
||||
no longer stands, and, for a receipt from a gstack that records review
|
||||
depth, when HEAD needs a deeper review than the receipt records, `--head` is
|
||||
not the checked-out HEAD, or the receipt records `gstack_version` without
|
||||
`review_depth` ([ship-policy.md](ship-policy.md#ship-receipt)). The receipt in
|
||||
the body is unchanged and still names the old head, so `gstack-ship-queue
|
||||
ready` refuses `QUEUE_RECEIPT_STALE`.
|
||||
|
||||
**Fix.** Commit or stash every working-tree change, then rerun
|
||||
`gstack-ship-receipt carry --pr <n>` at the new head. When the history is
|
||||
already rewritten and the receipt predates `head_tree`, run /ship on the new
|
||||
head; next time merge the base in instead of rebasing.
|
||||
head; next time merge the base in instead of rebasing. For a depth or
|
||||
exception refusal, run /ship on the new head (or check out `--head` and carry
|
||||
there).
|
||||
|
||||
<a id="review-loop-exception-invalid"></a>
|
||||
### `REVIEW_LOOP_EXCEPTION_INVALID`
|
||||
|
||||
**Meaning.** `gstack-ship-receipt write --review-loop-exception` refused the
|
||||
record and wrote nothing: it is not `{ accepted_by, date, via, reason,
|
||||
fix_check: { head, findings[, files] } }`, `--review-loop` is missing or counts
|
||||
no fix-check, `fix_check.head` is neither the receipt head nor its ancestor,
|
||||
or the `--body` file lacks the `Review-loop exception: …` line.
|
||||
|
||||
**Fix.** Record the owner's exact words and the fix-check the stop followed,
|
||||
pass the `gstack-review-scope summary` file as `--review-loop`, put the printed
|
||||
line in the PR body, and write again
|
||||
([ship-policy.md](ship-policy.md#ship-receipt)).
|
||||
|
||||
<a id="receipt-carry-tree-mismatch"></a>
|
||||
### `RECEIPT_CARRY_TREE_MISMATCH`
|
||||
|
||||
Reference in new issue
Block a user