Skip to content

About

A level-3 agentic harness — the companion code to Appendix A of the book Agentic Programming.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Repository files navigation

level-five (l5)

tests License: MIT

A level 3 agentic harness built by following Agentic Programming by Jerod W. Wilkerson. The harness is a story execution system: stories enter with an approved plan, move through implementation, testing, documentation, and verification, retry when verification fails, and end completed or escalated — or pause, when capacity runs out, and continue where they stopped.

About the name. The name is aspirational. What you'll find here is a level 3 harness, but level five is where the ladder leads, and the repository is built to grow in that direction.

Companion to the book

This repository tracks Agentic Programming through level 3. Part 3 (Chapters 12–19) explains how an agentic harness works, and Appendix A, "Building a Sample Level 3 Harness," builds this one from an empty directory to a working system, including the real escalations that happened along the way.

The book is at agenticprogrammingbook.com.

The appendix is the starting point, not the finish line. It stops at a deliberately small harness so the essential structure stays readable, and then hands you a roadmap: harden first, following Chapter 18, then scale, following Chapter 19. This repository is walking that roadmap. Every improvement arrives the way the book says it should — as a story the harness plans, executes, verifies, and documents itself.

The appendix state

The exact harness Appendix A describes is tagged appendix-a. If you are reading the appendix and want to check your own build against it, or want to start where the appendix stops, use that tag:

git clone https://github.com/jerodw/level-five.git
cd level-five
git checkout appendix-a

main has moved past it. The appendix's code excerpts — the workflow definition, the implementer prompt, the coordinator's routing loop — match the tag, and the differences on main are the point rather than drift.

What the harness does now

Everything below arrived as a story the harness planned, executed, verified and documented itself. Where the book argues for a capability, the chapter is named.

Planning. An interactive session interviews you and writes an approved story artifact, which it validates, commits, pushes, and offers to run. A story can be planned from typed text or from a brief the harness filed earlier (--brief). The session proposes which workflow the work belongs under and will not begin the interview until that is settled, because a workflow's stage list shapes the interview. The session plans with the same repository standards every judged stage is judged against, and where the work would end in a new standing claim under a workflow with no stage confined to the tests directory — no stage whose work is validation — it says so and does not start the interview. Before scope is written it sweeps the target's declared source directories, architecture documents and tests directory for every text that states the rule the story changes and lists what it found for approval. Before it writes tasks and criteria it works through a standing set of questions about what the change will break, drawn from the classes of problem the harness's own reviews keep filing, and says what each found — and, where one found nothing, where it looked. The questions about a second place doing the same job, or a written statement the change makes untrue, reach past that sweep; the duplication question covers test helpers and fixtures as well as the code under test; and a criterion names the property wanted rather than a spelling or a fixed list standing in for it. A problem planning finds in the part of the system the story changes becomes part of the story. One may be left out only because it belongs elsewhere, the workflow cannot do it, or it waits on a decision the developer chooses to defer, and it then goes in a list of its own, each item with the developer's reason, shown just before approval; a plan that schedules work on a file no stage may change is refused. Every path a plan leaves alone carries the developer's reason, saying what leaving it alone protects so a change that would not harm that is not blocked — an entry without one is refused before the artifact is committed, and an entry that would block what the work needs is settled with the developer before approval. The interview thinks at the highest level and the short turn that picks a workflow at medium, both stated by the harness rather than inherited from the developer's own settings. The draft the developer approves opens in plain English — what is wrong or wanted, what the work does, what it leaves alone — with the harness's own vocabulary kept for the paragraphs after. Planning happens on a worktree of its own, so a plan written during a run does not land in that run's branch. (Ch. 15, Ch. 18)

Execution. The Story Coordinator advances a story stage by stage, assembling each stage's context, injecting it into a prompt and invoking the agent headlessly. Two workflows ship: the story workflow, and a refactor workflow whose correctness claim is that behaviour is unchanged and whose implementer is guarded by a suite census instead of the create and revert checks. Each stage declares how hard its agent thinks, with the reason beside the value, so effort is spent where being wrong is expensive and does not follow the developer's own editor. A run works in a worktree of its own, carrying the build state the configured commands need. Runs of one repository take the machine in turn for their whole-suite runs: a run waits for a configured number of suite runs' worth of time, measured from what suites have taken here and from how long the current holder has held the turn, and a wait that expires takes the turn for one run rather than letting every waiter start at once. (Ch. 12–17, Ch. 19)

