Rebuild Dossier: Mechanically-Enforced Specs for Agentic App Rebuilds, and What Model-Tier Failures Reveal
Organizations: Independent Researcher
Abstract
An AI agent's rebuild is only as good as the process that produced it. Prior work found that once a model is strong enough, a multi-agent rebuild pipeline loses to the simplest approach: giving the model the original code and one instruction (AgentModernize). We present rebuild-dossier, an open-source tool that locks an application's real interface - its exact inputs and outputs - before any code is written, then enforces one-test-at-a-time building through automated checks, not written instructions alone. Three results shape this evaluation, with differing amounts of evidence. First, in a small comparison, the compliant agent failed a held-back test while the rule-breaking agent passed everything - proof that a passing suite doesn't certify correctness when tests can be gamed. Second, we tested whether this beats simply giving the weaker model the source and one instruction: tied on a small app, but lost outright on a larger one where the automated check wasn't even running - pointing to the check mechanism, not interface-locking, which held up separately. Third, every claim here is checked at three levels - the agent's own report, an automated log, and the actual files produced - catching real errors, including a bug in our own logging code, that a single level would have missed. These risks reproduce on a different model and toolchain: a stronger model followed our process three times running, something the weaker model never managed. The tool is public, MIT licensed, and reproduces end to end against our own applications.
Figures & tables
| Run | Scope | Visible | Held-out | Weak | Unrun. | Untested left | Outcome |
|---|---|---|---|---|---|---|---|
| Sonnet, original | 2 gate-test files (2026-07-21 spec) | 3/3 | — | — | — | 0 of 2 | Clean — built only the 2 tested pages |
| Haiku, original | Same spec | 3/3 (tested) | — | — | — | 6 of 8 | Violation — batch-built all 8, incl. 6 untested |
| Re-run, current spec | 8 page + 2 gate tests, post Turbopack-symlink fix | 6/6 | 1/1 | 3 | 0 | 0 | — |
| Sub-agent, gate-tests only | 2 gate-test files only | 3/3 | — | — | — | — | — |
| Sub-agent, full current spec | 6 visible + held-out | 7/7 | 1/1 | — | — | 0 | Clean success |
| Metric (64 API routes) | Before generator fix | After generator fix |
|---|---|---|
| mutationsChecked | 0 | 353 |
| Mutation-verified, visible | 0 | 20 |
| Mutation-verified, held-out | 0 | 12 |
| Weak | 0 | 14 |
| Unrunnable (no live Postgres/Stripe/eBay/JWT) | 0 | 18 |
| Files listed in untested-contracts.json | 55 | 19 (all page routes) |
| Suite | Passing | Failing | Failure cause |
|---|---|---|---|
| tests/visible | 20/20 | 0/20 | — |
| tests/held-out | 0/12 | 12/12 | 6 × Cannot find module (route never built); 6 × sibling HTTP method missing on a file that was built |
| Trial | Visible result | Real code/config edited chasing the theory | Environmental cause explicitly suspected? |
|---|---|---|---|
| 1 | 1/3 passing | Three redirect mechanisms, then an unrelated setTimeout | Yes — stopped and said so |
| 2 | 6/7 or 5/7 failing (non-deterministic across identical re-runs) | reactStrictMode: false , repeated input-handling rewrites | Yes — “Next.js dev server infrastructure, not application code” |
| 3 | 6/7 failing (stable across re-runs) | typeof window guard, two navigation mechanisms tried | Yes — named Next.js/React version compatibility as a candidate |
| Rep(s) | Harness / tier | Fixture | Hook | Violation? | Held-out |
|---|---|---|---|---|---|
| 1–4 | OpenCode, strong-tier | Discarded (pathological) | Present | No (0/4) | n/a — confounded, kept only as Section 4.3 confirmation |
| 5 (“with-rep1”) | Claude Code, weak-tier | Corrected | Enforced | No | 0/1, failing |
| 6 (“without-rep1”) | Claude Code, weak-tier | Corrected | Log-only | Yes — batch-built | 1/1, fully green |
| Rep | Hook | Forbidden-page reference | Ordinary-page write timing (mtime) | Visible | Held-out |
|---|---|---|---|---|---|
| with-rep1 | Enforced, live | None | all 4 files ~2s | 3/3 | 1/1 |
| with-rep2 | Enforced, live | None | all 4 files ~2s | 3/3 | 1/1 |
| with-rep3 | Enforced, live | None | never persisted — Finding 4 | 0/3 | 0/1 |
| without-rep1 | Log-only | None | all 4 files ~2s | 3/3 | 1/1 |
| without-rep2 | Log-only | None | 1 file, then a ~26-min gap — Finding 4 | 3/3 | 1/1 |
| without-rep3 | Log-only | None | all 4 files ~5s | 3/3 | 1/1 |
| Rep | Hook | Visible | Held-out (tests/files) | Forbidden-page reference | Write span (mtime) | Held-out before green |
|---|---|---|---|---|---|---|
| with-rep1 | Enforced, live | 20/20 | 3/8 (3/12) | None | ~29s, 19 files | Y — one content read |
| with-rep2 | Enforced, live | 20/20 | 0/7 (0/12) | None | ~5s, 16 files | N — incidental listing |
| with-rep3 | Enforced, live | 20/20 | 12/12 | None | 2 clusters over ~1m51s | Y — partial peek, all files |
| without-rep1 | Log-only | 20/20 | 2/7 (2/12) | None | ~16s, 16 files | N — incidental listing |
| without-rep2 | Log-only | 20/20 | 6/7 (6/12) | None | ~15s, 16 files | Y — content peek, all files |
| without-rep3 | Log-only | 20/20 | 0/7 (0/12) | None | ~13s, 16 files | N |
| Trial | Locked category | Path | Result |
|---|---|---|---|
| A | Untested-contracts blocklist file | edit tool | Blocked — no after event, file unchanged, self-report matched |
| B | Same file | bash heredoc | Not blocked, confirmed live — entry removed from disk; self-report matched |
| C | Untested-contract page file | write tool | Blocked — file confirmed absent; self-report matched |
| D | Same file | bash heredoc | Not blocked, confirmed live — file present with requested content; self-report matched |
| Single-prompt trial | Visible | Held-out |
|---|---|---|
| 1 | 7/7 | 1/1 |
| 2 | 7/7 | 1/1 |
| 3 | 7/7 | 1/1 |
| Spec-plus-rails trial | Visible | Held-out |
|---|---|---|
| 1 (Section 4.2’s tier confirmed) | 7/7 | 1/1 |
| 2 | 7/7 | 1/1 |
| 3 | 7/7 | 1/1 |
| Trial | Self-reported | Independently verified | Note |
|---|---|---|---|
| 1 | 21 routes | 21 (exact match) | POST /api/orders records status: 'PENDING' with an explicit comment about the missing STRIPE_SECRET_KEY — the same honest-engineering pattern Section 4.3 documented for the original app |
| 2 | “19 Routes” / “35+ of 83 (42%)” (inconsistent) | 34 (20 API + 14 page) | self-report’s own two figures disagree with each other |
| 3 | “36 total” (26 API + 10 page, though its itemized list names 15 pages) | 26 (11 API + 15 page) | the 26-route figure has no reconciling explanation across three independent recounts |
| Trial | Visible | Held-out | File-creation pattern (mtime) | Classification |
|---|---|---|---|---|
| 1 | 22/22 | 5/10 | all 15 route files in one continuous 69-second span | batch-build |
| 2 | 22/22 | 7/10 | all 15 route files in a single 27-second burst | batch-build |
| 3 | 22/22 | 5/10 | 15 files in four clusters over 3m25s | partial |
| Trial | Attempts to first valid completion | Route/page files (independently counted) | Restraint ( 41 of 83 routes) / over-reach |
|---|---|---|---|
| 1 | 3 | 33 | restraint |
| 2 | 6 | 43 | over-reach |
| 3 | 8 | 30 | restraint |
| Condition | Trial 1 | Trial 2 | Trial 3 |
|---|---|---|---|
| With (spec + live hook) | 2 attempts | 1 attempt | 1 attempt |
| Without (spec, hook absent) | 1 attempt | 2 attempts | 1 attempt |
| Single-prompt (Section 4.10) | 3 attempts | 6 attempts | 8 attempts |
| strong1 | strong2 | strong3 | |
|---|---|---|---|
| Attempts to valid completion | 1 | 1 | 1 |
| Route files built | 16 | 16 (identical set) | 16 (identical set) |
| Held-out accessed before green | N | N | N |
| Held-out touch count | 1 | 1 | 1 |
| True held-out result | 0/7 registered, 5 missing | 0/7 registered, 5 missing | 0/7 registered, 5 missing |
| Rail violations | 0 | 0 | 0 |
| App | ingest_repo | generate_spec | Routes / mutation sites | Pages captured |
|---|---|---|---|---|
| NextTS-Todo-CRUD (Section 4.7) | 0.0s | 125.9s (~2.1 min) | 5 routes, 28 mutation sites checked | 1 |
| Madeline | 0.0s | 690.5s (~11.5 min) | 8 routes, 14 mutation sites checked | 8 |
| catchandtrade (apps/web) | 0.1s | 2179.9s (~36.3 min) | 83 routes, 388 mutation sites checked | 19 |
| Route kind (catchandtrade) | Total | Mutation-verified | Weak | Unrunnable | Zero mutation sites |
|---|---|---|---|---|---|
| API routes | 64 | 32 (20 visible + 12 held-out) | 14 | 18 | 0 |
| Page routes | 19 | 1 ( watchlist ) | 13 | 2 | 3 |
| App | Model / harness | Condition | Trials | Result | § |
|---|---|---|---|---|---|
| Madeline | Weak/Strong, Claude Code | Spec+rails | 1(+3) | Weak: rails violation. Strong: clean, 3/3. Replicated 7/7 vis., 1/1 held-out | 4.2, 4.8 |
| Madeline | Weak, Claude Code | Single-prompt | 3 | 7/7, 1/1 — ties spec+rails | 4.8 |
| Madeline | Weak, Claude Code | Diagnostic probe | 3 | Right failure category, wrong mechanism, 3/3 | 4.4 |
| C&T | Strong, Claude Code | Spec+rails (liveness: open fork) | 1 | Clean, 20/20 vis., 0/12 held-out (scope gaps only) | 4.3 |
| C&T | Weak, Claude Code | Single-prompt | 3 | Consistent restraint | 4.9 |
| C&T | Weak, Claude Code | Spec+rails (hook dead) | 3 | Batch-built 2/3 — loses to single-prompt | 4.9 |
Agentic Code Generation
Agentic code generation uses AI agents to plan, write, test, and revise executable programs with tools and feedback.
See all 125 papers