feat: stack development in three phases - stack-dev (design), stack-build, stack-close, handed over through tracker states (#168)
CI / verify (push) Successful in 5m15s
CI / pwsh (push) Successful in 2m2s
Release / release (push) Successful in 34s

stack-build and stack-close carry disable-model-invocation, so each phase change is
the operator's slash command; no skill offers a mid-session /model or /effort switch.
Mode rules and the phase table move to instructions/dev/stack-mode.md, publish and CI
waiting to instructions/dev/publish-and-ci.md, the ready definition to issue-tracking.md.

Files changed:
- AGENTS.md
- CHANGES.md
- DEVELOPMENT.md
- README.md
- VERSION
- docs/model-and-effort-selection.md
- instructions/CONTRACT.md
- instructions/dev/commonplace-kb.md
- instructions/dev/dev-setup.md
- instructions/dev/doc-pull-through.md
- instructions/dev/issue-tracking.md
- instructions/dev/publish-and-ci.md
- instructions/dev/stack-build/SKILL.md
- instructions/dev/stack-close/SKILL.md
- instructions/dev/stack-dev/SKILL.md
- instructions/dev/stack-mode.md
- instructions/dev/testing-conventions.md
- instructions/dev/version-parts.md
- tools/chemenu/commands/git_publish.py
- tools/chemenu/tests/test_git_publish.py
- tools/chemenu/tests/test_instructions_cmd.py

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SnAJ7Z3CpVD3PRbN73QtU2
This commit is contained in:
torbenandClaude Opus 5.5 committed 2026-10-02 20:33:07 +02:00
1 parent 80488bee38
commit d8224ee2ab
21 files changed
+631 -331

No files matched your search

+77 -99
View File
@@ -1,98 +1,70 @@
---
name: stack-close
description: Closes out a stack-dev work package after its publish has landed - rewrites the issue body to its final state, checks for docs/ staleness, and names which model ran which phase of the session. Use right after a stack-dev session's tools/wikitool publish succeeds, or when resuming a package that was published but never closed.
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 records which model and effort ran each phase in the closing comment. 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 break `stack-dev` has to remember to ask for mid-flow.
own skill rather than a step the build session has to remember to take on its own.
**Trigger:** A `stack-dev` session's `tools/wikitool publish` just succeeded - `stack-dev` ends
there and hands off here rather than continuing into this phase in the same breath. Also: `publish`
printed its stack-machinery note ("this publish touched stack machinery...") and nothing has
closed the work package it belongs to yet; or a package was published in an earlier session and
never went through this skill (the gap this split exists to make impossible to skip past
silently - see `instructions/dev/issue-tracking.md`'s note that a closed body is the version
everyone reads afterwards and nobody revisits).
**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.
**This directory is dev-only.** Same boundary as `stack-dev`
(its own `instructions/dev/stack-dev/SKILL.md` has the full reasoning) - `dist export` prunes
`instructions/dev/` wholesale, so this skill never reaches a distributed instance.
`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, not `stack-dev`'s step 6
## Why this is a separate skill, and why the operator starts it
The two phases around the mechanical middle of a stack-dev session have no mechanical guard at
all - `pytest`, `docs verify` and `instructions verify` cover the code and tests in between, and
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 second unchecked stretch - as a prose break inside
`stack-dev`'s own step 6 - 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. Splitting the phase into its own skill does not add a check either - `wikitool` still
does not know this tracker exists and must not learn (see
`instructions/dev/issue-tracking.md` § What no tool checks) - but it removes the thing that
was actually failing: the closing *procedure* is no longer sitting in the session's context as a
next step to run past - it exists only inside a skill someone has to invoke.
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).
**Be precise about what that does and does not buy**, because the honest version is weaker than
"now it cannot be skipped". What did **not** change is the trigger: `stack-dev`'s "invoke it now"
is still a sentence, and `publish`'s stack-machinery note is deliberately generic enough not to
name this skill at all. Two of the three links in that chain remain self-discipline. The split
narrows the failure, it does not close it - treat a session that reaches this text as the
mechanism having worked *this time*, not as proof that it always will.
See Gitea #47 for the full incident history and the rejected alternative (a
model-switched subagent - not buildable in Claude Code, where a fork inherits the parent's model
and a fresh subagent starts without the session's context).
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. **Offer the model switch back up, once, and keep working either way.** A model of the
message, not a script to quote: say it in the instance's KB language, per `AGENTS.md`
§ File naming.
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.
> From here on no mechanical check applies - nothing verifies the issue body, `docs/`
> staleness, or the changelog prose. If you want to switch back to Opus, now is the moment.
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), and record it in step 4.
**Never block on the answer.** The change is already published; a session that stops here
leaves exactly the state this skill exists to prevent.
2. **Rewrite the issue body to its final state, then close.** The test is what a reader who
opens the closed issue tomorrow would conclude:
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
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
Then one short comment naming what changed against the previous state, and nothing else -
`instructions/dev/issue-tracking.md` steps 2-3 and 7 have the full shape; this is that
procedure, run at the point this skill exists to guarantee it actually gets run.
**Close only once CI on the published commit is green, and wait for it this way.** The
push-triggered runs take about **4 minutes**; a push that moved `VERSION` to a suffix-free
release adds about **1 minute** for the release job. So:
- Read the runs for the published commit's SHA through the authenticated Gitea connection
`ENVIRONMENT.md` lists (the Gitea MCP's `actions_run_read`, `list_runs`), right after
`publish`, to get their ids. **Never anonymously via `curl`:** Gitea answers the Actions
API with `401 token is required` even for this public repo.
- Check again after about 4 minutes (5 with a release job), then once a minute.
- **Give up after 15 minutes** and hand the open runs to the user by id and link, rather than
waiting on.
- Any shell loop that polls instead must **end on the first non-2xx status or missing field**
and print the raw response. A loop that treats an error as "not finished yet" never ends;
that happened in the #139 close-out.
A red run is not closed over: report it, and the package stays open until it is fixed.
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. Nothing mechanical
catches it, which is why this is a step - and now a whole skill - rather than a habit. #44 and
#45 both closed exactly this way on the old, single-skill shape, the second an hour after the
rule was first written down.
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
@@ -107,7 +79,7 @@ and a fresh subagent starts without the session's context).
**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-dev` step 5
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
@@ -118,42 +90,48 @@ and a fresh subagent starts without the session's context).
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.** This phase runs *after* `stack-dev` step 4
has already bumped the version, and the documents it touches are frequently the ones CI's
version gate watches - `types/`, `instructions/`, `tools/`, `AGENTS.md`, any
`<stage>/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 commit - it
only advances the running candidate's counter - rather than discovering it from a red run
after the issue is already closed.
**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 `<stage>/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. **Name which model ran which phase - not only this one.** This is the handover in full, not
a note about the tail alone: state the model for the design/version-part/boundary-judgment
phase (`stack-dev` step 3), for the mechanical middle (code, tests, the version bump), and for
this closing phase - all three, even when they are all the same model. A handover that only
flags a cheap-model *closing* phase stays silent exactly when the earlier, equally unchecked
design phase also ran cheap and nobody offered the switch back then either; naming all three
every time is what keeps that omission from being the quiet default.
4. **Record the handover, then close.** The closing comment carries the one changelog line
`instructions/dev/issue-tracking.md` step 3 asks for, and the handover for **every phase**,
not only this one:
| Phase | Model | Effort | Own session? | Context overflowed or compacted? |
|---|---|---|---|---|
| 1 Design (`stack-dev`) | | | | |
| 2 Build (`stack-build`) | | | | |
| 3 Closing (`stack-close`) | | | | |
plus the issue's `size/` label. Fill every cell, even when all three phases ran the same
model in one session - a handover that only flags the unusual case stays silent exactly when
an equally unchecked phase also ran cheap. Phases from an earlier session are named from the
record (the issue's comments, `CHANGES.md`), not from memory, and marked unknown where the
record does not say. These rows are the evidence `docs/model-and-effort-selection.md`'s
phase guide is checked against.
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 landed - not after every individual publish. A
package still open across sessions keeps its body current per
`instructions/dev/issue-tracking.md` step 2 in the meantime; that is maintenance, not
closing.
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." The handover in step 4 names the earlier phases from the historical record
(the issue's comments, `CHANGES.md`) rather than from memory.
it "correctly." Step 4 names the earlier phases from the record.
- **Nothing to close - the session's own exploration, no publish happened?** This skill does not
apply; there is no package to rewrite a body for.
apply; there is no package to close.
## Scope
Follows a `stack-dev` session's publish. 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.
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.