/build-discipline
A language- and domain-agnostic engineering discipline for building non-trivial software that actually survives real use — services, parsers, CLIs, data pipelines, simulators, interactive tools, agents, compilers, anything with state, logic, or behavior beyond a toy. Use this
$ npx -y skills add rohansx/build-discipline --skill build-discipline --agent claude-codeHow it fires
How this skill gets triggered: by you, by Claude, or both.
- Fires itselfAuto-invocation. Claude auto-loads it when your prompt matches the work.Auto-invocation is when the right skill fires by itself at the right moment, driven by a FLOW.md router and a hook, instead of you invoking it by name. It is the difference between a skill being installed and a skill actually getting used.Read the full definition →
- You can call itInvoke it directly when you want it.
- Slash command
/build-discipline
Context preview
The summary Claude sees to decide when to auto-load this skill.
A language- and domain-agnostic engineering discipline for building non-trivial software that actually survives real use — services, parsers, CLIs, data pipelines, simulators, interactive tools, agents, compilers, anything with state, logic, or behavior beyond a toy. Use this
SKILL.md
build-discipline.SKILL.mdname: build-discipline
description: >-
A language- and domain-agnostic engineering discipline for building non-trivial
software that actually survives real use — services, parsers, CLIs, data
pipelines, simulators, interactive tools, agents, compilers, anything with
state, logic, or behavior beyond a toy. Use this skill WHENEVER a task involves
writing or reviewing real code, especially "build me X", "implement Y", "why is
this flaky/collapsing/wrong", or "make this robust" — even when the user doesn't
ask for rigor by name. It exists to supply the discipline a senior engineer
applies by reflex and a model skips by default: separate state cleanly, measure
the constants you'd otherwise guess, verify with invariants instead of hope, and
assemble in gated stages. Apply it before writing code, not after the bug.
Build Discipline
A distilled engineering discipline for building software that keeps working after the demo. It is deliberately concrete: generic advice ("test your code", "watch performance") is worthless — the value is in *which* specific things break, *why*, and the exact habit that prevents each. Every rule here was paid for by a real failure.
**The one idea to keep:**
> Software survives real use not because of a clever algorithm, but because its > state is cleanly separated, every constant that gates behavior was *measured* > rather than guessed, correctness is checked by *invariants you can actually > run*, and the system is assembled in isolated stages you gate one at a time. > Correct behavior is discovered through instrumentation, not asserted by hope.
The most expensive bug in most builds is the same one every time: a magic number chosen by intuition instead of measurement. Internalize P3 and you have already avoided the failure that causes silent collapse, dead output, and days of vibes-based tuning.
---
The nine principles
P1 — Separate three kinds of state; never persist what you can derive
Almost every non-trivial program has exactly three kinds of state, and conflating them is the root of a whole family of bugs:
- **Source of truth** — inputs, user decisions, config, the seed. Persisted.
- **Derived** — anything computable from the truth (indexes, caches, layouts,
aggregates, similarity scores, resolved graphs). *Recomputed, never saved.*
- **Ephemeral** — session-only (cursor, hover, in-flight buffers, UI state). Dies
with the process.
Rule: **never persist what you can derive** (it goes stale and breeds migration hell); **never derive what encodes intent** (recomputing "the user dismissed this" destroys the decision). Route all derivation through one recompute path: mutation → recompute → present. One arrow, no partial-update bugs.
P2 — Keep a pure core, seed the randomness, make it reproducible
Keep the logic that *decides things* free of I/O — no network, disk, DOM, clock, or global mutable state reached from inside it. Inject those at the edges. Replace ambient randomness with an explicitly **seeded PRNG**. This is not purity for its own sake: a pure, seeded core runs thousands of times in milliseconds, headless, and reproduces exactly — which is the precondition for every other discipline here. The moment behavior is non-reproducible, you can no longer answer "did my change help?", because every run now differs for two reasons at once.
P3 — Calibrate every gating constant from printed data, never from intuition
**This is the principle that pays for the whole file.** Any constant that *gates behavior* — a threshold, timeout, retry count, batch size, cache TTL, pool size, backoff factor, similarity floor, rate — must be chosen by writing a tiny probe that prints its real distribution on realistic input, then reading the number off the printout. Not guessed.
Two real collapses, same root:
- A similarity threshold guessed at `0.28` when real values ran `0.05–0.30`
produced an application that silently did *nothing* — zero output, and no error to point at.
- A resource drained `27×` faster than it refilled collapsed a system whose
*global averages looked perfectly healthy* — the exhaustion was local and hidden.
Ten minutes of measurement replaces days of guessing. Always print human-readable context beside the number (the actual records beside the scores, the actual keys beside the counts) — the number alone can't tell you whether it's *right*.
P4 — Prefer composable, data-driven rules over special-cases and global reads
If you catch yourself writing `if (globalTotal > N)` or special-casing one situation, stop: you are hand-authoring a macro behavior that should fall out of the design, and it will be brittle. Prefer many small local rules expressed as **data** (tables, parameter vectors, weighted signal lists) over sprawling type-specific `if/else` code. Data is editable, testable, and can't hide special cases in a branch you forgot. When several weak signals exist, stack them with an explicit priority and merge — each layer stays debuggable alone.
P5 — Assert invariants, not outputs; build the harness before the UI
You often can't assert an exact output (it's input-, seed-, or corpus-dependent). You *can* assert the properties that must hold for **any** correct run, and you should build the thing that checks them *before* building the interface:
- Nothing is `NaN`/null-where-forbidden; every quantity stays within its bound.
- Bookkeeping is consistent (a fresh scan equals the maintained count).
- Reversible operations round-trip exactly: `save → load → continue` is identical
(two classic bugs live here — restore the RNG/cursor state *last*, and don't round-trip through a lossy format if you need bit-identity).
- Intent is honored: a deleted/dismissed thing never silently reappears.
- **Turn the fuzzy goal into a number.** "Does it cluster / converge / stay
stable?" → a metric that's ~baseline for a degenerate run and clearly higher when the property holds. Now "it works" is a re
Read more
name: build-discipline description: >- A language- and domain-agnostic engineering discipline for building non-trivial software that actually survives real use — services, parsers, CLIs, data pipelines, simulators, interactive tools, agents, compilers, anything with state, logic, or behavior beyond a toy. Use this skill WHENEVER a task involves writing or reviewing real code, especially "build me X", "implement Y", "why is this flaky/collapsing/wrong", or "make this robust" — even when the user doesn't ask for rigor by name. It exists to supply the discipline a senior engineer applies by reflex and a model skips by default: separate state cleanly, measure the constants you'd otherwise guess, verify with invariants instead of hope, and assemble in gated stages. Apply it before writing code, not after the bug.
Build Discipline
A distilled engineering discipline for building software that keeps working after the demo. It is deliberately concrete: generic advice ("test your code", "watch performance") is worthless — the value is in *which* specific things break, *why*, and the exact habit that prevents each. Every rule here was paid for by a real failure.
**The one idea to keep:**
> Software survives real use not because of a clever algorithm, but because its > state is cleanly separated, every constant that gates behavior was *measured* > rather than guessed, correctness is checked by *invariants you can actually > run*, and the system is assembled in isolated stages you gate one at a time. > Correct behavior is discovered through instrumentation, not asserted by hope.
The most expensive bug in most builds is the same one every time: a magic number chosen by intuition instead of measurement. Internalize P3 and you have already avoided the failure that causes silent collapse, dead output, and days of vibes-based tuning.
---
The nine principles
P1 — Separate three kinds of state; never persist what you can derive
Almost every non-trivial program has exactly three kinds of state, and conflating them is the root of a whole family of bugs:
- **Source of truth** — inputs, user decisions, config, the seed. Persisted.
- **Derived** — anything computable from the truth (indexes, caches, layouts,
aggregates, similarity scores, resolved graphs). *Recomputed, never saved.*
- **Ephemeral** — session-only (cursor, hover, in-flight buffers, UI state). Dies
with the process.
Rule: **never persist what you can derive** (it goes stale and breeds migration hell); **never derive what encodes intent** (recomputing "the user dismissed this" destroys the decision). Route all derivation through one recompute path: mutation → recompute → present. One arrow, no partial-update bugs.
P2 — Keep a pure core, seed the randomness, make it reproducible
Keep the logic that *decides things* free of I/O — no network, disk, DOM, clock, or global mutable state reached from inside it. Inject those at the edges. Replace ambient randomness with an explicitly **seeded PRNG**. This is not purity for its own sake: a pure, seeded core runs thousands of times in milliseconds, headless, and reproduces exactly — which is the precondition for every other discipline here. The moment behavior is non-reproducible, you can no longer answer "did my change help?", because every run now differs for two reasons at once.
P3 — Calibrate every gating constant from printed data, never from intuition
**This is the principle that pays for the whole file.** Any constant that *gates behavior* — a threshold, timeout, retry count, batch size, cache TTL, pool size, backoff factor, similarity floor, rate — must be chosen by writing a tiny probe that prints its real distribution on realistic input, then reading the number off the printout. Not guessed.
Two real collapses, same root:
- A similarity threshold guessed at `0.28` when real values ran `0.05–0.30`
produced an application that silently did *nothing* — zero output, and no error to point at.
- A resource drained `27×` faster than it refilled collapsed a system whose
*global averages looked perfectly healthy* — the exhaustion was local and hidden.
Ten minutes of measurement replaces days of guessing. Always print human-readable context beside the number (the actual records beside the scores, the actual keys beside the counts) — the number alone can't tell you whether it's *right*.
P4 — Prefer composable, data-driven rules over special-cases and global reads
If you catch yourself writing `if (globalTotal > N)` or special-casing one situation, stop: you are hand-authoring a macro behavior that should fall out of the design, and it will be brittle. Prefer many small local rules expressed as **data** (tables, parameter vectors, weighted signal lists) over sprawling type-specific `if/else` code. Data is editable, testable, and can't hide special cases in a branch you forgot. When several weak signals exist, stack them with an explicit priority and merge — each layer stays debuggable alone.
P5 — Assert invariants, not outputs; build the harness before the UI
You often can't assert an exact output (it's input-, seed-, or corpus-dependent). You *can* assert the properties that must hold for **any** correct run, and you should build the thing that checks them *before* building the interface:
- Nothing is `NaN`/null-where-forbidden; every quantity stays within its bound.
- Bookkeeping is consistent (a fresh scan equals the maintained count).
- Reversible operations round-trip exactly: `save → load → continue` is identical
(two classic bugs live here — restore the RNG/cursor state *last*, and don't round-trip through a lossy format if you need bit-identity).
- Intent is honored: a deleted/dismissed thing never silently reappears.
- **Turn the fuzzy goal into a number.** "Does it cluster / converge / stay
stable?" → a metric that's ~baseline for a degenerate run and clearly higher when the property holds. Now "it works" is a re
A single, language- and domain-agnostic engineering discipline you drop into your AI coding flow (Claude Code, Cursor, or any agent that reads skills / rules files) so it builds like a senior engineer instead of vibe-coding a demo that breaks on contact with
Repo: rohansx/build-discipline

