Skip to content

Publishing and Delivery

Mikhail Deynekin edited this page Oct 14, 2026 · 1 revision

Publishing and Delivery

How a finished wiki directory becomes a live GitHub Wiki: the seven steps of publish/publish.sh, the git mechanics of .wiki.git repositories (branch master, placeholder history, bundles), the two traps documented in PUBLISHING.md, and the REPORT.md contract that closes a run. All flows verified end-to-end during this wiki run (T15) against an uninitialised wiki remote — including the fallback path most sandbox runs take.

Contents


The wiki repository model

A GitHub Wiki is its own git repository, separate from the code:

Property Value Consequence
Remote https://github.com/OWNER/REPO.wiki.git clone, commit and push like any repo
Default branch master, not main pushing to main creates an orphan branch GitHub ignores (PITFALLS C2)
Creation GitHub creates the remote only after the first page is saved through the web UI a fresh project's .wiki.git does not exist until someone clicks "Create the first page"
Existing history usually one commit, Initial commit, with a placeholder Home.md commit on top; never force-push over history you did not create (PITFALLS C3)
Publishing only the default branch is live branches in a wiki repo are working space
Filenames spaces → hyphenated URLs; extension selects the renderer; `\ / : * ? " < > ` forbidden in titles

publish/publish.sh — seven steps, no prompts

bash publish/publish.sh workspace/wiki/OWNER.REPO.wiki OWNER/REPO          # hand-over mode
bash publish/publish.sh workspace/wiki/OWNER.REPO.wiki OWNER/REPO --push   # attempt the push too
Step What happens Failure behaviour
0 Pre-flight absolutises the wiki dir (a relative path silently breaks after the later cd); counts .md files; notes hyphenated filenames without blocking (the QA step is authoritative; a title like Self-Updater Guide.md legitimately has a hyphen) exits 1 on a missing dir or zero pages
1 QA gate qa_wiki.py --strict on the wiki dir "Fix the errors above, then re-run." — exit 1
2 Clone git clone …REPO.wiki.git; reports the default branch and existing history on clone failure (uninitialised wiki, private repo, no network): falls back to git init -b master and configures origin, so the hand-over survives snapshots that strip .git/config (PITFALLS A3), and prints the "create the first page" instructions
3 Copy + stage copies *.md, git add -A, prints git status --short —
4 Commit identity from GIT_AUTHOR_NAME/GIT_AUTHOR_EMAIL (default wiki-bot <wiki-bot@localhost>); subject from WIKI_COMMIT_SUBJECT (default docs(wiki): complete wiki for REPO); body embeds file and word counts computed inline "nothing changed — the wiki already matches" when the tree is identical
5 Bundle git bundle create … master (fallback --all), then git bundle verify with the output printed the verification line is the proof the bundle is complete
6 PUBLISH.md writes the hand-over next to the wiki dir: prepared-clone path, branch, commit SHA, remote, bundle path, file count, push instructions, bundle-apply instructions, the never-force-push rule, and a post-publish checklist stdout is gone after a context loss — a file is not (the 1.2.0 rationale)
7 Push only with --push failure prints the expected reason (missing credentials) and the two manual recipes; this is the normal sandbox outcome, not an error

Environment overrides: PUBLISH_OUT (clone dir, default <wiki-parent>/publish), PUBLISH_BUNDLE (default <wiki-parent>/<REPO>.wiki.bundle).

Measured (T15, this run): QA gate passed on the scaffolded wiki (0 errors); the subject's own .wiki.git remote is uninitialised, so the fallback engaged — git init on master, origin configured, commit a59eaa2 created with the derived message, bundle verified okay, PUBLISH.md written. The whole script prompts for nothing and needs no credentials.

The three ways to publish (from PUBLISH.md)

Option A — push the prepared clone (on a machine with GitHub access):

cd workspace/wiki/publish
git log -1 --format='%an <%ae> | %cn <%ce>'   # confirm attribution first
git push origin master

Option B — apply the bundle to a fresh clone:

git clone https://github.com/OWNER/REPO.wiki.git
cd REPO.wiki
git pull /abs/path/REPO.wiki.bundle master
git push origin master

Option C — just copy the files (when the wiki already has history you want to keep linear):

git clone https://github.com/OWNER/REPO.wiki.git && cd REPO.wiki
cp /abs/path/workspace/wiki/OWNER.REPO.wiki/*.md .
git add -A && git commit -m "docs(wiki): complete wiki" && git push origin master

Warning

Cloning a bundle directly needs an explicit branch: git clone -b master REPO.wiki.bundle outdir. A bundle carries no HEAD symref; without -b the clone fails with "remote HEAD refers to nonexistent ref" and leaves an empty working tree — after which every later check passes vacuously (PITFALLS B8). git pull <bundle> master inside an existing clone is unaffected.

After publishing, verify in the browser (the checklist PUBLISH.md prints): the sidebar renders on every page; [[wiki-links]] resolve to hyphenated URLs; mermaid diagrams render; > [!NOTE]-style alerts render as coloured callouts.

The two traps from PUBLISHING.md

1. Unrelated histories

A kit-built repository and an existing GitHub repository have different root commits, so a plain merge is refused:

$ git pull /path/to/kit.bundle main
fatal: refusing to merge unrelated histories

The fix is one flag, and the result is honest history — the owner's Initial commit stays reachable:

git merge --allow-unrelated-histories --no-edit FETCH_HEAD

Expect add/add conflicts on files both sides created (typically .gitignore, README.md); resolve in favour of the incoming version when the incoming side was written for that repository.

2. .gitignore silently dropping files on the copy path

GitHub's "New repository" screen offers ignore templates; the Python template contains a bare lib/ rule, which excludes any directory named lib — including one holding real source. The danger is asymmetry:

Publish path Effect of a lib/ ignore rule
merge from a bundle files arrive through history, already tracked → kept; ignore rules do not apply to tracked files
copy files then git add -A new untracked files → silently dropped; the add succeeds, the commit is created, and the files are simply not in it

The defence is a deep check after publishing: compare git ls-files against the intended tree (and git diff --cached --diff-filter=D --name-only must be empty before committing). This trap is not hypothetical for this kit — its own lib/ holds req.py and serve.py, which CI and the probe tool both import; the rule was lost once in a merge and restored in 1.2.1.

REPORT.md — closing the run

The final artefact lives at workspace/wiki/REPORT.md and has a fixed contract (from PROMPT.md Phase 4 and the shipped example examples/REPORT.md):

  1. Page inventory with per-page word counts and totals (the auditor's stats table is the source).
  2. What was verified and how — the empirical test list with evidence paths; which runtime versions were actually used ("verified on PHP 8.2.34, documented target 8.3+" is an honest and useful statement, not a defect).
  3. Findings where the subject's own documentation is wrong — each with the reproducing test and the source line that disagrees.
  4. Sources — the §A/§B/§C ledger.
  5. What remains to be improved, including the limits of the analysis: what could not be executed, what was reasoned from source rather than observed, which environments were not tested.

Then update STATE.md and PROGRESS.json (delivery block: prepared commit, bundle path, bundle_verified, push_status), take the final snapshot, and mirror the workspace to its backup.

Attribution and identity

Commits created by publish.sh default to wiki-bot <wiki-bot@localhost> — deliberately generic. Set GIT_AUTHOR_NAME/GIT_AUTHOR_EMAIL when the wiki should carry a person's or a bot account's identity; GitHub attributes commits by the email's account. The PUBLISH.md hand-over asks the pusher to confirm attribution (git log -1 --format=…) before pushing, because after a push the identity is public history.


See also

Clone this wiki locally