fab-kit findings · 2026-08-10 · experiment report

Kimi k3 vs Codex sol: four parallel fab pipelines, two changes each

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 twice through the full fab-fff pipeline: once with every stage worker on kimi (kimi --auto, k3) and once on codex (gpt-5.6-sol @ xhigh for apply/review/review-pr). All four worktrees were created in the same 8-second window and ran concurrently.

Verdict: kimi was ~2.5× faster on both changes; codex shipped the higher-quality PR on both.

On the small surgical change the quality gap is marginal (8.65 vs 8.40) and kimi's speed wins the economics. On the sweep-heavy change the gap is decisive (9.45 vs 8.65): kimi left two stale normative spec claims that the plan's token-based verification grep could not catch, while codex's sweep survived an adversarial re-grep intact.

kimi · mean wall-clock
20.8 min
worktree → draft PR (13m 35s / 28m 04s)
codex · mean wall-clock
52.6 min
worktree → draft PR (33m 27s / 71m 42s)
kimi · mean quality
8.53 /10
weighted rubric (8.40 / 8.65)
codex · mean quality
9.05 /10
weighted rubric (8.65 / 9.45)

The four runs

Same intake, same pipeline, same launch instant per pair — only the worker provider differs. All four PRs are draft, CI-green, and were produced without human intervention.

PRArmChangeWorker roster Wall-clockRework cyclesCopilot commentsCI
#570 kimirvza kimi --auto (k3), all stages 13m 35s00pass
#571 kimittff kimi --auto (k3), all stages 28m 04s15 (all fixed)pass
#572 codexrvza sol@xhigh; hydrate sol@high; ship luna@low 33m 27s01 (fixed)pass
#573 codexttff sol@xhigh; hydrate sol@high; ship luna@low 71m 42s2(review-pr not yet run)pass

Where the time went

Stage spans reconstructed from .fab-dispatch/ record and result-file timestamps; x-axis is minutes since worktree creation. Gaps between segments are orchestrator triage time. Only the final rework cycle's files survive, so the apply segment of a reworked run includes its interleaved failed reviews. The vertical PR tick is the measurement endpoint; the dashed gray segment after it is the post-PR Copilot review-pr stage, outside the wall-clock number.

apply review hydrate ship review-pr (post-PR) rework delivery
kimi · rvza
#570
3m 53s
13m 35s
kimi · ttff
#571
17m 31s · ×1 rework
28m 04s
codex · rvza
#572
15m 11s
33m 27s
codex · ttff
#573
51m 27s · ×2 rework
71m 42s
01530 456075 min
Data table — stage spans (IST)
RunWorktree bornApply spanReviewHydrateShip / PRReview-pr
#570 kimi·rvza11:24:5711:27:50–11:31:4311:32:44–11:34:1411:35:05–11:36:06PR 11:38:3211:39:44–11:44:59
#571 kimi·ttff11:25:0311:28:15–11:45:46 (×1 rw)11:46:16–11:48:0111:48:59–11:50:29PR 11:53:0711:54:13–12:03:36
#572 codex·rvza11:25:0011:28:46–11:43:5711:45:06–11:50:3711:52:16–11:55:44PR 11:58:2712:04:23–12:13:30
#573 codex·ttff11:25:0511:28:02–12:19:29 (×2 rw)12:20:10–12:27:3812:28:22–12:32:53PR 12:36:47not yet run

Quality scores

Each PR was scored blind-to-its-sibling by an independent sub-agent against the same anchored 0–10 rubric, grading the final branch state against the intake. Every score is backed by cited file:line evidence, and each scorer ran the Go test suite in its PR's worktree itself. Weights: intake fidelity 20% · correctness 20% · diff hygiene 20% · sweep completeness 15% · test quality 15% · prose & docs 10%.

kimi (k3) codex (sol)

rvza — config-show-compose-defaults #570 vs #572

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

ttff — agy-interactive-pane-capability #571 vs #573

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

Scorer verdicts

Top strengths and weaknesses per PR, as reported by each scoring sub-agent (full evidence in the scoring transcripts).

kimi

#570 · rvza

8.40
  • Reuses the existing readModelDefaults/DefaultsMapFor projection, so bare show cannot drift from the keyed and --origin views.
  • Complete five-surface mirror sweep; repo-wide grep finds no live stale claim.
  • New test asserts by parsed YAML value across three axes, not substrings.
  • The sharpest behavior flip — a config-less repo now prints the whole defaults tier — is documented in memory prose but pinned by no test (scorer verified it by building the binary).
  • Now-unreachable empty-state guard kept with a message that would be wrong if it ever fired; misleading commit subject ("init/unset fields").

"The implementation is the right one … precedence, empty-leaf fall-through, and the single-load warning rule are inherited rather than re-derived. The one real gap is test coverage of the empty-repo case … minor hygiene noise keeps this short of the top band."

