Regenerative Software
A safety-critical library accumulates lessons: a corrupt ENTSO-E slot that once truncated a submission, a leakage guard that must not confuse an unreadable model with a clean one, an outlier helper that silently mutated its caller’s frame. The code keeps the fixes, but not the reasons, and not the list of downstream scripts that rely on the exact behaviour. Regenerative Software (Chad Fowler, O’Reilly Early Release) asks what must exist outside the implementation so that a module can be deleted and rebuilt without losing either. spotforecast2-safe keeps that knowledge in the tracked regen/ directory and checks it on every make guard.
The deletion test
If the implementation of a module were deleted tomorrow, could it be rebuilt correctly, with its edge cases, its failure behaviour, and the reasons behind each of them, and could a replacement be judged without using the old code as the oracle? A module passes only when the answer is yes.
Seven primitives, one directory
| primitive | question | where |
|---|---|---|
| Intent | What must stay true, and why? | regen/units/*.toml, [[intent]] items |
| Compilation | Which architecture must the code fit into? | regen/architecture.toml |
| Evaluation | How is a replacement judged? | tests/test_regen_eval_*.py, marker regen |
| Provenance | Why is the system this way? | regen/decisions/*.toml |
| Pace | How fast may this layer change? | pace of each unit |
| Deletion | Who notices if it is gone? | [[consumers]], including downstream repositories |
| Compaction | What can be removed? | spotregen compact, known_cycles |
Intent items state a scope, a claim that any implementation must satisfy, and the reason for it. Negative items (“never passes silently because a model’s features could not be read”) are the most valuable, because they rule out whole classes of wrong implementations. Durable evaluations bind to the public boundary of a unit and may import only its public names: an evaluation that would break when the module is rewritten in another style is a unit test, not an evaluation. Decision records hold the driver, the rejected alternatives, the evidence, the assumptions and a review date, so that a rationale can expire instead of misleading the next reader.
The architecture file records which subpackage may import which. The baseline of 2026-09-29 contains 16 import cycles between subpackages. They are listed as existing debt, a new cycle fails the gate, and a vanished one must be removed from the list, so the debt can only shrink.
The pilot
Four modules carry their knowledge outside the code today:
| unit | pace | intent items | evaluations | planted bugs caught |
|---|---|---|---|---|
spotforecast2_safe.preprocessing.target_corruption |
slow | 8 | 32 | 14 of 14 |
spotforecast2_safe.preprocessing.outlier |
medium | 8 | 40 | 6 of 6 |
spotforecast2_safe.multitask.guards |
slow | 8 | 44 | 12 of 12 |
spotforecast2_safe.processing.forecast_scoring |
slow | 8 | 48 | 10 of 10 |
The consumers of these units include the daily ENTSO-E submitters in the bart26t and bart26l repositories, the challenge-leaderboard reproduction, spotforecast2’s plots and the bart26o evaluation chapter. They are declared as external consumers, because no check inside this repository can see them.
A blind regeneration
On 2026-09-29, multitask/guards.py was deleted in a separate worktree and rebuilt by an LLM agent (Claude Opus 5.5, claude-opus-5-5, as a Claude Code subagent) that received only the regeneration bundle (spotregen context multitask.guards: architecture, intent, decisions, public signatures and docstrings, evaluations, consumers) and was forbidden to read the old file, its unit tests or the history. The rebuild passed all 29 evaluations of that time on the first attempt. Three of the 18 old unit tests failed: they pinned the prefix of the error message for unreadable features, which the specification had not recorded. The prefix is now intent item LG-4 with its own evaluation, and the rebuild fails that evaluation, which is what a specification gap closing looks like.
The run is recorded in regen/runs/2026-09-29-multitask.guards-blind/: the bundle, the prompt, the generator’s report, the module it wrote and run.toml with the scores. Blindness was requested in the prompt and checked against the tool-call log afterwards. spotregen blind repeats the protocol with blindness enforced by a restricted generator.
The generator also reported the places where the specification was silent. Three of them were safety questions rather than wording: whether a leak on the training frame must still abort when the model’s features are unreadable, whether a missing surface should raise instead of counting as empty, and whether an empty target list should raise. The implementation answered all three leniently. The decision of 2026-09-30 (leakage-guard-fail-closed) answers them strictly. The specification changed first (intent items LG-4 and LG-8 with seven new evaluations, five of which failed on the old code), and the code followed. The guard now reads every surface before it raises, so a readable leak always aborts the submission.
On 2026-10-02 the declared consumers that execute the guard (the team4 submitters in four repositories and one rendered notebook) were run against the original and seven blind rebuilds (regen/runs/2026-10-02-multitask.guards-staging/). Every rebuild aborted on every leak, but every consumer submitted anyway when the guard could not read a surface, because the wrappers turned the RuntimeError into a warning. The wrappers now abort on both errors. The messages the operator then reads are part of the specification: the leak message has a fixed frame (LG-2) and the message for an unreadable estimator names the estimator’s type and the attributes tried (LG-4), decided in leakage-guard-message-contract.
Commands
make regen-check # the gate, first step of make guard
make regen-eval # pytest -m regen
uv run spotregen status
uv run spotregen deletion-test multitask.guards
uv run spotregen context multitask.guards -o bundle.md
uv run spotregen compact
uv run spotregen new-unit processing.blend --pace medium --risk medium --dry-runThe tool is the standard-library package spotregen, a dev dependency outside the wheel. The wheel, its SBOM and the scope of the Model/Method Card are unchanged. regen/README.md describes the file formats and the regeneration workflow.