Skip to content

0.3.0 — a claim is worth what its enforcement is worth

One day of work on this repository produced two findings that are the same finding seen from opposite sides, and together they are this release.

In the morning, an implementer working on something unrelated deleted a heading line from a template. Every test passed — 276 of them — across four presets and two Node majors, and the harness reported no test weakened, correctly, because no test had been touched. What had been lowered was the product: from then on every generated project would have had a CLAUDE.md with no title. Nothing caught it, because nothing had ever claimed the heading existed.

In the evening, on the same repository and through the same mechanism, an implementer narrowed an assertion that had been added hours earlier. The harness returned a test was deleted, skipped or narrowed, the ladder escalated, and the next attempt restored it — stricter than before — within a minute, long before a human read the diff.

Same day, same repository, same class of agent, opposite outcomes. The only difference was whether an assertion existed. That is the whole version.

What 0.3.0 adds

0.2.0 taught the tool to grade its own claims: doctor began reporting an enforcement level from L0 to L3 with a state and the evidence it read, cost learned to say unsupported instead of reporting an absence, discovery gained provenance in the manifest, and the policy checks the presets ship started reaching repositories that already had code — which 0.3.0 narrows again, for a reason worth reading under What it was checked against. 0.3.0 is what follows from having those grades — the ability to act on them.

construct sync moves a repository onto newer templates. It classifies every path against what the manifest recorded and what today's templates produce, reports before it writes anything, and writes only behind --apply. Ownership inside a file is decided by strategy: a whole file where the construct writes one, the block between the construct delimiters where it edits a section, and per key where it merges JSON. It was checked against four repositories built before it existed — see What it was checked against below, including what that sample does and does not prove.

doctor names the version gap. It reports the version that materialized the repository, the version reading it, and how many recorded paths a sync would add or update — as evidence beside an existing check, with no level and no effect on any exit code. A baseline that has moved on is information.

doctor looks for the composition marker where the repository keeps it. A manifest written before the variable existed records no location for it, and the answer is established from the repository rather than assumed from a default. A location the owner did record still wins.

Two guards against changes that pass green. An acceptance test now asserts what a generated project is rather than only that it works, and /plan carries the rule that an acceptance criterion is verified by what its own task changes.

What 0.3.0 does not do

This section is the same size as the one above on purpose. A tool that reports honestly is mostly made of what it refuses to say.

A merged file is reported and never written. package.json is compared key by key, and each difference is shown, but nothing is written to it. The manifest records the hash of the merged result and never recorded which keys were the construct's, so a three-way comparison cannot be reconstructed from any manifest written before sync existed. This is a limit, not a phase.

On a repository materialized by 0.1.x, the first sync writes almost nothing — and that is measured, not estimated. Across the four repositories below, 118 paths read as keep, 24 as update, 54 as conflict, and exactly two were offered for writing. Files carrying a construct block read unknown in all four: nothing records which template variant produced them, and no rendering matches the recorded hash, so the variant cannot be established and sync refuses to touch them. unknown is its own class, separate from conflict, because we cannot tell what made this and you changed this are different facts, and merging them tells an owner they broke something they never touched. From 0.3.0 onward init records the variant, so repositories materialized from here will not have this gap.

Discovery is not reproducible and does not pretend to be. Two runs over the same repository produce different prose. What reproduces is materialization: two init runs with identical pinned variables produce byte-identical trees, and that is tested. Discovery's contribution is covered by provenance instead.

doctor never claims L4, and never reports absent for CI. Branch protection and organisation rulesets live in the GitHub API, not in files, and doctor executes nothing from the repository it inspects — it is run through npx in clones nobody has decided to trust yet. With no complete scope of evidence, present or unknown are the only honest answers. Whether the harness is green on a clean checkout is likewise always unknown, because proving it means running it.

The run ledger is L0. It is written by a step of a skill — a record nobody is obliged to keep. Its value is that construct cost reconciles it against the runtime and shows the drift in both directions, not that it is complete.

Nothing is ever deleted, and nothing deleted is ever restored. A path the owner removed reads as removed for good. A file the construct never wrote is never adopted. There is no --force.

What it was checked against

Until this release every claim about the tool rested on its own tests, its own fixtures and its own tree — a generator graded by the generator. sync was run against four repositories materialized before it existed, and none of them was modified to make the run work.

The sample's bias comes first, because it is real. All four share an author, all four were materialized by earlier versions of this same tool — 0.1.0 and 0.1.1 — and all four were built in a similar style. The independence is narrow and exact: sync did not exist when they were made, so nothing in them was arranged for it. An earlier draft of this note called them four repositories this tool did not build; that was false, and the sentence sounded plausible enough to survive two readings.

repositorymanifestaddupdatekeepconflictunknownremovedorphaned
monorepo0.1.01663121
monorepo0.1.05441321
backend, Cursor target0.1.054511
frontend0.1.1181891

