Course · Term 16 of 30
Harness Engineering
Every lesson in this course builds one system: a stablecoin reserve attestation registry that discovers issuers, reads the PDFs they publish, and refuses to publish a field it could not read. What it is and where this lesson sits.
Closing chapter for Tier 3 Pro. Not a unit.
Why this is not a technique
There is no skill here called harness engineering. The term is a label for the union of four others: 15 Hooks (a rule becomes a program), 18 Headless Runs (a run stops needing a person), 21 Eval-Driven Loops (a change is defended with a number), and 28 Agent CI/CD (the number blocks a merge).
Learn those four and you have the whole thing. Learn this name without them and you have a word.
So this chapter does not invent a fifth skill to fill the slot. It spends its
words on the stance, and it ships the one artifact that makes the other four
usable by somebody who was not here: a Makefile where every gate and every
demo is reachable by name, from a clean clone, without reading the book.
The stance
The harness is the artifact. The session is disposable.
That is a position, and it is worth arguing rather than asserting, because the opposite position is the default and it is comfortable.
The default is that the session is where the work happens. You have a conversation, things get built, and what survives is the code plus whatever you remember about why. The context that made the decisions dies at the end of the turn. Next week a different session, or a different person, or the same person having forgotten, starts from the code and reconstructs the reasoning — usually wrongly, and always expensively.
The stance says: everything load-bearing about how this repository is worked on must be executable, and the executable thing is the deliverable.
Concretely, and each of these is a claim you can check in this repository:
A rule that is not a program is a suggestion. Term 8 measured its own
CLAUDE.md: priced on every turn, enforcing nothing. Term 15 turned the rules
that mattered into a hook. The rules that stayed prose are still prose and the
unit says so.
A check that has never failed is not a check. Twelve vacuous checks were found in this repository by applying one rule retroactively: no check ships without a committed transcript of it failing when its invariant breaks. The twelfth was found while writing term 21 — a hash invariant this course's own prose described and no test enforced.
A number without the identity of what produced it is not evidence. Term
21's suite hash exists for that reason, and term 28 blocks a merge when the
suite changed, whatever the score did. The eval corpus is frozen in
eval-corpus/ because the first version read the course's own documentation
and every unit written moved the baseline.
Judgement may produce evidence. It may not be the gate. Term 28's rule, enforced in the runner rather than in a README, because a rule that lives only in prose is the first bullet again.
The commit
Makefile, and the five demos and six gates it reaches.
make gates # gate-1 .. gate-5, reporting the boundary rather than the first miss
make demos # every tier's proof end to end, none may regress
make test # vitest
make lint # tsc --noEmit
make pdf-check # the book, with its page budget and freshness check
A gate is a script, not a quiz. Each one names the terms that unblock it and
fails saying so, from the day the scaffold lands — which is why make gates
was runnable and honest when it reported gates passing: 1 of 5.
The receipt
$ make demos
demo-tier-1 receipt -> artifacts/gate-1.json
demo-tier-2 receipt -> artifacts/gate-2.json
demo-tier-3 receipt -> artifacts/gate-3.json
demo-tier-4 receipt -> artifacts/gate-4.json
demo-tier-5 receipt -> artifacts/gate-5.json
$ make gates
gates passing: 5 of 5
That is the whole proof, and it is worth being precise about what it proves: every tier's claim is produced by a script, graded by a separate script, and both run from a clean clone by someone who has read none of this.
What reconciling the harness found
The gates were written in Stage 1, before the terms they grade. Wiring them to the finished build was not bookkeeping; it surfaced four things.
Gate 2 was grading a number nobody can produce. Its original check was
cache.hitRate >= cache.floor. Term 12 spends a paragraph explaining that a
cache hit rate is a fact about a provider's infrastructure on a particular day
and is not measurable here at all. The gate now grades the cacheable
share — how much of each request sits in a byte-identical stable prefix —
which is the part the architecture controls, and assert-2.mjs fails if a
hitRate field ever appears.
Gate 3 found a real rule violation. CLAUDE.md says every published field
carries the page it was read from. Twenty of fifty-six published fields
carried none. The offender was the word count: computed over the whole
document, read from nowhere, and with no page it could honestly cite. There
were three responses available — invent a page, delete the rule, or name the
distinction. Field.derived is the third. A read field must cite; a
derived field must declare itself derived. The rule survived contact
because it got more precise, not because it got weaker.
gen.mjs overwrote two authored interstitials. The unit loop had a
file-wins guard, added after this exact bug destroyed an authored unit once.
The interstitial loop, twelve lines below it, did not. A routine regeneration
replaced ~1,200 words of prose with stubs, and the only reason it survived is
that it had been committed twenty minutes earlier. Fixing one loop and leaving
its neighbour is how a guard becomes a thing you believe you have. The demo
scripts had the same hole and now declare themselves hand-written.
Every gate's precondition list pointed at paths that never existed. The
Stage 1 plan said evals/suite.ts, src/loop/optimise.ts, src/mcp/server.ts.
The build produced src/evals.ts, src/optimiser.ts, src/mcp.ts — one
module per term rather than one directory per term. Each gate now carries a
redefined note saying what moved and why, in the same form gate 1 already
used when its live-run requirement became impossible. The realised build is
the record; the plan is what you thought before you knew.
Where it overlaps
This chapter is the overlap, which is why it closes a tier instead of occupying a slot inside one. Line F is determinism wrapping stochasticity, and the harness is that wrapper made concrete. Line B is instruction packaging — what the harness makes executable is mostly instructions that used to be prose. Line D is execution surface: 6, 18, 17, 28, and the Makefile is where they all become one command. Line E is the trust boundary, which is the one thing the harness cannot make executable, and term 26 is why that matters.
Sharp edges
-
A Makefile is only as honest as its targets.
make gatesreports the boundary rather than stopping at the first miss, because the useful output is how far the repo got. A target that hides a failure to look tidy is worse than no target. -
The harness has no tests of its own. The gates test the repository; a gate with a bug in its assertions passes silently.
assert-*.mjsis hand-written for exactly that reason — the generated part is the precondition list, and judgement is the part that should never be templated — but hand-written is not the same as verified. -
Four terms is the union today. If a fifth thing becomes load-bearing about how you work, it belongs in the harness and this chapter's list is wrong until it is updated. An umbrella term that never changes its contents has stopped describing anything.
A personal teaching project, in development. The registry it builds is a teaching artifact and is not an assurance opinion about any issuer. Not affiliated with or endorsed by any employer, or by any vendor whose tools it describes. Where a unit depends on a specific flag or path, it names the version it was verified against. All units.