Autonomous Remediation

An overnight audit with Kimi, an epic derived with Opus, and a workflow that drove twenty-two findings from issue to merged PR in a single afternoon — no human gate between a finding and the branch. This is how the run actually went.

Version 1.0 · Updated

01

Two Runs, Inverted Polarity

On 2 June 2026, Roxabi's agent factory turned its own multi-agent machinery on its own codebase. A quality audit had already swept the repository — 69 agents across 20 waves, eight domains, 3,291 files — and distilled 1,258 raw issues into 22 actionable findings. This is the report of what came next: paying those 22 findings down, end to end, with no human gate between an issue and its merge.

22findings
19/19PRs merged
4 h 32 mwall-clock
0human merge gates

The audit and the remediation are the same shape run in opposite directions. The audit was a read fan-out: many agents looking, nothing changing. The remediation was a write fan-out followed by a serialized merge: many agents changing, one branch absorbing. Same machinery, inverted polarity.

PhaseWall-clockScaleOutput
Audit (precursor)2 h 20 m69 agents · 20 waves · 8 domains1,258 issues → 22 actionable
Remediation (this run)4 h 32 m19 leaves · 4 waves · auto-merge22 findings shipped · 4 CI debt-gates
02

From Audit to Epic

The remediation did not start from a hunch — it started from a plan, and the plan was machine-built the night before. The word “audit” covers two different instruments here, and they should not be confused.

Deterministic scannerDeep multi-agent audit
Whata suppression-marker debt metrica qualitative read fan-out
Howaudit_quality_debt.py · make quality-debt-report69 agents · 20 waves · 8 domains, driven by Kimi
Outputquality-debt-report.json (counts)a manifest + 8 domain reports → 1,258 issues
Cadenceevery PR — informational CI, never blocksone-off, on demand (overnight)

The scanner is the standing pulse — cheap, deterministic, always on. The deep pass is the diagnosis. It ran overnight as a read-only fan-out — many agents reading, none writing — partitioning 3,291 files across eight analysis domains in twenty waves, driven by Kimi: a low-cost, long-context model routed through the LiteLLM proxy, which is what makes a 69-agent sweep affordable to run while you sleep. It surfaced 1,258 raw issues, ranked them P0–P3, and synthesised them into a committed manifest and eight domain reports. That read half has its own write-up — the companion Multi-Agent Audit — and its method is a reusable artefact, the audit playbook.

A pile of 1,258 issues is not a plan. So Opus took the highest-severity findings — the P0 and P1 issues — and did the work that turns symptoms into a plan: it derived the root causes behind them. Not “fix these two hundred lines”, but “these are the handful of structural causes those lines share”.

Those root causes became epic #1662 — 22 actionable findings. The epic was decomposed into leaf tasks, and the leaves were wired together with native GitHub blocked_by edges into a dependency graph: where two fixes touched the same file, one was marked blocked by the other; where footprints were disjoint, no edge. That graph is the schedule.

Deep audit Kimi · 69 agents 20 waves · 8 domains Root cause Opus · from P0 / P1 findings Epic #1662 22 actionable findings blocked_by shared ⇒ serial disjoint ⇒ parallel Workflow one wave per invocation
From a model's overnight reading to a machine-walkable schedule. Kimi audits, Opus root-causes, the epic decomposes, blocked_by edges encode the order — and the workflow just walks the graph.

The dependency graph is the contract between planning and execution. The epic's blocked_by edges are the wave schedule — the harness reads them, it does not invent them.

03

The Harness

The schedule is data; the harness is the engine that executes it. epic-1662-wave.mjs is a Workflow script — deterministic JavaScript control flow (loops, fan-out, conditionals) wrapped around model-driven agent() calls. Each invocation runs exactly one dependency-wave of leaves and, deliberately, does not merge — merging is the main loop's job. That split is what lets each wave's dependents branch from an already-merged base.

Inside, every leaf runs the same pipeline, adapted to its size.

