>the work

These are the full write-ups behind the numbers on the home page, including an interactive explorer of the real 75-ticket drain graph and the pipeline from intake to production.

##case-studies

Production work, written up in terms that travel outside the company that paid for it.

[01] loops-and-graphs

##prototype-to-drained-epic

The largest run of the pipeline so far, followed all the way through: a 42-minute prototype walkthrough became 351 requirements and a dependency graph of 73 tickets, and parallel agent lanes drained it in 30 waves over a little more than four days. It was the second full run, so it is also where I could see what the first run had taught the system.

75
tickets merged in 30 waves
~4.2d
first → last merge
6d
prototype walkthrough → last merge
351
requirements authored before the drain
17–19
tickets merged per day, four days running
75/75
merges approved by a human

The first full run was in July. The pipeline ingested a product designer's prototype, we authored 114 requirements before any code was written, and agent lanes drained a 45-ticket epic in about 35 operator-paced hours, with 44 of the 45 slices merged across roughly 92 PRs in 3 repos, 111 of 111 end-to-end scenarios green, nothing reverted, and eight human decisions steering all of it. I presented that run publicly as "Agentic Loops and Graphs" at the Irvine AI meetup. In August I ran the same pipeline a second time on a larger feature, and what I wanted to find out was how much of the first run carried over.

It started on an engineering sync, where a teammate walked me through his prototype for 42 minutes, and I saved the transcript on purpose because I wanted it to be spec input. The prototype was frozen before the first reader ran, so every requirement candidate points at a line in the snapshot. Thirteen autonomous readers then mapped it against the shipped specs in about 25 minutes and extracted 511 requirement candidates with no errors. One of those readers compares new work against shipped decisions, and I had invented it during the first run, and this time it caught the new prototype reversing a decision the first run had made three weeks earlier. The first run had eight human checkpoints that I discovered along the way. This time I designed two gates up front, ruled seven scope decisions at them, and held one unit open on a question only the product owner could answer, which parked that unit without stalling the rest. The next day I ruled 60 of 61 pending candidates in one sitting with the product owner, and by the time the drain started the spec held 351 requirements.

Two mint workers turned the spec into 70 tickets in 31 minutes overnight. The first worker died of context overflow at ticket 36 and a fresh worker finished the job, and between them they produced two duplicates, which were caught and canceled before any lane started them. What surprised me was that only one of the two came from the restart. The other was the same requirement minted twice by the same worker under different vocabulary, so the fix I took away is a check that no two tickets cover the same set of requirements. Four more tickets were discovered while the drain was running, and each one landed inside the workstream that should have owned it. I had estimated about 66 slices at decomposition and the run executed 75, so the early count was good to within about 17%.

The drain ran 30 waves over a little more than four days. It started with two isolated lanes, one backend and one frontend, and went to three lanes from wave 13, and once it warmed up it merged 17 to 19 tickets a day for four days in a row, through a model-provider outage, a macOS permissions incident in the middle of a wave, and two nights where my machine simply went to sleep. Both lanes survived the permissions incident from their resume manifests. The third lane bought about 32% more tickets per hour and cost about 11% per lane, because the merge fences serialize and the lead is shared. I offered myself a fourth lane near the end and took it back within the hour, so I treat three as the practical ceiling on my hardware.

Partway through I asked why the drain kept choosing deep tickets while a pool of shallow ready ones sat waiting, and we answered it with a simulation over the real graph. Scheduling policy barely moves the finish line, because the graph is dominated by total work: the capacity-limited lower bound was 29 waves against a critical path of 10 tickets, and the real drain took 30. The better argument for going deep first is about information. Every expensive unknown lived on the deep runtime chain, including a response key that our own client had documented wrong and a branch of execution that no ticket owned, and a defect found early on a chain has no consumers to rework yet. The price was that QA had nothing testable until the chain closed, and I ruled on that trade explicitly once I could see it.

The human side of the run taught me the most. At mint time the pipeline proposed 53 human gates, because its rule put a review gate on every heavy-risk ticket, and only 3 of them ever fired, while the eight interventions that actually changed the course of the run had no gate pointing at them. I approved all 75 merges myself and requested no changes on any of them. As trust grew the fence got cheaper, and by the later waves the lead would stage a wave's PRs with a report and I would release them with one word. I now read that clean approval record as a warning as much as a result, because of what QA found afterward, which is the next case study.

// the drain, replayed — sanitized interactive record

