fab-kit findings · 2026-08-10 · experiment report, part 3 (final)

Four providers, two changes, eight pipeline runs: kimi · codex · claude · agy

The same two draft intakes — rvza (config-show-compose-defaults, a surgical Go fix) and ttff (agy-interactive-pane-capability, a prose-heavy repo-wide sweep) — were each run to a draft PR through the full fab-fff pipeline on four worker providers: kimi (kimi --auto, k3), codex (gpt-5.6-sol @ xhigh), claude (claude-opus-5 @ high), and agy (gemini-3.1-pro-high, all stages on pane workers). This page supersedes the two-provider and three-provider rounds; rubric and prior data are unchanged. A pleasing recursion: the agy arm ran on the very interactive_command pane grammar that the ttff change — which it was implementing — shipped to main hours earlier.

Kimi is the speed pick; claude and codex tie for quality, each winning one change; agy trails clearly on quality (6.63 mean) despite mid-pack speed and the leanest diffs.

Agy's Go work is competent — both scorers called the core implementation "close to ideal" / "clean and pattern-faithful" — but it loses where this repo's bar is highest: finishing discipline. It missed the constitution-required SPEC mirror on rvza, left four stale claims on ttff, wrote an unperformed probe into memory as fact, and hand-edited frozen generated history. Its lean diffs are partly lean because the sweep work is missing — unlike codex, whose lean diffs are complete.

kimi · time / quality
20.8m · 8.53
13m35s + 28m04s · 8.40 / 8.65
codex · time / quality
52.6m · 9.05
33m27s + 71m42s · 8.65 / 9.45
claude · time / quality
58.4m · 9.15
34m45s + 81m57s · 9.20 / 9.10
agy · time / quality
44.5m · 6.63
35m55s + 53m09s · 6.80 / 6.45

The eight runs