Governance. Every writing stage declares what it may write and carries a check that records what it wrote — the implementer what it may not create, the tester the directory it is confined to, the documenter the architecture documents the target configures. An edit outside a confinement is not forbidden but asked about: it is undone where the suite passes without it, and where the suite needs it a confined stage's edit is still undone and routed to the stage that owns the work, because a stage may not author what it validates. A stage that check governs is told it can settle the question by naming one test that passes on the tree it leaves and fails once its edit is undone, so the answer takes seconds rather than a whole-suite run. A deny-only tool guard travels with each invocation, and a stage that executes no suite cannot invoke one. (Ch. 15, Ch. 18)

Verification and routing. The verifier writes a verdict and the coordinator routes from the artifact: advance, retry, or escalate. A retry goes to the stage that owns the defect, named by the verifier from a category table the workflow declares, carrying structured guidance. A verdict may also report that no retry can finish the work, which escalates without spending the budget. On a passing verdict the suite runs again in a fresh clone with the story committed. A stage that fails mechanically runs again in place on a budget retries do not share. A red suite is carried forward for the verifier to judge rather than handed back to whichever stage ran last. A finding about the story's own change whose fix lies outside the plan's scope is not declined for that alone: the verifier extends the scope within declared bounds — never a blocked path, never a path a stage restriction forbids, and a path the developer left alone only where the verifier has recorded that it read the reason and the reason is about a different concern than the fix — and every extension is announced in the log and on the console. A finding whose fix needs a new test is repaired rather than declined, because the repair carries forward through the stage whose work is validation, and the verifier names the assertion that stage owes. A finding whose fix needs a design choice the plan left open is repaired where an existing source — a standard, the architecture document, or how the code already solves the same problem — settles it: the verifier names the source, and the decision, with its reason, its confidence and the cost of being wrong, is committed for review. The next check may reject it, which sends the finding to the backlog; a decision citing no source is never acted on. (Ch. 17–18)

Pausing and resuming. An invocation stopped because capacity ran out is not a failed one, so the run pauses rather than escalating: nothing is reset, nothing archived, the work is committed, and the run either waits in place or exits for a later l5-run to continue where it stopped. (Ch. 19)

Inspection and briefs. A completed story is inspected — the files it changed, the architecture documents it changed, plus what git tracks directly beside them. A finding about the story's own change is handed to that story's verifier as evidence; a finding about the code around it is filed as a brief. An inspection whose own process died is told apart from one that looked and found nothing: it runs again, and where it still cannot be made the run says so rather than reporting an empty result. Every routed finding is fixed, filed, or named in the run's own accounting, so none can go quiet: a finding counts as fixed exactly when a check in its attempt fixed it, whichever check that was, and one the run could not fix — its repair passes spent, its fix needing a file the run may not touch, or its design choice rejected — reaches the backlog. A finding complete in every other respect but naming no workflow to plan it under is filed with the target's configured one supplied and marked as supplied rather than chosen; a finding naming a workflow the harness does not define is still refused, and so is one missing any other required part. Size is a reason to fix and never a reason to file: a finding the verifier judges correct and too small to fail the run is corrected or repaired in the run, and one too small to be worth even that is dropped by name rather than sent to the backlog. Briefs open in the same plain English a story does, carry a category, a severity, a confidence and the area of the system they concern, and land in a triage column that classification already decides. (Ch. 18)

The outbox. Work to file externally goes to a durable local queue first and reaches a tracker on a sweep, so a provider that is slow or unreachable can never turn a finished story into a failed one. Entries are deduplicated by identity, receipts are kept apart from the queue, and a partly-filed entry says so. A read of the tracker that fails is tried again a few times before an update is given up, since a dropped connection looks like a misconfiguration from outside; a write is attempted once. (Ch. 17)

