docs · architecture
Architecture.
How the harness actually works as of v0.24 (a Rust binary with the three-verb surface from the v0.15 “slim” cut), and why it is shaped this way. Decisions that are expensive to reverse have their own records under docs/decisions/; this page describes the running system.
The One Idea.
Committed markdown is the truth. The binary is a scaffolding tool, not a runtime.
zharness owns exactly three verbs — install, update, uninstall — that manage a doc set inside a consumer git repository. The whole lifecycle (brainstorm → to-plan → work → check → handoff) runs from the committed markdown, repo scripts, and a pre-commit hook, with the binary absent from PATH. Delete the binary and every derived artifact, and the same lifecycle still executes, because the only irreplaceable bytes are the ones Git is already tracking.
cli/src/main.rs one Rust binary (clap), three verbs
|
v
install scaffolds the managed set, records a base, reports brownfield read-only
update fresh-overwrites playbooks/WORKFLOW.md; hash-guarded AGENTS.md block replace; PROJECT.md only when absent
uninstall removes the managed set; consumer bytes are never destroyed
The Three Verbs.
The zharness CLI operates strictly as a repository maintenance tool:
zharness install
ScaffoldScaffolds the managed doc set, writes the base manifest into .zharness/base/, and emits a read-only brownfield report. Never overwrites unmanaged files.
zharness update
SyncPlaybooks and WORKFLOW.md are pure upstream mirrors — always overwritten, no merge, no conflict. PROJECT.md is written only when absent. The AGENTS.md block is replaced between its markers; if it was edited since the last write, update prints the diff and writes nothing unless --force. --check reports drift without writing.
zharness uninstall
TeardownSafely removes managed files, restores original files backed up at install time, and preserves any locally modified consumer files with a warning.
all_targets (cli/src/installer/mod.rs) is the managed set: docs/WORKFLOW.md, docs/PROJECT.md (scaffolded from identity template), and the eight playbooks (six stages plus the work-full and check-validation companions). AGENTS.md is handled separately and surgically — only the marked ZHARNESS block inside it is swapped; consumer prose around it is untouched.
State lives in .zharness/base/: a manifest.json of {path, sha256} entries recording what zharness last wrote, and the ownership ledger ownership.tsv. There is no merge and no conflict state: every refusal check runs before the first write, so a refused update leaves every file as it was (ADR 0011).
The Fail-Closed Guards.
Fail-closed guarantees live in the pre-commit hook (scripts/install-git-hooks.sh, shared core between the # ZGUARD-CORE markers):
Proof Re-execution
A newly added ## Validation entry with verdict APPROVED (or APPROVE_WITH_REQUESTS) has every nested proof command re-executed by the pre-commit hook. Any non-zero exit immediately rejects the commit.
Independent Judge
On an active plan marked lane: high-risk, a newly added Validation entry carrying judge: same-session is strictly rejected. High-risk changes mandate an independent session or reviewer.
Independent Judge (full)
A newly added Validation entry that declares mode: full and judge: same-session is rejected, on every lane. Full checks require an independent judge.
At Most One Active Plan
More than one non-empty file under docs/plans/active/ is rejected; zero is a valid idle state.
CI workflows (.github/workflows/cli-ci.yml) re-run these checks on pushed commits, so bypassing local git hooks gains nothing. The hook reads staged bytes directly; there is no pass marker or hash that an authoring agent can forge. Handoff absorb is playbook protocol, not a hook: final close writes absorb: none or names an existing ADR/guard/memory.
Embedded Doc Set & Projection.
The zharness binary carries two embedded filesystems under cli/docs/embedded/:
- The Managed Set:
AGENTS.md,WORKFLOW.md, andplaybooks/. Projected intodocs/upon install or update.docs/playbooks/*.mdis a byte-identical projection of the embedded source: editcli/docs/embedded/, never the projection. - Templates:
templates/— emitted on demand (e.g.PROJECT.mdidentity template), never projected automatically.
State Model: What Lives Where.
The state model is entirely filesystem-driven and Git-native:
docs/plans/active/*.mddocs/PROJECT.mddocs/memory/*.mddocs/decisions/docs/playbooks/cli/docs/embedded/; synchronized via zharness update..zharness/base/manifest.json) of what zharness last wrote, plus the ownership.tsv ledger; gitignored.Evolution: v0.15 to v0.24.
In versions prior to v0.15, the harness utilized secondary derived indexes, complex caching layers, and background runtime assumptions.
In the v0.15 “slim” release, all secondary indexes, background runtime assumptions, and legacy commands were completely eliminated. The resulting architecture is zero-daemon and zero-database: committed markdown files are the sole source of truth, and the binary is strictly a three-verb scaffolding tool.
v0.24.0 rewrote that binary from Go to Rust. The verbs, flags, exit codes, written bytes, and manifest schema are unchanged, and the stripped binary shrank from about 3.0 MiB to about 574 KiB (darwin/arm64).