From 3e3ce39473e02c543b8b363223636c03a3b096f2 Mon Sep 17 00:00:00 2001 From: nullhack Date: Mon, 18 May 2026 00:21:56 -0400 Subject: [PATCH] =?UTF-8?q?release:=20bump=20version=208.6.0=20=E2=86=92?= =?UTF-8?q?=209.0.0=20(Adversarial=20Audit)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Define-flow reorder: architecture before refine-features. Adversarial audit: 47 knowledge files, 39 skills deduplicated. 3 knowledge files deleted (spec-architecture, rule-derivation, domain-modeling). Decomposition decision tree replaces stale triggers. WSJF scoring wired into select-feature. AI language markers expanded with Claude-specific patterns. .flowr/viz removed (deferred to flowr package). GitHub issues #143, #144, #159 resolved. --- .flowr/flows/architecture-flow.yaml | 3 +- .flowr/flows/define-flow.yaml | 67 +++ .flowr/flows/deliver-flow.yaml | 61 +++ .flowr/flows/delivery-flow.yaml | 105 ----- .flowr/flows/develop-flow.yaml | 141 ++++++ .flowr/flows/development-flow.yaml | 39 +- .flowr/flows/discovery-flow.yaml | 6 +- .flowr/flows/feature-development-flow.yaml | 42 -- .flowr/flows/main-flow.yaml | 45 -- .flowr/flows/planning-flow.yaml | 160 ------- .flowr/flows/spec-validation-flow.yaml | 12 +- .flowr/sessions/.gitkeep | 0 .flowr/sessions/doc-audit.yaml | 9 - .flowr/viz/app.js | 438 ------------------ .flowr/viz/generate-flowviz-data.py | 252 ---------- .flowr/viz/index.html | 386 --------------- .gitignore | 4 +- .../knowledge/agent-design/principles.md | 7 +- .opencode/knowledge/architecture/adr.md | 7 +- .../knowledge/architecture/assessment.md | 7 +- .../architecture/technical-design.md | 14 +- .opencode/knowledge/design/color-systems.md | 14 +- .opencode/knowledge/design/project-assets.md | 13 +- .opencode/knowledge/design/visual-harmony.md | 12 + .../domain-modeling/behavioral-contracts.md | 2 +- .../domain-modeling/domain-modeling.md | 77 --- .../domain-modeling/event-storming.md | 48 +- .../knowledge/requirements/decomposition.md | 75 ++- .../requirements/feature-boundaries.md | 4 +- .../requirements/feature-discovery.md | 20 +- .opencode/knowledge/requirements/gherkin.md | 2 +- .opencode/knowledge/requirements/invest.md | 11 +- .opencode/knowledge/requirements/moscow.md | 5 +- .../knowledge/requirements/rule-derivation.md | 57 --- .../requirements/spec-architecture.md | 118 ----- .../knowledge/requirements/spec-simulation.md | 5 +- .../requirements/ubiquitous-language.md | 2 +- .../knowledge/skill-design/principles.md | 6 +- .../knowledge/software-craft/versioning.md | 4 +- .../knowledge/workflow/flowr-operations.md | 81 +--- .opencode/knowledge/workflow/flowr-spec.md | 14 +- .../workflow/todo-anchor-protocol.md | 10 +- .../knowledge/writing/ai-language-markers.md | 55 ++- .opencode/skills/accept-feature/SKILL.md | 2 +- .opencode/skills/break-down-feature/SKILL.md | 23 - .opencode/skills/conduct-interview/SKILL.md | 4 +- .opencode/skills/create-domain-spec/SKILL.md | 6 +- .opencode/skills/create-py-stubs/SKILL.md | 6 +- .opencode/skills/discover-features/SKILL.md | 20 - .opencode/skills/discover-rules/SKILL.md | 14 - .opencode/skills/fix-spec/SKILL.md | 12 +- .opencode/skills/polish-code/SKILL.md | 5 +- .opencode/skills/refactor/SKILL.md | 18 +- .opencode/skills/refine-features/SKILL.md | 30 ++ .opencode/skills/review-architecture/SKILL.md | 2 +- .opencode/skills/review-gate/SKILL.md | 11 +- .opencode/skills/review-simulation/SKILL.md | 17 +- .opencode/skills/select-feature/SKILL.md | 9 +- .opencode/skills/setup-verify/SKILL.md | 2 +- .opencode/skills/simulate-spec/SKILL.md | 26 +- .opencode/skills/verify-traceability/SKILL.md | 6 +- .opencode/skills/write-bdd-features/SKILL.md | 34 +- .../features/.feature.template | 6 +- .../IN_YYYYMMDD_.md.template | 59 --- .templates/docs/spec/domain_spec.md.template | 9 +- .templates/docs/spec/glossary.md.template | 12 +- .../docs/spec/simulation_results.md.template | 59 --- AGENTS.md | 32 +- CHANGELOG.md | 41 ++ pyproject.toml | 8 +- uv.lock | 2 +- 71 files changed, 718 insertions(+), 2197 deletions(-) create mode 100644 .flowr/flows/define-flow.yaml create mode 100644 .flowr/flows/deliver-flow.yaml delete mode 100644 .flowr/flows/delivery-flow.yaml create mode 100644 .flowr/flows/develop-flow.yaml delete mode 100644 .flowr/flows/feature-development-flow.yaml delete mode 100644 .flowr/flows/main-flow.yaml delete mode 100644 .flowr/flows/planning-flow.yaml delete mode 100644 .flowr/sessions/.gitkeep delete mode 100644 .flowr/sessions/doc-audit.yaml delete mode 100644 .flowr/viz/app.js delete mode 100755 .flowr/viz/generate-flowviz-data.py delete mode 100644 .flowr/viz/index.html delete mode 100644 .opencode/knowledge/domain-modeling/domain-modeling.md delete mode 100644 .opencode/knowledge/requirements/rule-derivation.md delete mode 100644 .opencode/knowledge/requirements/spec-architecture.md delete mode 100644 .opencode/skills/break-down-feature/SKILL.md delete mode 100644 .opencode/skills/discover-features/SKILL.md delete mode 100644 .opencode/skills/discover-rules/SKILL.md create mode 100644 .opencode/skills/refine-features/SKILL.md delete mode 100644 .templates/docs/interview-notes/IN_YYYYMMDD_.md.template delete mode 100644 .templates/docs/spec/simulation_results.md.template diff --git a/.flowr/flows/architecture-flow.yaml b/.flowr/flows/architecture-flow.yaml index 87705d52..fb7432e5 100644 --- a/.flowr/flows/architecture-flow.yaml +++ b/.flowr/flows/architecture-flow.yaml @@ -15,7 +15,6 @@ states: in: - product_definition.md - domain_spec.md - - simulation_results.md out: - product_definition.md: - deployment @@ -71,6 +70,7 @@ states: - domain_spec.md - glossary.md - product_definition.md + - "adr/*.md" # optional — visibility into prior ADRs before creating new ones out: - product_definition.md: - technology_stack @@ -90,7 +90,6 @@ states: - "adr/.md" # optional - product_definition.md - domain_spec.md - - simulation_results.md - glossary.md out: [] conditions: diff --git a/.flowr/flows/define-flow.yaml b/.flowr/flows/define-flow.yaml new file mode 100644 index 00000000..9784845a --- /dev/null +++ b/.flowr/flows/define-flow.yaml @@ -0,0 +1,67 @@ +flow: define-flow +version: 2.0.0 +exits: + - validated + - needs-reinterview + +states: + - id: discovery + attrs: + description: "Interview stakeholders and create structural domain specification" + git: dev + flow: discovery-flow + flow-version: "^12" + next: + complete: spec-validation + + - id: spec-validation + attrs: + description: "Iteratively simulate domain spec, discover rules and pain points, fix until clean" + git: dev + flow: spec-validation-flow + flow-version: "^2" + next: + validated: architecture + needs-reinterview: discovery + + - id: refine-features + attrs: + description: "PO transforms simulation-created context-level .feature files into final feature-level files with stable titles, descriptions, rules, and constraints" + owner: PO + git: dev + skills: + - refine-features + in: + - product_definition.md + - domain_spec.md + - glossary.md + - "features/*.feature" + out: + - "features/.feature": + - feature_header + - constraints + - rules + conditions: + features-refined: + features-discovered: ==verified + invest-validated: + invest-passed: ==verified + committed-to-dev-locally: + committed-to-dev-locally: ==verified + next: + done: + to: validated + when: + - features-refined + - invest-validated + - committed-to-dev-locally + + - id: architecture + attrs: + description: "Design technical architecture, context boundaries, and API contracts — skippable if no architecture changes are needed" + git: dev + flow: architecture-flow + flow-version: "^10" + next: + complete: refine-features + needs-discovery: discovery \ No newline at end of file diff --git a/.flowr/flows/deliver-flow.yaml b/.flowr/flows/deliver-flow.yaml new file mode 100644 index 00000000..6128454b --- /dev/null +++ b/.flowr/flows/deliver-flow.yaml @@ -0,0 +1,61 @@ +flow: deliver-flow +version: 1.0.0 +params: [feature_id] +exits: + - published + - accumulate + - cancelled + - needs-development + +states: + - id: local-merge + attrs: + description: "SE squash-merges feature commits into local dev branch and resolves any conflicts" + owner: SE + git: dev + skills: + - merge-local + in: + - ".cache/acceptance/.md" + - features/.feature + out: + - merged-commits + next: + merged: publish-decision + conflict: needs-development + + - id: publish-decision + attrs: + description: "PO decides whether to publish the accumulated batch as a PR or continue accumulating features on local dev" + owner: PO + git: dev + skills: + - decide-batch-action + in: + - merged-commits + out: [] + next: + accumulate: accumulate + publish: pr-creation + + - id: pr-creation + attrs: + description: "SE creates an administrative PR for changes already on local dev" + owner: SE + git: dev + skills: + - create-pr + in: + - merged-commits + - features/.feature + out: [] + conditions: + merged: + ci-passes: ==verified + no-changes-requested: ==verified + next: + approved: + to: published + when: merged + changes-requested: needs-development + cancelled: cancelled diff --git a/.flowr/flows/delivery-flow.yaml b/.flowr/flows/delivery-flow.yaml deleted file mode 100644 index af61c4f8..00000000 --- a/.flowr/flows/delivery-flow.yaml +++ /dev/null @@ -1,105 +0,0 @@ -flow: delivery-flow -version: 9.0.0 -params: [feature_id] - -exits: - - next-feature - - rejected - - needs-development - - cancelled - -states: - - id: acceptance - attrs: - description: "PO validates business behavior against BDD examples and quality attributes" - owner: PO - git: feature - skills: - - accept-feature - - verify-traceability - in: - - features/.feature - - product_definition.md - - domain_spec.md - - glossary.md - - "interview-notes/*.md" # optional — for requirement traceability - out: - - approval_record - conditions: - feature-accepted: - acceptance-verified: ==verified - next: - approved: - to: polish - when: feature-accepted - rejected: rejected - - - id: polish - attrs: - description: "SE applies full project conventions — naming, docstrings, formatting, type annotations — now that the feature is functionally complete" - owner: SE - git: feature - skills: - - polish-code - in: - - source-implementations - - test-implementations - - features/.feature - out: - - polished-source - next: - polished: local-merge - - - id: local-merge - attrs: - description: "SE squash-merges feature commits into local dev branch and resolves any conflicts" - owner: SE - git: dev - skills: - - merge-local - in: - - polished-source - - feature-commits - - approval_record - - features/.feature - out: - - merged-commits - next: - merged: publish-decision - conflict: needs-development - - - id: publish-decision - attrs: - description: "PO decides whether to publish the accumulated batch as a PR or continue accumulating features on local dev" - owner: PO - git: dev - skills: - - decide-batch-action - in: - - merged-commits - out: [] - next: - accumulate: next-feature - publish: pr-creation - - - id: pr-creation - attrs: - description: "SE creates an administrative PR for changes already on local dev" - owner: SE - git: dev - skills: - - create-pr - in: - - merged-commits - - features/.feature - out: [] - conditions: - merged: - ci-passes: ==verified - no-changes-requested: ==verified - next: - approved: - to: next-feature - when: merged - changes-requested: needs-development - cancelled: cancelled diff --git a/.flowr/flows/develop-flow.yaml b/.flowr/flows/develop-flow.yaml new file mode 100644 index 00000000..8b30a9ec --- /dev/null +++ b/.flowr/flows/develop-flow.yaml @@ -0,0 +1,141 @@ +flow: develop-flow +version: 2.0.0 +params: [feature_id] +exits: + - completed + - needs-architecture + - cancelled + +states: + - id: feature-selection + attrs: + description: "PO selects the next feature — by delivery order for first feature, by WSJF for subsequent features" + owner: PO + git: dev + skills: + - select-feature + in: + - product_definition.md + - domain_spec.md + - "features/.feature" + out: [] + next: + selected: feature-examples + needs-architecture: needs-architecture + no-features: completed + + - id: feature-examples + attrs: + description: "PO converts rules to Example and Scenario Outline blocks with pre-mortem analysis" + owner: PO + git: dev + skills: + - write-bdd-features + in: + - features/.feature + - product_definition.md + - domain_spec.md + - glossary.md + out: + - features/.feature: + - examples + conditions: + examples-complete: + examples-complete: ==verified + next: + done: + to: spec-review + when: examples-complete + + - id: spec-review + attrs: + description: "R independently verifies the feature spec for cross-document consistency, domain alignment, BDD quality, and pre-mortem coverage" + owner: R + git: dev + skills: + - review-spec + in: + - features/.feature + - product_definition.md + - domain_spec.md + - glossary.md + out: [] + conditions: + spec-verified: + spec-verified: ==verified + next: + done: + to: development + when: spec-verified + fail: feature-examples + + - id: development + attrs: + description: "Implement the feature through TDD cycles, review, and commit" + git: feature + flow: development-flow + flow-version: "^13" + next: + done: acceptance + + - id: acceptance + attrs: + description: "PO validates business behavior against BDD examples and quality attributes" + owner: PO + git: feature + skills: + - accept-feature + - verify-traceability + in: + - features/.feature + - product_definition.md + - domain_spec.md + - glossary.md + - ".cache/interview-notes/*.md" + out: + - ".cache/acceptance/.md" + conditions: + feature-accepted: + acceptance-verified: ==verified + next: + approved: + to: polish + when: feature-accepted + rejected: development + + - id: polish + attrs: + description: "SE applies full project conventions — naming, docstrings, formatting, type annotations — after feature acceptance" + owner: SE + git: feature + skills: + - polish-code + in: + - source-implementations + - test-implementations + - features/.feature + out: + - polished-source + next: + polished: commit-polished + + - id: commit-polished + attrs: + description: "SE commits polished code to the feature branch with traceability" + owner: SE + git: feature + skills: + - commit-implementation + in: + - polished-source + - test-implementations + - features/.feature + out: + - feature-commits + conditions: + committed: + committed: ==verified + next: + done: + to: completed + when: committed \ No newline at end of file diff --git a/.flowr/flows/development-flow.yaml b/.flowr/flows/development-flow.yaml index 6fc6bf04..c91bc721 100644 --- a/.flowr/flows/development-flow.yaml +++ b/.flowr/flows/development-flow.yaml @@ -1,5 +1,5 @@ flow: development-flow -version: 12.0.0 +version: 13.0.0 params: [feature_id] exits: - done @@ -7,7 +7,7 @@ exits: states: - id: feature-structuring attrs: - description: "SA creates the feature branch and feature-level structure — package directories, port interfaces, aggregate root signatures — before TDD cycles begin" + description: "SA creates the feature branch and feature-level structure — package directories, port interfaces, aggregate root signatures — before stubs are generated" owner: SA git: feature skills: @@ -15,13 +15,42 @@ states: in: - features/.feature - domain_spec.md - - simulation_results.md - glossary.md - product_definition.md out: - git_branch next: - ready: tdd-cycle + ready: create-py-stubs + + - id: create-py-stubs + attrs: + description: "SA creates typed source stubs and generates test stubs via pytest-beehave, then verifies all planning artifacts are complete for baseline" + owner: SA + git: feature + skills: + - create-py-stubs + in: + - features/.feature + - domain_spec.md + - glossary.md + - product_definition.md + out: + - typed-source-stubs + - test-skeletons + conditions: + stubs-traceable: + beehave-check-passes: ==verified + baseline-confirmed: + baseline-confirmed: ==verified + committed: + committed: ==verified + next: + done: + to: tdd-cycle + when: + - stubs-traceable + - baseline-confirmed + - committed - id: tdd-cycle attrs: @@ -84,4 +113,4 @@ states: next: done: to: done - when: committed + when: committed \ No newline at end of file diff --git a/.flowr/flows/discovery-flow.yaml b/.flowr/flows/discovery-flow.yaml index a93e4100..50740f85 100644 --- a/.flowr/flows/discovery-flow.yaml +++ b/.flowr/flows/discovery-flow.yaml @@ -12,11 +12,11 @@ states: skills: - conduct-interview in: - - "interview-notes/*.md" # optional + - ".cache/interview-notes/*.md" # optional - "domain_spec.md" # optional — for re-interview context - "product_definition.md" # optional — for re-interview context out: - - "interview-notes/.md" + - ".cache/interview-notes/.md" next: needs-spec: spec-creation already-known: complete @@ -30,7 +30,7 @@ states: - create-domain-spec - define-ubiquitous-language in: - - interview-notes/*.md + - .cache/interview-notes/*.md - "domain_spec.md" # optional — cumulative edit across iterations - "glossary.md" # optional — cumulative edit across iterations - "product_definition.md" # optional — cumulative edit across iterations diff --git a/.flowr/flows/feature-development-flow.yaml b/.flowr/flows/feature-development-flow.yaml deleted file mode 100644 index ad621366..00000000 --- a/.flowr/flows/feature-development-flow.yaml +++ /dev/null @@ -1,42 +0,0 @@ -flow: feature-development-flow -version: 11.0.0 -params: [feature_id] - -exits: - - needs-architecture - - cancelled - - completed - - rejected - -states: - - id: planning - attrs: - description: "Plan feature breakdown, BDD examples, and development readiness" - git: dev - flow: planning-flow - flow-version: "^15" - next: - complete: development - needs-architecture: needs-architecture - no-features: completed - - - id: development - attrs: - description: "Implement the feature through TDD cycles, review, and commit" - git: feature - flow: development-flow - flow-version: "^12" - next: - done: delivery - - - id: delivery - attrs: - description: "Accept, merge, and publish the completed feature" - git: feature - flow: delivery-flow - flow-version: "^8" - next: - next-feature: planning - rejected: rejected - needs-development: development - cancelled: cancelled diff --git a/.flowr/flows/main-flow.yaml b/.flowr/flows/main-flow.yaml deleted file mode 100644 index 27c55a8f..00000000 --- a/.flowr/flows/main-flow.yaml +++ /dev/null @@ -1,45 +0,0 @@ -flow: main-flow -version: 11.0.0 -exits: [completed, cancelled, rejected] - -states: - - id: discovery - attrs: - description: "Interview stakeholders and create structural domain specification" - git: dev - flow: discovery-flow - flow-version: "^12" - next: - complete: spec-validation - - - id: spec-validation - attrs: - description: "Iteratively simulate domain spec, discover rules and pain points, fix until clean" - git: dev - flow: spec-validation-flow - flow-version: "^2" - next: - validated: architecture - needs-reinterview: discovery - - - id: architecture - attrs: - description: "Design technical architecture, context boundaries, and API contracts for the entire project" - git: dev - flow: architecture-flow - flow-version: "^10" - next: - complete: feature-development - needs-discovery: discovery - - - id: feature-development - attrs: - description: "Feature-level loop: Planning -> Development -> Acceptance -> Delivery per feature" - git: feature - flow: feature-development-flow - flow-version: "^12" - next: - needs-architecture: architecture - cancelled: cancelled - completed: completed - rejected: rejected diff --git a/.flowr/flows/planning-flow.yaml b/.flowr/flows/planning-flow.yaml deleted file mode 100644 index 4bd5d140..00000000 --- a/.flowr/flows/planning-flow.yaml +++ /dev/null @@ -1,160 +0,0 @@ -flow: planning-flow -version: 15.0.0 -params: [feature_id] -exits: - - complete - - needs-architecture - - no-features - -states: - - id: feature-discovery - attrs: - description: "PO refines simulation-created feature files: splits/renames, redistributes Rule blocks, adds Constraints from quality attributes" - owner: PO - git: dev - skills: - - discover-features - in: - - product_definition.md - - domain_spec.md - - simulation_results.md - - glossary.md - - "features/*.feature" # optional — may not exist on first cycle - out: - - features/.feature: - - feature_header - - constraints - - rules - conditions: - features-discovered: - features-discovered: ==verified - committed-to-dev-locally: - committed-to-dev-locally: ==verified - next: - done: - to: feature-selection - when: - - features-discovered - - committed-to-dev-locally - no-features: no-features - - - id: feature-selection - attrs: - description: "PO selects the next feature — by delivery order for first feature, by WSJF for subsequent features" - owner: PO - git: dev - skills: - - select-feature - in: - - product_definition.md - - domain_spec.md - - simulation_results.md - - "features/.feature" # optional — discover available features - out: [] - next: - selected: feature-breakdown - needs-architecture: needs-architecture - no-features: no-features - - - id: feature-breakdown - attrs: - description: "PO validates INVEST, adds behavior hints under each Rule" - owner: PO - git: dev - skills: - - break-down-feature - in: - - features/.feature - - product_definition.md - - domain_spec.md - - simulation_results.md - - glossary.md - out: - - features/.feature: - - rules - - behavior_hints - conditions: - invest-passed: - invest-passed: ==verified - next: - done: - to: feature-examples - when: invest-passed - - - id: feature-examples - attrs: - description: "PO converts behavior hints to Example and Scenario Outline blocks" - owner: PO - git: dev - skills: - - write-bdd-features - in: - - features/.feature - - product_definition.md - - domain_spec.md - - simulation_results.md - - glossary.md - out: - - features/.feature: - - examples - conditions: - examples-complete: - examples-complete: ==verified - next: - done: - to: spec-review - when: examples-complete - - - id: spec-review - attrs: - description: "R independently verifies the feature spec for cross-document consistency, domain alignment, BDD quality, and pre-mortem coverage" - owner: R - git: dev - skills: - - review-spec - in: - - features/.feature - - product_definition.md - - domain_spec.md - - simulation_results.md - - glossary.md - out: [] - conditions: - spec-verified: - spec-verified: ==verified - next: - done: - to: create-py-stubs - when: spec-verified - fail: feature-breakdown - - - id: create-py-stubs - attrs: - description: "SA creates typed source stubs and generates test stubs via pytest-beehave, then verifies all planning artifacts are complete before baseline" - owner: SA - git: dev - skills: - - create-py-stubs - in: - - features/.feature - - product_definition.md - - domain_spec.md - - simulation_results.md - - glossary.md - out: - - typed-source-stubs - - test-skeletons - conditions: - stubs-traceable: - beehave-check-passes: ==verified - baseline-confirmed: - baseline-confirmed: ==verified - committed-to-dev-locally: - committed-to-dev-locally: ==verified - next: - done: - to: complete - when: - - stubs-traceable - - baseline-confirmed - - committed-to-dev-locally diff --git a/.flowr/flows/spec-validation-flow.yaml b/.flowr/flows/spec-validation-flow.yaml index e1c5fb39..43340c88 100644 --- a/.flowr/flows/spec-validation-flow.yaml +++ b/.flowr/flows/spec-validation-flow.yaml @@ -16,9 +16,9 @@ states: - domain_spec.md - product_definition.md - glossary.md - - "simulation_results.md" # optional — may not exist on first iteration + - ".cache/sim/simulation_results_*.md" # optional — prior iterations out: - - simulation_results.md + - ".cache/sim/simulation_results_.md" - "features/.feature" next: done: review-simulation @@ -31,13 +31,13 @@ states: skills: - review-simulation in: - - simulation_results.md + - ".cache/sim/simulation_results_*.md" - domain_spec.md - product_definition.md - glossary.md - "features/*.feature" out: - - simulation_results.md: + - ".cache/sim/simulation_results_.md": - summary conditions: simulation-approved: @@ -61,14 +61,14 @@ states: skills: - fix-spec in: - - simulation_results.md + - ".cache/sim/simulation_results_*.md" - domain_spec.md - product_definition.md - glossary.md - "features/.feature" out: - domain_spec.md - - simulation_results.md + - ".cache/sim/simulation_results_.md" - "features/.feature" next: done: simulate-spec diff --git a/.flowr/sessions/.gitkeep b/.flowr/sessions/.gitkeep deleted file mode 100644 index e69de29b..00000000 diff --git a/.flowr/sessions/doc-audit.yaml b/.flowr/sessions/doc-audit.yaml deleted file mode 100644 index 50320a1f..00000000 --- a/.flowr/sessions/doc-audit.yaml +++ /dev/null @@ -1,9 +0,0 @@ -created_at: '2026-05-06T09:26:21.074902+00:00' -flow: architecture-flow -name: doc-audit -params: {} -stack: -- flow: main-flow - state: architecture -state: architecture-assessment -updated_at: '2026-05-06T10:25:28.454349+00:00' diff --git a/.flowr/viz/app.js b/.flowr/viz/app.js deleted file mode 100644 index 873ea5ed..00000000 --- a/.flowr/viz/app.js +++ /dev/null @@ -1,438 +0,0 @@ -/* global d3, dagre, FLOWVIZ_DATA */ - -(function () { - "use strict"; - - const errEl = document.getElementById("error"); - const metaEl = document.getElementById("flowMeta"); - const crumbsEl = document.getElementById("crumbs"); - const selectEl = document.getElementById("flowSelect"); - const backBtn = document.getElementById("backBtn"); - const fitBtn = document.getElementById("fitBtn"); - const resetBtn = document.getElementById("resetBtn"); - const tooltipEl = document.getElementById("tooltip"); - const svg = d3.select("#svg"); - - function esc(s) { - return String(s).replace(/&/g, "&").replace(//g, ">"); - } - - function showError(msg, err) { - const detail = err ? `\n${err.stack || err}` : ""; - errEl.textContent = `${msg}${detail}`; - errEl.style.display = "block"; - } - - function clearError() { - errEl.style.display = "none"; - errEl.textContent = ""; - } - - function showTooltip(html, event) { - tooltipEl.innerHTML = html; - tooltipEl.style.display = "block"; - const ttRect = tooltipEl.getBoundingClientRect(); - let x = event.clientX + 14; - let y = event.clientY - 10; - if (x + ttRect.width > window.innerWidth - 8) x = event.clientX - ttRect.width - 14; - if (y + ttRect.height > window.innerHeight - 8) y = window.innerHeight - ttRect.height - 8; - tooltipEl.style.left = x + "px"; - tooltipEl.style.top = y + "px"; - } - - function hideTooltip() { - tooltipEl.style.display = "none"; - } - - if (!window.FLOWVIZ_DATA || !FLOWVIZ_DATA.flows) { - showError( - "Missing data bundle. Run: python3 scripts/generate-flowviz-data.py", - ); - return; - } - - const flows = FLOWVIZ_DATA.flows; - const flowNames = Object.keys(flows).sort(); - - // Populate selector. - for (const name of flowNames) { - const opt = document.createElement("option"); - opt.value = name; - opt.textContent = name; - selectEl.appendChild(opt); - } - - const state = { - stack: [], - current: FLOWVIZ_DATA.defaultFlow || flowNames[0], - }; - state.stack = [state.current]; - selectEl.value = state.current; - - // SVG plumbing: a single zoomable group. - const defs = svg.append("defs"); - defs - .append("marker") - .attr("id", "arrow") - .attr("viewBox", "0 0 10 10") - .attr("refX", 9) - .attr("refY", 5) - .attr("markerWidth", 7) - .attr("markerHeight", 7) - .attr("orient", "auto-start-reverse") - .append("path") - .attr("d", "M 0 0 L 10 5 L 0 10 z") - .attr("fill", "#7a86b8"); - - const gRoot = svg.append("g").attr("class", "root"); - const gEdges = gRoot.append("g").attr("class", "edges"); - const gNodes = gRoot.append("g").attr("class", "nodes"); - - const zoom = d3 - .zoom() - .scaleExtent([0.2, 2.5]) - .on("zoom", (event) => { - gRoot.attr("transform", event.transform); - }); - svg.call(zoom); - - function nodeSize(n) { - // Best-effort sizing without measuring text. - // Rect sizes tuned for readability and dense graphs. - if (n.type === "exit") return { w: 120, h: 44 }; - if (n.type === "subflow") return { w: 180, h: 56 }; - return { w: 160, h: 52 }; - } - - function wrapLabel(label, maxLen) { - const words = String(label).split(/\s+/).filter(Boolean); - const lines = []; - let line = []; - let len = 0; - for (const w of words) { - const add = (line.length ? 1 : 0) + w.length; - if (len + add > maxLen && line.length) { - lines.push(line.join(" ")); - line = [w]; - len = w.length; - } else { - line.push(w); - len += add; - } - } - if (line.length) lines.push(line.join(" ")); - return lines.slice(0, 3); - } - - function updateCrumbs() { - crumbsEl.textContent = ""; - state.stack.forEach((name, idx) => { - if (idx > 0) crumbsEl.appendChild(document.createTextNode(" > ")); - const isLast = idx === state.stack.length - 1; - if (isLast) { - const span = document.createElement("span"); - span.textContent = name; - span.style.color = "#e6e9f5"; - span.style.fontWeight = "700"; - crumbsEl.appendChild(span); - } else { - const a = document.createElement("a"); - a.href = "#"; - a.textContent = name; - a.addEventListener("click", (e) => { - e.preventDefault(); - navigateToIndex(idx); - }); - crumbsEl.appendChild(a); - } - }); - } - - function updateMeta(flow) { - const exits = (flow.exits || []).join(", ") || "(none)"; - metaEl.innerHTML = - `
${flow.flow} v${flow.version}
` + - `
Nodes: ${flow.nodes.length}, Edges: ${flow.edges.length}
` + - `
Exits: ${exits}
`; - } - - function fitToGraph() { - const svgNode = svg.node(); - const bbox = gRoot.node().getBBox(); - const width = svgNode.clientWidth || 800; - const height = svgNode.clientHeight || 600; - if (!bbox.width || !bbox.height) return; - const pad = 30; - const scale = Math.min( - (width - pad * 2) / bbox.width, - (height - pad * 2) / bbox.height, - ); - const tx = (width - bbox.width * scale) / 2 - bbox.x * scale; - const ty = (height - bbox.height * scale) / 2 - bbox.y * scale; - svg - .transition() - .duration(250) - .call(zoom.transform, d3.zoomIdentity.translate(tx, ty).scale(scale)); - } - - function resetView() { - svg - .transition() - .duration(200) - .call(zoom.transform, d3.zoomIdentity); - } - - function render(flowName) { - clearError(); - const flow = flows[flowName]; - if (!flow) { - showError(`Unknown flow: ${flowName}`); - return; - } - updateMeta(flow); - updateCrumbs(); - - // Build dagre graph. - const g = new dagre.graphlib.Graph({ multigraph: true }); - g.setGraph({ - rankdir: "TB", - nodesep: 25, - ranksep: 60, - edgesep: 8, - marginx: 20, - marginy: 20, - }); - g.setDefaultEdgeLabel(() => ({})); - - for (const n of flow.nodes) { - const s = nodeSize(n); - g.setNode(n.id, { - width: s.w, - height: s.h, - ...n, - }); - } - - // Use stable keys for multi-edges. - for (let i = 0; i < flow.edges.length; i++) { - const e = flow.edges[i]; - g.setEdge(e.source, e.target, { ...e, _i: i }, `e${i}`); - } - - dagre.layout(g); - - const laidNodes = g.nodes().map((id) => ({ id, ...g.node(id) })); - const laidEdges = g.edges().map((ed) => ({ ...ed, ...g.edge(ed) })); - - const firstState = flow.nodes.find((n) => n.type !== "exit"); - const firstNode = firstState ? laidNodes.find((n) => n.id === firstState.id) : null; - - // Render edges. - const edgeSel = gEdges.selectAll("g.edge").data(laidEdges, (d) => d.name); - edgeSel.exit().remove(); - const edgeEnter = edgeSel.enter().append("g").attr("class", "edge"); - edgeEnter.append("path").attr("class", "edge-path"); - const edgesMerged = edgeEnter.merge(edgeSel); - - const line = d3 - .line() - .x((p) => p.x) - .y((p) => p.y) - .curve(d3.curveCatmullRom.alpha(0.5)); - - edgesMerged - .select("path.edge-path") - .attr("class", (d) => `edge-path ${d.kind === "exit" ? "exit" : ""}`) - .attr("marker-end", "url(#arrow)") - .attr("d", (d) => line(d.points || [])); - - edgesMerged - .on("mouseenter", (event, d) => { - const src = flow.nodes.find((n) => n.id === d.source); - const tgt = flow.nodes.find((n) => n.id === d.target); - const srcLabel = src ? esc(src.label || src.id) : esc(d.source); - const tgtLabel = tgt ? esc(tgt.label || tgt.id) : esc(d.target); - const label = esc(d.label || "(default)"); - let html = `
${srcLabel} → ${tgtLabel}
` + - `
${label} → ${tgtLabel}
`; - if (d.when && Object.keys(d.when).length > 0) { - const conds = Object.entries(d.when).map(([k, v]) => `${esc(k)}: ${esc(v)}`).join("
"); - html += `
When:
${conds}
`; - } - showTooltip(html, event); - }) - .on("mousemove", (event) => { - const ttRect = tooltipEl.getBoundingClientRect(); - let x = event.clientX + 14; - let y = event.clientY - 10; - if (x + ttRect.width > window.innerWidth - 8) x = event.clientX - ttRect.width - 14; - if (y + ttRect.height > window.innerHeight - 8) y = window.innerHeight - ttRect.height - 8; - tooltipEl.style.left = x + "px"; - tooltipEl.style.top = y + "px"; - }) - .on("mouseleave", hideTooltip); - - // Render nodes. - const nodeSel = gNodes.selectAll("g.node").data(laidNodes, (d) => d.id); - nodeSel.exit().remove(); - const nodeEnter = nodeSel - .enter() - .append("g") - .attr("class", (d) => `node ${d.type || "state"}`) - .style("cursor", (d) => (d.type === "subflow" ? "pointer" : "default")) - .on("click", (event, d) => { - if (d.type !== "subflow") return; - if (!d.subflow || !flows[d.subflow]) return; - navigateToFlow(d.subflow); - }); - - nodeEnter.append("rect"); - nodeEnter - .append("text") - .attr("text-anchor", "middle") - .attr("dominant-baseline", "middle"); - - nodeEnter - .on("mouseenter", (event, d) => { - const outEdges = flow.edges.filter((e) => e.source === d.id); - let html = `
${d.label || d.id}
`; - if (d.attrs) { - const labelMap = { - description: "Description", - owner: "Owner", - in: "In", - out: "Out", - input_artifacts: "In", - edited_artifacts: "Edit", - output_artifacts: "Out", - }; - const ordered = ["description", "owner", "in", "out", "input_artifacts", "edited_artifacts", "output_artifacts"]; - const rest = Object.keys(d.attrs).filter((k) => !ordered.includes(k)); - const allKeys = [...ordered, ...rest]; - for (const key of allKeys) { - if (d.attrs[key] == null) continue; - const label = labelMap[key] || key.charAt(0).toUpperCase() + key.slice(1).replace(/_/g, " "); - const val = d.attrs[key]; - if (Array.isArray(val) && val.length === 0) continue; - if (Array.isArray(val)) { - html += `
${esc(label)}:
${val.map(esc).join("
")}
`; - } else { - html += `
${esc(label)}: ${esc(val)}
`; - } - } - } - if (d.subflow) html += `
subflow: ${d.subflow} v${d.subflowVersion || "?"}
`; - if (d.type && !d.attrs) html += `
type: ${d.type}
`; - if (outEdges.length) { - html += `
`; - for (const e of outEdges) { - const tgt = flow.nodes.find((n) => n.id === e.target); - const tgtLabel = tgt ? esc(tgt.label || tgt.id) : esc(e.target); - const edgeLabel = esc(e.label || "(default)"); - html += `
${edgeLabel} → ${tgtLabel}
`; - } - html += `
`; - } - showTooltip(html, event); - }) - .on("mousemove", (event) => { - const ttRect = tooltipEl.getBoundingClientRect(); - let x = event.clientX + 14; - let y = event.clientY - 10; - if (x + ttRect.width > window.innerWidth - 8) x = event.clientX - ttRect.width - 14; - if (y + ttRect.height > window.innerHeight - 8) y = window.innerHeight - ttRect.height - 8; - tooltipEl.style.left = x + "px"; - tooltipEl.style.top = y + "px"; - }) - .on("mouseleave", hideTooltip); - const nodesMerged = nodeEnter.merge(nodeSel); - - nodesMerged.attr("transform", (d) => `translate(${d.x - d.width / 2},${d.y - d.height / 2})`); - - nodesMerged - .select("rect") - .attr("width", (d) => d.width) - .attr("height", (d) => d.height); - - nodesMerged.select("text").each(function (d) { - const text = d3.select(this); - text.selectAll("tspan").remove(); - const maxLen = d.type === "subflow" ? 18 : 16; - const lines = wrapLabel(d.label || d.id, maxLen); - const lineHeight = 13; - const startY = d.height / 2 - ((lines.length - 1) * lineHeight) / 2; - for (let i = 0; i < lines.length; i++) { - text - .append("tspan") - .attr("x", d.width / 2) - .attr("y", startY + i * lineHeight) - .text(lines[i]); - } - }); - - // Initial state indicator (small grey circle + arrow to first state) - const startSel = gNodes.selectAll("g.start-node").data(firstNode ? [firstNode] : []); - startSel.exit().remove(); - const startEnter = startSel.enter().append("g").attr("class", "start-node"); - startEnter.append("circle").attr("class", "outer").attr("r", 8).attr("cx", 0).attr("cy", 0); - startEnter.append("circle").attr("class", "inner").attr("r", 4).attr("cx", 0).attr("cy", 0); - - const startMerged = startEnter.merge(startSel); - startMerged.attr("transform", (d) => { - const cy = d.y - d.height / 2 - 28; - return `translate(${d.x},${cy})`; - }); - - // Also add the arrow marker line from start circle to first node - const startEdgeSel = gEdges.selectAll("path.start-edge").data(firstNode ? [firstNode] : []); - startEdgeSel.exit().remove(); - startEdgeSel.enter().append("path").attr("class", "start-edge") - .merge(startEdgeSel) - .attr("d", (d) => { - const cx = d.x; - const cy = d.y - d.height / 2 - 20; - return `M${cx},${cy} L${d.x},${d.y - d.height / 2}`; - }); - - // Initial fit after render. - fitToGraph(); - } - - function navigateToFlow(flowName) { - state.current = flowName; - state.stack.push(flowName); - selectEl.value = flowName; - render(flowName); - } - - function navigateToIndex(idx) { - state.stack = state.stack.slice(0, idx + 1); - state.current = state.stack[state.stack.length - 1]; - selectEl.value = state.current; - render(state.current); - } - - // UI handlers. - selectEl.addEventListener("change", () => { - state.current = selectEl.value; - state.stack = [state.current]; - render(state.current); - }); - - backBtn.addEventListener("click", () => { - if (state.stack.length <= 1) return; - state.stack.pop(); - state.current = state.stack[state.stack.length - 1]; - selectEl.value = state.current; - render(state.current); - }); - fitBtn.addEventListener("click", () => fitToGraph()); - resetBtn.addEventListener("click", () => resetView()); - - // Boot. - try { - render(state.current); - } catch (e) { - showError("Failed to render graph.", e); - } -})(); diff --git a/.flowr/viz/generate-flowviz-data.py b/.flowr/viz/generate-flowviz-data.py deleted file mode 100755 index 43172d17..00000000 --- a/.flowr/viz/generate-flowviz-data.py +++ /dev/null @@ -1,252 +0,0 @@ -#!/usr/bin/env python3 -"""Generate FlowViz data bundle from flowr YAML. - -Outputs a single JS file (`.flowr/viz/data.js`) that defines `window.FLOWVIZ_DATA`. -This is intentionally `file://` friendly: the HTML can be opened directly -without needing a local web server (no fetch/XHR). -""" - -from __future__ import annotations - -import json -from pathlib import Path - -import yaml - -ROOT = Path(__file__).resolve().parents[2] -FLOWS_DIR = ROOT / ".flowr" / "flows" -OUT_DIR = ROOT / ".flowr" / "viz" -OUT_FILE = OUT_DIR / "data.js" - - -def _title_case(s: str) -> str: - return " ".join([p.capitalize() for p in s.replace("_", "-").split("-") if p]) - - -def load_flow_yaml(path: Path) -> dict: - """Load and validate a flowr YAML file.""" - with path.open("r", encoding="utf-8") as f: - data = yaml.safe_load(f) - if not isinstance(data, dict) or "flow" not in data: - raise ValueError(f"Invalid flow YAML: {path}") - return data - - -def _flatten_artifacts(artifacts: list | None) -> list[str]: - if not artifacts: - return [] - result: list[str] = [] - for item in artifacts: - if isinstance(item, str): - result.append(item) - elif isinstance(item, dict): - for doc, sections in item.items(): - if isinstance(sections, list) and sections: - for sec in sections: - result.append(f"{doc}#{sec}") - else: - result.append(doc) - return result - - -def _flatten_attrs(attrs: dict | None) -> dict | None: - if not attrs: - return attrs - flat = {} - for key, val in attrs.items(): - if key in ( - "input_artifacts", - "edited_artifacts", - "output_artifacts", - "in", - "out", - ): - flat[key] = _flatten_artifacts(val) - else: - flat[key] = val - return flat - - -def _resolve_when( - when_clause: dict | list | str, - state_conditions: dict | None, - state_id: str, -) -> dict[str, str] | None: - """Resolve a when clause into a flat dict of conditions. - - Mirrors the flowr loader's resolve_when_clause logic but returns - a plain dict suitable for JSON serialization instead of GuardCondition. - """ - if isinstance(when_clause, dict): - return dict(when_clause) - - items = [when_clause] if isinstance(when_clause, str) else list(when_clause) - resolved: dict[str, str] = {} - - for item in items: - if isinstance(item, dict): - resolved.update(item) - elif isinstance(item, str): - # Named reference to a condition group on this state - if not state_conditions or item not in state_conditions: - raise ValueError( - f"Unknown condition reference '{item}' in state '{state_id}'" - ) - resolved.update(state_conditions[item]) - - return resolved or None - - -def _add_exit_node( - nodes: list[dict], node_ids: set[str], target: str, exits: list[str] -) -> None: - """Add an exit node to the graph if not already present.""" - if target not in node_ids: - nodes.append({"id": target, "type": "exit", "label": _title_case(target)}) - node_ids.add(target) - - -def _process_dict_transitions( - nxt: dict, - st_id: str, - exits: list[str], - state_conditions: dict | None, - nodes: list[dict], - node_ids: set[str], - edges: list[dict], -) -> None: - """Process dict-format transitions (flowr normal form).""" - for trigger, tgt in nxt.items(): - target: str | None = None - when: dict[str, str] | None = None - - if isinstance(tgt, str): - target = tgt - elif isinstance(tgt, dict): - target = tgt.get("to") - raw_when = tgt.get("when") - if raw_when is not None: - when = _resolve_when(raw_when, state_conditions, st_id) - - if target is None: - continue - - edge = { - "source": st_id, - "target": target, - "label": "" if trigger == "default" else str(trigger), - "kind": "exit" if target in exits else "transition", - } - if when: - edge["when"] = when - edges.append(edge) - _add_exit_node(nodes, node_ids, target, exits) - - -def _process_list_transitions( - nxt: list, - st_id: str, - exits: list[str], - nodes: list[dict], - node_ids: set[str], - edges: list[dict], -) -> None: - """Process list-format transitions (older/alternate format).""" - for t in nxt: - target = t["target"] - cond = t.get("when", "default") - edges.append( - { - "source": st_id, - "target": target, - "label": "" if cond == "default" else str(cond), - "kind": "exit" if target in exits else "transition", - } - ) - _add_exit_node(nodes, node_ids, target, exits) - - -def build_graph(flow_data: dict) -> dict: - """Build a visualization graph from flowr YAML data.""" - exits = list(flow_data.get("exits", []) or []) - states = list(flow_data.get("states", []) or []) - - nodes: list[dict] = [] - edges: list[dict] = [] - - for st in states: - st_id = st["id"] - is_subflow = "flow" in st - node_type = "subflow" if is_subflow else "state" - - nodes.append( - { - "id": st_id, - "type": node_type, - "label": _title_case(st_id), - "subflow": st.get("flow"), - "subflowVersion": st.get("flow-version"), - "attrs": _flatten_attrs(st.get("attrs")) or None, - } - ) - - for ex in exits: - nodes.append( - { - "id": ex, - "type": "exit", - "label": _title_case(ex), - } - ) - - node_ids = {n["id"] for n in nodes} - - for st in states: - st_id = st["id"] - state_conditions = st.get("conditions") - nxt = st.get("next") - if not nxt: - continue - - if isinstance(nxt, dict): - _process_dict_transitions( - nxt, st_id, exits, state_conditions, nodes, node_ids, edges - ) - elif isinstance(nxt, list): - _process_list_transitions(nxt, st_id, exits, nodes, node_ids, edges) - - return { - "flow": flow_data["flow"], - "version": flow_data.get("version", "0.0.0"), - "exits": exits, - "nodes": nodes, - "edges": edges, - } - - -def main() -> int: - """Generate the flowviz data bundle from all flowr YAML files.""" - if not FLOWS_DIR.exists(): - raise SystemExit(f"Missing flows directory: {FLOWS_DIR}") - - OUT_DIR.mkdir(parents=True, exist_ok=True) - - flows: dict[str, dict] = {} - for p in sorted(FLOWS_DIR.glob("*.yaml")): - data = load_flow_yaml(p) - flows[data["flow"]] = build_graph(data) - - bundle = { - "schema": 1, - "defaultFlow": "main-flow" if "main-flow" in flows else min(flows), - "flows": flows, - } - - js = "window.FLOWVIZ_DATA = " + json.dumps(bundle, indent=2, sort_keys=True) + ";\n" - OUT_FILE.write_text(js, encoding="utf-8") - - return 0 - - -if __name__ == "__main__": - raise SystemExit(main()) diff --git a/.flowr/viz/index.html b/.flowr/viz/index.html deleted file mode 100644 index 7c5f9c53..00000000 --- a/.flowr/viz/index.html +++ /dev/null @@ -1,386 +0,0 @@ - - - - - - FlowViz - - - -
-
-
-
FlowViz
-
-
-
- - - - -
-
-
-
- -
-
-
- -
-
- - - - - - - - - - diff --git a/.gitignore b/.gitignore index 99d963f8..76f52a56 100644 --- a/.gitignore +++ b/.gitignore @@ -171,8 +171,8 @@ cython_debug/ #.idea/ .mutmut-cache -# Session files (local working state) -.flowr/sessions/session.yaml +# Session files and simulation cache (local working state) +.cache/ # Generated flow visualization data (regenerate with: task regenerate-flowviz) .flowr/viz/data.js diff --git a/.opencode/knowledge/agent-design/principles.md b/.opencode/knowledge/agent-design/principles.md index 85748989..6268ecd5 100644 --- a/.opencode/knowledge/agent-design/principles.md +++ b/.opencode/knowledge/agent-design/principles.md @@ -23,12 +23,7 @@ last-updated: 2026-04-29 - **Positional attention degradation** (Liu et al., 2023: middle content receives less attention): keep files short - **Redundant content** creating competing attention targets: each fact in one location -| Concern | File | Purpose | Loaded When | -|---|---|---|---| -| Navigation | `AGENTS.md` | Where files live, how to resolve wikilinks | Every session | -| Identity | `.opencode/agents/*.md` | Who I am, what I decide | When role invoked | -| Procedure | `.opencode/skills/*/SKILL.md` | Step-by-step instructions | On demand | -| Reference | `.opencode/knowledge/*/` | What and why | On demand, via wikilinks | +See [[knowledge-design/principles#concepts]] for the separation-of-concerns table and failure-mode justification. **Subagents for Investigation**: When a task requires extensive reading (auditing code, researching decisions), use a subagent with read-only or restricted permissions. Subagents quarantine token cost and prevent anchoring bias from the main conversation context. diff --git a/.opencode/knowledge/architecture/adr.md b/.opencode/knowledge/architecture/adr.md index 3fbf7300..913bd5a7 100644 --- a/.opencode/knowledge/architecture/adr.md +++ b/.opencode/knowledge/architecture/adr.md @@ -9,7 +9,7 @@ last-updated: 2026-04-29 ## Key Takeaways - ADRs document architecturally significant decisions: decisions that are hard to change and affect multiple components (Nygard, 2011). -- Each ADR follows a fixed structure: Status, Context, Decision, Reason, Alternatives, Consequences (Nygard, 2011). +- Each ADR follows a fixed structure: Status, Context, Interview, Decision, Reason, Alternatives Considered, Consequences (Nygard, 2011). - ADRs are append-only. Once written, they are never edited. Superseded ADRs get a new "Superseded by" reference, not a revision. - ADRs must be consistent with feature requirements. Every ADR should reference the Example criteria it addresses. - ADR risk assessment uses Probability × Impact classification (Boehm, 1991) to prioritise mitigation effort on the highest-exposure risks. @@ -18,7 +18,7 @@ last-updated: 2026-04-29 **Architecturally Significant**: A decision is architecturally significant if it affects multiple components, is hard to reverse, or constrains future choices (Nygard, 2011; Fowler, 2003). Choosing a database is architecturally significant. Choosing a variable name is not. When in doubt, write the ADR. -**ADR Structure**: Every ADR contains (Nygard, 2011): Status (Proposed, Accepted, Deprecated, Superseded), Context (the forces at play, the problem being solved), Decision (the choice made), Reason (why this choice over alternatives), Alternatives (other options considered and why they were rejected), Consequences (what changes because of this decision, both positive and negative). +**ADR Structure**: Every ADR contains (Nygard, 2011): Status (Proposed, Accepted, Deprecated, Superseded), Context (the forces at play, the problem being solved), Interview (stakeholder questions and answers that inform the decision), Decision (the choice made), Reason (why this choice over alternatives), Alternatives Considered (other options considered and why they were rejected), Consequences (what changes because of this decision, both positive and negative). **Append-Only Discipline**: ADRs capture the decision as it was made at the time. If understanding changes, write a new ADR that supersedes the old one. This preserves the history of architectural reasoning and prevents retroactive justification. @@ -34,9 +34,10 @@ last-updated: 2026-04-29 |---|---| | Status | Proposed, Accepted, Deprecated, or Superseded | | Context | What is the issue that we're seeing that is motivating this decision? | +| Interview | Stakeholder questions and answers that inform this decision | | Decision | What is the change that we're proposing/making? | | Reason | Why is this the best choice given the alternatives? | -| Alternatives | What other choices were considered and why were they rejected? | +| Alternatives Considered | What other choices were considered and why were they rejected? | | Consequences | What becomes easier or harder to do because of this change? | ### When to Write an ADR diff --git a/.opencode/knowledge/architecture/assessment.md b/.opencode/knowledge/architecture/assessment.md index dade649d..b8a8d1b3 100644 --- a/.opencode/knowledge/architecture/assessment.md +++ b/.opencode/knowledge/architecture/assessment.md @@ -54,12 +54,7 @@ When assessing architecture for a new feature: ### Hexagonal Architecture Verification -When reviewing existing architecture: - -- Every external dependency must have a Protocol (interface) in the domain layer -- The domain layer must have zero imports from infrastructure packages -- Adapters must implement domain-defined Ports, not the other way around -- If the domain references a concrete technology, it's a violation +Verify hexagonal architecture compliance per [[architecture/contract-design#concepts]]. ## Related diff --git a/.opencode/knowledge/architecture/technical-design.md b/.opencode/knowledge/architecture/technical-design.md index 202a1f26..62d67ced 100644 --- a/.opencode/knowledge/architecture/technical-design.md +++ b/.opencode/knowledge/architecture/technical-design.md @@ -15,25 +15,19 @@ last-updated: 2026-04-29 ## Concepts -**Architectural Styles**: Common styles and when to choose them: Monolith (single deployment, simple ops, low latency between modules), Microservices (independent deployment, team autonomy, high operational complexity), Event-driven (loose coupling, eventual consistency, async workflows), Serverless (pay-per-use, auto-scaling, cold starts), Hexagonal/Ports & Adapters (testability, domain isolation, delivery-mechanism independence). +**Architectural Styles**: Common styles and when to choose them per [[architecture/quality-attributes#concepts]]. **C4 Diagrams** (Brown, 2018): Four levels of architectural visualization: Context (actors and external systems), Container (deployable units and their tech stacks), Component (internal modules and their responsibilities), Code (individual classes, rarely needed). Always start with Context, then Container. Component diagrams are optional. Code diagrams are rarely necessary. -**Module Structure**: Organize by bounded context first (Evans, 2003), then by layer (domain, application, infrastructure). Domain layer has zero infrastructure imports. Application layer orchestrates use cases. Infrastructure layer implements external concerns. The dependency arrow always points inward: infrastructure → application → domain (Clean Architecture, Martin, 2012; Hexagonal Architecture, Cockburn, 2005). +**Module Structure**: Dependency direction and layer separation per [[architecture/contract-design#concepts]]. Organize by bounded context first (Evans, 2003), then by layer (domain, application, infrastructure). -**Contract-First Design**: Define the boundaries before the implementation: API contracts (request/response shapes, error codes, authentication), Event contracts (event names, payload schemas, ordering guarantees), Interface definitions (Protocol/abstract classes that the domain defines and infrastructure implements). +**Contract-First Design**: Per [[architecture/contract-design#concepts]]. ## Content ### Architectural Style Selection -| Quality Attribute Priority | Recommended Style | -|---|---| -| Simplicity, fast time-to-market | Monolith | -| Team autonomy, independent scaling | Microservices | -| Loose coupling, async workflows | Event-driven | -| Cost optimization, variable load | Serverless | -| Testability, domain isolation | Hexagonal | +Style selection table per [[architecture/quality-attributes#content]]. Hybrid approaches are valid: a monolith with hexagonal internals, or microservices with event-driven communication between them. diff --git a/.opencode/knowledge/design/color-systems.md b/.opencode/knowledge/design/color-systems.md index 3e499365..64ce06b8 100644 --- a/.opencode/knowledge/design/color-systems.md +++ b/.opencode/knowledge/design/color-systems.md @@ -18,7 +18,7 @@ last-updated: 2026-04-30 ## Concepts -**Monochrome-First Process**: Design the entire mark in black on white, then white on black. If it does not work in monochrome, colour will not save it. Only after the shape holds identity in one colour should a second colour be introduced, and only as an accent, never carrying meaning that must be read. +**Monochrome-First Process**: Design monochrome-first per [[design/project-assets#concepts]]; only add colour after the shape holds identity. **Hue, Saturation, and Value as Independent Axes**: Hue (which colour) is one dimension. Saturation (how vivid vs muted) and value (how light vs dark) carry as much meaning as hue and are independent levers. A single hue can express different personalities by varying saturation and value: - High saturation + medium value: "digital, energetic, modern" (e.g., #3B82F6) @@ -95,17 +95,7 @@ The other four (cold-warm, simultaneous, hue, extension) are documented by Itten ### Visual Weight Proportions (Itten's Contrast of Extension) -When balancing colour areas in a composition, visual weight depends on inherent brightness: - -| Colour pair | Visual weight ratio (area for equal perceived weight) | -|------------|------------------------------------------------------| -| Yellow : Violet | 1 : 3 | -| Orange : Blue | 1 : 2 | -| Red : Green | 1 : 1 | -| Yellow : Orange | 1 : 1.5 | -| Light grey : Dark navy | 1 : 2 | - -A thin gold line on a navy field reads as "balanced" because the yellow's visual weight per unit area is 3× the violet's. +Balance visual weight per [[design/visual-harmony#concepts]]. ### WCAG Contrast Verification Checklist diff --git a/.opencode/knowledge/design/project-assets.md b/.opencode/knowledge/design/project-assets.md index ea75de23..f24412da 100644 --- a/.opencode/knowledge/design/project-assets.md +++ b/.opencode/knowledge/design/project-assets.md @@ -41,20 +41,11 @@ last-updated: 2026-04-30 ### Logo Design Process -1. **Brief**: Written brief with project name, tagline, 3 personality adjectives, forbidden elements, 5 reference logos -2. **Sketch**: 20–40 rough concepts, black pen on white paper, monochrome only -3. **Select**: Pick top 3–5, refine in vector, still monochrome -4. **Colour**: Add 1 colour, then 2 maximum. Test on white, black, and mid-gray backgrounds -5. **Stress-test**: Apply evaluation checklist (5-second, blur, monochrome, scalability, proximity, "one thing") -6. **Deliver**: Export all sizes and formats per delivery checklist +The logo and banner follow a favicon-first, monochrome-first, progressive-simplification process per the `design-assets` skill. ### Banner Design Process -1. **Layout**: 1280×640px canvas (2:1 for social preview), centred content zone -2. **Elements**: Logo mark (left or centre), project name in large type, tagline optional -3. **Colours**: Use brand primary and background only. Accent sparingly for rules/dividers -4. **Dark variant**: Same layout, inverted palette, tested on dark backgrounds -5. **Export**: SVG for README, PNG at 1280×640 for GitHub social preview +The logo and banner follow a favicon-first, monochrome-first, progressive-simplification process per the `design-assets` skill. ### File Delivery Checklist diff --git a/.opencode/knowledge/design/visual-harmony.md b/.opencode/knowledge/design/visual-harmony.md index e9c336c8..af74b22d 100644 --- a/.opencode/knowledge/design/visual-harmony.md +++ b/.opencode/knowledge/design/visual-harmony.md @@ -53,6 +53,18 @@ last-updated: 2026-04-30 | Creative, expressive | Sans-serif humanist | 300–500 | Wide | Nunito, Quicksand | | Technical, engineering | Monospace or geometric | 400–600 | Normal | JetBrains Mono, Fira Code | +### Visual Weight Proportions (Itten's Contrast of Extension) + +| Colour pair | Visual weight ratio (area for equal perceived weight) | +|------------|------------------------------------------------------| +| Yellow : Violet | 1 : 3 | +| Orange : Blue | 1 : 2 | +| Red : Green | 1 : 1 | +| Yellow : Orange | 1 : 1.5 | +| Light grey : Dark navy | 1 : 2 | + +Use these ratios to determine element sizing: a smaller area of a visually heavier colour balances a larger area of a lighter colour. + ### Composition Decision Guide | Desired effect | Layout strategy | Grid | Where to place primary element | diff --git a/.opencode/knowledge/domain-modeling/behavioral-contracts.md b/.opencode/knowledge/domain-modeling/behavioral-contracts.md index 6c3e6965..b5d89e99 100644 --- a/.opencode/knowledge/domain-modeling/behavioral-contracts.md +++ b/.opencode/knowledge/domain-modeling/behavioral-contracts.md @@ -26,7 +26,7 @@ last-updated: 2026-05-14 **Contract vs Integration Point**: Integration Points describe context-to-context communication (how bounded contexts talk to each other). External Contracts describe how the outside world talks to THIS context. Both are in the domain spec but serve different purposes. -**Simulation Target**: The SA walks through each External Contract with happy/edge/error scenarios, mentally executing the contract against the spec. The contract's Input/Output fields provide the I/O shape for /tmp/sim/ evidence files. Pain points discovered here drive spec fixes. +**Simulation Target**: The SA walks through each External Contract with happy/edge/error scenarios, mentally executing the contract against the spec. The contract's Input/Output fields provide the I/O shape for .cache/sim/ evidence files. Pain points discovered here drive spec fixes. ## Content diff --git a/.opencode/knowledge/domain-modeling/domain-modeling.md b/.opencode/knowledge/domain-modeling/domain-modeling.md deleted file mode 100644 index 42c726a7..00000000 --- a/.opencode/knowledge/domain-modeling/domain-modeling.md +++ /dev/null @@ -1,77 +0,0 @@ ---- -domain: domain-modeling -tags: [ddd, bounded-contexts, entities, relationships, aggregates, domain-model] -last-updated: 2026-05-08 ---- - -# Domain Modeling - -## Key Takeaways - -- Domain modeling formalizes event storming candidates into bounded contexts, entities, relationships, aggregate boundaries, and a context map — the convergent synthesis after divergent exploration. -- Bounded contexts are linguistic boundaries where every term has one meaning. Formalize by clustering aggregates that share a ubiquitous language and separating where terms change meaning (Evans, 2003). -- Entities have identity and lifecycle (Slot, TrackedOrder, Position). Value objects have no identity — defined by attributes (Token, Pair, Orderbook). Only entities can be aggregate roots. -- Relationships capture composition (Orderbook composed of PriceLevels), dependency (Strategy uses MarketSnapshot), and domain flow (Strategy produces OrderAction). Cardinality (1:1, 1:N, M:N) constrains design. -- Aggregate boundaries define transactional consistency. Every invariant within an aggregate must hold after each transaction. Invariants spanning entities indicate they belong to the same aggregate. - -## Concepts - -**Formalization Process**: Event storming produces candidates (events, commands, aggregates, contexts). Domain modeling formalizes these into a structural specification. For each candidate context: identify entities from event/command payloads, classify as entities (have identity) or value objects (defined by attributes), determine relationships, and define aggregate boundaries from transactional consistency requirements. - -**Bounded Context Identification**: Contexts emerge from linguistic boundaries. A boundary exists when: the same term changes meaning, different consistency requirements apply, or independent deployment is needed. Each context documents: name, responsibility, key entities, business capability, why separate, and integration points. - -**Entity vs Value Object**: An entity has identity persisting across state changes (a Slot is the same Slot regardless of state). A value object has no identity — defined entirely by attribute values (PriceLevel at price=100, qty=5 equals any other with same values). Only entities can be aggregate roots. Value objects are always owned by an entity. - -**Relationship Extraction**: Relationships derive from: event flow (entity A produces an event entity B consumes), data flow (entity A composed from entity B's data), and domain constraints (a rule involves both entities). Three types: composition, dependency, domain flow. Cardinality constrains design: 1:1 enables direct reference, 1:N enables collection, M:N requires indirection. - -**Aggregate Boundary Determination**: An aggregate is the unit of transactional consistency. Group entities sharing invariants (business rules that must hold atomically). Split when: the aggregate exceeds memory, transactions span multiple aggregates, or different parts have different consistency requirements. One aggregate per transaction; cross-aggregate references use identity only. - -## Content - -### Formalization Steps - -1. **List entity candidates from events and commands**: Each event's subject is an entity candidate. Each command's target is an entity candidate. Each read model references entity candidates. Deduplicate across events. - -2. **Classify each candidate**: Entity if it has identity (ID or natural key) and lifecycle. Value object if defined by attributes and immutable. - -3. **Determine relationships**: For each pair, classify: composition (A contains B), dependency (A uses B), or domain flow (A produces output for B). Determine cardinality: 1:1, 1:N, M:N. - -4. **Define aggregate boundaries**: Group entities sharing invariants. Document root entity, invariants, and business reason for grouping per [[domain-modeling/event-storming#key-takeaways]]. - -5. **Identify context boundaries**: Group aggregates sharing a ubiquitous language. Document: name, responsibility, key entities, capability, why separate, integration points. - -6. **Map context relationships**: Classify each inter-context relationship per [[domain-modeling/context-mapping#key-takeaways]]. Document upstream, downstream, pattern, and any anti-corruption layer. - -### Entity Table Format - -| Field | Purpose | -|---|---| -| Name | PascalCase entity name | -| Type | Entity or Value Object | -| Description | What it represents | -| Bounded Context | Owning context | -| Aggregate Root? | Yes if root, — if value object | - -### Aggregate Boundary Table Format - -| Field | Purpose | -|---|---| -| Aggregate | PascalCase name | -| Root Entity | Identity root | -| Invariants | Business rules enforced | -| Why Grouped | Business reason for boundary | -| Bounded Context | Owning context | - -### Boundary Decision Heuristics - -- If an invariant references two entities, they share an aggregate -- If an aggregate exceeds memory, split and accept eventual consistency -- If two contexts use the same term with different definitions, they are separate contexts -- If removing one aggregate doesn't change term meanings in another, they are separate contexts -- If a rule can be checked eventually (not immediately), entities may be in different aggregates - -## Related - -- [[domain-modeling/event-storming]] -- [[domain-modeling/context-mapping]] -- [[requirements/ubiquitous-language]] diff --git a/.opencode/knowledge/domain-modeling/event-storming.md b/.opencode/knowledge/domain-modeling/event-storming.md index e6425f17..9dd8c554 100644 --- a/.opencode/knowledge/domain-modeling/event-storming.md +++ b/.opencode/knowledge/domain-modeling/event-storming.md @@ -12,7 +12,7 @@ last-updated: 2026-05-08 - Domain events are facts expressed in past tense (OrderPlaced, FillDetected). Extract from interview transcripts by scanning for business-relevant state changes and outcome statements. - Commands are intents in imperative (PlaceOrder, DetectFill). Each command has an actor, preconditions, and produces zero or more events. Read models are the decision information needed before executing a command. - Hotspots mark conflicts, ambiguities, or disagreements. They are not resolved during event storming — they are flagged for stakeholder follow-up and indicate context boundary candidates. -- Candidate bounded contexts emerge from clustering related events. Candidate aggregates emerge from grouping events that must be transactionally consistent. +- Candidate bounded contexts emerge from clustering related events. Candidate aggregates emerge from grouping events that must be transactionally consistent. Formalization converts these candidates into entity tables, aggregate boundaries, and context maps. ## Concepts @@ -28,6 +28,51 @@ last-updated: 2026-05-08 ## Content +### Formalization + +Event storming produces candidates. Formalization converts candidates into structural specification artifacts. + +**Entity vs Value Object**: An entity has identity persisting across state changes (a Slot is the same Slot regardless of state). A value object has no identity — defined entirely by attribute values (PriceLevel at price=100, qty=5 equals any other with same values). Only entities can be aggregate roots. Value objects are always owned by an entity. + +**Relationship Extraction**: Relationships derive from: event flow (entity A produces an event entity B consumes), data flow (entity A composed from entity B's data), and domain constraints (a rule involves both entities). Three types: composition, dependency, domain flow. Cardinality constrains design: 1:1 enables direct reference, 1:N enables collection, M:N requires indirection. + +**Formalization Steps**: + +1. List entity candidates from events and commands. Each event's subject is an entity candidate. Each command's target is an entity candidate. Deduplicate across events. +2. Classify each candidate: entity if it has identity and lifecycle; value object if defined by attributes and immutable. +3. Determine relationships: composition (A contains B), dependency (A uses B), or domain flow (A produces output for B). Cardinality: 1:1, 1:N, M:N. +4. Define aggregate boundaries: group entities sharing invariants. Document root entity, invariants, and business reason for grouping. +5. Identify context boundaries: group aggregates sharing a ubiquitous language. Document name, responsibility, key entities, capability, why separate, integration points. +6. Map context relationships per [[domain-modeling/context-mapping#key-takeaways]]. + +**Entity Table Format**: + +| Field | Purpose | +|---|---| +| Name | PascalCase entity name | +| Type | Entity or Value Object | +| Description | What it represents | +| Bounded Context | Owning context | +| Aggregate Root? | Yes if root, — if value object | + +**Aggregate Boundary Table Format**: + +| Field | Purpose | +|---|---| +| Aggregate | PascalCase name | +| Root Entity | Identity root | +| Invariants | Business rules enforced | +| Why Grouped | Business reason for boundary | +| Bounded Context | Owning context | + +**Boundary Decision Heuristics**: + +- If an invariant references two entities, they share an aggregate +- If an aggregate exceeds memory, split and accept eventual consistency +- If two contexts use the same term with different definitions, they are separate contexts +- If removing one aggregate doesn't change term meanings in another, they are separate contexts +- If a rule can be checked eventually (not immediately), entities may be in different aggregates + ### Phase 1: Chaotic Exploration Goal: surface ALL domain events without judgment or ordering. @@ -99,7 +144,6 @@ Record as: `Candidate Aggregate: [name] — [events] — [commands] — [consist ## Related -- [[domain-modeling/domain-modeling]] - [[domain-modeling/context-mapping]] - [[requirements/ubiquitous-language]] - [[requirements/interview-techniques]] diff --git a/.opencode/knowledge/requirements/decomposition.md b/.opencode/knowledge/requirements/decomposition.md index 723fa647..57a5a828 100644 --- a/.opencode/knowledge/requirements/decomposition.md +++ b/.opencode/knowledge/requirements/decomposition.md @@ -1,31 +1,86 @@ --- domain: requirements tags: [decomposition, splitting, features, rules] -last-updated: 2026-04-29 +last-updated: 2026-05-17 --- # Feature Decomposition Rules ## Key Takeaways -- If a feature spans more than 2 distinct concerns, split it immediately into separate features. -- If a feature has more than 8 candidate Examples, split the Rule immediately. -- If Musts alone exceed 8 Examples after MoSCoW triage, split the Rule. +- Split triggers apply after grouping Examples by behavioral outcome and collapsing parameterized variants into Scenario Outlines, not on raw Example counts. +- If a Rule has >8 Must behaviors after MoSCoW triage and Scenario Outline collapse, split the Rule. +- If a Rule spans >1 bounded context, split immediately — bounded contexts are the decomposition unit, not "concerns." +- If a Rule behavioral paragraph contains "and" joining two independently testable outcomes, split into two Rules. +- Scenario Outlines count as one behavioral test for decomposition, regardless of Examples table row count. - A completed feature whose description or Rules changed must move back to backlog. -- Rules containing "and" must be split into two separate Rules. ## Concepts -**Immediate Split on Threshold**: When the PO detects >2 distinct concerns OR >8 candidate Examples, split immediately. Record the split in the session notes, create feature files for both parts, and continue. +**Behavioral Counting Before Threshold**: Raw Example count is meaningless for decomposition. Examples that share the same `Then` outcome test the same behavior (Wynne, 2015; Adzic, 2011). Group by `Then` outcome first, then collapse groups of 3+ value variants into Scenario Outlines per [[requirements/gherkin#concepts]]. Only the resulting behavioral group count feeds the decomposition threshold. -**Decomposition Check During Baselining**: A feature may only be baselined when it does not span >2 concerns AND does not have >8 candidate Examples. The flow enforces INVEST-S (Small) and INVEST-I (Independent), which subsume the decomposition check. +**Split Threshold**: After MoSCoW triage and Scenario Outline collapse, if a Rule's Must behaviors exceed 8, the Rule is too broad. Split it. This threshold fires at refine-features time (define-flow), when Rule structure can still be changed. During write-bdd-features (develop-flow), Rule structure is frozen and >8 Must Examples is a soft flag for PO review, not a split trigger. -**Rule-Level Splits**: If a Rule contains "and" → break it into two Rules. If a Rule duplicates another Rule → merge or differentiate. If a Rule spans multiple unrelated concerns → split immediately. "As the system, I want..." → no business value, must reframe. +**Context-Based Decomposition**: "Concern" is ambiguous — bounded context is not. Every bounded context is explicitly defined in `domain_spec.md`. If a Rule touches entities from two or more bounded contexts, split along context boundaries per [[requirements/feature-boundaries#concepts]]. Exceptions are documented there. + +**Rule-Level Splits**: Three structural triggers apply regardless of Example count: +1. "and" joining two independently testable outcomes → split. "Create offline AND sync later" is one sequential workflow (keep). "Plant must have a name AND a care profile" is two independent validations (split). +2. Duplicate Rule → merge or differentiate. +3. Rule with no beneficiary → every Rule must name who benefits; system-only rules lack business value. + +**Scenario Outline Collapse**: A Scenario Outline with 10 Examples table rows is one behavioral test, not 10. The decomposition threshold counts Scenario Outlines as one unit each. This prevents false splits when a Rule correctly uses parameterization to verify the same outcome across many inputs. **Completed Feature Regression**: If a completed feature's description or Rules changed, move it back to backlog. Description changes always imply behaviour changes. +## Content + +### Decomposition Decision Tree + +``` +When evaluating a Rule for decomposition: + +1. GROUP candidate Examples by Then-outcome. + Same Then = same behavior per [[requirements/gherkin#concepts]]. + +2. COLLAPSE groups with 3+ value variants into Scenario Outlines. + Each Scenario Outline = 1 behavioral test. + +3. COUNT distinct behavioral groups (plain Examples + Scenario Outlines). + +4. TRIAGE via MoSCoW per [[requirements/moscow#concepts]]. + Count only Must behaviors. + +5. CHECK structural triggers (apply regardless of count): + - Rule spans >1 bounded context → split along context boundaries + - "and" joins independently testable outcomes → split + - Rule has no beneficiary → reframe to name who benefits + - Duplicate Rule → merge or differentiate + +6. CHECK Must-behavior threshold: + - >8 Must behaviors → split Rule + - ≤8 → Rule is well-sized, no split needed +``` + +### Split Decision Examples + +| Rule | Raw Examples | After Group + Collapse | Must Behaviors | Split? | Why | +|------|-------------|----------------------|----------------|--------|-----| +| "Order rejected for invalid input" | 15 | 1 Scenario Outline | 1 | No | 15 variants of same Then-outcome → 1 Scenario Outline | +| "Plant must have name and care profile" | 6 | 2 Examples | 2 | Yes | "and" joins two independent validations → 2 Rules | +| "User registration" | 12 | 8 Examples + 1 Scenario Outline | 6 | No | 9 behavioral groups, 6 Musts → under threshold | +| "Care event lifecycle" (spans Plant Catalog + Care Logging) | 5 | 5 Examples | 4 | Yes | >1 bounded context → split along context lines | + +### When Decomposition Fires + +| Phase | Action | Trigger | +|-------|--------|---------| +| refine-features (define-flow) | Hard split | >8 Must behaviors, >1 context, "and" joins independent outcomes | +| write-bdd-features (develop-flow) | Soft flag for PO | >8 Must Examples — Rule structure is frozen | +| create-py-stubs (develop-flow) | Verify only | Confirm decomposition was applied during define-flow | + ## Related - [[requirements/invest]]: INVEST criteria that enforce decomposition -- [[requirements/moscow]]: MoSCoW triage thresholds that trigger splits -- [[requirements/gherkin]]: writing Examples for decomposed Rules \ No newline at end of file +- [[requirements/moscow]]: MoSCoW triage thresholds that feed the Must count +- [[requirements/gherkin]]: Scenario Outline format and behavioral distinctness +- [[requirements/feature-boundaries]]: feature-level split along context and aggregate lines diff --git a/.opencode/knowledge/requirements/feature-boundaries.md b/.opencode/knowledge/requirements/feature-boundaries.md index 512e0541..db0c62a9 100644 --- a/.opencode/knowledge/requirements/feature-boundaries.md +++ b/.opencode/knowledge/requirements/feature-boundaries.md @@ -50,7 +50,6 @@ When a delivery step spans multiple contexts or aggregates: |--------|-------|---------------| | Spans 2+ bounded contexts | Split along context boundaries | Shared-kernel types (Domain shared), Orchestrator, Tightly coupled co-deployed | | Spans 2+ aggregates | Split along aggregate boundaries | If aggregates must be transactionally consistent | -| Has >2 distinct concerns | Split into separate features per concern | If concerns are inseparable aspects of one capability | | Delivery step name contains "and" | Likely two features | If "and" joins inseparable aspects | ### Cross-Cutting Concern Mapping @@ -66,6 +65,5 @@ Example: Kill switch behavior appears in Engine (lifecycle control) and Order Ex ## Related - [[requirements/feature-discovery]] -- [[requirements/rule-derivation]] - [[requirements/decomposition]] -- [[domain-modeling/domain-modeling]] \ No newline at end of file +- [[domain-modeling/event-storming]] diff --git a/.opencode/knowledge/requirements/feature-discovery.md b/.opencode/knowledge/requirements/feature-discovery.md index 6960de15..db81996e 100644 --- a/.opencode/knowledge/requirements/feature-discovery.md +++ b/.opencode/knowledge/requirements/feature-discovery.md @@ -8,16 +8,16 @@ last-updated: 2026-05-14 ## Key Takeaways -- Feature discovery synthesizes analysis artifacts (domain spec, simulation results, domain spec, product definition, glossary) into coherent feature boundaries with scoped business rules. It is a genuine analysis step, not mechanical transcription. +- Feature discovery synthesizes analysis artifacts (domain spec, product definition, glossary) into coherent feature boundaries with scoped business rules. It is a genuine analysis step, not mechanical transcription. - Each feature captures coarse business rules: one-line statements of behavior that the feature must enforce or enable. These are simulation-validated behavioral statements, not hypotheses. - Feature boundaries respect bounded context borders, aggregate transactional boundaries, and module dependency order per [[requirements/feature-boundaries]]. Features that span boundaries are flagged for splitting. -- Rules are written directly to .feature files during simulation. Planning refines: splitting, renaming, adding behavior hints. -- Gaps discovered during feature discovery (a bounded context with no feature, a quality attribute with no enforcing feature) are flagged, not silently filled. -- Features progress through a lifecycle of increasing specificity: Rule blocks from simulation → behavior hints → Example blocks and Scenario Outlines. +- Rules are written directly to .feature files during simulation. Refine-features (in define-flow) splits, renames, adds descriptions, and validates rules. After define-flow, Feature titles, Rule titles, and Constraints are FROZEN. +- Gaps discovered during feature refinement (a bounded context with no feature, a quality attribute with no enforcing feature) are flagged, not silently filled. +- Features progress through a lifecycle: Rule blocks from simulation → feature boundaries and descriptions from refine-features → Example blocks from feature-examples. ## Concepts -**Feature Boundary Identification**: Deciding where one feature ends and another begins is a design judgment using the delivery order (from product_definition.md) validated against the context map as backbone (Patton, 2014), cross-checked against bounded context and aggregate boundaries from the domain spec. Each delivery step becomes a feature candidate; candidates spanning multiple contexts or aggregates are split per [[requirements/feature-boundaries]]. +**Feature Boundary Identification**: Feature boundary identification follows the rules defined in [[requirements/feature-boundaries#concepts]]. **Rule Organization from .feature Files**: Rules are written directly to .feature files during simulation, grouped by bounded context. Feature discovery redistributes Rule blocks when feature files are split or renamed. IF a rule spans multiple features → flag for cross-cutting handling. @@ -26,9 +26,9 @@ last-updated: 2026-05-14 **Gap Analysis**: Systematically verify coverage across three dimensions: (1) every bounded context from the domain spec is covered by at least one feature, (2) every quality attribute from the product definition is enforced by at least one feature's constraints, (3) every discovered rule is mapped to at least one feature. Uncovered areas indicate missing features or gaps in the spec itself. Flag both. **Feature Lifecycle**: Features follow a lifecycle of increasing specificity across phases: -1. **Discovery**: Feature boundaries identified from simulation-created .feature files. Features are split/renamed based on Delivery Order and context map. Rule blocks are redistributed (content unchanged). Quality attributes mapped to # Constraints:. -2. **Breakdown**: Rules validated against INVEST, behavior hints added. -3. **Example Writing and Baseline**: Behavior hints converted to Gherkin Example/Scenario Outline blocks. +1. **Simulation** (define-flow): Rules written directly to .feature files during simulation, grouped by bounded context. Technology constraints written as `# Constraints:`. +2. **Refinement** (define-flow, refine-features): Feature boundaries identified from simulation-created .feature files. Features are split/renamed based on Delivery Order and context map. Rule blocks redistributed (content unchanged). Quality attributes mapped to `# Constraints:`. Rule titles validated for INVEST. Feature titles and descriptions finalized. +3. **Example Writing** (develop-flow, feature-examples): Rules converted to Gherkin Example/Scenario Outline blocks with pre-mortem analysis. ## Content @@ -36,9 +36,9 @@ last-updated: 2026-05-14 Feature discovery is two sequential activities: -1. **Boundary identification** (discover-features skill): Use the delivery order from product_definition.md as backbone. Map each step to bounded contexts and aggregates from the domain spec. Split candidates that span contexts or aggregates. Name features and write descriptions per [[requirements/feature-boundaries]]. +1. **Boundary identification** (refine-features skill): Use the delivery order from product_definition.md as backbone. Map each step to bounded contexts and aggregates from the domain spec. Split candidates that span contexts or aggregates. Name features and write descriptions per [[requirements/feature-boundaries]]. -2. **Rule organization** (discover-rules skill): For each feature, organize the Rule blocks already written to .feature files during simulation. Split/rename Rules per context boundaries. Map quality attributes to constraints. Write `# Constraints:` into each .feature file. +2. **Rule organization** (refine-features skill): For each feature, organize the Rule blocks already written to .feature files during simulation. Split/rename Rules per context boundaries. Map quality attributes to constraints. Write `# Constraints:` into each .feature file. Validate INVEST criteria. ### Gap Analysis Procedure diff --git a/.opencode/knowledge/requirements/gherkin.md b/.opencode/knowledge/requirements/gherkin.md index 04a18a3b..189ad542 100644 --- a/.opencode/knowledge/requirements/gherkin.md +++ b/.opencode/knowledge/requirements/gherkin.md @@ -170,7 +170,7 @@ Implement both: - Imperative UI steps instead of declarative behaviour descriptions - Two Examples with the same `Then` but different input values: duplicate behaviour coverage per [[requirements/gherkin#concepts]] - Duplicate Example titles within a feature file: breaks pytest-beehave mapping -- Titles shorter than 3 words or longer than 8 words: produces ambiguous or unwieldy test identifiers +- Titles shorter than 2 words or longer than 6 words: produces ambiguous or unwieldy test identifiers - Using Scenario Outline for single-value or two-value cases: over-engineering, use plain Example instead - Using `Scenario:` keyword: use `Example:` for single cases or `Scenario Outline:` for parameterized cases - Placeholder names that are Python keywords or builtins: beehave rejects these at parse time diff --git a/.opencode/knowledge/requirements/invest.md b/.opencode/knowledge/requirements/invest.md index 36d08632..541704d6 100644 --- a/.opencode/knowledge/requirements/invest.md +++ b/.opencode/knowledge/requirements/invest.md @@ -10,7 +10,7 @@ last-updated: 2026-04-29 - Every Rule must pass all six INVEST letters: Independent, Negotiable, Valuable, Estimable, Small, Testable. - Each letter has a specific FAIL action: split or reorder dependencies (I), remove over-specification (N), reframe or drop (V), split or add discovery (E), split into smaller Rules (S), rewrite with observable outcomes (T). -- Common mistakes: "As the system, I want..." has no business value; duplicate Rules should be merged or differentiated. Rules containing "and" or spanning multiple concerns must be split per [[requirements/decomposition]]. +- Common mistakes: Rules with no beneficiary lack business value; duplicate Rules should be merged or differentiated. Split triggers per [[requirements/decomposition#key-takeaways]]. - Self-declare INVEST-I, INVEST-V, INVEST-S, and INVEST-T; every DISAGREE is a hard blocker. ## Concepts @@ -19,7 +19,7 @@ last-updated: 2026-04-29 **FAIL Actions**: Each letter has a specific corrective action when a Rule fails. Independent → split or reorder dependencies. Negotiable → remove over-specification. Valuable → reframe or drop. Estimable → split or add discovery questions. Small → split into smaller Rules. Testable → rewrite with observable outcomes. -**Self-Declaration**: Declare INVEST-I (each Rule is Independent), INVEST-V (each Rule delivers Value to a named user), INVEST-S (each Rule is Small enough for one development cycle), and INVEST-T (each Rule is Testable). Every DISAGREE is a hard blocker. The procedural steps for self-declaration are in the break-down-feature skill. +**Self-Declaration**: Declare INVEST-I (each Rule is Independent), INVEST-V (each Rule delivers Value to a named user), INVEST-S (each Rule is Small enough for one development cycle), and INVEST-T (each Rule is Testable). Every DISAGREE is a hard blocker. The procedural steps for self-declaration are in the refine-features skill. ## Content @@ -36,8 +36,9 @@ last-updated: 2026-04-29 ### Common Mistakes to Avoid -- "As the system, I want...": no business value. Every Rule must name a user role who benefits. -- Stories that duplicate another Rule: merge or differentiate. +- Rule with no beneficiary: no business value. Every Rule must name a user role who benefits. +- Duplicate Rules: merge or differentiate. +- Split triggers (bounded context, independently testable outcomes): per [[requirements/decomposition#key-takeaways]] ### Self-Declaration Protocol Before committing Rules, declare: @@ -52,6 +53,6 @@ Every DISAGREE is a hard blocker. ## Related - [[requirements/moscow]]: prioritizing Examples within a Rule -- [[requirements/decomposition]]: splitting Rules that fail INVEST-S or span too many concerns +- [[requirements/decomposition]]: splitting Rules that fail INVEST-S or span bounded contexts - [[requirements/gherkin]]: writing Examples from INVEST-qualified Rules - [[requirements/pre-mortem]]: finding hidden failure modes in rules \ No newline at end of file diff --git a/.opencode/knowledge/requirements/moscow.md b/.opencode/knowledge/requirements/moscow.md index b5e09e74..949b3256 100644 --- a/.opencode/knowledge/requirements/moscow.md +++ b/.opencode/knowledge/requirements/moscow.md @@ -9,9 +9,8 @@ last-updated: 2026-04-29 ## Key Takeaways - Classify each candidate Example as Must (required for correctness), Should (high value but deferrable), or Could (nice-to-have edge case). This classification is for internal triage only: it must NOT appear as Gherkin tags or in the .feature file. -- If MoSCoW triage reveals that Musts alone exceed 8 Examples or the Rule spans more than 2 concerns, split per [[requirements/decomposition]]. +- If MoSCoW triage reveals that Must behaviors exceed 8 after Scenario Outline collapse, split per [[requirements/decomposition]]. - Musts cannot exceed 60% of total effort at the Rule level (DSDM); if a Rule has 12 Examples and only 3 are Musts, the remaining 9 can be deferred. -- MoSCoW triage classifies Examples for internal prioritization only: it must NOT appear as Gherkin tags or in the .feature file. ## Concepts @@ -21,7 +20,7 @@ last-updated: 2026-04-29 **Could**: Nice-to-have edge case. Low risk if deferred. Could Examples cover unusual conditions or minor enhancements that improve robustness. -**Split Trigger**: If Musts alone exceed 8 Examples or the Rule spans more than 2 concerns, the decomposition rules in [[requirements/decomposition]] apply. MoSCoW triage surfaces the signal; decomposition handles the split. +**Split Trigger**: If Must behaviors exceed 8 after MoSCoW triage and Scenario Outline collapse, the decomposition rules in [[requirements/decomposition]] apply. MoSCoW triage surfaces the signal; decomposition handles the split. ## Content diff --git a/.opencode/knowledge/requirements/rule-derivation.md b/.opencode/knowledge/requirements/rule-derivation.md deleted file mode 100644 index a4395be3..00000000 --- a/.opencode/knowledge/requirements/rule-derivation.md +++ /dev/null @@ -1,57 +0,0 @@ ---- -domain: requirements -tags: [rule-derivation, discovered-rules, invariants] -last-updated: 2026-05-14 ---- - -# Rule Derivation - -## Key Takeaways - -- Rules come from two sources: simulation-discovered rules (now written directly to .feature files) and structural rules from the domain spec (invariants, relationships, aggregate boundaries). -- Simulation rules are descriptive statements discovered by walking External Contracts through happy/edge/error scenarios. Each discovered rule traces to the walkthrough and External Contract that produced it. -- Structural rules come from aggregate invariants: "[Entity] must always [condition]." These are non-negotiable consistency boundaries. -- Quality attributes from product_definition.md constrain rules: each attribute produces at least one Constraint that bounds the feature's behavior with a measurable threshold. - -## Concepts - -**Simulation-Discovered Rules**: The primary source of behavioral rules. As the SA walks through External Contracts in the domain spec with happy/edge/error walkthroughs, rules emerge about what the system must do, must not do, and what happens when things go wrong. These rules are written as Rule blocks in .feature files during simulation. - -**Structural Rules from Domain Spec**: Aggregate invariants from the domain spec produce structural rules — non-negotiable consistency boundaries. These rules are already present in the domain spec and are written to .feature files alongside discovered rules during simulation. - -**Quality Attribute -> Constraint Mapping**: Each quality attribute in product_definition.md (latency, reliability, safety) constrains feature behavior. Map each attribute to the feature(s) responsible for enforcing it. If no feature enforces a quality attribute, it is a gap. Constraints include measurable thresholds: "Latency: tick-to-order under 100ms", "Reliability: no orphaned orders after crash." - -**Rule Distribution**: Rules are written directly to .feature files during simulation, grouped by bounded context and entities. IF a discovered rule spans multiple features → flag for cross-cutting handling. Every rule must be assigned to at least one feature. Gaps indicate missing features or incomplete simulation. - -## Content - -### Simulation-to-Feature Flow - -During simulation, for each bounded context walked: - -**Step 1: Write discovered rules to .feature files.** As the SA walks through each bounded context, discovered rules are written as Rule blocks directly into the .feature file corresponding to that context's entities. No separate distribution step is needed. - -**Step 2: Assign structural rules from domain spec.** For each aggregate invariant in the domain spec, assign it to the feature that contains the aggregate root. These are non-negotiable consistency boundaries. - -**Step 3: Map quality attributes to constraints.** For each quality attribute in product_definition.md: -- Which feature(s) enforce this attribute? → Add Constraint to those features -- Include measurable threshold from the quality attribute -- If no feature enforces it → flag in interview notes as a gap - -**Step 4: Write constraints to .feature files.** Write each constraint under `# Constraints:` in the relevant .feature file. - -### Traceability Verification - -After distributing rules for all features, verify: - -1. **Every discovered rule → at least one feature.** If a rule has no feature, either it's out of scope or a feature is missing. Discovered rules originate in .feature files created during simulation. -2. **Every aggregate invariant → at least one feature.** If an invariant has no feature, flag it as a gap. -3. **Every quality attribute → at least one constraint.** If a quality attribute has no enforcing feature, flag it as a gap. -4. **Every feature → at least one business rule.** If a feature has no rules, flag it as potentially unnecessary. - -## Related - -- [[requirements/spec-simulation]]: how rules are discovered during simulation -- [[requirements/feature-discovery]] -- [[requirements/feature-boundaries]] -- [[domain-modeling/behavioral-contracts]]: External Contracts that simulation walks diff --git a/.opencode/knowledge/requirements/spec-architecture.md b/.opencode/knowledge/requirements/spec-architecture.md deleted file mode 100644 index 1c8d11b2..00000000 --- a/.opencode/knowledge/requirements/spec-architecture.md +++ /dev/null @@ -1,118 +0,0 @@ ---- -domain: requirements -tags: [spec-architecture, single-source-of-truth, progressive-refinement, terminology] -last-updated: 2026-05-15 ---- - -# Specification Architecture - -## Key Takeaways - -- Behavioral rules live in `.feature` files as the single source of truth. Domain spec (`domain_spec.md`) is structural only. Simulation results (`simulation_results.md`) is pain point tracking only. -- One .feature file per bounded context during simulation, refined into shippable features during planning. No rule exists in more than one file. -- The .feature file is progressively refined: simulation writes Rule blocks → feature-discovery renames/redistributes → feature-breakdown adds behavior hints → feature-examples writes Gherkin. -- Standardized terminology: walkthrough (simulation path), discovered rule (simulation finding), Rule block (Gherkin Rule:), behavior hint (pre-Gherkin outcome), Example block (Gherkin Example:). -- Spec documents are read-only during development. Issues are flagged, not fixed directly. - -## Concepts - -**Single Source of Truth**: Each behavioral requirement (Rule) lives in exactly one `.feature` file. The domain spec references `.feature` rules as derived summaries — not as authoritative copies. Pain points live in `simulation_results.md`. No triplicate storage. - -**Progressive Refinement Chain**: A `.feature` file is never written in one pass. It accumulates content across states: - -| State | Added | Removed | -|-------|-------|---------| -| `simulate-spec` | `Feature:` title + description, `Rule:` title + behavioral description | — | -| `feature-discovery` | Updated `Feature:` title + description (if split/renamed), `# Constraints:` comments | — | -| `feature-breakdown` | `Behavior hints:` under each Rule | — | -| `feature-examples` | `Example:` / `Scenario Outline:` blocks | `Behavior hints:` section | - -**Artifact Purposes**: - -| Artifact | Contains | Does NOT contain | -|----------|----------|-----------------| -| `domain_spec.md` | Entities, aggregates, data shapes, integration points, external contracts, context map. Derived summaries of state machines, error handling, invariants referencing .feature rules. | Behavioral rules as primary source. Given/When/Then. | -| `simulation_results.md` | Walkthroughs performed (with Discovered Rule column), I/O evidence references, pain points, resolution status, PASS/FAIL. | Rules Discovered. E2E Test Candidates. | -| `.feature` | `Feature:` title + description, `Rule:` blocks, `# Constraints:`, `Example:`/`Scenario Outline:` blocks. | Structural domain model. Pain points. State machines. | - -**Feature Boundary Discovery**: Simulation creates one `.feature` per bounded context. During planning, `feature-discovery` splits/renames coarse `.feature` files into shippable features using the delivery order from `product_definition.md` validated against the context map. Rule blocks are redistributed but content is never edited — only the SA (simulation) and reviewer (fix-spec) may change Rule descriptions. - -## Content - -### Artifact Dependency Graph - -``` -discovery-flow - domain_spec.md ──────────────────────────────────────────┐ - product_definition.md ───────────────┐ │ - glossary.md ─────────────────────────┤ │ - │ │ -spec-validation-flow │ │ - domain_spec.md ◄─────────────────────┤ (structural only) │ - product_definition.md ◄──────────────┤ │ - glossary.md ◄────────────────────────┤ │ - features/.feature ──────────┤ (Rule blocks) │ - simulation_results.md ───────────────┤ (pain points) │ - │ │ -planning-flow │ │ - features/.feature ◄─────────┤ (coarse input) │ - domain_spec.md ◄─────────────────────┘ │ - product_definition.md ◄──────────────┘ - ↓ - features/.feature (refined: splits, behavior hints, Gherkin) -``` - -### Terminology Map - -| Project Term | Gherkin Equivalent | Old (Deprecated) | -|-------------|-------------------|-----------------| -| walkthrough | — | scenario, simulation scenario | -| discovered rule | — | business rule, coarse rule | -| Rule / Rule block | `Rule:` | story, user story | -| behavior hint | — | `Scenarios:`, `# Business rules:` | -| Example block | `Example:` | BDD scenario, acceptance test | -| Scenario Outline block | `Scenario Outline:` | (unchanged — Gherkin keyword) | -| pain point | — | (unchanged) | - -### Rule Lifecycle - -``` -simulate-spec feature-discovery feature-breakdown feature-examples - │ │ │ │ - ▼ ▼ ▼ ▼ -Rule: Rule: <title> Rule: <title> Rule: <title> - description description description description - [written by SA] [moved between Behavior hints: Example: <title> - files if split] - x → y Given/When/Then - # Constraints: - a → b Example: <title> - # - <threshold> [added by PO] Given/When/Then - [added by PO] [hints removed] -``` - -### Constraint Lifecycle - -Constraints in `# Constraints:` comment blocks come from two sources: - -- **Quality attributes** from `product_definition.md` — mapped to features by `feature-discovery` (e.g. latency, reliability, security thresholds). -- **Technology requirements** from `domain_spec.md` Technology Requirements table — written by `simulate-spec` during spec validation (e.g. DSPy typed signatures, PostgreSQL persistence, gRPC service layer). - -**Writer**: Quality attribute constraints → `feature-discovery` (PO). Technology constraints → `simulate-spec` (SA). -**Format**: `# Constraints:` comment block with `# - <constraint>` bullets. Each constraint carries a verifiable claim. -**Enforcement**: `review-gate` Tier 1 verifies constraints by executing the domain_spec.md Verification instruction. `accept-feature` traces technology Q&As to constraint evidence. Constraints are never converted to Examples. - -### Cleanup Gates - -| State | Gate | -|-------|------| -| `feature-discovery` | Coarse `.feature` files from simulation that were split: original files deleted or archived. No orphaned `.feature` files remain. | -| `feature-examples` | No `Behavior hints:` text remains in the `.feature` file. All hints converted to Gherkin. | -| `spec-review` | `# Constraints:` present for every quality attribute mapped to this feature. | - -## Related - -- [[requirements/gherkin]]: Gherkin specification format and conventions -- [[requirements/spec-simulation]]: spec simulation walkthrough procedure -- [[requirements/rule-derivation]]: rule sources and distribution -- [[requirements/feature-discovery]]: feature boundary identification -- [[requirements/feature-boundaries]]: deriving feature boundaries -- [[requirements/invest]]: INVEST criteria for Rule blocks diff --git a/.opencode/knowledge/requirements/spec-simulation.md b/.opencode/knowledge/requirements/spec-simulation.md index 25dfb444..52ab588e 100644 --- a/.opencode/knowledge/requirements/spec-simulation.md +++ b/.opencode/knowledge/requirements/spec-simulation.md @@ -9,7 +9,7 @@ last-updated: 2026-05-14 ## Key Takeaways - Spec simulation walks through domain spec walkthroughs mentally before any code is written, discovering rules, pain points, and E2E test candidates. The domain spec is monolithic: one file containing all bounded contexts as `## <Context Name>` sections. -- Simulation iterates over all bounded contexts in the single domain_spec.md file. I/O evidence is per-context in /tmp/sim/<context>/. These files are NOT committed — they are ephemeral evidence of the simulation process. +- Simulation iterates over all bounded contexts in the single domain_spec.md file. I/O evidence is per-context in .cache/sim/<context>/. These files are NOT committed — they are ephemeral evidence of the simulation process. - Pain points are classified as: ambiguous (multiple readings), contradictory (conflicting specs), missing (not covered at all), or edge-case (happy path covered but not this boundary). - Discovered rules are surfaced as plain-language behavioral statements — no numbered prefixes. Each discovered rule traces to the walkthrough that produced it. - The simulation loop (simulate → review → fix → re-simulate) caps at 5 iterations. If unresolved pain points remain, the flow escalates to needs-reinterview. @@ -27,7 +27,7 @@ last-updated: 2026-05-14 - **Missing**: the walkthrough is not covered by the spec at all. Fix: add the missing content (new entity fields, integration contracts, error paths). - **Edge-case**: the spec covers the happy path but not this boundary condition. Fix: add explicit handling (constraints, guards) or state out-of-scope. -**I/O Evidence**: Each simulated walkthrough produces a pair of JSON files in /tmp/sim/<context>/: +**I/O Evidence**: Each simulated walkthrough produces a pair of JSON files in .cache/sim/<context>/: - `walkthrough_<N>_in.json`: the input state and stimulus. - `walkthrough_<N>_out.json`: the expected output state and response. These files provide concrete examples for later test writing and prove the simulation was thorough. @@ -60,5 +60,4 @@ The reviewer decides PASS or FAIL based on: ## Related - [[domain-modeling/behavioral-contracts]]: the External Contracts that simulation targets -- [[requirements/rule-derivation]]: systematic rule sources (now supplemented by simulation discovery) - [[requirements/pre-mortem]]: adversarial analysis technique used during simulation diff --git a/.opencode/knowledge/requirements/ubiquitous-language.md b/.opencode/knowledge/requirements/ubiquitous-language.md index f74f88f2..79c690d3 100644 --- a/.opencode/knowledge/requirements/ubiquitous-language.md +++ b/.opencode/knowledge/requirements/ubiquitous-language.md @@ -75,5 +75,5 @@ When the same word has different meanings in different bounded contexts: ## Related - [[domain-modeling/event-storming]] -- [[domain-modeling/domain-modeling]] +- [[domain-modeling/event-storming]] - [[requirements/interview-techniques]] diff --git a/.opencode/knowledge/skill-design/principles.md b/.opencode/knowledge/skill-design/principles.md index 401d8b9c..c4c8f46e 100644 --- a/.opencode/knowledge/skill-design/principles.md +++ b/.opencode/knowledge/skill-design/principles.md @@ -99,11 +99,7 @@ When two skills need the same knowledge, both should reference the same knowledg ### Input Handling -Every skill that has `in` artifacts follows two rules: - -1. **Verify before proceeding.** Check that all `in` artifacts exist on disk. If any are missing, stop and flag the missing artifact rather than proceeding with assumed knowledge. This prevents the most common source of rework: building on assumptions about documents that don't exist or have been moved. - -2. **Read all `in` artifacts before starting work.** All `in` artifacts are mandatory context — read them in full before executing the skill's procedural steps. For wildcard patterns (`*.md`), list the directory first to discover what's available, then read all discovered files. The `in` list defines what you *must* read, not what you *may* optionally reference. +Verify input artifacts per AGENTS.md Session Protocol. ## Related diff --git a/.opencode/knowledge/software-craft/versioning.md b/.opencode/knowledge/software-craft/versioning.md index a3591961..8b680d18 100644 --- a/.opencode/knowledge/software-craft/versioning.md +++ b/.opencode/knowledge/software-craft/versioning.md @@ -40,5 +40,5 @@ last-updated: 2026-04-30 ## References -- [[process/preston-werner_2013]]: SemVer 2.0.0 specification -- [[process/calver_2020]]: Calendar Versioning convention \ No newline at end of file +- SemVer 2.0.0 specification (semver.org) +- Calendar Versioning convention (calver.org) \ No newline at end of file diff --git a/.opencode/knowledge/workflow/flowr-operations.md b/.opencode/knowledge/workflow/flowr-operations.md index c3d792ec..a72d99e1 100644 --- a/.opencode/knowledge/workflow/flowr-operations.md +++ b/.opencode/knowledge/workflow/flowr-operations.md @@ -31,7 +31,7 @@ last-updated: 2026-05-06 **Flow Name Resolution**: Commands accept short flow names (e.g., `architecture-flow`) or full file paths. Short names are resolved by searching the configured flows directory. -**Sessions**: Sessions persist workflow progress (current flow, state, call stack for subflows) as YAML files in `.flowr/sessions/`. Use `--session <name>` on check/next/transition to resolve flow and state from the session. `transition --session` auto-updates the session after advancing. `session init` auto-enters subflow when the first state has a `flow:` field. +**Sessions**: Sessions persist workflow progress (current flow, state, call stack for subflows) as YAML files in `.cache/sessions/`. Use `--session <name>` on check/next/transition to resolve flow and state from the session. `transition --session` auto-updates the session after advancing. `session init` auto-enters subflow when the first state has a `flow:` field. **Subflow Exit Resolution**: In flowr v1.0.0, exit names resolve through the parent flow's transition map rather than being used directly as state IDs. This enables subflow chaining (e.g., discovery-flow → architecture-flow) and recursive subflow entry (3-level nesting) without manual state manipulation. @@ -73,84 +73,11 @@ All commands output JSON by default. Add `--text` for human-readable output. ### Output Formats -**`check`** returns: -```json -{ - "id": "feature-selection", - "attrs": { - "description": "...", "owner": "PO", "git": "main", - "skills": ["select-feature"], "in": [...], "out": [...] - }, - "transitions": ["selected", "needs_architecture", "no_features"] -} -``` - -**`next`** returns all transitions with status markers: -```json -{ - "state": "feature-breakdown", - "transitions": [ - { - "trigger": "done", - "target": "feature-examples", - "status": "blocked", - "conditions": { - "independent": "==no_shared_data_or_side_effects", - "negotiable": "==scope_negotiated", - "valuable": "==user_value_clear" - } - }, - { - "trigger": "needs_respecification", - "target": "feature-breakdown", - "status": "open", - "conditions": null - } - ] -} -``` - -**`check <target>`** returns transition conditions: -```json -{ - "from": "feature-breakdown", - "target": "done", - "conditions": { - "independent": "==no_shared_data_or_side_effects", - "negotiable": "==scope_negotiated" - } -} -``` - -**`transition`** returns the computed state change: -```json -{ - "from": "feature-selection", - "trigger": "selected", - "to": "feature-breakdown" -} -``` - -**`config`** returns resolved configuration with sources: -```json -[ - {"key": "flows_dir", "value": ".flowr/flows", "source": "pyproject.toml"}, - {"key": "default_session", "value": "default", "source": "default"} -] -``` +JSON output format examples per [[workflow/flowr-spec#concepts]]. ### Configuration -flowr reads configuration from `[tool.flowr]` in `pyproject.toml`, falling back to defaults: - -| Key | Default | Description | -|-----|---------|-------------| -| `flows_dir` | `.flowr/flows` | Directory containing flow YAML files | -| `sessions_dir` | `.flowr/sessions` | Directory for session YAML files | -| `default_flow` | `main-flow` | Flow name used when none specified | -| `default_session` | `default` | Session name used when `--session` is given without a value | - -CLI `--flows-dir` overrides `pyproject.toml` which overrides defaults. +Configuration table per [[workflow/flowr-spec#content]]. ### Evidence Syntax @@ -182,7 +109,7 @@ For ongoing work, use sessions to track progress: ### Session Protocol Integration -- The `owner` field from `check` output determines which agent to dispatch to (PO → product-owner, SE → software-engineer, SA → system-architect, DE → domain-expert, R → reviewer, Design Agent → design-agent, Setup Agent → setup-agent). +- Owner dispatch mapping per AGENTS.md Session Protocol. - The `skills` field lists which skills to load and execute. - The `in` and `out` fields define the artifact contract: what you may read and what you may write. - Do not skip the check step or guess transitions. Always verify the current state before starting work. diff --git a/.opencode/knowledge/workflow/flowr-spec.md b/.opencode/knowledge/workflow/flowr-spec.md index 87c56d2d..bdbe552b 100644 --- a/.opencode/knowledge/workflow/flowr-spec.md +++ b/.opencode/knowledge/workflow/flowr-spec.md @@ -15,10 +15,10 @@ last-updated: 2026-05-06 - Carry runtime metadata in state-level `attrs` (agent, skills, input_artifacts, etc.); `attrs` is opaque to the engine and replaces flow-level attrs entirely (no merge). - All CLI commands output **JSON by default** (structured, machine-parseable). Use `--text` flag for human-readable plain text. - `next` command shows **all** transitions with status markers (`"open"` / `"blocked"`) and condition hints for blocked transitions. -- Sessions track workflow progress (flow, state, call stack) as YAML files in `.flowr/sessions/` with atomic writes; `--session` on check/next/transition resolves flow/state automatically. +- Sessions track workflow progress (flow, state, call stack) as YAML files in `.cache/sessions/` with atomic writes; `--session` on check/next/transition resolves flow/state automatically. - Subflow exit names resolve through the parent flow's transition map (not used directly as state IDs). Enables subflow chaining and recursive entry up to 3 levels. - Configuration reads `[tool.flowr]` from `pyproject.toml` (flows_dir, sessions_dir, default_flow, default_session); CLI flags override pyproject.toml which overrides defaults. -- Flow name resolution: commands accept short names (e.g., `planning-flow`) resolved from the configured flows directory, or full file paths. +- Flow name resolution: commands accept short names (e.g., `architecture-flow`) resolved from the configured flows directory, or full file paths. - Immutable loaded flows, closed evidence schema, isolated subflow context, filesystem wins over session on conflict. Extension fields (non-reserved keys) are allowed and not interpreted by the validator. ## Concepts @@ -35,7 +35,7 @@ last-updated: 2026-05-06 **Subflow Invocation**: A state with a `flow:` field becomes a subflow invocation. The parent's `next` keys must match the child's `exits` exactly. Subflows use a call-stack mechanism: push on entry, pop on exit. Context is isolated: only the current flow is visible. Cross-flow cycles are forbidden. -**Subflow Exit Resolution (v1.0.0)**: Exit names resolve through the parent flow's transition map instead of being used directly as state IDs. This enables subflow chaining (atomic exit + re-enter next subflow) and recursive subflow entry up to 3 levels deep (e.g., main-flow → feature-dev-flow → planning-flow). Stack frames record the correct parent state (subflow wrapper, not pre-transition state). +**Subflow Exit Resolution (v1.0.0)**: Exit names resolve through the parent flow's transition map instead of being used directly as state IDs. This enables subflow chaining (atomic exit + re-enter next subflow) and recursive subflow entry up to 3 levels deep (e.g., define-flow → spec-validation-flow). Stack frames record the correct parent state (subflow wrapper, not pre-transition state). ## Content @@ -137,7 +137,7 @@ Named condition references in `when` clauses must resolve to a key in the same s - Cross-flow cycles are forbidden (detected via DFS at load time) - Exit names resolve through parent flow's transition map (not used directly as state IDs) - Subflow chaining: atomic exit + re-enter next subflow without manual state manipulation -- Recursive entry: supports up to 3-level nesting (main → feature-dev → planning) +- Recursive entry: supports up to 3-level nesting (define-flow → discovery-flow, develop-flow → development-flow, etc.) - Stack frames record the subflow wrapper state (not the pre-transition state) - `.yaml` extension fallback: flow references without extension are resolved automatically - `session init` auto-enters subflow when first state has a `flow:` field @@ -176,7 +176,7 @@ A flow definition MAY contain fields not specified in the specification. Such ex ### Session Model -Sessions persist workflow progress as YAML files in `.flowr/sessions/` with atomic writes (temp-file-then-rename). Each session tracks: +Sessions persist workflow progress as YAML files in `.cache/sessions/` with atomic writes (temp-file-then-rename). Each session tracks: | Field | Description | |-------|-------------| @@ -197,8 +197,8 @@ flowr reads `[tool.flowr]` from `pyproject.toml`. Resolution priority: CLI flags | Key | Default | Description | |-----|---------|-------------| | `flows_dir` | `.flowr/flows` | Directory containing flow YAML files | -| `sessions_dir` | `.flowr/sessions` | Directory for session YAML files | -| `default_flow` | `main-flow` | Flow name used when none specified | +| `sessions_dir` | `.cache/sessions` | Directory for session YAML files | +| `default_flow` | `define-flow` | Flow name used when none specified | | `default_session` | `default` | Session name used with bare `--session` | ### Design Principles diff --git a/.opencode/knowledge/workflow/todo-anchor-protocol.md b/.opencode/knowledge/workflow/todo-anchor-protocol.md index b89429d1..9c520b68 100644 --- a/.opencode/knowledge/workflow/todo-anchor-protocol.md +++ b/.opencode/knowledge/workflow/todo-anchor-protocol.md @@ -71,15 +71,7 @@ Before exiting a state, confirm each item: ### Owner Mapping -| Owner Code | Agent | -|---|---| -| `PO` | product-owner | -| `DE` | domain-expert | -| `SE` | software-engineer | -| `SA` | system-architect | -| `R` | reviewer | -| `Design Agent` | design-agent | -| `Setup Agent` | setup-agent | +Owner dispatch mapping per AGENTS.md Session Protocol. ## Related diff --git a/.opencode/knowledge/writing/ai-language-markers.md b/.opencode/knowledge/writing/ai-language-markers.md index 2058f2b5..ed60ed48 100644 --- a/.opencode/knowledge/writing/ai-language-markers.md +++ b/.opencode/knowledge/writing/ai-language-markers.md @@ -1,7 +1,7 @@ --- domain: writing tags: [ai-writing, detection, language-markers, rlhf-bias, humanization] -last-updated: 2026-05-06 +last-updated: 2026-05-18 --- # AI Language Markers @@ -13,7 +13,8 @@ last-updated: 2026-05-06 - Detect hedging verbs ("ensuring/ensures" at 4.3x, "rather than" at 2.5x) and filler phrases ("It's worth noting that" at 31x) as the strongest 2026-era signals. - Recognize structural formulaicity: em dash overuse (2.6x), "X plays a crucial role in shaping Y" (most formulaic trigram), uniform sentence length, rule of three. - Distinguish chatbot artifacts ("Absolutely!", "Great question!", "I'd be happy to help!") as conversational voice markers separate from content markers. -- Apply multi-signal detection scoring across vocabulary, structure, hedging, and voice dimensions — no single marker is diagnostic; 5+ hits across tiers indicates likely AI generation. +- Apply multi-signal detection scoring across vocabulary, structure, hedging, voice, and model-specific dimensions — no single marker is diagnostic; 5+ hits across tiers indicates likely AI generation. +- Claude-specific patterns (first-person avoidance, compulsive balance, conclusion recycling) are subtler than vocabulary markers but form a consistent fingerprint; multi-turn conversations accumulate verbal tics 110% by turn 5+. ## Concepts @@ -64,6 +65,30 @@ last-updated: 2026-05-06 | bolster | GPT-4 era | Wikipedia AI writing page | | kaleidoscope | Reddit-flagged | Reddit (300+ comments) | | scaffold | Academic metaphor | Writing community lists | +| navigate (metaphorical) | Clustered | Multiple sources | +| harness | Clustered | Anti-slop, HumanizerTech | +| unlock | Clustered | Anti-slop, HumanizerTech | +| unveil | Clustered | Anti-slop, slopwash | +| champion (verb) | Clustered | Anti-slop | +| elevate (verb) | Clustered | Anti-slop, HumanizerTech | +| illuminate (verb) | Clustered | Anti-slop | +| seamless | Clustered | Bloomberry, Anti-slop | +| innovative | Clustered | Anti-slop, slopwash | +| ecosystem (metaphorical) | Clustered | Anti-slop | +| framework (metaphorical) | Clustered | Anti-slop | +| paradigm | Clustered | Anti-slop | +| synergy | Clustered | Anti-slop, corporate-speak lists | +| catalyst (metaphorical) | Clustered | Anti-slop | +| transformative | Clustered | Anti-slop, Bloomberry | +| compelling | Clustered | HumanizerTech | +| profound | Clustered | Anti-slop | +| scalable | Clustered | Anti-slop | +| optimize (non-technical) | Clustered | Anti-slop, HumanizerTech | +| empower | Clustered | Anti-slop, slopwash | +| maximize (non-technical) | Clustered | Anti-slop | +| embody | Clustered | Anti-slop | +| revolutionize | Clustered | Anti-slop, Bloomberry | +| boilerplate (noun) | Self-referential marker | Slopwash (detection category becomes the marker) | ### Tier 2: Hedging Verbs (2026 strongest signals) @@ -103,6 +128,10 @@ last-updated: 2026-05-06 | "As we navigate" | Clustered | Multiple sources | | "Let's dive in" / "Let's explore" | Chatbot artifacts | Reddit, Wikipedia | | "Whether you're a [X] or a [Y]" | Template scaffolding | Noren | +| "Here's the thing/kicker/deal" | Conversational filler | Anti-slop, slopwash | +| "Imagine a world where" | Grandiose opener | Anti-slop | +| "Fear not" | Archaic reassurance | Anti-slop | +| "That being said" | Hedging transition | Detection lists | | Generic positive close | AI conclusion pattern | Multiple sources | ### Tier 5: Chatbot Artifacts ("Helpful Assistant" Voice) @@ -141,8 +170,28 @@ No single marker is diagnostic. Multi-signal detection scores across these dimen | Filler | "It's worth noting" or similar filler phrases | | Voice | No sentences under 8 words, no colloquial language, no personality | | Comparative | "Not X, but Y" appears more than twice | +| Claude patterns | 3+ Tier 7 structural patterns present | -Source: humanize-writing GitHub reference. +Source: humanize-writing GitHub reference, Bloomberry 2026 study (82% of AI outputs contain 2+ of: hedge openers, tricolon lists, em-dash connectors, resolution closers). + +### Tier 7: Claude-Specific Structural Patterns + +Claude produces smoother, less detectable prose than ChatGPT (70-85% detection vs 85-95%). These patterns are subtler than vocabulary markers but form a consistent fingerprint: + +| Pattern | Description | Detector Weight | +|---------|-------------|----------------| +| First-person avoidance | Uses "one might argue", "it could be suggested" instead of "I think" — Constitutional AI safety artifact | Medium | +| Systematic scope acknowledgement | Pre-emptive limitations at predictable positions ("of course, this is just one perspective") | Medium | +| Compulsive balance | Always presents both sides, even when the prompt asks for a position | High | +| Three-part list compulsion | RLHF artifact from evaluators rewarding triads; more rigid than human grouping | Medium | +| Conclusion recycling | Restate → summarise → gesture forward, mechanically consistent | High | +| Extended analogy preference | Verbose structural development of single metaphor rather than brief comparison | Low | +| Smooth paragraph transitions | No abrupt shifts; every paragraph bridge is explicitly signposted | Medium | +| "according to the text" / "based on the text" | Claude-specific citation phrasing (arXiv TF-IDF analysis across 8 models) | High | + +Claude also produces longer average sentence length with more complex clause structures than ChatGPT, and more consistent Markdown formatting. + +Verbal tic accumulation (arXiv 2604.19139): Multi-turn conversations show 110% increase in verbal tics by turn 5+. Review multi-turn outputs with heightened scrutiny. ## Related diff --git a/.opencode/skills/accept-feature/SKILL.md b/.opencode/skills/accept-feature/SKILL.md index 210f0e92..48190e13 100644 --- a/.opencode/skills/accept-feature/SKILL.md +++ b/.opencode/skills/accept-feature/SKILL.md @@ -11,7 +11,7 @@ Available knowledge: [[requirements/gherkin#key-takeaways]], [[software-craft/te 2. Verify all BDD examples pass from the end user's perspective, not the test harness, per [[software-craft/test-design#key-takeaways]]. 3. IF an example passes in the test harness but fails from the user's perspective → flag it as a semantic alignment gap per [[software-craft/test-design#concepts]]. 4. Verify structural traceability via `beehave check`: every Example in the feature file must have exactly one corresponding test function, and every test function must trace back to an Example. pytest-beehave enforces this via title-based mapping. Any violations reported by `beehave check` mean the feature is not done. -5. Verify semantic depth: for each Example that describes a user-facing command or API invocation, verify the test exercises the command/API handler, not just domain logic. Tests that bypass the entry point described in the acceptance criterion have structural traceability but wrong semantic depth per [[software-craft/test-design#concepts]]. +5. Verify semantic depth per [[software-craft/test-design#concepts]]. 6. Verify quality attributes are met. 7. Verify definition of done criteria are satisfied. 8. Produce a traceability matrix: for each stakeholder Q&A topic from interview notes, map to: diff --git a/.opencode/skills/break-down-feature/SKILL.md b/.opencode/skills/break-down-feature/SKILL.md deleted file mode 100644 index 0ce24f31..00000000 --- a/.opencode/skills/break-down-feature/SKILL.md +++ /dev/null @@ -1,23 +0,0 @@ ---- -name: break-down-feature -description: "Verify simulation-discovered rules are specific enough and write them as Rule blocks in the feature file" ---- - -# Break Down Feature - -Available knowledge: [[requirements/invest]], [[requirements/decomposition]], [[requirements/gherkin#key-takeaways]]. `in` artifacts: read all before starting work. - -1. Discover and read the feature file, product definition, domain spec, and glossary from `in`. The feature file contains Rule blocks with behavioral descriptions (from simulation). These rules were discovered during spec simulation — they are already validated behavioral statements, not hypotheses. -2. For each Rule block in the .feature file: - a. Verify the rule is specific enough to generate at least one Example. IF the rule is vague (e.g. "the system should handle errors") → flag for clarification. - b. Verify the rule is not contradicted by another rule or by the domain spec. - c. Verify the rule maps to at least one entity and state in the domain spec. -3. Validate each Rule block's title: 2–6 words, descriptive, unique within the feature file, and contain no special characters. Count words by splitting on whitespace. If a title is not 2–6 words or is not unique within the feature file → rephrase while preserving meaning. Do NOT rewrite the behavioral description paragraph — it is the simulation-validated rule body. Example: - ``` - Rule: Order must contain at least one item - ``` -4. IF clarification is needed for a rule → ask the stakeholder targeted questions. Record answers in the relevant interview notes. -5. IF a Rule contains "and" or spans more than 2 concerns → split into separate Rule blocks per [[requirements/decomposition#key-takeaways]]. -6. IF the feature has more than 8 rules → propose the split to the stakeholder with rationale per [[requirements/decomposition#key-takeaways]]. Stakeholder decides what's core vs. deferred. -7. Add Behavior hints under each Rule block. Format: `Behavior hints:` followed by `- <condition> → <outcome>` bullets. Each hint describes a behaviorally distinct outcome. Leave the `# Constraints:` comment block in place — these are non-functional requirements (performance, security, accessibility) that are not convertible to Gherkin Rules but must remain visible for the reviewer and implementer. -8. Validate the feature passes INVEST criteria per [[requirements/invest#concepts]]. Every criterion that fails is a hard blocker: fix before advancing. diff --git a/.opencode/skills/conduct-interview/SKILL.md b/.opencode/skills/conduct-interview/SKILL.md index ee840957..78171e9e 100644 --- a/.opencode/skills/conduct-interview/SKILL.md +++ b/.opencode/skills/conduct-interview/SKILL.md @@ -12,6 +12,6 @@ Available knowledge: [[requirements/interview-techniques#key-takeaways]]. `in` a cross-cutting group per [[requirements/interview-techniques#concepts]]. 3. If specific features are identified, drill into feature-level questions to define feature names and rough boundaries per [[requirements/interview-techniques#concepts]]. -4. If >2 concerns emerge for a single feature, split per [[requirements/decomposition]]. +4. If a feature candidate spans multiple bounded contexts, flag for splitting per [[requirements/decomposition]]. 5. Write confirmation gate before any file writes. -6. Write interview notes to `docs/interview-notes/IN_YYYYMMDD_<session_id>.md` from the template at `.templates/docs/interview-notes/IN_YYYYMMDD_<session_id>.md.template`. +6. Write interview notes to `.cache/interview-notes/IN_YYYYMMDD_<session_id>.md` from the template at `.templates/.cache/interview-notes/IN_YYYYMMDD_<session_id>.md.template`. diff --git a/.opencode/skills/create-domain-spec/SKILL.md b/.opencode/skills/create-domain-spec/SKILL.md index f0e13464..8b344a16 100644 --- a/.opencode/skills/create-domain-spec/SKILL.md +++ b/.opencode/skills/create-domain-spec/SKILL.md @@ -5,17 +5,17 @@ description: "SA and DE collaborate to create a monolithic domain specification # Create Domain Spec -Available knowledge: [[domain-modeling/behavioral-contracts#concepts]], [[domain-modeling/context-mapping#key-takeaways]], [[requirements/ubiquitous-language#key-takeaways]]. `in` artifacts: read all before starting work. +Available knowledge: [[domain-modeling/event-storming#concepts]], [[domain-modeling/context-mapping#key-takeaways]], [[requirements/ubiquitous-language#key-takeaways]]. `in` artifacts: read all before starting work. 1. Read all interview notes from `in` artifacts. -2. Identify bounded contexts from interview data: scan for clusters of related terminology, distinct lifecycles, and independent business capabilities per [[domain-modeling/behavioral-contracts#concepts]]. Record each context with a name, responsibility, and rough boundary. +2. Identify bounded contexts from interview data: scan for clusters of related terminology, distinct lifecycles, and independent business capabilities per [[domain-modeling/event-storming#concepts]]. Record each context with a name, responsibility, and rough boundary. 3. Write `product_definition.md` from the trimmed template: What IS/IS NOT, Why, Users, Quality Attributes, Out of Scope, Delivery Order. Use the interview data to fill every section. The delivery order drives which context gets specified and simulated first. 4. Write `domain_spec.md` from the template at `.templates/docs/spec/domain_spec.md.template`. This is a single monolithic file — one `## <Context Name>` section per bounded context. Fill in iteration-0 content for each context: - **Context Map**: leave the top-level section empty — architecture-flow context-mapping state refines it. - **Context**: 2-3 sentences about what this context does and why it exists. - **Entities**: name, type (Entity/Value Object), purpose, aggregate root designation. - **Relationships**: entity relationships with cardinality. - - **Aggregate Boundaries**: grouping rationale and invariants. + - **Aggregate Boundaries**: grouping rationale, root entity, and See cross-reference to the Invariants section (not invariant text — invariants are derived during simulation). - **Data Shapes**: field names and types from interview data. Constraints may be `?` if unknown. - **Integration Points**: direction, trigger, rough payload shape. Details may be `?`. - **External Contracts**: actor, trigger, input shape, output shape, known error conditions, side effects, and preconditions. Details may have `?` for unknowns. diff --git a/.opencode/skills/create-py-stubs/SKILL.md b/.opencode/skills/create-py-stubs/SKILL.md index 4486a117..e11fc857 100644 --- a/.opencode/skills/create-py-stubs/SKILL.md +++ b/.opencode/skills/create-py-stubs/SKILL.md @@ -13,7 +13,7 @@ Available knowledge: [[architecture/technical-design]], [[software-craft/source- - Plain Examples → bare function stubs with `...` body - Scenario Outlines → `@given` decorated stubs with inferred Hypothesis strategies + `@example` decorators for each Examples table row - Placeholder names become function parameters; strategy is inferred from Examples table column types -4. Run `beehave check` to verify all Examples in the feature file have corresponding test stubs and there are no orphan tests. This verifies: unmapped-scenario, unmapped-test, misplaced-test, missing-placeholder, missing-literal, example-mismatch. Stubs with `...` bodies are exempt from placeholder and literal checks. -5. Verify decomposition per [[requirements/decomposition#key-takeaways]]: no more than 2 concerns, no more than 8 Must Examples. +4. Run `beehave check` and resolve violations per [[software-craft/test-stubs#concepts]]. +5. Verify decomposition per [[requirements/decomposition#key-takeaways]]: no Rule spans >1 bounded context, no Rule has >8 Must behaviors after MoSCoW triage and Scenario Outline collapse. 6. Verify all planning artifacts are present and consistent: feature file, product definition, domain spec, glossary. -7. Commit all changes to the local dev branch. +7. Commit all changes to the feature branch per [[software-craft/git-conventions#content]]. diff --git a/.opencode/skills/discover-features/SKILL.md b/.opencode/skills/discover-features/SKILL.md deleted file mode 100644 index cc14dec7..00000000 --- a/.opencode/skills/discover-features/SKILL.md +++ /dev/null @@ -1,20 +0,0 @@ ---- -name: discover-features -description: "Identify feature boundaries from simulation-created .feature files, validated against bounded contexts and delivery order" ---- - -# Discover Features - -Available knowledge: [[requirements/feature-boundaries]], [[requirements/feature-discovery#concepts]], [[requirements/gherkin#key-takeaways]]. `in` artifacts: read all before starting work. - -1. Read product_definition.md, domain_spec.md, features/*.feature, and glossary.md from `in` artifacts. -2. Read simulation-created .feature files (one per bounded context from spec-validation). Read product_definition.md delivery order. These are the two inputs for feature candidate identification. -3. Read rules from .feature files (written by simulation). These are the rules that must be distributed to features. -4. For each candidate, map it to bounded contexts from the domain spec. IF a candidate spans multiple contexts → flag for splitting per [[requirements/feature-boundaries#key-takeaways]]. -5. For each candidate, map it to entities and state machines from the domain spec. IF a candidate requires cross-aggregate transactions → flag for splitting per [[requirements/feature-boundaries#key-takeaways]]. -6. Redistribute Rule blocks across split features. When a coarse .feature file is split into multiple features, move Rule blocks to the appropriate feature file. Do NOT edit Rule block content — only redistribute. IF a rule spans multiple features → flag for cross-cutting handling. -7. Name each feature per [[requirements/feature-boundaries#content]]: use the delivery step name, validated for clarity and specificity. The Feature title slug MUST match the .feature filename stem per [[requirements/gherkin#concepts]]. -8. Write a description for each feature per [[requirements/feature-boundaries#content]]: what it provides, which context it serves, why it exists, key entities. -9. Map quality attributes from product_definition.md to features. Write each as a `# Constraints:` bullet in the .feature file. -10. Edit each `.feature` file: update Feature title and description if the feature was split/renamed. Verify the new Feature title is 2–6 words and unique across all feature files per [[requirements/gherkin#key-takeaways]]. Count words by splitting on whitespace. If a title fails, rephrase and re-verify. The Feature title slug must match the .feature filename stem. Add `# Constraints:` comments from quality attribute mapping (Step 9). Feature files already exist from simulation — this step edits, not creates. -11. Run context coverage gap analysis: every bounded context from domain_spec.md covered by at least one .feature file. IF any gap → flag it. diff --git a/.opencode/skills/discover-rules/SKILL.md b/.opencode/skills/discover-rules/SKILL.md deleted file mode 100644 index da002661..00000000 --- a/.opencode/skills/discover-rules/SKILL.md +++ /dev/null @@ -1,14 +0,0 @@ ---- -name: discover-rules -description: "Map structural rules from domain spec aggregate invariants and quality attribute constraints into feature files" ---- - -# Discover Rules - -Available knowledge: [[requirements/feature-discovery#concepts]], [[requirements/gherkin#concepts]]. `in` artifacts: read all before starting work. - -1. Read product_definition.md, domain_spec.md, and all `.feature` files (created by simulation) from `in` artifacts. -2. For each aggregate invariant in domain_spec.md, verify the corresponding feature already has a Rule block covering it (written by simulation). Use [[requirements/gherkin#concepts]] for Rule block structure. -3. If an invariant has no corresponding Rule, add it as a Rule block with a behavioral description derived from the invariant. Format: `Rule: <2-6 word title>` followed by a behavioral description paragraph. -3a. Verify each new Rule title is 2–6 words and unique within the feature file per [[requirements/gherkin#key-takeaways]]. Count words by splitting on whitespace. If a title fails, rephrase and re-verify. -4. Run gap analysis: every aggregate invariant → at least one Rule; every quality attribute → at least one Constraint (constraints are written by discover-features). IF any gap → flag it. Do NOT silently fill gaps with assumed rules. diff --git a/.opencode/skills/fix-spec/SKILL.md b/.opencode/skills/fix-spec/SKILL.md index 5e7083ea..98052089 100644 --- a/.opencode/skills/fix-spec/SKILL.md +++ b/.opencode/skills/fix-spec/SKILL.md @@ -7,17 +7,13 @@ description: "Rewrite domain spec addressing all pain points from simulation, fu Available knowledge: [[requirements/spec-simulation#concepts]], [[requirements/gherkin#concepts]]. `in` artifacts: read all before starting work. -1. Read `simulation_results.md` (current iteration) and `domain_spec.md` (all contexts). If prior simulation_results exist, read those too for cumulative context. -2. List all unresolved pain points from simulation_results.md across all contexts. These are the fix targets. -3. Rewrite `domain_spec.md` addressing every pain point: - - **Ambiguous** pain points: clarify the spec text so it can only be read one way. - - **Contradictory** pain points: resolve the contradiction, choosing one interpretation and documenting why. - - **Missing** pain points: add the missing content (new entity fields, new integration contracts, new error paths). - - **Edge-case** pain points: add explicit handling (constraints, guards, or stated out-of-scope). +1. Read `.cache/sim/simulation_results_*.md` (all iterations, focus on the latest) and `domain_spec.md` (all contexts). Prior iterations provide cumulative context for previously discovered pain points. +2. List all unresolved pain points from the latest simulation results across all contexts. These are the fix targets. +3. Rewrite `domain_spec.md` addressing every pain point per its classification in [[requirements/spec-simulation#concepts]]. 4. Fill in any `?` constraints in Data Shapes that were resolved by this iteration. 5. Update **State Machines** section within each context (derived summaries referencing .feature rules): state transitions emerge from accumulated rules. If rules imply a lifecycle, formalize it as a state machine table. 6. Update **Error Handling** section within each context (derived summaries referencing .feature rules): error paths discovered during simulation get explicit entries. 7. Update **Invariants** section within each context (derived summaries referencing .feature rules): any "must always" conditions discovered get added. -8. Update `simulation_results.md` Resolution Status table: mark each pain point as Resolved with a brief description of how. This is the authoritative pain-point tracker — domain_spec does not duplicate them. +8. Update the latest `.cache/sim/simulation_results_<timestamp>.md` Resolution Status table: mark each pain point as Resolved with a brief description of how. This is the authoritative pain-point tracker — domain_spec does not duplicate them. 9. Update .feature files: correct Rule blocks that were based on resolved pain points. If a Rule description was ambiguous (pain point classified as ambiguous), rewrite it for clarity. If a Rule title was changed during correction, verify the new title is 2–6 words and unique within the feature file per [[requirements/gherkin#key-takeaways]]. Count words by splitting on whitespace. 10. This is a FULL REWRITE of domain_spec.md, not a patch. Read the entire existing spec (all contexts), then write the updated version from top to bottom. This ensures coherence — no context contradicts another because the whole document was written in one pass with full context. diff --git a/.opencode/skills/polish-code/SKILL.md b/.opencode/skills/polish-code/SKILL.md index 5431769c..93344669 100644 --- a/.opencode/skills/polish-code/SKILL.md +++ b/.opencode/skills/polish-code/SKILL.md @@ -5,7 +5,7 @@ description: "Apply full project conventions — naming, docstrings, formatting, # Polish Code -Available knowledge: [[software-craft/tdd#key-takeaways]]. `in` artifacts: read all before starting work. +Available knowledge: [[software-craft/tdd#key-takeaways]], [[writing/ai-language-markers#key-takeaways]]. `in` artifacts: read all before starting work. 1. Run `task conventions` to see all convention violations (naming, docstrings, formatting, import sorting, etc.). 2. Fix each violation manually. Do NOT use `--fix` (it can break code). @@ -15,4 +15,5 @@ Available knowledge: [[software-craft/tdd#key-takeaways]]. `in` artifacts: read 6. Run `task static-check` and fix any pyright errors. 7. Run `task test` to verify nothing broke. 8. IF `task test` shows coverage failure → run `task test-build` to see missing lines, then add coverage tests in `tests/unit/`. -9. Commit to the feature branch per [[software-craft/git-conventions#content]]. +9. Scan docstrings, comments, and user-facing strings for AI language markers per [[writing/ai-language-markers#key-takeaways]]. Rewrite any flagged text in natural, direct style. +10. Commit to the feature branch per [[software-craft/git-conventions#content]]. diff --git a/.opencode/skills/refactor/SKILL.md b/.opencode/skills/refactor/SKILL.md index 3d17f85c..a5c3b7b7 100644 --- a/.opencode/skills/refactor/SKILL.md +++ b/.opencode/skills/refactor/SKILL.md @@ -11,16 +11,8 @@ Available knowledge: [[software-craft/tdd]], [[software-craft/refactoring]], [[s 2. Refactor only if there is a test that would break if the refactoring is wrong per [[software-craft/tdd#key-takeaways]]. 3. Apply small steps: one refactoring at a time, tests green after each step, no new functionality per [[software-craft/refactoring#key-takeaways]]. 4. Apply design-only transformations per [[software-craft/tdd#concepts]]: YAGNI > KISS > DRY > ObjCal > Smells > SOLID > patterns. Do not apply convention compliance (docstrings, type hints, import ordering, format changes). Those belong in the Conventions Phase. -5. IF a class has >2 instance variables → split per [[software-craft/object-calisthenics#key-takeaways]]. -6. IF a method uses `else` → replace with early return or guard clause per [[software-craft/object-calisthenics#key-takeaways]]. -7. IF code calls `obj.get_x()` then decides → replace with Tell, Don't Ask per [[software-craft/object-calisthenics#key-takeaways]]. -8. IF Long Method → Extract Method per [[software-craft/smell-catalogue#concepts]]. -9. IF Switch Statements or repeated `if/elif` on type → Replace Conditional with Polymorphism per [[software-craft/smell-catalogue#concepts]]. -10. IF Feature Envy → Move Method per [[software-craft/smell-catalogue#concepts]]. -11. IF Primitive Obsession → Replace Data Value with Object per [[software-craft/smell-catalogue#concepts]]. -12. IF Data Clumps → Introduce Parameter Object per [[software-craft/smell-catalogue#concepts]]. -13. IF Shotgun Surgery or Divergent Change → Extract Class per [[software-craft/smell-catalogue#concepts]]. -14. IF no improvement is needed → skip refactoring and proceed to the next test. -15. IF a spec gap or inconsistency is discovered during refactoring → do NOT modify specification documents (domain_spec.md, glossary.md, product_definition.md, ADRs, feature files). Flag it in output notes. The SE may ONLY modify production code and test code. -16. Commit refactor changes separately from feature changes per [[software-craft/git-conventions#concepts]]. -17. Run `task test-fast` to confirm all tests remain green after refactoring. +5. Detect improvement opportunities per the design principle priority in [[software-craft/tdd#content]], loading ObjCal per [[software-craft/object-calisthenics#key-takeaways]], smells per [[software-craft/smell-catalogue#key-takeaways]], and SOLID per [[software-craft/solid#key-takeaways]]. Apply the appropriate refactoring technique per [[software-craft/refactoring-techniques#concepts]]. +6. IF no improvement is needed → skip refactoring and proceed to the next test. +7. IF a spec gap or inconsistency is discovered during refactoring → do NOT modify specification documents (domain_spec.md, glossary.md, product_definition.md, ADRs, feature files). Flag it in output notes. The SE may ONLY modify production code and test code. +8. Commit refactor changes separately from feature changes per [[software-craft/git-conventions#concepts]]. +9. Run `task test-fast` to confirm all tests remain green after refactoring. diff --git a/.opencode/skills/refine-features/SKILL.md b/.opencode/skills/refine-features/SKILL.md new file mode 100644 index 00000000..05b94479 --- /dev/null +++ b/.opencode/skills/refine-features/SKILL.md @@ -0,0 +1,30 @@ +--- +name: refine-features +description: "Transform simulation-created context-level .feature files into final feature-level files with stable titles, descriptions, rules, and constraints" +--- + +# Refine Features + +Available knowledge: [[requirements/feature-boundaries]], [[requirements/feature-discovery#concepts]], [[requirements/invest]], [[requirements/decomposition]], [[requirements/gherkin#key-takeaways]]. `in` artifacts: read all before starting work. + +This state transforms the output of spec-validation (context-level .feature files) into the final feature-level structure that develop-flow will use. After this state, .feature file structure (Feature titles, Rule titles, Constraints) is FROZEN. Only Examples/Outlines may be added during develop-flow. + +1. Read `product_definition.md`, `domain_spec.md`, `glossary.md`, and `features/*.feature` from `in` artifacts. The .feature files are simulation-created (one per bounded context, named by context). +2. Read `product_definition.md` delivery order. This determines feature priority and grouping. +3. For each bounded context in `domain_spec.md`, identify feature candidates per [[requirements/feature-boundaries#key-takeaways]]: + - Map candidates to bounded contexts. IF a candidate spans multiple contexts → flag for splitting. + - Map candidates to entities and aggregate boundaries. IF a candidate requires cross-aggregate transactions → flag for splitting. +4. Create feature-level .feature files by splitting context-level files per [[requirements/feature-discovery#concepts]]: + - Name each feature per [[requirements/feature-boundaries#content]]: use the delivery step name, validated for clarity and specificity. + - The Feature title slug MUST match the .feature filename stem per [[requirements/gherkin#concepts]]. + - Redistribute Rule blocks across split features. When a context .feature file is split into multiple features, move Rule blocks to the appropriate feature file. Do NOT edit Rule block content — only redistribute. IF a rule spans multiple features → flag for cross-cutting handling. + - Delete the original context-level .feature files after all their Rules have been redistributed to feature-level files. +5. Write a Feature description for each feature per [[requirements/feature-discovery#concepts]]: what it provides, which context it serves, why it exists, key entities. The description replaces the simulation-era placeholder text. +6. Validate each Rule block's title per [[requirements/gherkin#key-takeaways]]: 2–6 words, descriptive, unique within the feature file, no special characters. Count words by splitting on whitespace. IF a title is outside the range → rephrase while preserving meaning. Do NOT rewrite the behavioral description paragraph — it is the simulation-validated rule body. +7. Check decomposition per [[requirements/decomposition#key-takeaways]]. +8. Map quality attributes from `product_definition.md` to features. Write each as a `# Constraints:` bullet in the .feature file. Technology constraints from `domain_spec.md` Integration Points are already present from simulation — verify they remain and add quality attribute constraints. +9. Run context coverage gap analysis: every bounded context from `domain_spec.md` must be covered by at least one .feature file. IF any gap → flag it. +10. Validate each feature passes INVEST criteria per [[requirements/invest#concepts]]. Every criterion that fails is a hard blocker: fix before advancing. IF a feature fails Independent or Small → split per [[requirements/decomposition#key-takeaways]]. IF a feature fails Negotiable or Valuable → flag for stakeholder decision. +11. Commit all .feature file changes to the local dev branch. + +**Stability contract**: After this state completes, Feature titles, Rule titles, and the number of Rules per feature are FROZEN. Develop-flow may only add Examples/Outlines to existing Rules. Renaming, splitting, or removing existing structural elements would break beehave's title-based mapping to test files and functions. \ No newline at end of file diff --git a/.opencode/skills/review-architecture/SKILL.md b/.opencode/skills/review-architecture/SKILL.md index e1857533..5d5478bc 100644 --- a/.opencode/skills/review-architecture/SKILL.md +++ b/.opencode/skills/review-architecture/SKILL.md @@ -9,7 +9,7 @@ Available knowledge: [[software-craft/code-review#key-takeaways]], [[architectur **Adversarial stance**: Actively seek inconsistencies and gaps. Use accountability to an unknown audience (Tetlock, 1985) to produce more rigorous decisions. -1. Read all `in` artifacts: ADRs (if any), product_definition.md, domain_spec.md, simulation_results.md, glossary.md. +1. Read all `in` artifacts: ADRs (if any), product_definition.md, domain_spec.md, glossary.md. 2. Verify cross-document consistency per [[architecture/reconciliation#concepts]]: - ADR ↔ domain_spec: every ADR aligns with spec requirements; each ADR references specific Example criteria. - domain_spec ↔ product_definition: every bounded context in the spec maps to a quality attribute or delivery step. diff --git a/.opencode/skills/review-gate/SKILL.md b/.opencode/skills/review-gate/SKILL.md index 43cf805e..c86e28dd 100644 --- a/.opencode/skills/review-gate/SKILL.md +++ b/.opencode/skills/review-gate/SKILL.md @@ -15,19 +15,12 @@ Available knowledge: [[software-craft/code-review]], [[software-craft/test-desig 2. Verify implementation aligns with architectural decisions per [[software-craft/code-review#concepts]]: ADR compliance, quality attributes met. 3. Verify all `# Constraints:` in the .feature file are met in the implementation. For technology constraints, read domain_spec.md `### Technology Requirements` table and execute the Verification instruction for each row (grep imports, check file existence, inspect config). Zero evidence → FAIL. For quality attribute constraints, verify thresholds are enforced. 4. Verify implementation aligns with feature specification: all Examples have corresponding test implementations, behavior matches Gherkin steps. -5. Verify design principles adversarially per [[software-craft/tdd#key-takeaways]] priority order (YAGNI > KISS > DRY > ObjCal > Smells > SOLID > patterns): - - **YAGNI**: No premature abstractions, no speculative generalization, no code without an exercising test. - - **KISS**: No unnecessary complexity or over-engineering when a simpler solution exists. - - **DRY**: No duplicated logic, unless the duplication is simpler than the wrong abstraction (KISS overrides DRY). - - **ObjCal**: Load [[software-craft/object-calisthenics#key-takeaways]] — check 9 rules: one level of indentation, no `else`, wrapped primitives, one dot per line, no abbreviations, small entities, ≤2 instance variables, first-class collections, no getters/setters. - - **Smells**: Load [[software-craft/smell-catalogue#key-takeaways]] — check for bloaters, OO abusers, change preventers, dispensables, couplers. - - **SOLID**: Load [[software-craft/solid#key-takeaways]] — check SRP, OCP, LSP, ISP, DIP violations. - - **Patterns**: Verify every design pattern is driven by a smell. Patterns without a motivating smell violate YAGNI. +5. Verify design principles adversarially per the priority order in [[software-craft/tdd#content]], loading ObjCal per [[software-craft/object-calisthenics#key-takeaways]], smells per [[software-craft/smell-catalogue#key-takeaways]], and SOLID per [[software-craft/solid#key-takeaways]]. 6. **FAIL-FAST**: If any design violations found → exit `fail` with specific citations (file:line). Do NOT proceed to structure review. ## Tier 2: Structure Review -7. Verify structural traceability: run `beehave check` to confirm every Example in the feature file has a corresponding test and there are no orphan tests. pytest-beehave enforces this via title-based mapping. +7. Run `beehave check` per [[software-craft/test-stubs#concepts]]. 8. Verify test quality per [[software-craft/test-design#concepts]]: tests follow AAA pattern, clear assertions, behavior-focused not implementation-coupled. 9. Run `task test` and verify all tests pass with coverage. 10. Run `ruff check .` and verify no functional lint violations (the default ruff config only includes bug-catching rules). diff --git a/.opencode/skills/review-simulation/SKILL.md b/.opencode/skills/review-simulation/SKILL.md index 50532b66..b7a3f8aa 100644 --- a/.opencode/skills/review-simulation/SKILL.md +++ b/.opencode/skills/review-simulation/SKILL.md @@ -7,18 +7,17 @@ description: "Adversarial review of simulation results: find missed scenarios, v Available knowledge: [[requirements/spec-simulation#concepts]], [[architecture/reconciliation#key-takeaways]], [[requirements/gherkin#concepts]]. `in` artifacts: read all before starting work. -1. Read `simulation_results.md` and `domain_spec.md` (all contexts). +1. Read `.cache/sim/simulation_results_*.md` (all iterations, focus on the latest) and `domain_spec.md` (all contexts). 2. Declare adversarial stance per [[architecture/reconciliation#concepts]]: "I will actively search for missed scenarios and invalid pain points, not confirm the simulation's completeness." -3. **Walkthrough coverage check**: For each context in domain_spec.md, for each entity, verify at least one happy path, one edge case, and one error path walkthrough. For each integration point, verify at least one success and one failure walkthrough. For cross-context integration points, verify both sides of the bilateral relationship were tested. Flag any gaps. -4. **Pain point validation**: For each pain point in simulation_results.md, verify it is real and correctly classified. A pain point is invalid if the spec actually covers the walkthrough (reviewer misread) or if the walkthrough is out of scope per product_definition.md. Remove invalid pain points. Add any missed pain points. -5. **Rule quality check**: For each discovered rule (now in .feature files), verify it is: +3. Verify reviewer decision criteria per [[requirements/spec-simulation#content]]. +4. **Rule quality check**: For each discovered rule (now in .feature files), verify it is: - Specific enough to be testable (not vague like "the system should handle errors"). - - Traceable to a walkthrough that discovered it (via simulation_results.md provenance column). + - Traceable to a walkthrough that discovered it (via simulation results provenance column). - Not contradicted by another rule or by the spec. - Written as a Rule block with clear behavioral description, suitable for generating Examples. -6. **Quality attribute coverage**: For each quality attribute in product_definition.md, verify at least one walkthrough stresses it. If not, add a missed walkthrough. -7. **Cross-context consistency**: Verify integration points are consistent: if Context A says it sends payload X to Context B, verify Context B expects payload X. Bilateral mismatches are hard failures. -8. **Decide PASS or FAIL**: +5. **Quality attribute coverage**: For each quality attribute in product_definition.md, verify at least one walkthrough stresses it. If not, add a missed walkthrough. +6. **Cross-context consistency**: Verify integration points are consistent: if Context A says it sends payload X to Context B, verify Context B expects payload X. Bilateral mismatches are hard failures. +7. **Decide PASS or FAIL**: - **PASS**: zero unresolved pain points, all entities across all contexts covered by walkthroughs, all integration points tested, all quality attributes stressed, cross-context integration points consistent. - **FAIL**: any unresolved pain point, any untested entity or integration point, any untested quality attribute, any bilateral integration mismatch. -9. Record the decision in simulation_results.md Summary section. If FAIL, specify which pain points must be addressed in the next fix-spec iteration. +8. Record the decision in the latest simulation results Summary section. If FAIL, specify which pain points must be addressed in the next fix-spec iteration. diff --git a/.opencode/skills/select-feature/SKILL.md b/.opencode/skills/select-feature/SKILL.md index 612d305d..9e4c04a0 100644 --- a/.opencode/skills/select-feature/SKILL.md +++ b/.opencode/skills/select-feature/SKILL.md @@ -5,7 +5,7 @@ description: "Select the next feature to develop by detecting delivery status fr # Select Feature -`in` artifacts: read all before starting work. +Available knowledge: [[requirements/wsjf#key-takeaways]]. `in` artifacts: read all before starting work. 1. List available feature files in `docs/features/`. 2. IF no feature files exist → exit via `no-features`; features need discovery first. @@ -28,7 +28,8 @@ description: "Select the next feature to develop by detecting delivery status fr d. If the test directory does not exist, beehave check will report errors → feature is incomplete (select it). -5. Select the first incomplete feature by delivery order. -6. IF every feature in the delivery order is delivered (diff clean + tests pass for all) → +5. Collect all incomplete features. IF this is the first feature (no features have been delivered yet) → select the first incomplete feature by delivery order. Skip to step 7. +6. IF subsequent features: score each incomplete feature per [[requirements/wsjf#key-takeaways]]. For each, estimate Value (1-5, mapped to Kano categories) and Effort (1-5, mapped to complexity). Compute WSJF = Value / Effort. Eliminate Dependency=1 features. Select the highest WSJF score; ties broken by Value. +7. IF every feature in the delivery order is delivered (diff clean + tests pass for all) → exit via `no-features`. -7. Set the `feature_id` session param to the selected feature's filename stem (without `.feature` extension). +8. Set the `feature_id` session param to the selected feature's filename stem (without `.feature` extension). diff --git a/.opencode/skills/setup-verify/SKILL.md b/.opencode/skills/setup-verify/SKILL.md index d17864fc..a80f4d27 100644 --- a/.opencode/skills/setup-verify/SKILL.md +++ b/.opencode/skills/setup-verify/SKILL.md @@ -14,7 +14,7 @@ description: "Verify transformations, clean template artifacts, and finalize the 3. Clean template-specific artifacts: - Delete `.flowr/viz/data.js` (regeneratable) - Delete `docs/branding.md` (user creates their own) - - Delete `.flowr/sessions/` contents if any exist + - Delete `.cache/sessions/` contents if any exist - Delete `docs/adr/.gitkeep`, `docs/features/.gitkeep`, `docs/spec/` (template placeholders) 4. Set git remote: `git remote set-url origin git@github.com:{github_username}/{project_name}.git` 5. Commit all changes: `git add -A && git commit -m "chore: initialize project from temple8"` diff --git a/.opencode/skills/simulate-spec/SKILL.md b/.opencode/skills/simulate-spec/SKILL.md index 25c758f6..3a77e3fc 100644 --- a/.opencode/skills/simulate-spec/SKILL.md +++ b/.opencode/skills/simulate-spec/SKILL.md @@ -1,37 +1,29 @@ --- name: simulate-spec -description: "Walk through all domain spec contexts mentally, create I/O evidence in /tmp, discover rules and pain points" +description: "Walk through all domain spec contexts mentally, create I/O evidence in .cache/sim, discover rules and pain points" --- # Simulate Spec -Available knowledge: [[requirements/spec-simulation]], [[requirements/gherkin#key-takeaways]]. `in` artifacts: read all before starting work. `out` artifacts: `features/*.feature` (one per bounded context, created from template with Feature title and Rule blocks), `simulation_results.md`. +Available knowledge: [[requirements/spec-simulation]], [[requirements/gherkin#key-takeaways]]. `in` artifacts: read all before starting work. `out` artifacts: `features/*.feature` (one per bounded context, created from template with Feature title and Rule blocks), `.cache/sim/simulation_results_<timestamp>.md` (one file per run, timestamp format `YYYYMMDDTHHMMSS`). 1. Read `domain_spec.md` (all bounded contexts). Read `product_definition.md` for quality attributes that must be tested. Read `glossary.md` for term definitions. -2. If `simulation_results.md` exists from a prior iteration, read it for previously discovered rules and unresolved pain points — this iteration must walk walkthroughs that address those pain points. -3. For each bounded context (`## <Context Name>` section) in domain_spec.md, identify simulation walkthroughs per [[requirements/spec-simulation#concepts]]: - - **Happy paths**: primary use cases from interview data, one per user persona. - - **Edge cases**: boundary conditions (empty inputs, maximum values, concurrent operations, out-of-order events). - - **Error paths**: invalid inputs, precondition failures, integration point failures, timeout scenarios. - - **Quality attribute tests**: walkthroughs that stress each quality attribute from product_definition.md. +2. If prior simulation results exist (`.cache/sim/simulation_results_*.md`), read the latest (most recent timestamp) for previously discovered rules and unresolved pain points — this iteration must walk walkthroughs that address those pain points. +3. For each bounded context (`## <Context Name>` section) in domain_spec.md, identify simulation walkthroughs per [[requirements/spec-simulation#concepts]]. 4. For each walkthrough, mentally walk through the system as described in the domain spec: - What inputs arrive? What state is the system in? - What should happen according to the spec? - What is the resulting state and output? - Does the spec cover this walkthrough? If not, that is a pain point. -5. Create I/O evidence in `/tmp/sim/<context>/` per [[requirements/spec-simulation#concepts]]: `walkthrough_<N>_in.json` and `walkthrough_<N>_out.json` pairs. These files prove the simulation was walked and provide concrete examples for later test writing. -6. Classify and record pain points per [[requirements/spec-simulation#concepts]]: - - **Ambiguous**: spec can be read multiple ways. - - **Contradictory**: spec says two things that conflict. - - **Missing**: walkthrough not covered by spec at all. - - **Edge-case**: spec covers the happy path but not this boundary condition. +5. Create I/O evidence in `.cache/sim/<context>/` per [[requirements/spec-simulation#concepts]]: `walkthrough_<N>_in.json` and `walkthrough_<N>_out.json` pairs. These files prove the simulation was walked and provide concrete examples for later test writing. +6. Classify and record pain points per [[requirements/spec-simulation#concepts]]. 7. Write each discovered rule as a Rule block in the appropriate .feature file. Use the template at `.templates/docs/features/<feature_name>.feature.template` to create the file for each bounded context. The Rule block format: `Rule: <2-6 word title>` followed by a behavioral description paragraph. Each rule must cite the walkthrough that discovered it. 7a. Verify all titles meet constraints per [[requirements/gherkin#key-takeaways]]: every Feature title is 2–6 words and unique across all .feature files; every Rule title is 2–6 words and unique within its .feature file. Count words by splitting on whitespace. If a title fails, rephrase and re-check. 8. For each bounded context, read the `### Technology Requirements` table in domain_spec.md. Write each technology requirement as a `# Constraints:` entry in the context's .feature file. Use the Verification column's instruction as the constraint text. Technology constraints carry implementation mandates forward from the domain spec to the artifacts the SE reads. -9. Record walkthrough→rule provenance in `simulation_results.md`: each walkthrough's `Discovered Rule` column references the Rule title written to the .feature file. -10. **E2E completeness walk**: string all discovered rules together into an end-to-end user journey per bounded context. Read rules from the .feature files, not simulation_results.md. For each context, verify: +9. Record walkthrough→rule provenance in the simulation results file: each walkthrough's `Discovered Rule` column references the Rule title written to the .feature file. +10. **E2E completeness walk**: string all discovered rules together into an end-to-end user journey per bounded context. Read rules from the .feature files, not the simulation results file. For each context, verify: - The rules cover a complete happy-path flow from input to observable output. No "imagined" steps — every transition between rules has a defined trigger and output in the domain spec. - External Contract rules have enough detail to generate real fixtures: response shape, status codes, error shapes, field types. - The composed rules would produce a working application if implemented exactly as described. If any step in the E2E flow is undefined, record it as a pain point (type: `missing-e2e`). - Cross-context flows are complete: where one context's output feeds another context's input, both sides are specified with matching data shapes. -11. Write `simulation_results.md` from the template at `.templates/docs/spec/simulation_results.md.template`. One file covering all contexts. Record pain points and walkthrough→rule provenance only — discovered rules live in .feature files. Template sections: Walkthroughs Performed (with Discovered Rule column referencing .feature Rule titles), Pain Points, Resolution Status, Summary. +11. Write `.cache/sim/simulation_results_YYYYMMDDTHHMMSS.md` from the template at `.templates/.cache/sim/simulation_results_YYYYMMDDTHHMMSS.md.template`. Replace `YYYYMMDDTHHMMSS` with the current ISO timestamp. One file covering all contexts. Record pain points and walkthrough→rule provenance only — discovered rules live in .feature files. Template sections: Walkthroughs Performed (with Discovered Rule column referencing .feature Rule titles), Pain Points, Resolution Status, Summary. diff --git a/.opencode/skills/verify-traceability/SKILL.md b/.opencode/skills/verify-traceability/SKILL.md index 186c81d9..20c86c4d 100644 --- a/.opencode/skills/verify-traceability/SKILL.md +++ b/.opencode/skills/verify-traceability/SKILL.md @@ -7,7 +7,5 @@ description: "Verify example-to-test traceability via beehave check and semantic Available knowledge: [[software-craft/test-design#key-takeaways]], [[requirements/gherkin#key-takeaways]]. `in` artifacts: read all before starting work. -1. Run `beehave check` to verify structural traceability: every Example in the feature file has exactly one corresponding test function, and every test function traces back to an Example. pytest-beehave enforces this via title-based mapping (Example title → function name `test_<slug>`). `beehave check` reports: unmapped-scenario, unmapped-test, misplaced-test, missing-placeholder, missing-literal, example-mismatch. -2. IF `beehave check` reports any violations → traceability is incomplete. List the specific violations. -3. IF `beehave check` passes → structural traceability is complete. -4. Verify semantic depth per [[software-craft/test-design#concepts]]: for each Example that describes a user-facing command or API invocation, verify the corresponding test exercises the entry point described in the acceptance criterion (e.g., command handler, API endpoint), not just the domain logic in isolation. A test that calls domain methods directly when the AC describes a user-facing command is a semantic alignment gap: it has structural traceability but wrong semantic depth. +1. Run `beehave check` and verify all violations resolved per [[software-craft/test-stubs#concepts]]. +2. Verify semantic depth per [[software-craft/test-design#concepts]]: for each Example that describes a user-facing command or API invocation, verify the corresponding test exercises the entry point described in the acceptance criterion (e.g., command handler, API endpoint), not just the domain logic in isolation. A test that calls domain methods directly when the AC describes a user-facing command is a semantic alignment gap: it has structural traceability but wrong semantic depth. diff --git a/.opencode/skills/write-bdd-features/SKILL.md b/.opencode/skills/write-bdd-features/SKILL.md index 94dac5cd..4c20d06e 100644 --- a/.opencode/skills/write-bdd-features/SKILL.md +++ b/.opencode/skills/write-bdd-features/SKILL.md @@ -9,29 +9,19 @@ Available knowledge: [[requirements/gherkin]], [[requirements/moscow]], [[requir 1. Discover and read the feature file, product definition, domain spec, and glossary from `in`. 2. Run a pre-mortem per [[requirements/pre-mortem]] for each Rule before writing any Examples. All Rules must have their pre-mortems completed before any Examples are written. -3. IF hidden failure modes surface from the pre-mortem → add Examples to cover them per [[requirements/gherkin#key-takeaways]]. -4. Convert behavior hints to Example or Scenario Outline blocks per [[requirements/gherkin#concepts]]: - a. **Title constraint**: every Example/Scenario Outline title must be 2–6 words and unique within the feature file. Count words by splitting on whitespace. If a title is too short or too long, rephrase before writing the Example. - b. **Example vs Scenario Outline decision**: if the same behavioural outcome must be verified across 3+ different input/output value combinations → use `Scenario Outline:` with `<placeholder>` syntax and an `Examples:` table. Otherwise use `Example:`. Do NOT use Scenario Outline for single or two-value cases. - c. **Scenario Outline format**: include all `<placeholder>` names in Given/When/Then steps. Provide at least 3 concrete rows in the Examples table. Placeholder names must be valid Python identifiers (not keywords, not builtins). - d. **Literals for traceability**: use quoted strings (`"value"`) and bare numbers (`42`) in steps so beehave can extract and verify them in test bodies. - e. **Declarative style**: describe behaviour, not UI steps. Use `Example:` keyword for single cases (not `Scenario:`). -5. Remove behavior hints section after all hints are converted (cleanup gate — no `Behavior hints:` text should remain in the .feature file). -6. For each Rule, verify Examples cover distinct behaviours per [[requirements/gherkin#concepts]]: +3. IF hidden failure modes surface from the pre-mortem → plan Examples to cover them per [[requirements/gherkin#key-takeaways]]. +4. For each Rule, write Example or Scenario Outline blocks directly from the Rule description and domain spec knowledge per [[requirements/gherkin#concepts]]. Do NOT use behavior hints — they have been removed from the flow. Derive Example behavior directly from: + - The Rule's behavioral description paragraph + - The domain spec's External Contracts, Data Shapes, and Invariants + - The feature's `# Constraints:` comments + - Quality attributes from product_definition.md + Write Examples per format rules in [[requirements/gherkin#concepts]]. +5. For each Rule, verify Examples cover distinct behaviours per [[requirements/gherkin#concepts]]: a) Group Examples by `Then` outcome. Same outcome = same behaviour. Keep one representative per outcome. Discard duplicates. Exception: Scenario Outline rows are parameterized variants of the same behaviour — they are NOT duplicates. b) For each distinct outcome, run the behavior-level pre-mortem per [[requirements/pre-mortem#concepts]]. c) Add Examples targeting the failure modes surfaced. d) Structural (invariant) rules: one representative Example suffices. Defer full coverage to a Hypothesis property test per [[software-craft/test-design#concepts]]. -7. Classify each Example per [[requirements/moscow#concepts]]; MoSCoW classification is for internal triage only: do NOT add Must/Should/Could tags to Examples in the .feature file. -8. IF a Rule has more than 8 Must Examples → split the Rule per [[requirements/decomposition#key-takeaways]]. -9. IF a Rule spans more than 2 concerns → split per [[requirements/decomposition#key-takeaways]]. -10. Verify every Rule title is 2–6 words. If any Rule title is too long or too short, rephrase to fit within the constraint while preserving the rule's meaning. -11. Evaluate each Rule's Examples for quality, checking every criterion per [[requirements/gherkin#concepts]]: - a. **Title word count**: every Example and Scenario Outline title is 2–6 words. Count words by splitting on whitespace. If a title is outside the range → rephrase. Flag every violation. - b. **Uniqueness**: no duplicate Example or Scenario Outline titles within the feature file. - c. **Observable**: single outcome per Then clause. - d. **Declarative**: behaviour not steps. - e. **Distinct**: no duplicate coverage across Examples. - f. **Pre-mortem coverage**: failure modes from pre-mortem have corresponding Examples. - g. **Scenario Outline**: correct use for multi-variant cases (3+ rows in Examples table). - Every criterion that fails is a hard blocker: fix before advancing. +6. Classify each Example per [[requirements/moscow#concepts]]; MoSCoW classification is for internal triage only: do NOT add Must/Should/Could tags to Examples in the .feature file. +7. IF a Rule has more than 8 Must behaviors (after grouping by Then-outcome and collapsing Scenario Outlines) → this is a soft flag for PO review. Do NOT split or modify the Rule — Rule structure is frozen after define-flow. Decomposition was applied during refine-features; this check catches edge cases that slipped through. A Rule with 9+ Must behaviors is acceptable if the behaviour genuinely requires that many distinct cases. +8. Evaluate each Rule's Examples for quality, checking every criterion per [[requirements/gherkin#concepts]]: + Evaluate Example quality per criteria in [[requirements/gherkin#concepts]]. Every criterion that fails is a hard blocker: fix before advancing. \ No newline at end of file diff --git a/.templates/docs/features/<feature_name>.feature.template b/.templates/docs/features/<feature_name>.feature.template index 1dbb3106..bcde60ff 100644 --- a/.templates/docs/features/<feature_name>.feature.template +++ b/.templates/docs/features/<feature_name>.feature.template @@ -3,6 +3,6 @@ Feature: <Feature title> <2-4 sentence description of what this feature does and why it exists. Written in plain language, always kept current by the PO. - Discovered rules are written as Rule blocks during simulation. Behavior - hints are added during feature-breakdown and converted to Gherkin - Example and Scenario Outline blocks during feature-examples.> + Discovered rules are written as Rule blocks during simulation, then + refined into feature boundaries by refine-features. Examples and + Scenario Outline blocks are added during feature-examples.> \ No newline at end of file diff --git a/.templates/docs/interview-notes/IN_YYYYMMDD_<session_id>.md.template b/.templates/docs/interview-notes/IN_YYYYMMDD_<session_id>.md.template deleted file mode 100644 index 913ac194..00000000 --- a/.templates/docs/interview-notes/IN_YYYYMMDD_<session_id>.md.template +++ /dev/null @@ -1,59 +0,0 @@ -# IN_YYYYMMDD_<session_id> — <short-description> - -> **Status:** IN-PROGRESS | COMPLETE -> **Interviewer:** PO -> **Participant(s):** <stakeholder name(s)> -> **Session type:** Initial discovery | Domain deep-dive | Scope refinement | Feature specification - ---- - -## 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? | ... | - -## Domain Questions - -| ID | Question | Answer | -|----|----------|--------| -| Q8 | ... | ... | - -## Feature: <feature-stem> - -| ID | Question | Answer | -|----|----------|--------| -| Q9 | ... | ... | - ---- - -## Quality Attributes - -| ID | Attribute | Scenario | Target | Priority | -|----|-----------|----------|--------|----------| -| QA1 | <e.g. Performance / Availability / Security> | <stimulus-response: "When X happens, the system responds within Y"> | <measurable threshold> | <Must / Should / Nice> | -| QA2 | ... | ... | ... | ... | - ---- - -## Pain Points Identified - -- <Pain point from this session> - -## Business Goals Identified - -- <Goal from this session> - -## Terms to Define (for glossary) - -- <Term heard during interview that needs a glossary entry> - -## Action Items - -- [ ] <Follow-up action from this session> diff --git a/.templates/docs/spec/domain_spec.md.template b/.templates/docs/spec/domain_spec.md.template index 59fc4a8e..ed7a73b2 100644 --- a/.templates/docs/spec/domain_spec.md.template +++ b/.templates/docs/spec/domain_spec.md.template @@ -62,9 +62,9 @@ and why it exists as a separate context.> ### Aggregate Boundaries -| Aggregate | Root Entity | Invariants | Why Grouped | -|-----------|-------------|------------|-------------| -| `<Aggregate>` | `<RootEntity>` | <business rules this aggregate enforces> | <business reason for the boundary> | +| Aggregate | Root Entity | Why Grouped | See | +|-----------|-------------|-------------|-----| +| `<Aggregate>` | `<RootEntity>` | <business reason for the boundary> | ### Invariants | ### Data Shapes @@ -84,9 +84,12 @@ and why it exists as a separate context.> #### <This Context> -> <Other Context> - Purpose: <why these contexts communicate> - Trigger: <what initiates the communication> +- Mechanism: <sync HTTP, async event, shared DB, etc.> - Pattern: <Vernon pattern from Context Map> - Payload: {<field>: <type>} (may have ? for unknowns) - Response: {<field>: <type>} (may have ? for unknowns) +- Error handling: <retry, dead letter, circuit breaker, etc.> +- Ownership: <which team or service owns this integration point> ### External Contracts diff --git a/.templates/docs/spec/glossary.md.template b/.templates/docs/spec/glossary.md.template index f6984dbe..6fe9290f 100644 --- a/.templates/docs/spec/glossary.md.template +++ b/.templates/docs/spec/glossary.md.template @@ -20,12 +20,12 @@ **Source:** spec-validation-flow ### Rule - **Definition:** A behavioral requirement subject to INVEST validation. Represented as a `Rule:` block in a .feature file consisting of a 3-8 word title followed by a behavioral description paragraph. + **Definition:** A behavioral requirement subject to INVEST validation. Represented as a `Rule:` block in a .feature file consisting of a 2-6 word title followed by a behavioral description paragraph. **Aliases:** user story (deprecated), story (deprecated) **Source:** planning-flow ### Rule block - **Definition:** The Gherkin `Rule:` section in a .feature file: a 3-8 word title followed by a behavioral description paragraph. The authoritative form of a behavioral requirement. + **Definition:** The Gherkin `Rule:` section in a .feature file: a 2-6 word title followed by a behavioral description paragraph. The authoritative form of a behavioral requirement. **Source:** planning-flow ### behavior hint @@ -34,7 +34,7 @@ **Source:** planning-flow ### Example block - **Definition:** The Gherkin `Example:` section in a .feature file: a 3-8 word title followed by Given/When/Then steps describing a single behavioral outcome. + **Definition:** The Gherkin `Example:` section in a .feature file: a 2-6 word title followed by Given/When/Then steps describing a single behavioral outcome. **Aliases:** BDD scenario, BDD example, acceptance criterion (deprecated) **Source:** planning-flow @@ -43,7 +43,7 @@ **Source:** planning-flow ### pain point - **Definition:** A gap, contradiction, ambiguity, or edge case found during spec simulation. Classified as ambiguous, contradictory, missing, or edge-case. Tracked in simulation_results.md. + **Definition:** A gap, contradiction, ambiguity, or edge case found during spec simulation. Classified as ambiguous, contradictory, missing, or edge-case. Tracked in `.cache/sim/simulation_results_<timestamp>.md`. **Source:** spec-validation-flow --- @@ -54,8 +54,8 @@ **Definition:** Monolithic structural domain specification. Contains context map, entities, relationships, aggregate boundaries, data shapes, integration points, and external contracts. Behavioral sections (State Machines, Error Handling, Invariants) are derived summaries that reference .feature file rules. **Source:** discovery-flow -### simulation_results.md - **Definition:** Per-iteration simulation output. Contains walkthroughs performed (with Discovered Rule column referencing .feature Rule titles), pain points, resolution status, and reviewer PASS/FAIL decision. Rules themselves live in .feature files. +### simulation_results_<timestamp>.md + **Definition:** Per-iteration simulation environment artifact in `.cache/sim/`. Contains walkthroughs performed (with Discovered Rule column referencing .feature Rule titles), pain points, resolution status, and reviewer PASS/FAIL decision. Rules themselves live in .feature files. Not a persistent specification — domain knowledge migrates to domain_spec.md and .feature files during the spec-validation loop. **Source:** spec-validation-flow ### .feature file diff --git a/.templates/docs/spec/simulation_results.md.template b/.templates/docs/spec/simulation_results.md.template deleted file mode 100644 index ab87ff97..00000000 --- a/.templates/docs/spec/simulation_results.md.template +++ /dev/null @@ -1,59 +0,0 @@ -# Simulation Results — Iteration <N> - -> One file per simulation iteration, covering all bounded contexts. -> This document captures what the simulation found: walkthroughs performed, -> pain points discovered, and rule provenance. Discovered rules themselves -> are written directly to .feature files during simulation. - ---- - -## <Context Name> - -### Walkthroughs Performed - -| # | Walkthrough | Type | Outcome | Discovered Rule | -|---|-------------|------|---------|-----------------| -| 1 | <happy path description> | happy | PASS / FAIL: <reason> | `<Rule title in .feature>` | -| 2 | <edge case description> | edge | PASS / FAIL: <reason> | `<Rule title in .feature>` | -| 3 | <error path description> | error | PASS / FAIL: <reason> | `<Rule title in .feature>` | - -#### I/O Evidence - -Walkthroughs are backed by I/O pairs in /tmp/sim/<context>/: - -- `/tmp/sim/<context>/walkthrough_001_in.json` — input -- `/tmp/sim/<context>/walkthrough_001_out.json` — expected output -- `/tmp/sim/<context>/walkthrough_002_in.json` — input -- `/tmp/sim/<context>/walkthrough_002_out.json` — expected output - -### Pain Points - -< descriptive statements, no numbered prefixes > - -- <description> — <spec section> — <classification: ambiguous / contradictory / missing / edge-case> -- <description> — <spec section> — <classification> - -### Resolution Status - -| Pain Point | Resolved | How | -|------------|----------|-----| -| <description> | Yes/No | <how resolved, or "deferred to next iteration"> | - ---- - -## <Next Context Name> - -< Repeat the same section structure for each bounded context. > - ---- - -## Summary - -- Iteration: <N> of max 5 -- Contexts simulated: <count> -- Walkthroughs performed: <count> -- Rules discovered: <count> (written to .feature files) -- Pain points found: <count> -- Pain points resolved: <count> -- Pain points unresolved: <count> -- Reviewer decision: PASS / FAIL (needs iteration <N+1>) diff --git a/AGENTS.md b/AGENTS.md index 4943c7f6..15397316 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -12,7 +12,7 @@ Post-mortem analysis shows these practices prevent most project failures. Violat ## Project Structure - `.flowr/flows/`: YAML state machine definitions (source of truth for routing) -- `.flowr/sessions/`: runtime session state +- `.cache/sessions/`: runtime session state - `.templates/`: artifact templates (strip `.templates/` prefix and `.template` suffix → destination path) - `.opencode/`: agents, skills, and knowledge @@ -21,7 +21,9 @@ Post-mortem analysis shows these practices prevent most project failures. Violat When creating a document, use the template in `.templates/` that matches the artifact type. Strip the `.templates/` prefix and `.template` suffix to determine the destination path. For example: - `.templates/docs/adr/ADR_YYYYMMDD_<adr_id>.md.template` → `docs/adr/ADR_20260430_my_decision.md` - `.templates/docs/features/<feature_name>.feature.template` → `docs/features/my_feature.feature` -- `.templates/docs/interview-notes/IN_YYYYMMDD_<session_id>.md.template` → `docs/interview-notes/IN_20260430_session_management.md` +- `.templates/.cache/interview-notes/IN_YYYYMMDD_<session_id>.md.template` → `.cache/interview-notes/IN_20260430_session_management.md` +- `.templates/.cache/sim/simulation_results_YYYYMMDDTHHMMSS.md.template` → `.cache/sim/simulation_results_20260517T143000.md` +- `.templates/.cache/acceptance/<feature_id>.md.template` → `.cache/acceptance/domain_value_objects.md` If no template exists for an artifact type, create the document without one. @@ -73,17 +75,19 @@ Artifact names in `in` and `out` lists use these conventions: | Pattern | Meaning | Example | |---------|---------|---------| | `filename.md` | A specific document | `domain_spec.md`, `product_definition.md` | -| `dir/<param>.ext` | A specific instance identified by parameter | `features/<feature_id>.feature`, `interview-notes/<session_id>.md`, `adr/<adr_id>.md` | -| `dir/*.ext` | Multiple documents of that type available in `in` | `interview-notes/*.md`, `adr/*.md` | +| `dir/<param>.ext` | A specific instance identified by parameter | `features/<feature_id>.feature`, `.cache/interview-notes/<session_id>.md`, `adr/<adr_id>.md` | +| `dir/*.ext` | Multiple documents of that type available in `in` | `.cache/interview-notes/*.md`, `adr/*.md` | | `conceptual_name` | A runtime artifact that passes between states within a flow | `typed-source-stubs`, `test-implementations` | Placeholders in template filenames and flow artifact paths use the `<type_id>` pattern where **type** identifies the document kind and **_id** signals snake_case formatting. See template filenames for the canonical placeholder names. -**File naming rule:** All filenames use **snake_case** (e.g., `domain_value_objects.feature`, `ADR_20260504_protocol_adapters.md`). **Doc folders** use kebab-case for multi-word names (e.g., `interview-notes/`, `post-mortem/`). **Python/test folders** use snake_case (e.g., `tests/features/`). +**File naming rule:** All filenames use **snake_case** (e.g., `domain_value_objects.feature`, `ADR_20260504_protocol_adapters.md`). **Cache folders** use kebab-case for multi-word names (e.g., `interview-notes/`, `post-mortem/`). **Python/test folders** use snake_case (e.g., `tests/features/`). **Wildcards (`*`)** in `in` indicate that multiple documents of that type are available. List the directory contents first, then read selectively based on the task. When a state creates a single instance, use a `<parameter>` name instead. -**Runtime artifacts** (not backed by files) use descriptive names that make their purpose clear: `typed-source-stubs` (source files with type signatures only), `test-skeletons` (test files with structure only), `test-implementations` (tests with bodies), `source-implementations` (production code with behavior), `refactored-source` (code after refactoring pass), `feature-commits` (git commits for one feature), `merged-commits` (commits merged to local main), `root-cause-analysis` (analysis findings). +**Runtime artifacts** (not backed by files) use descriptive names that make their purpose clear: `typed-source-stubs` (source files with type signatures only), `test-skeletons` (test files with structure only), `test-implementations` (tests with bodies), `source-implementations` (production code with behavior), `refactored-source` (code after refactoring pass), `feature-commits` (git commits for one feature), `merged-commits` (commits merged to local main), `root-cause-analysis` (analysis findings), `polished-source` (code after convention application). + +**Cache artifacts** are persisted to `.cache/` for cross-session durability. They are not spec documents but process evidence that survives session boundaries: `.cache/acceptance/<feature_id>.md` (PO acceptance record with traceability matrix), `.cache/interview-notes/<session_id>.md` (raw stakeholder input, archival after discovery), `.cache/sim/simulation_results_<timestamp>.md` (simulation evidence per iteration). **Environment artifacts** are produced by tooling rather than flow states: `coverage-reports` (test coverage output), `test-output` (test runner output), `linter-output` (linter output). These exist on disk after running the relevant tool and are referenced in `in` but not in any state's `out`. @@ -113,7 +117,6 @@ Commands accept short flow names (e.g., `planning-flow`) or full file paths. Use | `python -m flowr session show [--name <name>]` | Display current session state and call stack | | `python -m flowr session set-state <state> [--name <name>]` | Manually update session state | | `python -m flowr session list` | List all sessions | -| `task regenerate-flowviz` | Regenerate interactive D3.js visualization | ## Project Commands @@ -178,7 +181,18 @@ Before starting a flow, create a session to track progress: python -m flowr session init <flow> --name <name> ``` -For project-level flows (discovery, architecture, branding, setup), use a descriptive name like `project`. For feature flows, use the feature name. The session tracks the current flow, state, call stack (for subflows), and params (including `feature-id`). When the first state has a `flow:` field, `session init` auto-enters the subflow. +For project-level flows, use a descriptive name like `project`. For feature flows, use the feature name. The session tracks the current flow, state, call stack (for subflows), and params (including `feature-id`). When the first state has a `flow:` field, `session init` auto-enters the subflow. + +The three primary flows are independently invocable: +- `define-flow` — spec creation, validation, feature refinement, and architecture (discovery → spec-validation → refine-features → architecture) +- `develop-flow` — feature selection, example writing, TDD implementation, acceptance (per feature cycle) +- `deliver-flow` — squash-merge, publish decision, PR creation + +### Cross-Flow Routing + +When develop-flow exits `needs-architecture`, the orchestrator must re-enter define-flow at the `architecture` state. Start a new define-flow session and use `flowr session set-state architecture` to skip to the architecture state. The architecture-flow fast-path (`architecture-complete: ==verified`) means re-running is cheap when no changes are needed. + +When post-mortem-flow exits `needs-architecture`, follow the same procedure: re-enter define-flow at `architecture`. ### Branch Discipline @@ -186,7 +200,7 @@ States declare their git context in `attrs.git`: - `git: main`: all changes are committed to the local main branch - `git: feature`: all changes are committed to the current feature branch -Before exiting a project-phase flow (discovery, architecture, branding, setup), the exit transition requires `committed-to-dev-locally: ==verified` evidence. This guarantees project artifacts are persisted before advancing to the next phase. +Before exiting a project-phase flow (define, branding, setup), the exit transition requires `committed-to-dev-locally: ==verified` evidence. This guarantees project artifacts are persisted before advancing to the next phase. ### Within a State diff --git a/CHANGELOG.md b/CHANGELOG.md index 75cf5e14..6b4cf7e8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,47 @@ All notable changes to this template will be documented in this file. +## [v9.0.0+20260518] - 2026-05-18: Adversarial Audit + +### Breaking + +- **Define-flow reorder**: `spec-validation → architecture → refine-features → validated` (was `spec-validation → refine-features → architecture → validated`). Architecture now runs before refine-features so quality_attributes, integration_points, and technology_stack are finalized before feature constraints are frozen. +- **Architecture skippable**: architecture-assessment sets `architecture-complete: ==verified` and exits immediately when no architecture changes needed — no spec mutations on the skip path. + +### Added + +- **Decomposition decision tree**: replaced 5 stale triggers (">8 candidate Examples", ">2 concerns", undefined "concerns") with behavioral decision tree: group by Then-outcome → collapse Scenario Outlines → count Must behaviors → >8 Musts splits Rule. Structural triggers: >1 bounded context, "and" joining independently testable outcomes, no beneficiary, duplicate Rule. +- **WSJF scoring wired**: `select-feature` now scores subsequent features by WSJF (Value/Effort/Dependency) per `[[requirements/wsjf#key-takeaways]]`. First feature still selected by delivery order. +- **AI language markers expanded**: Tier 1 +25 words (navigate, harness, seamless, boilerplate, etc.), Tier 4 +4 phrases, new Tier 7 Claude-Specific Structural Patterns (8 patterns), Claude detection dimension in scoring table. +- **Domain spec template**: Integration Points section now requires Mechanism, Error handling, Ownership fields (synced with context-mapping knowledge). + +### Changed + +- **Adversarial audit — 47 knowledge files, 39 skills**: comprehensive deduplication, stale content removal, and cross-reference alignment across all domains. +- **Knowledge deduplication** (12 files): invest.md, moscow.md, decomposition.md, feature-discovery.md, technical-design.md, assessment.md, flowr-operations.md, todo-anchor-protocol.md, agent-design/principles.md, skill-design/principles.md, color-systems.md, project-assets.md — inline duplications replaced with cross-references to canonical sources. +- **Skill deduplication** (10 files): refactor, review-gate, simulate-spec, fix-spec, review-simulation, create-py-stubs, verify-traceability, write-bdd-features, accept-feature, refine-features — inline knowledge restatements replaced with references. +- **ADR structure aligned**: `adr.md` now documents Interview section and "Alternatives Considered" field name matching the actual template. +- **Owner mapping consolidated**: removed from flowr-operations.md and todo-anchor-protocol.md; lives only in AGENTS.md. +- **"As the system, I want..." removed**: all user story format examples replaced with Rule-native language ("Rule with no beneficiary"). +- **spec-architecture.md Terminology Map**: removed "story, user story" from deprecated column to prevent agents interpreting it as a format hint. + +### Removed + +- **`spec-architecture.md`** (requirements): fully covered by rule-derivation, spec-simulation, feature-discovery, gherkin, feature-boundaries, and the flow YAML files themselves. Zero references, highest drift risk. +- **`rule-derivation.md`** (requirements): fully covered by spec-simulation.md (simulation rule discovery) and feature-discovery.md (traceability/gap verification). Zero skill references. +- **`domain-modeling.md`** (domain-modeling): merged into event-storming.md as `### Formalization` subsection (Entity/VO distinction, relationship types, table formats, boundary heuristics). Zero skill references. + +### Fixed + +- **gherkin.md:173**: "3 words or longer than 8" → "2 words or longer than 6" to match canonical 2-6 word constraint. +- **Glossary template drift**: "3-8 word title" → "2-6 word title" on lines 23, 28, 37. +- **versioning.md:43-44**: broken `[[process/...]]` references → plain text URLs (semver.org, calver.org). +- **create-domain-spec/SKILL.md:11**: misrouted reference from `[[domain-modeling/behavioral-contracts#concepts]]` → `[[domain-modeling/event-storming#concepts]]` for bounded context identification. +- **moscow.md:14**: removed duplicate Key Takeaway (identical to line 11). +- **Gh-159**: spec-to-feature pipeline gap resolved by define-flow reorder — architecture runs before refine-features, ensuring delivery items are validated against complete spec documents before feature files are frozen. +- **Gh-144**: technical_design.md.template redundancies resolved by knowledge dedup — technical-design.md no longer duplicates quality-attributes, contract-design, or assessment content. +- **Gh-143**: mandatory artifact reads, out-artifact lifecycle protocol, and cumulative editing guidance now in AGENTS.md Session Protocol. + ## [v8.6.0+20260515] - 2026-05-15: Dialogic Chronos ### Added diff --git a/pyproject.toml b/pyproject.toml index 99c9129b..25fe132b 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "temple8" -version = "8.6.0" +version = "9.0.0" description = "Spec-driven agent orchestration template with YAML flow definitions, multi-agent dispatch, and BDD traceability" readme = "README.md" requires-python = ">=3.14" @@ -39,8 +39,8 @@ dev = [ [tool.flowr] flows_dir = ".flowr/flows" -sessions_dir = ".flowr/sessions" -default_flow = "main-flow" +sessions_dir = ".cache/sessions" +default_flow = "define-flow" default_session = "default" [tool.beehave] @@ -146,7 +146,7 @@ pytest \ doc-publish = "task doc-build && ghp-import -n -p -f docs" static-check = "pyright app tests" validate-flows = "bash -c 'for f in .flowr/flows/*.yaml; do python -m flowr validate \"$f\"; done'" -regenerate-flowviz = "python .flowr/viz/generate-flowviz-data.py" + release-check = "task conventions && task static-check && task test && task doc-build" diff --git a/uv.lock b/uv.lock index 80ad204f..1d49b7ba 100644 --- a/uv.lock +++ b/uv.lock @@ -955,7 +955,7 @@ wheels = [ [[package]] name = "temple8" -version = "8.6.0" +version = "9.0.0" source = { virtual = "." } [package.optional-dependencies]