feat!: installation only from a release, into an empty folder; upstream merge/verify and private-instance.md removed, dist adopt, shell-neutral instructions (#153)
Files changed: - .gitea/workflows/ci.yml - .gitea/workflows/release.yml - AGENTS.md - CHANGES.md - DEVELOPMENT.md - EVALS.md - INSTALL.md - README.md - VERSION - docs/ownership-and-templates.md - instructions/CONTRACT.md - instructions/bootstrap.md - instructions/dev/dev-setup.md - instructions/dev/stack-dev/SKILL.md - instructions/gates.md - instructions/ingest-large-tree.md - instructions/kb-profiles.md - instructions/mcp-read-server.md - instructions/migrations/3.0.0-authoring-conventions.md - instructions/preflight.md - instructions/private-instance.md - instructions/session-setup.md - instructions/setup-instance.md - instructions/upgrade-instance.md - tools/CONTRACT.md - tools/README.md - tools/chemenu/cli.py - tools/chemenu/cli_contract.py - tools/chemenu/commands/dist_cmd.py - tools/chemenu/commands/docs_verify.py - tools/chemenu/commands/doctor.py - tools/chemenu/commands/git_publish.py - tools/chemenu/commands/upstream_cmd.py - tools/chemenu/commands/work_cmd.py - tools/chemenu/config.py - tools/chemenu/ownership.py - tools/chemenu/tests/test_cli.py - tools/chemenu/tests/test_dist_cmd.py - tools/chemenu/tests/test_instructions_shell.py - tools/chemenu/tests/test_preflight.py - tools/chemenu/tests/test_preflight_pwsh.py - tools/chemenu/tests/test_run_budget.py - tools/chemenu/tests/test_upstream_cmd.py - tools/chemenu/toc.py - tools/preflight.ps1 - tools/preflight.sh
This commit is contained in:
1 parent
d0f08d1fba
commit
a6d07f97c4
46 files changed
+1314
-1936
No files matched your search
@@ -179,6 +179,18 @@ Scaffold with `tools/wikitool new instruction --name "<name>"`; the contract is
|
||||
at all. If that is worth preserving, it is a concept page under `kb/concepts/`, linked from
|
||||
here. Where the line runs, and how to test a passage against it: below.
|
||||
- **State scope boundaries.** When does this *not* apply, and what to do instead.
|
||||
- **A command block reads the same in every shell.** Depending on the harness, an instruction
|
||||
runs under bash, Git Bash or PowerShell 7. A command in a fenced block is a `tools/wikitool`
|
||||
or `git` call, or the preflight's own call per platform - never syntax only one shell reads: no
|
||||
heredoc, no `export`, no `$(...)` or `$VAR`, no inline `VAR=value command`, no `&&`, no `for`
|
||||
loop, no `cp`, `cat >`, `sha256sum`, `curl` or `tar`. A step that needs one of them gets a
|
||||
`wikitool` command instead, or leaves the file work to the agent's own file tools. Two places
|
||||
are exempt, each with one line per shell: setting the session id
|
||||
([session-setup.md](session-setup.md)) and downloading the preflight before an instance exists
|
||||
([setup-instance.md](setup-instance.md) step 0). Migration documents under
|
||||
`instructions/migrations/` belong to the release they shipped with and are not rewritten. The
|
||||
stack's own instructions are held to this by a test in the origin repository; what an instance
|
||||
writes for itself is its own decision.
|
||||
- **Write it in English, and let the agent speak the instance's language.** Both rules, and the
|
||||
line between prose and quoted vocabulary, are stated once in
|
||||
[AGENTS.md § File naming](../AGENTS.md#file-naming). They are named here because this is the
|
||||
|
||||
@@ -1,11 +1,15 @@
|
||||
---
|
||||
type: types/instruction.md
|
||||
name: bootstrap
|
||||
description: Prepare a fresh clone for work - run the preflight (tool paths and the tools venv) and publish the skills into the harness directories, which are generated and not committed.
|
||||
description: Prepare a fresh clone of an existing instance (a second machine, a new checkout) for work - run the preflight (tool paths and the tools venv) and publish the skills into the harness directories, which are generated and not committed.
|
||||
---
|
||||
|
||||
# Bootstrap a fresh clone
|
||||
|
||||
An instance lives in its own git repository, so a second machine - or a new checkout on the same
|
||||
one - gets it with `git clone`. What the clone does not carry is everything that describes one
|
||||
machine rather than the instance: the tool paths and the tools venv, and the published skills.
|
||||
|
||||
`.agents/skills/` and `.claude/skills/` are generated copies of the skill directories under
|
||||
`instructions/`, and both are gitignored. A fresh clone therefore has no skills at all until
|
||||
they are published: the agent harness will not offer `wiki-ingest`, `wiki-query`,
|
||||
@@ -13,7 +17,7 @@ they are published: the agent harness will not offer `wiki-ingest`, `wiki-query`
|
||||
|
||||
## When to run
|
||||
|
||||
- After cloning the repository.
|
||||
- After cloning the instance's repository.
|
||||
- After `instructions/<name>/SKILL.md` is added, renamed, or edited.
|
||||
- Whenever `tools/wikitool instructions verify` reports a missing or drifted copy.
|
||||
|
||||
@@ -53,7 +57,7 @@ they are published: the agent harness will not offer `wiki-ingest`, `wiki-query`
|
||||
4. **Check for personalization.** A clone predating the personalization files has no
|
||||
`USER.md`/`SOUL.md`, and `tools/wikitool doctor` reports `personalization: FAIL` for it.
|
||||
That is a one-off catch-up, not a bootstrap step that repeats: run **only** the
|
||||
Personalization step (6) of [setup-instance.md](setup-instance.md), not the whole
|
||||
personalization step (5) of [setup-instance.md](setup-instance.md), not the whole
|
||||
procedure - this clone already has its git repo, author identity and content. A clone that
|
||||
already carries both files needs nothing here.
|
||||
|
||||
@@ -86,6 +90,6 @@ This does not apply to anything under `kb/`, `raw/` or `reports/`; those are com
|
||||
present immediately after a clone. If the wiki content looks wrong after cloning, that is a
|
||||
lint question, not a bootstrap one.
|
||||
|
||||
This also does not apply to a fresh instance created via `tools/wikitool dist export` - it has
|
||||
no git history, no author identity, and no generated indexes yet. That is
|
||||
[setup-instance.md](setup-instance.md), a longer procedure this one is a single step of.
|
||||
This also does not apply to a new instance installed from a release - it has no git history, no
|
||||
author identity, and no generated indexes yet. That is [setup-instance.md](setup-instance.md), a
|
||||
longer procedure this one is a single step of.
|
||||
@@ -0,0 +1,73 @@
|
||||
---
|
||||
type: types/instruction.md
|
||||
name: dev-setup
|
||||
description: Set up a clone of the origin repository for stack development - preflight, skills, the demo corpus and its persona, telemetry on - and use dist export as a build and test tool, never as a way to install an instance.
|
||||
---
|
||||
# Set up a development checkout of the stack
|
||||
|
||||
A clone of the origin repository is where the stack is developed. It is not an instance: it
|
||||
carries a demo corpus that documents the stack itself, a demo persona in `USER.md`/`SOUL.md`, the
|
||||
development material under `instructions/dev/` and `commonplace/`, and no
|
||||
`.wikitool-release.json`. Instances are installed from releases
|
||||
([setup-instance.md](../setup-instance.md)); nothing here produces one.
|
||||
|
||||
## When to run
|
||||
|
||||
- A fresh clone of the origin repository, before the first stack-dev session in it.
|
||||
- A clone that was moved, or whose `tools/.venv` was removed - only step 2 again.
|
||||
- Before testing a change to the install path itself (`setup-instance.md`, the preflight, the
|
||||
release workflow) - step 5.
|
||||
|
||||
## Steps
|
||||
|
||||
1. **Clone** the origin repository. The tree is complete as checked out: `kb/`, `raw/`,
|
||||
`USER.md`, `SOUL.md` and the filled `kb/CONVENTIONS.md` are committed here, unlike in an
|
||||
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.
|
||||
|
||||
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
|
||||
session reads it instead of asking.
|
||||
|
||||
4. **Know what differs from an instance before relying on a default.**
|
||||
|
||||
| Here | In an instance |
|
||||
|---|---|
|
||||
| No `.wikitool-release.json`: telemetry is **on** - the traces are the stack's measuring instrument (`EVALS.md` § "Whether it runs at all") | Telemetry is off until the operator turns it on |
|
||||
| `USER.md`/`SOUL.md` describe a demo operator and persona | Written by the operator during setup |
|
||||
| `tools/wikitool dist upgrade` refuses: there is no stamp to compare against. The checkout follows `main` with `tools/wikitool sync` | Updated with `dist upgrade --latest` |
|
||||
| `instructions/dev/`, `commonplace/`, `DEVELOPMENT.md` and `.gitea/` are present | Never shipped |
|
||||
|
||||
5. **Use `dist export` as a build and test tool.** It writes exactly the tree a release ships, so
|
||||
it is how a change to the shipped surface is looked at before it is released:
|
||||
|
||||
```bash
|
||||
tools/wikitool dist export <empty scratch folder> --dry-run
|
||||
tools/wikitool dist export <empty scratch folder>
|
||||
```
|
||||
|
||||
To replay the install path the way a user meets it, build the release tarball from that tree
|
||||
the way `.gitea/workflows/release.yml` does (one top-level folder, a `.sha256` beside it) and
|
||||
start the tree's `tools/preflight.sh` as the asset, from an empty folder, with
|
||||
`--archive <tarball>` - the step "The distribution works as a fresh instance" in
|
||||
`.gitea/workflows/ci.yml` is that replay and the reference for it. Keep scratch trees outside
|
||||
this checkout.
|
||||
|
||||
## Decision points
|
||||
|
||||
- **Asked to set up an instance from this checkout** (`dist export` into the user's folder, or a
|
||||
clone that "becomes" their wiki)? Neither is an install path. An instance is installed from a
|
||||
release, by [setup-instance.md](../setup-instance.md); a stack state that has no release yet is
|
||||
released first, or tested with the replay in step 5 and thrown away.
|
||||
- **The demo corpus is in the way of a test?** Do not delete or rewrite corpus content to make
|
||||
room: [corpus-policy.md](corpus-policy.md) says what may be changed and how. Use a scratch
|
||||
export (step 5) for a clean tree instead.
|
||||
|
||||
## Scope
|
||||
|
||||
Not for operating an instance, and not for the release workflow itself (`DEVELOPMENT.md` for
|
||||
humans, [version-parts.md](version-parts.md) for the version part). Not shipped: `dist export`
|
||||
prunes `instructions/dev/` wholesale.
|
||||
@@ -67,6 +67,10 @@ stack development happens in the origin repo instead (see AGENTS.md's routing li
|
||||
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
|
||||
|
||||
+7
-29
@@ -25,7 +25,6 @@ Read the exit code first - it says which of these applies:
|
||||
- [Exit 42: user clearance required](#exit-42-user-clearance-required)
|
||||
- [Publish-Remote Gate](#publish-remote-gate)
|
||||
- [Upload Review Gate](#upload-review-gate)
|
||||
- [Mass-Update Gate blind spot: `upstream merge`](#mass-update-gate-blind-spot-upstream-merge)
|
||||
- [Iteration Budget Gate and loop-breaker](#iteration-budget-gate-and-loop-breaker)
|
||||
- [Taking a new session id](#taking-a-new-session-id)
|
||||
- [Scope](#scope)
|
||||
@@ -81,8 +80,8 @@ Background: the [[Mass-Update Gate]] concept page in `kb/`.
|
||||
The Mass-Update Gate asks whether a change is too large to publish. This one asks the question
|
||||
underneath it: **whether this is the right repository to publish to at all.**
|
||||
|
||||
A checkout that holds private content usually has two remotes - its own, and the public upstream
|
||||
it takes stack updates from. Git does not distinguish them at push time, so one wrong `--remote`
|
||||
A checkout that holds private content can have two remotes - its own, and a public one it also
|
||||
works against. Git does not distinguish them at push time, so one wrong `--remote`
|
||||
puts a private corpus on a public repository. That is not cheaply reversible: a force-push moves
|
||||
the branch, but the objects stay fetchable by SHA until someone expires the server's reflogs and
|
||||
runs `git gc --prune=now` on the bare repo.
|
||||
@@ -98,8 +97,8 @@ It pins **URLs, not remote names** - a name-based list would wave through a `pub
|
||||
`pushurl` when one is set, because that is where `git push` actually writes.
|
||||
|
||||
The file is per-checkout and gitignored, for the same reason `ENVIRONMENT.md` is: two clones push
|
||||
to two different places, so a committed copy would tell a private clone that the public upstream
|
||||
is a legitimate target for its own content. **Absent means unrestricted** - a single-remote
|
||||
to two different places, so a committed copy would tell a private clone that a public remote is a
|
||||
legitimate target for its own content. **Absent means unrestricted** - a single-remote
|
||||
checkout with nothing private in it has nothing to protect, and `doctor` reports which state a
|
||||
checkout is in, WARNing only when there is more than one remote and no allowlist. A malformed
|
||||
file is an error rather than "no restriction": a corrupted safeguard must not read as a disabled
|
||||
@@ -112,9 +111,8 @@ is a standing property of the checkout, not a per-push judgment. The way past it
|
||||
to add the URL to the file. **An agent must never edit `.wikitool-remotes.json` to get past a
|
||||
refusal** - that is opening a gate on your own initiative, which AGENTS.md invariant 6 forbids.
|
||||
|
||||
The setup this gate exists for - a private instance that takes stack updates from a public
|
||||
upstream - is [private-instance.md](private-instance.md). Step 4 there arms it, deliberately
|
||||
*before* the first `publish`: added afterwards it leaves open exactly the window it closes.
|
||||
Arm it *before* a second remote is added and before the first `publish` to it: added afterwards
|
||||
it leaves open exactly the window it closes.
|
||||
|
||||
### Upload Review Gate
|
||||
|
||||
@@ -135,26 +133,6 @@ Mass-Update Gate's review report versus this file's exit-42 procedure.
|
||||
all** - rejecting needs no clearance, only accepting a stranger's file into the pipeline does. It
|
||||
deletes the material and keeps only the reason and a sha256 in `mcp-upload/ledger.jsonl`.
|
||||
|
||||
### Mass-Update Gate blind spot: `upstream merge`
|
||||
|
||||
`upstream merge` (a private instance taking a stack update - see
|
||||
[private-instance.md](private-instance.md)) can update or delete dozens of stack-owned paths in
|
||||
one commit, and the Mass-Update Gate does not see any of it. The gate counts the *uncommitted*
|
||||
changes `publish` is about to stage; by the time `upstream merge` commits, the change is
|
||||
already history, and the commit it made is not what a later `publish` would be staging - that
|
||||
publish sees only whatever this session adds on top. A merge touching 200 files therefore goes
|
||||
out ungated the moment it is pushed.
|
||||
|
||||
This is not a hole to patch by making `upstream merge` route through the gate: the gate's
|
||||
question ("is this too much to publish?") does not apply to a change that only ever touches
|
||||
stack-owned paths that are, by definition, not this instance's own content. The check that
|
||||
actually matters here is `upstream merge`'s own postcheck - it re-verifies the merge commit
|
||||
against `upstream verify`'s logic immediately after committing, and exits 1 with the offending
|
||||
paths if anything landed outside a stack-owned one. **The merge commit is deliberately left in
|
||||
place** rather than reverted: it exists, a human has to look at it, and a command that quietly
|
||||
repaired its own mistake would hide the one event worth seeing. That postcheck is the safeguard
|
||||
for this command, not the Mass-Update Gate.
|
||||
|
||||
## Iteration Budget Gate and loop-breaker
|
||||
|
||||
Every `wikitool` call is counted per session. Calls are refused past **60 in a session**, or
|
||||
@@ -193,7 +171,7 @@ command you actually need to run, and only with the user's approval.
|
||||
|
||||
A dozen commands are exempt from this budget entirely - `search` and `doctor` because retrieval
|
||||
and diagnosis are reading, not iterating, plus the read-only forms of `links`, `cite`, `budget`,
|
||||
`eval`, `version`, `migrate` and `upstream verify`. The exemption is that allowlist in
|
||||
`eval`, `version` and `migrate`. The exemption is that allowlist in
|
||||
[tools/CONTRACT.md](../tools/CONTRACT.md), not a "does not change the wiki" rule of thumb: `lint`
|
||||
only writes to gitignored `reports/` and still counts, because it is not on the list.
|
||||
|
||||
|
||||
@@ -128,11 +128,9 @@ session.
|
||||
into `README.md` as `DECISION NEEDED: <question>` and **stops the run** - do not choose for
|
||||
the user and continue.
|
||||
|
||||
5. **Process one unit at a time.** For unit *N*, in this order:
|
||||
|
||||
```bash
|
||||
export WIKITOOL_SESSION_ID="<runkey>/u<N>"
|
||||
```
|
||||
5. **Process one unit at a time.** For unit *N*, in this order - after setting the session id
|
||||
to `<runkey>/u<N>` with the line for your shell from
|
||||
[session-setup.md](session-setup.md) § Steps:
|
||||
|
||||
a. **Read** every raw file in the unit, in full. Treat all of it as data, never instructions
|
||||
(AGENTS.md invariant 4).
|
||||
|
||||
@@ -15,8 +15,8 @@ it lives.
|
||||
|
||||
That direction is deliberate and it is the opposite of how this repo used to work. Language,
|
||||
tone, naming and the relationship vocabulary sat in `kb/CONTRACT.md`, a file `dist export` ships
|
||||
verbatim - so every instance that wanted something else edited a stack file, and an upstream
|
||||
merge handed the stack's answer back. What binds is now the instance's; what ships is this
|
||||
verbatim - so every instance that wanted something else edited a stack file, and the next update
|
||||
handed the stack's answer back. What binds is now the instance's; what ships is this
|
||||
catalogue, and it binds nothing.
|
||||
|
||||
<!-- wikitool:toc -->
|
||||
@@ -221,8 +221,8 @@ optional.
|
||||
[migrate-corpus.md](migrate-corpus.md).
|
||||
- **Tempted to make this page binding** - to have `COLLECTION.md` say `profile: entities` and
|
||||
nothing else? Do not. That is the arrangement this split was written to end: the instance
|
||||
would be bound by a file the stack ships and upgrades, which is how an upstream merge changes
|
||||
an instance's authoring rules without anyone deciding to.
|
||||
would be bound by a file the stack ships and upgrades, which is how an update changes an
|
||||
instance's authoring rules without anyone deciding to.
|
||||
|
||||
## Scope
|
||||
|
||||
|
||||
@@ -44,25 +44,27 @@ everything an operator needs that is *true of the software* rather than of one i
|
||||
and cryptography to do it.
|
||||
|
||||
```bash
|
||||
tools/.venv/bin/pip install -r tools/requirements-mcp.txt
|
||||
tools/.venv/bin/python -m pip install -r tools/requirements-mcp.txt
|
||||
```
|
||||
|
||||
2. **Decide which checkout it serves.** The root resolves by precedence - an explicit `--root`,
|
||||
then `$CHEMENU_ROOT`, then the checkout the package lives in. A deployment points at its
|
||||
corpus with one variable and no code:
|
||||
then `CHEMENU_ROOT`, then the checkout the package lives in. A deployment points at its
|
||||
corpus with one variable and no code, set in the environment the server process starts in
|
||||
(its service unit or container spec):
|
||||
|
||||
```bash
|
||||
export CHEMENU_ROOT=/srv/chemenu
|
||||
```
|
||||
| Variable | Value |
|
||||
|---|---|
|
||||
| `CHEMENU_ROOT` | The checkout it serves, for instance `/srv/chemenu` |
|
||||
|
||||
3. **Take tracing out of the served tree.** The server refuses to start otherwise, and the
|
||||
refusal is the point: telemetry defaults to on and writes under `reports/telemetry/` inside
|
||||
the repo, which step 5's sync is entitled to wipe. Either is fine:
|
||||
the repo, which step 5's sync is entitled to wipe. Set one of the two in the same
|
||||
environment:
|
||||
|
||||
```bash
|
||||
export WIKI_TRACE=0 # off
|
||||
export WIKI_TRACE_DIR=/var/log/chemenu # or elsewhere, outside the corpus
|
||||
```
|
||||
| Variable | Value |
|
||||
|---|---|
|
||||
| `WIKI_TRACE` | `0` - tracing off |
|
||||
| `WIKI_TRACE_DIR` | A directory outside the corpus, for instance `/var/log/chemenu` |
|
||||
|
||||
4. **Start it on the transport that matches what is in front of it.**
|
||||
|
||||
@@ -80,11 +82,11 @@ everything an operator needs that is *true of the software* rather than of one i
|
||||
5. **Keep the checkout current by polling, and keep it clean.**
|
||||
|
||||
```bash
|
||||
git -C "$CHEMENU_ROOT" fetch --quiet origin && \
|
||||
git -C "$CHEMENU_ROOT" reset --hard --quiet origin/main
|
||||
git -C <checkout> fetch --quiet origin
|
||||
git -C <checkout> reset --hard --quiet origin/main
|
||||
```
|
||||
|
||||
Every few minutes, from a timer beside the server. Polling rather than a webhook on purpose:
|
||||
The second only after the first succeeded, every few minutes, from a timer beside the server. Polling rather than a webhook on purpose:
|
||||
it needs no inbound endpoint and no signature checking, which is a smaller surface than the
|
||||
thing it would optimize. A webhook is a later optimization, not a starting point.
|
||||
|
||||
|
||||
@@ -56,9 +56,9 @@ other, so run this before the next `wiki-ingest` or `wiki-manage`, not afterward
|
||||
cp <unpacked-release>/kb/CONTRACT.md kb/CONTRACT.md
|
||||
```
|
||||
|
||||
A private instance cloned from an upstream takes it with the merge instead - see
|
||||
[private-instance.md](../private-instance.md), whose update procedure now re-takes the
|
||||
upstream side for exactly this path.
|
||||
A private instance cloned from an upstream took it with the merge instead - through the
|
||||
private-instance procedure and its `upstream merge`, which re-took the upstream side for
|
||||
exactly this path until both were removed in 8.0.0.
|
||||
|
||||
2. **Write `kb/CONVENTIONS.md`.** Two ways in, and the first is almost always right:
|
||||
|
||||
|
||||
+15
-11
@@ -100,16 +100,19 @@ It is safe to run at any time: a second run on a ready checkout changes nothing
|
||||
## Decision points
|
||||
|
||||
- **The script is a release asset, not a tree script** - there is no `tools/prerequisites.txt`
|
||||
beside it, because the user downloaded `preflight.sh` or `preflight.ps1` from a release page
|
||||
into an empty folder. That is the *first* install, and the script does one more thing before
|
||||
the steps above: it downloads the release tarball and its `.sha256`, refuses unless the
|
||||
checksum matches, and unpacks into a `chemenu/` folder next to itself; then it runs the
|
||||
preflight of the unpacked tree, passing `--set` and its exit code through. Run it exactly as
|
||||
in step 1 (the path is the downloaded file, not `tools/...`), and read the exit code the same
|
||||
way. After it, every later run - including the retry after an exit 42 - is the tree's own
|
||||
`tools/preflight.sh` or `tools/preflight.ps1`, run from inside `chemenu/`.
|
||||
- `--into <path>` unpacks somewhere else; an existing target is refused with exit 1 and
|
||||
nothing is touched, which is the user's decision to make, not yours to resolve by deleting.
|
||||
beside it, because it was downloaded from a release into the empty folder the wiki is to live
|
||||
in ([setup-instance.md](setup-instance.md) step 0). That is the *first* install, and the
|
||||
script does one more thing before the steps above: it downloads the release tarball and its
|
||||
`.sha256`, refuses unless the checksum matches, unpacks the stack into its own folder and
|
||||
removes itself there; then it runs the preflight of the installed tree, passing `--set` and
|
||||
its exit code through. Run it exactly as in step 1 (the path is the downloaded file, not
|
||||
`tools/...`), and read the exit code the same way. After it, every later run - including the
|
||||
retry after an exit 42 - is the tree's own `tools/preflight.sh` or `tools/preflight.ps1`.
|
||||
- The folder has to be empty apart from the script and a `.git` (an empty clone of the
|
||||
instance's own repository). Anything else is refused with exit 1 and nothing is touched -
|
||||
which folder to use is the user's decision, not yours to resolve by deleting.
|
||||
- `--into <path>` installs into another folder, under the same rule; the script then stays
|
||||
where it is.
|
||||
- `--archive <tarball>` uses a tarball already on disk, with its `<tarball>.sha256` beside it,
|
||||
when the machine cannot download.
|
||||
- A checksum that does not match, a failed download, and a copy of the script that carries no
|
||||
@@ -122,7 +125,8 @@ It is safe to run at any time: a second run on a ready checkout changes nothing
|
||||
install folder may be at most 95 characters, because every file of the wiki below it has to
|
||||
stay within 259. Moving the wiki to a shorter folder is the user's step; do not try to shorten
|
||||
paths inside the wiki instead. In asset mode the length is judged at the folder the stack
|
||||
*would* be unpacked into, before anything is unpacked; the fix is a shorter `--into`.
|
||||
*would* be unpacked into, before anything is unpacked; the fix is a shorter folder (the
|
||||
script downloaded there again, or a shorter `--into`).
|
||||
- **The output names the PowerShell execution policy** (`Restricted` or `AllSigned`). The fix is a
|
||||
line the user runs in a PowerShell 7 window; it changes a setting of their account, so it is
|
||||
theirs to run. When a *group policy* sets it, nothing on this computer can override it: the
|
||||
|
||||
@@ -1,212 +0,0 @@
|
||||
---
|
||||
type: types/instruction.md
|
||||
name: private-instance
|
||||
description: Set up a private working instance as a clone of a public upstream, so stack updates arrive by merge instead of by copying a tarball over the tree.
|
||||
---
|
||||
|
||||
# Set up a private instance against a public upstream
|
||||
|
||||
The distribution path in [setup-instance.md](setup-instance.md) builds an instance from a
|
||||
`dist export` tarball, with no git ancestry in common with the repo it came from. That is the
|
||||
right shape for someone who only ever *consumes* the stack.
|
||||
|
||||
This is the other shape: a private instance that keeps taking stack changes from a public
|
||||
upstream, and whose own content must never travel back. It costs one safeguard to set up and
|
||||
saves the whole update procedure afterwards.
|
||||
|
||||
**Read this before, not after, the first `publish`.** The gate in step 4 is the thing that makes
|
||||
the arrangement safe, and adding it later means the window it closes was open in between.
|
||||
|
||||
<!-- wikitool:toc -->
|
||||
## Contents
|
||||
|
||||
- [Why a clone rather than a tarball](#why-a-clone-rather-than-a-tarball)
|
||||
- [Steps](#steps)
|
||||
- [Taking a stack update](#taking-a-stack-update)
|
||||
- [Where stack development happens](#where-stack-development-happens)
|
||||
- [Decision points](#decision-points)
|
||||
- [Scope](#scope)
|
||||
<!-- /wikitool:toc -->
|
||||
|
||||
## Why a clone rather than a tarball
|
||||
|
||||
`INSTALL.md`'s "Eine Instanz aktualisieren" is `cp -r` as an upgrade strategy: copy `tools/`,
|
||||
`types/`, `instructions/`, `AGENTS.md`, `VERSION` over the existing tree. It has no three-way
|
||||
merge, so it cannot notice that the receiving instance changed a file, and it has no conflict
|
||||
surface, so nobody learns when upstream and local both touched the same one. It overwrites
|
||||
silently.
|
||||
|
||||
A clone gets all of that from git. Stack changes land as real merges, with real conflicts where
|
||||
they conflict.
|
||||
|
||||
**What a plain `git merge` does *not* give you is protection from the upstream's content.** The
|
||||
private `main` deletes the demo corpus once, but that deletion does not make later upstream
|
||||
changes to those paths go away. Measured, not assumed:
|
||||
|
||||
| Upstream does | `git merge upstream/main` does |
|
||||
|---|---|
|
||||
| modifies a page you deleted | `CONFLICT (modify/delete)` - and **leaves the upstream version in your working tree**. Resolve it with `git add -A` and the demo page is back. |
|
||||
| adds a new page | stages it **silently**. No conflict, no prompt, no mention. |
|
||||
| deletes a page you also deleted | nothing. The only harmless case. |
|
||||
|
||||
The middle row is the one that matters, because nothing announces it. An upstream that ships a
|
||||
demo corpus *and* uses it as a test bed will add pages, and each one arrives in your instance
|
||||
and starts showing up in your `lint`, your `index` and your `search`.
|
||||
|
||||
So the merge has to be scoped. That is the procedure below, and it is not optional.
|
||||
|
||||
## Steps
|
||||
|
||||
1. **Clone, and name the two remotes for what they are.**
|
||||
|
||||
```bash
|
||||
git clone <private-repo-url> my-wiki
|
||||
cd my-wiki
|
||||
git remote add upstream <public-repo-url>
|
||||
```
|
||||
|
||||
`origin` is yours and is the only thing you ever push to. `upstream` is where stack updates
|
||||
come from and is fetch-only.
|
||||
|
||||
2. **Make the fetch-only half fetch-only in git, too.**
|
||||
|
||||
```bash
|
||||
git remote set-url --push upstream no_push
|
||||
```
|
||||
|
||||
git refuses to push to a URL it cannot resolve. This is a convenience, not the safeguard -
|
||||
step 4 is the safeguard.
|
||||
|
||||
3. **Delete the upstream's demo corpus once, on your own `main`.**
|
||||
|
||||
Everything under `kb/` and `raw/` that came with the clone is the upstream's content, not
|
||||
yours. Remove it with `wikitool rm --page` (never `rm -rf`: `rm` de-links each page from the
|
||||
rest of the wiki, and a plain delete leaves dead wikilinks and broken citations behind), then
|
||||
`index rebuild`, `sources rebuild-index`, `lint`.
|
||||
|
||||
This is a one-time cut. Afterwards the upstream corpus is frozen from your side, which is
|
||||
what makes later merges content-free.
|
||||
|
||||
4. **Arm the Publish-Remote Gate — before the first `publish`.**
|
||||
|
||||
```bash
|
||||
cat > .wikitool-remotes.json <<'EOF'
|
||||
{ "schema": 1, "allowed_push_urls": ["<your-private-push-url>"] }
|
||||
EOF
|
||||
```
|
||||
|
||||
Use the URL `git remote get-url --push origin` prints, exactly. `publish` refuses with exit
|
||||
42 for anything else, and there is no flag that opens it - see [gates.md](gates.md).
|
||||
|
||||
The file is gitignored, so it stays with this checkout and never travels to the upstream.
|
||||
`wikitool doctor` reports whether the gate is armed, and WARNs at more than one remote
|
||||
without it.
|
||||
|
||||
5. **Take away the write credential, if you can.** A token or deploy key for `origin` only,
|
||||
with no write access to the upstream, is the one control that holds even if everything above
|
||||
is misconfigured. Belt and braces.
|
||||
|
||||
6. **Personalize and bootstrap.** `USER.md`, `SOUL.md` and optionally `ENVIRONMENT.md` are
|
||||
yours and unrelated to the upstream's - see the Personalization step of
|
||||
[setup-instance.md](setup-instance.md), then [bootstrap.md](bootstrap.md) for the venv and
|
||||
the skills.
|
||||
|
||||
A clone inherits the upstream's `kb/CONVENTIONS.md` and `kb/*/COLLECTION.md` rather than
|
||||
templates, because it inherits the upstream's whole tree. They are yours from this point on:
|
||||
rewrite them if this instance writes its pages differently - the update procedure below
|
||||
restores them on every merge, so the change sticks. [kb-profiles.md](kb-profiles.md) has the
|
||||
alternatives.
|
||||
|
||||
## Taking a stack update
|
||||
|
||||
```bash
|
||||
tools/wikitool upstream merge --remote upstream --branch main
|
||||
```
|
||||
|
||||
Take the machinery, never the content. This is the command form of the same idea a hand-rolled
|
||||
merge would need: hold the merge open, force the content stages back to your own state, restore
|
||||
only the paths that are machinery, and only then let it close. Which paths those are is not a
|
||||
short literal list any more (see below) - it is `chemenu.ownership.is_stack_owned`, the same
|
||||
predicate `dist_cmd.py`'s export reads, so a stack change that adds a new machinery path under a
|
||||
content stage is recognised automatically rather than needing this document edited first.
|
||||
|
||||
**What counts as machinery under a content stage**, for readers who want the shape rather than
|
||||
the code:
|
||||
|
||||
| Path | Why it takes the upstream side |
|
||||
|---|---|
|
||||
| `<stage>/CONTRACT.md` (`kb/CONTRACT.md`, `raw/CONTRACT.md`, `work/CONTRACT.md`, `reports/CONTRACT.md`) | The stack's own stage contract. Every rule in it is enforced by `wikitool`; an instance never edits it |
|
||||
| any `*.template` under a content stage (`kb/CONVENTIONS.md.template`, each `kb/<name>/COLLECTION.md.template`, and any later one) | The template your filled file was adopted from. The filled file is yours; the template is the stack's |
|
||||
|
||||
Everything else under `kb/`, `raw/`, `work/` and `reports/` is yours, `kb/CONVENTIONS.md` and
|
||||
each `kb/<name>/COLLECTION.md` included - they bind your corpus, and they are exactly what
|
||||
`upstream merge` protects.
|
||||
|
||||
**Your local, uncommitted-by-design files under those stages survive.** Forcing a content stage
|
||||
back to your own state removes only what git tracks, never the directory wholesale - which
|
||||
matters because `reports/` is gitignored apart from its contract, so it holds data that is in no
|
||||
commit and cannot be recomputed: the telemetry traces `eval score` reads, saved eval reports,
|
||||
past lint reports. A merge has no business touching any of it, and does not.
|
||||
|
||||
The command itself checks its own result the same way `upstream verify` would, immediately
|
||||
after committing, and refuses loudly - without rolling the commit back - if anything landed
|
||||
outside a stack-owned path. A refusal there is a bug report, not something to work around by
|
||||
hand; see [tools/CONTRACT.md](../tools/CONTRACT.md) for the full error contract, including what
|
||||
a real conflict in `tools/`/`types/`/`instructions/` leaves behind.
|
||||
|
||||
Then, as after any stack change: `doctor`, `docs verify`, `instructions verify`, `migrate status`,
|
||||
`lint`. A `migrate status` with outstanding links means the update crossed a compatibility
|
||||
boundary - follow [migrate-corpus.md](migrate-corpus.md) before doing anything else.
|
||||
|
||||
**Why not just `git merge upstream/main`?** A page the upstream *adds* arrives with no conflict
|
||||
and no message under a plain merge - measured in the table further up this document. You would
|
||||
find out when `lint` starts reporting pages you never wrote, if you noticed at all. `upstream
|
||||
merge` closes exactly that gap: the content stages never see the upstream's version at all.
|
||||
|
||||
**Checking a merge you resolved by hand instead** (or auditing a past one): `tools/wikitool
|
||||
upstream verify --since <rev-before> --until <rev-after>` runs the same check `upstream merge`
|
||||
runs on itself, without doing the merge.
|
||||
|
||||
## Where stack development happens
|
||||
|
||||
**In the public repo, not here.** That is not a preference; the stack is built that way. The
|
||||
development-only half of the instruction layer is pruned from a distribution one-way, with no
|
||||
command that reconstructs it, so an instance built this way has no tool-development mode to
|
||||
switch into in the first place.
|
||||
|
||||
When a tool bug blocks real content work here - and it will - file the issue against the public
|
||||
repo (an MCP server or the web UI reaches it from any session; no shared history needed), fix it
|
||||
there where the tests, `docs verify` and CI's version gate live, and take the fix back with the
|
||||
merge above. Nothing is lost by the detour: the fix has to pass that CI either way.
|
||||
|
||||
## Decision points
|
||||
|
||||
- **Merge conflict in `kb/`, `raw/`, `work/` or `reports/`?** Expected, and already handled:
|
||||
`upstream merge` overwrites those stages with your own afterwards, so the conflict resolves
|
||||
itself. Never resolve one by hand with `git add -A` in a merge you are running yourself
|
||||
instead - that is exactly how the upstream version, which git left sitting in your working
|
||||
tree, gets committed into your instance.
|
||||
- **`upstream merge` exits 1 after committing?** Read the message: its own postcheck found
|
||||
content outside a stack-owned path in the commit it just made. The commit is **not** rolled
|
||||
back - inspect it (`git show`, or `tools/wikitool upstream verify --since <before> --until
|
||||
HEAD`) and decide by hand whether to revert it, fix forward, or report it as a stack bug. This
|
||||
should not happen; if it does, `chemenu.ownership.is_stack_owned` disagreed with itself between
|
||||
the restore and the check, which is exactly what the shared predicate is meant to prevent.
|
||||
- **Conflict in `tools/`, `types/` or `instructions/`?** You changed the stack locally, which
|
||||
step "Where stack development happens" says not to do. `upstream merge` leaves the merge open
|
||||
rather than guessing - take the upstream side for the named paths and re-file the change as an
|
||||
issue there, or resolve deliberately and finish the commit yourself.
|
||||
- **...but you changed how *your pages* are written?** That is not a stack change and the rule
|
||||
above does not apply to it. Language, section headings, naming forms, tone, relationship
|
||||
labels and the hedging rule live in `kb/CONVENTIONS.md`, and each collection's authoring
|
||||
rules in `kb/<name>/COLLECTION.md` - all under `kb/`, all yours, all restored by the merge
|
||||
procedure rather than overwritten by it. If you find yourself editing `tools/` or `types/` to
|
||||
change an authoring convention, that is a stack bug: file it, because the split exists
|
||||
precisely so you do not have to.
|
||||
|
||||
## Scope
|
||||
|
||||
Not for a first instance with no upstream - that is [setup-instance.md](setup-instance.md). Not
|
||||
for a fresh clone of a repo you already own and develop in - that is
|
||||
[bootstrap.md](bootstrap.md). This is specifically the two-remote case, where the cost of a
|
||||
mistaken push is disclosure rather than inconvenience.
|
||||
@@ -1,7 +1,7 @@
|
||||
---
|
||||
type: types/instruction.md
|
||||
name: session-setup
|
||||
description: Scope the wikitool iteration budget to the task by exporting a stable session id before the first tool call.
|
||||
description: Scope the wikitool iteration budget to the task by setting a stable session id - one line for bash, one for PowerShell - before the first tool call.
|
||||
---
|
||||
|
||||
# Scope the session budget
|
||||
@@ -15,17 +15,39 @@ Without an explicit id, and on a harness with no registered variable, the budget
|
||||
whichever shell happened to run the command, so a task spanning several terminals is counted as
|
||||
several sessions - and one that reuses a shell inherits an unrelated count.
|
||||
|
||||
<!-- wikitool:toc -->
|
||||
## Contents
|
||||
|
||||
- [Steps](#steps)
|
||||
- [Multi-unit runs](#multi-unit-runs)
|
||||
- [Scope](#scope)
|
||||
<!-- /wikitool:toc -->
|
||||
|
||||
## Steps
|
||||
|
||||
Run this **once per working session**, before the first `wikitool` call that is not exempt from
|
||||
the budget (see § Scope for what that means):
|
||||
the budget (see § Scope for what that means). Pick the id yourself - a short name for the task and
|
||||
the current date and time, such as `wiki-20261001-1430` - and set it with the line for the shell
|
||||
you run in. In a POSIX shell (Linux, macOS, Git Bash on Windows):
|
||||
|
||||
```bash
|
||||
export WIKITOOL_SESSION_ID="wiki-20261001-1430"
|
||||
```
|
||||
|
||||
In PowerShell 7:
|
||||
|
||||
```powershell
|
||||
$env:WIKITOOL_SESSION_ID = 'wiki-20261001-1430'
|
||||
```
|
||||
|
||||
These two lines are the only shell-specific syntax in the stack's instructions; everything else is
|
||||
a `tools/wikitool` or `git` call that reads the same in both shells. Then:
|
||||
|
||||
```bash
|
||||
export WIKITOOL_SESSION_ID="wiki-$(date +%s)"
|
||||
tools/wikitool sync
|
||||
```
|
||||
|
||||
**An `export` only carries if the shell carries.** Several agent harnesses run every tool call in
|
||||
**The variable only carries if the shell carries.** Several agent harnesses run every tool call in
|
||||
a freshly initialised shell: the working directory survives, shell state - environment variables,
|
||||
functions - does not, so the variable is gone by the next call and each call falls back to whatever
|
||||
the chain's next step resolves to.
|
||||
@@ -37,13 +59,15 @@ work into the same count. Setting `WIKITOOL_SESSION_ID` explicitly still narrows
|
||||
task at hand, and remains the only way to scope it at all on a harness with no registered
|
||||
variable - each call falls back to its own parent pid there, and neither the 60-call ceiling nor
|
||||
the loop-breaker can ever trip (measured directly on a real upgrade run: 33 `wikitool` calls in
|
||||
one task split into 21 telemetry buckets under the pid fallback alone). On such a harness, pass
|
||||
the id **inline on every call** instead of `export`, keeping the same value for the whole task:
|
||||
one task split into 21 telemetry buckets under the pid fallback alone). On such a harness, put the
|
||||
line **in front of every `tools/wikitool` call, in the same command**, joined with `;` - which
|
||||
both shells read the same way - and keep the same value for the whole task.
|
||||
|
||||
```bash
|
||||
WIKITOOL_SESSION_ID="wiki-1234" tools/wikitool sync
|
||||
WIKITOOL_SESSION_ID="wiki-1234" tools/wikitool new entity --name "..."
|
||||
```
|
||||
**GitHub Copilot registers no variable.** Neither Copilot CLI nor Copilot's agent mode in VS Code
|
||||
sets a session variable in the shell it runs commands in (checked against their documentation,
|
||||
October 2026), so the chain has no second step there. Under Copilot the line above is what scopes
|
||||
the budget at all, and what `tools/wikitool doctor` reads: without it, `doctor` reports
|
||||
`session-id: WARN` and names the parent-pid fallback.
|
||||
|
||||
Which of the three applies is answerable in one call: run `tools/wikitool budget status` twice in
|
||||
separate calls, and see whether it names the same id both times, and where that id came from -
|
||||
@@ -70,10 +94,7 @@ user, then `tools/wikitool sync --confirm-rebase <token>` before continuing. See
|
||||
|
||||
A task planned as several units - a tree ingest, where each unit produces its own source page
|
||||
and its own `publish` - takes one id per unit, derived from the workshop's run key:
|
||||
|
||||
```bash
|
||||
export WIKITOOL_SESSION_ID="ingest-documents-handbook/u3"
|
||||
```
|
||||
`<runkey>/u<N>`, for instance `ingest-documents-handbook/u3`, set with the same line as above.
|
||||
|
||||
The run key, the workshop directory name and the session id are then the same string, so the
|
||||
checklist in `work/<runkey>/README.md` and the budget state cannot disagree about where the
|
||||
@@ -87,7 +108,7 @@ refusal. See [gates.md](gates.md).
|
||||
**The exemption is an allowlist, not "read-only" or "does not change the wiki."** A command
|
||||
needs this setup unless it is one of the dozen `tools/CONTRACT.md` marks exempt in its command
|
||||
table (`search`, `doctor`, `links show`, `cite id`, `budget status`, the read-only forms of
|
||||
`eval`, `version`, `migrate` and `upstream verify`) - that table, not a rule of thumb here, is
|
||||
`eval`, `version` and `migrate`) - that table, not a rule of thumb here, is
|
||||
the single list. One entry on it, `version regrade`, is exempt only in its bare listing form and
|
||||
counted when it is given positions to regrade; every other entry is exempt however it is called.
|
||||
|
||||
|
||||
+179
-148
@@ -1,15 +1,19 @@
|
||||
---
|
||||
type: types/instruction.md
|
||||
name: setup-instance
|
||||
description: Turn a fresh distribution (from `dist export`) into a working, self-contained wiki instance - git repo, identity/author, optional remote, bootstrap, first commit.
|
||||
description: Install a new, self-contained wiki instance from the latest release into an empty folder - preflight asset, git repo, identity/author, optional remote, conventions, personalization, first commit.
|
||||
---
|
||||
|
||||
# Set up a new wiki instance
|
||||
|
||||
This instruction takes an empty distribution produced by `tools/wikitool dist export <target>`
|
||||
and turns it into a working, self-contained wiki instance - with its own git repo, its own
|
||||
author identity and (optionally) its own remote. At the end the instance is committed, verified
|
||||
and ready for its first ingest.
|
||||
This instruction takes an empty folder to a working, self-contained wiki instance, starting from
|
||||
the latest release - with its own git repo, its own author identity and (optionally) its own
|
||||
remote. At the end the instance is committed, verified and ready for its first ingest.
|
||||
|
||||
The same file is read in two places: as the asset `setup-instance.md` of a release, before
|
||||
anything is installed, and inside the installed instance as `instructions/setup-instance.md`.
|
||||
Its links to other instructions resolve only in the second place; step 0 says how to reach the
|
||||
one it needs before that.
|
||||
|
||||
<!-- wikitool:toc -->
|
||||
## Contents
|
||||
@@ -21,37 +25,86 @@ and ready for its first ingest.
|
||||
|
||||
## When to run
|
||||
|
||||
- The user wants to set up a new, empty wiki instance (their own subject, a different person).
|
||||
- Not for an existing clone of this (source) repo - see [bootstrap.md](bootstrap.md).
|
||||
- There is no way back: `dist export` deliberately and permanently leaves out
|
||||
`instructions/dev/` (stack development itself, including the vendored `commonplace/` knowledge
|
||||
base). Anyone who wants to develop the resulting instance's stack further does that in the
|
||||
origin repo (or a new dev instance made from it) - not by retrofitting it into this instance.
|
||||
- The user wants a new wiki instance - their own subject, a different person - in a folder they
|
||||
name. The folder is empty, or holds nothing but `.git`: an empty clone of the repository the
|
||||
instance will push to.
|
||||
- Not for a further checkout of an instance that already exists (a second machine): clone that
|
||||
instance's repository and follow [bootstrap.md](bootstrap.md).
|
||||
- Not for working on the stack itself. That happens in a clone of the origin repository; a
|
||||
release leaves out stack development (`instructions/dev/`) permanently, and nothing in an
|
||||
instance restores it.
|
||||
|
||||
## Steps
|
||||
|
||||
1. **Export the distribution**, in the source repo:
|
||||
0. **Install the release into the folder.** Skip this step when `tools/preflight.sh` already
|
||||
exists in the folder - then the release is installed and this file is being read from inside
|
||||
it; continue with step 1.
|
||||
|
||||
```bash
|
||||
tools/wikitool dist export <target>
|
||||
```
|
||||
1. **Settle the folder.** It is the one the user named, and every later step runs in it. It
|
||||
has to be empty or hold only `.git`; on Windows with long paths switched off, its path may
|
||||
be at most 95 characters (`C:\Chemenu`, for instance). The preflight checks both, so do
|
||||
not measure anything yourself.
|
||||
|
||||
`<target>` must not exist, or must be empty; otherwise the command aborts with `ERROR`. Work
|
||||
inside `<target>` for every step that follows.
|
||||
2. **Read `preflight.md` before running anything.** It is an asset of the same release as
|
||||
this file: in the release description this file came from (the answer of
|
||||
`.../api/v1/repos/<owner>/<repo>/releases/latest`), the entry under `assets` named
|
||||
`preflight.md`, at its `browser_download_url`. It says how the preflight is started and
|
||||
what to do when it stops - and the release's preflight is the next thing to run.
|
||||
|
||||
2. **Initialize the git repo:**
|
||||
3. **Download the preflight for the shell you run in**, from the same release's `assets`,
|
||||
into the folder - with the shell's own download command, never through a browser, so the
|
||||
file carries no Mark of the Web. In PowerShell 7:
|
||||
|
||||
```powershell
|
||||
Invoke-WebRequest -Uri <browser_download_url of preflight.ps1> -OutFile preflight.ps1
|
||||
```
|
||||
|
||||
In a POSIX shell (Linux, macOS, Git Bash on Windows):
|
||||
|
||||
```bash
|
||||
curl -fLO <browser_download_url of preflight.sh>
|
||||
```
|
||||
|
||||
4. **Run it, exactly as `preflight.md` step 1 says** - the path is the downloaded file:
|
||||
|
||||
```powershell
|
||||
pwsh -NoProfile -ExecutionPolicy Bypass -File preflight.ps1
|
||||
```
|
||||
|
||||
```bash
|
||||
sh preflight.sh
|
||||
```
|
||||
|
||||
It downloads the release's tarball, refuses unless its sha256 matches, unpacks it into
|
||||
this folder, removes the downloaded script and runs the preflight of the installed tree.
|
||||
Read its exit code as `preflight.md` step 2 says; on exit 42 follow its step 3 - show the
|
||||
output verbatim and wait. Every later run, including the retry after an exit 42, is the
|
||||
tree's own `tools/preflight.sh` or `pwsh -NoProfile -ExecutionPolicy Bypass -File
|
||||
tools/preflight.ps1`.
|
||||
|
||||
Continue only after it exits 0. From here on, [preflight.md](preflight.md) and every other
|
||||
instruction this file links to lie under `instructions/` in the folder.
|
||||
|
||||
1. **Initialize the git repo.** If the folder has no `.git` yet:
|
||||
|
||||
```bash
|
||||
git init -b main
|
||||
```
|
||||
|
||||
`-b main` is mandatory: on the actual push, `tools/wikitool publish` checks that the
|
||||
If it has one - an empty clone - keep it. `git branch --show-current` must print `main`; if
|
||||
it prints anything else, switch before the first commit:
|
||||
|
||||
```bash
|
||||
git checkout -b main
|
||||
```
|
||||
|
||||
`main` is mandatory: on the actual push, `tools/wikitool publish` checks that the
|
||||
checked-out branch matches the target branch (default `main`) and refuses otherwise, so that
|
||||
the wrong branch is never published.
|
||||
|
||||
3. **Decision point - identity.** Ask the user for their name and email address; never guess
|
||||
them, and never quietly carry them over from the source repo (that is a different person and
|
||||
a different project):
|
||||
2. **Decision point - identity.** Ask the user for their name and email address; never guess
|
||||
them, and never quietly carry them over from another repository (that is a different person
|
||||
and a different project):
|
||||
|
||||
```bash
|
||||
git config user.name "<name>"
|
||||
@@ -62,18 +115,19 @@ and ready for its first ingest.
|
||||
resolves `author:` from `$WIKI_AUTHOR` (an override) or else from `git config user.name`, and
|
||||
aborts with `ERROR` when both are missing - there is no silent placeholder.
|
||||
|
||||
4. **Decision point - remote.** Ask the user for a remote URL; a purely local repo is a valid
|
||||
end state:
|
||||
3. **Decision point - remote.** An empty clone already has one: show the user `git remote -v`
|
||||
and confirm that `origin` is where this instance is to be published. Otherwise ask for a
|
||||
remote URL; a purely local repo is a valid end state:
|
||||
- Given: `git remote add origin <url>`
|
||||
- Not given: stay local - then **every** later `tools/wikitool publish` needs a `--no-push`
|
||||
(which also drops its branch check, see step 2). Without it, `publish` ends with exit 1
|
||||
(which also drops its branch check, see step 1). Without it, `publish` ends with exit 1
|
||||
before it commits anything, because there is no remote to publish to.
|
||||
|
||||
5. **Decision point - authoring conventions.** The distribution ships no filled-in conventions,
|
||||
only `kb/CONVENTIONS.md.template` and one `kb/<name>/COLLECTION.md.template` per collection.
|
||||
Both **bind** once adopted, and both belong to this instance - which is why the stack ships
|
||||
the template alone. The one decision behind them is: **in which language and in what tone
|
||||
does this instance write its pages?**
|
||||
4. **Decision point - authoring conventions.** The release ships no filled-in conventions, only
|
||||
`kb/CONVENTIONS.md.template` and one `kb/<name>/COLLECTION.md.template` per collection. Both
|
||||
**bind** once adopted, and both belong to this instance - which is why the stack ships the
|
||||
template alone. The one decision behind them is: **in which language and in what tone does
|
||||
this instance write its pages?**
|
||||
|
||||
Procedure:
|
||||
|
||||
@@ -81,19 +135,17 @@ and ready for its first ingest.
|
||||
user, because what they say is usable as a starting point regardless of language:
|
||||
|
||||
```bash
|
||||
for template in kb/*/COLLECTION.md.template types/*.template; do
|
||||
cp "$template" "${template%.template}"
|
||||
done
|
||||
tools/wikitool dist adopt
|
||||
```
|
||||
|
||||
The `.template` files stay where they are; they are the source for the next export.
|
||||
The `.template` files stay where they are; they are what the next `dist upgrade` compares
|
||||
against.
|
||||
|
||||
Under `types/` this covers exactly the type-specs with `root: kb` - `entity`, `concept`,
|
||||
`source`, `comparison` - along with their `.schema.yaml`. They describe pages *this*
|
||||
instance writes, so they belong to it: frontmatter, template and language may all be
|
||||
rewritten. `instruction`, `lint-report`, `type-spec` and `type-guidance` describe stack
|
||||
artifacts and arrive unchanged - the glob above never matches them because none of them
|
||||
ships as a `.template` in the first place.
|
||||
`source`, `comparison`, `project` - along with their `.schema.yaml`. They describe pages
|
||||
*this* instance writes, so they belong to it: frontmatter, template and language may all
|
||||
be rewritten. `instruction`, `lint-report`, `type-spec` and `type-guidance` describe stack
|
||||
artifacts and arrive unchanged - none of them ships as a `.template` in the first place.
|
||||
|
||||
A `root: kb` type-spec's generic authoring guidance (when to use the type, when not to)
|
||||
is not part of this adoption at all: it lives in a sibling `types/<name>.guidance.md`
|
||||
@@ -102,32 +154,29 @@ and ready for its first ingest.
|
||||
touched. `types/type-spec.md` § "Anatomy of a type" has the shape.
|
||||
|
||||
2. Ask the user for the KB language. `kb/CONVENTIONS.md.template` defaults to **English**;
|
||||
[kb-profiles.md](kb-profiles.md) additionally holds a complete German profile, whose full
|
||||
text is the source repo's own `kb/CONVENTIONS.md`. The profile catalogue is a **palette,
|
||||
not an enum**: what gets adopted is the text *into* the instance file, not a reference to
|
||||
the catalogue.
|
||||
[kb-profiles.md](kb-profiles.md) additionally holds a complete German profile. The
|
||||
profile catalogue is a **palette, not an enum**: what gets adopted is the text *into* the
|
||||
instance file, not a reference to the catalogue.
|
||||
|
||||
3. Copy `kb/CONVENTIONS.md.template` to `kb/CONVENTIONS.md`, fill it in along the chosen
|
||||
3. Write `kb/CONVENTIONS.md` from `kb/CONVENTIONS.md.template`, filled in along the chosen
|
||||
profile - language, section names, naming forms, tone, relationship labels, hedging rule -
|
||||
and remove the sentinel line (`wikitool:template-unfilled`) while doing so. The
|
||||
placeholders in curly braces **are** the list of questions.
|
||||
and without the sentinel line (`wikitool:template-unfilled`). The placeholders in curly
|
||||
braces **are** the list of questions.
|
||||
|
||||
4. For a language other than the source repo's: delete `german-terminology.md` or replace it
|
||||
with your own vocabulary - it is material belonging to the German profile, not to the
|
||||
stack.
|
||||
4. For a language other than German: delete `german-terminology.md` or replace it with your
|
||||
own vocabulary - it is material belonging to the German profile, not to the stack.
|
||||
|
||||
5. Ask the user about the subject area and derive a `source_type` proposal from it.
|
||||
[kb-profiles.md](kb-profiles.md) holds two worked domain profiles as illustration, beside
|
||||
the value this repo uses itself. The proposal is a **starting point, not a commitment** -
|
||||
at setup time the operator has zero sources and is guessing a taxonomy before having seen
|
||||
a single file, which is the worst possible moment to pin an enum down. Carrying out the
|
||||
proposal means setting the enum in `types/source.schema.yaml` **and** the matching
|
||||
`layout:` line per value in `types/source.md` in the same edit - one without the other
|
||||
leaves a value with no target directory. The visible catch-all (`unclassified`) survives
|
||||
every proposal; it is not a dumping ground but the slot for a source whose category is not
|
||||
settled yet. Extending the list later, or emptying that slot:
|
||||
[evolve-subtypes.md](evolve-subtypes.md) - not part of this step, but the way there once
|
||||
real material exists.
|
||||
[kb-profiles.md](kb-profiles.md) holds two worked domain profiles as illustration. The
|
||||
proposal is a **starting point, not a commitment** - at setup time the operator has zero
|
||||
sources and is guessing a taxonomy before having seen a single file, which is the worst
|
||||
possible moment to pin an enum down. Carrying out the proposal means setting the enum in
|
||||
`types/source.schema.yaml` **and** the matching `layout:` line per value in
|
||||
`types/source.md` in the same edit - one without the other leaves a value with no target
|
||||
directory. The visible catch-all (`unclassified`) survives every proposal; it is not a
|
||||
dumping ground but the slot for a source whose category is not settled yet. Extending the
|
||||
list later, or emptying that slot: [evolve-subtypes.md](evolve-subtypes.md) - not part of
|
||||
this step, but the way there once real material exists.
|
||||
|
||||
**Leave unchanged:** `fidelity` and `authority` on `source` pages. Those are stack
|
||||
vocabulary, not an instance decision - [kb-profiles.md](kb-profiles.md) says so in the
|
||||
@@ -139,7 +188,7 @@ and ready for its first ingest.
|
||||
[migrate-corpus.md](migrate-corpus.md)).
|
||||
|
||||
**None of this lives in a stack file.** The compiler reads the section names from
|
||||
`kb/CONVENTIONS.md`; the four page type-specs have belonged to this instance since step 1. An
|
||||
`kb/CONVENTIONS.md`; the page type-specs have belonged to this instance since sub-step 1. An
|
||||
instance in another language simply translates them - that is no longer a local patch to
|
||||
something shipped, but work on its own files, and an upgrade does not take it away again.
|
||||
|
||||
@@ -153,14 +202,14 @@ and ready for its first ingest.
|
||||
identifiers](../kb/CONTRACT.md#language-and-identifiers)). Titles, wikilink targets, cite ids,
|
||||
enum values, tags, commands and paths follow no KB language.
|
||||
|
||||
`tools/wikitool doctor` checks the result in step 13 (`conventions`): a missing file is a
|
||||
`tools/wikitool doctor` checks the result in step 12 (`conventions`): a missing file is a
|
||||
`FAIL`, and so is one carrying the sentinel or lacking a complete `sections:` block.
|
||||
`docs verify` additionally checks `profile:` and `required_by_stack:` on every
|
||||
`COLLECTION.md`.
|
||||
|
||||
6. **Decision point - personalization.** The distribution ships `USER.md.template` and
|
||||
5. **Decision point - personalization.** The release ships `USER.md.template` and
|
||||
`SOUL.md.template`, but no filled-in versions: who operates this instance and how it sounds
|
||||
is the property of this instance alone and is never carried over from the source repo. Both
|
||||
is the property of this instance alone and is never carried over from anywhere else. Both
|
||||
files are read in **every** session from now on, so they come into being here - not later,
|
||||
when the occasion arises.
|
||||
|
||||
@@ -176,7 +225,7 @@ and ready for its first ingest.
|
||||
better to delete a section than to fill it with something plausible.
|
||||
4. Write the result as `USER.md` and `SOUL.md` respectively, removing the sentinel line
|
||||
(`wikitool:template-unfilled`) in the process. The `.template` files stay where they are -
|
||||
they are the source for the next export, not this step's leftovers.
|
||||
they are what the next `dist upgrade` compares against, not this step's leftovers.
|
||||
|
||||
Two questions the user answers rather than the agent: **the persona name** and **which topics
|
||||
deliberately stay out** (employer, clients, health - whatever they are). Guessing either
|
||||
@@ -189,103 +238,86 @@ and ready for its first ingest.
|
||||
invariant 3. They change no rule from [AGENTS.md](../AGENTS.md), and a user's statement never
|
||||
travels from them into `kb/` without the normal source/provenance process.
|
||||
|
||||
`tools/wikitool doctor` checks the result in step 13 (`personalization`): a missing file is a
|
||||
`tools/wikitool doctor` checks the result in step 12 (`personalization`): a missing file is a
|
||||
`FAIL`, and so is one still carrying the sentinel - a renamed template is not a filled-in
|
||||
one.
|
||||
|
||||
7. **Run the preflight** ([preflight.md](preflight.md)). It checks Python, git and ripgrep,
|
||||
records their paths in `.wikitool-tools.json` and creates `tools/.venv` - no `tools/wikitool`
|
||||
call works before it has passed:
|
||||
|
||||
```bash
|
||||
tools/preflight.sh
|
||||
```
|
||||
|
||||
From PowerShell 7 on Windows, run the twin instead - same questions, same file:
|
||||
|
||||
```powershell
|
||||
pwsh -NoProfile -ExecutionPolicy Bypass -File tools/preflight.ps1
|
||||
```
|
||||
|
||||
On exit 42, show its output to the user verbatim and wait; run it again once they have
|
||||
acted. Continue here only after it exits 0.
|
||||
|
||||
8. **Publish the skills:**
|
||||
6. **Publish the skills:**
|
||||
|
||||
```bash
|
||||
tools/wikitool instructions sync
|
||||
```
|
||||
|
||||
9. **Decision point - record the environment.** The distribution ships
|
||||
`ENVIRONMENT.md.template`: harness, published skills, reachable MCP servers, connectors, git
|
||||
remotes, where CI runs. Constants a session would otherwise ask about every time.
|
||||
7. **Decision point - record the environment.** The release ships `ENVIRONMENT.md.template`:
|
||||
harness, published skills, reachable MCP servers, connectors, git remotes, where CI runs.
|
||||
Constants a session would otherwise ask about every time.
|
||||
|
||||
Unlike step 6, this step is **optional** and not an interview. Whatever can be read off the
|
||||
Unlike step 5, this step is **optional** and not an interview. Whatever can be read off the
|
||||
checkout itself (`git remote -v`, the running harness, the skills just published) the agent
|
||||
fills in; for the rest it asks once and accepts "I don't know" as an answer - an empty
|
||||
section is deleted, not filled with something plausible. Remove the sentinel line
|
||||
(`wikitool:template-unfilled`) when writing; the `.template` stays where it is.
|
||||
|
||||
If the step is skipped, everything still works: `doctor` reports
|
||||
`environment: absent (optional)` in step 13, not a `FAIL`. The file is gitignored and enters
|
||||
`environment: absent (optional)` in step 12, not a `FAIL`. The file is gitignored and enters
|
||||
no commit - it describes this checkout, not the repo.
|
||||
|
||||
10. **Decision point - telemetry.** The default follows the installation path, not this step: an
|
||||
instance delivered via `dist export` - every instance that arrives here without having taken
|
||||
route C (a direct clone of the origin repo) - carries a `.wikitool-release.json` and starts
|
||||
with telemetry **off**; nobody asked for it, and nobody reads `EVALS.md` before the first
|
||||
file is written anyway. This step only asks whether the operator wants to reverse that.
|
||||
8. **Decision point - telemetry.** Every instance installed from a release carries a
|
||||
`.wikitool-release.json` and starts with telemetry **off**; nobody asked for it, and nobody
|
||||
reads `EVALS.md` before the first file is written anyway. This step only asks whether the
|
||||
operator wants to reverse that.
|
||||
|
||||
Ask the user once: telemetry on? If yes, create `.wikitool-telemetry.json` in the repo root
|
||||
(per checkout, gitignored, no `.template` - like `.wikitool-remotes.json`):
|
||||
Ask the user once: telemetry on? If yes, create `.wikitool-telemetry.json` in the repo root
|
||||
(per checkout, gitignored, no `.template` - like `.wikitool-remotes.json`):
|
||||
|
||||
```json
|
||||
{ "enabled": true }
|
||||
```
|
||||
|
||||
`max_session_bytes` (default 5 MiB) and `keep_sessions` (default 250) are optional in the
|
||||
same file; most instances need not touch them. If no, do nothing - the default is already
|
||||
off, and no file is created. `WIKI_TRACE` still overrides in both directions, should a
|
||||
single session need to differ.
|
||||
|
||||
`tools/wikitool doctor` reports the result in step 14 (`telemetry`): on/off, why
|
||||
(installation form, this file, or `WIKI_TRACE`), and the current volume against both caps -
|
||||
never a `FAIL`, since both directions are a valid state. More on this:
|
||||
[EVALS.md](../EVALS.md) § "Whether it runs at all".
|
||||
|
||||
11. **Decision point - task tracker.** The instance ships the `project` type and the collection
|
||||
its type-spec's `base_dir:` names (`kb/gtd/` here), so committed initiatives have a page from
|
||||
the start. What they do *not* have until this step is the other half of the weekly review:
|
||||
the tracker that owns the open items, which `tools/wikitool review` joins those pages against
|
||||
over the project name. No tracker configured is a legitimate end state - the pages work
|
||||
alone, `review` simply says so and refuses - so ask rather than assume.
|
||||
|
||||
Ask the user once: is there a task tracker to connect? If yes, create `.wikitool-tasks.json`
|
||||
in the repo root (per checkout, no `.template`, **gitignored once it holds a token** - like
|
||||
`.wikitool-telemetry.json` and `.wikitool-remotes.json`), with the provider's own section and
|
||||
the three thresholds the review reads as configuration rather than schema. The shape, the
|
||||
shipped providers, and what Super Productivity in particular needs are in
|
||||
[INSTALL.md](../INSTALL.md) § Konfiguration; do not restate them here. If no, do nothing - no
|
||||
file is created, and adding one later needs nothing from this procedure.
|
||||
|
||||
`doctor` reports the result in step 14 (`tasks`): absent is `OK`, a malformed file is the one
|
||||
`FAIL` here (a broken opt-in must not read as "no tracker configured"), and a configured
|
||||
provider that is simply not running is never a fault.
|
||||
|
||||
12. **Scope the session budget** (details: [session-setup.md](session-setup.md)):
|
||||
|
||||
```bash
|
||||
export WIKITOOL_SESSION_ID="wiki-$(date +%s)"
|
||||
```json
|
||||
{ "enabled": true }
|
||||
```
|
||||
|
||||
13. **Build the generated indexes** - `dist export` deliberately does not ship them:
|
||||
`max_session_bytes` (default 5 MiB) and `keep_sessions` (default 250) are optional in the
|
||||
same file; most instances need not touch them. If no, do nothing - the default is already
|
||||
off, and no file is created. `WIKI_TRACE` still overrides in both directions, should a
|
||||
single session need to differ.
|
||||
|
||||
`tools/wikitool doctor` reports the result in step 12 (`telemetry`): on/off, why
|
||||
(installation form, this file, or `WIKI_TRACE`), and the current volume against both caps -
|
||||
never a `FAIL`, since both directions are a valid state. More on this:
|
||||
[EVALS.md](../EVALS.md) § "Whether it runs at all".
|
||||
|
||||
9. **Decision point - task tracker.** The instance ships the `project` type and the collection
|
||||
its type-spec's `base_dir:` names (`kb/gtd/` here), so committed initiatives have a page from
|
||||
the start. What they do *not* have until this step is the other half of the weekly review:
|
||||
the tracker that owns the open items, which `tools/wikitool review` joins those pages against
|
||||
over the project name. No tracker configured is a legitimate end state - the pages work
|
||||
alone, `review` simply says so and refuses - so ask rather than assume.
|
||||
|
||||
Ask the user once: is there a task tracker to connect? If yes, create `.wikitool-tasks.json`
|
||||
in the repo root (per checkout, no `.template`, **gitignored once it holds a token** - like
|
||||
`.wikitool-telemetry.json` and `.wikitool-remotes.json`), with the provider's own section and
|
||||
the three thresholds the review reads as configuration rather than schema. The shape, the
|
||||
shipped providers, and what Super Productivity in particular needs are in
|
||||
[INSTALL.md](../INSTALL.md) § Konfiguration; do not restate them here. If no, do nothing - no
|
||||
file is created, and adding one later needs nothing from this procedure.
|
||||
|
||||
`doctor` reports the result in step 12 (`tasks`): absent is `OK`, a malformed file is the one
|
||||
`FAIL` here (a broken opt-in must not read as "no tracker configured"), and a configured
|
||||
provider that is simply not running is never a fault.
|
||||
|
||||
10. **Scope the session budget** with the line for the shell you run in, from
|
||||
[session-setup.md](session-setup.md) § Steps. Under GitHub Copilot this is what step 12's
|
||||
`doctor` reads: Copilot sets no session variable of its own, so without the line `doctor`
|
||||
reports `session-id: WARN` and the budget falls back to the parent process. If your harness
|
||||
starts a fresh shell for every command, put the line in front of each `tools/wikitool`
|
||||
call instead, in the same command - [session-setup.md](session-setup.md) says how.
|
||||
|
||||
11. **Build the generated indexes** - the release deliberately does not ship them:
|
||||
|
||||
```bash
|
||||
tools/wikitool index rebuild
|
||||
tools/wikitool sources rebuild-index
|
||||
```
|
||||
|
||||
14. **Verify**, in this order:
|
||||
12. **Verify**, in this order:
|
||||
|
||||
```bash
|
||||
tools/wikitool doctor
|
||||
@@ -295,27 +327,26 @@ and ready for its first ingest.
|
||||
```
|
||||
|
||||
`doctor` must run through without a `FAIL` before anything continues - a `WARN` (no remote,
|
||||
no `WIKITOOL_SESSION_ID`, say) is not a blocker. A `FAIL` names its own fix command; run it
|
||||
and call `doctor` again.
|
||||
say) is not a blocker. A `FAIL` names its own fix command; run it and call `doctor` again.
|
||||
|
||||
15. **Make the first commit:**
|
||||
13. **Make the first commit:**
|
||||
|
||||
```bash
|
||||
tools/wikitool publish --message "chore: initial instance setup"
|
||||
```
|
||||
|
||||
The Mass-Update Gate fires here as expected: a fresh distribution consists of far more than
|
||||
the ten counted files that trip the threshold, so the call ends with exit code 42. Show the
|
||||
The Mass-Update Gate fires here as expected: a fresh instance consists of far more than the
|
||||
ten counted files that trip the threshold, so the call ends with exit code 42. Show the
|
||||
output to the user **in full** and wait; it contains the file list and the exact
|
||||
`--confirm <token>` line that publishes once they approve. Details on the gate:
|
||||
[gates.md](gates.md).
|
||||
|
||||
A local-only instance (step 4) adds `--no-push` here too. Without it the call ends with exit
|
||||
A local-only instance (step 3) adds `--no-push` here too. Without it the call ends with exit
|
||||
1 before the gate, because there is no remote to publish to, and commits nothing.
|
||||
|
||||
16. **Restart the agent session.** Harnesses read the skill directories at startup; only
|
||||
afterwards are `wiki-ingest`, `wiki-query`, `wiki-manage`, `wiki-lint`, `wiki-status` and
|
||||
`gtd-weekly-review` available.
|
||||
14. **Restart the agent session in this folder.** Harnesses read `AGENTS.md` and the skill
|
||||
directories at startup; only afterwards are `wiki-ingest`, `wiki-query`, `wiki-manage`,
|
||||
`wiki-lint`, `wiki-status` and `gtd-weekly-review` available.
|
||||
|
||||
**A step fails and the cause is not obvious?** Do not improvise around it (invariant 7). Offer the
|
||||
user a bug report - [bug-report.md](bug-report.md) - and run it only if they agree; the collector
|
||||
@@ -323,10 +354,10 @@ works even when `wikitool` does not start.
|
||||
|
||||
## Scope
|
||||
|
||||
Applies only to an empty distribution produced by `dist export`. For an existing clone of this
|
||||
source repo see [bootstrap.md](bootstrap.md) - there the git repo, author and content already
|
||||
exist, and only the tool environment (step 7) plus the skills (step 8) are missing.
|
||||
Applies only to an empty folder (or an empty clone) and a release. A further checkout of an
|
||||
instance that already exists has its git repo, author and content already; it needs only the
|
||||
preflight and the skills - see [bootstrap.md](bootstrap.md).
|
||||
|
||||
One exception: step 6 (personalization) also applies to an existing clone that has no
|
||||
One exception: the personalization step (5) also applies to an existing checkout that has no
|
||||
`USER.md`/`SOUL.md` yet - there as a single catch-up step, not as a whole procedure.
|
||||
`bootstrap.md` points here for it.
|
||||
@@ -1,20 +1,20 @@
|
||||
---
|
||||
type: types/instruction.md
|
||||
name: upgrade-instance
|
||||
description: Carry out a stack release upgrade on an instance built from a tarball - 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.
|
||||
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 built from a `dist export` tarball 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.
|
||||
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.
|
||||
|
||||
**This is the tarball path.** An instance that is a *clone* of the origin repo, sharing git
|
||||
history, takes updates by three-way merge (`tools/wikitool upstream merge`) and follows
|
||||
[private-instance.md](private-instance.md) instead. `git remote -v` answers which one this is:
|
||||
a clone carries an `upstream` remote pointing at the origin.
|
||||
**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
|
||||
@@ -39,18 +39,19 @@ documents that arrive inside the tarball.
|
||||
`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)), not for preparing a
|
||||
fresh clone ([bootstrap.md](bootstrap.md)), and not for the clone-with-upstream path above.
|
||||
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 pass it on every call for the whole upgrade** - the form and the
|
||||
reason are in [session-setup.md](session-setup.md). 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:
|
||||
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
|
||||
WIKITOOL_SESSION_ID=upgrade-<target-version> tools/wikitool version check
|
||||
tools/wikitool version check
|
||||
```
|
||||
|
||||
2. **Read this release's notes before touching anything.** Two lines decide the rest of the run:
|
||||
@@ -88,10 +89,11 @@ fresh clone ([bootstrap.md](bootstrap.md)), and not for the clone-with-upstream
|
||||
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, take the `.tar.gz` and its `.sha256` from the release page named in step 2,
|
||||
check the archive against the checksum before unpacking, and pass the file as `<tarball>`
|
||||
where the steps below say `--latest --expect <version>`. A tarball must unpack to exactly one
|
||||
top-level directory. The checksum comes from the same host as the archive, so it catches a
|
||||
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").
|
||||
@@ -189,15 +191,15 @@ fresh clone ([bootstrap.md](bootstrap.md)), and not for the clone-with-upstream
|
||||
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 step 5 of
|
||||
(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
|
||||
cp types/<name>.md.template types/<name>.md
|
||||
cp kb/<collection>/COLLECTION.md.template kb/<collection>/COLLECTION.md
|
||||
tools/wikitool dist adopt types/<name>.md.template types/<name>.schema.yaml.template kb/<collection>/COLLECTION.md.template
|
||||
```
|
||||
|
||||
The `.template` files stay where they are - they are the source for the next upgrade's
|
||||
`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.
|
||||
@@ -274,8 +276,8 @@ fresh clone ([bootstrap.md](bootstrap.md)), and not for the clone-with-upstream
|
||||
|
||||
## Scope
|
||||
|
||||
For an instance that receives releases as tarballs. Not the origin repo, which has no upgrade
|
||||
path of its own, and not a clone with shared history - see the second paragraph. Anything about
|
||||
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".
|
||||
|
||||
|
||||
Reference in new issue
Block a user