From 2cb359cbc58cbec20c75b89e83fa739f9e24b236 Mon Sep 17 00:00:00 2001 From: Garry Tan Date: Wed, 26 Aug 2026 00:08:46 +0000 Subject: [PATCH] fix(ship): name the /document-release subagent at every Step 18 decision point MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The v1.54.0.0 carve moved Step 18 (documentation sync) into ship/sections/pr-body.md and the Claude-host skeleton stopped saying "document-release" anywhere in the workflow body — the dispatch became invisible at exactly the moments an agent decides whether to open the section. Restore visibility at three touchpoints, all subagent-framed (never bare-slash-framed, which would invite an inline Skill invocation that bypasses the fresh-context subagent + JSON contract): - manifest trigger (renders into the section-index row AND the STOP pointer): "dispatching the /document-release subagent to sync docs (Step 18) and then creating or updating the PR/MR (Step 19)" - Step 17 handoff line names Step 18's dispatch explicitly - new hoisted doc-sync invariant beside the PR-title invariant: the dispatch itself is never skipped; only a failed subagent is non-blocking Pin it in carve-guards: 'the /document-release subagent' (all three touchpoints) + 'dispatches the /document-release subagent' (invariant) must stay in the skeleton; the carved imperative 'Dispatch /document-release as a subagent' must stay carved. Skeleton cap 91,600 → 92,300 (measured 91,764; trigger renders twice). Goldens regenerated for all three hosts. Co-Authored-By: Claude Fable 5 --- ship/SKILL.md | 8 +++--- ship/SKILL.md.tmpl | 4 ++- ship/sections/manifest.json | 2 +- test/fixtures/golden/claude-ship-SKILL.md | 8 +++--- test/fixtures/golden/codex-ship-SKILL.md | 4 ++- test/fixtures/golden/factory-ship-SKILL.md | 4 ++- test/helpers/carve-guards.ts | 29 +++++++++++++++++++--- 7 files changed, 45 insertions(+), 14 deletions(-) diff --git a/ship/SKILL.md b/ship/SKILL.md index 6c61f37ed..584d2bc53 100644 --- a/ship/SKILL.md +++ b/ship/SKILL.md @@ -937,7 +937,7 @@ sections. Read a section in full before doing its step; do not work from memory. | addressing Greptile review comments when a PR exists (Step 10) | `sections/greptile.md` | | the adversarial review and learnings capture (Step 11) | `sections/adversarial.md` | | writing the CHANGELOG entry (Step 13) | `sections/changelog.md` | -| syncing docs and creating or updating the PR/MR (Steps 18-19) | `sections/pr-body.md` | +| dispatching the /document-release subagent to sync docs (Step 18) and then creating or updating the PR/MR (Step 19) | `sections/pr-body.md` | --- @@ -1417,13 +1417,15 @@ If `ALREADY_PUSHED`, skip the push but continue to Step 18. Otherwise push with git push -u origin ``` -**You are NOT done.** The code is pushed but documentation sync and PR creation are mandatory final steps. Continue to Step 18. +**You are NOT done.** The code is pushed but Step 18 (dispatch the /document-release subagent to sync docs) and Step 19 (create the PR/MR) are mandatory final steps. Continue to Step 18. --- **PR/MR title invariant (always applies — do not skip even if you don't open the section below):** Any PR or MR you create OR update in the next step MUST have a title that starts with `v$NEW_VERSION` (the version bumped in Step 12), in the format `v : `. Never create or edit a PR/MR title without this prefix. Compute the correct title with the single source of truth helper: `~/.claude/skills/gstack/bin/gstack-pr-title-rewrite.sh "$NEW_VERSION" ""`. The full create/update procedure (idempotency, redaction scan, self-check) is in the section below. -> **STOP.** Before syncing docs and creating or updating the PR/MR (Steps 18-19), Read `~/.claude/skills/gstack/ship/sections/pr-body.md` and execute it +**Doc-sync invariant (always applies — do not skip even if you don't open the section below):** Step 18 dispatches the /document-release subagent BEFORE the PR/MR is created or updated in Step 19. Never skip the dispatch itself; only a failed subagent is non-blocking (proceed to Step 19 without a `## Documentation` section). + +> **STOP.** Before dispatching the /document-release subagent to sync docs (Step 18) and then creating or updating the PR/MR (Step 19), Read `~/.claude/skills/gstack/ship/sections/pr-body.md` and execute it > in full. Do not work from memory — that section is the source of truth for this step. ## Step 20: Persist ship metrics diff --git a/ship/SKILL.md.tmpl b/ship/SKILL.md.tmpl index 19c2a0876..ac41e2c5a 100644 --- a/ship/SKILL.md.tmpl +++ b/ship/SKILL.md.tmpl @@ -496,12 +496,14 @@ If `ALREADY_PUSHED`, skip the push but continue to Step 18. Otherwise push with git push -u origin ``` -**You are NOT done.** The code is pushed but documentation sync and PR creation are mandatory final steps. Continue to Step 18. +**You are NOT done.** The code is pushed but Step 18 (dispatch the /document-release subagent to sync docs) and Step 19 (create the PR/MR) are mandatory final steps. Continue to Step 18. --- **PR/MR title invariant (always applies — do not skip even if you don't open the section below):** Any PR or MR you create OR update in the next step MUST have a title that starts with `v$NEW_VERSION` (the version bumped in Step 12), in the format `v : `. Never create or edit a PR/MR title without this prefix. Compute the correct title with the single source of truth helper: `~/.claude/skills/gstack/bin/gstack-pr-title-rewrite.sh "$NEW_VERSION" ""`. The full create/update procedure (idempotency, redaction scan, self-check) is in the section below. +**Doc-sync invariant (always applies — do not skip even if you don't open the section below):** Step 18 dispatches the /document-release subagent BEFORE the PR/MR is created or updated in Step 19. Never skip the dispatch itself; only a failed subagent is non-blocking (proceed to Step 19 without a `## Documentation` section). + {{SECTION:pr-body}} ## Step 20: Persist ship metrics diff --git a/ship/sections/manifest.json b/ship/sections/manifest.json index 4ffc25023..e4394e562 100644 --- a/ship/sections/manifest.json +++ b/ship/sections/manifest.json @@ -56,7 +56,7 @@ "id": "pr-body", "file": "pr-body.md", "title": "Documentation sync + PR/MR creation", - "trigger": "syncing docs and creating or updating the PR/MR (Steps 18-19)" + "trigger": "dispatching the /document-release subagent to sync docs (Step 18) and then creating or updating the PR/MR (Step 19)" } ] } diff --git a/test/fixtures/golden/claude-ship-SKILL.md b/test/fixtures/golden/claude-ship-SKILL.md index 6c61f37ed..584d2bc53 100644 --- a/test/fixtures/golden/claude-ship-SKILL.md +++ b/test/fixtures/golden/claude-ship-SKILL.md @@ -937,7 +937,7 @@ sections. Read a section in full before doing its step; do not work from memory. | addressing Greptile review comments when a PR exists (Step 10) | `sections/greptile.md` | | the adversarial review and learnings capture (Step 11) | `sections/adversarial.md` | | writing the CHANGELOG entry (Step 13) | `sections/changelog.md` | -| syncing docs and creating or updating the PR/MR (Steps 18-19) | `sections/pr-body.md` | +| dispatching the /document-release subagent to sync docs (Step 18) and then creating or updating the PR/MR (Step 19) | `sections/pr-body.md` | --- @@ -1417,13 +1417,15 @@ If `ALREADY_PUSHED`, skip the push but continue to Step 18. Otherwise push with git push -u origin ``` -**You are NOT done.** The code is pushed but documentation sync and PR creation are mandatory final steps. Continue to Step 18. +**You are NOT done.** The code is pushed but Step 18 (dispatch the /document-release subagent to sync docs) and Step 19 (create the PR/MR) are mandatory final steps. Continue to Step 18. --- **PR/MR title invariant (always applies — do not skip even if you don't open the section below):** Any PR or MR you create OR update in the next step MUST have a title that starts with `v$NEW_VERSION` (the version bumped in Step 12), in the format `v : `. Never create or edit a PR/MR title without this prefix. Compute the correct title with the single source of truth helper: `~/.claude/skills/gstack/bin/gstack-pr-title-rewrite.sh "$NEW_VERSION" ""`. The full create/update procedure (idempotency, redaction scan, self-check) is in the section below. -> **STOP.** Before syncing docs and creating or updating the PR/MR (Steps 18-19), Read `~/.claude/skills/gstack/ship/sections/pr-body.md` and execute it +**Doc-sync invariant (always applies — do not skip even if you don't open the section below):** Step 18 dispatches the /document-release subagent BEFORE the PR/MR is created or updated in Step 19. Never skip the dispatch itself; only a failed subagent is non-blocking (proceed to Step 19 without a `## Documentation` section). + +> **STOP.** Before dispatching the /document-release subagent to sync docs (Step 18) and then creating or updating the PR/MR (Step 19), Read `~/.claude/skills/gstack/ship/sections/pr-body.md` and execute it > in full. Do not work from memory — that section is the source of truth for this step. ## Step 20: Persist ship metrics diff --git a/test/fixtures/golden/codex-ship-SKILL.md b/test/fixtures/golden/codex-ship-SKILL.md index ba77c8d78..7466c4b07 100644 --- a/test/fixtures/golden/codex-ship-SKILL.md +++ b/test/fixtures/golden/codex-ship-SKILL.md @@ -2672,12 +2672,14 @@ If `ALREADY_PUSHED`, skip the push but continue to Step 18. Otherwise push with git push -u origin ``` -**You are NOT done.** The code is pushed but documentation sync and PR creation are mandatory final steps. Continue to Step 18. +**You are NOT done.** The code is pushed but Step 18 (dispatch the /document-release subagent to sync docs) and Step 19 (create the PR/MR) are mandatory final steps. Continue to Step 18. --- **PR/MR title invariant (always applies — do not skip even if you don't open the section below):** Any PR or MR you create OR update in the next step MUST have a title that starts with `v$NEW_VERSION` (the version bumped in Step 12), in the format `v : `. Never create or edit a PR/MR title without this prefix. Compute the correct title with the single source of truth helper: `$GSTACK_ROOT/bin/gstack-pr-title-rewrite.sh "$NEW_VERSION" ""`. The full create/update procedure (idempotency, redaction scan, self-check) is in the section below. +**Doc-sync invariant (always applies — do not skip even if you don't open the section below):** Step 18 dispatches the /document-release subagent BEFORE the PR/MR is created or updated in Step 19. Never skip the dispatch itself; only a failed subagent is non-blocking (proceed to Step 19 without a `## Documentation` section). + ## Step 18: Documentation sync (via subagent, before PR creation) **Dispatch /document-release as a subagent** using the Agent tool with `subagent_type: "general-purpose"`. The subagent gets a fresh context window — zero rot from the preceding 17 steps. It also runs the **full** `/document-release` workflow (with CHANGELOG clobber protection, doc exclusions, risky-change gates, named staging, race-safe PR body editing) rather than a weaker reimplementation. diff --git a/test/fixtures/golden/factory-ship-SKILL.md b/test/fixtures/golden/factory-ship-SKILL.md index 6f0a3d0d4..2fca22277 100644 --- a/test/fixtures/golden/factory-ship-SKILL.md +++ b/test/fixtures/golden/factory-ship-SKILL.md @@ -3078,12 +3078,14 @@ If `ALREADY_PUSHED`, skip the push but continue to Step 18. Otherwise push with git push -u origin ``` -**You are NOT done.** The code is pushed but documentation sync and PR creation are mandatory final steps. Continue to Step 18. +**You are NOT done.** The code is pushed but Step 18 (dispatch the /document-release subagent to sync docs) and Step 19 (create the PR/MR) are mandatory final steps. Continue to Step 18. --- **PR/MR title invariant (always applies — do not skip even if you don't open the section below):** Any PR or MR you create OR update in the next step MUST have a title that starts with `v$NEW_VERSION` (the version bumped in Step 12), in the format `v : `. Never create or edit a PR/MR title without this prefix. Compute the correct title with the single source of truth helper: `$GSTACK_ROOT/bin/gstack-pr-title-rewrite.sh "$NEW_VERSION" ""`. The full create/update procedure (idempotency, redaction scan, self-check) is in the section below. +**Doc-sync invariant (always applies — do not skip even if you don't open the section below):** Step 18 dispatches the /document-release subagent BEFORE the PR/MR is created or updated in Step 19. Never skip the dispatch itself; only a failed subagent is non-blocking (proceed to Step 19 without a `## Documentation` section). + ## Step 18: Documentation sync (via subagent, before PR creation) **Dispatch /document-release as a subagent** using the Agent tool with `subagent_type: "general-purpose"`. The subagent gets a fresh context window — zero rot from the preceding 17 steps. It also runs the **full** `/document-release` workflow (with CHANGELOG clobber protection, doc exclusions, risky-change gates, named staging, race-safe PR body editing) rather than a weaker reimplementation. diff --git a/test/helpers/carve-guards.ts b/test/helpers/carve-guards.ts index f665248c2..25243a8da 100644 --- a/test/helpers/carve-guards.ts +++ b/test/helpers/carve-guards.ts @@ -116,17 +116,38 @@ export const CARVE_GUARDS: Record = { // The PR-title-version invariant MUST stay always-loaded: the v1.54.0.0 // carve stranded it in pr-body.md and PRs started landing with bare titles // (CI backstop: test/pr-title-sync-workflow-safety.test.ts). - mustStayInSkeleton: ['v$NEW_VERSION', 'gstack-pr-title-rewrite'], + // Same carve also stranded the Step 18 /document-release dispatch out of + // sight — the skeleton never named it and the handoff "got lost" (#2666 + // follow-up). The two subagent anchors pin the restored visibility: + // 'the /document-release subagent' matches all three touchpoints (trigger + // via section-index + STOP pointer, Step 17 handoff, hoisted invariant); + // 'dispatches the /document-release subagent' pins the invariant itself. + // Matching is case-sensitive String.includes — "dispatching the" does NOT + // contain "dispatch the" — so update anchors in lockstep with any + // touchpoint rewording. + mustStayInSkeleton: [ + 'v$NEW_VERSION', + 'gstack-pr-title-rewrite', + 'the /document-release subagent', + 'dispatches the /document-release subagent', + ], // ...while the full create/update procedure stays carved into pr-body.md // (out of the skeleton, present in the union). Asserts BOTH PR paths - // survive: the create path and the idempotent update path. - mustMoveToSection: ['gh pr create --base', 'gh pr edit --title'], + // survive: the create path and the idempotent update path. The Step 18 + // dispatch imperative stays carved too — pasting that literal into the + // skeleton (correctly) fails this guard; the skeleton speaks of "the + // /document-release subagent", never the carved imperative. + mustMoveToSection: [ + 'gh pr create --base', + 'gh pr edit --title', + 'Dispatch /document-release as a subagent', + ], // ship is operational (multi-STOP, not a plan review); no single post-STOP gate. gateAfterStop: undefined, }, behavioral: 'external', externalTest: 'test/skill-e2e-ship-section-loading.test.ts', - maxSkeletonBytes: 91_600, // v1.68 fix wave: unconditional learnings capture (#2402, ~450B/skill); measured 91,061 + maxSkeletonBytes: 92_300, // document-release visibility restore: named trigger (renders twice) + Step 17 handoff + hoisted doc-sync invariant; measured 91,764 minUnionBytes: 120_000, mustContain: ['VERSION', 'CHANGELOG', 'review', 'merge', 'PR'], // v1.58.5.0: pre-push-guard install (#2077) stacks on the shared first-run-guidance preamble.