Refactoring the Build Engine Into Concern-Focused Modules
Turning one oversized file into a directory of per-concern modules, with the test suite as proof nothing moved but the code.
2 milestones 11 sprints 24 agents
148k peak context
108k median execute
5h 23m total 9m 28s per sprint
7m 58s plan 1h 26m execute 19m 13s review
71 edits 442 commands
claude harness
46.1M tokens in 423k tokens out
$53.08 nominal cost
Gantry's build engine had grown into a single Rust file that held plan identity, job routing, design setup, every build driver, gate execution, agent dispatch, merge handling, commit plumbing, and the tests for all of it. This job was not a rewrite of that engine. It was a move-only refactor that gave each concern a file while preserving the public module paths the rest of the crate already used.
The run split the work by dependency. The first milestone made the directory module and moved the clusters that could still call support methods left in the parent module. The second milestone moved the support clusters to sibling modules, made the visibility changes those sibling calls required, and finally moved the tests into files that match the production layout. Most of that structure still stands at HEAD, with later work extending the same directory rather than returning to a single file.
How this walkthrough is structured
Feature
What did the run build and ship?
The build engine now has stable addresses for its major concerns, with a thin module root that preserves external paths while later work continues to add files beside the original split.
Build
How did Gantry structure the work?
The cut treated the monolith as a dependency problem: move the low-friction leaves and drivers first, then move the sibling support clusters once the compiler could name each required visibility boundary.
Milestone 1
Establish Module Scaffold
6 sprints 0 agents 1h 3m wall time 135k peak context $26.77 nominal cost
The first milestone left the build engine with a directory and the first concern files: plan source, job routing, design logic, and the map, milestone, and flat-sprint drivers. At current HEAD, the directory remains the build engine's home. The original flat-sprint driver file is no longer present under that name, but its concern survives in the later context-sprint module and matching tests.
This milestone carried the part of the refactor that could stay mostly inside Rust's parent-child privacy rules. It created the directory module, moved the clean leaf clusters, and then moved the driver method groups while gate, agent, finish, commit, and tests still lived in the module root. That made the milestone agent-sized despite the source file's size: each sprint could relocate one concern and compile before the next concern moved. The run facts show this whole milestone stayed green, so the split held at the first boundary.
Module Structure Foundation
0 agents 7m 28s wall time 86k peak context 21.7k tokens out $2.98 nominal cost
The plan-source code now lives in its own file and still provides the run identity, content hashing, slugging, and label helpers through the build module root. The module root remains the public spine for those names. Current code has added more detail around plan source handling, but the concern remains isolated in the file this sprint created.
This sprint had to change the shape of the Rust module before any later extraction could be local. Its brief was narrow: turn the flat file into a directory module and move the plan-source identity code, which had no build context dependency. A fresh agent could hold that because the new module root still contained the rest of the engine, so the only real boundary was preserving re-exports for callers outside the module.
Job Model and Routing
0 agents 8m 42s wall time 109k peak context 28.6k tokens out $4.07 nominal cost
Job selection, persisted job metadata, routing, and related flag parsing now have a dedicated module. The file is still present, and later driver families have grown inside the same job-routing concern rather than being placed back in the build root. External callers continue to import the public job types through the build module path.
The second sprint applied the pattern to the job model and routing cluster. That code was mostly free functions and data types for deciding what kind of run is being resumed or started, which kept the extraction cohesive. It depended on the first sprint's re-export pattern rather than on any new behavioral change, and the gate did not push back.
Design Logic and Methods
0 agents 10m 43s wall time 108k peak context 38.8k tokens out $4.06 nominal cost
Design setup now has its own module, including authorization, view detection, clean-slate target discovery, and the driver methods that run design passes. The file remains in the build directory at HEAD. Later changes have expanded the build subsystem around it, but this concern is still readable without opening the module root.
This sprint was the first to move a build-context method group, but it was still a descendant module of the root that owned the private fields and many called methods. The brief grouped design authorization, view discovery, clean-slate detection, and the design driver together, so the agent had one concern rather than a slice by line number. It stayed green, which matches the intended boundary: method bodies moved, but the calling shape had not yet become a sibling-module problem.
Map Driver Extraction
0 agents 12m 40s wall time 135k peak context 40.7k tokens out $5.50 nominal cost
Map execution still has its own driver file, and current HEAD has grown additional map-specific structure around it. Some helper placement has changed since the original move, especially around partial merge support now exported through the finish layer. The main feature still stands: map orchestration is no longer buried in the root build file.
The map driver was larger than the leaf clusters, but its boundary was clear because it owned the worker-pool flow, unit execution, checkpoints, and map-only helper code. Support methods still lived above it, so the agent could move the driver without solving the later sibling visibility problem. Its green boundary says the map driver was a coherent method cluster at this stage.
Milestone Driver Extraction
0 agents 9m 28s wall time 98k peak context 28.1k tokens out $3.75 nominal cost
The milestone driver remains a dedicated file. The current tree still keeps campaign planning and milestone-run orchestration out of the build root, with later announcement and roster work nearby rather than folded back into the original monolith. The sprint's contribution is still visible as a named driver concern.
This sprint repeated the driver extraction pattern for milestone campaigns. Its scope was the planning, announcement, milestone build, review, and future-milestone helper code, all of which belonged to one run mode. Because the support clusters still stayed in the parent module, the agent could focus on relocating the campaign driver rather than changing call semantics.
Sprint Driver Extraction
0 agents 9m 10s wall time 98k peak context 28.6k tokens out $3.76 nominal cost
The exact file created by this sprint is no longer present at HEAD. Its responsibilities now live in the later context-sprint module, with tests still grouped under the sprint-driver test file. That means the original extraction can no longer be isolated by filename, but the flat-sprint concern remains split from the module root.
This was the closing driver extraction for the first milestone. It moved the flat-sprint loop, including execute, gate, repair, investigate, review, replan, and completion checks, while deliberately leaving the support methods and tests for the next milestone. The boundary was narrow enough for one agent because the sprint driver could still call private root methods as a descendant module.
Milestone 2
Extract Support Clusters
5 sprints 0 agents 59m 5s wall time 148k peak context $25.35 nominal cost
This milestone left the build directory with gate, agent, finish, commit, and test modules beside the earlier driver and leaf files. Those files still exist at HEAD, although later refactors introduced a context layer and moved some exports through it. The root module is still a spine of shared types, declarations, and re-exports rather than the place where each build concern lives.
The second milestone took on the part the first milestone intentionally postponed: moving support methods into sibling files where driver calls would cross module boundaries. That made the compiler the main boundary checker, since it identified which methods needed visibility changes. Most sprints stayed green, but the final test-splitting sprint stalled once and triggered a failed milestone gate before the run resumed and finished.
Extract Gate Logic
0 agents 12m 33s wall time 114k peak context 44.7k tokens out $5.28 nominal cost
Gate execution and gate-result parsing still have their own module, though current code exposes them through a context-facing re-export for tests and internal callers. The production concern remains separated from the driver code that invokes it. That makes gate behavior easier to inspect without reading the map, milestone, or sprint drivers.
Gate logic was the first support cluster to move because every later sibling extraction would rely on the visibility pattern it established. The sprint moved planning and gate methods plus the small parsing helpers, then let the compiler identify which driver calls now crossed the sibling boundary. It stayed green, so the first support split proved the approach without needing a repair pass.
Extract Agent Module
0 agents 15m 47s wall time 148k peak context 58.9k tokens out $6.95 nominal cost
Agent dispatch and confinement handling still live in a dedicated build module. Current HEAD has extended the area with additional agent-tag and recovery behavior, and the tests have followed that concern. The original split remains recognizable because prompt generation, support text, and confinement parsing are not mixed into driver files.
The agent cluster was broad because dispatch, prompt rendering, confinement checks, support handling, and sub-build execution are called from many other concerns. That made it a visibility-heavy sprint rather than a logic-heavy one: move the bodies, then make only the sibling calls compile. The run facts show no red gate here, which means the call surface was large but still mechanically bounded.
Extract Finish Logic
0 agents 11m 6s wall time 119k peak context 38.7k tokens out $4.82 nominal cost
Finish and merge behavior still has its own large module. Since the job ran, additional partial-merge paths have accumulated there, so some helper ownership differs from the original brief. The visible outcome remains the same structural one: finalization and conflict handling are inspectable apart from gate and agent dispatch.
This sprint moved the finish, merge, and conflict-resolution cluster, including helpers consumed outside the build module. Its boundary was sharper than the agent cluster because it owned the run endgame: merge back, partial finish, resolver handling, and resolver logs. The hard part was preserving public crate paths while moving code, and the sprint landed green.
Extract Commit Module
0 agents 7m 6s wall time 84k peak context 22.3k tokens out $3.12 nominal cost
Commit and diff plumbing still has a dedicated file. The module now gives the build root a place to delegate commit-message cleanup, generated cards, aborted-state reporting, and tree comparisons. That concern is no longer interleaved with the driver loops that decide when to call it.
The final production-code extraction was small in domain but important for completing the method split. It moved commit message generation, card decoration, fallback cards, tree snapshots, and diff helpers out of the root. Because the previous support clusters were already siblings, this sprint mostly finished the same visibility exercise on a narrower plumbing surface.
Organize Tests
0 agents 5m 53s wall time 64k peak context 17.5k tokens out $2.29 nominal cost
The build tests now live under a tests subdirectory that mirrors the production concerns, with shared helpers separated from the individual test files. Current HEAD has added test files for newer drivers and tags, but the layout still follows the pattern this sprint introduced. One small inline test module remains in the map driver for later map-specific code, so the original sprint's result has been extended rather than kept perfectly pure.
This sprint carried the last and most verification-sensitive move: the inline test module had to become a test directory without deleting, renaming, weakening, or duplicating tests. The run came under stress here. Its first execute did not finish cleanly, the milestone gate went red, and then the sprint resumed and passed, locating the job's real difficulty in preserving the test suite while changing its shape.