feat: stack development in three phases - stack-dev (design), stack-build, stack-close, handed over through tracker states (#168)
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:
1 parent
80488bee38
commit
d8224ee2ab
21 files changed
+631
-331
No files matched your search
@@ -335,8 +335,9 @@ see [Gates](#gates).
|
||||
|
||||
Extending `tools/wikitool`, the type schema, or the instruction/skill layer itself (rather than
|
||||
operating on wiki content) is a different session type with different rules - see the
|
||||
`stack-dev` skill, nested under [instructions/dev/](instructions/dev/) along with the
|
||||
procedures it routes to. Setting up a clone of this origin repository for that work - the demo
|
||||
`stack-dev` skill, the entry to that work's three phases (`stack-dev`, `stack-build`,
|
||||
`stack-close`), nested under [instructions/dev/](instructions/dev/) along with the
|
||||
procedures they route to. Setting up a clone of this origin repository for that work - the demo
|
||||
corpus, the preflight, `dist export` as a build and test tool - is
|
||||
[instructions/dev/dev-setup.md](instructions/dev/dev-setup.md). Never present in a distributed
|
||||
instance.
|
||||
|
||||
+38
-1
@@ -59,7 +59,7 @@ concern - readable here, never shipped as something to parse.
|
||||
|
||||
---
|
||||
|
||||
## 8.0.0-beta.23 - 2026-10-02 - publish keeps a closing trailer block of --message last, so git reads Co-Authored-By again (#149)
|
||||
## 8.0.0-beta.24 - 2026-10-02 - Stack-Entwicklung in drei Phasen: stack-dev (Design), stack-build, stack-close - Übergabe über den Tracker, kein Modellwechsel in der Sitzung
|
||||
|
||||
**Author:** Torben Nehmer
|
||||
|
||||
@@ -98,6 +98,7 @@ concern - readable here, never shipped as something to parse.
|
||||
- INSTALL.md an die Installationsinstruktionen gekoppelt: Voraussetzungen generiert, Setup-Fragen geprüft
|
||||
- tools/bugreport: Starter für den Bugreport-Sammler, überspringt die Store-Aliase (#166)
|
||||
- publish keeps a closing trailer block of --message last, so git reads Co-Authored-By again (#149)
|
||||
- Stack-Entwicklung in drei Phasen: stack-dev (Design), stack-build, stack-close - Übergabe über den Tracker, kein Modellwechsel in der Sitzung
|
||||
|
||||
**Low impact**
|
||||
- version bump no longer points at version release in its output
|
||||
@@ -135,6 +136,42 @@ concern - readable here, never shipped as something to parse.
|
||||
- preflight.ps1: the asset-mode error helper is Exit-Asset, so PSScriptAnalyzer passes
|
||||
<!-- /wikitool:bumps -->
|
||||
|
||||
### Stack-Entwicklung in drei Phasen: `stack-dev` (Design), `stack-build`, `stack-close` (#168)
|
||||
|
||||
Bisher hatte die Stack-Entwicklung zwei Skills für drei Phasen und koppelte jeden Phasenwechsel an
|
||||
einen Modellwechsel mitten in der Sitzung: `stack-dev` umfasste Design und Bau und bot am Übergang
|
||||
`/model sonnet` an, `stack-close` bot `/model opus` an und wartete auch noch auf CI. Der Wechsel
|
||||
fand in der Praxis nie statt (#50), er hätte den Prompt-Cache verworfen, und die Bauphase passt
|
||||
nicht in Sonnets Kontext (#151 B lief in die Kompaktierung). Ein roter CI-Lauf machte außerdem die
|
||||
Abschlussphase wieder zur Bauphase.
|
||||
|
||||
Jetzt hat jede Phase ihren Skill, und übergeben wird über einen Zustand im Tracker statt über den
|
||||
Kontext einer Sitzung:
|
||||
|
||||
- `stack-dev` ist der Design-Skill und bleibt der automatische Einstieg. Er endet an einem Body,
|
||||
der **ready** ist (`instructions/dev/issue-tracking.md` § Ready to build), und nennt dem
|
||||
Betreiber `/stack-build #N`.
|
||||
- `stack-build` (neu) prüft zuerst, ob der Body ready ist, baut, bumpt, zieht die Dokumente nach,
|
||||
publiziert und wartet auf grünes CI. Den Body pflegt er an drei festen Stellen: bei einer
|
||||
Abweichung, nach dem Publish, bei grünem CI.
|
||||
- `stack-close` prüft den Endzustand des Bodys und veraltete `docs/`- und Contract-Prosa, dann
|
||||
schließt er das Issue. Der Schließkommentar nennt pro Phase Modell, Effort, Sitzungsgrenze und
|
||||
Kontextüberlauf, dazu `size/`.
|
||||
|
||||
`stack-build` und `stack-close` tragen `disable-model-invocation: true`. In Claude Code kann sie
|
||||
deshalb nur der Betreiber starten, und jeder Phasenwechsel ist ein echter Halt, an dem er über
|
||||
Weitermachen, `/clear` oder ein anderes Modell entscheidet. Keiner der drei Skills bietet noch
|
||||
einen `/model`- oder `/effort`-Wechsel in der Sitzung an. Laut Anthropics Doku zum Prompt-Caching
|
||||
invalidiert auch eine Effort-Änderung den gecachten Gesprächsverlauf, daher wird auch dort an
|
||||
einer Übergabe geschnitten statt umgeschaltet.
|
||||
|
||||
Gemeinsames steht je einmal in eigenen Instruktionen: die Mode-Regeln, die Phasentabelle und der
|
||||
Katalog der Dev-Verfahren in `instructions/dev/stack-mode.md`, die lokalen Checks, `publish` und
|
||||
das CI-Warten in `instructions/dev/publish-and-ci.md`. Die Notiz, die `publish` nach einem
|
||||
Stack-Publish druckt, sagt jetzt, dass CI noch kommt und erst danach die ungeprüfte Strecke
|
||||
beginnt. `docs/model-and-effort-selection.md` beschreibt die Modellwahl pro Sitzung und nennt
|
||||
Sonnet nicht mehr als Standard für die Bauphase.
|
||||
|
||||
### `publish` keeps a closing trailer block of `--message` last (#149)
|
||||
|
||||
`publish` appended its `Files changed:` list to the end of `--message`. git reads trailers only
|
||||
|
||||
+12
-9
@@ -164,12 +164,15 @@ Tracker laufen lässt und was ein roter Lauf bedeutet, steht in
|
||||
|
||||
## Stack-Entwicklung als eigener Sitzungstyp
|
||||
|
||||
Der `stack-dev`-Skill (`instructions/dev/`, nur in diesem Ursprungs-Repo vorhanden) fasst die
|
||||
Regeln für eine Sitzung, die den Stack selbst statt Wiki-Inhalt bearbeitet: wann
|
||||
Quellenbindung nicht gilt, wo Design endet und die mechanische Phase beginnt (mit dem
|
||||
Modellwechsel-Hinweis), und endet mit dem Publish. Die Schlussphase - Issue-Body als Rewrite
|
||||
statt Kommentar, `docs/`-Veralterung, die Modell-Handover-Zeile über die ganze Sitzung - liegt
|
||||
seit `4.6.0` in einem eigenen Folge-Skill, `stack-close`, den `stack-dev` an dieser Stelle
|
||||
übergibt statt sie als weiteren eigenen Schritt zu führen. Siehe
|
||||
[instructions/dev/issue-tracking.md](instructions/dev/issue-tracking.md) für den
|
||||
Issue-Tracker selbst.
|
||||
Drei Skills (`instructions/dev/`, nur in diesem Ursprungs-Repo vorhanden) führen eine Sitzung,
|
||||
die den Stack selbst statt Wiki-Inhalt bearbeitet, durch drei Phasen: `stack-dev` arbeitet das
|
||||
Issue aus, bis sein Body „ready“ ist, `stack-build` baut, publiziert und wartet auf einen grünen
|
||||
CI-Lauf, `stack-close` prüft den Endzustand des Bodys und veraltete `docs/`- und Contract-Prosa
|
||||
und schließt das Issue. Übergeben wird über den Zustand im Tracker, nicht über den Kontext einer
|
||||
Sitzung: Jeder Phasenwechsel geht in derselben Sitzung oder nach `/clear`. `stack-dev` greift
|
||||
automatisch; `stack-build` und `stack-close` startet nur der Betreiber per Slash-Kommando
|
||||
(`/stack-build #N`, `/stack-close`) - an genau dieser Stelle fällt die Wahl, ob es in derselben
|
||||
Sitzung weitergeht oder in einer neuen, auf welchem Modell. Einen Modellwechsel mitten in der
|
||||
Sitzung bietet keiner der drei an. Regeln und Phasentabelle stehen in
|
||||
[instructions/dev/stack-mode.md](instructions/dev/stack-mode.md), der Issue-Tracker selbst in
|
||||
[instructions/dev/issue-tracking.md](instructions/dev/issue-tracking.md).
|
||||
@@ -450,9 +450,9 @@ tools/wikitool <command> -h
|
||||
|
||||
<!-- dist:strip-start -->
|
||||
Dev-instance-only: extending `tools/wikitool`, the type schema, or the instruction/skill layer
|
||||
itself is a separate session type with its own rules, covered by the `stack-dev` skill nested
|
||||
under `instructions/dev/` (never present in a distributed instance - `tools/CONTRACT.md`
|
||||
explains why).
|
||||
itself is a separate session type with its own rules, covered by the `stack-dev`,
|
||||
`stack-build` and `stack-close` skills nested under `instructions/dev/` (never present in a
|
||||
distributed instance - `tools/CONTRACT.md` explains why).
|
||||
<!-- dist:strip-end -->
|
||||
|
||||
### MCP read server (optional)
|
||||
|
||||
@@ -14,11 +14,23 @@ one more round. Work behind nothing but a session reading prose does not surface
|
||||
ships, and stays until someone happens to notice. That asymmetry, not task size, is what the
|
||||
phase guide below is built on.
|
||||
|
||||
<!-- wikitool:toc -->
|
||||
## Contents
|
||||
|
||||
- [Phase guide](#phase-guide)
|
||||
- [Model per session, not per phase](#model-per-session-not-per-phase)
|
||||
- [Subagent models](#subagent-models)
|
||||
- [`/code-review` effort](#code-review-effort)
|
||||
- [When it's unclear](#when-its-unclear)
|
||||
- [Scope](#scope)
|
||||
<!-- /wikitool:toc -->
|
||||
|
||||
## Phase guide
|
||||
|
||||
<!-- dist:strip-start -->
|
||||
This repo's own stack-development work splits the axis into three phases, one per switch point
|
||||
in its `stack-dev`/`stack-close` skills:
|
||||
This repo's own stack-development work splits the axis into three phases, one skill each -
|
||||
`stack-dev` (design), `stack-build` (build) and `stack-close` (closing) - handed over through
|
||||
states in the issue tracker rather than inside one session:
|
||||
|
||||
<!-- dist:strip-end -->
|
||||
| Phase / task | What would catch a mistake | Suggested model | Effort |
|
||||
@@ -27,20 +39,48 @@ in its `stack-dev`/`stack-close` skills:
|
||||
| `wiki-lint` | `lint` itself is the check | Sonnet | default |
|
||||
| `wiki-ingest`, `wiki-manage`, judgment-heavy `wiki-query` | `lint` and `docs verify`, partly | Sonnet | high |
|
||||
| Stack dev: design, the version part, a boundary-crossing judgment | nothing mechanical | Opus | high |
|
||||
| Stack dev: code, tests, mechanical doc sync | `pytest`, `docs verify`, `instructions verify`, CI | Sonnet | high |
|
||||
| Stack dev: code, tests, mechanical doc sync, waiting for CI | `pytest`, `docs verify`, `instructions verify`, CI | Opus (open - see below) | high; medium for a small change |
|
||||
| Stack dev: closing an issue, `docs/` staleness, changelog prose | nothing, by construction | Opus | high |
|
||||
|
||||
The middle stack-dev row is where the tokens are and where the checks are, so it is the one worth
|
||||
running cheaper. The two rows around it are short - minutes, not hours - so keeping them on the
|
||||
stronger model costs little and protects the only work in the session that fails silently.
|
||||
The two unchecked rows are short - minutes, not hours - so keeping them on the strongest model
|
||||
costs little and protects the only work that fails silently. The checked middle row is where the
|
||||
tokens are, which makes it the tempting one to run cheaper. It is also the row with the least
|
||||
settled answer: a build phase of this stack reads a lot of the tree, and a smaller context window
|
||||
does not hold it - it runs into compaction, which costs more than the cheaper model saves. Sonnet
|
||||
at high effort remains possible, but only as a session of its own (below). Which default the
|
||||
middle row should have is left to the record: every closed work package names the model, effort
|
||||
and session shape of each phase, and the table follows that evidence rather than the other way
|
||||
round.
|
||||
|
||||
**Effort is the cheaper lever than the model.** A reduced effort level is what gives up
|
||||
multi-file consistency first, so `high` is a reasonable floor for anything touching more than one
|
||||
file or a contract; `default` suits a single-file mechanical edit with a test behind it.
|
||||
file or a contract; `default` or `medium` suits a small mechanical change with a test behind it.
|
||||
|
||||
A session cannot switch its own model - that is the user's `/model` - so this table only pays off
|
||||
if someone offers the switch at the moment a phase changes, once, without turning it into a
|
||||
debate.
|
||||
## Model per session, not per phase
|
||||
|
||||
A session cannot switch its own model - that is the user's `/model` - and it should not be asked
|
||||
to mid-flow either. Two reasons:
|
||||
|
||||
- **A switch throws away the prompt cache.** A cache entry belongs to the model that wrote it,
|
||||
so a new model starts the session's whole history from cold. The same holds for effort: changing it always invalidates
|
||||
the cached message history - by far the largest part of a long session - and, on some models,
|
||||
the tool and system prefix too (Anthropic's prompt-caching documentation lists effort and the
|
||||
thinking configuration among what invalidates the cache). A switch from one effort to another
|
||||
costs the same re-read of the whole session as a switch of model.
|
||||
- **An offered switch is rarely taken.** A sentence in the output at the moment a phase changes
|
||||
is easy to read past - for the agent writing it and for the user reading it - and the session
|
||||
just carries on in whatever it started as.
|
||||
|
||||
So the choice is made once, when a session starts, and phases that want different models or
|
||||
effort levels are separated by a session boundary instead: `/clear`, then the next phase in a
|
||||
session started the right way. That is only cheap if the next phase does not depend on the
|
||||
previous session's context - which is why the handover has to live somewhere outside the session
|
||||
(an issue body, a page) and be kept current at fixed points, not reconstructed at the end.
|
||||
<!-- dist:strip-start -->
|
||||
In this repo the handover points are a ready issue body (design → build) and a green CI run with
|
||||
the body updated (build → closing); the two later skills can only be started by the user's slash
|
||||
command, so each phase change is a real stop at which that choice is made.
|
||||
<!-- dist:strip-end -->
|
||||
|
||||
## Subagent models
|
||||
|
||||
@@ -70,9 +110,10 @@ choice a session *can* make on its own:
|
||||
reflexively over-provisioning is a standing cost every session pays.
|
||||
- Not sure whether a phase is checked: treat it as unchecked - a needless Opus phase costs money
|
||||
once, an unchecked Sonnet phase can ship something nobody looks at again.
|
||||
- Mid-session and the phase changed but nobody switched: keep working - never block a publish or
|
||||
an issue close on a model the session cannot change itself. Naming which model ran which phase
|
||||
in the handover keeps the gap visible instead of silent.
|
||||
- The phase changed and the session runs on a model or effort the table would not pick: keep
|
||||
working, and cut the session at the next handover rather than switching mid-flow - never block a
|
||||
publish or an issue close on a choice the session cannot make itself. Naming which model and
|
||||
effort ran which phase in the handover keeps the gap visible instead of silent.
|
||||
|
||||
## Scope
|
||||
|
||||
|
||||
@@ -239,7 +239,8 @@ that layer is named GTD rather than folded into `wiki-`), and `stack-` for the s
|
||||
development, nested under `instructions/dev/` and therefore never present in a distributed
|
||||
instance (`instructions/dev/` above).
|
||||
<!-- dist:strip-start -->
|
||||
Dev-instance-only: the two skills in that family today are `stack-dev` and `stack-close`.
|
||||
Dev-instance-only: the three skills in that family today are `stack-dev`, `stack-build` and
|
||||
`stack-close`.
|
||||
<!-- dist:strip-end -->
|
||||
A new skill takes the prefix of the family it belongs to, or opens a new one deliberately - never
|
||||
a bare name.
|
||||
@@ -350,8 +351,8 @@ symmetry: every other skill's flow is short enough, and fails loudly enough step
|
||||
reader cannot lose the thread even without a checklist - `wiki-manage`'s two flows, `wiki-query`,
|
||||
`wiki-status` and `gtd-weekly-review` all clear that bar.
|
||||
<!-- dist:strip-start -->
|
||||
Dev-instance-only: `stack-dev` and `stack-close` sit under the same threshold, for the same
|
||||
reason.
|
||||
Dev-instance-only: `stack-dev`, `stack-build` and `stack-close` sit under the same threshold,
|
||||
for the same reason.
|
||||
<!-- dist:strip-end -->
|
||||
|
||||
None of this is counted by number on purpose: a per-skill step count is a claim about a file this
|
||||
|
||||
@@ -24,5 +24,6 @@ carries it (see [tools/CONTRACT.md](../../tools/CONTRACT.md) for what `dist expo
|
||||
|
||||
## Scope
|
||||
|
||||
Only relevant while working in [stack-dev](stack-dev/SKILL.md) mode. Not part of the wiki
|
||||
Only relevant in a stack-development session ([stack-mode.md](stack-mode.md)), mostly while
|
||||
designing. Not part of the wiki
|
||||
content pipeline, and not linked from anything outside `instructions/dev/`.
|
||||
@@ -25,8 +25,8 @@ development material under `instructions/dev/` and `commonplace/`, and no
|
||||
instance.
|
||||
|
||||
2. **Run [bootstrap.md](../bootstrap.md)** - the preflight, then `tools/wikitool instructions
|
||||
sync`. That publishes `stack-dev` and `stack-close` along with the content skills; both exist
|
||||
only in this repository.
|
||||
sync`. That publishes `stack-dev`, `stack-build` and `stack-close` along with the content skills;
|
||||
all three exist only in this repository.
|
||||
|
||||
3. **Record the environment** (bootstrap.md step 5). Here it is worth the minute: which harness,
|
||||
that `gitea-mcp` reaches the tracker and CI, which remote `publish` talks to. Every stack-dev
|
||||
|
||||
@@ -18,8 +18,8 @@ way; see Gitea #89 for one).
|
||||
|
||||
## When to run
|
||||
|
||||
Before `tools/wikitool docs verify`/`publish` in a `stack-dev` session that changed behaviour -
|
||||
`stack-dev` step 5 sends you here. Read the table below and update every row whose surface you
|
||||
Before `tools/wikitool docs verify`/`publish` in a build session that changed behaviour -
|
||||
`stack-build` step 5 sends you here. Read the table below and update every row whose surface you
|
||||
touched; a row that does not apply needs no action.
|
||||
|
||||
## Steps
|
||||
@@ -76,7 +76,7 @@ touched; a row that does not apply needs no action.
|
||||
- **Unsure whether a `docs/` page's reasoning moved?** Read it. A `docs/` page carries no
|
||||
normative sentence and nothing verifies it by construction (AGENTS.md § File naming), so an
|
||||
unsure guess defaults to reading the page rather than skipping the question -
|
||||
[`stack-close`](stack-close/SKILL.md) step 3 asks it again at the end of the session as a
|
||||
[`stack-close`](stack-close/SKILL.md) step 3 asks it again in the closing phase as a
|
||||
backstop, not as the only time it is asked.
|
||||
- **The surface is a whole new stage, collection, or gate?** The table's rows are the steady
|
||||
state; a new row-worthy category is itself a change to this instruction - add the row here
|
||||
@@ -84,7 +84,7 @@ touched; a row that does not apply needs no action.
|
||||
|
||||
## Scope
|
||||
|
||||
Applies to `stack-dev` sessions only - wiki content changes have their own provenance and
|
||||
Applies to stack-development sessions only - wiki content changes have their own provenance and
|
||||
cross-reference rules (`kb/CONTRACT.md`, `wiki-manage`), which already pull the relevant pages
|
||||
through as part of the normal skill. Not a replacement for `stack-close` step 3, which re-asks
|
||||
the `docs/`-staleness question after publish as the second, session-final check.
|
||||
the `docs/`-staleness question after the green CI run as the second, final check.
|
||||
@@ -26,6 +26,7 @@ issues at that URL, which is exactly why `dist export` excludes
|
||||
- [When to run](#when-to-run)
|
||||
- [Steps](#steps)
|
||||
- [Incoming stubs](#incoming-stubs)
|
||||
- [Ready to build](#ready-to-build)
|
||||
- [Renames and other decay in the tracker](#renames-and-other-decay-in-the-tracker)
|
||||
- [Citing an issue in the repo](#citing-an-issue-in-the-repo)
|
||||
- [What no tool checks](#what-no-tool-checks)
|
||||
@@ -45,6 +46,8 @@ issues at that URL, which is exactly why `dist export` excludes
|
||||
(§ Incoming stubs).
|
||||
- **While working on one:** the body is updated as the state moves, not at the
|
||||
end (step 2). A session that is interrupted leaves the body as its handover.
|
||||
- Ending a design session, or starting a build from a body: check that it is
|
||||
ready (§ Ready to build).
|
||||
- Prioritising: deciding what to pick up next, or re-labelling after the ground
|
||||
moved.
|
||||
- Closing one: the body is rewritten to its final state first, and only then
|
||||
@@ -310,6 +313,30 @@ no publish - it is tracker work, and several stubs can be worked out in one pass
|
||||
What comes *after* it is an ordinary work package, picked up on its merits like
|
||||
any other.
|
||||
|
||||
## Ready to build
|
||||
|
||||
A body is **ready** when a session that has never seen the design conversation can build from
|
||||
it alone. That is the handover from design to build ([stack-mode.md](stack-mode.md) § The
|
||||
three phases): `stack-dev` ends by checking it, and `stack-build` checks it again as its first
|
||||
step. All four hold:
|
||||
|
||||
- **The body says what will be built**, and its acceptance criteria are checkable properties
|
||||
(step 1), not activities.
|
||||
- **No open question is left in it.** It carries `kind/build`, and neither `status/incoming`
|
||||
nor `status/unconfirmed`. A question that is genuinely not blocking may stay, marked as such
|
||||
and with the answer's consequence stated either way - "check X while building; if it does
|
||||
not hold, do Y" is a decision, "X is unclear" is not.
|
||||
- **The version part is named** ([version-parts.md](version-parts.md)). Whether a change is a
|
||||
drop-in replacement is a judgment with no mechanical guard, so it is made while designing, not
|
||||
discovered at the bump.
|
||||
- **The files or surfaces involved are named**, so the build starts from the tree rather than
|
||||
from a search for where the change belongs.
|
||||
|
||||
A body that fails one of these goes back to design. It is not built around: the gap a build
|
||||
session fills on its own is the same gap a `status/incoming` stub leaves (§ Incoming stubs),
|
||||
only better disguised. The useful side effect of the cut between the two phases is that it
|
||||
tests this definition - if a cold session cannot build from the body, it was not ready.
|
||||
|
||||
## Renames and other decay in the tracker
|
||||
|
||||
A rename is not finished when the tree is green. Renaming a package, a path,
|
||||
@@ -356,7 +383,7 @@ it in a `<!-- dist:strip-start/end -->` block ([instructions/CONTRACT.md](../CON
|
||||
|
||||
`tools/**/*.py` is deliberately outside all of this. A code comment addresses whoever edits that
|
||||
line, and that only ever happens in the origin repo, because `dist export` prunes the
|
||||
`stack-dev` skill together with this directory; a distributed `tools/` tree is runtime
|
||||
`stack-` skills together with this directory; a distributed `tools/` tree is runtime
|
||||
machinery, not reading material. The same holds for `.gitignore` and `tools/.coveragerc` -
|
||||
config, not documentation.
|
||||
|
||||
|
||||
@@ -0,0 +1,59 @@
|
||||
---
|
||||
type: types/instruction.md
|
||||
name: publish-and-ci
|
||||
description: How a stack change is published and how its CI run is waited for - the local checks first, tools/wikitool publish, reading the runs through the authenticated Gitea connection rather than curl, how long to wait, when to give up, and why a red run means the build phase is not over.
|
||||
---
|
||||
# Publish a stack change and wait for its CI run
|
||||
|
||||
The local checks cover what they cover on this machine; CI covers the same checks plus a full
|
||||
`setup-instance.md` replay against a fresh `dist export` (`.gitea/workflows/ci.yml`). A green run
|
||||
on the published commit is therefore the end of the checked stretch of a work package, not a
|
||||
formality after it - which is why waiting for it belongs to the phase that wrote the code
|
||||
(`stack-build`), and a red run sends the work back there rather than into the closing phase.
|
||||
|
||||
## When to run
|
||||
|
||||
- `stack-build`, every time it publishes - its last step.
|
||||
- `stack-close`, when its own pull-through of a stale document publishes something of its own
|
||||
(that skill's step 3 says when).
|
||||
|
||||
## Steps
|
||||
|
||||
1. **Run the local checks, explicitly rather than assumed:**
|
||||
|
||||
```bash
|
||||
tools/wikitool docs verify
|
||||
tools/wikitool instructions verify
|
||||
```
|
||||
|
||||
plus the relevant `pytest` run in `tools/` ([testing-conventions.md](testing-conventions.md)).
|
||||
A red check here is fixed before anything is published.
|
||||
|
||||
2. **Publish with `tools/wikitool publish`.** The gates apply as everywhere (AGENTS.md § Gates);
|
||||
an exit 42 is shown to the user verbatim and waited on. When the changeset touches `tools/`,
|
||||
`types/`, `instructions/`, `AGENTS.md` or a `<stage>/CONTRACT.md`, `publish` prints a
|
||||
one-line note that CI is the last mechanical check still to come and that what follows it is
|
||||
covered by none. A push to `main` that moves `VERSION` additionally triggers a tagged release.
|
||||
**CI does the tagging** - a session never creates a tag, which is what keeps AGENTS.md
|
||||
invariant 5 intact.
|
||||
|
||||
3. **Wait for CI on the published commit, 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.
|
||||
|
||||
- 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.
|
||||
- Keep the polling out of the main context where the harness allows it - a background
|
||||
command or a fork - so a long wait does not fill the session with run listings.
|
||||
|
||||
4. **A red run is not waited past.** Read the failing job's log, fix the cause, and go back to
|
||||
step 1: this is still build work, whichever skill published. The phase that published ends
|
||||
only on a green run, and the issue stays open until there is one.
|
||||
@@ -0,0 +1,121 @@
|
||||
---
|
||||
name: stack-build
|
||||
description: Builds a stack work package whose Gitea issue body is ready - code, tests, version bump, document pull-through, publish, and waiting for a green CI run, keeping the issue body current at fixed points along the way. Started only by the operator as /stack-build #N, after stack-dev has ended its design phase.
|
||||
disable-model-invocation: true
|
||||
---
|
||||
|
||||
# Stack Build
|
||||
|
||||
**Purpose:** Carry a designed work package through the checked stretch of its life - from a
|
||||
ready issue body to a green CI run on the published commit - and leave the body current enough
|
||||
that the closing phase, or anyone else, can work from it without this session's context.
|
||||
|
||||
**Trigger:** The operator runs `/stack-build #N`. 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. This session may be the continuation of the design
|
||||
session, or a fresh one after `/clear` - assume the second, and work from the body.
|
||||
|
||||
## Steps
|
||||
|
||||
1. **Read `instructions/dev/stack-mode.md`.** It holds the rules that change in a
|
||||
stack-development session and the catalogue of dev-only procedures; a cold session has
|
||||
neither yet.
|
||||
|
||||
2. **Check that the body is ready, before anything else.** Read issue #N's body against
|
||||
`instructions/dev/issue-tracking.md` § Ready to build. If a point fails, stop: name it, and
|
||||
recommend `/stack-dev` to finish the design. **Do not build around the gap** - a build session
|
||||
that answers an open design question on its own produces a change that matches its own
|
||||
reading of the body, recorded nowhere as a decision.
|
||||
|
||||
3. **Build the change and its tests.** Follow `instructions/dev/testing-conventions.md` before
|
||||
adding or changing a test, and the other procedures the mode file names for the surface you
|
||||
touch.
|
||||
|
||||
**"Covered by tests" means covered by the tests that exist, not by the tests that should
|
||||
exist.** Whether the right test was written is itself a judgment call with no mechanical
|
||||
guard: two data-destroying bugs in `upstream merge` (Gitea #30) shipped past a green
|
||||
`pytest`/`docs verify`/`instructions verify`/CI because no test exercised the case, not
|
||||
because the code for the tested case was worse. Where the body names a destructive step, its
|
||||
invariant is a test (issue-tracking.md step 1).
|
||||
|
||||
**Body upkeep, first fixed point - a deviation goes into the body at once.** An assumption
|
||||
that turns out false, a criterion that moves, an approach dropped: rewrite the body where it
|
||||
stands, in this session, not at the end
|
||||
(`instructions/dev/issue-tracking.md` step 2 has the rule; this is where it applies).
|
||||
A deviation that reopens the design - a boundary crossing, a decision the body did not make -
|
||||
is not settled here: stop and put it to the operator, as in step 2.
|
||||
|
||||
4. **Raise the version, if the change ships.** A change under `tools/`, `types/`,
|
||||
`instructions/`, `AGENTS.md` or a `CONTRACT.md` reaches every future instance, so it needs a
|
||||
version and a changelog entry. The part was named in the body during design; bump that part:
|
||||
|
||||
```bash
|
||||
tools/wikitool version bump --patch --title "<what changed>" --impact medium
|
||||
```
|
||||
|
||||
`--impact high|medium|low` (default `medium`) grades this bump in the changelog entry's own
|
||||
list - `tools/wikitool version regrade` corrects it later if the candidate's overall shape
|
||||
changes the read on an earlier one; see `instructions/dev/version-parts.md` § The candidate
|
||||
model.
|
||||
|
||||
Never edit `VERSION` or the entry's heading by hand - `bump` writes both, and `docs verify`
|
||||
fails a tree where they disagree. Then write the entry's body - `bump` deliberately leaves it
|
||||
empty, the same way `new` leaves the prose. A `--major` bump needs `--breaking` and either a
|
||||
migration document or `--no-migration`; `instructions/dev/version-parts.md` has all of it.
|
||||
|
||||
Prose-only changes (`README.md`, `INSTALL.md`, `EVALS.md`) and the workflows under `.gitea/`
|
||||
do not need a bump - CI's version gate is scoped to what changes behaviour.
|
||||
|
||||
5. **Pull through every document that makes a claim about the surface you touched.**
|
||||
`instructions/dev/doc-pull-through.md` has the table of which document that is, per surface,
|
||||
and its step 3 for the one part that is not prose: a reference file whose headings moved needs
|
||||
`tools/wikitool docs toc --apply`, never a hand-written list. Prose you write here is English,
|
||||
whatever language the session is held in - `AGENTS.md` § File naming has both language rules.
|
||||
|
||||
6. **Publish and wait for CI** per `instructions/dev/publish-and-ci.md`: the local checks,
|
||||
`tools/wikitool publish`, then the run on the published commit.
|
||||
|
||||
**Body upkeep, second fixed point - after the publish:** tick the criteria the publish met,
|
||||
and name the version and the commit in the body.
|
||||
|
||||
**Third fixed point - CI green:** name the run in the body as what verified the change. A red
|
||||
run is not this point: it is step 3 again, then this step again.
|
||||
|
||||
Then the one changelog comment for this session's worth of change
|
||||
(`instructions/dev/issue-tracking.md` step 3).
|
||||
|
||||
7. **End the phase.** Recommend how the closing phase should run, and stop:
|
||||
|
||||
- **This session ran on Opus at high effort:** continue here with `/stack-close` - what was
|
||||
built is still in context, and that is what the closing phase checks against.
|
||||
- **It ran on another model or a lower effort:** `/clear`, then `/stack-close` in a new
|
||||
session on Opus at high effort, which works from the body and the diff. That is why the
|
||||
three fixed points above are not optional.
|
||||
|
||||
Close with the fixed line, in the instance's KB language per `AGENTS.md` § File naming:
|
||||
|
||||
> #N is published and CI is green (run <id>). Next: `/stack-close` - here, or after `/clear`.
|
||||
|
||||
**Do not run `stack-close` yourself.** Offer no `/model` or `/effort` switch either - see
|
||||
`instructions/dev/stack-mode.md` § Sessions and models.
|
||||
|
||||
## Decision points
|
||||
|
||||
- **The change turns out not to be a drop-in replacement after all?** Do not bump across the
|
||||
boundary on your own initiative. Every existing instance pays for a breaking change once, by
|
||||
hand, so the user decides whether it is worth that: show them what breaks, what an instance
|
||||
has to do about it, and the alternatives (avoid the break with a shim, defer and batch it with
|
||||
the next one, or split it behind a deprecation window), then recommend one and wait for a
|
||||
go-ahead. `instructions/dev/version-parts.md` step 4 has the full shape, and the body takes the
|
||||
answer as a design change (step 3's first fixed point).
|
||||
- **The package needs several build sessions?** Each one ends with the body current and its own
|
||||
changelog comment; the phase ends once, at the green run after the last publish.
|
||||
- **CI gave up after 15 minutes, or is red for a reason outside this change?** Hand the open or
|
||||
failing runs to the operator by id and link. The phase is not over, and the closing line in
|
||||
step 7 is not given.
|
||||
|
||||
## Scope
|
||||
|
||||
Only for a body that is ready - the design is `stack-dev`
|
||||
(`instructions/dev/stack-dev/SKILL.md`), the closing after a green run is `stack-close`
|
||||
(`instructions/dev/stack-close/SKILL.md`). Not for wiki content work.
|
||||
@@ -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.
|
||||
@@ -1,206 +1,85 @@
|
||||
---
|
||||
name: stack-dev
|
||||
description: Switches a session into tool-development mode - extending tools/wikitool, the compiler, the type schema, or the instruction/skill layer itself, instead of operating on wiki content. Use when the user asks to add a wikitool command, change a type-spec, fix or extend the compiler, or otherwise work on the stack rather than ingest/query/manage/lint the wiki.
|
||||
description: Switches a session into tool-development mode and runs its design phase - extending tools/wikitool, the compiler, the type schema, or the instruction/skill layer itself, instead of operating on wiki content. Use when the user asks to add a wikitool command, change a type-spec, fix or extend the compiler, triage or work out a stack issue, or otherwise work on the stack rather than ingest/query/manage/lint the wiki.
|
||||
---
|
||||
|
||||
# Stack Development Mode
|
||||
# Stack Development Mode - Design
|
||||
|
||||
**Purpose:** Recognize a session that is about the tool stack itself - `tools/wikitool`, the
|
||||
type schema, the instruction/skill layer - rather than wiki content, and switch the rules that
|
||||
apply accordingly.
|
||||
type schema, the instruction/skill layer - rather than wiki content, switch the rules that apply
|
||||
accordingly, and carry a work package through its design phase: to an issue body a cold session
|
||||
can build from.
|
||||
|
||||
**Trigger:** The user asks to add or change a `wikitool` command, extend the compiler, change a
|
||||
type-spec, or work on `instructions/`/`types/`/`tools/` as code rather than as a place to run
|
||||
`wiki-ingest`/`wiki-query`/`wiki-manage`/`wiki-lint`/`wiki-status` against.
|
||||
`wiki-ingest`/`wiki-query`/`wiki-manage`/`wiki-lint`/`wiki-status` against. Also: triaging a
|
||||
`status/incoming` stub, refreshing an old spec against today's tree, or analysing a defect in the
|
||||
stack before anything is fixed.
|
||||
|
||||
**This directory is dev-only.** `instructions/dev/` is excluded wholesale by
|
||||
`tools/wikitool dist export` - nothing here ever reaches a distributed instance, and there is
|
||||
no restore path. If you are in a distributed instance, this skill should not be present at all;
|
||||
stack development happens in the origin repo instead (see AGENTS.md's routing line).
|
||||
|
||||
## What changes in this mode
|
||||
|
||||
- **Source-binding does not apply to code.** AGENTS.md invariant 3 ("never file an unsourced
|
||||
answer into the wiki") governs `kb/` content, not the code you write to extend the stack.
|
||||
Ordinary software-engineering judgment applies to `tools/chemenu/*.py`, `types/*`,
|
||||
`instructions/*` - it does not need a `raw/` source or a citation.
|
||||
- **Test and review conventions from `instructions/dev/` apply instead**, once written down
|
||||
there (step 2 below lists what currently exists). Until a given convention has its own
|
||||
instruction file, follow the existing test files' own patterns
|
||||
(`tools/chemenu/tests/`) rather than inventing a new one silently.
|
||||
- **Everything outside this directory still applies.** The tool error contract, the gates, and
|
||||
"never hand-edit generated files" (AGENTS.md invariants 1, 5-8) are about how the tool
|
||||
behaves at runtime, not about developing it, but they still bind normal session conduct
|
||||
(e.g. still use `tools/wikitool publish`, still respect the gates, when the session also
|
||||
touches wiki content).
|
||||
This is the first of three phases. The build (`stack-build`) and the closing (`stack-close`)
|
||||
are separate skills that only the operator starts - this one never runs them, and never builds
|
||||
past the design. `instructions/dev/stack-mode.md` has the phases, their handovers and why they
|
||||
are split that way.
|
||||
|
||||
## Steps
|
||||
|
||||
1. **Confirm the mode.** If the task is ambiguous between "extend the tool" and "operate the
|
||||
wiki", ask rather than guess - the two have different rules for the same directories.
|
||||
2. **Consult `instructions/dev/` for the concrete procedure.** Currently:
|
||||
`instructions/dev/commonplace-kb.md` - vendored knowledge base on agent context
|
||||
engineering, memory and deploy-time learning; consult before a design decision in those
|
||||
areas.
|
||||
`instructions/dev/issue-tracking.md` - open work lives in Gitea issues, one per work
|
||||
package, labelled `area/`, `kind/`, `prio/` and `size/`. There is no `TODO.md`. **The body
|
||||
of the issue you are working on is this session's plan file:** keep it current as the state
|
||||
moves, so an interrupted session leaves a body the next one can resume from, *and* rewrite it
|
||||
to its final state before closing. Both halves bind; the second is what
|
||||
`stack-close` (`instructions/dev/stack-close/SKILL.md`) carries out once this skill's own work is published -
|
||||
see step 6 below. An issue labelled `status/incoming` is the exception to all of that: it is a
|
||||
human's stub, not a spec, and it is **never implemented as it stands** - it gets worked out and
|
||||
triaged first. Read this file before filing something for later, before editing or closing an
|
||||
issue, before picking up an incoming stub, or before deciding what to pick up next.
|
||||
`instructions/dev/testing-conventions.md` - the suite runs against a deliberately
|
||||
empty machine; what the autouse fixture already neutralizes, and what a test still has to
|
||||
establish itself. Read it before adding or changing a test.
|
||||
`instructions/dev/tracker-testing.md` - how the task-tracker adapters are tested against a
|
||||
real Super Productivity and CalDAV server: the `live_tracker` suite, the profile procedure for
|
||||
a tracker of your own, the nightly workflow (which you dispatch yourself after touching the
|
||||
Super Productivity surface), what a red night means, and refreshing the recorded fixtures.
|
||||
Read it before changing an adapter under `tools/chemenu/tasks/`.
|
||||
`instructions/dev/version-parts.md` - which part a change bumps: the drop-in test, the
|
||||
catalogue of breaks that cross the compatibility boundary with `kb/` untouched, and what to
|
||||
put in front of the user before a breaking bump. Read it before step 4.
|
||||
`instructions/dev/corpus-policy.md` - what "curated enough" means for the shared
|
||||
demo/testbed `kb/`, the measurable floors that define it, and what a reactive fix may and may
|
||||
not do to corpus content. Read it before judging whether the corpus can exercise a change, or
|
||||
before any fix that would touch `kb/` content.
|
||||
`instructions/dev/dev-setup.md` - setting up a clone of the origin repo for this work, what
|
||||
differs from an instance there (telemetry on, demo persona, no release stamp), and
|
||||
`dist export` as a build and test tool rather than an install path. Read it in a fresh clone,
|
||||
or before testing a change to the install path.
|
||||
`instructions/dev/doc-pull-through.md` - which document makes a claim about a touched
|
||||
surface (a `wikitool` command, a stage's rules, an `AGENTS.md` rule/gate/invariant, a
|
||||
README-shaped human doc, a `docs/` page's reasoning) and therefore needs updating alongside
|
||||
the code, since `docs verify` never reads a cell's prose. Read it before step 6.
|
||||
More instructions are added here incrementally as stack-development needs come up - this
|
||||
list grows without needing this skill file to change shape.
|
||||
3. **Settle the design before building - and break there for the model switch.** These are two
|
||||
different kinds of work, and the split is not stylistic: design, the version part and any
|
||||
boundary judgment have **no** mechanical guard, while the code and tests that follow are mostly
|
||||
covered - `pytest`, `docs verify`, `instructions verify` and CI catch a mistake **in what they
|
||||
cover**.
|
||||
1. **Confirm the mode, then read `instructions/dev/stack-mode.md`.** If the task is ambiguous
|
||||
between "extend the tool" and "operate the wiki", ask rather than guess - the two have
|
||||
different rules for the same directories. The mode file holds the rules that change, and the
|
||||
catalogue of dev-only procedures (§ Where the procedures are); consult what it names for the
|
||||
task at hand rather than re-deriving it.
|
||||
|
||||
So when the design is settled - the issue body says what will be built, the open questions are
|
||||
answered - stop and say so, in one sentence that names what the mechanical stretch does **not**
|
||||
cover. The message below is a model of what to say, not a script to quote: say it in the
|
||||
instance's KB language, per `AGENTS.md` § File naming.
|
||||
2. **Find or open the work package.** One Gitea issue per package
|
||||
(`instructions/dev/issue-tracking.md`). Read its body as the current spec, and correct it
|
||||
first where the tree or a comment proves it wrong. A `status/incoming` stub is worked out per
|
||||
that file's § Incoming stubs before anything else happens to it.
|
||||
|
||||
> The plan is settled. From here the work is mostly mechanical and covered by tests/CI -
|
||||
> except the changelog prose (step 4), any `docs/` page you touch, new human-facing
|
||||
> documentation, and the prose half of an instruction. If you are on Opus, now is the moment
|
||||
> for `/model sonnet` at effort `high`.
|
||||
3. **Work the design out in the body, not beside it.** Whatever this session establishes - a
|
||||
decision and its reasoning, a root cause, a rejected approach, the files involved - goes into
|
||||
the body as it is settled (`instructions/dev/issue-tracking.md` step 2), with one changelog
|
||||
comment for the session's worth of change (step 3). A question only the user can answer is
|
||||
asked in the chat and stays a question in the body until it is answered; it is never settled
|
||||
by a guess.
|
||||
|
||||
**You cannot make this switch yourself** - the session's model is the user's `/model`, not a
|
||||
setting an agent applies. Offer it once and keep working either way; a session that argues
|
||||
about its own model has already cost more than the difference. If the design turns out not to
|
||||
be settled after all - a boundary crossing surfaces, an assumption breaks - that is a reason to
|
||||
offer the switch back up, not to decide it alone.
|
||||
4. **Name the version part.** Apply the drop-in test in `instructions/dev/version-parts.md`
|
||||
and write the result into the body. Whether a change is a drop-in replacement has no
|
||||
mechanical guard, so it is decided here, not at the bump. A change that crosses the
|
||||
compatibility boundary goes to the user with what breaks, what an instance has to do about
|
||||
it, and the alternatives, before the body can be ready (version-parts.md step 4).
|
||||
|
||||
**"Covered by tests" means covered by the tests that exist, not by the tests that should
|
||||
exist.** Whether the right test was written is itself a judgment call with no mechanical
|
||||
guard: two data-destroying bugs in `upstream merge` (Gitea #30) shipped past a green
|
||||
`pytest`/`docs verify`/`instructions verify`/CI because no test exercised the case, not
|
||||
because a weaker model wrote worse code for the case that *was* tested. This is not a third
|
||||
break - it is a caveat on this one: the middle phase stays the cheaper phase to run on, but its
|
||||
test suite is only as complete as the judgment that wrote it, and that judgment is unchecked
|
||||
the same way the design phase is.
|
||||
5. **End the phase at a ready body.** Check the body against
|
||||
`instructions/dev/issue-tracking.md` § Ready to build. If it fails, say which point is open
|
||||
and stay in this phase. If it passes, recommend one of two ways on, and stop:
|
||||
|
||||
Effort is the cheaper lever than the model, and `high` is the floor for anything touching more
|
||||
than one file or a contract. Full table and reasoning:
|
||||
`docs/model-and-effort-selection.md`.
|
||||
- **A long design session** - much exploration, a defect analysis, a stub worked out from
|
||||
scratch: `/clear`, then `/stack-build #N`. The build starts lean, and a cold start is the
|
||||
real test of whether the body is ready.
|
||||
- **A short one** - an existing spec refreshed against the tree: continue in this session
|
||||
with `/stack-build #N`.
|
||||
|
||||
4. **Raise the version, if the change ships.** A change under `tools/`, `types/`,
|
||||
`instructions/`, `AGENTS.md` or a `CONTRACT.md` reaches every future instance, so it needs a
|
||||
version and a changelog entry:
|
||||
Close with the fixed line, in the instance's KB language per `AGENTS.md` § File naming:
|
||||
|
||||
```bash
|
||||
tools/wikitool version bump --patch --title "<what changed>" --impact medium
|
||||
```
|
||||
> #N is ready. Next: `/stack-build #N` - here, or after `/clear`.
|
||||
|
||||
`--impact high|medium|low` (default `medium`) grades this bump in the changelog entry's own
|
||||
list - `tools/wikitool version regrade` corrects it later if the candidate's overall shape
|
||||
changes the read on an earlier one; see
|
||||
`instructions/dev/version-parts.md` § The candidate model.
|
||||
|
||||
Never edit `VERSION` or the entry's heading by hand - `bump` writes both, and `docs verify`
|
||||
fails a tree where they disagree. Pick the part by whether the new version is a **drop-in
|
||||
replacement** for the old one - not by whether content has to be migrated:
|
||||
|
||||
| Change | Part |
|
||||
|--------|------|
|
||||
| Fix, no interface change | `--patch` |
|
||||
| New capability, still drop-in in both directions | `--minor` |
|
||||
| **Not a drop-in replacement** - any hand-work by the user or a migration script, or a downgrade that no longer works | `--major` |
|
||||
|
||||
Content migration is one way to land in the last row, not the definition of it: a rename of
|
||||
the update path, the artefact, an import name, a flag or an envvar breaks a swap with `kb/`
|
||||
entirely untouched. The full test, the catalogue of such breaks, and what to put in front of
|
||||
the user first are in `instructions/dev/version-parts.md` - **read it before choosing
|
||||
`--major`.**
|
||||
|
||||
A `--major` bump therefore needs two things recorded. `--breaking "<what stops working>"`
|
||||
is required on every boundary-crossing bump; on top of it, a migration document for the new
|
||||
version - written per `instructions/migrate-corpus.md` - or
|
||||
`--no-migration "<reason>"` when no content actually has to change. `bump` refuses without
|
||||
either, and so does `docs verify`: an instance learning that it must migrate, with nothing
|
||||
telling it how, is a dead end.
|
||||
|
||||
Then write the entry's body - `bump` deliberately leaves it empty, the same way `new` leaves
|
||||
the prose.
|
||||
|
||||
Prose-only changes (`README.md`, `INSTALL.md`, `EVALS.md`) and the workflows under `.gitea/`
|
||||
do not need a bump - CI's version gate is scoped to what changes behaviour.
|
||||
|
||||
5. **Pull through every document that makes a claim about the surface you touched - `docs verify`
|
||||
checks a cell's presence, never its prose.** `instructions/dev/doc-pull-through.md` has
|
||||
the table of which document that is, per surface, and its step 3 for the one part of the
|
||||
pull-through that is *not* prose: a reference file whose headings moved needs
|
||||
`tools/wikitool docs toc --apply`, never a hand-written list.
|
||||
|
||||
Prose you write here is English, whatever language the session is being held in -
|
||||
`AGENTS.md` § File naming has both language rules and the line between prose and quoted
|
||||
vocabulary.
|
||||
|
||||
6. **Verify, then publish.** `tools/wikitool docs verify`, `tools/wikitool instructions verify`,
|
||||
and the relevant `pytest` run in `tools/` - the same checks any stack change must pass, run
|
||||
explicitly rather than assumed. CI (`.gitea/workflows/ci.yml`) runs these plus a full
|
||||
`setup-instance.md` replay against a fresh `dist export`; a push to `main` that moves `VERSION`
|
||||
additionally triggers a tagged release. **CI does the tagging** - a session never creates a
|
||||
tag, which is what keeps AGENTS.md invariant 5 intact.
|
||||
|
||||
Publish with `tools/wikitool publish`. When the changeset touches `tools/`, `types/`,
|
||||
`instructions/`, `AGENTS.md` or a `<stage>/CONTRACT.md`, `publish` itself prints a one-line
|
||||
reminder that the phase past this point is not covered by any of the checks above - that line
|
||||
is the cue that this skill's own job just ended.
|
||||
|
||||
**This skill stops here.** The closing phase - rewriting the issue body to its final state,
|
||||
checking for `docs/` staleness, and naming which model ran which phase of the session - lives
|
||||
in `stack-close` (`instructions/dev/stack-close/SKILL.md`), not in a further step of this one. Invoke it now;
|
||||
do not fold its work into this session under this skill's rules, and do not treat "the change
|
||||
is published" as this work package being done.
|
||||
**Do not run `stack-build` yourself, and do not start building.** The phase change is the
|
||||
operator's moment to decide on context and model; a design session that carries on into code
|
||||
takes that decision away. Offer no `/model` or `/effort` switch either - see
|
||||
`instructions/dev/stack-mode.md` § Sessions and models.
|
||||
|
||||
## Decision points
|
||||
|
||||
- **Touches both stack code and wiki content in one session?** Apply this skill's rules to the
|
||||
code changes and the normal content skills' rules to the content changes - they are not
|
||||
mutually exclusive within a session, only per change.
|
||||
- **The change turns out not to be a drop-in replacement?** Do not bump across the boundary on
|
||||
your own initiative. Every existing instance pays for a breaking change once, by hand, so the
|
||||
user decides whether it is worth that: show them what breaks, what an instance has to do about
|
||||
it, and the alternatives (avoid the break with a shim, defer and batch it with the next one,
|
||||
or split it behind a deprecation window), then recommend one and wait for a go-ahead.
|
||||
`instructions/dev/version-parts.md` step 4 has the full shape. A surfacing boundary crossing
|
||||
is also a reason to offer the model switch back up (step 3): the judgment it needs has no
|
||||
mechanical guard, and `docs verify` only checks that a crossing documents itself, never that the
|
||||
part was chosen correctly.
|
||||
- **Nothing to build - the session answered a question or filed a follow-up?** The phase ends
|
||||
with the issue in whatever state it reached, body current; there is no `/stack-build` line to
|
||||
give.
|
||||
- **The task is a one-line fix that seems not to need a design?** It still needs a body that
|
||||
says what is fixed and which version part it takes - which for a real one-liner is a short
|
||||
body, written in minutes. The cut to `stack-build` can then happen in the same session.
|
||||
- **The design turns out to cross the compatibility boundary?** Do not decide it alone - step 4.
|
||||
|
||||
## Scope
|
||||
|
||||
Not for wiki content work - use `wiki-ingest`/`wiki-query`/`wiki-manage`/`wiki-lint`/
|
||||
`wiki-status`/`gtd-weekly-review` for that. Not for setting up a new instance
|
||||
(`instructions/setup-instance.md`) or
|
||||
a fresh clone of this repo (`instructions/bootstrap.md`). Not for closing a work package after
|
||||
its publish has landed - that is `stack-close` (`instructions/dev/stack-close/SKILL.md`).
|
||||
(`instructions/setup-instance.md`) or a fresh clone of this repo (`instructions/bootstrap.md`).
|
||||
Not for building a ready body (`stack-build`, `instructions/dev/stack-build/SKILL.md`) or closing
|
||||
a published package (`stack-close`, `instructions/dev/stack-close/SKILL.md`).
|
||||
@@ -0,0 +1,122 @@
|
||||
---
|
||||
type: types/instruction.md
|
||||
name: stack-mode
|
||||
description: What changes when a session works on the stack itself rather than on wiki content - which rules stop and start applying, the three phases a work package moves through and the tracker state that hands each one over, where the dev-only procedures are, and why a model is chosen per session rather than per phase.
|
||||
---
|
||||
# Rules for a stack-development session
|
||||
|
||||
Shared by the three skills of the `stack-` family - `stack-dev` (design), `stack-build` (build)
|
||||
and `stack-close` (closing). Each of them can be the first thing a session runs: `/stack-build #N`
|
||||
after a `/clear` starts cold, with none of `stack-dev`'s context. So each skill loads this file at
|
||||
its entry, rather than one of them carrying these rules for the other two.
|
||||
|
||||
**This directory is dev-only.** `instructions/dev/` is excluded wholesale by
|
||||
`tools/wikitool dist export` - nothing here ever reaches a distributed instance, and there is no
|
||||
restore path. If you are in a distributed instance, none of these skills should be present at
|
||||
all; stack development happens in the origin repo instead (see AGENTS.md's routing line).
|
||||
|
||||
<!-- wikitool:toc -->
|
||||
## Contents
|
||||
|
||||
- [The three phases](#the-three-phases)
|
||||
- [What changes in this mode](#what-changes-in-this-mode)
|
||||
- [Where the procedures are](#where-the-procedures-are)
|
||||
- [Sessions and models](#sessions-and-models)
|
||||
<!-- /wikitool:toc -->
|
||||
|
||||
## The three phases
|
||||
|
||||
A work package is one Gitea issue, and it moves through three phases. A skill is a unit of
|
||||
procedure; a session is a unit of context and model. The two are deliberately not the same
|
||||
thing: each phase ends in a **state in the tracker**, and the next phase starts from that state,
|
||||
so every handover works either in the same session or after a `/clear`.
|
||||
|
||||
| Phase | Skill | Invoked by | Ends with (the handover) | What catches a mistake |
|
||||
|---|---|---|---|---|
|
||||
| 1 Design/triage | `stack-dev` | the harness on a matching task, or `/stack-dev` | the issue body is **ready** ([issue-tracking.md](issue-tracking.md) § Ready to build) | nothing mechanical |
|
||||
| 2 Build | `stack-build` | **only** the operator: `/stack-build #N` | a **green CI run** on the published commit, body current | `pytest`, `docs verify`, `instructions verify`, CI |
|
||||
| 3 Closing | `stack-close` | **only** the operator: `/stack-close` | body in its final state, issue closed | nothing mechanical |
|
||||
|
||||
`stack-build` and `stack-close` carry `disable-model-invocation: true` in their frontmatter, so in
|
||||
Claude Code only the operator can start them. That is the point of the split: each phase change is
|
||||
the moment the operator decides whether to continue in this session, `/clear` first, or start the
|
||||
next session on a different model - and a skill the agent could invoke itself would take that
|
||||
moment away again. `instructions sync` copies the frontmatter unchanged; the other harnesses
|
||||
ignore the key, so there the skill's own prose is the only thing that holds the line.
|
||||
|
||||
**So no skill tells the agent to run the next one.** A phase ends with a fixed line naming the
|
||||
slash command for the operator, and stops.
|
||||
|
||||
## What changes in this mode
|
||||
|
||||
- **Source-binding does not apply to code.** AGENTS.md invariant 3 ("never file an unsourced
|
||||
answer into the wiki") governs `kb/` content, not the code you write to extend the stack.
|
||||
Ordinary software-engineering judgment applies to `tools/chemenu/*.py`, `types/*`,
|
||||
`instructions/*` - it does not need a `raw/` source or a citation.
|
||||
- **Test and review conventions from `instructions/dev/` apply instead**, once written down
|
||||
there (§ Where the procedures are, below). Until a given convention has its own instruction
|
||||
file, follow the existing test files' own patterns (`tools/chemenu/tests/`) rather than
|
||||
inventing a new one silently.
|
||||
- **Everything outside this directory still applies.** The tool error contract, the gates, and
|
||||
"never hand-edit generated files" (AGENTS.md invariants 1, 5-8) are about how the tool
|
||||
behaves at runtime, not about developing it, but they still bind normal session conduct
|
||||
(e.g. still use `tools/wikitool publish`, still respect the gates, when the session also
|
||||
touches wiki content).
|
||||
- **A session that touches both stack code and wiki content** applies these rules to the code
|
||||
changes and the normal content skills' rules to the content changes - they are not mutually
|
||||
exclusive within a session, only per change.
|
||||
|
||||
## Where the procedures are
|
||||
|
||||
- [issue-tracking.md](issue-tracking.md) - open work lives in Gitea issues, one per work
|
||||
package, labelled `area/`, `kind/`, `prio/` and `size/`. There is no `TODO.md`. **The body of
|
||||
the issue you are working on is the plan file** of every phase: kept current as the state
|
||||
moves, so an interrupted session leaves a body the next one can resume from, and rewritten to
|
||||
its final state before closing. It also defines when a body is ready to build. An issue
|
||||
labelled `status/incoming` is a human's stub, not a spec, and is **never implemented as it
|
||||
stands**. Read it before filing something for later, before editing or closing an issue,
|
||||
before picking up an incoming stub, or before deciding what to pick up next.
|
||||
- [version-parts.md](version-parts.md) - which part a change bumps: the drop-in test, the
|
||||
catalogue of breaks that cross the compatibility boundary with `kb/` untouched, and what to put
|
||||
in front of the user before a breaking bump. Read it when the design names the part, and again
|
||||
before the bump.
|
||||
- [testing-conventions.md](testing-conventions.md) - the suite runs against a deliberately empty
|
||||
machine; what the autouse fixture already neutralizes, and what a test still has to establish
|
||||
itself. Read it before adding or changing a test.
|
||||
- [tracker-testing.md](tracker-testing.md) - how the task-tracker adapters are tested against a
|
||||
real Super Productivity and CalDAV server: the `live_tracker` suite, the profile procedure for
|
||||
a tracker of your own, the nightly workflow (which you dispatch yourself after touching the
|
||||
Super Productivity surface), what a red night means, and refreshing the recorded fixtures.
|
||||
Read it before changing an adapter under `tools/chemenu/tasks/`.
|
||||
- [doc-pull-through.md](doc-pull-through.md) - which document makes a claim about a touched
|
||||
surface (a `wikitool` command, a stage's rules, an `AGENTS.md` rule/gate/invariant, a
|
||||
README-shaped human doc, a `docs/` page's reasoning) and therefore needs updating alongside
|
||||
the code, since `docs verify` never reads a cell's prose. Read it before publishing.
|
||||
- [publish-and-ci.md](publish-and-ci.md) - the local checks, `publish`, and waiting for the CI
|
||||
run on the published commit. Read it whenever a phase publishes.
|
||||
- [corpus-policy.md](corpus-policy.md) - what "curated enough" means for the shared demo/testbed
|
||||
`kb/`, the measurable floors that define it, and what a reactive fix may and may not do to
|
||||
corpus content. Read it before judging whether the corpus can exercise a change, or before any
|
||||
fix that would touch `kb/` content.
|
||||
- [dev-setup.md](dev-setup.md) - setting up a clone of the origin repo for this work, what
|
||||
differs from an instance there (telemetry on, demo persona, no release stamp), and
|
||||
`dist export` as a build and test tool rather than an install path. Read it in a fresh clone,
|
||||
or before testing a change to the install path.
|
||||
- [commonplace-kb.md](commonplace-kb.md) - vendored knowledge base on agent context engineering,
|
||||
memory and deploy-time learning; consult before a design decision in those areas.
|
||||
|
||||
More instructions are added here as stack-development needs come up - this list grows without
|
||||
any of the three skills having to change shape.
|
||||
|
||||
## Sessions and models
|
||||
|
||||
**A model is chosen per session, never switched inside one.** Neither `/model` nor `/effort`
|
||||
is offered mid-session: either change throws away the prompt cache for everything the session
|
||||
has read so far, and Sonnet's smaller context window does not hold a build phase of this stack.
|
||||
Where two phases should run on different models, the cut goes at a handover - `/clear`, then the
|
||||
next phase's slash command in a session started on the right model - and the tracker state is
|
||||
what carries the work across it.
|
||||
|
||||
Which model suits which phase, and the reasoning, is
|
||||
`docs/model-and-effort-selection.md`. What every phase records, so that reasoning can be checked
|
||||
against practice, is `stack-close`'s handover (its own step 4).
|
||||
@@ -186,6 +186,7 @@ Whenever you add or change a test under `tools/chemenu/tests/`.
|
||||
## Scope
|
||||
|
||||
Applies to `tools/chemenu/tests/` only. It says nothing about what to test - the test/review
|
||||
expectations for a stack change are the `stack-dev` skill's step 6 (`docs verify`,
|
||||
`instructions verify`, pytest). CI runs the suite once, unhardened, because the fixture makes a
|
||||
second hardened run redundant; see the note on the Tests step in `.gitea/workflows/ci.yml`.
|
||||
expectations for a stack change are the local checks in [publish-and-ci.md](publish-and-ci.md)
|
||||
(`docs verify`, `instructions verify`, pytest), run from `stack-build`. CI runs the suite once,
|
||||
unhardened, because the fixture makes a second hardened run redundant; see the note on the Tests
|
||||
step in `.gitea/workflows/ci.yml`.
|
||||
@@ -92,7 +92,8 @@ a new one, and only `version release` turns it into something the release workfl
|
||||
|
||||
## When to run
|
||||
|
||||
Before every `tools/wikitool version bump` - the `stack-dev` skill's step 4 sends you here.
|
||||
When a design names the version part (`stack-dev` step 4), and again before every
|
||||
`tools/wikitool version bump` (`stack-build` step 4).
|
||||
Read it in full the first time a change looks like it might be boundary-crossing; afterwards
|
||||
the three-line test below is usually enough.
|
||||
|
||||
|
||||
@@ -393,20 +393,21 @@ STACK_MACHINERY_PREFIXES = ("tools/", "types/", "instructions/")
|
||||
STACK_MACHINERY_NAMES = ("AGENTS.md",)
|
||||
|
||||
STACK_MACHINERY_NOTE = (
|
||||
"Note: this publish touched stack machinery. What a stack-dev session "
|
||||
"does next - closing prose, a changelog entry's accuracy, whether a "
|
||||
"docs/ page went stale - is not covered by docs verify, instructions "
|
||||
"verify, or pytest. No tool checks it; a session has to."
|
||||
"Note: this publish touched stack machinery. CI on the pushed commit is "
|
||||
"the last mechanical check still to come. What follows a green run - "
|
||||
"closing prose, a changelog entry's accuracy, whether a docs/ page went "
|
||||
"stale - is covered by no tool; a session has to check it."
|
||||
)
|
||||
|
||||
|
||||
def touches_stack_machinery(changed_files: list[str]) -> bool:
|
||||
"""Whether `changed_files` includes a path under the stack version's own
|
||||
scope - `tools/`, `types/`, `instructions/`, `AGENTS.md`, or a path ending
|
||||
in `CONTRACT.md` at any depth. A publish in this class is, by construction
|
||||
of the `stack-dev`/`stack-close` split, always followed by the unchecked
|
||||
closing phase - `STACK_MACHINERY_NOTE` times a reminder to land exactly
|
||||
there, for any session, not only one that read the skill that names it.
|
||||
in `CONTRACT.md` at any depth. A publish in this class is followed first by
|
||||
CI - still a mechanical check - and then by the unchecked closing phase of
|
||||
the `stack-dev`/`stack-build`/`stack-close` split. `STACK_MACHINERY_NOTE`
|
||||
names both, in that order, for any session, not only one that read the
|
||||
skills that name them.
|
||||
|
||||
Deliberately a shade broader than CI's version gate, which matches
|
||||
`<one-segment>/CONTRACT.md` only: this decides whether to print a sentence,
|
||||
|
||||
@@ -395,7 +395,7 @@ def test_below_threshold_publish_goes_straight_through(repo):
|
||||
|
||||
|
||||
def test_publish_notes_stack_machinery_after_success(repo, capsys):
|
||||
"""The closing-phase reminder lands exactly once, after the OK line, and
|
||||
"""The CI-then-closing-phase reminder lands exactly once, after the OK line, and
|
||||
only when the changeset actually falls under version-parts.md's scope."""
|
||||
(repo / "instructions").mkdir()
|
||||
(repo / "instructions/example.md").write_text("x\n", encoding="utf-8")
|
||||
@@ -405,6 +405,17 @@ def test_publish_notes_stack_machinery_after_success(repo, capsys):
|
||||
assert out.count(STACK_MACHINERY_NOTE) == 1
|
||||
|
||||
|
||||
def test_stack_machinery_note_puts_ci_before_the_unchecked_phase():
|
||||
"""A stack publish is followed by CI first - a mechanical check - and only
|
||||
after a green run by the phase nothing checks. The note must not claim the
|
||||
unchecked phase starts right at the publish (#168): that sent CI waiting
|
||||
into the closing phase, where a red run turned it back into a build."""
|
||||
ci = STACK_MACHINERY_NOTE.find("CI")
|
||||
unchecked = STACK_MACHINERY_NOTE.find("covered by no tool")
|
||||
assert ci != -1 and unchecked != -1
|
||||
assert ci < unchecked
|
||||
|
||||
|
||||
def test_publish_stays_quiet_for_ordinary_content(repo, capsys):
|
||||
_write_files(repo, 1)
|
||||
_publish(message="ordinary content")
|
||||
|
||||
@@ -64,7 +64,7 @@ def test_the_real_repo_publishes_every_skill():
|
||||
"""Guards the actual layout, not a fixture: these are the skills the
|
||||
harness is expected to offer, one per naming family - `wiki-` for the
|
||||
knowledge pipeline, `gtd-` for the commitment layer, `stack-` for the
|
||||
stack's own development. The `stack-` pair is nested under
|
||||
stack's own development. The `stack-` trio is nested under
|
||||
instructions/dev/, discovered the same way as the top-level ones."""
|
||||
names = {p.name for p in instructions_cmd.skill_dirs()}
|
||||
assert {
|
||||
@@ -75,10 +75,26 @@ def test_the_real_repo_publishes_every_skill():
|
||||
"wiki-status",
|
||||
"gtd-weekly-review",
|
||||
"stack-dev",
|
||||
"stack-build",
|
||||
"stack-close",
|
||||
} <= names
|
||||
|
||||
|
||||
def test_only_the_operator_starts_the_build_and_close_phases():
|
||||
"""#168: the phase changes are the operator's slash command, so
|
||||
`stack-build` and `stack-close` carry `disable-model-invocation: true`,
|
||||
while `stack-dev` - the entry point the harness has to find on its own -
|
||||
does not. `sync` copies the frontmatter byte for byte, which the drift
|
||||
check in `verify` already holds the published copies to."""
|
||||
skills = {p.name: p for p in instructions_cmd.skill_dirs()}
|
||||
flags = {}
|
||||
for name in ("stack-dev", "stack-build", "stack-close"):
|
||||
frontmatter, error = instructions_cmd._read_frontmatter(skills[name] / "SKILL.md")
|
||||
assert frontmatter is not None, error
|
||||
flags[name] = frontmatter.get("disable-model-invocation", False)
|
||||
assert flags == {"stack-dev": False, "stack-build": True, "stack-close": True}
|
||||
|
||||
|
||||
_NO_PROVIDER_FORBIDDEN_TERMS = [
|
||||
"super productivity",
|
||||
"azure devops",
|
||||
|
||||
Reference in new issue
Block a user