This is the real dependency graph from the 75-ticket epic, laid out by build depth, so every arrow flows left to right and each column became buildable the moment the column before it merged. The tall columns are where the drain could run lanes in parallel, and the critical path of 10 tickets is the floor. Click a slice to trace what it waited on and what it unblocked, and to see which of the 30 real waves built it. I have scrubbed the identifiers and titles, but the topology, the risk tiers, the wave and lane assignments, and the timestamps are all real.

WS-A · config APIWS-B · runtime chainWS-C · list & editor frameWS-D · option-one editorWS-E · groupsWS-F · applicabilityWS-G · rule boardWS-H · templatesWS-I · cross-site copyWS-J · flag & navH heavy riskL lightX canceled duplicate+ minted mid-drain
depth 0
depth 1
depth 2
depth 3
depth 4
depth 5
depth 6
depth 7
depth 8
depth 9
parallel lanes over time · aug 20 → aug 24 (pacific) · solid = autonomous build · hatched = waiting at the human merge fence
Aug 20Aug 21Aug 22Aug 23Aug 24S-02 · wave 1 · all-sites table card, roster, search, summary line — built in 576 min, waited 9 min at the fenceS-01 · wave 2 · config aggregate schema + per-site read — built in 106 min, waited 3 min at the fenceS-73 · wave 2 · wave-1 card conformance to ruled rows — built in 79 min, waited 12 min at the fenceS-07 · wave 3 · feature flag + RBAC gate on every endpoint — built in 64 min, waited 197 min at the fenceS-09 · wave 3 · per-site frame: entry, sub-tab shell, session reset — built in 114 min, waited 148 min at the fenceS-05 · wave 4 · resolved config: option literal, wire mapping, branch isolation — built in 87 min, waited 11 min at the fenceS-08 · wave 4 · derived row cells: option chip, needs-setup, coverage — built in 67 min, waited 17 min at the fenceS-16 · wave 5 · derived offer set recomputed at call time — built in 79 min, waited 43 min at the fenceS-21 · wave 5 · both hosts: dashboard placement, mode source, tab isolation — built in 92 min, waited 31 min at the fenceS-03 · wave 6 · applicability read model + audited toggle endpoint — built in 105 min, waited 24 min at the fenceS-71 · wave 6 · settings hub + top-nav composition (four flag combinations) — built in 90 min, waited 36 min at the fenceS-15 · wave 7 · weekday eligibility in the site's timezone — built in 131 min, waited 8 min at the fenceS-26 · wave 7 · applicability panel in the org-mode tab — built in 131 min, waited 13 min at the fenceS-13 · wave 8 · applicability badge, scoping copy, zero-agent state — built in 77 min, waited 70 min at the fenceS-28 · wave 8 · payload on the agent's per-call config seam + off-switch suppression — built in 122 min, waited 23 min at the fenceS-04 · wave 9 · group schema, CRUD API, name validation — built in 67 min, waited 17 min at the fenceS-29 · wave 9 · option-one strict request builder (single id and group union) — built in 64 min, waited 23 min at the fenceS-40 · wave 10 · option-two request builder: never an empty resource list — built in 105 min, waited 3 min at the fenceS-14 · wave 10 · groups sub-tab list + empty state — built in 89 min, waited 20 min at the fenceS-52 · wave 11 · weekday-grouped fan-out + fixed request parameters — built in 112 min, waited 49 min at the fenceS-27 · wave 11 · add / edit group modal — built in 68 min, waited 94 min at the fenceS-17 · wave 12 · type reference data read with live overlay — built in 276 min, waited 28 min at the fenceS-38 · wave 12 · delete group with consequence confirmation — built in 288 min, waited 16 min at the fenceS-19 · wave 13 · PUT option + per-resource save contract — built in 81 min, waited 291 min at the fenceS-24 · wave 13 · step 1: choose-an-approach option cards — built in 67 min, waited 305 min at the fenceS-06 · wave 13 · legacy column non-interaction guard — built in 676 min, waited 9 min at the fenceS-18 · wave 14 · resource reference data from the upstream service — built in 354 min, waited 7 min at the fenceS-32 · wave 14 · two-commit staging, save bar, eight-state status line — built in 113 min, waited 148 min at the fenceS-30 · wave 15 · PUT option-one selection + derived offer set — built in 60 min, waited 33 min at the fenceS-44 · wave 15 · focused-editor takeover chrome + per-kind action clusters — built in 78 min, waited 24 min at the fenceS-74 · wave 15 · strict-branch execution, unowned until the chain surfaced it — built in 25 min, waited 22 min at the fenceS-12 · wave 16 · derived offer set + verbatim selection storage — built in -4 min, waited 57 min at the fenceS-25 · wave 16 · rule board frame: resource columns, pills, locked columns — built in 33 min, waited 25 min at the fenceS-31 · wave 16 · PUT option-two assignment board with three day-states — built in 150 min, waited 10 min at the fenceS-11 · wave 17 · template persistence: record shape, CRUD contract, copy-not-reference — built in 49 min, waited 12 min at the fenceS-37 · wave 17 · day-state semantics: default row, inherit/custom/closed, chip anatomy — built in 35 min, waited 7 min at the fenceS-41 · wave 17 · group CRUD with dual-namespace name validation — built in 29 min, waited 18 min at the fenceS-43 · wave 18 · template CRUD with provenance back-reference — built in 32 min, waited 13 min at the fenceS-51 · wave 18 · board edit session: snapshot, save gate, guarded rollback — built in 47 min, waited 15 min at the fenceS-62 · wave 18 · final write contract, id carry-forward, conflict re-offer — built in 70 min, waited 15 min at the fenceS-10 · wave 19 · copy preview + write boundary: target-site intersection, naming — built in 28 min, waited 64 min at the fenceS-63 · wave 19 · empty-result posture: no search, graceful handoff, next date — built in 27 min, waited 74 min at the fenceS-65 · wave 19 · apply from template: modal, staged overwrite, provenance link — built in 42 min, waited 51 min at the fenceS-55 · wave 20 · write-boundary validation invariants + saved-empty semantics — built in 28 min, waited 68 min at the fenceS-61 · wave 20 · edit-mode source panel: search, sort, three-way filter — built in 37 min, waited 52 min at the fenceS-64 · wave 20 · fan-out bounds: cap, parallelism, per-call timeout, degradation — built in 62 min, waited 25 min at the fenceS-36 · wave 21 · step 2: read-only scope panel — built in 83 min, waited 675 min at the fenceS-54 · wave 21 · cross-site copy preview: id intersection with kept/dropped — built in 82 min, waited 676 min at the fenceS-68 · wave 21 · create template from a resource: entry points, eligibility — built in 83 min, waited 682 min at the fenceS-49 · wave 22 · focused scope editor: select all, clear, count, save gate — built in 37 min, waited 62 min at the fenceS-53 · wave 22 · config-summary list for the all-sites table — built in 54 min, waited 43 min at the fenceS-75 · wave 22 · write-side activity gate across every config write — built in 76 min, waited 22 min at the fenceS-22 · wave 23 · shared import modal shell: one component, two kinds, three entries — built in 66 min, waited 48 min at the fenceS-23 · wave 23 · templates sub-tab + list view — built in 50 min, waited 43 min at the fenceS-59 · wave 23 · stale selections surfaced as deselectable rows — built in 61 min, waited 45 min at the fenceS-33 · wave 24 · import item list: kept/dropped rows, warning states, CTA gating — built in 31 min, waited 89 min at the fenceS-35 · wave 24 · template editor shell: name, uniqueness clash, save gate — built in 25 min, waited 92 min at the fenceS-58 · wave 24 · create group from inside the scope editor — built in 9 min, waited 104 min at the fenceS-45 · wave 25 · group copy lands prefilled in the add modal — built in -19 min, waited 34 min at the fenceS-47 · wave 25 · template editor day board + source panel — built in -16 min, waited 42 min at the fenceS-60 · wave 25 · unsaved-changes guard on option switch + cancel — built in -18 min, waited 39 min at the fenceS-46 · wave 26 · template copy lands as an unsaved draft — built in 12 min, waited 58 min at the fenceS-56 · wave 26 · unsaved-changes guard on every dismissal path — built in 32 min, waited 40 min at the fenceS-69 · wave 26 · board controls: column filters, multi-select, toolbar — built in 18 min, waited 50 min at the fenceS-48 · wave 27 · template row actions: duplicate, copy-from, delete — built in 15 min, waited 635 min at the fenceS-67 · wave 27 · placement gestures: drag-drop + keyboard click-to-arm — built in 595 min, waited 55 min at the fenceS-76 · wave 27 · scope rows adopt the shipped selector anatomy — built in 20 min, waited 630 min at the fenceS-50 · wave 28 · derived offer set + stale-assignment reconciliation — built in 40 min, waited 0 min at the fenceS-57 · wave 28 · mid-edit template fill with overwrite confirmation — built in 40 min, waited 0 min at the fenceS-70 · wave 28 · in-context group create entry points + copy landing — built in 40 min, waited 0 min at the fenceS-39 · wave 29 · group offerability semantics across both options — built in 26 min, waited 1 min at the fenceS-66 · wave 29 · template editor unsaved-changes guard + draft clearing — built in 29 min, waited 1 min at the fenceS-72 · wave 29 · agent dashboard configuration tab — built in 28 min, waited 2 min at the fenceS-34 · wave 30 · retention hint on the templates tab — built in 16 min, waited 25 min at the fenceS-77 · wave 30 · import modal conformance trio — built in 14 min, waited 31 min at the fencewave 1 · first lane starts overnightwave 30 · last merge · epic drained

