← Work

Agentic Workflow System

A harness-neutral workflow that routes every task through stages, gates edits on an accepted plan, and demands proof per surface before anything is called done

Role
Sole designer and builder
Status
In daily use
Period
2026
Stack
Claude Code, Codex, OpenCode

Problem

My day job is a multi-repo B2B manufacturing SaaS platform: several backend services, frontends, infrastructure, and a local copy of the whole stack in Docker. By 2026 I was doing much of that work through coding agents, across more than one harness.

Agents were not the bottleneck. The failures were around them:

  • Cold starts. Every session re-read the repos, rediscovered conventions, and repeated mistakes already paid for.
  • Edits before agreement. An agent would start changing files before anyone had agreed what should change.
  • "Done" that meant nothing. A green typecheck reported as a working feature. An agent's summary of its own work treated as proof.
  • Shared resources. Parallel agents fighting over one local environment, and over one context window that grew until the session fell over.

I wanted a system where any capable agent can pick up a task, knows exactly what stage it is in, cannot skip the parts that make the work trustworthy, and leaves behind something the next agent can use.

Constraints

  • Harness-neutral. I use Claude Code, Codex, OpenCode and Cursor, with T3 Code as an orchestration host. The system cannot live inside any one of them.
  • Markdown never grants authority. A plan file, an issue, a PR description or a handoff note cannot approve a write. Only my live words can.
  • Context is a budget. Rules have to load when a stage needs them, not all at once.
  • Hooks fail open. A broken enforcement script must never block real work.

Architecture

Me
direction, four checkpoints
Claude Code
thin adapter
Codex
thin adapter
OpenCode
plugin
Cursor
rule
AGENTS.md kernel
router, capped at 14 KB
Stage rules
rules, patterns, ADRs
Working memory
briefs, dated updates
Skills
25 authored
Hooks and gates
Bun scripts
Local runtime
journal, leases
Verification queue
one agent at a time
The kernel routes; stages, memory and gates are shared by every harness

The kernel. One AGENTS.md file is the router. It has thirteen sections and a "Start here" table that maps each stage to the files that stage needs, so detailed rules load only when they are relevant. A check caps the file at 14,336 bytes. Each harness gets a thin adapter that points back to it: a CLAUDE.md, a Cursor rule, an OpenCode plugin that re-injects the routing directive every turn, and Codex hooks.

Trust order. A policy file ranks sources: my live instructions, then live evidence, runtime state, the accepted plan, the task brief, dated history, and last a generated catalog. Issue text, PR text, logs and imported Markdown are marked untrusted input, which is the system's answer to prompt injection from a ticket.

Working memory. Each task gets a compact brief of about 4 KiB, plus dated updates for detail. A generated catalog of aliases is what routing reads. Product facts live in a separate "brain", and behaviour lessons are captured as candidates that wait for me to accept them. Nothing a model writes is promoted into memory on its own.

Skills. Skills are authored once in one folder and symlinked into every harness's skill directory, with a script that checks the links. Third-party skills are pinned by hash in a lock file.

The task lifecycle.

  1. Route. A session-start hook lists active briefs. A bare "continue" resolves through a resume script; otherwise the agent matches an explicit issue ID, a plan's memory link, or exactly one catalog alias. More than one match and it stops to ask.
  2. Intake. A brief records a profile (quick-fix, bug, feature, investigation, review-only, incident), a risk tier from R0 to R4, and the authority I gave, in my words.
  3. The ladder. Intake, understand, discover, frame, plan, implement, style, review, verify, handoff. Each stage has an artifact and an exit gate. The profile decides which stages run, and a skipped stage is recorded as skipped, not silently dropped. I am consulted at four points only: problem confirmed, contract accepted, effect approved, and blocked.
  4. Plan. The plan ends in an acceptance table where every criterion names its proof class and scenario.
  5. Implement. Work happens in a git worktree per task, inside the change contract. Every changed hunk is classified as required, optional or unrelated, and the last two are removed before review.
  6. Review. The rung depends on risk: self-audit, specialist reviewers, an independent reviewer in a separate thread, then a security pass.
  7. Verify. Fresh evidence for each proof class: source-static, test, database, browser, provider, deployment, production. Any criterion a user can see needs a narrated video, filmed by the verification queue I built into Ad Studio.
  8. Handoff. A dated update with one checks line per criterion, structured blocks for the brain and learnings, and exactly one next action.

