--- name: stack-close description: Closes out a stack work package once its publish has a green CI run - checks the issue body's final state, checks for docs/ and contract staleness, and closes the issue. Started only by the operator as /stack-close, after stack-build has ended its phase. disable-model-invocation: true --- # Stack Close **Purpose:** Carry out the unchecked closing phase of a stack-development work package, as its own skill rather than a step the build session has to remember to take on its own. **Trigger:** The operator runs `/stack-close`, after `stack-build` ended its phase with a green CI run on the published commit. Nothing else starts this skill: the frontmatter's `disable-model-invocation` keeps Claude Code from invoking it, and in a harness that ignores that key this sentence is the rule. Also for a package that was published in an earlier session and never closed - the operator starts it the same way. `instructions/dev/stack-mode.md` has the rules of a stack-development session and the three phases this one ends; read it first when this session started cold. ## Why this is a separate skill, and why the operator starts it The phases around the checked middle of a work package have no mechanical guard at all - `pytest`, `docs verify`, `instructions verify` and CI cover the code and tests in between, and nothing covers a changelog entry's accuracy, a `docs/` page's staleness, or an issue body's final state (see `docs/model-and-effort-selection.md`). Asking the same session to notice it has crossed into that unchecked stretch - as a prose break inside one long skill - failed twice in a row on this stack (Gitea #42, then #30): both times the session knew the rule and skipped past it anyway, because nothing in the moment forced the question. The first split (Gitea #47) moved the closing procedure into its own skill, so it no longer sat in the session's context as a next step to run past - but the trigger stayed a sentence: the build skill told the agent to "invoke it now", and a prose model-switch offer at the same point never once led to a switch (Gitea #50). Since Gitea #168 the trigger is the operator's slash command, and the skill cannot be invoked by the agent at all in Claude Code. **Be precise about what that buys.** The phase change is now a real stop rather than a sentence in the output, and it is where the operator decides on context and model. What moved is the risk #47 named: forgetting the close is now the operator's failure, not the agent's. Two signals stay to catch it - `publish`'s note that what follows CI is checked by nothing, and an open issue on the board whose criteria are ticked and whose CI is green. Treat a session that reaches this text as the mechanism having worked *this time*, not as proof that it always will. ## Steps 1. **Check the handover you start from.** The body names a green CI run on the published commit (`stack-build` step 6). If it does not, or the run is red, this is not the closing phase yet: say so and recommend `/stack-build #N`. A red run is never closed over. This phase is meant to run on Opus at high effort - a lower effort gives up multi-file consistency first, which is exactly what the staleness check in step 3 needs. If this session runs on something else, say so once and carry on; offer no `/model` or `/effort` switch (see `instructions/dev/stack-mode.md` § Sessions and models). 2. **Check that the body is in its final state.** `stack-build` kept it current at three fixed points, so this is a check, not a rewrite - but the test is still what a reader who opens the closed issue tomorrow would conclude: - every acceptance criterion ticked, or struck with the reason it was dropped - proposals that were decided read as decided; a "to decide" section has become the decision and its reasoning; an open, non-blocking question carries its answer - nothing left in the present tense about a defect that no longer exists - what was verified is named - which checks ran, which CI run - not a commit hash alone Fix what is off by rewriting the body (`instructions/dev/issue-tracking.md` steps 2 and 7). **A closing report in a comment does not satisfy this**, however thorough: it reads as complete to whoever writes it and leaves a body still phrased as open work. #44 and #45 both closed exactly this way, the second an hour after the rule was first written down. 3. **Check whether a `docs/` page, a contract, or a new human doc went stale.** A `docs/` page carries no normative sentence, so nothing verifies it by construction (AGENTS.md § File naming) - the same is true of `tools/CONTRACT.md`'s generated command records and any touched `/CONTRACT.md`, whose prose `docs verify` checks only for presence and record membership, never for what a field or a section actually says (`instructions/dev/doc-pull-through.md`); of `README.md`/`INSTALL.md`/`DEVELOPMENT.md` prose; and of a new instruction's own wording, which `instructions verify` checks structurally but never for what it claims. If the change this package shipped moved the reasoning or the behaviour one of these documents describes, update it now; if none did, say so rather than leaving the question unasked. **If the published diff touches an installation instruction, read the human guide against it once more.** Which instructions those are and which human document answers for each is the pull-through table's row in `instructions/dev/doc-pull-through.md` - `stack-build` step 5 applied it before the publish; this is the second reading, after. A deviation found here is filed as a follow-up issue naming both files and the sentence that disagrees, rather than fixed in this phase: the pull-through before the publish missed it, and that miss is worth a record of its own. **If that update moved a `##`/`###` heading, the file's table of contents is now stale** - regenerate it with `tools/wikitool docs toc --apply`, never by editing the list. The region is generated (AGENTS.md invariant 1), `docs verify` fails on stale exactly as on missing, and a pull-through in this phase is a common way to move a heading without noticing. **A pull-through of its own needs its own bump, publish and CI run.** The documents this phase touches are frequently the ones CI's version gate watches - `types/`, `instructions/`, `tools/`, `AGENTS.md`, any `/CONTRACT.md`. A commit into one of those without a `VERSION` line fails the gate (`.gitea/workflows/ci.yml`, "Version gate"), whatever the session meant it as. Reading the edit as "only documentation" is the trap: `types/source.md` is a document *and* a shipped behaviour description, and the gate is scoped by path, not by intent. So run `tools/wikitool version bump --patch` in the same breath as the pull-through - it only advances the running candidate's counter - then publish and wait per `instructions/dev/publish-and-ci.md`, and name that run in the body too. 4. **Comment, then close.** The closing comment carries the one changelog line `instructions/dev/issue-tracking.md` step 3 asks for. Then close the issue. ## Decision points - **The work package spans several sessions?** Run this skill once, at the point the package is actually finished and its last publish has a green run - not after every individual publish. - **Resuming a package whose publish landed in an earlier, already-ended session?** Run this skill now, on whatever model the current session is - do not reopen the earlier session to run it "correctly." - **Nothing to close - the session's own exploration, no publish happened?** This skill does not apply; there is no package to close. ## Scope Follows `stack-build`'s green CI run (`instructions/dev/stack-build/SKILL.md`). Not for wiki content work - use `wiki-ingest`/`wiki-query`/`wiki-manage`/`wiki-lint`/`wiki-status`/ `gtd-weekly-review` for that, whose own closing conventions (`kb/log.md`, page provenance) are unrelated to this tracker-body procedure.