All 75 buildable slices shipped and merged across 30 waves. The two marked X were duplicates that the mint produced, and both were caught and canceled before any lane started them. The four marked + were gaps the drain itself discovered, and each was minted as its own ticket inside the workstream that should have owned it. The long hatched tails are mostly nights when I was asleep.

// outcome

What carried over from the first run is the part I am proudest of. The front door had become a documented command, prototype anchors were frozen at intake, the reader that defends earlier decisions ran by default, and the pipeline began measuring itself with a telemetry manifest at every mint, which is where the forecasting numbers above come from. A third project on a different product began adopting the pipeline before this run had finished, with another engineer porting the prompts, and that is the clearest evidence I have so far that the context is in the repository where other people can pick it up.

[02] suite

##compound-engineering-suite

The engine underneath the drain: 15 compound commands, 6 spec commands, and a deterministic debate engine, all sharing one build spine, and since September 2026 running from intake all the way through the production deploy and the soak watch after it.

37%
QA re-open rate before test-cases (35 of 93)
~15%
QA re-open rate after
7/7
reopened tickets caught in blind re-derivation
17
requirement-analysis areas per ticket
5
durable artifacts per run
0
pipeline approvals an agent can grant

Two pipelines share one engine, and the triage front door in front of them accepts the work in whatever form it arrives — a prototype, a Figma file, a ticket, a meeting transcript, a freeform prompt. Small work runs a single lane to a PR, and big features go through the spec-decomposition funnel and end in an epic drain. Whether a lane is running solo or as part of a drain, it runs the same spine: real-data sync, a test-case ledger gate, a file-domain-isolated strike team with a QA loop, live verification against the running app, multi-lens read-only review, fix-every-finding iteration, and learnings capture before ship.

