Files
torbenandClaude Opus 5.5 55ddf652cb
CI / verify (push) Successful in 8m12s
CI / pwsh (push) Successful in 2m42s
Release / converters (push) Successful in 26s
Release / release (push) Successful in 37s
Release / wiki-mirror (push) Skipped
tools: Abgleich - Publish-, Sync-Hilfe und Log-Vorlage nennen den reinen Log-Zusatz (#223 close); 10.1.1-beta.2
Files changed:
- CHANGES.md
- VERSION
- docs/reference/commands/git.md
- tools/CONTRACT.md
- tools/chemenu/commands/git_publish.py
- tools/chemenu/dist_templates/log.md

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SnAJ7Z3CpVD3PRbN73QtU2
2026-10-16 20:13:53 +02:00

19 KiB

Git

Pulling in the remote and publishing a change - the only way a change leaves this checkout - and backing the checkout up into a synced folder.

Contents

sync

Fetch <remote>/<branch> and bring the local branch up to date with it.

Synopsis

  • wikitool sync [--remote origin] [--branch main] [--confirm-rebase TOKEN]

Options

  --remote <str>          Git remote to reconcile against  [default: origin]
  --branch <str>          Branch to reconcile  [default: main]
  --confirm-rebase TOKEN  Clear the Rebase-Review Gate for the exact upstream
                          state and file overlap this token was issued for. The
                          token comes from the gate's own refusal output.
  -h, --help              Show this message and exit.

Examples

tools/wikitool sync
tools/wikitool sync --confirm-rebase <token>    # re-run after exit 42, once the user approved

Behaviour

  • Fetches <remote>/<branch>, then: fast-forwards when only the remote moved; rebases the local commits on top when both sides moved but touched disjoint files; exits 42 (Rebase-Review Gate) when both sides touched the same file.
  • Files wikitool generates (kb/index.md, kb/log.md, kb/provenance.md, every kb/**/INDEX.md) never count as an overlap and never reach the gate. Where they are what stops git - uncommitted ones in the way of a fast-forward or rebase, or a rebase conflict in them - they are merged mechanically: kb/log.md becomes the incoming log plus the entries only this side appended, and the catalog and kb/provenance.md are regenerated from the merged pages and left as an uncommitted change. When only kb/log.md stood in the way - an open log entry, nothing else generated - the catalog is not rebuilt: the incoming one stays, and just the local entries are appended again. A reconcile git can do on its own is unchanged and regenerates nothing.
  • Uncommitted changes that no incoming commit touches do not stop a rebase: they are carried through it (git rebase --autostash) and come back unchanged.
  • Before anything is set aside, the working tree is stored as a commit under refs/wikitool/reconcile-backup. A successful reconcile drops it; a failed one restores the working tree and names the ref.
  • A refused call performs no rebase attempt and leaves the branch where it was.
  • The --confirm-rebase token covers the exact upstream state and the set of files touched on both sides, generated files excluded; either one moving makes it stale.
  • Makes no commit, no push, and no forced operation of any kind.
  • Three messages for a fetch that fails: no remote of that name, a remote that answers but has no such branch yet (a new, empty repository), and a remote that cannot be reached. All three exit 0 here; publish stops on the first and the last.
  • Run it once at the start of a writing session.

Properties

  • Changes files: yes
  • Idempotent: yes
  • Atomic: No - fetch, then at most one merge/rebase attempt, aborted cleanly on failure with the working tree restored; a success that merged generated files then rewrites the catalog, unless kb/log.md was the only one
  • Counts toward the session call budget: yes
  • Can reach the network: yes
  • Gates: rebase-review - see exit codes and gates

Exit codes

  • 0 - success
  • 0 - No remote configured - reported and skipped, not a failure
  • 0 - The remote cannot be reached - reported and skipped, not a failure
  • 1 - The automatic rebase hit a real conflict outside the generated files (git failed), or kb/log.md was edited rather than appended to; it is aborted cleanly and the working tree restored
  • 1 - An uncommitted change - or an untracked file - sits on a path the incoming commits also change; nothing was changed
  • 42 - Rebase-Review Gate: <remote>/<branch> moved and both sides changed the same file, generated files aside; the output lists the upstream commits, the overlapping files and their diff

See also

  • wikitool publish - runs the same reconcile before it commits and pushes
  • instructions/session-setup.md - where a session runs sync
  • instructions/gates.md - the gate procedure

publish

Reconcile with <remote>/<branch>, then stage all changes, commit, and push.

Synopsis

  • wikitool publish --message "<op>: <desc>" [--no-push] [--confirm TOKEN] [--confirm-rebase TOKEN] [--threshold N] [--remote origin] [--branch main] [--path P ...]

Options

  --message <str>         Commit message summary, e.g. 'ingest: docker-
                          cheatsheet'  [required]
  --push / --no-push      [default: push]
  --confirm TOKEN         Clear the Mass-Update Gate for the exact changeset
                          this token was issued for. The token comes from the
                          gate's own refusal output - run without it first to
                          see the file list, show that output to the user, and
                          only pass this once they have approved it.
  --confirm-rebase TOKEN  Clear the Rebase-Review Gate raised by the pre-push
                          reconcile step. The token comes from that gate's own
                          refusal output - see `tools/wikitool sync`.
  --threshold <int>       Mass-Update Gate threshold: number of counted files
                          at/above which clearance is required.  [default: 10]
  --remote <str>          Git remote to push to  [default: origin]
  --branch <str>          Branch to push  [default: main]
  --path <str>            Limit this publish to a path (repeatable). Lets a
                          large change be committed in reviewable batches
                          instead of one opaque commit - the Mass-Update Gate
                          then counts only the files in scope.
  -h, --help              Show this message and exit.

Examples

tools/wikitool publish --message "ingest: docker-cheatsheet"
tools/wikitool publish --confirm <token> --message "ingest: docker-cheatsheet"    # re-run after a Mass-Update exit 42, once the user approved
tools/wikitool publish --confirm-rebase <token> --message "ingest: docker-cheatsheet"    # re-run after a rebase-review exit 42, once the user approved

Behaviour

  • Order: branch check and Publish-Remote Gate, then the reconcile with <remote>/<branch>, then the Mass-Update Gate, then git add -A, commit and push. --no-push skips all but the Mass-Update Gate and the commit.
  • Without --no-push, a remote that is not configured or cannot be reached ends the call with exit 1 at the reconcile - before the gate, git add and the commit, and also on a clean tree. Nothing is committed, the index and the working tree are unchanged, and the message names --no-push as the way to a local commit. The next publish that reaches the remote pushes that commit along with whatever is new. A remote that answers but has no <branch> yet (a new, empty repository) is not this case: the first publish of an instance commits and pushes as before.
  • Reconcile: fetches <remote>/<branch>, fast-forwards when only the remote moved, rebases the local commits on top when both sides moved but touched disjoint files, and exits 42 (Rebase-Review Gate) when both sides touched the same file. A refused reconcile performs no rebase attempt. The --confirm-rebase token covers the exact upstream state and the set of files touched on both sides.
  • Generated files are never an overlap: where they are all that stops the reconcile, it merges them as sync does (both sides' log entries kept, the catalog and kb/provenance.md regenerated unless the log alone stood in the way) and carries uncommitted changes no incoming commit touches through the rebase. The proactive reconcile runs before staging, so this publish commits the regenerated files - with --path too, see there; on the retry after a rejected push they get a commit of their own before the second push.
  • The push target must be the checked-out branch; this is checked before anything is staged. The unborn branch of a fresh git init -b main counts as checked out, so the first publish of a new instance works; a real detached HEAD is refused.
  • With nothing new to stage, a local commit the remote lacks is still pushed: one left behind by an earlier publish whose push failed, or every commit when the remote answers but does not have the branch yet (a new, empty remote repository).
  • A rejected push that finds the remote unreachable on its one retry reports the original push error.
  • A rejected push gets exactly one more reconcile-and-push; never more than one.
  • Mass-Update Gate: counts the files that would be committed, refuses with exit 42 at --threshold (default 10) or more, and prints a review report - a scale line (file count, total lines added/removed, status breakdown), attention notes where they apply (deletions by name, control-plane and harness-config touches, published pages, the largest single change, binaries), and every counted path grouped by area with its status and churn. The list is what the commit will hold: it is computed from a scratch copy of the index after git add -A, so a path that is staged as deleted and back in the working tree is not counted twice, and a rename counts as its old path deleted plus its new path added. The real index and the working tree are not touched, so a refused publish leaves both byte-identical.
  • Never counted and never shown for approval, but committed like everything else: anything under work/, and the files wikitool generates itself (kb/index.md, kb/log.md, kb/provenance.md, every INDEX.md under kb/). The refusal line accounts for both, by reason.
  • The --confirm token covers each counted path, the blob id of its contents and the publish target: a different file list or edited contents need a new clearance.
  • Publish-Remote Gate: when .wikitool-local.json carries remotes.allowed_push_urls and the push URL of --remote is not listed in it, exits 42 before the reconcile fetches anything. The URL is read with git remote get-url --push, so a repointed remote does not pass on its name. An absent section means unrestricted; a malformed one is an error, not permission.
  • The remotes settings file of an earlier version is not read: while it exists this exits 42 and names tools/wikitool config migrate, so a restriction is never lost silently.
  • --path (repeatable) scopes the whole operation - gate count, staging and commit - to that subtree, with one exception: uncommitted generated files outside --path (kb/index.md, kb/log.md, kb/provenance.md, every INDEX.md under kb/) are staged and committed too, and a line names them - whether a reconcile or the change itself wrote them. They stay out of the gate count, and the --confirm token and the re-run line cover the --path given. Every other file outside --path is left as it is.
  • Commit message: --message, then a Files changed: paragraph listing every committed path. When --message ends in a paragraph git reads as a trailer block (Co-Authored-By: and the like), that block stays the last paragraph and the list goes in front of it, since git reads trailers from the last paragraph only. git interpret-trailers decides whether there is such a block, and the list moves only when git reads the same trailers from the result; otherwise it is appended at the end. --message is not part of any gate token.
  • Machine-data backup: with a backup section in .wikitool-local.json whose machine_data is true, a call that pushed then mirrors this machine's files that are not in git into <target>/<machine>/ - the same step as backup run. A failure there prints a WARN line with the reason and still exits 0, since the commit and the push stand. No backup with --no-push, after a failed push, or when there was nothing to push. A malformed backup section stops the call with exit 1 before anything is committed, --no-push included.
  • Export after publish: every target of the export section with after_publish: true is refreshed once a commit was made - the same step as export run, --no-push included, since the export reads the committed kb/. A failure there prints a WARN line with the reason and still exits 0, since the commit stands; export run repeats it. A malformed export section stops the call with exit 1 before anything is committed, but only when some target sets after_publish.
  • After a successful commit or push whose changed files include tools/, types/, instructions/, AGENTS.md or a path ending in CONTRACT.md, prints one reminder line: the phase past this point (an issue-body rewrite, docs/ staleness, a changelog entry's accuracy) is not covered by docs verify, instructions verify or pytest. It is not a gate: no exit code change, nothing to clear, and silent for an ordinary content publish.

Properties

  • Changes files: yes
  • Idempotent: no
  • Atomic: No - sequential git operations. Every gate runs before staging, except on the one retry of a rejected push, where the Rebase-Review Gate can exit 42 after the commit: nothing is pushed, and the re-run with --confirm-rebase pushes that commit
  • Counts toward the session call budget: yes
  • Can reach the network: yes
  • Gates: mass-update, publish-remote, rebase-review - see exit codes and gates

Exit codes

  • 0 - success
  • 0 - The machine-data backup after the push failed: a WARN line names the reason, and the commit and the push stand
  • 0 - The export after the commit failed: a WARN line names the target and the reason, and the commit stands
  • 1 - git failed - git add, git commit, git push, or the reconcile's automatic rebase (a conflict outside the generated files, an edited kb/log.md, or an uncommitted change on a path the incoming commits change)
  • 1 - The push target (--branch) is not the checked-out branch, or HEAD is detached; the unborn branch of a fresh git init is not this case
  • 1 - No --no-push, and the remote is not configured or cannot be reached; nothing was committed
  • 1 - --yes/-y was passed - the flag does not exist and fails with an explicit error
  • 1 - The settings files do not pass their check, remotes.allowed_push_urls is not a list of strings, the backup section is malformed, or the export section is malformed while a target sets after_publish; nothing was committed
  • 42 - The remotes settings file of an earlier version is still there
  • 42 - Mass-Update Gate: --threshold (default 10) or more counted files would be committed, or the --confirm token does not match this changeset
  • 42 - Rebase-Review Gate: <remote>/<branch> moved and both sides changed the same file; the output lists the upstream commits, the overlapping files and their diff
  • 42 - Publish-Remote Gate: remotes.allowed_push_urls is set and the push URL of --remote is not listed in it, or --remote resolves to no push URL

See also

  • wikitool sync - the same reconcile on its own, without committing or pushing
  • instructions/gates.md - the gate procedure
  • instructions/setup-instance.md - the first publish of a new instance
  • wikitool backup run - the machine-data backup on its own
  • wikitool export run - the page export on its own

backup init

Create the backup target's bare repository and make it origin when there is none.

Synopsis

  • wikitool backup init

Options

  -h, --help  Show this message and exit.

Examples

tools/wikitool backup init

Behaviour

  • Reads the backup section of .wikitool-local.json and creates <target>/ and <target>/git, the latter as a bare repository (git init --bare -b main). An existing bare repository there, or an empty directory, is used as it is.
  • Sets origin to <target>/git when the checkout has no origin. An origin that already pushes there is left as it is; one that pushes anywhere else stops the command.
  • When remotes.allowed_push_urls exists and lacks the URL origin pushes to, prints the remotes section with that URL added, for the user to copy into .wikitool-local.json. It never writes that file: the field is a gate.
  • Pushes nothing: the next publish sends the history to the new origin.

Properties

  • Changes files: yes
  • Idempotent: yes
  • Atomic: Every precondition is checked first; a refusal changes nothing
  • Counts toward the session call budget: yes
  • Can reach the network: no

Exit codes

  • 0 - success
  • 1 - No backup section in .wikitool-local.json, or a malformed one - a target that is not absolute or lies inside the instance, a machine_name that cleans to nothing or to git, an extra path that resolves outside the instance or into .git/
  • 1 - <target>/git exists and is not a bare repository; nothing was changed
  • 1 - origin already pushes to another URL; the message names both and nothing was changed

See also

  • wikitool backup run - the machine-data half of the backup
  • wikitool publish - pushes to the bare repository and runs the machine-data backup after it
  • docs/how-to/back-up-to-a-synced-folder.md - the whole procedure, restoring included

backup run

Mirror this machine's files that are not in git into <target>/<machine>/.

Synopsis

  • wikitool backup run

Options

  -h, --help  Show this message and exit.

Examples

tools/wikitool backup run

Behaviour

  • Mirrors .wikitool-local.json, ENVIRONMENT.md if present, every file in incoming/ but .gitkeep, and the extra paths of the backup section into <target>/<machine>/, then writes the manifest .chemenu-backup.json there: time, stack version, machine name, and per file its relative path, size and sha256.
  • Never mirrors quarantine/, reports/, .wikitool-tools.json, tools/.venv/, the published skills or .git/ - unless an extra path names one of the first ones. The secrets file lies outside the instance and is never read.
  • Writes and deletes only under <target>/<machine>/. Deletes only a file the previous manifest lists and the instance no longer has; a file there the manifest does not list stays as it is. A file whose size and sha256 match the previous manifest is not rewritten.
  • A symbolic link or junction inside a mirrored directory is skipped and named in the output, never followed.
  • publish runs the same step after every push when machine_data is true.

Properties

  • Changes files: yes
  • Idempotent: yes
  • Atomic: Per file - each is written to a temporary file and renamed, the manifest last; an interrupted run leaves whole files and the previous manifest
  • Counts toward the session call budget: yes
  • Can reach the network: no

Exit codes

  • 0 - success
  • 1 - No backup section in .wikitool-local.json, or a malformed one - a target that is not absolute or lies inside the instance, a machine_name that cleans to nothing or to git, an extra path that resolves outside the instance or into .git/
  • 1 - machine_data is false
  • 1 - <target>/<machine>/ is not empty and has no manifest, or its manifest cannot be read; nothing was written
  • 1 - The target is not a directory (the synced folder is missing or not mounted), or a write failed

See also

  • wikitool backup init - sets up the target's bare repository
  • wikitool doctor - the backup line reports the age of the last run
  • docs/how-to/back-up-to-a-synced-folder.md - the whole procedure, restoring included