tools: command records, Private instances group - one line per cause, examples, prohibitions (#142)
CI / verify (push) Successful in 1m13s
Release / release (push) Successful in 38s

Files changed:
- CHANGES.md
- VERSION
- tools/CONTRACT.md
- tools/chemenu/commands/upstream_cmd.py
This commit is contained in:
torben committed 2026-09-26 09:29:09 +02:00
1 parent 243db66134
commit b83a3982c5
4 files changed
+167 -54

No files matched your search

+9 -1
View File
@@ -59,7 +59,7 @@ concern - readable here, never shipped as something to parse.
--- ---
## 7.1.0-beta.18 - 2026-09-26 - Command records, Content migrations group: one line per cause, examples, prohibitions ## 7.1.0-beta.19 - 2026-09-26 - Command records, Private instances group: one line per cause, examples, prohibitions
**Author:** Torben Nehmer **Author:** Torben Nehmer
@@ -87,6 +87,7 @@ concern - readable here, never shipped as something to parse.
- Command records, Types, instructions and docs group: one line per cause, examples, prohibitions - Command records, Types, instructions and docs group: one line per cause, examples, prohibitions
- Command records, Telemetry group: examples, the missing --fail-on-error exit line - Command records, Telemetry group: examples, the missing --fail-on-error exit line
- Command records, Content migrations group: one line per cause, examples, prohibitions - Command records, Content migrations group: one line per cause, examples, prohibitions
- Command records, Private instances group: one line per cause, examples, prohibitions
<!-- /wikitool:bumps --> <!-- /wikitool:bumps -->
### CalDAV task-tracker provider (Nextcloud Tasks, iOS Reminders); review reports unknown-value findings instead of skipping them ### CalDAV task-tracker provider (Nextcloud Tasks, iOS Reminders); review reports unknown-value findings instead of skipping them
@@ -316,6 +317,13 @@ never advance the version with `baseline --force` or by editing `.wikitool-kb.js
behind counting marker pairs rather than comparing their names, and behind an offered migration behind counting marker pairs rather than comparing their names, and behind an offered migration
ignoring the chain, moved into the `verify` and `done` docstrings. ignoring the chain, moved into the `verify` and `done` docstrings.
### Command records, Private instances group: one line per cause, examples, prohibitions
`upstream merge` and `upstream verify` rewritten the same way; text only. `upstream merge`'s
single paragraph is now one bullet per step of the merge, and its exit-1 causes - including the
fetch failure, a failing git step inside the open merge, and the post-commit leak, which its old
record mentioned only in passing - each carry their own reaction.
--- ---
## 7.0.0 - 2026-09-22 - Task-Tracker-Anbindung: Vorhaben als Seitenart, Verpflichtungsschicht, Weekly Review als Read-Time-Join ## 7.0.0 - 2026-09-22 - Task-Tracker-Anbindung: Vorhaben als Seitenart, Verpflichtungsschicht, Weekly Review als Read-Time-Join
+1 -1
View File
@@ -1 +1 @@
7.1.0-beta.18 7.1.0-beta.19
+58 -6
View File
@@ -3039,18 +3039,51 @@ Take a stack update into a private instance's branch, machinery only.
- budget: counted - budget: counted
- network: no - network: no
**EXAMPLES**
- `tools/wikitool upstream merge`
- `tools/wikitool upstream merge --remote upstream --branch main --no-fetch`
**EXIT STATUS** **EXIT STATUS**
- 0 success - 0 success
- 1 Dirty working tree, a merge already in progress, the remote does not resolve, git refused to open the merge at all (unrelated histories), or a real conflict remains in `tools/`/`types/`/`instructions/` after the content stages and stack-owned paths were restored - 1 Dirty working tree, or a merge already in progress
- 1 The remote does not resolve, the fetch failed, or `HEAD` does not resolve
- 1 git refused to open the merge at all (unrelated histories); nothing was touched
- 1 A real conflict remains in `tools/`/`types/`/`instructions/` after the content stages and stack-owned paths were restored; the merge is left open
- 1 A git step failed inside the open merge (`git checkout MERGE_HEAD -- <path>` or `git commit --no-edit`)
- 1 The postcheck after the commit found a leak; the merge commit already exists
**ON FAILURE** **ON FAILURE**
- Dirty working tree, a merge already in progress, the remote does not resolve, git refused to open the merge at all (unrelated histories), or a real conflict remains in `tools/`/`types/`/`instructions/` after the content stages and stack-owned paths were restored -> **Not idempotent, and not safe to retry unchanged.** For a dirty tree or an in-progress merge: fix the named precondition and retry once. For a real conflict: **do not retry, do not force** - resolve the named paths by hand (take the upstream side, or re-file the local change as an issue against the public repo per `instructions/private-instance.md`) and either `git commit --no-edit` yourself or `git merge --abort`. If the postcheck after commit finds a leak, the merge commit already exists and is **not** rolled back automatically - inspect it by hand; this is a bug report, not a retry - Dirty working tree, or a merge already in progress -> Fix the named precondition and retry once
- The remote does not resolve, the fetch failed, or `HEAD` does not resolve -> Fix `--remote`/`--branch` or the repository state, then retry once
- git refused to open the merge at all (unrelated histories); nothing was touched -> Do not retry unchanged - report it to the user
- A real conflict remains in `tools/`/`types/`/`instructions/` after the content stages and stack-owned paths were restored; the merge is left open -> **Do not retry, do not force** - resolve the named paths by hand (take the upstream side, or re-file the local change as an issue against the public repo per `instructions/private-instance.md`) and either `git commit --no-edit` yourself or `git merge --abort`
- A git step failed inside the open merge (`git checkout MERGE_HEAD -- <path>` or `git commit --no-edit`) -> Do not retry unchanged - inspect the open merge by hand
- The postcheck after the commit found a leak; the merge commit already exists -> It is **not** rolled back automatically - inspect it by hand; this is a bug report, not a retry
**NEVER**
- Never retry a failed merge unchanged, and never force.
**NOTES** **NOTES**
The code procedure behind `instructions/private-instance.md` § "Taking a stack update". Refuses on a dirty working tree, a merge already in progress, or a remote that does not resolve; WARNs (does not block) when `.wikitool-remotes.json` is absent, pointing at the setup step that arms it. Fetches `<remote>/<branch>` (unless `--no-fetch`) and reports "already up to date" if nothing new exists. Otherwise opens `git merge --no-commit --no-ff <remote>/<branch>` - and stops, untouched, if git refused to open a merge at all (unrelated histories), since without a `MERGE_HEAD` every stack path would read as "the upstream deleted it". Then forces every content stage (`kb/`, `raw/`, `work/`, `reports/`) back to the local side by removing **only the paths tracked in either tree** and checking `HEAD`'s back out - never the stage directory wholesale, because `reports/` is gitignored apart from its contract and holds local, non-recomputable data (telemetry traces `eval score` reads, saved eval and lint reports) that no merge has business deleting. Then restores from the upstream side exactly the paths `chemenu.ownership.is_stack_owned` recognises as machinery (`<stage>/CONTRACT.md`, and anything ending `.template` under a content stage) - including a deletion, if the upstream removed one. A real conflict left in `tools/`, `types/` or `instructions/` after that leaves the merge open, uncommitted, and exits 1 rather than guessing. Commits with `git commit --no-edit`, then re-checks the resulting range with the same logic as `upstream verify`; a finding there is a loud, uncommitted-nothing-rolled-back error, because the merge commit already exists and needs a human's eyes, not an automatic repair. Never pushes. Not idempotent - see the tool error contract below - Refuses on a dirty working tree, a merge already in progress, or a remote that does not resolve. WARNs (does not block) when `.wikitool-remotes.json` is absent, pointing at the setup step that arms it.
- Fetches `<remote>/<branch>` (unless `--no-fetch`) and reports "already up to date" if nothing new exists.
- Otherwise opens `git merge --no-commit --no-ff <remote>/<branch>`, and stops, untouched, if git refused to open a merge at all (unrelated histories).
- Forces every content stage (`kb/`, `raw/`, `work/`, `reports/`) back to the local side by removing **only the paths tracked in either tree** and checking `HEAD`'s back out - never the stage directory wholesale, so untracked and ignored local data under a stage (telemetry traces, saved eval and lint reports) is never deleted.
- Then restores from the upstream side exactly the machinery paths - `<stage>/CONTRACT.md` and anything ending `.template` under a content stage - including a deletion, if the upstream removed one.
- A real conflict left in `tools/`, `types/` or `instructions/` after that leaves the merge open, uncommitted, and exits 1 rather than guessing.
- Commits with `git commit --no-edit`, then re-checks the resulting range with the `upstream verify` check; a finding there is a loud error, and the merge commit is not rolled back.
- Never pushes.
- Not idempotent, and not safe to retry unchanged.
**SEE ALSO**
- `instructions/private-instance.md` § "Taking a stack update" - the procedure this implements
- `wikitool upstream verify` - the same check on any revision range
- `wikitool publish` - pushes the merge afterwards
#### `upstream verify` #### `upstream verify`
@@ -3068,18 +3101,37 @@ Compare two revisions: did anything under a content stage change except through
- budget: exempt - budget: exempt
- network: no - network: no
**EXAMPLES**
- `tools/wikitool upstream verify --since HEAD~1`
- `tools/wikitool upstream verify --since v7.0.0 --until HEAD`
**EXIT STATUS** **EXIT STATUS**
- 0 success - 0 success
- 1 A leak was found (content changed under a content stage through a path that is not stack-owned), or `--since`/`--until` is not a revision in this repository - 1 A leak: content changed under a content stage through a path that is not stack-owned
- 1 `--since`/`--until` is not a revision in this repository
**ON FAILURE** **ON FAILURE**
- A leak was found (content changed under a content stage through a path that is not stack-owned), or `--since`/`--until` is not a revision in this repository -> A finding is not fixed by re-running - it names the paths that leaked. Fix the revision argument and retry for the second case - A leak: content changed under a content stage through a path that is not stack-owned -> A finding is not fixed by re-running - it names the paths that leaked
- `--since`/`--until` is not a revision in this repository -> Fix the revision argument and retry
**NEVER**
- Never re-run to make a leak finding go away.
**NOTES** **NOTES**
Shares its check with `upstream merge`'s own postcheck, so a hand-resolved merge conflict, or a `dist upgrade`, can be verified the same way. Exit 1 with the offending paths if anything leaked; otherwise reports which stack-owned paths legitimately moved. Read-only and exempt from the Iteration Budget Gate, like `migrate verify` - Compares `--since` with `--until` (default `HEAD`): did anything under a content stage change except through a stack-owned path?
- The same check `upstream merge` runs after its commit, so a hand-resolved merge conflict, or a `dist upgrade`, can be verified the same way.
- Exits 1 with the offending paths if anything leaked; otherwise reports which stack-owned paths legitimately moved.
- Read-only and exempt from the Iteration Budget Gate.
**SEE ALSO**
- `wikitool upstream merge` - runs this check after its commit
- `instructions/private-instance.md` - the private-instance workflow
### Instance health ### Instance health
+99 -46
View File
@@ -250,42 +250,75 @@ def _merge_success_message(
atomic="**No** - can leave an open, uncommitted merge behind on refusal after fetching", atomic="**No** - can leave an open, uncommitted merge behind on refusal after fetching",
budget=cli_contract.Budget.COUNTED, budget=cli_contract.Budget.COUNTED,
), ),
notes="The code procedure behind `instructions/private-instance.md` § \"Taking a stack " notes=(
"update\". Refuses on a dirty working tree, a merge already in progress, or a remote that " "Refuses on a dirty working tree, a merge already in progress, or a remote that does "
"does not resolve; WARNs (does not block) when `.wikitool-remotes.json` is absent, pointing " "not resolve. WARNs (does not block) when `.wikitool-remotes.json` is absent, pointing "
"at the setup step that arms it. Fetches `<remote>/<branch>` (unless `--no-fetch`) and " "at the setup step that arms it.",
"reports \"already up to date\" if nothing new exists. Otherwise opens " "Fetches `<remote>/<branch>` (unless `--no-fetch`) and reports \"already up to date\" "
"`git merge --no-commit --no-ff <remote>/<branch>` - and stops, untouched, if git refused " "if nothing new exists.",
"to open a merge at all (unrelated histories), since without a `MERGE_HEAD` every stack " "Otherwise opens `git merge --no-commit --no-ff <remote>/<branch>`, and stops, "
"path would read as \"the upstream deleted it\". Then forces every content stage (`kb/`, " "untouched, if git refused to open a merge at all (unrelated histories).",
"`raw/`, `work/`, `reports/`) back to the local side by removing **only the paths tracked " "Forces every content stage (`kb/`, `raw/`, `work/`, `reports/`) back to the local "
"in either tree** and checking `HEAD`'s back out - never the stage directory wholesale, " "side by removing **only the paths tracked in either tree** and checking `HEAD`'s back "
"because `reports/` is gitignored apart from its contract and holds local, non-recomputable " "out - never the stage directory wholesale, so untracked and ignored local data under "
"data (telemetry traces `eval score` reads, saved eval and lint reports) that no merge has " "a stage (telemetry traces, saved eval and lint reports) is never deleted.",
"business deleting. Then restores from the upstream side exactly the paths " "Then restores from the upstream side exactly the machinery paths - `<stage>/CONTRACT.md` "
"`chemenu.ownership.is_stack_owned` recognises as machinery (`<stage>/CONTRACT.md`, and " "and anything ending `.template` under a content stage - including a deletion, if the "
"anything ending `.template` under a content stage) - including a deletion, if the upstream " "upstream removed one.",
"removed one. A real conflict left in `tools/`, `types/` or `instructions/` after that " "A real conflict left in `tools/`, `types/` or `instructions/` after that leaves the "
"leaves the merge open, uncommitted, and exits 1 rather than guessing. Commits with " "merge open, uncommitted, and exits 1 rather than guessing.",
"`git commit --no-edit`, then re-checks the resulting range with the same logic as " "Commits with `git commit --no-edit`, then re-checks the resulting range with the "
"`upstream verify`; a finding there is a loud, uncommitted-nothing-rolled-back error, " "`upstream verify` check; a finding there is a loud error, and the merge commit is not "
"because the merge commit already exists and needs a human's eyes, not an automatic repair. " "rolled back.",
"Never pushes. Not idempotent - see the tool error contract below", "Never pushes.",
failures=(cli_contract.Failure( "Not idempotent, and not safe to retry unchanged.",
label="", ),
cause="Dirty working tree, a merge already in progress, the remote does not resolve, " failures=(
"git refused to open the merge at all (unrelated histories), or a real conflict remains " cli_contract.Failure(
"in `tools/`/`types/`/`instructions/` after the content stages and stack-owned paths " cause="Dirty working tree, or a merge already in progress",
"were restored", reaction="Fix the named precondition and retry once",
reaction="**Not idempotent, and not safe to retry unchanged.** For a dirty tree or an " ),
"in-progress merge: fix the named precondition and retry once. For a real conflict: " cli_contract.Failure(
"**do not retry, do not force** - resolve the named paths by hand (take the upstream " cause="The remote does not resolve, the fetch failed, or `HEAD` does not resolve",
"side, or re-file the local change as an issue against the public repo per " reaction="Fix `--remote`/`--branch` or the repository state, then retry once",
),
cli_contract.Failure(
cause="git refused to open the merge at all (unrelated histories); nothing was "
"touched",
reaction="Do not retry unchanged - report it to the user",
),
cli_contract.Failure(
cause="A real conflict remains in `tools/`/`types/`/`instructions/` after the "
"content stages and stack-owned paths were restored; the merge is left open",
reaction="**Do not retry, do not force** - resolve the named paths by hand (take the "
"upstream side, or re-file the local change as an issue against the public repo per "
"`instructions/private-instance.md`) and either `git commit --no-edit` yourself or " "`instructions/private-instance.md`) and either `git commit --no-edit` yourself or "
"`git merge --abort`. If the postcheck after commit finds a leak, the merge commit " "`git merge --abort`",
"already exists and is **not** rolled back automatically - inspect it by hand; this is " ),
"a bug report, not a retry", cli_contract.Failure(
),), cause="A git step failed inside the open merge (`git checkout MERGE_HEAD -- <path>` "
"or `git commit --no-edit`)",
reaction="Do not retry unchanged - inspect the open merge by hand",
),
cli_contract.Failure(
cause="The postcheck after the commit found a leak; the merge commit already exists",
reaction="It is **not** rolled back automatically - inspect it by hand; this is a bug "
"report, not a retry",
),
),
examples=(
"tools/wikitool upstream merge",
"tools/wikitool upstream merge --remote upstream --branch main --no-fetch",
),
never=(
"Never retry a failed merge unchanged, and never force.",
),
see_also=(
"`instructions/private-instance.md` § \"Taking a stack update\" - the procedure this "
"implements",
"`wikitool upstream verify` - the same check on any revision range",
"`wikitool publish` - pushes the merge afterwards",
),
)) ))
@app.command("merge") @app.command("merge")
def merge_command( def merge_command(
@@ -431,17 +464,37 @@ def _verify_success_message(stack_moved: list[str], since: str, until: str) -> s
atomic="Read-only", atomic="Read-only",
budget=cli_contract.Budget.EXEMPT, budget=cli_contract.Budget.EXEMPT,
), ),
notes="Shares its check with `upstream merge`'s own postcheck, so a hand-resolved merge " notes=(
"conflict, or a `dist upgrade`, can be verified the same way. Exit 1 with the offending " "Compares `--since` with `--until` (default `HEAD`): did anything under a content stage "
"paths if anything leaked; otherwise reports which stack-owned paths legitimately moved. " "change except through a stack-owned path?",
"Read-only and exempt from the Iteration Budget Gate, like `migrate verify`", "The same check `upstream merge` runs after its commit, so a hand-resolved merge "
failures=(cli_contract.Failure( "conflict, or a `dist upgrade`, can be verified the same way.",
label="", "Exits 1 with the offending paths if anything leaked; otherwise reports which "
cause="A leak was found (content changed under a content stage through a path that is " "stack-owned paths legitimately moved.",
"not stack-owned), or `--since`/`--until` is not a revision in this repository", "Read-only and exempt from the Iteration Budget Gate.",
reaction="A finding is not fixed by re-running - it names the paths that leaked. Fix the " ),
"revision argument and retry for the second case", failures=(
),), cli_contract.Failure(
cause="A leak: content changed under a content stage through a path that is not "
"stack-owned",
reaction="A finding is not fixed by re-running - it names the paths that leaked",
),
cli_contract.Failure(
cause="`--since`/`--until` is not a revision in this repository",
reaction="Fix the revision argument and retry",
),
),
examples=(
"tools/wikitool upstream verify --since HEAD~1",
"tools/wikitool upstream verify --since v7.0.0 --until HEAD",
),
never=(
"Never re-run to make a leak finding go away.",
),
see_also=(
"`wikitool upstream merge` - runs this check after its commit",
"`instructions/private-instance.md` - the private-instance workflow",
),
)) ))
@app.command("verify") @app.command("verify")
def verify_command( def verify_command(