feat: Publish-Remote Gate und die Anleitung fuer eine private Instanz (2.2.0)
CI / verify (push) Successful in 44s
Release / release (push) Successful in 36s

Files changed:
- .gitignore
- AGENTS.md
- CHANGES.md
- VERSION
- instructions/gates.md
- instructions/private-instance.md
- tools/chemenu/commands/doctor.py
- tools/chemenu/commands/git_publish.py
- tools/chemenu/config.py
- tools/chemenu/tests/test_git_publish.py
This commit is contained in:
2026-09-01 18:06:55 +02:00
parent fb97d46888
commit 7263f85936
10 changed files with 506 additions and 10 deletions
+41 -4
View File
@@ -22,10 +22,11 @@ Read the exit code first - it says which of these applies:
## Exit 42: user clearance required
A `wikitool` command that exits **42** is not reporting an error. It is refusing to act until a
human has *read its output*. Two gates use it today - the Mass-Update Gate (`publish`, on a
change touching 10 or more counted files) and the rebase-review gate (`sync` and `publish`, on
a rebase whose incoming commits touch a file this session is also changing) - but the rule is
about the exit code, not the command:
human has *read its output*. Three gates use it today - the Mass-Update Gate (`publish`, on a
change touching 10 or more counted files), the rebase-review gate (`sync` and `publish`, on
a rebase whose incoming commits touch a file this session is also changing), and the
Publish-Remote Gate (`publish`, on a push to a target this checkout has not declared) - but the
rule is about the exit code, not the command:
> **Copy the command's output into your reply - the substance of it, not a description of it -
> and stop.** Run no further commands in that turn.
@@ -62,6 +63,42 @@ clearance.
Background: [[Mass-Update Gate]] (`kb/concepts/Mass-Update Gate.md`).
### Publish-Remote Gate
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`
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.
`.wikitool-remotes.json` names the push URLs a checkout permits:
```json
{ "schema": 1, "allowed_push_urls": ["ssh://git@example.net:22/you/your-wiki.git"] }
```
It pins **URLs, not remote names** - a name-based list would wave through a `publish` whose
`origin` had been repointed, which is the failure it exists to catch. It reads the remote's
`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
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
one.
**This gate has no `--confirm` token, on purpose.** The other two clear with a token because the
question they ask ("is this change right?") is one the agent can put to the user and the user can
answer for that one changeset. This one asks "does this content belong to that repository?", which
is a standing property of the checkout, not a per-push judgment. The way past it is for the user
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.
## Iteration Budget Gate and loop-breaker
Every `wikitool` call is counted per session. Calls are refused past **60 in a session**, or