The newest stage sits between triage and planning, and it came out of a retrospective on the epic in the case study above. Its first release had drained clean, 75 tickets with every lane green, and by the time QA tested it a follow-on delta had brought the epic to 93 tickets. QA reopened 35 of them, which is 37%. I ran a fix drain over those 35 and QA reopened 13 of them, which is 37% again, and the same number twice is what bothered me, because it meant that whatever produced the reopens had not been touched by anything I changed between the two drains. When I traced the findings, 22 of the 43 attributable ones sat in coverage categories that QA's own derivation rules name and that my pre-PR verification had never named, and about half of everything QA found was a spec gap or a ruling rather than a code defect. The tickets had carried no test cases, so verify had been executing its own guesses. None of the 36 pre-PR test plans had driven the UI in a real browser either, although the verify prompt said a browser drive was mandatory, and the cause turned out to be one shared browser profile that parallel lanes could not use at the same time. So I split QA's ticket-testing skill in two and pulled the derivation half left of the build. It reads the ticket with every comment and attachment, runs a 17-area requirement analysis, and derives cases under a shared rules file whose version is pinned into every ledger, so the pre-build and post-merge derivations cannot drift apart. Rules generate scenarios and only spec rows may supply expected outcomes, so a rule with no governing row becomes a typed spec-gap line with a proposed value, two rows that disagree become a conflict line that is never buildable as-is, and every one of those lines needs a human disposition before an implementer may start. Each case gets a content-hashed ID so a refresh keeps the unchanged ones, a substrate tag that says whether a unit run, an API call, a real browser, or a human can prove it, and a dependency list so a superseded spec row invalidates the cases that leaned on it. The ledger is then executed by the implementer's QA teammate, by verify in a real browser, and finally by QA after merge, all from the same list, which is posted once as a single comment on the ticket. As the acceptance test I re-ran the derivation blind on seven reopened tickets with their inputs frozen to pre-build state, and it reproduced the later QA finding on all seven. The re-open rate since has been about 15%.

