Files
chemenu/instructions/upgrade-instance.md
T
torbenandClaude Opus 5.5 d8cb494d58
CI / verify (push) Successful in 5m17s
CI / pwsh (push) Successful in 2m1s
Release / release (push) Successful in 34s
feat: a subtype gets its own page skeleton from types/<type>.<value>.md (#117)
Files changed:
- AGENTS.md
- CHANGES.md
- README.md
- VERSION
- docs/ownership-and-templates.md
- instructions/evolve-subtypes.md
- instructions/setup-instance.md
- instructions/subtype-templates.md
- instructions/upgrade-instance.md
- tools/CONTRACT.md
- tools/chemenu/commands/dist_cmd.py
- tools/chemenu/commands/docs_verify.py
- tools/chemenu/commands/new_page.py
- tools/chemenu/tests/test_dist_cmd.py
- tools/chemenu/tests/test_docs_verify.py
- tools/chemenu/tests/test_new_page.py
- tools/chemenu/tests/test_toc.py
- tools/chemenu/tests/test_type_resolver.py
- tools/chemenu/toc.py
- tools/chemenu/type_resolver.py
- types/concept.decision.md
- types/entity.guidance.md
- types/entity.md
- types/entity.person.md
- types/type-spec.md

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SnAJ7Z3CpVD3PRbN73QtU2
2026-10-03 15:58:15 +02:00

291 lines
16 KiB
Markdown

---
type: types/instruction.md
name: upgrade-instance
description: Carry out a stack release upgrade on an instance installed from a release - read this release's notes, swap the machinery with dist upgrade, work the migration chain, verify, publish, and restart the session at the point where the new control plane starts to matter.
manual: true
---
# Upgrade this instance to a new stack release
An instance installed from a release takes stack updates by copying a newer release over its
machinery. This is the order in which that happens, what each step decides, and where the two
known rough edges are. It ends with the instance on the new `VERSION`, its content version
recorded, every check green, and the change published.
**Every instance takes this path.** An instance comes from a release and carries the
`.wikitool-release.json` that release wrote; `dist upgrade` refuses to run without it. A clone of
the origin repository is a development checkout of the stack itself, not an instance, and is
updated with git rather than with this file.
**One thing this file deliberately does not know.** The copy you are reading shipped with the
release this instance is *leaving*, not the one it is going to - so nothing specific to a
particular jump is written here. That belongs to the release notes (step 2) and to the migration
documents that arrive inside the tarball.
<!-- wikitool:toc -->
## Contents
- [When to run](#when-to-run)
- [Steps](#steps)
- [Decision points](#decision-points)
- [Scope](#scope)
<!-- /wikitool:toc -->
## When to run
- `tools/wikitool version check` reports `state: update` or `state: migration`, and the operator
wants the new release installed.
- An operator asks for the stack, the tooling or "the wiki software" to be brought up to date.
- An interrupted upgrade is being resumed. Do not restart from step 1: `migrate status` and
`dist upgrade --dry-run` both report the true state, and the step that matches what they say
is where this run continues.
Not for setting up a new instance ([setup-instance.md](setup-instance.md)) and not for preparing
a further checkout of this one ([bootstrap.md](bootstrap.md)).
## Steps
1. **Take a session id and keep it for every call of the whole upgrade:** `upgrade-<target
version>`, set with the line for your shell from [session-setup.md](session-setup.md) § Steps
- which also says what to do on a harness that starts a fresh shell per command. An upgrade is
one of the longest runs this stack has, and the iteration budget only sees it as one run if
every call carries the same id. Then:
```bash
tools/wikitool version check
```
2. **Read this release's notes before touching anything.** Two lines decide the rest of the run:
**Breaking Change:** says what stops working and what this instance must do about it, and
**Migration:** says whether the corpus has to be rewritten (`none required` when it does not).
```bash
tools/wikitool version notes
```
On an instance this answers out of the release feed, not out of the local `CHANGES.md` - that
file arrives as a stub with no version entries and `dist upgrade` never overwrites it, so the
command reads the notes off the release the feed publishes instead. Two things follow that are
worth knowing before reading the output. It can only ask for the feed's *latest* release, so
while `VERSION` still names the release being left, the version it answers with is **not** the
one this tree declares - it says so on stderr, and that is the normal shape here rather than a
fault. And if the feed cannot be reached, the error names the release page from
`.wikitool-release.json`'s `release_url`; read it there and continue.
3. **Ask what is already outstanding, while `VERSION` is still the old one:**
```bash
tools/wikitool migrate status
```
Anything in the outstanding chain is finished **before** the swap - `dist upgrade` refuses
otherwise, and a chain that was already owed is not this release's business. The procedure is
step 12's, run against the migration documents this instance already has. An `offered` upgrade
listed separately blocks nothing and is decided later, in step 12.
4. **Nothing to fetch by hand.** `dist upgrade --latest` asks the release feed for the latest
release, downloads its `.tar.gz` and `.sha256` into a scratch directory, checks the archive
against the checksum and removes both again - all inside the calls of steps 5 to 7. Note the
version step 2's `version notes` printed: steps 5 to 7 pass it as `--expect`, so a release that
appeared in the meantime is refused before anything is downloaded, rather than applied unread.
The offline alternative is the tarball path: with the feed unreachable, or an archive the
operator supplies, the operator puts the `.tar.gz` and its `.sha256` side by side, from the
release page named in step 2, and you pass the archive as `<tarball>` where the steps below
say `--latest --expect <version>`. `dist upgrade` checks the archive against the `.sha256`
beside it before unpacking, and refuses one that does not match. A tarball must unpack to
exactly one top-level directory. The checksum comes from the same host as the archive, so it catches a
damaged transfer, not a compromised host - who is trusted to publish releases is the
operator's decision, made before this file starts ([INSTALL.md](../INSTALL.md) § "Version und
Updates").
5. **Dry-run the swap and read all four counts:**
```bash
tools/wikitool dist upgrade --latest --expect <version from step 2> --dry-run
```
`unchanged` / `new` / `locally changed` / `removed from the release`. `unchanged` needs no
decision. `new` needs one only in a single shape: a `<name>.template` for a page type or
collection this instance does not have yet. `dist upgrade` writes the template and stops there
- adopting it (copying it to the unsuffixed name) is the instance's own act, and where the
stack *requires* that type the omission is what step 9's `docs verify` refuses. Step 2's
**Breaking Change:** line says when a release is in that shape; step 9 has the repair.
One more shape of `new` needs no decision at all: a subtype template
`types/<type>.<value>.md.template` beside a type this instance has already adopted. Adopt it
(`tools/wikitool dist adopt types/<type>.<value>.md.template`) and `wikitool new` scaffolds
pages of that subtype from it; leave it lying and they keep the type's `## Template` block.
Both are valid - [subtype-templates.md](subtype-templates.md) is how to judge whether the
corpus wants it. `locally changed` is step 6. `removed` matters only if `--prune` is wanted, which is optional
and never required.
6. **Only if a file is reported as locally changed: decide whose file it is, then reconcile it.**
The classification is against the sha256 the *installed* release recorded, so "locally
changed" means the working tree differs from what this instance was given - deliberately or
by a stray editor save.
| Whose file | What to do |
|---|---|
| The instance's own | Cannot appear here, which is worth knowing so a report that looks like it is read again rather than acted on: a file the instance owns either ships only as `<name>.template` (`kb/CONVENTIONS.md`, each `COLLECTION.md`, `USER.md`/`SOUL.md`/`ENVIRONMENT.md`) and is never classified at all, or is seeded once and then kept out of the write set (`.wikitool-kb.json`, `CHANGES.md`) |
| Machinery (a `CONTRACT.md`, anything under `tools/`, `types/`, `instructions/`, `AGENTS.md`, and every `<name>.template` beside an owned file) | It should not have local changes at all. Take the release's version: `--take-release <path>`, one per file |
| Machinery this instance changed **on purpose** | `--keep-local` keeps every listed file untouched - but the new stamp records the release digest anyway, so the same file is reported again at every future upgrade. That is the right answer only for a difference the instance intends to carry indefinitely |
The decision is per path, and the two flags compose - which is what a mixed report needs, one
file reset and another kept. Preview it before it writes:
```bash
tools/wikitool dist upgrade --latest --expect <version from step 2> --dry-run --take-release <path> [--take-release <path>]
```
The preview marks every named path as one it would overwrite from the release, and a path that
is not actually in the locally-changed list is refused *here* rather than in the writing run.
Nothing else is needed: no copy out of the unpacked tarball by hand, and no commit made only
to satisfy the next command's clean-tree precondition. Carry the flags you settled on into
step 7.
**Where `--keep-local` answers for some paths and `--take-release` for others, both go on the
same call.** Without `--keep-local`, a locally changed path that no `--take-release` names
still aborts the run: every one of them has to be answered for, and the abort's own text
names the three answers with the command line already filled in.
7. **Swap the machinery**, with whatever step 6 settled on. Note the commit the instance is on
first - step 13 compares against it:
```bash
git rev-parse --short HEAD # the pre-swap commit; keep it
tools/wikitool dist upgrade --latest --expect <version from step 2> [--take-release <path>] [--keep-local]
```
It writes, and commits nothing.
8. **Run the preflight, then republish the skills.** The release may need other tools or
other libraries than the one it replaced, and `tools/wikitool` refuses to start (exit 42)
until the preflight has passed against the new `tools/` - an instance upgrading from a
release without one has never run it at all. On its exit 42, show the output verbatim and
wait ([preflight.md](preflight.md)):
```bash
tools/preflight.sh
tools/wikitool instructions sync
```
From PowerShell 7 on Windows, run the twin instead - same questions, same file:
```powershell
pwsh -NoProfile -ExecutionPolicy Bypass -File tools/preflight.ps1
tools/wikitool instructions sync
```
`instructions sync` is needed because the published skill directories are copies: until it
runs, the harness is still offering the previous release's skills.
9. **Verify the machinery, and fix what the release said would need fixing:**
```bash
tools/wikitool doctor
tools/wikitool docs verify
tools/wikitool instructions verify
tools/wikitool lint
```
A `docs verify` failure naming a missing or stale table of contents is repaired with
`tools/wikitool docs toc --apply`, never by hand - a release that widened the set of files
carrying a region will produce exactly that on files this instance adopted before the
widening.
A failure naming a page type the stack requires, or the collection that type's `base_dir:`
points at, is the other repairable shape - the `new` template from step 5 that nobody adopted.
The fix is the ordinary adoption every `root: kb` type already needs, not a data migration:
copy the shipped templates to their unsuffixed names, then fill the instance-owned parts
(language, template text, any extra fields) the way the authoring-conventions step of
[setup-instance.md](setup-instance.md) describes for a fresh instance.
```bash
tools/wikitool dist adopt types/<name>.md.template types/<name>.schema.yaml.template kb/<collection>/COLLECTION.md.template
```
`dist adopt` copies only what does not exist yet, so a file this instance already adopted
and filled is never touched. The `.template` files stay where they are - they are the source for the next upgrade's
comparison. Any other failure is read against step 2's **Breaking Change:** line: if the
release predicted it, the notes also say what fixes it; if it did not, stop and report it
rather than improvising.
10. **Publish the machinery swap.** A release swap is far above the Mass-Update Gate's threshold,
so expect exit 42. That is not an error and not yours to clear: reproduce the file breakdown
it prints for the operator, stop, and publish with the token it named once they have
approved it. See [gates.md](gates.md).
Publishing here, before the content migrations, is deliberate. The intermediate state -
new machinery, content still at the old shape - is a state the stack names rather than
avoids (`.wikitool-kb.json` records it), and it keeps a 200-file swap out of the same commit
as a content rewrite.
11. **Restart the agent session.** Everything the previous steps replaced - `AGENTS.md`, the
contracts, the type-specs, the skills - is still in the running session's context in its
*old* form. A migration document written against a rule that arrived in this release will
otherwise be carried out against the rule it replaced, and nothing checks that.
The new session resumes at step 12. `tools/wikitool migrate status` is the resume point:
it is stateful, so it says what is left without being told what already happened.
12. **Work the migration chain.** `tools/wikitool migrate status` lists what is outstanding, in
the order it has to run - a jump across several releases lists several. For each one, run
the named document under `instructions/migrations/` following
[migrate-corpus.md](migrate-corpus.md), then record it:
```bash
tools/wikitool migrate done <version>
```
An `offered` migration is a separate decision, not part of the chain: it changes a file this
instance owns, blocks nothing, and recording it does not move `kb_version`. Take it or
decline it deliberately; both are correct answers.
**Whatever the migration changes, capture the before.** Where a document asks that some
command's output "read the same as before", that is only checkable if the before was written
down - redirect it to a file first and `diff` afterwards, rather than reading two long
outputs from memory. Reading either one through `head` or `tail` is how a difference in the
middle survives the check.
13. **Verify the content, then publish.** Only after the chain has run, and against the commit
noted in step 7:
```bash
tools/wikitool migrate verify --from <pre-swap commit>
tools/wikitool lint
```
`migrate verify` is the only check that sees a page which lost a citation, a wikilink or a
generated-region marker in the rewrite - `lint` reports a corpus that is internally
consistent, which a corpus that quietly lost something still is. Then publish, the same way
as in step 10.
## Decision points
- **`version check` reports `state: migration` (a compatibility boundary)?** That is a statement
about the machinery being a drop-in replacement, not about the corpus. A boundary crossing with
an empty migration chain is normal and means the hand-work is elsewhere - which is precisely
what step 2's **Breaking Change:** line names.
- **`dist upgrade` refuses because the tree is not clean?** Commit or stash what is there first,
and look at what it is: work in progress is committed through `publish`, an editor's stray
reformatting of machinery is step 6's case.
- **A required migration cannot be completed now?** Stop after step 10 and leave it. The
intermediate state is legitimate and `migrate status` resumes it; what is not legitimate is
recording a migration with `migrate done` that was not carried out - the version then describes
a shape the corpus is not in.
- **A step fails and the cause is not obvious?** Stop rather than improvise, and offer the user a
bug report - [bug-report.md](bug-report.md). It runs even when `wikitool` does not start, and
only when the user agrees.
- **`doctor` reports `kb-version` behind `VERSION` after everything is done?** Correct when the
release's chain was empty or carried only `offered` entries: an offer changes a file the
instance owns, not the shape of its content, so the content version stays where it was.
## Scope
For an instance installed from a release. Not the origin repo, which has no upgrade path of its
own - see the second paragraph. Anything about
*writing* a migration document rather than running one is
[migrate-corpus.md](migrate-corpus.md) § "Writing the migration document".
What a human decides before any of this starts - which release, whether to take it at all, where
the tarball comes from - is [INSTALL.md](../INSTALL.md) § "Version und Updates".