① Implement — size-adaptive S → one impl agent (isolated worktree) F → PLAN ⇒ sub-impl ×N (disjoint files) ⇒ INTEGRATE PR · decision trace ② Review · 5 lenses in parallel correctness · axial · security · tests · rules blocking / major ③ Validate · 2 skeptics / finding refute by default — survive only if confirmed confirmed ④ Fix · apply → re-gate → push at most two rounds, then defer residual ≤ 2 rounds Defer → sibling issue main loop (not the workflow) CI green → +reviewed → auto-merge → next wave
One leaf, end to end. The dashed box is the only part outside the workflow script: the harness produces a reviewed PR and stops; the main loop merges and re-invokes it for the next wave.
  • Implement. A small leaf gets one implementation agent in an isolated git worktree. A large one is first decomposed by a PLAN agent into disjoint file-groups, implemented by parallel sub-agents (no two touch the same file, so the merge is conflict-free by construction), then stitched together by an INTEGRATE agent. Either way it ends in a PR carrying a decision trace.
  • Review. Five lenses read the diff in parallel — correctness, axial architecture, security, tests, and rules-compliance — each a specialist agent. Only blocking and major findings advance.
  • Validate. Every advancing finding faces two skeptics, each prompted to refute it by default. A finding survives only if confirmed — plausible-but-wrong review noise dies here.
  • Fix & defer. A fixer applies the confirmed findings, re-runs the gates, and pushes. The loop runs at most twice; anything still unresolved is filed as a deferred sibling issue, blocked by the original — the batch never stalls on a stubborn finding.

The shape is small enough to read in one screen — a parallel review panel, then a per-finding skeptic vote:

epic-1662-wave.mjs · review → validate
// 5 lenses read the diff in parallel — each a specialist agent
const reviews = await parallel(LENSES.map((L) => () =>
  agent(reviewPrompt(n, L, diff), { agentType: L.agent, schema: FINDINGS_SCHEMA })))

// keep blocking/major, then refute each finding with 2 skeptics
const verdicts = await parallel(findings.map((f) => () =>
  parallel([1, 2].map((k) => () =>
    agent(validatePrompt(n, f, diff, k), { schema: VERDICT_SCHEMA })))
    .then((votes) => ({ f, real: votes.some((v) => v.real) }))))

const confirmed = verdicts.filter((v) => v.real).map((v) => v.f)

Four properties hold for every agent in the run, by construction:

  • Typed output, never prose. Every agent is forced to emit a structured result against a JSON schema — the failure mode “agent ended in a paragraph instead of a result” is designed out.
  • One contract, injected verbatim. The same RCA rules (§6) go into every prompt, so nineteen uncoordinated agents reason the same way.
  • Retry at the inference level. A tryAgent wrapper retries three times with escalating back-off — the fix for the opening-night overload storm, now a default.
  • Isolate, defer, continue. A leaf that throws is caught, recorded, and skipped; the wave never aborts on one failure. Concurrency is capped at three issues in flight, for merge cadence.
04

From Reviewed PR to Merged

The workflow hands back a reviewed PR; the main loop takes it from there — and this is where it stays autonomous. The split matters: the workflow develops, the main loop merges, so dependents always branch from a freshly-merged base.

1
CI gate
ci and the secret scan must be green — the same checks the agent already ran locally.
2
Label + auto-merge
Add the reviewed label, turn on gh pr merge --auto. No human approval in the path.
3
Strict-base watcher
staging requires up-to-date branches; a poll loop runs update-branch on every BEHIND and aborts on DIRTY.
4
Merged → unblock
The merge releases the leaves that were blocked_by it, opening the next wave.
5
Next wave
Re-invoke the workflow; its agents branch from the base the last wave just merged.

The merge gate was CI green + a reviewed label + the in-run cross-agent review — and nothing else. No human approval stood between a passing PR and the staging branch.

05

Scheduling by Footprint

Nineteen leaves cannot all run at once — some touch the same files, and two agents editing one file produce a merge conflict, not a feature. The scheduler used a single predicate, read straight off the epic's blocked_by graph: disjoint footprint ⇒ parallel; shared file ⇒ serial. Parallelism was capped at three concurrent agents, and authorized only where footprints were provably disjoint.

