tlc-spec-lean · diff
v2.0.0 to v2.1.0
20 added, 11 removed. Audit A to A.
---
name: tlc-spec-lean
description: 'Spec-driven feature work that freezes obligations instead of the plan: one human-reviewed plan with EARS criteria, path, entities, interface and one-way doors, then proof-backed checks, then build, then an independent Verifier. Use when the user says "tlc-spec-lean", "plan feature", "specify feature", "write the checks", "build this plan", or "verify work". Do NOT use for standalone design documents unattached to a feature, architecture decomposition analysis, or work that already has a task list or checklist to execute.'
license: CC-BY-4.0
metadata:
author: Tech Leads Club - github.com/tech-leads-club
- version: '2.0.0'
+ version: '2.1.0'
---
# Tech Lead's Club - Spec, Lean
Freeze the obligations. Free the plan. Prove it with someone who did not build it. Derived from tlc-spec-driven 3.3.0 (Felipe Rodrigues), tlc-plan, and tlc-implement.
```
┌──────┐ ┌────────┐ ┌───────┐ ┌────────┐
│ PLAN │ → │ CHECKS │ → │ BUILD │ → │ VERIFY │
└──────┘ └────────┘ └───────┘ └────────┘
read it obligations yours always
```
The common failure of a coding agent is a requirement that never became an active obligation,
followed by a completion claim. What works against it is a small, frozen, external obligation set
and a verifier that is not the author, so the budget goes there and not into choreographing the
model. A human confirms what must be true and how it is being built in one document; only then does
it become obligations with proofs. There is no task breakdown and no component catalogue: what is
hard to reverse gets a one-way door with its literal shape, and the rest is decided while building.
## Rules
These hold even if no reference file is read. Each one lives here; the references point back.
1. Every check is one observable claim with a concrete value plus the proof that settles it: a test
- or command whose exit code decides, or, for a state a capture draws, the author's picture of it
- judged against the capture (rule 9).
+ or command whose exit code decides, or, for a state a capture draws, a picture of it judged
+ against the capture (rule 9).
2. Tests assert what the checks say, not what the code happens to do. Do not write a test by
reading the implementation.
3. A test changes only with the claim it proves, and leaves with the code it proves, in the same
commit. Changing a test to make a suite pass hides the failure it exists to show: a wrong check
is a stop-and-ask.
4. Checks are fixed once `validate_checks.py` passes; a check may be appended when the walk or the
Verifier finds a missing obligation. In the plan, `Landing`, `Surface` and the stored shape are
additive; the path and `Changes underneath` are kept true in the commit that changes them.
5. The Verifier is a fresh sub-agent, never the author, and it always runs, once per feature,
beside the screens sub-agent. `check_proofs.py` settles the mechanical part - every proof run
and passing at `HEAD`, the pictures, the walk, the mutations killed - and the Verifier spends
its time on judgement: mutations where a wrong rule would stay green, examples nobody named,
assertions looser than their criterion. The work is done when every finding is closed by a
commit and `proofs.md` is green at `HEAD`.
6. The mutation count is declared in `checks.md` (`Mutations:`, 3 by default) and decides how many
rules the Verifier probes, not how good the work is: code quality, the tests behind every check
and a complete `Coverage` join are owed whatever N is. The checks are the floor; an edge case or
guard no check names is still the work.
- 7. The completion gate is a script: `validate_verification.py` exits 0.
+ 7. The completion gate is a script: `validate_verification.py` exits 0. The final report opens
+ with its last line; a red gate is said first, never left for the reader to find.
8. An approved spec authorizes local edits and local commits. `git push`, deploy and production
data changes need an explicit go-ahead for that action.
9. **A state a capture draws is one picture.** The author reaches that state and saves
- `<capture>.running.png` beside the capture, at the capture's size: a wider window lays the page
- out differently. Nobody reopens the app to judge it: a screens sub-agent judges the file
- ([verify-screens.md](references/verify-screens.md)), and the picture matches the capture except
- for what `## Differences from the source` lists. A state no capture draws, and an existing
- screen the feature does not change, are proven by a test or a `walk` proof, with no picture.
+ `<capture>.running.png` beside the capture from a browser window at the capture's size - its
+ pixel size over its scale: a wider window lays the page out differently, and resizing the image
+ afterwards hides it. Nobody reopens the app to judge it: a screens sub-agent judges the file
+ ([verify-screens.md](references/verify-screens.md)), and a picture whose text reads smaller than
+ the capture's was resized and is retaken. It matches the capture except for what
+ `## Differences from the source` lists. A state no capture draws, and an existing screen the
+ feature does not change, are proven by a test or a `walk` proof, with no picture and no judge.
10. A capture, frame or live site that comes with the request binds unless the user called it
inspiration. A binding source is changed only by the user. Removing, renaming or departing
from something it draws is asked, recorded as a decision in `.specs/STATE.md`, and listed in
the plan's `## Differences from the source` with `Confirmed: y - AD-NNN`.
+ 11. **The files hold the run; the chat does not survive a compaction.** The user's approval is
+ written in plan.md as `**Approved**: <date> - user: "<their words>"` - never one the user did
+ not give. Each answer goes into its file in the turn it arrives. Each sub-agent goes on the
+ `**Dispatched:**` line of `checks.md` `## Handoff` before it is sent. After a compaction or in a
+ new session, re-read this file and the current phase's reference, then resume from the files
+ ([memory.md](references/memory.md)).
## Configuration
The project declares it in `AGENTS.md` or equivalent; `checks.md` copies the count onto its
`Mutations:` line.
```markdown
## tlc-spec-lean
mutations: 3
```
| Part | Runs | Cannot catch |
| --- | --- | --- |
| `check_proofs.py` (always) | every proof once per runner at `HEAD`, each check matched to the tests its filter selects, pictures and walk checked, mutations injected | a test that passes for the wrong reason |
| The Verifier (always) | N mutations wherever a rule lives, two examples nobody named, assertions looser than their criterion, checks that contradict a binding capture ([verify.md](references/verify.md)) | the rules no mutation probed |
- | The screens sub-agent (a capture has a picture) | an inventory of each capture, every row `same` or `differs` ([verify-screens.md](references/verify-screens.md)) | spacing, colour and type weight; detail smaller than a screenshot shows |
+ | The screens sub-agent (a criterion cites a capture) | an inventory of each capture against the author's picture, every row `same` or `differs`, at a named `HEAD` ([verify-screens.md](references/verify-screens.md)) | spacing, colour and type weight; detail smaller than a screenshot shows; screens no capture draws |
Screens take no mutation; spend N where a wrong implementation costs most. Where N looks too low
for the feature, say so in one line and let the user raise it.
## Artifacts
```
.specs/
├── STATE.md # Decisions log (AD-NNN)
├── LESSONS.md # rendered by scripts/lessons.py - do not hand-edit
├── lessons.json # machine-owned
└── features/<feature>/
├── plan.md # problem, shape, surface, doors, criteria
├── checks.md # claims + proofs, the coverage join, swept, handoff
├── captures/ # binding source files, and the author's <capture>.running.png
├── screens.md # the screens sub-agent's inventory and verdict, per picture
├── proofs.md # written by check_proofs.py: every proof and mutation at HEAD
├── mutations.json # the Verifier's mutations, re-run after the fix
└── verification.md # the Verifier's findings, each closed by a commit
```
For a change under roughly three files with no one-way door, write only `checks.md` with an
`## Intent` paragraph and skip `plan.md`.
## Phases
| Phase | What it produces | Reference |
| --- | --- | --- |
| Plan | problem, shape, surface, doors, criteria; the one default stop for a human | [plan.md](references/plan.md) |
| Checks | claims with proofs, every set joined member by member, the nine dimensions swept | [checks.md](references/checks.md) |
| Build | tests from the checks, the code, commits, the screens walked and saved | [build.md](references/build.md) |
| Verify | rule 5 | [verify.md](references/verify.md), [verify-screens.md](references/verify-screens.md) |
Decisions and lessons: [memory.md](references/memory.md).
## Scripts
Resolve `<skill-dir>` as the directory containing this `SKILL.md` and run
`python3 <skill-dir>/scripts/<name>.py`. Pass `--root` when cwd is not the project root. A non-zero
exit means stop and fix. Run a script rather than read it: its messages say what to change.
| When | Command |
| --- | --- |
| Before presenting the plan | `validate_plan.py <feature>` |
- | Before starting to build | `validate_checks.py <feature>` |
+ | Before starting to build | `validate_checks.py <feature>` - it also requires the plan's `**Approved**:` line |
| Before each commit | `check_commit.py --message "<msg>"` |
| Before dispatching the Verifier, and after the fix | `check_proofs.py <feature> [--mutations <mutations.json>]` |
| Before declaring done | `validate_verification.py <feature>` |
| At distillation | `lessons.py add ...` |
| After editing a validator or template | `selftest.py` |
`selftest.py` mutates the fixtures in `scripts/fixtures/` once per gate rule and requires every
mutant to be killed; the fixtures are also a worked example of a passing artifact set.
## Requires
| Needs | For | Without it |
| --- | --- | --- |
| Python 3.8+ (standard library only) | every script | the gates do not run; the phases still apply |
| git | `mutate.py` and `check_proofs.py` (HEAD, clean tree) | no mutations and no HEAD check: say so in the report |
| sub-agents | the Verifier and the screens sub-agent | run their file in a new session and write `self-verified`; the gate warns |
| a model that reads images | the screens sub-agent | hand the pictures and the captures to the user |
| a browser the agent can drive | saving `<capture>.running.png` and the walk | hand the steps to the user |
Test runners: `check_proofs.py` groups bun, vitest, jest, pytest and go test into one run each
([scripts/runners.py](scripts/runners.py)); any other command runs alone and is judged by its exit
code.
## Output
Produce the artifact; do not narrate the phase. Lead with the verdict and state decisions plainly.
Section headings stay in English; prose follows the project; identifiers are never translated.