An optional local runtime sits underneath: a daemon with an append-only journal, readiness leases, mediated file replacement with intent and receipt records, and claims with fencing so two tasks cannot hold the same resource.

Key decisions

  1. Markdown never grants authority. The policy sets markdown_can_grant: false and handoff_transfers_approval: false, and unknown effects are denied. An agent that reads "approved" in a plan file has learned nothing about whether I approved it. The trade-off is that a long-running task has to come back to me at the effect checkpoint, even when the plan already looked settled.

  2. Small kernel, rules by stage. Loading every rule into every session is how context windows die. The kernel says where to look; stage files say what to do. The cost is discipline: the kernel plus the main skill are now about 21.9 KB against a 20 KiB budget, and the check says so on every commit.

  3. Same gates, different mechanics. Claude Code hooks can read the transcript, so they check routing directly. Codex, OpenCode and Cursor use a route guard that reads git's dirty state instead. Every hook fails open. That means a crashed hook lets an edit through, which I accept because the opposite, a broken hook freezing every session, would get the whole system switched off.

  4. Warn first, then block. The edit gate started as a warning so I could see how often it fired. It now blocks any edit outside a short list of exempt paths unless some brief is at the implement stage or later, with acceptance criteria defined.

  5. Proof classes stay separate. The kernel says it in one line: an agent statement is not evidence. A passing typecheck says nothing about whether the empty state renders, and an integration suite that mvn test never runs proves nothing either.

  6. Boring memory on purpose. No vector store, no automatic promotion of model output. Briefs are files with hash-checked replacement, and a human promotes lessons. It is less clever and much easier to audit when an agent does something strange.

  7. Humans review intent; agents review implementation. Reviewer agents handle style, dead code, missing tests and "a helper for this already exists". I handle whether the change should exist, data-model fit, and the unhappy paths. One intent per PR, around 400 lines or a stack.

  8. Decide by measurement. Merging the pre-tool hooks into one looked like an obvious win; measured, it made each edit about 4 ms slower, so it was reverted. Capping Bash output and trimming skill descriptions were rejected because each would save 3% or less of tokens.

What broke

  • One malformed block stopped learning for two days. The learnings projector was all-or-nothing. A single bad block made it fail on 82 consecutive Stop hooks. It now skips the bad update and names the fields that failed; the next 76 Stop events ran clean.
  • An orchestrator ignored delegation. A coordinating thread ran native subagents instead of the delegation tool it was told to use, and said as much when asked. The diagnosis was salience, not ambiguity: the rule was right but buried. The fix was one line in the per-turn directive, not another blocking hook.
  • Personas that never loaded. Eight reviewer persona files had never parsed under the runtime's parser, and nothing checked what they returned. A contract guard and runtime validation now catch both.
  • The gate was slow, and the metric was flattering. The edit gate cost about 36 ms per edit; it now costs about 4. "Unrouted turns" dropped from 64% to 7%, but most of that drop came from fixing how routing was detected, not from agents behaving better. I report it that way.
  • Free-text phases drifted. Before the ladder, a task's phase was free text, and values like pr-open, in-review and reviewing meant the same thing to a human and three different things to a script. That drift is why the ladder has fixed names.
  • The context budget, proven live. In a test with a 120k token limit, the budget hook denied a tool call at about 133k and forced a handoff to a fresh session, which is exactly what it is for.

Results

Counted from the working memory and plans on 14 October 2026:

  • 174 task briefs and 1,285 dated updates, from 20 July to 13 October 2026.
  • 269 dated plans since March 2026, and 192 review reports.
  • 25 authored skills; about 12,000 lines of enforcement scripts with 21 test files; 14 hook entries in Claude Code and 13 in Codex.
  • A token study over 136 sessions found Bash output was 18.4% of processed tokens and file reads 9.9%.

Three tools grew out of it. Tachyon is the MCP server agents use for code search and to record why they changed something. Ad Studio's verification queue films the proof videos. AgentLens reads the harness logs so I can measure the system instead of guessing.

What I'd change

  • Learning capture works; promotion does not. There are 115 lesson candidates waiting and none accepted. The review step needs to be cheap enough that I actually do it, probably a weekly digest that accepts or rejects in one pass.
  • Get the kernel back under budget. It is about 1.9 KB over. The next stage-specific rule should move out of the kernel, not into it.
  • Retire the legacy phase values still sitting in older briefs, so every script can trust the ladder.
Next case studyTachyon →