Observability. A run opens by saying what the whole story is expected to take — the time if it goes straight through, the time if it is sent back, and how often that happens — taken over the stories the history already holds and excluding time spent waiting for the machine; a target with too few stories to say so is told nothing. l5-status reports runs and their stages. Every run leaves its state, the same events in two renderings, a record of every retry, and the artifacts each stage produced. Long steps announce themselves before they start and say how long they are expected to take, from what they have taken before; a fresh target with no history announces nothing invented. What a run and an inspection cost is recorded where a reader meets it. A completed run's report says what became of every finding it made — repaired, sent to the backlog and on what grounds, discarded, or abandoned — and lists every decision it made on the developer's behalf, costliest to get wrong first. That account is committed, so it outlives the run's working files, and the assist agent reads it when asked how a story went, opening each cited source and saying where it does not support the choice. (Ch. 18)

What's next

The roadmap, in the order it is being worked:

  • One defect is filed once per run, however many attempts the run takes. An inspection reads the change on each attempt and re-derives the name it files under, so one defect can reach the backlog three times.
  • A planning session runs under a declared tool policy, as a stage does.
  • A completed story can be sent back for work without editing its branch by hand.
  • The architecture document is inspected as the code is — it is changed by nearly every story and read by no inspection.

Further out, in the order Chapters 18 and 19 recommend: per-agent logs and a watcher, a fuller hook-based tool policy in place of the static allowed_tools allowlist, an adjudicator, parallel story execution, and a real initialization library.

How it was built

Every capability above arrived as a numbered story, and each story's commit is titled with the behaviour it produced. So the list of what has changed since the appendix is one command rather than a document somebody maintains:

git log appendix-a..main --oneline --grep="^story-"

The approved plan behind each is committed in .harness/stories/, and the reasoning — why a thing is the way it is, and what it deliberately does not do — is in .harness/docs/ARCHITECTURE.md, which the documenter maintains as a stage of every run.

Run directories are execution state rather than source, so .harness/runs/ is gitignored and does not travel with a clone. The runs worth keeping — the escalations, and the runs preserved for what they showed — are copied into .harness/runs-archive/.

Prerequisites

The harness runs on macOS and Linux only. It does not run on Windows: the coordinator takes its suite lock with fcntl, bounds every configured command with a POSIX process group, and links build state into worktrees with symlinks, and the reference tracker script is bash with jq. WSL is untested.

  • Claude Code CLI (claude) with an active subscription
  • Python 3 (3.10+)
  • Git

The harness itself has no third-party runtime dependency — it uses only the Python standard library. Running its test suite needs what requirements-dev.txt declares; see Tests.

Scripts

All harness capabilities are invoked through l5- scripts in scripts/:

Script Purpose
l5-init Initialize a .harness/ structure in a target repository
l5-plan Plan a story interactively, from request text or from a filed brief named with --brief; commits and pushes the artifact, then offers to run it
l5-run Execute an approved story through its workflow
l5-status Show a snapshot of story runs, or one run's detail
l5-assist Launch the interactive assist agent with harness context and the skills the harness ships
l5-check Report whether a stage's required outputs are present, fresh and valid — the coordinator's own check, reachable while the turn is still open. It decides nothing
l5-inspect Inspect a scope of the code deliberately and file what it finds as briefs
l5-sync Drain the outbox, reporting what landed, what is pending, and what no sweep will clear on its own
l5-prune Prune the cross-run history to history_retention_days on the default branch and commit the rewritten files; a run only reports what is past the bound

Example:

scripts/l5-plan "Add a --dry-run flag to l5-run"
scripts/l5-plan --brief <the key the tracker reported for a filed brief>
scripts/l5-run story-001
scripts/l5-status

Layout

workflows/       workflow definitions: stages, artifact routes, retry rules
schemas/         JSON Schemas for the structured artifacts, plus their manifest
prompts/         reusable agent prompt templates ({{placeholder}} injection)
plugin/          the skills the harness loads into the sessions it starts
orchestration/   the Story Coordinator and its supporting modules
rules/           execution rules enforced by the coordinator
scripts/         thin l5- entry points
hooks/           the deny-only tool guard each invocation carries, and the
                 turn-end check that asks a stage for the outputs it owes
templates/       starter files l5-init copies into a new target repository
tests/           the coordinator's test suite, run without model calls
.harness/        target-repository state: config, standards, stories and
                 docs/ARCHITECTURE.md; plus runs, logs and the outbox queue,
                 which are gitignored execution state