WAVE SCHEDULE TIME → W1 9 leaves parallel fan-out W2 3 leaves gated start W3 5 leaves strictly serial W4 2 leaves serial disjoint footprints core · infra · config waits on a W1 merge share ci.yml · pre-commit · stack.yml share a CLAUDE.md
Four waves, scheduled by footprint: parallel where leaves are disjoint, serial where they share a file. Serializing W3 — five leaves all editing the CI config — kept the batch conflict-free.
WaveLeavesStrategyWhy
W19parallel fan-outdisjoint footprints — core, infra, config, adapters
W23gated starteach depends on a W1 merge
W35strictly serialall share ci.yml · pre-commit · stack.yml
W42serialshare a CLAUDE.md

Serializing W3 was the single most important scheduling decision. Five leaves all appended to the same CI config; run in parallel they would have clobbered each other. Run in order, each saw the previous one's change.

06

One Contract, Nineteen Agents

Nineteen agents worked in isolation, none aware of the others. What kept their output consistent — and reviewable — was a single reasoning contract, injected verbatim into every implementation agent and echoed into every PR body.

obs(finding)  →  root_cause              # not the symptom
              →  corrections ∈ {Patch ⊻ Archi}   # choose, explicitly
              →  fix at the level of the cause
              →  trust(input)?  ·  tested ∧ correct   # green ≠ correct
              →  trace the decision in the PR body

The rule that did the most work was green ≠ correct: a passing CI run is necessary, never sufficient. And forcing every agent to choose between a patch (treat the symptom) and an architectural fix (move the cause) — out loud, in the PR — turned nineteen uncoordinated runs into an auditable record. This document is writable because that record exists.

07

What Broke, and Why

The value of a retrospective is in the failures. Seven blockers stalled the run; each was traced to a root cause and fixed at that level, not patched over. The expensive ones were systemic — and now live as hardened defaults in the orchestration harness.

What brokeRoot causeFix
9 of 9 agents failed on the first tryA transient API 529 overload on first inference, masked as “completed”Retry + back-off at the inference level
Heavy agents ended in prose, no resultLarge diffs blew the structured-output budgetMake the diff optional, fetch it out of band, split oversized leaves
PRs never mergedA strict-merge behind-deadlock — no PR is up to date, so the auto-update never firesA manual update-branch watcher loop per PR
Parallel refactors went DIRTYInterdependent edits to the same CI filesSerialize waves by shared-file footprint
The required CI check silently vanishedAn unquoted colon in a YAML step name: invalidated the whole workflowQuote any name: with a colon + a validate-YAML gate
A debt-gate cried wolfThe formatter reflowed a multi-line call off its comment lineRewrote the gate to walk balanced parens, multi-line-aware
One agent shipped broken wiringA worktree branched off a stale local staging and never saw the new hooksFast-forward and pin the base to origin/staging before spawning

Only the last needed a human rescue. Everything else was caught, root-caused, and turned into a default — the difference between a one-off fix and a pipeline that runs cleaner next time.

08

The Cost, Honestly

Time is hard data, read straight off PR timestamps. Tokens are not — the harness never captured per-agent usage, so the run total is a bottom-up estimate: count the agents the pipeline actually spawns, multiply by what one agent costs. It is labelled as an estimate, but the order of magnitude is not in doubt.

4 h 32 mfirst PR → last merge
~3 minmean CI per PR
~96commits
+4.6k / −1.5klines changed

How many agents per issue?

The run was never thirty agents — that was the count of top-level /dev invocations. The real fan-out is the per-issue pipeline (§4) spawning a fresh sub-agent at every step, and the heavy steps fanning out again. Read straight off epic-1662-wave.mjs:

Step (§4)Agents, one roundNote
Implement — small leaf21 impl (isolated worktree) + 1 out-of-band diff harvest
Implement — large leaf5–61 PLAN + 2–3 parallel sub-impl + 1 INTEGRATE + 1 diff
Review panel5one specialist agent per lens, in parallel
Validate2 × each findingtwo independent skeptics per finding (~3 findings → ~6 agents)
Fix1 / roundthe review→validate→fix loop runs up to 2 rounds
Defer0–1once, only if residual findings survive 2 rounds

A representative small leaf, single review round, ~3 findings: 2 + 5 + 6 + 1 ≈ 14 agents. A second round nearly doubles the review/validate/fix block, pushing a contested leaf toward ~25. Blended across the nineteen merged leaves (mostly small, four large), call it ~18 agents per issue → ~340 agent invocations for the run.

