| name | build-annealing |
| description | Use when a project's design space has ridges โ incremental tuning keeps finding local optima that break, the good solution is not reachable by monotonic improvement from the first working version, or a first-draft architecture has hardened prematurely and needs controlled melting. Also use whenever the user invokes "annealing", "temperature", "molten", "freeze order", "reheat", or "quench". |
Annealing Build
In annealing there are no phases โ there is a temperature schedule. At high temperature, moves that make the code worse are legal and expected: committing a rewrite that loses functionality, breaking tests, abandoning a good abstraction, because the move might open a region of design space unreachable by monotonic improvement. As layers cool, the acceptable-regression window narrows until frozen layers admit no change without a formal reheat. The method's enemy is quenching: the first working version becoming the architecture because momentum is expensive. The first success is a data point, not a commitment.
The energy function โ write it before heating up
Annealing without a stable notion of "better" is thrashing with vocabulary. Before any molten work, write the energy function into ANNEAL.md: 3โ6 qualities the final system is optimizing toward (e.g. "hot path under N ms", "a new contributor ships a change in one sitting", "zero coordination between modules X and Y"). It doesn't need to be numeric, but it must be stable across the whole run โ every accepted regression is justified against it ("this rewrite breaks the API but opens a path to zero-coordination"). If you find yourself wanting to change the energy function mid-run, stop and treat that as a project-level finding, not an edit.
The temperature table
Lives in ANNEAL.md (or the project's existing STATE.md if one is in use โ annealing composes with existing state-file discipline). Every layer of the system gets a row:
## Temperature table (updated every session)
| Layer | Temp | Sessions-since-change | Freeze criteria |
|------------------|----------|-----------------------|-------------------------------------|
| File formats | FROZEN | 9 | frozen 2026-06-12, see FREEZE log |
| Data model | COOLING | 3 | 5 quiet sessions + migration story |
| Public interface | WARM | 1 | 3 quiet sessions |
| Module bounds | WARM | 0 | 3 quiet sessions |
| Internals | MOLTEN | โ | not before interface cools |
| Naming / style | MOLTEN | โ | last to freeze |
Temperatures and what's legal at each:
- MOLTEN โ any move, including regressions, deletions of working code, incompatible rewrites. No justification needed beyond a one-line note tying the move to the energy function.
- WARM โ changes allowed, regressions allowed only with an explicit logged trade ("accepting broken pagination for a week to trial cursor model"). Every regression gets a repair-by note.
- COOLING โ changes must be arguably monotonic improvements; no regressions. Each change must state why it doesn't conflict with the freeze criteria.
- FROZEN โ no changes. Work around a frozen layer is illegal too โ if a frozen decision is blocking, that's a REHEAT (below), not a hack.
Freeze order follows cost-of-change, most expensive to melt later freezes first: persisted formats & schemas โ data model โ public interfaces โ module boundaries โ internals โ naming/style. Deviate if the project's real cost structure differs, but write down why.
Cooling by observed volatility, not calendar
A layer cools when it stops wanting to change: the default freeze criterion is N consecutive sessions in which no accepted move touched the layer (default N=3 for warmโcooling, 5 for coolingโfrozen). Update Sessions-since-change at the end of every session โ this makes temperature a measurement, not a declaration. A layer you keep saying is "done" but keep editing is warm no matter what the table claims; correct the table, not the behavior.
Freezing is an event: write a FREEZE entry in ANNEAL.md โ layer, date, the decision now locked (in one or two sentences), and what downstream work may now safely build on it. The freeze log is the project's real design document.
Session protocol (Claude Code)
Every session inherits the temperature table. Session prompts (or the CLAUDE.md / STATE.md the session reads) must state the current temperatures so the agent knows its legal move set โ e.g.: "Data model is COOLING (no regressions, justify against freeze criteria). Interfaces are WARM (regressions need a logged trade). Internals are MOLTEN (free)." At session end: update sessions-since-change for every layer, log accepted regressions with repair-by notes, check whether any layer met its freeze criteria.
During molten-heavy early sessions, checkpoint liberally (git branches per wild move) โ molten legality means regressions are accepted, not that history is discarded. A rejected wild move should be revertible in one command.
The REHEAT protocol
When a frozen decision blocks progress late, do not hack around it. Declare a reheat in ANNEAL.md:
- Name the frozen decision being unfrozen โ the specific FREEZE entry, not the layer generally.
- State the melting cost: everything built on top of the decision that re-cooling will force you to revisit. List it concretely. If you can't enumerate the cost, you don't understand the dependency structure yet โ map it first.
- Scope the reheat: local (this one decision returns to WARM) by default. Full-layer reheats are legal but rare; justify against the energy function.
- Do the work, then re-cool explicitly: the reheated decision passes back through its freeze criteria; it doesn't snap back to frozen because the edit is done.
The reheat ritual's friction is the point โ it converts "refactor" from a mood into a named decision with a stated bill.
Quench detection
Run this check whenever a working version first appears, and again any time the project feels "basically done" early: Has anything structural changed since the first version that worked? If the answer is no and the temperature table still shows molten layers, you are quenching โ the schedule says the material is hot, but behavior says it's frozen. Either deliberately schedule molten moves (pick the layer with the most energy-function tension and force one wild alternative) or honestly cool the table and accept the first shape as the shape. Both are legal; drifting between them is not.
Cooling is gated on an energy audit. Before any layer moves MOLTEN โ WARM (and before "honestly cool" is chosen at a quench check), state for each criterion in the energy function whether the current shape plausibly meets it at target scale โ not merely on the test corpus. A shape that fails a criterion cannot be honestly cooled: either schedule molten moves against that specific tension, or surface the mismatch as a project-level finding (the energy function or the target must change, and that call belongs to the author, not the session). User enthusiasm ("feels basically done", "lock it in") is not evidence; an audit line like "criterion 1 demands sub-second at 100k, current shape is a linear scan tested at 2k โ FAILS" is. MOLTEN โ WARM is a declaration made only after a passing audit; from WARM onward the sessions-since-change criteria take over (default 3 quiet for warm โ cooling, 5 for cooling โ frozen).
Failure modes
- Quenching โ covered above; the signature failure.
- Perpetual molten: never cooling because melting is fun. Volatility-based criteria counter this โ if a layer genuinely hasn't changed in N sessions, cool it even if it feels unfinished.
- Thrash disguised as heat: wild moves with no energy-function justification. One line per move; if the line can't be written, the move is noise.
- Silent reheats: editing a frozen layer without the ritual. This destroys the freeze log's authority, which destroys the method.
- Uniform temperature: the whole table molten or the whole table cooling. Temperature is differential by design; if all layers share a temp, you're doing phases with extra words.
Claude Code mechanics & siblings
- State files: if the project routes working files to a directory (e.g.
.ai/), put ANNEAL.md there (.ai/build/ANNEAL.md), reference it from session prompts, and keep the path stable across sessions.
- Wild moves map naturally to isolated dispatch: a molten alternative can be built by a subagent in a worktree so a rejected move never touches the main tree.
- Routing: shaping and planning stay upstream (intent-shape, mega-brainstorm, mega-plan); annealing replaces the execution strategy for the component it governs. Siblings โ tournament when "better" is measurable up front; subtractive when the answer is recognizable by taste in one sitting; dialectical when exactly two philosophies contest. Annealing is for ridged design spaces worked across many sessions.
When NOT to use this
Hard deadlines that can't absorb molten-phase throwaway states. Well-understood shapes reachable by monotonic improvement (linear is cheaper). Codebases with heavy external consumers where interfaces are effectively born frozen โ annealing needs meltable material.