Start, a trial · installs only where you enable it

constraint-driven-development

about 6,800 tokens

constraint-driven-development as it installs, each line coloured by where its words came from.

  • 3 files
  • 39 edits
  • 123 sentences left out
  • 1 source
  • last changed 2026-10-19
  • trial

The text

SKILL.md

about 3,400 tokens
  • Addy Osmani's agent-skills
  • greenline

---

name: "constraint-driven-development"

description: "Writes the repository's quality bar down as a contract in its decision home, a command behind every number, and guards it against an agent quietly lowering it: a new suppression, a skipped or deleted test, a stripped assertion, an unimplemented stub, a threshold edited down. Use when no quality bar is written down, when the owner says \"set up constraints\" or \"define our standards\", or when an agent keeps silencing checks or skipping tests to reach green."

---

greenlineOpens the start: the bar is a change; defaults install nothing unpicked

constraint-driven-development is a trial start for a repository whose quality bar is not written down, or whose checks were loosened to reach green. Writing the bar is a change: propose its mode and wait, as AGENTS.md says, open its record with a `## start` section naming constraint-driven-development, and run the line from `frame`; the interview below is the frame step's, its four questions asked at any depth since each has a default, and the bar is written at the build step. Install only what the owner picked: an option is not an obligation to install a dependency, as groundwork says, and a default the owner takes with "I don't know" is no pick: what it would install is proposed with its cost and installed only on the owner's yes.

greenlinePoints at the skill's references page for paths and commands

Greenline's paths and commands for this skill stand on its references page, [references/greenline.md](references/greenline.md): read it when the work lands in the workspace.

Addy Osmani's agent-skillsconstraint-driven-development/SKILL.md

This skill produces 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.

greenlineApplying the floor unattended waits for the mode unless proceed allows it

The interview needs a live user. **Don't run it in non-interactive contexts** (CI, 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. In such a context the floor is applied only as a change the workspace's `proceed` setting lets you make without waiting; otherwise propose it, as AGENTS.md says.

Addy Osmani's agent-skillsconstraint-driven-development/SKILL.md

## 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` |

greenlineCoverage needs a provider; with none there is no number yet

| Coverage today | `coverage/` output, or the suite run once with its coverage provider; with none there is no number, until Q1's (a) adds one |

Addy Osmani's agent-skillsconstraint-driven-development/SKILL.md

| CI | `.github/workflows/`, `.gitlab-ci.yml` |

Addy Osmani's agent-skillsconstraint-driven-development/SKILL.md

Report what you found in two lines, then ask only what's left.

### Step 2: Four questions, each with a default

greenlineLinks frame's interview page, its differences, one round and the answers' home

Follow the frame skill's interview, [../frame/references/interview.md](../frame/references/interview.md), with the differences this start keeps: the four questions below are fixed, each has a default, so "I don't know" is a complete answer that still produces a working config, and no round follows them. At every depth, the inline one included, ask all four in one round, each with its guess, and wait, and record the answers in the change record's frame section, never in a section of this start's own.

Addy Osmani's agent-skillsconstraint-driven-development/SKILL.md

```

Q1: Beyond the floor, which of these do you want enforced?

(a) Test coverage on new code

(b) Security scanning

(c) Performance budgets

(d) Accessibility

(e) Architecture boundaries

GUESS: (a) and (b) — you have a test runner already and you're handling user input.

DEFAULT if unsure: (a) and (b).

Say what each pick costs: (c) and (d) need a running URL, (e) needs a rules file written.

```

```

Q2: When a check fails while the agent is mid-task, should it block or warn?

GUESS: Block. You're running agents unattended and a warning nobody reads is a warning.

DEFAULT if unsure: Block on the floor, warn on everything else for the first two weeks.

```

```

Q3: Do you have target numbers in mind, or should I measure where you are today and hold that line?

GUESS: Measure. Most teams don't have a number, and an invented one gets ignored.

DEFAULT if unsure: Measure and hold. See "Ratchets" below.

```

```

Q4: What's the slowest check you'll tolerate before the agent hands work back?

GUESS: About 90 seconds. Longer and you'll stop running it.

DEFAULT if unsure: 90 seconds at task end, unlimited in CI.

```

Stop at four. A twelve-question intake produces a config nobody understands and a user who regrets starting.

greenlineWrites the bar into the decisions document every agent reads, not CONSTRAINTS.md

### Step 3: Write the bar into the decision home

One section, `## Constraints`, in the repository's decision home: its own decisions document, or the decisions book where it keeps none. Every agent reads it, since AGENTS.md sends each one to the decision home, and a change to it shows up in review where it belongs.

Addy Osmani's agent-skillsconstraint-driven-development/SKILL.md

Last reviewed: 2026-08-08 by @addy

### Floor (always enforced, no setup required)

- No new suppression comments: `@ts-ignore`, `eslint-disable`, `# noqa`, `# type: ignore`

- No unimplemented stubs: `throw new Error("Not implemented")`, empty `catch {}`

- No secrets in source

| Dimension | Rule | Checked by | Runs at |

|-----------|------|-----------|---------|

| Types | Zero type errors | `tsc --noEmit` | every edit |

| Lint | Zero errors from our config | `biome check` | every edit |

| Secrets | No secrets in source | `gitleaks detect --redact` | every edit |

| Coverage | Changed lines ≥ 80% covered | `vitest run --coverage` + git diff | task end, CI |

| Security: code | No high findings | `semgrep scan --config p/default` | CI |

| Security: deps | Nothing at high or above | `osv-scanner scan source -r .` | CI |

| Accessibility | Zero critical or serious | `axe $PREVIEW_URL --tags wcag2a,wcag2aa,wcag21aa` | preview deploy |

| Performance | LCP ≤ 2500ms, CLS ≤ 0.1 | `lighthouse $PREVIEW_URL --output=json` | preview deploy |

Every row names the command that produces the verdict. A dimension with a

number and no command in this column is an aspiration, not a constraint.

| Metric | Today | Direction |

|--------|-------|-----------|

| Project coverage | 62.4% | must not fall |

| Bundle size (main) | 184 kB | must not grow |

| ID | Rule | Path | Reason | Owner | Expires |

|----|------|------|--------|-------|---------|

| W1 | `no-explicit-any` | `src/legacy/**` | Rewrite tracked in ENG-441 | @addy | 2026-11-01 |

```

greenlineAdds no line to the instructions file; AGENTS.md already points at decisions

No line goes into the agent instructions file: AGENTS.md already sends every agent to the decision home.

Addy Osmani's agent-skillsconstraint-driven-development/SKILL.md

### Step 4: 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.

Addy Osmani's agent-skillsconstraint-driven-development/SKILL.md

| Step | When | What runs | Budget |

|-------|---------|-----------|--------|

| build | after each edit | Types, lint, secrets, the floor | under 5s, changed file only |

| verify | at task end | Related tests, coverage on changed lines | under 90s |

| review | at the review step | Everything, plus the guards below | minutes |

| CI | before a merge | Direction checks, no regressions | CI |

Addy Osmani's agent-skillsconstraint-driven-development/SKILL.md

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.

Addy Osmani's agent-skillsconstraint-driven-development/SKILL.md

### Step 5: 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 the bar in the decision home 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.

greenlineThe guard runs through greenline check, and directly in full-history CI

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. Run the floor guard as one of the repository's own checks, recorded as a check, with `--bar` naming the file that holds the bar where the decision home is the repository's own, at the build and review steps; it never runs as a harness hook. In CI, where the repository has one, the job calls the guard directly on each change before it merges, not as a recorded check, whose facts a CI run does not keep: it checks out the whole history of the change and of the branch it merges into, and passes `--base` naming that branch as CI fetched it, such as `origin/main`, and `--bar` as above; a shallow checkout holds no merge base, so the guard bails with exit `2`, which fails the job as exit `1` does.

Addy Osmani's agent-skillsconstraint-driven-development/SKILL.md

**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 6: 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.

Addy Osmani's agent-skillsconstraint-driven-development/SKILL.md

## Escalation Path

Constraints work at three levels of teeth. Start at the first.

greenlineAt the first level, the bar is written and agents read it

1. **Written only.** The bar is written in the decision home, and agents read it. Costs nothing, catches the honest mistakes, relies on the agent complying.

Addy Osmani's agent-skillsconstraint-driven-development/SKILL.md

2. **Scripted.** An `npm run check` (or `make check`) that runs the fast checks, recorded as a check and in your CI. Deterministic, no new dependency.

3. **Tool-backed.** A dedicated runner that handles diff scoping, budgets, ratchets, and the guard checks. Use when the config outgrows a shell script. The floor-guard reference in [references/floor-guard.md](references/floor-guard.md) is the starting point for the guard-checks half of this.

Most projects should stop at 2. Move to 3 when you're maintaining more than about thirty lines of check-running shell.

**A first run can be floor-only.** The floor guard is diff-only and needs no installs, so you can enforce the floor on day one and add numbered dimensions as you install each tool, rather than standing up every checker before the first commit is protected. Security tools that install machine-wide (gitleaks, osv-scanner) can also run CI-only if you'd rather keep laptops clean; declare where each dimension runs in the `Runs at` column.

greenlineHands over the bar and the floor guard; next is frame

## Handoff

Consumes: the repository, its stack, its checks and its CI; the owner's answers to the four questions; the decision home Produces: through the change, the bar in the decision home's `## Constraints` section, a command behind every number, the measured values a ratchet holds, and the floor guard as one of the repository's checks Next: frame, under the mode the owner approves

