Docs audit: troubleshooting codes and ship-policy sections match the shipped train rules (GSTA-129)

The /document-release audit (edit mode) corrected two docs the PR had not
reached: docs/troubleshooting.md's QUEUE_TRAIN_TREE_MISMATCH, QUEUE_PROOF_MISSING,
QUEUE_TRAIN_FROZEN and POLICY_SOURCE_UNAVAILABLE entries, and docs/ship-policy.md's
train build conflict rule (the carried stacked child is the exception), the
verify freeze paragraph (slot-1 GitHub-MERGED freeze, unreadable-state refusal)
and the restamp stamp_paths stage (base-added lines stay; an unrunnable base diff
refuses). Five mid-sentence colons in the PR's own paragraphs are rewritten for
the prose lint, and the CHANGELOG Docs bullet names docs/troubleshooting.md.

Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
This commit is contained in:
garrytanandcapy-ai[bot] committed 2026-10-27 16:32:52 +00:00
1 parent ada2accea0
commit e139fee6b7
4 files changed
+43 -17

No files matched your search

+1 -1
View File
@@ -13,7 +13,7 @@
- `test/ship-queue-train.test.ts`: a stacked child's merged-in head the pre-merge verify accepted confirms after its squash merge (froze before), with the correction bullet accepted pre-merge; a post-merge CHANGELOG holding a line the pin never wrote still freezes, and the freeze lists the slot GitHub reports MERGED under `merged`; a slot-1 squash GitHub reports MERGED that fails the post-merge check freezes, and an unreadable GitHub state refuses; a stacked child behind another slot is gated on the carried CHANGELOG (the released parent's text appears once, under its own heading, with the child's corrections in the child's entry). `test/restamp-base-merge.test.ts` (new): train 24's shape (pin forked before the base's key, base merge staged), for a key naming an older release and for one at the base version; the dry run lists the four stamp files plus the branch's own key file only, committed or staged, and the receipt bound is asserted for both keys. `test/changelog-merge-damage.test.ts`: the real a78ce08a/3e4240a7/d87eec95 CHANGELOGs (`test/fixtures/merge-train-3453/`) merge to main's sections unchanged plus #3453's entry with the two corrections; without the base they stay null; shape controls for an unedited, a dropped, an added and an empty child.
### Docs
- docs/ship-policy.md § Run a merge train: the one CHANGELOG rule, the stamp-files rule (never a fifth), the stacked-child-after-squash resolution, and that an unstamped head changes a release output by regenerating it at its own VERSION (the Version Gate has accepted that since 1.91.237.0; #3434 reverted its digest change against the older gate). docs/captain.md § Merge trains and captain/templates/slot-call.md carry the stamp-files rule (the files the slot call names: `STAMP_FILES`, plus the file carrying a `*_KEY_VERSION` the PR introduced when the call names it; a file the restamp lists that the call did not name is left out and reported) and the stacked-child rule.
- docs/ship-policy.md § Run a merge train: the one CHANGELOG rule, the stamp-files rule (never a fifth), the stacked-child-after-squash resolution, and that an unstamped head changes a release output by regenerating it at its own VERSION (the Version Gate has accepted that since 1.91.237.0; #3434 reverted its digest change against the older gate). docs/troubleshooting.md: `QUEUE_TRAIN_TREE_MISMATCH`, `QUEUE_PROOF_MISSING`, `QUEUE_TRAIN_FROZEN` and `POLICY_SOURCE_UNAVAILABLE` describe the post-merge CHANGELOG exception, the unreadable-state refusal, the GitHub-MERGED freeze record and the restamp base-diff refusal. docs/captain.md § Merge trains and captain/templates/slot-call.md carry the stamp-files rule (the files the slot call names: `STAMP_FILES`, plus the file carrying a `*_KEY_VERSION` the PR introduced when the call names it; a file the restamp lists that the call did not name is left out and reported) and the stacked-child rule.
### Gate integrity
- Correction to the 1.91.276.0 entry below: "copies of shared sections twice until a follow-up dedupes them." should read "versions of shared sections beside the shared file (office-hours' copies are deduped in the entry above).".
+1 -1
View File
@@ -215,7 +215,7 @@ fifth: a file the restamp lists that the call did not name is not a stamp, so th
leaves it out and says so in its READY. The restamp itself never
moves a line the base added since the pin's fork point, base merge committed or staged. A
pin stacked on a PR that squash-merged earlier conflicts beyond the CHANGELOG where the
parent's squash touched the pin's lines: the lane takes its pin's bytes for those files
parent's squash touched the pin's lines, so the lane takes its pin's bytes for those files
(`verify` checks the tree against T<i>, which already carries the parent), and
`mergeChangelogs` carries the pin's edits to the parent's now-released entry as Gate
integrity corrections in the pin's own entry. The post-merge `verify` judges the CHANGELOG
+18 -9
View File
@@ -486,7 +486,9 @@ missing, cancelled or on another SHA). In a merge, version files take the
prefix's side and the CHANGELOG merges by section, since the stamp at merge
rewrites both; a PR whose CHANGELOG drops or renames a released heading (or
carries a released entry's text in a section of its own) is a CHANGELOG
conflict, never merged with the damage. READY receipts and train records are
conflict, never merged with the damage, except the stacked child whose parent
was released since their merge base, which `mergeChangelogs` carries (see *A
stacked child whose parent merged earlier*, below). READY receipts and train records are
keyed on origin's GitHub owner/repo, so lanes whose origin uses another URL
form (git.capy.ai, ssh, a token URL) share the coordinator's queue.
@@ -579,8 +581,8 @@ GSTA-67 shape, a released heading dropped by hand, and stays a hand merge.
**A generator change on an unstamped head.** A release output such as
`agents-digest/gstack-AGENTS.md` is stamped at the slot, and an unstamped PR
changes it the ordinary way: edit the generator, regenerate at the branch's
own `VERSION` (its merge base's), and commit both. CI's Version Gate
changes it the ordinary way, which is to edit the generator, regenerate at the
branch's own `VERSION` (its merge base's), and commit both. CI's Version Gate
regenerates the output at that VERSION and reads a byte-identical result as
unstamped ([what /ship reads](#ship), since 1.91.237.0); the freshness test
compares against the checkout's VERSION, so it passes before and after the
@@ -604,18 +606,25 @@ writes for that pin against main, above it); after it, main's tree must equal
the prefix after it modulo stamps, or differ from it in the CHANGELOG alone
while being its own first parent's CHANGELOG (the main the pre-merge verify
saw, itself the prefix before modulo stamps) plus the PR's own entry under the
same rule. One CHANGELOG rule, before and after: a head the pre-merge verify
accepted confirms after its squash merge with the same CHANGELOG, and a
same rule. One CHANGELOG rule applies before and after, so a head the pre-merge
verify accepted confirms after its squash merge with the same CHANGELOG, and a
stacked child whose one `[Unreleased]` became two released sections is not a
mismatch (train 24, 2026-10-27, froze on exactly that). Anything else is
`QUEUE_TRAIN_TREE_MISMATCH` (or `QUEUE_TRAIN_MOVED`). When nothing of the train
has merged, that is a plain refusal: build a new train on current main. When
has merged, that is a plain refusal, and a new train is built on current main. When
earlier slots already merged, main holds a prefix no gate covered on its own,
so verify freezes the queue: the freeze goes to local state and the notes ref
so verify freezes the queue. The freeze goes to local state and the notes ref
first, then the full gate is dispatched on main's SHA at once (the backstop),
and the `QUEUE_TRAIN_FROZEN:` line lists the merged PRs, main's tree and the
backstop run. A slot GitHub reports MERGED counts as merged in that record,
verified tree or not, so the rebuild after the repair never seats it again. While frozen, ordinary trains and other trains' merges are refused
verified tree or not, so the rebuild after the repair never seats it again
(the reason says "merged on GitHub without a verified tree"); a first slot
whose squash GitHub reports MERGED but whose tree fails the post-merge check
freezes the same way, since main already holds the unverified squash. When
whether a slot merged on GitHub cannot be read, verify refuses
(`QUEUE_PROOF_MISSING`, "whether #<n> merged on GitHub could not be read")
and asks to be rerun once `gh pr view` answers; an unreadable state is never
"nothing merged". While frozen, ordinary trains and other trains' merges are refused
(`QUEUE_TRAIN_FROZEN`). Admit one repair PR at its exact head:
```bash
@@ -933,7 +942,7 @@ Stages, each journaled in `.gstack/tmp/restamp-journal.json`:
1. **version**: `gstack-version-bump write --version <X>` in the staging worktree (VERSION, the manifest, npm lockfiles); skipped when `restamp_command` owns the version.
2. **release_tool**: the policy command, under the contract below.
3. **changelog**: the PR's `[Unreleased]` or old-version section moves to the top under `<X>`, above its predecessor's entry. A duplicate heading the PR introduces (`CHANGELOG_DUPLICATE_HEADING`: the headings are counted against the base's CHANGELOG at `origin/<base>` and at the fork point, so a version the base already heads twice passes as long as the PR adds no further copy) and a top entry that is not `<X>` (`CHANGELOG_TOP_MISMATCH`) are errors; the section being re-headed itself may exist only once.
4. **stamp_paths**: exact `(was X)`, `since: X` and `vX` rewrites, plus every `stamp_patterns` template, within `stamp_paths` and `stamp_files`. A quote around X in the first two forms is part of the form and is kept as written: `since: 'X'`, `since: "X"`, `(was 'X')` and `(was "X")` move like the bare ones (the shape of a quoted source row such as gbrain's `{ since: '0.60.468.0', … }`), an opened quote must close right after X (`since: '1.2.3-beta'` and `since: '1.2.3"` are not the form, in the writer and in the tree receipt alike), and `vX` already accepts a quote before the `v`. Which X moves is decided by provenance. The branch's own tentative stamp (its VERSION when it moved past base) and `--was <X>`, when given, are each rewritten on every line of every stamp file. Beyond that, only lines the branch itself added are read, by their position in the file (`git diff --unified=0` from the fork point), so a base line with the same text, or a CRLF checkout whose diff is LF-normalized, is told apart: a stamp-form mention there of a version newer than the version at the branch's fork point is a tentative stamp and moves (this is what finds a quoted row after a lost slot and a merge of the base, since the base's release is newer than the fork point), and when the branch has no stamp of its own because its VERSION equals the base's and its fork point is the base itself (a branch rebased onto the base after it reached X, or one never stamped) a mention of the base version on a line it added is its stale stamp and moves too, while the same version on a line the base already carries is the base's release and stays. The stage detail names both counts per file (`VERSION equals base X, so X moved only on lines this branch added and stayed on the base's own lines (<file>: N moved, M kept)`), so the stamp commit shows which lines moved and why. The rule reads line ownership, not meaning: a branch-added line inside `stamp_paths` that means the base's own release in a stamp form (`Requires vX or later` written on a branch forked at X) moves with it, which the `--dry-run` diff and the per-file counts show before anything is written; write such a reference outside a stamp form, or keep that file out of `stamp_paths`. `--was X` is the opposite tool: it rewrites X everywhere, the base's own lines included, and re-heads the CHANGELOG section `## [X]`. Only the repo's own version width counts, so a 3-digit dependency pin in a 4-digit repo is never a candidate.
4. **stamp_paths**: exact `(was X)`, `since: X` and `vX` rewrites, plus every `stamp_patterns` template, within `stamp_paths` and `stamp_files`. A quote around X in the first two forms is part of the form and is kept as written: `since: 'X'`, `since: "X"`, `(was 'X')` and `(was "X")` move like the bare ones (the shape of a quoted source row such as gbrain's `{ since: '0.60.468.0', … }`), an opened quote must close right after X (`since: '1.2.3-beta'` and `since: '1.2.3"` are not the form, in the writer and in the tree receipt alike), and `vX` already accepts a quote before the `v`. Which X moves is decided by provenance. The branch's own tentative stamp (its VERSION when it moved past base) and `--was <X>`, when given, are each rewritten on every line of every stamp file. Beyond that, only lines the branch itself added are read, by their position in the file (`git diff --unified=0` from the fork point), so a base line with the same text, or a CRLF checkout whose diff is LF-normalized, is told apart: a stamp-form mention there of a version newer than the version at the branch's fork point is a tentative stamp and moves (this is what finds a quoted row after a lost slot and a merge of the base, since the base's release is newer than the fork point), and when the branch has no stamp of its own because its VERSION equals the base's and its fork point is the base itself (a branch rebased onto the base after it reached X, or one never stamped) a mention of the base version on a line it added is its stale stamp and moves too, while the same version on a line the base already carries is the base's release and stays. The stage detail names both counts per file (`VERSION equals base X, so X moved only on lines this branch added and stayed on the base's own lines (<file>: N moved, M kept)`), so the stamp commit shows which lines moved and why. A line the base added since the fork point is the base's even when that diff shows it (a merge of the base on the branch, committed or still staged, brings every line the base added since the fork into the diff from the fork point, main's own release keys included): it is left out, the detail says `N line(s) the base added since the fork point are the base's, not this branch's, and stay (<file>: N)`, and a base diff git cannot run refuses the stage (`POLICY_SOURCE_UNAVAILABLE`, `cannot diff <since>..<base> for the stamp_paths stage`) rather than reading as an empty set that would move the base's lines again. The rule reads line ownership, not meaning: a branch-added line inside `stamp_paths` that means the base's own release in a stamp form (`Requires vX or later` written on a branch forked at X) moves with it, which the `--dry-run` diff and the per-file counts show before anything is written; write such a reference outside a stamp form, or keep that file out of `stamp_paths`. `--was X` is the opposite tool: it rewrites X everywhere, the base's own lines included, and re-heads the CHANGELOG section `## [X]`. Only the repo's own version width counts, so a 3-digit dependency pin in a 4-digit repo is never a candidate.
5. **mirror**: tier 2; declared and validated, reported as not run.
6. **apply**: every changed file is written to the branch only after all stages validated, each against the fingerprinted preimage; a file edited meanwhile is `RESTAMP_CONFLICT` and the applied writes roll back (rollback refuses to overwrite a file whose bytes differ from the recorded postimage).
+23 -6
View File
@@ -2648,7 +2648,11 @@ from the working directory (a bare `bun gstack-pregate`), since a branch could
then pick the user policy: run `gstack-pregate` directly or as
`bun --no-env-file`. `gstack-ship-queue ready` under a train policy fetches
`origin/<base>` first and refuses with this code, quoting git's error, when
that fetch fails.
that fetch fails. `gstack-restamp`'s `stamp_paths` stage refuses with it when
the diff that tells the base's added lines from the branch's own cannot run
(`cannot diff <since>..<base> for the stamp_paths stage (<git's first stderr
line>); nothing written`), since an empty diff would read as "the base added
nothing" and move the base's lines again.
**Fix.** `git fetch origin <base> --depth=1`, then rerun. To run against the
branch's own file deliberately, pass `--policy-from head --allow-repo-commands`;
@@ -4084,7 +4088,10 @@ gate-ahead merge could not be recomputed with `git merge-tree`. The train
commands use it too: `train forget` on a record that does not exist or on a
train still building, running or merging, `train watch` on an unknown record,
`verify --pr <n> --screen` on a run with no verdict to read (an INCOMPLETE or
unreadable run), and `ready --screen` when the scratch merge branch cannot be
unreadable run), `verify --pr <n> --train <nonce>` when main's tree mismatches
and whether a slot merged on GitHub cannot be read (so whether the queue must
freeze is unknown; it never answers "nothing merged" from an unreadable
state), and `ready --screen` when the scratch merge branch cannot be
pushed. Nothing was written unless the `QUEUE_STAMP_REFUSED:` line says
`committed=yes`.
@@ -4322,17 +4329,27 @@ the repair merges (a changed train).
**Meaning.** Main's tree is neither the train's prefix before this PR nor the
one after it, modulo stamps, so the merge would not publish what the gate
tested.
tested. After a merge, a main that differs from the prefix after the PR in
the CHANGELOG alone, while being its own first parent's CHANGELOG plus the
PR's own entry (the rule the pre-merge verify applied to the head), is not a
mismatch ([one CHANGELOG rule, before and after](ship-policy.md#run-a-merge-train)).
**Fix.** Stop merging. If earlier PRs of the train merged, the queue is now
frozen (`QUEUE_TRAIN_FROZEN`); otherwise build a new train on current main.
**Fix.** Stop merging. If a PR of the train merged (verified, or reported
MERGED by GitHub without a verified tree, slot 1's own squash included), the
queue is now frozen (`QUEUE_TRAIN_FROZEN`); otherwise build a new train on
current main. When whether a slot merged on GitHub cannot be read, verify
refuses with `QUEUE_PROOF_MISSING` instead of either answer; rerun it once
`gh pr view` answers.
<a id="queue-train-frozen"></a>
### `QUEUE_TRAIN_FROZEN`
**Meaning.** A train stopped partway through publication, so main holds a
prefix no gate covered on its own. The `QUEUE_TRAIN_FROZEN:` line lists the
merged PRs, main's tree and the backstop run dispatched on main's SHA. The
merged PRs (a slot GitHub reports MERGED counts as merged, verified tree or
not, and the reason then says `merged on GitHub without a verified tree`, so
the repair's rebuild never seats it again), main's tree and the backstop run
dispatched on main's SHA. The
freeze lives in local state and on `refs/notes/gstack-trains`. Ordinary
trains and other trains' merges are refused until a repair lands.