diff --git a/.github/workflows/community-smoke.yml b/.github/workflows/community-smoke.yml index 4fc9f6541f..1b0dc74758 100644 --- a/.github/workflows/community-smoke.yml +++ b/.github/workflows/community-smoke.yml @@ -65,31 +65,38 @@ jobs: PY test -f .specify/presets/workflow-preset/templates/behavior/behavior-scenarios-draft.json test ! -e .specify/presets/workflow-preset/templates/behavior/behavior-testability-checklist.md - test -f .specify/presets/workflow-preset/templates/behavior/behavior-testability.md + test ! -e .specify/presets/workflow-preset/templates/behavior/behavior-testability.md test -f .specify/presets/workflow-preset/templates/requirements/behavior-gate.md test -f .specify/presets/workflow-preset/templates/requirements/domain-gate.md test -f .specify/presets/workflow-preset/templates/requirements/nfr-gate.md test -f .specify/presets/workflow-preset/templates/requirements/visual-gate.md test -f .specify/presets/workflow-preset/templates/behavior/bdd-draft.feature test -f .specify/presets/workflow-preset/templates/behavior/bdd-contract.feature - test -f .specify/presets/workflow-preset/templates/behavior/uif-intent.json + test ! -e .specify/presets/workflow-preset/templates/behavior/uif-intent.json test -f .specify/presets/workflow-preset/templates/behavior/uif-expected.json test -f .specify/presets/workflow-preset/templates/behavior/scenario-instances.json - test -f .specify/presets/workflow-preset/templates/behavior/data-fixtures-intent.json + test ! -e .specify/presets/workflow-preset/templates/behavior/data-fixtures-intent.json test -f .specify/presets/workflow-preset/templates/behavior/data-fixtures.json test -f .specify/presets/workflow-preset/templates/behavior/assertions.json test -f .specify/presets/workflow-preset/templates/constitution-template.md + test -f .specify/presets/workflow-preset/templates/class-diagram-template.md + test -f .specify/presets/workflow-preset/templates/sequences-template.md + test -f .specify/presets/workflow-preset/templates/ui-ux-design-template.md + test -f .specify/presets/workflow-preset/templates/quickstart-template.md + test -f .specify/presets/workflow-preset/templates/test-readiness-template.md + test -f .specify/presets/workflow-preset/templates/test/test-conditions.json test -f .specify/presets/workflow-preset/schemas/speckit.behavior.scenarios.draft.v1.schema.json - test -f .specify/presets/workflow-preset/schemas/speckit.behavior.uif.intent.v1.schema.json - test -f .specify/presets/workflow-preset/schemas/speckit.behavior.data-fixtures.intent.v1.schema.json + test ! -e .specify/presets/workflow-preset/schemas/speckit.behavior.uif.intent.v1.schema.json + test ! -e .specify/presets/workflow-preset/schemas/speckit.behavior.data-fixtures.intent.v1.schema.json test -f .specify/presets/workflow-preset/schemas/speckit.behavior.uif.expected.v1.schema.json test -f .specify/presets/workflow-preset/schemas/speckit.behavior.scenario-instances.v1.schema.json test -f .specify/presets/workflow-preset/schemas/speckit.behavior.data-fixtures.v1.schema.json test -f .specify/presets/workflow-preset/schemas/speckit.behavior.assertions.v1.schema.json - test -f .specify/presets/workflow-preset/.composed/speckit.specify.md - test -f .specify/presets/workflow-preset/.composed/speckit.clarify.md + test -f .specify/presets/workflow-preset/schemas/speckit.test.conditions.v1.schema.json + test ! -e .specify/presets/workflow-preset/.composed/speckit.specify.md + test ! -e .specify/presets/workflow-preset/.composed/speckit.clarify.md test -f .specify/presets/workflow-preset/.composed/speckit.checklist.md - test -f .specify/presets/workflow-preset/.composed/speckit.constitution.md + test ! -e .specify/presets/workflow-preset/.composed/speckit.constitution.md test -f .specify/presets/workflow-preset/.composed/speckit.analyze.md test -f .specify/presets/workflow-preset/.composed/speckit.plan.md test -f .specify/presets/workflow-preset/.composed/speckit.tasks.md @@ -114,8 +121,11 @@ jobs: test -f .claude/skills/speckit-implement/SKILL.md grep -q "Change Scope Granularity" .claude/skills/speckit-constitution/SKILL.md - grep -q "Phase 0 Behavior Projection" .claude/skills/speckit-plan/SKILL.md - grep -q "Validation Task Derivation" .claude/skills/speckit-tasks/SKILL.md + grep -q "Full-Spectrum Projection" .claude/skills/speckit-specify/SKILL.md + grep -q "Cross-Domain Ambiguity Map" .claude/skills/speckit-clarify/SKILL.md + grep -q "Cross-Command Consistency Gates" .claude/skills/speckit-analyze/SKILL.md + grep -q "X0 — Feature Plan Control" .claude/skills/speckit-plan/SKILL.md + grep -q "PLAN_OUTPUT_READY" .claude/skills/speckit-tasks/SKILL.md grep -q "## Pre-Execution Checks" .claude/skills/speckit-implement/SKILL.md grep -q "mark the task off as \[X\] in the tasks file" .claude/skills/speckit-implement/SKILL.md diff --git a/presets/catalog.community.json b/presets/catalog.community.json index 0c1f1b956e..89b63ddfd7 100644 --- a/presets/catalog.community.json +++ b/presets/catalog.community.json @@ -670,11 +670,11 @@ "workflow-preset": { "name": "Workflow Preset", "id": "workflow-preset", - "version": "3.0.0", + "version": "3.1.0", "description": "Constitution-managed architecture, behavior-first specification, design artifacts, and execution-ready task mapping", "author": "bigsmartben", "repository": "https://github.com/bigsmartben/spec-kit-workflow-preset", - "download_url": "https://github.com/bigsmartben/spec-kit-workflow-preset/releases/download/v3.0.0/spec-kit-workflow-preset-v3.0.0.zip", + "download_url": "https://github.com/bigsmartben/spec-kit-workflow-preset/releases/download/v3.1.0/spec-kit-workflow-preset-v3.1.0.zip", "homepage": "https://github.com/bigsmartben/spec-kit-workflow-preset", "documentation": "https://github.com/bigsmartben/spec-kit-workflow-preset/blob/main/README.md", "license": "MIT", @@ -682,7 +682,7 @@ "speckit_version": ">=0.12.7.dev0" }, "provides": { - "templates": 24, + "templates": 27, "commands": 7 }, "tags": [ @@ -690,13 +690,15 @@ "constitution", "behavior", "bdd", + "test-first", + "ui-ux", "planning", "implementation" ], "created_at": "2026-05-27T00:00:00Z", "updated_at": "2026-07-26T00:00:00Z", - "source_commit": "c35c96cec18c280445ea4f9f73c929e5b61f8393", - "sha256": "3924817ded5c59649ea3ef685cd1a4572363f95e9379becd0eafe1d3757ee852" + "source_commit": "e20dcafde5a1b031643156db49eb3e988e322bf9", + "sha256": "9f92d3820ce5365a80a3816890850fb9cc190006fd3ad025ff941baa90165fcb" } } } diff --git a/presets/catalog.json b/presets/catalog.json index 8f90050fbd..06a9a8996f 100644 --- a/presets/catalog.json +++ b/presets/catalog.json @@ -29,7 +29,7 @@ "workflow-preset": { "name": "Workflow Preset", "id": "workflow-preset", - "version": "3.0.0", + "version": "3.1.0", "description": "Constitution-managed architecture, behavior-first specification, design artifacts, and execution-ready task mapping", "author": "bigsmartben", "repository": "https://github.com/bigsmartben/spec-kit-workflow-preset", @@ -40,18 +40,20 @@ }, "provides": { "commands": 7, - "templates": 24 + "templates": 27 }, "tags": [ "architecture", "constitution", "behavior", "bdd", + "test-first", + "ui-ux", "planning", "implementation" ], - "source_commit": "c35c96cec18c280445ea4f9f73c929e5b61f8393", - "sha256": "3924817ded5c59649ea3ef685cd1a4572363f95e9379becd0eafe1d3757ee852" + "source_commit": "e20dcafde5a1b031643156db49eb3e988e322bf9", + "sha256": "9f92d3820ce5365a80a3816890850fb9cc190006fd3ad025ff941baa90165fcb" } } } diff --git a/presets/workflow-preset.release.json b/presets/workflow-preset.release.json index 4b1a6990b8..98d57ead12 100644 --- a/presets/workflow-preset.release.json +++ b/presets/workflow-preset.release.json @@ -1,16 +1,16 @@ { "artifact": { - "name": "spec-kit-workflow-preset-v3.0.0.zip", - "sha256": "3924817ded5c59649ea3ef685cd1a4572363f95e9379becd0eafe1d3757ee852" + "name": "spec-kit-workflow-preset-v3.1.0.zip", + "sha256": "9f92d3820ce5365a80a3816890850fb9cc190006fd3ad025ff941baa90165fcb" }, "files": [ { "path": "AGENTS.md", - "sha256": "a3e48054a1cdedfa8ef91c642fe7b166bf951df1916ed92bc3b370d7b1f64d48" + "sha256": "90000ee5a32160f23dcbdafb73dad5be32457986255d7b9486b97d10cf81561d" }, { "path": "CHANGELOG.md", - "sha256": "b5500b5bdbe6febc33778e9bbf3dab0bb0426030788235bcf1c3d3bd154cf2e3" + "sha256": "ce932c8a0ef0bf82d6c342ea1fedb8e90cd11878674d168059d6b1e5e5561a3e" }, { "path": "LICENSE", @@ -18,43 +18,43 @@ }, { "path": "README.md", - "sha256": "b392ddb13c8cd4bf627da2d41e1ffb5ad13cf2a8c7ee6e69d1f460ecebf328eb" + "sha256": "bdfe954c0737ec587eebf557ed042836f26f0f27a076e17d17ab895c0a2822b6" }, { "path": "commands/speckit.analyze.md", - "sha256": "4ee1593b26cf7bc96246a78c3d44409775c7715cdb250b98ba6924f8b667de0e" + "sha256": "5fbd1e926038c6d587fd120851af4fd4c4153a678780113eeeaccc76e90cf0e0" }, { "path": "commands/speckit.checklist.md", - "sha256": "931dc432b490f76dc4df80e8d087bbeb1468205fa986369870ed0fd85152c9e3" + "sha256": "ff4452ea7e85fe0be28b0c88d4dd5b29ab5a1dc9738e4f2681d4d99c3cf17814" }, { "path": "commands/speckit.clarify.md", - "sha256": "cc893a93b96b2f1196540ad315cae25d0f4316651a3a1260396ed8cac3c95260" + "sha256": "e8d5af23f6a1b75d3e1dc45d9f79200f1a61774b6f1007f2d725049ec8d801bb" }, { "path": "commands/speckit.constitution.md", - "sha256": "a32ac47fb666bb2475d93d0d2ebb93426a2a5c70723ede8f5dd38005f87b0448" + "sha256": "ba051da41eae2d0eb1ec8d7df3e11fc8f251ac75f929163503a3e2add7b87121" }, { "path": "commands/speckit.plan.md", - "sha256": "26bbc0ff6ba91b72b86c317c501cfd65cf22d0e9bfc67e55d722ce6cb95b2e9e" + "sha256": "9e07188cab506100041ad1f9dbf9b92feb0bae3c9c5fde948ab2e4b9287e8eed" }, { "path": "commands/speckit.specify.md", - "sha256": "0e9a1c1521a2932d708ad34af5c67215b15d094691b12f9c6be501b20413e7c2" + "sha256": "8c5c744f42265223ce807b38e0a0626b59125bd88f253fb9449c670e198962c6" }, { "path": "commands/speckit.tasks.md", - "sha256": "7434d434f9092ab99ba02857e933f8426cbd731ffa61b24473c96382aeded219" + "sha256": "a26f2b305cc99505bfbeb80a50e1b812c949b634e1a12780c0d5247374876339" }, { "path": "docs/extension-governance.md", - "sha256": "6f9d183ff3defb1abcfc6a25970feda8855e04f70e66cb98df96d33a030a3961" + "sha256": "c2f8ae1500adf144c1d3102c6e00424ab4c420a4e25ded9f54974fd8eb38bc26" }, { "path": "preset.yml", - "sha256": "d6812ee256ac3719e5d5ea95704e7a97c46aae63cbe5a19eb7dbcf91068cd8d6" + "sha256": "68967ef1e3fb93af09ebaf3894d05cf5a57325f7839df0661dc44f2f618ec86c" }, { "path": "requirements-dev.txt", @@ -62,19 +62,15 @@ }, { "path": "schemas/speckit.behavior.assertions.v1.schema.json", - "sha256": "8f39b5b1615172ee1d0ac15a72545b60b98355474695694cca60cf0615772fed" - }, - { - "path": "schemas/speckit.behavior.data-fixtures.intent.v1.schema.json", - "sha256": "e43a3f4341b80507a8cee8e07949dd6cc0380c88655894c60c51ba0ac92873ed" + "sha256": "c9c0aef4a75f412e23fe288575e1e6b7c0be97d32ee0ea823c63e2ded1e21614" }, { "path": "schemas/speckit.behavior.data-fixtures.v1.schema.json", - "sha256": "7b763ede880aff476746b5f9eb24c540d42bcbc4d24a33b002376bdecbbca5dd" + "sha256": "3b256294997e580317caa9f37f128ec2209690cf34860c17a59d6a9f4d8e34c8" }, { "path": "schemas/speckit.behavior.scenario-instances.v1.schema.json", - "sha256": "5f847f913b32829fd4c205cbef9e994e0e065a8596f8f8be525bb34f653f2191" + "sha256": "ee2447ae9e14ec4b34aeba6f150506b4c026a6186563c8d7fe80b9661b78e4f7" }, { "path": "schemas/speckit.behavior.scenarios.draft.v1.schema.json", @@ -82,19 +78,19 @@ }, { "path": "schemas/speckit.behavior.uif.expected.v1.schema.json", - "sha256": "024653cfaef2255a1dec521feae52847e24c5e88782d2babbbdb3120e5bc81bc" + "sha256": "76165c9b0a2c8abe0314b9bd41e74c04e148c7447c643c0281fb41f5dc849fec" }, { - "path": "schemas/speckit.behavior.uif.intent.v1.schema.json", - "sha256": "f8600df13a610634330b736116f560b9b5ea72ba6f092dea096a7522e6a362ed" + "path": "schemas/speckit.test.conditions.v1.schema.json", + "sha256": "e5e0ce7adb69f031ba29bebbf801d3e7acc97590ec836581cd3de062f7ab7bfd" }, { "path": "templates/architecture-template.md", - "sha256": "9f8460ec74e6aeaef0cef686eec1ff3843aced4cd6e68ff2eb3e7291b87bcd40" + "sha256": "40435c22605f1c211134fcfea21bd19808918b57372392615767db874253fa25" }, { "path": "templates/behavior/assertions.json", - "sha256": "b47c06f6fc77bfbf76dd4fbfd1207d292196bfe5be16391e17fcbfb6e2df2bd6" + "sha256": "83b3d2e1bc7763ef0697a314b77b2a808201f4ffe5089e1bdf95b43a400bd03b" }, { "path": "templates/behavior/bdd-contract.feature", @@ -108,74 +104,98 @@ "path": "templates/behavior/behavior-scenarios-draft.json", "sha256": "6def3acc8d3a44b2023d60c3e7717d1183ef4e4b7ff7be8bfb9d1d8f4fbd451c" }, - { - "path": "templates/behavior/behavior-testability.md", - "sha256": "bbedd7a31c1ebb0b03ba1585401551f2910f3f297f62bcc3ded8d1e2b8057ad0" - }, - { - "path": "templates/behavior/data-fixtures-intent.json", - "sha256": "52860246ad830f8a3d620fbb78773127777839d50cb72da25383677adf4aba17" - }, { "path": "templates/behavior/data-fixtures.json", - "sha256": "aba3c98fd9d4a0332a2d5a57bc007885bf46279aa356e619e19b2a4ff9e47387" + "sha256": "5fa5219975d6e897c4085ee0ca79b7aaf99be8907602ccb1c9151b367c89737a" }, { "path": "templates/behavior/scenario-instances.json", - "sha256": "1bf2999050106565bbb5a0c0b93fdaabe3d64602643822b17c041db1afc2a120" + "sha256": "e666267aac2d8c1fbdedc3b800b5ff9a83ad9bb31c992915ba8179880027b03d" }, { "path": "templates/behavior/uif-expected.json", - "sha256": "20c45e08817cfd926c9586f8ccdc7bf55eb7f2e08a57b783e41a4fb5efd97959" + "sha256": "2af9d8bb3885069ba2e93f40382dffa7e23a917cbcbc8601d3e60becabc6b254" }, { - "path": "templates/behavior/uif-intent.json", - "sha256": "66572ff70127e5fcb23c0815f902a0fc525081a3c1c0d1e0e87c4be4c19bbbad" + "path": "templates/class-diagram-template.md", + "sha256": "9cf14a1d3248ec974b27ee6882fba9cd46a6047171fdd6d63db82cab7ca5ff70" }, { "path": "templates/constitution-template.md", - "sha256": "6763fba121628fa5b81b1c52ba8c6bc5485ce4c15ebb4653d88f3ebbd507391c" + "sha256": "77a3ce00c81e0a44ce5287a4ce39cde1d8d74e57777ad3a50b1ae599f230eb80" }, { "path": "templates/plan-template.md", - "sha256": "3ac5ca372aad14e8b2110aa7c48d36fbe5409646c8267444cf6fc59317b18da0" + "sha256": "653684caafb63216b5c6b1ba80adc4c04b7446494ccba55c6cd0cb80474437cf" + }, + { + "path": "templates/quickstart-template.md", + "sha256": "b80ea21a46c4c271457dcbdb805849057cd744fdf86347ef2ca3d259df3dc25c" }, { "path": "templates/requirements/behavior-gate.md", - "sha256": "432c805d3f0f1e7feea6bc0171573a410bf20a64ccc16411de2c2cc9eb7628d6" + "sha256": "687e14fb6d0262722f813d998dc3b28dc23d44d909d3b425a79f536cdf308af5" }, { "path": "templates/requirements/domain-gate.md", - "sha256": "d79afadb37e0f95a2faf4f429c88d0577e536000f97f4126ae92194bef0c26e8" + "sha256": "537f8998f43160f101f33f3755df08c4faaef72c359672b3be3e01d5aff8bcda" }, { "path": "templates/requirements/nfr-gate.md", - "sha256": "3aa029528263aae1f082822214301a39bb2008dd14a56f3c730959f63a50c79c" + "sha256": "6c05f7cfad45c9891e932994c09dfc8cd14bebe1aecd99821cf38270a52fbd1f" }, { "path": "templates/requirements/visual-gate.md", - "sha256": "0884437eba2d8e2f9ea49bc6027c721763987cee6ab63dc3806b50843cbc6974" + "sha256": "3c25de98d60f7f5f4e5352c1c8d0054b73b6c1c3ceeeb0d56c3f1d364221a828" + }, + { + "path": "templates/sequences-template.md", + "sha256": "efc7ab0dcd6d44befa8d97eb5c395cb62f53968a311991784b0b4cce58a42045" + }, + { + "path": "templates/spec-template.md", + "sha256": "400116470fc3c4dc9222eaf16b48a9f03b383158cfc271f915151cfc599dbe6a" + }, + { + "path": "templates/test-readiness-template.md", + "sha256": "7695440deacf892f1646f12e12245e28f09f4d1383d8dcb130e1240bb58793b4" + }, + { + "path": "templates/test/test-conditions.json", + "sha256": "86577f987a6d22f32c3da56a36412890918a7437e88630428ac0ed23621306e1" + }, + { + "path": "templates/ui-ux-design-template.md", + "sha256": "deda68c43af32410e529b4addde3d86fabdcce37a7251ae6975a04eaefef53f5" }, { "path": "tests/contracts/speckit-cross-agent-protocol.md", - "sha256": "17c7b2dd55f4f4c775f3bd2ed8649afea2bd9620d182ced9feaeab1bca8b2aa8" + "sha256": "faade06b315050d256d54d55fe15a8c4104ed669a0be4ad0dc01822fee776f71" }, { "path": "tests/test_preset_contract.py", - "sha256": "b513ee724b850f7e1d489bb1df2a3c50c24239c4a45697852cb2b50048268dea" + "sha256": "526e9f4bb20d17a93b2c75e0ec9e4958d57aa34ab5cc286719fcb27013b4194e" }, { "path": "validators/__init__.py", "sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" }, + { + "path": "validators/speckit_analyze_contract.py", + "sha256": "8f108da51daf1f582781bcf9833946ec8bf4e99131bb0561513cd24ea1f44394" + }, { "path": "validators/speckit_behavior_contract.py", - "sha256": "988131eb3138cc6a4a21a5ef3c936534aeceea17fe866be822ef0a3f09476ac9" + "sha256": "112d2438a6fa4d1002ee45ebfb8a86ff1fe37390c80ef9492512a8a4debc141a" + }, + { + "path": "validators/speckit_test_contract.py", + "sha256": "4c0827812f68c4c67258f1c57e5af22465d0d3e7abddc6f699bdbdc995ac6e8b" } ], "preset_id": "workflow-preset", "schema_version": "1.0", - "source_commit": "c35c96cec18c280445ea4f9f73c929e5b61f8393", + "source_commit": "e20dcafde5a1b031643156db49eb3e988e322bf9", "source_repository": "https://github.com/bigsmartben/spec-kit-workflow-preset", - "version": "3.0.0" + "version": "3.1.0" } diff --git a/presets/workflow-preset/AGENTS.md b/presets/workflow-preset/AGENTS.md index 670f8eae45..042a66453f 100644 --- a/presets/workflow-preset/AGENTS.md +++ b/presets/workflow-preset/AGENTS.md @@ -23,13 +23,17 @@ This repository is a Spec Kit community preset named `workflow-preset`. protocol, execution manifest, worker result protocol, Python orchestration, workflow shell dispatch, integration adapter scripts, or script-based worker dispatch. -- Planning design artifacts are optional and contextual: - - `class-diagram.md` - - `contracts/sequences.md` -- Validation decisions stay in `research.md` and executable paths in - `quickstart.md`; plan closeout maps them into - `behavior/behavior-testability.md`, and `/speckit.tasks` derives concrete - tasks. Do not add standalone `test-plan.md`. +- Planning uses X0–X4 internal milestones inside the unchanged Core Plan + lifecycle. X2-A Domain/Object/Interface, X2-B UI/UX Delivery, and X2-C Test & + Acceptance are parallel lanes. +- `class-diagram.md` and `contracts/sequences.md` are contextual X2-A artifacts + with explicit triggers or N/A reasons. +- `ui-ux-design.md` is the X2-B delivery/readiness carrier. +- `contracts/test/test-conditions.json` is the X2-C parent Test contract; BDD, + scenario, fixture, and assertion artifacts are optional technique children. +- Validation decisions stay in `research.md`, executable `VAL-*` paths stay in + `quickstart.md`, and `test-readiness.md` is the single Test/Tasks handoff. + Do not restore `behavior/behavior-testability.md` or add `test-plan.md`. - Do not move product requirements out of `spec.md`, domain model details out of `data-model.md`, interface schemas out of `contracts/`, or validation run guidance out of `quickstart.md`. ## Integration Boundary diff --git a/presets/workflow-preset/CHANGELOG.md b/presets/workflow-preset/CHANGELOG.md index 5051ef40f6..c2cb0d9a80 100644 --- a/presets/workflow-preset/CHANGELOG.md +++ b/presets/workflow-preset/CHANGELOG.md @@ -2,6 +2,35 @@ ## Unreleased +## 3.1.0 - 2026-07-27 + +- Split SDD governance from repository Architecture and added independent + output-ready gates for both Constitution-managed artifacts. +- Made Specify and Clarify independent, full-spectrum requirement commands; + Checklist remains a question-form requirement quality gate. +- Added one source-neutral `SRC-*` contract for natural-language direction, + requirement documents, executable visual references, technical evidence, and + context-only material without requiring an upstream provider or workflow. +- Made `spec.md` the feature-local WHAT/WHY SSOT after authorized projection; + external source identity remains opaque provenance and is never an automatic + read, execution, freshness, fidelity, or publication-state target. +- Nested X0–X4 design and test milestones inside the unchanged Core Plan + lifecycle, with parallel domain/interface, UI/UX, and test/acceptance lanes. +- Added canonical Test Conditions, executable `VAL-*` paths, and a single + `test-readiness.md` handoff; BDD, scenario, fixture, and assertion contracts + are optional technique children. +- Added `SRC-* + UI/VIS-*` projection through UI/UX Delivery and UIF + `source_refs`/`requirement_refs` without creating an external dependency. +- Made Tasks a pure T0–T5 mapper from `PLAN_OUTPUT_READY`, with required test + conditions overriding the Core optional-test default and Final Code Review + remaining last. +- Added deterministic, read-only Analyze checks for Architecture → Plan, + Plan → Tasks, M + U scope, data-model obligations, and local source-reference + integrity/projection. +- Removed obsolete intent and behavior-testability artifacts and prohibited + pixel/screenshot/diff/baseline/restoration/rendered-review, external-source + validation, and provider-tool task generation. + ## 3.0.0 - 2026-07-26 - Removed the preset-owned `speckit.implement` replacement and its manifest, diff --git a/presets/workflow-preset/README.md b/presets/workflow-preset/README.md index e71163653a..1547ad3499 100644 --- a/presets/workflow-preset/README.md +++ b/presets/workflow-preset/README.md @@ -1,224 +1,151 @@ # Workflow Preset -`workflow-preset` extends Spec Kit with Constitution-managed project -Architecture, behavior-first requirements and planning, UI/UX delivery -contracts, and an execution-ready task mapping. +`workflow-preset` 是一个 Spec Kit 社区预设(community preset),把需求、 +架构、设计、测试条件与任务映射成一条可审计的 SDD(Specification-Driven +Development,规格驱动开发)链路。 -It deliberately does **not** provide `speckit.implement`. After installation, -`/speckit.implement` always resolves to the implementation command supplied by -the currently installed Spec Kit core. +它不提供或替换 `/speckit.implement`。实现执行始终由当前安装的 Spec Kit +core(核心命令)负责。 -## Ownership Model +## 命令所有权 -| Stage | What this preset adds | Primary output | +| 命令 | 本预设的职责 | 写入边界 | |---|---|---| -| Specify | behavior, visual, and UI/UX requirement ownership | `spec.md` | -| Checklist | requirement-domain readiness gates | `checklists/*.md` | -| Plan | Architecture consumption, BDD/UIF contracts, validation design | `plan.md`, `research.md`, `quickstart.md`, `contracts/`, behavior artifacts | -| Tasks | mapping of upstream artifacts to ordered implementation, validation, e2e, and review work | `tasks.md` | -| Implement | no preset override; standard core behavior | execution of `tasks.md` | +| `/speckit.constitution` | 分离治理规则与仓库技术架构 | `constitution.md`、`architecture.md` | +| `/speckit.specify` | 编写完整 WHAT/WHY 需求 | `spec.md` | +| `/speckit.clarify` | 按影响 × 不确定性消除产品歧义 | 仅 `spec.md` | +| `/speckit.checklist` | 用问题形式检查需求写作质量 | `checklists/*.md` | +| `/speckit.plan` | 在 Core Plan 生命周期内完成 X0–X4 | Plan 设计产物 | +| `/speckit.tasks` | 把已批准的 Plan 记录映射成可执行任务 | 仅 `tasks.md` | +| `/speckit.analyze` | 只读审计跨命令追踪链 | 不写文件 | +| `/speckit.implement` | 由 Spec Kit core 执行 `tasks.md` | 本预设无覆盖 | -The lifecycle is: +## 生命周期 ```text -spec requirements and UI/UX intent - -> requirement readiness gates - -> plan, behavior/UI contracts, and validation design - -> tasks.md execution checklist - -> standard core implement +Constitution + Architecture + ↓ + Spec → Clarify → Checklist + ↓ + Core Plan(内含 X0–X4) + ↓ + Tasks(T0–T5 纯映射) + ↓ + Core Implement ``` -## Commands +### 需求层 -The preset packages seven command wrappers: +授权来源投影完成后,`spec.md` 是当前功能的产品需求唯一事实源(SSOT, +Single Source of Truth)。功能需求、非功能需求、UX/UI、视觉、安全隐私、 +数据、集成、依赖、边界、假设和排除项都使用可选载体;不适用时明确写 +N/A。 -1. `/speckit.specify` -2. `/speckit.clarify` -3. `/speckit.checklist` -4. `/speckit.constitution` -5. `/speckit.plan` -6. `/speckit.tasks` -7. `/speckit.analyze` +`/speckit.specify` 与 `/speckit.clarify` 不生成或修改 checklist。 +`/speckit.checklist` 只提出可回答的问题,不把实现方案写回需求。 -`speckit.implement` is intentionally absent from `preset.yml` and `commands/`. -This prevents the preset from freezing or shadowing a core implementation -command. +例如:退款需求可以同时声明 `FR-001`(退款规则)、`NFR-001`(响应时间)和 +`UI-001`(加载/成功/失败状态);若已授权的设计说明缺少结账失败态证据, +就在对应 `SRC-*` 行记录本地阻塞,不把它混成产品澄清问题。 -## UI/UX From Specify +### 来源中立契约 -UI/UX is a requirement concern before it is an implementation concern. -`/speckit.specify` records applicable visual and interaction needs in -`spec.md`, including states, viewport behavior, source/evidence refs, and Client -Asset Contract expectations. `/speckit.checklist` decides whether those -requirements are ready for planning. - -External provider capture stays outside the preset. Confirmed visual SSOT refs, -HTML SSOT refs, structured IR refs, screenshots, and visual proof refs may be -consumed after an intake extension has projected them into the specification. -Missing provider evidence remains an intake blocker; it is not converted into a -product clarification. - -Example: +自然语言、需求文档、可执行视觉引用和技术证据统一进入现有 +Source Reference Contract(来源引用契约): ```text -Requirement: Checkout shows loading, success, validation-error, and -payment-declined states at desktop and mobile viewports. +SRC ref | role | opaque locator/description | revision/identity +| authorized scope/facts | projected requirement refs | status/blocker ``` -The Visual Fidelity Evidence Matrix in `checklists/visual.md` records the -planning-readiness status of that requirement. It does not define screenshot -comparison, visual diff, baseline capture, or final visual review work. - -## Validation Design From Plan - -`/speckit.plan` consumes checklist-approved requirements and creates the -technical and validation contracts required for task derivation: - -- `research.md` records validation decisions, test levels, fixture strategy, - and external-system strategy. -- `quickstart.md` records executable validation paths and real-system - integration/e2e scenarios. -- `behavior/bdd.draft.feature`, `behavior/uif.intent.json`, and - `behavior/data-fixtures.intent.json` provide Phase 0 projections. -- `contracts/bdd/`, `contracts/uif/`, and `contracts/behavior/` contain formal - behavior contracts. -- `behavior/behavior-testability.md` closes the BDD Plan with a READY or - BLOCKED decision. -- `class-diagram.md` and `contracts/sequences.md` are optional contextual design - artifacts. +| 角色 | 含义 | 例子 | +|---|---|---| +| `requirement-input` | 可投影已确认、已切片的 WHAT/WHY | 当前对话中的退款规则 | +| `visual-input` | 只可投影 `UI-*` / `VIS-*` | 结账错误态的可执行页面引用 | +| `technical-evidence` | 可引用,但不升级成产品需求 | 性能测量报告 | +| `context-only` | 只作背景,不授权规范性事实 | 竞品介绍 | -This is broader than unit testing. Applicable plans cover unit, contract, -integration, UI acceptance, and real-system e2e validation. +定位符(locator)、路径、版本、摘要或文字描述都按不透明来源信息保存。 +预设不会因为看到一个引用就打开、执行或验证它,也不会推断相邻目录或要求 +上游工具。宽泛来源必须先确定当前功能切片;无法安全切片时只记录本地阻塞 +或待澄清项,不默认整份导入。 -Example: +本地链路是: ```text -quickstart.md: a buyer submits a refund against a sandbox payment service and -observes the persisted refund plus the user-visible confirmation state. +SRC-* + 本地需求 + ↓ +spec.md(本功能 WHAT/WHY SSOT) + ↓ +X2-B: ui-ux-design.md → UIF + ↓ +Tasks 只做实现映射;Analyze 只做本地引用审计 ``` -That path is a source for integration/e2e tasks, not an implementation detail -invented later by Tasks or Implement. - -## Tasks Is A Mapping Stage - -`/speckit.tasks` produces only `tasks.md`. It maps upstream deliverables into -the core checklist format and user-story organization. - -For each applicable story, it derives: - -- fixtures and environment setup; -- unit and contract validation; -- UI implementation and UI acceptance; -- implementation work; -- data-side-effect validation; -- integration and real-system e2e validation; -- evidence collection and blocker reporting. - -Task text binds the work to concrete source, test, fixture, configuration, and -asset paths plus upstream scenario, contract, visual/IR, or quickstart refs. -Tasks does not create a second plan, execution manifest, transfer file, worker -result file, write-path protocol, or execution queue. - -Example mapping: - -| Upstream product | `tasks.md` result | -|---|---| -| `SCN-ERR-001` permission failure | fixture + BDD/contract test + implementation + evidence tasks | -| Expected UIF submit event and error feedback | UI implementation + UI acceptance tasks | -| `quickstart.md` sandbox refund path | integration/e2e environment + execution + evidence tasks | -| persistence update rules | implementation + data-side-effect validation tasks | +### Plan:X0–X4 -## Mandatory Final Code Review +X0–X4 是嵌套在原有 Core Plan 流程中的内部里程碑,不替换 Core 的 setup、 +Phase 0、Phase 1 或 Constitution re-check。 -Tasks appends **Final Code Review** after all user-story, integration, and -validation work. It is a mandatory final phase in `tasks.md`, so the standard -core implementation command executes it in normal checklist order. - -The phase includes applicable checks for: - -- the planned `M + U` boundary; -- interface and behavior contracts; -- implemented UI states and viewport behavior; -- visual/IR traceability refs and Client Asset Contract bindings; -- field-level and runtime data side effects; -- cross-boundary sequence consistency; -- integration/e2e evidence and unresolved blockers. - -Code Review is not a separate command, reviewer runtime, or orchestration -protocol. Completion means the review checklist items pass; failures remain -open tasks or explicit blockers. - -## Architecture And Scope - -`/speckit.constitution` manages separate project-level artifacts: - -- `.specify/memory/constitution.md` -- `.specify/memory/architecture.md` +| 里程碑 | 目标 | 主要产物 | +|---|---|---| +| X0 | 输入、门禁和架构修订对齐 | `plan.md` | +| X1 | 研究并关闭技术/验证未知项 | `research.md` | +| X2-A | 领域、对象和接口设计 | `data-model.md`、`contracts/`,按需生成 class/sequence | +| X2-B | UI/UX 交付就绪设计 | `ui-ux-design.md` | +| X2-C | 测试与验收设计 | `contracts/test/test-conditions.json` 及可选技术子契约 | +| X3 | 定义可执行验证路径 | `quickstart.md` 中的 `VAL-*` | +| X4 | 汇总测试/任务交接 | `test-readiness.md`、`PLAN_OUTPUT_READY` | -The Architecture follows the System Boundary -> Conceptual Model -> Technical -Decisions & Evidence -> Planning Guardrails & Gaps chain. +`contracts/test/test-conditions.json` 是测试条件父契约。BDD、场景、fixture +(测试数据)和 assertion(断言)只有在技术适用时才生成;非 UI 功能和 +无 fixture 场景可以记录明确理由。 -Change Scope Granularity uses the fixed R/M/U/O model: R is Repository / Workspace, M is Module / Capability, U is Unit / Design Object, and O is Operation / Detail. Planning locks `M + U`; Tasks maps those design objects to concrete executable paths without widening the planned boundary. +UI/UX 像素级交付准备可以在 Plan 中完成,但 Tasks 不得生成像素还原、 +截图对比、visual diff(视觉差异)、baseline(基线)、视觉恢复或渲染审查 +任务。 -## Behavior Contracts +### Tasks 与 Analyze -The preset packages separate templates and JSON schemas for behavior drafts, -Expected UIF, scenario instances, fixtures, and assertions. -`validators/speckit_behavior_contract.py` checks cross-field relationships such -as scenario-to-fixture references, exception-case structure, Expected UIF -steps, and required-case coverage. +`/speckit.tasks` 只消费 `PLAN_OUTPUT_READY`,按 T0–T5 映射已存在的设计对象、 +路径、依赖、测试条件和 `VAL-*`。必需的 `TC-*` 会覆盖 Core 模板中“测试可选” +的默认提示;最后一个强制阶段始终是 **Final Code Review**。 -The behavior validator is independent of implementation execution. There are no -implementation manifest, transfer, or worker-result schemas in this package. +`/speckit.analyze` 一次读取 Constitution、Architecture、Spec、Plan 与 Tasks, +输出稳定 finding ID、严重级别、证据和第一个阻塞点。它负责检查 +Architecture → Plan、Plan → Tasks、M + U 范围,以及数据模型的幂等、提供方 +绑定、重试、恢复和生命周期投影;同时检查 `SRC-*` 的本地存在性、角色和 +X2-B/UIF 投影。它不会访问外部定位符、修文件或发明新的 Plan 策略。 -## Install +## 安装 -Development checkout: +开发目录: ```bash specify preset add --dev /path/to/spec-kit-workflow-preset ``` -Published release: +已发布版本: ```bash -specify preset add --from https://github.com/bigsmartben/spec-kit-workflow-preset/releases/download/v3.0.0/spec-kit-workflow-preset-v3.0.0.zip +specify preset add --from https://github.com/bigsmartben/spec-kit-workflow-preset/releases/download/v3.1.0/spec-kit-workflow-preset-v3.1.0.zip ``` -After installation, resolve a preset-owned wrapper: +安装后可检查预设信息: ```bash -specify preset resolve speckit.tasks +specify preset info workflow-preset ``` -Resolve implementation through the normal command surface. Because the preset -does not declare it, `speckit.implement` comes from the active Spec Kit core. - -## Release Integrity - -A source release is immutable. The integration fork must record: - -- source repository URL; -- release version; -- source commit SHA; -- release download URL; -- release artifact SHA-256; -- per-file hashes from the release manifest. - -The fork extracts the release snapshot without edits. Bundled installation and -installation from the same release must produce identical preset files and -manifest content. Any functional change requires a new source version; a -published version is never modified in place. - -## Development - -Install test requirements and run the contract suite: +## 开发与验证 ```bash python3 -m pip install -r requirements-dev.txt python3 -m unittest tests/test_preset_contract.py ``` -Repository extension rules are in -[`docs/extension-governance.md`](docs/extension-governance.md). +扩展规则见 +[`docs/extension-governance.md`](docs/extension-governance.md)。发布产物必须 +记录源仓库、版本、source commit SHA、下载地址、压缩包 SHA-256 和逐文件 +哈希;下游集成先进入 `bigsmartben/spec-kit`,不得从本仓库直接写入 +`github/spec-kit`。 diff --git a/presets/workflow-preset/commands/speckit.analyze.md b/presets/workflow-preset/commands/speckit.analyze.md index 9ea345bdc0..3585940742 100644 --- a/presets/workflow-preset/commands/speckit.analyze.md +++ b/presets/workflow-preset/commands/speckit.analyze.md @@ -1,61 +1,199 @@ --- -description: Wrap core analysis with behavior-first vertical consistency checks. +description: Read-only cross-command consistency audit across Constitution, Architecture, Spec, Plan, and Tasks. strategy: wrap --- -## Change Scope Granularity +Follow cross-agent protocol profile: `speckit.analyze.read_only_parallel_review`. -Check that tasks preserve the planned `M + U` scope. Report missing, widened, or ambiguous scope boundaries as blockers. +## Exclusive Ownership And Read-Only Boundary -## Behavior Vertical Consistency +Analyze exclusively owns Cross-Command Consistency Gates. Producing commands own +their own output quality; official Core gates remain unchanged. -Follow cross-agent protocol profile: `speckit.analyze.read_only_parallel_review`. +Analyze may read available Constitution, Architecture, repository facts, Spec, +Plan outputs, and Tasks. It MUST NOT modify or repair any artifact, generate a +receipt/compliance matrix/audit file, invoke another command, inspect +post-implementation code as proof of design, or promote a finding into a new +Plan/Tasks gate. + +Run against artifacts currently available. Missing later-stage artifacts limit +the relevant audit rather than authorizing invention. + +## One-Pass Inventory + +Build one in-memory inventory before deep reading: + +- Constitution revision, SDD authority statements, exact R/M/U/O model; +- Architecture Revision plus `BND-*`, `CON-*`, `DEC-*`, `CST-*`, `GAP-*`; +- Spec `SRC-*` rows with role, scope, projection, and blocker plus + `FR/NFR/UX/UI/VIS` and other stable refs; +- X0–X4 gates, decisions, design/readiness products, `TC-*`, `VAL-*`; +- Tasks IDs, concrete paths, dependencies, Test Condition refs, Final Code + Review position. + +Use stable IDs as the primary consistency surface. Read surrounding prose only +when an ID, source, mapping, or blocker is missing/ambiguous. Stop expanding one +branch after the first blocker that proves the downstream link cannot close. +Separate blockers from warnings. + +## Audit Chain S — Local Source Reference Integrity + +Audit only the local Source Reference Contract and its local projections: -Analyze whether the feature artifacts close the -`requirement gates -> BDD/UIF intent -> contracts -> behavior testability -> tasks` -loop. This command checks planning consistency only; it does not inspect -implementation code or infer interaction flows from built code. +- every referenced `SRC-*` exists exactly once in the Spec carrier; +- every source has exactly one allowed role, opaque locator/description, + explicit authorized scope/facts, projection refs or a reason for none, and a + status/blocker; +- every projected requirement ref exists locally and is compatible with the + role: `requirement-input` may project WHAT/WHY refs, `visual-input` only + `UI-*`/`VIS-*`, while `technical-evidence` and `context-only` authorize no + normative requirement; +- a broad source without a safe feature slice remains blocked or needs + clarification instead of projecting unrelated facts; +- orphan, contradictory, missing, role-invalid, and projection-invalid refs + produce stable findings; +- every applicable `SRC-* + UI/VIS-*` pair reaches X2-B UI/UX Delivery records + and, when an interaction contract is required, UIF `source_refs` plus + `requirement_refs`. -## Analysis Performance Guardrails +Use stable codes including `SRC_REF_MISSING`, `SRC_REF_DUPLICATE`, +`SRC_FIELD_INVALID`, `SRC_ROLE_INVALID`, `SRC_ROLE_PROJECTION_INVALID`, +`SRC_PROJECTED_REF_MISSING`, `SRC_FEATURE_SLICE_MISSING`, `SRC_ORPHAN`, +`SRC_STATUS_CONTRADICTORY`, `SRC_UIUX_MAPPING_MISSING`, and +`SRC_UIF_MAPPING_MISSING`. -Keep analysis bounded to the existing planning artifacts. +Do not open, run, inspect, compare, fetch, or otherwise dereference an external +locator. Do not decide source authenticity, availability, revision/digest +freshness, fidelity, or publication state. Analyze writes no persistent audit +artifact and does not acquire missing evidence. -- Build a one-pass artifact inventory before deep reading. Record which expected files and directories exist, then short-circuit missing artifact branches as source artifact -> target artifact blockers. -- Use stable IDs as the primary consistency surface: `CASE-`, `SCN-`, `UIF-`, `FIX-`, `AST-`, and `BLK-`. Compare ID sets and declared method/path references before prose-level interpretation. -- Read `tasks.md` and `quickstart.md` once for ID, contract path, API method/path, and validation path evidence. Do not repeatedly scan them per scenario when a single evidence map can answer coverage. -- Read surrounding prose only when a required ID, source section, or blocker explanation is missing or ambiguous. -- Stop expanding a branch after the first blocker that proves the downstream link cannot be closed. Report the blocker with the source artifact and target artifact instead of continuing speculative checks. -- Do not create new analysis artifacts, workflow runners, or external-tool requirements. +## Audit Chain A — Constitution To Spec / Plan + +Check cross-artifact effects only: + +- Spec/Plan do not claim Constitution or Architecture authority; +- Plan/Tasks preserve the exact planned `M + U` boundary and do not widen to R; +- command outputs do not introduce Intake as an SDD stage; +- Plan does not create a cross-command Architecture Conformance Gate; +- Tasks does not perform upstream consistency repair; +- downstream authority does not contradict Constitution's command ownership. + +Do not re-run `CONSTITUTION_OUTPUT_READY`. + +## Audit Chain B — Architecture To Plan Products + +For every repository, audit one strategy: + +```text +repo-first planning within Architecture constraints +``` + +Do not classify Plan as Greenfield/Brownfield. Check that current repository +facts ground planned modules/paths/dependencies, planned creation is explicit, +and Plan does not silently amend Architecture. Check: -- spec.md user stories have BDD coverage. -- BDD Given steps map to fixtures. -- BDD When steps map to UIF events or API requests. -- BDD Then steps map to feedback or behavior assertions. -- behavior/uif.intent.json is formalized into contracts/uif/*.expected.json. -- behavior drafts exist but formal contracts are missing, without a matching `N/A or blocker` explanation tied to the source draft and missing planning input. -- UIF API calls exist in contracts/api/. -- behavior contracts cover scenarios, fixtures, and assertions. -- tasks.md covers BDD, UIF, API, fixtures, and quickstart validation paths. -- case coverage is closed from `checklists/behavior.md` through behavior drafts, - formal contracts, `behavior/behavior-testability.md`, and implementation - tasks. -- Required case types in `checklists/behavior.md` map to behavior draft - scenarios, formal behavior contracts, Task Derivation Matrix rows, tasks, - and quickstart validation paths. -- `behavior/behavior-testability.md` carries current spec/plan revisions and is - READY before tasks are considered complete. -- every Required Case maps to a fixture → validation/test → implementation → - evidence task chain. -- positive, negative, boundary, permission, validation, and state_conflict case types are either covered or have `N/A or blocker` evidence. -- failure scenarios declare error code, failure feedback, and state invariant, rollback, or compensation assertion. -- quickstart validation paths cover Required failure scenarios. - -Report missing, inconsistent, or stale links by source artifact and target artifact. Keep findings actionable and separate blockers from warnings. +| Architecture source | Required downstream projection | +|---|---| +| `DEC-*` | `research.md` decision and affected X2/X3 refs | +| `CON-*` | `data-model.md` meaning, ownership, lifecycle, invariants | +| `BND-*` | interface contracts and dependency direction | +| `CST-*` | applicable `plan.md`, design, and `quickstart.md` constraints | +| `GAP-*` | stable blocker/prerequisite without fake evidence | +| Architecture Revision | current refs in `plan.md` and readiness products | + +Report omitted IDs, contradictions, stale revision, repository-grounding gaps, +unauthorized authority promotion, or an Architecture change that should return +to `/speckit.constitution`. Do not run a Plan-internal Architecture gate. + +### #24 concrete data-model projection + +When applicable Architecture/Spec refs require them, verify the domain/design +chain explicitly represents: + +- idempotency record/key composition and create uniqueness; +- provider task binding to clip/shot/render plan; +- provider configuration/version lock; +- retry/force-retry retention of entitlement, SKU, language, render plan, + provider lock, and attempt history; +- provider switching as an explicit recovery decision, not ordinary retry; +- lifecycle separation among entitlement readiness, provider completion, and + client availability (`ready` versus `completed`). + +Use source → target blocker codes such as +`ARCH_DATA_MODEL_IDEMPOTENCY_MISSING`, +`ARCH_PROVIDER_BINDING_MISSING`, `ARCH_PROVIDER_LOCK_MISSING`, +`ARCH_RETRY_CONTEXT_MISSING`, `ARCH_RECOVERY_DECISION_MISSING`, and +`ARCH_LIFECYCLE_PROJECTION_MISSING`. These are Analyze findings, not business +fields mandated for unrelated features. + +## Audit Chain C — Spec To X0/X1/X2/X3/X4 + +Check applicable Spec refs project without contradiction: + +- goals/exclusions and requirement refs into X0; +- product constraints into X1 decisions; +- domain/interface requirements into X2-A; +- UX/UI/VIS into UI/UX Design and Delivery Readiness; +- functional/NFR/security/accessibility/recovery/data-side-effect acceptance + into `TC-*` and Test Readiness; +- Test Conditions needing execution into `VAL-*`; +- blockers retain their actual owning lane. + +BDD/fixtures/assertions are required only when the parent Test Condition selects +that technique. Pixel delivery/readiness stays in UI/UX and is absent from Test +Conditions/Test Readiness. + +## Audit Chain D — Plan To Tasks + +Check every populated Plan record maps to concrete Tasks work or an explicit +blocker: + +- Design Object rows → implementation paths; +- interface/sequence refs → contract/orchestration dependencies; +- UI/UX Delivery rows → component/state/responsive/accessibility/asset + implementation only; +- Required `TC-*` → required fixture/test/validation/evidence tasks; +- `VAL-*` → runnable environment/integration/e2e/evidence work; +- Plan blockers → blocked task scope, never complete-looking work; +- Final Code Review covers applicable Plan sources and is the last phase. + +Detect Tasks-side strategy invention and missing Plan-to-Tasks mappings. +Required Test Conditions must not be dropped by Core optional-test prose. + +Assert #37's downstream visual boundary: no screenshot comparison, visual diff, +baseline, restoration, pixel assertion, visual acceptance, or rendered-fidelity +review task. Visual refs may guide UI implementation only. + +## Finding Format And Implementation Readiness + +For each finding output: + +```text +severity | stable code | source artifact + ID/location +| target artifact + expected mapping | evidence | owning command / next action +``` + +Recommended top-level summary: + +```text +Source References -> Local Projections: PASS | BLOCKED +Constitution -> Spec/Plan: PASS | BLOCKED +Architecture -> Plan Products: PASS | BLOCKED +Spec -> Plan Products: PASS | BLOCKED +Plan -> Tasks: PASS | BLOCKED +M + U Preservation: PASS | BLOCKED +Implementation Readiness: PASS | BLOCKED +``` + +`Implementation Readiness: BLOCKED` prevents proceeding to Core Implement. A +PASS means the available cross-command chains close; it does not replace any +producing command's output gate or prove implementation results. {CORE_TEMPLATE} -## Behavior Analysis Reporting +## Preset Completion Addition -Before finishing, report whether the vertical consistency chain is closed and list blockers that prevent implementation from continuing. +Report the inventory, chain summaries, blockers, warnings, and implementation +readiness. Confirm no files were written. diff --git a/presets/workflow-preset/commands/speckit.checklist.md b/presets/workflow-preset/commands/speckit.checklist.md index 1809db2b6a..5baded34db 100644 --- a/presets/workflow-preset/commands/speckit.checklist.md +++ b/presets/workflow-preset/commands/speckit.checklist.md @@ -1,104 +1,50 @@ --- -description: Wrap core checklist generation with multi-domain requirement gates. +description: Generate question-form unit tests for requirement writing without evaluating or repairing the specification. strategy: wrap --- -## Multi-Domain Requirement Gate +## Preset Checklist Ownership -This wrapper extends the core spec-only checklist contract. It must not read -`plan.md` or `tasks.md`, create planning artifacts, redefine extension hooks, -or introduce a new command. +`$ARGUMENTS` is an optional focus. Generate either a broad requirements +checklist or a focused domain checklist under `checklists/.md`. -Use `$ARGUMENTS` only to prioritize requirement-quality focus. The standard -domain evaluation is always: +Checklist reads the current `spec.md` and writes only the selected checklist. +It MUST NOT modify `spec.md`, answer its own questions, ask clarification +questions, invoke Specify/Clarify, aggregate Planning Readiness, compute +PASS/BLOCKED, route blockers, validate IDs/numbering/references, or read Plan and +Tasks as strategy inputs. -| Domain | Output | Template | -|---|---|---| -| requirements | `checklists/requirements.md` | core baseline | -| behavior | `checklists/behavior.md` | `requirement-behavior-gate-template` | -| UX | `checklists/ux.md` | `requirement-domain-gate-template` | -| security | `checklists/security.md` | `requirement-domain-gate-template` | -| NFR | `checklists/nfr.md` | `requirement-nfr-gate-template` | -| visual | `checklists/visual.md` | `requirement-visual-gate-template` | +Every checklist item is a question-form “unit test for requirements writing”. +Use checklist-local `CHK-*` IDs as formatting, not as a global consistency +claim. Cite a relevant spec section or append `[Gap]` when the expected +requirement text cannot be located. -Every standard domain must be written as `APPLICABLE` or -`NOT_APPLICABLE` with a concrete reason. Every file uses the core -`Stage/Domain/Gate/Applicability/Status/Spec Revision` metadata contract. -Planning Readiness is aggregated in memory; do not create -`planning-readiness.md`. +For broad scope, cover completeness, clarity, consistency, measurability, +scenarios, edge/failure cases, NFRs, security/privacy, data/integration, +dependencies, assumptions, exclusions, and success criteria. For focused scope, +generate domain-aware questions without inventing a second specification +schema. -The legacy `checklists/behavior-testability.md` is not an input or output of -this command. Preserve an existing legacy file for migration history but never -update it or treat it as a current Gate. +Source-focused questions may ask whether each `SRC-*` is clearly identified, +feature-scoped, assigned one allowed role, and connected to observable local +requirements or an explicit blocker. Checklist MUST NOT dereference a locator, +acquire missing evidence, validate authenticity/freshness/publication state, or +answer whether an external source is correct. -## Behavior Requirement Gate +Examples: -Write behavior requirement quality and the Case Coverage Matrix to -`checklists/behavior.md`. +```markdown +- [ ] CHK-UX-001 Are empty, loading, error, and recovery states specified for each critical journey? [Completeness] [Spec § UX] +- [ ] CHK-VIS-001 Is “brand-consistent” grounded in an explicit source or observable criterion? [Clarity] [Gap] +- [ ] CHK-NFR-001 Are user-visible performance expectations measurable for the critical path? [Measurability] [Spec § NFR] +``` -- Evaluate user-story readiness, observable acceptance behavior, and Given, - When, and Then requirement readiness. -- Use one row per story/capability and case type. -- Cover positive, negative, boundary, permission, validation, and - state_conflict. -- Use stable Case IDs. -- Status is `Required|Not Applicable|Unknown`. -- Required rows cite a `spec.md` section. -- Not Applicable requires rationale. -- Unknown becomes `[blocker:product-decision]` and blocks PASS. -- Scenario IDs and `case_coverage_blockers` remain `/speckit.plan` outputs. +Generated items remain unchecked questions. If gaps are exposed, recommend that +the user independently rerun Specify or Clarify with the relevant focus. -This gate checks whether behavior requirements are projectable. It does not -decide test level, fixtures, assertions, contracts, or Task Readiness. - -## NFR Requirement Gate - -Write NFR readiness to `checklists/nfr.md`. Evaluate performance, security and -privacy, reliability and recovery, accessibility, compliance and auditability, -observability, compatibility, data lifecycle, and cost or operational -constraints. - -Each dimension is `Required`, `Not Applicable`, or `Unknown`. Required items -need verifiable product-level criteria. Not Applicable needs a rationale. -Unknown items affecting planning are product-decision blockers. Do not require -technical designs or invent architecture. - -## Visual Requirement Gate - -Write visual readiness and the only Visual Fidelity Evidence Matrix to -`checklists/visual.md`. - -Apply the gate when `spec.md` contains a Visual & UI Specification, visual -requirements, visual/HTML/structured-IR SSOT refs, external intake refs, -provider blockers, pixel-perfect/brand-critical requirements, responsive -visual requirements, or UI visual acceptance requirements. - -Every visual item is `Required`, `Not Applicable`, `Unknown`, or -`[BLOCKED: PROVIDER_EVIDENCE]`. - -- Unknown product semantics become `[blocker:product-decision]`. -- Missing provider proof remains `[blocker:provider-evidence] [return:intake]`. -- Provider blockers must not be converted into clarify questions. -- Required items need source traceability and observable requirement text. -- The matrix records source refs, provider dependency, visual SSOT refs, HTML - SSOT refs, structured IR refs, other evidence refs, readiness input, - accepted exceptions, and blocker IDs. -- Responsive visual requirements block PASS only when required source-backed - state or viewport evidence is missing for a feature that depends on provider - evidence. - -Do not call provider tools, rebuild intake evidence, parse provider or HTML -artifacts, define screenshot comparison, visual diff, baseline capture, or -final visual review. - -## Recompute and Reporting - -Recompute generated sections using stable CHK/CASE/NFR/VIS IDs. Never append -duplicate status blocks, stale blockers, or repeated matrix rows. Preserve -unrelated manual notes. +{CORE_TEMPLATE} -Report per-domain applicability/status, current spec revision, the in-memory -Planning Readiness aggregate, product-decision blockers, and provider-evidence -blockers separately. +## Preset Completion Addition -{CORE_TEMPLATE} +Report the checklist path, focus, and item count. Do not report a readiness +status or mutate the specification. diff --git a/presets/workflow-preset/commands/speckit.clarify.md b/presets/workflow-preset/commands/speckit.clarify.md index 880f7a16da..5930c58cef 100644 --- a/presets/workflow-preset/commands/speckit.clarify.md +++ b/presets/workflow-preset/commands/speckit.clarify.md @@ -1,84 +1,95 @@ --- -description: Wrap core clarification with product-decision gate repair. -strategy: wrap +description: Resolve high-impact product ambiguity and record accepted decisions only in spec.md. +strategy: replace +scripts: + sh: scripts/bash/check-prerequisites.sh --json --paths-only + ps: scripts/powershell/check-prerequisites.ps1 -Json -PathsOnly --- -## Spec-Only Clarification Policy +## User Input -This wrapper must not redefine core-owned User Input, Pre-Execution Checks, extension hooks, base path resolution, or core file handling. +```text +$ARGUMENTS +``` -Use `spec.md` as the clarification source. Ask and record clarification only for requirement ambiguity that affects product behavior, constraints, non-functional requirement assumptions, visual/UI requirement coverage status, acceptance criteria, user roles, permissions, entity states, data semantics, exceptions, validation rules, or boundaries. +Use optional arguments only as focus for this clarification run. -Also read unchecked blockers from all metadata-bearing -`checklists/*.md` files with `Stage: requirements`, -`Gate: planning-readiness`, and `Status: BLOCKED`. -Prioritize `[blocker:product-decision]` items as the repair queue. Never ask a -question for `[blocker:provider-evidence]`; preserve its `[return:intake]` -route and keep Planning Readiness BLOCKED. +## Extension Hooks And Path Resolution -Do not read or update behavior draft artifacts. Do not use behavior drafts as clarification inputs, and do not open a separate behavior-question channel. Product requirements stay in `spec.md`; update `spec.md` only after user-provided answers make the requirement clear. +Run enabled, unconditional `hooks.before_clarify` before analysis and +`hooks.after_clarify` after successful writes. Invoke and await mandatory hooks. -## Wrapper Input Additions +Run `{SCRIPT}` once from the repository root and read `FEATURE_DIR` and +`FEATURE_SPEC`. If resolution fails or `spec.md` is missing, stop and recommend +running `/speckit.specify`; do not create a specification here. -Treat `$ARGUMENTS` as prioritization context for the current clarification run. Do not ask the user to restate requirements already present in `spec.md`. +## Ownership -## Wrapper Preflight Additions +Read and write only `FEATURE_SPEC` aside from official path/hook mechanics. +Do not read blocked checklists as a queue. Do not create, recompute, answer, +toggle, or mutate checklist files. Do not aggregate readiness, revise gates, +validate IDs/numbering/references, create external-source artifacts, or modify Plan, +Tasks, Architecture, contracts, or tests. -Load the active `spec.md` through the core command. Official hooks still apply: `hooks.before_clarify` runs before Outline, `hooks.after_clarify` runs before Completion Report, and mandatory hooks emit `EXECUTE_COMMAND`. If `spec.md` is missing, follow the core command error path and do not create a new spec here. +External source-evidence blockers remain in their canonical `SRC-*` rows and +stay outside the product-decision question loop. Do not dereference a locator, +acquire missing evidence, validate external state, or require external +write-back or synchronization. -## Wrapper Outline Additions +## Cross-Domain Ambiguity Map -## Design Requirement Clarification Strategy +Build an in-memory map across: -When `spec.md` was created from external intake evidence or visual SSOT refs, prioritize clarification questions for evidence-derived gaps already written in `spec.md`. Scan `spec.md` first for `[NEEDS CLARIFICATION]`, visual/UI coverage status `Unknown`, and gaps about provider-unprovided states, responsive behavior, business rules, permissions, and error handling. +- scope and observable behavior; +- roles, permissions, security/privacy, and compliance; +- domain/data semantics and lifecycle; +- UX journeys, UI states, accessibility, and failure recovery; +- NFRs and measurable completion signals; +- integrations, dependency failures, boundaries, constraints, and terminology. -Do not call provider tools. Do not re-extract design facts, re-parse provider design links, parse HTML SSOT bundles, re-parse structured IR artifacts, or turn clarification into an intake step. External intake owns source capture and provider readiness; `/speckit.specify` only projects confirmed evidence-backed requirements and trace refs into `spec.md`. `/speckit.clarify` only selects high-impact questions from existing `spec.md` product-decision gaps and records confirmed answers. Do not ask the user to fix provider extraction artifacts. +Prioritize candidates by `impact × uncertainty`. Do not use a UI-first fixed +order. Exclude decisions already answered, low-impact stylistic preferences, +source-evidence gaps, and implementation choices better owned by Plan. -Ask at most 5 high-impact questions whose answers materially affect requirements, implementation planning, or validation readiness. Maximum of 5 total questions. Present EXACTLY ONE question at a time. Do NOT output them all at once. Never reveal future queued questions. +## Question Loop -Format recommendations as `**Recommended:** Option [X] - ` when a discrete 2-5 option choice is available. Keep the rationale short and decision-focused. For short-answer gaps, use `Suggested` and constrain answers to `<=5 words`. Accept `yes`, `recommended`, or `suggested` as approval of the shown recommendation. Question selection order: +Ask at most five high-impact questions, exactly one at a time. Never reveal the +future queue. Use either 2–5 mutually exclusive options with +`**Recommended:** Option X - ...`, or a short answer constrained to `<=5 words` +with `**Suggested:** ...`. Accept `yes`, `recommended`, or `suggested` for the +displayed recommendation. -1. Visual/UI coverage status: Required, Not Applicable, Unknown, or `[BLOCKED: PROVIDER_EVIDENCE]`. -2. Required frames, states, and breakpoints for acceptance. -3. visual fidelity scope: pixel-perfect, design-system faithful, or functional equivalent. -4. missing UI states such as loading, empty, error, disabled, hover, and focus. -5. responsive behavior, scrolling, safe areas, and long-copy handling. -6. required component reuse constraints explicitly stated in `spec.md`. -7. data semantics for mock copy, API-backed copy, and interface-driven values. -8. Prototype-uncovered navigation, dialogs, recovery paths, and failure handling. -9. product-side acceptance evidence and accepted exception approval flow. +After each accepted answer: -After each accepted answer, write confirmed answers back into `spec.md` in the relevant Requirements, User Scenarios, Acceptance Criteria, Assumptions, Open Questions, or Visual & UI Specification, visual/responsive/state sections. Update affected visual/UI coverage status when the answer resolves an `Unknown` item. Ensure `## Clarifications`, `### Session YYYY-MM-DD`, and one `- Q: ... -> A: ...` bullet exist for the session. Save `spec.md` after each accepted answer. Do not create a separate provider-specific clarification document. +1. ensure `## Clarifications` and `### Session YYYY-MM-DD` exist; +2. append exactly one `- Q: ... -> A: ...` entry; +3. update the existing canonical section that owns the decision; +4. replace the ambiguous statement rather than duplicating it; +5. preserve the originating `SRC-*` provenance and clarification history while + making the accepted local decision current; +6. atomically save `spec.md`. -Do not generate visual restoration checklists. Clarification fills requirement gaps in `spec.md`; `/speckit.checklist` remains responsible for checking requirement text quality and readiness. +The accepted answer is owned locally by `spec.md` and may supersede an +ambiguous projected statement. Do not present the superseded statement as the +current decision, erase its provenance, or require a change to the external +source. -## Validation after each write +## Local Validation After Every Write -Run validation after EACH write plus final pass. Confirm the accepted answer appears once in `spec.md`, Total asked questions is at most 5, the targeted ambiguity is removed or replaced, no contradictory earlier statement remains, and heading structure is preserved. +Check only: -After each `spec.md` write, recompute affected requirement gates by stable -CHK/CASE/NFR/VIS IDs. Verify unaffected gate sources before stamping every gate -with the new spec SHA-256 revision. Replace generated status and blocker -sections; do not append duplicate IDs or stale blockers. Aggregate Planning -Readiness in memory and never create `planning-readiness.md`. +- one history bullet per accepted answer and no more than five; +- the targeted ambiguity is removed; +- the accepted answer appears once in its owning section; +- no contradiction or terminology drift was introduced; +- Markdown structure and template-owned headings remain valid; +- no artifact other than `spec.md` was changed. -Do not read or update the legacy `checklists/behavior-testability.md`. - -{CORE_TEMPLATE} +These are local write-safety checks, not requirement completeness or +cross-artifact validation. ## Completion Report -Before finishing, report answered questions, `spec.md` sections updated, -recomputed gate files, aggregate Planning Readiness, and unresolved -product-decision versus provider-evidence blockers separately. - -## Done When - -- [ ] No more than 5 high-impact questions were asked. -- [ ] Each accepted answer was written back to `spec.md`. -- [ ] Any answered visual/UI coverage status was updated in `spec.md`. -- [ ] Validation after each write found no duplicate or contradictory clarification. -- [ ] Affected requirement gates were recomputed using stable IDs and current spec revision. -- [ ] Provider-evidence blockers were preserved and routed to intake. -- [ ] No Planning Readiness summary artifact was created. -- [ ] Completion reported with sections touched and remaining blockers. +Report questions asked, decisions recorded, sections updated, remaining product +ambiguities, source-evidence blockers, and hook status. Recommend independently +rerunning Checklist when requirement-writing quality should be reassessed. diff --git a/presets/workflow-preset/commands/speckit.constitution.md b/presets/workflow-preset/commands/speckit.constitution.md index af84a2f235..dcd06a8764 100644 --- a/presets/workflow-preset/commands/speckit.constitution.md +++ b/presets/workflow-preset/commands/speckit.constitution.md @@ -1,124 +1,202 @@ --- -description: Wrap core constitution updates with change scope granularity and Constitution-managed architecture governance. -strategy: wrap +description: Manage SDD governance and repository technical Architecture as separate project-memory contracts. +strategy: replace --- -## Constitution Stage Input Agreement +## User Input -Before writing either project-memory artifact, establish an explicit input agreement with the user: +```text +$ARGUMENTS +``` + +You MUST consider the user input before proceeding. + +## Pre-Execution Hooks + +Read `.specify/extensions.yml` when it exists and run enabled, unconditional +`hooks.before_constitution` entries. Mandatory hooks MUST be invoked and awaited; +conditional hooks remain the HookExecutor's responsibility. Invalid or absent +hook configuration is skipped without changing this command's ownership. + +## Explicit Input Agreement -- project mode: `greenfield`, `brownfield`, or `amendment`; -- the goal of this Constitution-stage run; -- which user-selected sources are authoritative and the role of each source; -- which candidate sources are excluded; -- whether repository inspection is authorized and its exact scope; -- whether this run may update Constitution, Architecture, or both. +Before writing, confirm and retain an in-memory agreement containing: -Conversation input, UC/PRD/product documents, an existing Constitution or Architecture, repository evidence, and external constraints are all possible sources. No conventional path is mandatory. In particular, `uc.md`, `inception/product/uc.md`, `.specify/memory/uc.md`, README files, source code, tests, configuration, and directory names are candidate sources only until the user authorizes their role. +- Architecture generation mode: `greenfield`, `brownfield`, or `amendment`; +- run goal; +- every authorized source, one allowed role, opaque identity, and explicit + technical scope; +- excluded candidate sources; +- repository inspection authorization and exact scope; +- write scope: Constitution only, Architecture only, or both. -If the agreement is absent, ambiguous, or insufficient for the requested update, stop and confirm it with the user before writing. Do not silently discover an input and promote it to authority. +No conventional file is authoritative by default. Conversation input, product +documents, an existing Constitution or Architecture, repository code, tests, +configuration, documentation, and directory names are candidate sources until +the user authorizes their role. Core-style repository inference MUST NOT expand +the agreement. If the agreement is missing or ambiguous, stop before writing. -## Project Mode +Use the source-neutral roles `requirement-input`, `visual-input`, +`technical-evidence`, and `context-only`. Only explicitly authorized +`technical-evidence` may support observed or inferred technical records. +Product-facing `requirement-input` or `visual-input` may establish approved +target context but does not become a technical decision merely because it is +available; `context-only` authorizes no normative Architecture fact. A source +locator remains opaque and authorizes neither adjacent repository inspection +nor external execution, authenticity/freshness checks, or publication-state +validation. -Apply the source rules for the agreed mode: +## Independent Write Scopes -- `greenfield`: derive prospective governance and Architecture from confirmed intent and selected project/product sources. Do not infer target Architecture from scaffolding. -- `brownfield`: inspect only the authorized repository scope. Keep observed current state, approved governance, target Architecture, and migration or unresolved gaps distinct. Existing code is evidence, not automatically a ratified principle or target decision. -- `amendment`: update the existing Constitution and/or Architecture baseline within the agreed scope. Preserve unaffected content and record the reason for each material Architecture change. +This command owns two independent files: + +```text +.specify/memory/constitution.md +.specify/memory/architecture.md +``` -If an existing `.specify/memory/architecture.md` uses the retired 4+1 or nine-section planning-contract format, report `ARCH_LEGACY_FORMAT`. Rewrite it only when the input agreement authorizes an Architecture update; never silently migrate it. +- Constitution-only changes MUST NOT modify Architecture. +- Architecture-only changes MUST NOT modify Constitution. +- Combined changes validate and report each output independently. +- Never create a second Architecture artifact, conformance receipt, audit file, + compliance matrix, implementation manifest, or task artifact. -## Change Scope Granularity +Resolve the active `constitution-template` through the preset resolution stack. +Resolve the workflow-preset `architecture-template` only when Architecture is in +the authorized write scope. Preserve unaffected content during amendments. -Always preserve the Change Scope Granularity principle in `.specify/memory/constitution.md`. +## Constitution Contract -Constitution updates must not remove, weaken, or contradict the principle's R/M/U/O model, boundary timing, or context-gap rule. Keep the principle normative, including `Planning locks M + U`. +Constitution is the sole SSOT (single source of truth) for SDD workflow +governance. It owns: -The R/M/U/O letter mapping is fixed and MUST remain exact: +- command responsibilities and artifact authority; +- allowed read/write/block boundaries; +- the distinction among Command Internal Gates, official Core Gates, and + Cross-Command Consistency Gates; +- conflict routing to the command that owns the affected artifact; +- Architecture's role as repository technical SSOT; +- the rule that Plan, Tasks, and Core Implement respect Architecture; +- Analyze's exclusive ownership of cross-command consistency. + +Constitution MUST NOT contain concrete frameworks, databases, services, modules, +directory facts, dependency directions, CI details, product requirements, +feature-local mappings, task details, or Intake as an SDD stage. + +Always preserve the exact Change Scope Granularity model: - R: Repository / Workspace. Environment only; too broad for scoped changes. - M: Module / Capability. Hard outer boundary. - U: Unit / Design Object. Primary planning boundary. - O: Operation / Detail. Execution detail. -Do not paraphrase, expand, rename, translate, or substitute these letters with other nouns such as Requirement, Model, User/API Interface, or Operations. +The mapping MUST NOT be renamed or paraphrased. Preserve `Planning locks M + U`. +If it drifts, do not write Constitution; report +`CONSTITUTION_RMUO_MAPPING_DRIFT`. -If a drafted constitution changes this mapping, discard the draft and report blocker code `CONSTITUTION_RMUO_MAPPING_DRIFT` instead of writing `.specify/memory/constitution.md`. +### `CONSTITUTION_OUTPUT_READY` -When producing the Sync Impact Report, report template or command file status only after checking the actual path. If a path cannot be checked, report `CONSTITUTION_TEMPLATE_STATUS_UNCHECKED`; do not report it as missing. -If the root `.specify/templates/constitution-template.md` is still the core placeholder, do not treat that as the workflow-preset template being absent. Resolve or check `.specify/presets/workflow-preset/templates/constitution-template.md` before reporting preset template status. +PASS only when the Constitution: -## Separate Artifact Ownership +- contains SDD Workflow Governance and Gate Ownership; +- preserves the exact R/M/U/O mapping; +- assigns command output quality to the producing command; +- leaves official Core gates unchanged; +- assigns cross-command consistency exclusively to Analyze; +- contains no concrete repository Architecture or Intake stage. -The Constitution stage manages two independent project-memory files: +This is a command-internal output gate. It does not evaluate downstream +artifacts. -```text -.specify/memory/constitution.md -.specify/memory/architecture.md -``` +## Architecture Contract -- `constitution.md` stores durable governance principles. -- `architecture.md` stores project-level boundaries, concepts, technical direction, constraints, evidence, revisit conditions, and unresolved gaps. -- Architecture facts must not be embedded in ratified Constitution principles. -- Feature-local `research.md`, `data-model.md`, `contracts/`, `plan.md`, and `quickstart.md` consume and refine the project Architecture for one feature; they do not replace it. +Architecture is the repository technical SSOT. It contains only: -## Architecture Lifecycle +- revision identity and authorized evidence scope; +- technical boundaries and dependency direction (`BND-*`); +- stable technical concepts, relationships, lifecycle, and invariants (`CON-*`); +- technical decisions, status, evidence, consequence, revisit condition, and + supersession (`DEC-*`); +- technical constraints and risks (`CST-*`); +- unresolved technical gaps and triggers (`GAP-*`). -When the input agreement authorizes an Architecture update, load the workflow-preset `architecture-template.md` and write exactly one Architecture artifact: `.specify/memory/architecture.md`. +It MUST NOT contain command names, SDD gate definitions, planning consumption +instructions, task derivation, compliance matrices, product requirements, or +implementation operations. -Use one sequential reasoning chain, without 4+1: +### Mode-specific generation -```text -System Boundary - -> Conceptual Model - -> Technical Decisions & Evidence - -> Planning Guardrails & Gaps -``` +`greenfield` is intent-first. Derive target Architecture only from confirmed +intent and authorized constraints. Empty scaffolding and directory names do not +establish target technical choices; unresolved choices remain candidates or +`GAP-*`. -Render exactly these five top-level sections: +`brownfield` is repo-first: -1. `Architecture Overview` -2. `System Boundary` -3. `Conceptual Model` -4. `Technical Decisions & Evidence` -5. `Planning Guardrails & Gaps` +```text +authorized repository snapshot + -> repository technical facts + -> stable boundaries and concepts + -> established decisions + -> contradictions and technical gaps + -> repository Architecture SSOT +``` -Technical validation is evidence registration only. Record a candidate, conclusion, available evidence, and an explicit evidence gap or revisit condition when validation is still required. Do not create PoC code, application source, tests, migrations, build changes, deployment changes, secondary Architecture models, view files, or receipts. +Every core abstraction needs repository evidence or an explicit gap. Distinguish +`observed-current`, `inferred`, `approved-target`, and `migration-gap`. External +direction may define a target change but cannot overwrite observed facts +silently. -An Architecture update is ready only when it: +`amendment` starts from the current Architecture, preserves unaffected records, +and records reason, effect, supersession, and revision change for every material +update. -- states the Architecture goal, authorized sources, and at least one explicit boundary with ownership and non-responsibility; -- defines applicable core concepts with stable meaning, ownership, relationships, lifecycle, and invariants; -- records established technical decisions with scope, consequence, evidence, and revisit conditions; -- gives every item marked `MUST_VALIDATE` a conclusion plus evidence or an explicit validation gap; -- states applicable planning constraints and unresolved gaps without requiring downstream inference; -- contains no invented product requirement, implementation plan, task breakdown, or unresolved ambiguity presented as fact. +### `ARCHITECTURE_OUTPUT_READY` -Optional tables may be empty when they are genuinely not applicable. Do not manufacture extension points, decisions, or open questions to fill the template. +PASS only when: -When the agreement excludes an Architecture update, do not modify `architecture.md`. Report whether the existing file is missing, legacy, ready, or blocked so the user understands whether `/speckit.plan` can proceed. +- Architecture Revision and source scope are explicit; +- all records use the correct stable ID and defined status; +- Greenfield is intent-first or Brownfield is repo-first, as agreed; +- evidence is precise enough to distinguish fact, inference, approved target, + and gap; +- decisions include consequence, revisit condition, and supersession; +- no SDD governance, product requirement, task, or downstream conformance + conclusion is present. -## Architecture-Guided Planning +This is a command-internal output gate. It does not check whether Plan or Tasks +has consumed Architecture correctly. -Always preserve the Architecture-Guided Planning principle in `.specify/memory/constitution.md`. +## Update Procedure -`/speckit.plan` MUST read `.specify/memory/architecture.md` before producing planning artifacts. +1. Establish the explicit input agreement. +2. Read only authorized sources. +3. Load only artifacts in the authorized write scope plus their resolved + templates. +4. Draft Constitution and Architecture independently. +5. Preserve template headings and existing ratification metadata where + applicable. +6. Run `CONSTITUTION_OUTPUT_READY` and/or `ARCHITECTURE_OUTPUT_READY`. +7. Write only outputs whose internal gate passes. Use atomic replacement. +8. Never repair or analyze downstream Spec, Plan, Tasks, or implementation. -- `research.md` MUST follow established technical decisions and evidence, unless an Architecture revisit condition is met. -- `data-model.md` MUST preserve defined concepts, ownership, relationships, lifecycle, and invariants. -- `contracts/` MUST preserve system boundaries, responsibilities, interface ownership, and dependency direction. -- `plan.md` and `quickstart.md` MUST carry forward applicable Architecture constraints, gaps, and validation implications. +Do not create a separate source manifest, import/handoff package, adapter, or +provider-specific schema. Source authorization is carried only by the existing +input agreement and Architecture artifact. -If any planning artifact conflicts with or requires changing the Architecture, planning MUST stop and return to the Constitution stage. +## Post-Execution Hooks -{CORE_TEMPLATE} +After successful authorized writes, run enabled, unconditional +`hooks.after_constitution` entries before the completion report. Mandatory hooks +MUST be invoked and awaited. A failed mandatory hook is reported as a blocker. -## Constitution Stage Reporting +## Completion Report -Before finishing, report: +Report: -- the agreed project mode, goal, authorized and excluded sources, repository-inspection scope, and update scope; -- whether `constitution.md` preserves Change Scope Granularity, `Planning locks M + U`, and preserves the exact R/M/U/O letter mapping; -- whether Constitution facts and Architecture facts remain in their separate files; -- whether `architecture.md` was created, updated, preserved, missing, legacy, ready, or blocked; -- unresolved governance or Architecture gaps without presenting them as ratified facts. +- mode, goal, authorized/excluded sources, inspection scope, and write scope; +- each artifact path and whether it was created, updated, preserved, or blocked; +- `CONSTITUTION_OUTPUT_READY` and `ARCHITECTURE_OUTPUT_READY` independently; +- Architecture Revision when applicable; +- unresolved governance or technical gaps without promoting them to facts; +- whether mandatory post-hooks completed. diff --git a/presets/workflow-preset/commands/speckit.plan.md b/presets/workflow-preset/commands/speckit.plan.md index 42fa396f3d..0c9ef16ec7 100644 --- a/presets/workflow-preset/commands/speckit.plan.md +++ b/presets/workflow-preset/commands/speckit.plan.md @@ -1,242 +1,215 @@ --- -description: Wrap core planning with project Architecture consumption, Phase 0 behavior projection, formal contracts, and BDD Plan closeout. +description: Wrap Core Plan with X0–X4 feature control, parallel design lanes, and test-first acceptance contracts. strategy: wrap --- -## Change Scope Granularity - -Apply the constitution's Change Scope Granularity principle. - -During planning, lock the change scope to `M + U`: module/capability plus design object. Do not lock operation-level implementation details or concrete write paths. +Follow cross-agent protocol profile: `speckit.plan.stage_local_planning`. -## Architecture-Guided Planning +## Core Compatibility -Before Phase 0 preflight or any planning write, read: +The X labels are preset-internal milestones nested inside the official Core +Plan lifecycle: ```text -.specify/memory/constitution.md -.specify/memory/architecture.md +Core setup + plan-template materialization -> X0 +Core Phase 0 Outline & Research -> X1 +Core Phase 1 Design & Contracts -> X2 and X3 +Core post-design Constitution re-check -> unchanged +Preset closeout before completion report -> X4 ``` -If `architecture.md` is missing, uses the retired 4+1 or nine-section planning-contract format, lacks an Architecture goal or authorized sources, or has no explicit system boundary with ownership and non-responsibility, stop with a report-only/no-write failure and return to `/speckit.constitution`. - -Consume applicable Architecture content through the normal planning artifacts: - -- `research.md` MUST follow established technical decisions and evidence. If a documented revisit condition is met, record the evidence that triggered it; do not silently replace the Architecture decision. -- `data-model.md` MUST preserve defined concepts, ownership, relationships, lifecycle, and invariants. -- `contracts/` MUST preserve system boundaries, responsibilities, interface ownership, and dependency direction. -- `plan.md` and `quickstart.md` MUST carry forward applicable Architecture constraints, unresolved gaps, revisit conditions, and validation implications. - -An Architecture gap may shape or block planning, but must not be converted into an invented decision. If any planning artifact conflicts with or requires changing `.specify/memory/architecture.md`, stop planning and return to the Constitution stage. Do not repair or rewrite project Architecture from `/speckit.plan`. - -Planning artifacts demonstrate Architecture consumption in their normal content. Do not create a compliance matrix, consumption report, audit receipt, or separate traceability artifact. - -## Plan Agent Topology - -Follow cross-agent protocol profile: `speckit.plan.stage_local_planning`. - -Plan Core Agent owns requirement-gate consumption, stage-local delegation, -conflict resolution, BDD Plan closeout, and final writes to planning artifacts. -Delegated agents return bounded drafts, source refs, blockers, and -`context_gaps`; Plan Core Agent consumes those outputs rather than subagent conversation history. - -Use only planning-local roles: Behavior Projection Agent, Formal Contract Agent, Design Artifact Agent, Validation Planning Agent, and Visual Planning Agent. Each payload declares assigned scope, allowed reads, allowed sections, and output contract. If runtime subagents are unavailable, Plan Core Agent processes one assigned scope at a time with the same boundaries and final-write ownership. - -## Design Artifact Policy - -Core planning remains authoritative. Optional design artifacts carry structured details that do not belong in `plan.md`. - -Generate design artifacts only when the feature requires internal object design or cross-boundary sequence constraints: - -- `class-diagram.md`: internal implementation object structure. -- `contracts/sequences.md`: service-call, command, event, and integration sequencing. - -For simple features, keep artifacts concise. `N/A` sections require a concrete rationale, for example "No service boundary exists for this static documentation change." Do not create large placeholder files. - -Keep `plan.md` as summary/navigation. It must link generated design artifacts and must not embed complete class diagrams or complete sequence diagrams. - -Store service sequences only at `contracts/sequences.md`, even when there are no other contract files. Do not create a root-level `sequences.md`. - -Validation strategy is not a standalone planning document. Planning-time -validation decisions belong in `research.md`; executable validation paths belong in `quickstart.md`; BDD Plan closeout maps those decisions into -`behavior/behavior-testability.md`; concrete tasks belong in `tasks.md`. - -## Phase 0 Gate Consumption - -Use the core plan command's read-only Planning Readiness preflight before any -planning write. This wrapper consumes: - -- `checklists/requirements.md` -- `checklists/behavior.md` -- `checklists/ux.md` -- `checklists/security.md` -- `checklists/nfr.md` -- `checklists/visual.md` - -All standard domains must be evaluated, metadata must match the current spec -revision, and every applicable gate must PASS. Do not accept the legacy -`checklists/behavior-testability.md` as evidence. Missing, BLOCKED, malformed, -or stale gates produce the core report-only/no-write failure. Return product -decisions to `/speckit.clarify` and provider evidence to intake. - -## Phase 0 Behavior Projection - -After Phase 0 preflight passes and before core research or design work, project the accepted `spec.md` requirements into behavior drafts: +Preserve Core user input, setup scripts, Technical Context, Constitution Check, +pre/post hooks, Phase 0, Phase 1, and completion behavior in their official +order. Do not add, remove, rename, reorder, duplicate, or reinterpret a Core +phase or gate. Do not re-run Checklist or aggregate Planning Readiness. -- `behavior/bdd.draft.feature`: readable BDD draft scenarios. -- `behavior/behavior-scenarios.draft.json`: structured draft scenario IDs, Given inputs, When actions, Then outcomes, and source. -- `behavior/uif.intent.json`: interaction intent extracted from accepted requirements. -- `behavior/data-fixtures.intent.json`: data setup intent required by draft scenarios. +## External Input Boundary -Required case types from `checklists/behavior.md` must project into -`behavior/behavior-scenarios.draft.json`. Do not continue with only positive -scenarios when Required case types exist. If a Required case type cannot be -projected without inventing requirements, stop without partial behavior writes -and return to `/speckit.checklist` or `/speckit.clarify`. +Read only `spec.md`, `.specify/memory/constitution.md`, +`.specify/memory/architecture.md`, and current repository facts. Planning has +one strategy for every repository: -Phase 0 behavior projection is a projection step, not a new requirement-discovery step: - -- Do not discover new requirement problems. -- Do not ask clarification questions. -- Do not modify `spec.md`. -- Do not generate formal contracts. -- Do not decide test level, fixture strategy, external-system strategy, interface design, or validation commands. - -Structured JSON draft artifacts must follow their matching `schemas/speckit.behavior.*.schema.json` contracts. - -If Phase 0 cannot generate behavior drafts from a `spec.md` that passed checklist, stop with a report-only/no-write failure. Do not create or update partial behavior artifacts. The remedy is to return to `/speckit.checklist` or `/speckit.clarify`; do not invent missing requirements during planning. - -## Additional Phase 1 Design Outputs - -During Phase 1, after Phase 0 behavior projection and core research have resolved planning unknowns and while producing design/contracts, create or update these artifacts only when their trigger conditions are met: - -1. `class-diagram.md` - - Capture key classes, interfaces, abstract types, services, repositories, adapters, factories, strategies, controllers, and coordinators. - - Explain each core type's responsibility and the relationships that constrain implementation: inheritance, composition, aggregation, dependency, and references. - - Format must be Mermaid, PlantUML, or structured table; selected format must expose type responsibilities and relationships. - - Do not define API request/response fields, domain business fields, test cases, task IDs, private helpers, or method-level implementation details. - -2. `contracts/sequences.md` - - Capture the observable flow of API requests, commands, events, callbacks, async workers, external systems, retries, compensation, rollback, and failure branches. - - Include participants, service boundaries, main success paths, important alternate paths, and failure handling that affects implementation or testing. - - Format must be Mermaid sequence diagram or structured text; selected format must expose participants, boundaries, success paths, and failure paths. - - Do not define field schemas, internal class inheritance, test matrices, or user-facing run instructions. - -When `plan.md` has a design artifact/navigation section, include links to: - -- Internal object design: `./class-diagram.md` -- Service sequences: `./contracts/sequences.md` -- Behavior draft: `./behavior/bdd.draft.feature` -- BDD contracts: `./contracts/bdd/` -- Expected UIF contracts: `./contracts/uif/` -- Behavior contracts: `./contracts/behavior/` -- Data model: `./data-model.md` -- Interface contracts: `./contracts/` -- Validation path: `./quickstart.md` -- Behavior testability: `./behavior/behavior-testability.md` - -When visual requirements are in scope, keep `plan.md` navigation linked to visual fidelity scope, source refs, visual SSOT refs, HTML SSOT refs, structured IR refs, screenshot refs, visual proof refs, and other external evidence refs already accepted by `spec.md` and the readiness checklist. - -## Visual Planning Responsibilities - -When visual requirements are in scope, planning must keep -`checklists/visual.md` and its Visual Fidelity Evidence Matrix as the upstream -readiness record and split visual carry-forward across the existing planning -outputs. - -Use the Visual Fidelity Evidence Matrix `Requirement Status` as the visual planning input filter. Carry forward only visual rows with status `Required` or an accepted exception rule. Rows with status `Unknown` or `[BLOCKED: PROVIDER_EVIDENCE]` must already have blocked checklist PASS; if encountered during planning, stop with a report-only/no-write upstream gate failure and return to `/speckit.checklist`, `/speckit.clarify`, or the external intake extension as appropriate. Do not project `Not Applicable` rows into visual planning outputs. - -- `research.md`: carry forward visual and IR planning inputs for each relevant Visual Item ID or visual SSOT ref. Record source refs, HTML SSOT refs, structured IR refs, readiness status, accepted exception refs, unresolved blocker refs, external evidence refs, related quickstart path, and related UIF or behavior contract path. Do not define visual validation strategy, screenshot comparison, visual diff, baseline capture, or final visual review work; do not copy the Visual Fidelity Evidence Matrix into `research.md`, create new visual requirements, call provider tools, rebuild external intake evidence, or rebuild provider evidence matrices. -- `contracts/uif/` and `contracts/behavior/`: formalize accepted visual interaction and state constraints only when they affect observable behavior. Expected UIF contracts may carry visual_item_refs, viewport_matrix_refs, state_matrix_refs, visual_proof_refs, and accepted_exception_refs. Behavior contracts may reference visual assertion IDs or blockers when a visual state cannot be formalized without inventing requirements. Interface contracts in `contracts/` may model only API or data fields needed to support UI states, assets, or feedback; they must not contain layout rules or screenshot proof decisions. -- `contracts/sequences.md`: add UI interaction sequence, visual state handoff points, responsive branch trigger refs, and visual proof references only when visual states affect cross-boundary order, async callbacks, retries, rollback, compensation, or error propagation. Keep visual style, tokens, layout breakpoints, screenshot matrices, and validation commands out of `contracts/sequences.md`. - -## Behavior-First Planning Inputs +```text +current repository facts + -> applicable Architecture constraints + -> repository-grounded technical design + -> Plan outputs +``` -Use the Phase 0 behavior projection drafts as planning inputs: +Treat empty/minimal repositories as observed fact. Do not invent an existing +module, path, dependency, or implementation surface. Architecture IDs and +revision are provenance inputs. Plan never amends Architecture or declares +cross-command conformance. If required design cannot fit Architecture, record a +blocker routed to `/speckit.constitution`. -- `behavior/bdd.draft.feature` -- `behavior/behavior-scenarios.draft.json` -- `behavior/uif.intent.json` -- `behavior/data-fixtures.intent.json` +Consume confirmed local WHAT/WHY requirements, local blockers, and Architecture +records. `SRC-*` locators are opaque provenance, not automatic read or +execution targets. Do not rewrite Spec, invoke Clarify/Checklist, acquire +external evidence, dereference or execute a source locator, or validate source +authenticity, revision, digest, freshness, publication state, or availability. +Unavailable evidence remains the blocker already projected into Spec or +Architecture. -Phase 1 outputs must cite applicable draft scenario IDs or record `N/A or blocker`. +Apply Change Scope Granularity: lock planned `M + U`; `plan.md` may record +repository/module directory topology required by Core, but no task IDs, +per-task paths, operation-level changes, or implementation order. -During Phase 1, if behavior drafts exist and the requirement gates have passed, -you must formalize them into formal behavior contracts: +## X0 — Feature Plan Control -- `contracts/bdd/`: acceptance-level BDD contracts. -- `contracts/uif/`: Expected UIF contracts. -- `contracts/behavior/`: scenario instance, fixture, and assertion contracts. +Run after Core setup has materialized `plan.md`, before detailed research. +Populate the control sections supplied by `plan-template`: -Required case types from `checklists/behavior.md` must formalize into -`contracts/behavior/scenario-instances.json`. Do not continue with only positive -scenarios when Required case types exist. Map each Required Case ID to a -Scenario ID or `case_coverage_blockers` entry. When a Required case type cannot -be formalized, write `case_coverage_blockers` in -`contracts/behavior/scenario-instances.json` and record `N/A or blocker` with -the Case ID, missing planning input, and downstream contract path. +- feature goal and exclusions; +- repository-grounded planned `M + U`; +- Spec and applicable Architecture revision/ID refs; +- X2-A, X2-B, X2-C applicability (`Required`, `Not Applicable: `, or + `Blocked: `); +- declared independent artifact outputs and internal gates; +- cross-lane dependencies; +- navigation without broken placeholder links. -When formalizing BDD Draft into `contracts/bdd/*.feature`: +`X0_CONTROL_READY` passes only when goal/scope, lane applicability, outputs, +dependencies, Core Technical Context, and Constitution Check are explicit and +no detailed design/test artifact is duplicated into `plan.md`. -- Preserve scenario intent and business outcome from the draft. -- Convert ambiguous Given steps into formal fixture, actor, state, permission, or start-view conditions. -- Convert When steps into formal user events, request cases, or system triggers aligned with UIF/API contracts. -- Convert Then steps into formal feedback, response, business state, or assertion expectations. -- If a step cannot be formalized without inventing information, record `N/A or blocker` instead of guessing. -- Do not introduce independent traceability mechanisms for BDD formalization. +## X1 — Research & Decisions -If behavior drafts exist but cannot be formalized, write `N/A or blocker` in the affected planning artifact with the source draft path, the missing planning input, and the downstream contract path that could not be produced. Do not silently skip behavior draft formalization. +Within Core Phase 0, use `research.md` as a shared decision record. Every +material decision has: -BDD draft reasoning must feed the normal planning outputs: +```text +Decision ID | source/constraint refs | decision | rationale | alternatives +| affected outputs | status +``` -- `research.md`: record the selected test level, fixture strategy, mock/external-system strategy, and error-branch validation decisions for each behavior scenario type that affects implementation. -- `data-model.md`: model formal behavior entities referenced by behavior contracts, including `BehaviorScenarioInstance`, `DataFixture`, `UIFPath`, `FeedbackView`, and `BehaviorAssertion`. -- `contracts/`: align interface contracts with BDD When steps, Expected UIF `api_call` steps, and behavior assertions. -- `quickstart.md`: include validation paths that exercise the formal BDD/UIF/behavior contracts. +Use `DEC-TECH-*`, `DEC-DATA-*`, `DEC-IF-*`, `DEC-UI-*`, and `DEC-TEST-*`. +Record applicable decisions for technical topology/runtime, data +consistency/migration/retry/rollback, interface ownership/compatibility, UI/UX +state/responsive/token/asset/accessibility delivery, and Test risk/level/type/ +technique/fixture/environment/oracle/evidence. + +`research.md` records decisions, not complete designs, Test Conditions, tasks, +results, or cross-command audits. `X1_DECISIONS_READY` requires Core technical +unknowns and active-lane decisions to be decided, routed upstream, or retained +as stable runtime prerequisites. “Use E2E/BDD” alone is incomplete. + +## X2 — Parallel Design & Contracts + +X2-A, X2-B, and X2-C are parallel and mutually constraining. A lane returns a +bounded gap to the owning lane; it never silently owns another lane's schema. + +### X2-A Domain / Object / Interface / Sequence + +- `data-model.md` owns domain concepts, fields, relationships, lifecycle, + invariants, validation, ownership, and persistence semantics. Test fixtures, + scenario instances, UIF paths, feedback views, assertions, DTO schemas, and + task paths are not domain entities unless genuinely part of the product. +- `class-diagram.md` follows the stable template and owns implementation object + responsibilities and relationships. Trigger it for multiple cooperating + objects, dependency direction, patterns, or ownership that `plan.md` cannot + express; otherwise record a specific N/A reason. +- interface contracts own externally observable protocol, input/output, + errors, compatibility/versioning, and state effects. +- `contracts/sequences.md` owns cross-boundary order, async callbacks, retry, + rollback, compensation, and failure propagation when order is observable; + otherwise record a specific N/A reason. + +`X2A_DESIGN_READY` requires every triggered artifact populated or explicitly +N/A, non-overlapping ownership, resolved internal refs, and no placeholder +presented as a decision. + +### X2-B UI/UX Delivery + +When UI/UX or visual delivery applies, create `ui-ux-design.md` from its stable +template. It owns surfaces, components, composition, state, navigation/events, +viewports/responsive behavior, tokens/themes/variants, assets/fallbacks, +accessibility implementation, opaque accepted-source provenance, local delivery +method, and UI/UX Delivery Readiness. + +`contracts/uif/*.expected.json` is a UI/UX interaction contract: start view, +events, routes, observable states/feedback, API call refs, and transitions. It +maps applicable `source_refs` plus local `requirement_refs` (`UI-*`/`VIS-*`) and +may carry `visual_item_refs`, `viewport_matrix_refs`, `state_matrix_refs`, +`visual_proof_refs`, and `accepted_exception_refs` as declared schema fields. It +does not own pixel comparison, styling, or API payload schemas. + +Do not produce `behavior/uif.intent.json` as a mandatory parent or second SSOT. +Formal UIF derives from accepted Spec refs plus `ui-ux-design.md`. + +`X2B_UIUX_READY` requires each applicable `SRC-* + UI/VIS-*` pair to map to +surface/component/state, viewport/responsive/accessibility, asset/variant/ +fallback, accepted-source/delivery-method, and UIF records or a stable local +blocker. X2-B preserves opaque provenance but MUST NOT open, run, inspect, +compare, or certify an external source or its fidelity/state as part of the +local gate. Local pixel delivery ownership stays in UI/UX, never Test +Conditions. + +### X2-C Test & Acceptance + +Create `contracts/test/test-conditions.json` from its schema/template. One +`TC-*` represents one required condition and records source, risk/priority, +level, type, technique, execution mode, fixture decision, environment, +oracle, evidence, related design/UIF/interface refs, X3 path/blocker, and status. + +BDD drafts/contracts and structured behavior scenario/fixture/assertion +artifacts are optional children only when selected techniques require them: + +- BDD/scenario artifacts must reference their parent `TC-*`; +- non-UI scenarios omit `uif_path_id` with `non_ui_rationale`; +- fixture-free scenarios omit fixture refs with `no_fixture_rationale`; +- non-UI/non-message oracles do not require feedback; +- formal fixtures exist only when a Test Condition requires reusable setup; +- assertions may express state, error, invariant, threshold, accessibility, + security, reliability, recovery, or data-side-effect outcomes. + +Reject pixel-perfect comparison, screenshot diff, baseline capture, pixel-level +layout/style assertions, visual restoration, and final pixel review from Test +Conditions or Test Readiness. + +`X2C_TEST_DESIGN_READY` requires all TC dimensions and internal refs, every +technique-triggered child or stable blocker, and zero pixel-level Test entries. + +## X3 — Integration & Validation Paths + +Within Core Phase 1, populate `quickstart.md` using stable `VAL-*` records: -Keep `plan.md` as summary/navigation for these formal behavior contracts. Product requirements stay in `spec.md`, domain details stay in `data-model.md`, interface schemas stay in `contracts/`, and validation run guidance stays in `quickstart.md`. +```text +VAL ID | covered TC/design/contract refs | purpose and level/type +| prerequisites/environment/mode | actor/journey | fixture/data +| systems/boundaries | ordered actions | oracle | evidence | cleanup | blocker +``` -## BDD Plan / Behavior Testability Closeout +Unit/component/contract conditions may share a command path when their oracle +and evidence remain identifiable. Integration/system/e2e and real-system +conditions require explicit journey/environment paths. Do not embed suites, +implementation bodies, migrations, task IDs/order, or fabricated results. -After Phase 1 contracts, `research.md`, and `quickstart.md` are complete, -generate `behavior/behavior-testability.md` from the -`behavior-testability-template`. +`X3_VALIDATION_PATHS_READY` requires each condition needing a runnable path to +map to a complete `VAL-*` or stable runtime blocker. -Compute and record the current spec and plan SHA-256 revisions. Build one Task -Derivation Matrix row for every Required Case from `checklists/behavior.md`. -Each row maps: +## X4 — Independent Closeout -`Case ID → Scenario ID → BDD ref → UIF ref → fixture ref → assertion ref → -validation level → research decision → quickstart path → visual/NFR refs`. +After Core design and its post-design Constitution re-check: -- UIF may be `N/A` only with a concrete non-UI reason. -- Visual or NFR may be N/A only by referencing the corresponding requirement - gate and rationale. -- Validation level is `unit`, `contract`, `integration`, or `e2e`. -- Every missing mapping gets a stable blocker ID. -- `Behavior Testability Status: READY` requires every Required Case to have a - complete derivation row and no blocking items. -- Otherwise set `Behavior Testability Status: BLOCKED`. +1. Finalize `plan.md` Design Object Derivation Index: + `source refs | Architecture provenance | M | U/design object | data-model | + class | interface/sequence | blocker`. +2. Finalize UI/UX Delivery Readiness in `ui-ux-design.md` or explicit N/A. +3. Create `test-readiness.md` as the only Test/Tasks readiness SSOT, one row per + required `TC-*`. Do not create/retain authoritative + `behavior/behavior-testability.md`, `planning-readiness.md`, or + `test-plan.md`. +4. Summarize gate status and lane-owned blockers in `plan.md`. -Recompute generated sections by stable Case ID. Do not append duplicate Gate -Status blocks, retain resolved blockers, copy the legacy -`checklists/behavior-testability.md`, re-check requirement prose, call provider -intake, or ask product clarification. +`PLAN_OUTPUT_READY` equals X0 + X1 + applicable X2 gates + applicable X3 + +complete Design/UI/UX/Test readiness + resolved Plan-internal refs + no +placeholder presented as a decision. It validates Plan outputs only. {CORE_TEMPLATE} -## Design Artifact Reporting - -Before finishing, the final report must list generated artifacts and state whether each is populated or intentionally minimal: - -- `class-diagram.md`: populated, intentionally minimal, or not applicable with reason. -- `contracts/sequences.md`: populated, intentionally minimal, or not applicable with reason. - -Also report where validation decisions were recorded: - -- `research.md`: selected test level, fixture strategy, mock/external-system strategy, and error-branch validation decisions required by behavior contracts. -- `quickstart.md`: executable validation paths for the planned behavior contracts. -- `behavior/behavior-testability.md`: READY or BLOCKED, with Required Case - coverage and blocker IDs. +## Preset Completion Addition -Report unresolved design gaps separately from downstream tasks. Do not mark the planning run complete if a design artifact contains unresolved `NEEDS CLARIFICATION` items that block task generation. +Report each X0–X4 gate, active/N/A lanes, artifacts created or omitted with +reasons, readiness products, Architecture revision refs, runtime prerequisites, +lane-owned blockers, and `PLAN_OUTPUT_READY`. Do not report Tasks, execution +results, or cross-command consistency. diff --git a/presets/workflow-preset/commands/speckit.specify.md b/presets/workflow-preset/commands/speckit.specify.md index fbb873fddc..7421a8e993 100644 --- a/presets/workflow-preset/commands/speckit.specify.md +++ b/presets/workflow-preset/commands/speckit.specify.md @@ -1,59 +1,134 @@ --- -description: Wrap core specification with spec-only requirement ownership. -strategy: wrap +description: Create one full-spectrum WHAT/WHY specification without generating readiness artifacts. +strategy: replace --- Follow cross-agent protocol profile: `speckit.specify.single_core`. -## Spec-Only Requirement Policy -This wrapper must not redefine core-owned User Input, Pre-Execution Checks, extension hooks, base path resolution, or core file handling. - -Preset-added requirement output writes only `spec.md`. -Product requirements stay in `spec.md`: user stories, acceptance criteria, functional requirements, non-functional requirements, visual and UI requirements, constraints, assumptions, and any clarification markers required by the core template. - -Keep requirement text implementation-agnostic and scoped to product behavior. Non-functional requirements must be explicit product-level assumptions or constraints, including no-special-requirement or not-applicable statements when that is the confirmed requirement. - -## Wrapper Input Additions -Treat product notes, PRDs, user prompts, confirmed external intake facts, visual SSOT refs, HTML SSOT refs, structured IR refs, evidence refs, screenshots, and visual proof refs as input to the same feature description. If the core feature description is empty, follow the core command error path. - -Treat confirmed Visual Asset Registry refs as external source artifact inputs only. They describe visual media inventory such as icons, images, illustrations, fonts, motion, video, textures, source refs, variants, license status, fallback policy, and blocker status. - -## Wrapper Preflight Additions -Before writing evidence-derived requirements, consume only confirmed external intake facts or explicit user-provided requirement text. This preset does not perform intake, call provider tools, parse HTML SSOT bundles, re-parse structured IR artifacts, decide provider source readiness, or generate provider artifact instances. - -Classify gaps by ownership: missing product decisions become `[NEEDS CLARIFICATION]`; missing provider or intake evidence for a feature that depends on that evidence becomes `[BLOCKED: PROVIDER_EVIDENCE]`; features that do not depend on HTML SSOT, structured IR, or provider evidence are `Not Applicable`. - -## Wrapper Outline Additions -Specification Projection Policy: write one implementation-agnostic `spec.md` from confirmed product facts, explicit product constraints, and source-backed external intake facts. - -When visual or UI requirements apply, write a `Visual & UI Specification` section inside `spec.md` for observable visual and UI requirements only. When no visual or UI surface applies, record a Not Applicable rationale in `spec.md`. - -Every identified visual or UI requirement must be recorded with status `Required`, `Not Applicable`, `Unknown`, or `[BLOCKED: PROVIDER_EVIDENCE]`; do not silently omit low-evidence visual or UI requirements. - -For visual requirements, preserve visual SSOT refs, HTML SSOT refs, structured IR refs, evidence refs, state and viewport refs, visual proof refs, and Client Asset Contract facts: source refs, asset source strategy, required variants, fallback policy, and blocker status. - -Promote only confirmed product facts and source-backed visual, layout, state, interaction, responsive, accessibility, and acceptance facts with source refs. Do not promote provider evidence gaps into product requirements or `[NEEDS CLARIFICATION]` markers. - -Treat Component State Matrix content as Visual & UI Specification requirements, not visual assets. Record observable states, visual feedback, and interaction outcomes; do not turn them into framework component names or implementation contracts. - -Do not invent code props, code state names, component reuse decisions, self-drawing bans, copy restrictions, DOM structure, CSS selectors, component props, generated code organization, asset binding, or packaging strategy from external visual evidence. - -When visual SSOT, HTML SSOT, structured IR, or provider evidence refs are blocked or unavailable, keep explicit visual or UI requirement coverage in `spec.md`, mark evidence-derived coverage as `[BLOCKED: PROVIDER_EVIDENCE]`, and do not invent missing visual facts. - -## Official Style Alignment -Focus on WHAT users need and WHY. Avoid HOW to implement. Limit [NEEDS CLARIFICATION] markers to the highest-impact unresolved product decisions; record low-impact gaps in Assumptions and provider readiness gaps as `[BLOCKED: PROVIDER_EVIDENCE]`. - -## Specification Quality Validation -Validate that requirement text is stakeholder-readable, testable, implementation-agnostic, and explicit about assumptions, NFR applicability, visual evidence source refs, provider blockers, and unresolved product decisions. - -{CORE_TEMPLATE} +## User Input + +```text +$ARGUMENTS +``` + +The arguments are the feature description. If empty, stop with +`No feature description provided`. + +## Extension Hooks + +Read `.specify/extensions.yml` when present. Run enabled, unconditional +`hooks.before_specify` entries before creating the specification and +`hooks.after_specify` entries after the write but before reporting. Invoke and +await mandatory hooks; condition evaluation remains the HookExecutor's +responsibility. + +## Feature Path And Template + +1. Generate a concise 2–4 word action/noun short name. +2. If a successful pre-hook provides feature metadata, retain it without using + the branch name as the specification directory identity. +3. Resolve `SPECIFY_FEATURE_DIRECTORY` from explicit input first. Otherwise use + `.specify/init-options.json` feature numbering and create one directory under + `specs/`. +4. Resolve the active `spec-template` through the preset resolution stack. +5. Materialize exactly one `spec.md` from that resolved template. +6. Persist the actual directory in `.specify/feature.json`. + +This command writes only the feature directory bootstrap, `.specify/feature.json`, +and `spec.md`. It MUST NOT create, read, evaluate, or modify +`checklists/requirements.md` or any other checklist, Plan, Tasks, Architecture, +contract, test-design, or implementation artifact. + +## Authorized Source Input Contract + +Treat natural-language direction and every user-provided or explicitly +authorized external reference through the same local Source Reference Contract: + +```text +SRC ref | role | opaque locator/description | revision/identity +| authorized scope/facts | projected requirement refs | status/blocker +``` + +1. Read only the current conversation and sources the user explicitly + authorizes. A locator, directory, repository, provider, or neighboring file + does not expand that scope. +2. Record every used source as one unique `SRC-*` row with exactly one role: + `requirement-input`, `visual-input`, `technical-evidence`, or `context-only`. +3. Establish the current feature slice before projecting a broad source. + Project only facts inside the explicit slice. When no safe slice exists, + record `[NEEDS CLARIFICATION: ...]` or a stable local source blocker and do + not import the complete source. +4. Keep confirmed facts, assumptions, clarification needs, unavailable + evidence, and informative context distinguishable. +5. `requirement-input` may authorize applicable WHAT/WHY requirement carriers. + `visual-input` may authorize only `UI-*` and `VIS-*`. + `technical-evidence` may be cited as evidence but does not become a product + requirement. `context-only` authorizes no normative requirement. + +Opaque identity is optional provenance. Preserve a supplied URI, path, +revision, digest, conversation reference, or description, but do not interpret +or validate its external meaning. The presence of a reference MUST NOT cause +this command to invoke a provider tool, dereference or execute a locator, +inspect adjacent source scope, validate authenticity/freshness/publication +state, or create an import manifest, handoff package, adapter, provider-specific +schema, or external synchronization record. Intake is not an SDD stage. + +## Full-Spectrum Projection + +Project confirmed facts from the Authorized Source Input Contract into the +resolved template. Keep the result stakeholder-readable, technology-agnostic, +and focused on WHAT users need and WHY. + +Populate applicable carriers for: + +- product goals, actors, journeys, observable behavior, edge/failure cases; +- functional requirements (`FR-*`); +- non-functional outcomes (`NFR-*`); +- UX journeys and interaction expectations (`UX-*`); +- UI surfaces, states, feedback, and responsive behavior (`UI-*`); +- visual requirements and confirmed source refs (`VIS-*`); +- security/privacy, data/integration, dependencies, boundaries; +- assumptions, exclusions, measurable success criteria; +- source references, unresolved product decisions, source-evidence blockers, and + clarification history. + +Optional domains remain optional. Use a specific `Not Applicable` statement only +when the supplied feature context establishes non-applicability; absence alone +is not proof. The template is a carrier, not a completeness result. + +Make informed, documented assumptions for low-impact gaps. Use at most three +`[NEEDS CLARIFICATION: ...]` markers for high-impact product decisions with no +safe default. Missing external evidence is a stable source blocker, not a +product decision. + +Visual, HTML, structured IR, executable, document, and technical-evidence +inputs use the same `SRC-*` row shape. Preserve only their authorized opaque +provenance and projected local refs. Do not execute or certify them, or invent +DOM/CSS structure, framework components, code props, local asset paths, hashes, +or implementation strategies. + +After projection, `spec.md` is the feature-local WHAT/WHY SSOT. Downstream +commands consume its local requirements and blockers, not the external source +format or workflow. + +## Local Write Safety + +Before finishing, check only the artifact this command owns: + +- the resolved template headings remain structurally valid; +- the feature description was projected into user scenarios, applicable + requirement carriers, and measurable outcomes; +- every used source has one allowed role, an explicit authorized scope, local + projection refs or a reason for none, and a local status/blocker; +- assumptions and unresolved decisions are not presented as confirmed facts; +- no implementation design or foreign-stage artifact was written. + +Do not compute completeness, PASS/BLOCKED readiness, ID uniqueness, numbering +gaps, stale refs, cross-artifact coverage, or cross-command consistency. ## Completion Report -Before finishing, report the `spec.md` sections created or updated, confirmed requirements, visual SSOT refs preserved, provider blockers, and unresolved requirement ambiguities. - -## Done When -- [ ] Confirmed requirement facts, visual SSOT refs, HTML SSOT refs, structured IR refs, and applicable Client Asset Contract facts are reflected in `spec.md`. -- [ ] Functional, non-functional, and visual/UI requirement coverage is present or explicitly marked Not Applicable, Unknown, or `[BLOCKED: PROVIDER_EVIDENCE]`. -- [ ] Product `[NEEDS CLARIFICATION]` markers are limited to high-impact unresolved decisions. -- [ ] Provider readiness blockers remain `[BLOCKED: PROVIDER_EVIDENCE]`. -- [ ] Completion reported with updated `spec.md` sections and remaining blockers. + +Report the `spec.md` path, populated specification areas, assumptions, unresolved +product decisions, source-evidence blockers, and hook status. Suggest +`/speckit.clarify` for product decisions or `/speckit.checklist` for independent +requirement-writing questions. Do not declare Planning Readiness. diff --git a/presets/workflow-preset/commands/speckit.tasks.md b/presets/workflow-preset/commands/speckit.tasks.md index 28a8f13278..e286a95373 100644 --- a/presets/workflow-preset/commands/speckit.tasks.md +++ b/presets/workflow-preset/commands/speckit.tasks.md @@ -1,172 +1,209 @@ --- -description: Wrap task generation with optional design artifact awareness. +description: Map completed Plan products to concrete paths, dependencies, and executable checklist tasks. strategy: wrap --- -## Derivation Boundary - -Preserve the planned `M + U` scope in task text when deriving implementation, validation, and integration tasks. Do not generate execution metadata or write-path fields. - -## Behavior Testability Preflight - -Before writing `tasks.md`, require -`behavior/behavior-testability.md` with: +Follow cross-agent protocol profile: `speckit.tasks.stage_local_derivation`. -- `Stage: plan` -- `Behavior Testability Status: READY` -- current `Spec Revision` and `Plan Revision` -- one complete Task Derivation Matrix row for every Required Case +## Core Precedence And Ownership -If the file is missing, stale, malformed, or BLOCKED, stop before writing -`tasks.md` and report its blocker IDs. The legacy -`checklists/behavior-testability.md` cannot satisfy this preflight. +Preserve Core input/path resolution, task format, story organization, parallel +markers, dependency sections, and completion behavior. -Use the Task Derivation Matrix as the primary task input. For each Required -Case, generate the ordered chain: +The completed Plan is authoritative for test requirements: ```text -fixture → validation/test → implementation → evidence +Required TC-* in Test Readiness + => corresponding fixture/test/validation work is required ``` -## Task-Derivation Subagents - -Follow cross-agent protocol profile: `speckit.tasks.stage_local_derivation`. - -Use a context-reduced multi-subagent derivation model when the command runtime supports subagents. This is a derivation-time partitioning rule only: do not create implementation transfer artifacts, manifests, context digests, execution modes, persistent orchestration files, schemas, scripts, or task write-path metadata. If subagents are unavailable, the Tasks Core Agent must apply the same scoped-read and output-contract rules sequentially. - -The Tasks Core Agent coordinates task derivation, partitions source inputs by user story or review scope, and assembles the final `tasks.md`. It must consume only subagent drafts, structured summaries, blocker reports, and the current command inputs. It must not consume full conversation history as task-derivation context. - -Use these subagent roles only for task derivation: - -- Tasks Core Agent: orchestration, scope partitioning, blocker aggregation, deduplication, final checklist assembly, and preservation of the planned `M + U` scope. -- Story Task Agent: story-local implementation, fixture, validation, evidence, and integration task chains. -- Contract Validation Agent: interface contract, BDD, behavior contract, UIF `api_call`, sequence, external-system, data side-effect, retry, rollback, and quickstart validation task derivation. -- Visual Task Agent: visual readiness, UIF `user_event`, Client Asset Contract, UI implementation, non-visual UI acceptance, asset binding, provider blocker routing, and traceability preservation. -- Review Task Agent: final review tasks for `boundary`, `interface_contract`, `visual`, `data_side_effect`, `behavior_contract`, `sequence_consistency`, and `asset_binding` scopes. - -Every subagent payload must declare: - -- `assigned_scope`: the user story, contract group, visual item group, review scope, or blocker-check scope assigned to the subagent. -- `allowed_read_paths`: the exact files, directories, or glob groups the subagent may read. -- `allowed_sections`: the exact headings, table names, contract IDs, scenario IDs, Visual Item IDs, or summary slices the subagent may inspect within allowed files. -- `output_contract`: the required draft shape, including task candidates, evidence refs, source refs, blockers, and `context_gaps`. - -Subagents must not read full `spec.md`, `plan.md`, `research.md`, or `contracts/` trees unless the payload explicitly lists those files or directories in `allowed_read_paths` and lists the permitted headings, IDs, or contract groups in `allowed_sections`. Prefer scoped excerpts, extracted summaries, contract IDs, scenario IDs, and readiness rows over whole-file reads. A subagent that needs context outside its declared payload must return a `context_gaps` entry instead of widening its own reads. - -`context_gaps` is a blocking output whenever required derivation context is absent, contradictory, outside the subagent payload, or only available by reading an unapproved full artifact. Each gap must include blocker code `TASK_DERIVATION_CONTEXT_GAP`, the missing or inaccessible source, the affected assigned scope, the task type that cannot be derived, and the reason the existing payload is insufficient. The Tasks Core Agent must surface unresolved `context_gaps` as blockers and must not generate complete-looking tasks for the affected scope. - -Keep task granularity compact. Split checklist items only when the validation level, implementation owner, dependency order, evidence source, or review scope differs. Otherwise keep one scenario as a single fixture -> test or validation -> implementation -> evidence chain. +Core's generic “tests are optional” rule applies only when the completed Plan +contains no Required Test Condition for the slice. Tasks MUST NOT drop or +optionalize a required Plan Test Condition. -## Planning Input Taxonomy +Tasks owns task IDs, concrete paths, checklist formatting, dependency order, +story/capability grouping, `[P]` markers, and compact task boundaries. It does +not choose Architecture, design objects, test level/type/technique/priority, +fixture strategy, execution mode, oracle, evidence, or UI/UX ownership. -If any listed file exists under FEATURE_DIR, task generation must consume it as an input: +## T0 — Plan Handoff Preflight -- `class-diagram.md`: internal object structure, dependency direction, and design pattern participants. -- `contracts/sequences.md`: service, command, event, async, retry, rollback, and failure-path flows. -- `research.md`: selected validation level, fixture strategy, external-system execution mode, and error-branch validation decisions. -- `quickstart.md`: executable validation paths and evidence collection guidance. -- `spec.md` visual acceptance requirements: visual fidelity requirements, source refs, visual SSOT refs, HTML SSOT refs, structured IR refs, screenshot refs, visual proof refs, and external evidence refs. -- `spec.md` Client Asset Contract: asset source strategy, required variants, fallback policy, and blocker status. -- `behavior/behavior-testability.md`: Required Case to scenario, contract, - fixture, assertion, validation, quickstart, and Visual/NFR mapping. -- `checklists/visual.md` Visual Fidelity Readiness: `Requirement Status`, - readiness input, visual/IR traceability refs, blockers, and accepted - exceptions. -- `contracts/bdd/`: formal BDD acceptance contracts. -- `contracts/uif/`: Expected UIF interaction contracts. -- `contracts/behavior/`: formal scenario instance, fixture, and assertion contracts. -- `contracts/`: interface schemas and API/message contracts used by validation tasks. +Before writing `tasks.md`, require the current Plan handoff: -Use these inputs to derive implementation, integration, orchestration, failure-handling, and non-visual validation tasks. For behavior contracts, derive test-first task chains in user-story order: fixture setup, BDD/E2E or contract test, implementation, and verification evidence. Keep task output in the existing checklist format and user-story organization. - -`/speckit.tasks` owns implementation, non-visual validation, and review task definition in `tasks.md`. Task derivation must not invent validation strategy, visual validation work, add lifecycle roles, change requirements, update contracts, or widen scope. - -For Client Asset Contract entries, derive asset preparation, binding, implementation, and non-visual acceptance tasks in dependency order. Missing required client visual assets are readiness blockers. - -Use Visual Fidelity Readiness as the only visual planning readiness source. Do not create a second readiness rule from screenshot coverage, external intake artifacts, HTML SSOT refs, structured IR refs, or provider evidence artifacts; if required provider evidence is missing for a dependent feature, report `[BLOCKED: PROVIDER_EVIDENCE]` instead of deriving complete-looking UI tasks. - -Use each Visual Fidelity Readiness row's `Requirement Status` as the visual task input filter. Generate UI implementation, asset binding, and non-visual acceptance tasks only for rows with status `Required` or `Required` plus an accepted exception; tasks for accepted exceptions must cite the exception rule. Do not generate implementation, validation, verification, evidence, asset binding, UI acceptance, or review tasks for `Not Applicable`, `Unknown`, or `[BLOCKED: PROVIDER_EVIDENCE]` rows. Route `Unknown` rows back to `/speckit.clarify`; route `[BLOCKED: PROVIDER_EVIDENCE]` rows to the external intake extension. `/speckit.tasks` must not discover visual requirements, repair evidence, re-parse provider artifacts, or define visual validation strategy; it only decomposes visual specifications that already passed the readiness gate. - -Missing Required case coverage is a coverage blocker, not silently skipped -work. If `behavior/behavior-testability.md` contains a Required Case without a -complete derivation row, report the blocker instead of generating a -complete-looking task list. - -## Validation Task Derivation - -Do not create or require a standalone test strategy artifact. Instead, derive the validation level, fixture strategy, external-system execution mode, and inline evidence requirement while generating `tasks.md`. +```text +PLAN_OUTPUT_READY +├── plan.md + Design Object Derivation Index +├── research.md +├── X2-A artifacts when active +├── ui-ux-design.md + UI/UX Delivery Readiness when active +├── contracts/test/test-conditions.json when Test is active +├── technique-specific contracts when selected +├── quickstart.md VAL-* paths +└── test-readiness.md when Test is active +``` -Use this validation level taxonomy for each scenario or validation path: +Verify only immediate handoff completeness and Tasks' own output contract: -- `unit`: pure domain rules, data validation, state transitions, or behavior assertions that do not cross a process, network, database, browser, or external-system boundary. -- `contract`: API, message, schema, BDD request/response, or Expected UIF contract step with type `api_call` that can be verified at an interface boundary. -- `integration`: service orchestration, persistence, async events, retries, rollback, callbacks, external sandbox calls, or `contracts/sequences.md` failure branches. -- `e2e`: user-visible journeys that require frontend/CLI interaction plus backend behavior, multiple services, or final feedback verification. +- `PLAN_OUTPUT_READY: READY`; +- current Plan/artifact revisions; +- Design, UI/UX, and Test readiness independently present or explicitly N/A; +- every Required mapping resolves to declared Plan refs; +- stable Plan blockers and runtime prerequisites remain distinguishable. -Use this fixture strategy and external-system execution mode taxonomy: +Missing information produces `PLAN_OUTPUT_INCOMPLETE`; stop before writing +complete-looking tasks. Do not recover by treating Spec/Checklist as direct +strategy inputs, reconstructing missing Plan decisions, or performing +Analyze-owned cross-command conformance. -- Attach fixture IDs and setup strategies from `contracts/behavior/` when they exist. -- Use fixture intent only when it is recorded in `research.md` or formal `contracts/behavior/` blocker notes for a scenario documented as `Not Applicable` or blocked by `case_coverage_blockers`. -- Use mock, sandbox, or real-system decisions from `research.md`. -- External-system validation must use mock or sandbox unless `research.md` and `quickstart.md` explicitly require a real-system validation path. -- Add a separate validation task for high-risk, non-functional, external-system, async, retry, rollback, permission, validation, state_conflict, negative, boundary, or error behavior. +## T1 — Concrete Path Binding -Evidence binding: every generated test or validation task must name at least one relevant BDD scenario, behavior assertion, API contract, UIF path, quickstart validation path, visual/IR traceability ref, or command output. +Bind each populated Plan record to the smallest relevant set of concrete source, +test, fixture, configuration, migration, and asset paths. Preserve the planned +`M + U` scope and responsibility. Exact paths are a Tasks output; Plan does not +own them. -Generate explicit validation tasks from this validation task taxonomy instead of relying on final code review for primary validation responsibility: +Examples: -- `contract_validation`: contract ref, implementation surface, validation command, and evidence; report a blocker when mapping is unavailable. -- `ui_acceptance`: user-facing UIF path or BDD scenario, Visual Item ID when applicable, Visual Fidelity Readiness row, viewport/state requirement refs, accepted exception refs, quickstart validation path, and non-visual evidence. -- `data_side_effect_validation`: affected entity or state transition, expected write behavior, rollback/compensation/retry/migration/backfill or invariant assertion when applicable, and validation evidence. -- `integration_e2e_validation`: user-visible journey or cross-boundary flow, scenario/assertion refs, external-system strategy, quickstart validation path, and captured command output. +```text +PaymentCoordinator -> backend/src/checkout/payment_coordinator.py +PaymentFeedbackPanel -> frontend/src/checkout/PaymentFeedbackPanel.tsx +TC-PAYMENT-DECLINED -> tests/contract/checkout/test_payment_declined.py +``` -Task shape: checklist item plus story tag, validation level, strategy when applicable, and evidence refs. +Changing object ownership, test level, or sandbox to mock is re-planning and is +forbidden. -Behavior traceability must be explicit: +## T2 — Dependency Graph -- For each BehaviorScenarioInstance, create a fixture task, BDD/E2E or contract test task, implementation task, and verification evidence task unless the scenario is documented as `Not Applicable` or blocked by `case_coverage_blockers`. -- For each BehaviorScenarioInstance with type `negative`, `boundary`, `permission`, `validation`, or `state_conflict`, derive fixture, contract or BDD test, implementation, and verification evidence tasks. For failure outcomes, name the expected error code, failure feedback, and state invariant, rollback, or compensation assertion when present. -- For each Expected UIF contract step with type `user_event`, create the frontend, CLI, or interaction task that emits or handles the event. -- For each Expected UIF contract step with type `api_call`, create the backend/API or contract task that provides the declared method and path. -- For each quickstart validation path, create a validation task that can collect evidence for the relevant scenario IDs and assertions. +Build dependencies from Plan refs, shared paths, fixtures, contracts, and +execution boundaries. X2-A/X2-B/X2-C are parallel; never impose a fixed +`data -> UI -> test` lane order. Add `[P]` only when tasks touch independent +paths and have no unresolved dependency. -Use only this UI/visual task taxonomy when a user story includes `contracts/uif/`, visual acceptance requirements, Visual Fidelity Readiness rows, or Client Asset Contract entries: +Do not emit normal tasks for N/A, intentionally minimal, placeholder, or blocked +records. -- Maintain story-local task granularity: `visual_setup` -> `visual_implementation` -> `ui_acceptance` or `asset_binding` as needed. Do not create a separate visual lifecycle phase. -- `visual_setup`: prepare UI fixtures, viewport configuration when required by accepted requirements, client resource setup, asset variants, fallback policy mapping, and source ref wiring. -- `visual_implementation`: implement the visual or UI behavior, including page or component states, interaction feedback, responsive layout, asset binding, empty/error/loading/disabled/hover/focus states, and fallback behavior. -- `ui_acceptance`: verify a user-facing UIF path or BDD scenario without screenshot comparison, visual diff, baseline capture, or final visual review. It may assert user action, feedback, page state, accessibility behavior, and visible result described by accepted requirements. -- `asset_binding`: when a Client Asset Contract applies, bind source assets, variants, license or authorization refs, fallback policy, code paths, and missing-asset blockers. -- `visual_setup`, `visual_implementation`, `ui_acceptance`, and `asset_binding` are the only visual/UI task types. -- Visual/UI tasks must name concrete source, test, fixture, configuration, asset paths, and visual/IR traceability refs when derivable; otherwise report a readiness blocker instead of generating an ambiguous task. -- UI acceptance tasks must verify the same UIF path, Visual Item ID, scenario ID, asset contract entry, or quickstart validation path as the implementation task, including required state and viewport coverage when responsive visual behavior is in scope. -- UI acceptance evidence must reference at least one relevant UIF path, BDD or behavior scenario, visual SSOT ref, HTML SSOT ref, structured IR ref, accepted exception ref, quickstart validation path, API contract, or captured command output. -- Missing required provider evidence, Client Asset Contract entries, asset variants, fallback policy, HTML SSOT refs, or structured IR refs are Visual Fidelity Readiness blockers when the feature depends on them. +## T3 — Story / Capability Derivation -For each applicable Visual Fidelity Readiness row with `Requirement Status` `Required` or `Required` plus an accepted exception, generate UI implementation and non-visual acceptance work only when it follows from ready requirements and contracts. Do not generate visual validation, screenshot comparison, visual diff, baseline capture, final visual review, or visual tasks for rows with `Requirement Status` `Not Applicable`, `Unknown`, or `[BLOCKED: PROVIDER_EVIDENCE]`. +Use user stories or deliverable/capability slices as phase boundaries. Map only +populated Plan products: -When an implementation task depends on `contracts/`, include a paired contract validation task that names the contract ref, expected implementation surface, validation command or quickstart path, and evidence requirement. Do not instruct implementers to modify `spec.md`, `contracts/`, readiness checklists, or Visual Fidelity Readiness to make implementation pass; report a blocker if implementation requires requirement or contract changes. +| Plan product | Tasks result | +|---|---| +| scope / `M + U` | task scope and phase boundary | +| technical decisions | setup/configuration/dependency tasks | +| Design Object Derivation Index | implementation object paths | +| data model | domain/persistence/migration/invariant work | +| interface contracts | contract tests and interface implementation | +| sequences | orchestration/retry/rollback/compensation dependencies | +| UI/UX design | component/state/responsive/interaction/accessibility implementation | +| asset mapping | asset preparation, variants, binding, fallback | +| Test Readiness | fixture, test-first, validation, evidence work | +| `VAL-*` paths | runnable integration/e2e/evidence work | -When persistence, migrations, external writes, retries, rollback, or compensation are in scope, include a data-side-effect validation task before final code review. The task must name the affected entity, expected mutation behavior, invariant or rollback/compensation assertion, and evidence source. +Do not emit a task because a file merely exists. Keep tasks compact and split +only at meaningful path ownership, dependency, execution-command, or evidence +boundaries. -## Final Code Review +## T4 — Functional Validation And Evidence -When generating `tasks.md`, append the final phase after user-story tasks in -the same checklist format. Use this final review scope taxonomy when -applicable: `boundary`, `interface_contract`, `visual`, `data_side_effect`, -`behavior_contract`, `sequence_consistency`, and `asset_binding`. Checked sources include -`class-diagram.md`, `contracts/sequences.md`, `contracts/`, -`contracts/uif/`, `research.md`, `quickstart.md`, -`behavior/behavior-testability.md`, `spec.md` visual acceptance requirements, -`spec.md` Client Asset Contract entries, and `checklists/visual.md` Visual -Fidelity Readiness, plus data side-effect review and real-system e2e environment readiness. +For each Required `TC-*`, preserve the Plan-selected level, type, technique, +fixture/data decision, environment/mode, oracle, evidence, related refs, and +`VAL-*` path. Generate the smallest meaningful chain: -Code review task text must require review of runtime database writes and other persistent data changes, including field-level update/delete behavior, bulk writes, soft deletes, ORM whole-object saves, migrations/backfills, retries, rollback/compensation, and external-system writes. Do not generate field-level mutation allowlists or pre-implementation data-write gates in normal tasks. +```text +fixture/environment -> test skeleton or Red -> implementation + -> runnable validation/evidence +``` -Code review task text must require boundary review: task scope stays within planned `M + U`, implementation matches the referenced contracts, validation evidence covers quickstart or contract paths, and no implementation task changed `spec.md`, `contracts/`, readiness checklists, or Visual Fidelity Readiness to make execution pass. +Do not force four tasks when one command naturally owns validation and evidence. +Unit/component/contract/integration/system/e2e work is emitted only when Test +Readiness requires it. Functional UI, accessibility, responsive behavior, and +user-journey tests are generated only from Required Test Conditions. + +UI/UX Delivery Readiness is implementation readiness only. It may generate: + +- view/component and state implementation; +- loading/empty/error/success/permission/disabled/focus behavior; +- functional responsive, navigation, interaction, and accessibility work; +- asset preparation, variants, binding, authorization refs, and fallback. + +Local `SRC-* + UI/VIS-*` mappings may guide component, state, responsive, +accessibility, asset, variant, and fallback implementation. The external +locator remains opaque and does not create acceptance/verification work. + +### Forbidden task scope + +Never generate: + +- `visual_acceptance` or `pixel_fidelity_review`; +- screenshot comparison, visual diff, or baseline capture; +- visual restoration or final visual review; +- pixel-level layout/style assertions; +- screenshot-based evidence requirements; +- source dereference, execution, authenticity/freshness/revision/publication + checks, or external-source validation; +- provider-tool, source acquisition, external baseline certification, or + locator-availability tasks; +- an automatic UI acceptance phase; +- a Final Code Review scope that judges rendered fidelity. + +Pixel delivery/review may remain a Plan-owned UI/UX design record, but Tasks +does not execute it. This #37 boundary overrides older Plan language that could +be read as authorizing pixel tasks. + +## Task-Derivation Delegation + +When the runtime supports bounded derivation subagents, the Tasks Core Agent +remains the sole writer. Partition by story/capability, Plan record group, or +review scope. Payloads declare assigned scope, exact allowed reads/sections, and +an output contract containing task candidates, source refs, blockers, and +`context_gaps`. A derivation unit that needs undeclared context returns +`TASK_DERIVATION_CONTEXT_GAP`; it does not widen its own reads. + +Allowed derivation roles are Story/Capability Mapping, Contract/Test Mapping, +UI Implementation Mapping, and Final Review Mapping. They do not implement, +write upstream artifacts, invent strategy, or define a persistent transfer +protocol. + +## T5 — Final Code Review + +When generating `tasks.md`, append the final phase after user-story tasks. It is +mandatory and no phase may follow it. Core `/speckit.implement` executes it as +ordinary ordered checklist work. + +Applicable review scopes and sources: + +| Scope | Plan source | +|---|---| +| boundary | `plan.md` M + U | +| design object | derivation index and class diagram | +| interface contract | interface contracts | +| behavior/test contract | Test Readiness and optional technique contracts | +| data side effect | data model, sequences, invariants, oracles | +| sequence consistency | sequence contracts | +| UI component/state contract | UI/UX design and UIF | +| responsive/accessibility behavior | UI/UX design + Required TC | +| asset binding | asset/variant/fallback records | +| evidence completeness | Test Readiness and `VAL-*` paths | + +Each review task names concrete source artifacts, implementation surfaces, and +functional evidence. UI review is code/design-contract review only; it MUST NOT +judge rendered visual fidelity or require screenshot/pixel evidence. + +Review may authorize bounded implementation repair and affected-test reruns. If +repair requires changing Spec, Architecture, Plan, research, contracts, +readiness products, or quickstart, keep the task open and report an upstream +blocker. + +Do not add an implementation reviewer runtime, receipt, manifest, worker +protocol, dispatch script, or preset-owned Implement command. -Code review task text may require UI consistency review when UI or visual acceptance was in scope. The review must reconcile implemented UI states and viewport behavior with accepted requirements, Visual Fidelity Readiness, UIF paths, visual/IR traceability refs, and Client Asset Contract bindings, variants, and fallback policy; it must not require screenshot comparison, visual diff, baseline capture, or final visual review. +{CORE_TEMPLATE} -Review evidence binding: final review tasks must name concrete review scope, source artifacts, implementation surfaces, and evidence refs. If review scope exposes drift from the plan, sequences, contracts, or data-side-effect expectations, express it as review evidence, bounded repair permission, or a blocker. If resolving the drift would require changing `spec.md`, `contracts/`, `research.md`, `quickstart.md`, readiness checklists, or planning artifacts, record a blocker instead of treating the change as implementation work. Real-system e2e environment gaps must remain visible as evidence gaps instead of treated as passing evidence. +## Preset Completion Addition -{CORE_TEMPLATE} +Report handoff revision, mapped/blocked Plan records, concrete path groups, +Required Test Condition coverage, dependency/parallel summary, and confirmation +that Final Code Review is the last mandatory phase. Do not declare +cross-command consistency. diff --git a/presets/workflow-preset/docs/extension-governance.md b/presets/workflow-preset/docs/extension-governance.md index ac1281e13c..31ef70d8ca 100644 --- a/presets/workflow-preset/docs/extension-governance.md +++ b/presets/workflow-preset/docs/extension-governance.md @@ -17,15 +17,68 @@ This document defines the ownership boundaries for `workflow-preset`. The preset enriches existing Spec Kit stages. It does not own execution orchestration or add a second implementation engine. +## Authority And Gate Ownership + +| Concern | Single owner | +|---|---| +| SDD workflow governance | `.specify/memory/constitution.md` | +| repository technical truth | `.specify/memory/architecture.md` | +| one command's output quality | the producing command | +| official workflow gates | Spec Kit Core, unchanged | +| cross-command consistency | `/speckit.analyze`, read-only | + +Intake is external evidence acquisition, not an SDD stage. Constitution does +not contain concrete repository Architecture. Architecture does not contain +command procedures, gate definitions, product requirements, task derivation, or +downstream conformance conclusions. + +`/speckit.constitution` is an enforceable replacement command so the authorized +source agreement can suppress unapproved repository inference. It preserves +user input, hooks, independent write scopes, validation, and completion +reporting. + +The Constitution command owns two independent internal gates: + +- `CONSTITUTION_OUTPUT_READY` validates SDD governance only. +- `ARCHITECTURE_OUTPUT_READY` validates the technical Architecture contract, + including intent-first Greenfield and repo-first Brownfield generation. + +Neither gate checks downstream artifacts. Constitution → Spec/Plan, +Architecture → Plan, Spec → Plan, and Plan → Tasks consistency belong only to +Analyze. + +## Analyze Cross-Command Audit + +Analyze is the only owner of cross-command consistency: + +```text +Constitution -> Spec / Plan +Architecture -> research / data-model / contracts / plan / quickstart +Spec -> X0 / X1 / X2 / X3 / X4 +Plan readiness products -> Tasks +Constitution M + U -> Plan / Tasks +``` + +It inventories once, uses stable IDs first, stops a branch at the first +conclusive blocker, separates blockers from warnings, and writes no artifact. +It audits one repo-first, Architecture-constrained Plan strategy for every +repository; Greenfield/Brownfield remain Constitution-only Architecture +generation modes. + +The concrete #24 idempotency, provider binding/lock, retry context, provider +switching recovery, and `ready`/`completed` lifecycle cases are Analyze +fixtures. They are required only when applicable Architecture/Spec refs demand +them, and they do not become a Plan-local conformance gate. + | Stage | Owner | Durable output | |---|---|---| -| `/speckit.constitution` | preset wrapper | Constitution and project Architecture | -| `/speckit.specify` | preset wrapper | requirement and UI/UX intent in `spec.md` | -| `/speckit.clarify` | preset wrapper | clarified requirement decisions | -| `/speckit.checklist` | preset wrapper | requirement-readiness gates | +| `/speckit.constitution` | preset replacement | independently authorized Constitution and project Architecture outputs | +| `/speckit.specify` | preset replacement | full-spectrum WHAT/WHY content in `spec.md` | +| `/speckit.clarify` | preset replacement | accepted product decisions in `spec.md` | +| `/speckit.checklist` | preset wrapper | unanswered requirement-writing questions | | `/speckit.plan` | preset wrapper | design, behavior contracts, and validation design | | `/speckit.tasks` | preset wrapper | executable checklist in `tasks.md` | -| `/speckit.analyze` | preset wrapper | read-only consistency findings | +| `/speckit.analyze` | preset wrapper | read-only cross-command consistency findings | | `/speckit.implement` | Spec Kit core | execution of `tasks.md` | `workflow-preset` MUST NOT declare, package, copy, or replace @@ -42,8 +95,11 @@ protocol, manual execution queue, or implementation validator. The workflow is a producer-to-consumer pipeline: ```text -spec.md + requirement gates - -> plan artifacts + BDD/UIF/validation design +spec.md + optional independent requirement-writing checklists + -> X0 plan control + -> X1 shared decisions + -> X2-A domain/object/interface + X2-B UI/UX + X2-C Test contracts + -> X3 VAL paths + X4 independent readiness -> tasks.md implementation and validation checklist -> core /speckit.implement execution ``` @@ -51,15 +107,86 @@ spec.md + requirement gates Tasks maps upstream artifacts into checklist items. It must not create another planning system or execution protocol. +## Requirement Command Independence + +Specify, Clarify, and Checklist are independent: + +| Command | Writes | Does not own | +|---|---|---| +| Specify | one `spec.md` plus official feature bootstrap metadata | checklists, completeness/readiness, ID validation | +| Clarify | accepted decisions in `spec.md` | checklist recomputation, source acquisition, cross-artifact checks | +| Checklist | `checklists/.md` questions | answers, spec repair, readiness aggregation | + +The full-spectrum `spec-template` supplies optional carriers for functional, +NFR, UX, UI, visual, security/privacy, data/integration, dependency, boundary, +assumption, exclusion, source, unresolved-decision, source-blocker, and +measurable outcome content. A carrier's presence is not a completeness claim. + +Specify and Clarify are replacement commands because active Core side effects +would otherwise create or re-evaluate `checklists/requirements.md`. Their +replacement contracts preserve user input, feature/path resolution, extension +hooks, local write safety, and completion reporting. Checklist remains a Core +wrapper and produces only unanswered question-form checks. + Examples: -- A UI state in `spec.md` and `contracts/uif/` becomes a concrete UI - implementation task plus a UI acceptance task. +- A UI state in `ui-ux-design.md` and `contracts/uif/` becomes concrete UI + implementation work. Functional tests exist only when Test Readiness contains + a Required Test Condition. - A real-system path in `quickstart.md` becomes an integration/e2e task with environment and evidence expectations. - A persistence change becomes implementation and data-side-effect validation tasks, followed by the final review scope. +## Source Reference Contract + +Authorized external material is an input to existing commands, not an SDD stage +or runtime dependency. `spec.md` carries one canonical, source-neutral shape: + +```text +SRC ref | role | opaque locator/description | revision/identity +| authorized scope/facts | projected requirement refs | status/blocker +``` + +The allowed roles are exactly: + +| Role | Local authority | +|---|---| +| `requirement-input` | confirmed, feature-scoped WHAT/WHY facts | +| `visual-input` | feature-scoped `UI-*` and `VIS-*` facts | +| `technical-evidence` | citable evidence that does not become a product requirement | +| `context-only` | informative context with no normative projection authority | + +Every used source has one role and a feature slice. A broad source without a +safe slice remains blocked or needs clarification instead of being imported in +full. Supplied URI/path/revision/digest/description values are opaque +provenance; the preset does not infer adjacent scope or validate their external +meaning, authenticity, freshness, publication state, availability, or +fidelity. + +After authorized projection, `spec.md` is the feature-local WHAT/WHY SSOT. +Clarify may make a user-accepted local decision current while retaining the +originating `SRC-*` and clarification history; no external write-back or +synchronization is required. + +Architecture reuses the four role meanings inside its existing explicit source +agreement without merging Architecture and Specify. Only authorized +`technical-evidence` supports observed or inferred technical records. +Product-facing sources do not become technical decisions automatically. + +Plan reads only local Spec, Constitution, Architecture, and current repository +facts. `SRC-*` locators are provenance, not read or execution targets. +Applicable visual projection follows: + +```text +SRC-* + UI/VIS-* -> ui-ux-design.md -> UIF source_refs + requirement_refs +``` + +Tasks uses that local mapping for implementation guidance only. Analyze checks +local source existence, uniqueness, role compatibility, projection targets, +orphans, contradictions, and X2-B/UIF mappings. Neither command acquires, +dereferences, executes, compares, or certifies an external source. + ## Final Code Review Gate `/speckit.tasks` MUST append Final Code Review as the last mandatory phase of @@ -70,8 +197,8 @@ The phase must cover each applicable scope: - planned `M + U` boundary; - interface contracts; -- behavior contracts; -- UI state, viewport, and visual/IR consistency; +- behavior and Test contracts; +- UI component/state, responsive/accessibility behavior, and asset contracts; - data side effects; - sequence consistency; - asset bindings; @@ -80,6 +207,36 @@ The phase must cover each applicable scope: Completion requires the review tasks themselves to pass. No separate worker result file or orchestration layer is required. +UI review is code/design-contract review. Tasks and Final Code Review never +create or evaluate visual acceptance, pixel fidelity, screenshot comparison, +visual diff, baseline capture, visual restoration, or final rendered-visual +review. Visual/IR/source refs may guide implementation but do not create a +validation task. + +## Tasks As A Pure Plan Mapper + +`/speckit.tasks` starts from `PLAN_OUTPUT_READY`, not Spec or Checklist strategy. +Its lifecycle is: + +```text +T0 handoff preflight + -> T1 concrete path binding + -> T2 dependency graph + -> T3 story/capability mapping + -> T4 required functional validation/evidence + -> T5 Final Code Review (last mandatory phase) +``` + +Tasks owns exact paths, task IDs, dependency order, `[P]`, and checklist shape. +It preserves Plan-selected design/test/UI decisions. Missing mappings produce +`PLAN_OUTPUT_INCOMPLETE`; Tasks does not reconstruct them. + +A Required `TC-*` overrides Core's generic optional-test wording. UI, +accessibility, responsive, and journey tests exist only when Test Readiness +requires them. UI/UX Delivery Readiness otherwise maps only to component, state, +interaction, accessibility, responsive, asset, variant, and fallback +implementation work. + ## Structured Artifact Rules Machine-readable JSON artifacts are contracts, not prose examples. Stable @@ -101,32 +258,52 @@ Shared stage-local behavior is documented in their own stage profile. Commands must not use `tests/` or `docs/` paths as runtime sources. -## Planning Artifact Boundaries +## X0–X4 Planning Artifact Boundaries Keep `/speckit.plan` and `/speckit.tasks` as core-template wrappers unless an intentional contract change says otherwise. -Optional contextual design artifacts include: - -- `class-diagram.md` -- `contracts/sequences.md` +The X labels are preset-internal milestones nested inside unchanged Core Plan +setup, Phase 0, Phase 1, post-design Constitution Check, hooks, and completion: + +| Milestone/lane | Owning artifact | +|---|---| +| X0 Feature Plan Control | `plan.md` | +| X1 Research & Decisions | `research.md` | +| X2-A Domain/Object/Interface | `data-model.md`, contextual `class-diagram.md`, interface contracts, contextual `contracts/sequences.md` | +| X2-B UI/UX Delivery | `ui-ux-design.md`, `contracts/uif/` | +| X2-C Test & Acceptance | `contracts/test/test-conditions.json`, optional technique children | +| X3 Validation Paths | `quickstart.md` `VAL-*` paths | +| X4 Design Readiness | `plan.md` derivation index | +| X4 UI/UX Delivery Readiness | `ui-ux-design.md` | +| X4 Test Readiness | `test-readiness.md` | + +X2-A, X2-B, and X2-C are parallel and mutually constraining. BDD is an optional +Test technique, not the parent of planning. Test Conditions may cover +functional, accessibility, security, performance, reliability, recovery, +compatibility, and data-side-effect concerns across unit/component/contract/ +integration/system/e2e levels. + +Pixel-fidelity delivery and review belong only to UI/UX Delivery Readiness. +Pixel, screenshot, diff, baseline, restoration, and rendered-visual-review work +is rejected from Test Conditions and Test Readiness. Validation decisions stay in `research.md`, executable paths stay in -`quickstart.md`, and BDD Plan closeout maps them into -`behavior/behavior-testability.md`. `/speckit.tasks` derives unit, contract, -integration, UI acceptance, real-system e2e, and review tasks from that mapping. -Do not add a standalone `test-plan.md`. +`quickstart.md`, and `test-readiness.md` is the single Test/Tasks handoff. Do +not restore `behavior/behavior-testability.md`, create a generic +`planning-readiness.md`, or add a standalone `test-plan.md`. -## External Intake Boundary +## External Source Boundary -External source capture, provider access, rendered HTML, structured IR, -screenshots, authentication, and provider evidence generation belong to -extensions. This preset only consumes confirmed refs already projected into -requirements and readiness artifacts. +External source capture, access, authentication, rendering, and evidence +generation remain outside this preset. The preset does not require an upstream +workflow, directory convention, provider, artifact format, publication state, +import manifest, handoff package, adapter runtime, orchestration script, or +provider-specific schema. -Provider evidence gaps remain intake blockers. Product decision gaps return to -clarification. Neither planning nor implementation may silently manufacture -missing evidence. +Missing source evidence remains a local `SRC-*` blocker. Product decision gaps +return to clarification. Planning, Tasks, and Analyze do not acquire or repair +either kind of gap, and Intake is not added as an SDD stage. ## Release And Integration Boundary diff --git a/presets/workflow-preset/preset.yml b/presets/workflow-preset/preset.yml index df777a9069..a91e00b083 100644 --- a/presets/workflow-preset/preset.yml +++ b/presets/workflow-preset/preset.yml @@ -2,9 +2,9 @@ schema_version: '1.0' preset: id: workflow-preset name: Workflow Preset - version: 3.0.0 - description: Constitution-managed architecture, behavior-first specification, design - artifacts, and execution-ready task mapping + version: 3.1.0 + description: Separated SDD governance, source-neutral specification, X0-X4 design, + test-first contracts, and execution-ready task mapping author: bigsmartben repository: https://github.com/bigsmartben/spec-kit-workflow-preset license: MIT @@ -18,6 +18,49 @@ provides: description: Add design artifact navigation to the plan template replaces: plan-template strategy: wrap + - type: template + name: spec-template + file: templates/spec-template.md + description: Add full-spectrum optional requirement carriers to the core specification + template + replaces: spec-template + strategy: wrap + - type: template + name: class-diagram-template + file: templates/class-diagram-template.md + description: Structured object responsibility and dependency design + replaces: class-diagram-template + strategy: replace + - type: template + name: sequences-template + file: templates/sequences-template.md + description: Structured cross-boundary success, retry, rollback, and failure sequences + replaces: sequences-template + strategy: replace + - type: template + name: ui-ux-design-template + file: templates/ui-ux-design-template.md + description: Canonical UI/UX delivery design and readiness carrier + replaces: ui-ux-design-template + strategy: replace + - type: template + name: quickstart-template + file: templates/quickstart-template.md + description: Provide stable VAL path contracts for the quickstart guide + replaces: quickstart-template + strategy: replace + - type: template + name: test-conditions-template + file: templates/test/test-conditions.json + description: Canonical test-first acceptance condition contract + replaces: test-conditions-template + strategy: replace + - type: template + name: test-readiness-template + file: templates/test-readiness-template.md + description: Single Test readiness and Tasks handoff carrier + replaces: test-readiness-template + strategy: replace - type: template name: constitution-template file: templates/constitution-template.md @@ -35,51 +78,54 @@ provides: - type: command name: speckit.specify file: commands/speckit.specify.md - description: Wrap core specification with spec-only requirement ownership + description: Create one full-spectrum WHAT/WHY specification without checklist + side effects replaces: speckit.specify - strategy: wrap + strategy: replace - type: command name: speckit.clarify file: commands/speckit.clarify.md - description: Wrap core clarification with product-decision gate repair + description: Resolve high-impact product ambiguity only in spec.md replaces: speckit.clarify - strategy: wrap + strategy: replace - type: command name: speckit.checklist file: commands/speckit.checklist.md - description: Wrap core checklist generation with multi-domain requirement gates + description: Generate broad or focused question-form tests for requirement writing replaces: speckit.checklist strategy: wrap - type: command name: speckit.constitution file: commands/speckit.constitution.md - description: Manage separate Constitution and Architecture artifacts under one - project lifecycle + description: Manage SDD governance and repository technical Architecture with + independent output gates replaces: speckit.constitution - strategy: wrap + strategy: replace - type: command name: speckit.analyze file: commands/speckit.analyze.md - description: Trace requirement gates through BDD Plan and task derivation + description: Audit Constitution, Architecture, Spec, Plan, and Tasks cross-command + consistency replaces: speckit.analyze strategy: wrap - type: command name: speckit.plan file: commands/speckit.plan.md - description: Consume project Architecture through Phase 0 behavior projection, - formal contracts, and BDD Plan closeout + description: Nest X0-X4 control, parallel design, validation paths, and readiness + inside Core Plan replaces: speckit.plan strategy: wrap - type: command name: speckit.tasks file: commands/speckit.tasks.md - description: Require READY behavior testability and derive complete task chains + description: Map PLAN_OUTPUT_READY records to concrete paths, dependencies, tests, + and final review replaces: speckit.tasks strategy: wrap - type: template name: behavior-bdd-draft-template file: templates/behavior/bdd-draft.feature - description: Template for Phase 0 BDD drafts + description: Optional BDD technique draft derived from canonical Test Conditions replaces: behavior-bdd-draft-template strategy: replace - type: template @@ -88,18 +134,6 @@ provides: description: Template for structured behavior scenario drafts replaces: behavior-scenarios-draft-template strategy: replace - - type: template - name: behavior-uif-intent-template - file: templates/behavior/uif-intent.json - description: Template for Phase 0 UIF intent - replaces: behavior-uif-intent-template - strategy: replace - - type: template - name: behavior-data-fixtures-intent-template - file: templates/behavior/data-fixtures-intent.json - description: Template for Phase 0 data fixture intent - replaces: behavior-data-fixtures-intent-template - strategy: replace - type: template name: requirement-domain-gate-template file: templates/requirements/domain-gate.md @@ -121,15 +155,10 @@ provides: - type: template name: requirement-visual-gate-template file: templates/requirements/visual-gate.md - description: Template for visual requirement and provider-evidence readiness + description: Question-form check for visual requirements and source-reference + quality replaces: requirement-visual-gate-template strategy: replace - - type: template - name: behavior-testability-template - file: templates/behavior/behavior-testability.md - description: Template for plan-stage behavior testability and task readiness - replaces: behavior-testability-template - strategy: replace - type: template name: behavior-bdd-contract-template file: templates/behavior/bdd-contract.feature @@ -166,18 +195,6 @@ provides: description: Schema for structured behavior scenario drafts replaces: speckit-behavior-scenarios-draft-v1-schema strategy: replace - - type: template - name: speckit-behavior-uif-intent-v1-schema - file: schemas/speckit.behavior.uif.intent.v1.schema.json - description: Schema for Phase 0 UIF intent - replaces: speckit-behavior-uif-intent-v1-schema - strategy: replace - - type: template - name: speckit-behavior-data-fixtures-intent-v1-schema - file: schemas/speckit.behavior.data-fixtures.intent.v1.schema.json - description: Schema for data fixture intent - replaces: speckit-behavior-data-fixtures-intent-v1-schema - strategy: replace - type: template name: speckit-behavior-uif-expected-v1-schema file: schemas/speckit.behavior.uif.expected.v1.schema.json @@ -202,10 +219,18 @@ provides: description: Schema for formal behavior assertions replaces: speckit-behavior-assertions-v1-schema strategy: replace + - type: template + name: speckit-test-conditions-v1-schema + file: schemas/speckit.test.conditions.v1.schema.json + description: Schema for canonical Test Conditions + replaces: speckit-test-conditions-v1-schema + strategy: replace tags: - architecture - constitution - behavior - bdd +- test-first +- ui-ux - planning - implementation diff --git a/presets/workflow-preset/schemas/speckit.behavior.assertions.v1.schema.json b/presets/workflow-preset/schemas/speckit.behavior.assertions.v1.schema.json index 7ac37a3d01..e77e4631b9 100644 --- a/presets/workflow-preset/schemas/speckit.behavior.assertions.v1.schema.json +++ b/presets/workflow-preset/schemas/speckit.behavior.assertions.v1.schema.json @@ -18,7 +18,20 @@ "properties": { "id": {"type": "string", "minLength": 1}, "target": {"type": "string", "minLength": 1}, - "operator": {"enum": ["equals", "not_equals", "contains", "exists", "matches"]}, + "operator": { + "enum": [ + "equals", + "not_equals", + "contains", + "exists", + "matches", + "less_than", + "less_than_or_equal", + "greater_than", + "greater_than_or_equal", + "passes_audit" + ] + }, "expected": {}, "intent": { "enum": [ @@ -27,7 +40,13 @@ "failure_feedback", "state_invariant", "rollback", - "compensation" + "compensation", + "threshold", + "accessibility_audit", + "security_audit", + "reliability", + "recovery", + "data_side_effect" ] } } diff --git a/presets/workflow-preset/schemas/speckit.behavior.data-fixtures.intent.v1.schema.json b/presets/workflow-preset/schemas/speckit.behavior.data-fixtures.intent.v1.schema.json deleted file mode 100644 index 1c77687f2d..0000000000 --- a/presets/workflow-preset/schemas/speckit.behavior.data-fixtures.intent.v1.schema.json +++ /dev/null @@ -1,33 +0,0 @@ -{ - "$schema": "https://json-schema.org/draft/2020-12/schema", - "$id": "speckit.behavior.data-fixtures.intent.v1.schema.json", - "title": "Spec Kit Data Fixtures Intent", - "type": "object", - "additionalProperties": false, - "required": ["contract_type", "fixtures"], - "properties": { - "contract_type": { - "const": "speckit.behavior.data_fixtures.intent.v1" - }, - "fixtures": { - "type": "array", - "items": { - "type": "object", - "additionalProperties": false, - "required": ["id", "description", "required_for", "required_states"], - "properties": { - "id": {"type": "string", "minLength": 1}, - "description": {"type": "string", "minLength": 1}, - "required_for": { - "type": "array", - "items": {"type": "string", "minLength": 1} - }, - "required_states": { - "type": "object", - "additionalProperties": true - } - } - } - } - } -} diff --git a/presets/workflow-preset/schemas/speckit.behavior.data-fixtures.v1.schema.json b/presets/workflow-preset/schemas/speckit.behavior.data-fixtures.v1.schema.json index 16fc079667..c18ce0f633 100644 --- a/presets/workflow-preset/schemas/speckit.behavior.data-fixtures.v1.schema.json +++ b/presets/workflow-preset/schemas/speckit.behavior.data-fixtures.v1.schema.json @@ -14,7 +14,7 @@ "items": { "type": "object", "additionalProperties": false, - "required": ["id", "name", "entities", "required_states", "constraints", "setup_strategy"], + "required": ["id", "name", "entities", "required_states", "constraints", "setup_strategy", "reset_strategy", "environment_refs"], "properties": { "id": {"type": "string", "minLength": 1}, "name": {"type": "string", "minLength": 1}, @@ -30,7 +30,13 @@ "type": "array", "items": {"type": "string"} }, - "setup_strategy": {"type": "string", "minLength": 1} + "setup_strategy": {"type": "string", "minLength": 1}, + "reset_strategy": {"type": "string", "minLength": 1}, + "environment_refs": { + "type": "array", + "minItems": 1, + "items": {"type": "string", "minLength": 1} + } } } } diff --git a/presets/workflow-preset/schemas/speckit.behavior.scenario-instances.v1.schema.json b/presets/workflow-preset/schemas/speckit.behavior.scenario-instances.v1.schema.json index baa30bfe6c..df0290bc91 100644 --- a/presets/workflow-preset/schemas/speckit.behavior.scenario-instances.v1.schema.json +++ b/presets/workflow-preset/schemas/speckit.behavior.scenario-instances.v1.schema.json @@ -1,121 +1,62 @@ { "$schema": "https://json-schema.org/draft/2020-12/schema", "$id": "speckit.behavior.scenario-instances.v1.schema.json", - "title": "Spec Kit Behavior Scenario Instances", + "title": "Optional Scenario-Based Acceptance Instances", "type": "object", "additionalProperties": false, "required": ["contract_type", "scenarios"], "properties": { - "contract_type": { - "const": "speckit.behavior.scenario_instances.v1" - }, - "case_coverage_blockers": { - "type": "array", - "items": { - "type": "object", - "additionalProperties": false, - "required": ["id", "case_id", "case_type", "source", "reason", "downstream_contract_path"], - "properties": { - "id": {"type": "string", "minLength": 1}, - "case_id": {"type": "string", "minLength": 1}, - "case_type": { - "enum": ["positive", "negative", "boundary", "permission", "validation", "state_conflict"] - }, - "source": {"type": "string", "minLength": 1}, - "reason": {"type": "string", "minLength": 1}, - "downstream_contract_path": {"type": "string", "minLength": 1} - } - } - }, + "contract_type": {"const": "speckit.behavior.scenario_instances.v1"}, "scenarios": { "type": "array", "minItems": 1, "items": { "type": "object", "additionalProperties": false, - "allOf": [ - { - "if": { - "properties": { - "type": {"not": {"const": "positive"}} - }, - "required": ["type"] - }, - "then": { - "properties": { - "request_case": { - "required": ["id", "case_kind", "outcome", "trigger"] - } - } - } - }, - { - "if": { - "properties": { - "request_case": { - "properties": { - "outcome": {"const": "failure"} - }, - "required": ["outcome"] - } - }, - "required": ["request_case"] - }, - "then": { - "properties": { - "expected_response": { - "required": ["error_code"] - }, - "expected_feedback": { - "required": ["type", "message"] - } - } - } - }, - { - "if": {"properties": {"type": {"const": "negative"}}, "required": ["type"]}, - "then": {"properties": {"request_case": {"properties": {"case_kind": {"const": "negative"}, "outcome": {"const": "failure"}}}}} - }, - { - "if": {"properties": {"type": {"const": "boundary"}}, "required": ["type"]}, - "then": {"properties": {"request_case": {"properties": {"case_kind": {"const": "boundary"}}}}} - }, - { - "if": {"properties": {"type": {"const": "permission"}}, "required": ["type"]}, - "then": {"properties": {"request_case": {"properties": {"case_kind": {"const": "permission"}, "outcome": {"const": "failure"}}}}} - }, - { - "if": {"properties": {"type": {"const": "validation"}}, "required": ["type"]}, - "then": {"properties": {"request_case": {"properties": {"case_kind": {"const": "validation"}, "outcome": {"const": "failure"}}}}} - }, - { - "if": {"properties": {"type": {"const": "state_conflict"}}, "required": ["type"]}, - "then": {"properties": {"request_case": {"properties": {"case_kind": {"const": "state_conflict"}, "outcome": {"const": "failure"}}}}} - } - ], "required": [ "id", "title", "type", - "uif_path_id", - "fixture_ids", + "test_condition_refs", "request_case", "expected_response", - "expected_feedback", "assertion_ids" ], + "allOf": [ + { + "oneOf": [ + {"required": ["uif_path_id"]}, + {"required": ["non_ui_rationale"]} + ] + }, + { + "oneOf": [ + {"required": ["fixture_ids"]}, + {"required": ["no_fixture_rationale"]} + ] + } + ], "properties": { - "id": {"type": "string", "minLength": 1}, + "id": {"type": "string", "pattern": "^SCN-[A-Z0-9][A-Z0-9-]*$"}, "title": {"type": "string", "minLength": 1}, "type": { "enum": ["positive", "negative", "boundary", "permission", "validation", "state_conflict"] }, + "test_condition_refs": { + "type": "array", + "minItems": 1, + "uniqueItems": true, + "items": {"type": "string", "pattern": "^TC-[A-Z0-9][A-Z0-9-]*$"} + }, "uif_path_id": {"type": "string", "minLength": 1}, + "non_ui_rationale": {"type": "string", "minLength": 1}, "fixture_ids": { "type": "array", "minItems": 1, + "uniqueItems": true, "items": {"type": "string", "minLength": 1} }, + "no_fixture_rationale": {"type": "string", "minLength": 1}, "request_case": { "type": "object", "additionalProperties": true, @@ -131,24 +72,17 @@ }, "expected_response": { "type": "object", - "additionalProperties": true, - "properties": { - "business_code": {"type": "string", "minLength": 1}, - "status": {"type": ["integer", "string"]}, - "error_code": {"type": "string", "minLength": 1} - } + "additionalProperties": true }, "expected_feedback": { "type": "object", "additionalProperties": true, - "properties": { - "type": {"type": "string", "minLength": 1}, - "message": {"type": "string", "minLength": 1} - } + "minProperties": 1 }, "assertion_ids": { "type": "array", "minItems": 1, + "uniqueItems": true, "items": {"type": "string", "minLength": 1} } } diff --git a/presets/workflow-preset/schemas/speckit.behavior.uif.expected.v1.schema.json b/presets/workflow-preset/schemas/speckit.behavior.uif.expected.v1.schema.json index df54b76701..eb35ff9d2a 100644 --- a/presets/workflow-preset/schemas/speckit.behavior.uif.expected.v1.schema.json +++ b/presets/workflow-preset/schemas/speckit.behavior.uif.expected.v1.schema.json @@ -4,13 +4,25 @@ "title": "Spec Kit Expected UIF Contract", "type": "object", "additionalProperties": false, - "required": ["contract_type", "id", "source", "type", "start_view", "steps", "feedback_candidates"], + "required": ["contract_type", "id", "source", "source_refs", "requirement_refs", "type", "start_view", "steps", "feedback_candidates"], "properties": { "contract_type": { "const": "speckit.behavior.uif.expected.v1" }, "id": {"type": "string", "minLength": 1}, "source": {"type": "string", "minLength": 1}, + "source_refs": { + "type": "array", + "minItems": 1, + "uniqueItems": true, + "items": {"type": "string", "pattern": "^SRC-[A-Z0-9][A-Z0-9_-]*$"} + }, + "requirement_refs": { + "type": "array", + "minItems": 1, + "uniqueItems": true, + "items": {"type": "string", "pattern": "^(UI|VIS)-[A-Z0-9][A-Z0-9_-]*$"} + }, "type": {"const": "expected"}, "start_view": { "type": "object", @@ -91,6 +103,31 @@ "message": {"type": "string", "minLength": 1} } } + }, + "visual_item_refs": { + "type": "array", + "uniqueItems": true, + "items": {"type": "string", "minLength": 1} + }, + "viewport_matrix_refs": { + "type": "array", + "uniqueItems": true, + "items": {"type": "string", "minLength": 1} + }, + "state_matrix_refs": { + "type": "array", + "uniqueItems": true, + "items": {"type": "string", "minLength": 1} + }, + "visual_proof_refs": { + "type": "array", + "uniqueItems": true, + "items": {"type": "string", "minLength": 1} + }, + "accepted_exception_refs": { + "type": "array", + "uniqueItems": true, + "items": {"type": "string", "minLength": 1} } } } diff --git a/presets/workflow-preset/schemas/speckit.behavior.uif.intent.v1.schema.json b/presets/workflow-preset/schemas/speckit.behavior.uif.intent.v1.schema.json deleted file mode 100644 index 2f75350272..0000000000 --- a/presets/workflow-preset/schemas/speckit.behavior.uif.intent.v1.schema.json +++ /dev/null @@ -1,55 +0,0 @@ -{ - "$schema": "https://json-schema.org/draft/2020-12/schema", - "$id": "speckit.behavior.uif.intent.v1.schema.json", - "title": "Spec Kit UIF Intent", - "type": "object", - "additionalProperties": false, - "required": ["contract_type", "feature", "intents"], - "properties": { - "contract_type": { - "const": "speckit.behavior.uif.intent.v1" - }, - "feature": { - "type": "string", - "minLength": 1 - }, - "intents": { - "type": "array", - "items": { - "type": "object", - "additionalProperties": false, - "required": [ - "id", - "start_view", - "events", - "expected_feedback", - "possible_transition_types" - ], - "properties": { - "id": {"type": "string", "minLength": 1}, - "start_view": {"type": "string", "minLength": 1}, - "events": { - "type": "array", - "items": { - "type": "object", - "additionalProperties": false, - "required": ["name", "label"], - "properties": { - "name": {"type": "string", "minLength": 1}, - "label": {"type": "string", "minLength": 1} - } - } - }, - "expected_feedback": { - "type": "array", - "items": {"type": "string", "minLength": 1} - }, - "possible_transition_types": { - "type": "array", - "items": {"enum": ["local_route", "api_call", "external_link", "system_event"]} - } - } - } - } - } -} diff --git a/presets/workflow-preset/schemas/speckit.test.conditions.v1.schema.json b/presets/workflow-preset/schemas/speckit.test.conditions.v1.schema.json new file mode 100644 index 0000000000..994de9a1c1 --- /dev/null +++ b/presets/workflow-preset/schemas/speckit.test.conditions.v1.schema.json @@ -0,0 +1,156 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "speckit.test.conditions.v1.schema.json", + "title": "Spec Kit Test Conditions", + "type": "object", + "additionalProperties": false, + "required": ["contract_type", "feature", "conditions"], + "properties": { + "contract_type": {"const": "speckit.test.conditions.v1"}, + "feature": {"type": "string", "minLength": 1}, + "conditions": { + "type": "array", + "minItems": 1, + "items": { + "type": "object", + "additionalProperties": false, + "required": [ + "id", + "source_refs", + "risk_or_priority", + "levels", + "types", + "techniques", + "execution_mode", + "environment_refs", + "oracle", + "evidence_requirement", + "related_refs", + "status" + ], + "oneOf": [ + {"required": ["fixture_refs"]}, + {"required": ["no_fixture_rationale"]} + ], + "properties": { + "id": {"type": "string", "pattern": "^TC-[A-Z0-9][A-Z0-9-]*$"}, + "source_refs": { + "type": "array", + "minItems": 1, + "uniqueItems": true, + "items": {"type": "string", "minLength": 1} + }, + "risk_or_priority": {"type": "string", "minLength": 1}, + "levels": { + "type": "array", + "minItems": 1, + "uniqueItems": true, + "items": { + "enum": ["unit", "component", "contract", "integration", "system", "e2e"] + } + }, + "types": { + "type": "array", + "minItems": 1, + "uniqueItems": true, + "items": { + "enum": [ + "functional", + "accessibility", + "security", + "performance", + "reliability", + "recovery", + "compatibility", + "data_side_effect" + ] + } + }, + "techniques": { + "type": "array", + "minItems": 1, + "uniqueItems": true, + "items": { + "enum": [ + "BDD", + "example_based", + "boundary_value", + "state_transition", + "contract_testing", + "other" + ] + } + }, + "other_technique": {"type": "string", "minLength": 1}, + "execution_mode": { + "enum": ["local", "in_process", "mock", "sandbox", "real_system"] + }, + "fixture_refs": { + "type": "array", + "minItems": 1, + "uniqueItems": true, + "items": {"type": "string", "minLength": 1} + }, + "no_fixture_rationale": {"type": "string", "minLength": 1}, + "environment_refs": { + "type": "array", + "minItems": 1, + "uniqueItems": true, + "items": {"type": "string", "minLength": 1} + }, + "oracle": { + "type": "object", + "additionalProperties": false, + "required": ["kind", "expected"], + "properties": { + "kind": { + "enum": [ + "state", + "response", + "error", + "ui_feedback", + "invariant", + "threshold", + "audit", + "accessibility", + "security", + "reliability", + "recovery", + "data_side_effect" + ] + }, + "expected": {} + } + }, + "evidence_requirement": {"type": "string", "minLength": 1}, + "related_refs": { + "type": "array", + "minItems": 1, + "uniqueItems": true, + "items": {"type": "string", "minLength": 1} + }, + "quickstart_ref": {"type": "string", "pattern": "^VAL-[A-Z0-9][A-Z0-9-]*$"}, + "x3_blocker": {"type": "string", "minLength": 1}, + "status": {"enum": ["required", "blocked"]}, + "blocker": {"type": "string", "minLength": 1} + }, + "allOf": [ + { + "if": {"properties": {"techniques": {"contains": {"const": "other"}}}}, + "then": {"required": ["other_technique"]} + }, + { + "if": {"properties": {"status": {"const": "blocked"}}}, + "then": {"required": ["blocker"]} + }, + { + "oneOf": [ + {"required": ["quickstart_ref"]}, + {"required": ["x3_blocker"]} + ] + } + ] + } + } + } +} diff --git a/presets/workflow-preset/templates/architecture-template.md b/presets/workflow-preset/templates/architecture-template.md index 09c9840162..4b6643cdd0 100644 --- a/presets/workflow-preset/templates/architecture-template.md +++ b/presets/workflow-preset/templates/architecture-template.md @@ -1,51 +1,73 @@ # Project Architecture: [PROJECT] -**Architecture Goal**: [State the project-level architecture outcome this artifact guides.] +**Architecture Goal**: [Repository-level technical outcome.] -**Project Mode**: [greenfield | brownfield | amendment] +**Architecture Revision**: [ARCH-REV-YYYYMMDD-N] -**Last Updated**: [DATE] +**Generation Mode**: [greenfield | brownfield | amendment] + +**Repository Revision / Snapshot**: [Commit, tag, snapshot ID, or N/A with reason.] **Authorized Sources**: -- [Source and its agreed role] +| Source ref | Role | Opaque locator / identity | Authorized technical scope / facts | Affected Architecture IDs | Status / gap | +|---|---|---|---|---|---| +| SRC-ARCH-001 | technical-evidence | [Repository snapshot, supplied reference, or description.] | [Explicit technical evidence scope.] | [BND/CON/DEC/CST/GAP refs] | [retained / verified locally / GAP ref] | + +Allowed roles use the source-neutral Spec semantics: `requirement-input`, +`visual-input`, `technical-evidence`, and `context-only`. Only +`technical-evidence` supports observed or inferred technical records. +Product-facing sources may establish approved target context but do not become +technical decisions automatically. Locator identity stays opaque. **Excluded Sources**: -- [Source or `None`] +- [Source or `None`.] ## Architecture Overview -[Summarize the target architecture, the current-to-target distinction when applicable, and the reasoning scope. Do not include an implementation plan.] +[Summarize current technical state, approved target, and migration delta when +applicable. Do not include product requirements, SDD procedures, or tasks.] ## System Boundary -| Boundary | Owns | Does Not Own | External Relationship / Dependency Direction | Source | -|----------|------|--------------|----------------------------------------------|--------| -| [At least one explicit boundary] | [Responsibility] | [Explicit non-responsibility] | [Inbound/outbound relationship] | [Authorized source] | +| ID | State | Owns | Does Not Own | Dependency Direction | Evidence | Evidence Status | +|---|---|---|---|---|---|---| +| BND-001 | observed-current | [Responsibility] | [Non-responsibility] | [Inbound/outbound] | [Revision + path/source] | verified | + +Allowed `State` values: `observed-current`, `inferred`, `approved-target`, +`migration-gap`. An inferred record is never treated as an approved target. ## Conceptual Model -| Concept | Stable Meaning | Owner | Relationships | Lifecycle | Invariants | Source | -|---------|----------------|-------|---------------|-----------|------------|--------| +| ID | State | Stable Meaning | Owner | Relationships | Lifecycle | Invariants | Evidence | +|---|---|---|---|---|---|---|---| +| CON-001 | observed-current | [Meaning] | [Boundary ID] | [Related IDs] | [States/transitions] | [Invariant] | [Revision + path/source] | ## Technical Decisions & Evidence -| Decision / Candidate | Scope | Conclusion | Consequence | Evidence Or Explicit Gap | Revisit Condition | Validation | -|----------------------|-------|------------|-------------|--------------------------|-------------------|------------| +| ID | State | Scope | Decision / Candidate | Technical Consequence | Evidence | Evidence Status | Revisit Condition | Supersedes | +|---|---|---|---|---|---|---|---|---| +| DEC-001 | approved-target | [BND/CON refs] | [Conclusion] | [Technical effect] | [Stable ref] | verified | [Trigger] | [DEC ID or None] | -Use `MUST_VALIDATE` in the Validation column only when planning depends on evidence that is not yet sufficient. Such a row requires a current conclusion and either available evidence or an explicit validation gap. +Allowed `Evidence Status` values: `verified`, `partial`, `unverified`, +`contradictory`. A decision with insufficient evidence remains a candidate or +has a `GAP-*`; it is not silently ratified. -## Planning Guardrails & Gaps +## Technical Constraints & Gaps -### Constraints +### Technical Constraints -| Constraint | Applies To | Planning Implication | Source | -|------------|------------|----------------------|--------| +| ID | State | Applies To | Constraint | Technical Consequence | Evidence | Revisit Condition | +|---|---|---|---|---|---|---| +| CST-001 | approved-target | [BND/CON/DEC refs] | [Constraint] | [Technical effect] | [Stable ref] | [Trigger] | -### Unresolved Gaps +### Unresolved Technical Gaps -| Gap | Planning Impact | Resolution Owner / Trigger | Source | -|-----|-----------------|----------------------------|--------| +| ID | State | Gap | Technical Risk | Resolution Owner / Trigger | Evidence | Supersedes | +|---|---|---|---|---|---|---| +| GAP-001 | migration-gap | [Unknown or contradiction] | [Risk] | [Owner/trigger] | [Stable ref] | [GAP ID or None] | -Optional tables may remain empty when they are not applicable. Do not add placeholder facts or invented records. +Optional tables may be empty only with a concrete `Not Applicable` reason. +Do not add placeholder facts, command names, downstream gate instructions, +feature task paths, or implementation operations. diff --git a/presets/workflow-preset/templates/behavior/assertions.json b/presets/workflow-preset/templates/behavior/assertions.json index cef4516207..9776913ad0 100644 --- a/presets/workflow-preset/templates/behavior/assertions.json +++ b/presets/workflow-preset/templates/behavior/assertions.json @@ -14,6 +14,13 @@ "operator": "equals", "expected": "unchanged", "intent": "state_invariant" + }, + { + "id": "AST-NFR-001", + "target": "critical_path.latency_ms", + "operator": "less_than_or_equal", + "expected": 500, + "intent": "threshold" } ] } diff --git a/presets/workflow-preset/templates/behavior/behavior-testability.md b/presets/workflow-preset/templates/behavior/behavior-testability.md deleted file mode 100644 index fec51dec26..0000000000 --- a/presets/workflow-preset/templates/behavior/behavior-testability.md +++ /dev/null @@ -1,46 +0,0 @@ -# Behavior Testability / Task Readiness - -**Stage**: plan -**Behavior Testability Status**: READY | BLOCKED -**Spec Revision**: sha256:[SPEC_CONTENT_HASH] -**Plan Revision**: sha256:[PLAN_CONTENT_HASH] - -## Input Revisions - -| Input | Revision / Reference | -|---|---| -| `spec.md` | sha256:[SPEC_CONTENT_HASH] | -| `plan.md` | sha256:[PLAN_CONTENT_HASH] | -| Requirement gates | [paths and revisions] | -| Behavior drafts | [paths] | -| Formal contracts | [paths] | -| `research.md` | [reference] | -| `quickstart.md` | [reference] | - -## Task Derivation Matrix - -One row per Required Case. Every row must either map to a complete task -derivation path or name a blocker. - -| Case ID | Scenario ID | BDD Ref | UIF Ref | Fixture Ref | Assertion Ref | Validation Level | Research Ref | Quickstart Path | Visual/NFR Refs | Blocker ID | -|---|---|---|---|---|---|---|---|---|---|---| -| CASE-001 | SCN-001 | contracts/bdd/example.feature | N/A: non-UI behavior | FIX-001 | AST-001 | unit | research.md#decision | quickstart.md#path | checklists/nfr.md#NFR-001 | none | - -Validation Level is one of `unit`, `contract`, `integration`, or `e2e`. -UIF may be `N/A` only with a reason. NFR or visual references may point to an -explicit Not Applicable gate result. - -## Blocking Items - -- none - -## Task Readiness Decision - -- READY only when every Required Case has a Scenario ID, formal BDD/behavior - contract, fixture, assertion, validation decision, and quickstart path, plus - UIF and Visual/NFR references when applicable. -- BLOCKED when any Required Case lacks that mapping or any referenced planning - input is missing or stale. - - diff --git a/presets/workflow-preset/templates/behavior/data-fixtures-intent.json b/presets/workflow-preset/templates/behavior/data-fixtures-intent.json deleted file mode 100644 index 8f83cb34fa..0000000000 --- a/presets/workflow-preset/templates/behavior/data-fixtures-intent.json +++ /dev/null @@ -1,13 +0,0 @@ -{ - "contract_type": "speckit.behavior.data_fixtures.intent.v1", - "fixtures": [ - { - "id": "FIX-001", - "description": "", - "required_for": ["SCN-001"], - "required_states": { - "entity.field": "value" - } - } - ] -} diff --git a/presets/workflow-preset/templates/behavior/data-fixtures.json b/presets/workflow-preset/templates/behavior/data-fixtures.json index b70ec623a8..d5c16d83bd 100644 --- a/presets/workflow-preset/templates/behavior/data-fixtures.json +++ b/presets/workflow-preset/templates/behavior/data-fixtures.json @@ -9,7 +9,9 @@ "entity.field": "value" }, "constraints": [], - "setup_strategy": "factory" + "setup_strategy": "factory", + "reset_strategy": "transaction_rollback", + "environment_refs": ["ENV-LOCAL"] } ] } diff --git a/presets/workflow-preset/templates/behavior/scenario-instances.json b/presets/workflow-preset/templates/behavior/scenario-instances.json index 5650fd5665..18fe182d2c 100644 --- a/presets/workflow-preset/templates/behavior/scenario-instances.json +++ b/presets/workflow-preset/templates/behavior/scenario-instances.json @@ -1,54 +1,22 @@ { "contract_type": "speckit.behavior.scenario_instances.v1", - "case_coverage_blockers": [ - { - "id": "BLK-001", - "case_id": "CASE-VALIDATION-001", - "case_type": "validation", - "source": "spec.md#...", - "reason": "", - "downstream_contract_path": "contracts/behavior/scenario-instances.json" - } - ], "scenarios": [ { "id": "SCN-001", - "title": "", + "title": "", "type": "positive", - "uif_path_id": "UIF-001", - "fixture_ids": ["FIX-001"], + "test_condition_refs": ["TC-001"], + "non_ui_rationale": "No user interface participates in this contract condition.", + "no_fixture_rationale": "The in-process input is self-contained.", "request_case": { - "id": "REQ-001" + "id": "REQ-001", + "outcome": "success", + "trigger": "" }, "expected_response": { "business_code": "SUCCESS" }, - "expected_feedback": { - "message": "" - }, "assertion_ids": ["AST-001"] - }, - { - "id": "SCN-ERR-001", - "title": "", - "type": "permission", - "uif_path_id": "UIF-001", - "fixture_ids": ["FIX-001"], - "request_case": { - "id": "REQ-ERR-001", - "case_kind": "permission", - "outcome": "failure", - "trigger": "" - }, - "expected_response": { - "status": 403, - "error_code": "" - }, - "expected_feedback": { - "type": "inline_error", - "message": "" - }, - "assertion_ids": ["AST-ERR-001"] } ] } diff --git a/presets/workflow-preset/templates/behavior/uif-expected.json b/presets/workflow-preset/templates/behavior/uif-expected.json index 364770e189..f3b96436b7 100644 --- a/presets/workflow-preset/templates/behavior/uif-expected.json +++ b/presets/workflow-preset/templates/behavior/uif-expected.json @@ -1,7 +1,9 @@ { "contract_type": "speckit.behavior.uif.expected.v1", "id": "UIF-001", - "source": "behavior/uif.intent.json", + "source": "ui-ux-design.md#uif-contracts", + "source_refs": ["SRC-001"], + "requirement_refs": ["UI-001", "VIS-001"], "type": "expected", "start_view": { "id": "VIEW-001", @@ -20,5 +22,10 @@ "type": "message", "message": "" } - ] + ], + "visual_item_refs": ["VIS-001"], + "viewport_matrix_refs": ["UI-001#viewports"], + "state_matrix_refs": ["UI-001#states"], + "visual_proof_refs": [], + "accepted_exception_refs": [] } diff --git a/presets/workflow-preset/templates/behavior/uif-intent.json b/presets/workflow-preset/templates/behavior/uif-intent.json deleted file mode 100644 index 8dbb4f676c..0000000000 --- a/presets/workflow-preset/templates/behavior/uif-intent.json +++ /dev/null @@ -1,18 +0,0 @@ -{ - "contract_type": "speckit.behavior.uif.intent.v1", - "feature": "", - "intents": [ - { - "id": "UIF-INTENT-001", - "start_view": "", - "events": [ - { - "name": "user_action", - "label": "" - } - ], - "expected_feedback": [""], - "possible_transition_types": ["local_route", "api_call"] - } - ] -} diff --git a/presets/workflow-preset/templates/class-diagram-template.md b/presets/workflow-preset/templates/class-diagram-template.md new file mode 100644 index 0000000000..3d9b6ae605 --- /dev/null +++ b/presets/workflow-preset/templates/class-diagram-template.md @@ -0,0 +1,19 @@ +# Class / Object Responsibility Design: [FEATURE] + +**Trigger**: [Why multiple cooperating design objects or dependency direction requires this artifact.] + +| Object / Interface | Kind | Responsibility | Collaborators | Relationship / Direction | Planned U | +|---|---|---|---|---|---| +| [Name] | [service/repository/adapter/controller/view-model/etc.] | [Non-overlapping responsibility] | [Names] | [implements/composes/depends/references] | [U ref] | + +## Diagram + +```mermaid +classDiagram + class Example { + <> + } +``` + +Do not copy complete domain fields, boundary payload schemas, test matrices, +task IDs, private helpers, or method bodies. diff --git a/presets/workflow-preset/templates/constitution-template.md b/presets/workflow-preset/templates/constitution-template.md index ae85a8fcc4..3555810319 100644 --- a/presets/workflow-preset/templates/constitution-template.md +++ b/presets/workflow-preset/templates/constitution-template.md @@ -9,30 +9,47 @@ Spec Kit planning and execution MUST use R/M/U/O scope granularity: - U: Unit / Design Object. Primary planning boundary. - O: Operation / Detail. Execution detail. -The R/M/U/O letter mapping is fixed. Do not paraphrase, expand, rename, translate, or substitute these letters with other nouns. - -Planning locks M + U. -Execution maps U -> concrete paths -> O-level changes. -If U -> concrete paths cannot be determined, report a context gap. Do not widen scope to R or broad M. - -This principle applies from planning onward. Requirement specification, clarification, and checklist readiness MUST NOT infer M/U/O boundaries. - -## Constitution And Architecture Boundary - -The Constitution stage maintains separate project-memory artifacts: - -- `.specify/memory/constitution.md` contains durable governance principles. -- `.specify/memory/architecture.md` contains project-level boundaries, concepts, technical direction, evidence, constraints, and gaps. - -Ratified Constitution principles MUST NOT copy concrete Architecture facts. Feature-local planning artifacts may refine Architecture for one feature, but MUST NOT silently replace project Architecture. - -## Architecture-Guided Planning - -`/speckit.plan` MUST read `.specify/memory/architecture.md` before producing planning artifacts. - -- `research.md` MUST follow established technical decisions and evidence, unless an Architecture revisit condition is met. -- `data-model.md` MUST preserve defined concepts, ownership, relationships, lifecycle, and invariants. -- `contracts/` MUST preserve system boundaries, responsibilities, interface ownership, and dependency direction. -- `plan.md` and `quickstart.md` MUST carry forward applicable Architecture constraints, gaps, and validation implications. - -If any planning artifact conflicts with or requires changing the Architecture, planning MUST stop and return to the Constitution stage. +The mapping is fixed and MUST NOT be renamed, translated, or substituted. +Planning locks M + U. Tasks binds U to concrete paths; implementation performs +O-level changes. Requirement commands MUST NOT infer M/U/O boundaries. + +## SDD Workflow Governance + +The Constitution is the sole governance SSOT for the SDD workflow: + +| Command | Owns | Durable write boundary | +|---|---|---| +| Constitution | workflow governance and repository Architecture generation contracts | `constitution.md`, independently authorized `architecture.md` | +| Specify | WHAT/WHY requirements | `spec.md` | +| Clarify | accepted product decisions | `spec.md` | +| Checklist | requirement-writing quality questions | `checklists/.md` | +| Plan | feature technical, UI/UX, and Test design | feature Plan artifacts | +| Tasks | concrete path binding and ordered checklist work | `tasks.md` | +| Analyze | read-only cross-command consistency | none | +| Core Implement | execution of `tasks.md` | implementation surfaces named by Tasks | + +Intake is external evidence acquisition, not an SDD stage. A command may consume +an upstream artifact but MUST NOT rewrite it, emulate another command, or decide +cross-command consistency. + +## Gate Ownership + +| Gate category | Owner | Meaning | +|---|---|---| +| Command Internal Gate | producing command | its output satisfies its own contract | +| Official Core Gate | Spec Kit Core | unchanged official workflow gate | +| Cross-Command Consistency Gate | Analyze exclusively | artifacts from different commands agree and are current | + +Commands MUST NOT copy, broaden, reorder, or reinterpret official Core gates. +Analyze is read-only and routes conflicts to the command that owns the affected +artifact. + +## Constitution and Architecture Authority + +- `.specify/memory/constitution.md` contains only durable SDD governance. +- `.specify/memory/architecture.md` contains only repository technical facts, + abstractions, decisions, evidence, constraints, risks, and gaps. +- Constitution MUST NOT duplicate concrete Architecture facts. +- Plan is repo-first and Architecture-constrained; it never amends Architecture. +- Tasks maps completed Plan products; it never reinterprets Architecture. +- Cross-artifact Architecture projection is verified only by Analyze. diff --git a/presets/workflow-preset/templates/plan-template.md b/presets/workflow-preset/templates/plan-template.md index f82b14ff4e..6d6bde479f 100644 --- a/presets/workflow-preset/templates/plan-template.md +++ b/presets/workflow-preset/templates/plan-template.md @@ -1,20 +1,73 @@ {CORE_TEMPLATE} -## Design Artifacts - -- Internal object design: `./class-diagram.md` -- Service sequences: `./contracts/sequences.md` -- Behavior draft: `./behavior/bdd.draft.feature` -- BDD contracts: `./contracts/bdd/` -- Expected UIF contracts: `./contracts/uif/` -- Behavior contracts: `./contracts/behavior/` -- Data model: `./data-model.md` +## X0 Feature Plan Control + +### Feature Goal And Exclusions + +- **Goal**: [Feature outcome.] +- **Exclusions**: [Explicit non-goals.] +- **Planned M + U**: [Module/capability + design object.] +- **Repository Topology**: [Core-required source/test directory topology only.] + +### Upstream References + +- **Spec**: [path + revision] +- **Spec Source Contract**: [Applicable local SRC refs and blockers; locators remain opaque.] +- **Architecture Revision**: [revision] +- **Applicable Architecture IDs**: [BND/CON/DEC/CST/GAP refs] + +### Active Lane Matrix + +| Lane | Applicability | Source refs | Declared outputs | Dependencies | Internal gate | +|---|---|---|---|---|---| +| X2-A Domain/Object/Interface | Required / N/A: reason / Blocked: ID | [refs] | [paths] | [lane refs] | X2A_DESIGN_READY | +| X2-B UI/UX Delivery | Required / N/A: reason / Blocked: ID | [SRC + UI/VIS refs] | [paths] | [lane refs] | X2B_UIUX_READY | +| X2-C Test & Acceptance | Required / N/A: reason / Blocked: ID | [refs] | [paths] | [lane refs] | X2C_TEST_DESIGN_READY | + +### Cross-Lane Dependency Register + +| ID | Producer | Consumer | Required contract/decision | Status/blocker | +|---|---|---|---|---| + +### Internal Gate Summary + +| Gate | READY / BLOCKED / N/A | Evidence / blocker | +|---|---|---| +| X0_CONTROL_READY | [status] | [ref] | +| X1_DECISIONS_READY | [status] | [ref] | +| X2A_DESIGN_READY | [status] | [ref] | +| X2B_UIUX_READY | [status] | [ref] | +| X2C_TEST_DESIGN_READY | [status] | [ref] | +| X3_VALIDATION_PATHS_READY | [status] | [ref] | + +## Artifact Navigation + +- Shared decisions: `./research.md` +- Domain model: `./data-model.md` +- Object responsibilities: `./class-diagram.md` - Interface contracts: `./contracts/` -- Validation path: `./quickstart.md` +- Cross-boundary sequences: `./contracts/sequences.md` +- UI/UX delivery design: `./ui-ux-design.md` +- UI interaction contracts: `./contracts/uif/` +- Test Conditions: `./contracts/test/test-conditions.json` +- Optional technique contracts: `./contracts/bdd/`, `./contracts/behavior/` +- Validation paths: `./quickstart.md` +- Test readiness: `./test-readiness.md` + +Remove links for explicitly N/A artifacts; do not leave broken placeholders. + +## X4 Design Object Derivation Index + +| Source refs | Architecture refs | M | U / design object | Data-model ref | Class ref | Interface/sequence refs | Blocker | +|---|---|---|---|---|---|---|---| + +No task IDs, exact per-task paths, or implementation order belong here. -## Visual fidelity navigation +## X4 Closeout Summary -- Visual/IR source refs and readiness inputs: `./research.md` -- Visual interaction contracts: `./contracts/uif/` and `./contracts/behavior/` -- Visual flow sequences: `./contracts/sequences.md` -- Non-visual acceptance execution: `./quickstart.md` +- **Design Readiness**: [READY/BLOCKED + index link] +- **UI/UX Delivery Readiness**: [READY/BLOCKED/N/A + link/reason] +- **Test Readiness**: [READY/BLOCKED/N/A + link/reason] +- **X3 Validation Paths**: [READY/BLOCKED/N/A] +- **Blockers by lane**: [IDs] +- **PLAN_OUTPUT_READY**: READY | BLOCKED diff --git a/presets/workflow-preset/templates/quickstart-template.md b/presets/workflow-preset/templates/quickstart-template.md new file mode 100644 index 0000000000..02264b4de1 --- /dev/null +++ b/presets/workflow-preset/templates/quickstart-template.md @@ -0,0 +1,20 @@ +# Quickstart And Validation Paths: [FEATURE] + +## Validation Paths + +### VAL-001 — [Path name] + +- **Covered refs**: [TC/design/contract refs] +- **Purpose / level / type**: [values] +- **Prerequisites and environment/mode**: [requirements] +- **Actor and journey entry**: [entry] +- **Fixture/data setup**: [setup or no-fixture rationale] +- **Systems and crossed boundaries**: [participants] +- **Ordered actions**: [sequence] +- **Expected oracle**: [response/state/feedback/invariant/threshold/audit result] +- **Evidence collection**: [command output/report/state/trace] +- **Cleanup/reset**: [steps or N/A reason] +- **Runtime blocker**: [ID or None] + +Do not embed complete suites, implementation bodies, migrations, task IDs/order, +or fabricated execution results. diff --git a/presets/workflow-preset/templates/requirements/behavior-gate.md b/presets/workflow-preset/templates/requirements/behavior-gate.md index 80203b61c5..fa80c8b35c 100644 --- a/presets/workflow-preset/templates/requirements/behavior-gate.md +++ b/presets/workflow-preset/templates/requirements/behavior-gate.md @@ -1,42 +1,9 @@ -# Behavior Requirement Gate +# Behavior Requirements Writing Checklist: [FEATURE] -**Purpose**: Validate observable behavior and case coverage before behavior projection -**Stage**: requirements -**Domain**: behavior -**Gate**: planning-readiness -**Applicability**: APPLICABLE -**Status**: PASS | BLOCKED -**Spec Revision**: sha256:[SPEC_CONTENT_HASH] +- [ ] CHK-BEH-001 Are primary, alternate, negative, boundary, permission, and validation outcomes specified when applicable? [Scenario Coverage] +- [ ] CHK-BEH-002 Does each observable behavior identify actor, trigger, outcome, and failure feedback? [Clarity] +- [ ] CHK-BEH-003 Are lifecycle and recovery expectations internally consistent? [Consistency] +- [ ] CHK-BEH-004 Are behavior exclusions and assumptions explicit? [Completeness] -## User Story Readiness - -- [ ] CHK-BEH-001 [blocker:product-decision] [spec:STORY] Does each applicable story define observable acceptance behavior? - -## Given / When / Then Readiness - -- [ ] CHK-BEH-002 [blocker:product-decision] [spec:SECTION] Are roles, permissions, starting state, and required data explicit? -- [ ] CHK-BEH-003 [blocker:product-decision] [spec:SECTION] Is each trigger an executable user action, request case, or system event? -- [ ] CHK-BEH-004 [blocker:product-decision] [spec:SECTION] Does each outcome define feedback, business state, error semantics, or assertion intent? - -## Case Coverage Matrix - -One row per story or capability case type. Status: -`Required|Not Applicable|Unknown`. Each row has a stable Case ID. A Required -case cites its source `spec.md` section. Not Applicable requires rationale. -Unknown appears in Blocking Items. - -| Case ID | Story/Capability | Case Type | Status | Source `spec.md` section | Blocking Item ID | Rationale | -|---|---|---|---|---|---|---| -| CASE-[STORY]-POS-001 | [story] | positive | Required | [section] | none | [reason] | -| CASE-[STORY]-NEG-001 | [story] | negative | Unknown | [section] | BLK-BEH-001 | [reason] | - -Evaluate positive, negative, boundary, permission, validation, and -state_conflict case types. Scenario IDs and `case_coverage_blockers` are -assigned during `/speckit.plan`. - -## Blocking Items - -- none - - +Use citations or `[Gap]`. Do not answer the questions or generate behavior +contracts. diff --git a/presets/workflow-preset/templates/requirements/domain-gate.md b/presets/workflow-preset/templates/requirements/domain-gate.md index bd3cfff012..8084a624c9 100644 --- a/presets/workflow-preset/templates/requirements/domain-gate.md +++ b/presets/workflow-preset/templates/requirements/domain-gate.md @@ -1,29 +1,13 @@ -# [DOMAIN] Requirement Gate +# [DOMAIN] Requirements Writing Checklist: [FEATURE] -**Purpose**: Validate [DOMAIN] requirement quality before planning -**Stage**: requirements -**Domain**: [DOMAIN_ID] -**Gate**: planning-readiness -**Applicability**: APPLICABLE | NOT_APPLICABLE -**Status**: PASS | BLOCKED -**Spec Revision**: sha256:[SPEC_CONTENT_HASH] -**Applicability Reason**: [Required when NOT_APPLICABLE] +**Purpose**: Question the quality of `[DOMAIN]` requirement writing. -## Requirement Completeness +**Spec**: [spec.md] -- [ ] CHK-[DOMAIN]-001 [blocker:product-decision] [spec:SECTION] Is the applicable [DOMAIN] behavior completely specified? +- [ ] CHK-[DOMAIN]-001 Is the domain's applicable scope explicit? [Completeness] +- [ ] CHK-[DOMAIN]-002 Are actors, states, failures, and boundaries unambiguous? [Clarity] +- [ ] CHK-[DOMAIN]-003 Are observable outcomes measurable without implementation detail? [Measurability] +- [ ] CHK-[DOMAIN]-004 Are assumptions, dependencies, exclusions, and edge cases stated? [Coverage] -## Requirement Clarity - -- [ ] CHK-[DOMAIN]-002 [blocker:product-decision] [spec:SECTION] Are ambiguous [DOMAIN] terms quantified or bounded? - -## Requirement Traceability - -- [ ] CHK-[DOMAIN]-003 [spec:SECTION] Does each applicable requirement cite its source section? - -## Blocking Items - -- none - - +Items are unanswered quality questions. Use a spec citation or `[Gap]`; do not +compute PASS/BLOCKED or repair `spec.md`. diff --git a/presets/workflow-preset/templates/requirements/nfr-gate.md b/presets/workflow-preset/templates/requirements/nfr-gate.md index b135c01189..7645084019 100644 --- a/presets/workflow-preset/templates/requirements/nfr-gate.md +++ b/presets/workflow-preset/templates/requirements/nfr-gate.md @@ -1,33 +1,8 @@ -# Non-Functional Requirement Gate +# NFR Requirements Writing Checklist: [FEATURE] -**Purpose**: Validate product-level NFR declarations before planning -**Stage**: requirements -**Domain**: nfr -**Gate**: planning-readiness -**Applicability**: APPLICABLE | NOT_APPLICABLE -**Status**: PASS | BLOCKED -**Spec Revision**: sha256:[SPEC_CONTENT_HASH] -**Applicability Reason**: [Required when NOT_APPLICABLE] +- [ ] CHK-NFR-001 Are applicable performance expectations measurable on named user-visible paths? [Measurability] +- [ ] CHK-NFR-002 Are reliability, recovery, security/privacy, accessibility, and compatibility expectations stated or specifically N/A? [Completeness] +- [ ] CHK-NFR-003 Are thresholds, populations, environments, and observation windows unambiguous? [Clarity] +- [ ] CHK-NFR-004 Do NFRs avoid prescribing implementation unless it is an authorized constraint? [Abstraction] -## NFR Coverage Matrix - -Each dimension must be `Required`, `Not Applicable`, or `Unknown`. Required -dimensions need verifiable product-level criteria. Not Applicable needs a -rationale. Unknown items affecting design are blockers. Do not prescribe -architecture. - -| NFR ID | Dimension | Status | Source `spec.md` section | Criterion / Rationale | Blocking Item ID | -|---|---|---|---|---|---| -| NFR-PERF-001 | Performance | [status] | [section] | [criterion] | [id/none] | -| NFR-SEC-001 | Security and Privacy | [status] | [section] | [criterion] | [id/none] | -| NFR-REL-001 | Reliability and Recovery | [status] | [section] | [criterion] | [id/none] | -| NFR-A11Y-001 | Accessibility | [status] | [section] | [criterion] | [id/none] | -| NFR-COMP-001 | Compliance and Auditability | [status] | [section] | [criterion] | [id/none] | -| NFR-OBS-001 | Observability | [status] | [section] | [criterion] | [id/none] | -| NFR-COMPAT-001 | Compatibility | [status] | [section] | [criterion] | [id/none] | -| NFR-DATA-001 | Data Lifecycle | [status] | [section] | [criterion] | [id/none] | -| NFR-COST-001 | Cost and Operational Constraints | [status] | [section] | [criterion] | [id/none] | - -## Blocking Items - -- none +Use citations or `[Gap]`. Do not calculate readiness. diff --git a/presets/workflow-preset/templates/requirements/visual-gate.md b/presets/workflow-preset/templates/requirements/visual-gate.md index 9e1183e6dd..322623f7e1 100644 --- a/presets/workflow-preset/templates/requirements/visual-gate.md +++ b/presets/workflow-preset/templates/requirements/visual-gate.md @@ -1,37 +1,10 @@ -# Visual Requirement Gate +# Visual and UI Requirements Writing Checklist: [FEATURE] -**Purpose**: Validate visual/UI requirements and cited evidence before planning -**Stage**: requirements -**Domain**: visual -**Gate**: planning-readiness -**Applicability**: APPLICABLE | NOT_APPLICABLE -**Status**: PASS | BLOCKED -**Spec Revision**: sha256:[SPEC_CONTENT_HASH] -**Applicability Reason**: [Required when NOT_APPLICABLE] +- [ ] CHK-UI-001 Are critical surfaces, loading/empty/error/success/disabled/focus states, and recovery feedback specified? [Completeness] +- [ ] CHK-UX-001 Are journeys, navigation, keyboard/accessibility behavior, and responsive expectations observable? [Clarity] +- [ ] CHK-VIS-001 Are visual adjectives grounded in applicable `SRC-*` plus `UI/VIS-*` refs or observable criteria? [Measurability] +- [ ] CHK-VIS-002 Does each visual source have the `visual-input` role, an authorized feature slice, and explicit local projection refs? [Consistency] +- [ ] CHK-UI-002 Are viewport, long-copy, safe-area, asset-variant, and fallback expectations stated when applicable? [Coverage] -## Visual Fidelity Readiness - -Every identified visual/UI requirement uses `Required`, `Not Applicable`, -`Unknown`, or `[BLOCKED: PROVIDER_EVIDENCE]`. Required items need observable -requirement text. Unknown items become product-decision blockers. Provider -evidence gaps remain intake blockers and are never converted to clarification. - -## Visual Fidelity Evidence Matrix - -This is the single visual planning-readiness record. - -| Visual Item ID | Source `spec.md` section | Requirement Status | Provider Evidence Dependency | Visual SSOT Refs | HTML SSOT Refs | Structured IR Refs | Other Evidence Refs | Readiness Input | Blocking Item ID | Accepted Exception Refs | -|---|---|---|---|---|---|---|---|---|---|---| -| VIS-001 | [section] | [status] | [yes/no] | [refs] | [refs] | [refs] | [refs] | [input] | [id/none] | [refs/none] | - -Record state, responsive, accessibility, component mapping, asset/fallback, and -accepted-exception coverage when applicable. Responsive visual requirements -block PASS only when required source-backed state or viewport evidence is -missing for a feature that depends on provider evidence. - -Do not call provider tools, re-parse provider artifacts, define screenshot -comparison, visual diff, baseline capture, or final visual review. - -## Blocking Items - -- none +Use citations or `[Gap]`. Do not dereference or validate external sources, +acquire evidence, answer these questions, or modify `spec.md`. diff --git a/presets/workflow-preset/templates/sequences-template.md b/presets/workflow-preset/templates/sequences-template.md new file mode 100644 index 0000000000..e3b56a6e82 --- /dev/null +++ b/presets/workflow-preset/templates/sequences-template.md @@ -0,0 +1,10 @@ +# Cross-Boundary Sequences: [FEATURE] + +**Trigger**: [Observable ordering, async, retry, rollback, compensation, or failure reason.] + +| Sequence ID | Participants / boundaries | Main path | Alternate / retry | Rollback / compensation | Failure propagation | Contract refs | +|---|---|---|---|---|---|---| +| SEQ-001 | [Participants] | [Ordered interactions] | [Branch] | [Recovery] | [Observable failure] | [Refs] | + +Do not duplicate field schemas, class inheritance, UI styling/tokens, complete +test matrices, or run instructions. diff --git a/presets/workflow-preset/templates/spec-template.md b/presets/workflow-preset/templates/spec-template.md new file mode 100644 index 0000000000..a622eeecb8 --- /dev/null +++ b/presets/workflow-preset/templates/spec-template.md @@ -0,0 +1,80 @@ +{CORE_TEMPLATE} + +## Specification Spectrum + +This section is a content carrier, not a completeness checklist. Populate only +applicable domains and retain a concrete `Not Applicable: ` when +non-applicability is confirmed. + +### Functional Requirements + +- **FR-001**: [Observable product behavior.] + +### Non-Functional Requirements + +- **NFR-001**: [Measurable quality outcome, constraint, or explicit N/A.] + +### UX Journeys and Interaction Expectations + +- **UX-001**: [Actor goal, journey, feedback, recovery, and accessibility expectation.] + +### UI Surfaces and States + +- **UI-001**: [Surface, loading/empty/error/success/disabled/focus states, feedback, and responsive behavior.] + +### Visual Requirements and Sources + +- **VIS-001**: [Observable visual requirement, applicable SRC/UI refs, viewport/state refs, or source-evidence blocker.] + +### Security and Privacy + +- [Requirement, constraint, assumption, or specific N/A reason.] + +### Data and Integration Constraints + +- [Data semantics, external dependency, boundary, failure, compatibility, or specific N/A reason.] + +### Dependencies and Boundaries + +- [Owned/non-owned scope and external dependency.] + +## Assumptions + +- [Documented default that is not presented as confirmed fact.] + +## Exclusions + +- [Explicitly out-of-scope outcome.] + +## Source References + +This table is the feature-local Source Reference Contract. A source identity is +opaque provenance: retain a supplied URI, path, revision, digest, conversation +reference, or human description without interpreting or validating its external +meaning or state. + +| SRC ref | Role | Opaque locator / description | Revision / identity | Authorized scope / facts | Projected requirement refs | Status / blocker | +|---|---|---|---|---|---|---| +| SRC-001 | requirement-input | [Conversation direction, document, reference, or description.] | [Optional supplied identity or `Not supplied`.] | [Current feature slice and authorized WHAT/WHY facts.] | [FR/NFR/UX/UI/VIS refs, or `None`.] | [projected / retained / NEEDS CLARIFICATION / BLOCKED with reason.] | + +Allowed roles are exactly `requirement-input`, `visual-input`, +`technical-evidence`, and `context-only`. `context-only` and +`technical-evidence` do not authorize normative `FR/NFR/UX/UI/VIS` projection. +`visual-input` may project only `UI-*` and `VIS-*`. A broad source without a +safe feature slice stays blocked or needs clarification; it is not imported in +full. + +## Unresolved Product Decisions + +- [NEEDS CLARIFICATION: high-impact product decision, or `None`.] + +## Source Evidence Blockers + +- [SRC ref + missing evidence + affected local refs, or `None`. The matching + Source References row remains the canonical status.] + +## Clarifications + +### Session YYYY-MM-DD + +- Q: [Question] -> A: [Accepted answer] diff --git a/presets/workflow-preset/templates/test-readiness-template.md b/presets/workflow-preset/templates/test-readiness-template.md new file mode 100644 index 0000000000..14246987cf --- /dev/null +++ b/presets/workflow-preset/templates/test-readiness-template.md @@ -0,0 +1,12 @@ +# Test Readiness: [FEATURE] + +**Plan Revision**: [revision] + +**Status**: READY | BLOCKED | NOT_APPLICABLE + +| TC ID | Source refs | Level | Type | Technique | Fixture/data | Environment/mode | Oracle | Contract refs | VAL path | Evidence | Blocker | +|---|---|---|---|---|---|---|---|---|---|---|---| + +Every required `TC-*` has exactly one row. BDD/scenario/UIF refs are optional +according to the Test Condition. Pixel-fidelity, screenshot, diff, baseline, +restoration, and rendered-visual-review items MUST NOT appear here. diff --git a/presets/workflow-preset/templates/test/test-conditions.json b/presets/workflow-preset/templates/test/test-conditions.json new file mode 100644 index 0000000000..b3ed3b0cc0 --- /dev/null +++ b/presets/workflow-preset/templates/test/test-conditions.json @@ -0,0 +1,25 @@ +{ + "contract_type": "speckit.test.conditions.v1", + "feature": "", + "conditions": [ + { + "id": "TC-001", + "source_refs": ["FR-001"], + "risk_or_priority": "high", + "levels": ["contract"], + "types": ["functional"], + "techniques": ["contract_testing"], + "execution_mode": "sandbox", + "fixture_refs": ["FIX-001"], + "environment_refs": ["ENV-SANDBOX"], + "oracle": { + "kind": "response", + "expected": "HTTP 200 with accepted contract" + }, + "evidence_requirement": "captured command output", + "related_refs": ["contracts/api/example.yaml"], + "quickstart_ref": "VAL-001", + "status": "required" + } + ] +} diff --git a/presets/workflow-preset/templates/ui-ux-design-template.md b/presets/workflow-preset/templates/ui-ux-design-template.md new file mode 100644 index 0000000000..f2432037ec --- /dev/null +++ b/presets/workflow-preset/templates/ui-ux-design-template.md @@ -0,0 +1,29 @@ +# UI/UX Delivery Design: [FEATURE] + +## Surface, Component, And State Model + +| SRC + UI/VIS refs | View / surface | Component responsibility | Composition | State owner / transitions | Events / navigation | Viewports / responsive | Accessibility | +|---|---|---|---|---|---|---|---| + +## Theme, Assets, And Delivery + +| SRC + UI/VIS refs | Tokens/theme/variant | Asset source | Required variants | Fallback | Opaque accepted-source provenance / local baseline | Local delivery/review method | Local evidence expectation | Blocker | +|---|---|---|---|---|---|---|---|---| + +## UIF Contracts + +| SRC + UI/VIS refs | UIF path | Start view / events / routes | Observable states/feedback | API/interface refs | Blocker | +|---|---|---|---|---|---| + +UIF references interface schemas; it does not duplicate API payloads. + +## X4 UI/UX Delivery Readiness + +| SRC + UI/VIS refs | View/state/viewport | Component | Asset/variant/fallback | UIF ref | Opaque source provenance / local baseline | Local delivery/review method | Local evidence requirement | Blocker | +|---|---|---|---|---|---|---|---|---| + +Pixel delivery/review is owned here, never by Test Conditions. This artifact +contains no BDD strategy, general test levels, task IDs, or implementation +results. Source locators are provenance only: X2-B does not dereference, +execute, inspect, compare against, or certify an external source, its fidelity, +freshness, revision, availability, or publication state. diff --git a/presets/workflow-preset/tests/contracts/speckit-cross-agent-protocol.md b/presets/workflow-preset/tests/contracts/speckit-cross-agent-protocol.md index a92aa3958a..417f46c84f 100644 --- a/presets/workflow-preset/tests/contracts/speckit-cross-agent-protocol.md +++ b/presets/workflow-preset/tests/contracts/speckit-cross-agent-protocol.md @@ -26,22 +26,28 @@ Every preset-owned command profile defines: - `stage`: requirement projection. - `owner_agent`: Specify Core Agent. +- `input_scope`: current natural-language direction plus explicitly authorized, + feature-sliced sources recorded through one local `SRC-*` role. - `allowed_writes`: `spec.md` only. -- `output_contract`: source-aware product, behavior, visual, and UI/UX - requirements. +- `output_contract`: source-neutral rows plus local product, behavior, visual, + and UI/UX requirement projections or stable blockers. +- `stop_conditions`: a broad source without a safe feature slice does not + authorize full import. - `fallback`: single-core execution. ### `speckit.plan.stage_local_planning` -- `stage`: Phase 0 behavior projection and Phase 1 planning. +- `stage`: X0–X4 milestones nested in Core Plan. - `owner_agent`: Plan Core Agent. -- `input_scope`: checklist-approved requirements and assigned planning artifact - families. +- `input_scope`: local Spec facts/blockers, Constitution, current repository + facts, applicable Architecture refs, and assigned X1/X2/X3/X4 artifact + families. External `SRC-*` locators are not allowed reads. - `allowed_writes`: final planning artifacts owned by `/speckit.plan`. -- `output_contract`: behavior drafts, formal contracts, design drafts, - validation design, blockers, and `context_gaps`. -- `validation_gate`: checklist PASS, matching behavior schemas, and planning - blocker aggregation. +- `output_contract`: lane-qualified decisions, X2-A/X2-B/X2-C designs, + `TC-*`, `VAL-*`, independent readiness products, blockers, and + `context_gaps`. +- `validation_gate`: `PLAN_OUTPUT_READY` over Plan outputs and internal refs + only. - `fallback`: the Plan Core Agent processes one assigned scope at a time and preserves final-write ownership. @@ -49,24 +55,31 @@ Every preset-owned command profile defines: - `stage`: upstream-artifact-to-checklist mapping. - `owner_agent`: Tasks Core Agent. -- `input_scope`: user stories, behavior and interface contracts, research - decisions, quickstart paths, UI/visual readiness, and review scopes. +- `input_scope`: `PLAN_OUTPUT_READY`, Design Object, UI/UX Delivery, Test + Readiness, contracts, and `VAL-*` paths. - `allowed_reads`: only the scoped inputs declared for each derivation unit. - `allowed_writes`: `tasks.md` only. -- `output_contract`: ordered implementation, validation, integration/e2e, and - Final Code Review checklist items, plus blockers and `context_gaps`. +- `output_contract`: concrete path bindings, dependency-ordered implementation, + required functional validation/evidence, and last-phase Final Code Review + items, plus blockers and `context_gaps`. - `validation_gate`: source/evidence binding, dependency ordering, final review placement, and blocker aggregation. -- `stop_conditions`: missing required case coverage, missing provider evidence, - or unresolved derivation context. +- `stop_conditions`: `PLAN_OUTPUT_INCOMPLETE`, an unresolved Plan blocker, + unresolved derivation context, or an attempted source-acquisition, + locator-execution, external-state-validation, or visual-fidelity task derived + from a `SRC-*`. - `fallback`: the Tasks Core Agent processes one scope at a time. ### `speckit.analyze.read_only_parallel_review` -- `stage`: vertical consistency analysis. +- `stage`: cross-command consistency analysis. - `owner_agent`: Analyze Core Agent. +- `input_scope`: local Source References → Plan/UIF, Constitution/Architecture + → Spec/Plan, Spec → Plan, Architecture → X1/X2/X3, Plan → Tasks, and M + U + preservation. External locators are never accessed. - `allowed_writes`: none. -- `output_contract`: findings, blockers, warnings, and closed-chain summary. +- `output_contract`: stable-code findings, blockers, warnings, closed-chain + summary, and implementation readiness. - `fallback`: sequential read-only review. ## Permission Boundary diff --git a/presets/workflow-preset/tests/test_preset_contract.py b/presets/workflow-preset/tests/test_preset_contract.py index 5ed90a29fa..0865badf36 100644 --- a/presets/workflow-preset/tests/test_preset_contract.py +++ b/presets/workflow-preset/tests/test_preset_contract.py @@ -1,304 +1,164 @@ from __future__ import annotations -import unittest import json import re +import unittest from pathlib import Path import yaml from jsonschema import Draft202012Validator from jsonschema.exceptions import ValidationError -from validators.speckit_behavior_contract import ( - validate_behavior_case_coverage, - validate_behavior_contract_bundle, - validate_behavior_draft_contract, -) - - -REPO_ROOT = Path(__file__).resolve().parents[1] -PRESET_PATH = REPO_ROOT / "preset.yml" -README_PATH = REPO_ROOT / "README.md" -CHANGELOG_PATH = REPO_ROOT / "CHANGELOG.md" -CROSS_AGENT_PROTOCOL_PATH = REPO_ROOT / "tests" / "contracts" / "speckit-cross-agent-protocol.md" -AGENTS_PATH = REPO_ROOT / "AGENTS.md" -EXTENSION_GOVERNANCE_PATH = REPO_ROOT / "docs" / "extension-governance.md" -SPECIFY_COMMAND_PATH = REPO_ROOT / "commands" / "speckit.specify.md" -CLARIFY_COMMAND_PATH = REPO_ROOT / "commands" / "speckit.clarify.md" -CHECKLIST_COMMAND_PATH = REPO_ROOT / "commands" / "speckit.checklist.md" -CONSTITUTION_COMMAND_PATH = REPO_ROOT / "commands" / "speckit.constitution.md" -ANALYZE_COMMAND_PATH = REPO_ROOT / "commands" / "speckit.analyze.md" -PLAN_COMMAND_PATH = REPO_ROOT / "commands" / "speckit.plan.md" -TASKS_COMMAND_PATH = REPO_ROOT / "commands" / "speckit.tasks.md" -CONSTITUTION_TEMPLATE_PATH = REPO_ROOT / "templates" / "constitution-template.md" -ARCHITECTURE_TEMPLATE_PATH = REPO_ROOT / "templates" / "architecture-template.md" -PLAN_TEMPLATE_PATH = REPO_ROOT / "templates" / "plan-template.md" -CANONICAL_RESPONSIVE_VISUAL_RULE = ( - "Responsive visual requirements block PASS only when required source-backed " - "state or viewport evidence is missing for a feature that depends on provider evidence" -) -FORBIDDEN_VISUAL_COMPAT_TERMS = ( - "legacy visual", - "previous-version", - "previous version", - "backward-compatible", - "backward compatible", - "fallback visual", - "fallback visual rule", - "compatibility mode", - "历史版本", - "旧版兼容", - "兼容旧版", - "回退视觉规则", +from validators.speckit_analyze_contract import ( + audit_cross_command_consistency, + audit_data_model_obligations, + audit_source_reference_contract, ) -REQUIREMENTS_DEV_PATH = REPO_ROOT / "requirements-dev.txt" -BEHAVIOR_SCHEMA_PATHS = { - "speckit.behavior.scenarios.draft.v1": REPO_ROOT - / "schemas" - / "speckit.behavior.scenarios.draft.v1.schema.json", - "speckit.behavior.uif.intent.v1": REPO_ROOT - / "schemas" - / "speckit.behavior.uif.intent.v1.schema.json", - "speckit.behavior.data_fixtures.intent.v1": REPO_ROOT - / "schemas" - / "speckit.behavior.data-fixtures.intent.v1.schema.json", - "speckit.behavior.uif.expected.v1": REPO_ROOT - / "schemas" - / "speckit.behavior.uif.expected.v1.schema.json", - "speckit.behavior.scenario_instances.v1": REPO_ROOT - / "schemas" - / "speckit.behavior.scenario-instances.v1.schema.json", - "speckit.behavior.data_fixtures.v1": REPO_ROOT - / "schemas" - / "speckit.behavior.data-fixtures.v1.schema.json", - "speckit.behavior.assertions.v1": REPO_ROOT - / "schemas" - / "speckit.behavior.assertions.v1.schema.json", -} -BEHAVIOR_TEMPLATE_PATHS = { - "behavior-bdd-draft-template": REPO_ROOT / "templates" / "behavior" / "bdd-draft.feature", - "behavior-scenarios-draft-template": REPO_ROOT - / "templates" - / "behavior" - / "behavior-scenarios-draft.json", - "behavior-uif-intent-template": REPO_ROOT / "templates" / "behavior" / "uif-intent.json", - "behavior-data-fixtures-intent-template": REPO_ROOT - / "templates" - / "behavior" - / "data-fixtures-intent.json", - "behavior-testability-template": REPO_ROOT - / "templates" - / "behavior" - / "behavior-testability.md", - "behavior-bdd-contract-template": REPO_ROOT / "templates" / "behavior" / "bdd-contract.feature", - "behavior-uif-expected-template": REPO_ROOT / "templates" / "behavior" / "uif-expected.json", - "behavior-scenario-instances-template": REPO_ROOT - / "templates" - / "behavior" - / "scenario-instances.json", - "behavior-data-fixtures-template": REPO_ROOT / "templates" / "behavior" / "data-fixtures.json", - "behavior-assertions-template": REPO_ROOT / "templates" / "behavior" / "assertions.json", -} -REQUIREMENT_TEMPLATE_PATHS = { - "requirement-domain-gate-template": REPO_ROOT - / "templates" - / "requirements" - / "domain-gate.md", - "requirement-behavior-gate-template": REPO_ROOT - / "templates" - / "requirements" - / "behavior-gate.md", - "requirement-nfr-gate-template": REPO_ROOT - / "templates" - / "requirements" - / "nfr-gate.md", - "requirement-visual-gate-template": REPO_ROOT - / "templates" - / "requirements" - / "visual-gate.md", -} -REMOVED_IMPLEMENT_RUNTIME_PATHS = ( - REPO_ROOT / "commands" / "speckit.implement.md", - REPO_ROOT / "schemas" / "speckit.implement.manifest.v1.schema.json", - REPO_ROOT / "schemas" / "speckit.implement.handoff.v2.schema.json", - REPO_ROOT / "schemas" / "speckit.implement.receipt.v1.schema.json", - REPO_ROOT / "validators" / "speckit_implement_contract.py", - REPO_ROOT / "tests" / "contracts" / "speckit-cross-agent-subagents.md", +from validators.speckit_behavior_contract import validate_behavior_contract_bundle +from validators.speckit_test_contract import ( + validate_test_conditions, + validate_test_readiness, ) -FEATURE_PATH = "specs/001-demo" - - - - - - +ROOT = Path(__file__).resolve().parents[1] +MANIFEST = ROOT / "preset.yml" +COMMANDS = ROOT / "commands" +TEMPLATES = ROOT / "templates" +SCHEMAS = ROOT / "schemas" +VALIDATORS = ROOT / "validators" +GOVERNANCE = ROOT / "docs" / "extension-governance.md" +README = ROOT / "README.md" +AGENTS = ROOT / "AGENTS.md" +CROSS_AGENT = ROOT / "tests" / "contracts" / "speckit-cross-agent-protocol.md" +ARTIFACT_WORKFLOW = ROOT / ".github" / "workflows" / "preset-artifact.yml" +def read(path: Path) -> str: + return path.read_text(encoding="utf-8") - -def minimal_behavior_scenarios_draft( - *, - scenario_id: str = "SCN-001", - scenario_type: str = "positive", -) -> dict: - return { - "contract_type": "speckit.behavior.scenarios.draft.v1", - "feature": "refund-application", - "scenarios": [ - { - "id": scenario_id, - "title": "Submit refund", - "type": scenario_type, - "given": ["FIX-BUYER"], - "when": ["click_refund", "submit_refund"], - "then": ["show_refund_submitted"], - "source": "plan-phase-0", - } - ], - } - - -def minimal_uif_intent() -> dict: - return { - "contract_type": "speckit.behavior.uif.intent.v1", - "feature": "refund-application", - "intents": [ - { - "id": "UIF-INTENT-001", - "start_view": "OrderDetailPage", - "events": [{"name": "submit_refund", "label": "Submit refund"}], - "expected_feedback": ["Refund submitted"], - "possible_transition_types": ["local_route", "api_call"], - } - ], - } +def load_json(path: Path) -> dict: + return json.loads(read(path)) -def minimal_data_fixtures_intent() -> dict: +def minimal_test_conditions(*, technique: str = "contract_testing") -> dict: return { - "contract_type": "speckit.behavior.data_fixtures.intent.v1", - "fixtures": [ + "contract_type": "speckit.test.conditions.v1", + "feature": "refund", + "conditions": [ { - "id": "FIX-BUYER", - "description": "Buyer user", - "required_for": ["SCN-001"], - "required_states": {"user.role": "buyer"}, + "id": "TC-001", + "source_refs": ["FR-001"], + "risk_or_priority": "high", + "levels": ["contract"], + "types": ["functional"], + "techniques": [technique], + "execution_mode": "sandbox", + "fixture_refs": ["FIX-001"], + "environment_refs": ["ENV-SANDBOX"], + "oracle": {"kind": "response", "expected": "SUCCESS"}, + "evidence_requirement": "captured command output", + "related_refs": ["contracts/api/refund.yaml"], + "quickstart_ref": "VAL-001", + "status": "required", } ], } -def minimal_uif_expected() -> dict: +def minimal_uif() -> dict: return { "contract_type": "speckit.behavior.uif.expected.v1", "id": "UIF-001", - "source": "behavior/uif.intent.json", + "source": "ui-ux-design.md#uif-contracts", + "source_refs": ["SRC-003"], + "requirement_refs": ["UI-001", "VIS-001"], "type": "expected", - "start_view": {"id": "VIEW-ORDER-DETAIL", "name": "Order detail"}, + "start_view": {"id": "VIEW-001", "name": "Order"}, "steps": [ - {"id": "EVT-SUBMIT-REFUND", "type": "user_event", "label": "Submit refund"}, - {"type": "api_call", "api": {"method": "POST", "path": "/orders/{orderId}/refund"}}, + {"id": "EVT-001", "type": "user_event", "label": "Submit"}, + { + "type": "api_call", + "api": {"method": "POST", "path": "/orders/1/refund"}, + }, ], "feedback_candidates": [ - {"id": "FB-SUCCESS", "type": "toast", "message": "Refund submitted"} + {"id": "FB-001", "type": "toast", "message": "Submitted"} ], + "visual_item_refs": ["VIS-001"], + "viewport_matrix_refs": ["UI-001#viewports"], + "state_matrix_refs": ["UI-001#states"], + "visual_proof_refs": [], + "accepted_exception_refs": [], } -def minimal_behavior_scenario_instances() -> dict: +def source_contract_snapshot() -> dict: return { - "contract_type": "speckit.behavior.scenario_instances.v1", - "scenarios": [ - { - "id": "SCN-001", - "title": "Submit refund", - "type": "positive", - "uif_path_id": "UIF-001", - "fixture_ids": ["FIX-BUYER"], - "request_case": {"id": "REQ-001", "reason": "QUALITY_ISSUE"}, - "expected_response": {"business_code": "SUCCESS"}, - "expected_feedback": {"message": "Refund submitted"}, - "assertion_ids": ["AST-001"], - } - ], - } - - -def minimal_exception_behavior_scenario_instances(*, scenario_type: str = "permission") -> dict: - instances = minimal_behavior_scenario_instances() - scenario = instances["scenarios"][0] - scenario["id"] = "SCN-ERR-001" - scenario["title"] = "Reject refund request" - scenario["type"] = scenario_type - scenario["request_case"] = { - "id": "REQ-ERR-001", - "case_kind": scenario_type, - "outcome": "failure", - "trigger": "submit_refund_without_required_permission", - } - scenario["expected_response"] = { - "business_code": "REJECTED", - "status": 403, - "error_code": "ERR_PERMISSION_DENIED", - } - scenario["expected_feedback"] = { - "type": "inline_error", - "message": "Permission denied", - } - scenario["assertion_ids"] = ["AST-001"] - return instances - - -def minimal_case_coverage() -> dict: - return { - "case_coverage": [ - { - "story": "Refund request", - "case_id": "CASE-001", - "case_type": "permission", - "status": "Required", - "source": "spec.md#user-story-1", - "scenario_id": "SCN-ERR-001", - } - ] - } - - -def minimal_case_coverage_with_blocker() -> dict: - return { - "case_coverage": [ - { - "story": "Refund request", - "case_id": "CASE-002", - "case_type": "validation", - "status": "Required", - "source": "spec.md#user-story-1", - "blocker_id": "BLK-001", - } - ] + "spec": { + "requirement_refs": ["FR-001", "FR-002", "UI-001", "VIS-001"], + "sources": [ + { + "ref": "SRC-001", + "role": "requirement-input", + "locator_or_description": "current conversation direction", + "authorized_scope": "refund submission behavior", + "projected_refs": ["FR-001"], + "status": "projected", + }, + { + "ref": "SRC-002", + "role": "requirement-input", + "locator_or_description": "opaque product document", + "revision": "supplied-r7", + "authorized_scope": "refund eligibility section", + "feature_slice": "refund eligibility", + "broad": True, + "projected_refs": ["FR-002"], + "status": "projected", + }, + { + "ref": "SRC-003", + "role": "visual-input", + "locator_or_description": "opaque executable visual reference", + "authorized_scope": "refund error states", + "projected_refs": ["UI-001", "VIS-001"], + "status": "projected", + "uif_required": True, + }, + { + "ref": "SRC-004", + "role": "technical-evidence", + "locator_or_description": "latency measurement report", + "authorized_scope": "technical evidence citation only", + "projected_refs": [], + "status": "retained", + }, + { + "ref": "SRC-005", + "role": "context-only", + "locator_or_description": "competitor overview", + "authorized_scope": "background only", + "projected_refs": [], + "status": "context-only", + }, + ], + }, + "plan": { + "ui_ux_mappings": [ + {"source_ref": "SRC-003", "requirement_ref": "UI-001"}, + {"source_ref": "SRC-003", "requirement_ref": "VIS-001"}, + ], + "uif_mappings": [ + {"source_ref": "SRC-003", "requirement_ref": "UI-001"}, + {"source_ref": "SRC-003", "requirement_ref": "VIS-001"}, + ], + }, } -def minimal_behavior_data_fixtures() -> dict: - return { - "contract_type": "speckit.behavior.data_fixtures.v1", - "fixtures": [ - { - "id": "FIX-BUYER", - "name": "Buyer user", - "entities": ["user"], - "required_states": {"user.role": "buyer"}, - "constraints": [], - "setup_strategy": "factory", - } - ], - } - - -def minimal_behavior_assertions() -> dict: +def minimal_assertions() -> dict: return { "contract_type": "speckit.behavior.assertions.v1", "assertions": [ @@ -307,1974 +167,729 @@ def minimal_behavior_assertions() -> dict: "target": "refund.status", "operator": "equals", "expected": "PENDING", + "intent": "business_state", } ], } -def minimal_exception_behavior_assertions() -> dict: - return minimal_exception_behavior_assertions_with_intent("state_invariant") - - -def minimal_exception_behavior_assertions_with_intent(intent: str) -> dict: - assertions = minimal_behavior_assertions() - assertions["assertions"][0]["intent"] = intent - return assertions - - -class PresetContractTests(unittest.TestCase): - def test_manifest_excludes_implement_override_and_runtime(self) -> None: - manifest = yaml.safe_load(PRESET_PATH.read_text(encoding="utf-8")) +class ManifestAndGovernanceTests(unittest.TestCase): + def test_manifest_declares_existing_files_and_no_implement_override(self) -> None: + manifest = yaml.safe_load(read(MANIFEST)) entries = manifest["provides"]["templates"] - command_entries = [entry for entry in entries if entry["type"] == "command"] - template_entries = [entry for entry in entries if entry["type"] == "template"] - - self.assertEqual(7, len(command_entries)) - self.assertEqual(24, len(template_entries)) - self.assertEqual(31, len(entries)) - self.assertNotIn( - "speckit.implement", - {entry["name"] for entry in command_entries}, - ) - self.assertFalse( - any("speckit.implement" in entry["file"] for entry in entries) - ) - for path in REMOVED_IMPLEMENT_RUNTIME_PATHS: - self.assertFalse(path.exists(), path) + names = {entry["name"] for entry in entries} + + self.assertEqual(len(names), len(entries)) + self.assertEqual(7, sum(entry["type"] == "command" for entry in entries)) + self.assertNotIn("speckit.implement", names) + for entry in entries: + self.assertTrue((ROOT / entry["file"]).exists(), entry) - def test_tasks_end_with_mandatory_code_review_without_runtime_protocol(self) -> None: - tasks = TASKS_COMMAND_PATH.read_text(encoding="utf-8") - self.assertIn("Final Code Review", tasks) - self.assertIn("append the final phase after user-story tasks", tasks) - self.assertIn( - "`boundary`, `interface_contract`, `visual`, `data_side_effect`, " - "`behavior_contract`, `sequence_consistency`, and `asset_binding`", - tasks, - ) for forbidden in ( - "speckit.implement.handoff", - "speckit.implement.receipt", - "handoff-manifest.json", - "Manual Worker Queue", - "Reviewer runtime", + COMMANDS / "speckit.implement.md", + SCHEMAS / "speckit.implement.manifest.v1.schema.json", + SCHEMAS / "speckit.implement.handoff.v2.schema.json", + SCHEMAS / "speckit.implement.receipt.v1.schema.json", + VALIDATORS / "speckit_implement_contract.py", ): - self.assertNotIn(forbidden, tasks) - - def test_current_docs_define_core_implement_ownership(self) -> None: - current_docs = ( - README_PATH.read_text(encoding="utf-8"), - EXTENSION_GOVERNANCE_PATH.read_text(encoding="utf-8"), - AGENTS_PATH.read_text(encoding="utf-8"), - CROSS_AGENT_PROTOCOL_PATH.read_text(encoding="utf-8"), - ) - for document in current_docs: - self.assertIn("Spec Kit core", document) - for forbidden in ( - "speckit.implement.persistent_handoff_orchestration", - "Manual Worker Queue", - "Vertical Planner Agent", - "Worker Agent mode", - "speckit.implement.receipt.v1", - ): - self.assertNotIn(forbidden, document) - - def test_requirement_gate_and_clarify_repair_contract(self) -> None: - checklist = CHECKLIST_COMMAND_PATH.read_text(encoding="utf-8") - clarify = CLARIFY_COMMAND_PATH.read_text(encoding="utf-8") + self.assertFalse(forbidden.exists(), forbidden) - for path in ( - "checklists/requirements.md", - "checklists/behavior.md", - "checklists/ux.md", - "checklists/security.md", - "checklists/nfr.md", - "checklists/visual.md", + def test_manifest_strategies_enforce_negative_ownership(self) -> None: + entries = { + item["name"]: item + for item in yaml.safe_load(read(MANIFEST))["provides"]["templates"] + } + self.assertEqual("replace", entries["speckit.constitution"]["strategy"]) + self.assertEqual("replace", entries["speckit.specify"]["strategy"]) + self.assertEqual("replace", entries["speckit.clarify"]["strategy"]) + for wrapped in ( + "speckit.checklist", + "speckit.plan", + "speckit.tasks", + "speckit.analyze", + "spec-template", + "plan-template", ): - self.assertIn(path, checklist) - self.assertIn("Planning Readiness is aggregated in memory", checklist) - self.assertIn("do not create\n`planning-readiness.md`", checklist) - self.assertIn("Case Coverage Matrix", checklist) - self.assertIn("Visual Fidelity Evidence Matrix", checklist) - self.assertIn("[blocker:provider-evidence] [return:intake]", checklist) - self.assertIn("Recompute generated sections using stable", checklist) - self.assertIn("legacy `checklists/behavior-testability.md`", checklist) - - self.assertIn("[blocker:product-decision]", clarify) - self.assertIn("[blocker:provider-evidence]", clarify) - self.assertIn("preserve its `[return:intake]`", clarify) - self.assertIn("recompute affected requirement gates", clarify) - self.assertIn("never create `planning-readiness.md`", clarify) - - def test_bdd_plan_task_readiness_contract(self) -> None: - plan = PLAN_COMMAND_PATH.read_text(encoding="utf-8") - tasks = TASKS_COMMAND_PATH.read_text(encoding="utf-8") - analyze = ANALYZE_COMMAND_PATH.read_text(encoding="utf-8") - - self.assertIn("Phase 0 Gate Consumption", plan) - self.assertIn("Required case types from `checklists/behavior.md`", plan) - self.assertIn("BDD Plan / Behavior Testability Closeout", plan) - self.assertIn("generate `behavior/behavior-testability.md`", plan) - self.assertIn("Behavior Testability Status: READY", plan) - self.assertIn("Task\nDerivation Matrix", plan) - self.assertIn("UIF may be `N/A` only with a concrete non-UI reason", plan) - self.assertIn("Do not accept the legacy", plan) - - self.assertIn("Behavior Testability Preflight", tasks) - self.assertIn("Behavior Testability Status: READY", tasks) - self.assertIn("stop before writing\n`tasks.md`", tasks) - self.assertIn("Task Derivation Matrix as the primary task input", tasks) - self.assertIn("fixture → validation/test → implementation → evidence", tasks) + self.assertEqual("wrap", entries[wrapped]["strategy"]) + def test_governance_uses_one_authority_and_gate_vocabulary(self) -> None: + documents = (read(GOVERNANCE), read(AGENTS), read(CROSS_AGENT)) + for document in documents: + self.assertIn("Spec Kit core", document) + self.assertNotIn("implementation-manifest.json", document) + self.assertNotIn("worker-result.json", document) + + governance = documents[0] + self.assertIn("Authority And Gate Ownership", governance) + self.assertIn("Requirement Command Independence", governance) + self.assertIn("X0–X4 Planning Artifact Boundaries", governance) + self.assertIn("Tasks As A Pure Plan Mapper", governance) + self.assertIn("Analyze Cross-Command Audit", governance) + self.assertIn("Source Reference Contract", governance) self.assertIn( - "requirement gates -> BDD/UIF intent -> contracts -> behavior testability -> tasks", - analyze, - ) - self.assertIn("`behavior/behavior-testability.md` carries current spec/plan revisions", analyze) - - def test_requirement_and_behavior_testability_templates_contract(self) -> None: - for path in (*REQUIREMENT_TEMPLATE_PATHS.values(), *BEHAVIOR_TEMPLATE_PATHS.values()): - self.assertTrue(path.exists(), path) - - behavior_gate = REQUIREMENT_TEMPLATE_PATHS[ - "requirement-behavior-gate-template" - ].read_text(encoding="utf-8") - nfr_gate = REQUIREMENT_TEMPLATE_PATHS[ - "requirement-nfr-gate-template" - ].read_text(encoding="utf-8") - visual_gate = REQUIREMENT_TEMPLATE_PATHS[ - "requirement-visual-gate-template" - ].read_text(encoding="utf-8") - task_readiness = BEHAVIOR_TEMPLATE_PATHS[ - "behavior-testability-template" - ].read_text(encoding="utf-8") - - self.assertIn("**Stage**: requirements", behavior_gate) - self.assertIn("Case Coverage Matrix", behavior_gate) - self.assertIn("positive, negative, boundary, permission, validation", behavior_gate) - self.assertIn("NFR Coverage Matrix", nfr_gate) - self.assertIn("Not Applicable", nfr_gate) - self.assertIn("Visual Fidelity Evidence Matrix", visual_gate) - self.assertIn("[BLOCKED: PROVIDER_EVIDENCE]", visual_gate) - - self.assertIn("Behavior Testability / Task Readiness", task_readiness) - self.assertIn("**Stage**: plan", task_readiness) - self.assertIn("**Behavior Testability Status**: READY | BLOCKED", task_readiness) - self.assertIn("**Spec Revision**", task_readiness) - self.assertIn("**Plan Revision**", task_readiness) - self.assertIn("Task Derivation Matrix", task_readiness) - self.assertIn("| Case ID | Scenario ID | BDD Ref | UIF Ref | Fixture Ref | Assertion Ref |", task_readiness) - self.assertFalse( - (REPO_ROOT / "templates" / "behavior" / "behavior-testability-checklist.md").exists() + "SRC-* + UI/VIS-* -> ui-ux-design.md -> UIF source_refs + requirement_refs", + governance, ) - def test_public_docs_define_two_stage_ownership(self) -> None: - readme = README_PATH.read_text(encoding="utf-8") - governance = EXTENSION_GOVERNANCE_PATH.read_text(encoding="utf-8") - - self.assertIn("requirement-domain readiness gates", readme) - self.assertIn("behavior/behavior-testability.md", readme) - self.assertIn("Missing provider evidence remains an intake blocker", readme) - - self.assertIn("requirement-readiness gates", governance) - self.assertIn("BDD Plan closeout", governance) - self.assertIn("behavior/behavior-testability.md", governance) - - - def test_plan_command_wrapper_contract(self) -> None: - command = PLAN_COMMAND_PATH.read_text(encoding="utf-8") + def test_source_contract_adds_no_intake_or_transfer_runtime(self) -> None: + for path in ( + ROOT / "templates" / "source-import-manifest.json", + ROOT / "schemas" / "speckit.source-import.v1.schema.json", + ROOT / "validators" / "speckit_source_adapter.py", + ROOT / "commands" / "speckit.intake.md", + ROOT / "scripts" / "source-dispatch.sh", + ): + self.assertFalse(path.exists(), path) - self.assertIn("{CORE_TEMPLATE}", command) - self.assertIn("class-diagram.md", command) - self.assertIn("contracts/sequences.md", command) - self.assertNotIn("test-plan.md", command) - self.assertIn("strategy: wrap", command) - self.assertIn("Generate design artifacts only when the feature requires internal object design or cross-boundary sequence constraints", command) - self.assertIn("Keep `plan.md` as summary/navigation", command) - self.assertIn("validation decisions belong in `research.md`", command) - self.assertIn("executable validation paths belong in `quickstart.md`", command) - self.assertIn("final report must list generated artifacts", command) - self.assertIn("## Architecture-Guided Planning", command) - self.assertIn(".specify/memory/architecture.md", command) - self.assertIn("`research.md` MUST follow established technical decisions and evidence", command) - self.assertIn("`data-model.md` MUST preserve defined concepts", command) - self.assertIn("`contracts/` MUST preserve system boundaries", command) - self.assertIn("`plan.md` and `quickstart.md` MUST carry forward", command) - self.assertIn("return to the Constitution stage", command) - self.assertIn("Do not create a compliance matrix", command) - self.assertIn("Plan Agent Topology", command) - self.assertIn( - "Follow cross-agent protocol profile: `speckit.plan.stage_local_planning`", - command, - ) - self.assertIn("Plan Core Agent", command) - for agent_role in ( - "Behavior Projection Agent", - "Formal Contract Agent", - "Design Artifact Agent", - "Validation Planning Agent", - "Visual Planning Agent", + for document in ( + read(GOVERNANCE), + read(COMMANDS / "speckit.specify.md"), + read(COMMANDS / "speckit.plan.md"), + read(COMMANDS / "speckit.analyze.md"), ): - self.assertIn(agent_role, command) - self.assertIn("Each payload declares assigned scope, allowed reads, allowed sections, and output contract", command) - self.assertIn("rather than subagent conversation history", command) - self.assertNotIn("speckit.tasks", command) - self.assertNotIn("speckit.implement", command) - - def test_plan_template_navigation_contract(self) -> None: - template = PLAN_TEMPLATE_PATH.read_text(encoding="utf-8") - - self.assertIn("{CORE_TEMPLATE}", template) - self.assertIn("## Design Artifacts", template) - self.assertIn("./class-diagram.md", template) - self.assertIn("./contracts/sequences.md", template) - self.assertNotIn("test-plan.md", template) - self.assertIn("./data-model.md", template) - self.assertIn("./contracts/", template) - self.assertIn("./quickstart.md", template) - - def test_plan_visual_substage_enhancement_contract(self) -> None: - command = PLAN_COMMAND_PATH.read_text(encoding="utf-8") - template = PLAN_TEMPLATE_PATH.read_text(encoding="utf-8") - readme = README_PATH.read_text(encoding="utf-8") - governance = EXTENSION_GOVERNANCE_PATH.read_text(encoding="utf-8") + self.assertNotIn("## Intake", document) + +class ConstitutionAndArchitectureTests(unittest.TestCase): + def test_constitution_replacement_preserves_hooks_and_independent_scopes(self) -> None: + command = read(COMMANDS / "speckit.constitution.md") + self.assertIn("strategy: replace", command) + self.assertNotIn("{CORE_TEMPLATE}", command) for term in ( - "Visual Planning Responsibilities", - "visual and IR planning inputs", - "Visual Item ID", - "HTML SSOT refs", - "structured IR refs", - "readiness status", - "unresolved blocker refs", - "do not copy the Visual Fidelity Evidence Matrix into `research.md`", - "rebuild provider evidence matrices", - "Do not define visual validation strategy", - "visual_item_refs", - "viewport_matrix_refs", - "state_matrix_refs", - "visual_proof_refs", - "accepted_exception_refs", - "UI interaction sequence", - "visual state handoff points", - "responsive branch trigger refs", + "## User Input", + "hooks.before_constitution", + "hooks.after_constitution", + "Explicit Input Agreement", + "Independent Write Scopes", + "CONSTITUTION_OUTPUT_READY", + "ARCHITECTURE_OUTPUT_READY", + "intent-first", + "repo-first", + "technical-evidence", + "locator remains opaque", ): self.assertIn(term, command) + def test_constitution_template_owns_sdd_governance_only(self) -> None: + template = read(TEMPLATES / "constitution-template.md") for term in ( - "Visual fidelity navigation", - "Visual/IR source refs and readiness inputs: `./research.md`", - "Visual interaction contracts: `./contracts/uif/` and `./contracts/behavior/`", - "Visual flow sequences: `./contracts/sequences.md`", - "Non-visual acceptance execution: `./quickstart.md`", + "{CORE_TEMPLATE}", + "SDD Workflow Governance", + "Gate Ownership", + "Command Internal Gate", + "Official Core Gate", + "Cross-Command Consistency Gate", + "Intake is external evidence acquisition, not an SDD stage", ): self.assertIn(term, template) + for mapping in ( + "R: Repository / Workspace", + "M: Module / Capability", + "U: Unit / Design Object", + "O: Operation / Detail", + "Planning locks M + U", + ): + self.assertIn(mapping, template) - for document in (readme, governance): - self.assertIn("research.md", document) - self.assertIn("visual/IR", document) - self.assertIn("contracts/sequences.md", document) - - self.assertIn( - "fixed R/M/U/O model: R is Repository / Workspace, M is Module / Capability, U is Unit / Design Object, and O is Operation / Detail", - readme, - ) - self.assertIn("System Boundary -> Conceptual Model", readme) - - def test_constitution_change_scope_granularity_contract(self) -> None: - command = CONSTITUTION_COMMAND_PATH.read_text(encoding="utf-8") - template = CONSTITUTION_TEMPLATE_PATH.read_text(encoding="utf-8") - architecture = ARCHITECTURE_TEMPLATE_PATH.read_text(encoding="utf-8") - - exact_mapping = [ - "R: Repository / Workspace. Environment only; too broad for scoped changes.", - "M: Module / Capability. Hard outer boundary.", - "U: Unit / Design Object. Primary planning boundary.", - "O: Operation / Detail. Execution detail.", - ] - forbidden_mapping_drift = [ - "R, Requirement", - "R: Requirement", - "M, Model", - "M: Model", - "U, User/API Interface", - "U: User/API Interface", - "O, Operations", - "O: Operations", - ] - - for document in (command, template): - self.assertIn("{CORE_TEMPLATE}", document) - self.assertIn("Change Scope Granularity", document) - self.assertIn("R/M/U/O", document) - self.assertIn("Planning locks M + U", document) - for mapping in exact_mapping: - self.assertIn(mapping, document) - for forbidden in forbidden_mapping_drift: - self.assertNotIn(forbidden, document) - - self.assertIn("strategy: wrap", command) - self.assertIn("Spec Kit planning and execution MUST use R/M/U/O scope granularity", template) - self.assertIn("This principle applies from planning onward", template) - self.assertIn("Requirement specification, clarification, and checklist readiness MUST NOT infer M/U/O boundaries", template) - self.assertIn("preserve the Change Scope Granularity principle", command) - self.assertIn("must not remove, weaken, or contradict", command) - self.assertIn("The R/M/U/O letter mapping is fixed and MUST remain exact", command) - self.assertIn("preserves the exact R/M/U/O letter mapping", command) - self.assertIn("CONSTITUTION_RMUO_MAPPING_DRIFT", command) - self.assertIn("CONSTITUTION_TEMPLATE_STATUS_UNCHECKED", command) - self.assertIn("do not report it as missing", command) - self.assertIn("do not treat that as the workflow-preset template being absent", command) - self.assertIn("Constitution Stage Input Agreement", command) - for mode in ("greenfield", "brownfield", "amendment"): - self.assertIn(mode, command) - self.assertIn("No conventional path is mandatory", command) - self.assertIn("candidate sources only until the user authorizes their role", command) - self.assertIn("Existing code is evidence", command) - self.assertIn("ARCH_LEGACY_FORMAT", command) - self.assertIn("Separate Artifact Ownership", command) - self.assertIn(".specify/memory/constitution.md", command) - self.assertIn(".specify/memory/architecture.md", command) - self.assertIn("write exactly one Architecture artifact", command) - self.assertIn("without 4+1", command) - self.assertIn("Technical validation is evidence registration only", command) - self.assertIn("Optional tables may be empty", command) - self.assertIn("Do not create PoC code", command) - self.assertIn("Architecture-Guided Planning", command) - self.assertIn("`/speckit.plan` MUST read", command) - self.assertIn("planning MUST stop and return to the Constitution stage", command) - self.assertIn("The R/M/U/O letter mapping is fixed", template) - self.assertIn("Constitution And Architecture Boundary", template) - self.assertIn("Feature-local planning artifacts may refine Architecture", template) - self.assertIn("Architecture-Guided Planning", template) - self.assertIn("`research.md` MUST follow established technical decisions", template) - self.assertIn("`contracts/` MUST preserve system boundaries", template) - - expected_sections = [ - "Architecture Overview", - "System Boundary", - "Conceptual Model", - "Technical Decisions & Evidence", - "Planning Guardrails & Gaps", - ] + def test_architecture_template_is_pure_technical_ssot(self) -> None: + template = read(TEMPLATES / "architecture-template.md") self.assertEqual( - expected_sections, - re.findall(r"^## (.+)$", architecture, flags=re.MULTILINE), - ) - self.assertIn("**Architecture Goal**", architecture) - self.assertIn("**Authorized Sources**", architecture) - self.assertIn("Does Not Own", architecture) - self.assertIn("Evidence Or Explicit Gap", architecture) - self.assertIn("MUST_VALIDATE", architecture) - self.assertIn("Optional tables may remain empty", architecture) - self.assertNotIn("4+1", architecture) - - def test_change_scope_granularity_stage_references(self) -> None: - plan = PLAN_COMMAND_PATH.read_text(encoding="utf-8") - tasks = TASKS_COMMAND_PATH.read_text(encoding="utf-8") - analyze = ANALYZE_COMMAND_PATH.read_text(encoding="utf-8") - - self.assertIn("Apply the constitution's Change Scope Granularity principle.", plan) - self.assertIn("During planning, lock the change scope to `M + U`", plan) - self.assertIn("Do not lock operation-level implementation details or concrete write paths.", plan) - self.assertNotIn("Architecture SSOT Compliance", plan) - self.assertNotIn("PLANNING_ARCH_SSOT_CONFLICT", plan) - - self.assertIn("Preserve the planned `M + U` scope", tasks) - self.assertIn("Do not generate execution metadata or write-path fields.", tasks) - - self.assertIn("Check that tasks preserve the planned `M + U` scope.", analyze) - self.assertIn("Report missing, widened, or ambiguous scope boundaries as blockers.", analyze) - - self.assertFalse((REPO_ROOT / "commands" / "speckit.implement.md").exists()) - - def test_preplanning_commands_do_not_infer_scope_granularity(self) -> None: - for path in (SPECIFY_COMMAND_PATH, CLARIFY_COMMAND_PATH, CHECKLIST_COMMAND_PATH): - command = path.read_text(encoding="utf-8") - for forbidden in ( - "Change Scope Granularity", - "R/M/U/O", - "M + U", - "U -> concrete paths", - "module/capability plus design object", - "concrete write paths", - "allowed_write_paths", - "context_gaps", - ): - self.assertNotIn(forbidden, command, f"{path} contains {forbidden}") - - def test_tasks_command_wrapper_contract(self) -> None: - tasks = TASKS_COMMAND_PATH.read_text(encoding="utf-8") - - self.assertIn("{CORE_TEMPLATE}", tasks) - self.assertIn("class-diagram.md", tasks) - self.assertIn("contracts/sequences.md", tasks) - self.assertNotIn("test-plan.md", tasks) - self.assertIn("strategy: wrap", tasks) - self.assertIn("implementation, integration, orchestration", tasks) - self.assertIn("existing checklist format and user-story organization", tasks) - self.assertIn("`/speckit.tasks` owns implementation, non-visual validation, and review task definition in `tasks.md`", tasks) - self.assertIn("must not invent validation strategy", tasks) - self.assertIn("change requirements, update contracts, or widen scope", tasks) - self.assertIn("Task-Derivation Subagents", tasks) - self.assertIn("context-reduced multi-subagent derivation model", tasks) - self.assertIn("derivation-time partitioning rule only", tasks) - self.assertIn("do not create implementation transfer artifacts", tasks) - self.assertIn("Tasks Core Agent", tasks) - for agent_role in ( - "Story Task Agent", - "Contract Validation Agent", - "Visual Task Agent", - "Review Task Agent", - ): - self.assertIn(agent_role, tasks) - for payload_field in ( - "`assigned_scope`", - "`allowed_read_paths`", - "`allowed_sections`", - "`output_contract`", - ): - self.assertIn(payload_field, tasks) - self.assertIn("TASK_DERIVATION_CONTEXT_GAP", tasks) - self.assertIn("must not consume full conversation history", tasks) - self.assertIn("Split checklist items only when the validation level, implementation owner, dependency order, evidence source, or review scope differs", tasks) - self.assertIn("Planning Input Taxonomy", tasks) - self.assertIn("validation level taxonomy", tasks) - self.assertIn("fixture strategy and external-system execution mode taxonomy", tasks) - self.assertIn("Evidence binding", tasks) - self.assertIn("Validation Task Derivation", tasks) - self.assertIn("derive the validation level", tasks) - self.assertIn("fixture strategy, external-system execution mode", tasks) - self.assertIn("inline evidence requirement", tasks) - self.assertIn("validation task taxonomy", tasks) - for validation_scope in ( - "`contract_validation`", - "`ui_acceptance`", - "`data_side_effect_validation`", - "`integration_e2e_validation`", + [ + "Architecture Overview", + "System Boundary", + "Conceptual Model", + "Technical Decisions & Evidence", + "Technical Constraints & Gaps", + ], + re.findall(r"^## (.+)$", template, flags=re.MULTILINE), + ) + for prefix in ("BND-", "CON-", "DEC-", "CST-", "GAP-"): + self.assertIn(prefix, template) + for state in ( + "observed-current", + "inferred", + "approved-target", + "migration-gap", ): - self.assertIn(validation_scope, tasks) - self.assertIn("Final Code Review", tasks) - self.assertIn("append the final phase after user-story tasks", tasks) - self.assertIn("final review scope taxonomy", tasks) - self.assertIn("`boundary`, `interface_contract`, `visual`, `data_side_effect`, `behavior_contract`, `sequence_consistency`, and `asset_binding`", tasks) - self.assertIn("Checked sources include", tasks) - self.assertIn("`contracts/uif/`", tasks) - self.assertIn("`spec.md` Client Asset Contract entries", tasks) - self.assertIn("Visual Fidelity Readiness", tasks) - self.assertIn("data side-effect review", tasks) - self.assertIn("field-level update/delete", tasks) - self.assertIn("runtime database writes", tasks) - self.assertIn("boundary review", tasks) - self.assertIn("task scope stays within planned `M + U`", tasks) - self.assertIn("no implementation task changed `spec.md`, `contracts/`, readiness checklists, or Visual Fidelity Readiness", tasks) - self.assertIn("UI consistency review", tasks) - self.assertIn("implemented UI states and viewport behavior", tasks) - self.assertIn("visual/IR traceability refs", tasks) - self.assertIn("UI/visual task taxonomy", tasks) - self.assertIn("story-local task granularity", tasks) - self.assertIn("`visual_setup` -> `visual_implementation` -> `ui_acceptance` or `asset_binding`", tasks) - self.assertIn("Do not create a separate visual lifecycle phase", tasks) - self.assertIn("Visual/UI tasks must name concrete source, test, fixture, configuration, asset paths, and visual/IR traceability refs", tasks) - self.assertIn("report a readiness blocker instead of generating an ambiguous task", tasks) - self.assertIn("Client Asset Contract bindings, variants, and fallback policy", tasks) - self.assertIn("screenshot comparison, visual diff, baseline capture, or final visual review", tasks) - self.assertIn("real-system e2e environment readiness", tasks) - self.assertIn("Review evidence binding", tasks) - self.assertIn("concrete review scope, source artifacts, implementation surfaces, and evidence refs", tasks) - self.assertIn("bounded repair permission", tasks) - self.assertIn("review evidence, bounded repair permission, or a blocker", tasks) - self.assertIn("record a blocker instead of treating the change as implementation work", tasks) - self.assertNotIn("handoff", tasks) - self.assertNotIn("allowed_write_paths", tasks) - self.assertNotIn("receipt", tasks) - self.assertNotIn("speckit.implement.receipt.v1", tasks) - self.assertNotIn("task_type: code_review", tasks) - self.assertNotIn("data_side_effect_review", tasks) - self.assertNotIn("review_conclusion", tasks) - self.assertNotIn("checked_sources", tasks) - self.assertNotIn("consistency_repairs", tasks) - self.assertNotIn("deferred_validation_todos", tasks) - self.assertNotIn("empty arrays or objects indicate no entries", tasks) - self.assertNotIn("task_type: visual_verification", tasks) - self.assertNotIn("`visual_validation`", tasks) - self.assertNotIn("`visual_verification`", tasks) - self.assertNotIn("`final_visual_review`", tasks) - self.assertNotIn("visual regression tests", tasks) - self.assertNotIn("screenshot comparison, state or viewport coverage validation", tasks) - self.assertNotIn("task_type: interface_validation", tasks) - self.assertNotIn("task_type: data_side_effect_validation", tasks) - - def test_behavior_first_command_wrapper_contracts(self) -> None: - specify = SPECIFY_COMMAND_PATH.read_text(encoding="utf-8") - clarify = CLARIFY_COMMAND_PATH.read_text(encoding="utf-8") - checklist = CHECKLIST_COMMAND_PATH.read_text(encoding="utf-8") - - for command in (specify, clarify, checklist): - self.assertIn("{CORE_TEMPLATE}", command) - self.assertIn("strategy: wrap", command) - for command in (specify, clarify): - self.assertIn( - "This wrapper must not redefine core-owned User Input, Pre-Execution Checks, extension hooks, base path resolution, or core file handling.", - command, - ) - self.assertIn("This wrapper extends the core spec-only checklist contract", checklist) - self.assertIn("must not read\n`plan.md` or `tasks.md`", checklist) - self.assertIn("Planning Readiness is aggregated in memory", checklist) - - self.assertIn("Spec-Only Requirement Policy", specify) - self.assertIn("Wrapper Input Additions", specify) - self.assertIn("Wrapper Preflight Additions", specify) - self.assertIn("Wrapper Outline Additions", specify) - self.assertNotIn("## User Input", specify) - self.assertNotIn("## Pre-Execution Checks", specify) - self.assertIn("Preset-added requirement output writes only `spec.md`", specify) - self.assertIn("Product requirements stay in `spec.md`", specify) - self.assertIn("non-functional requirements", specify) - self.assertIn("visual and UI requirements", specify) - self.assertIn("report the `spec.md` sections created or updated", specify) - for term in ( - "Official Style Alignment", - "Focus on WHAT users need and WHY", - "Avoid HOW to implement", - "Limit [NEEDS CLARIFICATION] markers to the highest-impact unresolved product decisions", - "Specification Quality Validation", - "Done When", - ): - self.assertIn(term, specify) - for term in ( - "confirmed external intake facts", - "visual SSOT refs", - "structured IR refs", - "evidence refs", - "does not perform intake", - "call provider tools", - "parse HTML SSOT bundles", - "re-parse structured IR artifacts", - "decide provider source readiness", - "generate provider artifact instances", - "Specification Projection Policy", - "source-backed external intake facts", - "Visual Asset Registry", - "external source artifact inputs", - "visual media inventory", - "license status", - "Visual & UI Specification", - "observable visual and UI requirements", - "write a `Visual & UI Specification` section", - "Not Applicable rationale", - "Every identified visual or UI requirement must be recorded", - "status `Required`, `Not Applicable`, `Unknown`, or `[BLOCKED: PROVIDER_EVIDENCE]`", - "do not silently omit low-evidence visual or UI requirements", - "source refs", - "HTML SSOT refs", - "structured IR refs", - "state and viewport refs", - "Client Asset Contract facts", - "asset source strategy", - "required variants", - "fallback policy", - "blocker status", - "Promote only confirmed product facts and source-backed visual, layout, state, interaction, responsive, accessibility, and acceptance facts", - "Component State Matrix content as Visual & UI Specification requirements, not visual assets", - "observable states, visual feedback, and interaction outcomes", - "missing product decisions become `[NEEDS CLARIFICATION]`", - "missing provider or intake evidence for a feature that depends on that evidence becomes `[BLOCKED: PROVIDER_EVIDENCE]`", - "features that do not depend on HTML SSOT, structured IR, or provider evidence are `Not Applicable`", - "DOM structure", - "CSS selectors", - "component props", - "provider blockers", - "[BLOCKED: PROVIDER_EVIDENCE]", - "keep explicit visual or UI requirement coverage in `spec.md`", - "Functional, non-functional, and visual/UI requirement coverage", - "Do not promote provider evidence gaps into product requirements or `[NEEDS CLARIFICATION]` markers", - "[NEEDS CLARIFICATION]", - "visual SSOT refs preserved", - ): - self.assertIn(term, specify) - self.assertLessEqual(len(specify.splitlines()), 70) + self.assertIn(state, template) for forbidden in ( + "Planning Guardrails", + "Planning Implication", + "Planning Impact", "/speckit.plan", - "/speckit.checklist", - "Visual Fidelity Evidence Matrix", - "`[NEEDS CLARIFICATION]` item requesting a filled Provider Evidence Packet", - "behavior/bdd.draft.feature", - "behavior/behavior-scenarios.draft.json", - "behavior/uif.intent.json", - "behavior/data-fixtures.intent.json", - "behavior/open-questions.json", - "formal behavior contracts", - "interface schemas", - "validation commands", - "task plans", - "design artifacts", - "local asset path", - "asset hash", - "allowed_write_paths", - "Design intake input", - "Provider Evidence Packet readiness", - "Requirement Merge Report", - "raw get_metadata", - "Stage 0:", - "Stage 1:", - "Stage 2:", - "Stage 3:", - "Observed from provider design", - ): - self.assertNotIn(forbidden, specify) - self.assertNotIn("contracts/bdd/", specify) - self.assertNotIn("contracts/uif/", specify) - - self.assertIn("Spec-Only Clarification Policy", clarify) - self.assertIn("Wrapper Input Additions", clarify) - self.assertIn("Wrapper Preflight Additions", clarify) - self.assertIn("Wrapper Outline Additions", clarify) - self.assertNotIn("## User Input", clarify) - self.assertNotIn("## Pre-Execution Checks", clarify) - self.assertIn("Use `spec.md` as the clarification source", clarify) - self.assertIn("Do not read or update behavior draft artifacts", clarify) - self.assertIn("Product requirements stay in `spec.md`", clarify) - self.assertIn("non-functional requirement assumptions", clarify) - self.assertIn("visual/UI requirement coverage status", clarify) - self.assertIn("only after user-provided answers", clarify) - self.assertIn("Design Requirement Clarification Strategy", clarify) - self.assertIn("external intake evidence", clarify) - self.assertIn("visual SSOT refs", clarify) - self.assertIn("evidence-derived gaps", clarify) - self.assertIn("visual/UI coverage status `Unknown`", clarify) - self.assertIn("[NEEDS CLARIFICATION]", clarify) - self.assertIn("Do not call provider tools", clarify) - self.assertIn("Do not re-extract design facts", clarify) - self.assertIn("re-parse provider design links", clarify) - self.assertIn("parse HTML SSOT bundles", clarify) - self.assertIn("re-parse structured IR artifacts", clarify) - self.assertIn("External intake owns source capture and provider readiness", clarify) - self.assertIn("confirmed evidence-backed requirements and trace refs", clarify) - self.assertIn("Do not ask the user to fix provider extraction artifacts", clarify) - self.assertIn("Ask at most 5 high-impact questions", clarify) - self.assertIn("Present EXACTLY ONE question at a time", clarify) - self.assertIn("Do NOT output them all at once", clarify) - self.assertIn("Never reveal future queued questions", clarify) - self.assertIn("Maximum of 5 total questions", clarify) - self.assertIn("Format recommendations as `**Recommended:** Option [X] - `", clarify) - self.assertIn("Keep the rationale short and decision-focused", clarify) - self.assertNotIn("", clarify) - self.assertIn("Suggested", clarify) - self.assertIn("2-5", clarify) - self.assertIn("<=5 words", clarify) - self.assertIn("yes", clarify) - self.assertIn("recommended", clarify) - self.assertIn("suggested", clarify) - self.assertIn("Save `spec.md` after each accepted answer", clarify) - self.assertIn("## Clarifications", clarify) - self.assertIn("### Session YYYY-MM-DD", clarify) - self.assertIn("Q:", clarify) - self.assertIn("A:", clarify) - self.assertIn("provider-specific clarification document", clarify) - self.assertIn("Validation after each write", clarify) - self.assertIn("after EACH write plus final pass", clarify) - self.assertIn("Total asked", clarify) - self.assertIn("no contradictory earlier statement remains", clarify) - self.assertIn("recompute affected requirement gates", clarify) - self.assertIn("Replace generated status and blocker", clarify) - self.assertNotIn("FEATURE_DIR/checklists/requirements.md", clarify) - self.assertNotIn("Only toggle the `[ ]`/`[x]` marker", clarify) - self.assertIn("hooks.before_clarify", clarify) - self.assertIn("hooks.after_clarify", clarify) - self.assertIn("EXECUTE_COMMAND", clarify) - self.assertIn("Completion Report", clarify) - self.assertIn("Visual/UI coverage status: Required, Not Applicable, Unknown, or `[BLOCKED: PROVIDER_EVIDENCE]`", clarify) - self.assertIn("visual fidelity scope", clarify) - self.assertIn("missing UI states", clarify) - self.assertIn("responsive behavior", clarify) - self.assertIn("component reuse constraints", clarify) - self.assertIn("data semantics", clarify) - self.assertIn("acceptance evidence", clarify) - self.assertIn("accepted exception approval flow", clarify) - self.assertIn("write confirmed answers back into `spec.md`", clarify) - self.assertIn("Update affected visual/UI coverage status", clarify) - self.assertIn("Any answered visual/UI coverage status was updated in `spec.md`", clarify) - self.assertIn("Do not generate visual restoration checklists", clarify) - for forbidden in ( - "behavior/bdd.draft.feature", - "behavior/behavior-scenarios.draft.json", - "behavior/uif.intent.json", - "behavior/data-fixtures.intent.json", - "behavior/open-questions.json", - "use_provider_tool", - "get_design_context", - "fetch provider design URL", - "read provider design URL", - "Provider Evidence Packet", - "Design Requirement" + " Intake", - "Inferred from Structure", - "update checklists/behavior-testability.md", + "/speckit.tasks", ): - self.assertNotIn(forbidden, clarify) + self.assertNotIn(forbidden, template) - for term in ( - "Multi-Domain Requirement Gate", - "Use `$ARGUMENTS` only to prioritize requirement-quality focus", - "Stage/Domain/Gate/Applicability/Status/Spec Revision", - "The legacy `checklists/behavior-testability.md` is not an input or output", - "Behavior Requirement Gate", - "Case Coverage Matrix", - "positive, negative, boundary, permission, validation, and", - "state_conflict", - "stable Case IDs", - "Scenario IDs and `case_coverage_blockers` remain `/speckit.plan` outputs", - "This gate checks whether behavior requirements are projectable", - "NFR Requirement Gate", - "verifiable product-level criteria", - "Do not require", - "technical designs or invent architecture", - "Recompute generated sections using stable CHK/CASE/NFR/VIS IDs", - "Never append", - "duplicate status blocks, stale blockers, or repeated matrix rows", - ): - self.assertIn(term, checklist) - for term in ( - "Visual Requirement Gate", - "Visual & UI Specification", - "Apply the gate when `spec.md` contains a Visual & UI Specification", - "Every visual item is", - "`[BLOCKED: PROVIDER_EVIDENCE]`", - "Unknown product semantics become `[blocker:product-decision]`", - "Missing provider proof remains `[blocker:provider-evidence] [return:intake]`", - "Provider blockers must not be converted into clarify questions", - "visual SSOT refs", - "external intake refs", - "structured IR refs", - "Visual Fidelity Evidence Matrix", - "source traceability", - "readiness input", - "Responsive visual requirements block PASS only when required source-backed", - "Do not call provider tools, rebuild intake evidence", + +class RequirementCommandTests(unittest.TestCase): + def test_full_spectrum_spec_carrier_is_optional_and_stable(self) -> None: + template = read(TEMPLATES / "spec-template.md") + for heading in ( + "Functional Requirements", + "Non-Functional Requirements", + "UX Journeys and Interaction Expectations", + "UI Surfaces and States", + "Visual Requirements and Sources", + "Security and Privacy", + "Data and Integration Constraints", + "Dependencies and Boundaries", + "Assumptions", + "Exclusions", + "Source References", + "Unresolved Product Decisions", + "Source Evidence Blockers", + "Clarifications", ): - self.assertIn(term, checklist) - for term in ( - "| Visual Item ID | Source `spec.md` section | Requirement Status | Fidelity Scope | Screenshot Level | Evidence Refs | Visual Proof Required | Blocking Item ID | Exception Rule |", - "Screenshot evidence level", - "declared visual proof required", - "proof level sufficiency", - "screenshot sufficiency", - "raw metadata completeness", - "metadata index completeness proof", - "node inventory parity", - "blocker lint errors", - "Responsive visual readiness must record viewport-specific evidence or set Gate Status: BLOCKED", + self.assertIn(heading, template) + for prefix in ("FR-", "NFR-", "UX-", "UI-", "VIS-"): + self.assertIn(prefix, template) + self.assertIn("content carrier, not a completeness checklist", template) + + def test_source_reference_template_has_one_source_neutral_shape(self) -> None: + template = read(TEMPLATES / "spec-template.md") + for column in ( + "SRC ref", + "Role", + "Opaque locator / description", + "Revision / identity", + "Authorized scope / facts", + "Projected requirement refs", + "Status / blocker", ): - self.assertNotIn(term, checklist) - self.assertIn("Planning Readiness aggregate", checklist) - self.assertIn("provider-evidence\nblockers separately", checklist) - return - self.assertIn("PASS", checklist) - self.assertIn("BLOCKED", checklist) - self.assertIn("product-decision blockers, and provider-evidence", checklist) - - def test_behavior_first_plan_and_tasks_awareness_contract(self) -> None: - plan = PLAN_COMMAND_PATH.read_text(encoding="utf-8") - tasks = TASKS_COMMAND_PATH.read_text(encoding="utf-8") - template = PLAN_TEMPLATE_PATH.read_text(encoding="utf-8") - - for term in ( - "behavior/bdd.draft.feature", - "behavior/uif.intent.json", - "behavior/data-fixtures.intent.json", - "contracts/bdd/", - "contracts/uif/", - "contracts/behavior/", - "formal behavior contracts", - "must formalize", - "N/A or blocker", - "research.md", - "test level", - "fixture strategy", - "mock/external-system strategy", - "BehaviorScenarioInstance", - "DataFixture", - "UIFPath", - "FeedbackView", - "BehaviorAssertion", - "Required case types from `checklists/behavior.md`", - "must project into", - "`behavior/behavior-scenarios.draft.json`", - "must formalize into", - "`contracts/behavior/scenario-instances.json`", - "Do not continue with only positive", - "scenarios when Required case types exist", - "Map each Required Case ID to a", - "Scenario ID or `case_coverage_blockers` entry", - "write `case_coverage_blockers`", - "record `N/A or blocker` with", - "the Case ID, missing planning input", + self.assertIn(column, template) + for role in ( + "requirement-input", + "visual-input", + "technical-evidence", + "context-only", ): - self.assertIn(term, plan) - - self.assertIn("BDD Plan closeout", plan) - self.assertIn("behavior/behavior-testability.md", plan) - return + self.assertIn(role, template) + self.assertIn("broad source without a\nsafe feature slice", template) + def test_specify_has_no_core_checklist_side_effect(self) -> None: + command = read(COMMANDS / "speckit.specify.md") for term in ( - "Phase 0 Gate Consumption", - "Phase 0 Behavior Projection", - "read-only Planning Readiness preflight", - "before core research or design work", - "visual fidelity scope", - "source refs", - "HTML SSOT refs", - "structured IR refs", - "screenshot refs", - "visual proof refs", - "visual SSOT refs", - "Visual Fidelity Evidence Matrix `Requirement Status`", - "Carry forward only visual rows with status `Required` or an accepted exception rule", - "Rows with status `Unknown` or `[BLOCKED: PROVIDER_EVIDENCE]` must already have blocked checklist PASS", - "report-only/no-write upstream gate failure", - "Do not project `Not Applicable` rows into visual planning outputs", - "behavior/behavior-scenarios.draft.json", - "report-only/no-write failure", - "Do not create or update partial behavior artifacts", - "Do not discover new requirement problems", - "Do not ask clarification questions", - "Do not modify `spec.md`", - "upstream gate failure", - "Return to `/speckit.checklist` or `/speckit.clarify`", + "strategy: replace", + "hooks.before_specify", + "hooks.after_specify", + "SPECIFY_FEATURE_DIRECTORY", + ".specify/feature.json", + "Authorized Source Input Contract", + "Full-Spectrum Projection", + "feature-local WHAT/WHY SSOT", + "Do not compute completeness", ): - self.assertIn(term, plan) - - self.assertNotIn("empty, or records only an upstream gate failure", plan) - self.assertNotIn("behavior/open-questions.json", plan) - self.assertNotIn("test-plan.md", plan) - self.assertIn("BDD Plan closeout", plan) - self.assertIn("behavior/behavior-testability.md", plan) - return - + self.assertIn(term, command) + self.assertIn("checklists/requirements.md", command) + self.assertIn("MUST NOT create, read, evaluate, or modify", command) + self.assertNotIn("{CORE_TEMPLATE}", command) + + def test_source_commands_keep_external_actions_outside_preset(self) -> None: + specify = read(COMMANDS / "speckit.specify.md") + clarify = read(COMMANDS / "speckit.clarify.md") + checklist = read(COMMANDS / "speckit.checklist.md") for term in ( - "contracts/bdd/", - "contracts/uif/", - "contracts/behavior/", - "`spec.md` visual acceptance requirements", - "`checklists/visual.md` Visual Fidelity Readiness", - "HTML SSOT refs", - "structured IR refs", - "screenshot refs", - "visual proof refs", - "visual SSOT refs", - "external evidence refs", - "visual fidelity requirements", - "test-first", - "existing checklist format and user-story organization", - "For each BehaviorScenarioInstance", - "fixture task", - "BDD/E2E or contract test task", - "implementation task", - "verification evidence task", - "Expected UIF contract step with type `user_event`", - "Expected UIF contract step with type `api_call`", - "UI/visual task taxonomy", - "`ui_acceptance`", - "UI acceptance task", - "viewport/state requirement refs", - "required state and viewport coverage", - "visual/IR traceability ref", - "For each quickstart validation path", - "derive the validation level", - "fixture strategy, external-system execution mode", - "inline evidence requirement", - "Planning Input Taxonomy", - "`/speckit.tasks` owns implementation, non-visual validation, and review task definition in `tasks.md`", - "must not invent validation strategy", - "visual validation work", - "validation level taxonomy", - "fixture strategy and external-system execution mode taxonomy", - "Evidence binding", - "validation task taxonomy", - "`contract_validation`", - "`ui_acceptance`", - "`data_side_effect_validation`", - "`integration_e2e_validation`", - "Client Asset Contract", - "derive asset preparation, binding, implementation, and non-visual acceptance tasks", - "Missing required client visual assets are readiness blockers", - "Use Visual Fidelity Readiness as the only visual planning readiness source", - "`Requirement Status` as the visual task input filter", - "Generate UI implementation, asset binding, and non-visual acceptance tasks only for rows with status `Required` or `Required` plus an accepted exception", - "tasks for accepted exceptions must cite the exception rule", - "Do not generate implementation, validation, verification, evidence, asset binding, UI acceptance, or review tasks for `Not Applicable`, `Unknown`, or `[BLOCKED: PROVIDER_EVIDENCE]` rows", - "Route `Unknown` rows back to `/speckit.clarify`", - "route `[BLOCKED: PROVIDER_EVIDENCE]` rows to the external intake extension", - "`/speckit.tasks` must not discover visual requirements, repair evidence, re-parse provider artifacts, or define visual validation strategy", - "only decomposes visual specifications that already passed the readiness gate", - "Do not create a second readiness rule", - "HTML SSOT refs", - "structured IR refs", - "external intake artifacts", - "Do not generate execution metadata or write-path fields.", - "Missing Required case coverage is a coverage blocker, not silently skipped work", - "`negative`, `boundary`, `permission`, `validation`, or `state_conflict`", - "For each BehaviorScenarioInstance with type", - "derive fixture, contract or BDD test, implementation, and verification evidence tasks", - "UI consistency review", - "implemented UI states and viewport behavior", - "UI/visual task taxonomy", - "story-local task granularity", - "`visual_setup` -> `visual_implementation` -> `ui_acceptance` or `asset_binding`", - "`asset_binding`", - "`visual_setup`, `visual_implementation`, `ui_acceptance`, and `asset_binding` are the only visual/UI task types", - "without screenshot comparison, visual diff, baseline capture, or final visual review", - "empty/error/loading/disabled/hover/focus states", - "license or authorization refs", - "Do not create a separate visual lifecycle phase", - "Visual/UI tasks must name concrete source, test, fixture, configuration, asset paths, and visual/IR traceability refs", - "report a readiness blocker instead of generating an ambiguous task", - "Do not generate visual validation, screenshot comparison, visual diff, baseline capture, final visual review, or visual tasks for rows with `Requirement Status` `Not Applicable`, `Unknown`, or `[BLOCKED: PROVIDER_EVIDENCE]`", - "Client Asset Contract bindings, variants, and fallback policy", - "Review evidence binding", - "bounded repair permission", - "final review scope taxonomy", - "`boundary`, `interface_contract`, `visual`, `data_side_effect`, `behavior_contract`, `sequence_consistency`, and `asset_binding`", - "boundary review", - "no implementation task changed `spec.md`, `contracts/`, readiness checklists, or Visual Fidelity Readiness", + "dereference or execute a locator", + "import manifest", + "provider-specific\nschema", + "Intake is not an SDD stage", ): - self.assertIn(term, tasks) - - self.assertNotIn("task_type: visual_verification", tasks) - self.assertNotIn("`visual_validation`", tasks) - self.assertNotIn("`visual_verification`", tasks) - self.assertNotIn("`final_visual_review`", tasks) - self.assertNotIn("visual regression tests", tasks) - self.assertNotIn("task_type: interface_validation", tasks) - self.assertNotIn("task_type: data_side_effect_validation", tasks) - self.assertNotIn("test-plan.md", tasks) - - self.assertIn("./behavior/bdd.draft.feature", template) - self.assertIn("./contracts/bdd/", template) - self.assertIn("./contracts/uif/", template) - self.assertIn("./contracts/behavior/", template) - - self.assertNotIn("tests/contracts/", implement) - self.assertIn("Read contracts/ for API specifications and test requirements", implement) - self.assertIn("Requirement Status", cross_agent) - self.assertIn("visual shard candidates must come only from `tasks.md` visual/UI task types", cross_agent) - self.assertIn("only `Required` or `Required` plus an accepted exception is executable", cross_agent) - self.assertIn("do not create visual shards for `Not Applicable`, `Unknown`, or `[BLOCKED: PROVIDER_EVIDENCE]`", cross_agent) - self.assertIn("route `Unknown` back to `/speckit.clarify`", cross_agent) - self.assertIn("`[BLOCKED: PROVIDER_EVIDENCE]` to the external intake extension", cross_agent) - self.assertIn("missing required HTML SSOT refs", cross_agent) - self.assertIn("missing structured IR refs", cross_agent) - self.assertNotIn("final_visual_review tasks", cross_agent) - self.assertNotIn("Visual Review Worker", cross_agent) - self.assertNotIn("`visual_validation`", cross_agent) - self.assertNotIn("`visual_verification`", cross_agent) - self.assertNotIn("`final_visual_review`", cross_agent) - self.assertIn("planned `U` design object", cross_agent) - self.assertIn("specific source, test, fixture, configuration, or receipt paths", cross_agent) - - def test_bdd_formalization_strengthens_reasoning_without_traceability_system(self) -> None: - plan = PLAN_COMMAND_PATH.read_text(encoding="utf-8") - bdd_contract_template = BEHAVIOR_TEMPLATE_PATHS[ - "behavior-bdd-contract-template" - ].read_text(encoding="utf-8") + self.assertIn(term, specify) + self.assertIn("external\nwrite-back or synchronization", clarify) + self.assertIn("preserve the originating `SRC-*` provenance", clarify) + self.assertIn("MUST NOT dereference a locator", checklist) + def test_clarify_writes_only_spec_and_uses_cross_domain_priority(self) -> None: + command = read(COMMANDS / "speckit.clarify.md") for term in ( - "When formalizing BDD Draft into `contracts/bdd/*.feature`", - "Preserve scenario intent and business outcome from the draft.", - "Convert ambiguous Given steps into formal fixture, actor, state, permission, or start-view conditions.", - "Convert When steps into formal user events, request cases, or system triggers aligned with UIF/API contracts.", - "Convert Then steps into formal feedback, response, business state, or assertion expectations.", - "If a step cannot be formalized without inventing information, record `N/A or blocker` instead of guessing.", - "Do not introduce independent traceability mechanisms for BDD formalization.", + "strategy: replace", + "Run `{SCRIPT}` once", + "Read and write only `FEATURE_SPEC`", + "impact × uncertainty", + "exactly one at a time", + "## Clarifications", + "Local Validation After Every Write", ): - self.assertIn(term, plan) + self.assertIn(term, command) + self.assertNotIn("{CORE_TEMPLATE}", command) + self.assertNotIn("checklists/requirements.md", command) - for forbidden in ( - "@SCN-", - "trace table", - "coverage matrix", - "reverse index", + def test_checklist_generates_unanswered_questions_only(self) -> None: + command = read(COMMANDS / "speckit.checklist.md") + self.assertIn("{CORE_TEMPLATE}", command) + self.assertIn("question-form", command) + self.assertIn("Generated items remain unchecked questions", command) + self.assertIn("MUST NOT modify `spec.md`", command) + for path in (TEMPLATES / "requirements").glob("*.md"): + template = read(path) + self.assertRegex(template, r"- \[ \] CHK-") + self.assertNotIn("PASS | BLOCKED", template) + self.assertNotIn("Readiness Matrix", template) + + +class PlanContractTests(unittest.TestCase): + def test_plan_nests_x0_x4_in_core_without_new_cross_command_gate(self) -> None: + command = read(COMMANDS / "speckit.plan.md") + self.assertIn("{CORE_TEMPLATE}", command) + for marker in ( + "Core setup + plan-template materialization -> X0", + "Core Phase 0 Outline & Research", + "Core Phase 1 Design & Contracts", + "Core post-design Constitution re-check", + "Preset closeout before completion report", ): - self.assertNotIn(forbidden, plan) - self.assertNotIn(forbidden, bdd_contract_template) - - def test_analyze_command_owns_vertical_consistency_contract(self) -> None: - analyze = ANALYZE_COMMAND_PATH.read_text(encoding="utf-8") - - self.assertIn("{CORE_TEMPLATE}", analyze) - self.assertIn("strategy: wrap", analyze) - self.assertIn("vertical consistency", analyze) - self.assertIn( - "requirement gates -> BDD/UIF intent -> contracts -> behavior testability -> tasks", - analyze, - ) - self.assertIn("spec.md user stories have BDD coverage", analyze) - self.assertIn("BDD Given steps map to fixtures", analyze) - self.assertIn("BDD When steps map to UIF events or API requests", analyze) - self.assertIn("BDD Then steps map to feedback or behavior assertions", analyze) - self.assertIn("behavior/uif.intent.json is formalized into contracts/uif/*.expected.json", analyze) - self.assertIn("behavior drafts exist but formal contracts are missing", analyze) - self.assertIn("source draft and missing planning input", analyze) - self.assertNotIn("behavior/open-questions.json", analyze) - self.assertIn("N/A or blocker", analyze) - self.assertIn("UIF API calls exist in contracts/api/", analyze) - self.assertIn("behavior contracts cover scenarios, fixtures, and assertions", analyze) - self.assertIn("tasks.md covers BDD, UIF, API, fixtures, and quickstart validation paths", analyze) - self.assertIn("case coverage", analyze) - self.assertIn("Required case types in `checklists/behavior.md`", analyze) - self.assertIn("`behavior/behavior-testability.md` carries current spec/plan revisions", analyze) - self.assertIn("case types are either covered or have `N/A or blocker` evidence", analyze) - self.assertIn( - "failure scenarios declare error code, failure feedback, and state invariant, rollback, or compensation assertion", - analyze, - ) - self.assertIn("quickstart validation paths cover Required failure scenarios", analyze) - self.assertIn("Build a one-pass artifact inventory before deep reading", analyze) - self.assertIn("Use stable IDs as the primary consistency surface", analyze) - self.assertIn("CASE-", analyze) - self.assertIn("SCN-", analyze) - self.assertIn("UIF-", analyze) - self.assertIn("FIX-", analyze) - self.assertIn("AST-", analyze) - self.assertIn("BLK-", analyze) - self.assertIn("Read surrounding prose only when a required ID, source section, or blocker explanation is missing or ambiguous", analyze) - self.assertIn("Stop expanding a branch after the first blocker that proves the downstream link cannot be closed", analyze) - self.assertNotIn("uif.actual.json", analyze) - self.assertNotIn("uif.diff.json", analyze) - self.assertNotIn("Actual UIF", analyze) - - def test_actual_uif_artifacts_are_not_part_of_preset_contract(self) -> None: - paths = [ - README_PATH, - SPECIFY_COMMAND_PATH, - CLARIFY_COMMAND_PATH, - CHECKLIST_COMMAND_PATH, - ANALYZE_COMMAND_PATH, - PLAN_COMMAND_PATH, - TASKS_COMMAND_PATH, - PRESET_PATH, - ] - forbidden_terms = [ - "Expected UIF vs Actual UIF", - "Actual UIF", - "uif.actual.json", - "uif.diff.json", - "from implementation", - "implementation-derived UIF", - "static analysis tooling", - ] - for path in paths: - document = path.read_text(encoding="utf-8") - for term in forbidden_terms: - self.assertNotIn(term, document, f"{path} contains {term}") - - def test_behavior_first_templates_exist_and_are_decoupled(self) -> None: - for path in BEHAVIOR_TEMPLATE_PATHS.values(): - self.assertTrue(path.exists(), path) - - self.assertIn("Feature:", BEHAVIOR_TEMPLATE_PATHS["behavior-bdd-draft-template"].read_text()) - self.assertIn("Feature:", BEHAVIOR_TEMPLATE_PATHS["behavior-bdd-contract-template"].read_text()) - behavior_testability = BEHAVIOR_TEMPLATE_PATHS[ - "behavior-testability-template" - ].read_text(encoding="utf-8") - self.assertIn("**Stage**: plan", behavior_testability) - self.assertIn("**Behavior Testability Status**: READY | BLOCKED", behavior_testability) - self.assertIn("| Case ID | Scenario ID | BDD Ref | UIF Ref | Fixture Ref | Assertion Ref |", behavior_testability) - self.assertIn("Task Derivation Matrix", behavior_testability) - for path in REQUIREMENT_TEMPLATE_PATHS.values(): - self.assertTrue(path.exists(), path) - behavior_gate = REQUIREMENT_TEMPLATE_PATHS[ - "requirement-behavior-gate-template" - ].read_text(encoding="utf-8") - nfr_gate = REQUIREMENT_TEMPLATE_PATHS["requirement-nfr-gate-template"].read_text( - encoding="utf-8" - ) - visual_gate = REQUIREMENT_TEMPLATE_PATHS[ - "requirement-visual-gate-template" - ].read_text(encoding="utf-8") - self.assertIn("Case Coverage Matrix", behavior_gate) - self.assertIn("`Required|Not Applicable|Unknown`", behavior_gate) - self.assertIn("NFR Coverage Matrix", nfr_gate) - self.assertIn("Visual Fidelity Evidence Matrix", visual_gate) - self.assertIn("**Status**: PASS | BLOCKED", visual_gate) - self.assertFalse( - (REPO_ROOT / "templates" / "behavior" / "behavior-testability-checklist.md").exists() - ) - self.assertFalse((REPO_ROOT / "templates" / "behavior" / "open-questions.json").exists()) - self.assertFalse( - ( - REPO_ROOT - / "schemas" - / "speckit.behavior.open-questions.v1.schema.json" - ).exists() - ) - - for template_name in ( - "behavior-scenarios-draft-template", - "behavior-uif-intent-template", - "behavior-data-fixtures-intent-template", - "behavior-uif-expected-template", - "behavior-scenario-instances-template", - "behavior-data-fixtures-template", - "behavior-assertions-template", + self.assertIn(marker, command) + for gate in ( + "X0_CONTROL_READY", + "X1_DECISIONS_READY", + "X2A_DESIGN_READY", + "X2B_UIUX_READY", + "X2C_TEST_DESIGN_READY", + "X3_VALIDATION_PATHS_READY", + "PLAN_OUTPUT_READY", ): - self.assertIn( - "contract_type", - BEHAVIOR_TEMPLATE_PATHS[template_name].read_text(encoding="utf-8"), - ) - - scenario_instances_template = BEHAVIOR_TEMPLATE_PATHS[ - "behavior-scenario-instances-template" - ].read_text(encoding="utf-8") - self.assertIn('"case_coverage_blockers"', scenario_instances_template) - self.assertIn('"type": "permission"', scenario_instances_template) - self.assertIn('"case_kind": "permission"', scenario_instances_template) - self.assertIn('"error_code"', scenario_instances_template) - self.assertIn('"expected_feedback"', scenario_instances_template) - - assertions_template = BEHAVIOR_TEMPLATE_PATHS["behavior-assertions-template"].read_text( - encoding="utf-8" - ) - self.assertIn('"intent": "state_invariant"', assertions_template) - - def test_visual_fidelity_screenshot_evidence_gate_contract(self) -> None: - command = CHECKLIST_COMMAND_PATH.read_text(encoding="utf-8") - template = REQUIREMENT_TEMPLATE_PATHS[ - "requirement-visual-gate-template" - ].read_text(encoding="utf-8") + self.assertIn(gate, command) + self.assertIn("Plan never amends Architecture", command) + self.assertIn("validates Plan outputs only", command) + self.assertNotIn("Architecture Conformance Gate", command) + def test_plan_control_template_has_lanes_navigation_and_closeout(self) -> None: + template = read(TEMPLATES / "plan-template.md") for term in ( - "Write visual readiness and the only Visual Fidelity Evidence Matrix to", - "`checklists/visual.md`", - "[blocker:product-decision]", - "[blocker:provider-evidence] [return:intake]", - "Provider blockers must not be converted into clarify questions", - "Do not call provider tools, rebuild intake evidence, parse provider or HTML", - "define screenshot comparison, visual diff, baseline capture, or", - "final visual review", - "Planning Readiness aggregate", - ): - self.assertIn(term, command) - for term in ( - "| Visual Item ID | Source `spec.md` section | Requirement Status | Fidelity Scope | Screenshot Level | Evidence Refs | Visual Proof Required | Blocking Item ID | Exception Rule |", - "raw metadata completeness", - "metadata index completeness proof", - "node inventory parity", - "blocker lint errors", - "Responsive visual readiness must record viewport-specific evidence or set Gate Status: BLOCKED", - ): - self.assertNotIn(term, command) - - for term in ( - "**Stage**: requirements", - "**Domain**: visual", - "**Gate**: planning-readiness", - "**Applicability**: APPLICABLE | NOT_APPLICABLE", - "**Status**: PASS | BLOCKED", - "Visual Fidelity Evidence Matrix", - "single visual planning-readiness record", - "Provider Evidence Dependency", - "Visual SSOT Refs", - "HTML SSOT Refs", - "Structured IR Refs", - "Other Evidence Refs", - "Readiness Input", - "Accepted Exception Refs", - "Unknown items become product-decision blockers", - "evidence gaps remain intake blockers and are never converted to clarification", - "Do not call provider tools, re-parse provider artifacts, define screenshot", - "comparison, visual diff, baseline capture, or final visual review", - "## Blocking Items", + "X0 Feature Plan Control", + "Active Lane Matrix", + "Cross-Lane Dependency Register", + "Internal Gate Summary", + "Artifact Navigation", + "Design Object Derivation Index", + "X4 Closeout Summary", + "PLAN_OUTPUT_READY", ): self.assertIn(term, template) - self.assertEqual( - len( - re.findall( - r"^## Visual Fidelity Evidence Matrix$", - template, - flags=re.MULTILINE, - ) - ), - 1, - ) - self.assertEqual( - template.count( - "| Visual Item ID | Source `spec.md` section | Requirement Status | Provider Evidence Dependency | Visual SSOT Refs | HTML SSOT Refs | Structured IR Refs | Other Evidence Refs | Readiness Input | Blocking Item ID | Accepted Exception Refs |" - ), - 1, - ) - self.assertEqual( - template.count( - "This is the single visual planning-readiness record" - ), - 1, - ) - for forbidden in ( - "Screenshot evidence level", - "visual proof refs", - "L0|L1|L2|L3", - "declared visual proof required", - "proof level sufficiency", - "screenshot sufficiency", - "Missing screenshot evidence sets Gate Status: BLOCKED", - "High-fidelity requirements without L3 screenshot evidence set Gate Status: BLOCKED", - "Pixel-perfect requirements without L3 screenshot evidence set Gate Status: BLOCKED", - "L3 Visual Baseline", - "Responsive visual readiness must record viewport-specific evidence or set Gate Status: BLOCKED", - "Responsive visual readiness records viewport-specific evidence or sets Gate Status: BLOCKED", - "Screenshot Coverage Matrix", - "Visual Proof Matrix", - "Visual Restoration Checklist", + self.assertIn("Repository Topology", template) + self.assertIn("No task IDs, exact per-task paths", template) + + def test_plan_artifact_templates_have_non_overlapping_ownership(self) -> None: + class_template = read(TEMPLATES / "class-diagram-template.md") + sequence_template = read(TEMPLATES / "sequences-template.md") + ui_template = read(TEMPLATES / "ui-ux-design-template.md") + quickstart = read(TEMPLATES / "quickstart-template.md") + readiness = read(TEMPLATES / "test-readiness-template.md") + + self.assertIn("Do not copy complete domain fields", class_template) + self.assertIn("Rollback / compensation", sequence_template) + self.assertIn("X4 UI/UX Delivery Readiness", ui_template) + self.assertIn("Pixel delivery/review is owned here", ui_template) + self.assertIn("SRC + UI/VIS refs", ui_template) + self.assertIn("does not dereference", ui_template) + self.assertIn("### VAL-001", quickstart) + self.assertIn("Cleanup/reset", quickstart) + self.assertIn("Every required `TC-*` has exactly one row", readiness) + self.assertIn("MUST NOT appear", readiness) + + def test_removed_behavior_parents_are_not_packaged(self) -> None: + for path in ( + TEMPLATES / "behavior" / "uif-intent.json", + TEMPLATES / "behavior" / "data-fixtures-intent.json", + TEMPLATES / "behavior" / "behavior-testability.md", + SCHEMAS / "speckit.behavior.uif.intent.v1.schema.json", + SCHEMAS / "speckit.behavior.data-fixtures.intent.v1.schema.json", ): - self.assertNotIn(forbidden, template) - - for document in (command, template): - lowered = document.lower() - for forbidden in FORBIDDEN_VISUAL_COMPAT_TERMS: - self.assertNotIn(forbidden, lowered) - - - - - def test_behavior_first_schema_contracts_accept_minimal_examples(self) -> None: - examples = { - "speckit.behavior.scenarios.draft.v1": minimal_behavior_scenarios_draft(), - "speckit.behavior.uif.intent.v1": minimal_uif_intent(), - "speckit.behavior.data_fixtures.intent.v1": minimal_data_fixtures_intent(), - "speckit.behavior.uif.expected.v1": minimal_uif_expected(), - "speckit.behavior.scenario_instances.v1": minimal_behavior_scenario_instances(), - "speckit.behavior.data_fixtures.v1": minimal_behavior_data_fixtures(), - "speckit.behavior.assertions.v1": minimal_behavior_assertions(), - } - - for contract_type, path in BEHAVIOR_SCHEMA_PATHS.items(): - schema = json.loads(path.read_text(encoding="utf-8")) - self.assertEqual("object", schema["type"]) - self.assertIn("required", schema) - self.assertIn("properties", schema) - self.assertEqual(contract_type, schema["properties"]["contract_type"]["const"]) - Draft202012Validator(schema).validate(examples[contract_type]) - - def test_behavior_draft_schema_rejects_empty_given_when_then(self) -> None: - schema = json.loads( - BEHAVIOR_SCHEMA_PATHS["speckit.behavior.scenarios.draft.v1"].read_text( - encoding="utf-8" - ) - ) - - for field in ("given", "when", "then"): - with self.subTest(field=field): - draft = minimal_behavior_scenarios_draft() - draft["scenarios"][0][field] = [] - - with self.assertRaises(ValidationError): - Draft202012Validator(schema).validate(draft) - - def test_behavior_scenario_instances_schema_rejects_empty_contract_refs(self) -> None: - schema = json.loads( - BEHAVIOR_SCHEMA_PATHS["speckit.behavior.scenario_instances.v1"].read_text( - encoding="utf-8" - ) - ) - - for field in ("fixture_ids", "assertion_ids"): - with self.subTest(field=field): - instances = minimal_behavior_scenario_instances() - instances["scenarios"][0][field] = [] - - with self.assertRaises(ValidationError): - Draft202012Validator(schema).validate(instances) - - def test_behavior_scenario_instances_schema_accepts_structured_exception_cases(self) -> None: - schema = json.loads( - BEHAVIOR_SCHEMA_PATHS["speckit.behavior.scenario_instances.v1"].read_text( - encoding="utf-8" - ) - ) - - for scenario_type in ("negative", "boundary", "permission", "validation", "state_conflict"): - with self.subTest(scenario_type=scenario_type): - Draft202012Validator(schema).validate( - minimal_exception_behavior_scenario_instances( - scenario_type=scenario_type, - ) - ) - - def test_behavior_scenario_instances_schema_rejects_exception_case_shells(self) -> None: - schema = json.loads( - BEHAVIOR_SCHEMA_PATHS["speckit.behavior.scenario_instances.v1"].read_text( - encoding="utf-8" - ) - ) - invalid_mutations = [ - ("case_kind", lambda scenario: scenario["request_case"].pop("case_kind")), - ("trigger", lambda scenario: scenario["request_case"].pop("trigger")), - ("expected_response", lambda scenario: scenario.update({"expected_response": {}})), - ("error_code", lambda scenario: scenario["expected_response"].pop("error_code")), - ("expected_feedback", lambda scenario: scenario.update({"expected_feedback": {}})), - ("feedback_type", lambda scenario: scenario["expected_feedback"].pop("type")), - ("feedback_message", lambda scenario: scenario["expected_feedback"].pop("message")), - ] - - for label, mutate in invalid_mutations: - with self.subTest(label=label): - instances = minimal_exception_behavior_scenario_instances() - mutate(instances["scenarios"][0]) - - with self.assertRaises(ValidationError): - Draft202012Validator(schema).validate(instances) - - def test_behavior_scenario_instances_schema_rejects_mismatched_exception_case_kind(self) -> None: - schema = json.loads( - BEHAVIOR_SCHEMA_PATHS["speckit.behavior.scenario_instances.v1"].read_text( - encoding="utf-8" - ) - ) - instances = minimal_exception_behavior_scenario_instances(scenario_type="permission") - instances["scenarios"][0]["request_case"]["case_kind"] = "validation" + self.assertFalse(path.exists(), path) - with self.assertRaises(ValidationError): - Draft202012Validator(schema).validate(instances) - def test_behavior_scenario_instances_schema_accepts_case_coverage_blockers(self) -> None: - schema = json.loads( - BEHAVIOR_SCHEMA_PATHS["speckit.behavior.scenario_instances.v1"].read_text( - encoding="utf-8" - ) - ) - instances = minimal_behavior_scenario_instances() - instances["case_coverage_blockers"] = [ +class SchemaAndValidatorTests(unittest.TestCase): + def test_all_json_artifacts_parse_and_all_schemas_are_valid(self) -> None: + for path in [*SCHEMAS.glob("*.json"), *TEMPLATES.rglob("*.json")]: + load_json(path) + for path in SCHEMAS.glob("*.json"): + Draft202012Validator.check_schema(load_json(path)) + + def test_test_conditions_schema_accepts_generalized_non_bdd_contract(self) -> None: + payload = minimal_test_conditions() + schema = load_json(SCHEMAS / "speckit.test.conditions.v1.schema.json") + Draft202012Validator(schema).validate(payload) + validate_test_conditions(payload) + + condition = payload["conditions"][0] + condition["types"] = ["security", "performance", "recovery"] + condition["techniques"] = ["boundary_value", "state_transition"] + condition["oracle"] = {"kind": "threshold", "expected": 500} + validate_test_conditions(payload) + + def test_test_validator_rejects_pixel_scope_and_incomplete_dimensions(self) -> None: + payload = minimal_test_conditions() + payload["conditions"][0]["evidence_requirement"] = "screenshot diff baseline" + with self.assertRaisesRegex(ValueError, "pixel-level visual scope"): + validate_test_conditions(payload) + + payload = minimal_test_conditions() + payload["conditions"][0]["levels"] = [] + with self.assertRaisesRegex(ValueError, "non-empty levels"): + validate_test_conditions(payload) + + def test_bdd_requires_child_and_readiness_has_one_row_per_required_tc(self) -> None: + payload = minimal_test_conditions(technique="BDD") + with self.assertRaisesRegex(ValueError, "no BDD child"): + validate_test_conditions(payload) + validate_test_conditions(payload, available_bdd_tc_refs={"TC-001"}) + validate_test_readiness(payload, [{"tc_id": "TC-001"}]) + with self.assertRaisesRegex(ValueError, "TC mismatch"): + validate_test_readiness(payload, []) + + def test_expected_uif_reference_fields_match_schema(self) -> None: + validator = Draft202012Validator( + load_json(SCHEMAS / "speckit.behavior.uif.expected.v1.schema.json") + ) + uif = minimal_uif() + validator.validate(uif) + validate_behavior_contract_bundle( { - "id": "BLK-001", - "case_id": "CASE-002", - "case_type": "validation", - "source": "spec.md#user-story-1", - "reason": "Validation rule is marked Unknown in checklist.", - "downstream_contract_path": "contracts/behavior/scenario-instances.json", - } - ] - - Draft202012Validator(schema).validate(instances) - - def test_behavior_scenario_instances_schema_rejects_incomplete_case_coverage_blockers(self) -> None: - schema = json.loads( - BEHAVIOR_SCHEMA_PATHS["speckit.behavior.scenario_instances.v1"].read_text( - encoding="utf-8" - ) - ) - required_fields = ( - "id", - "case_id", - "case_type", - "source", - "reason", - "downstream_contract_path", - ) - - for field in required_fields: - with self.subTest(field=field): - instances = minimal_behavior_scenario_instances() - blocker = { - "id": "BLK-001", - "case_id": "CASE-002", - "case_type": "validation", - "source": "spec.md#user-story-1", - "reason": "Validation rule is marked Unknown in checklist.", - "downstream_contract_path": "contracts/behavior/scenario-instances.json", - } - blocker.pop(field) - instances["case_coverage_blockers"] = [blocker] - - with self.assertRaises(ValidationError): - Draft202012Validator(schema).validate(instances) - - def test_behavior_scenario_instances_schema_accepts_success_boundary_case(self) -> None: - schema = json.loads( - BEHAVIOR_SCHEMA_PATHS["speckit.behavior.scenario_instances.v1"].read_text( - encoding="utf-8" - ) - ) - instances = minimal_exception_behavior_scenario_instances(scenario_type="boundary") - scenario = instances["scenarios"][0] - scenario["request_case"]["outcome"] = "success" - scenario["expected_response"] = {"business_code": "ACCEPTED_AT_LIMIT"} - scenario["expected_feedback"] = {"message": "Limit accepted"} - - Draft202012Validator(schema).validate(instances) - - def test_behavior_scenario_instances_schema_rejects_boundary_failure_without_error(self) -> None: - schema = json.loads( - BEHAVIOR_SCHEMA_PATHS["speckit.behavior.scenario_instances.v1"].read_text( - encoding="utf-8" - ) - ) - instances = minimal_exception_behavior_scenario_instances(scenario_type="boundary") - scenario = instances["scenarios"][0] - scenario["request_case"]["outcome"] = "failure" - scenario["expected_response"] = {"status": 422} - scenario["expected_feedback"] = {"message": "Limit exceeded"} - + "scenarios": [ + { + "id": "SCN-UI-001", + "type": "positive", + "test_condition_refs": ["TC-001"], + "fixture_ids": ["FIX-001"], + "uif_path_id": "UIF-001", + "assertion_ids": ["AST-001"], + } + ] + }, + {"fixtures": [{"id": "FIX-001"}]}, + minimal_assertions(), + [uif], + {"TC-001"}, + ) + + missing_source_mapping = minimal_uif() + del missing_source_mapping["source_refs"] with self.assertRaises(ValidationError): - Draft202012Validator(schema).validate(instances) - - def test_behavior_assertions_schema_accepts_exception_assertion_intent(self) -> None: - schema = json.loads( - BEHAVIOR_SCHEMA_PATHS["speckit.behavior.assertions.v1"].read_text( - encoding="utf-8" - ) - ) - - Draft202012Validator(schema).validate(minimal_exception_behavior_assertions()) - - def test_expected_uif_schema_rejects_underspecified_typed_steps(self) -> None: - schema = json.loads( - BEHAVIOR_SCHEMA_PATHS["speckit.behavior.uif.expected.v1"].read_text( - encoding="utf-8" - ) - ) - - underspecified_steps = [ - {"type": "api_call"}, - {"type": "local_route"}, - {"type": "user_event"}, - ] - for step in underspecified_steps: - with self.subTest(step_type=step["type"]): - uif = minimal_uif_expected() - uif["steps"] = [step] - - with self.assertRaises(ValidationError): - Draft202012Validator(schema).validate(uif) - - def test_behavior_draft_validator_rejects_fixture_for_unknown_scenario(self) -> None: - fixtures = minimal_data_fixtures_intent() - fixtures["fixtures"][0]["required_for"] = ["SCN-404"] - - with self.assertRaises(ValueError): - validate_behavior_draft_contract( - minimal_behavior_scenarios_draft(), - fixtures, - ) - - def test_behavior_draft_validator_rejects_empty_given_when_then(self) -> None: - for field in ("given", "when", "then"): - with self.subTest(field=field): - draft = minimal_behavior_scenarios_draft() - draft["scenarios"][0][field] = [] - - with self.assertRaisesRegex(ValueError, field): - validate_behavior_draft_contract( - draft, - minimal_data_fixtures_intent(), - ) - - def test_behavior_draft_validator_accepts_valid_cross_fields(self) -> None: - validate_behavior_draft_contract( - minimal_behavior_scenarios_draft(), - minimal_data_fixtures_intent(), - ) - - def test_behavior_contract_validator_rejects_missing_fixture_reference(self) -> None: - instances = minimal_behavior_scenario_instances() - instances["scenarios"][0]["fixture_ids"] = ["FIX-MISSING"] - - with self.assertRaises(ValueError): - validate_behavior_contract_bundle( - instances, - minimal_behavior_data_fixtures(), - minimal_behavior_assertions(), - [minimal_uif_expected()], - ) - - def test_behavior_contract_validator_rejects_empty_contract_refs(self) -> None: - for field in ("fixture_ids", "assertion_ids"): - with self.subTest(field=field): - instances = minimal_behavior_scenario_instances() - instances["scenarios"][0][field] = [] - - with self.assertRaisesRegex(ValueError, field): - validate_behavior_contract_bundle( - instances, - minimal_behavior_data_fixtures(), - minimal_behavior_assertions(), - [minimal_uif_expected()], - ) - - def test_behavior_contract_validator_rejects_underspecified_uif_steps(self) -> None: - for step in ( - {"type": "api_call"}, - {"type": "local_route"}, - {"type": "user_event"}, - ): - with self.subTest(step_type=step["type"]): - uif = minimal_uif_expected() - uif["steps"] = [step] - - with self.assertRaises(ValueError): - validate_behavior_contract_bundle( - minimal_behavior_scenario_instances(), - minimal_behavior_data_fixtures(), - minimal_behavior_assertions(), - [uif], - ) - - def test_behavior_contract_validator_rejects_exception_case_shells(self) -> None: - invalid_mutations = [ - ("case_kind", lambda scenario: scenario["request_case"].pop("case_kind")), - ("trigger", lambda scenario: scenario["request_case"].pop("trigger")), - ("expected_response", lambda scenario: scenario.update({"expected_response": {}})), - ("error_code", lambda scenario: scenario["expected_response"].pop("error_code")), - ("expected_feedback", lambda scenario: scenario.update({"expected_feedback": {}})), - ("feedback_type", lambda scenario: scenario["expected_feedback"].pop("type")), - ("feedback_message", lambda scenario: scenario["expected_feedback"].pop("message")), - ] - - for label, mutate in invalid_mutations: - with self.subTest(label=label): - instances = minimal_exception_behavior_scenario_instances() - mutate(instances["scenarios"][0]) - - with self.assertRaisesRegex(ValueError, label): - validate_behavior_contract_bundle( - instances, - minimal_behavior_data_fixtures(), - minimal_exception_behavior_assertions(), - [minimal_uif_expected()], - ) - - def test_behavior_contract_validator_rejects_exception_without_state_or_rollback_assertion(self) -> None: - with self.assertRaisesRegex( - ValueError, - "state_invariant_rollback_or_compensation_assertion", - ): + validator.validate(missing_source_mapping) + with self.assertRaisesRegex(ValueError, "non-empty source_refs"): validate_behavior_contract_bundle( - minimal_exception_behavior_scenario_instances(), - minimal_behavior_data_fixtures(), - minimal_behavior_assertions(), - [minimal_uif_expected()], + { + "scenarios": [ + { + "id": "SCN-UI-001", + "type": "positive", + "test_condition_refs": ["TC-001"], + "fixture_ids": ["FIX-001"], + "uif_path_id": "UIF-001", + "assertion_ids": ["AST-001"], + } + ] + }, + {"fixtures": [{"id": "FIX-001"}]}, + minimal_assertions(), + [missing_source_mapping], + {"TC-001"}, ) - def test_behavior_contract_validator_rejects_mismatched_exception_case_kind(self) -> None: - instances = minimal_exception_behavior_scenario_instances(scenario_type="permission") - instances["scenarios"][0]["request_case"]["case_kind"] = "validation" - - with self.assertRaisesRegex(ValueError, "case_kind"): - validate_behavior_contract_bundle( - instances, - minimal_behavior_data_fixtures(), - minimal_exception_behavior_assertions(), - [minimal_uif_expected()], + def test_scenario_bundle_supports_non_ui_and_fixture_free_acceptance(self) -> None: + instances = { + "contract_type": "speckit.behavior.scenario_instances.v1", + "scenarios": [ + { + "id": "SCN-NONUI-001", + "title": "Reject duplicate command", + "type": "positive", + "test_condition_refs": ["TC-001"], + "non_ui_rationale": "Command-only contract.", + "no_fixture_rationale": "Input is self-contained.", + "request_case": {"id": "REQ-001"}, + "expected_response": {"error_code": "DUPLICATE"}, + "assertion_ids": ["AST-001"], + } + ], + } + Draft202012Validator( + load_json( + SCHEMAS / "speckit.behavior.scenario-instances.v1.schema.json" ) - - def test_behavior_contract_validator_accepts_structured_exception_cases(self) -> None: - for scenario_type in ("negative", "boundary", "permission", "validation", "state_conflict"): - with self.subTest(scenario_type=scenario_type): - validate_behavior_contract_bundle( - minimal_exception_behavior_scenario_instances( - scenario_type=scenario_type, - ), - minimal_behavior_data_fixtures(), - minimal_exception_behavior_assertions(), - [minimal_uif_expected()], - ) - - def test_behavior_contract_validator_accepts_rollback_and_compensation_assertions(self) -> None: - for intent in ("rollback", "compensation"): - with self.subTest(intent=intent): - validate_behavior_contract_bundle( - minimal_exception_behavior_scenario_instances(), - minimal_behavior_data_fixtures(), - minimal_exception_behavior_assertions_with_intent(intent), - [minimal_uif_expected()], - ) - - def test_behavior_contract_validator_accepts_success_boundary_case(self) -> None: - instances = minimal_exception_behavior_scenario_instances(scenario_type="boundary") - scenario = instances["scenarios"][0] - scenario["request_case"]["outcome"] = "success" - scenario["expected_response"] = {"business_code": "ACCEPTED_AT_LIMIT"} - scenario["expected_feedback"] = {"message": "Limit accepted"} - + ).validate(instances) validate_behavior_contract_bundle( instances, - minimal_behavior_data_fixtures(), - minimal_behavior_assertions(), - [minimal_uif_expected()], - ) - - def test_behavior_contract_validator_rejects_boundary_failure_without_error(self) -> None: - instances = minimal_exception_behavior_scenario_instances(scenario_type="boundary") - scenario = instances["scenarios"][0] - scenario["request_case"]["outcome"] = "failure" - scenario["expected_response"] = {"status": 422} - scenario["expected_feedback"] = {"message": "Limit exceeded"} - - with self.assertRaisesRegex(ValueError, "error_code"): + {"fixtures": []}, + minimal_assertions(), + [], + {"TC-001"}, + ) + + def test_scenario_bundle_rejects_unknown_child_refs(self) -> None: + instances = { + "scenarios": [ + { + "id": "SCN-001", + "test_condition_refs": ["TC-UNKNOWN"], + "non_ui_rationale": "No UI.", + "no_fixture_rationale": "No setup.", + "assertion_ids": ["AST-UNKNOWN"], + "type": "positive", + } + ] + } + with self.assertRaisesRegex(ValueError, "unknown assertion"): validate_behavior_contract_bundle( instances, - minimal_behavior_data_fixtures(), - minimal_exception_behavior_assertions(), - [minimal_uif_expected()], + {"fixtures": []}, + minimal_assertions(), + [], + {"TC-001"}, ) - def test_behavior_case_coverage_validator_rejects_missing_required_case(self) -> None: - with self.assertRaisesRegex(ValueError, "Required case"): - validate_behavior_case_coverage( - minimal_case_coverage(), - minimal_behavior_scenarios_draft(), - minimal_behavior_scenario_instances(), - "T001 implement SCN-001", - "Validate SCN-001", - ) - def test_behavior_case_coverage_validator_rejects_empty_matrix(self) -> None: - with self.assertRaisesRegex(ValueError, "case_coverage"): - validate_behavior_case_coverage( - {}, - minimal_behavior_scenarios_draft(), - minimal_behavior_scenario_instances(), - "T001 implement SCN-001", - "Validate SCN-001", - ) - - def test_behavior_case_coverage_validator_requires_tasks_and_quickstart_evidence(self) -> None: - with self.assertRaisesRegex(ValueError, "tasks.md"): - validate_behavior_case_coverage( - minimal_case_coverage(), - minimal_behavior_scenarios_draft( - scenario_type="permission", - scenario_id="SCN-ERR-001", - ), - minimal_exception_behavior_scenario_instances(), - "T001 implement SCN-001", - "Validate SCN-ERR-001", - ) - - with self.assertRaisesRegex(ValueError, "quickstart.md"): - validate_behavior_case_coverage( - minimal_case_coverage(), - minimal_behavior_scenarios_draft( - scenario_type="permission", - scenario_id="SCN-ERR-001", - ), - minimal_exception_behavior_scenario_instances(), - "T001 implement SCN-ERR-001", - "Validate SCN-001", - ) +class TasksAndAnalyzeTests(unittest.TestCase): + def test_tasks_is_pure_plan_mapper_and_required_tc_overrides_core_optional(self) -> None: + command = read(COMMANDS / "speckit.tasks.md") + for stage in ( + "T0 — Plan Handoff Preflight", + "T1 — Concrete Path Binding", + "T2 — Dependency Graph", + "T3 — Story / Capability Derivation", + "T4 — Functional Validation And Evidence", + "T5 — Final Code Review", + ): + self.assertIn(stage, command) + self.assertIn("Tasks MUST NOT drop", command) + self.assertIn("tests are optional", command) + self.assertIn("PLAN_OUTPUT_INCOMPLETE", command) + self.assertIn("Exact paths are a Tasks output", command) + self.assertIn("Spec/Checklist as direct\nstrategy inputs", command) + + def test_tasks_forbids_pixel_work_and_keeps_final_review_last(self) -> None: + command = read(COMMANDS / "speckit.tasks.md") + self.assertIn("Never generate", command) + for forbidden_task in ( + "visual_acceptance", + "pixel_fidelity_review", + "screenshot comparison", + "visual diff", + "baseline capture", + "visual restoration", + "final visual review", + "pixel-level layout/style assertions", + "screenshot-based evidence", + "source dereference", + "external-source validation", + "provider-tool", + ): + self.assertIn(forbidden_task, command) + self.assertIn("append the final phase after user-story tasks", command) + self.assertIn("no phase may follow it", command) + self.assertIn("MUST NOT\njudge rendered visual fidelity", command) - def test_behavior_case_coverage_validator_accepts_closed_required_case(self) -> None: - validate_behavior_case_coverage( - minimal_case_coverage(), - minimal_behavior_scenarios_draft( - scenario_type="permission", - scenario_id="SCN-ERR-001", - ), - minimal_exception_behavior_scenario_instances(), - "T001 implement SCN-ERR-001 and AST-001", - "Validate SCN-ERR-001 through quickstart path", - ) + def test_analyze_is_read_only_and_owns_all_cross_command_chains(self) -> None: + command = read(COMMANDS / "speckit.analyze.md") + for term in ( + "Analyze exclusively owns Cross-Command Consistency Gates", + "MUST NOT modify or repair any artifact", + "One-Pass Inventory", + "Use stable IDs as the primary consistency surface", + "first blocker", + "Constitution To Spec / Plan", + "Architecture To Plan Products", + "Spec To X0/X1/X2/X3/X4", + "Plan To Tasks", + "Implementation Readiness: PASS | BLOCKED", + "Confirm no files were written", + ): + self.assertIn(term, command) + self.assertIn("repo-first planning within Architecture constraints", command) + self.assertIn("Do not classify Plan as Greenfield/Brownfield", command) - def test_behavior_case_coverage_validator_accepts_formal_blocker_for_required_case(self) -> None: - instances = minimal_behavior_scenario_instances() - instances["case_coverage_blockers"] = [ + def test_source_shapes_converge_without_external_coupling(self) -> None: + self.assertEqual( + [], + audit_source_reference_contract(source_contract_snapshot()), + ) + + def test_source_audit_reports_role_slice_and_projection_failures(self) -> None: + snapshot = source_contract_snapshot() + snapshot["spec"]["sources"].extend( + [ + { + "ref": "SRC-006", + "role": "provider-input", + "locator_or_description": "provider-specific packet", + "provider_node_id": "node-42", + "authorized_scope": "refund feature", + "projected_refs": [], + "status": "retained", + }, + { + "ref": "SRC-007", + "role": "context-only", + "locator_or_description": "background note", + "authorized_scope": "background only", + "projected_refs": ["FR-001"], + "status": "projected", + }, + { + "ref": "SRC-008", + "role": "requirement-input", + "locator_or_description": "broad roadmap", + "authorized_scope": "entire roadmap", + "broad": True, + "projected_refs": [], + "status": "projected", + }, + ] + ) + codes = { + finding["code"] + for finding in audit_source_reference_contract(snapshot) + } + self.assertTrue( { - "id": "BLK-001", - "case_id": "CASE-002", - "case_type": "validation", - "source": "spec.md#user-story-1", - "reason": "Validation rule is still Unknown in checklist.", - "downstream_contract_path": "contracts/behavior/scenario-instances.json", - } - ] - - validate_behavior_case_coverage( - minimal_case_coverage_with_blocker(), - minimal_behavior_scenarios_draft(), - instances, - "T001 blocked by BLK-001", - "BLK-001 blocks quickstart validation", - ) - - def test_behavior_case_coverage_validator_requires_blocker_downstream_evidence(self) -> None: - instances = minimal_behavior_scenario_instances() - instances["case_coverage_blockers"] = [ + "SRC_ROLE_INVALID", + "SRC_FIELD_INVALID", + "SRC_ROLE_PROJECTION_INVALID", + "SRC_FEATURE_SLICE_MISSING", + "SRC_ORPHAN", + }.issubset(codes) + ) + + def test_source_audit_reports_local_integrity_and_x2b_uif_gaps(self) -> None: + snapshot = source_contract_snapshot() + snapshot["spec"]["sources"].append( + dict(snapshot["spec"]["sources"][0]) + ) + snapshot["spec"]["sources"][2]["projected_refs"].append("UI-404") + snapshot["spec"]["sources"][4]["status"] = "contradictory" + snapshot["referenced_source_refs"] = ["SRC-404"] + snapshot["plan"]["ui_ux_mappings"] = [] + snapshot["plan"]["uif_mappings"] = [] + + codes = { + finding["code"] + for finding in audit_source_reference_contract(snapshot) + } + self.assertTrue( { - "id": "BLK-001", - "case_id": "CASE-002", - "case_type": "validation", - "source": "spec.md#user-story-1", - "reason": "Validation rule is still Unknown in checklist.", - "downstream_contract_path": "contracts/behavior/scenario-instances.json", - } - ] - - with self.assertRaisesRegex(ValueError, "tasks.md"): - validate_behavior_case_coverage( - minimal_case_coverage_with_blocker(), - minimal_behavior_scenarios_draft(), - instances, - "T001 implement SCN-001", - "BLK-001 blocks quickstart validation", - ) - - with self.assertRaisesRegex(ValueError, "quickstart.md"): - validate_behavior_case_coverage( - minimal_case_coverage_with_blocker(), - minimal_behavior_scenarios_draft(), - instances, - "T001 blocked by BLK-001", - "Validate SCN-001", - ) - - def test_behavior_case_coverage_validator_rejects_blocker_source_mismatch(self) -> None: - instances = minimal_behavior_scenario_instances() - instances["case_coverage_blockers"] = [ + "SRC_REF_MISSING", + "SRC_REF_DUPLICATE", + "SRC_PROJECTED_REF_MISSING", + "SRC_STATUS_CONTRADICTORY", + "SRC_UIUX_MAPPING_MISSING", + "SRC_UIF_MAPPING_MISSING", + }.issubset(codes) + ) + + def test_audit_reports_deterministic_vertical_breaks(self) -> None: + snapshot = { + "architecture": { + "revision": "ARCH-2", + "decisions": ["DEC-001"], + "concepts": ["CON-001"], + "boundaries": ["BND-001"], + "constraints": ["CST-001"], + "gaps": ["GAP-001"], + }, + "plan": { + "architecture_revision": "ARCH-1", + "research_refs": [], + "data_model_refs": [], + "contract_refs": [], + "plan_constraint_refs": [], + "blocker_refs": [], + "design_objects": ["PaymentCoordinator"], + "required_test_conditions": ["TC-001"], + "mu_scope": "checkout/PaymentCoordinator", + }, + "tasks": { + "design_object_refs": [], + "test_condition_refs": [], + "mu_scope": "repository/*", + }, + } + self.assertEqual( + [ + "ARCH_REVISION_STALE", + "ARCH_DECISION_OMITTED", + "ARCH_CONCEPT_OMITTED", + "ARCH_BOUNDARY_OMITTED", + "ARCH_CONSTRAINT_OMITTED", + "ARCH_GAP_OMITTED", + "PLAN_TASK_MAPPING_MISSING", + "PLAN_REQUIRED_TEST_TASK_MISSING", + "MU_SCOPE_WIDENED", + ], + [ + finding["code"] + for finding in audit_cross_command_consistency(snapshot) + ], + ) + + def test_issue_24_obligations_map_to_stable_analyze_codes(self) -> None: + required = { + "idempotency_key", + "provider_task_binding", + "provider_lock", + "retry_context", + "recovery_decision", + "readiness_lifecycle", + } + findings = audit_data_model_obligations(required, {"provider_lock"}) + self.assertEqual( { - "id": "BLK-001", - "case_id": "CASE-002", - "case_type": "validation", - "source": "spec.md#different-story", - "reason": "Validation rule is still Unknown in checklist.", - "downstream_contract_path": "contracts/behavior/scenario-instances.json", - } - ] - - with self.assertRaisesRegex(ValueError, "source"): - validate_behavior_case_coverage( - minimal_case_coverage_with_blocker(), - minimal_behavior_scenarios_draft(), - instances, - "T001 blocked by BLK-001", - "BLK-001 blocks quickstart validation", - ) - - def test_behavior_contract_validator_accepts_valid_cross_fields(self) -> None: - validate_behavior_contract_bundle( - minimal_behavior_scenario_instances(), - minimal_behavior_data_fixtures(), - minimal_behavior_assertions(), - [minimal_uif_expected()], - ) - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - def test_agents_references_extension_governance(self) -> None: - agents = AGENTS_PATH.read_text(encoding="utf-8") - - self.assertIn("docs/extension-governance.md", agents) - self.assertIn("Extension Governance", agents) - - def _workflow_on(self, workflow: dict) -> dict: - return workflow.get("on") or workflow.get(True) or {} - - def test_github_actions_contract_workflow(self) -> None: - workflow_path = REPO_ROOT / ".github" / "workflows" / "ci.yml" - if not workflow_path.exists(): - self.skipTest("source repository workflow file is not packaged in the preset") - workflow = yaml.safe_load(workflow_path.read_text(encoding="utf-8")) + "ARCH_DATA_MODEL_IDEMPOTENCY_MISSING", + "ARCH_PROVIDER_BINDING_MISSING", + "ARCH_RETRY_CONTEXT_MISSING", + "ARCH_RECOVERY_DECISION_MISSING", + "ARCH_LIFECYCLE_PROJECTION_MISSING", + }, + {finding["code"] for finding in findings}, + ) + + def test_closed_cross_command_chain_has_no_findings(self) -> None: + snapshot = { + "architecture": { + "revision": "ARCH-2", + "decisions": ["DEC-001"], + "concepts": ["CON-001"], + "boundaries": ["BND-001"], + "constraints": ["CST-001"], + "gaps": ["GAP-001"], + }, + "plan": { + "architecture_revision": "ARCH-2", + "research_refs": ["DEC-001"], + "data_model_refs": ["CON-001"], + "contract_refs": ["BND-001"], + "plan_constraint_refs": ["CST-001"], + "blocker_refs": ["GAP-001"], + "design_objects": ["PaymentCoordinator"], + "required_test_conditions": ["TC-001"], + "mu_scope": "checkout/PaymentCoordinator", + }, + "tasks": { + "design_object_refs": ["PaymentCoordinator"], + "test_condition_refs": ["TC-001"], + "mu_scope": "checkout/PaymentCoordinator", + }, + } + self.assertEqual([], audit_cross_command_consistency(snapshot)) - self.assertEqual("Preset Contract", workflow["name"]) - self.assertEqual({"contents": "read"}, workflow["permissions"]) - triggers = self._workflow_on(workflow) - self.assertIn("pull_request", triggers) - self.assertEqual(["main"], triggers["push"]["branches"]) - self.assertIn("workflow_dispatch", triggers) - contract_job = workflow["jobs"]["contract"] - self.assertEqual("ubuntu-latest", contract_job["runs-on"]) - self.assertEqual( - ["3.10", "3.13"], - contract_job["strategy"]["matrix"]["python-version"], - ) - workflow_text = workflow_path.read_text(encoding="utf-8") - self.assertIn("python3 -m pip install -r requirements-dev.txt", workflow_text) - self.assertIn("python3 -m unittest tests/test_preset_contract.py", workflow_text) - - def test_github_actions_artifact_release_and_integration_pr_workflow(self) -> None: - workflow_path = REPO_ROOT / ".github" / "workflows" / "preset-artifact.yml" - if not workflow_path.exists(): - self.skipTest("source repository workflow file is not packaged in the preset") - workflow = yaml.safe_load(workflow_path.read_text(encoding="utf-8")) - - self.assertEqual("Preset Artifact", workflow["name"]) - self.assertEqual({"contents": "write"}, workflow["permissions"]) - triggers = self._workflow_on(workflow) - self.assertEqual(["v*"], triggers["push"]["tags"]) - self.assertIn("workflow_dispatch", triggers) - inputs = triggers["workflow_dispatch"]["inputs"] - self.assertIn("version", inputs) - self.assertIn("spec_kit_ref", inputs) - self.assertIn("create_integration_pr", inputs) - - workflow_text = workflow_path.read_text(encoding="utf-8") - required_terms = [ - "spec-kit-workflow-preset-v${VERSION}.zip", - "NEXT_PATCH_VERSION", - "python3 -m unittest tests/test_preset_contract.py", - "python3 -m venv \"${GITHUB_WORKSPACE}/.venv-specify-smoke\"", - "echo \"${GITHUB_WORKSPACE}/.venv-specify-smoke/bin\" >> \"${GITHUB_PATH}\"", - 'PATH="${GITHUB_WORKSPACE}/.venv-specify-smoke/bin:${PATH}"', - 'project_dir="$(mktemp -d "${RUNNER_TEMP}/workflow-preset-smoke.XXXXXX")"', - 'resolve_out="${RUNNER_TEMP}/plan-template-resolve.txt"', - 'constitution_resolve_out="${RUNNER_TEMP}/constitution-template-resolve.txt"', - "PIP_CONFIG_FILE: /dev/null", - 'PYTEST_ADDOPTS: ""', - 'export TMPDIR="${RUNNER_TEMP}"', - 'export TEMP="${RUNNER_TEMP}"', - 'export TMP="${RUNNER_TEMP}"', - 'specify init --here --integration claude --script sh --ignore-agent-tools', - "specify preset remove workflow-preset", +class ReleaseBoundaryTests(unittest.TestCase): + def test_release_workflow_smokes_install_and_preserves_core_implement(self) -> None: + workflow = read(ARTIFACT_WORKFLOW) + for term in ( + "specify init --here", "specify preset add --dev", + "specify integration upgrade claude", "specify preset resolve plan-template", - "specify preset resolve constitution-template", - "R: Repository / Workspace", - "M: Module / Capability", - "U: Unit / Design Object", - "O: Operation / Detail", - ".claude/skills/speckit-implement/SKILL.md", - "SPEC_KIT_FORK_PR_TOKEN", - "bigsmartben/spec-kit", - "workflow-preset-release-v${VERSION}", - "gh pr create", - "gh pr edit", - "WORKFLOW_PRESET_DOWNLOAD_URL", - "presets/catalog.community.json", - "community_catalog_path", - "community_catalog", - "download_url", - 'assert entry\\["version"\\] == "[0-9]+\\.[0-9]+\\.[0-9]+"', - "tests/test_presets.py", - "__pycache__", - ".pyc", - "ZipInfo", - "1980, 1, 1", - 'MANIFEST_NAME="spec-kit-workflow-preset-v${VERSION}.manifest.json"', - '"source_commit": source_commit', - '"sha256": zip_sha256', - "Verify release manifest", - "validators/speckit_behavior_contract.py", - '"requirements-dev.txt"', - '"tests"', + "test -f .claude/skills/speckit-implement/SKILL.md", "test ! -e .specify/presets/workflow-preset/commands/speckit.implement.md", - "core_implement_sha", - "WORKFLOW_PRESET_MANIFEST_URL", - "presets/workflow-preset.release.json", - 'entry["source_commit"] = release_manifest["source_commit"]', - 'entry["sha256"] = release_manifest["artifact"]["sha256"]', - "curl --fail --location", - "archive.extractall", - 'cmp "${ZIP_PATH}" "${existing_dir}/${ZIP_NAME}"', - "github.ref_type == 'tag' || (github.event_name == 'workflow_dispatch' && env.CREATE_INTEGRATION_PR == 'true')", - "env.CREATE_INTEGRATION_PR == 'true'", - "refs/tags/v${VERSION}", - "^[0-9]+\\.[0-9]+\\.[0-9]+$", - "persist-credentials: false", - "git rev-parse HEAD", - "refs/tags/v${VERSION}^{}", - "SPEC_KIT_FORK_PR_TOKEN is required when integration PR creation is requested.", - "exit 1", - ] - for term in required_terms: - self.assertIn(term, workflow_text) - forbidden_terms = [ - "specify preset resolve workflow-preset plan-template", - "specify preset resolve workflow-preset speckit.implement", - "client_payload[version]", - "client_payload[download_url]", - "repository_dispatch", - "repos/bigsmartben/spec-kit/dispatches", - "::warning::SPEC_KIT_FORK_DISPATCH_TOKEN", - "skipping integration PR", - "gh release upload \"${TAG_NAME}\" \"${ZIP_PATH}\" --clobber", - "\"${GITHUB_WORKSPACE}/\" \"${fork_dir}/spec-kit/presets/workflow-preset/\"", - ] - for term in forbidden_terms: - self.assertNotIn(term, workflow_text) - self.assertNotIn("github/spec-kit", workflow_text) + ): + self.assertIn(term, workflow) + + def test_public_docs_do_not_claim_preset_implement_ownership(self) -> None: + for document in (read(README), read(GOVERNANCE), read(AGENTS)): + self.assertIn("Spec Kit core", document) + self.assertNotIn("preset-owned Implement", document) if __name__ == "__main__": diff --git a/presets/workflow-preset/validators/speckit_analyze_contract.py b/presets/workflow-preset/validators/speckit_analyze_contract.py new file mode 100644 index 0000000000..c2cd1e9ba9 --- /dev/null +++ b/presets/workflow-preset/validators/speckit_analyze_contract.py @@ -0,0 +1,443 @@ +"""Pure in-memory cross-command audit helpers used by contract fixtures.""" +from __future__ import annotations + +from typing import Any + + +ARCH_PROJECTION = { + "decisions": ("research_refs", "ARCH_DECISION_OMITTED"), + "concepts": ("data_model_refs", "ARCH_CONCEPT_OMITTED"), + "boundaries": ("contract_refs", "ARCH_BOUNDARY_OMITTED"), + "constraints": ("plan_constraint_refs", "ARCH_CONSTRAINT_OMITTED"), + "gaps": ("blocker_refs", "ARCH_GAP_OMITTED"), +} + +DATA_MODEL_OBLIGATION_CODES = { + "idempotency_key": "ARCH_DATA_MODEL_IDEMPOTENCY_MISSING", + "provider_task_binding": "ARCH_PROVIDER_BINDING_MISSING", + "provider_lock": "ARCH_PROVIDER_LOCK_MISSING", + "retry_context": "ARCH_RETRY_CONTEXT_MISSING", + "recovery_decision": "ARCH_RECOVERY_DECISION_MISSING", + "readiness_lifecycle": "ARCH_LIFECYCLE_PROJECTION_MISSING", +} + +SOURCE_ROLES = { + "requirement-input", + "visual-input", + "technical-evidence", + "context-only", +} + +NORMATIVE_REQUIREMENT_PREFIXES = ("FR-", "NFR-", "UX-", "UI-", "VIS-") +VISUAL_REQUIREMENT_PREFIXES = ("UI-", "VIS-") +SOURCE_ROW_FIELDS = { + "ref", + "role", + "locator_or_description", + "revision", + "authorized_scope", + "projected_refs", + "status", + # In-memory audit hints derived from row prose and downstream applicability. + "broad", + "feature_slice", + "ui_ux_applicable", + "uif_required", +} + + +def _finding( + code: str, + *, + source: str, + target: str, + evidence: str, + owner: str, +) -> dict[str, str]: + return { + "severity": "BLOCKER", + "code": code, + "source": source, + "target": target, + "evidence": evidence, + "owner": owner, + } + + +def _source_pair_set(rows: Any) -> set[tuple[str, str]]: + if not isinstance(rows, list): + return set() + return { + (str(row.get("source_ref")), str(row.get("requirement_ref"))) + for row in rows + if isinstance(row, dict) + and row.get("source_ref") + and row.get("requirement_ref") + } + + +def audit_source_reference_contract( + snapshot: dict[str, Any], +) -> list[dict[str, str]]: + """Audit local source rows and projections without external access.""" + + findings: list[dict[str, str]] = [] + spec = snapshot.get("spec", {}) + plan = snapshot.get("plan", {}) + sources = spec.get("sources", []) + if not isinstance(sources, list): + return [ + _finding( + "SRC_CONTRACT_INVALID", + source="spec.md:Source References", + target="local source inventory", + evidence="sources must be a list", + owner="speckit.specify", + ) + ] + + source_refs = [ + str(source.get("ref")) + for source in sources + if isinstance(source, dict) and source.get("ref") + ] + duplicate_refs = sorted( + {ref for ref in source_refs if source_refs.count(ref) > 1} + ) + for ref in duplicate_refs: + findings.append( + _finding( + "SRC_REF_DUPLICATE", + source=f"spec.md:{ref}", + target="spec.md:Source References", + evidence="source identity is not unique", + owner="speckit.specify", + ) + ) + + known_refs = set(source_refs) + requirement_refs = set(spec.get("requirement_refs", [])) + downstream_refs = set(snapshot.get("referenced_source_refs", [])) + downstream_refs.update(plan.get("source_refs", [])) + ui_ux_mappings = _source_pair_set(plan.get("ui_ux_mappings")) + uif_mappings = _source_pair_set(plan.get("uif_mappings")) + downstream_refs.update(source_ref for source_ref, _ in ui_ux_mappings) + downstream_refs.update(source_ref for source_ref, _ in uif_mappings) + + for source in sources: + if not isinstance(source, dict): + findings.append( + _finding( + "SRC_CONTRACT_INVALID", + source="spec.md:Source References", + target="local source inventory", + evidence="source row must be an object", + owner="speckit.specify", + ) + ) + continue + + if not source.get("ref"): + findings.append( + _finding( + "SRC_REF_MISSING", + source="spec.md:Source References row", + target="spec.md:SRC ref", + evidence="source row has no local identity", + owner="speckit.specify", + ) + ) + + ref = str(source.get("ref", "")) + role = source.get("role") + projected_refs = source.get("projected_refs", []) + if not isinstance(projected_refs, list): + projected_refs = [] + + invalid_fields = sorted(set(source) - SOURCE_ROW_FIELDS) + if invalid_fields: + findings.append( + _finding( + "SRC_FIELD_INVALID", + source=f"spec.md:{ref}", + target="spec.md:Source References columns", + evidence=f"provider/source-specific field: {invalid_fields[0]}", + owner="speckit.specify", + ) + ) + + if not isinstance(role, str) or role not in SOURCE_ROLES: + findings.append( + _finding( + "SRC_ROLE_INVALID", + source=f"spec.md:{ref}", + target="spec.md:Source References.role", + evidence=str(role), + owner="speckit.specify", + ) + ) + + if not source.get("locator_or_description"): + findings.append( + _finding( + "SRC_IDENTITY_MISSING", + source=f"spec.md:{ref}", + target="spec.md:opaque locator / description", + evidence="missing local source description", + owner="speckit.specify", + ) + ) + + if not source.get("authorized_scope"): + findings.append( + _finding( + "SRC_AUTHORIZED_SCOPE_MISSING", + source=f"spec.md:{ref}", + target="spec.md:authorized scope / facts", + evidence="missing explicit feature scope", + owner="speckit.specify", + ) + ) + + if not source.get("status"): + findings.append( + _finding( + "SRC_STATUS_MISSING", + source=f"spec.md:{ref}", + target="spec.md:status / blocker", + evidence="missing local projection status", + owner="speckit.specify", + ) + ) + + status = str(source.get("status", "")).lower() + if "contradict" in status: + findings.append( + _finding( + "SRC_STATUS_CONTRADICTORY", + source=f"spec.md:{ref}", + target="local requirement projection", + evidence=str(source.get("status")), + owner="speckit.clarify", + ) + ) + + if ( + source.get("broad") + and not source.get("feature_slice") + and "block" not in status + and "clarif" not in status + ): + findings.append( + _finding( + "SRC_FEATURE_SLICE_MISSING", + source=f"spec.md:{ref}", + target="spec.md:authorized scope / facts", + evidence="broad source projected without safe feature slice", + owner="speckit.specify", + ) + ) + + normative_refs = [ + str(projected_ref) + for projected_ref in projected_refs + if str(projected_ref).startswith(NORMATIVE_REQUIREMENT_PREFIXES) + ] + invalid_for_role = False + if role in {"technical-evidence", "context-only"} and normative_refs: + invalid_for_role = True + elif role == "visual-input" and any( + not ref_value.startswith(VISUAL_REQUIREMENT_PREFIXES) + for ref_value in normative_refs + ): + invalid_for_role = True + if invalid_for_role: + findings.append( + _finding( + "SRC_ROLE_PROJECTION_INVALID", + source=f"spec.md:{ref}", + target="spec.md:projected requirement refs", + evidence=f"{role} -> {normative_refs}", + owner="speckit.specify", + ) + ) + + missing_projected_refs = sorted(set(projected_refs) - requirement_refs) + if missing_projected_refs: + findings.append( + _finding( + "SRC_PROJECTED_REF_MISSING", + source=f"spec.md:{ref}", + target=f"spec.md:{missing_projected_refs[0]}", + evidence="projected requirement ref does not exist locally", + owner="speckit.specify", + ) + ) + + if ( + not projected_refs + and ref not in downstream_refs + and status not in {"retained", "context-only"} + and "block" not in status + and "clarif" not in status + ): + findings.append( + _finding( + "SRC_ORPHAN", + source=f"spec.md:{ref}", + target="local requirements or blocker", + evidence="source has no local projection or retained reason", + owner="speckit.specify", + ) + ) + + if source.get("ui_ux_applicable", True): + for projected_ref in normative_refs: + if not projected_ref.startswith(VISUAL_REQUIREMENT_PREFIXES): + continue + pair = (ref, projected_ref) + if pair not in ui_ux_mappings: + findings.append( + _finding( + "SRC_UIUX_MAPPING_MISSING", + source=f"spec.md:{ref}+{projected_ref}", + target="ui-ux-design.md", + evidence="applicable source/UI-VIS pair is unmapped", + owner="speckit.plan", + ) + ) + if source.get("uif_required") and pair not in uif_mappings: + findings.append( + _finding( + "SRC_UIF_MAPPING_MISSING", + source=f"spec.md:{ref}+{projected_ref}", + target="contracts/uif/*.expected.json", + evidence="required UIF source/requirement pair is unmapped", + owner="speckit.plan", + ) + ) + + for missing_ref in sorted(downstream_refs - known_refs): + findings.append( + _finding( + "SRC_REF_MISSING", + source=f"downstream:{missing_ref}", + target="spec.md:Source References", + evidence="referenced source does not exist locally", + owner="speckit.specify", + ) + ) + + return findings + + +def audit_cross_command_consistency(snapshot: dict[str, Any]) -> list[dict[str, str]]: + """Return deterministic blocker findings without mutating the snapshot.""" + + findings = audit_source_reference_contract(snapshot) + architecture = snapshot.get("architecture", {}) + plan = snapshot.get("plan", {}) + tasks = snapshot.get("tasks", {}) + + architecture_revision = architecture.get("revision") + if architecture_revision and plan.get("architecture_revision") != architecture_revision: + findings.append( + _finding( + "ARCH_REVISION_STALE", + source=f"architecture:{architecture_revision}", + target="plan.md:Architecture Revision", + evidence=str(plan.get("architecture_revision")), + owner="speckit.plan", + ) + ) + + for architecture_key, (plan_key, code) in ARCH_PROJECTION.items(): + required = set(architecture.get(architecture_key, [])) + projected = set(plan.get(plan_key, [])) + missing = sorted(required - projected) + if missing: + findings.append( + _finding( + code, + source=f"architecture:{missing[0]}", + target=f"plan-products:{plan_key}", + evidence="missing stable ID projection", + owner="speckit.plan", + ) + ) + + if plan.get("spec_conflicts"): + findings.append( + _finding( + "SPEC_PLAN_CONFLICT", + source=str(plan["spec_conflicts"][0]), + target="plan products", + evidence="explicit contradictory mapping", + owner="speckit.plan", + ) + ) + + planned_objects = set(plan.get("design_objects", [])) + task_objects = set(tasks.get("design_object_refs", [])) + missing_objects = sorted(planned_objects - task_objects) + if missing_objects: + findings.append( + _finding( + "PLAN_TASK_MAPPING_MISSING", + source=f"plan:{missing_objects[0]}", + target="tasks.md", + evidence="no concrete path task", + owner="speckit.tasks", + ) + ) + + required_conditions = set(plan.get("required_test_conditions", [])) + task_conditions = set(tasks.get("test_condition_refs", [])) + missing_conditions = sorted(required_conditions - task_conditions) + if missing_conditions: + findings.append( + _finding( + "PLAN_REQUIRED_TEST_TASK_MISSING", + source=f"test-readiness:{missing_conditions[0]}", + target="tasks.md", + evidence="Required Test Condition has no required task", + owner="speckit.tasks", + ) + ) + + if plan.get("mu_scope") and tasks.get("mu_scope") != plan.get("mu_scope"): + findings.append( + _finding( + "MU_SCOPE_WIDENED", + source=f"plan:{plan.get('mu_scope')}", + target="tasks.md:M+U", + evidence=str(tasks.get("mu_scope")), + owner="speckit.tasks", + ) + ) + + return findings + + +def audit_data_model_obligations( + required_obligations: set[str], + projected_obligations: set[str], +) -> list[dict[str, str]]: + """Audit the concrete #24 Architecture-to-data-model failure surface.""" + + findings: list[dict[str, str]] = [] + for obligation in sorted(required_obligations - projected_obligations): + code = DATA_MODEL_OBLIGATION_CODES.get( + obligation, + "ARCH_DATA_MODEL_OBLIGATION_MISSING", + ) + findings.append( + _finding( + code, + source=f"architecture/spec:{obligation}", + target="data-model.md", + evidence="required model/state/invariant projection missing", + owner="speckit.plan", + ) + ) + return findings diff --git a/presets/workflow-preset/validators/speckit_behavior_contract.py b/presets/workflow-preset/validators/speckit_behavior_contract.py index 14742b2eb6..79d3be5609 100644 --- a/presets/workflow-preset/validators/speckit_behavior_contract.py +++ b/presets/workflow-preset/validators/speckit_behavior_contract.py @@ -28,6 +28,29 @@ def _require_non_empty_list(item: dict[str, Any], *, key: str, context: str) -> def _validate_expected_uif_contract(uif_contract: dict[str, Any]) -> None: uif_id = uif_contract.get("id", "") + source_refs = uif_contract.get("source_refs") + if not isinstance(source_refs, list) or not source_refs: + raise ValueError( + f"expected UIF contract {uif_id} must include non-empty source_refs" + ) + if any(not str(ref).startswith("SRC-") for ref in source_refs): + raise ValueError( + f"expected UIF contract {uif_id} has invalid source_refs" + ) + + requirement_refs = uif_contract.get("requirement_refs") + if not isinstance(requirement_refs, list) or not requirement_refs: + raise ValueError( + f"expected UIF contract {uif_id} must include non-empty requirement_refs" + ) + if any( + not str(ref).startswith(("UI-", "VIS-")) + for ref in requirement_refs + ): + raise ValueError( + f"expected UIF contract {uif_id} has non-UI/VIS requirement_refs" + ) + steps = uif_contract.get("steps") if not isinstance(steps, list) or not steps: raise ValueError(f"expected UIF contract {uif_id} must include non-empty steps") @@ -81,12 +104,13 @@ def _validate_non_positive_behavior_scenario( raise ValueError(f"behavior scenario {scenario_id} missing error_code") expected_feedback = scenario.get("expected_feedback") - if not isinstance(expected_feedback, dict) or not expected_feedback: - raise ValueError(f"behavior scenario {scenario_id} missing expected_feedback") - if not expected_feedback.get("type"): - raise ValueError(f"behavior scenario {scenario_id} missing feedback_type") - if not expected_feedback.get("message"): - raise ValueError(f"behavior scenario {scenario_id} missing feedback_message") + if expected_feedback is not None: + if not isinstance(expected_feedback, dict) or not expected_feedback: + raise ValueError(f"behavior scenario {scenario_id} has invalid expected_feedback") + if not expected_feedback.get("type"): + raise ValueError(f"behavior scenario {scenario_id} missing feedback_type") + if not expected_feedback.get("message"): + raise ValueError(f"behavior scenario {scenario_id} missing feedback_message") invariant_intents = {"state_invariant", "rollback", "compensation"} if not any( @@ -225,6 +249,7 @@ def validate_behavior_contract_bundle( data_fixtures: dict[str, Any], assertions: dict[str, Any], uif_expected_contracts: list[dict[str, Any]], + test_condition_ids: set[str] | None = None, ) -> None: scenarios = scenario_instances.get("scenarios", []) if not scenarios: @@ -261,17 +286,29 @@ def validate_behavior_contract_bundle( for scenario in scenarios: _require_non_empty_list( scenario, - key="fixture_ids", + key="assertion_ids", context="behavior scenario instance", ) _require_non_empty_list( scenario, - key="assertion_ids", + key="test_condition_refs", context="behavior scenario instance", ) + has_fixture_refs = bool(scenario.get("fixture_ids")) + has_no_fixture_rationale = bool(scenario.get("no_fixture_rationale")) + if has_fixture_refs == has_no_fixture_rationale: + raise ValueError( + "scenario must declare exactly one fixture_ids or no_fixture_rationale" + ) + uif_path_id = scenario.get("uif_path_id") - if uif_path_id not in uif_path_ids: + has_non_ui_rationale = bool(scenario.get("non_ui_rationale")) + if bool(uif_path_id) == has_non_ui_rationale: + raise ValueError( + "scenario must declare exactly one uif_path_id or non_ui_rationale" + ) + if uif_path_id and uif_path_id not in uif_path_ids: raise ValueError(f"scenario references unknown uif_path_id: {uif_path_id}") for fixture_id in scenario.get("fixture_ids", []): @@ -282,6 +319,13 @@ def validate_behavior_contract_bundle( if assertion_id not in assertion_ids: raise ValueError(f"scenario references unknown assertion: {assertion_id}") + if test_condition_ids is not None: + for tc_id in scenario.get("test_condition_refs", []): + if tc_id not in test_condition_ids: + raise ValueError( + f"scenario references unknown test condition: {tc_id}" + ) + _validate_non_positive_behavior_scenario(scenario, assertions_by_id) if len(scenario_ids) != len(scenario_instances.get("scenarios", [])): diff --git a/presets/workflow-preset/validators/speckit_test_contract.py b/presets/workflow-preset/validators/speckit_test_contract.py new file mode 100644 index 0000000000..4dab5dd2e4 --- /dev/null +++ b/presets/workflow-preset/validators/speckit_test_contract.py @@ -0,0 +1,126 @@ +"""Pure in-memory validators for Plan Test & Acceptance contracts.""" +from __future__ import annotations + +from typing import Any, Iterable + + +PIXEL_TERMS = { + "pixel-perfect", + "pixel perfect", + "pixel_fidelity", + "screenshot", + "visual diff", + "baseline capture", + "visual restoration", + "rendered visual review", + "final visual review", +} + + +def _duplicates(values: Iterable[str]) -> set[str]: + seen: set[str] = set() + duplicates: set[str] = set() + for value in values: + if value in seen: + duplicates.add(value) + seen.add(value) + return duplicates + + +def _contains_pixel_scope(value: Any) -> bool: + if isinstance(value, dict): + return any(_contains_pixel_scope(item) for item in value.values()) + if isinstance(value, list): + return any(_contains_pixel_scope(item) for item in value) + if not isinstance(value, str): + return False + normalized = value.casefold().replace("-", " ") + return any(term.replace("-", " ") in normalized for term in PIXEL_TERMS) + + +def validate_test_conditions( + payload: dict[str, Any], + *, + available_bdd_tc_refs: set[str] | None = None, +) -> None: + """Validate cross-field rules owned by the Test Conditions contract.""" + + conditions = payload.get("conditions") + if not isinstance(conditions, list) or not conditions: + raise ValueError("test conditions must include at least one condition") + + ids = [condition.get("id") for condition in conditions] + if any(not isinstance(item, str) or not item.startswith("TC-") for item in ids): + raise ValueError("test condition ids must use TC-*") + duplicates = _duplicates(ids) + if duplicates: + raise ValueError(f"duplicate test condition id: {sorted(duplicates)[0]}") + + for condition in conditions: + condition_id = condition["id"] + for field in ( + "source_refs", + "levels", + "types", + "techniques", + "environment_refs", + "related_refs", + ): + if not isinstance(condition.get(field), list) or not condition[field]: + raise ValueError(f"{condition_id} missing non-empty {field}") + + has_fixtures = bool(condition.get("fixture_refs")) + has_no_fixture = bool(condition.get("no_fixture_rationale")) + if has_fixtures == has_no_fixture: + raise ValueError( + f"{condition_id} must declare exactly one fixture_refs or no_fixture_rationale" + ) + + has_path = bool(condition.get("quickstart_ref")) + has_x3_blocker = bool(condition.get("x3_blocker")) + if has_path == has_x3_blocker: + raise ValueError( + f"{condition_id} must declare exactly one quickstart_ref or x3_blocker" + ) + + oracle = condition.get("oracle") + if not isinstance(oracle, dict) or not oracle.get("kind") or "expected" not in oracle: + raise ValueError(f"{condition_id} missing complete oracle") + if not condition.get("risk_or_priority"): + raise ValueError(f"{condition_id} missing risk_or_priority") + if not condition.get("execution_mode"): + raise ValueError(f"{condition_id} missing execution_mode") + if not condition.get("evidence_requirement"): + raise ValueError(f"{condition_id} missing evidence_requirement") + + if condition.get("status") == "blocked" and not condition.get("blocker"): + raise ValueError(f"{condition_id} blocked condition missing blocker") + + if _contains_pixel_scope(condition): + raise ValueError(f"{condition_id} contains pixel-level visual scope") + + if "BDD" in condition["techniques"]: + if available_bdd_tc_refs is None or condition_id not in available_bdd_tc_refs: + raise ValueError(f"{condition_id} selects BDD but has no BDD child artifact") + + +def validate_test_readiness( + conditions: dict[str, Any], + readiness_rows: list[dict[str, Any]], +) -> None: + """Validate the one-row-per-required-TC readiness handoff.""" + + required_ids = { + condition["id"] + for condition in conditions.get("conditions", []) + if condition.get("status") == "required" + } + row_ids = [row.get("tc_id") for row in readiness_rows] + if _duplicates(row_ids): + raise ValueError("test readiness contains duplicate TC rows") + if set(row_ids) != required_ids: + missing = sorted(required_ids - set(row_ids)) + extra = sorted(set(row_ids) - required_ids) + raise ValueError(f"test readiness TC mismatch missing={missing} extra={extra}") + if _contains_pixel_scope(readiness_rows): + raise ValueError("test readiness contains pixel-level visual scope") diff --git a/tests/integrations/test_cli.py b/tests/integrations/test_cli.py index 733c498656..71c905ed41 100644 --- a/tests/integrations/test_cli.py +++ b/tests/integrations/test_cli.py @@ -1066,7 +1066,16 @@ def test_workflow_preset_registers_commands_and_composes_wrappers(self, tmp_path assert not (preset_dir / "schemas" / "speckit.implement.handoff.v2.schema.json").exists() assert (preset_dir / "templates" / "constitution-template.md").exists() assert (preset_dir / "templates" / "behavior" / "bdd-draft.feature").exists() - assert (preset_dir / ".composed" / "speckit.constitution.md").exists() + assert not (preset_dir / "templates" / "behavior" / "behavior-testability.md").exists() + assert not (preset_dir / "templates" / "behavior" / "uif-intent.json").exists() + assert not (preset_dir / "templates" / "behavior" / "data-fixtures-intent.json").exists() + assert (preset_dir / "templates" / "ui-ux-design-template.md").exists() + assert (preset_dir / "templates" / "test" / "test-conditions.json").exists() + assert (preset_dir / "templates" / "test-readiness-template.md").exists() + assert not (preset_dir / ".composed" / "speckit.specify.md").exists() + assert not (preset_dir / ".composed" / "speckit.clarify.md").exists() + assert (preset_dir / ".composed" / "speckit.checklist.md").exists() + assert not (preset_dir / ".composed" / "speckit.constitution.md").exists() assert (preset_dir / ".composed" / "speckit.plan.md").exists() assert (preset_dir / ".composed" / "speckit.tasks.md").exists() assert (preset_dir / ".composed" / "speckit.analyze.md").exists() @@ -1091,16 +1100,30 @@ def test_workflow_preset_registers_commands_and_composes_wrappers(self, tmp_path } assert set(workflow_entry["registered_commands"]["claude"]) == expected_preset_commands + specify_skill = project / ".claude" / "skills" / "speckit-specify" / "SKILL.md" + clarify_skill = project / ".claude" / "skills" / "speckit-clarify" / "SKILL.md" + analyze_skill = project / ".claude" / "skills" / "speckit-analyze" / "SKILL.md" plan_skill = project / ".claude" / "skills" / "speckit-plan" / "SKILL.md" constitution_skill = project / ".claude" / "skills" / "speckit-constitution" / "SKILL.md" tasks_skill = project / ".claude" / "skills" / "speckit-tasks" / "SKILL.md" implement_skill = project / ".claude" / "skills" / "speckit-implement" / "SKILL.md" - for skill_path in (plan_skill, constitution_skill, tasks_skill, implement_skill): + for skill_path in ( + specify_skill, + clarify_skill, + analyze_skill, + plan_skill, + constitution_skill, + tasks_skill, + implement_skill, + ): assert skill_path.exists(), f"{skill_path} was not registered" assert "Change Scope Granularity" in constitution_skill.read_text(encoding="utf-8") - assert "Phase 0 Behavior Projection" in plan_skill.read_text(encoding="utf-8") - assert "validation task derivation" in tasks_skill.read_text(encoding="utf-8").lower() + assert "Full-Spectrum Projection" in specify_skill.read_text(encoding="utf-8") + assert "Cross-Domain Ambiguity Map" in clarify_skill.read_text(encoding="utf-8") + assert "Cross-Command Consistency Gates" in analyze_skill.read_text(encoding="utf-8") + assert "X0 — Feature Plan Control" in plan_skill.read_text(encoding="utf-8") + assert "PLAN_OUTPUT_READY" in tasks_skill.read_text(encoding="utf-8") implement_text = implement_skill.read_text(encoding="utf-8") assert "## Pre-Execution Checks" in implement_text assert "## Mandatory Post-Execution Hooks" in implement_text @@ -1149,7 +1172,7 @@ def test_repeated_init_reapplies_active_workflow_preset( plan_file = project / plan_path first_content = plan_file.read_text(encoding="utf-8") assert "workflow-preset" in first_content - assert "Phase 0 preflight" in first_content + assert "X0 — Feature Plan Control" in first_content second = runner.invoke( app, diff --git a/tests/integrations/test_integration_subcommand.py b/tests/integrations/test_integration_subcommand.py index c2a9fa696a..d6c15d9dd5 100644 --- a/tests/integrations/test_integration_subcommand.py +++ b/tests/integrations/test_integration_subcommand.py @@ -1300,13 +1300,13 @@ def test_first_active_install_reapplies_existing_preset(self, tmp_path): plan = project / ".agents" / "skills" / "speckit-plan" / "SKILL.md" content = plan.read_text(encoding="utf-8") assert "source: preset:workflow-preset" in content - assert "Phase 0 preflight" in content + assert "X0 — Feature Plan Control" in content def test_secondary_install_preserves_active_preset_projection(self, tmp_path): project = _init_project(tmp_path, "amp") active_plan = project / ".agents" / "commands" / "speckit.plan.md" before = active_plan.read_bytes() - assert b"Phase 0 preflight" in before + assert "X0 — Feature Plan Control".encode("utf-8") in before result = _run_in_project(project, [ "integration", "install", "codex", @@ -1641,7 +1641,7 @@ def test_uninstall_default_reapplies_preset_to_fallback(self, tmp_path): ) content = fallback_plan.read_text(encoding="utf-8") assert "source: preset:workflow-preset" in content - assert "Phase 0 preflight" in content + assert "X0 — Feature Plan Control" in content def test_uninstall_preserves_shared_infra(self, tmp_path): """Shared scripts and templates are not removed by integration uninstall.""" @@ -1705,7 +1705,7 @@ def test_use_reapplies_preset_to_new_active_integration(self, tmp_path): assert result.exit_code == 0, result.output content = codex_plan.read_text(encoding="utf-8") assert "source: preset:workflow-preset" in content - assert "Phase 0 preflight" in content + assert "X0 — Feature Plan Control" in content preset_registry = json.loads( (project / ".specify" / "presets" / ".registry").read_text( @@ -1966,7 +1966,7 @@ def test_switch_new_target_reapplies_preset(self, tmp_path): plan = project / ".agents" / "commands" / "speckit.plan.md" content = plan.read_text(encoding="utf-8") - assert "Phase 0 preflight" in content + assert "X0 — Feature Plan Control" in content assert "workflow-preset" in content def test_switch_installed_target_reapplies_preset(self, tmp_path): @@ -1985,7 +1985,7 @@ def test_switch_installed_target_reapplies_preset(self, tmp_path): plan = project / ".agents" / "skills" / "speckit-plan" / "SKILL.md" content = plan.read_text(encoding="utf-8") assert "source: preset:workflow-preset" in content - assert "Phase 0 preflight" in content + assert "X0 — Feature Plan Control" in content def test_switch_migrates_extension_commands(self, tmp_path): """Switching should migrate extension commands to the new agent directory.""" @@ -2737,7 +2737,7 @@ def test_upgrade_reapplies_preset_and_refreshes_manifest_hash(self, tmp_path): content = plan.read_text(encoding="utf-8") assert "source: preset:workflow-preset" in content - assert "Phase 0 preflight" in content + assert "X0 — Feature Plan Control" in content manifest = json.loads(manifest_path.read_text(encoding="utf-8")) expected_hash = hashlib.sha256(plan.read_bytes()).hexdigest() @@ -2816,7 +2816,7 @@ def test_upgrade_ignores_disabled_preset(self, tmp_path): ]) assert result.exit_code == 0, result.output plan = project / ".agents" / "commands" / "speckit.plan.md" - assert "Phase 0 preflight" not in plan.read_text(encoding="utf-8") + assert "X0 — Feature Plan Control" not in plan.read_text(encoding="utf-8") def test_upgrade_warns_for_broken_preset_and_continues(self, tmp_path): from specify_cli.presets import PresetManager @@ -2841,7 +2841,7 @@ def test_upgrade_warns_for_broken_preset_and_continues(self, tmp_path): assert result.exit_code == 0, result.output plan = project / ".agents" / "commands" / "speckit.plan.md" - assert "Phase 0 preflight" in plan.read_text(encoding="utf-8") + assert "X0 — Feature Plan Control" in plan.read_text(encoding="utf-8") def test_upgrade_non_active_agent_preserves_active_agent_skills(self, tmp_path): """Upgrading a non-active agent must not touch the active agent's skills. diff --git a/tests/test_presets.py b/tests/test_presets.py index ec93dfd7f4..3188115d97 100644 --- a/tests/test_presets.py +++ b/tests/test_presets.py @@ -4651,14 +4651,14 @@ def test_workflow_preset_catalog_matches_manifest(self): assert catalog_updated_at >= datetime(2026, 6, 18, tzinfo=timezone.utc) assert entry["bundled"] is True - assert entry["version"] == "3.0.0" + assert entry["version"] == "3.1.0" assert entry["version"] == manifest["preset"]["version"] assert entry["repository"] == manifest["preset"]["repository"] assert entry["requires"]["speckit_version"] == manifest["requires"]["speckit_version"] assert entry["provides"]["commands"] == command_count assert entry["provides"]["templates"] == template_count assert command_count == 7 - assert template_count == 24 + assert template_count == 27 assert entry["tags"] == manifest["tags"] assert len(entry["source_commit"]) == 40 assert entry["sha256"] @@ -4686,7 +4686,7 @@ def test_workflow_preset_community_catalog_matches_manifest(self): assert entry["provides"]["commands"] == command_count assert entry["provides"]["templates"] == template_count assert command_count == 7 - assert template_count == 24 + assert template_count == 27 assert entry["tags"] == manifest["tags"] assert len(entry["source_commit"]) == 40 assert entry["sha256"] @@ -4833,9 +4833,17 @@ def test_community_smoke_checks_wheel_assets_and_extension_dev_reinstall(self): "behavior-testability-checklist.md" ) in verify_run assert ( - "test -f .specify/presets/workflow-preset/templates/behavior/" + "test ! -e .specify/presets/workflow-preset/templates/behavior/" "behavior-testability.md" ) in verify_run + for removed_template in ( + "uif-intent.json", + "data-fixtures-intent.json", + ): + assert ( + "test ! -e .specify/presets/workflow-preset/templates/behavior/" + f"{removed_template}" + ) in verify_run for template in ( "behavior-gate.md", "domain-gate.md", @@ -4843,6 +4851,34 @@ def test_community_smoke_checks_wheel_assets_and_extension_dev_reinstall(self): "visual-gate.md", ): assert f"templates/requirements/{template}" in verify_run + for template in ( + "class-diagram-template.md", + "sequences-template.md", + "ui-ux-design-template.md", + "quickstart-template.md", + "test-readiness-template.md", + "test/test-conditions.json", + ): + assert f"templates/{template}" in verify_run + assert "schemas/speckit.test.conditions.v1.schema.json" in verify_run + for command in ("specify", "clarify", "constitution"): + assert ( + "test ! -e .specify/presets/workflow-preset/.composed/" + f"speckit.{command}.md" + ) in verify_run + for command in ("checklist", "analyze", "plan", "tasks"): + assert ( + "test -f .specify/presets/workflow-preset/.composed/" + f"speckit.{command}.md" + ) in verify_run + for marker in ( + "Full-Spectrum Projection", + "Cross-Domain Ambiguity Map", + "Cross-Command Consistency Gates", + "X0 — Feature Plan Control", + "PLAN_OUTPUT_READY", + ): + assert marker in verify_run assert ( 'for extension_id in arch discovery inception intake preview repository-governance; do' in verify_run