SKILL.md as installed, byte for byte SHA-256 4717a3b4fe62f834ded92b5ed709517ed7c798b593b3cad6e3f2cb7313093ad1

references/floor-guard.md

about 3,300 tokens
  • Addy Osmani's agent-skills
  • greenline

greenlineKeeps the claim but drops the reference to Step 4, now garden's

Every numbered dimension of the bar maps to a de facto tool. The **floor** does not: it is a diff-scoped check for the five moves in Step 5, and without a shipped reference every agent invents its own, so two runs (or a Python repo and a Go one) produce two different guards. That is the exact non-determinism this skill exists to remove.

Addy Osmani's agent-skillsconstraint-driven-development/references/floor-guard.md

This is the reference. Adapt the patterns to your stack; keep the contract identical.

## Contract

- **Input:** the diff between the merge base and the working tree (added *and* removed lines, plus untracked files). A guard that reads only `git diff` misses new files and staged-but-uncommitted work.

- **Detects the five Step 5 moves:** a weakened threshold in the bar, a test made easier (`.skip`, a deleted test file, an assertion removed from a test that stayed), a silenced checker (a new suppression comment), unfinished work (a stub or empty `catch`), a new Exceptions row.

- **Exit codes:** `0` clean, `1` at least one floor violation (block the change), `2` the guard could not run (no merge base, not a git repo). Never let a `2` read as a `0`.

greenlineReferences to Step 4 leave with the step, which is garden's

- **Reports the rule and the location, never the matched secret value.** Redaction is not optional.

greenlineThe guard exempts its own file and the bar's section from scanning

- **Exempts its own bar and file:** the bar's `## Constraints` section and the guard's own file name the suppressions, stubs and skips they forbid, so neither is scanned for them, and the change that introduces the two passes; the same pattern added anywhere else is flagged.

Addy Osmani's agent-skillsconstraint-driven-development/references/floor-guard.md

- **Tightening is silent, loosening is loud:** only surfaces moves that lower the bar.

## Reference (Node, ~stack-agnostic patterns)

```js

#!/usr/bin/env node

const i = process.argv.indexOf('--base');

return i > -1 ? process.argv[i + 1] : 'origin/main';

// `git diff --no-index` exits 1 whenever the two sides differ, which is the normal case for a

// new file, so that output is kept. Any other failure is null, and null never reads as clean.

const git = (args, { diffExit = false } = {}) => {

try { return execFileSync('git', args, { encoding: 'utf8', stdio: ['ignore', 'pipe', 'pipe'] }); }

catch (e) { return diffExit && e.status === 1 && typeof e.stdout === 'string' ? e.stdout : null; }

};

const bail = (msg) => { console.error('floor-guard: ' + msg); process.exit(2); };

// Run from the top of the work tree. `git ls-files` lists only the current directory's subtree,

// relative to it, so a guard started in a subfolder would miss untracked files elsewhere and name

// the rest differently from `git diff`, which always covers the whole tree.

const top = git(['rev-parse', '--show-toplevel'])?.trim();

if (!top) bail('not inside a git work tree');

const mergeBase = git(['merge-base', base, 'HEAD'])?.trim();

if (!mergeBase) bail('no merge base against ' + base);

// Unified diff plus untracked files (git diff alone cannot see new files).

const tracked = git(['diff', '--unified=0', mergeBase, '--']);

if (tracked === null) bail('could not diff against ' + mergeBase);

const untrackedFiles = git(['ls-files', '--others', '--exclude-standard']);

if (untrackedFiles === null) bail('could not list untracked files');

const untracked = untrackedFiles.split('\n').filter(Boolean).map((f) => {

const d = git(['diff', '--no-index', '--unified=0', '/dev/null', f], { diffExit: true });

if (d === null) bail('could not diff untracked file ' + f);

return d;

}).join('\n');

const diff = tracked + '\n' + untracked;

// Walk the diff. `---` and `+++` are file headers only between a file's `diff` line and its first

// `@@` hunk; inside a hunk every line is content, so an added `++i` (shown as `+++i`) or a removed

// `-- comment` is a change, not a header. Both headers name the file, so a deletion

// (`+++ /dev/null`) keeps its name.

const added = [], removed = [], deleted = [];

const pathOf = (s) => s.replace(/^[ab]\//, '');

for (const line of diff.split('\n')) {

if (line.startsWith('diff ')) inHeader = true;

else if (inHeader) {

if (line.startsWith('--- ')) oldFile = pathOf(line.slice(4));

else if (line.startsWith('+++ ')) {

const newFile = pathOf(line.slice(4));

file = newFile === '/dev/null' ? oldFile : newFile;

if (newFile === '/dev/null') deleted.push(file);

}

}

}

const findings = [];

const flag = (rule, f, text) => findings.push({ rule, file: f, text: text.trim().slice(0, 120) });

const isTest = (f) => /\.(test|spec)\.|_test\.|test_/.test(f);

// 1. Silenced checker — extend this list for your ecosystem.

const SUPPRESSIONS = /@ts-ignore|@ts-nocheck|eslint-disable|biome-ignore|# *noqa|# *type: *ignore|istanbul ignore|nosemgrep|gitleaks:allow|Stryker disable/;

// 4. Unfinished work.

const STUBS = /throw new (Error|NotImplemented).*[Nn]ot implemented|catch\s*\(\w*\)\s*\{\s*\}|catch\s*\{\s*\}|\bTODO\b|\bpass\s*# *stub/;

// 2. A test made easier (added skips).

const SKIPS = /\.(skip|todo)\b|\bxit\(|\bxdescribe\(|@pytest\.mark\.skip|t\.Skip\(/;

if (scanned && SKIPS.test(text)) flag('test-made-easier', file, text);

if (isConstraints(entry, 'new') && /^\| *(W|E)\d+ *\|/.test(text)) flag('new-exception', file, text);

}

// 2b. A test file deleted, or an assertion removed from a test file that still exists.

for (const f of deleted) if (isTest(f)) flag('test-deleted', f, 'file deleted');

for (const { file, text } of removed) {

if (isTest(file) && !deleted.includes(file) && /\b(expect|assert|should)\b/.test(text)) {

flag('assertion-removed', file, text);

}

}

// 1b/2c. A rule of the bar weakened or removed. A rule is a floor bullet or a table row,

// identified by the bullet's text before its first colon or by the row's first cell. Each number

// carries a direction read from the words around it: a minimum (>=, at least, must not fall) is

// loosened by going down, a maximum (<=, at most, under, must not grow) by going up. A number whose

// direction cannot be read is reported whenever it changes, because the guard cannot tell

// tightening from loosening and staying quiet is the wrong default. Numbers are paired within

// their direction (the first minimum with the first minimum, and so on), so a number added

// elsewhere in the text does not shift the pairing; a threshold with no counterpart after the

// edit was removed, and an added one tightens.

const ruleKey = (t) => {

const s = t.trim();

if (s.startsWith('|')) return s.split('|').map((c) => c.trim()).filter(Boolean)[0] ?? '';

if (/^[-*] /.test(s)) return s.slice(2).split(':')[0].trim();

return null; // prose, headings, dates: not a rule

};

const isException = (t) => /^\| *(W|E)\d+ *\|/.test(t.trim());

const MIN_BEFORE = /(>=|>|≥|at least|minimum|\bmin\b|no less than|not fall|not drop)\s*$/;

const MAX_BEFORE = /(<=|<|≤|at most|maximum|\bmax\b|no more than|under|below|not grow|not exceed)\s*$/;

const MIN_AFTER = /^\s*\S*\s*(or more|or higher|must not fall|must not drop)/;

const MAX_AFTER = /^\s*\S*\s*(or less|or lower|must not grow|must not exceed)/;

const thresholds = (t) => {

const out = [], re = /\d+(?:\.\d+)?/g;

let m;

while ((m = re.exec(t))) {

const before = t.slice(Math.max(0, m.index - 24), m.index).toLowerCase();

const after = t.slice(m.index + m[0].length, m.index + m[0].length + 40).toLowerCase();

const dir = MIN_BEFORE.test(before) || MIN_AFTER.test(after) ? 'min'

: MAX_BEFORE.test(before) || MAX_AFTER.test(after) ? 'max' : null;

out.push({ n: Number(m[0]), dir });

}

return out;

};

const removedRules = removed.filter((l) => isConstraints(l, 'old') && ruleKey(l.text) !== null);

const addedRules = added.filter((l) => isConstraints(l, 'new') && ruleKey(l.text) !== null);

for (const r of removedRules) {

const a = addedRules.find((x) => ruleKey(x.text) === ruleKey(r.text));

if (!a) {

if (!isException(r.text)) flag('rule-removed', r.file, r.text); // dropping an exception tightens: silent

continue;

}

const before = thresholds(r.text), after = thresholds(a.text);

let verdict = null;

for (const dir of ['min', 'max', null]) {

const was = before.filter((x) => x.dir === dir), now = after.filter((x) => x.dir === dir);

was.forEach((b, i) => {

const n = now[i];

if (verdict) return;

if (!n) verdict = 'threshold-removed';

else if (n.n === b.n) return;

else if (dir === 'min' ? n.n < b.n : dir === 'max' ? n.n > b.n : true) {

verdict = dir ? 'threshold-loosened' : 'threshold-changed';

}

});

}

if (verdict) flag(verdict, r.file, r.text + ' -> ' + a.text);

}

if (findings.length === 0) { console.log('floor-guard: clean'); process.exit(0); }

console.error('floor-guard: ' + findings.length + ' floor violation(s):');

for (const f of findings) console.error(` [${f.rule}] ${f.file}: ${f.text}`);

if (findings.some((f) => f.rule === 'rule-removed')) {

console.error('\nA rule-removed finding can also mean the rule\'s label changed: rename a rule in one commit and change its thresholds in another.');

}

if (findings.some((f) => f.rule === 'threshold-removed')) {

console.error('\nA threshold-removed finding can also mean a number gained or lost its direction words (">= 80%" becoming "80%", or the reverse): compare the two lines before assuming a threshold was deleted.');

}

console.error('\nEach is a move that lowers the bar. Fix the code, or route it through a tracked exception.');

process.exit(1);

```