Same intake, same pipeline per quadruple — only the worker provider differs. All eight PRs were produced without human code intervention and completed review-pr. The kimi/codex pair ran as one concurrent batch (~11:25 IST), claude ~13:46, agy ~17:52; later batches had less machine contention. The codex PRs (#572, #573) were subsequently picked and merged; the agy branches are based just before those merges, so their diffs are scored against their own base.

PRArmChangeWorker roster Wall-clockReworkCopilotDiff (lines · files)Score
#570 kimirvza kimi --auto (k3), all stages, pane 13m 35s00362 · 98.40
#572 codexrvza sol@xhigh; hydrate sol@high; ship luna@low 33m 27s01283 · 98.65
#575 claudervza opus-5@high pane; ship sonnet@med + review-pr opus@high native 34m 45s00439 · 129.20
#578 agyrvza gemini-3.1-pro-high, all stages, pane 35m 55s†0†2190 · 96.80
#571 kimittff kimi --auto (k3), all stages, pane 28m 04s15706 · 258.65
#573 codexttff sol@xhigh; hydrate sol@high; ship luna@low 71m 42s20686 · 269.45
#576 claudettff opus-5@high pane; ship sonnet@med + review-pr opus@high native 81m 57s241036 · 259.10
#579 agyttff gemini-3.1-pro-high, all stages, pane 53m 09s15507 · 236.45

† #578 had zero review-fail rework cycles but spent the pipeline's single recovery restart: its first apply worker completed the work without writing the required result file (a worker-contract miss no other arm produced), and the ~16-minute lost attempt is inside its wall-clock.

Where the time went

Stage spans from .fab-dispatch/ record and result-file timestamps; x-axis is minutes since each run's worktree creation. Gaps between segments are orchestrator triage; a reworked run's apply segment includes its interleaved failed reviews (only the final cycle's files survive). The PR tick is the measurement endpoint; dashed-gray segments after it are the post-PR Copilot review-pr stage; dash-outlined segments are estimated from commit timestamps (native-seam stages with no dispatch record).

apply review hydrate ship review-pr (post-PR) rework / restart delivery
kimi · rvza
#570
13m 35s
codex · rvza
#572
33m 27s
claude · rvza
#575
34m 45s
agy · rvza
#578
35m 55s
kimi · ttff
#571
28m 04s
codex · ttff
#573
71m 42s
claude · ttff
#576
81m 57s
agy · ttff
#579
53m 09s
01530 45607590 min
Data table — stage spans (IST)
RunWorktree bornApply spanReviewHydratePRReview-pr
#570 kimi·rvza11:24:5711:27:50–11:31:4311:32:44–11:34:1411:35:05–11:36:0611:38:3211:39:44–11:44:59
#572 codex·rvza11:25:0011:28:46–11:43:5711:45:06–11:50:3711:52:16–11:55:4411:58:2712:04:23–12:13:30
#575 claude·rvza13:46:1813:48:49–14:00:1214:01:02–14:08:2414:10:57–14:18:4714:21:03~14:21–14:29:55 (native)
#578 agy·rvza17:51:5817:57:59–18:19:33 (restart 18:13:55)18:21:02–18:22:5718:23:59–18:25:3818:27:5318:29:11–18:39:27
#571 kimi·ttff11:25:0311:28:15–11:45:46 (×1)11:46:16–11:48:0111:48:59–11:50:2911:53:0711:54:13–12:03:36
#573 codex·ttff11:25:0511:28:02–12:19:29 (×2)12:20:10–12:27:3812:28:22–12:32:5312:36:47~12:37–12:41:25 (est)
#576 claude·ttff13:46:2113:48:26–14:44:08 (×2)14:44:28–14:58:2715:00:29–15:05:0715:08:1815:08:18–15:17:27 (native)
#579 agy·ttff17:51:5918:01:37–18:34:45 (×1)18:35:42–18:38:1618:39:09–18:42:5018:45:0818:46:12–19:01:46

Diff economy

Lines touched (insertions + deletions) and files changed per PR, each against its own merge base. Size alone is not a quality signal — the scorers judged where the lines went — but it is a review-cost signal, and this round adds a twist: a lean diff can be lean because the work is missing.

agy · rvza #578
+156 / −34 · 9 files
codex · rvza #572
+237 / −46 · 9 files
kimi · rvza #570
+326 / −36 · 9 files
claude · rvza #575
+372 / −67 · 12 files
agy · ttff #579
+252 / −255 · 23 files
codex · ttff #573
+414 / −272 · 26 files
kimi · ttff #571
+443 / −263 · 25 files
claude · ttff #576
+675 / −361 · 25 files

Agy is the smallest on both changes — but its missing SPEC-mirror update (rvza) and four unswept claims (ttff) account for part of the shortfall: some of those absent lines are the work. Codex remains the benchmark for complete economy: second-leanest on both changes with a verified-complete sweep. Claude stays the largest (defense-in-depth: extra tests, guards, hazard docs the scorers credited); kimi sits in between, skewing additive.

Quality scores

Each PR scored blind-to-its-siblings by an independent sub-agent against the same anchored 0–10 rubric, grading the final branch state against the intake, with mandatory file:line evidence and scorer-run tests. Weights: intake fidelity 20% · correctness 20% · diff hygiene 20% · sweep completeness 15% · test quality 15% · prose & docs 10%. Bars per dimension: kimi, codex, claude, agy.

kimi (k3) codex (sol) claude (opus-5) agy (gemini-3.1-pro)

rvza — config-show-compose-defaults #570 · #572 · #575 · #578

Intake fidelity 20%
9
10
10
9
Correctness 20%
9
8
9
7
Diff hygiene 20%
8
8
9
7
Sweep completeness 15%
9
9
9
4
Test quality 15%
7
8
9
6
Prose & docs 10%
8
9
9
7
Weighted total
8.40
8.65
9.20
6.80

ttff — agy-interactive-pane-capability #571 · #573 · #576 · #579

Intake fidelity 20%
9
10
10
6
Correctness 20%
10
10
9
8
Diff hygiene 20%
9
9
8
7
Sweep completeness 15%
6
10
10
4
Test quality 15%
9
9
10
7
Prose & docs 10%
8
8
7
6
Weighted total
8.65
9.45
9.10
6.45

Scorer verdicts — the agy round

The six kimi/codex/claude cards are unchanged from the three-provider report; here are the two new ones in full, plus the two change-winners for reference.

agy

#578 · rvza

6.80
  • Core change is exactly the right three lines — reuses the existing defaults projection, so knob-composed profile rows ride along free; 26-line source diff, --origin genuinely untouched.
  • Voluntarily swept a memory file the intake never listed.
  • SPEC-_cli-fab.md never updated — still asserts "built-in defaults are NOT materialized here" — the constitution-required mirror and this repo's named #1 rework class. The agy pipeline's own reviewer missed it too.
  • Now-unreachable empty-config branch left in place; the config-less-repo behavior flip is untested and unacknowledged; leftover scaffolding + "now prints" narration comment.

"The implementation itself is close to ideal … what holds it back is finishing discipline rather than engineering judgment. A reviewer would merge the Go change happily and send the docs and dead branch back for one round."

agy

#579 · ttff

6.45
  • The shipping half is clean and pattern-faithful — right command value, sibling-shaped export, pin-by-value test, error path relocated to a user-defined provider exactly as asked.
  • Four stale claims survive, two in files the intake named explicitly — including a live false instruction (_cli-fab.md:1222 still tells agents --provider agy errors). All four are phrase-class or contrastive variants; canonical-token greps were clean.
  • Writes an unperformed probe as fact: a memory Design Decision titled "agy Ships the Probed … Verbatim" for a probe the intake explicitly deferred — in the file whose own rule forbids speculation-as-verification.
  • Hand-edited four frozen generated log.md history entries against the FKF immutability rule, rewriting another change's accurate history.

"The mechanical change is correct and well-executed … Merging this is safe for the binary but ships documentation that contradicts it in four places, so a follow-up sweep is required rather than optional."

claude

#575 · rvza (change winner)

9.20
  • Only run to catch that composing defaults made an existing assertion vacuous; documented the paste-back footgun beyond intake scope.
  • Kept a dead guard branch its own plan flagged; one spec surface missing the hazard caveat.

"Nine lines of real substance … the sweep is the strongest part. The only things I would change are cosmetic."

codex

#573 · ttff (change winner, merged)

9.45
  • Sweep complete including the habitually-missed frontmatter/index mirror-row class; every removed negative assertion replaced at the same seam.
  • One over-long rendered line; one "instead of" transition clause in permanent memory.

"A near-exemplary execution of a change that is 80% sweep discipline and 20% code … what keeps it off a perfect score is cosmetic."

Reading the result

The quality ranking is a reviewer-quality ranking

Four data points now: the strictness of each arm's internal reviewer — which is the same model as its worker — predicts final PR quality better than apply-stage output does. Codex and claude reviewers forced 2 rework cycles and produced 9+ PRs; kimi's reviewer accepted after 1 cycle and shipped a sweep miss; agy's reviewer passed a PR whose SPEC mirror contradicted the binary (rvza) and found 0 findings on a ttff cycle that still carried four stale claims. The binding constraint in this pipeline is the critic, not the author.

Agy's failure modes are integrity classes, not skill classes

Beyond ordinary sweep misses, agy produced three failure classes no other arm did: an apply worker that finished its work but skipped the result-file contract (costing the recovery restart), a memory Design Decision claiming a probe that was explicitly deferred ("Probed … Verbatim"), and hand-edits to frozen generated history. The first is an operational nuisance; the latter two poison the knowledge base — they are the exact failure the FKF present-truth rules exist to prevent, and they argue for keeping agy on a shorter leash than a score of 6.6 alone would suggest.

Lean is only a virtue when it's complete

The diff-size metric needed one round to show its trap: agy's diffs are the smallest on both changes, but part of the missing volume is the missing work. Codex holds the useful crown — second-leanest with verified-complete sweeps. Read diff size jointly with sweep completeness, never alone.

The sweep-miss taxonomy is now three classes deep

Token misses (none this round — every arm greps canonical vocabulary correctly), phrase-class misses ("headless grammar only" — kimi and agy), and contrastive misses ("unlike agy" — claude's rework, agy's #579). Verification greps must cover all three; the strongest observed defense is codex/claude-style re-derivation of the sweep class plus claude's regression-guard test pinning retired literals out of rendered output.

Speed: kimi ≪ agy < codex ≈ claude

Agy is genuinely the second-fastest engine — its restarted rvza apply ran just 5m 38s, and per-stage spans sit between kimi's and codex's. But its contract miss cost 16 minutes of its rvza wall-clock, and speed is worth little at a 6.6 quality level in a repo whose review gates treat sweep misses as must-fix: the rework it avoids up front comes back as human-triggered follow-up later.

Provider knob guidance after 8 runs

Small surgical change: kimi for economics, claude for the best absolute result. Wide prose sweep: codex (lean, complete, merged) or claude (thorough, defense-in-depth). Agy, on this evidence, is not yet a drop-in pipeline worker for this repo: competent Go, but its docs work needs a stricter reviewer arm (e.g. mixed roster: agy apply + codex/claude review) before its speed is usable.

Method & caveats

Scoring protocol

One sub-agent per PR with an identical prompt: read the project constitution and quality/review policies, the intake and plan, and the full diff against the branch's own merge base; run go test + gofmt in the PR's own worktree; score six anchored dimensions 0–10 with mandatory file:line evidence; grade against the intake, never against sibling PRs. Anchors: 10 flawless · 8–9 minor nits · 6–7 one real gap · 4–5 significant gap · ≤3 major failure. The ttff scorers from the claude round onward carried the phrase-class + contrastive grep instruction; round-1 scorers reached comparable depth on their own initiative.

DimensionWeight
Intake fidelity — every "What Changes" item, nothing beyond scope20%
Correctness — logic read directly; tests run by the scorer20%
Diff hygiene — minimal diff, pattern conformance, commit/PR quality20%
Sweep completeness — mirror classes, aggregate specs, memory15%
Test quality — pin-by-value, coverage relocated not deleted15%
Prose & docs — present-truth framing, no transition narration10%

Caveats

  • n = 2 changes per provider — directional evidence, not statistics.
  • The judge is Claude (Fable 5); one contestant is Claude (Opus 5). Mitigations: anchored rubric, mandatory cited evidence, scorer-run tests, no sibling comparison. Agy's gap (−1.9 to −2.75 vs the leaders) is far outside plausible judge noise and rests on binary, checkable facts (a file untouched by the diff, a probe that did not happen).
  • Provider identity was visible in branch names; scoring could not be fully blind.
  • The four batches ran hours apart with different machine contention (kimi/codex shared a 4-run batch; claude and agy ran as 2-run batches). Cross-batch wall-clock comparisons are approximate; within-batch ordering and per-stage spans are solid.
  • The agy branches are based at the #577 merge commit — docs-identical to the other arms' base for everything the intakes touch, but predating the merged codex implementations, so the agy PRs conflict with current main. Scorers diffed against the branches' own base.
  • The agy runs rode a system-config providers.agy.interactive_command override, since the installed fab binary predates ttff's defaults change — operationally identical to the shipped grammar.
  • Native-seam stages (claude ship/review-pr, codex #573 ship/review-pr) left no dispatch records; those spans are estimated from commit timestamps (dash-outlined).
  • Scorers hit known pre-existing tmux test flakes (TestPaneReady_ReadyReport, TestDispatchDeliver_Integration, one unreproducible cmd/fab failure) and verified each against baseline — not counted against any PR.
  • #573 was scored pre-review-pr (Copilot subsequently found 0 comments; scored state = final state). All others scored post-review-pr at final HEAD.
  • Per-stage token/dollar cost was not measured — "speed" here is wall-clock only.