codex

#572 · rvza

8.65
  • Smallest correct implementation — deletes a special case rather than adding a branch; net +20/−23 impl diff.
  • Mirror sweep genuinely complete, including the conceptual composition-vs-provenance distinction landed in _cli-fab.md.
  • Tests parse real YAML output and assert typed values, including the knob-aware derived-profiles row.
  • Shipped dead code its own plan had flagged as a deletion candidate — the Copilot round had to remove it.
  • Stale doc-comment residual in internal/config/config.go:707; PR body never mentions the output-size blast radius.

"A tight, well-targeted change that does exactly what the intake specified and nothing more … What keeps it out of the top band is self-review rather than judgment."

kimi

#571 · ttff

8.65
  • Go change exactly minimal and correct — every test assertion flipped rather than deleted; coverage strengthened (two-rung compose table, both-forms {effort} guard).
  • Post-Copilot commit is a genuine correctness improvement applied consistently across all six files carrying the over-claim.
  • Two stale "agy is headless-only" claims survive in normative spec text (docs/specs/config.md:224, docs/specs/stage-models.md:323) — both in files the change edited elsewhere.
  • Root cause: the plan's verification grep matched tokens (dispatch-only, agik) but not the phrase-class restatement ("headless grammar only").

"Technically … clean and well-shaped. It falls short on exactly the axis the intake flagged as the repo's #1 rework cause … Fixing the two lines and widening the sweep grep to phrase classes would take this to a 9-plus."

codex

#573 · ttff

9.45
  • Sweep complete including the habitually-missed frontmatter-description + index mirror-row class; every residual grep hit independently verified legitimate.
  • Every removed negative assertion replaced by a positive one at the same seam; error-path coverage relocated to user-defined providers, not deleted.
  • Old "agy Is Dispatch-Only" Design Decision replaced outright with its own provenance — present-truth discipline throughout.
  • One 92-char comment line in rendered fab config explain output breaks the block's ≤84 wrap; one "instead of" transition clause left 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 speed gap is apply-stage compute, not pipeline overhead

Kimi's apply on the surgical rvza change took 3m 53s to codex's 15m 11s for near-equivalent output (8.40 vs 8.65). Review, hydrate, and ship stages show the same ~3× per-stage ratio. The orchestrator overhead between stages is comparable across arms.

Task shape decides which arm wins the economics

On the small Go fix, kimi delivered 97% of codex's quality score at 2.5× the speed — the clear pick. On the sweep-heavy prose change, codex's xhigh-effort diligence bought the one thing that matters in this repo (sweep 10 vs 6) — the two claims kimi missed are exactly the class that historically comes back as rework, which costs more than the 44 extra minutes.

Kimi's miss is a grep-design lesson, not just a model gap

Both arms ran the same plan with the same verification grep tokens. Codex re-derived the sweep class beyond the tokens; kimi trusted the tokens. Sweep-verification greps should target phrase classes of the retired claim ("headless grammar only", "carries a headless_command only"), not just its canonical vocabulary.

More rework ≠ worse output

Codex's ttff run was sent back twice by its own (sol) reviewer and emerged with the highest score of the four and a clean internal review (0 findings). Kimi's pipeline accepted after one rework — and Copilot then raised 5 comments on the PR. The internal review gate's strictness tracks the worker model's, which compounds the quality gap.

4/4 pipeline success, zero human touches

Both providers drove every stage — including pane-worker delivery, rework choreography, ship, and (3 of 4 so far) Copilot-comment triage — to CI-green draft PRs unattended. The provider choice is now a pure cost/quality/speed knob.

Method & caveats

Scoring protocol

One sub-agent per PR, launched in parallel with an identical prompt: read the project constitution and quality/review policies, the intake and plan, and the full main...HEAD diff; 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 the sibling PR. Anchors: 10 flawless · 8–9 minor nits · 6–7 one real gap · 4–5 significant gap · ≤3 major failure.

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 scorer per PR; a same-family bias toward either style can't be excluded. Evidence citations and scorer-run tests are the mitigation.
  • Provider identity was visible in branch names; scoring could not be fully blind.
  • #573 has not been through review-pr yet — its Copilot column is empty and its final state may still change.
  • #573's ship stage ran outside pane dispatch (no record); its ship span is estimated from commit timestamps.
  • The four runs shared one machine and one orchestrator's attention; wall-clock includes queuing for triage. Per-stage spans bound this: the ordering (kimi ≪ codex) is robust.
  • Two scorers independently hit TestPaneReady_ReadyReport flakes and verified them pre-existing on the main baseline — not counted against any PR.
  • Intermediate rework-cycle timestamps are overwritten by the final cycle; apply spans for reworked runs include interleaved failed reviews.