diff --git a/.agents/.skill-lock.json b/.agents/.skill-lock.json new file mode 100644 index 0000000..d31fb45 --- /dev/null +++ b/.agents/.skill-lock.json @@ -0,0 +1,19 @@ +{ + "version": 2, + "skills": { + "tlc-spec-driven": { + "name": "tlc-spec-driven", + "source": "local", + "contentHash": "e3d5c4dec38bff044207b31229fb28114d5fdb79f2c7200f3ec49a13a70024e1", + "installedAt": "2026-07-24T13:23:56.326Z", + "updatedAt": "2026-07-24T13:23:56.360Z", + "agents": [ + "cursor", + "claude-code", + "windsurf" + ], + "method": "copy", + "global": false + } + } +} \ No newline at end of file diff --git a/.agents/.skill-lock.json.backup b/.agents/.skill-lock.json.backup new file mode 100644 index 0000000..2704f77 --- /dev/null +++ b/.agents/.skill-lock.json.backup @@ -0,0 +1,18 @@ +{ + "version": 2, + "skills": { + "tlc-spec-driven": { + "name": "tlc-spec-driven", + "source": "local", + "contentHash": "e3d5c4dec38bff044207b31229fb28114d5fdb79f2c7200f3ec49a13a70024e1", + "installedAt": "2026-07-24T13:23:56.326Z", + "updatedAt": "2026-07-24T13:23:56.346Z", + "agents": [ + "cursor", + "claude-code" + ], + "method": "copy", + "global": false + } + } +} \ No newline at end of file diff --git a/.claude/skills/abrir-pr-jobs-scraper/skill.md b/.claude/skills/abrir-pr-jobs-scraper/skill.md index b92230f..8729f1a 100644 --- a/.claude/skills/abrir-pr-jobs-scraper/skill.md +++ b/.claude/skills/abrir-pr-jobs-scraper/skill.md @@ -52,8 +52,14 @@ Rode os comandos de verificacao da tabela acima. Se algum falhar, informe e pare > **Importante:** Este projeto usa um modelo de fork. O remote `origin` aponta para o fork do > desenvolvedor (ex: `jeremiassnts/Jobs_Scraper_Global`) e o remote `upstream` aponta para o -> repositorio principal (`Benevanio/Jobs_Scraper_Global`). O PR sempre deve ser criado do fork +> repositorio principal (`Cla-Code-Community/candidate`). O PR sempre deve ser criado do fork > (origin) para o upstream, usando a flag `--head` do `gh pr create`. +> +> **Owner do fork:** derive sempre a partir do remote `origin` (nao use `gh repo view`, que +> resolve o repo default e retorna o owner do upstream): +> ```bash +> FORK_OWNER=$(git remote get-url origin | sed -E 's#.*[/:]([^/]+)/[^/]+(\.git)?$#\1#') +> ``` 1. Buscar os commits da branch que estao a frente de develop: ```bash @@ -64,8 +70,8 @@ Rode os comandos de verificacao da tabela acima. Se algum falhar, informe e pare - Informar: "Esta branch nao tem commits a frente de develop. Nao ha mudancas para abrir PR." - Parar a execucao 3. **Se ja existir um PR aberto para essa branch:** - - Detectar o owner do fork: `gh repo view --json owner -q .owner.login` (rodado no diretorio do projeto, que aponta para origin) - - Verificar com: `gh pr list --head :$(git branch --show-current) --repo Benevanio/Jobs_Scraper_Global --state open` + - Detectar o owner do fork a partir do remote `origin`: `FORK_OWNER=$(git remote get-url origin | sed -E 's#.*[/:]([^/]+)/[^/]+(\.git)?$#\1#')` + - Verificar com: `gh pr list --head "$FORK_OWNER:$(git branch --show-current)" --repo Cla-Code-Community/candidate --state open` - Se existir, informar ao usuario e perguntar se quer atualizar o PR existente ou parar ### Passo 4: Rodar testes @@ -100,6 +106,8 @@ Rode os comandos de verificacao da tabela acima. Se algum falhar, informe e pare ### Passo 6: Montar e mostrar preview +> **Por que o formato importa:** o identificador `PAV-XX` no titulo e o link do Linear no corpo sao o que a integracao Linear ↔ GitHub usa para vincular o PR ao card e move-lo automaticamente (para **In Review** ao abrir o PR e para **Done** ao mergear). Nao remova o `PAV-XX` do titulo nem o link do Linear do corpo — sem eles, o card nao se move sozinho. + Montar o PR completo e mostrar ao usuario: **Titulo:** @@ -109,7 +117,7 @@ PAV-XX: **Target:** ``` -Benevanio/Jobs_Scraper_Global (branch develop) +Cla-Code-Community/candidate (branch develop) ``` **Body:** @@ -136,7 +144,7 @@ Mostrar tudo formatado e perguntar: **"O PR esta correto? Confirma a criacao? (s ### Passo 8: Criar o PR -> **Modelo de fork:** O PR e criado do fork (origin) para o upstream (Benevanio/Jobs_Scraper_Global). +> **Modelo de fork:** O PR e criado do fork (origin) para o upstream (Cla-Code-Community/candidate). > E necessario usar `--head :` para que o GitHub identifique corretamente > a branch de origem no fork. @@ -147,20 +155,49 @@ Mostrar tudo formatado e perguntar: **"O PR esta correto? Confirma a criacao? (s - **Se a branch nao existe no remote:** perguntar ao usuario: "A branch ainda nao foi enviada ao remote. Deseja fazer push agora? (s/n)" - Se confirmar, rodar: `git push -u origin $(git branch --show-current)` - Se negar, parar a execucao -2. Detectar o owner do fork: +2. Detectar o owner do fork a partir do remote `origin`: ```bash - FORK_OWNER=$(gh repo view --json owner -q .owner.login) + FORK_OWNER=$(git remote get-url origin | sed -E 's#.*[/:]([^/]+)/[^/]+(\.git)?$#\1#') ``` 3. Criar o PR via GitHub CLI: ```bash gh pr create \ - --repo Benevanio/Jobs_Scraper_Global \ + --repo Cla-Code-Community/candidate \ --base develop \ --head "$FORK_OWNER:$(git branch --show-current)" \ --title "PAV-XX: " \ --body "" ``` -4. Confirmar ao usuario com o link do PR criado +4. **Fallback obrigatorio se o `gh pr create` falhar** com erros do tipo + `No commits between ...`, `Head sha can't be blank` ou `Head ref must be a branch` + (mesmo com a branch existindo no fork e com commits a frente de develop): + + > **Por que acontece:** em fork cujo nome difere do upstream, o `gh pr create` + > pode corromper o `--head` ao montar a requisicao (ex.: truncar `feature/...` + > para `ature/...`), fazendo o GitHub nao encontrar a branch. Antes de tentar o + > fallback, confirme que a branch esta ok comparando direto na API: + > ```bash + > gh api "repos/Cla-Code-Community/candidate/compare/develop...$FORK_OWNER:$(git branch --show-current)" \ + > --jq '{status:.status, ahead_by:.ahead_by}' + > ``` + > Se retornar `ahead_by > 0`, a branch esta correta e o problema e o `gh pr create`. + + Criar o PR direto pela API REST (que respeita o `head` sem corromper): + ```bash + PAYLOAD=$(mktemp) + cat > "$PAYLOAD" <", + "head": "$FORK_OWNER:$(git branch --show-current)", + "base": "develop", + "body": + } + JSON + gh api --method POST repos/Cla-Code-Community/candidate/pulls --input "$PAYLOAD" \ + --jq '{number:.number, url:.html_url}' + rm -f "$PAYLOAD" + ``` +5. Confirmar ao usuario com o link do PR criado ## Erros comuns @@ -170,3 +207,4 @@ Mostrar tudo formatado e perguntar: **"O PR esta correto? Confirma a criacao? (s - **Bloquear por causa de testes falhando** — mostrar as falhas, mas deixar o usuario decidir - **Esquecer de buscar upstream/develop atualizado** — sempre rodar `git fetch upstream develop` antes de comparar - **Nao usar --head no gh pr create** — como o projeto usa fork, e obrigatorio passar `--head :` para o PR ser criado corretamente +- **Desistir quando o `gh pr create` falha com "No commits between..."** — em fork com nome diferente do upstream o `gh` pode corromper o `--head`; valide a branch com o `gh api .../compare` e crie o PR pelo fallback via `gh api .../pulls` (Passo 8) diff --git a/.claude/skills/tlc-spec-driven/.skill-meta.json b/.claude/skills/tlc-spec-driven/.skill-meta.json new file mode 100644 index 0000000..3acd26c --- /dev/null +++ b/.claude/skills/tlc-spec-driven/.skill-meta.json @@ -0,0 +1,4 @@ +{ + "contentHash": "e3d5c4dec38bff044207b31229fb28114d5fdb79f2c7200f3ec49a13a70024e1", + "downloadedAt": 1784654807392 +} \ No newline at end of file diff --git a/.claude/skills/tlc-spec-driven/SKILL.md b/.claude/skills/tlc-spec-driven/SKILL.md new file mode 100644 index 0000000..0261dec --- /dev/null +++ b/.claude/skills/tlc-spec-driven/SKILL.md @@ -0,0 +1,164 @@ +--- +name: tlc-spec-driven +description: Feature planning and implementation with 4 adaptive phases — Specify, Design, Tasks, Execute. Auto-sizes depth by complexity. Creates atomic tasks with verification criteria, atomic git commits, and requirement traceability. Features an independent Verifier (author != verifier, evidence-or-zero), persistent decision log (STATE.md), and test-coverage-matrix-driven tests, plus a self-improving lessons layer that turns verification failures into reusable project-local guidance. Stack-agnostic. Use when (1) Planning features (requirements, design, task breakdown), (2) Implementing with verification and atomic commits, (3) Validating or verifying an implementation against a spec. Triggers on "specify feature", "discuss feature", "design", "tasks", "implement", "validate", "verify work", "UAT", "record decision", "pause work", "resume work". Do NOT use for architecture decomposition analysis (use architecture skills) or technical design docs (use create-technical-design-doc). +license: CC-BY-4.0 +metadata: + author: Felipe Rodrigues - github.com/felipfr + version: 3.2.0 +--- + +# Tech Lead's Club - Spec-Driven Development + +Plan and implement features with precision. Granular tasks. Clear dependencies. Right tools. Zero ceremony. + +``` +┌──────────┐ ┌──────────┐ ┌─────────┐ ┌─────────┐ +│ SPECIFY │ → │ DESIGN │ → │ TASKS │ → │ EXECUTE │ +└──────────┘ └──────────┘ └─────────┘ └─────────┘ + required optional* optional* required + +* Agent auto-skips when scope doesn't need it +``` + +## Critical Rules (read before acting) + +**Loading this skill's files.** Reference files live under `references/` in this skill's own directory (where this `SKILL.md` resides). Resolve them relative to the skill directory — never the workspace root — and load them through the active skill by name; never assume a fixed install path. When a step tells you to read a reference, **read it completely (to EOF)** before acting — never act on a partial/truncated read. + +**Execution contract — every task, non-negotiable (holds even if you do not open the reference files):** + +1. Tests derive from the spec's acceptance criteria and assert spec-defined outcomes — they never mirror the implementation. +2. The gate must pass (tests pass) before a task is done — the test runner decides, not self-assessment. +3. One atomic commit per task. Never batch tasks; never weaken, skip, or delete tests to make them pass. +4. After the LAST task, a fresh **Verifier always runs automatically** (author ≠ verifier) — spec-anchored outcome check + discrimination sensor. It is never optional and never prompted. See Sub-Agent Delegation. + +**Before Execute:** read [implement.md](references/implement.md) completely; if a formal `tasks.md` packs into more than one task-budgeted batch (> ~8 tasks), present the sub-agent offer first (see Sub-Agent Delegation). + +## Auto-Sizing: The Core Principle + +**The complexity determines the depth, not a fixed pipeline.** Before starting any feature, assess its scope and apply only what's needed: + +| Scope | What | Specify | Design | Tasks | Execute | +| ----------- | ------------------------ | ------------------------------------------------------- | ----------------------------------------------- | ----------------------------- | ----------------------------------------------------- | +| **Small** | ≤3 files, one sentence | One-liner spec (inline) | Skip | Skip | Implement + verify inline | +| **Medium** | Clear feature, <10 tasks | Spec (brief) | Skip — design inline | Skip — tasks implicit | Implement + verify | +| **Large** | Multi-component feature | Full spec + requirement IDs | Architecture + components | Full breakdown + dependencies | Implement + verify per task | +| **Complex** | Ambiguity, new domain | Full spec + [discuss gray areas](references/discuss.md) | [Research](references/design.md) + architecture | Breakdown + phase plan | Implement + [interactive UAT](references/validate.md) | + +**Rules:** + +- **Specify and Execute are always required** — you always need to know WHAT and DO it +- **Design is skipped** when the change is straightforward (no architectural decisions, no new patterns) +- **Tasks is skipped** when there are ≤3 obvious steps (they become implicit in Execute) +- **Discuss is triggered within Specify** when the agent detects ambiguous gray areas that need user input, or when the feature has any implicit-requirement dimension present (persistence/state, external calls, auth, payments, concurrency, state transitions) +- **Interactive UAT is triggered within Execute** only for user-facing features with complex behavior + +**Safety valve:** Even when Tasks is skipped, Execute ALWAYS starts by listing atomic steps inline (see [implement.md](references/implement.md)). If that listing reveals >5 steps or complex dependencies, STOP and create a formal `tasks.md` — the Tasks phase was wrongly skipped. + +## .specs Structure + +``` +.specs/ +├── STATE.md # Project memory: Decisions log (AD-NNN) + Handoff snapshot +├── LESSONS.md # Self-improving lessons playbook (rendered by scripts/lessons.py — do not hand-edit) +├── lessons.json # Canonical lessons state (machine-owned) +└── features/ # Feature specifications + └── [feature]/ + ├── spec.md # Requirements with traceable IDs + ├── context.md # User decisions for gray areas (only when discuss is triggered) + ├── design.md # Architecture & components (only for Large/Complex) + ├── tasks.md # Atomic tasks with verification (only for Large/Complex) + └── validation.md # Verifier report: PASS/FAIL, per-AC evidence, sensor result, diff range +``` + +## Workflow + +**New feature:** + +1. Specify → (Design) → (Tasks) → Execute (depth auto-sized) + +**Resume work:** + +Read `.specs/STATE.md` — Handoff section for in-flight state, Decisions section to re-confirm active constraints — then propose the next step. + +## Context Loading Strategy + +**On-demand load (only what the current task needs):** + +- `.specs/STATE.md` — Decisions section (read at Design, re-read on resume); Handoff section (read on resume only) +- confirmed lessons — load at Specify and Design via `python3 scripts/lessons.py list --status confirmed` ([lessons.md](references/lessons.md)); confirmed only, never candidates +- spec.md (when working on a specific feature) +- context.md (when designing or implementing from user decisions) +- design.md (when implementing from design) +- tasks.md (when executing tasks) + +**Never load simultaneously:** + +- Multiple feature specs +- Multiple architecture docs + +**Target:** <40k tokens total context +**Reserve:** 160k+ tokens for work, reasoning, outputs +**Monitoring:** Display status when >40k (see [context-limits.md](references/context-limits.md)) + +## Sub-Agent Delegation + +**Trigger:** count total tasks. If the feature packs into more than one task-budgeted batch (> ~8 tasks) → offer sub-agents; if it fits a single batch (≤ ~8 tasks) → execute inline. + +**Offer-then-confirm** — never auto-spawn. The user must accept before any sub-agent is dispatched. + +**One worker per task-budgeted batch (~7 tasks, whole phases):** Phases stay the semantic/dependency unit; a **batch** is the execution unit — one or more *consecutive whole phases* packed to ~7 tasks. Walk phases in order, accumulate whole phases into the current batch until it reaches the budget, then start the next — **never split a phase** across workers. ~20 tasks → ~3 workers; scales linearly (40 → ~6). Each worker executes all its tasks in order (implement → gate → atomic commit), then reports a compact summary (tasks done, commit hashes, test counts, deviations). Batches run sequentially — a batch never starts until the previous one reports all tasks complete. Workers never spawn further sub-agents. + +**Verifier (always-on, never prompted):** After the final task is committed, the orchestrator dispatches a fresh Verifier sub-agent automatically — regardless of phase count. Validation never requires a user prompt; it is the closing step of Execute. **Author ≠ verifier**: the Verifier re-derives coverage independently using evidence-or-zero; it does not inherit the author's mental model. The Verifier: (1) performs a **spec-anchored outcome check** — confirms each test's asserted value matches the spec-defined expected outcome, flags spec-precision gaps; (2) runs a **discrimination sensor** — injects behavior-level faults in scratch state, confirms tests kill them, discards mutations, surviving mutants become fix tasks; (3) writes `.specs/features/[feature]/validation.md` (PASS/FAIL, per-AC evidence, sensor result, diff range); (4) returns a compact verdict + ranked gap list to the orchestrator in chat. Gaps become fix tasks; the fix→re-verify loop is bounded to 3 iterations before escalating. (5) **distills lessons** — turns each grounded failure (surviving mutant, spec-precision gap, failed AC, SPEC_DEVIATION) into a reusable project-local lesson via `scripts/lessons.py`; a clean PASS records nothing (see [lessons.md](references/lessons.md)). + +**Standalone fallback:** Without sub-agents, run `validate.md` as an independent fresh-eyes pass after the final commit — including the spec-anchored check and discrimination sensor. + +Full mechanics (worker payload, compact summary format, failure handling, context sizing, Verifier report format): [sub-agents.md](references/sub-agents.md). + +## Commands + +**Feature-level (auto-sized):** +| Trigger Pattern | Reference | +|----------------|-----------| +| Specify feature, define requirements | [specify.md](references/specify.md) | +| Discuss feature, capture context, how should this work | [discuss.md](references/discuss.md) | +| Design feature, architecture | [design.md](references/design.md) | +| Break into tasks, create tasks | [tasks.md](references/tasks.md) | +| Implement task, build, execute | [implement.md](references/implement.md) | +| Validate, verify, test, UAT, walk me through it | [validate.md](references/validate.md) | + +**Memory:** +| Trigger Pattern | Reference | +|----------------|-----------| +| Record decision, this is a project-level decision | [memory.md](references/memory.md) | +| Pause work, end session, I need to stop | [memory.md](references/memory.md) | +| Resume work, continue, pick up where we left off | [memory.md](references/memory.md) | +| Load lessons, what have we learned, apply past lessons | [lessons.md](references/lessons.md) | +| Record lesson, distill lessons (auto-runs after validation) | [lessons.md](references/lessons.md) | + +## Knowledge Verification Chain + +When researching, designing, or making any technical decision, follow this chain in strict order. Never skip steps. + +``` +Step 1: Codebase → check existing code, conventions, and patterns already in use +Step 2: Project docs → README, docs/, inline comments, `.specs/STATE.md` (Decisions) +Step 3: Context7 MCP → resolve library ID, then query for current API/patterns +Step 4: Web search → official docs, reputable sources, community patterns +Step 5: Flag as uncertain → "I'm not certain about X — here's my reasoning, but verify" +``` + +**Rules:** + +- Never skip to Step 5 if Steps 1-4 are available +- Step 5 is ALWAYS flagged as uncertain — never presented as fact +- **NEVER assume or fabricate.** If you cannot find an answer, say "I don't know" or "I couldn't find documentation for this". Inventing APIs, patterns, or behaviors causes cascading failures across design → tasks → implementation. Uncertainty is always preferable to fabrication. + +## Output Behavior + +**Model guidance:** After completing lightweight tasks (validation, feature-level checks), naturally mention once per session that such tasks work well with faster/cheaper models. For heavy tasks (complex design, large features), briefly note the reasoning requirements before starting. + +Be conversational, not robotic. Don't interrupt workflow—add as a natural closing note. Skip if user seems experienced or has already acknowledged the tip. + +## Code Analysis + +Use available tools with graceful degradation. See [code-analysis.md](references/code-analysis.md). diff --git a/.claude/skills/tlc-spec-driven/references/code-analysis.md b/.claude/skills/tlc-spec-driven/references/code-analysis.md new file mode 100644 index 0000000..18257d7 --- /dev/null +++ b/.claude/skills/tlc-spec-driven/references/code-analysis.md @@ -0,0 +1,98 @@ +# Code Analysis Tools + +Use graceful degradation for code search and structural analysis. + +## Tool Priority + +1. **ast-grep** (`sg`) - Structural pattern-based search +2. **ripgrep** (`rg`) - Fast context-aware text search +3. **grep** - Standard text search (always available) + +## Detection + +Check tool availability before use: + +```bash +# Check for ast-grep +if command -v sg >/dev/null 2>&1; then + # Use ast-grep for structural search +elif command -v rg >/dev/null 2>&1; then + # Fall back to ripgrep +else + # Use standard grep as final fallback +fi +``` + +## Usage Examples + +**Finding function definitions:** + +```bash +# ast-grep (best - structural) +sg -p 'function $NAME($$$) { $$$ }' + +# ripgrep (fallback - fast text) +rg '^function\s+\w+\(' --type-add 'source:*.[extension]' -t source + +# grep (last resort - basic) +grep -r '^function ' --include="*.[extension]" +``` + +**Finding imports/requires:** + +```bash +# ast-grep +sg -p 'import { $$$ } from "$MODULE"' + +# ripgrep +rg '^import .* from' --type-add 'source:*.[extension]' -t source + +# grep +grep -r '^import ' --include="*.[extension]" +``` + +**Finding class/component definitions:** + +```bash +# ast-grep +sg -p 'class $NAME { $$$ }' + +# ripgrep +rg '^(class|export class)\s+\w+' --type-add 'source:*.[extension]' -t source + +# grep +grep -r '^class ' --include="*.[extension]" +``` + +## Search Scope + +**Best practices:** + +- Limit to source file extensions relevant to project +- Exclude directories: `node_modules`, `vendor`, `dist`, `build`, `.git` +- Focus on source directories: `src`, `lib`, `app` +- Use file type filters when available + +**Performance tips:** + +- Use specific patterns over broad searches +- Limit directory depth with `--max-depth` (ripgrep/grep) +- Cache results for repeated queries + +## Fallback Notice + +If ast-grep unavailable, display once per session: + +``` +⚠️ ast-grep not detected. Install for more precise structural code analysis. + https://ast-grep.github.io/guide/quick-start.html +``` + +## When to Use + +- Finding usage patterns across codebase +- Identifying code structure and organization +- Locating function/class/component definitions +- Analyzing import/dependency patterns +- Refactoring impact analysis +- Code navigation in unfamiliar codebases diff --git a/.claude/skills/tlc-spec-driven/references/coding-principles.md b/.claude/skills/tlc-spec-driven/references/coding-principles.md new file mode 100644 index 0000000..f861c0a --- /dev/null +++ b/.claude/skills/tlc-spec-driven/references/coding-principles.md @@ -0,0 +1,56 @@ +# Coding Principles + +Behavioral bias, not checklist. Read before every implementation. + +--- + +## Before Coding + +- State assumptions explicitly. If uncertain, ask. +- Multiple interpretations exist? Present all—don't pick silently. +- Simpler approach exists? Say so. Push back when warranted. +- Something unclear? Stop. Name what's confusing. Ask. +- User's approach seems wrong? Disagree honestly. Don't be sycophantic. + +--- + +## During Implementation + +### Simplicity + +- No features beyond what was asked +- No abstractions for single-use code +- No "flexibility" or "configurability" not requested +- No error handling for impossible scenarios +- 200 lines that could be 50? Rewrite it. + +### Surgical Changes + +- Don't "improve" adjacent code, comments, or formatting +- Don't refactor things that aren't broken +- Match existing style, even if you'd do differently +- Unrelated dead code noticed? Mention it—don't delete it +- Remove ONLY imports/variables/functions YOUR changes orphaned +- Don't remove pre-existing dead code unless asked + +### Test Integrity + +- NEVER weaken an existing test assertion to make it pass +- NEVER delete a test to reduce failure count +- NEVER use the test framework's skip/disable/pending mechanism to bypass a failing test +- NEVER modify a task's tests afterward to make the implementation pass +- If a test is genuinely wrong, STOP and confirm with the user before changing it +- Tests are the spec — implementation conforms to tests, not the other way around + +### Goal-Driven + +- Transform vague tasks into verifiable goals +- Multi-step work? State brief plan with verify checkpoints +- Every changed line must trace directly to user's request + +--- + +## After Each Change + +Ask: "Would senior engineer call this overcomplicated?" +If yes → simplify before proceeding. diff --git a/.claude/skills/tlc-spec-driven/references/context-limits.md b/.claude/skills/tlc-spec-driven/references/context-limits.md new file mode 100644 index 0000000..1cba5f9 --- /dev/null +++ b/.claude/skills/tlc-spec-driven/references/context-limits.md @@ -0,0 +1,31 @@ +# Context Limits + +## File Size Limits + +| File | Max Tokens | ~Words | Warning At | +| --------- | ---------- | ------ | ---------- | +| spec.md | 5,000 | 3,000 | 4,000 | +| design.md | 8,000 | 4,800 | 6,400 | +| tasks.md | 10,000 | 6,000 | 8,000 | + +## Context Zones + +🟢 **Healthy** (<40k total): Silent +🟡 **Moderate** (40-60k): Discrete footer note +🔴 **Critical** (>60k): Active warning, suggest optimization + +## Monitoring + +Display context status in footer when >40k: + +``` +📊 Context: 52k tokens (moderate) + - tasks.md: 11k (ok) + - design.md: 6k (ok) + - Total: 52k / 200k (26%) +``` + +## Principles + +**Target:** <40k tokens loaded (20% of window) +**Reserve:** 160k+ tokens for work, reasoning, outputs diff --git a/.claude/skills/tlc-spec-driven/references/design.md b/.claude/skills/tlc-spec-driven/references/design.md new file mode 100644 index 0000000..a756e91 --- /dev/null +++ b/.claude/skills/tlc-spec-driven/references/design.md @@ -0,0 +1,199 @@ +# Design + +**Goal**: Define HOW to build it. Architecture, components, what to reuse. + +**Skip this phase when:** The change is straightforward — no architectural decisions, no new patterns, no component interactions to plan. For simple features, design happens inline during Execute. + +## Process + +### 1. Load Context + +Read `.specs/features/[feature]/spec.md` before designing. If `.specs/features/[feature]/context.md` exists, load it too — it contains implementation decisions that constrain the design (layout choices, behavior preferences, interaction patterns). Decisions marked as "Agent's Discretion" are yours to decide. + +**Mandatory: read `.specs/STATE.md` `## Decisions` now.** This MUST happen before any architectural choices are made. Every `active` `AD-NNN` entry is a project-level constraint this design must conform to. If a decision from a prior feature conflicts with what is best for this feature, you have two options — both require an explicit choice: + +1. **Conform** — Design within the active constraint. +2. **Supersede** — Append a new `AD-NNN` entry to `.specs/STATE.md` `## Decisions` that supersedes the old one (set the old entry's `status` to `superseded by AD-NNN`) and document the reason. The new decision becomes the project standard going forward. + +Silently ignoring an active decision is not an option — it creates invisible inconsistency across features. + +**Also load confirmed lessons** relevant to this feature: `python3 scripts/lessons.py list --status confirmed` (filter with `--scope`/`--query`). These are past verification failures distilled into guidance — apply them while designing. Load only `confirmed`. Skip silently if no store or no code tool. See [lessons.md](lessons.md). + +### 1.5. Research (Optional but Recommended) + +If the feature involves unfamiliar technology, patterns, or integrations, research before designing. Document findings briefly in the design doc or as inline notes. This prevents incorrect assumptions from propagating into tasks. + +Follow the **Knowledge Verification Chain** (see SKILL.md) in strict order: + +``` +Codebase → Project docs → Context7 MCP → Web search → Flag as uncertain +``` + +**CRITICAL: NEVER assume or fabricate information.** If you cannot find an answer through the chain, explicitly say "I don't know" or "I couldn't find documentation for this". Inventing an API, a pattern, or a behavior that doesn't exist is far worse than admitting uncertainty. Wrong assumptions propagate through design → tasks → implementation and cause cascading failures. + +Good triggers for research: new libraries, unfamiliar APIs, performance-sensitive features, security-sensitive features, patterns you haven't used in this codebase before. + +**Concern flagging (MUST do while reading code):** While walking the codebase via the Knowledge Verification Chain, flag any concerns you encounter in the areas this feature touches. Capture each finding in the `## Risks & Concerns` section of `design.md`: + +- **Fragile code** — tight coupling, large functions, implicit state +- **Tech debt** — hacks, workarounds, deprecated APIs +- **Security risks** — unvalidated input, auth gaps, exposed secrets +- **Performance bottlenecks** — N+1 queries, unbounded loops, missing indexes +- **Test coverage gaps** — untested paths the feature depends on + +Every flagged concern MUST include a mitigation — how the design (or a follow-up task) addresses it. + +### 2. Define Architecture + +**Large/Complex only — approach exploration:** Before committing to a single architecture, present 2–3 viable approaches with trade-offs and a recommendation. Lead with the recommendation to avoid analysis paralysis. All approaches must deliver the same scoped thing (no alternative scopes). Confirm the chosen approach with the user before detailing components. Medium features: skip — design inline. + +Overview of how components interact. Use mermaid diagrams when helpful. + +### 3. Identify Code Reuse + +**CRITICAL**: What existing code can we leverage? This saves tokens and reduces errors. + +Flag any concerns found here per step 1.5 into `## Risks & Concerns`. + +### 4. Define Components and Interfaces + +Each component: Purpose, Location, Interfaces, Dependencies, What it reuses. + +### 5. Define Data Models + +If the feature involves data, define models before implementation. + +--- + +## Template: `.specs/features/[feature]/design.md` + +````markdown +# [Feature] Design + +**Spec**: `.specs/features/[feature]/spec.md` +**Status**: Draft | Approved + +--- + +## Architecture Overview + +[Brief description of the architecture approach] + +```mermaid +graph TD + A[User Action] --> B[Component A] + B --> C[Service Layer] + C --> D[Data Store] + B --> E[Component B] +``` +```` + +--- + +## Code Reuse Analysis + +### Existing Components to Leverage + +| Component | Location | How to Use | +| -------------------- | ------------------- | ------------------------- | +| [Existing Component] | `src/path/to/file` | [Extend/Import/Reference] | +| [Existing Utility] | `src/utils/file` | [How it helps] | +| [Existing Pattern] | `src/patterns/file` | [Apply same pattern] | + +### Integration Points + +| System | Integration Method | +| -------------- | --------------------------------------- | +| [Existing API] | [How new feature connects] | +| [Database] | [How data connects to existing schemas] | + +--- + +## Components + +### [Component Name] + +- **Purpose**: [What this component does - one sentence] +- **Location**: `src/path/to/component/` +- **Interfaces**: + - `methodName(param: Type): ReturnType` - [description] + - `methodName(param: Type): ReturnType` - [description] +- **Dependencies**: [What it needs to function] +- **Reuses**: [Existing code this builds upon] + +### [Component Name] + +- **Purpose**: [What this component does] +- **Location**: `src/path/to/component/` +- **Interfaces**: + - `methodName(param: Type): ReturnType` +- **Dependencies**: [Dependencies] +- **Reuses**: [Existing code] + +--- + +## Data Models (if applicable) + +### [Model Name] + +```typescript +interface ModelName { + id: string + field1: string + field2: number + createdAt: Date +} +``` + +**Relationships**: [How this relates to other models] + +### [Model Name] + +```typescript +interface AnotherModel { + id: string + // ... +} +``` + +--- + +## Error Handling Strategy + +| Error Scenario | Handling | User Impact | +| -------------- | ------------- | ---------------- | +| [Scenario 1] | [How handled] | [What user sees] | +| [Scenario 2] | [How handled] | [What user sees] | + +--- + +## Risks & Concerns + +| Concern | Location (file:line) | Impact | Mitigation | +| ------- | -------------------- | ------ | ---------- | +| [Fragile code / tech debt / security / perf / test gap] | `src/path/file.ts:42` | [What breaks or degrades] | [How the design or a follow-up task addresses it] | + +> None found — is a valid entry. + +--- + +## Tech Decisions (only non-obvious ones) + +| Decision | Choice | Rationale | +| ----------------- | --------------- | ------------- | +| [What we decided] | [What we chose] | [Why - brief] | + +> **Project-level decisions:** If a decision here sets a convention, pattern, or constraint that future features must follow, append it to `.specs/STATE.md` `## Decisions` as the next `AD-NNN` entry (see [memory.md](memory.md)). Feature-local decisions stay only in this table. + +--- + +## Tips + +- **Load context first** — If context.md exists, decisions there are locked +- **Research when uncertain** — 5 minutes of research prevents hours of rework +- **Reuse is king** — Every component should reference existing patterns +- **Interfaces first** — Define contracts before implementation +- **Keep it visual** — Diagrams save 1000 words +- **Small components** — If component does 3+ things, split it +- **Flag concerns inline** — Risks found during research go in Risks & Concerns with a mitigation +- **Confirm before Tasks** — User approves design before breaking into tasks diff --git a/.claude/skills/tlc-spec-driven/references/discuss.md b/.claude/skills/tlc-spec-driven/references/discuss.md new file mode 100644 index 0000000..6e67352 --- /dev/null +++ b/.claude/skills/tlc-spec-driven/references/discuss.md @@ -0,0 +1,136 @@ +# Specify: Discuss Gray Areas + +**Goal:** Capture HOW the user envisions the feature when the spec has ambiguous areas. This is NOT a separate phase — it's triggered within Specify when the agent detects gray areas that need user input. + +**Trigger:** Automatically when gray areas are detected during spec creation, or explicitly via "discuss feature", "how should this work?", "capture context" + +**When to trigger (auto-detect):** The spec contains user-facing behavior that could go multiple ways AND the user hasn't expressed a preference. If the spec is clear and unambiguous, skip this entirely. + +**When NOT to trigger:** Genuinely trivial features — a pure read endpoint, a config tweak, features with no [implicit-requirement dimensions](specify.md#implicit-requirement-dimensions) present (no persistence/state, external calls, auth, payments, concurrency, or state transitions). When any dimension is present, trigger discuss. + +## Why This Phase Exists + +Specifications capture WHAT to build. Design captures the architecture. But neither captures the user's vision for ambiguous areas — layout preferences, interaction patterns, error handling style, content tone. Without this, the agent guesses. With this, the agent builds what the user actually imagined. + +The output — `context.md` — feeds directly into Design and Tasks: + +- **Design reads it** to know what decisions are locked vs. flexible +- **Tasks reads it** to include specific behaviors in task definitions + +## Process + +### 1. Analyze the Feature + +Read `.specs/features/[feature]/spec.md` and identify the domain: + +| Domain | Gray areas to explore | +| ------------------------------ | ------------------------------------------------------------- | +| Something users **SEE** | Layout, density, interactions, empty states, visual hierarchy | +| Something users **CALL** (API) | Response format, errors, auth, versioning, rate limiting | +| Something users **RUN** (CLI) | Output format, flags, modes, error handling, verbosity | +| Something users **READ** | Structure, tone, depth, flow, navigation | +| Something being **ORGANIZED** | Grouping criteria, naming, duplicates, exceptions | +| Something with **backend / state / contract** | Failure & partial-failure states, idempotency/retry/dedup, auth boundaries & rate limits, data lifecycle/expiry, concurrency/ordering — see [implicit-requirement dimensions](specify.md#implicit-requirement-dimensions) | + +Generate 3-4 **feature-specific** gray areas. Not generic categories, but concrete decisions for THIS feature. + +### 2. Present Gray Areas + +Present the feature boundary (from spec.md) and the gray areas to the user. Let them choose which to discuss. Do NOT include a "skip all" option — the user invoked this phase to discuss. + +Any gray area the user **declines** to discuss, or that goes undiscussed, is written to the spec's **Assumptions & Open Questions** section (agent's chosen default + rationale) — never silently dropped. This ensures the spec's closure gate can pass: every gray area is either resolved through discussion or recorded as a signed-off assumption. + +### 3. Deep-Dive Each Area + +For each selected area: + +1. Ask 3-4 concrete questions with specific options (not vague categories) +2. After the questions, check: "More about [area], or move on?" +3. If more → ask 3-4 more, check again +4. After all areas → "Ready to create context?" + +**Question design:** + +- Options should be concrete ("Card layout" not "Option A") +- Each answer should inform the next question +- Include "You decide" as an option when reasonable — captures agent discretion + +### 4. Scope Guardrail (CRITICAL) + +The feature boundary from spec.md is **fixed**. Discussion clarifies HOW to implement, never WHETHER to add new capabilities. + +**Allowed:** "How should posts be displayed?" (clarifying ambiguity) +**Not allowed:** "Should we also add comments?" (new capability) + +When user suggests scope creep: "That sounds like a separate feature. I'll note it in Deferred Ideas. Back to [current area]." + +### 5. Write context.md + +--- + +## Template: `.specs/features/[feature]/context.md` + +```markdown +# [Feature] Context + +**Gathered:** [date] +**Spec:** `.specs/features/[feature]/spec.md` +**Status:** Ready for design + +--- + +## Feature Boundary + +[Clear statement of what this feature delivers — the scope anchor from spec.md] + +--- + +## Implementation Decisions + +### [Area 1 that was discussed] + +- [Specific decision made] +- [Another decision if applicable] + +### [Area 2 that was discussed] + +- [Specific decision made] + +### [Area 3 that was discussed] + +- [Specific decision made] + +### Agent's Discretion + +[Areas where user explicitly said "you decide" — agent has flexibility here during design/implementation] + +### Declined / Undiscussed Gray Areas → Assumptions + +[Gray areas the user declined to discuss or that were not covered. Each entry is written to the spec's Assumptions & Open Questions section with the agent's chosen default and rationale — not left silently unresolved.] + +--- + +## Specific References + +[Any "I want it like X" moments, product references, specific behaviors, interaction patterns mentioned during discussion] + +[If none: "No specific requirements — open to standard approaches"] + +--- + +## Deferred Ideas + +[Ideas that came up during discussion but belong in other features/phases. Captured here so they're not lost, but explicitly out of scope] + +[If none: "None — discussion stayed within feature scope"] +``` + +--- + +## Tips + +- **Decisions, not vision** — "Card-based layout with subtle shadows" is a decision. "Should feel modern" is not. +- **Scope is sacred** — Deferred Ideas captures scope creep without losing ideas +- **User = visionary, Agent = builder** — Ask about how they imagine it, not about technical implementation +- **Don't ask about:** Technical architecture, performance, implementation details — that's Design's job +- **Confirm before Design** — User approves context.md before moving to design phase diff --git a/.claude/skills/tlc-spec-driven/references/implement.md b/.claude/skills/tlc-spec-driven/references/implement.md new file mode 100644 index 0000000..bd9a1dc --- /dev/null +++ b/.claude/skills/tlc-spec-driven/references/implement.md @@ -0,0 +1,426 @@ +# Execute + +**Goal**: Implement ONE task at a time. Surgical changes. Verify. Commit. Repeat. + +This is where code gets written. Every task follows the same cycle: plan → implement → verify → commit. Verification is built into every task, not a separate phase. + +--- + +## MANDATORY: Before Starting Any Implementation + +**Read [coding-principles.md](coding-principles.md) and state:** + +1. **Assumptions** - What am I assuming? Any uncertainty? +2. **Files to touch** - List ONLY files this task requires +3. **Success criteria** - How will I verify this works? + +⚠️ **Do not proceed without stating these explicitly.** + +--- + +## Process + +**Batch worker context:** When this task is executed as part of a phase-batch sub-agent, the worker +receives the task definitions for every phase in its batch, coding principles, the generated Test +Coverage Matrix and Gate Check Commands from tasks.md, and relevant spec/design context. A batch is +one or more consecutive whole phases packed to ~7 tasks. The worker executes ALL tasks in its +assigned batch in order — finishing every task in one phase before starting the next phase in the +batch — and each task follows every step below (implement → gate → atomic commit) before moving to +the next. After all tasks in the batch are complete, the worker reports a compact summary (tasks +done, commit hashes, test counts, deviations/blockers) to the orchestrator. See +[sub-agents.md](sub-agents.md) for the full model. + +### Before implementing: assess sub-agent delegation (MANDATORY — before the first task) + +Before implementing anything, if a formal `tasks.md` with an Execution Plan exists, **count its total tasks** and pack the phases into task-budgeted batches (~7 tasks per worker, whole phases — see [sub-agents.md](sub-agents.md)). If that yields **more than one batch** (> ~8 tasks), you MUST present the sub-agent offer to the user and wait for their choice before starting Execute — do not silently proceed inline. If the feature fits a single batch (≤ ~8 tasks, or the user declines), execute inline. Skip this check only when you are already a batch worker executing a delegated batch (the orchestrator already made the delegation decision). + +### 0. List Atomic Steps (MANDATORY when Tasks phase was skipped) + +If there is no `tasks.md` for this feature, you MUST list atomic steps before writing any code. This is non-negotiable — it prevents the agent from losing focus and doing too many things at once. + +``` +## Execution Plan + +1. [Step] → files: [list] → verify: [how] → commit: [message] +2. [Step] → files: [list] → verify: [how] → commit: [message] +3. [Step] → files: [list] → verify: [how] → commit: [message] +``` + +**Each step must be:** + +- ONE deliverable (one component, one function, one endpoint, one file change) +- Independently verifiable (can prove it works before moving on) +- Independently committable (gets its own atomic git commit) + +If listing steps reveals >5 steps or complex dependencies, STOP and create a formal `tasks.md` instead. The Tasks phase was wrongly skipped. + +### 1. Pick Task + +From tasks.md (if exists) or from the execution plan above. User specifies ("implement T3") or suggest next available. + +### 2. Verify Dependencies + +If tasks.md exists, check dependencies. If using inline plan, follow the order listed. + +❌ If blocked: "T3 depends on T2 which isn't done. Should I do T2 first?" + +### 3. State Implementation Plan + +Before writing code: + +``` +Files: [list] +Approach: [brief description] +Success: [how to verify] +``` + +### 4. Write Tests (derived from spec, not from implementation) + +If the task includes tests (per the Tests field and **Test Coverage Matrix** in tasks.md): + +1. Write the test file(s) covering the task's acceptance criteria. +2. Tests MUST be derived from the task's "Done when" criteria and `spec.md` ACs — **not** from the implementation. Each test encodes what the spec requires; never write tests by reading the code and asserting what it currently does. +3. Each acceptance criterion from "Done when" maps to at least one test assertion whose asserted value matches the **spec-defined expected outcome**. Where the spec does not define a precise outcome, note it as a **spec-precision gap** rather than writing a vague assertion and passing silently. +4. Edge cases from spec.md that apply to this task get test cases too. + +**HARD CONSTRAINTS (test integrity — never violate):** + +- Do NOT weaken assertions (making them less specific to pass more easily) +- Do NOT delete or skip test cases +- Do NOT use the test framework's skip/disable/pending mechanism to bypass failing tests + +If a test is genuinely wrong (tests the wrong behavior per spec), STOP and ask the user +before modifying it. Never silently change a test. + +If the task does NOT include tests (e.g., entity-only, config-only), skip to Step 4b. + +### 4b. Implement + +Write the minimum implementation needed to satisfy the task's success criteria: pass all relevant tests (when present) and meet the defined verification/gate checks when there are no direct tests. + +**HARD CONSTRAINTS:** + +- Do NOT modify tests except to fix a genuinely wrong assertion (ask the user first). The tests are the spec — implementation conforms to them. +- Do NOT weaken assertions (making them less specific to pass more easily) +- Do NOT delete or skip test cases +- Do NOT use the test framework's skip/disable/pending mechanism to bypass failing tests +- Minimum code to pass — save structural improvements for a refactor task + +Follow [coding-principles.md](coding-principles.md): + +- Simplest code that works +- Touch ONLY listed files +- No scope creep + +### 5. Gate Check (VERIFY) + +Run the gate check command from the task definition. This is MANDATORY — not "if applicable." + +1. Look up the command for the task's Gate level (quick/full/build) in the **Gate Check Commands** section of tasks.md, then run it +2. Non-zero exit code = STOP. Fix the failure. Re-run. Do not proceed until it passes. +3. Confirm the test count matches expectations (no tests were silently deleted or skipped) + +**Tiered gates (from the Gate Check Commands section of tasks.md):** + +| Task includes | Gate level | What runs | +| -------------------------------- | ---------- | ------------------------ | +| Unit tests only | Quick | Unit test command | +| E2E or integration tests | Full | Unit + E2E commands | +| Last task in a phase | Build | Build + lint + all tests | +| No tests (config, entities, etc) | Build | Build + lint only | + +The gate check is deterministic. The test runner decides if the code is correct, +not the agent's self-assessment. + +### 6. Post-Gate Review + +After the gate check passes: + +1. Verify test count: Are there at least as many test cases as before? (prevents silent deletion) +2. Verify no SPEC_DEVIATION: If implementation diverged from spec/design, add a marker: + +``` +// SPEC_DEVIATION: [what diverged] +// Reason: [why the deviation was necessary] +``` + +3. Quick complexity check: "Would senior engineer flag this as overcomplicated?" + - Yes → Simplify, re-run gate + - No → Proceed + +4. **Test Adequacy Review (MANDATORY — hard gate).** + + A task cannot be committed or marked done until all four checks below pass. Tests must be both **necessary** (every test traces to a requirement) and **sufficient** (every requirement is covered). The scope boundary is the feature spec — do not test beyond it. + + **Check A — Sufficient coverage (per-layer depth).** Build and output this table: + + | Done-when criterion / spec AC / listed edge case | `file:line` + assertion expression | Spec-defined outcome | Covered? | + | ------------------------------------------------- | ---------------------------------- | -------------------- | -------- | + | [criterion from task or spec] | `path/to/test.ts:42` — `expect(result.field).toBe(expected)` | [expected value from spec] | ✅ Yes / ❌ No / ⚠️ Spec-precision gap | + + **Evidence-or-zero rule:** Each covered cell MUST cite the exact `file:line` where the assertion lives AND reproduce the assertion expression (not just the `describe`/`it` name). A criterion with no located `file:line` evidence counts as **NOT covered**; the task cannot be marked done. Do not declare a criterion absent without first searching the test files — show the search before concluding it is missing (mirror: evidence or zero, never a guess). + + **Spec-anchored outcome check:** For each covered criterion, derive the expected outcome from `spec.md` (or the task's "Done when" field) and confirm the test's asserted value matches it — not just that an assertion exists. Where the spec defines a precise outcome (e.g., a specific status code, a specific field value, a specific error message), the test assertion MUST target that exact outcome. Where the spec does not define a precise outcome, mark the cell as **⚠️ Spec-precision gap** and add a note; do NOT silently pass a vague assertion as if it were covered. + + Every "Done when" criterion, every spec.md acceptance criterion, and every listed edge case that applies to this task must map to at least one concrete test assertion. Enforce the layer's Coverage Expectation from the Test Coverage Matrix: + + - Domain / service layer: assertions map 1:1 to spec ACs; every listed edge case has a dedicated test. + - Route / controller / e2e layer: every route the task adds or modifies must have a happy-path test, a test for each listed edge case, and a test for each documented error/failure path. + + No criterion left unverified. + + **Check B — Non-shallow litmus.** Reject each of the following shallow patterns: + - Assertion-free tests or `expect(true)` / `expect(1).toBe(1)` style tautologies + - "No error thrown" as the only assertion — unless not-throwing IS the specified behavior + - Asserting only on mock call counts when the actual output/state is what the criterion demands + - Happy-path only when the task's "Done when" or spec.md lists edge cases + + **Payload/conjunction rule.** For each named field in an emitted event, returned object, or persisted record, apply a separate check: + 1. Open the constructed object at its `file:line` and confirm the field is present in the assertion. + 2. Confirm the assertion targets the field's **value or state**, not just the call that produced it. + 3. A present `emit(...)` / `return ...` / `save(...)` call does NOT prove the field — only an assertion on the result does. + 4. Asserting a method was called (spy/mock) != asserting the resulting state. Both may be needed; neither substitutes for the other. + + Apply this check to every payload-bearing criterion before marking it covered. + + **Stack-agnostic litmus:** An assertion is shallow if it would still pass under a plausible *wrong* implementation. If so, strengthen it before committing. + + **Check C — Necessary (no tests beyond the spec).** Reverse-map every test back to a spec AC, a listed edge case, or a "Done when" criterion. Build this table: + + | `file:line` + assertion expression | Maps to (AC / edge case / Done-when criterion) | Keep? | + | ---------------------------------- | ---------------------------------------------- | ----- | + | `path/to/test.ts:42` — `expect(result.field).toBe(expected)` | [requirement ID or criterion text] | ✅ Keep / ❌ Remove | + + Any test that maps to nothing → remove it. A test with no requirement is scope creep — it proves nothing about the feature and expands scope beyond the spec. Do not write speculative "what if" tests, do not test framework or library behavior, and do not duplicate an assertion that is already covered at another layer for the same scenario. + + **Check D — Guideline conformance.** If project quality/testing guidelines were found in step 0 of tasks.md step 1.5, verify this task's tests conform to them (naming conventions, file locations, coverage thresholds, etc.). Note the guideline file followed. + + **Bound:** Tests prove the work; they do not expand it. Thoroughness is scoped to the feature + spec. Repo depth is a floor (never less thorough than existing tests for the same layer); the spec is the ceiling. Do not invent requirements or tests that have no spec anchor. + + **Anti-patterns — known verification cheats (treat any of these as an automatic Check failure):** + + | Anti-pattern | Why it fails | + | ------------ | ------------ | + | Committing before the gate check passes | Skips the deterministic verifier — the gate is not optional | + | Asserting call count / spy invocation instead of the resulting state | Proves the method ran, not that it did the right thing | + | Marking a criterion covered without a `file:line` citation | Violates evidence-or-zero; suspicion of coverage is not coverage | + | Weakening an assertion (making it less specific) to force a pass | Moves the goalposts instead of fixing the code | + | Deleting or skipping a test to make the suite pass | Destroys coverage permanently; a failing test is a signal, not noise | + | "Tested elsewhere" deferral without citing where | Coverage gaps hide behind vague claims; cite the file:line or it doesn't count | + | Speculative "what if" tests with no spec anchor | Expands scope beyond the ceiling; remove them in Check C | + | Testing framework or library behavior | Tests a dependency, not the feature; remove them in Check C | + + **On any failure** → rewrite or remove the affected test(s), re-run the gate, then re-run this review. + + *Honest caveat:* This is an inspection-based review (model judgment), complementary to — not a replacement for — the deterministic gate. The gate confirms the test suite runs; the feature-level discrimination sensor (step 10) confirms the tests can detect regressions. This review confirms the suite is meaningful and bounded. + + Add the two mapping tables and a one-line adequacy verdict to the Execution Template's Post-Gate section. + +### 7. Atomic Git Commit + +Each task gets its own commit immediately after verification. Never batch multiple tasks into one commit. + +**Format ([Conventional Commits 1.0.0](https://www.conventionalcommits.org/en/v1.0.0/)):** + +``` +(): + +[optional body] + +[optional footer(s)] +``` + +**Types:** + +| Type | When to use | +| ---------- | ------------------------------------------------------- | +| `feat` | New feature or capability | +| `fix` | Bug fix | +| `refactor` | Code change that neither fixes a bug nor adds a feature | +| `docs` | Documentation only | +| `test` | Adding or correcting tests | +| `style` | Formatting, missing semicolons, etc. (no code change) | +| `perf` | Performance improvement | +| `build` | Build system or external dependencies | +| `ci` | CI configuration files and scripts | +| `chore` | Maintenance tasks that don't modify src or test files | + +**Scope:** Feature name or module area, lowercase, e.g., `auth`, `cart`, `api` + +**Description rules:** + +- Imperative mood ("add", not "added" or "adds") +- Lowercase first letter +- No period at the end +- Complete the sentence: "If applied, this commit will _[your description]_" + +**Breaking changes:** Append `!` after type/scope AND add `BREAKING CHANGE:` footer: + +``` +feat(api)!: change authentication endpoint response format + +BREAKING CHANGE: login endpoint now returns JWT in body instead of cookie +``` + +**Examples:** + +``` +feat(auth): add email validation to login form +``` + +``` +fix(cart): prevent negative quantity on item decrement +``` + +``` +refactor(api): extract token refresh logic into service + +Move token refresh from inline handler to dedicated AuthTokenService +for reuse across multiple endpoints. +``` + +**Rules:** + +- One task = one commit +- Description references what was DONE, not what was planned +- Include only files listed in the task — never sneak in "while I'm here" changes +- If tests are part of the task, include them in the same commit + +### 8. Scope Guardrail + +During implementation, you will notice things that could be improved, refactored, or added. **Do not act on them.** Instead: + +- If it's a bug: surface it to the user (or capture it as a separate task) +- If it's an improvement: add it to the feature's `context.md` under "Deferred Ideas" (or surface it to the user if there is no `context.md`) +- If it's related to the current task: only include it if it's in the "Done when" criteria + +**The heuristic:** "Is this in my task definition?" If no, don't touch it. + +### 9. Update Task Status + +Mark task complete in tasks.md. Update requirement traceability in spec.md if requirement IDs are used. + +### 10. Feature-Level Validation (after the LAST task — MANDATORY, always runs) + +When the task you just completed is the **last task of the feature** (or of a priority group being delivered on its own, e.g. all P1 tasks), you MUST run feature-level validation before reporting the work as done. **This is not optional and is never prompted — it runs automatically.** Do not stop at the final task's commit. + +**Author ≠ verifier rationale.** The implementer (you, or the batch worker) is the author of the code and tests. An author checking their own work applies the same mental model that may have produced any gaps. The Verifier is a fresh sub-agent that re-derives coverage from the spec independently — it does not inherit the author's assumptions. This separation is the quality gate, not just a style preference. + +**Layering:** +- Per-task adequacy self-check (steps 5–6): cheap, always runs, author does it, confirms each task in isolation. +- Feature-level validation (step 10): one trustworthy independent gate at completion, always-on, Verifier sub-agent does it. + +**How to delegate to the Verifier:** +Dispatch a fresh sub-agent following the **Verifier** role described in [sub-agents.md](sub-agents.md). Provide it with: +- `spec.md` (ACs = source of truth) +- The git diff surface for this feature (commit range) +- The test files in scope +- `validate.md` as its operating checklist + +**What the Verifier does (full description in [validate.md](validate.md) and [sub-agents.md](sub-agents.md)):** +1. **Spec-anchored coverage check** — re-derives coverage evidence-or-zero; confirms each test's asserted value matches the spec-defined outcome; flags spec-precision gaps. +2. **Discrimination sensor** — injects a small behavior-level fault (flip a condition, change a return value, off-by-one) in a scratch/throwaway state (git stash or temp copy), runs the relevant tests, confirms they kill the mutant, then discards the mutation. Reports killed/survived; surviving mutants become fix tasks. +3. **Persisted report** — writes `.specs/features/[feature]/validation.md` with PASS/FAIL, per-AC evidence (`file:line` + assertion + spec outcome), gate exit results, sensor result, and the diff/commit range covered. +4. **Chat return** — returns a compact verdict + ranked gap list to the orchestrator in chat; the orchestrator surfaces it and routes gaps to fix tasks. + +The Verifier runs read-only over the real implementation tree (mutations run in a scratch state only). It does NOT fix. + +If the Verifier returns FAIL, the orchestrator routes the ranked gaps back to an implementer as fix tasks, then re-dispatches the Verifier — bounded to a max of **3 fix→re-verify iterations** before escalating to the user. + +If you are unsure whether more tasks remain, check `tasks.md`: if every task is marked complete, dispatch the Verifier now. + +--- + +## Execution Template + +```markdown +## Implementing T[X]: [Task Title] + +**Reading**: task definition from tasks.md +**Dependencies**: [All done? ✅ | Blocked by: TY] +**Tests**: [unit/e2e/integration/none] +**Gate**: [quick/full/build] + +### Pre-Implementation (MANDATORY) + +- **Assumptions**: [state explicitly] +- **Files to touch**: [list ONLY these] +- **Success criteria**: [how to verify] + +### Tests: Write tests derived from spec ACs + +- Test file(s): [paths] +- Test count: [N test cases] +- Spec-derived: each test's asserted value maps to spec-defined outcome (or gap flagged) + +### Implement + +[Write minimum code to pass tests] + +- Tests modified: None +- Tests skipped/deleted: None + +### VERIFY: Gate Check + +- Command: [gate check command] +- Result: [X passed, 0 failed] +- Test count: [N — matches planned test count] + +### Post-Gate + +- [x] No SPEC_DEVIATION (or markers added) +- [x] No unnecessary changes made +- [x] Matches existing patterns + +**Test Adequacy Review:** + +*Check A — Sufficient (coverage mapping):* + +| Done-when criterion / spec AC / listed edge case | `file:line` + assertion expression | Spec-defined outcome | Covered? | +| ------------------------------------------------- | ---------------------------------- | -------------------- | -------- | +| [criterion] | `path/to/test.ts:42` — `expect(result.field).toBe(expected)` | [spec value] | ✅ Yes / ⚠️ Gap | + +*Check C — Necessary (reverse mapping):* + +| `file:line` + assertion expression | Maps to (AC / edge case / Done-when criterion) | Keep? | +| ---------------------------------- | ---------------------------------------------- | ----- | +| `path/to/test.ts:42` — `expect(result.field).toBe(expected)` | [requirement or criterion text] | ✅ Keep | + +- [ ] Check A: every criterion covered with `file:line` evidence; spec-defined outcomes matched or gap flagged; per-layer depth met +- [ ] Check B: no shallow assertions; payload/conjunction rule applied to every payload-bearing criterion +- [ ] Check C: every test maps to a requirement — no speculative or unclaimed tests +- [ ] Check D: guideline conformance — [guideline file followed, or "none — strong defaults applied"] + +**Verdict**: [All criteria covered, spec outcomes matched, no shallow assertions, all tests necessary] / [Rewritten: describe what was fixed] + +**Status**: ✅ Complete | ❌ Blocked | ⚠️ Partial +``` + +**After the LAST task:** dispatch the Verifier sub-agent (see step 10 and [sub-agents.md](sub-agents.md)) for independent feature-level validation, including the spec-anchored check and discrimination sensor. Validation always runs automatically — never prompted. Execute is not done until the Verifier reports PASS and the validation report is written. + +--- + +## Tips + +- **One task at a time** — Focus prevents errors +- **Tools matter** — Wrong MCP = wrong approach +- **Reuses save tokens** — Copy patterns, don't reinvent +- **Check before commit** — Verify all criteria, then commit +- **Stay surgical** — Touch only what's necessary +- **Commit per task** — Clean git history enables bisect and rollback +- **Never "while I'm here"** — Scope creep during implementation is the #1 quality killer +- **Learn from mistakes** — If something goes wrong, surface it to the user so it informs the next task +- **Don't stop at the last commit** — Feature-level validation (step 10) is the final step of Execute, not optional + +--- + +## Pause / End of Session + +When work is interrupted, paused, or a session ends before the feature is complete: + +1. Open `.specs/STATE.md`. +2. Locate the `## Handoff` section. +3. **Replace only that section's body** with the current snapshot (feature, phase/task, completed, in-progress `file:line`, next step, blockers, uncommitted files, branch). See [memory.md](memory.md) for the exact format. +4. Do NOT touch the `## Decisions` section above it — decisions are written only during Design. + +**Section-scoped write (critical):** Replace the content between the `## Handoff` header and the next `##` header (or end of file). Never overwrite the full file — doing so silently destroys the Decisions log. diff --git a/.claude/skills/tlc-spec-driven/references/lessons.md b/.claude/skills/tlc-spec-driven/references/lessons.md new file mode 100644 index 0000000..37f7486 --- /dev/null +++ b/.claude/skills/tlc-spec-driven/references/lessons.md @@ -0,0 +1,113 @@ +# Lessons — Self-Improving Layer + +**Purpose**: Turn verification failures into reusable, project-local guidance that actually changes future behavior — without the lessons file rotting into a dead log. + +**The split that keeps it alive**: the agent (you) supplies *judgment* — read the failure, phrase the lesson, cite its grounding. The script `scripts/lessons.py` owns everything *mechanical* — IDs, recurrence counting across distinct features, candidate→confirmed promotion, pruning, demotion, and rendering. Hand-kept bookkeeping is exactly what rots, so it is not your job; the script's job. + +**What feeds it**: only the execution signals already produced by the Verifier in [validate.md](validate.md) and written to `.specs/features/[feature]/validation.md`. No signal → no lesson. This is the hard gate: a lesson with no grounding in a real verification outcome is an opinion, and the script refuses it. + +**Scope discipline (critical)**: this layer captures *execution* lessons that are project-local and grounded in a signal. It does **NOT** capture methodology opinions about the SDD process itself ("we should always discuss earlier"). Those are maintainer decisions that ship in a version bump — never auto-written. If a candidate lesson is really about how to run the skill rather than about this codebase, do not record it. + +--- + +## Files + +| File | Owner | Purpose | +| ---- | ----- | ------- | +| `.specs/lessons.json` | script | Canonical machine state. Never hand-edit. | +| `.specs/LESSONS.md` | script (rendered) | Human/agent-readable playbook. Read it; never write it by hand. | +| `scripts/lessons.py` | package | The only way to mutate lessons. | + +`confirmed` lessons are the playbook the agent loads. `candidate` lessons are tracked but NOT trusted until corroborated across `promote_threshold` distinct features (default 2). `quarantined` lessons failed when applied and are ignored. + +--- + +## WRITE — distill lessons (runs inside Execute, after validation) + +This is **not a new phase**. It is the final action of the Verifier step in [validate.md](validate.md), grafted onto a step that already always runs. Do it immediately after `validation.md` is written, before reporting completion. + +### When to write + +Walk the just-written `validation.md`. For each **grounded** signal, record one lesson: + +| validation.md signal | `--signal` value | +| -------------------- | ---------------- | +| An acceptance criterion failed or had no evidence | `ac_gap` | +| A discrimination-sensor mutant survived (weak test) | `surviving_mutant` | +| A criterion flagged ⚠️ Spec-precision gap | `spec_precision_gap` | +| A `// SPEC_DEVIATION` marker was added during implement | `spec_deviation` | +| The build-level gate check failed | `gate_fail` | + +If `validation.md` is a clean PASS with no surviving mutants, no spec-precision gaps, and no deviations → **write nothing**. A clean run produces no lessons. This is correct, not a miss. + +### How to write + +For each signal, phrase the lesson as **one terse, actionable, codebase-general sentence** — a rule a future feature could apply, not a restatement of this bug. Then call the script: + +```bash +python3 scripts/lessons.py add \ + --feature "[feature folder name]" \ + --signal "[signal value from table above]" \ + --source "[file:line | AC id | mutant id | SPEC_DEVIATION ref from validation.md]" \ + --text "[the one-sentence lesson]" \ + --scope "[optional: path/layer/tag, e.g. billing, routes, repo-layer]" +``` + +**Phrasing rules** (they make recurrences actually merge — dedup is exact-after-normalization, not semantic): + +- Write the general rule, not the incident. ✅ `"Assert the exact persisted status value, not just that a status field exists"` ❌ `"The subscription test on line 88 was too weak"`. +- Be canonical and terse. Two lessons that mean the same thing must read the same way, or the script counts them as different and neither gets promoted. +- One lesson per signal. Don't bundle. + +`--source` is **mandatory**. The script exits non-zero if it is empty — that is the grounding gate working, not an error to route around. + +### Self-check (do not skip) + +After distilling, if `validation.md` contained any FAIL, surviving mutant, spec-precision gap, or SPEC_DEVIATION but you recorded zero lessons, state plainly in chat: *"Validation had signal X but no lesson was recorded — recording now / here's why it's out of scope."* Silent skipping is how the file dies. + +### Demotion + +If a `confirmed` lesson was loaded for this feature (see READ below) and the *same* failure recurred anyway, the guidance is not working: + +```bash +python3 scripts/lessons.py penalize --id L-NNN +``` + +Two penalties quarantine it. Use sparingly and only on real repeats. + +--- + +## READ — load lessons (runs at Specify and Design) + +A lessons file nobody reads is dead by definition. Loading is **mandatory**, not optional. + +At the start of **Specify** (and again at **Design** for Large/Complex), load the confirmed lessons relevant to this feature: + +```bash +# All confirmed lessons: +python3 scripts/lessons.py list --status confirmed + +# Or filter by the area this feature touches: +python3 scripts/lessons.py list --status confirmed --scope billing +python3 scripts/lessons.py list --status confirmed --query "idempotency" +``` + +Apply the returned lessons as guidance while writing the spec / design. Do **not** load `candidate` or `quarantined` lessons as guidance — they are not trusted. Keep the loaded set small; this runs inside the <40k token budget. + +--- + +## Fallback when code execution is unavailable + +Some harnesses cannot run Python. Only then: maintain `.specs/LESSONS.md` by hand, following the exact same rules — grounded entries only, candidate→confirmed after 2 distinct features, prune stale candidates. **This path is degraded**: hand bookkeeping is the failure mode this layer exists to avoid, so prefer the script wherever a code tool exists. State once in chat that you are in the no-script fallback so the user knows accounting is best-effort. + +--- + +## Disable + +This layer is additive and self-gating (no signal → no write). To turn it off for a project, delete `.specs/lessons.json` and `.specs/LESSONS.md` and skip the WRITE/READ steps. The core Specify→Design→Tasks→Execute flow is unaffected. + +--- + +## Known limitation + +Deduplication is exact-after-normalization (lowercase, punctuation-stripped) — there are no embeddings (stdlib-only, zero-dependency by design). Near-duplicate lessons phrased differently will not merge and will each sit as separate candidates that never promote. Mitigation: follow the phrasing rules above. A future version may add embedding-based dedup. diff --git a/.claude/skills/tlc-spec-driven/references/memory.md b/.claude/skills/tlc-spec-driven/references/memory.md new file mode 100644 index 0000000..473f611 --- /dev/null +++ b/.claude/skills/tlc-spec-driven/references/memory.md @@ -0,0 +1,126 @@ +# Memory Layer + +**File:** `.specs/STATE.md` + +A single file with two section-scoped parts. Each section has its own lifecycle; writes are always targeted — never whole-file overwrites. + +--- + +## Sections + +### `## Decisions` — append-only log + +Records **project-level** decisions only: conventions, patterns, constraints, or cross-cutting technology choices that future features must follow or supersede. + +**Not project-level → stays in the feature's `design.md` Tech Decisions table.** +Heuristic: would a different feature need to know about this? If yes → project-level. If no → feature-local. + +**Format** (one entry per decision): + +```markdown +## Decisions + +### AD-001 +- **Decision**: [what was decided — one sentence] +- **Reason**: [why this option was chosen] +- **Trade-off**: [what was given up] +- **Scope**: [which features / packages / layers this governs] +- **Date**: YYYY-MM-DD +- **Status**: active | superseded by AD-NNN +``` + +**Supersession rule:** When a new decision replaces an old one, append a new `AD-NNN` entry and update the old entry's `status` field to `superseded by AD-NNN`. Never delete old entries — the history is the audit trail. + +--- + +### `## Handoff` — pause snapshot (~500 tokens, overwritten each pause) + +Captures mid-task / in-flight state so work can resume without re-reading the full task history. This is the sole position tracker; it complements `tasks.md` by recording state that `tasks.md` does not capture. + +**Format:** + +```markdown +## Handoff + +- **Feature**: [feature name / .specs path] +- **Phase / Task**: [e.g., Phase 2 / T4 — implement repository layer] +- **Completed**: [comma-separated task IDs or "none"] +- **In-progress** (file:line): [e.g., `src/billing/subscription.service.ts:88` — mid-write] +- **Next step**: [one sentence — exactly what to do next] +- **Blockers**: [none | description] +- **Uncommitted files**: [list or "none"] +- **Branch**: [git branch name] +``` + +--- + +## File shape + +```markdown +# STATE + +## Decisions + +[AD-NNN entries…] + +## Handoff + +[latest snapshot…] +``` + +If the file does not yet exist, create it with both section headers and empty bodies. + +--- + +## Read / Write Triggers + +| Trigger | Section | Operation | +| ------- | ------- | --------- | +| Design phase, Step 1 (Load Context) | `## Decisions` | **Read** — conform to active decisions or supersede | +| Design phase, Tech Decisions step | `## Decisions` | **Append** — only for project-level decisions | +| Pause work / end of session | `## Handoff` | **Replace** — overwrite Handoff section only | +| Resume work / start of session | `## Handoff` | **Read** — load snapshot, propose next step | +| Resume work / start of session | `## Decisions` | **Read** — re-confirm active constraints before designing | + +--- + +## Section-scoped write rule (critical) + +One file holds two lifecycles. Writes MUST target their section only: + +- **Design appends** to `## Decisions`. It MUST NOT touch `## Handoff`. +- **Pause replaces** `## Handoff`. It MUST NOT rewrite, reorder, or drop any entry in `## Decisions`. + +The correct technique: locate the target section header, replace only the content between it and the next `##` header (or end of file). Never overwrite the full file. + +Violating this rule causes one of two failures: +1. A pause write clobbers the decisions log → decisions are silently lost. +2. A design append touches the handoff snapshot → mid-task state is corrupted. + +Both are silent data loss. The section-scoped write rule is the single correctness invariant of this memory layer. + +--- + +## Pause / Resume Procedure + +### Pause + +1. Locate the `## Handoff` section in `.specs/STATE.md`. +2. Replace its body (everything between `## Handoff` and the next `##` or EOF) with the current snapshot. +3. Do NOT modify anything above or before `## Handoff`. +4. Commit or stash outstanding changes as appropriate. + +### Resume + +1. Read `.specs/STATE.md` — both sections. +2. Re-confirm active decisions from `## Decisions` — nothing superseded since last session? +3. Read `## Handoff` — identify feature, phase/task, next step, blockers, uncommitted files, branch. +4. Propose the next step to the user before writing any code. + +--- + +## AD-NNN numbering + +- Numbers are sequential, project-scoped, and permanent — never reused. +- The counter starts at `AD-001`. Check existing entries before assigning the next number. +- If `.specs/STATE.md` does not exist, the first decision is `AD-001`. diff --git a/.claude/skills/tlc-spec-driven/references/specify.md b/.claude/skills/tlc-spec-driven/references/specify.md new file mode 100644 index 0000000..eb53533 --- /dev/null +++ b/.claude/skills/tlc-spec-driven/references/specify.md @@ -0,0 +1,210 @@ +# Specify + +**Goal**: Capture WHAT to build with testable, traceable requirements. + +If the feature has ambiguous gray areas (multiple valid approaches for user-facing behavior), the agent will automatically trigger the [discuss gray areas](discuss.md) process within this phase. For clear, well-defined features, it goes straight to the next phase. + +## Implicit-Requirement Dimensions + +The canonical rubric for requirements that are easy to miss. Referenced by [discuss.md](discuss.md) — defined here, not duplicated. + +| Dimension | What to cover | +| --------- | ------------- | +| Input validation & bounds | Limits, formats, sanitization | +| Failure / partial-failure states | Timeouts, partial saves, rollbacks | +| Idempotency / retry / duplicate handling | Safe retries, dedup keys | +| Auth boundaries & rate limits | Who can call what, throttle rules | +| Concurrency / ordering | Race conditions, ordering guarantees | +| Data lifecycle / expiry | TTL, archival, deletion | +| Observability | Logging, metrics, tracing hooks | +| External-dependency failure | Circuit breakers, fallbacks | +| State-transition integrity | Valid transitions, guards | + +--- + +## Process + +### 1. Clarify Requirements + +**Load confirmed lessons first:** Before clarifying, load the project's confirmed lessons so past verification failures shape this spec instead of repeating. Run `python3 scripts/lessons.py list --status confirmed` (optionally `--scope [area]` or `--query [term]` for the area this feature touches) and apply what comes back as guidance. Load only `confirmed` — never `candidate` or `quarantined`. If no store exists yet or no code tool is available, skip silently. See [lessons.md](lessons.md). + +**Lightweight context scan first (Knowledge Verification Chain Step 1):** Before asking questions, briefly scan existing code, patterns, and neighboring features relevant to this feature. Use what you find to ground your clarifying questions in reality — not to constrain the spec to current implementation. Keep it lightweight (stay within the <40k token budget; reuse the chain, no new machinery). The spec captures WHAT is needed, not only what exists. + +You are a thinking partner, not an interviewer. Start open — let the user dump their mental model. Follow the energy: whatever they emphasize, dig into that. + +Ask conversationally (not as a checklist): + +- "What problem are you solving?" +- "Who is the user and what's their pain?" +- "What does success look like?" + +If needed: + +- "What are the constraints (time, tech, resources)?" +- "What is explicitly out of scope?" + +**Challenge vagueness.** Never accept fuzzy answers. "Good" means what? "Users" means who? "Simple" means how? Make the abstract concrete: "Walk me through using this." "What does that actually look like?" + +**Know when to stop — then run the dimensions sweep.** When you understand what they're building, why, who it's for, and what done looks like, run a closing **implicit-requirement dimensions sweep** before offering to proceed: + +- **Large / Complex:** Cover every dimension above — each must resolve to a requirement OR an explicit `N/A because [reason]`. No blank entries allowed. +- **Medium:** Cover only dimensions obviously present for this feature's domain; collapse the rest to a single `remaining dimensions N/A for this scope`. +- **Small:** Skip the sweep entirely. + +The `N/A because...` escape is mandatory — it prevents inventing requirements to fill the checklist. Bound the sweep to THIS feature's scope; never add requirements outside the feature boundary. + +### 2. Capture User Stories with Priorities + +**P1 = MVP** (must ship), **P2** (should have), **P3** (nice to have) + +Each story MUST be **independently testable** - you can implement and demo just that story. + +### 3. Write Acceptance Criteria + +Use **WHEN/THEN/SHALL** format - it's precise and testable: + +- WHEN [event/action] THEN [system] SHALL [response/behavior] + +### 4. Requirement Closure Gate (before confirm) + +Before presenting the spec for confirmation, run the three checks below. The spec is not presentable for confirmation until every item is resolved or assumption-logged — this is the guarantee that no requirement leaves the spec silently unclear. + +**Scope-tiered:** Large/Complex = full gate; Medium = resolve obvious ambiguities, log the rest as assumptions; Small = skip entirely (consistent with skipping the sweep). + +1. **Unambiguity + precision (hard).** Every AC must (a) have a single interpretation and (b) define a precise, spec-defined expected outcome. Any AC that fails either check: resolve with the user, split it, or log it as an explicit assumption with the chosen interpretation and rationale. No AC proceeds readable two ways or with an undefined outcome. + +2. **Open-questions / assumptions closure.** Enumerate every unresolved decision that surfaced during clarification. Each must be either (a) resolved with the user OR (b) recorded as an **assumption** (chosen default + rationale) in the spec's Assumptions & Open Questions section. Nothing proceeds unmarked. + +3. **Declined gray areas become assumptions.** Any gray area the user declined to discuss or that went undiscussed is written to the spec's Assumptions & Open Questions section (agent's chosen default + rationale) — never silently dropped. See [discuss.md](discuss.md). + +Fix inline. This gate is bounded to THIS feature's stated dimensions and actual behavior — never to "anything imaginable." The Out of Scope table and anti-scope-creep rules remain the counterweights: the gate clarifies existing requirements, it never invents new ones. + +--- + +## Template: `.specs/features/[feature]/spec.md` + +```markdown +# [Feature Name] Specification + +## Problem Statement + +[Describe the problem in 2-3 sentences. What pain point are we solving? Why now?] + +## Goals + +- [ ] [Primary goal with measurable outcome] +- [ ] [Secondary goal with measurable outcome] + +## Out of Scope + +Explicitly excluded. Documented to prevent scope creep. + +| Feature | Reason | +| ----------- | -------------- | +| [Feature X] | [Why excluded] | +| [Feature Y] | [Why excluded] | + +--- + +## Assumptions & Open Questions + +Every ambiguity is resolved or recorded here — nothing is left silently unclear. + +| Assumption / decision | Chosen default | Rationale | Confirmed? | +| --------------------- | --------------- | --------- | ---------- | +| [ambiguity] | [what we'll do] | [why] | [y/n] | + +**Open questions:** none — all resolved or logged above (required before the spec is confirmed). + +--- + +## User Stories + +### P1: [Story Title] ⭐ MVP + +**User Story**: As a [role], I want [capability] so that [benefit]. + +**Why P1**: [Why this is critical for MVP] + +**Acceptance Criteria**: + +1. WHEN [user action/event] THEN system SHALL [expected behavior] +2. WHEN [user action/event] THEN system SHALL [expected behavior] +3. WHEN [edge case] THEN system SHALL [graceful handling] + +**Independent Test**: [How to verify this story works alone - e.g., "Can demo by doing X and seeing Y"] + +--- + +### P2: [Story Title] + +**User Story**: As a [role], I want [capability] so that [benefit]. + +**Why P2**: [Why this isn't MVP but important] + +**Acceptance Criteria**: + +1. WHEN [event] THEN system SHALL [behavior] +2. WHEN [event] THEN system SHALL [behavior] + +**Independent Test**: [How to verify] + +--- + +### P3: [Story Title] + +**User Story**: As a [role], I want [capability] so that [benefit]. + +**Why P3**: [Why this is nice-to-have] + +**Acceptance Criteria**: + +1. WHEN [event] THEN system SHALL [behavior] + +--- + +## Edge Cases + +- WHEN [boundary condition] THEN system SHALL [behavior] +- WHEN [error scenario] THEN system SHALL [graceful handling] +- WHEN [unexpected input] THEN system SHALL [validation response] + +--- + +## Requirement Traceability + +Each requirement gets a unique ID for tracking across design, tasks, and validation. + +| Requirement ID | Story | Phase | Status | +| -------------- | ----------- | ------ | ------- | +| [FEAT]-01 | P1: [Story] | Design | Pending | +| [FEAT]-02 | P1: [Story] | Design | Pending | +| [FEAT]-03 | P2: [Story] | - | Pending | + +**ID format:** `[CATEGORY]-[NUMBER]` (e.g., `AUTH-01`, `CART-03`, `NOTIF-02`) + +**Status values:** Pending → In Design → In Tasks → Implementing → Verified + +**Coverage:** X total, Y mapped to tasks, Z unmapped ⚠️ + +--- + +## Success Criteria + +How we know the feature is successful: + +- [ ] [Measurable outcome - e.g., "User can complete X in < 2 minutes"] +- [ ] [Measurable outcome - e.g., "Zero errors in Y scenario"] +``` + +--- + +## Tips + +- **P1 = Vertical Slice** — A complete, demo-able feature, not just backend or frontend +- **WHEN/THEN is code** — If you can't write it as a test, rewrite it +- **Requirement IDs are mandatory** — Every story maps to trackable IDs +- **Edge cases matter** — What breaks? What's empty? What's huge? +- **Out of Scope prevents creep** — If it's not here, it doesn't get built +- **Closure gate before confirm** — Three checks: unambiguity + precision, open-questions/assumptions closure, declined gray areas logged; scope-tiered; bounded to stated dimensions; never invents requirements +- **Confirm after the gate passes** — Present the spec for user confirmation only after the closure gate passes (no unresolved-and-unmarked items remain); user approves spec before moving to discuss phase diff --git a/.claude/skills/tlc-spec-driven/references/sub-agents.md b/.claude/skills/tlc-spec-driven/references/sub-agents.md new file mode 100644 index 0000000..2ae3d73 --- /dev/null +++ b/.claude/skills/tlc-spec-driven/references/sub-agents.md @@ -0,0 +1,124 @@ +# Sub-Agent Delegation + +Full mechanics for phase-batch workers and the Verifier sub-agent used during Execute. + +## Phase-Batch Workers + +**Two layers — keep them distinct:** + +- **Phase** = the semantic / dependency unit (Foundation → Core → Integration), authored during Tasks. Indivisible. +- **Batch** = the execution / logistics unit — one or more *consecutive whole phases* assigned to a single worker. + +Conflating the two (one worker per phase) is what fragments execution: a feature's dependency-layer count has nothing to do with the ideal per-worker workload. Batching by task budget separates the two concerns without breaking phases. + +**Trigger:** Count total tasks across all phases. If the feature packs into **more than one batch** (> ~8 tasks), offer the user phase-batch sub-agents before starting Execute. If it fits a single batch (≤ ~8 tasks), execute inline in the main window — no sub-agents spawned. + +**Batching algorithm (task budget ≈ 7 tasks/worker, phase-aligned):** + +The benchmarked sweet spot is ~7 tasks of context per worker (~20 tasks → 3 workers). Pack whole phases into that budget: + +1. Count total tasks `T`. +2. If `T ≤ ~8` → inline, no sub-agents. +3. Otherwise walk phases **in order**, accumulating whole phases into the current batch. When the batch's running task count reaches ~7 **and** phases remain, close the batch and start the next. +4. **Never split a phase** across workers — the cut only ever lands on a phase boundary. This preserves dependency ordering and keeps a phase's tasks + shared context in one worker. +5. If the final batch is a lone tail (1–2 tasks), fold it into the previous batch. + +Result ≈ `ceil(T / 7)` workers, scaling linearly. Unevenness is absorbed by greedy packing — phases never need to divide evenly. Worked examples (20 tasks): + +- Phases `[3,3,3,3,4,4]` → `{P1+P2=6, P3+P4=6, P5+P6=8}` = **3 workers** +- Phases `[8,2,2,8]` → `{P1=8, P2+P3=4, P4=8}` = **3 workers** (no even split needed) +- Phases `[5,5,5,5]` → `{P1+P2=10, P3+P4=10}` = **2 workers** (phases too coarse to hit 3 — see below) + +**Coarse-phase caveat:** Because the cut lands only on phase boundaries, very coarse phases limit how finely you can pack. If a single phase alone exceeds ~1.5× the budget (~10+ tasks), that is a Tasks-authoring smell — split it into real sub-phases during Tasks (at a genuine dependency/cohesion boundary), never at dispatch time. + +**Offer-then-confirm (never auto-spawn):** + +> "This feature has [T] tasks across [N] phases. I can pack them into [K] sub-agents (~7 tasks each, whole phases per worker) — every worker runs its phases in order, reports a compact summary, and the orchestrator advances to the next batch. This keeps the main window lean without over-fragmenting. Want to proceed that way?" + +The user must explicitly accept. If they decline (or if the feature fits one batch), execute inline. + +**Execution model — one worker per task-budgeted batch, sequential:** + +``` +Phases 1+2 (7 tasks) ──→ Batch Worker 1 ──→ compact summary ──→ orchestrator updates tasks.md +Phases 3+4 (6 tasks) ──→ Batch Worker 2 ──→ compact summary ──→ orchestrator updates tasks.md +Phase 5 (7 tasks) ──→ Batch Worker 3 ──→ compact summary ──→ orchestrator updates tasks.md +... +``` + +Batches run strictly sequentially: a batch never starts until the previous batch's summary shows all its tasks complete. + +**What a batch worker receives:** + +- The task definitions for **every** phase in its batch (from `tasks.md`) +- The Test Coverage Matrix and Gate Check Commands (from `tasks.md`) +- `references/coding-principles.md` +- Relevant `spec.md` and `design.md` context for the feature (not all specs) + +**What a batch worker does:** + +Executes ALL tasks in its assigned batch **in order** — finishing every task in one phase before starting the next phase in the batch — following the `implement.md` cycle for each task (implement → gate → atomic commit). It does NOT spawn further sub-agents. After completing all tasks in the batch, the worker reports a **compact summary** to the orchestrator: + +``` +Batch (phases [N]–[M]) complete: +- Tasks done: [list with commit hashes] +- Tests: [N passed, 0 failed] +- Deviations/blockers: [none | description] +``` + +No raw logs, no full test output — only the above fields keep the main context clean. + +**No nesting:** Batch workers execute their tasks themselves. They never spawn sub-sub-agents. Execution is strictly sequential within and across batches — there is no intra-phase or intra-batch parallelism. + +**The orchestrating agent's role during Execute:** + +1. Count total tasks and pack phases into task-budgeted batches (~7 tasks each) — if that yields more than one batch, offer batch sub-agents and wait for the user to accept +2. Dispatch the next batch to a worker (or execute inline if not using sub-agents) +3. Receive the compact summary +4. Update `tasks.md` with results +5. If all tasks in the summary show complete: dispatch the next batch +6. If a task failed: the worker has already stopped; decide fix/escalate before dispatching the next batch + +**Failure handling:** If a task in a batch fails (gate does not pass, blocker hit), the worker stops and includes the failure in its summary. The next batch does not start until the current batch's summary shows all tasks complete. The orchestrator decides: fix and re-run, or escalate to the user. + +**Context sizing signal:** If a batch's task list would likely push the worker's context beyond ~40k tokens, close the batch at an earlier phase boundary (fewer phases per worker). If a *single* phase alone would blow the budget, that phase is too coarse — split it during Tasks per the granularity guidance in `references/tasks.md`. + +--- + +## Verifier Sub-Agent + +**Always-on, never prompted — one per feature completion.** The Verifier is a separate role from the batch worker. It runs once — after the last task of the feature is committed — as an independent quality gate, dispatched automatically by the orchestrator. It is **not** gated behind the batching offer; it always runs. Do NOT ask the user whether to run validation; it is mandatory. + +**Author ≠ verifier:** The agent (or batch worker) that wrote the code and tests is the author. The Verifier is a fresh sub-agent dispatched by the orchestrator after the final commit. It does not inherit the author's context, mental model, or assumptions. This separation is what makes the gate trustworthy. + +**What the Verifier receives:** +- `spec.md` for the feature (ACs = source of truth) +- The git diff surface for the feature (scoped to the feature branch or commit range) +- The test files in scope +- `references/validate.md` as its operating checklist + +**What the Verifier does (full process in `validate.md`):** +1. **Spec-anchored coverage check** — re-derives coverage evidence-or-zero: every AC traced to `file:line` + assertion expression. For each covered criterion, confirms the test's asserted value matches the **spec-defined expected outcome** (not just that an assertion exists). Where the spec does not define a precise outcome, flags a **spec-precision gap** rather than passing silently. +2. **Discrimination sensor** — injects a small behavior-level fault (flip a condition, change a return value, off-by-one, remove a required side effect) in a **scratch/throwaway state** (git stash or temp copy), runs the relevant tests, confirms they FAIL (kill the mutant), then discards the mutation. Tiered by risk: lightweight (1–3 mutations) for standard features; expanded (≥5 mutations or full mutation tooling) for P0/critical paths. Surviving mutants become fix tasks. +3. Applies the **payload/conjunction rule**: checks payload fields are asserted on value/state, not just that the call occurred. +4. **Writes the persisted report** to `.specs/features/[feature]/validation.md` — PASS/FAIL, per-AC evidence (`file:line` + assertion + spec outcome), sensor result (killed/survived per mutation), gate exit results, diff/commit range. +5. **Returns a compact verdict in chat** to the orchestrator. +6. Does **NOT** write, modify, or fix any code or tests — the real working tree is never mutated (sensor mutations run in scratch state only). + +**What the Verifier reports back (compact chat format):** +``` +## Validation: [feature name] — [PASS ✅ | FAIL ❌] + +**Spec-anchored check**: [N/N ACs matched spec outcome | M spec-precision gaps flagged] +**Gate**: [X passed, 0 failed] +**Sensor**: [N mutations injected, N killed, N survived] +**Report**: `.specs/features/[feature]/validation.md` + +**Ranked gaps** (if FAIL): +1. [Gap description] — [AC or criterion] — [file:line or "no evidence"] +2. ... +``` + +**Failure handling:** The orchestrator routes the ranked gaps to an implementer as fix tasks, then re-dispatches the Verifier. This fix→re-verify loop is bounded to a maximum of **3 iterations**. If gaps remain after 3 iterations, escalate to the user. + +**Standalone fallback:** When running without sub-agents (a single agent executing the full feature), run `validate.md` as an independent fresh-eyes pass — re-read `spec.md` and the diff from scratch, apply evidence-or-zero, run the spec-anchored check and discrimination sensor, write the report file, and report PASS/FAIL before marking the feature done. diff --git a/.claude/skills/tlc-spec-driven/references/tasks.md b/.claude/skills/tlc-spec-driven/references/tasks.md new file mode 100644 index 0000000..06c511d --- /dev/null +++ b/.claude/skills/tlc-spec-driven/references/tasks.md @@ -0,0 +1,449 @@ +# Tasks + +**Goal**: Break into GRANULAR, ATOMIC tasks. Clear dependencies. Right tools. Sequential phase execution plan. + +**Skip this phase when:** There are ≤3 obvious steps. In that case, tasks are implicit — go straight to Execute and list them inline in your implementation plan. + +## Why Granular Tasks? + +| Vague Task (BAD) | Granular Tasks (GOOD) | +| ---------------- | --------------------------------- | +| "Create form" | T1: Create email input component | +| | T2: Add email validation function | +| | T3: Create submit button | +| | T4: Add form state management | +| | T5: Connect form to API | +| "Implement auth" | T1: Create login form | +| | T2: Create register form | +| | T3: Add token storage utility | +| | T4: Create auth API service | +| | T5: Add route protection | + +**Benefits of granular:** + +- **Agents don't err** - Single focus, no ambiguity +- **Easy to test** - Each task = one verifiable outcome +- **Clean commits** - Each task = one atomic, revertable commit +- **Errors isolated** - One failure doesn't block everything + +**Rule**: One task = ONE of these: + +- One component +- One function +- One API endpoint +- One file change + +--- + +## Process + +### 1. Review Design + +Read `.specs/features/[feature]/design.md` before creating tasks. + +### 1.5. Generate the Test Coverage Matrix (ALWAYS) + +This step ALWAYS runs — there is no precondition. Decide which of two paths to take, then generate the three sections below. + +**Step 0 — Read project quality/testing guidelines (ALWAYS, before anything else).** + +Before sampling tests or inferring anything, scan the project for documented quality and testing standards. Stack-agnostic sources to check (illustrative, not exhaustive): + +- Agent/AI instructions: `AGENTS.md`, `CLAUDE.md`, `.cursor/rules/**`, `.github/copilot-instructions.md` +- Contributor guides: `CONTRIBUTING.md`, `docs/` (testing, quality, or standards subdocs), README testing section +- Tool configuration: coverage thresholds in the test runner config (e.g., `jest.config.*`, `vitest.config.*`, `pytest.ini`, `.nycrc`, `Makefile` coverage targets, CI coverage gates) + +**If guidelines are found:** the Coverage Expectation (see matrix below) conforms to them. Existing test samples fill gaps in style/location/framework only. Cite the specific files found in the matrix provenance note. + +**If no guidelines are found:** apply the strong default — cover every spec AC and every listed edge case; domain/business logic maps 1:1 to spec ACs; routes/e2e cover happy + edge + error paths. This default may exceed the current repo's depth, which is intentional. + +**Decision:** + +- **Existing tests in the repo** → infer the matrix and gate commands by sampling the codebase. +- **No tests at all** → ask the user: "What test types will this project use (unit / integration / e2e / none)? What commands run them?" + +**How to infer (path 1 — existing tests):** + +1. **Sample test files.** Locate 5–10 existing test files. Map each file's location relative to its source file to identify which code layers are exercised and at what level (unit, integration, e2e). Use these samples for style, location patterns, framework, and test type — and as a **floor** (never produce tests less thorough than existing ones for the same layer). Existing tests are NOT a ceiling on thoroughness; the thoroughness target comes from the spec ACs, listed edge cases, and guidelines (or strong default). The Coverage Expectation column captures the target per layer. +2. **Discover commands from the repo.** Do NOT invent commands and do NOT assume an ecosystem. Read the project's own build/task manifests, test config, and CI workflows to extract the actual commands — for example: `package.json` / `project.json` (JS/TS), `Makefile`, `pyproject.toml` / `tox.ini` / `pytest` (Python), `Cargo.toml` (Rust), `go test` invocations (Go), `pom.xml` / `build.gradle` (Java/Kotlin), `Gemfile` / `Rakefile` (Ruby), `composer.json` (PHP), `.github/workflows` / `.gitlab-ci.yml`. The list is illustrative; detect what this repo actually uses. + +**Output contract — render these two sections verbatim into `tasks.md`** (the exact headings downstream phases reference): + +--- + +## Test Coverage Matrix + +> Generated from codebase, project guidelines, and spec — confirm before Execute. Guidelines found: [list files, e.g. `AGENTS.md`, `jest.config.ts` — or "none — strong defaults applied"]. + +| Code Layer | Required Test Type | Coverage Expectation | Location Pattern | Run Command | +| ---------- | ------------------ | -------------------- | ---------------- | ----------- | +| [layer] | [unit/integration/e2e/none] | [depth target for this layer] | [glob or path pattern] | [command] | + +**Coverage Expectation values** — set from guidelines first; use strong defaults when no guideline applies: + +| Layer type | Strong default (no guideline) | +| ---------- | ----------------------------- | +| Domain / business-logic (service, use-case, domain model) | All branches; 1:1 to spec ACs; every listed edge case has a test | +| Route / controller / e2e / integration | All routes in scope: happy path + every listed edge case + error/failure paths | +| Repository / data-access | Key query paths + error handling; infer from existing repo tests | +| Entity / config / schema | none — build gate only | + +These defaults may exceed the current repo's depth. That is intentional — they are a **target**, not a reflection of what already exists. + +*Example (filled in):* + +| Code Layer | Required Test Type | Coverage Expectation | Location Pattern | Run Command | +| ---------- | ------------------ | -------------------- | ---------------- | ----------- | +| Service | unit | All branches; 1:1 to spec ACs; all listed edge cases | `src/**/__test__/*.spec.ts` | `yarn test:unit` | +| Repository | integration | Key query paths + error paths | `src/**/__test__/*.e2e-spec.ts` | `yarn test:e2e` | +| Controller/Resolver | e2e | All routes: happy + edge + error | `src/**/__test__/*.e2e-spec.ts` | `yarn test:e2e` | +| Entity / Config | none | — (build gate only) | — | build gate only | + +## Gate Check Commands + +> Generated from codebase — confirm before Execute. + +| Gate Level | When to Use | Command | +| ---------- | ----------- | ------- | +| Quick | After tasks with unit tests only | [unit test command] | +| Full | After tasks with e2e/integration tests | [unit + e2e commands] | +| Build | After phase completion or config/entity-only tasks | [build + lint + all tests] | + +--- + +**Co-located tests:** Every task that creates or modifies a code layer with a required test type MUST include writing/updating those tests in the same task. Tests are NOT separate tasks. The tests must satisfy the layer's **Coverage Expectation** from the matrix — not merely exist. + +| Task creates... | Done When must include... | +| ----------------------------------------- | ------------------------------------------------------------------------------------------------------------------ | +| Code layer with "unit" requirement | Unit tests written satisfying the layer's Coverage Expectation (e.g., 1:1 AC mapping for domain logic; all listed edge cases covered) + quick gate passes | +| Code layer with "e2e" requirement | E2E tests written satisfying the layer's Coverage Expectation (e.g., every route the task adds: happy path + edge + error paths) + full gate passes | +| Code layer with "integration" requirement | Integration tests written satisfying the layer's Coverage Expectation + full gate passes | +| Code layer with "none" requirement | Gate check at appropriate level | + +### 2. Break Into Atomic Tasks + +**Task = ONE deliverable**. Examples: + +- ✅ "Create UserService interface" (one file, one concept) +- ❌ "Implement user management" (too vague, multiple files) + +### 3. Define Dependencies + +What MUST be done before this task can start? + +### 4. Create Execution Plan + +Group tasks into ordered phases. Each phase depends on the ones before it; tasks execute sequentially within a phase. + +**Size phases near the worker budget.** During Execute, phases are packed into task-budgeted batches (~7 tasks per sub-agent, whole phases — see [sub-agents.md](sub-agents.md)). Because a batch cut may only land on a phase boundary, a phase that is much larger than the budget forces an over-sized worker. Keep each phase from greatly exceeding the budget: + +- If a phase would hold **more than ~10 tasks (≈1.5× the budget)**, split it into cohesive sub-phases at a genuine dependency/cohesion seam — not at an arbitrary task index. +- Only leave a phase over-sized when its tasks are one tight dependency chain that genuinely cannot be split. That is a legitimate (if fat) single-worker phase, not a smell. + +This keeps phase boundaries meaningful while letting the packing hit its target worker count. + +### 5. Validate Before Presenting (MANDATORY) + +Before showing tasks to the user, run ALL three pre-approval checks. These are NOT optional — they are gates. If any check fails, restructure the tasks and re-run until all pass. + +**Check 1: Task Granularity** — verify each task is atomic (see Granularity Check section). + +**Check 2: Diagram-Definition Cross-Check** — verify the execution diagram matches every task's `Depends on` field (see Diagram-Definition Cross-Check section). Build the cross-check table and include it in the output. + +**Check 3: Test Co-location Validation** — verify every task's `Tests` field matches the **Test Coverage Matrix** generated above (see Test Co-location Validation section). Build the validation table and include it in the output. + +**Output both tables with the tasks** so the user can see the validation results. Any ❌ means you MUST restructure before presenting — do not show failing tasks to the user and ask them to approve. + +**Note on the generated matrix:** The two sections (`Test Coverage Matrix`, `Gate Check Commands`) are provisional — generated from codebase sampling or user input and included in this file for user confirmation as part of task approval. They become authoritative once the user approves the tasks. + +### 6. ASK About MCPs and Skills + +**CRITICAL**: Before execution, ask the user: + +> "For each task, which tools should I use?" +> +> **Available MCPs**: [list from project or user] +> **Available Skills**: [list from project or user] + +--- + +## Template: `.specs/features/[feature]/tasks.md` + +```markdown +# [Feature] Tasks + +## Execution Protocol (MANDATORY -- do not skip) + +Implement these tasks with the `tlc-spec-driven` skill: **activate it by name and follow its Execute flow and Critical Rules.** Do not search for skill files by filesystem path. The skill is the source of truth for the full flow (per-task cycle, sub-agent delegation, adequacy review, Verifier, discrimination sensor). + +**If the skill cannot be activated, STOP and tell the user — do not proceed without it.** + +--- + +**Design**: `.specs/features/[feature]/design.md` +**Status**: Draft | Approved | In Progress | Done + +--- + + + +## Test Coverage Matrix + +[Generated in step 1.5 — see process above] + +## Gate Check Commands + +[Generated in step 1.5 — see process above] + +--- + +## Execution Plan + +Phases are ordered and run sequentially — each phase completes before the next begins, and tasks within a phase execute in order. + +### Phase 1: Foundation + +Tasks that must be done first, in order. + +``` +T1 → T2 → T3 +``` + +### Phase 2: Core Implementation + +Builds on the foundation. + +``` +T4 → T5 → T6 → T7 +``` + +### Phase 3: Integration + +Bringing it all together. + +``` +T8 → T9 +``` + +--- + +## Task Breakdown + +### T1: [Create X Interface] + +**What**: [One sentence: exact deliverable] +**Where**: `src/path/to/file.ts` +**Depends on**: None +**Reuses**: `src/existing/BaseInterface.ts` +**Requirement**: [FEAT]-01 + +**Tools**: + +- MCP: `filesystem` (or NONE) +- Skill: NONE + +**Done when**: + +- [ ] Interface defined with all methods from design +- [ ] Types exported correctly +- [ ] No TypeScript errors + +**Tests**: [unit/e2e/integration/none — from coverage matrix] +**Gate**: [quick/full/build — from gate check commands] + +--- + +### T2: [Implement Y Service] + +**What**: [Exact deliverable] +**Where**: `src/services/YService.ts` +**Depends on**: T1 +**Reuses**: `src/services/BaseService.ts` patterns + +**Tools**: + +- MCP: `filesystem`, `context7` +- Skill: NONE + +**Done when**: + +- [ ] Implements interface from T1 +- [ ] Handles error cases from design +- [ ] Gate check passes: `[quick gate command from the Gate Check Commands above]` +- [ ] Test count: [N] tests pass (no silent deletions) + +**Tests**: unit +**Gate**: quick + +--- + +### T3: [Create Z Component] + +**What**: [Exact deliverable] +**Where**: `src/components/ZComponent.tsx` +**Depends on**: T1 +**Reuses**: `src/components/BaseComponent.tsx` + +**Tools**: + +- MCP: `filesystem` +- Skill: NONE + +**Done when**: + +- [ ] Component renders correctly +- [ ] Handles props from interface +- [ ] Follows existing component patterns +- [ ] Gate check passes: `[quick gate command from the Gate Check Commands above]` +- [ ] Test count: [N] tests pass (no silent deletions) + +**Tests**: unit +**Gate**: quick + +--- + +### T4: [Add A Feature to Y] + +**What**: [Exact deliverable] +**Where**: `src/services/YService.ts` (modify) +**Depends on**: T2, T3 +**Reuses**: Existing service patterns + +**Tools**: + +- MCP: `filesystem`, `github` +- Skill: `api-design` + +**Done when**: + +- [ ] Feature works per acceptance criteria +- [ ] Gate check passes: `[full gate command from the Gate Check Commands above]` +- [ ] Test count: [N] tests pass (no silent deletions) + +**Tests**: integration +**Gate**: full + +**Commit**: `feat([scope]): [description]` + +--- + +## Phase Execution Map + +Visual representation of task ordering. Phases run in sequence, and tasks within a phase run in order: + +``` +Phase 1 → Phase 2 → Phase 3 + +Phase 1: T1 ──→ T2 ──→ T3 +Phase 2: T4 ──→ T5 ──→ T6 ──→ T7 +Phase 3: T8 ──→ T9 +``` + +Execution is strictly sequential — there is no intra-phase parallelism. A single agent (or batch worker) works one task at a time, in order. + +**How phase-based execution works:** + +At Execute, the agent counts total tasks and packs phases into **task-budgeted batches** (~7 tasks +per worker, whole phases — the benchmarked sweet spot is ~20 tasks → ~3 workers). A **phase** is the +semantic/dependency unit; a **batch** is one or more *consecutive whole phases* assigned to one +worker. The cut only ever lands on a phase boundary — a phase is never split across workers. When +packing yields more than one batch (> ~8 tasks), the agent offers to dispatch batch sub-agents. +Batches run sequentially: each worker executes ALL its tasks in order, then reports a compact summary +before the next batch starts. This right-sizes the worker count by workload instead of by phase +count (one-per-phase is too fragmented; expensive and slow). See [sub-agents.md](sub-agents.md) for +the full model — packing algorithm, offer-then-confirm, worker payload, compact summary contract, +failure handling, and context sizing guidance. + +When the whole feature fits a single batch (≤ ~8 tasks), execution happens inline in the main window +with no sub-agents spawned. + +**The orchestrating agent's role during Execute:** +1. Count total tasks and pack phases into ~7-task batches — offer batch sub-agents if that yields more than one batch and the user accepts +2. Dispatch the next batch (to a worker, or execute inline) +3. Receive the compact batch summary +4. Update tasks.md with results +5. If the batch summary shows all tasks complete: proceed to the next batch +6. If a task failed: decide fix/escalate before dispatching the next batch + +--- + +## Task Granularity Check + +Before approving tasks, verify they are granular enough: + +| Task | Scope | Status | +| ------------------------------- | ------------- | ------------ | +| T1: Create email input | 1 component | ✅ Granular | +| T2: Add validation function | 1 function | ✅ Granular | +| T3: Create form with all fields | 5+ components | ❌ Split it! | +| T4: Connect to API | 1 function | ✅ Granular | + +**Granularity check**: + +- ✅ 1 component / 1 function / 1 endpoint = Good +- ⚠️ 2-3 related things in same file = OK if cohesive +- ❌ Multiple components or files = MUST split + +--- + +## Diagram-Definition Cross-Check + +Before approving tasks, verify the execution diagram is consistent with the task definitions. These are independent artifacts that can drift — the diagram is drawn for visual clarity while task bodies are written for precision. Both must agree. + +For each task, check: + +| Task | Depends On (task body) | Diagram Shows | Status | +| ---- | ---------------------- | ------------- | ------ | +| T[N] | [deps from body] | [deps from diagram arrows] | ✅ Match or ❌ Mismatch | + +**Rules:** + +- Every `Depends on` in a task body must have a corresponding arrow in the diagram. +- Every arrow in the diagram must correspond to a `Depends on` in the target task's body. +- A task must never depend on a task in a later phase — dependencies point backward or within the same phase only. + +--- + +## Test Co-location Validation + +Before approving tasks, verify EVERY task's `Tests` field is consistent with the **Test Coverage Matrix** generated above. This is a hard gate — tasks that fail this check MUST be fixed. + +For each task, check: does the task create or modify a code layer that has a required test type in the coverage matrix? If yes, the task's `Tests` field MUST match. + +| Task | Code Layer Created/Modified | Matrix Requires | Task Says | Status | +| ---- | --------------------------- | --------------- | --------- | ------ | +| T[N]: [name] | [layer from coverage matrix] | [test type] | [task's Tests field] | ✅ OK or ❌ VIOLATION | + +**Rules:** + +- "Tested in another task" is NOT a valid justification for `Tests: none`. That is test deferral — the exact anti-pattern this validation prevents. +- `Tests: none` is only valid when the coverage matrix says "none" for that code layer. +- If a task creates MULTIPLE code layers (e.g., service + controller), use the HIGHEST test type required by any of them. +- Any ❌ VIOLATION → restructure the task to include its required tests before proceeding. + +**Resolving compilation dependencies:** + +When a task creates code that can't be tested until a later task completes (e.g., a controller that needs module wiring before its e2e tests can run), do NOT defer the tests to a separate task. Instead, restructure: + +1. **Merge forward:** Move the untestable task's tests into the earliest task where they become runnable (e.g., the wiring task includes wiring + e2e tests for the controller it enables). +2. **Merge backward:** Absorb the blocking dependency into the current task so it becomes self-testable (e.g., controller task includes its own module registration). + +Pick whichever option keeps tasks atomic and cohesive. The goal: no task produces unverified code. If code can't be tested in the task that creates it, the task boundaries are wrong. + +--- + +## Tips + +- **Phases are ordered** — Each phase completes before the next; tasks run in order within a phase +- **Reuses = Token saver** — Always reference existing code +- **Tools per task** — MCPs and Skills prevent wrong approaches +- **Dependencies are gates** — Clear what blocks what +- **Done when = Testable** — If you can't verify it, rewrite it +- **Requirement ID = Traceable** — Every task traces back to a spec requirement +- **One commit per task** — Plan the commit message format in advance + +--- + +## Task Verification Standards + +Every task MUST follow the `Done when` + `Tests` + `Gate` fields defined in the **Task Breakdown** template above. Each `Done when` entry must be specific, testable (binary pass/fail), and reference the gate check command from the `Gate Check Commands` section. Include the expected test count to prevent silent deletions. diff --git a/.claude/skills/tlc-spec-driven/references/validate.md b/.claude/skills/tlc-spec-driven/references/validate.md new file mode 100644 index 0000000..b1283e3 --- /dev/null +++ b/.claude/skills/tlc-spec-driven/references/validate.md @@ -0,0 +1,350 @@ +# Execute: Validate & Verify + +**Goal**: Verify implementation meets spec AND coding principles. This is NOT a separate phase — verification is part of every task's completion within Execute. + +**Three levels of verification:** + +1. **Per-task verification (always, author self-check):** After implementing each task, verify its "Done when" criteria before committing. This is mandatory and automatic. The implementer runs it. + +2. **Feature-level validation (independent Verifier sub-agent, always-on, never prompted):** After all tasks for a feature (or priority group) are done, validation runs automatically — the orchestrator dispatches a **fresh Verifier sub-agent** (see [sub-agents.md](sub-agents.md)). Do NOT ask the user whether to run it; it is the safety net, not an opt-in. User interaction is limited to interactive UAT (for user-facing features) and acting on a FAIL verdict ("fix these gaps now?"). The Verifier: + - Runs **read-only** over the real implementation and tests — mutations run in a scratch/throwaway state only (see Discrimination Sensor section) + - Scopes coverage to the feature's **git diff surface** (not the full repository) + - Re-derives coverage independently using **evidence-or-zero**: every AC must be traced to a `file:line` + assertion expression; a criterion with no `file:line` citation counts as NOT covered + - Runs the **spec-anchored outcome check** and the **discrimination sensor** (both described below) + - Writes `.specs/features/[feature]/validation.md` with the full evidence report + - Returns a compact verdict + ranked gap list to the orchestrator in chat + - Gaps become **fix tasks** routed back to an implementer; re-verification follows with a maximum of **3 fix→re-verify iterations** before escalating to the user + +3. **Interactive UAT (for user-facing features only):** The feature has complex user-facing behavior where human judgment matters (UI flows, interaction patterns, visual design). For backend-only or infrastructure work, automated checks are sufficient. + +**Trigger for explicit validation:** "Validate", "verify work", "UAT", "test with me", "walk me through it" + +--- + +## Process + +### 1. Check Completed Tasks + +Go through tasks.md: + +- [ ] All tasks marked done? +- [ ] Any blocked or partial? + +### 2. Spec-Anchored Acceptance Criteria Check + +For each acceptance criterion in `spec.md`, the Verifier re-derives the **spec-defined expected outcome** and confirms the test's actual assertion matches it: + +```markdown +### P1: [Story Title] + +**Acceptance Criteria**: + +| Criterion (WHEN X THEN Y) | Spec-defined outcome | `file:line` + assertion expression | Result | +| ------------------------- | -------------------- | ---------------------------------- | ------ | +| WHEN [X] THEN [Y] | [precise value/state from spec] | `path/to/test.ts:42` — `expect(result.field).toBe(expected)` | ✅ PASS / ❌ GAP / ⚠️ Spec-precision gap | +``` + +**Rules:** + +- Where the spec defines a precise outcome (specific status code, field value, error message, state), the test assertion MUST target that exact outcome — not just that an assertion exists. +- Where the spec does NOT define a precise outcome, mark as **⚠️ Spec-precision gap** and flag it in the report. Do NOT silently pass a vague assertion. +- Evidence-or-zero: a criterion with no `file:line` citation counts as NOT covered. + +### 3. Check Edge Cases + +From spec.md edge cases: + +- [ ] [Edge case 1] handled correctly +- [ ] [Edge case 2] handled correctly + +### 4. Run Build-Level Gate Check (MANDATORY) + +Run the Build-level gate check from the **Gate Check Commands** section in tasks.md. This is NOT optional. + +1. Run: `[Build gate command from the Gate Check Commands section in tasks.md]` +2. Non-zero exit code = STOP. Do not proceed to Code Quality Check. +3. Record results: + - Total test count: [N] + - Passed: [N] + - Failed: [list] + - Skipped: [list — each skip must be justified] + +**Test Integrity Check:** + +- Compare current test count against the count before this feature was implemented +- If test count DECREASED: investigate why. Tests should only be deleted with explicit justification. +- If assertions were weakened (less specific than before): flag as potential regression + +### 5. Discrimination Sensor (MANDATORY — always runs after gate check passes) + +The sensor provides the empirical guarantee that the tests can actually detect regressions. It runs in a scratch/throwaway state — the real working tree is never modified. + +**How it works:** + +1. **Prepare a scratch state.** Use one of (choose the safest available for the environment): + - `git stash` the current state, apply a mutation, run tests, then `git stash pop`; OR + - A temporary worktree (`git worktree add`) or temp copy of the affected file(s). +2. **Inject a behavior-level fault** into the new code introduced by this feature. Choose a mutation proportional to the code's risk: + - Flip a boolean condition (`if (x)` → `if (!x)`, `>` → `>=`) + - Change a return value (return a wrong status code, wrong field, zero instead of a computed value) + - Off-by-one (shift a loop bound, change a slice index) + - Remove a required side effect (delete a method call that the spec requires) +3. **Run the tests** that cover the mutated code. Use the Quick or Full gate command from tasks.md. +4. **Confirm the mutant is killed** (tests FAIL). Then discard the mutation (restore the scratch state). +5. **If a mutant survives** (tests still pass after the fault), the tests are not discriminating for that behavior — add a fix task to strengthen the assertion. + +**Tiering (proportional, not optional):** + +| Context | Sensor depth | +| ------- | ------------ | +| Default (all features) | Lightweight fault-injection: 1–3 targeted behavior-level mutations per feature, focused on the highest-risk new code | +| P0 / critical paths (payment, auth, data integrity) | Full mutation run: use language-appropriate mutation tooling if available (e.g., Stryker, mutmut, cargo-mutants, pitest); otherwise increase the number of manual fault-injection mutations to ≥5 covering all branches | + +**Stack-agnostic:** The sensor targets behavior-level semantics (what the code does), not a specific tool. Any language, any framework. + +**Report:** Record killed/survived for each mutation attempt. Surviving mutants → create fix tasks before marking the feature done. + +### 6. Code Quality Check (MANDATORY) + +For each changed file, verify against [coding-principles.md](coding-principles.md): + +| Check | Pass? | +| ------------------------------------ | ----- | +| No features beyond what was asked | | +| No abstractions for single-use code | | +| No unnecessary "flexibility" added | | +| Only touched files required for task | | +| Didn't "improve" unrelated code | | +| Matches existing patterns/style | | +| Would senior engineer approve? | | +| Tests map to acceptance criteria and are non-shallow (spot-check one story) | | +| Spec-anchored outcome check: each test's asserted value matches the spec-defined outcome (or gap flagged) | | +| Per-layer Coverage Expectation met: domain logic has 1:1 AC mapping; routes/e2e cover happy + edge + error paths for every route in scope | | +| Every test in scope maps to a spec AC, listed edge case, or Done-when criterion (no unclaimed tests) | | +| Documented project quality/testing guidelines followed (cite guideline file, or "none — strong defaults applied") | | + +❌ Any "No"? → Fix before marking complete. + +### 7. Interactive UAT (if user-facing feature) + +For each testable deliverable, present one test at a time: + +``` +Test [N]: [Test Name] + +Expected: [What should happen — specific and observable] + +→ Does this work? Describe what you see. +``` + +Wait for user response: + +| User says | Interpret as | +| ------------------------------ | ----------------------- | +| "yes", "pass", "works", "next" | ✅ Pass | +| "skip", "can't test", "n/a" | ⏭️ Skip | +| Anything else | ❌ Issue — log verbatim | + +**Severity inference (never ask the user for severity):** + +| User description contains | Inferred severity | +| --------------------------------------- | ----------------- | +| crash, error, exception, fails, broken | Blocker | +| doesn't work, wrong, missing, can't | Major | +| slow, weird, off, minor, small | Minor | +| color, font, spacing, alignment, visual | Cosmetic | +| (unclear) | Major (default) | + +### 8. Generate Fix Plans (if issues found) + +For each issue found during UAT or from the Verifier: + +1. **Diagnose** — Analyze the codebase to find root cause +2. **Create fix task** — Write a task definition with: + - What: The specific fix + - Where: File paths + - Verify: How to prove the fix works + - Done when: Acceptance criteria for the fix +3. **Present fix plan** — Show all fix tasks to user for approval + +Fix tasks follow the same format as regular tasks and can be executed with the implement phase. + +**Guardrail:** Maximum 3 diagnostic iterations per issue. If root cause isn't found after 3 attempts, flag for human investigation. The same 3-iteration bound applies to the Verifier's fix→re-verify cycle: if gaps persist after 3 rounds, escalate to the user rather than continuing to loop. + +### 9. Write Validation Report File + Return Chat Summary (MANDATORY) + +After all checks complete, the Verifier MUST: + +1. **Write the persisted report** to `.specs/features/[feature]/validation.md` (see template below). This file is the evidence artifact — it survives the session and can be referenced by CI, reviewers, or future agents. +2. **Return a compact summary in chat** to the orchestrator (see Compact Chat Summary section below). The orchestrator surfaces it to the user and routes any ranked gaps to fix tasks. + +### 10. Distill Lessons (MANDATORY when validation.md has signal) + +This is the closing action of validation — not a separate phase. Immediately after the report is written, turn its grounded failures into reusable, project-local guidance by following [lessons.md](lessons.md). In short: for each surviving mutant, spec-precision gap, failed/uncovered AC, or `// SPEC_DEVIATION`, record one terse general lesson via `python3 scripts/lessons.py add` (the script enforces grounding and owns all bookkeeping). A clean PASS with no signal → record nothing. Run the self-check: if there was signal but no lesson was recorded, say so in chat. See [lessons.md](lessons.md) for the exact commands, phrasing rules, scope discipline, and the no-script fallback. + +--- + +## Compact Chat Summary (returned in chat after validation) + +The Verifier returns this block to the orchestrator after completing all checks: + +```markdown +## Validation: [Feature] — [PASS ✅ | FAIL ❌] + +**Spec-anchored check**: [N/N ACs matched spec outcome | M spec-precision gaps flagged] +**Gate**: [X passed, 0 failed] +**Sensor**: [N mutations injected, N killed, N survived] +**Report**: `.specs/features/[feature]/validation.md` + +**Ranked gaps** (if FAIL): +1. [Gap description] — [AC or criterion] — [file:line or "no evidence"] +2. ... +``` + +--- + +## Validation Report Template (`.specs/features/[feature]/validation.md`) + +```markdown +# [Feature] Validation + +**Date**: [YYYY-MM-DD] +**Spec**: `.specs/features/[feature]/spec.md` +**Diff range**: [commit range or branch..HEAD] +**Verifier**: independent sub-agent (author ≠ verifier) + +--- + +## Task Completion + +| Task | Status | Notes | +| ---- | ---------- | ------- | +| T1 | ✅ Done | - | +| T2 | ✅ Done | - | +| T3 | ⚠️ Partial | [Issue] | + +--- + +## Spec-Anchored Acceptance Criteria + +| Criterion (WHEN X THEN Y) | Spec-defined outcome | `file:line` + assertion | Result | +| ------------------------- | -------------------- | ----------------------- | ------ | +| WHEN X THEN Y | [precise value/state from spec] | `path/to/test.ts:42` — `expect(result.field).toBe(expected)` | ✅ PASS | +| WHEN A THEN B | [expected value] | `path/to/test.ts:88` — `expect(res.status).toBe(400)` | ✅ PASS | +| WHEN C THEN D | not precisely defined in spec | — | ⚠️ Spec-precision gap | + +**Status**: ✅ All ACs covered / ❌ Gaps present / ⚠️ Spec-precision gaps flagged + +--- + +## Discrimination Sensor + +| Mutation | File:line | Description | Killed? | +| -------- | --------- | ----------- | ------- | +| 1 | `src/service.ts:42` | Flipped condition `x > 0` → `x >= 0` | ✅ Killed | +| 2 | `src/service.ts:88` | Changed return value `status: 'active'` → `status: 'inactive'` | ✅ Killed | +| 3 | `src/handler.ts:15` | Removed side-effect call to `notify()` | ❌ Survived → fix task created | + +**Sensor depth**: [lightweight / P0-full] +**Result**: [N/N killed] — [PASS ✅ | FAIL ❌] + +--- + +## Interactive UAT Results (if performed) + +| # | Test | Result | Details | +| --- | ----------- | -------- | ----------------------------------------------- | +| 1 | [Test name] | ✅ Pass | - | +| 2 | [Test name] | ❌ Issue | [Verbatim user response] — Severity: [inferred] | +| 3 | [Test name] | ⏭️ Skip | [Reason] | + +--- + +## Code Quality + +| Principle | Status | +| ---------------- | ------ | +| Minimum code | ✅ | +| Surgical changes | ✅ | +| No scope creep | ✅ | +| Matches patterns | ✅ | +| Spec-anchored outcome check (asserted values match spec) | ✅ | +| Per-layer Coverage Expectation met (domain 1:1 ACs; routes happy+edge+error) | ✅ | +| Every test maps to a spec requirement — no unclaimed tests | ✅ | +| Documented guidelines followed: [file(s) or "none — strong defaults applied"] | ✅ | + +--- + +## Edge Cases + +- [x] Edge case 1: Handled correctly +- [ ] Edge case 2: NOT handled - needs fix + +--- + +## Gate Check + +- **Gate command**: [Build gate command from the Gate Check Commands section in tasks.md] +- **Result**: [X] passed, [Y] failed, [Z] skipped +- **Test count before feature**: [N] +- **Test count after feature**: [M] +- **Delta**: [+(M - N) new tests] +- **Skipped tests**: [list with justification for each] +- **Failures**: [list with details] + +--- + +## Fix Plans (if issues found) + +### Fix 1: [Issue description] + +- **Root cause**: [What's actually wrong] +- **Fix task**: [Task definition] +- **Priority**: [Blocker/Major/Minor/Cosmetic] + +--- + +## Requirement Traceability Update + +Update spec.md requirement statuses: + +| Requirement | Previous Status | New Status | +| ----------- | --------------- | ------------ | +| [FEAT]-01 | Implementing | ✅ Verified | +| [FEAT]-02 | Implementing | ❌ Needs Fix | + +--- + +## Summary + +**Overall**: ✅ Ready | ⚠️ Issues | ❌ Not Ready + +**Spec-anchored check**: [N/N ACs matched spec outcome | M spec-precision gaps] +**Sensor**: [N/N mutations killed] +**Gate**: [X passed] + +**What works**: [List] + +**Issues found**: [Issue 1: How to fix] + +**Next steps**: [Action] +``` + +--- + +## Tips + +- **Validation is never prompted** — it always runs after the last task; do not ask the user whether to run it +- **Spec-anchored, not just covered** — "there is an assertion" is not enough; the assertion must target the spec-defined outcome +- **Sensor in scratch only** — never mutate the real tree; stash/worktree/temp copy, run, discard +- **Surviving mutants are fix tasks** — do not mark the feature done if the sensor found weak tests +- **P1 first** — MVP must work before P2/P3 +- **WHEN/THEN = Test** — Each criterion is a test case +- **Be specific** — "Doesn't work" isn't helpful +- **Recommend fixes** — Don't just report problems, create fix tasks +- **Quality check is mandatory** — Not optional +- **Infer severity** — Never ask the user "how bad is this?" +- **Max 3 diagnostic iterations** — Prevents infinite investigation loops +- **Update traceability** — Every verified requirement updates spec.md status +- **Always write the report file** — `.specs/features/[feature]/validation.md` is the persisted evidence artifact +- **Distill after writing** — turn grounded failures into lessons via `scripts/lessons.py` ([lessons.md](lessons.md)); clean PASS → no lesson diff --git a/.claude/skills/tlc-spec-driven/scripts/lessons.py b/.claude/skills/tlc-spec-driven/scripts/lessons.py new file mode 100644 index 0000000..963fa1a --- /dev/null +++ b/.claude/skills/tlc-spec-driven/scripts/lessons.py @@ -0,0 +1,370 @@ +#!/usr/bin/env python3 +""" +lessons.py — deterministic bookkeeping for the tlc-spec-driven lessons layer. + +The LLM supplies judgment (which failure happened, how to phrase the lesson, what +signal grounds it). This script owns everything mechanical: IDs, distinct-feature +recurrence counting, candidate->confirmed promotion, pruning, demotion, and +rendering the human/agent-readable playbook. Bookkeeping by hand is exactly what +rots a lessons file, so it lives here, not in a prompt. + +Canonical state: .specs/lessons.json (machine-owned — do NOT hand-edit) +Rendered view: .specs/LESSONS.md (regenerated on every write) + +Pure standard library. No dependencies. Run from the project root (the dir that +contains .specs), or pass --root. + +Commands: + add Record a grounded lesson from a verification signal. + list Print lessons (default: confirmed) for loading at Specify/Design. + penalize Mark a confirmed lesson as having failed when applied (-> quarantine). + prune Drop stale uncorroborated candidates (also runs automatically on add/list). + status Print counts (used by the self-check in validate.md). + init Create empty store + rendered file. + +Exit codes: 0 ok, 2 usage/validation error (e.g. missing grounding). +""" + +import argparse +import datetime as _dt +import json +import os +import re +import sys + +STORE_REL = os.path.join(".specs", "lessons.json") +RENDER_REL = os.path.join(".specs", "LESSONS.md") + +SIGNALS = { + "ac_gap": "Acceptance criterion not covered / failed", + "surviving_mutant": "Discrimination sensor mutant survived (weak test)", + "spec_precision_gap": "Spec did not define a precise outcome", + "spec_deviation": "Implementation diverged from spec/design (SPEC_DEVIATION)", + "gate_fail": "Build-level gate check failed", +} + +DEFAULTS = {"promote_threshold": 2, "window_days": 45, "quarantine_threshold": 2} + + +def _now(): + return _dt.datetime.now(_dt.timezone.utc).strftime("%Y-%m-%dT%H:%M:%SZ") + + +def _parse_date(s): + try: + return _dt.datetime.strptime(s, "%Y-%m-%dT%H:%M:%SZ").replace(tzinfo=_dt.timezone.utc) + except Exception: + return _dt.datetime.now(_dt.timezone.utc) + + +def _store_path(root): + return os.path.join(root, STORE_REL) + + +def _render_path(root): + return os.path.join(root, RENDER_REL) + + +def _load(root): + path = _store_path(root) + if not os.path.exists(path): + return { + "schema": 1, + "promote_threshold": DEFAULTS["promote_threshold"], + "window_days": DEFAULTS["window_days"], + "quarantine_threshold": DEFAULTS["quarantine_threshold"], + "next_id": 1, + "lessons": [], + } + with open(path, "r", encoding="utf-8") as f: + data = json.load(f) + for k, v in DEFAULTS.items(): + data.setdefault(k, v) + data.setdefault("schema", 1) + data.setdefault("next_id", 1) + data.setdefault("lessons", []) + return data + + +def _save(root, data): + os.makedirs(os.path.join(root, ".specs"), exist_ok=True) + with open(_store_path(root), "w", encoding="utf-8") as f: + json.dump(data, f, indent=2, ensure_ascii=False) + f.write("\n") + _render(root, data) + + +def _norm(text): + """Normalized dedup key: lowercase, strip punctuation, collapse whitespace. + Exact-after-normalization only — no semantic matching (stdlib-only limitation). + Phrase lessons tersely and canonically so recurrences actually merge.""" + t = text.lower().strip() + t = re.sub(r"[^a-z0-9\s]", " ", t) + t = re.sub(r"\s+", " ", t).strip() + return t + + +def _key(signal, text): + return signal + "::" + _norm(text) + + +def _auto_prune(data): + """Drop candidates that never recurred within the window. Mutates data.""" + threshold = data["promote_threshold"] + window = data["window_days"] + now = _dt.datetime.now(_dt.timezone.utc) + kept = [] + dropped = [] + for l in data["lessons"]: + if l["status"] == "candidate" and l["recurrence"] < threshold: + age_days = (now - _parse_date(l.get("last_seen", l.get("created", _now())))).days + if age_days > window: + dropped.append(l["id"]) + continue + kept.append(l) + data["lessons"] = kept + return dropped + + +def _find(data, signal, text): + k = _key(signal, text) + for l in data["lessons"]: + if l.get("key") == k: + return l + return None + + +def _render(root, data): + lines = [] + lines.append("# LESSONS — auto-maintained by scripts/lessons.py") + lines.append("") + lines.append("> Machine-owned. Do NOT hand-edit. Changes are overwritten on the next `lessons.py` write.") + lines.append("> Canonical state lives in `.specs/lessons.json`. Edit lessons only via the script.") + lines.append(f"> promote_threshold={data['promote_threshold']} distinct features · window_days={data['window_days']} · quarantine_threshold={data['quarantine_threshold']}") + lines.append("") + + by_status = {"confirmed": [], "candidate": [], "quarantined": []} + for l in data["lessons"]: + by_status.get(l["status"], by_status["candidate"]).append(l) + + def block(title, items, note): + out = [f"## {title}", ""] + if note: + out.append(note) + out.append("") + if not items: + out.append("_none_") + out.append("") + return out + for l in sorted(items, key=lambda x: x["id"]): + scope = f" · scope: `{l['scope']}`" if l.get("scope") else "" + out.append(f"### {l['id']} — {l['text']}") + out.append( + f"- signal: `{l['signal']}` · recurrence: {l['recurrence']} feature(s){scope} · harmful: {l.get('harmful', 0)}" + ) + feats = ", ".join(l.get("features", [])) or "—" + out.append(f"- features: {feats}") + ev = l.get("evidence", []) + if ev: + out.append(f"- evidence: {ev[0]}" + (f" (+{len(ev) - 1} more)" if len(ev) > 1 else "")) + out.append(f"- last seen: {l.get('last_seen', '—')}") + out.append("") + return out + + lines += block( + "Confirmed (load these at Specify/Design)", + by_status["confirmed"], + "Corroborated across multiple features. Safe to apply as guidance.", + ) + lines += block( + "Candidates (under observation — do NOT load as guidance yet)", + by_status["candidate"], + "Seen once or not yet corroborated. Tracked, not trusted.", + ) + lines += block( + "Quarantined (failed when applied — ignore)", + by_status["quarantined"], + "A confirmed lesson that recurred alongside failure. Kept for the maintainer to review.", + ) + + with open(_render_path(root), "w", encoding="utf-8") as f: + f.write("\n".join(lines).rstrip() + "\n") + + +# ----------------------------- commands ----------------------------- + +def cmd_init(root, args): + data = _load(root) + _save(root, data) + print(f"Initialized lessons store at {_store_path(root)} and {_render_path(root)}") + return 0 + + +def cmd_add(root, args): + signal = args.signal + source = (args.source or "").strip() + text = (args.text or "").strip() + feature = (args.feature or "").strip() + + # Grounding is enforced here, deterministically — not left to the prompt. + if signal not in SIGNALS: + print(f"ERROR: --signal must be one of {sorted(SIGNALS)}", file=sys.stderr) + return 2 + if not feature: + print("ERROR: --feature is required (the feature the signal came from).", file=sys.stderr) + return 2 + if not source: + print("ERROR: --source is required (file:line / AC id / mutant id / SPEC_DEVIATION ref).", file=sys.stderr) + print(" A lesson with no grounding in validation.md is an opinion, not a lesson. Refused.", file=sys.stderr) + return 2 + if len(text) < 12: + print("ERROR: --text too short. State the actionable lesson in one terse sentence.", file=sys.stderr) + return 2 + + data = _load(root) + _auto_prune(data) + existing = _find(data, signal, text) + now = _now() + + if existing: + if feature not in existing["features"]: + existing["features"].append(feature) + existing["recurrence"] = len(existing["features"]) + existing["last_seen"] = now + ev = source if not args.scope else f"{source} ({args.scope})" + if ev not in existing["evidence"]: + existing["evidence"].append(ev) + promoted = False + if existing["status"] == "candidate" and existing["recurrence"] >= data["promote_threshold"]: + existing["status"] = "confirmed" + promoted = True + _save(root, data) + msg = f"UPDATED {existing['id']} (recurrence={existing['recurrence']}, status={existing['status']})" + if promoted: + msg += " — PROMOTED to confirmed" + print(msg) + else: + lid = f"L-{data['next_id']:03d}" + data["next_id"] += 1 + data["lessons"].append( + { + "id": lid, + "key": _key(signal, text), + "text": text, + "signal": signal, + "scope": (args.scope or "").strip(), + "status": "candidate", + "features": [feature], + "recurrence": 1, + "harmful": 0, + "evidence": [source if not args.scope else f"{source} ({args.scope})"], + "created": now, + "last_seen": now, + } + ) + _save(root, data) + print(f"ADDED {lid} (status=candidate, recurrence=1)") + return 0 + + +def cmd_penalize(root, args): + data = _load(root) + target = None + for l in data["lessons"]: + if l["id"].lower() == args.id.lower(): + target = l + break + if not target: + print(f"ERROR: no lesson with id {args.id}", file=sys.stderr) + return 2 + target["harmful"] = target.get("harmful", 0) + 1 + target["last_seen"] = _now() + if target["harmful"] >= data["quarantine_threshold"]: + target["status"] = "quarantined" + _save(root, data) + print(f"PENALIZED {target['id']} (harmful={target['harmful']}, status={target['status']})") + return 0 + + +def cmd_list(root, args): + data = _load(root) + if _auto_prune(data): + _save(root, data) + want = args.status + q = (args.query or "").lower().strip() + scope = (args.scope or "").lower().strip() + rows = [] + for l in data["lessons"]: + if want != "all" and l["status"] != want: + continue + if q and q not in l["text"].lower(): + continue + if scope and scope not in (l.get("scope", "").lower()): + continue + rows.append(l) + if not rows: + print(f"(no {want} lessons" + (f" matching '{q or scope}'" if (q or scope) else "") + ")") + return 0 + for l in sorted(rows, key=lambda x: x["id"]): + sc = f" [scope:{l['scope']}]" if l.get("scope") else "" + print(f"{l['id']} ({l['status']}, x{l['recurrence']}){sc}: {l['text']}") + return 0 + + +def cmd_prune(root, args): + data = _load(root) + dropped = _auto_prune(data) + _save(root, data) + print(f"Pruned {len(dropped)} stale candidate(s): {', '.join(dropped) if dropped else '—'}") + return 0 + + +def cmd_status(root, args): + data = _load(root) + counts = {"confirmed": 0, "candidate": 0, "quarantined": 0} + for l in data["lessons"]: + counts[l["status"]] = counts.get(l["status"], 0) + 1 + total = len(data["lessons"]) + print(f"lessons: {total} total | confirmed={counts['confirmed']} candidate={counts['candidate']} quarantined={counts['quarantined']}") + return 0 + + +def main(argv=None): + p = argparse.ArgumentParser(prog="lessons.py", description="Deterministic lessons bookkeeping for tlc-spec-driven.") + p.add_argument("--root", default=".", help="Project root containing .specs/ (default: current dir)") + sub = p.add_subparsers(dest="cmd", required=True) + + sp = sub.add_parser("init", help="Create empty store + rendered file") + sp.set_defaults(fn=cmd_init) + + sp = sub.add_parser("add", help="Record a grounded lesson") + sp.add_argument("--feature", required=True) + sp.add_argument("--signal", required=True, choices=sorted(SIGNALS)) + sp.add_argument("--source", required=True, help="file:line / AC id / mutant id / SPEC_DEVIATION ref") + sp.add_argument("--text", required=True, help="One terse, actionable sentence") + sp.add_argument("--scope", default="", help="Optional: path/layer/tag for retrieval filtering") + sp.set_defaults(fn=cmd_add) + + sp = sub.add_parser("penalize", help="Mark a confirmed lesson as failed-when-applied") + sp.add_argument("--id", required=True) + sp.set_defaults(fn=cmd_penalize) + + sp = sub.add_parser("list", help="Print lessons for loading") + sp.add_argument("--status", default="confirmed", choices=["confirmed", "candidate", "quarantined", "all"]) + sp.add_argument("--query", default="", help="Substring filter on lesson text") + sp.add_argument("--scope", default="", help="Substring filter on scope") + sp.set_defaults(fn=cmd_list) + + sp = sub.add_parser("prune", help="Drop stale uncorroborated candidates") + sp.set_defaults(fn=cmd_prune) + + sp = sub.add_parser("status", help="Print counts") + sp.set_defaults(fn=cmd_status) + + args = p.parse_args(argv) + root = os.path.abspath(args.root) + return args.fn(root, args) + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/GUIA-LINEAR-GITHUB.md b/GUIA-LINEAR-GITHUB.md new file mode 100644 index 0000000..198c971 --- /dev/null +++ b/GUIA-LINEAR-GITHUB.md @@ -0,0 +1,241 @@ +# Guia do Usuário — Linear e integração automática com GitHub + +Este guia explica, do ponto de vista de quem usa no dia a dia, como o time **Painel Vagas (PAV)** trabalha com o [Linear](https://linear.app/tatame/team/PAV/all) e como aproveitar a integração que já está ativa com o GitHub, que movimenta os cards automaticamente conforme o fluxo de desenvolvimento. + +> O objetivo é o **uso**: a integração já está implementada e funcionando. Você não precisa configurar nada — só entender como usá-la a seu favor. + +## Sumário + +- [1. Conceitos básicos do Linear](#1-conceitos-básicos-do-linear) +- [2. Passo a passo: usando suas tasks no fluxo do time](#2-passo-a-passo-usando-suas-tasks-no-fluxo-do-time) +- [3. Como vincular um card a um branch, PR ou commit](#3-como-vincular-um-card-a-um-branch-pr-ou-commit) +- [4. Quais ações no GitHub movem os cards (e para onde)](#4-quais-ações-no-github-movem-os-cards-e-para-onde) +- [5. Exemplo prático de ponta a ponta](#5-exemplo-prático-de-ponta-a-ponta) +- [6. Perguntas frequentes](#6-perguntas-frequentes) + +--- + +## 1. Conceitos básicos do Linear + +O Linear é a ferramenta oficial de gestão de tasks do time. Alguns conceitos que você usa todo dia: + +### Time (Team) + +Um agrupamento de pessoas e trabalho. O nosso é o **Painel Vagas**, cujo prefixo é **`PAV`**. Por isso todo card recebe um identificador como `PAV-93`, `PAV-94`, etc. Esse identificador é a "chave" que conecta o Linear ao GitHub — guarde bem esse conceito, ele aparece na seção 3. + +### Issue / Card + +Uma unidade de trabalho: uma feature, um bug, uma tarefa de documentação. Cada card tem: + +- **Identificador** único (ex: `PAV-93`). +- **Título** e **descrição** (contexto, o que fazer e os **critérios de aceite**). +- **Responsável (assignee)** — quem está tocando o card. +- **Prioridade** e **estado**. + +### Estados (Status) + +O estado indica em que ponto do fluxo o card está. No time PAV os estados são: + +| Estado | Tipo | Significado | +| ----------------------- | ---------- | ----------------------------------------------------------------- | +| **Backlog** | backlog | Ideia/tarefa registrada, ainda não priorizada para execução. | +| **Todo** | unstarted | Priorizada e pronta para começar. | +| **In Progress** | started | Em desenvolvimento ativo. | +| **In Review** | started | Código pronto, aguardando revisão (PR aberto). | +| **Blocked / Dependent** | unstarted | Parada por dependência ou impedimento externo. | +| **Done** | completed | Concluída e integrada. | +| **Canceled** | canceled | Descartada, não será feita. | +| **Duplicate** | duplicate | Repetida de outro card. | + +O fluxo típico de uma task saudável é: + +``` +Backlog → Todo → In Progress → In Review → Done +``` + +A boa notícia: da parte **In Progress → In Review → Done** você quase nunca precisa mover o card na mão — o GitHub faz isso por você (seção 4). + +### Prioridade + +Cada card pode ter uma prioridade: **Urgent, High, Medium, Low** ou **No priority**. Ela orienta a ordem de execução dentro do Backlog/Todo. Ao pegar trabalho, prefira os de maior prioridade. + +### Ciclos (Cycles) + +Ciclos são janelas de tempo (semelhantes a sprints) usadas para organizar o que o time pretende entregar em um período. Cards podem ser associados a um ciclo para dar visibilidade do que está no radar agora versus depois. + +--- + +## 2. Passo a passo: usando suas tasks no fluxo do time + +O fluxo oficial do projeto (alinhado ao [contribuition.md](contribuition.md) e ao README) é: + +1. **Escolher ou criar um card no Linear.** + - No board do PAV, pegue um card em **Todo** (ou **Backlog**, se for algo novo priorizado com o time). + - Para criar um card rápido, você pode usar o comando `/criar-task-linear` no Claude Code. + +2. **Se atribuir o card.** + - Defina você como **assignee**. Isso deixa claro para o time quem está tocando aquilo. + +3. **Iniciar o trabalho criando o branch.** + - Crie um branch de feature **a partir da master**, usando o identificador do card no nome (detalhes na seção 3). + - **Ao criar esse branch, o card sai de Todo/Backlog e vai para `In Progress` automaticamente** (seção 4). Você não precisa arrastar o card. + +4. **Desenvolver e commitar em blocos pequenos.** + - Faça commits coerentes, com o identificador do card na mensagem (ex: `PAV-93 ...`). + - Rode os testes/lint localmente antes de subir (ver [contribuition.md](contribuition.md) e [TESTING.md](TESTING.md)). + +5. **Abrir o Pull Request.** + - Abra o PR do seu fork (`origin`) para o `upstream` na branch **`develop`**. + - Você pode usar o comando `/abrir-pr-jobs-scraper`, que já monta o PR padronizado e inclui o link do card. + - **Com o PR aberto e vinculado, o card avança para `In Review`.** + +6. **Revisão e ajustes.** + - O revisor comenta; você ajusta e atualiza o PR. O card permanece em **In Review** enquanto isso acontece. + +7. **Merge.** + - Após aprovação, o PR é mergeado em `develop`. **O card vai para `Done` automaticamente.** + - Depois, no fluxo de release, abre-se o PR de `develop` para `master` (PRs para `master` só são aceitos a partir de `develop` — isso é garantido pelo workflow `block-master-pr.yml`). + +> **Movimentação manual ainda existe:** você pode arrastar um card entre estados no Linear a qualquer momento (por exemplo, mover de **Backlog** para **Todo**, ou marcar como **Blocked / Dependent** quando ficar travado). A automação do GitHub cuida principalmente das transições **In Progress → In Review → Done**. + +--- + +## 3. Como vincular um card a um branch, PR ou commit + +A integração conecta Linear ↔ GitHub pelo **identificador do card** (ex: `PAV-93`). Sempre que esse identificador aparece em um lugar que a integração observa, o card e o trabalho no GitHub ficam vinculados. Há três formas, e você normalmente usa as três juntas: + +### a) Pelo nome do branch (forma principal) + +Inclua o identificador no nome do branch. Convenção oficial do projeto: + +``` +feature/- +fix/- +chore/- +``` + +Exemplo real deste repositório: + +```bash +git checkout master +git pull origin master +git checkout -b feature/pav-93-doc-linear-github +``` + +> **Dica:** o próprio Linear sugere um nome de branch pronto no card (botão de copiar branch name). Usar o identificador `PAV-93` no nome é o que dispara o vínculo e o "In Progress". + +### b) Pela mensagem de commit + +Coloque o identificador do card na mensagem de commit. Padrão usado no projeto: + +```bash +git commit -m "PAV-93 cria guia de uso do Linear e integração com GitHub" +``` + +Isso deixa o histórico rastreável e reforça o vínculo do trabalho com o card. + +### c) Pelo Pull Request + +Referencie o card no **título** ou na **descrição** do PR. O PR padronizado do projeto já faz isso: + +- **Título:** `PAV-93: ` +- **Descrição:** inclui o **link do card** do Linear. + +Se quiser que o merge **feche** o card automaticamente, use uma palavra-chave de fechamento seguida do identificador na descrição do PR, por exemplo: + +``` +Closes PAV-93 +``` + +> Na prática, no fluxo deste time o PR já é criado com o identificador no título e o link do card no corpo (via `/abrir-pr-jobs-scraper`), o que é suficiente para o Linear reconhecer o vínculo e movimentar o card. + +--- + +## 4. Quais ações no GitHub movem os cards (e para onde) + +Esta é a parte que economiza seu tempo: uma vez que o card está vinculado (seção 3), as ações no GitHub empurram o estado no Linear automaticamente. + +| Ação no GitHub | Estado do card no Linear | +| ------------------------------------------------------ | ------------------------ | +| **Branch criado** com o identificador do card | → **In Progress** | +| **Pull Request aberto** e vinculado ao card | → **In Review** | +| **PR mergeado** (na `develop`) | → **Done** | + +Observações práticas: + +- A transição **Branch criado → In Progress** é o comportamento que você observa neste próprio time: o card `PAV-93`, por exemplo, saiu de **Backlog** e entrou em **In Progress** no exato momento em que seu branch foi criado. +- Enquanto o PR está aberto e recebendo revisão, o card permanece em **In Review** — você não precisa ficar movendo nada durante o vai-e-vem de review. +- O **merge** é o gatilho de conclusão: ao integrar o PR, o card fecha em **Done**. + +> **Nota:** o mapeamento exato de cada ação para cada estado é definido nas configurações da integração GitHub dentro do workspace do Linear (feito pelos administradores). Os estados **In Progress**, **In Review** e **Done** existem no time PAV justamente para suportar esse fluxo. Se alguma transição não acontecer como esperado, confirme se o identificador do card (`PAV-XX`) está presente no branch/PR — é a ausência do identificador o motivo mais comum de o card "não se mover sozinho". + +--- + +## 5. Exemplo prático de ponta a ponta + +Cenário: você vai desenvolver o card **`PAV-93 — Criar documento de guia do usuário sobre Linear e integração automática com GitHub`**. + +**1. No Linear:** o card está em **Todo**. Você se atribui como assignee. + +**2. Cria o branch** a partir da master, com o identificador no nome: + +```bash +git checkout master +git pull origin master +git checkout -b feature/pav-93-doc-linear-github +``` + +➡️ No Linear, o card `PAV-93` muda sozinho de **Todo** para **In Progress**. + +**3. Desenvolve e commita** em blocos, com o identificador na mensagem: + +```bash +git add GUIA-LINEAR-GITHUB.md +git commit -m "PAV-93 cria guia de uso do Linear e integração com GitHub" +``` + +**4. Sobe o branch e abre o PR** para `develop` (ex.: via `/abrir-pr-jobs-scraper`): + +- Título: `PAV-93: Criar documento de guia do usuário sobre Linear e integração automática com GitHub` +- Descrição: resumo das mudanças + **link do card** `PAV-93`. + +➡️ No Linear, o card avança de **In Progress** para **In Review**. + +**5. Revisão:** o revisor pede um ajuste; você corrige, commita e atualiza o PR. O card segue em **In Review**. + +**6. Merge:** aprovado, o PR é mergeado em `develop`. + +➡️ No Linear, o card `PAV-93` fecha automaticamente em **Done**. + +**7. Release:** no momento da release, abre-se o PR de `develop` para `master` (obrigatoriamente a partir de `develop`, conforme o workflow `block-master-pr.yml`). + +Resultado: você tocou o card do início ao fim tendo movido o Linear manualmente **apenas uma vez** (Todo → assignee), enquanto **In Progress**, **In Review** e **Done** foram atualizados sozinhos pela integração. + +--- + +## 6. Perguntas frequentes + +**O card não saiu de Todo quando criei o branch. O que houve?** +Provavelmente o identificador do card não está no nome do branch. Confirme que o branch contém `PAV-XX` (ex: `feature/pav-93-...`). Sem o identificador, a integração não sabe qual card mover. + +**Preciso mover o card na mão em algum momento?** +Sim, nas transições que o GitHub não cobre: tirar do **Backlog** para **Todo**, marcar como **Blocked / Dependent** quando travar, ou **Canceled** se o trabalho for descartado. As transições In Progress → In Review → Done são automáticas. + +**Posso vincular vários commits/PRs ao mesmo card?** +Sim. Basta que todos referenciem o mesmo identificador `PAV-XX`. + +**O que fecha o card em Done: abrir o PR ou o merge?** +O **merge**. Abrir o PR leva o card para **In Review**; é o merge que o conclui em **Done**. + +**Onde vejo o board do time?** +Board oficial do PAV: https://linear.app/tatame/team/PAV/all + +--- + +### Referências internas + +- [README.md](README.md) — visão geral, fluxo de desenvolvimento e branching. +- [contribuition.md](contribuition.md) — processo oficial de contribuição, padrão de branch e commit. +- [TESTING.md](TESTING.md) — guia de testes e política de qualidade. +- Comando `/criar-task-linear` — cria cards no Linear a partir do Claude Code. +- Comando `/abrir-pr-jobs-scraper` — abre PR padronizado já vinculado ao card. diff --git a/README.md b/README.md index 606683a..3d419c9 100644 --- a/README.md +++ b/README.md @@ -12,6 +12,7 @@ O produto evoluiu para um modelo orientado a serviços (API + scraper Go + cache ## Links oficiais - Gestão de produto (Linear): https://linear.app/tatame/team/PAV/all +- Guia do usuário — Linear e integração com GitHub: [GUIA-LINEAR-GITHUB.md](GUIA-LINEAR-GITHUB.md) - Design system oficial (Figma): https://www.figma.com/design/gollJBtK8PGkffNN4zk9t9/Painel-Dev---releitura?node-id=0-1&p=f&t=zU8zrFzPsNPxZ3qU-0 - Documentação backend detalhada: [BACKEND.md](BACKEND.md) - Documentação scraper Go: [SCRAPER.md](SCRAPER.md)