Build-Loop — the per-feature build→review discipline
You are running the loop the harness already runs but never wrote down
(cartograph/STATE.md:37, cartograph/PLAN-oracle-reviewer.md). It is a CONDUCTOR:
each phase delegates to an existing artifact and clears a falsifiable GATE before
the next. Reimplement nothing. The cost of skipping it is the green-but-wrong
build (STATE.md:41 — 3 bugs survived green unit tests, caught only in practice)
and the re-prompting tax this skill exists to remove.
Canonical token (use these exact phase names everywhere): align → criteria →
red tests → pre-build review → build to green → post-build review → verify in practice → capture →
ship.
The funnel (phase · falsifiable gate · who owns it)
- ALIGN ON INTENT. Before anything, confirm you understand what the user
actually wants and the payoff they picture. If the request is ambiguous or you
are inferring, ASK (AskUserQuestion, one decision per turn) — a full build on
an unconfirmed interpretation is confident-wrong and gets cut wholesale. GATE:
success criteria are CONFIRMED by the user, not inferred. Do NOT auto-fire
phases 2+ until this passes, even under an explicit "build it".
- CRITERIA & PREDICT. State falsifiable success criteria; log the
load-bearing prediction. → skill
calibration. First run cartograph/extract.py --query governed-by <target> (Decision D, create-vs-update): a HIT means a spec:
already governs this file — READ the criteria from its requirements: EARS clauses
and STRENGTHEN that binding; a MISS means write criteria inline (optionally author a
new binding). GATE: a prediction id is logged whose --expect ties ≥1 clause to
something you did NOT author (user-confirmed intent / external check), per
calibration's self-confirming---expect trap.
- RED TESTS (example + property). Before a line of production code, write
BOTH example/unit tests (pin known cases) AND property/invariant tests derived
from the spec's INTENT (one property per intent clause whose falsification =
green-but-wrong). Run them; confirm they FAIL. GATE: tests exist and are RED
before any impl. → references/property-tests.md.
- PRE-BUILD REVIEW. Spawn a fresh-context reviewer on the criteria + RED
tests BEFORE building: does the spec match intent (intent-fit, not just
code-correctness)? under-build? over-build / out-of-scope (a finding, not a
bonus)? → agent
critic (give it ONLY the request + criteria + test paths,
never your reasoning). GATE: the critic's verdict is addressed; a wrong
criterion is fixed while it is still cheap.
- BUILD TO GREEN. Write code until the full suite passes — cold, not cached.
On the SECOND identical failure, → skill
stuck-detection (stop, switch
strategy class, don't re-parameterize). GATE: full suite green in a clean run.
- POST-BUILD REVIEW → VERIFY IN PRACTICE. FIRST, after cold-green and BEFORE
the live run, spawn a fresh-context reviewer on the IMPLEMENTATION (not the
spec): the phase-3 critic reviewed tests that had no code to be wrong yet, so
green + a happy-path run can still hide a green-but-wrong impl. → agent
critic;
for an enforcement-layer change the harness-auditor is MANDATORY. Treat a
suggested FIX as a HYPOTHESIS, not a patch — apply it against the FULL suite,
since a fix aimed at one finding can violate a contract another test pins; bind
every confirmed finding with a regression test. THEN run the real thing
end-to-end — the actual tool / trigger / environment, not a proxy → builtin
/verify, and score the load-bearing prediction on the REAL path. GATE:
impl-review findings are regression-bound + addressed, AND the prediction scores
hit on the real path (green gates verify the ARTIFACTS; only the live run
verifies the CLAIM — calibration). (session 21078e9b, 2026-06-23: every
pre-build critic found only test-coverage gaps; ALL real code bugs — 2
selection-follow bugs + 3 enforcement-guard bypasses — surfaced only in the
post-build impl critic + auditor, and one critic's own suggested fix broke a
contract a prior test caught; prediction 6b1e4a12 missed exactly the 'reviews
force no rework' clause.)
TRUNK-DRIFT on a tracked-tree invariant (a coverage / "every X is wired" /
lockfile / manifest check that enumerates tracked files): local-green does NOT
predict CI-green — CI grades your branch MERGED WITH CURRENT main, so a peer who
landed state after you branched flips it RED on inputs you never saw locally. Under
a known-active concurrent peer (multiple live sessions / a worktree holding main):
make THAT the prediction's residual risk (not a generic OS-portability hunch), and
before merge re-fetch + rebase onto origin/main and re-run the full suite on the
rebased tree so local == CI's merged view. (session 37226faa, 2026-06-23: a
CI-coverage guard went RED on 3 tests a concurrent mission-control merge added to
main mid-build; local full-suite-green missed them; prediction 970bdc74 named the
wrong residual risk.)
PLACEHOLDER-METADATA HONESTY (a named green-but-wrong class): when the build ships
placeholder/generated assets, any displayed metadata about them (durations, sizes,
counts) must be DERIVED from — or mechanically TEST-BOUND to — the actual files;
never free-floating hand-written values. Hand-authored metadata drifts to the
aspirational final state, and structural/BDD tests compare data files to each
other, never to the binary assets, so the whole suite stays green while the
product lies. Exactly what the fresh-context critic catches — brief it on the RAW
source + the user's verbatim goal ("judge only what exists"), not on derived docs.
(2026-07-01, session 54794ff2: catalog said 3:47, placeholder audio ended ~1:10;
survived 133 tests + 14-check e2e; caught by critic. First fix hand-SYNCED values —
a sync, not a derivation; the class stayed open until a test binding catalog
durations to the WAV headers landed the same session.)
A FRESHLY WRITTEN E2E DRIVER IS ITSELF UNTESTED CODE: on a first-run failure the
default hypothesis is driver miscalibration, not a product bug. Two recurring
driver errors: (1) one-shot asserts on async state — poll to a deadline instead of
sampling once (media needs load time); (2) asserting state written only by an event
the script never triggered (a best-score persisted only at game-over — the drive
must actually lose first). Read the implementation's state-write points BEFORE
touching product code. House the driver in the repo (tests/e2e/ + dev dep + npm
script), never a scratchpad — temp dirs can't resolve the repo's node_modules, and
the drive must persist as the fix-round's rerunnable gate. (2026-07-01, session
54794ff2: 12/14 → 14/14 with zero product changes.)
- CAPTURE. If the result recurs, was correction-born, or encodes taste the
user articulated, snapshot it. → skill
eval-capture + /run-evals (passes
day-one). Where a spec governs the target (phase-1's governed-by check), write the
eval-corpus case into the governing spec's — or the satisfied requirement's —
verified_by:, and ensure that case EXISTS or the dangling-spec gate blocks; a
status: shipped spec needs every requirement carrying a resolving verified_by or
untested-requirement blocks (extract.py --check). GATE: a corpus case is green
today, or a conscious skip is stated.
- SHIP. Branch + PR. Harness-artifact changes →
/harness-pr (lint, auditor,
body template, human merges). GATE: on a branch, prediction scored, PR opened.
When to run it
Auto-trigger on ANY non-trivial build/feature/fix/refactor — anything that
produces shippable code, changes behavior, or has a definition of done — the
MOMENT phase-0 intent is confirmed. Don't wait to be told; being re-prompted is
the miss this skill closes. SKIP: trivial one-liners, pure lookups, doc-only
edits, read-only analysis. Under-firing returns you to re-prompting; over-firing
taxes quick tasks — phase 0's confirm-gate is what stops it firing on an
ambiguous ask.
Verifying on this Windows checkout (cp1252)
Ad-hoc python3 -c "...open()..." verify one-liners default to cp1252 here and
crash two ways: READING a byte undefined in cp1252 (e.g. 0x8f, from
open(f).read()), and PRINTING a char cp1252 can't encode (arrows →, em-dashes,
CJK from ledger data) to a strict console. It bites in phases 4–5 AND on the late
ship commands (push/PR/score), where it is easy to drop the flag after a clean run. Prefix EVERY inline-python verify command with
PYTHONUTF8=1; to read a file's contents prefer python3 -m py_compile <f> over
open(f).read(). This is the THROWAWAY-command side; making a COMMITTED script
robust (open(encoding="utf-8"), reconfigure stdout) lives in skill
harness-authoring "Running scripts on this Windows checkout" — don't duplicate
it. Do NOT reach for a global PYTHONUTF8 env override: it masks a non-robust
committed script that then breaks in CI / another env, defeating that robustness.
(session 908de0ac, 2026-06-21: inline ast.parse(open(...)) + a later command that
dropped the flag crashed cp1252 3× during an otherwise-clean auto-healer build/verify.)
To confirm a test is import-safe for no-pip CI, do NOT grep '^(import|from)': that
anchor misses third-party imports indented inside try/except ImportError skip-guards
(and lazy in-function imports) — a FALSE "stdlib-only" all-clear. Match leading
whitespace (^\s*(import|from)) or, better, PROVE it: run the test under bare python3
with the suspect package un-importable (a sys.modules blocker / pip-less env); a green
run is proof, a source regex is a guess. (session 37226faa, 2026-06-23: an ^import-
anchored scan twice declared 3 textual-importing mission_control tests "stdlib-only";
only the harness-auditor, which ran them under a textual blocker, caught it.)
Property tests bind green to intent (the one new procedure)
An example test can certify your own assumption; a property derived from the
spec's INTENT, before the code, is tied to something you did not author — so green
proves intent, not just example-pass. The authoring bar + worked examples live in
references/property-tests.md. The cartograph evals' "contracts not counts" check
scripts are the in-repo precedent.
Fan-out refactors with a static-equivalence contract
When N independent files need the SAME semantics-preserving refactor (a codemod, a
rename, composing a shared layer), dispatch one subagent per file IN PARALLEL — but
replace post-hoc verification (the orchestrator diffing each result) with a
correctness-BY-CONSTRUCTION contract each agent must satisfy and PROVE:
- Hand it the canonical mapping (old value → new symbol) as an explicit table.
- Rule: apply a swap ONLY where EVERY property the element sets EXACTLY equals the
target's; otherwise leave it local. "When in doubt, leave local." Forbid swaps
that ADD or remove a property — that changes computed output (not semantics-preserving).
- Require a per-swap proof line in the report (
old == new: YES) + a mechanical gate
(lint / type-check / suite) that must PASS.
The proof obligation is what makes parallel fan-out safe WITHOUT a human diffing each
result: the orchestrator reconciles only the REPORTED exceptions, then runs the full
suite ONCE (the cold green of phase 4-5). (session 1a5cff26, 2026-06-22: 7 surface
files retrofitted to compose a shared CSS component layer — 7/7 returned clean, agents
even refused swaps that would ADD a line-height the element lacked.)
Relationships (one name per concept — never fork)
- venture-build is the multi-session, board-managed, scaffold+ledger SUPERSET
for whole products; it CITES this skill for the per-feature inner loop and keeps
its own grading-independence + validate-live gates. This skill is the generic
core, not a competitor; venture-build phase 4 keeps tests-as-you-go for MVP
pace, while this loop tightens to tests-RED-first for an individual feature.
- Specs: the binding format now EXISTS (proposal
2026-06-21-spec-driven-dev.md;
the cartograph spec layer — spec: frontmatter resolved by extract.py --query governed-by/traces, gated by --check). Phase-1 READS criteria from a governing
spec's requirements:; phase-6 writes the regression into its verified_by:. This
skill CONSUMES that format — it never defines or forks it (routing-learnings:
strengthen the near-match). With no governing spec, write the criteria inline as before.
- cartograph oracle (
cartograph/extract.py --context/--query): query it for
"what depends on this / blast radius" instead of narrating relationships.
- Strengthen THIS skill for any loop refinement; never spawn a second methodology
skill (routing-learnings: strengthen a near-match). A mechanical always/never →
hook; an isolated role → agent — route per the tree, don't bolt it on here.
1---2name: build-loop3description: Build-Loop — the per-feature build→review discipline4---56# Build-Loop — the per-feature build→review discipline78You are running the loop the harness already runs but never wrote down9(cartograph/STATE.md:37, cartograph/PLAN-oracle-reviewer.md). It is a CONDUCTOR:10each phase delegates to an existing artifact and clears a falsifiable GATE before11the next. Reimplement nothing. The cost of skipping it is the green-but-wrong12build (STATE.md:41 — 3 bugs survived green unit tests, caught only in practice)13and the re-prompting tax this skill exists to remove.1415Canonical token (use these exact phase names everywhere): **align → criteria →16red tests → pre-build review → build to green → post-build review → verify in practice → capture →17ship.**1819## The funnel (phase · falsifiable gate · who owns it)20210. **ALIGN ON INTENT.** Before anything, confirm you understand what the user22 actually wants and the payoff they picture. If the request is ambiguous or you23 are inferring, ASK (AskUserQuestion, one decision per turn) — a full build on24 an unconfirmed interpretation is confident-wrong and gets cut wholesale. GATE:25 success criteria are CONFIRMED by the user, not inferred. Do NOT auto-fire26 phases 2+ until this passes, even under an explicit "build it".271. **CRITERIA & PREDICT.** State falsifiable success criteria; log the28 load-bearing prediction. → skill `calibration`. First run `cartograph/extract.py29 --query governed-by <target>` (Decision D, create-vs-update): a HIT means a `spec:`30 already governs this file — READ the criteria from its `requirements:` EARS clauses31 and STRENGTHEN that binding; a MISS means write criteria inline (optionally author a32 new binding). GATE: a prediction id is logged whose `--expect` ties ≥1 clause to33 something you did NOT author (user-confirmed intent / external check), per34 calibration's self-confirming-`--expect` trap.352. **RED TESTS (example + property).** Before a line of production code, write36 BOTH example/unit tests (pin known cases) AND property/invariant tests derived37 from the spec's INTENT (one property per intent clause whose falsification =38 green-but-wrong). Run them; confirm they FAIL. GATE: tests exist and are RED39 before any impl. → references/property-tests.md.403. **PRE-BUILD REVIEW.** Spawn a fresh-context reviewer on the criteria + RED41 tests BEFORE building: does the spec match intent (intent-fit, not just42 code-correctness)? under-build? over-build / out-of-scope (a finding, not a43 bonus)? → agent `critic` (give it ONLY the request + criteria + test paths,44 never your reasoning). GATE: the critic's verdict is addressed; a wrong45 criterion is fixed while it is still cheap.464. **BUILD TO GREEN.** Write code until the full suite passes — cold, not cached.47 On the SECOND identical failure, → skill `stuck-detection` (stop, switch48 strategy class, don't re-parameterize). GATE: full suite green in a clean run.495. **POST-BUILD REVIEW → VERIFY IN PRACTICE.** FIRST, after cold-green and BEFORE50 the live run, spawn a fresh-context reviewer on the IMPLEMENTATION (not the51 spec): the phase-3 critic reviewed tests that had no code to be wrong yet, so52 green + a happy-path run can still hide a green-but-wrong impl. → agent `critic`;53 for an enforcement-layer change the `harness-auditor` is MANDATORY. Treat a54 suggested FIX as a HYPOTHESIS, not a patch — apply it against the FULL suite,55 since a fix aimed at one finding can violate a contract another test pins; bind56 every confirmed finding with a regression test. THEN run the real thing57 end-to-end — the actual tool / trigger / environment, not a proxy → builtin58 `/verify`, and score the load-bearing prediction on the REAL path. GATE:59 impl-review findings are regression-bound + addressed, AND the prediction scores60 hit on the real path (green gates verify the ARTIFACTS; only the live run61 verifies the CLAIM — calibration). (session 21078e9b, 2026-06-23: every62 pre-build critic found only test-coverage gaps; ALL real code bugs — 263 selection-follow bugs + 3 enforcement-guard bypasses — surfaced only in the64 post-build impl critic + auditor, and one critic's own suggested fix broke a65 contract a prior test caught; prediction 6b1e4a12 missed exactly the 'reviews66 force no rework' clause.)67 TRUNK-DRIFT on a tracked-tree invariant (a coverage / "every X is wired" /68 lockfile / manifest check that enumerates tracked files): local-green does NOT69 predict CI-green — CI grades your branch MERGED WITH CURRENT main, so a peer who70 landed state after you branched flips it RED on inputs you never saw locally. Under71 a known-active concurrent peer (multiple live sessions / a worktree holding main):72 make THAT the prediction's residual risk (not a generic OS-portability hunch), and73 before merge re-fetch + rebase onto origin/main and re-run the full suite on the74 rebased tree so local == CI's merged view. (session 37226faa, 2026-06-23: a75 CI-coverage guard went RED on 3 tests a concurrent mission-control merge added to76 main mid-build; local full-suite-green missed them; prediction 970bdc74 named the77 wrong residual risk.)78 PLACEHOLDER-METADATA HONESTY (a named green-but-wrong class): when the build ships79 placeholder/generated assets, any displayed metadata about them (durations, sizes,80 counts) must be DERIVED from — or mechanically TEST-BOUND to — the actual files;81 never free-floating hand-written values. Hand-authored metadata drifts to the82 aspirational final state, and structural/BDD tests compare data files to each83 other, never to the binary assets, so the whole suite stays green while the84 product lies. Exactly what the fresh-context critic catches — brief it on the RAW85 source + the user's verbatim goal ("judge only what exists"), not on derived docs.86 (2026-07-01, session 54794ff2: catalog said 3:47, placeholder audio ended ~1:10;87 survived 133 tests + 14-check e2e; caught by critic. First fix hand-SYNCED values —88 a sync, not a derivation; the class stayed open until a test binding catalog89 durations to the WAV headers landed the same session.)90 A FRESHLY WRITTEN E2E DRIVER IS ITSELF UNTESTED CODE: on a first-run failure the91 default hypothesis is driver miscalibration, not a product bug. Two recurring92 driver errors: (1) one-shot asserts on async state — poll to a deadline instead of93 sampling once (media needs load time); (2) asserting state written only by an event94 the script never triggered (a best-score persisted only at game-over — the drive95 must actually lose first). Read the implementation's state-write points BEFORE96 touching product code. House the driver in the repo (tests/e2e/ + dev dep + npm97 script), never a scratchpad — temp dirs can't resolve the repo's node_modules, and98 the drive must persist as the fix-round's rerunnable gate. (2026-07-01, session99 54794ff2: 12/14 → 14/14 with zero product changes.)1006. **CAPTURE.** If the result recurs, was correction-born, or encodes taste the101 user articulated, snapshot it. → skill `eval-capture` + `/run-evals` (passes102 day-one). Where a spec governs the target (phase-1's `governed-by` check), write the103 eval-corpus case into the governing spec's — or the satisfied requirement's —104 `verified_by:`, and ensure that case EXISTS or the `dangling-spec` gate blocks; a105 `status: shipped` spec needs every requirement carrying a resolving `verified_by` or106 `untested-requirement` blocks (`extract.py --check`). GATE: a corpus case is green107 today, or a conscious skip is stated.1087. **SHIP.** Branch + PR. Harness-artifact changes → `/harness-pr` (lint, auditor,109 body template, human merges). GATE: on a branch, prediction scored, PR opened.110111## When to run it112113Auto-trigger on ANY non-trivial build/feature/fix/refactor — anything that114produces shippable code, changes behavior, or has a definition of done — the115MOMENT phase-0 intent is confirmed. Don't wait to be told; being re-prompted is116the miss this skill closes. SKIP: trivial one-liners, pure lookups, doc-only117edits, read-only analysis. Under-firing returns you to re-prompting; over-firing118taxes quick tasks — phase 0's confirm-gate is what stops it firing on an119ambiguous ask.120121## Verifying on this Windows checkout (cp1252)122123Ad-hoc `python3 -c "...open()..."` verify one-liners default to cp1252 here and124crash two ways: READING a byte undefined in cp1252 (e.g. `0x8f`, from125`open(f).read()`), and PRINTING a char cp1252 can't encode (arrows →, em-dashes,126CJK from ledger data) to a strict console. It bites in phases 4–5 AND on the late127ship commands (push/PR/score), where it is easy to drop the flag after a clean run. Prefix EVERY inline-python verify command with128`PYTHONUTF8=1`; to read a file's contents prefer `python3 -m py_compile <f>` over129`open(f).read()`. This is the THROWAWAY-command side; making a COMMITTED script130robust (`open(encoding="utf-8")`, reconfigure stdout) lives in skill131`harness-authoring` "Running scripts on this Windows checkout" — don't duplicate132it. Do NOT reach for a global `PYTHONUTF8` env override: it masks a non-robust133committed script that then breaks in CI / another env, defeating that robustness.134(session 908de0ac, 2026-06-21: inline `ast.parse(open(...))` + a later command that135dropped the flag crashed cp1252 3× during an otherwise-clean auto-healer build/verify.)136137To confirm a test is import-safe for no-pip CI, do NOT `grep '^(import|from)'`: that138anchor misses third-party imports indented inside `try/except ImportError` skip-guards139(and lazy in-function imports) — a FALSE "stdlib-only" all-clear. Match leading140whitespace (`^\s*(import|from)`) or, better, PROVE it: run the test under bare `python3`141with the suspect package un-importable (a `sys.modules` blocker / pip-less env); a green142run is proof, a source regex is a guess. (session 37226faa, 2026-06-23: an `^import`-143anchored scan twice declared 3 textual-importing mission_control tests "stdlib-only";144only the harness-auditor, which ran them under a textual blocker, caught it.)145146## Property tests bind green to intent (the one new procedure)147148An example test can certify your own assumption; a property derived from the149spec's INTENT, before the code, is tied to something you did not author — so green150proves intent, not just example-pass. The authoring bar + worked examples live in151references/property-tests.md. The cartograph evals' "contracts not counts" check152scripts are the in-repo precedent.153154## Fan-out refactors with a static-equivalence contract155156When N independent files need the SAME semantics-preserving refactor (a codemod, a157rename, composing a shared layer), dispatch one subagent per file IN PARALLEL — but158replace post-hoc verification (the orchestrator diffing each result) with a159correctness-BY-CONSTRUCTION contract each agent must satisfy and PROVE:160 1. Hand it the canonical mapping (old value → new symbol) as an explicit table.161 2. Rule: apply a swap ONLY where EVERY property the element sets EXACTLY equals the162 target's; otherwise leave it local. "When in doubt, leave local." Forbid swaps163 that ADD or remove a property — that changes computed output (not semantics-preserving).164 3. Require a per-swap proof line in the report (`old == new: YES`) + a mechanical gate165 (lint / type-check / suite) that must PASS.166The proof obligation is what makes parallel fan-out safe WITHOUT a human diffing each167result: the orchestrator reconciles only the REPORTED exceptions, then runs the full168suite ONCE (the cold green of phase 4-5). (session 1a5cff26, 2026-06-22: 7 surface169files retrofitted to compose a shared CSS component layer — 7/7 returned clean, agents170even refused swaps that would ADD a line-height the element lacked.)171172## Relationships (one name per concept — never fork)173174- **venture-build** is the multi-session, board-managed, scaffold+ledger SUPERSET175 for whole products; it CITES this skill for the per-feature inner loop and keeps176 its own grading-independence + validate-live gates. This skill is the generic177 core, not a competitor; venture-build phase 4 keeps tests-as-you-go for MVP178 pace, while this loop tightens to tests-RED-first for an individual feature.179- **Specs**: the binding format now EXISTS (proposal `2026-06-21-spec-driven-dev.md`;180 the cartograph spec layer — `spec:` frontmatter resolved by `extract.py --query181 governed-by`/`traces`, gated by `--check`). Phase-1 READS criteria from a governing182 spec's `requirements:`; phase-6 writes the regression into its `verified_by:`. This183 skill CONSUMES that format — it never defines or forks it (routing-learnings:184 strengthen the near-match). With no governing spec, write the criteria inline as before.185- **cartograph oracle** (`cartograph/extract.py --context/--query`): query it for186 "what depends on this / blast radius" instead of narrating relationships.187- Strengthen THIS skill for any loop refinement; never spawn a second methodology188 skill (routing-learnings: strengthen a near-match). A mechanical always/never →189 hook; an isolated role → agent — route per the tree, don't bolt it on here.