About tlc-spec-lean
A lean spec-driven development skill for coding agents. It freezes requirements in a concise plan, then uses checks, building, and verification, focusing on verifiable claims and review by someone other than the author rather than task breakdowns or component catalogs.
Key features
- 01Captures requirements and shape in a lean plan
- 02Turns checks into observable claims
- 03Links each check to a test or proof
- 04Requires verification by a non-author
- 05Saves and reviews screen captures when needed
Use cases
- 01Skip a plan document for changes across few files
- 02Attach concrete proof to feature acceptance criteria
- 03Check an implementation against a source capture
- 04Review tests, changes, and verification before completion
Install
Copy the command and run it in a terminal.
npx skills add https:/ / github.com/ tech-leads-club/ agent-skills --skill tlc-spec-leanAfter installing, type/tlc-spec-leanin Claude Code to confirm it loaded.
Bundled files
References and scripts installed along with SKILL.md.
references/build.mdchecks.mdmemory.mdplan.mdverify-screens.mdverify.mdscripts/check_commit.pycheck_proofs.pylean_config.pylessons.pymutate.pyrunners.pyselftest.pyvalidate_checks.pyvalidate_plan.pyvalidate_verification.pyscripts/fixtures/checks.mdplan.mdproofs.mdverification.mdDetails
From the project READMETech 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.
- 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, a picture of it judged against the capture (rule 9).
- Tests assert what the checks say, not what the code happens to do. Do not write a test by reading the implementation.
- 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.
- Checks are fixed once
validate_checks.pypasses; a check may be appended when the walk or the Verifier finds a missing obligation. In the plan,Landing,Surfaceand the stored shape are additive; the path andChanges underneathare kept true in the commit that changes them. - The Verifier is a fresh sub-agent, never the author, and it always runs, once per feature,
beside the screens sub-agent.
check_proofs.pysettles the mechanical part - every proof run and passing atHEAD, 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 andproofs.mdis green atHEAD. - 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 completeCoveragejoin are owed whatever N is. The checks are the floor; an edge case or guard no check names is still the work. - The completion gate is a script:
validate_verification.pyexits 0. The final report opens with its last line; a red gate is said first, never left for the reader to find. - An approved spec authorizes local edits and local commits.
git push, deploy and production data changes need an explicit go-ahead for that action. - A state a capture draws is one picture. The author reaches that state and saves
<capture>.running.pngbeside 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), 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 sourcelists. A state no capture draws, and an existing screen the feature does not change, are proven by a test or awalkproof, with no picture and no judge. - 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 sourcewithConfirmed: y - AD-NNN. - 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 ofchecks.md## Handoffbefore 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).
Configuration
The project declares it in AGENTS.md or equivalent; checks.md copies the count onto its
Mutations: line.
## 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) | the rules no mutation probed |
| 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) | 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 |
| Checks | claims with proofs, every set joined member by member, the nine dimensions swept | checks.md |
| Build | tests from the checks, the code, commits, the screens walked and saved | build.md |
| Verify | rule 5 | verify.md, verify-screens.md |
Decisions and lessons: 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> - 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); 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.