diff --git a/.opencode/agents/product-owner.md b/.opencode/agents/product-owner.md index a7974ff..a5ec440 100644 --- a/.opencode/agents/product-owner.md +++ b/.opencode/agents/product-owner.md @@ -21,11 +21,14 @@ You interview the human stakeholder to discover what to build, write Gherkin spe Load `skill run-session` first — it reads FLOW.md, orients you to the current step and feature, and tells you what to do next. +**[STEP-1-BACKLOG-CRITERIA] detection**: If `run-session` detects this state (no file in `in-progress/` AND backlog features with `Status: BASELINED` have no `@id` tags), do **not** treat it as `[IDLE]`. The action is to write `Rule:` blocks and `Example:` blocks with `@id` tags for the BASELINED backlog features. Files stay in `backlog/`. Do NOT move any feature to `in-progress/` during this state. + ## Step Routing | Step | Action | |---|---| -| **Step 1 — SCOPE** | Load `skill define-scope` — contains Stage 1 (Discovery sessions) and Stage 2 (Stories + Criteria). At the end of Stage 2 Step B (criteria), write the `## Self-Declaration` block into `FLOW.md` before committing — every DISAGREE is a hard blocker. | +| **[STEP-1-BACKLOG-CRITERIA]** | Write `Rule:` + `Example:` blocks with `@id` tags for all BASELINED backlog features. Commit per feature: `feat(criteria): write acceptance criteria for `. Files stay in `backlog/`. | +| **Step 1 — SCOPE** | Load `skill define-scope` — contains Stage 1 (Discovery sessions) and Stage 2 (Stories + Criteria). At the end of Stage 2 Step B (criteria), write the `## Self-Declaration` block as a verbal declaration before committing — every DISAGREE is a hard blocker. | | **Step 5 — ACCEPT** | See acceptance protocol below | ## Ownership Rules @@ -42,8 +45,8 @@ After the system-architect approves (Step 4): 1. Run or observe the feature yourself. If user interaction is involved, interact with it. A feature that passes all tests but doesn't work for a real user is rejected. 2. Review the working feature against the original user stories (`Rule:` blocks in the `.feature` file). -3. **If accepted**: move `docs/features/in-progress/.feature` → `docs/features/completed/.feature`; update FLOW.md; notify stakeholder. The stakeholder decides when to trigger PR and release. The system-architect creates the PR; the stakeholder (or their delegate) creates the release when requested. -4. **If rejected**: write specific feedback in FLOW.md, send back to the relevant step. +3. **If accepted**: move `docs/features/in-progress/.feature` → `docs/features/completed/.feature`; update `WORK.md` (`@state: STEP-5-MERGE`, append to Session Log); notify stakeholder. The stakeholder decides when to trigger PR and release. The system-architect creates the PR; the stakeholder (or their delegate) creates the release when requested. +4. **If rejected**: write specific feedback in `WORK.md` (Session Log + `Next:` line pointing to the failing step), send back to the relevant step. ## Handling Gaps @@ -61,7 +64,7 @@ When a gap is reported (by software-engineer or system-architect): When a defect is reported against any feature: 1. Add a `@bug` Example to the relevant `Rule:` block in the `.feature` file using the standard `Given/When/Then` format describing the correct behavior. -2. Update FLOW.md to note the new bug Example for the SE to implement. +2. Update `WORK.md` Session Log to note the new bug Example; set `Next:` to `Run @software-engineer — implement @bug Example in `. 3. SE implements the test in `tests/features/` **and** a `@given` Hypothesis property test in `tests/unit/`. Both are required. ## Available Skills diff --git a/.opencode/agents/software-engineer.md b/.opencode/agents/software-engineer.md index b271ccc..a58a148 100644 --- a/.opencode/agents/software-engineer.md +++ b/.opencode/agents/software-engineer.md @@ -53,14 +53,14 @@ Load `skill run-session` first — it reads FLOW.md, orients you to the current If `docs/features/in-progress/` contains only `.gitkeep` (no `.feature` file): 1. Do not pick a feature from backlog yourself. -2. Update FLOW.md: `Next: Run @product-owner — load skill select-feature and pick the next BASELINED feature from backlog.` +2. Update `WORK.md` `Next:` line: `Run @product-owner — load skill select-feature and pick the next BASELINED feature from backlog.` 3. Stop. The PO must move the chosen feature into `in-progress/` before you can begin Step 3. ## Spec Gaps If during implementation you discover behavior not covered by existing acceptance criteria: - Do not extend criteria yourself — escalate to the PO -- Note the gap in FLOW.md under `## Next` +- Note the gap in `WORK.md` `Next:` line and Session Log ## Available Skills diff --git a/.opencode/agents/system-architect.md b/.opencode/agents/system-architect.md index bc78c9a..1457029 100644 --- a/.opencode/agents/system-architect.md +++ b/.opencode/agents/system-architect.md @@ -44,16 +44,16 @@ Load `skill run-session` first — it reads FLOW.md, orients you to the current ## Ownership Rules - You own all architectural decisions: module structure, domain model, interfaces, Protocols, patterns -- You own `docs/domain-model.md`, `docs/system.md`, and `docs/adr/ADR-*.md` — create and update these at Step 2 +- You own `docs/system.md` (including the `## Domain Model` section) and `docs/adr/ADR-*.md` — create and update these at Step 2 - You review implementation at Step 4 to ensure architectural decisions were respected - **PO approves**: new runtime dependencies, changed entry points, scope changes -- **You never move `.feature` files.** The PO is the sole owner of all feature file moves. If you find no `.feature` file in `docs/features/in-progress/`, **STOP** — do not self-select a feature. Write the gap in FLOW.md and escalate to PO. +- **You never move `.feature` files.** The PO is the sole owner of all feature file moves. If you find no `.feature` file in `docs/features/in-progress/`, **STOP** — do not self-select a feature. Update `WORK.md` `Next:` and escalate to PO. ## Step 2 → Step 3 Handoff After architecture is complete and test stubs are generated: 1. Commit all changes on `feat/` -2. Update FLOW.md: `Next: Run @software-engineer — Step 3 TDD Loop` +2. Update `WORK.md`: set `@state: STEP-3-WORKING`, append to Session Log, set `Next: Run @software-engineer — Step 3 TDD Loop` 3. Stop. The SE takes over for implementation. ## Step 4 Review Stance @@ -67,7 +67,7 @@ Your default hypothesis is that the code is broken despite passing automated che If during Step 2 or Step 4 you discover behavior not covered by existing acceptance criteria: - Do not extend criteria yourself — escalate to the PO -- Note the gap in FLOW.md under `## Next` +- Note the gap in `WORK.md` `Next:` line and Session Log ## Available Skills @@ -76,4 +76,3 @@ If during Step 2 or Step 4 you discover behavior not covered by existing accepta - `verify` — Step 4: adversarial technical review - `create-pr` — Step 5: create and merge PR after PO acceptance - `apply-patterns` — on-demand when smell detected during architecture or review -- `create-skill` — meta: create new skills when needed diff --git a/.opencode/skills/architect/SKILL.md b/.opencode/skills/architect/SKILL.md index b4a2ffd..55f005b 100644 --- a/.opencode/skills/architect/SKILL.md +++ b/.opencode/skills/architect/SKILL.md @@ -1,7 +1,7 @@ --- name: architect description: Step 2 — Architecture and domain design, one feature at a time -version: "1.0" +version: "2.0" author: system-architect audience: system-architect workflow: feature-lifecycle @@ -9,7 +9,7 @@ workflow: feature-lifecycle # Architect -Step 2: design the domain model, write architecture stubs, record decisions, and generate test stubs. The system-architect owns this step entirely. +Step 2: conduct the architectural interview, design the domain model, write architecture stubs, record decisions as ADRs, and generate test stubs. The system-architect owns this step entirely. ## When to Use @@ -43,35 +43,38 @@ Design correctness is far more important than lint/pyright/coverage compliance. 2. Confirm directory exists: `ls /` 3. All new source files go under `/` -**Note on feature file moves**: The PO moves `.feature` files between folders. The system-architect never moves, creates, or edits `.feature` files. Update FLOW.md `Feature:` and `Source:` to reflect `in-progress/` once the PO has moved the file. +**Note on feature file moves**: The PO moves `.feature` files between folders. The system-architect never moves, creates, or edits `.feature` files. Verify `WORK.md` has the correct `@id` and `@branch` set before beginning architecture work. ### Read Phase (targeted reads only — before writing anything) -1. Read `docs/system.md` — understand current system structure and constraints +1. Read `docs/system.md` — all sections: domain model, Context, Container, module structure, constraints, ADR index 2. Read `docs/glossary.md` if it exists — use existing domain terms when naming classes, methods, and modules; do not invent synonyms 3. Read in-progress `.feature` file (full: Rules + Examples + @id) 4. Run `tree /` — understand package structure without reading every file 5. Read **specific `.py` files** whose names match nouns from the feature — understand what already exists before adding anything. Do not read the entire package. -### Domain Analysis +--- -From `docs/glossary.md` + Rules (Business) in the `.feature` file: -- **Nouns** → candidate classes, value objects, aggregates -- **Verbs** → method names with typed signatures -- **Datasets** → named types (not bare dict/list) -- **Bounded Context check**: same word, different meaning across features? → module boundary -- **Cross-feature entities** → candidate shared domain layer +## Architectural Interview Protocol -### Create / Update Domain Model +The arch interview surfaces decisions that must be recorded as ADRs. Each unresolved question becomes one ADR. -**If `docs/domain-model.md` does not exist**: create it from the domain analysis using the template in `domain-model.md.template` in the `implement` skill's directory. +### Gap-Finding Techniques -**If `docs/domain-model.md` exists**: append new entities, verbs, and relationships discovered in this feature. Deprecate old entries if they are superseded. Never edit existing live entries — code depends on them. +Three techniques surface decisions the feature file has not yet made explicit. Apply them during the domain analysis pass. -This file is system-architect-owned. The PO reads it but never writes to it. +**Critical Incident Technique (CIT) — Flanagan 1954** +Ask about a specific failure scenario rather than a general description. +- "If this entity is misused, what breaks?" +- "Tell me about a concrete case where this boundary would be crossed." -### Silent Pre-mortem (before writing anything) +**Laddering / Means-End Chain — Reynolds & Gutman 1988** +Climb from surface constraint to architectural consequence. +- "Why does this need to be immutable?" +- "What breaks if this is not behind a Protocol?" +- Stop when the answer produces a design constraint that can be written into an ADR. +**Silent Pre-mortem (before writing anything)** > "In 6 months this design is a mess. What mistakes did we make?" For each candidate class: @@ -86,7 +89,67 @@ For each noun: If pattern smell detected, load `skill apply-patterns`. -### Write Stubs into Package +### ADR Interview Pattern + +For each unresolved decision identified during domain analysis: + +1. **Frame the question**: state the decision as a clear question with known alternatives. + Example: "Should `FrameworkAdapter` be a `typing.Protocol` or an ABC?" + +2. **State constraints**: list what is known from the feature file, glossary, and existing ADRs that constrains the answer. + +3. **Evaluate alternatives**: for each option, state the consequence. Apply laddering to surface hidden consequences. + +4. **Record the decision**: write one ADR per question. Use the template in `adr.md.template`. + - `## Context` — the question + constraints that produced it + - `## Decision` — one sentence + - `## Reason` — one sentence + - `## Alternatives Considered` — rejected options with reasons + - `## Consequences` — (+) and (-) outcomes + +5. **Commit each ADR** as it is finalized: `feat(): add ADR-` + +Only create an ADR for non-obvious decisions with meaningful trade-offs. Routine YAGNI choices do not need a record. + +--- + +## Domain Analysis + +From `docs/glossary.md` + Rules (Business) in the `.feature` file: +- **Nouns** → candidate classes, value objects, aggregates +- **Verbs** → method names with typed signatures +- **Datasets** → named types (not bare dict/list) +- **Bounded Context check**: same word, different meaning across features? → module boundary +- **Cross-feature entities** → candidate shared domain layer + +### Update Domain Model (in `docs/system.md`) + +Update the `## Domain Model` section of `docs/system.md`: + +- **New feature, first entities**: add bounded contexts, entities, verbs, and relationships to the Domain Model section. +- **Existing feature**: append new entities and verbs. Deprecate old entries if superseded — move them to a `### Deprecated` subsection. Never edit existing live entries — code depends on them. +- Update the `## Context` and `## Container` sections if new actors, external systems, or containers are identified. + +The PO reads `docs/system.md` but never writes to it. + +### Architecture Smell Check (hard gate) + +Apply to the stub files just written: + +- [ ] No class with >2 responsibilities (SOLID-S) +- [ ] No behavioural class with >2 instance variables (OC-8; dataclasses, Pydantic models, value objects, and TypedDicts are exempt) +- [ ] All external deps assigned a Protocol (SOLID-D + Hexagonal) — N/A if no external dependencies identified in scope +- [ ] No noun with different meaning across modules (DDD Bounded Context) +- [ ] No missing Creational pattern: repeated construction without Factory/Builder +- [ ] No missing Structural pattern: type-switching without Strategy/Visitor +- [ ] No missing Behavioral pattern: state machine or scattered notification without State/Observer +- [ ] Each ADR consistent with each @id AC — no contradictions + +If any check fails: fix the stub files before committing. + +--- + +## Write Stubs into Package From the domain analysis, write or extend `.py` files in `/`. For each entity: @@ -123,36 +186,9 @@ class UserRepository(Protocol): Place stubs where responsibility dictates — do not pre-create `ports/` or `adapters/` folders unless a concrete external dependency was identified in scope. Structure follows domain analysis, not a template. -### Record Architectural Decisions - -For each significant decision, create a new file: - -```bash -docs/adr/ADR-YYYY-MM-DD-.md -``` - -Use the template in `adr.md.template` in the `implement` skill's directory. Fill in Decision, Reason, Alternatives Considered, and Consequences. - -Only create an ADR for non-obvious decisions with meaningful trade-offs. Routine YAGNI choices do not need a record. - -Reference relevant ADRs from `docs/system.md` so other agents know which decisions affect the current system state. - -### Architecture Smell Check (hard gate) - -Apply to the stub files just written: - -- [ ] No class with >2 responsibilities (SOLID-S) -- [ ] No behavioural class with >2 instance variables (OC-8; dataclasses, Pydantic models, value objects, and TypedDicts are exempt) -- [ ] All external deps assigned a Protocol (SOLID-D + Hexagonal) — N/A if no external dependencies identified in scope -- [ ] No noun with different meaning across modules (DDD Bounded Context) -- [ ] No missing Creational pattern: repeated construction without Factory/Builder -- [ ] No missing Structural pattern: type-switching without Strategy/Visitor -- [ ] No missing Behavioral pattern: state machine or scattered notification without State/Observer -- [ ] Each ADR consistent with each @id AC — no contradictions - -If any check fails: fix the stub files before committing. +--- -### Generate Test Stubs +## Generate Test Stubs Run `uv run task test-fast` once. It reads the in-progress `.feature` file, assigns `@id` tags to any untagged `Example:` blocks (writing them back to the `.feature` file), and generates `tests/features//_test.py` — one file per `Rule:` block, one skipped function per `@id`. Verify the files were created, then stage all changes (including any `@id` write-backs to the `.feature` file). @@ -165,7 +201,7 @@ Commit: `feat(): add architecture and test stubs` - Feature file path - Summary of stubs created - Any ADRs that constrain implementation - - Any domain-model changes + - Any domain model changes in `system.md` 3. Stop. The SE takes over. --- @@ -174,18 +210,17 @@ Commit: `feat(): add architecture and test stubs` If during architecture you discover behavior not covered by existing acceptance criteria: - **Do not extend criteria yourself** — escalate to PO -- Note the gap in FLOW.md under `## Next` +- Note the gap in `WORK.md` Next: line and Session Log - The PO will decide whether to add a new Example to the `.feature` file --- ## Templates -Templates for files written by this skill live in the `implement` skill's directory: +Templates for files written by this skill live in this skill's directory (`architect/`): -- `domain-model.md.template` — `docs/domain-model.md` structure -- `system.md.template` — `docs/system.md` structure -- `adr.md.template` — individual ADR file structure +- `system.md.template` — `docs/system.md` structure (domain model + Context + Container sections included) +- `adr.md.template` — individual ADR file structure (includes `## Context` section) Base directory for this skill: file:///home/user/Documents/projects/python-project-template/.opencode/skills/architect Relative paths in this skill (e.g., scripts/, reference/) are relative to this base directory. diff --git a/.opencode/skills/create-skill/SKILL.md b/.opencode/skills/create-skill/SKILL.md index 049f899..a929646 100644 --- a/.opencode/skills/create-skill/SKILL.md +++ b/.opencode/skills/create-skill/SKILL.md @@ -135,13 +135,13 @@ Add the skill name to the agent's "Available Skills" section so the agent knows | `run-session` | all agents | Session start/end protocol | | `select-feature` | product-owner | Score and select next backlog feature (WSJF) | | `define-scope` | product-owner | Step 1: define acceptance criteria | -| `implement` | software-engineer | Steps 2-3: architecture + TDD loop | +| `implement` | software-engineer | Step 3: TDD loop | | `apply-patterns` | software-engineer | Steps 2, 3: refactor when smell detected | | `verify` | system-architect | Step 4: adversarial verification | | `check-quality` | software-engineer | Quick reference — redirects to verify | | `create-pr` | system-architect | Step 5: create PR with --no-ff merge | | `git-release` | stakeholder | Step 5: calver versioning and release | -| `update-docs` | product-owner | Step 5 (after acceptance) + on stakeholder demand: C4 diagrams + glossary | +| `update-docs` | system-architect | post-acceptance + on stakeholder demand: Context, Container sections, and glossary | | `design-colors` | designer | Color palette selection and WCAG validation | | `design-assets` | designer | SVG visual asset creation and updates | | `create-skill` | software-engineer | Create new skills | diff --git a/.opencode/skills/define-scope/SKILL.md b/.opencode/skills/define-scope/SKILL.md index 7c93c7d..58f0d3b 100644 --- a/.opencode/skills/define-scope/SKILL.md +++ b/.opencode/skills/define-scope/SKILL.md @@ -79,7 +79,7 @@ Discovery is a continuous, iterative process. Sessions happen whenever scope nee 1. Check `docs/scope_journal.md` for the most recent session block. - If the most recent block has `Status: IN-PROGRESS` → the previous session was interrupted. Resume it: check which `.feature` files need updating (compare journal Q&A against current `.feature` descriptions), write the `discovery.md` synthesis block if missing, then mark the block `Status: COMPLETE`. Only then begin a new session. - If `docs/scope_journal.md` does not exist → this is the first session. Create both `docs/scope_journal.md` and `docs/discovery.md` using the templates in `scope-journal.md.template` and `discovery.md.template` in this skill's directory. -2. Read `docs/domain-model.md` (if it exists) to check existing entities. The PO reads this file but never writes to it. If it does not exist yet, the SA will create it at Step 2. +2. Read the `## Domain Model` section of `docs/system.md` (if the file exists) to check existing entities. The PO reads this section but never writes to `system.md` — it is SA-owned. If `system.md` does not yet have a Domain Model section, the SA will add it at Step 2. 3. Declare session scope to the stakeholder: announce the total groups and estimated question count (e.g., "3 groups: General (7 Q), Cross-cutting, Feature: login"). 4. Open `docs/scope_journal.md` and append a new session header: ```markdown @@ -140,7 +140,7 @@ Target behavior groups, bounded contexts, integration points, lifecycle events, **3. Feature questions** (one feature at a time) For each feature the session touches: -- Extract relevant nouns and verbs from `docs/glossary.md` and `docs/domain-model.md` (if they exist) +- Extract relevant nouns and verbs from `docs/glossary.md` and the `## Domain Model` section of `docs/system.md` (if it exists) - Generate questions from entity gaps: boundaries, edge cases, interactions, failure modes - Run a silent pre-mortem: "Imagine the developer builds this feature exactly as described, all tests pass, but the feature doesn't work for the user. What would be missing?" - Apply CIT, Laddering, and CI Perspective Change per question @@ -185,13 +185,13 @@ Group headers use this format: **Step B — Update glossary and discovery.md** -1. Update `docs/glossary.md` (new or corrected definitions; edits allowed). +1. Update `docs/glossary.md` **after** the session closes — batch update, not real-time during the interview. Read `glossary.md` before the session starts to anchor interview language; update it after all Q&A is complete. New or corrected definitions; edits allowed. 2. Append to `docs/discovery.md` (use the template in `discovery.md.template`): - - 3-line session summary (general/behavioral focus) - - Entities **added or deprecated** this session (suggestions for the SE; not a formal model) - - Features **touched** this session + 1-line reason why + - One `## Session YYYY-MM-DD` block per session + - Summary paragraph (3 lines max; general/behavioral focus) + - `| Feature | Change | Source questions | Reason |` table — one row per `.feature` file that was created or updated this session. **Confirmations (no file change) → no row.** Source questions reference journal Q-IDs (e.g. `C4, I2`). -The PO does **not** write `docs/domain-model.md`. Entity suggestions live in `discovery.md` for the SA to formalize at Step 2. +The PO does **not** write `docs/system.md`. Entity and domain model updates are SA-owned and happen at Step 2. **Step C — Update .feature descriptions** @@ -393,7 +393,7 @@ The **Rules (Business)** section captures business rules that hold across multip The **Constraints** section captures non-functional requirements. Testable constraints should become `Example:` blocks with `@id` tags. What is **not** in `.feature` files: -- Entities table — domain model lives in `docs/domain-model.md` (SE-owned) +- Domain model or entities — domain model lives in the `## Domain Model` section of `docs/system.md` (SA-owned) - Session Q&A blocks — live in `docs/scope_journal.md` - Architecture section — lives in `docs/adr/ADR-*.md` @@ -428,7 +428,7 @@ Stakeholder reports a feature is wrong after PO acceptance attempt. ``` 4. **PO scans `docs/post-mortem/`**, selects relevant files by `` or `` in filename. 5. **PO reads selected post-mortems** for context before handoff. -6. **PO resets FLOW.md**: Status to [STEP-2-ARCH], `Next: Run @system-architect — restart Step 2 for on fix/ with post-mortem context`. +6. **PO updates `WORK.md`**: set `@state: STEP-2-ARCH`, `@branch: fix/`; append to Session Log; set `Next: Run @system-architect — restart Step 2 for on fix/ with post-mortem context`. 7. **SA begins Step 2** on `fix/`, reading relevant post-mortems as input. ### Document Format diff --git a/.opencode/skills/define-scope/discovery.md.template b/.opencode/skills/define-scope/discovery.md.template index e07dbb7..e975155 100644 --- a/.opencode/skills/define-scope/discovery.md.template +++ b/.opencode/skills/define-scope/discovery.md.template @@ -2,23 +2,17 @@ > Append-only session synthesis log. > Written by the product-owner at the end of each discovery session. -> Each block summarizes one session: what was learned, what entities were suggested, and which features were touched. +> Each block records one session: a summary paragraph and a table of features whose behavior changed. +> A row appears only when a `.feature` file would be updated as a result of the session. +> Confirmations of existing behavior are not recorded here — see `docs/scope_journal.md` for the full Q&A. > Never edit past blocks — later blocks extend or supersede earlier ones. --- -## Session: YYYY-MM-DD +## Session YYYY-MM-DD -### Summary -<3-line synthesis of the session: what was discussed, what decisions were made, what new information emerged.> +**Summary**: <3-line synthesis: what was discussed, what decisions were made, what new information emerged.> -### Entities Added or Deprecated -| Action | Type | Name | Notes | -|--------|------|------|-------| -| Added | Noun | | | -| Deprecated | Verb | | | -(Write "No changes" if no entities were added or deprecated this session.) - -### Features Touched -- `` — -(Write "No changes" if no features were added or modified this session.) +| Feature | Change | Source questions | Reason | +|---------|--------|-----------------|--------| +| `` | created \| updated \| deprecated | : "" → | | diff --git a/.opencode/skills/define-scope/glossary.md.template b/.opencode/skills/define-scope/glossary.md.template new file mode 100644 index 0000000..c7cdc7a --- /dev/null +++ b/.opencode/skills/define-scope/glossary.md.template @@ -0,0 +1,19 @@ +# Glossary: + +> Living glossary of domain terms. +> Written and maintained by the product-owner. +> Terms are added after each discovery session, updated when meaning changes. +> If code or tests diverge from a term here, refactor the code — not the glossary. + +--- + +## + + + +--- + +## + + diff --git a/.opencode/skills/git-release/SKILL.md b/.opencode/skills/git-release/SKILL.md index 62d701c..6f3794d 100644 --- a/.opencode/skills/git-release/SKILL.md +++ b/.opencode/skills/git-release/SKILL.md @@ -100,11 +100,11 @@ Add at the top. If a release name was generated in Step 0, include it; otherwise ### 5. Update living docs -Run the `update-docs` skill to reflect the newly accepted feature in C4 diagrams and the glossary. This step runs inline — do not commit separately. +Run the `update-docs` skill to reflect the newly accepted feature in the Context and Container sections and the glossary. This step runs inline — do not commit separately. Load and execute the full `update-docs` skill now: -- Update `docs/context.md` (C4 Level 1) -- Update `docs/container.md` (C4 Level 2, if multi-container) +- Update `## Context` section in `docs/system.md` +- Update `## Container` section in `docs/system.md` (if multi-container) - Update `docs/glossary.md` (living glossary) The `update-docs` commit step is **skipped** here — all changed files are staged together with the version bump in step 6. @@ -116,7 +116,7 @@ After updating `pyproject.toml`, regenerate the lockfile — CI runs `uv sync -- ```bash uv lock git add pyproject.toml /__init__.py CHANGELOG.md uv.lock \ - docs/context.md docs/container.md docs/glossary.md + docs/system.md docs/glossary.md git commit -m "chore(release): bump version to v{version}[ - {Release Name}]" # Include " - {Release Name}" only if a release name was generated in Step 0; omit otherwise. ``` @@ -181,7 +181,7 @@ The release notes and title do not need to change — only the target commit mov - [ ] `uv lock` run after version bump — lockfile must be up to date - [ ] `/__version__` matches `pyproject.toml` version - [ ] CHANGELOG.md updated -- [ ] `update-docs` skill run — C4 diagrams and glossary reflect the new feature +- [ ] `update-docs` skill run — Context, Container sections, and glossary reflect the new feature - [ ] Release name not used before - [ ] Release notes follow the template format - [ ] If a hotfix was pushed after the tag: tag reassigned to hotfix commit diff --git a/.opencode/skills/implement/SKILL.md b/.opencode/skills/implement/SKILL.md index 533370b..793ad01 100644 --- a/.opencode/skills/implement/SKILL.md +++ b/.opencode/skills/implement/SKILL.md @@ -75,7 +75,7 @@ INNER LOOP ├── uv run task test-fast after each individual change └── EXIT: test-fast passes; no smells remain -Mark @id completed in FLOW.md Session Log +Mark @id completed in WORK.md Session Log Commit when a meaningful increment is green ``` @@ -84,7 +84,7 @@ Commit when a meaningful increment is green ```bash uv run task lint uv run task static-check -uv run task test-coverage # coverage must be 100% +uv run task test # coverage must be 100% timeout 10s uv run task run ``` @@ -295,9 +295,8 @@ class UserRepository(Protocol): Templates for architecture files live in the `architect` skill's directory: -- `domain-model.md.template` — `docs/domain-model.md` structure -- `system.md.template` — `docs/system.md` structure -- `adr.md.template` — individual ADR file structure +- `system.md.template` — `docs/system.md` structure (includes `## Domain Model`, `## Context`, `## Container` sections) +- `adr.md.template` — individual ADR file structure (includes `## Context` section) Base directory for this skill: file:///home/user/Documents/projects/python-project-template/.opencode/skills/implement Relative paths in this skill (e.g., scripts/, reference/) are relative to this base directory. diff --git a/.opencode/skills/implement/adr.md.template b/.opencode/skills/implement/adr.md.template index 3670892..80b0bb6 100644 --- a/.opencode/skills/implement/adr.md.template +++ b/.opencode/skills/implement/adr.md.template @@ -1,23 +1,36 @@ # ADR: > Architectural Decision Record -> Written by the software-engineer during Step 2 for non-obvious decisions with meaningful trade-offs. +> Written by the system-architect during Step 2 for non-obvious decisions with meaningful trade-offs. > Routine YAGNI choices do not need a record. | Field | Value | |-------|-------| | **Date** | YYYY-MM-DD | | **Feature** | | -| **Status** | Proposed | Accepted | Superseded | +| **Status** | Proposed \| Accepted \| Superseded | + +## Context + +**Question ():** + +<1–3 sentences: what was known, what was asked, what constraints or stakeholder input produced the decision> + +--- ## Decision + ## Reason + ## Alternatives Considered - + +- ****: rejected — ## Consequences - + +- (+) +- (-) diff --git a/.opencode/skills/implement/domain-model.md.template b/.opencode/skills/implement/domain-model.md.template deleted file mode 100644 index 5df4e1a..0000000 --- a/.opencode/skills/implement/domain-model.md.template +++ /dev/null @@ -1,37 +0,0 @@ -# Domain Model: - -> Living reference of code-facing domain entities. -> Owned by the software-engineer. Created and updated at Step 2. -> The product-owner reads this file to check existing entities during discovery, but never writes to it. -> Append-only: add new entries at the bottom. Deprecate old entries by moving them to the Deprecated section. -> Never edit existing live entries — code depends on them. - ---- - -## Entities - -| Name | Type | Description | Bounded Context | First Appeared | -|------|------|-------------|-----------------|----------------| -| | Entity | | | | -| | Value Object | | | | -| | Aggregate | | | | - -## Verbs - -| Name | Actor | Object | Description | First Appeared | -|------|-------|--------|-------------|----------------| -| | | | | | - -## Relationships - -| Subject | Relation | Object | Cardinality | Notes | -|---------|----------|--------|-------------|-------| -| | | | <1:1 / 1:N / M:N> | | - ---- - -## Deprecated - -| Name | Type | Deprecated Date | Replaced By | Reason | -|------|------|-----------------|-------------|--------| -| | Entity | YYYY-MM-DD | | | diff --git a/.opencode/skills/implement/system.md.template b/.opencode/skills/implement/system.md.template index 05fb2b6..a492697 100644 --- a/.opencode/skills/implement/system.md.template +++ b/.opencode/skills/implement/system.md.template @@ -1,27 +1,143 @@ # System Overview: > Current-state description of the production system. -> Rewritten by the software-engineer at Step 2 for each feature cycle. +> Rewritten by the system-architect at Step 2 for each feature cycle. > Reviewed by the product-owner at Step 5. > Contains only completed features — nothing from backlog or in-progress. +--- + ## Summary + <3–5 sentence description of what the system currently does, who uses it, and its primary boundaries.> +--- + ## Actors -- `` — (from completed features) -## Modules / Components -- `` — (from completed features and ADRs) +| Actor | Needs | +|-------|-------| +| `` | | + +--- + +## Structure + +| Module | Responsibility | +|--------|----------------| +| `` | | + +--- + +## Key Decisions + +- + +--- + +## Configuration Keys + +| Key | Type | Default | Description | +|-----|------|---------|-------------| +| `` | string | `""` | | + +--- ## External Dependencies -- `` — (from ADRs) -## Constraints +| Dependency | What it provides | Why not replaced | +|------------|------------------|-----------------| +| `` | | | + +--- + +## Active Constraints + - -## Relevant ADRs -- ADR-YYYY-MM-DD- (only ADRs affecting current system state) +--- + +## Domain Model + +### Bounded Contexts + +| Context | Responsibility | Key Modules | +|---------|----------------|-------------| +| `` | | `//` | + +### Entities + +| Name | Type | Description | Bounded Context | +|------|------|-------------|-----------------| +| `` | Entity | | | +| `` | Value Object | | | + +### Verbs + +| Name | Actor | Object | Description | +|------|-------|--------|-------------| +| `` | | | | + +### Relationships + +| Subject | Relation | Object | Cardinality | Notes | +|---------|----------|--------|-------------|-------| +| `` | | `` | <1:1 / 1:N / M:N> | | + +### Module Dependency Graph + +``` + ──► ──► () +``` + +--- + +## Context + +```mermaid +C4Context + title System Context — + + Person(, "", "") + + System(, "", "") + + System_Ext(, "", "") + + Rel(, , " - +
🗺️
-
Context Diagram
-
C4 Level 1 — system boundaries and external actors
+
C4 Diagrams
+
Context and Container diagrams — inside System Overview
📋
diff --git a/docs/scope_journal.md b/docs/scope_journal.md index 6fe6902..24d7f0b 100644 --- a/docs/scope_journal.md +++ b/docs/scope_journal.md @@ -1,32 +1,38 @@ -# Scope Journal: +# Scope Journal: temple8 --- -## YYYY-MM-DD — Session 1 +## 2026-04-22 — Session 1 + Status: IN-PROGRESS ### General | ID | Question | Answer | |----|----------|--------| -| Q1 | Who are the users? | ... | -| Q2 | What does the product do at a high level? | ... | -| Q3 | Why does it exist — what problem does it solve? | ... | -| Q4 | When and where is it used? | ... | -| Q5 | Success — what does "done" look like? | ... | -| Q6 | Failure — what must never happen? | ... | -| Q7 | Out-of-scope — what are we explicitly not building? | ... | +| Q1 | Who are the users? | Python engineers starting a new project who want rigorous tooling without the setup cost. | +| Q2 | What does the product do at a high level? | Provides a fully configured Python project skeleton: CI, quality tooling, test infrastructure, and an AI-assisted five-step delivery workflow. | +| Q3 | Why does it exist — what problem does it solve? | Setting up a production-grade Python environment from scratch is expensive and often skipped; engineers then accrue quality debt from day one. | +| Q4 | When and where is it used? | At project inception — cloned once, then evolved as features are added via the built-in workflow. | +| Q5 | Success — what does "done" look like? | An engineer clones the template and ships a meaningful first feature within a single session, with all quality gates passing. | +| Q6 | Failure — what must never happen? | The template introduces more friction than it removes, or locks engineers into choices they cannot override. | +| Q7 | Out-of-scope — what are we explicitly not building? | Runtime infrastructure (databases, queues, cloud deployment), UI frameworks, domain-specific business logic. | -### +### Runtime Behaviour | ID | Question | Answer | |----|----------|--------| -| Q8 | ... | ... | +| Q8 | Should the template ship with any working feature, or be purely empty? | It should ship with exactly one working demonstration feature so engineers see the full workflow end-to-end. | +| Q9 | What is the simplest useful feature for that demonstration? | Displaying the application version read from `pyproject.toml` — it exercises the full stack with no external dependencies. | -### Feature: +### Feature: display-version | ID | Question | Answer | |----|----------|--------| -| Q9 | ... | ... | +| Q10 | Where is the authoritative version stored? | In `pyproject.toml` under `[project] version`. No other copy should exist. | +| Q11 | How should verbosity be controlled? | Via a string parameter to `main()` matching Python's standard log level names. Invalid values should raise a `ValueError`. | +| Q12 | At what log level should the version be emitted? | INFO — visible by default in most environments, suppressible by raising to WARNING. | +| Q13 | Is the version needed at import time, or only when `main()` runs? | Only when `main()` runs; no module-level side effects. | +| Q14 | What should happen with an unrecognised verbosity string? | Raise `ValueError` naming the invalid value and listing the valid options. | Status: COMPLETE diff --git a/docs/system.md b/docs/system.md index efb9650..3b9eebd 100644 --- a/docs/system.md +++ b/docs/system.md @@ -1,8 +1,14 @@ -# System: +# System Overview: temple8 -> Last updated: YYYY-MM-DD — +> Last updated: 2026-04-22 — display-version -**Purpose:** +**Purpose:** Provide a production-ready Python project template that eliminates setup boilerplate so engineers can ship features immediately. + +--- + +## Summary + +temple8 is a Python project template. Engineers clone it and run a five-step AI-assisted delivery workflow — Scope → Arch → TDD Loop → Verify → Accept — to ship features with quality gates from day one. The template ships with one working demonstration feature (`display-version`) that exercises the full stack end-to-end: it reads the application version from `pyproject.toml` at runtime via `tomllib`, logs it at INFO level, and gates visibility on a `ValidVerbosity` parameter. Quality tooling (ruff, pyright, pytest, hypothesis) and CI are preconfigured; no setup required beyond cloning. --- @@ -10,7 +16,8 @@ | Actor | Needs | |-------|-------| -| | | +| Engineer | Clones the template; runs `python -m app` to verify the installed version and control log verbosity; ships features using the built-in workflow | +| CI Pipeline | Imports the package; runs the full test suite, lint, and type-check on every push | --- @@ -18,13 +25,15 @@ | Module | Responsibility | |--------|----------------| -| | | +| `app/__main__.py` | CLI entry point; accepts `--verbosity` flag; validates it and delegates to `version()` | +| `app/version.py` | Reads `pyproject.toml` via `tomllib`; logs and returns the version string | --- ## Key Decisions -- +- Version is read from `pyproject.toml` at runtime via `tomllib`; no hardcoded `__version__` constant. (see `ADR-2026-04-22-version-source`) +- Log verbosity is validated against the five standard Python log levels before use; invalid values raise `ValueError`. (see `ADR-2026-04-22-verbosity-validation`) --- @@ -32,9 +41,111 @@ | Dependency | What it provides | Why not replaced | |------------|------------------|-----------------| +| `fire` | CLI argument parsing from function signatures | Zero boilerplate; consistent with template philosophy | +| `tomllib` (stdlib, Python ≥ 3.11) | TOML parsing for `pyproject.toml` | Standard library; no extra dependency needed | --- ## Active Constraints -- +- `pyproject.toml` is the single source of truth for the version string; never duplicate it. +- `main()` must accept `verbosity` as its only parameter; no global state. +- All new modules must achieve 100% test coverage before merging. + +--- + +## Domain Model + +### Bounded Contexts + +| Context | Responsibility | Key Modules | +|---------|----------------|-------------| +| **Version** | Read the project version and emit a log message | `app/version.py` | +| **CLI** | Parse CLI arguments; validate verbosity; compose entry point | `app/__main__.py` | + +### Entities + +| Name | Type | Description | Bounded Context | +|------|------|-------------|-----------------| +| `Version` | Value Object | The semver string (`MAJOR.MINOR.YYYYMMDD`) read from `pyproject.toml` at runtime via `tomllib`. Never duplicated as a source-code constant. | Version | +| `ValidVerbosity` | Value Object | A string drawn from the closed set `{DEBUG, INFO, WARNING, ERROR, CRITICAL}`. Any other value is invalid and raises `ValueError`. | CLI | + +### Actions + +| Name | Actor | Object | Description | +|------|-------|--------|-------------| +| `version()` | version module | `pyproject.toml` → `Version` | Reads `pyproject.toml`, emits an INFO log in the format `"Version: "`, and returns the version string | +| `main(verbosity)` | CLI entry point | `ValidVerbosity` → None | Validates verbosity, configures the root logger, then calls `version()`. Raises `ValueError` on invalid verbosity | + +### Relationships + +| Subject | Relation | Object | Cardinality | Notes | +|---------|----------|--------|-------------|-------| +| `main()` | validates-and-calls | `version()` | 1:1 | Verbosity guard runs before version read | +| `version()` | reads | `pyproject.toml` | 1:1 | Single file read per call; no caching | +| `ValidVerbosity` | constrains | `main()` | 1:1 | Only valid level names accepted | + +--- + +## Context + +```mermaid +C4Context + title System Context — temple8 + + Person(engineer, "Engineer", "Clones the template; runs app to verify version; ships features via workflow") + Person(ci, "CI Pipeline", "Imports the package; runs full test suite, lint, type-check on every push") + + System(temple8, "temple8", "Production-ready Python project template with AI-assisted five-step delivery workflow") + + System_Ext(pyproject, "pyproject.toml", "Single source of truth for project version and metadata") + System_Ext(github, "GitHub Actions", "Runs quality gates on every push via .github/workflows/ci.yml") + + Rel(engineer, temple8, "Runs", "CLI: python -m app [--verbosity LEVEL]") + Rel(ci, temple8, "Imports and tests", "pytest / pyright / ruff") + Rel(temple8, pyproject, "Reads version at runtime", "tomllib (stdlib)") + Rel(github, temple8, "Executes quality gates", "uv run task lint / test / static-check") +``` + +--- + +## Container + +```mermaid +C4Container + title Container Diagram — temple8 + + Person(engineer, "Engineer", "") + Person(ci, "CI Pipeline", "") + + System_Boundary(temple8_sys, "temple8") { + Container(cli, "CLI Entry Point", "Python / fire", "app/__main__.py — accepts --verbosity, validates it against ValidVerbosity, calls version().") + Container(version_mod, "Version Module", "Python / tomllib", "app/version.py — reads pyproject.toml, emits INFO log, returns version string.") + } + + System_Ext(pyproject, "pyproject.toml", "Project version and metadata") + System_Ext(github, "GitHub Actions", "CI pipeline") + + Rel(engineer, cli, "runs", "CLI: python -m app [--verbosity LEVEL]") + Rel(ci, cli, "imports and tests", "pytest / pyright / ruff") + Rel(cli, version_mod, "calls version()") + Rel(version_mod, pyproject, "reads [project] version", "tomllib / filesystem") + Rel(github, cli, "executes quality gates", "uv run task lint / test / static-check") +``` + +--- + +## ADR Index + +| ADR | Decision | +|-----|----------| +| [ADR-2026-04-22-version-source](adr/ADR-2026-04-22-version-source.md) | Read version from `pyproject.toml` via `tomllib` at runtime; no hardcoded constant | +| [ADR-2026-04-22-verbosity-validation](adr/ADR-2026-04-22-verbosity-validation.md) | Validate verbosity against a closed set; raise `ValueError` on invalid input | + +--- + +## Completed Features + +| Feature | Description | +|---------|-------------| +| `display-version` | Reads version from `pyproject.toml` at runtime and logs it; verbosity controls log visibility |