diff --git a/docs/workflow.md b/docs/workflow.md index 6d952ed..6618155 100644 --- a/docs/workflow.md +++ b/docs/workflow.md @@ -35,18 +35,25 @@ Each step has a designated agent and a specific deliverable. No step is skipped. │ │ │ Phase 1 — Project Discovery (once per project) │ │ PO asks stakeholder 7 questions → silent pre-mortem │ -│ → baseline → create backlog/.feature stubs │ +│ → paraphrase + clarify + summarize → stakeholder confirms │ +│ → baseline docs/features/discovery.md │ +│ → create backlog/.feature stubs (discovery section only) │ │ │ │ Phase 2 — Feature Discovery (per feature) │ -│ PO populates Entities table → generates questions from gaps │ -│ → interview rounds → stakeholder says "baseline" │ +│ PO populates Entities table in .feature file description │ +│ → generates questions from gaps, ambiguities, boundaries │ +│ → interview rounds → after each round: │ +│ paraphrase + clarify + summarize → stakeholder confirms │ +│ → stakeholder says "baseline" to freeze discovery │ │ → decomposition check (>2 concerns or >8 examples → split) │ +│ → Status: BASELINED written into .feature file description │ │ │ │ Phase 3 — Stories (PO alone) │ │ Write Rule: blocks with user story headers (no Examples yet) │ │ commit: feat(stories): write user stories for │ │ │ │ Phase 4 — Criteria (PO alone) │ +│ Silent pre-mortem per Rule │ │ Write @id-tagged Example: blocks under each Rule: │ │ commit: feat(criteria): write acceptance criteria for │ │ ★ FROZEN — changes require @deprecated + new Example │ @@ -58,11 +65,14 @@ Each step has a designated agent and a specific deliverable. No step is skipped. ├─────────────────────────────────────────────────────────────────────┤ │ │ │ mv backlog/.feature → in-progress/.feature │ -│ Read discovery + feature file │ +│ Read docs/features/discovery.md (project-level) │ +│ Read ALL backlog .feature files (discovery + entities sections) │ +│ Read in-progress .feature file (full) │ +│ Identify cross-feature entities, shared interfaces, extension pts │ │ Silent pre-mortem (YAGNI/KISS/DRY/SOLID/OC/patterns) │ -│ Append Architecture section to feature file description │ +│ Append Architecture section to in-progress .feature description │ │ (Module Structure + ADRs + Build Changes) │ -│ Architecture contradiction check → PO acknowledges │ +│ Architecture contradiction check — resolve with PO if needed │ │ commit: feat(): add architecture │ │ │ └─────────────────────────────────────────────────────────────────────┘ @@ -94,13 +104,17 @@ Each step has a designated agent and a specific deliverable. No step is skipped. │ ↓ │ │ next test │ │ │ -│ RED: confirm test fails │ +│ RED: confirm test fails │ │ GREEN: minimum code to pass (YAGNI + KISS only) │ │ REFACTOR: DRY → SOLID → Object Calisthenics (9 rules) │ │ → type hints → docstrings │ │ SELF-DECLARE: write ## Self-Declaration block in TODO.md │ -│ 21-item checklist with file:line evidence │ +│ 21-item checklist (YAGNI×2, KISS×2, DRY×2, │ +│ SOLID×5, OC×9, Semantic×1) with file:line evidence │ +│ each item: checked box + evidence, or N/A + reason │ │ REVIEWER: code-design check only (no lint/pyright/coverage) │ +│ reviewer independently verifies YES claims │ +│ reviewer does NOT re-audit self-declared failures │ │ COMMIT: feat(): implement │ │ │ │ After all tests green: │ @@ -142,7 +156,7 @@ Each step has a designated agent and a specific deliverable. No step is skipped. │ │ │ ACCEPTED: │ │ mv in-progress/.feature → completed/.feature │ -│ developer creates PR + tags release │ +│ developer creates PR (squash merge) + tags release │ │ │ │ REJECTED: │ │ feedback in TODO.md → back to relevant step │ @@ -152,13 +166,64 @@ Each step has a designated agent and a specific deliverable. No step is skipped. --- +## Feature File Structure + +Each feature is a single `.feature` file. The free-form description before the first `Rule:` contains all discovery content added progressively through the workflow: + +``` +Feature: + + Discovery: + + Status: BASELINED (YYYY-MM-DD) + + Entities: + | Type | Name | Candidate Class/Method | In Scope | + + Rules (Business): + - <business rule> + + Constraints: + - <non-functional requirement> + + Questions: + | ID | Question | Answer | Status | + + Architecture: ← added at Step 2 by developer + + ### Module Structure + - <package>/domain/entity.py — ... + + ### Key Decisions + ADR-001: <title> + Decision: <what> + Reason: <why> + + Rule: <story title> + As a <role> + I want <goal> + So that <benefit> + + @id:a3f2b1c4 + Example: <scenario> + Given <context> + When <action> + Then <observable outcome> +``` + +Two discovery sources: +- `docs/features/discovery.md` — project-level (Who/What/Why/When, once per project) +- Feature file description — per-feature discovery, entities, questions, architecture + +--- + ## Supporting Tools | Command | When | Purpose | |---|---|---| | `uv run task gen-tests` | Step 3, Step 4 | Reads `.feature` files → creates/syncs test stubs in `tests/features/` | | `uv run task gen-tests -- --check` | Before gen-tests | Dry run — preview what would change | -| `uv run task gen-tests -- --orphans` | Step 5 | List tests with no matching `@id` | +| `uv run task gen-tests -- --orphans` | Step 5 | List tests with no matching `@id` — already validated by gen-tests | | `uv run task gen-todo` | Every session | Reads in-progress `.feature` → syncs `TODO.md` | | `uv run task gen-id` | Step 1 Phase 4 | Generate 8-char hex `@id` for a new Example | | `uv run task test-fast` | Step 4 cycle | Fast test run (no coverage) — used during Red-Green-Refactor | @@ -183,13 +248,13 @@ tests/ --- -## TODO.md Structure (Step 4) +## TODO.md Structure ```markdown # Current Work Feature: <name> -Step: 4 (implement) +Step: <1-6> (<step name>) Source: docs/features/in-progress/<name>.feature ## Cycle State @@ -197,7 +262,27 @@ Test: @id:<hex> — <description> Phase: RED | GREEN | REFACTOR | SELF-DECLARE | REVIEWER(code-design) | COMMITTED ## Self-Declaration (@id:<hex>) -- [x] YAGNI-1 … SOLID-D … OC-1…OC-9 … Semantic (21 items, file:line each) +- [x] YAGNI-1: No abstractions beyond current AC — `file:line` +- [x] YAGNI-2: No speculative parameters or flags — `file:line` +- [x] KISS-1: Every function has one job — `file:line` +- [x] KISS-2: No unnecessary indirection — `file:line` +- [x] DRY-1: No duplicated logic — `file:line` +- [x] DRY-2: Every shared concept in one place — `file:line` +- [x] SOLID-S: One reason to change — `file:line` +- [x] SOLID-O: Extension, not modification — `file:line` or N/A +- [x] SOLID-L: Subtypes fully substitutable — `file:line` or N/A +- [x] SOLID-I: No forced stub methods — `file:line` or N/A +- [x] SOLID-D: Domain depends on Protocols — `file:line` +- [x] OC-1: One indent level per method — `file:line` +- [x] OC-2: No else after return — `file:line` or N/A +- [x] OC-3: No bare primitives as domain concepts — `file:line` or N/A +- [x] OC-4: No bare collections as domain values — `file:line` or N/A +- [x] OC-5: No chained dot navigation — `file:line` or N/A +- [x] OC-6: No abbreviations — `file:line` or N/A +- [x] OC-7: Functions ≤ 20 lines, classes ≤ 50 lines — `file:line` +- [x] OC-8: ≤ 2 instance variables per class — `file:line` +- [x] OC-9: No getters/setters — `file:line` or N/A +- [x] Semantic: test abstraction matches AC level — `file:line` ## Progress - [x] @id:<hex>: <done> — reviewer(code-design) APPROVED @@ -208,6 +293,8 @@ Phase: RED | GREEN | REFACTOR | SELF-DECLARE | REVIEWER(code-design) | COMMITTED <one actionable sentence> ``` +`## Cycle State` is updated at every phase transition. `## Self-Declaration` is replaced per-test cycle. Both sections are present only during Step 4; omit when in other steps. + --- ## Roles @@ -232,7 +319,6 @@ Phase: RED | GREEN | REFACTOR | SELF-DECLARE | REVIEWER(code-design) | COMMITTED | Class length | ≤ 50 lines | | Max nesting | 2 levels | | Instance variables per class | ≤ 2 | -| Uncovered `@id` tags | 0 | | `noqa` comments | 0 | | `type: ignore` comments | 0 | | Orphaned tests | 0 |