All seven classes occurred in the wild. orphaned — a path the manifest records that no template produces any more — and removed — a path the record carries and the tree does not — were the two that most resembled cells invented to complete a grid. They turned up in repositories built for their own purposes rather than in fixtures written to exercise the classifier, which is what the third source was being kept for.

Three of these four could not produce a report at all when the run started. Their manifests predate the compositionDir variable, and the replay stopped and named it. The fix is the rule this release is built on: a variable the detector can establish about the repository is re-established, never demanded; a variable the owner decided is read from the record and never re-detected; and a fact about the machine rather than the repository is still a legitimate reason to stop.

One of them showed a check that was true, enforced at the level the whole harness is enforced at, and beside the point. The presets ship a test asserting the lint policy's resolved selectors. The test is correct and it runs inside pnpm run quality, which is what CI runs and what a protected branch blocks on. It also arrived in a repository whose eslint.config.mjs belongs to its owner and carries an older, deliberately narrower form of that policy. What was observed is precise and small: pnpm run quality failed immediately after construct init, on a configuration that is not wrong. No guard fixes this: deriving the expectations from the resolved selectors makes the test assert that what is declared is declared, green everywhere and incapable of failing. The line between a restriction that failed and a restriction the owner declined is not ours to draw, so the test now ships only where the construct wrote the configuration, and the same fact reaches everyone else through doctor, which reports the policy absent with its evidence.

And the difference between verified and not observed, said out loud. Those four repositories are a real basis for the claims about sync above, within the bias named at the top of this section. They are not a basis for every claim in this note. All four keep their composition models at the default path, so the live runs could not show the defect doctor had there — a default hidden in a function signature that would have reported a filled discovery section as missing, in the release whose headline is that it stopped doing that. It took a synthetic repository, built to put the models somewhere else, to make it visible. Four green trees said not observed here, which is a different sentence from does not happen. Reality checks what a fixture would never have invented; a fixture checks what reality did not happen to contain. Neither source is complete, and this note does not claim they add up to one.

What it costs, measured

Every figure below comes from a named run of construct cost on this repository, using billable tokens — input, cache writes, cache reads and output summed.

Corrected on 2026-09-20

The figures first published here were about twice their true size. construct cost summed the usage of every assistant line in an agent's journal, and the journal writes one line per content block of a response, repeating that response's usage on each. Every multi-block response was therefore counted two or three times. The command now deduplicates by request identifier and a test holds it there; the numbers below are the corrected ones, each still from the run it names.

The comparisons this section makes are unaffected. The same bias applied to every figure, so the shape of the argument — that the entry into the repository dominates, that it grows with the repository rather than the task, that the class predicts the price poorly — stands exactly as written. What was wrong was every absolute number.

The reasoning class predicts the price poorly. Nine medium runs spanned 437k (wf_3e8b49cd-1e7) to 2.83M (wf_64c0dcf9-188). What dominates is each agent's entry into the repository: a fresh exploration paid in full before anything is produced, and paid again by every agent that starts. It scales with the size of the repository rather than the size of the task. Over one day the same role cost 1.19M (wf_f910421e-fe0), then 2.05M (wf_d850d85f-8ce), then 3.16M (wf_c7fe977a-3d9), then 3.91M (wf_5c3b4127-cfb), as src/ grew beneath it.

A low run pays that entry twice — an implementer and the harness that refuses to let the implementer mark its own homework. A high run pays it three times. The ladder's value is not that it saves money on simple work; it is that it pays the entry as many times as the result demands and no more.

And the ladder's reliability, because /implement ships with the tool and you inherit this number along with it. The architect agent failed to return an answer its schema would accept in four of nine invocations on this repository — wf_61e24c81-eda, wf_d5444abd-7dd, wf_d27aa47d-c36 and wf_5c3b4127-cfb — each time on a long structured field, together burning 8,312,965 tokens and producing nothing. Three of those four were re-run at a lower class with the design written into the brief, and each succeeded.

Two consequences are already in the tool. A response the schema rejects is not re-asked by default, because a retry buys another entry rather than another attempt. The order of the difference, measured directly by splitting five architect entries at their first structured-output call: the exploration a new entry pays before it answers anything costs 385k to 1.48M, while one further attempt inside a call already paid for costs 57k to 100k. That is five to twenty to one — the estimate this note first carried, arrived at by subtracting two different runs, put it at a thousand to one and was wrong. Decision 0008 records the measurement and one argument against its own conclusion. And a run whose design step did not complete blocks at high rather than continuing undesigned, and reports the effort it actually performed instead of the one it was asked for.

A narrower schema for the design step, and giving that step its entry through the discovery artifacts instead of a broad traversal, are the next two things to try. Neither has been measured yet, and this note does not claim they will work.

Names are a tribute to Cyberpunk 2077. Not affiliated with CD Projekt Red.