## Adapting it

- **Patterns are the only stack-specific part.** Add your language's suppression and stub forms to the three regexes; the diff plumbing, the bar's checks, and the exit codes stay as-is.

- **A `.constraintsignore`** (one glob per line) lets you exempt a path the guard would otherwise flag; check each added line's file against it before flagging, so a genuine exception is a tracked file rather than a loosened rule.

- **This is a starting point, not a finished tool.** It is deliberately regex-shallow: it catches the cheap-road-to-green moves agents actually make, not a determined human hiding a change. That is the right trade for a check that runs on every diff. Once you outgrow it, move to a real runner (Escalation Path level 3).

references/greenline.md

about 160 tokens
  • greenline
greenlineOpens the skill's mechanics page, read with SKILL.md

# Constraint-driven development: the practice's mechanics

This page holds where constraint-driven-development's work lands in the workspace and the commands it runs, which `SKILL.md` names by their nouns. Read it with `SKILL.md` when the work lands in the workspace.

greenlineThe bar's default book and the guard's recorded check

## Where its work lands

The decisions book is `.greenline/DECISIONS.md`, where the bar stands when the repository keeps no decision home of its own and which the floor guard reads by default. The guard runs as a check through `greenline check -- node <guard> --bar <path>`, its `--bar` naming the file that holds the bar where the decision home is the repository's own.

The edits

Red is upstream's text; green is what greenline shipped in its place.