The harness pieces are reusable across target repositories. The .harness/ directory is target-repository state; run l5-init to create it in any other repository you want the harness to work on.

This repository is both the harness repository and its own first target repository. Every demo story is a real harness feature, so the harness participates in building itself from the start.

Looking for the architecture document? It is .harness/docs/ARCHITECTURE.md, not a top-level docs/. Its location is a configuration value rather than a fixed part of the layout: architecture_docs in .harness/config.yaml names it, and the coordinator injects whatever that key names into the implementer's context on every run. It sits beside .harness/standards/ because both are agent context, and a target repository is free to keep them elsewhere.

How a story runs

  1. Plan. l5-plan interviews you, writes the story artifact, validates it, commits and pushes it, and offers to run it.
  2. Enter. l5-run hands the story to the Story Coordinator, which stands the run on a story branch in a worktree of its own and creates a run directory under .harness/runs/<story-id>/.
  3. Advance. The coordinator walks the workflow's stages, assembling context, rendering the stage prompt and invoking the agent. After each turn it checks what the stage produced: the required artifacts are present, they were written by this attempt, they satisfy their schemas, and what the stage changed is what it was allowed to change.
  4. Judge. The documenter runs before verification, so what it writes is judged rather than taken on trust. The verifier writes a verdict; the coordinator routes from it.
  5. Repair or retry. A failing verdict routes to the stage that owns the defect. A passing verdict carrying small findings re-enters a stage to act on them without spending a retry. A stage that failed mechanically runs again in place.
  6. Confirm. On a passing verdict the suite runs again in a fresh clone with the story committed, because the working tree is the one place that commit does not yet exist.
  7. Inspect. A completed run is inspected, and what it finds reaches either this story's verifier or the backlog.

Every one of these steps has a reason, a boundary and a set of things it deliberately does not do. Those are in .harness/docs/ARCHITECTURE.md, which the documenter maintains as a stage of every run.

Tests

The Story Coordinator is deterministic and fully unit-tested without any model calls (a fake runner plays back scripted stage artifacts). Run the suite with:

.venv/bin/python -m pytest tests/ -q -n auto

That is test_command in .harness/config.yaml verbatim, and running it verbatim is the point: the harness's own gates — the revert check, the coordinator's suite run in the tree, and the clean-clone check — execute the configured command, so what a developer runs and what the gates run cannot drift apart. -n auto reads the core count of whatever machine it lands on, so no core count is written down anywhere.

The dependencies that command needs are declared in requirements-dev.txt, and it has to be installed into both interpreters this repository configures — the one you run the suite in and the one verification_runner names:

.venv/bin/pip install -r requirements-dev.txt
.venv310/bin/pip install -r requirements-dev.txt

Install it into only the first and your local suite is green while the clean-clone check dies on an unrecognized argument, because that check runs the same command under the second interpreter. That is the failure this instruction exists to prevent.

A test that asserts something is absent carries a demonstration that it can fail: the same check, run again over a case where the thing it looks for is present. That negative case is a mutation of a shipped file or of the reader, read back through the same reader the positive assertion runs — never a string the test built by concatenating the searched-for text onto it, which asserts a property of Python and stays green through any edit to the subject. A standing check reports that shape rather than leaving it to the rule: a test that plants a phrase into a value and then asserts that same phrase against that same value, without handing the result to the check it claims to demonstrate, is named by the suite. It reads what a test does rather than what it calls itself, so a control that never uses the word "control" carries no exemption.

Contributing and feedback

This repository tracks the book through level 3, so its scope is what Part 3 and Appendix A describe. Small fixes — genuine bugs, or errors in the code and its docs — are welcome via pull request. Improvements the book's roadmap calls for are welcome as issues; they are best planned and executed through the harness itself, which is the whole point of it. Changes that would take the harness past level 3, or in a direction the book does not argue for, are out of scope here.

Found a bug in the harness code? Open a GitHub issue. For anything about the book's content — typos, unclear passages, errata — please use the feedback form at agenticprogrammingbook.com/feedback rather than GitHub Issues.

About

A level-3 agentic harness — the companion code to Appendix A of the book Agentic Programming.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages