About tlc-plan
tlc-plan is a planning skill for coding agents that turns a request into a reviewed, testable feature plan before coding begins. It emphasizes resolving open decisions with the user and recording criteria and binding design references for a separate builder.
Key features
- 01Resolve ambiguous requirements through questions
- 02Write observable acceptance criteria
- 03Record design references and decisions
- 04Validate plans with scripts
- 05Define feature boundaries and dependencies
Use cases
- 01Preparing a feature brief for another agent
- 02Clarifying choices before implementation
- 03Turning design captures into binding references
- 04Splitting work with ordering constraints
Install
Copy the command and run it in a terminal.
npx skills add https:/ / github.com/ tech-leads-club/ agent-skills --skill tlc-planAfter installing, type/tlc-planin Claude Code to confirm it loaded.
Bundled files
References and scripts installed along with SKILL.md.
references/plan.mdscripts/lean_config.pylessons.pyselftest.pyvalidate_plan.pyscripts/fixtures/plan.mdDetails
From the project READMETLC Plan
Ask until nothing is the builder's to decide. Write it down so a stranger can build it.
┌─────┐ ┌───────┐ ┌──────┐ ┌─────────┐
│ ASK │ → │ SHAPE │ → │ CHECK│ → │ APPROVE │ → tlc-implement
└─────┘ └───────┘ └──────┘ └─────────┘
The common failure of a coding agent is a requirement that never became an obligation, followed by a completion claim. It starts here: a gray area the plan settled by guessing, a capture treated as a suggestion, a sentence of the request that landed nowhere. This skill produces the one document a human reads and objects to before any code exists, and it is the whole brief for whoever builds, often a different agent with none of this conversation.
Rules
- Every criterion is one observable outcome with a concrete value - a status code, a field, a bound, a label - that one run settles. "Gracefully", a percentile or "the columns exist" is not a criterion.
- Ask every gray area before presenting the plan: a decision that is the user's, has more than one defensible answer, and is not settled by the code. An assumption never stands in for a question to save one. Asking clarifies how, never whether to add a capability.
- A capture, frame or live site that comes with the request binds unless the user called it
inspiration. Only the user changes a binding source: a departure from it is asked, recorded as
an
AD-NNNin.specs/STATE.md, and listed in## Differences from the source. - The rules that change what a user sees run in a layer the repo's tests reach. Filtering, searching, sorting and counting are rules.
- Only what is hard to reverse is decided here, with its literal shape. Placement, names and types inside the conventions are the builder's and are settled in the diff.
validate_plan.pyexits 0 before the plan is presented, and approval is the one stop for a human. The approval is written under the plan's title as**Approved**: <date> - user: "<their words>"- never one the user did not give - and each answer goes into the plan in the turn it arrives: a compaction keeps the file, not the chat. After one, or in a new session, re-read this file and the plan reference, then continue fromplan.md. The plan stays true through the build:Landing,Surfaceand the data are additive; the path andChanges underneathchange in the commit that changes them.
Cut: how many plans
Read every source completely, then decide how many plans it becomes. Default to one. Every one-way door in the source is then reviewed together, where the interactions between them are visible; split six ways, the same doors arrive in six batches and nobody sees the whole.
Split only for a reason you can defend from evidence: a piece cannot land before another without breaking production, a piece waits on an answer only someone else can give, or a piece belongs to another team. Each piece is then its own feature folder with its own plan.
Say when it is big, and what makes it big: one-way doors first, a migration among them, then the number of slices. Show the seams that exist - order constraints first, marked as the kind that cannot be collapsed - and let the user pick where to cut.
A slice inside a plan is one outcome someone can watch, never a layer. Preparation that proves nothing on its own - a nullable column, a client with no caller - is a commit, not a slice.
Artifacts
.specs/
├── STATE.md # decisions log (AD-NNN)
├── LESSONS.md # rendered by scripts/lessons.py - read it
└── features/<feature>/
├── plan.md # problem, shape, surface, doors, criteria
└── captures/ # the binding source files
The plan format, how to ask, the surface walk and EARS: references/plan.md. Its section headings are a schema tlc-implement and the validator read; prose follows the project's language, and identifiers are never translated.
Decisions and lessons
.specs/STATE.md holds the decisions a future feature has to conform to:
# Project state
## Decisions
| ID | Decision | Rationale | Status | Date |
| --- | --- | --- | --- | --- |
| AD-001 | Webhook ingest is idempotent on provider delivery id | replays are routine and cheap to dedup at the edge | active | 2026-02-14 |
Edit only the body of ## Decisions. Where the repo already keeps ADRs or a decision log, append
there instead. Every active entry constrains the plan: conform, or append one that supersedes it
(the old row's status becomes superseded by AD-NNN) and say why. An answer that departs from a
binding source is a decision: it says what the product shows instead, and its rationale quotes the
question and the option chosen.
Lessons are written by tlc-implement from real verification findings. Read the confirmed ones for the area before asking anything:
python3 <skill-dir>/scripts/lessons.py --root <project> list --status confirmed [--scope <area>]
Scripts
Resolve <skill-dir> as the directory containing this SKILL.md. Pass --root when cwd is not
the project root. Run a script rather than read it: its messages say what to change.
| When | Command |
|---|---|
| Before presenting the plan, and after each change to it | python3 <skill-dir>/scripts/validate_plan.py <feature> |
| Before asking anything | python3 <skill-dir>/scripts/lessons.py --root <project> list --status confirmed |
| After editing the validator or the template | python3 <skill-dir>/scripts/selftest.py |
Requires
| Needs | For | Without it |
|---|---|---|
| Python 3.8+ (standard library only) | the scripts | the plan still applies; say the validator did not run |
| a model that reads images | binding captures | ask the user to describe each state, and say so in Sources |
| access to the design source (file, MCP, URL) | exporting each state to captures/ | ask for the files as a blocks question |
Output
Produce the artifact; do not narrate the phase. When you stop, lead with the verdict: the plan, the questions that block first with your recommendation, then the defaults you picked for the user to correct. Section headings stay in English.