news / engineering
the constitution stops being aspirational

the constitution stops being aspirational

in one week, khaos.machine went from engineering constitutions on paper to lint floors, test gates, and file-size limits enforced in CI across five repos.

the gap between an engineering constitution and a shipping standard is the CI gate. khaos.machine’s Go and TypeScript constitutions existed as documents from late 2025 — error wrapping with %w, no panic in production, strict mode mandatory, unknown over any. the week of March 7–13 2026 was when those rules stopped being aspirational.

the lint-debt campaign

khaos-foundation tracked workspace lint violations as a leading indicator of constitution drift. on df129ed (March 7), the count stood at 3528 across the workspace. by 37ca1e3 (March 13), it was 486. the reduction of 86.2% came from coordinated passes across five repos — not a single automated sweep, but targeted work per file cluster, landing in batches at 6ce0df6, 67a30af, and 1b17eb3.

the violations were not exotic. they were the standard accumulation of a codebase that had been evolving faster than its reviewers could annotate: unused imports, exported types with no doc comment, variable shadowing in nested scopes, implicit any in function parameters where TypeScript had no context to infer a narrower type. each category was cheap to fix individually; the bulk was in aggregate volume.

the decision to measure and address it as a single coordinated campaign rather than opportunistic cleanup had a structural effect: it surfaced clusters of violations that shared a root cause. three clusters in khaos-tools traced to a single pattern — generic handler functions accepting interface{} arguments that had been written before the Go constitution codified the any preference and the %w wrapping rule. fixing the cluster fixed the pattern site, not just the symptom.

khaos-gui: from zero tests to 229

khaos-gui’s test situation at the start of the week was characteristic of a fast-moving GUI codebase: the CI pipeline ran pnpm build:web and declared success if it produced bundles. that was the gate. no unit test runner was wired.

393719e added ESLint to the CI matrix. b29fb92 fixed the 13 typecheck errors that surfaced immediately — they had been masked by the missing type-check step. ca5f535 and aabed75 introduced the test runner and the first passing suite. by d60b0c6 and 9b57068, the suite covered 229 assertions across 17 files.

the progress-handler regression at 35507b6 is the most instructive commit in this cluster. a refactor of the job progress handler had introduced a path where a completed job’s progress event could arrive after the handler had already torn down its subscriber. the new test suite caught it at 1a514b2 before it merged to main; 40b0300 confirmed the fix held. that regression had existed in the codebase for two weeks prior to the test suite. it would have been caught in production by a user watching a job finish.

monolith decomposition in the GUI

the Go constitution’s 500-line file limit had a TypeScript parallel in the workspace standards: app-level components above that ceiling were flagged for decomposition. khaos-gui’s app.vue files — one per web app — had grown well past it. the dashboard app.vue was the largest offender.

c3acb79, 82c6acb, db53133, and f8d0f5a decomposed these monoliths. the pattern was consistent across all four commits: extract composables for data-fetching concerns, extract sub-components for discrete UI panels, leave app.vue as the composition root only. ESLint zero-warnings was the exit criterion, not a line count. the decomposition happened to bring all four files under 500 lines, but the measure that mattered was the warning count.

the benefit is not aesthetic. a 200-line app.vue that imports eight composables is easier to review than a 900-line app.vue with eight inline reactive blocks because the reviewer’s attention is bounded: each composable is a contract with a name. the inline block requires reading the surrounding context to understand its invariants.

khaos-tools: 27 files split, two god functions decomposed

khaos-tools had accumulated the workspace’s most severe file-size violations: 27 production files above the 500-line ceiling. 675afd90 and 917f56a8 split the majority, working by package. the decomposition was not cosmetic — each split introduced a package boundary, which forced the caller sites to declare their dependencies explicitly rather than importing from a single monolith package.

Parse and WritePackage were the two god functions. Parse handled format detection, reader construction, AST traversal, KSPD entity assembly, and error accumulation — five distinct concerns in one function. 50839877 extracted format detection and reader construction into their own functions, leaving Parse as an orchestrator. WritePackage followed the same pattern at 8756f119.

the DI change for CLIRunner is worth noting separately. prior to 8756f119, CLIRunner constructed its own subprocess executor inline. the constitution’s dependency injection rule — “inject dependencies via parameters, not global state” — applied, but enforcement was manual. after the change, CLIRunner accepts an executor interface, which made the existing test doubles for subprocess calls usable without test-specific build tags.

khaos-wfl: coverage floor and error classification

khaos-wfl gained a coverage floor via b1af078. the floor was intentionally set low — 60% — because the goal was not coverage theater but coverage baseline: a CI gate that would catch regressions from zero, and a number against which the next quarter’s work could improve.

5305aad and cfaf4c4 introduced error classification with retry and fail-fast semantics. the Reflex DAG had been treating all node failures as retriable; the classification separated transient errors (network timeouts, provider rate-limit responses) from terminal errors (authentication failures, schema validation rejections). terminal errors now trigger immediate suspension of the DAG rather than exhausting the retry budget. 9467746 validated the classification logic against a representative set of provider error shapes.

kspdprotocol v0.2.0 and jsonx omitzero migration

bc42cf5 cut kspdprotocol v0.2.0, which introduced canonical story data types shared across khaos-tools and khaos-wfl. the prior state had each component maintaining its own KSPD struct definitions, leading to silent drift when the spec changed — the compiler would pass because each component’s types were internally consistent, but the JSON shapes diverged at the boundary.

v0.2.0 established a single source of truth. e236e10 followed with the jsonx omitzero migration: fields that were previously omitted with json:",omitempty" but had zero-value semantics distinct from absent-value semantics were switched to the omitzero annotation. this matters for KSPD round-tripping because a scene with zero characters is not the same as a scene where the character list is absent — the former is a valid analysis result; the latter signals a parsing failure.

what this week enabled

the pattern across all five repos is the same: constitutions written as prose are suggestions; constitutions enforced by CI are standards. the lint floor blocked new violations from accumulating without requiring any reviewer to remember the rule. the test suite caught the progress-handler regression before it reached a user. the file-size decompositions made the subsequent voice provider expansion — a larger feature arc that landed the following week — reviewable in a reasonable session.

the faster reviewer cycles in the months after March 13 trace directly to this week. not because the code became simpler, but because the reviewers could trust that the mechanical checks had already been run.