Compare commits
3 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| b1883befc7 | |||
| 56ecfc7fee | |||
| 4e80a07ac7 |
@@ -275,3 +275,10 @@ and `tools/README.md` are part of the change that introduced a stage, a command
|
|||||||
not follow-up work: nobody comes back for them, and a document that describes a repo which no
|
not follow-up work: nobody comes back for them, and a document that describes a repo which no
|
||||||
longer exists is worse than none. The mechanical half - command tables, contracts, ignore
|
longer exists is worse than none. The mechanical half - command tables, contracts, ignore
|
||||||
canaries - is checked by `tools/wikitool docs verify`; the prose half is yours.
|
canaries - is checked by `tools/wikitool docs verify`; the prose half is yours.
|
||||||
|
|
||||||
|
`docs/` pages are held to a different clock than those three. A README goes stale on every new
|
||||||
|
flag; a `docs/` page goes stale only when the reasoning it wrote down stops holding - a gate
|
||||||
|
that stops living in code, an ownership line that moves, a boundary redrawn - which is rarer
|
||||||
|
and not tied to any one commit. Nothing checks this by construction: a page there carries no
|
||||||
|
normative sentence (see [File naming](#file-naming)), so there is no rule for `docs verify` to
|
||||||
|
check, only a rationale for a session to notice has gone stale and to update or retire.
|
||||||
|
|||||||
+125
@@ -20,6 +20,131 @@ their date-only headings.
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
## 4.3.3 - 2026-09-03 - Modellwahl nach Pruefbarkeit statt nach Aufgabenname; stack-dev bricht an den Phasenwechseln fuer den Model-Switch
|
||||||
|
|
||||||
|
**Author:** Torben Nehmer
|
||||||
|
|
||||||
|
`instructions/claude-code-model-selection.md` routete bisher nach Skill: eine Zeile "Stack
|
||||||
|
development -> Opus/high" fuer alles, was `tools/`, `types/` oder `instructions/` anfasst. Das ist
|
||||||
|
zu grob in beide Richtungen - es verteuert die lange, mechanische Mitte einer Stack-Sitzung, und es
|
||||||
|
sagt nichts darueber, dass Anfang und Ende derselben Sitzung anders zu behandeln sind.
|
||||||
|
|
||||||
|
**Die neue Achse ist "was faengt hier einen Fehler ab".** Wo ein Check in Code steht - `pytest`,
|
||||||
|
`docs verify`, `instructions verify`, CI, die Gates - kostet der Fehler eines schwaecheren Modells
|
||||||
|
eine Runde und faellt auf. Wo die einzige Durchsetzung eine Sitzung ist, die Prosa liest, faellt
|
||||||
|
derselbe Fehler gar nicht auf: er wird ausgeliefert und bleibt stehen. Das ist dasselbe Argument,
|
||||||
|
das `docs/why-gates-are-code.md` fuer Gates fuehrt, angewandt auf die Modellwahl.
|
||||||
|
|
||||||
|
Stack-Entwicklung ist damit **nicht mehr eine Zeile, sondern drei**:
|
||||||
|
|
||||||
|
| Phase | Was einen Fehler faengt | Modell |
|
||||||
|
|---|---|---|
|
||||||
|
| Design, Versionsstelle, Grenzuebertritts-Urteil | nichts | Opus/high |
|
||||||
|
| Code, Tests, mechanische Doku-Synchronisation | pytest, CI, `docs verify` | Sonnet/high |
|
||||||
|
| Issue-Abschluss, `docs/`-Veralterung, Changelog-Prosa | nichts, per Konstruktion | Opus/high |
|
||||||
|
|
||||||
|
Die Mitte ist die lange Phase und die mit den Checks - dort liegt die Ersparnis. Die beiden
|
||||||
|
Raender sind kurz (Minuten, nicht Stunden), haben aber keinen maschinellen Waechter: `wikitool`
|
||||||
|
kennt den Issue-Tracker bewusst nicht, und eine `docs/`-Seite traegt keinen normativen Satz, also
|
||||||
|
gibt es dort nichts zu verifizieren. Sie oben zu lassen ist billig und schuetzt genau die Arbeit,
|
||||||
|
die still scheitert.
|
||||||
|
|
||||||
|
Zwei Praezisierungen dazu: **Effort ist der billigere Hebel als das Modell** - `medium` steht fuer
|
||||||
|
Stack-Arbeit bewusst in keiner Zeile, weil Mehrdatei-Konsistenz das ist, was ein reduzierter
|
||||||
|
Effort zuerst aufgibt; `high` ist die Untergrenze, sobald mehr als eine Datei oder ein Contract
|
||||||
|
betroffen ist. Und die Asymmetrie ist benannt: eine unnoetige Opus-Phase kostet einmal Geld, eine
|
||||||
|
ungepruefte Sonnet-Phase kann etwas ausliefern, das nie wieder jemand ansieht.
|
||||||
|
|
||||||
|
**Damit die Tabelle ueberhaupt wirksam wird, braucht sie Haltepunkte.** Eine Sitzung kann ihr
|
||||||
|
eigenes Modell nicht wechseln - das ist `/model` und gehoert dem Nutzer. Eine Empfehlung, die
|
||||||
|
niemand zum richtigen Zeitpunkt ausspricht, aendert nichts. `instructions/dev/stack-dev/SKILL.md`
|
||||||
|
bekommt deshalb zwei ausdrueckliche Breaks:
|
||||||
|
|
||||||
|
- **Neuer Schritt 3** - "Settle the design before building", mit dem Angebot zum Wechsel nach
|
||||||
|
unten, sobald der Plan steht und die Arbeit mechanisch wird. Einmal aussprechen, dann so oder
|
||||||
|
so weiterarbeiten.
|
||||||
|
- **Schritt 6 (Abschluss) bricht in die Gegenrichtung** - ab dort greift wieder kein Check. Mit
|
||||||
|
der ausdruecklichen Auflage, die Arbeit **unabhaengig von der Antwort** zu tun: nach dem Publish
|
||||||
|
auf einen Modellwechsel zu blockieren wuerde genau den Zustand hinterlassen, den Schritt 6
|
||||||
|
verhindern soll. Lief die Phase auf dem billigeren Modell, gehoert das in die Uebergabe statt
|
||||||
|
ins Schweigen.
|
||||||
|
|
||||||
|
Ein auftauchender Grenzuebertritt ist unter den Decision points ebenfalls als Anlass zum Wechsel
|
||||||
|
nach oben benannt: `docs verify` prueft, dass ein Uebertritt sich dokumentiert, nie dass die
|
||||||
|
Stelle richtig gewaehlt war.
|
||||||
|
|
||||||
|
Die uebrigen Schritte sind unveraendert und nur umnummeriert (alt 3-5 -> neu 4-6).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 4.3.2 - 2026-09-03 - stack-dev: Issue-Abschluss ist ein nummerierter Schritt, kein Zeiger in einer Routing-Liste
|
||||||
|
|
||||||
|
**Author:** Torben Nehmer
|
||||||
|
|
||||||
|
Nachfassen zu 4.1.2 (#44), das die Regel geschaerft, aber den Weg zu ihr nicht geaendert hat.
|
||||||
|
`instructions/dev/issue-tracking.md` bekam damals Schritt 7 ("Closing is the last body update,
|
||||||
|
not a comment"); `instructions/dev/stack-dev/SKILL.md` bekam nur eine umformulierte Zeile in
|
||||||
|
seiner Routing-Liste. Eine Stunde spaeter schloss #45 auf exakt dieselbe Weise: gruendlicher
|
||||||
|
Abschlusskommentar ueber einem Body mit unangehakten Kriterien.
|
||||||
|
|
||||||
|
**Die Ursache lag nicht am Text der Regel, sondern an ihrer Erreichbarkeit.** Die nummerierten
|
||||||
|
Schritte des Skills endeten bei "Verify before publishing". Ein Issue zu schliessen war ueberhaupt
|
||||||
|
kein Schritt - es hing an einem Zeiger *innerhalb* von Schritt 2, und Schritt 2 ist eine
|
||||||
|
Routing-Tabelle aus fuenf "read X before Y"-Eintraegen, keine Checkliste. Eine Sitzung folgt dem
|
||||||
|
Spine, den sie im Kontext hat; was nur hinter einem Link steht, wird genau in dem Moment nicht
|
||||||
|
aufgeschlagen, in dem es greift - am Ende einer langen Sitzung, wenn der Kontext am vollsten und
|
||||||
|
die verbleibende Instruktionsflaeche am duennsten ist.
|
||||||
|
|
||||||
|
Verschaerfend arbeitete der Blurb gegen seine eigene Regel: fett gesetzt war "keep it current as
|
||||||
|
the state moves, **not at the end**". Wer den Body unterwegs ungefaehr gepflegt hatte, las daraus
|
||||||
|
Konformitaet - der eigentliche Abschlusstest stand nur in der verlinkten Datei.
|
||||||
|
|
||||||
|
Geaendert:
|
||||||
|
|
||||||
|
- **Neuer Schritt 5 in `stack-dev/SKILL.md`** - "Close the issue with a body rewrite, not a
|
||||||
|
comment", mit dem Test inline (Kriterien abgehakt oder mit Begruendung gestrichen,
|
||||||
|
Entscheidungen als entschieden formuliert, kein Praesens ueber einen behobenen Defekt,
|
||||||
|
Verifikation benannt) und dem Verweis auf Schritt 7 fuer die volle Form. Damit steht der
|
||||||
|
Abschluss auf dem Spine.
|
||||||
|
- **Schritt-2-Blurb rebalanciert** - beide Haelften binden jetzt sichtbar: fortlaufende Pflege
|
||||||
|
*und* der Rewrite vor dem Schliessen, mit Verweis auf Schritt 5.
|
||||||
|
|
||||||
|
Nichts davon ist maschinell pruefbar, und das bleibt richtig so: `wikitool` kennt den Tracker
|
||||||
|
nicht und darf ihn nicht lernen, weil es an Instanzen ausliefert, die kein Board haben
|
||||||
|
(`issue-tracking.md` § "What no tool checks"). Der Skill-Spine ist die einzige Durchsetzung, die
|
||||||
|
es geben kann - was der Grund ist, den Schritt zu nummerieren statt ihn zu verlinken.
|
||||||
|
|
||||||
|
Verallgemeinerbar: eine Regel, die in eine verlinkte Instruction geschrieben wird, erreicht
|
||||||
|
Sitzungen nur, wenn die nummerierten Schritte des zustaendigen Skills sie in dem Moment
|
||||||
|
ansteuern, in dem sie greift.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 4.3.1 - 2026-09-03 - docs/ befuellt - Stack-Hintergrund fuer vier Themen, Pflegeklausel in AGENTS.md ergaenzt
|
||||||
|
|
||||||
|
**Author:** Torben Nehmer
|
||||||
|
|
||||||
|
Gitea #45: die von #38 angelegte, bis dahin leere `docs/` bekommt ihre ersten vier Seiten - frisch
|
||||||
|
geschrieben, nicht durch Umzug aus `kb/` befuellt, jede ohne normativen Satz und mit Verweis auf
|
||||||
|
das bindende Dokument statt einer Wiederholung seiner Regeln:
|
||||||
|
|
||||||
|
- `docs/pipeline-rationale.md` - warum `raw -> types/tools -> kb -> reports` vier getrennte Stufen
|
||||||
|
sind und was "never re-derive, always compile" praktisch bedeutet
|
||||||
|
- `docs/why-gates-are-code.md` - warum Mass-Update-, Publish-Remote- und Iteration-Budget-Gate in
|
||||||
|
`tools/wikitool` statt in einer Instruktion stehen
|
||||||
|
- `docs/ownership-and-templates.md` - der Unterschied zwischen stack-eigenen, verbatim
|
||||||
|
ausgelieferten Dateien und instanz-eigenen `.template`-Dateien
|
||||||
|
- `docs/version-model.md` - warum Drop-in-Kompatibilitaet und Migrationsbedarf zwei unabhaengige
|
||||||
|
Fragen sind, illustriert an der 2.0.0-Fallstudie
|
||||||
|
|
||||||
|
**AGENTS.md § Changelog:** neue Klausel zur Pflege von `docs/`, ergaenzt neben der bestehenden
|
||||||
|
Regel zu `README.md`/`EVALS.md`/`tools/README.md`. Eine `docs/`-Seite veraltet nicht wie ein
|
||||||
|
README bei jedem neuen Flag, sondern nur, wenn die aufgeschriebene Begruendung selbst nicht mehr
|
||||||
|
traegt - per Konstruktion ungeprueft, da die Seite keinen normativen Satz enthaelt, den
|
||||||
|
`docs verify` pruefen koennte.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
## 4.3.0 - 2026-09-03 - docs/ als ausgelieferter Hintergrund-Ort; Decision-Seiten bleiben in kb/, Decay-Skip fuer concept_type: decision
|
## 4.3.0 - 2026-09-03 - docs/ als ausgelieferter Hintergrund-Ort; Decision-Seiten bleiben in kb/, Decay-Skip fuer concept_type: decision
|
||||||
|
|
||||||
**Author:** Torben Nehmer
|
**Author:** Torben Nehmer
|
||||||
|
|||||||
@@ -0,0 +1,71 @@
|
|||||||
|
# Ownership and Templates
|
||||||
|
|
||||||
|
Chemenu ships two kinds of files side by side, and at a glance they look the same: both are
|
||||||
|
plain markdown, both sit in the repo root or under `kb/`, both get read at session start. But a
|
||||||
|
stack upgrade treats them completely differently. Some - [AGENTS.md](../AGENTS.md),
|
||||||
|
[kb/CONTRACT.md](../kb/CONTRACT.md), the per-stage contracts - are identical in every instance
|
||||||
|
that runs this stack and can simply be overwritten by the next release. Others - `USER.md`,
|
||||||
|
`SOUL.md`, `kb/CONVENTIONS.md`, `ENVIRONMENT.md` - describe one particular instance, and
|
||||||
|
overwriting them would silently erase a choice someone made on purpose.
|
||||||
|
|
||||||
|
## Two different kinds of truth
|
||||||
|
|
||||||
|
The stack-owned files describe how the tool works. `kb/CONTRACT.md` opens by saying it holds
|
||||||
|
what `tools/wikitool` enforces or what follows mechanically from how it operates - see
|
||||||
|
[kb/CONTRACT.md](../kb/CONTRACT.md), lines 10-13. That kind of statement doesn't vary by
|
||||||
|
instance: the compiler behaves the same way regardless of who is running it, so the sentence
|
||||||
|
describing that behavior can be copied byte-for-byte into every checkout without becoming
|
||||||
|
wrong anywhere.
|
||||||
|
|
||||||
|
The instance-owned files describe a choice: which language pages are written in, what tone the
|
||||||
|
agent takes, who the operator is, which git remote is authoritative, which MCP servers are
|
||||||
|
reachable. None of that follows from the tool's mechanics - two instances of the identical
|
||||||
|
stack can answer all of these differently and both be correct. [AGENTS.md § Personalization](../AGENTS.md#personalization)
|
||||||
|
frames the split the same way for `kb/CONTRACT.md` versus `kb/CONVENTIONS.md`: "the split is by
|
||||||
|
who may change the sentence, not by what it is about." A rule about page structure could in
|
||||||
|
principle have been written per-instance too, but then every instance answering "not German" to
|
||||||
|
setup would be hand-editing a file the stack also ships, and the next `dist export` merge would
|
||||||
|
hand the instance's own file back to it, discarding the customization.
|
||||||
|
|
||||||
|
## Why silent overwrite is the failure being designed against
|
||||||
|
|
||||||
|
A stack update is meant to be a routine, low-risk operation: pull the latest release, get
|
||||||
|
whatever fixes and features shipped since the last one. That only stays low-risk if the update
|
||||||
|
knows which files it's allowed to touch. If `USER.md` or `kb/CONVENTIONS.md` were treated the
|
||||||
|
same as `AGENTS.md` - shipped and periodically re-copied - an upgrade would quietly replace a
|
||||||
|
description of *this* operator, in *this* language, with whatever placeholder or default the
|
||||||
|
stack maintainers wrote. The damage wouldn't be loud: nothing crashes, the files still parse,
|
||||||
|
the agent just starts acting on the wrong premises until someone notices the voice or the
|
||||||
|
language changed.
|
||||||
|
|
||||||
|
Keeping the boundary at the file level, rather than trying to merge changes within a shared
|
||||||
|
file, means an upgrade never has to guess which lines are "stack" and which are "instance" -
|
||||||
|
the file itself already answers that.
|
||||||
|
|
||||||
|
## Why a `.template`, not just an absent file
|
||||||
|
|
||||||
|
The mechanism for instance-owned content is a `.template` file the distribution ships instead
|
||||||
|
of the real one - `USER.md.template`, `SOUL.md.template`, `kb/CONVENTIONS.md.template`,
|
||||||
|
`ENVIRONMENT.md.template`. An alternative would have been to ship nothing at all and let a
|
||||||
|
brand-new instance start from a blank page. The template exists because a blank page doesn't
|
||||||
|
tell [instructions/setup-instance.md](../instructions/setup-instance.md) what shape the answer
|
||||||
|
should take, and it gives nothing for a validator to check afterward.
|
||||||
|
|
||||||
|
A template carries a placeholder value - a sentinel - in the fields that need a real answer.
|
||||||
|
Setup interviews the operator and replaces the sentinel with what they actually said. That
|
||||||
|
gives `doctor` a mechanical way to tell "personalized" from "not yet": a file that still
|
||||||
|
contains the sentinel hasn't been through setup, regardless of whether the file exists. That's
|
||||||
|
also why `ENVIRONMENT.md` only warrants a WARN rather than a FAIL when absent - see
|
||||||
|
[AGENTS.md § Environment](../AGENTS.md#environment) - while a missing or unfilled
|
||||||
|
`USER.md`/`SOUL.md`/`kb/CONVENTIONS.md` is a harder failure: `ENVIRONMENT.md` describes one
|
||||||
|
checkout among possibly several and is gitignored for that reason, so its absence is a normal
|
||||||
|
state rather than a sign setup was skipped.
|
||||||
|
|
||||||
|
## The consequence in practice
|
||||||
|
|
||||||
|
Running a stack upgrade against an existing instance boils down to: overwrite the verbatim
|
||||||
|
files, leave the `.template`-sourced files alone. The verbatim files are safe to replace
|
||||||
|
wholesale because they were never instance-specific to begin with - identical content going
|
||||||
|
back in changes nothing an instance actually decided. The template-sourced files were filled in
|
||||||
|
once, by a person, for a reason, and nothing about a newer release of the stack's mechanics
|
||||||
|
gives it standing to override that.
|
||||||
@@ -0,0 +1,64 @@
|
|||||||
|
# Why the pipeline has four stages
|
||||||
|
|
||||||
|
Chemenu could, in principle, be one directory: drop a file in, ask a question, get an answer
|
||||||
|
computed fresh each time. It isn't built that way. The pipeline in
|
||||||
|
[AGENTS.md](../AGENTS.md#routing) - `raw/` -> `[types/ + tools/]` -> `kb/` -> `reports/`, with
|
||||||
|
`work/` alongside rather than inside it - separates *material* from *meaning* from
|
||||||
|
*byproduct*, and each seam exists because collapsing it costs something specific.
|
||||||
|
|
||||||
|
## Why raw material stays untouched
|
||||||
|
|
||||||
|
[raw/CONTRACT.md](../raw/CONTRACT.md) keeps a source exactly as it arrived. The reasoning is
|
||||||
|
simple once stated: the moment someone "cleans up" or reformats a source on the way in, the
|
||||||
|
thing later claims get checked against is no longer the thing that was actually said. An
|
||||||
|
immutable `raw/` means a citation always resolves to the original, not to somebody's tidied
|
||||||
|
memory of it. It also draws a trust boundary in one place instead of scattering it - everything
|
||||||
|
past `raw/` can be treated as reviewed, because nothing upstream of it silently already was.
|
||||||
|
|
||||||
|
## Why extraction happens once, through a schema
|
||||||
|
|
||||||
|
[types/type-spec.md](../types/type-spec.md) is what stands between a raw file and a `kb/` page:
|
||||||
|
a type-spec defines what a conforming instance of a page looks like, and the compiler
|
||||||
|
(`tools/wikitool`) applies it. The alternative - every query re-reading and re-interpreting the
|
||||||
|
source on demand - would mean paying the cost of understanding the material every single time,
|
||||||
|
and getting a slightly different answer each time depending on how the question was phrased.
|
||||||
|
Extracting once, against a fixed schema, turns "re-read and re-guess" into "look up what was
|
||||||
|
already compiled." That is the "never re-derive, always compile" principle from
|
||||||
|
[AGENTS.md](../AGENTS.md): understanding a source is expensive and worth doing exactly once,
|
||||||
|
after which it becomes a cheap, stable lookup.
|
||||||
|
|
||||||
|
## Why a `kb/` page has to stand on its own
|
||||||
|
|
||||||
|
[kb/CONTRACT.md](../kb/CONTRACT.md) sets the bar for the compiled layer: a page should answer a
|
||||||
|
future question without sending the reader back to the source it came from. That's the payoff
|
||||||
|
of compiling in the first place - if every answer still bottomed out in "go re-read the raw
|
||||||
|
file," the `kb/` layer would just be a pointer with extra steps, and the cost of extraction
|
||||||
|
would have bought nothing. A page that stands alone is what makes the corpus fast and
|
||||||
|
consistent to query: the work of understanding is already sitting there, done.
|
||||||
|
|
||||||
|
## Why `reports/` doesn't need to be maintained
|
||||||
|
|
||||||
|
[reports/CONTRACT.md](../reports/CONTRACT.md) treats most of what lands in `reports/` -
|
||||||
|
lint output, telemetry traces - as disposable. The structural content of a lint report can be
|
||||||
|
recomputed from the tree at any commit, so keeping an old copy around would just be a second
|
||||||
|
version of something the tool can already answer on demand, and a second copy is exactly the
|
||||||
|
kind of thing that quietly goes stale. Treating it as derived output rather than a fourth thing
|
||||||
|
to maintain means there is nothing there to fall out of sync - regenerating it is cheaper than
|
||||||
|
reconciling it. The one part that genuinely can't be recomputed - the judgment a pass produced -
|
||||||
|
is carried out into `kb/` or `kb/log.md` before the report itself is discarded, which is the
|
||||||
|
distinction between what's recomputable and what isn't.
|
||||||
|
|
||||||
|
## Where `work/` fits
|
||||||
|
|
||||||
|
[work/CONTRACT.md](../work/CONTRACT.md) describes a workshop, not a fifth pipeline stage: a
|
||||||
|
place for the notes, extracts and open decisions of a task that spans more than one session, on
|
||||||
|
its way toward becoming a `kb/` page. It sits beside the raw -> kb -> reports flow rather than
|
||||||
|
inside it - closer in spirit to a desk than to a conveyor belt.
|
||||||
|
|
||||||
|
## The shape this produces
|
||||||
|
|
||||||
|
Four stages, each answering a different question: `raw/` - what was actually said; `types/` +
|
||||||
|
`tools/` - how to turn that into structured understanding; `kb/` - what is now known;
|
||||||
|
`reports/` - what a pass over the corpus noticed in passing. Keeping them separate is what lets
|
||||||
|
each one be trusted for what it is, instead of every layer having to double as all four at
|
||||||
|
once.
|
||||||
@@ -0,0 +1,84 @@
|
|||||||
|
# Why the stack version splits compatibility from migration
|
||||||
|
|
||||||
|
A stack version number looks like it answers one question. It actually answers two, and the two
|
||||||
|
are independent of each other.
|
||||||
|
|
||||||
|
## Two questions, not one
|
||||||
|
|
||||||
|
The first question is whether the new version is a drop-in replacement for the old one - whether
|
||||||
|
an existing instance can install it, and can also go back, without anyone doing hand-work. That
|
||||||
|
is what a version number *is*: a promise. The second question is whether the existing corpus in
|
||||||
|
`kb/` needs to change shape to keep working under the new version. These sound like the same
|
||||||
|
question, because most of the time a change that breaks compatibility also happens to touch
|
||||||
|
content, and most of the time a change that leaves content untouched also happens to be
|
||||||
|
compatible. The correlation is real; it just is not a law. `instructions/dev/version-parts.md`
|
||||||
|
carries the actual test for telling them apart and the steps that follow from it - this page is
|
||||||
|
about why the split exists at all.
|
||||||
|
|
||||||
|
## Why "kb/ untouched" is not proof of anything
|
||||||
|
|
||||||
|
The tempting shortcut is: if no page in `kb/` had to change, the bump can't be that serious. This
|
||||||
|
is exactly backwards for a class of changes that live entirely outside the corpus - a renamed
|
||||||
|
release artefact, a Python import path, an environment variable, the URL an instance's own
|
||||||
|
updater points at. None of those touch a single page. All of them can strand an existing
|
||||||
|
instance just as thoroughly as a rewritten type-spec would. The corpus is the part of the stack
|
||||||
|
that looks at itself; the compatibility question is about everything an instance depends on to
|
||||||
|
keep functioning, most of which the corpus never sees.
|
||||||
|
|
||||||
|
## Reading compatibility off the leftmost non-zero component
|
||||||
|
|
||||||
|
Semantic versioning gives every component a job, but only one of them is where an existing
|
||||||
|
instance's tooling actually looks to decide "is this safe." On a `2.x` stack that is MAJOR; on a
|
||||||
|
still-pre-1.0 `0.x` stack, by the same convention, it's MINOR - the leftmost slot that isn't
|
||||||
|
pinned to zero is the one an automated updater treats as the compatibility boundary. Bump
|
||||||
|
anything to its left, or bump that slot itself, and the promise changes. Everything to the right
|
||||||
|
of it can move as freely as the project likes without touching that promise. This is why the
|
||||||
|
question "is it boundary-crossing" always resolves to one specific digit, not to a feeling about
|
||||||
|
how big the change is.
|
||||||
|
|
||||||
|
## Downgrade is half the promise
|
||||||
|
|
||||||
|
It's natural to test compatibility by only asking "does the upgrade work." The other half -
|
||||||
|
"can an instance that upgraded put the old version back and land where it started" - carries
|
||||||
|
equal weight, and it's the half that's easy to forget because forward motion is what everyone is
|
||||||
|
testing for anyway. A state file the old version can no longer parse, a generated index in a new
|
||||||
|
shape, a stamp file that got renamed: none of these have to break the upgrade to break the
|
||||||
|
downgrade. An instance that can go forward but not back has already lost the property a
|
||||||
|
compatible version number is supposed to guarantee.
|
||||||
|
|
||||||
|
## A promise made to a machine, not only to a person
|
||||||
|
|
||||||
|
A human reading a changelog can absorb "this technically isn't compatible but it's fine, just
|
||||||
|
update those two things by hand." An instance's own update mechanism cannot. It reads a version
|
||||||
|
number, decides whether to pull the new release, and has no channel for nuance - which is exactly
|
||||||
|
why the update path itself is one of the sharpest ways to cross the boundary invisibly: if the
|
||||||
|
new version moves where updates come from, the very channel that would have told an instance to
|
||||||
|
adjust is the channel that just broke. The version number isn't documentation aimed at a reader;
|
||||||
|
it's an input consumed by code that has no other way to ask.
|
||||||
|
|
||||||
|
## The 2.0.0 story
|
||||||
|
|
||||||
|
This isn't hypothetical for this stack. The rebranding that produced Chemenu renamed the repo,
|
||||||
|
the release artefact, and the Python package - and left every page in `kb/` untouched. The first
|
||||||
|
instinct was a MINOR bump, on the reasoning that nothing in the corpus needed migrating. That
|
||||||
|
reasoning was correct on its own terms and answered the wrong question. Three things broke
|
||||||
|
underneath it: every existing instance's `update_url` pointed at a repo path that no longer
|
||||||
|
existed and, because it's a machine-written file, couldn't be hand-repaired; the release artefact
|
||||||
|
name changed, breaking every download script and pin against it; and the import name changed,
|
||||||
|
breaking anything importing the package from outside the shipped tree. The corpus had nothing to
|
||||||
|
say about any of this, because none of it lived in the corpus.
|
||||||
|
|
||||||
|
What caught the mistake was a person looking at the diff and asking whether it really was a
|
||||||
|
drop-in replacement, not a validator. No check in `docs verify` or anywhere else confirms that a
|
||||||
|
version part was chosen correctly - it only confirms that a boundary-crossing bump documents
|
||||||
|
what it breaks. The 2.0.0 entry in `CHANGES.md` carries the corrected reasoning in full, and the
|
||||||
|
version bump that shipped it was `--major --no-migration`: boundary-crossing and untouched
|
||||||
|
corpus, at the same time, which is precisely the combination the two-question split exists to
|
||||||
|
make visible.
|
||||||
|
|
||||||
|
## Where the procedure lives
|
||||||
|
|
||||||
|
The drop-in test, the catalogue of changes that cross the boundary with no page touched, and the
|
||||||
|
steps for a boundary-crossing bump - the `--breaking` line, the migration document or
|
||||||
|
`--no-migration` reason, talking to the user before bumping - are one procedure, kept at one
|
||||||
|
place: [instructions/dev/version-parts.md](../instructions/dev/version-parts.md).
|
||||||
@@ -0,0 +1,57 @@
|
|||||||
|
# Why gates are code
|
||||||
|
|
||||||
|
Chemenu has three hard limits - the Mass-Update Gate, the Publish-Remote Gate, and the
|
||||||
|
Iteration Budget Gate - and all three live inside `tools/wikitool`, not in a paragraph of
|
||||||
|
instructions an agent reads and follows. The rules themselves, and what to do when one trips,
|
||||||
|
are in [AGENTS.md § Gates](../AGENTS.md#gates) and [instructions/gates.md](../instructions/gates.md).
|
||||||
|
This page is only about the design choice underneath them: why code, and why these three
|
||||||
|
mechanisms in particular.
|
||||||
|
|
||||||
|
## A suggestion an agent can talk itself past
|
||||||
|
|
||||||
|
An instruction like "don't publish too much at once" or "don't loop forever" lives in the same
|
||||||
|
place as every other piece of guidance a session is holding - alongside the task, the user's
|
||||||
|
last message, and whatever context made the moment feel urgent. Under pressure, or with a
|
||||||
|
plausible-sounding reason ("this batch is different, it's mechanical"), that guidance can be
|
||||||
|
reasoned around without anyone deciding to break a rule. Nothing enforces it; it just competes
|
||||||
|
for attention with everything else in the context window, and sometimes loses.
|
||||||
|
|
||||||
|
A check compiled into the tool doesn't have that problem, because it isn't part of the
|
||||||
|
conversation at all. It runs before the command dispatches, regardless of how convincing the
|
||||||
|
case for skipping it seemed a moment earlier. The difference isn't that code is smarter than a
|
||||||
|
well-written instruction - it's that code doesn't get talked into anything.
|
||||||
|
|
||||||
|
## Why three different mechanisms, not one
|
||||||
|
|
||||||
|
The three gates ask three different questions, and each one's shape follows from what kind of
|
||||||
|
question it is.
|
||||||
|
|
||||||
|
The Mass-Update Gate asks *is this change too large to publish unreviewed* - a judgment that
|
||||||
|
varies changeset by changeset, so it clears with a `--confirm` token tied to the specific
|
||||||
|
output the user just read. Approval is scoped to that one publish.
|
||||||
|
|
||||||
|
The Publish-Remote Gate asks something underneath that: *is this even the right repository*.
|
||||||
|
That's not a per-push judgment, it's a standing property of the checkout - true or false for
|
||||||
|
every publish that checkout will ever attempt, not just this one. A confirm token would let an
|
||||||
|
agent clear it once and then treat the answer as settled, which is exactly backwards for a
|
||||||
|
question whose answer shouldn't move at all mid-session. The only way past it is the user
|
||||||
|
editing `.wikitool-remotes.json` directly, outside the gate's own flow.
|
||||||
|
|
||||||
|
The Iteration Budget Gate asks a third kind of question - not "is this instance correct" but
|
||||||
|
"has this session stopped making progress." That's read from the shape of the call history
|
||||||
|
itself (call count, repeated identical calls), not from anything about the content of any one
|
||||||
|
call.
|
||||||
|
|
||||||
|
## Numbers that come from measurement, not intuition
|
||||||
|
|
||||||
|
The iteration ceiling didn't start where it sits now. It used to run 15-25, borrowed from a
|
||||||
|
general rule of thumb, until four real ingest runs measured 24, 26, 29 and 30 calls apiece -
|
||||||
|
every one of them an ordinary workflow doing nothing wrong, and every one of them at or past
|
||||||
|
where the old ceiling would have refused it. A limit that the normal case keeps tripping stops
|
||||||
|
functioning as a limit; it becomes background noise a session learns to route `--override-budget`
|
||||||
|
around as a matter of course, and the whole point of a hard-coded check is that it isn't supposed
|
||||||
|
to feel routine.
|
||||||
|
|
||||||
|
That's the deeper reason these numbers live in a tool rather than in prose: prose is read once
|
||||||
|
and remembered loosely, but a threshold enforced every call is tested by every call, and a
|
||||||
|
threshold that fails its own test gets noticed and re-measured rather than quietly ignored.
|
||||||
@@ -6,10 +6,24 @@ description: Which Claude model and effort level to run a Claude Code session, a
|
|||||||
|
|
||||||
# Pick the Claude model and effort level for the task at hand
|
# Pick the Claude model and effort level for the task at hand
|
||||||
|
|
||||||
Scale the model and effort to how much judgment the task actually needs. Running everything at
|
Scale the model and effort to **what catches a mistake in this part of the work** - not to how
|
||||||
the most capable model and highest effort is safe but wasteful: the gates in [gates.md](gates.md)
|
important the task feels, and not to its name. Running everything at the most capable model and
|
||||||
are enforced in code, not by model judgment, so a weaker model cannot bypass them - it can only
|
highest effort is safe but wasteful: the gates in [gates.md](gates.md) are enforced in code, not
|
||||||
do a worse job of the calls the gates don't cover.
|
by model judgment, so a weaker model cannot bypass them - it can only do a worse job of the calls
|
||||||
|
the gates don't cover.
|
||||||
|
|
||||||
|
That last clause is the whole rule, turned into a test. Where a check lives in code - `pytest`,
|
||||||
|
`docs verify`, `instructions verify`, CI, the gates - a weaker model's mistake surfaces and costs
|
||||||
|
one more round. Where the only enforcement is a session reading prose, the same mistake does not
|
||||||
|
surface at all: it ships, and it stays until someone happens to notice. The two are not the same
|
||||||
|
risk, and they should not get the same model. This is the argument
|
||||||
|
[docs/why-gates-are-code.md](../docs/why-gates-are-code.md) makes about gates, applied to who is
|
||||||
|
holding the keyboard.
|
||||||
|
|
||||||
|
Both directions cost something, which is why the axis matters rather than a blanket answer:
|
||||||
|
over-provisioning is a standing cost paid every session, while under-provisioning in an unchecked
|
||||||
|
phase is a silent error with a long tail. A corrective session, its bump, its CI runs and its
|
||||||
|
release together cost more compute than the model difference they were saving.
|
||||||
|
|
||||||
Claude-Code-only, and imported by CLAUDE.md rather than linked from AGENTS.md: the model names,
|
Claude-Code-only, and imported by CLAUDE.md rather than linked from AGENTS.md: the model names,
|
||||||
the `/code-review` effort dial and the `Agent` tool's `model:` override have no equivalent in the
|
the `/code-review` effort dial and the `Agent` tool's `model:` override have no equivalent in the
|
||||||
@@ -28,17 +42,41 @@ to *make*, not a setting to apply.
|
|||||||
|
|
||||||
## Steps
|
## Steps
|
||||||
|
|
||||||
1. **Recommend the session's model and effort by the skill in use**, when asked or when the
|
1. **Recommend the session's model and effort by what catches a mistake in the phase it is in**,
|
||||||
mismatch is worth one sentence. Say it once and continue working either way - a session that
|
when asked or when the mismatch is worth one sentence. Say it once and continue working either
|
||||||
argues about its own model instead of doing the task has already cost more than the model
|
way - a session that argues about its own model instead of doing the task has already cost
|
||||||
difference:
|
more than the model difference:
|
||||||
|
|
||||||
| Skill / task | Model | Effort |
|
| Phase / task | What catches a mistake here | Model | Effort |
|
||||||
|---|---|---|
|
|---|---|---|---|
|
||||||
| `wiki-status`, simple `wiki-query` lookups | Sonnet | default |
|
| `wiki-status`, simple `wiki-query` lookups | the answer is re-checkable against the corpus | Sonnet | default |
|
||||||
| `wiki-lint` | Sonnet | default |
|
| `wiki-lint` | `lint` itself is the check | Sonnet | default |
|
||||||
| `wiki-ingest`, `wiki-manage`, judgment-heavy `wiki-query` | Sonnet | high |
|
| `wiki-ingest`, `wiki-manage`, judgment-heavy `wiki-query` | `lint` and `docs verify`, partly - the judgment about a claim is not covered | Sonnet | high |
|
||||||
| Stack development: `tools/`, `types/`, `instructions/` as code | Opus | high |
|
| Stack dev: design, the version part, a boundary-crossing judgment | nothing - `docs verify` checks that a crossing documents itself, never that the part was right | Opus | high |
|
||||||
|
| Stack dev: code, tests, mechanical doc sync (command tables, contract rows) | `pytest`, `docs verify`, `instructions verify`, CI | Sonnet | high |
|
||||||
|
| Stack dev: closing an issue, `docs/` staleness, changelog prose | nothing, by construction - see below | Opus | high |
|
||||||
|
|
||||||
|
**Stack development is not one row**, which is the point of splitting it. The middle phase is
|
||||||
|
where the tokens are and where the checks are, so it is the phase worth running cheaper. The
|
||||||
|
two around it have no mechanical guard at all - a `docs/` page carries no normative sentence,
|
||||||
|
so there is nothing for `docs verify` to check ([AGENTS.md](../AGENTS.md) § File naming), and
|
||||||
|
the same holds for whatever tracker an instance keeps its open work in, which `wikitool`
|
||||||
|
deliberately knows nothing about. Those two phases are short - minutes, not hours - so keeping
|
||||||
|
them on the stronger model is cheap, and it protects the only work in the session that fails
|
||||||
|
silently.
|
||||||
|
|
||||||
|
**Effort is the cheaper lever than the model.** Reach for it first: `medium` deliberately does
|
||||||
|
not appear in this table for stack work, because multi-file consistency is exactly what a
|
||||||
|
reduced effort level gives up. Sonnet at `high` is the floor for anything touching more than
|
||||||
|
one file or a contract; `default` is for a single-file mechanical edit with a test behind it.
|
||||||
|
|
||||||
|
**A session cannot switch its own model**, so these rows only become real if someone offers the
|
||||||
|
switch at the moment the phase changes - once, without arguing about it, and never as a reason
|
||||||
|
to stop work that is already underway.
|
||||||
|
<!-- dist:strip-start -->
|
||||||
|
In this repo those moments are named: the `stack-dev` skill breaks for them at its steps 3
|
||||||
|
(design settled, work turns mechanical) and 6 (publish done, the unchecked tail begins).
|
||||||
|
<!-- dist:strip-end -->
|
||||||
|
|
||||||
2. **Pick a spawned subagent's model by what it does**, via the `Agent` tool's `model:`
|
2. **Pick a spawned subagent's model by what it does**, via the `Agent` tool's `model:`
|
||||||
parameter - the values are `haiku`, `sonnet`, `opus`, `fable`:
|
parameter - the values are `haiku`, `sonnet`, `opus`, `fable`:
|
||||||
@@ -66,8 +104,14 @@ to *make*, not a setting to apply.
|
|||||||
mechanical one - `wikitool` carries the mechanical part regardless of which model is
|
mechanical one - `wikitool` carries the mechanical part regardless of which model is
|
||||||
supervising it.
|
supervising it.
|
||||||
- **Unsure which row applies?** Default to Sonnet at high effort, not the most capable model at
|
- **Unsure which row applies?** Default to Sonnet at high effort, not the most capable model at
|
||||||
the highest effort. Under-provisioning costs one worse answer in one session; reflexively
|
the highest effort. Under-provisioning *where a check exists* costs one worse answer in one
|
||||||
over-provisioning is a standing cost paid every session.
|
session; reflexively over-provisioning is a standing cost paid every session.
|
||||||
|
- **Unsure whether the phase is checked?** Treat it as unchecked. The asymmetry is not symmetric:
|
||||||
|
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?** Do the work anyway - never block a
|
||||||
|
publish or an issue close on a model the session cannot change itself. Say which phase ran on
|
||||||
|
which model in the handover, so the gap is visible rather than silent.
|
||||||
|
|
||||||
## Scope
|
## Scope
|
||||||
|
|
||||||
|
|||||||
@@ -45,22 +45,44 @@ stack development happens in the origin repo instead (see AGENTS.md's routing li
|
|||||||
[issue-tracking.md](../issue-tracking.md) - open work lives in Gitea issues, one per work
|
[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
|
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
|
of the issue you are working on is this session's plan file:** keep it current as the state
|
||||||
moves, not at the end, so an interrupted session leaves a body the next one can resume from.
|
moves, so an interrupted session leaves a body the next one can resume from, *and* rewrite it
|
||||||
Read it before filing something for later, before editing or closing an issue, or before
|
to its final state before closing. Both halves bind; the second is step 6 below. Read it
|
||||||
deciding what to pick up next.
|
before filing something for later, before editing or closing an issue, or before deciding
|
||||||
|
what to pick up next.
|
||||||
[testing-conventions.md](../testing-conventions.md) - the suite runs against a deliberately
|
[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
|
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.
|
establish itself. Read it before adding or changing a test.
|
||||||
[version-parts.md](../version-parts.md) - which part a change bumps: the drop-in test, the
|
[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
|
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 3.
|
put in front of the user before a breaking bump. Read it before step 4.
|
||||||
[corpus-policy.md](../corpus-policy.md) - what "curated enough" means for the shared
|
[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
|
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
|
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.
|
before any fix that would touch `kb/` content.
|
||||||
More instructions are added here incrementally as stack-development needs come up - this
|
More instructions are added here incrementally as stack-development needs come up - this
|
||||||
list grows without needing this skill file to change shape.
|
list grows without needing this skill file to change shape.
|
||||||
3. **Raise the version, if the change ships.** A change under `tools/`, `types/`,
|
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 have
|
||||||
|
`pytest`, `docs verify`, `instructions verify` and CI behind them.
|
||||||
|
|
||||||
|
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:
|
||||||
|
|
||||||
|
> Der Plan steht, ab hier ist die Arbeit mechanisch und durch Tests/CI abgedeckt. Wenn du auf
|
||||||
|
> Opus bist, ist jetzt der Moment für `/model sonnet` bei Effort `high`.
|
||||||
|
|
||||||
|
**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.
|
||||||
|
|
||||||
|
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:
|
||||||
|
[claude-code-model-selection.md](../../claude-code-model-selection.md).
|
||||||
|
|
||||||
|
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
|
`instructions/`, `AGENTS.md` or a `CONTRACT.md` reaches every future instance, so it needs a
|
||||||
version and a changelog entry:
|
version and a changelog entry:
|
||||||
|
|
||||||
@@ -97,13 +119,48 @@ stack development happens in the origin repo instead (see AGENTS.md's routing li
|
|||||||
Prose-only changes (`README.md`, `INSTALL.md`, `EVALS.md`) and the workflows under `.gitea/`
|
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.
|
do not need a bump - CI's version gate is scoped to what changes behaviour.
|
||||||
|
|
||||||
4. **Verify before publishing.** `tools/wikitool docs verify`, `tools/wikitool instructions
|
5. **Verify before publishing.** `tools/wikitool docs verify`, `tools/wikitool instructions
|
||||||
verify`, and the relevant `pytest` run in `tools/` - the same checks any stack change must
|
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
|
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
|
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
|
`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.
|
creates a tag, which is what keeps AGENTS.md invariant 5 intact.
|
||||||
|
|
||||||
|
6. **Close the issue with a body rewrite, not a comment.** The last act of a session that
|
||||||
|
finished a work package, and the one most easily skipped: by here the change is published and
|
||||||
|
the issue feels done. It is not. The body is the version everyone reads afterwards and nobody
|
||||||
|
revisits, so it is the one place the debt comes due at the worst moment.
|
||||||
|
|
||||||
|
**Break here too, in the other direction.** Everything left in the session - this rewrite,
|
||||||
|
whether a `docs/` page's reasoning went stale, the changelog prose - is the unchecked kind of
|
||||||
|
work again, the mirror of step 3. If the session dropped to Sonnet there, say so now:
|
||||||
|
|
||||||
|
> Ab hier greift kein maschineller Check mehr - Issue-Body, `docs/`-Veralterung und
|
||||||
|
> Changelog-Prosa prüft nichts. Wenn du zurück auf Opus willst, ist jetzt der Moment.
|
||||||
|
|
||||||
|
Then **do the work regardless of the answer.** Never block a close on a model switch: the
|
||||||
|
change is already published, and a session that stops here leaves exactly the state this step
|
||||||
|
exists to prevent. If it ran on the cheaper model, name that in the handover rather than
|
||||||
|
leaving it silent.
|
||||||
|
|
||||||
|
Rewrite it to its final state *first*, then close. The test is 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
|
||||||
|
- 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.
|
||||||
|
|
||||||
|
**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 - `wikitool` does not know this tracker exists and must not learn it,
|
||||||
|
since it ships to instances that have no board - so this step is the only enforcement there
|
||||||
|
is. #44 and #45 both closed exactly this way, the second an hour after the rule was written.
|
||||||
|
[issue-tracking.md](../issue-tracking.md) step 7 has the full shape.
|
||||||
|
|
||||||
## Decision points
|
## Decision points
|
||||||
|
|
||||||
- **Touches both stack code and wiki content in one session?** Apply this skill's rules to the
|
- **Touches both stack code and wiki content in one session?** Apply this skill's rules to the
|
||||||
@@ -114,7 +171,10 @@ stack development happens in the origin repo instead (see AGENTS.md's routing li
|
|||||||
user decides whether it is worth that: show them what breaks, what an instance has to do about
|
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,
|
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.
|
or split it behind a deprecation window), then recommend one and wait for a go-ahead.
|
||||||
[version-parts.md](../version-parts.md) step 4 has the full shape.
|
[version-parts.md](../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.
|
||||||
|
|
||||||
## Scope
|
## Scope
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user