constraint-driven-development

Establishes a project's quality bar as a written contract and stops agents quietly lowering it. Interviews the user on which dimensions matter, supplies sane default thresholds when they have no number in mind, records everything in CONSTRAINTS.md, and watches the diff for a weakened bar — new @ts-i

By addyosmani · 6,070 installs

npx skills add addyosmani/agent-skills --skill constraint-driven-development

Source repository · Upstream listing

Constraint Driven Development Overview Other skills in this pack describe what good looks like. code review and quality gives you five axes. test driven development gives you a cycle. security and hardening gives you a threat list. All of that lives in prose the agent reads and may or may not follow, and none of it survives the end of the session. This skill produces something different: a written record of this project's bar, with numbers, that outlives the conversation and can be checked mechanically. The reason matters. When you wrote the code, reading it told you whether it was any good. An agent writes more in an afternoon than you will read that week, so the judgement moves out of your head and into checks that run around the loop. Those checks need to exist, they need numbers you actually chose, and they need to fire close enough to the work that the agent fixes its own output. Spec driven development says what to build. Test driven development proves it works. Constraint driven development defines what "good enough to ship" means, before anyone argues about it in a pull request. When to Use Apply this skill when: Starting a project or a significant feature and no quality bar is written down The user asks to "set up constraints", "add quality gates", "define our standards", or "stop the agent shipping junk" An agent is producing volume nobody is reading line by line CI has checks but nobody can say which ones block a merge and which ones are decoration Coverage, performance, or accessibility numbers get argued about per PR instead of decided once You're about to run /build auto or any autonomous loop, and the only thing standing between it and main is a test suite the agent also wrote When NOT to use: The project already has a CONSTRAINTS.md and the user isn't changing it — read it and follow it instead One off scripts, spikes, throwaway prototypes The user wants a code review right now ( code review and quality ) or a CI pipeline built ( ci cd and automation ) Pre product market fit code with a two week expected lifetime — the floor below is still worth it, the rest isn't Loading Constraints The interview needs a live user. Don't run it in non interactive contexts (CI, /loop , autonomous runs). If constraints are missing and you're in one of those, apply the Floor below, note that you did, and flag the rest for a human. The Process Step 1: Detect before you ask Never ask what you can read. Before the first question, gather: What Where to look Language and stack package.json , pyproject.toml , go.mod , Cargo.toml Test runner dev dependencies, test script, existing test files Existing linters eslint.config. , biome.json , .ruff.toml Coverage today coverage/ output, or run the suite once CI .github/workflows/ , .gitlab ci.yml Agent harness .claude/ , .codex/ , AGENTS.md Report what you found in two lines, then ask only what's left. Step 2: Four questions, each with a default Follow the one question at a time discipline from interview me , with one change: every question here has a default, so "I don't know" is a complete answer that still produces a working config. Stop at four. A twelve question intake produces a config nobody understands and a user who regrets starting. Step 3: Write CONSTRAINTS.md One file at the repo root. Any agent on any harness can read it, and a change to it shows up in review where it belongs. Then add one line to AGENTS.md and CLAUDE.md : Read CONSTRAINTS.md before writing code. Do not weaken it to make a change pass. Step 4: Install what each dimension needs Picking a dimension means installing something. Don't leave the user with a number and no mechanism, and don't invent your own checker when a de facto one exists — these tools are listed because their rule formats and thresholds are what everything else in the ecosystem targets, so the team's existing config keeps working. Dimension Tool Install Run Gate on Types (TS) tsc already there tsc noEmit any error Types (Python) mypy pip install mypy mypy . any error Lint your existing config already there eslint . / biome check / ruff check any error Coverage (JS) your test runner already there vitest run coverage (or jest coverage ) coverage of changed lines Coverage (Python) pytest cov pip install pytest cov pytest cov cov report=lcov same Security: code Semgrep pipx install semgrep semgrep scan config p/default config p/owasp top ten any high finding Security: secrets gitleaks brew install gitleaks gitleaks detect redact no banner any finding Security: dependencies osv scanner brew install osv scanner osv scanner scan source r . high or above Performance: page Lighthouse npm i D lighthouse lighthouse $URL output=json quiet LCP, CLS, performance score Performance: bundle size limit npm i D size limit size limit json per entry byte budget Accessibility axe core npm i D @axe core/cli axe $URL tags wcag2a,wcag2aa,wcag21aa zero critical or serious Architecture dependency cruiser npm i D dependency cruiser depcruise validate src any violation Assertion quality Stryker npm i D @stryker mutator/core stryker run mutate <changed files mutation score Five things that will bite you if you skip them: 1. redact on gitleaks is not optional. Without it the matched secret lands in the agent's transcript, which is how a leaked key ends up in a log, a summary, or a commit message. Report the rule and the location, never the value. 2. Lighthouse and axe need a URL. They only work against a running app, so they belong in the runtime stage against a preview deploy or a local server you start first. If the project has no URL to hit — a CLI, a library, a desktop app — say so and drop the dimension rather than inventing a check that can't run. 3. Scope the expensive ones to the diff. stryker run mutate on the whole repo takes hours and gets turned off; on the files a change touched it takes under a minute. Same for Semgrep, which takes a path list. 4. Coverage needs no second test run. Read the lcov your suite already writes and intersect it with git diff . Running the suite twice to get a number is the fastest way to make people hate this. 5. Semgrep's registry rules are free to run; check the licence before redistributing them. opengrep is a drop in fork with the same rule format and JSON output if that matters to your legal team. Add each one to the project's own script so it's reproducible without an agent: That mapping matters more than the tools. check:fast is what runs after an edit, check:task when the agent thinks it's done, check:full in CI. The commands now live in two places — the Checked by column in CONSTRAINTS.md and these scripts. CONSTRAINTS.md is the canonical source: it carries the reason alongside each command and it shows up in review. The scripts are convenience wrappers that must mirror it, not a second source of truth; if they drift, the file wins. Step 5: Wire it to the lifecycle The single biggest mistake is running everything everywhere. A check that stalls the agent gets switched off, and a gate people switched off is worse than no gate, because the bar still looks like it exists. Phase Command What runs Budget BUILD /build Types, lint, secrets, the floor under 5s, changed file only VERIFY /test Related tests, coverage on changed lines under 90s REVIEW /review Everything, plus the guards below minutes SHIP /ship Direction checks, no regressions CI Two rules that keep this tolerable: 1. Scope to the diff. Check the lines this change touched, not the whole repo. Coverage of changed lines is a number the agent can move; project coverage is one it inherited. 2. Cost decides placement. Anything over a few seconds moves out of the edit loop. Mutation testing on a whole repo takes hours; on the files a change touched it takes under a minute, which is the difference between a check people run and one they don't. Step 6: Guard the bar itself Someone will point out that if the agent writes the code and the checks, the checks prove nothing. Half right, and worth engineering around. Agents don't craft clever loopholes. They hit a red check and take the cheapest road to green. Watch for these five moves in the diff, at review time: 1. The threshold moved. A budget lowered, a severity dropped, a check removed from the fast stage. Compare CONSTRAINTS.md against its state at the branch point. 2. A test got easier. .skip added, a test file deleted, assertions pulled out of tests that stayed. 3. A checker got silenced. New @ts ignore or eslint disable . Four suppressions deserve special attention because they switch off a check you're relying on: istanbul ignore drops code from coverage instead of testing it, Stryker disable hides a surviving mutant, nosemgrep and gitleaks:allow do it for security findings. 4. Work is unfinished. A stub that throws, an empty catch turning a failure into silence, a TODO standing where the implementation should be. 5. An exception appeared. A new row in the Exceptions table nobody discussed. None of this needs tooling beyond git diff . Tightening the bar should be silent; loosening it should be loud. Unlike the numbered dimensions, the floor has no de facto tool of its own, so an agent asked to enforce it tends to write a checker from scratch, and two agents write two different ones. A reference implementation of these five checks ships with this skill in [references/floor guard.md](references/floor guard.md) (diff scoped, exit 0 / 1 / 2 , patterns adaptable per ecosystem). Adapt that rather than reinventing it, for the same reason every dimension names a de facto tool: so the mechanism is the same across runs and stacks. Not all checks are equally circular. Rank them by one question: can the agent make this pass by writing code that doesn't work? External — axe core encodes WCAG, osv scanner reads a vulnerability database, Lighthouse measures a real browser. The agent can't argue with these. Project — your lint rules, your layer boundaries. A human owns the file. Suite — your own tests. Most useful, and the only genuinely circular one. A bar made entirely of the third kind is worth less than one with an outside opinion in it. Check that at least one external constraint is present. Step 7: Ratchets, when you don't have a number Set 80% coverage on a codebase at 62% and you get a red build forever, then a team that learns to ignore red builds. The alternative asks for no decision: record where you are, then refuse to get worse. Put it in the "Measured, not yet enforced" table with today's number and a direction. Every check compares against the recorded value, not an aspiration. When a number improves, update it; when it drops, that's the finding. This also answers a fair objection to training. Models are rewarded for passing tests, which you can evaluate in seconds. Architectural rot shows up over months and never reaches the weights. A ratchet is the missing penalty, written down where the build can see it. Sane Defaults When the user has no opinion, use these. They're chosen to be met by most codebases on day one. Constraint Default Why this number Coverage of changed lines ≥ 80% High enough to force a test, low enough to allow a config line Project coverage today's value, must not fall No argument needed to adopt Mutation score (if used) ≥ 60% to start Typical for a suite never mutated before; 80% is mature Dependency vulnerabilities nothing at high or abo