diff --git a/.gstack/ship-policy.json b/.gstack/ship-policy.json
index 54426b45e..c73522088 100644
--- a/.gstack/ship-policy.json
+++ b/.gstack/ship-policy.json
@@ -1,7 +1,7 @@
{
"$schema": "https://github.com/garrytan/gstack/docs/ship-policy.md#schema-v1",
- "bump": "ask",
- "queue_mode": "claim-at-open",
+ "bump": "patch",
+ "queue_mode": "stamp-at-merge",
"release_tool": "bun scripts/gen-agents-digest.ts",
"release_outputs": ["agents-digest/gstack-AGENTS.md"],
"stamp_paths": [],
@@ -11,5 +11,5 @@
"preregistration_shas": [],
"stamp_sensitive_gates": ["tests"],
"pr_count": { "default_pr_count": 1, "split_requires_owner": true },
- "maintainers": []
+ "maintainers": ["garrytan"]
}
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 2821f471e..cd8f4e1ca 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,8 @@
# Changelog
+## [1.91.83.0] - 2026-10-11
+
+**gstack ships itself under `stamp-at-merge`.** `.gstack/ship-policy.json` flips `queue_mode` to `stamp-at-merge`, `bump` to `patch` and names `garrytan` under `maintainers`. The version is now allocated once, when the PR is next in the queue (`gstack-restamp --next --expect-base --expect-order`), instead of claimed at open and re-slotted on every reorder; every bump is PATCH, so unattended ships never meet the MINOR/MAJOR prompt; and `/review` contributor mode reads the maintainer set from the file instead of calling the collaborators API. `docs/ship-policy.md` gains the queue-side procedure: what a PR carries before its stamp, who runs the restamp and when, and what the merging thread verifies on the exact SHA instead of "VERSION above main".
## [1.91.82.0] - 2026-10-11
**Plan-review reality rows name their inputs.** The `{{PLAN_REALITY_ROWS}}` prose in plan-eng-review, plan-ceo-review and the autoplan CEO/Eng phases wrote `gstack-plan-reality check --phase
` without saying what `` is, and the completion summary cited a `PLAN_REALITY` line no step produced. The check now reads `` (the review you wrote, or stdin) and says it prints the `PLAN_REALITY:` line the summary cites. Found by the plan-eng-review workflow judge on an unrelated PR (clarity 3 / actionability 3 median, both citing these two references).
diff --git a/VERSION b/VERSION
index 309d3fc72..4033bad18 100644
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-1.91.82.0
+1.91.83.0
diff --git a/agents-digest/gstack-AGENTS.md b/agents-digest/gstack-AGENTS.md
index 0277a610a..6912dbebc 100644
--- a/agents-digest/gstack-AGENTS.md
+++ b/agents-digest/gstack-AGENTS.md
@@ -1,4 +1,4 @@
-# gstack digest v1.91.82.0 — regenerate/re-copy after upgrading gstack
+# gstack digest v1.91.83.0 — regenerate/re-copy after upgrading gstack
Behavioral rules from gstack (https://github.com/garrytan/gstack), compressed
for agent hosts without a full skill install. The full skills add workflows,
diff --git a/docs/ship-policy.md b/docs/ship-policy.md
index 463db7a50..e1e7ef715 100644
--- a/docs/ship-policy.md
+++ b/docs/ship-policy.md
@@ -54,14 +54,17 @@ confined to that directory.
## gstack's own files
-gstack's policy (`.gstack/ship-policy.json`, the only tracked file under the
-otherwise ignored `.gstack/`) keeps today's behavior and names the one release
-tool `gstack-version-bump --regen-digest` used to hard-code:
+gstack's policy (`.gstack/ship-policy.json`, tracked beside `generated.json`
+and `pregate.json` under the otherwise ignored `.gstack/`) runs the repo on
+stamp-at-merge: the version is allocated once, when the PR is next in the queue,
+every bump is PATCH, and `/review` reads the maintainer set from the file. It
+names the one release tool `gstack-version-bump --regen-digest` used to
+hard-code:
```json
{
- "bump": "ask",
- "queue_mode": "claim-at-open",
+ "bump": "patch",
+ "queue_mode": "stamp-at-merge",
"release_tool": "bun scripts/gen-agents-digest.ts",
"release_outputs": ["agents-digest/gstack-AGENTS.md"],
"stamp_paths": [],
@@ -74,11 +77,49 @@ tool `gstack-version-bump --regen-digest` used to hard-code:
`.gstack/ship-policy.example.json` shows every key set the way a serial-merge
repo with a release script, docs mentions and a mirror would set them.
-```bash
-gstack-ship-policy show-effective --base main # resolved values with the source of each
-gstack-ship-policy validate # the working-tree file (what you are editing)
-gstack-ship-policy init --explain # key meanings and defaults
-```
+### Queue-side procedure for gstack
+
+Who does what under `stamp-at-merge`, for the PR author and for the thread
+running the merge queue:
+
+1. **Open.** The branch carries a tentative stamp of the base's version plus
+ PATCH (`gstack-version-bump write --version --regen-digest`)
+ and its CHANGELOG section headed with that version directly under
+ `# Changelog`; `## [Unreleased]` is also accepted. Nothing is claimed and
+ `gstack-next-version` is not consulted; the tentative version is only the
+ guess that makes the common case free. CI runs on this head as usual.
+2. **Register.** The queue records the PR and its intended order.
+3. **Stamp when next.** With every check green on the current head and
+ `origin/main` at the SHA the queue assumed, the PR owner (never the queue)
+ runs on the branch:
+ `gstack-restamp --next --base main --allow-repo-commands --pr --expect-base --expect-order `.
+ When the base has not moved past the tentative stamp it prints
+ `RESTAMP: X -> X (noop)` and `FILES: (none)`, and the green head is the
+ merge candidate with no further CI. When the base moved, it stamps base +
+ PATCH, re-heads the CHANGELOG above the base's top entry, reruns the
+ release tool, prints `STAMP_VERSION: ` and the stamp diff, and refuses
+ with `QUEUE_STALE` (nothing written) when the base or order moved after
+ `--expect-base` was read. The owner commits and pushes the stamp commit,
+ lets CI run on it, and reports `READY # STAMP_VERSION=`.
+4. **Merge.** The queue verifies on that exact SHA: `VERSION` equals
+ `STAMP_VERSION`, the CHANGELOG's top heading is `[]` and the heading
+ below it is the base's current version, every check is green on that SHA,
+ and, when a stamp commit was pushed, it changed only `VERSION`,
+ `package.json`, `CHANGELOG.md` and the release outputs
+ (`gstack-tree-receipt --gated --current ` prints the
+ per-file verdict). It then squash-merges with the merge API's expected-head
+ `sha` set to that SHA, so a late push is rejected by the server. "VERSION
+ above main" is replaced by "VERSION equals the restamp's STAMP_VERSION on
+ the SHA being merged, and that version is base + PATCH at merge time".
+5. **Not next.** A PR waiting behind another may gate ahead locally with
+ `gstack-restamp --after `; it pushes nothing. When the
+ predecessor lands, the waiting PR merges `origin/main` in (never a rebase),
+ keeping its own CHANGELOG section above the newly merged one, and step 3
+ runs.
+
+Until this policy is on `origin/main`, `gstack-restamp --next` reads the base's
+`bump: ask` and falls back to a MICRO bump; pass `--bump patch` on the first
+PR stamped after the flip if its base predates it.
## What `/ship` reads from it
diff --git a/package.json b/package.json
index bf5c06e3c..7310de30c 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "gstack",
- "version": "1.91.82",
+ "version": "1.91.83",
"description": "Garry's Stack — Claude Code skills + fast headless browser. One repo, one install, entire AI engineering workflow.",
"license": "MIT",
"type": "module",
diff --git a/scripts/harness-version.json b/scripts/harness-version.json
index 2dca53298..bc212144e 100644
--- a/scripts/harness-version.json
+++ b/scripts/harness-version.json
@@ -16,7 +16,7 @@
"scripts/lib/published-text.ts": {"blob":"3f2e4d616b5dd0204035710c66b64d49055c5714","since":4},
"scripts/lib/shard-engine.ts": {"blob":"3cd0b1aaa84018b00bf9ea0ce48093f3c8492f66","since":4},
"scripts/test-paid-shards.ts": {"blob":"8ddee5f11ed7856876135520dd8025ab989e2db9","since":4},
- "scripts/test-pr-profile.ts": {"blob":"1b3c215f8f50901c16357665a585101eaa27ff31","since":4,"nonBehavioral":"C-pregate: FREE_ONLY_PR_FILES gains .gstack/generated.json and .gstack/pregate.json (gstack's own pin files, read by no paid case); selection only, no case verdict rule changes"},
+ "scripts/test-pr-profile.ts": {"blob":"4ae0a4525f1855c331f4a6e6333c72e24e172654","since":4,"nonBehavioral":"FREE_ONLY_PR_FILES gains .gstack/ship-policy.json (selection only: paid cases run in fixture repos and read the policy from origin/ of the repo they run in; no verdict rule changed)"},
"scripts/test-strict-output.ts": {"blob":"01185d20dc9761b04b9b2f751ec98664fe0eaf93","since":4},
"test/helpers/agent-sdk-runner.ts": {"blob":"210bba92d241a90df578b2c7fc943ac5755d0025","since":4},
"test/helpers/auq-substance-panel.ts": {"blob":"4881260ef793668e4963a062db6d06a5e1f7a40f","since":4},
diff --git a/scripts/test-pr-profile.ts b/scripts/test-pr-profile.ts
index 1b3c215f8..4ae0a4525 100644
--- a/scripts/test-pr-profile.ts
+++ b/scripts/test-pr-profile.ts
@@ -214,6 +214,7 @@ export const FREE_ONLY_PR_FILES = [
'.gitignore', // Changes which untracked files git reports, never tracked content a case reads.
'.gstack/generated.json', // gstack's own regen registry: gstack-regen/gstack-pregate read it from origin/ of the repo they run in; paid cases run in fixture repos.
'.gstack/pregate.json', // gstack's own pre-gate policy: same trust boundary, same reason.
+ '.gstack/ship-policy.json', // gstack's own ship policy: gstack-ship-policy/gstack-restamp read it from origin/ of the repo they run in; same reason.
'scripts/retired-command.ts', // One-release stubs for retired package scripts; no paid case imports it.
'careful/bin/check-careful.sh', // /careful PreToolUse hook: covered by test/hook-scripts.test.ts; no paid fixture copies or runs it.
// Reporting and launch tools: they read CI history or start a lane, never run inside a paid case.