What the edit kinds mean
method
changes what the method does, under a recorded ruling
correction
fixes an error
scope
fits the text to this skill
pointer
re-points a reference
smaller edits
rename, relocate or adjust tool details
  1. Edit 1 pointer SKILL.md:14

    Upstream: This skill produces something different: a written record of **this project's** bar, with numbers, that outlives the conversation and can be checked mechanically.

    Shipped: This skill produces a written record of **this project's** bar, with numbers, that outlives the conversation and can be checked mechanically.

    Without the pack comparison, the record is not called different

    Full reason

    The comparison with the upstream pack's other skills is dropped, so the record is not called different.

  2. Edit 9 method SKILL.md:89

    Upstream: - No skipped or deleted tests without a reason in the commit message

    Shipped: - No skipped or deleted tests; the floor guard flags each one, and only the owner lets one through

    One skip rule, the guard's; only the owner lets a skip through

    Full reason

    The floor guard flags every added skip and deleted test and reads no commit message, so the bar states the one rule the guard enforces, with the owner's word the only way past it.

    Ruling R5

  3. Edit 35 method references/floor-guard.md:25

    Upstream: const base = (() => {

    Shipped: import { relative } from 'node:path'; import { fileURLToPath } from 'node:url'; const base = (() => {

    The guard exempts its own file and the bar's section from scanning

    Full reason

    The guard's patterns matched its own source and the bar's floor bullets, so the change that introduces them failed the guard; the two are exempt from the pattern scan, and nothing else is.

    Ruling R8

  4. Edit 36 method references/floor-guard.md:51

    Upstream: process.chdir(top);

    Shipped: process.chdir(top); // The guard's own file as the diff names it, which it never scans for the patterns it holds. const self = relative(top, fileURLToPath(import.meta.url)).replace(/\\/g, '/');

    The guard exempts its own file and the bar's section from scanning

    Full reason

    The guard's patterns matched its own source and the bar's floor bullets, so the change that introduces them failed the guard; the two are exempt from the pattern scan, and nothing else is.

    Ruling R8

  5. Edit 37 method references/floor-guard.md:55

    Upstream: // Merge base; bail to exit 2 rather than pretending a shallow/rootless clone is clean.

    Shipped: // Merge base; bail to exit 2 rather than pretending a shallow/rootless clone is clean. In CI, check // out the whole history first, since a shallow clone's commits have no parents, and fail the job // on exit 2 as on exit 1.

    The comment tells CI to fetch full history and fail on 2

    Full reason

    The guard's comment says what CI does around it: a checkout holding the whole history, since a shallow clone's commits have no parents, and exit 2 failing the job as exit 1 does.

    Ruling R9

  6. Edit 38 method references/floor-guard.md:132

    Upstream: if (SUPPRESSIONS.test(text)) flag('silenced-checker', file, text); if (STUBS.test(text)) flag('unfinished-work', file, text); if (SKIPS.test(text)) flag('test-made-easier', file, text);

    Shipped: // The bar's section and the guard's own file name the patterns they forbid, so neither is // scanned for them: the change that introduces the two passes, and the same pattern added // anywhere else is flagged. const scanned = file !== self && !isConstraints(entry, 'new'); if (scanned && SUPPRESSIONS.test(text)) flag('silenced-checker', file, text); if (scanned && STUBS.test(text)) flag('unfinished-work', file, text); if (scanned && SKIPS.test(text)) flag('test-made-easier', file, text);

    The guard exempts its own file and the bar's section from scanning

    Full reason

    The guard's patterns matched its own source and the bar's floor bullets, so the change that introduces them failed the guard; the two are exempt from the pattern scan, and nothing else is.

    Ruling R8

33 smaller edits

Renames, locations and tool details, each with its reason.

  1. Edit 2 tool details SKILL.md:22

    Upstream: **Don't run it in non-interactive contexts** (CI, `/loop`, autonomous runs).

    Shipped: **Don't run it in non-interactive contexts** (CI, autonomous runs).

    Drops one agent's loop command from the non-interactive contexts

    Full reason

    One harness's loop command leaves the list of non-interactive contexts (R21).

  2. Edit 3 relocate SKILL.md:80

    Upstream: ```markdown # Constraints

    Shipped: ```markdown ## Constraints

    The bar is a section now, so its headings drop one level

    Full reason

    The bar is a section of the decision home, so its headings sit one level down.

  3. Edit 4 relocate SKILL.md:84

    Upstream: ## Floor (always

    Shipped: ### Floor (always

    The bar is a section now, so its headings drop one level

    Full reason

    The bar is a section of the decision home, so its headings sit one level down.

  4. Edit 5 relocate SKILL.md:92

    Upstream: ## Enforced with numbers

    Shipped: ### Enforced with numbers

    The bar is a section now, so its headings drop one level

    Full reason

    The bar is a section of the decision home, so its headings sit one level down.

  5. Edit 6 relocate SKILL.md:108

    Upstream: ## Measured, not yet enforced

    Shipped: ### Measured, not yet enforced

    The bar is a section now, so its headings drop one level

    Full reason

    The bar is a section of the decision home, so its headings sit one level down.

  6. Edit 7 relocate SKILL.md:115

    Upstream: ## Exceptions

    Shipped: ### Exceptions

    The bar is a section now, so its headings drop one level

    Full reason

    The bar is a section of the decision home, so its headings sit one level down.

  7. Edit 8 relocate SKILL.md:91

    Upstream: - This file does not get weakened to make a change pass

    Shipped: - This section does not get weakened to make a change pass

    Says this section, since the bar is no longer its own file

    Full reason

    The bar is a section of the decision home.

  8. Edit 10 rename SKILL.md:125

    Upstream: ### Step 5: Wire it to the lifecycle

    Shipped: ### Step 4: Wire it to the lifecycle

    Renumbers the steps after the untaken Step 4

    Full reason

    Step 4 is not taken, so the steps after it are numbered on.

  9. Edit 11 tool details SKILL.md:129

    Upstream: | Phase | Command | What runs | Budget |

    Shipped: | Step | When | What runs | Budget |

    The lifecycle table lists the change's steps, not slash commands

    Full reason

    The lifecycle is the change's steps, where upstream named one harness's slash commands (R21).

  10. Edit 12 tool details SKILL.md:131

    Upstream: | BUILD | `/build` | Types, lint, secrets, the floor | under 5s, changed file only |

    Shipped: | build | after each edit | Types, lint, secrets, the floor | under 5s, changed file only |

    The lifecycle table lists the change's steps, not slash commands

    Full reason

    The lifecycle is the change's steps, where upstream named one harness's slash commands (R21).

  11. Edit 13 tool details SKILL.md:132

    Upstream: | VERIFY | `/test` | Related tests, coverage on changed lines | under 90s |

    Shipped: | verify | at task end | Related tests, coverage on changed lines | under 90s |

    The lifecycle table lists the change's steps, not slash commands

    Full reason

    The lifecycle is the change's steps, where upstream named one harness's slash commands (R21).

  12. Edit 14 tool details SKILL.md:133

    Upstream: | REVIEW | `/review` | Everything, plus the guards below | minutes |

    Shipped: | review | at the review step | Everything, plus the guards below | minutes |

    The lifecycle table lists the change's steps, not slash commands

    Full reason

    The lifecycle is the change's steps, where upstream named one harness's slash commands (R21).

  13. Edit 15 tool details SKILL.md:134

    Upstream: | SHIP | `/ship` | Direction checks, no regressions | CI |

    Shipped: | CI | before a merge | Direction checks, no regressions | CI |

    The lifecycle table lists the change's steps, not slash commands

    Full reason

    The lifecycle is the change's steps, where upstream named one harness's slash commands (R21).

  14. Edit 16 rename SKILL.md:141

    Upstream: ### Step 6: Guard the bar itself

    Shipped: ### Step 5: Guard the bar itself

    Renumbers the steps after the untaken Step 4

    Full reason

    Step 4 is not taken, so the steps after it are numbered on.

  15. Edit 17 relocate SKILL.md:147

    Upstream: Compare `CONSTRAINTS.md` against its state at the branch point.

    Shipped: Compare the bar in the decision home against its state at the branch point.

    Each CONSTRAINTS.md mention becomes the bar in the decisions document

    Full reason

    The bar lives in the decision home.

  16. Edit 18 rename SKILL.md:165

    Upstream: ### Step 7: Ratchets, when you don't have a number

    Shipped: ### Step 6: Ratchets, when you don't have a number

    Renumbers the steps after the untaken Step 4

    Full reason

    Step 4 is not taken, so the steps after it are numbered on.

  17. Edit 19 tool details SKILL.md:178

    Upstream: An `npm run check` (or `make check`) that runs the fast checks, wired into your agent's post-edit hook and your CI.

    Shipped: An `npm run check` (or `make check`) that runs the fast checks, recorded as a check and in your CI.

    Scripted checks run through greenline check and CI, not post-edit hooks

    Full reason

    The scripted level runs through greenline check, never a harness hook (R21). Since J-93 S2 the front names greenline's homes and commands by their nouns, their paths and commands on the skill's references page.

  18. Edit 20 rename references/floor-guard.md:3

    Upstream: The **floor** does not: it is a diff-scoped check for the five moves in Step 6, and without a shipped reference every agent invents its own, so two runs (or a Python repo and a Go one) produce two different guards.

    Shipped: The **floor** does not: it is a diff-scoped check for the five moves in Step 5, and without a shipped reference every agent invents its own, so two runs (or a Python repo and a Go one) produce two different guards.

    Renumbers the guard's step to Step 5 after the untaken Step 4

    Full reason

    Step 4 is not taken, so the guard's step is numbered on.

  19. Edit 21 rename references/floor-guard.md:10

    Upstream: **Detects the five Step 6 moves:** a weakened threshold in `CONSTRAINTS.md`, a test made easier (`.skip`, a deleted test file, an assertion removed from a test that stayed), a silenced checker (a new suppression comment), unfinished work (a stub or empty `catch`), a new Exceptions row.

    Shipped: **Detects the five Step 5 moves:** a weakened threshold in `CONSTRAINTS.md`, a test made easier (`.skip`, a deleted test file, an assertion removed from a test that stayed), a silenced checker (a new suppression comment), unfinished work (a stub or empty `catch`), a new Exceptions row.

    Renumbers the guard's step to Step 5 after the untaken Step 4

    Full reason

    Step 4 is not taken, so the guard's step is numbered on.

  20. Edit 22 relocate references/floor-guard.md:10

    Upstream: **Detects the five Step 6 moves:** a weakened threshold in `CONSTRAINTS.md`, a test made easier (`.skip`, a deleted test file, an assertion removed from a test that stayed), a silenced checker (a new suppression comment), unfinished work (a stub or empty `catch`), a new Exceptions row.

    Shipped: **Detects the five Step 6 moves:** a weakened threshold in the bar, a test made easier (`.skip`, a deleted test file, an assertion removed from a test that stayed), a silenced checker (a new suppression comment), unfinished work (a stub or empty `catch`), a new Exceptions row.

    Each CONSTRAINTS.md mention becomes the bar in the decisions document

    Full reason

    The bar lives in the decision home.

  21. Edit 23 relocate references/floor-guard.md:21

    Upstream: // floor-guard.mjs — diff-scoped enforcement of the CONSTRAINTS.md floor. // Usage: node floor-guard.mjs [--base <ref>] (default base: origin/main)

    Shipped: // floor-guard.mjs: diff-scoped enforcement of the bar's floor, in the decision home. // Usage: node floor-guard.mjs [--base <ref>] [--bar <path>] // (defaults: origin/main, and the decisions book .greenline/DECISIONS.md)

    The guard reads the bar from the decisions book by default

    Full reason

    The bar lives in the decision home, so the guard reads the file that holds it, the decisions book by default.

  22. Edit 24 relocate references/floor-guard.md:32

    Upstream: })(); // `git diff --no-index` exits 1 whenever the two sides differ, which is the normal case for a

    Shipped: })(); const bar = (() => { const i = process.argv.indexOf('--bar'); return i > -1 ? process.argv[i + 1] : '.greenline/DECISIONS.md'; })(); // `git diff --no-index` exits 1 whenever the two sides differ, which is the normal case for a

    The guard takes a --bar path, defaulting to the decisions book

    Full reason

    The guard takes the path of the file that holds the bar, the decisions book by default.

  23. Edit 25 relocate references/floor-guard.md:103

    Upstream: const isConstraints = (f) => /CONSTRAINTS\.md$/.test(f);

    Shipped: // The bar is the `## Constraints` section of that file: a line counts only inside the section, in the old // text for a removed line and in the new text for an added one, so an edit elsewhere in the decision // home is no move on the bar. const sectionOf = (text) => { if (text === null) return null; const lines = text.split('\n'); const start = lines.findIndex((l) => /^## Constraints\s*$/.test(l)); if (start < 0) return null; const end = lines.findIndex((l, i) => i > start && /^## /.test(l)); return [start + 2, end < 0 ? lines.length : end]; }; const readBar = () => { try { return readFileSync(bar, 'utf8'); } catch { return null; } }; const oldBar = sectionOf(git(['show', mergeBase + ':' + bar])); const newBar = sectionOf(readBar()); const within = (range, n) => range !== null && n >= range[0] && n <= range[1]; const isConstraints = (l, side) => l.file === bar && within(side === 'old' ? oldBar : newBar, l.line); // A section the old text had and the new one lacks is the bar removed or renamed, never a quiet // skip. if (oldBar !== null && newBar === null) flag('section-removed', bar, '## Constraints');

    Only lines inside the Constraints section count as moves on the bar

    Full reason

    The bar is the `## Constraints` section of the file named, so only a line inside that section, in the old or the new text, is a move on the bar; an edit elsewhere in the decision home is not.

  24. Edit 26 relocate references/floor-guard.md:150

    Upstream: // 1b/2c. A rule in CONSTRAINTS.md weakened or removed. A rule is a floor bullet or a table row,

    Shipped: // 1b/2c. A rule of the bar weakened or removed. A rule is a floor bullet or a table row,

    Each CONSTRAINTS.md mention becomes the bar in the decisions document

    Full reason

    The bar lives in the decision home.

  25. Edit 27 relocate references/floor-guard.md:24

    Upstream: import { execFileSync } from 'node:child_process';

    Shipped: import { execFileSync } from 'node:child_process'; import { readFileSync } from 'node:fs';

    The guard tracks line numbers and reads the file at both ends

    Full reason

    The guard reads the bar's section alone, so each diff line carries its line number in the old or the new text, and the file is read at both ends.

  26. Edit 28 relocate references/floor-guard.md:79

    Upstream: let file = '', oldFile = '', inHeader = false;

    Shipped: let file = '', oldFile = '', inHeader = false, oldLine = 0, newLine = 0;

    The guard tracks line numbers and reads the file at both ends

    Full reason

    The guard reads the bar's section alone, so each diff line carries its line number in the old or the new text, and the file is read at both ends.

  27. Edit 29 relocate references/floor-guard.md:82

    Upstream: else if (line.startsWith('@@')) inHeader = false;

    Shipped: else if (line.startsWith('@@')) { inHeader = false; const m = /^@@ -(\d+)(?:,\d+)? \+(\d+)/.exec(line); oldLine = m ? Number(m[1]) : 0; newLine = m ? Number(m[2]) : 0; }

    The guard tracks line numbers and reads the file at both ends

    Full reason

    The guard reads the bar's section alone, so each diff line carries its line number in the old or the new text, and the file is read at both ends.

  28. Edit 30 relocate references/floor-guard.md:96

    Upstream: else if (line.startsWith('+')) added.push({ file, text: line.slice(1) }); else if (line.startsWith('-')) removed.push({ file, text: line.slice(1) });

    Shipped: else if (line.startsWith('+')) added.push({ file, text: line.slice(1), line: newLine++ }); else if (line.startsWith('-')) removed.push({ file, text: line.slice(1), line: oldLine++ });

    The guard tracks line numbers and reads the file at both ends

    Full reason

    The guard reads the bar's section alone, so each diff line carries its line number in the old or the new text, and the file is read at both ends.

  29. Edit 31 relocate references/floor-guard.md:130

    Upstream: for (const { file, text } of added) {

    Shipped: for (const entry of added) { const { file, text } = entry;

    The guard tracks line numbers and reads the file at both ends

    Full reason

    The guard reads the bar's section alone, so each diff line carries its line number in the old or the new text, and the file is read at both ends.

  30. Edit 32 relocate references/floor-guard.md:139

    Upstream: if (isConstraints(file) && /^\| *(W|E)\d+ *\|/.test(text)) flag('new-exception', file, text);

    Shipped: if (isConstraints(entry, 'new') && /^\| *(W|E)\d+ *\|/.test(text)) flag('new-exception', file, text);

    The guard tracks line numbers and reads the file at both ends

    Full reason

    The guard reads the bar's section alone, so each diff line carries its line number in the old or the new text, and the file is read at both ends.

  31. Edit 33 relocate references/floor-guard.md:182

    Upstream: const removedRules = removed.filter((l) => isConstraints(l.file) && ruleKey(l.text) !== null);

    Shipped: const removedRules = removed.filter((l) => isConstraints(l, 'old') && ruleKey(l.text) !== null);

    The guard tracks line numbers and reads the file at both ends

    Full reason

    The guard reads the bar's section alone, so each diff line carries its line number in the old or the new text, and the file is read at both ends.

  32. Edit 34 relocate references/floor-guard.md:183

    Upstream: const addedRules = added.filter((l) => isConstraints(l.file) && ruleKey(l.text) !== null);

    Shipped: const addedRules = added.filter((l) => isConstraints(l, 'new') && ruleKey(l.text) !== null);

    The guard tracks line numbers and reads the file at both ends

    Full reason

    The guard reads the bar's section alone, so each diff line carries its line number in the old or the new text, and the file is read at both ends.

  33. Edit 39 relocate references/floor-guard.md:222

    Upstream: Add your language's suppression and stub forms to the three regexes; the diff plumbing, the CONSTRAINTS.md checks, and the exit codes stay as-is.

    Shipped: Add your language's suppression and stub forms to the three regexes; the diff plumbing, the bar's checks, and the exit codes stay as-is.

    Each CONSTRAINTS.md mention becomes the bar in the decisions document

    Full reason

    The bar lives in the decision home.

What greenline wrote

The full reason for every passage greenline wrote, in the order of the text.

  1. SKILL.md:8 method

    The opening in the start's own voice: when the block's situation sends an agent here, the bar written as a change on the line, and groundwork's rule that only the owner's picks are installed (contradiction 29), a default taken on "I don't know" included, since Q1's default installs tools. R1

  2. SKILL.md:10 pointer

    The front points at the skill's references page, where greenline's paths and commands for it stand since J-93 S2, so the front holds the method alone.

  3. SKILL.md:22 method

    Applying the floor unattended is an edit, so it waits for the mode unless the proceed setting allows it, as the block's proposal says. R2

  4. SKILL.md:35 method

    Running the suite gives no coverage number where the repository has no coverage provider, so the row says so, and that a measured coverage waits on Q1's (a); upstream's row assumed a provider. R3

    Credits: Addy Osmani's agent-skills

  5. SKILL.md:42 method

    The intake follows the frame skill's interview, which asks a frontier in one round (contradiction 1), where upstream pointed at interview-me, which the roster lacks; it links the page beside frame, says what this start keeps apart, and where frame records the four answers. R4

    Credits: Addy Osmani's agent-skills

  6. SKILL.md:76 relocate

    The bar lives in the decision home, which the block already sends every agent to, where upstream wrote CONSTRAINTS.md at the repository root (R21). Since J-93 S2 the front names greenline's homes and commands by their nouns, their paths and commands on the skill's references page.

    Credits: Addy Osmani's agent-skills

  7. SKILL.md:123 method

    Upstream added a line to AGENTS.md and CLAUDE.md; the bar is in the decision home the block already reads, so no line goes into the instructions file (R21). R6

  8. SKILL.md:155 method

    The floor guard is a repository check run through greenline check, where upstream wired checks into an agent's hooks (R21); in CI it runs directly, with its base and bar named, on a checkout holding the whole history, since a shallow checkout has no merge base. Since J-93 S2 the front names greenline's homes and commands by their nouns, their paths and commands on the skill's references page. R7

  9. SKILL.md:177 relocate

    The first level of teeth is the bar written in the decision home.

    Credits: Addy Osmani's agent-skills

  10. SKILL.md:185 pointer

    A start's handoff: the bar and the guard produced through the change, and the step its hand reaches, frame under the approved mode.

  11. references/floor-guard.md:3 pointer

    Step 4, the tool table, is garden's, so the sentence keeps its claim and drops the pointer to it.

    Credits: Addy Osmani's agent-skills

  12. references/floor-guard.md:12 pointer

    The pointer to Step 4 leaves with the step, which is garden's.

    Credits: Addy Osmani's agent-skills

  13. references/floor-guard.md:13 method

    The guard's patterns matched its own source and the bar's floor bullets, so the change that introduces them failed the guard; the two are exempt from the pattern scan, and nothing else is. R8

  14. references/greenline.md:1 scope

    The skill's references page opens: greenline's mechanics for the skill, the paths and commands its front names by their nouns, held apart from the upstream prose since J-93 S2.

  15. references/greenline.md:5 relocate

    constraint-driven-development's mechanics: the decisions book that holds the bar by default and the floor guard recorded through greenline check, held apart since J-93 S2.

What it left out

Addy Osmani's agent-skills

Addy Osmani's agent-skills, constraint-driven-development/SKILL.md: 121 of 230 sentences left out
  1. Other skills in this pack describe what good looks like.
  2. `code-review-and-quality` gives you five axes.
  3. `test-driven-development` gives you a cycle.
  4. `security-and-hardening` gives you a threat list.
  5. 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. 5 sentences left out: The overview's comparison names other skills of the upstream pack, which the roster does not carry.
  6. 8 sentences taken
  7. ## When to Use
  8. Apply this skill when:
  9. Starting a project or a significant feature and no quality bar is written down
  10. The user asks to "set up constraints", "add quality gates", "define our standards", or "stop the agent shipping junk"
  11. An agent is producing volume nobody is reading line by line
  12. CI has checks but nobody can say which ones block a merge and which ones are decoration
  13. Coverage, performance, or accessibility numbers get argued about per-PR instead of decided once
  14. 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
  15. **When NOT to use:**
  16. The project already has a `CONSTRAINTS.md` and the user isn't changing it — read it and follow it instead
  17. One-off scripts, spikes, throwaway prototypes
  18. The user wants a code review right now (`code-review-and-quality`) or a CI pipeline built (`ci-cd-and-automation`)
  19. Pre-product-market-fit code with a two-week expected lifetime — the floor below is still worth it, the rest isn't 13 sentences left out: When to use is the description's and the block's situation row; its pointers name skills the roster does not carry.
  20. 13 sentences taken
  21. | Coverage today | `coverage/` output, or run the suite once | left out: Running the suite gives no coverage number where the repository has no coverage provider, so the row says so, and that a measured coverage waits on Q1's (a); upstream's row assumed a provider. R3 replaced by line 35 of SKILL.md
  22. 1 sentence taken
  23. | Agent harness | `.claude/`, `.codex/`, `AGENTS.md` | left out: The row of agent harness paths is harness-bound (R21): the agent instructions file is read in any case.
  24. 2 sentences taken
  25. 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. left out: The intake follows the frame skill's interview, which asks a frontier in one round (contradiction 1), where upstream pointed at interview-me, which the roster lacks; it links the page beside frame, says what this start keeps apart, and where frame records the four answers. R4 replaced by line 42 of SKILL.md
  26. 6 sentences taken
  27. ### Step 3: Write CONSTRAINTS.md
  28. One file at the repo root.
  29. Any agent on any harness can read it, and a change to it shows up in review where it belongs. 3 sentences left out: Step 3 writes the bar into the decision home, where upstream wrote CONSTRAINTS.md at the repository root (R21). replaced by line 35 of SKILL.md
  30. 1 sentence taken
  31. 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.` left out: No line goes into AGENTS.md or CLAUDE.md: the bar is in the decision home the block reads (R21). R6 replaced by line 38 of SKILL.md
  32. ### Step 4: Install what each dimension needs
  33. Picking a dimension means installing something.
  34. 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. 3 sentences left out: Step 4 is not taken: "Picking a dimension means installing something" loses to groundwork's rule that an option is no obligation to install (contradiction 29), and its tool table is garden's. R10
  35. | Dimension | Tool | Install | Run | Gate on |
  36. |-----------|------|---------|-----|---------|
  37. | Types (TS) | tsc | already there | `tsc --noEmit` | any error |
  38. | Types (Python) | mypy | `pip install mypy` | `mypy .` | any error |
  39. | Lint | your existing config | already there | `eslint .` / `biome check` / `ruff check` | any error |
  40. | Coverage (JS) | your test runner | already there | `vitest run --coverage` (or `jest --coverage`) | coverage of changed lines |
  41. | Coverage (Python) | pytest-cov | `pip install pytest-cov` | `pytest --cov --cov-report=lcov` | same |
  42. | Security: code | Semgrep | `pipx install semgrep` | `semgrep scan --config p/default --config p/owasp-top-ten` | any high finding |
  43. | Security: secrets | gitleaks | `brew install gitleaks` | `gitleaks detect --redact --no-banner` | any finding |
  44. | Security: dependencies | osv-scanner | `brew install osv-scanner` | `osv-scanner scan source -r .` | high or above |
  45. | Performance: page | Lighthouse | `npm i -D lighthouse` | `lighthouse $URL --output=json --quiet` | LCP, CLS, performance score |
  46. | Performance: bundle | size-limit | `npm i -D size-limit` | `size-limit --json` | per-entry byte budget |
  47. | Accessibility | axe-core | `npm i -D @axe-core/cli` | `axe $URL --tags wcag2a,wcag2aa,wcag21aa` | zero critical or serious |
  48. | Architecture | dependency-cruiser | `npm i -D dependency-cruiser` | `depcruise --validate src` | any violation |
  49. | Assertion quality | Stryker | `npm i -D @stryker-mutator/core` | `stryker run --mutate <changed files>` | mutation score | 15 sentences left out: The tool, install and default-threshold tables are knowledge consulted for an engineering choice, handed to garden's maintainer. R10
  50. Five things that will bite you if you skip them:
  51. **`--redact` on gitleaks is not optional.**
  52. 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.
  53. Report the rule and the location, never the value.
  54. **Lighthouse and axe need a URL.**
  55. 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.
  56. 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.
  57. **Scope the expensive ones to the diff.**
  58. `stryker run --mutate` on the whole repo takes hours and gets turned off; on the files a change touched it takes under a minute.
  59. Same for Semgrep, which takes a path list.
  60. **Coverage needs no second test run.**
  61. Read the lcov your suite already writes and intersect it with `git diff`.
  62. Running the suite twice to get a number is the fastest way to make people hate this.
  63. **Semgrep's registry rules are free to run; check the licence before redistributing them.**
  64. `opengrep` is a drop-in fork with the same rule format and JSON output if that matters to your legal team.
  65. Add each one to the project's own script so it's reproducible without an agent:
  66. ```json { "scripts": { "check:fast": "tsc --noEmit && eslint . && gitleaks detect --redact --no-banner", "check:task": "npm run check:fast && vitest run --coverage", "check:full": "npm run check:task && semgrep scan --config p/default && osv-scanner scan source -r ." } } ```
  67. That mapping matters more than the tools.
  68. `check:fast` is what runs after an edit, `check:task` when the agent thinks it's done, `check:full` in CI.
  69. The commands now live in two places — the `Checked by` column in `CONSTRAINTS.md` and these scripts.
  70. `CONSTRAINTS.md` is the canonical source: it carries the reason alongside each command and it shows up in review.
  71. The scripts are convenience wrappers that must mirror it, not a second source of truth; if they drift, the file wins. 22 sentences left out: Step 4's cautions, scripts and mapping go with the step, the tools and their flags being garden's. R10
  72. 59 sentences taken
  73. ## Sane Defaults
  74. When the user has no opinion, use these.
  75. They're chosen to be met by most codebases on day one.
  76. | Constraint | Default | Why this number |
  77. |------------|---------|-----------------|
  78. | Coverage of changed lines | ≥ 80% | High enough to force a test, low enough to allow a config line |
  79. | Project coverage | today's value, must not fall | No argument needed to adopt |
  80. | Mutation score (if used) | ≥ 60% to start | Typical for a suite never mutated before; 80% is mature |
  81. | Dependency vulnerabilities | nothing at high or above | Below that is mostly noise |
  82. | LCP | ≤ 2500 ms | Core Web Vitals "good" threshold |
  83. | CLS | ≤ 0.1 | Same |
  84. | Accessibility | zero critical or serious axe violations | Moderate and minor are often debatable |
  85. | Exception lifetime | 90 days | Long enough to plan the fix, short enough to remember |
  86. | Ratchet tolerance | 0.5% | Absorbs drift when an unrelated file moves the number |
  87. State the number and the reason together.
  88. A threshold without a rationale gets deleted by the next person who hits it. 16 sentences left out: The tool, install and default-threshold tables are knowledge consulted for an engineering choice, handed to garden's maintainer. R10
  89. 4 sentences taken
  90. `CONSTRAINTS.md` exists and agents read it. left out: The written level is the bar in the decision home. replaced by line 136 of SKILL.md
  91. 13 sentences taken
  92. ## Common Rationalizations
  93. | Excuse | Reality |
  94. |--------|---------|
  95. | "We'll add constraints once the code settles" | Code settles around whatever was allowed while it was moving |
  96. | "The tests are the constraints" | Tests you wrote prove you agree with yourself; they say nothing about coverage of new code, dependency risk, or bundle growth |
  97. | "We can't hit 80% coverage" | Then don't set 80%. Set today's number and hold it |
  98. | "This will slow the agent down" | Only if you put slow checks in the fast loop. That's a placement error, not an argument against constraints |
  99. | "I'll remember what our standards are" | The agent won't, and it's writing most of the code |
  100. | "Constraints will block us shipping" | An exception with an owner and a date unblocks you. Deleting the constraint unblocks everyone forever |
  101. ## Red Flags
  102. Stop and reconsider if you notice:
  103. The interview ran past four questions, or produced a config the user can't explain
  104. A budget was set that the codebase fails today, with no plan to reach it
  105. A dimension was written into CONSTRAINTS.md with a number but no tool behind it
  106. A checker was hand-rolled when a de facto one exists, so the team's existing config is ignored
  107. Every constraint is checked by the project's own test suite, with no external opinion
  108. `CONSTRAINTS.md` changed in the same commit as the feature that was failing
  109. An exception has no owner, or an expiry more than a year out
  110. The agent proposed relaxing a threshold instead of fixing the code
  111. Slow checks landed in the edit loop and someone has started passing `--no-verify`
  112. Nobody has opened `CONSTRAINTS.md` since it was written
  113. ## Verification
  114. The skill was applied correctly when:
  115. [ ] `CONSTRAINTS.md` exists, and every number in it has a stated reason
  116. [ ] The floor is enforced and passes on the current codebase without changes
  117. [ ] Every dimension the user picked has a tool installed and a command that runs today
  118. [ ] Each constraint says where it runs, and the fast stage stays under a few seconds
  119. [ ] At least one constraint is external (not judged by this project's own tests)
  120. [ ] Measured-only metrics record today's value and a direction
  121. [ ] Exceptions have an owner and an expiry date
  122. [ ] `AGENTS.md` or `CLAUDE.md` points at the file
  123. [ ] A trial run on the current branch produces no failures the user disagrees with
  124. ## See Also
  125. `interview-me` — the one-question-at-a-time discipline this skill's intake borrows
  126. `code-review-and-quality` — how to review; this skill decides what the review enforces
  127. `ci-cd-and-automation` — building the pipeline these constraints run in
  128. `test-driven-development` — the suite that coverage and mutation constraints measure
  129. `security-and-hardening` — what the security dimension should contain
  130. `performance-optimization` — where the performance numbers come from 39 sentences left out: The rationalizations, the red flags, the verification and the see-also restate the steps or name skills the roster does not carry, outside the method core ruled in.
Addy Osmani's agent-skills, constraint-driven-development/references/floor-guard.md: 2 of 25 sentences left out
  1. Every numbered dimension in `CONSTRAINTS.md` maps to a de facto tool (Step 4). left out: The pointer to Step 4 leaves with the step, which is garden's. replaced by line 3 of references/floor-guard.md
  2. 11 sentences taken
  3. Redaction is not optional (Step 4). left out: The pointer to Step 4 leaves with the step, which is garden's. replaced by line 12 of references/floor-guard.md

The ledger entry

greenline's ledger logs how each skill was made and changed, with a reason for each entry.

2026-10-19 2026-10-19-constraint-driven-development-front-and-references

constraint-driven-development's paths and commands move to a references page

The front names greenline's homes and commands by their nouns and points at a references page, which holds their paths and commands (J-93 S2).

Authorised by
2026-10-18 The operator's words of 2026-10-18 on the redesign (N-56, items 2 and 7), "we need the upstream prose upfront and references hold how to work meaning the artefacts etc" and, on the ingest procedures, "we wanna have there prose .. what we don't wanna have is there artefact system and fit them to use ours"; and J-93 S2's finalise words of 2026-10-19, "Fast route, merged slices, parallel jobs (Recommended)": a skill's front is the upstream prose its recipe cuts, and its mechanics, where its work lands, its section's form, its depths at each mode and its commands, stand on its references page
Pages it touched
SKILL.md, references/greenline.md
Rulings, R1 to R21
  1. The operator's rulings of 2026-10-08 on the intake R3 ("yes go ahead") and on the re-cut brief R22 ("yes amend, with one change"): constraint-driven-development enters as a copy of its method core, its steps 1 to 3 and 5 to 7, its escalation path and its floor guard as a support file, a trial start under that name; its tool, install and default-threshold tables are garden's; its bar lives in the decision home; and groundwork's side of contradiction 29 stands, an option being no obligation to install; The operator's rulings of 2026-10-04, "cool then lock everything in": a start hands the job to a mode the owner approves and every check runs through greenline check; R16 of 2026-10-07, a start chosen from the situation a request begins in; the operator's card answers of 2026-10-12 on the first evaluation's brief, "The roster after the cut" and, for the floor guard, "Exempt its own bar and file": the start is cured of what the evaluation found, a default taken on "I don't know" installing nothing the owner did not pick among it
  2. The operator's rulings of 2026-10-08 on the intake R3 ("yes go ahead") and on the re-cut brief R22 ("yes amend, with one change"): constraint-driven-development enters as a copy of its method core, its steps 1 to 3 and 5 to 7, its escalation path and its floor guard as a support file, a trial start under that name; its tool, install and default-threshold tables are garden's; its bar lives in the decision home; and groundwork's side of contradiction 29 stands, an option being no obligation to install; The operator's rulings of 2026-10-04, "cool then lock everything in": a start hands the job to a mode the owner approves and every check runs through greenline check; R16 of 2026-10-07, a start chosen from the situation a request begins in
  3. The operator's card answers of 2026-10-12 on the first evaluation's brief, "The roster after the cut" and, for the floor guard, "Exempt its own bar and file": the start is cured of what the evaluation found, the coverage row saying a number needs a coverage provider among it
  4. The operator's rulings of 2026-10-08 on the intake R3 ("yes go ahead") and on the re-cut brief R22 ("yes amend, with one change"): constraint-driven-development enters as a copy of its method core, its steps 1 to 3 and 5 to 7, its escalation path and its floor guard as a support file, a trial start under that name; its tool, install and default-threshold tables are garden's; its bar lives in the decision home; and groundwork's side of contradiction 29 stands, an option being no obligation to install; the operator's card answers of 2026-10-12 on the first evaluation's brief, "The roster after the cut": the pointer is the exact link to frame's interview page and the start says in one sentence how its four questions run at every depth; and the card of 2026-10-14, "The record's frame section": a start's four frame answers live in the change record's frame section
  5. The operator's card answers of 2026-10-12 on the first evaluation's brief, "The roster after the cut" and, for the floor guard, "Exempt its own bar and file": the start is cured of what the evaluation found, one rule for skipped and deleted tests, stated once, among it
  6. The operator's ruling R21 of 2026-10-08 on harness-bound content ("harness specific skills have be re thought in to being usable on all harnesses") and the R3 ruling of that day, "yes go ahead", on the portable forms: the bar lives in the decision home, which the block already reads, with no line in the agent instructions file, and the floor guard runs as one of the repository's own checks through greenline check, never a harness hook
  7. The operator's ruling R21 of 2026-10-08 on harness-bound content ("harness specific skills have be re thought in to being usable on all harnesses") and the R3 ruling of that day, "yes go ahead", on the portable forms: the bar lives in the decision home, which the block already reads, with no line in the agent instructions file, and the floor guard runs as one of the repository's own checks through greenline check, never a harness hook; the operator's card answers of 2026-10-12 on the first evaluation's brief, "The roster after the cut" and, for the floor guard, "Exempt its own bar and file": the start says how CI calls the guard, the base it diffs against on a shallow checkout and the merge-base bail; The operator's words of 2026-10-18 on the redesign (N-56, items 2 and 7), "we need the upstream prose upfront and references hold how to work meaning the artefacts etc" and, on the ingest procedures, "we wanna have there prose .. what we don't wanna have is there artefact system and fit them to use ours": a skill's front is the upstream prose its recipe cuts, and greenline's mechanics, where the step's work lands, its section's form, its commands and its depths at each mode, stand on its references page (J-93 S2)
  8. The operator's card answers of 2026-10-12 on the first evaluation's brief, "The roster after the cut" and, for the floor guard, "Exempt its own bar and file": the guard's suppression, stub and skip patterns exempt the bar's Constraints section and the guard's own file, so the change that introduces them passes
  9. The operator's card answers of 2026-10-12 on the first evaluation's brief, "The roster after the cut" and, for the floor guard, "Exempt its own bar and file": the start says how CI calls the guard, the base it diffs against on a shallow checkout and the merge-base bail
  10. The operator's rulings of 2026-10-08 on the intake R3 ("yes go ahead") and on the re-cut brief R22 ("yes amend, with one change"): constraint-driven-development enters as a copy of its method core, its steps 1 to 3 and 5 to 7, its escalation path and its floor guard as a support file, a trial start under that name; its tool, install and default-threshold tables are garden's; its bar lives in the decision home; and groundwork's side of contradiction 29 stands, an option being no obligation to install
  11. The method items carry the operator's rulings of 2026-10-08 on the intake R3, "yes go ahead", the verdicts and the contradictions standing as recommended, the method core taken, the tables garden's and groundwork's side of contradiction 29 standing
  12. the operator's ruling R21 of 2026-10-08 on harness-bound and orchestration content ("harness specific skills have be re thought in to being usable on all harnesses") and the R3 ruling of that day, "yes go ahead", on the portable forms, the bar in the decision home with no line in the instructions file and the floor guard a repository check, never a harness hook
  13. the operator's ruling R22 of 2026-10-08 on the re-cut brief, "yes amend, with one change": the roster table's names stand, router, diagnose and architecture-survey among them, with the trial names idea-refine, constraint-driven-development, correct and improve-instructions
  14. the operator's rulings of 2026-10-04, "cool then lock everything in": a start hands the job to a mode the owner approves, every change has one record with its working files and its folder, and a start that edits no file needs no mode and no record
  15. R16 of 2026-10-07, a start chosen from the situation a request begins in ("update the agents md created by greenline product to mention the kind with the skill table and also prose about when to use what kind skill")
  16. its retention under the minimum is caused by the portable form ruled under R21 and the operator's "yes go ahead" of 2026-10-08, the harness names, the home of the bar and the steps renumbered once step 4 left
  17. J-59, on the operator's words of 2026-10-09 ('okay then implement all of this'): a gist of at most twelve words on every account whose reason is stated; no text, measure or method changes
  18. R44 of 2026-10-11, "S6, right after S5, same worker, normal route", and the ruling made that day under the operator's delegation ("i want you to finish implementing them from here and out"): constraint-driven-development asks its four questions at any frame depth, so its pointer names the page frame's interview now lives on
  19. The operator's card answers of 2026-10-12 on the first evaluation's brief, "The roster after the cut" and, for the floor guard, "Exempt its own bar and file": the floor guard's suppression, stub and skip patterns exempt the bar's Constraints section and the guard's own file, so the change that introduces them passes; the start says how CI calls the guard, on a checkout holding the whole history, with its base and bar named, and the merge-base bail; one rule holds skipped and deleted tests; a default taken on "I don't know" installs nothing the owner did not pick; a coverage number needs a coverage provider; the start's pointer is the exact link to frame's interview page; and the start says in one sentence that its four questions are asked in one round, each with its guess, and wait
  20. The operator's card of 2026-10-14, "The record's frame section": a start's four frame answers live in the change record's frame section, and the start writes no section of its own for them
  21. The operator's words of 2026-10-18 on the redesign (N-56, items 2 and 7), "we need the upstream prose upfront and references hold how to work meaning the artefacts etc" and, on the ingest procedures, "we wanna have there prose .. what we don't wanna have is there artefact system and fit them to use ours"; and J-93 S2's finalise words of 2026-10-19, "Fast route, merged slices, parallel jobs (Recommended)": a skill's front is the upstream prose its recipe cuts, and its mechanics, where its work lands, its section's form, its depths at each mode and its commands, stand on its references page
4 earlier entries
  1. 2026-10-14 2026-10-14-constraint-driven-development-guard-passes-its-change

    The floor guard passes its own change; frame's interview is linked exactly

    Pages it touched: SKILL.md, references/floor-guard.md

  2. 2026-10-11 2026-10-11-constraint-driven-development-interview-page

    Its pointer names the page where frame's interview now lives

    Pages it touched: SKILL.md

  3. 2026-10-09 2026-10-09-constraint-driven-development-gists

    Every account gets a short gist for the website

  4. 2026-10-08 2026-10-08-constraint-driven-development-recipe

    Enters as a trial start; its tables stay garden's

    Pages it touched: SKILL.md, references/floor-guard.md