Repository navigation
Architecture
How the kit is put together: the component map, the data flow between the four phases, the design decisions behind each tool (every one of which exists because a real run broke without it), the recovery architecture that survives context loss, and the CI pipeline that keeps the kit honest. All statements are traced to source at commit 7e17a11 or to tests executed during this wiki run.
- The shape of the system
- Directory layout and responsibilities
- Layering
- Data contracts between phases
- Design decisions (and the incident behind each)
- Recovery architecture
- CI architecture
- What the architecture deliberately does not do
- See also
The kit is not a program; it is a pipeline an agent walks, with one tool per job and files as the only interface between phases. Nothing is in-memory across phases: each phase reads the previous phase's files and writes its own. That is deliberate — the files are the recovery points.
flowchart LR
subgraph P1["PHASE 1 — research (~40%)"]
A1["setup/bootstrap.sh<br/>capability + egress report"] --> A2["git clone SUBJECT"]
A2 --> A3["scan/repo_scan.py --brief<br/>inventory in 2 minutes"]
A3 --> A4["lib/serve.py + probe/webapp_probe.py<br/>RUN the subject"]
A4 --> A5["research/*.md + facts.json<br/>+ sources.md sections A-D"]
end
subgraph P2["PHASE 2 — architecture (~3%)"]
B1["page tree in PROGRESS.json"] --> B2["ban.json<br/>(from sources.md section D)"]
B2 --> B3["terms.json<br/>(from the glossary)"]
end
subgraph P3["PHASE 3 — writing (~45%)"]
C1["scaffold/new_page.py --init<br/>QA-clean skeletons"] --> C2["fill pages"]
C2 --> C3["qa/fix_toc.py<br/>rebuild every Contents block"]
end
subgraph P4["PHASE 4 — QA + delivery (~12%)"]
D1["qa/qa_wiki.py --self-test<br/>then --strict to 0 errors"] --> D2["qa/verify_commands.py<br/>re-run documented commands"]
D2 --> D3["publish/publish.sh<br/>commit + bundle + PUBLISH.md"]
D3 --> D4["REPORT.md<br/>incl. limits of analysis"]
end
P1 --> P2 --> P3 --> P4
The effort split is measured, not aspirational: the reference run (MD.Viewer, 22 files, 73,013 words, 0 QA errors) spent ~40% of its effort on research, ~3% on wiki architecture, ~45% on writing and ~12% on QA and delivery. Phase 3 is only fast because Phase 1 was thorough.
github-wiki-llm-writer/
├── SKILL.md # agent entry point (Agent Skills frontmatter + workflow)
├── AGENTS.md # phase-to-tool mapping, tool table, workspace discipline
├── PROMPT.md # ready-to-paste task prompt; only the header block changes
├── PUBLISHING.md # verified publishing flow + the two traps
├── CHANGELOG.md # the version history (no git tags exist)
├── REWORK-PLAN.md # completed hardening plan from the field report (evidence trail)
├── setup/ # bootstrap.sh · PITFALLS.md · check_runtime.py · snapshot.sh
├── scan/ # repo_scan.py
├── lib/ # req.py · serve.py · session.py · evidence.py
├── probe/ # webapp_probe.py (+ SUITES registry)
├── scaffold/ # new_page.py
├── qa/ # qa_wiki.py · fix_toc.py · verify_commands.py
├── publish/ # publish.sh
├── templates/ # ban.json · terms.json · STATE/PROGRESS/REWORK-STATE templates
├── examples/ # reference-run artefacts + run2-mdscribe (1571-line battery)
└── .github/workflows/selftest.yml
Measured size at 7e17a11: 44 files, ~621 KB, 15 code files, 5,523 code lines; the largest logic files are qa/qa_wiki.py (802), probe/webapp_probe.py (457), scan/repo_scan.py (433), scaffold/new_page.py (353), lib/serve.py (325). Dependencies: none declared — no manifest of any ecosystem exists; the floor is Python 3.8+ and bash, with PHP only when the subject is a PHP app.
Three layers, with strict dependency direction:
flowchart TB
subgraph L3["orchestration"]
W["webapp_probe.py<br/>(batteries + suites)"]
N["new_page.py"]
Q["qa_wiki.py / fix_toc.py"]
P["publish.sh"]
end
subgraph L2["capabilities"]
S["serve.py<br/>ServedApp"]
R["req.py<br/>Response tuple"]
SE["session.py<br/>SessionClient"]
E["evidence.py<br/>write_evidence"]
SC["repo_scan.py"]
end
subgraph L1["substrate"]
PY["python stdlib: urllib, socket, subprocess,<br/>json, re, tempfile, pathlib"]
SH["bash + git + (php for PHP subjects)"]
end
W --> S
W --> R
W --> E
S --> R
SE --> PY
Q --> SH
P --> SH
N --> PY
SC --> SH
S --> SH
R --> PY
E --> PY
Rules the layering enforces in practice:
-
req.pyis the only HTTP primitive;serve.py,session.pyandwebapp_probe.pyall build on the same(status, headers, body)contract, so a status code observed anywhere is comparable everywhere. -
qa_wiki.pynever executes wiki code: shell blocks go throughbash -nwith the text on stdin (not as an argument), Python blocks throughcompile(). The auditor is safe to run on untrusted prose. -
fix_toc.pyimportsgithub_slug/mask_codefromqa_wiki.pywhen shipped insideqa/, so the anchor algorithm has exactly one implementation; the standalone fallback exists only for copies outside the tree. - No layer makes any LLM API call. The intelligence lives in the agent operating the kit; the kit's job is to make every claim checkable.
| Artefact | Producer | Consumer | Contract |
|---|---|---|---|
research/scan.json |
repo_scan.py |
agent (Phase 1 notes) | full inventory: git, license, deps, files, symbols, hosts, signals, config keys |
| staged instance | serve.py |
probe batteries | clean docroot, verified-free port, process-group teardown |
research/probe.{json,md} |
webapp_probe.py |
Phase 3 pages, sources.md §B |
every request: status, ms, bytes, headers, 180-char excerpt, expectation match |
research/facts.json |
agent | Phase 3, recovery | every value carries source: code:file:line | readme | git | test:name | web:url
|
research/sources.md §D |
agent | ban.json |
claims deliberately NOT made, with reasons |
PROGRESS.json |
agent (Phase 2) |
new_page.py --progress, resuming agents |
page tree + groups + style contract + do_not_claim
|
wiki *.md (spaces in names) |
Phase 3 |
qa_wiki.py, publish.sh
|
filenames with spaces; [[Page Name]] links; computed anchors |
| QA JSON | qa_wiki.py --json |
PROGRESS.json qa block |
errors/warnings/infos/stats incl. per-page table |
commit + bundle + PUBLISH.md
|
publish.sh |
the owner | branch master, verified bundle, three push options, never force-push |
REPORT.md |
agent (Phase 4) | the owner | inventory, verification methods, doc-vs-code findings, limits of the analysis |
| Decision | Incident that forced it |
|---|---|
ServedApp stages a clean copy on a verified-free port and raises on a busy preferred port |
A hand-rolled php -S 127.0.0.1:8080 & died on "Address already in use"; every subsequent test hit the OLD server with OLD state, producing plausible, false conclusions (PITFALLS A1) |
req.py returns 4xx/5xx as data and never follows redirects |
A probe suite that raises on 404 silently loses exactly the cases it exists to document; a login 302 is part of the contract |
session.py keeps its own cookie jar |
dict(resp.headers) collapses multiple Set-Cookie headers — stateful flows break invisibly |
| CSRF token goes to body field AND header, never URL | The MD.Scribe subject rejected query-string tokens outright (403, no hint why) |
write_evidence() falls back instead of raising |
A field run's final evidence write hit FileNotFoundError while the shell saw the directory fine a second later (PITFALLS B11) |
check_runtime.py exits 2 on a runtime-floor mismatch |
Probing a subject under PHP 8.2 that needs 8.3 measures a corpse — every "finding" is fiction (PITFALLS B10) |
qa_wiki.py --self-test is mandatory doctrine |
Three checker bugs produced 381, 12 and 235 phantom errors in real runs; "a checker that cannot fail is worse than no checker" |
fix_toc.py replaces only the list block, with a word-count guard and --bak
|
The first TOC regenerator ate a page's lead paragraph, badges and an alert on a page lacking a --- separator (PITFALLS D8) |
| Scaffold derives every link from the actual page list | Hardcoded [[Glossary]]/[[Getting Started]] in templates became site-wide dead links for any other page list (PITFALLS D7) |
| Ban/terms match a scrubbed view with negation downgrade and exemptions | A field run produced 10 QA errors and zero real problems — negations, refutations, verbatim subject strings, anchor slugs and domain terms all fired (PITFALLS C8) |
publish.sh never prompts, never force-pushes, configures origin on fallback init |
Sandboxes have no controlling terminal; wiki repos carry the owner's Initial commit; snapshots strip .git/config (PITFALLS A3/B6) |
.gitignore refuses run workspaces, and CI enforces it |
A 184-file / 4.4 MB run workspace sat one git add -A away from being published with the kit (CHANGELOG 1.2.1) |
Long agent runs get compacted or interrupted. The kit treats resumability as a first-class property:
flowchart LR
L["context lost /<br/>session interrupted"] --> O["1. ls workspace + head STATE.md"]
O --> R["2. read REWORK-STATE.md log<br/>(append-only, newest last)"]
R --> E["3. rebuild environment<br/>(bootstrap.sh, clone)"]
E --> V["4. re-run last verification<br/>command from the log"]
V --> N["5. continue from 'Next action'"]
F["research/facts.json +<br/>sources.md"] -.->|"resume WRITING without<br/>re-reading subject code"| N
Five artefacts carry the state: STATE.md (orientation: phase, layout, confirmed facts, checklist, next action), PROGRESS.json (per-page status/words, style contract, do_not_claim, QA numbers, delivery status), research/ (the five Phase-1 files — with facts.json + sources.md a fresh agent resumes writing without re-reading the subject), REWORK-STATE.md (resume recipe + environment rebuild + sandbox quirks + append-only log), and snapshot.sh git snapshots at phase boundaries so a bad bulk edit costs minutes, not hours. The doctrine: update these at phase boundaries, not at the end.
This wiki run exercised the recovery layer for real: the run's REWORK-STATE.md log recorded a sandbox path-rewriting discovery mid-Phase-1 (the harness rewrote /home/user inside command strings but not inside script files — PITFALLS B4 manifesting at the tooling layer), which a resuming agent would otherwise have re-debugged from scratch.
.github/workflows/selftest.yml (triggers: push to main/master, PRs, manual; matrix Python 3.9/3.11/3.12, fail-fast: false) runs ten steps that mirror the kit's own doctrine — including negative controls, because a gate that cannot fail is theatre:
| Step | What it proves |
|---|---|
Python ast.parse on every .py; bash -n on every .sh; json.load on every tracked .json
|
nothing ships syntactically dead (the JSON step exists because examples/research/facts.json once shipped as Markdown with a fence inside) |
--help on all 11 Python tools + a NOTE for any .py neither exercised nor excused |
a new tool cannot ship with a broken CLI invisibly (six tools once were invisible to CI) |
qa_wiki.py --self-test |
the auditor can fail — 22 detections |
fix_toc.py --self-test, evidence.py --self-test, session.py --help
|
the 1.1.0/1.2.0 tools are exercised (their live PHP run is a Phase-1 task, not CI) |
scaffold round-trip × 3 page lists → qa_wiki.py --strict
|
skeletons are QA-clean by construction, including the degenerate one-page case |
--ban/--terms contract fixtures |
a prose phrase fires, an inline-code phrase stays silent, an all-meta ban file warns — the step cannot pass vacuously (the 1.2.1 fix after inline-code masking turned it into a no-op) |
| documented paths exist | every file the docs link is present |
| workspace-refusal | no v3.*/, uploads/, incoming/, workspace-fallback/ present, unignored or tracked |
Per CHANGELOG 1.2.1, the full gate was verified on a real GNU/Linux host (Debian 12, bash 5.2.15, Python 3.11.2) under three shells, with negative controls: a dead ban loader, a reverted structural-key filter, a deleted documented file, an unignored v3.2/, and a tool with a broken --help each make their step exit non-zero. This wiki run replicated the runnable steps locally (T4–T8): all green.
- No daemon, no service, no state of its own. Every tool is a one-shot process; state lives in files you can read, diff and back up.
- No LLM calls. The kit cannot "write" anything; scaffolds are templates with slots. This keeps the output auditable and the dependency surface at zero.
-
No language lock-in at the transport level. The batteries speak HTTP;
ServedAppspeaks a command template. PHP is the default, not a constraint (--serve-cmd 'node {root}/server.js','python3 -m http.server {port} -d {root}'). CLI-shaped subjects skipserve.pyentirely and shell out — the reusable part is the shape: declarative cases → evidence JSON → Markdown tables. - No trust in its own templates. The scaffold round-trip and the ban/terms fixtures exist because the kit's own templates shipped bugs (D7: hardcoded footer links; 1.2.1: the template banned its own structural keys).
- Tool Reference — per-tool flags, exit codes and internals
- The Four Phase Workflow — walking the pipeline step by step
- Research and Probing Guide — Phase 1 in depth
- Sandbox Survival Guide — the environment assumptions the architecture makes
- Changelog and Versioning — how each layer earned its incident-driven feature
github-wiki-llm-writer 1.2.1 · MIT · Repository · Report an issue · Home · Glossary · FAQ · Troubleshooting
Facts in this wiki are verified against the source at commit 7e17a11; see Changelog and Versioning.
Home Getting Started Features and Verification Model Comparison with Alternatives
Installation and Setup Configuration Reference
The Four Phase Workflow Research and Probing Guide Writing Pages Guide QA and Audit Guide
Publishing and Delivery Security Considerations Sandbox Survival Guide Extending the Kit
Tool Reference Architecture Troubleshooting FAQ Changelog and Versioning Glossary
1.2.1 · MIT