Plus debug. The opening overload storm (§7) spawned a full wave that died on first inference — 9 agents, ≈ 0 tokens each (the API 529 hit before any real work), so it cost wall-clock and a re-run, not tokens. Add a handful of re-devs and the #1697 stale-base hand-recovery on top.

Tokens, estimated

A live dev, review, or fix agent reads its slice of the repo, the PR diff, and runs the full gate suite — that lands a single agent at 100–200k tokens, sometimes more. The one hard datapoint sits at the low end of that band, exactly as expected: the two instrumented agents were post-compaction recovery runs, resumed from a shrunk context. So ~340 agents × ~150k ≈ ~50M tokens for the run — a ~35–70M band at the 100–200k bounds — and ~2.5–3M tokens per issue. The earlier “~1.5–2.5M for the whole run” guess was off by ~20×: it was roughly the cost of a single issue.

What was measured?
Two post-compaction recovery agents reported 122,505 tokens combined (~61k each) with high confidence, straight from their task notifications — the low-end anchor for one agent.
What was estimated?
~340 agent invocations × 100–200k tokens each ⇒ ~35–70M tokens for the full run (central ~50M), ≈ ~2.5–3M per issue. Low confidence on the exact multiplier; high confidence it is tens of millions, not single-digit.
Next: per-agent token capture in the harness — you cannot optimize what you cannot see.
09

Proof It Landed

Nineteen merged PRs prove the process finished. They do not prove the code is right — a green pipeline and a closed epic can both sit on top of a fix that quietly drifted from its intent. So once the run was done, a second-layer audit read the merged staging source directly, finding by finding, asking one question: did each fix actually land the way its PR claimed?

Three specialist agents split the surface — refactors and architecture, the security-sensitive paths, and cross-layer drift — and read the files, not the diffs.

AuditorScopeVerdict
backend-devDRY refactors, stage-axis, configs, protocols8 / 8 ✅
security-auditorprocess_one, turn-logging, broad-catch, sleep()4 / 4 ✅
axial-adr-reviewcross-layer pollution, drift off the primary axis2 minor leaks
19/19leaves confirmed in source
0regressions
0blockers
3residual nits

The two architectural leaks were real but small — hub-core code still discriminating two platform-specific message types where a generic trait belongs, and an error classifier keying off adapter module names instead of a taxonomy the adapters register. Add one hardcoded limit that wants a name, and that was the whole residue: filed as non-blocking follow-ups, on the record rather than buried.

The same machinery that found the debt was turned back on the code that paid it down. 19 of 19 fixes confirmed in source, zero regressions — and the three nits it surfaced are filed, not swept. Validation is just the read fan-out, run once more.

10

What Compounds

The findings were specific to one codebase. The patterns are not — these are the parts that carry to the next run.

  • Leaves over umbrellas. Schedule on the actionable leaf; let parent issues auto-close when their children merge. No phantom dependencies.
  • Footprint-based parallelism. “Disjoint footprint ⇒ parallel; shared file ⇒ serial” is a cleaner predicate for conflict avoidance than issue size or priority.
  • Decision-trace PR bodies. Root cause + Patch ⊻ Archi + level, in every PR. It makes independent agents auditable — and a retrospective writable.
  • Isolate, defer, continue. A failed leaf is isolated and deferred; the wave moves on. No single failure stalls the batch.
  • Baseline-grandfather for debt gates. A new gate ships blocking new violations while grandfathering the existing ones, with a burn-down to follow — so the gate lands today instead of waiting on a 90-item refactor.

One operator drove twenty-two findings to merge in a single afternoon — and every decision is on the record. That is the whole thesis in one run: team-scale output, fully auditable, no lock-in.

11

Sources & Code

Every artefact in this report is public. The repository is Roxabi/roxabi-factory (AGPL-3.0).

ArtefactWhat it is
Epic #1662the 22-finding epic (closed)
epic-1662-wave.mjsthe workflow harness (§3)
epic-1662-ledger.mdexecution ledger (single source of truth)
AUDIT-SUMMARY.mdthe deep-audit final report
REMEDIATION-RECAP.mdthe retrospective this doc distils
multi-agent audit playbookthe reusable audit methodology
quality-debt pipeline playbookthe standing debt-scanner how-to