Before anything executes, the plan has to survive an argument. The debate engine runs a 5-lens reviewer panel covering correctness, coverage, consistency, exhaustiveness, and test-case coverage, and each reviewer traces the requirement through the codebase before it is allowed to read the plan. Then an author agent validates every blocker against real code. All of this is deterministic workflow code with severity gates, round caps, token reserves, and a formal dispute-escalation path, and when the disagreement is genuine that path hands it to the human rather than resolving it automatically.

Everything runs under budgets and stop conditions: spawn caps, a 3-strike rule keyed on root-cause tags rather than raw failure counts, broken-baseline hard stops, and an operator-owned-decision rule that stops the loop and hands back whenever a genuine product call comes up. Subagents never return prose. Every fan-out is schema-validated so that orchestration is always consuming typed data.

The far end now reaches production. I wrote the deploy stage by encoding what I had improvised by hand during one release night: a live-state pre-flight that caught a missing database default-privileges grant, a pipeline watch that turned a base-image CVE failure into a dependency-bump PR within minutes, and an unprompted filtered log watch while the team tested. The command reads live production state first and builds a go/no-go card from it. It computes the last green run from stage results, because a non-gating post-deploy test stage makes every run read as failed, lists the pending migrations in dependency order, checks the grants, and requires a rollback answer with a named database-restore owner before the card can go green. On a green card it queues the backend pipeline and then the frontend one, and a named human approves the pipeline gate every time, because the agent has no approve path at all. Every red stage is classified against a fixed playbook, and the ledger write lands before any action. Verification compares each workload's revision, image tag, traffic split, migration head, grants, and cache headers against expected text written into the release ledger ahead of time, so no row can pass by construction, and an unreadable check is recorded as blocked rather than failed, which means a tunnel outage can neither open the pointer bump nor print the rollback commands. Then a soak reads the error logs every ten minutes, subtracts a noise list and everything already seen, and turns every new signature into a row for a human to look at. The release ledger itself is a self-contained HTML page committed to git with append-only events and no server behind it, and the close-out tags the release on the ledger's own merge commit.

// pipeline — intake to production
  1. intake
    prototype · PRD · ticket · bug · transcript · prompt
  2. spec-decompose
    map the intent, freeze prototype anchors
    scope
  3. spec-reconcile
    diff against the shipped specs
    accept / reject
  4. spec-author
    mint permanent, immutable IDs
  5. spec-to-tickets
    vertical slices wired by blocked-by edges
    epic approval
  6. compound-test-cases
    cases and spec gaps derived before any code
    spec rulingsnew · 2026-09
  7. compound-plan
    executable plan
  8. compound-debate
    5-lens reviewer panel argues against the plan
  9. compound-master
    9-step spine: sync · ledger gate · work · verify · review · fix · iterate · learnings · dial
  10. compound-drain
    N parallel lanes over the ticket graph
    PR merge
  11. compound-deploy
    pre-flight card · launch · red-stage playbook · verify · soak · close-out
    GO card + pipeline approvalnew · 2026-09
  12. monitor
    filtered error-log soak while humans test; release ledger and tag

marks a human gate. The two outlined stages are the September 2026 additions: one pulls QA's test-case derivation left of the build, and the other carries a merged range through the production pipelines and the watch afterward. Small work skips the spec funnel and goes triage → test-cases → plan → debate → master → PR.

// outcome

The same engine serves supervised daily work, unattended epic drains, and now the production release, and what changes between them is the throttle rather than the guarantees. It fails closed. If a budget is exceeded or a schema does not validate, the workflow halts and escalates instead of quietly degrading.

[03] survey

##cross-team-prompt-pattern-survey

I surveyed another team's prompt suite, found five patterns that travel, adopted them into a different greenfield codebase, and wrote up the playbook.

Prompt engineering is converging across teams, and as far as I could tell nobody was harvesting the parts that travel. So I surveyed a brownfield team's suite, roughly 6,800 lines with 21 slash commands and 5 subagent templates, and I read it the way you would read another engineer's library, asking what is generalizable and what is load-bearing on their particular context.

Five patterns travelled: risk-driven mode selection, file-domain isolation between subagents, task-log files as durable phase contracts, active-fixer review agents, and frontmatter-registered subagents. I adopted all five into a different team's greenfield suite, and then I wrote a teammate-facing report so that other AI engineers could pilot the same patterns without having to redo the archaeology themselves.

// outcome

One engineer's archaeology lifted the capability of several teams. The skill underneath it is pattern extraction across codebases, and what it really takes is knowing which conventions survive translation into a new codebase and which ones were only scaffolding for the old one.

##contact

// get in touch
[loc]
Redondo Beach, CA

// open to roles and engagements building AI SDLC · enterprise engineering teams · AI labs