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.

zharness — architecture overview Rust
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

Scaffold

Scaffolds the managed doc set, writes the base manifest into .zharness/base/, and emits a read-only brownfield report. Never overwrites unmanaged files.

zharness update

Sync

Playbooks 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

Teardown

Safely 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):

Guard 1

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.

Guard 2

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.

Guard 3

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.

Guard 4

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, and playbooks/. Projected into docs/ upon install or update. docs/playbooks/*.md is a byte-identical projection of the embedded source: edit cli/docs/embedded/, never the projection.
  • Templates: templates/ — emitted on demand (e.g. PROJECT.md identity template), never projected automatically.

State Model: What Lives Where.

The state model is entirely filesystem-driven and Git-native:

Path
Classification
Purpose & Guarantees
01
docs/plans/active/*.md
Authoritative
The single active initiative record; append-only Log and Validation.
02
docs/PROJECT.md
Authoritative
Repository identity and architecture scope, locked during brainstorm phase.
03
docs/memory/*.md
Authoritative
Project memory as markdown files; agents search and grep directly.
04
docs/decisions/
Authoritative
Permanent architecture decision records (ADRs) with sequential numbering.
05
docs/playbooks/
Projected
Projected from cli/docs/embedded/; synchronized via zharness update.
06
.zharness/base/
Bookkeeping
sha256 manifest (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).

Ready to see how the playbooks and workflow run?
View Workflow Explore CLI Verbs