From 9f4679cad931bef8d353dcc6cc21e1e8578e7ed0 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 18 Jun 2026 10:10:30 +0000 Subject: [PATCH] Update workflow-preset to v1.3.7 --- presets/catalog.json | 4 +- presets/workflow-preset/CHANGELOG.md | 7 + presets/workflow-preset/README.md | 112 +- .../commands/speckit.analyze.md | 18 +- .../commands/speckit.checklist.md | 40 +- .../commands/speckit.clarify.md | 39 +- .../commands/speckit.implement.md | 10 +- .../workflow-preset/commands/speckit.plan.md | 38 +- .../commands/speckit.specify.md | 71 +- .../workflow-preset/commands/speckit.tasks.md | 27 +- .../docs/extension-governance.md | 14 +- presets/workflow-preset/preset.yml | 24 +- ...speckit.behavior.assertions.v1.schema.json | 12 +- ...behavior.scenario-instances.v1.schema.json | 98 +- .../templates/behavior/assertions.json | 10 +- .../behavior-testability-checklist.md | 50 +- .../behavior/scenario-instances.json | 32 + .../design-requirement-intake-template.md | 130 ++ .../figma-evidence-packet-template.md | 50 +- .../templates/figma-intake-contract.md | 19 +- .../templates/plan-template.md | 7 + .../requirement-merge-report-template.md | 71 + .../tests/test_preset_contract.py | 1198 +++++++++++++++-- .../validators/speckit_implement_contract.py | 185 ++- tests/test_presets.py | 2 +- 25 files changed, 1979 insertions(+), 289 deletions(-) create mode 100644 presets/workflow-preset/templates/design-requirement-intake-template.md create mode 100644 presets/workflow-preset/templates/requirement-merge-report-template.md diff --git a/presets/catalog.json b/presets/catalog.json index 7c9e0b3819..699eb65a62 100644 --- a/presets/catalog.json +++ b/presets/catalog.json @@ -29,7 +29,7 @@ "workflow-preset": { "name": "Workflow Preset", "id": "workflow-preset", - "version": "1.3.6", + "version": "1.3.7", "description": "Behavior-first specification, design artifacts, and agent-native handoff orchestration", "author": "bigsmartben", "repository": "https://github.com/bigsmartben/spec-kit-workflow-preset", @@ -40,7 +40,7 @@ }, "provides": { "commands": 8, - "templates": 24 + "templates": 26 }, "tags": [ "behavior", diff --git a/presets/workflow-preset/CHANGELOG.md b/presets/workflow-preset/CHANGELOG.md index 084de19a3b..9725355d37 100644 --- a/presets/workflow-preset/CHANGELOG.md +++ b/presets/workflow-preset/CHANGELOG.md @@ -2,6 +2,13 @@ ## Unreleased +## 1.3.7 + +- Generalized Figma-derived requirement handling into provider-neutral Design Requirement Intake and Requirement Merge templates while keeping Figma MCP execution outside the preset. +- Added a row-per-case Case Coverage Matrix to make positive, negative, boundary, permission, validation, and state_conflict applicability explicit before planning. +- Hardened failure behavior scenarios so permission, validation, state_conflict, and other error paths require structured request cases, error responses, failure feedback, and assertions before tasks are generated. +- Hardened UI task generation so UI implementation and acceptance tasks are paired with explicit state coverage, viewport coverage, visual proof refs, screenshot refs, and readiness blockers for missing visual or asset evidence. + ## 1.3.6 - Added a Figma Evidence Packet input template and Figma intake contract for Figma-derived specifications without adding Figma MCP execution to the preset. diff --git a/presets/workflow-preset/README.md b/presets/workflow-preset/README.md index d091ca97f1..7652d01058 100644 --- a/presets/workflow-preset/README.md +++ b/presets/workflow-preset/README.md @@ -2,7 +2,7 @@ This Spec Kit community preset combines behavior-first specification, design-aware planning, and agent-native handoff orchestration. -It keeps `/speckit.specify`, `/speckit.clarify`, `/speckit.checklist`, `/speckit.plan`, `/speckit.tasks`, and `/speckit.analyze` compatible with the core workflow while adding a BDD and NFR readiness gate, Phase 0 behavior projection, optional design artifacts for internal object design and service sequencing, and task-time test strategy derivation. It replaces `/speckit.implement` with a Core Agent, Vertical Planner Agent, and Worker Agent orchestration contract that writes handoffs to disk. +It wraps `/speckit.specify`, `/speckit.clarify`, `/speckit.checklist`, `/speckit.plan`, `/speckit.tasks`, and `/speckit.analyze` with BDD, NFR, and applicable Visual Fidelity readiness gates, Phase 0 behavior projection, optional design artifacts for internal object design and service sequencing, and task-time test strategy derivation. It replaces `/speckit.implement` with a Core Agent, Vertical Planner Agent, and Worker Agent orchestration contract that writes handoffs to disk. ## Goal @@ -10,7 +10,7 @@ It keeps `/speckit.specify`, `/speckit.clarify`, `/speckit.checklist`, `/speckit The preset has four goals: -- Make BDD readiness and NFR readiness explicit before planning by checking `spec.md` for observable, verifiable behavior and explicit non-functional requirement declarations. +- Make BDD/NFR/applicable Visual Fidelity readiness explicit before planning by checking `spec.md` for observable, verifiable behavior, explicit non-functional requirement declarations, and design evidence when relevant. - Project accepted requirements into BDD, UIF intent, and fixture intent drafts during `/speckit.plan` Phase 0. - Preserve richer planning intent so downstream tasks and implementation do not lose object design, service-flow, or validation decisions. - Execute implementation through agent-native handoff orchestration so each worker receives explicit task IDs, lifecycle stage, vertical capability, context, read/write paths, validation commands, and receipt requirements. @@ -21,7 +21,7 @@ Large Spec Kit features can overload the implementation phase. A single `/specki `workflow-preset` reduces that failure mode in three complementary ways: -- Requirement enhancement keeps product requirements in `spec.md` and gates planning with a BDD/NFR readiness checklist. +- Requirement enhancement keeps product requirements in `spec.md` and gates planning with a BDD/NFR/applicable Visual Fidelity readiness checklist. - Plan enhancement projects accepted behavior drafts, then gives object design, service sequencing, and validation intent stable homes before tasks are generated. - Implement handoff orchestration slices work by lifecycle and vertical capability, then gives each Worker Agent a compact digest, scoped paths, validation commands, and a receipt contract instead of the full planning corpus. @@ -33,10 +33,12 @@ Requirement capabilities: - Wraps `/speckit.specify` so it produces or updates `spec.md` only. - Wraps `/speckit.clarify` so it resolves requirement ambiguity in `spec.md` only. -- When a Figma Evidence Packet has already been written into `spec.md`, `/speckit.clarify` clarifies Figma-derived gaps already written in `spec.md` and does not call Figma. +- Treats Product Requirement + Design Requirement as the requirement inputs for `spec.md`; Figma is a Design Requirement provider, not a core SDD stage. +- When Design Requirement Intake or a Figma Evidence Packet has already been written into `spec.md`, `/speckit.clarify` clarifies design-derived gaps already written in `spec.md` and does not call Figma. - Wraps `/speckit.checklist` to add `checklists/behavior-testability.md` as a BDD readiness gate, NFR readiness gate, and applicable Visual Fidelity readiness gate. -- Checks user stories, acceptance criteria, Given/When/Then readiness, roles, permissions, states, data, validation, boundary, exception, state-conflict behavior, and non-functional requirements directly from `spec.md`. -- Checks Figma-derived requirements for raw metadata completeness, node inventory parity, source traceability, and blocker lint errors before planning. +- Checks user stories, acceptance criteria, Given/When/Then readiness, roles, permissions, states, data, validation, boundary, exception, state_conflict behavior, and non-functional requirements directly from `spec.md`. +- Adds a Case Coverage Matrix with one row per story or capability case type so positive, negative, boundary, permission, validation, and state_conflict cases are marked Required, Not Applicable, or Unknown before planning. +- Checks design-derived requirements for source traceability, provider readiness status, evidence refs, blockers, and visual fidelity scope before planning. - Requires NFR dimensions to be marked Required, Not Applicable, or Unknown in product language before planning. - Blocks planning when readiness gaps or missing or unverifiable NFR assumptions must return to `/speckit.clarify` or `/speckit.specify`. @@ -46,19 +48,23 @@ Planning capabilities: - Requires the BDD, NFR, and applicable Visual Fidelity readiness gates to pass before planning. - Treats Phase 0 preflight failures as report-only/no-write failures. - Writes `behavior/bdd.draft.feature`, `behavior/behavior-scenarios.draft.json`, `behavior/uif.intent.json`, and `behavior/data-fixtures.intent.json` during Phase 0 behavior projection. -- Consumes Phase 0 behavior drafts and must formalize them into `contracts/bdd/`, `contracts/uif/`, and `contracts/behavior/` when the BDD and NFR readiness gate has passed. -- Records `N/A or blocker` when behavior drafts cannot be formalized. +- Projects Required case coverage into `behavior/behavior-scenarios.draft.json` instead of allowing Required cases to disappear behind positive-only drafts. +- Consumes Phase 0 behavior drafts and must formalize them into `contracts/bdd/`, `contracts/uif/`, and `contracts/behavior/` when the BDD, NFR, and applicable Visual Fidelity readiness gates have passed. +- Requires failure scenarios in `contracts/behavior/` to carry an explicit trigger, case kind, error code, failure feedback, and state invariant, rollback, or compensation assertion reference. +- Records `N/A or blocker` and `case_coverage_blockers` when behavior drafts cannot be formalized. - Keeps `plan.md` focused on technical decisions and navigation. - Adds plan-template navigation to the core plan output. - Stores internal object design in `class-diagram.md`. - Stores service, command, event, async, retry, rollback, and failure-path flows in `contracts/sequences.md`. - Records validation decisions in `research.md` and validation paths in `quickstart.md`. +- When visual requirements are in scope, research.md records visual validation decisions, contracts formalize visual interaction and state constraints, and contracts/sequences.md records visual state flow only when it affects cross-boundary sequencing. - Keeps product requirements in `spec.md`, domain facts in `data-model.md`, interface schemas in `contracts/`, and executable validation guidance in `quickstart.md`. Task generation capabilities: - Wraps `/speckit.tasks` so task generation can consume the design artifacts. - Uses formal BDD, UIF, and behavior contracts to derive test-first fixture, acceptance test, implementation, and verification tasks. +- Treats missing Required failure behavior scenarios as blockers instead of generating complete-looking happy-path-only tasks. - Performs test strategy derivation from BDD contracts, Expected UIF contracts, behavior contracts, interface contracts, `research.md`, and `quickstart.md` without writing a separate strategy artifact. - Uses design artifacts to derive implementation, integration, orchestration, failure-handling, and validation tasks. - Preserves the existing checklist format and user-story organization. @@ -67,6 +73,7 @@ Analysis capabilities: - Wraps `/speckit.analyze` to check vertical consistency from `spec.md` through BDD/UIF intent, formal contracts, and `tasks.md`. - Checks that user stories, Given/When/Then steps, UIF API calls, behavior contracts, tasks, and quickstart validation paths remain traceable. +- Adds case coverage checks so Required case types remain traceable through behavior drafts, formal contracts, tasks, and quickstart validation paths. - Treats UIF as a requirement behavior projection, formalized during planning as Expected UIF contracts. Implementation capabilities: @@ -98,7 +105,7 @@ Context-load controls: 1. `/speckit.specify` keeps the core requirements output in `spec.md`. 2. `/speckit.clarify` resolves requirement ambiguity in `spec.md`. -3. `/speckit.checklist` checks BDD and NFR readiness directly from `spec.md` and blocks planning when readiness gaps remain. +3. `/speckit.checklist` checks BDD, NFR, and applicable Visual Fidelity readiness directly from `spec.md` and blocks planning when readiness gaps remain. 4. `/speckit.plan` runs Phase 0 preflight, performs Phase 0 behavior projection, formalizes behavior drafts into contracts, and adds design artifacts when they help implementation. 5. `/speckit.tasks` reads the core plan outputs, optional design artifacts, behavior contracts, interface contracts, `research.md`, and `quickstart.md`, then produces executable tasks with inline test level, data strategy, and evidence requirements. 6. `/speckit.analyze` checks vertical consistency across requirements, behavior drafts, contracts, and tasks. @@ -125,7 +132,7 @@ Context-load controls: Release install: ```bash -specify preset add workflow-preset --from https://github.com/bigsmartben/spec-kit-workflow-preset/releases/download/v1.3.6/spec-kit-workflow-preset-v1.3.6.zip +specify preset add workflow-preset --from https://github.com/bigsmartben/spec-kit-workflow-preset/releases/download/v1.3.7/spec-kit-workflow-preset-v1.3.7.zip ``` Local development install: @@ -147,18 +154,56 @@ Run the behavior-first workflow: /speckit.analyze ``` -### Figma Input +### Design Requirement Input -`/speckit.specify` supports direct Figma URL input when the runtime agent has Figma MCP access: +The SDD requirement model is: + +```text +Product Requirement + Design Requirement -> Requirement Merge -> baseline spec.md +``` + +Design Requirement Intake is provider-neutral. Figma is a Design Requirement +provider for the current toolchain; other providers can supply screenshots, +prototype documents, design-system documentation, or other design evidence. +Requirement Merge resolves Product Requirement and Design Requirement inputs +into `spec.md` while leaving unresolved conflicts as `[NEEDS CLARIFICATION]`. + +### Screenshot Evidence + +Screenshot is evidence, not intake. Screenshots are optional but strongly recommended provider evidence for UI work, and Design Requirement Intake only references them while recording structured design facts. +In other words, screenshots are provider evidence and visual proof. + +L0-L3 screenshot evidence levels: + +- L0 No Screenshot: non-visual, low-fidelity, backend, API, or data-flow work. +- L1 Key Screenshots: ordinary UI visual requirements and critical paths. +- L2 State + Viewport Matrix: complex UI, responsive, or multi-state work. +- L3 Visual Baseline: high-fidelity visual matching, pixel-perfect work, + brand-critical pages, design systems, or visual regression. + +Screenshots can prove layout, density, state, viewport, asset, and visual +baseline facts. Screenshots cannot upgrade product semantics such as permissions, +business effects, validation rules, or data ownership into confirmed +requirements. +Missing screenshot evidence blocks readiness when `spec.md` declares visual proof required and the checklist template requires the missing screenshot level. +Responsive visual requirements block PASS only when they are complex, multi-state, or declare L2 or L3 visual proof; missing viewport-specific evidence then sets Gate Status: BLOCKED and lists the item in Blocking Items. +The Visual Fidelity Evidence Matrix is the single visual readiness record; visual evidence decisions should not be duplicated outside the matrix and Blocking Items. +Ordinary UI screenshots remain recommended unless `spec.md` declares visual proof required. + +### Figma Provider Input + +Some runtime environments support direct Figma URL input when the runtime agent has Figma MCP access: ```text /speckit.specify ``` -The runtime agent should extract design evidence into a Figma Evidence Packet -before writing `spec.md`. The preset defines the packet format, requirement -ownership rules, and Figma intake contract; it does not provide Figma MCP connection, authentication, or execution. -The preset defines the required Figma intake artifact structure and ready gate; +The preset does not extract from the URL. When the runtime agent or external +Figma intake has extracted provider evidence into a Figma Evidence Packet, the +preset can consume the qualified evidence before writing `spec.md`. The preset +defines the packet format, Design Requirement ownership rules, and Figma +provider source readiness contract; it does not provide Figma MCP connection, authentication, or execution. +The preset defines the required design intake and provider readiness artifact structure and ready gate; the runtime agent or external Figma intake calls Figma MCP and writes `figma-metadata.part-*.xml`, `figma-metadata.index.yaml`, and `figma-node-inventory.yaml`. The preset consumes qualified evidence and does @@ -194,10 +239,12 @@ This preset adds checklist artifacts: - `specs//checklists/behavior-testability.md` -Figma intake artifact instances are written by the runtime agent or external -Figma intake. The preset defines their required structure and consumes the -qualified evidence from `spec.md` after `/speckit.specify` writes or marks it as -`[NEEDS CLARIFICATION]`; it does not generate the artifact instances. +Design requirement intake and provider artifact instances are written by the +runtime agent, external design intake, or provider-specific intake. The preset +defines their required structure and consumes the qualified evidence from +`spec.md` after `/speckit.specify` writes confirmed requirements or records +`[BLOCKED: PROVIDER_EVIDENCE]`; it does not generate the artifact instances. +Provider evidence blockers do not become `[NEEDS CLARIFICATION]`. This preset adds Phase 0 behavior artifacts: @@ -241,7 +288,10 @@ Contract files packaged by the preset: Input evidence template packaged by the preset: +- `templates/design-requirement-intake-template.md` +- `templates/requirement-merge-report-template.md` - `templates/figma-evidence-packet-template.md` +- `templates/figma-intake-contract.md` Development-only contract helpers: @@ -249,23 +299,29 @@ Development-only contract helpers: ## Artifact Roles -`checklists/behavior-testability.md` is the BDD, NFR, and applicable Visual Fidelity readiness gate. It checks `spec.md` before planning so user stories, acceptance criteria, Given context, executable When actions, observable Then outcomes, explicit non-functional requirement declarations, and Figma-derived evidence are ready for behavior projection and planning. Each NFR dimension is marked Required, Not Applicable, or Unknown; missing or unverifiable NFR assumptions block planning when they affect downstream design. +`checklists/behavior-testability.md` is the BDD, NFR, and applicable Visual Fidelity readiness gate. It checks `spec.md` before planning so behavior, NFRs, design-derived evidence, and product-side visual requirements such as pixel-perfect, brand-critical, responsive visual, or UI visual acceptance requirements are ready for behavior projection and planning. Its Case Coverage Matrix uses one row per story or capability case type; rows mark Required, Not Applicable, or Unknown, cite source sections, and list Blocker IDs while Scenario IDs remain a `/speckit.plan` output. Its Visual Fidelity Evidence Matrix uses one row per visual requirement or visual proof obligation and is the single visual readiness record for source section, fidelity scope, screenshot level, evidence refs, visual proof requirement, blocking item ID, and exception rule. Missing Required case coverage, Unknown case applicability, or missing NFR criteria blocks planning when it affects downstream behavior projection or design. -`templates/figma-evidence-packet-template.md` defines how Figma-derived design facts are normalized before `/speckit.specify` writes requirements. It separates observed design facts, structural inferences, missing requirements, and excluded scope so Figma evidence does not get treated as complete product behavior. It references Figma intake contract results for raw metadata completeness, metadata index completeness proof, node inventory parity, and blocker lint errors before Figma-derived requirements can be treated as ready. +`templates/design-requirement-intake-template.md` defines the provider-neutral Design Requirement Intake shape for page inventory, hierarchy, user paths, component states, visual tokens, layout, responsive, motion, state coverage, visual acceptance requirements, screenshot traceability, and traceability. -`templates/figma-intake-contract.md` defines the raw Figma intake artifact contract for `figma-metadata.part-*.xml`, `figma-metadata.index.yaml`, and `figma-node-inventory.yaml`. It owns raw metadata completeness, metadata index completeness proof, node inventory parity, blocker lint errors, and the ready gate; the Evidence Packet references those results as normalized input for `spec.md`. +`templates/requirement-merge-report-template.md` defines how Product Requirement and Design Requirement inputs are reconciled before baseline `spec.md` generation. It records merge rules, product-owned facts, design-owned facts, design requirement promotion rules, conflicts, clarification outputs, and the `spec.md` handoff. + +`templates/figma-evidence-packet-template.md` defines how Figma-derived provider evidence is normalized before Design Requirement Intake and `/speckit.specify` write requirements. It separates observed design facts, screenshot evidence, structural inferences, missing requirements, and excluded scope so Figma evidence does not get treated as complete product behavior. It references Figma provider source readiness contract results for raw metadata completeness, metadata index completeness proof, node inventory parity, and blocker lint errors before Figma-derived requirements can be treated as ready. + +`templates/figma-intake-contract.md` defines the raw Figma provider source readiness contract for `figma-metadata.part-*.xml`, `figma-metadata.index.yaml`, and `figma-node-inventory.yaml`. It owns raw metadata completeness, metadata index completeness proof, node inventory parity, blocker lint errors, and the ready gate; the Evidence Packet references those results as normalized provider input for `spec.md`. `behavior/bdd.draft.feature` captures Phase 0 behavior projection in readable Given/When/Then form. `behavior/behavior-scenarios.draft.json`, `behavior/uif.intent.json`, and `behavior/data-fixtures.intent.json` make the same draft behavior machine-readable enough for planning formalization. -`contracts/bdd/`, `contracts/uif/`, and `contracts/behavior/` contain planning-phase formal behavior contracts. They are generated from Phase 0 drafts after planning has resolved fixture strategy, data model, interface contracts, and validation paths, unless planning records `N/A or blocker` for missing planning input. +`contracts/bdd/`, `contracts/uif/`, and `contracts/behavior/` contain planning-phase formal behavior contracts. They are generated from Phase 0 drafts after planning has resolved fixture strategy, data model, interface contracts, and validation paths, unless planning records `N/A or blocker` for missing planning input. `contracts/behavior/scenario-instances.json` carries `case_coverage_blockers` for Required cases that cannot be formalized. Failure scenarios must be structured enough to constrain implementation, including error code, failure feedback, and state invariant, rollback, or compensation assertion references. `class-diagram.md` captures internal implementation object structure: classes, interfaces, abstract types, composition, dependencies, references, and design pattern participants. It is the object design map that helps implementation preserve boundaries between services, adapters, repositories, strategies, factories, controllers, coordinators, and extension points. `contracts/sequences.md` captures service-call, command, event, external-system, retry, rollback, compensation, async, and failure-path sequencing. It is the flow design map that helps implementation preserve call order, service boundaries, async behavior, idempotency, compensation, and error propagation. Sequences always live at this path, even when there are no other contract files. +For visual planning, research.md records visual validation decisions by Visual Item ID, including viewport and state coverage, asset or fixture strategy, visual proof strategy, related contracts, and quickstart validation paths. contracts formalize visual interaction and state constraints by linking accepted visual items to Expected UIF, behavior scenarios, assertions, and supporting API/data schemas. contracts/sequences.md records visual state flow only when it affects cross-boundary sequencing, async results, retries, rollback, compensation, or error propagation; it does not redefine layout, tokens, screenshot matrices, or visual readiness. + Test strategy derivation happens during `/speckit.tasks`. The command derives unit, contract, integration, and end-to-end validation work from BDD contracts, Expected UIF contracts, behavior contracts, interface contracts, `research.md`, and `quickstart.md`, then writes the strategy inline on the relevant `tasks.md` checklist items. -The handoff context digest includes relevant design constraints, validation decisions, quickstart paths, and behavior contracts when present, so Worker Agents can preserve object boundaries, service flows, and validation intent without reading full planning documents by default. +The handoff context digest includes relevant design constraints, visual fidelity requirements, screenshot refs, visual proof refs, Design Requirement trace refs, validation decisions, quickstart paths, and behavior contracts when present, so Worker Agents can preserve object boundaries, service flows, visual intent, and validation intent without reading full planning documents by default. See `speckit-cross-agent-subagents.md` for the cross-platform subagent mapping, worker prompt, parallel dispatch rules, and minimal handoff/receipt contract. @@ -364,11 +420,11 @@ This repository owns preset artifact health: - publish or confirm the release artifact for a tag or manual release run; - create or update a `workflow-preset-release-v` integration PR in `bigsmartben/spec-kit` on tag releases or manual runs with `create_integration_pr=true`. -Manual release runs default to the next patch version when `version` is omitted. For example, a `preset.yml` version of `1.3.6` defaults to release version `1.3.7`. +Manual release runs default to the next patch version when `version` is omitted. For example, a `preset.yml` version of `1.3.7` defaults to release version `1.3.8`. The integration PR step requires a repository secret named `SPEC_KIT_FORK_PR_TOKEN` with permission to push branches and open pull requests in `bigsmartben/spec-kit`. If a tag release or manual `create_integration_pr=true` run reaches that step without the secret, the workflow fails fast instead of skipping integration PR creation. -This repository owns the release artifact and the fork integration PR. It does not open pull requests to `github/spec-kit`. The `bigsmartben/spec-kit` fork owns downstream integration validation, core compatibility fixes, catalog resolver checks, and any later community catalog PR flow. +This repository owns the release artifact and the fork integration PR. It does not open pull requests to `github/spec-kit`. The `bigsmartben/spec-kit` fork owns downstream integration validation, core workflow fixes, catalog resolver checks, and any later community catalog PR flow. Optional local CLI sanity check: @@ -383,7 +439,7 @@ Release install smoke validation is intentionally owned by GitHub Actions, not b After tagging a release, validate archive installation: ```bash -specify preset add workflow-preset --from https://github.com/bigsmartben/spec-kit-workflow-preset/releases/download/v1.3.6/spec-kit-workflow-preset-v1.3.6.zip +specify preset add workflow-preset --from https://github.com/bigsmartben/spec-kit-workflow-preset/releases/download/v1.3.7/spec-kit-workflow-preset-v1.3.7.zip ``` ## Source Rationale diff --git a/presets/workflow-preset/commands/speckit.analyze.md b/presets/workflow-preset/commands/speckit.analyze.md index 27d9cb2de4..1a14569274 100644 --- a/presets/workflow-preset/commands/speckit.analyze.md +++ b/presets/workflow-preset/commands/speckit.analyze.md @@ -11,6 +11,17 @@ Check that tasks preserve the planned `M + U` scope. Report missing, widened, or Analyze whether the feature artifacts close the `spec -> BDD/UIF intent -> contracts -> tasks` loop. This command checks planning consistency only; it does not inspect implementation code or infer interaction flows from built code. +## Analysis Performance Guardrails + +Keep analysis bounded to the existing planning artifacts. + +- 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. + Check: - spec.md user stories have BDD coverage. @@ -22,6 +33,11 @@ Check: - 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 checklist through implementation tasks. +- Required case types in `checklists/behavior-testability.md` map to behavior draft scenarios, formal behavior contracts, tasks, and quickstart validation paths. +- 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. @@ -29,4 +45,4 @@ Report missing, inconsistent, or stale links by source artifact and target artif ## Behavior Analysis Reporting -Before finishing, report whether the vertical consistency chain is closed and list blockers that should be resolved before implementation continues. +Before finishing, report whether the vertical consistency chain is closed and list blockers that prevent implementation from continuing. diff --git a/presets/workflow-preset/commands/speckit.checklist.md b/presets/workflow-preset/commands/speckit.checklist.md index 7ac295fef5..b10c6dda3e 100644 --- a/presets/workflow-preset/commands/speckit.checklist.md +++ b/presets/workflow-preset/commands/speckit.checklist.md @@ -1,8 +1,20 @@ --- -description: Wrap core checklist generation with BDD readiness gate. +description: Wrap core checklist generation with BDD, NFR, and Visual Fidelity readiness gate. strategy: wrap --- +## Checklist Purpose: "Unit Tests for English" + +This wrapper must not redefine core-owned User Input, Pre-Execution Checks, extension hooks, base path resolution, or core file handling. + +Checklists validate whether requirements are complete, clear, consistent, measurable, and ready for downstream planning. NOT for verification/testing: do not test implementation behavior, code execution, UI rendering, API responses, or whether the built system works. + +CORE PRINCIPLE - Test the Requirements, Not the Implementation. Checklist questions must use requirement-quality forms such as "Are ... specified?", "Is ... quantified?", "Can ... be objectively verified?", or "Are ... requirements consistent?" + +Use `$ARGUMENTS` as checklist intent. Generate dynamic clarifying questions with no pre-baked catalog only when the answer changes BDD, NFR, or Visual Fidelity checklist content. Use Q1/Q2/Q3 for initial questions and Q4/Q5 only for justified follow-up gaps. + +For `checklists/behavior-testability.md`, create the file when absent; otherwise append or update without deleting existing checklist content. Before finishing, report the full path, item count, update mode, focus areas, depth level, actor/timing, must-have items, readiness status, and blockers. + ## BDD Readiness Gate Create or update `checklists/behavior-testability.md` as checklist artifacts only. This checklist is the plan-entry quality gate for BDD readiness and must evaluate requirements directly from `spec.md`; it must not depend on behavior drafts. @@ -12,28 +24,34 @@ Include these sections: - User Story Readiness - Acceptance Criteria Quality - Scenario Coverage +- Case Coverage Matrix - Given Readiness - When Readiness - Then Readiness - Visual Fidelity Readiness +- Visual Fidelity Evidence Matrix - Non-Functional Requirement Readiness - Gate Status - Blocking Items -Check that each applicable user story has observable acceptance behavior, each acceptance criterion is verifiable, and primary, alternate, exception, boundary, permission, validation, and state-conflict paths are covered when applicable. +Check that each applicable user story has observable acceptance behavior, each acceptance criterion is verifiable, and primary, alternate, exception, boundary, permission, validation, and state_conflict paths are covered when applicable. + +Build a Case Coverage Matrix with one row per story or capability case type. Use case status: Required|Not Applicable|Unknown. Cover positive, negative, boundary, permission, validation, and state_conflict case types. Each row must have a stable Case ID. Required rows must cite the source `spec.md` section. Scenario IDs and `case_coverage_blockers` are assigned during `/speckit.plan`. Not Applicable requires rationale. Unknown must appear in Blocking Items. Required case type without observable acceptance behavior blocks PASS. Check Given readiness from `spec.md`: required roles, permissions, starting state, entity state, and data are explicit enough for later fixture setup. Check When readiness from `spec.md`: each trigger is an executable user action, request case, or system trigger. -Check Then readiness from `spec.md`: each outcome can become feedback, business state, error semantics, or assertion intent. +Check Then readiness from `spec.md`: each outcome maps to feedback, business state, error semantics, or assertion intent. -Check Visual Fidelity Readiness when `spec.md` contains Figma-derived requirements. -Require source traceability, ready gate evidence, and clear visual requirements for -state, responsive, accessibility, component mapping, and accepted exception -coverage. Missing raw metadata completeness, metadata index completeness proof, -node inventory parity, or blocker lint errors are blocking items for -Figma-derived requirements. +Check Visual Fidelity Readiness when `spec.md` contains design-derived requirements, a design source, provider evidence blockers, or provider-specific design evidence requests. Also apply it when `spec.md` contains product-side visual requirements such as pixel-perfect, brand-critical, responsive visual, or UI visual acceptance requirements. +Use the behavior-testability checklist template as the visual gate authority. +Require source traceability, provider readiness status, evidence refs, and blockers, and clear visual requirements for state, responsive, accessibility, component mapping, and accepted exception coverage. +Build a Visual Fidelity Evidence Matrix with one row per visual requirement or visual proof obligation. Record Screenshot evidence level, declared visual proof required, provider evidence refs or screenshot refs, and any Gate Status: BLOCKED item in Blocking Items. +Use one Visual Fidelity Evidence Matrix as the single visual readiness record; do not duplicate visual evidence decisions outside the matrix and Blocking Items. +Do not add historical visual rules or alternate visual decision paths. +Responsive visual requirements block PASS only when they are complex, multi-state, or declare L2 or L3 visual proof; missing viewport-specific evidence then sets Gate Status: BLOCKED and lists the item in Blocking Items. +Screenshots support visual facts but do not create product semantics. Check Non-Functional Requirement Readiness from `spec.md`: applicable performance, security and privacy, reliability and recovery, accessibility, compliance and auditability, observability, compatibility, data lifecycle, and cost or operational constraints are explicitly declared in `spec.md` as `Required`, `Not Applicable`, or `Unknown`. @@ -43,10 +61,10 @@ Treat these NFR readiness gaps as blocking items: Required but missing from `spe Set `Gate Status: PASS` only when every applicable readiness item is checked and `Blocking Items: none`. Otherwise set `Gate Status: BLOCKED` and list each unchecked readiness item that prevents behavior projection or downstream planning. -Unchecked readiness items that prevent behavior projection or downstream planning are blocking items. Do not proceed to `/speckit.plan`; Return to `/speckit.clarify` or `/speckit.specify` to resolve missing requirements before planning. +Unchecked readiness items that prevent behavior projection or downstream planning are blocking items. Do not proceed to `/speckit.plan`. Requirement ambiguity returns to `/speckit.clarify` or `/speckit.specify` to resolve missing requirements before planning. Provider evidence readiness blockers return to `/speckit.specify` or provider intake, not `/speckit.clarify`. {CORE_TEMPLATE} ## Behavior Checklist Reporting -Before finishing, report the BDD readiness status and call out unchecked items that block planning. +Before finishing, report the BDD, NFR, and Visual Fidelity readiness status and call out unchecked items that block planning. diff --git a/presets/workflow-preset/commands/speckit.clarify.md b/presets/workflow-preset/commands/speckit.clarify.md index 9505245c51..8b20fdaf36 100644 --- a/presets/workflow-preset/commands/speckit.clarify.md +++ b/presets/workflow-preset/commands/speckit.clarify.md @@ -5,17 +5,31 @@ strategy: wrap ## Spec-Only Clarification Policy +This wrapper must not redefine core-owned User Input, Pre-Execution Checks, extension hooks, base path resolution, or core file handling. + Use `spec.md` as the clarification source. Ask and record clarification only for requirement ambiguity that affects product behavior, constraints, non-functional requirement assumptions, acceptance criteria, user roles, permissions, entity states, data semantics, exceptions, validation rules, or boundaries. 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. -## Figma-Derived Clarification Strategy +## User Input + +Treat `$ARGUMENTS` as prioritization context for the current clarification run. Do not ask the user to restate requirements already present in `spec.md`. + +## Pre-Execution Checks + +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. + +## Outline + +## Design Requirement Clarification Strategy -When `spec.md` was created from a Figma Evidence Packet, prioritize clarification questions for Figma-derived gaps already written in `spec.md`. Scan `spec.md` first for `Missing / Needs clarification`, `[NEEDS CLARIFICATION]`, `Inferred from structure`, and gaps about Figma-unprovided states, responsive behavior, business rules, permissions, and error handling. +When `spec.md` was created from Design Requirement Intake or provider-specific evidence such as a Figma Evidence Packet, prioritize clarification questions for design-derived gaps already written in `spec.md`. Scan `spec.md` first for `Missing / Needs Clarification`, `[NEEDS CLARIFICATION]`, `Inferred from Structure`, and gaps about provider-unprovided states, responsive behavior, business rules, permissions, and error handling. -Do not call Figma MCP. Do not re-extract design facts, re-parse Figma links, or turn clarification into a Figma extraction step. `/speckit.specify` owns writing Figma evidence into `spec.md`; `/speckit.clarify` only selects high-impact questions from existing `spec.md` gaps and records confirmed answers. +Do not call Figma MCP. Do not re-extract design facts, re-parse Figma links, or turn clarification into a Figma extraction step. `/speckit.specify` owns writing qualified evidence-backed design-derived requirements and trace refs into `spec.md`; it does not write raw Figma evidence into `spec.md`. `/speckit.clarify` only selects high-impact questions from existing `spec.md` gaps and records confirmed answers. Do not ask the user to fix provider extraction artifacts. -Ask at most 5 high-impact questions whose answers materially affect requirements, implementation planning, or validation readiness. Prefer questions in this order: +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. + +Format recommendations as `**Recommended:** Option [X] - ` when a discrete 2-5 option choice is available. 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: 1. Required frames, states, and breakpoints for acceptance. 2. visual fidelity scope: pixel-perfect, design-system faithful, or functional equivalent. @@ -26,12 +40,25 @@ Ask at most 5 high-impact questions whose answers materially affect requirements 7. Prototype-uncovered navigation, dialogs, recovery paths, and failure handling. 8. acceptance evidence, visual-difference tolerance, and exception approval flow. -After the user answers, write confirmed answers back into `spec.md` in the relevant Requirements, User Scenarios, Acceptance Criteria, Assumptions, Open Questions, or visual/responsive/state sections. Do not create a separate Figma clarification document. +After each accepted answer, write confirmed answers back into `spec.md` in the relevant Requirements, User Scenarios, Acceptance Criteria, Assumptions, Open Questions, or visual/responsive/state sections. 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 Figma clarification document. Do not generate visual restoration checklists. Clarification fills requirement gaps in `spec.md`; `/speckit.checklist` remains responsible for checking requirement text quality and readiness. +## Validation after each 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. + +Do not update checklist artifacts. After each `spec.md` write, report checklist impact as unresolved readiness context for `/speckit.checklist`. + {CORE_TEMPLATE} -## Clarification Reporting +## Completion Report Before finishing, report answered questions, `spec.md` sections updated, and any unresolved requirement ambiguity that still blocks checklist readiness. + +## Done When + +- [ ] No more than 5 high-impact questions were asked. +- [ ] Each accepted answer was written back to `spec.md`. +- [ ] Validation after each write found no duplicate or contradictory clarification. +- [ ] Completion reported with sections touched and remaining blockers. diff --git a/presets/workflow-preset/commands/speckit.implement.md b/presets/workflow-preset/commands/speckit.implement.md index 6164c896bc..62948f891f 100644 --- a/presets/workflow-preset/commands/speckit.implement.md +++ b/presets/workflow-preset/commands/speckit.implement.md @@ -58,9 +58,10 @@ Map planned `U` design objects to concrete source, test, fixture, configuration, - write only `allowed_write_paths` - write `task_status_update.receipt_path` as `speckit.implement.receipt.v1` - validation_evidence must reference the relevant BDD scenario, behavior assertion, API contract, or quickstart path when the handoff context includes behavior contracts -- Code review tasks must echo `task_type: code_review`, write `review_conclusion.checked_sources`, `data_side_effect_review`, `consistency_repairs`, and `deferred_validation_todos` when applicable +- asset binding must map Client Asset Contract entries to local asset paths or code asset mappings from the handoff context; missing required client visual assets, mappings, variants, or fallbacks become `context_gaps` +- Code review tasks must echo `task_type: code_review`, write `review_conclusion.checked_sources`, `data_side_effect_review`, `consistency_repairs`, and `deferred_validation_todos`; use empty arrays or objects when there are no entries - For data side-effect review, inspect the actual implementation diff for runtime database writes and field-level update/delete behavior, bulk writes, soft deletes, ORM whole-object saves, migrations/backfills, retries, rollback/compensation, and external-system writes -- repair design, sequence, or contract drift and high-risk data side effects only inside `allowed_write_paths`; real e2e cannot run becomes a todo +- repair design, sequence, or contract drift and high-risk data side effects only inside `allowed_write_paths`; when real e2e cannot run, Worker receipt must record missing environment and commands in `deferred_validation_todos` - Do not edit `tasks.md`, create handoffs, dispatch workers ## Lifecycle `intake` -> `context_indexing` -> `vertical_planning` -> `manifest_assembly` -> `worker_dispatch` -> `worker_execution` -> `receipt_review` -> `code_review` -> `task_commit` -> `integration_verification` -> `closeout` @@ -81,9 +82,12 @@ Map planned `U` design objects to concrete source, test, fixture, configuration, - include relevant `class-diagram.md` and `contracts/sequences.md` constraints - include relevant `research.md` validation decisions and include relevant `quickstart.md` validation paths - include relevant `contracts/bdd/`, `contracts/uif/`, and `contracts/behavior/` behavior contract constraints +- include relevant visual fidelity requirements, screenshot refs, visual proof refs, and Design Requirement trace refs +- include relevant Client Asset Contract entries, asset source strategy, required variants, fallback policy, and blocker status - omit unrelated full `spec.md`, `plan.md`, `research.md`, `contracts/`, `class-diagram.md`, and `quickstart.md`; record unresolved required context as `context_gaps` ## Path Rules -- derive `allowed_write_paths` from paths referenced by assigned task text +- derive `allowed_write_paths` from paths referenced by assigned task text, including specific source, test, fixture, or configuration file paths for the planned `U` design object and target component or module +- If no concrete file path can be derived, record `context_gaps` - include receipt path in `allowed_write_paths` - derive `allowed_read_paths` from allowed write parents, validation files, context digest, and context index - include `tasks.md` in `allowed_read_paths` diff --git a/presets/workflow-preset/commands/speckit.plan.md b/presets/workflow-preset/commands/speckit.plan.md index a4c1e41dc1..3840258a1b 100644 --- a/presets/workflow-preset/commands/speckit.plan.md +++ b/presets/workflow-preset/commands/speckit.plan.md @@ -11,16 +11,16 @@ During planning, lock the change scope to `M + U`: module/capability plus design ## Design Artifact Policy -This preset preserves the core planning workflow and adds optional/contextual design artifacts for features that need more structure than `plan.md` should carry. +Core planning remains authoritative. Optional design artifacts carry structured details that do not belong in `plan.md`. -Generate the two design artifacts only when useful for the feature: +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 these artifacts concise. It is acceptable to create a short artifact with `N/A` sections when the reason is concrete, for example "No service boundary exists for this static documentation change." Do not create large placeholder files. +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 should point to detailed design artifacts when they exist, but it must not embed complete class diagrams or complete sequence diagrams. +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`. @@ -28,7 +28,7 @@ Validation strategy is not a standalone planning artifact. Planning-time validat ## Phase 0 Preflight -Before core research or design work, verify checklists/behavior-testability.md has passed: it must have `Gate Status: PASS` and `Blocking Items: none`. +Before core research or design work, verify checklists/behavior-testability.md has passed: it must have `Gate Status: PASS` and `Blocking Items: none` or a `Blocking Items` section containing only `- none`. If the checklist is missing, incomplete, has `Gate Status: BLOCKED`, or lists blocking items, stop with a report-only/no-write failure before planning artifacts are generated. Report an upstream gate failure with the missing checklist item or readiness gap. Do not create or update feature files, and must not create or update behavior artifacts. Return to `/speckit.checklist` or `/speckit.clarify` instead of repairing requirements inside planning. @@ -43,6 +43,8 @@ After Phase 0 preflight passes and before core research or design work, project - `behavior/uif.intent.json`: interaction intent extracted from accepted requirements. - `behavior/data-fixtures.intent.json`: data setup intent required by draft scenarios. +Required case types from `checklists/behavior-testability.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 with a report-only/no-write failure and return to `/speckit.checklist` or `/speckit.clarify`. + Phase 0 behavior projection is a projection step, not a new requirement-discovery step: - Do not discover new requirement problems. @@ -57,18 +59,18 @@ If Phase 0 cannot generate behavior drafts from a `spec.md` that passed checklis ## 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 when relevant: +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. - - Use Mermaid class diagrams by default when diagrams help, but text tables or PlantUML are acceptable if they better fit the project. + - 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. - - Use Mermaid sequence diagrams by default when diagrams help, but structured text is acceptable for simple flows. + - 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: @@ -83,6 +85,16 @@ When `plan.md` has a design artifact/navigation section, include links to: - Interface contracts: `./contracts/` - Validation path: `./quickstart.md` +When visual requirements are in scope, keep `plan.md` navigation linked to visual fidelity scope, screenshot refs, visual proof refs, and Design Requirement trace refs already accepted by `spec.md` and the readiness checklist. + +## Visual Planning Responsibilities + +When visual requirements are in scope, planning must keep the Visual Fidelity Evidence Matrix as the upstream readiness record and split visual carry-forward across the existing planning outputs: + +- `research.md`: add Visual validation decisions for each relevant Visual Item ID. Record selected test level, fixture or asset strategy, viewport/state coverage strategy, visual regression or baseline proof strategy, screenshot refs, visual proof refs, Design Requirement trace refs, related quickstart validation path, and related UIF or behavior contract path. Do not copy the Visual Fidelity Evidence Matrix into `research.md`, do not create new visual requirements, and do not call Figma or other provider tools. +- `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 Use the Phase 0 behavior projection drafts as planning inputs: @@ -92,7 +104,7 @@ Use the Phase 0 behavior projection drafts as planning inputs: - `behavior/uif.intent.json` - `behavior/data-fixtures.intent.json` -Use these drafts to guide research decisions, fixture strategy, data-model entities, interface contracts, and quickstart validation paths. +Phase 1 outputs must cite applicable draft scenario IDs or record `N/A or blocker`. During Phase 1, if behavior drafts exist and checklists/behavior-testability.md has passed, you must formalize them into formal behavior contracts: @@ -100,6 +112,8 @@ During Phase 1, if behavior drafts exist and checklists/behavior-testability.md - `contracts/uif/`: Expected UIF contracts. - `contracts/behavior/`: scenario instance, fixture, and assertion contracts. +Required case types from `checklists/behavior-testability.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. + When formalizing BDD Draft into `contracts/bdd/*.feature`: - Preserve scenario intent and business outcome from the draft. @@ -114,7 +128,7 @@ If behavior drafts exist but cannot be formalized, write `N/A or blocker` in the BDD draft reasoning must feed the normal planning outputs: - `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 when relevant, including `BehaviorScenarioInstance`, `DataFixture`, `UIFPath`, `FeedbackView`, and `BehaviorAssertion`. +- `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. @@ -131,7 +145,7 @@ Before finishing, the final report must list generated artifacts and state wheth Also report where validation decisions were recorded: -- `research.md`: selected test level, fixture strategy, mock/external-system strategy, and error-branch validation decisions when relevant. -- `quickstart.md`: executable validation paths for the planned behavior when relevant. +- `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. 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. diff --git a/presets/workflow-preset/commands/speckit.specify.md b/presets/workflow-preset/commands/speckit.specify.md index 62d7518ac6..a71b71b88f 100644 --- a/presets/workflow-preset/commands/speckit.specify.md +++ b/presets/workflow-preset/commands/speckit.specify.md @@ -4,40 +4,61 @@ strategy: wrap --- ## 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. -During specification, produce or update `spec.md` only. This command writes only `spec.md`. - +Preset-added requirement output writes only `spec.md`. Product requirements stay in `spec.md`: user stories, acceptance criteria, functional requirements, non-functional 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 should be explicit product-level assumptions or constraints, including no-special-requirement or not-applicable statements when that is the confirmed requirement. +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. + +## User Input +Treat product notes, PRDs, user prompts, design evidence, provider source packets, screenshots, visual proof, and source refs as input to the same feature description. If the core feature description is empty, follow the core command error path. + +## Pre-Execution Checks +Before writing design-derived requirements, check whether provider evidence is ready. This preset consumes qualified provider evidence; it does not call Figma MCP and does not generate provider artifact instances. + +For Figma-derived evidence, require a ready Figma Evidence Packet and Figma provider source readiness contract: raw metadata completeness, metadata index completeness proof, node inventory parity, and no blocker lint errors. If evidence is not ready, write explicit non-design requirements only and record Provider evidence readiness blockers as `[BLOCKED: PROVIDER_EVIDENCE]`; provider blockers must not become product `[NEEDS CLARIFICATION]` items. + +## Outline +Design Requirement Input Policy: run specification as staged intake and merge work without changing this command's write scope: + +### Stage 0: Product Requirement Intake +Product intake input: PRD, user prompt, product notes, and explicit product constraints. Product intake output: product-owned requirement facts in `spec.md`, including stories, functional requirements, non-functional requirements, assumptions, and product `[NEEDS CLARIFICATION]` markers. + +### Stage 1: Design Requirement Intake +Design intake input: provider-neutral design evidence, provider source packets, screenshots, visual proof, and source refs. Design intake output: evidence-backed design facts recorded only in `spec.md`, including Client Asset Contract facts: source refs, asset source strategy, required variants, fallback policy, and blocker status. + +If the design source is a Figma URL and a ready packet is supplied by a runtime agent or external Figma intake that has Figma MCP access: + +- Require a ready Figma Evidence Packet before writing design-derived requirements. +- Use the Figma provider source readiness contract; the preset defines the required design intake and provider readiness artifact structure and ready gate. +- Treat the runtime agent or external Figma intake as the source of artifact instances; this command consumes qualified evidence, does not call Figma MCP, and does not generate the artifact instances. +- If the packet is not ready, do not write design-derived requirements from that evidence. Write only explicit non-design requirements and record Provider evidence readiness blockers as `[BLOCKED: PROVIDER_EVIDENCE]`. + +Use `Observed from Figma` as design evidence. Treat `Inferred from Structure`, `Missing / Needs Clarification`, and `Out of Scope` as interpretation, unresolved requirements, and excluded evidence respectively. +Screenshots support visual facts only; screenshots must not create product semantics. Screenshot-implied business rules stay `[NEEDS CLARIFICATION]`. -## Figma URL Input Policy +If Figma MCP access is unavailable, Continue to write only `spec.md` and record `[BLOCKED: PROVIDER_EVIDENCE]` for the missing Figma Evidence Packet, screenshots, or design facts. -If the raw request is a Figma URL and the runtime agent has Figma MCP access: +### Stage 2: Requirement Merge +Merge input: product facts from Stage 0 plus qualified design facts from Stage 1. Merge output: confirmed baseline requirements, conflicts, assumptions, clarification markers, and provider blockers recorded in `spec.md`. Apply Design Requirement Promotion Rules: promote only evidence-backed visual, layout, state, interaction, responsive, accessibility, and acceptance facts; product semantics implied only by provider evidence stay `[NEEDS CLARIFICATION]`. -- Require a ready Figma Evidence Packet before writing Figma-derived requirements. -- Use the Figma intake contract: the preset defines the required Figma intake artifact structure and ready gate. -- Treat the runtime agent or external Figma intake as the source of artifact instances; this command consumes qualified evidence and does not generate the artifact instances. -- Apply the ready gate: -- `raw_metadata_complete: true` -- `node_inventory_coverage: 100%` -- `parity_passed: true` -- No blocker lint errors -- If the packet is not ready, do not write Figma-derived requirements from that evidence. Write only explicit non-Figma requirements and add `[NEEDS CLARIFICATION]` items for missing raw metadata, metadata index, node inventory parity, or blocker lint remediation. +### Stage 3: Generate baseline spec.md +Baseline spec output: one implementation-agnostic `spec.md` contract containing confirmed product requirements, qualified design-derived requirements, source refs, `[NEEDS CLARIFICATION]`, and `[BLOCKED: PROVIDER_EVIDENCE]` items. -Use `Observed from Figma` as design evidence. Treat `Inferred from Structure`, -`Missing / Needs Clarification`, and `Out of Scope` as interpretation, -unresolved requirements, and excluded evidence respectively. Mark business -semantics, permissions, validation, dynamic states, responsive behavior, error -handling, data semantics, and analytics as `[NEEDS CLARIFICATION]` unless packet -evidence explicitly confirms them. +## 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]`. -If Figma MCP access is unavailable, Continue to write only `spec.md` and add a -`[NEEDS CLARIFICATION]` item requesting a filled Figma Evidence Packet, -screenshots, or design facts. +## 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} -## Spec Reporting +## Completion Report +Before finishing, provide a stage-wise report and report the `spec.md` sections created or updated, confirmed requirements, provider blockers, and unresolved requirement ambiguities. -Before finishing, report the `spec.md` sections created or updated and list unresolved requirement ambiguities separately from confirmed requirements. +## Done When +- [ ] Stage 0-3 intake and merge decisions are reflected in `spec.md`. +- [ ] 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. diff --git a/presets/workflow-preset/commands/speckit.tasks.md b/presets/workflow-preset/commands/speckit.tasks.md index d13e4e35b8..0ab64a2280 100644 --- a/presets/workflow-preset/commands/speckit.tasks.md +++ b/presets/workflow-preset/commands/speckit.tasks.md @@ -9,12 +9,15 @@ Preserve the planned `M + U` scope in task text when deriving implementation, va ## Additional Design Inputs -When present, treat these files as optional task-generation inputs under FEATURE_DIR: +If any listed file exists under FEATURE_DIR, task generation must consume it as an input: - `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 test level, fixture strategy, mock/external-system strategy, and error-branch validation decisions. - `quickstart.md`: executable validation paths and evidence collection guidance. +- `spec.md` visual acceptance requirements: visual fidelity requirements, screenshot refs, visual proof refs, and Design Requirement trace refs. +- `spec.md` Client Asset Contract: asset source strategy, required variants, fallback policy, and blocker status. +- `checklists/behavior-testability.md` Visual Fidelity Readiness: passed visual proof level, 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. @@ -22,6 +25,10 @@ When present, treat these files as optional task-generation inputs under FEATURE Use these inputs to derive implementation, integration, orchestration, failure-handling, and validation tasks. For behavior contracts, derive test-first tasks 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. +For Client Asset Contract entries, derive asset preparation, binding, implementation, and validation tasks in dependency order. Missing required client visual assets become readiness blockers; do not generate handoff fields or `allowed_write_paths`. + +Missing Required case scenarios must become blockers, not silently skipped tasks. If `checklists/behavior-testability.md` marks a case type Required but the matching BDD or behavior contract is absent and no `N/A or blocker` exists, report the missing case instead of generating a complete-looking task list. + ## Test Strategy Derivation Do not create or require a standalone test strategy artifact. Instead, derive the test level, fixture/mock/sandbox/real-system strategy, and inline evidence requirement while generating `tasks.md`. @@ -38,10 +45,10 @@ Use this data and external-system strategy: - 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 `N/A or blocker`. - Use mock, sandbox, or real-system decisions from `research.md`. -- Prefer mock or sandbox for external systems 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, or security-relevant behavior. +- 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. -Every generated test or validation task must include an inline evidence requirement. Evidence should name the relevant BDD scenario, behavior assertion, API contract, UIF path, quickstart validation path, or command output. +Every generated test or validation task must include an inline evidence requirement. Evidence must name at least one relevant BDD scenario, behavior assertion, API contract, UIF path, quickstart validation path, visual proof ref, screenshot ref, or command output. Example task shape: @@ -53,16 +60,26 @@ Example task shape: Behavior task derivation must be explicit: - 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 `N/A` with a planning blocker. +- For each non-positive BehaviorScenarioInstance, derive fixture, contract or BDD test, implementation, and verification evidence tasks. The task text must preserve the negative, boundary, permission, validation, state_conflict, or error behavior and name the expected error code, failure feedback, and state invariant, rollback, or compensation assertion when present. - For each UIF user_event, create the frontend, CLI, or interaction task that emits or handles the event. - For each UIF 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. +UI implementation and acceptance tasks must be paired when a user story includes `contracts/uif/`, visual acceptance requirements, Visual Fidelity Readiness rows, or Client Asset Contract entries: + +- Create a UI implementation task for the concrete component, view, CLI surface, or interaction path that implements the referenced UIF event, UIF api_call, visual item, state, or asset binding. +- Create a paired UI acceptance task that verifies the same UIF path, Visual Item ID, scenario ID, asset contract entry, or quickstart validation path before the story is complete. +- Each UI acceptance task must name the required state coverage from the accepted contracts or readiness matrix, such as default, hover, focus, active, disabled, loading, empty, and error states. +- Each UI acceptance task must name the required viewport coverage from `research.md`, Visual Fidelity Readiness, or `quickstart.md` when responsive visual behavior is in scope. +- Each UI acceptance task must include evidence refs: at least one relevant UIF path, BDD or behavior scenario, visual proof ref, screenshot ref, quickstart validation path, API contract, or captured command output. +- If a required visual proof ref, screenshot ref, viewport/state coverage rule, Client Asset Contract entry, asset variant, or fallback policy is missing, report a readiness blocker instead of generating a complete-looking UI implementation or acceptance task. + ## Final Code Review When generating `tasks.md`, append the final phase after user-story tasks in the same checklist format. Add code review tasks that check design, sequence, and contract consistency against `class-diagram.md`, `contracts/sequences.md`, `contracts/`, `research.md`, and `quickstart.md`, plus data side-effect review and real e2e environment readiness. Code review task text must require review of the actual implementation diff for runtime database writes and other persistent data changes, especially 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. -Code review task evidence must require a `speckit.implement.receipt.v1` review receipt with `task_type: code_review`, `review_conclusion.checked_sources`, `data_side_effect_review`, plus `review_conclusion` and, when applicable, `consistency_repairs` and `deferred_validation_todos`. The task text must require quickstart/contract validation command evidence and state that implementation drift from the plan, sequences, contracts, or data side-effect review is repaired during `/speckit.implement` when the repair path is authorized; real e2e environment gaps are recorded as todos instead of treated as passing evidence. +Code review task evidence must require a `speckit.implement.receipt.v1` review receipt with `task_type: code_review`, `review_conclusion.checked_sources`, `data_side_effect_review`, `review_conclusion`, `consistency_repairs`, and `deferred_validation_todos`; empty arrays or objects indicate no entries. The task text must require quickstart/contract validation command evidence and state that implementation drift from the plan, sequences, contracts, or data side-effect review is repaired during `/speckit.implement` when the repair path is authorized; real e2e environment gaps are recorded as todos instead of treated as passing evidence. {CORE_TEMPLATE} diff --git a/presets/workflow-preset/docs/extension-governance.md b/presets/workflow-preset/docs/extension-governance.md index 9ff3f55e52..35a2797685 100644 --- a/presets/workflow-preset/docs/extension-governance.md +++ b/presets/workflow-preset/docs/extension-governance.md @@ -26,6 +26,10 @@ adapter scripts, or worker dispatch from scripts. Evidence templates: packaged evidence templates are allowed preset artifacts. Intake contract templates are also allowed when they define input shape, completeness gates, and blocker lint rules without executing an external system. +Design Requirement Intake and Requirement Merge templates may define +provider-neutral design facts and merge reports. Figma is a provider-specific design source; +Screenshot is provider evidence and visual proof. +Screenshots must not become the primary Design Requirement Intake carrier or a source of product semantics. Figma MCP execution, hooks, adapter scripts, and authentication are external integration concerns and remain outside this preset. @@ -44,16 +48,19 @@ Stage ownership: - `/speckit.constitution`: constitution governance and project principles only. - `/speckit.specify`: requirement artifacts only. - `/speckit.clarify`: requirement clarification only. -- `/speckit.checklist`: checklist artifacts and BDD/NFR readiness gates only. +- `/speckit.checklist`: checklist artifacts and BDD/NFR/Visual Fidelity readiness gates only. - `/speckit.plan`: Phase 0 behavior projection, planning artifacts, and formal contracts. - `/speckit.tasks`: `tasks.md` only. - `/speckit.analyze`: vertical consistency checks across requirements, behavior drafts, contracts, and tasks only. - `/speckit.implement`: implementation handoff execution only. -When a Figma Evidence Packet has already been written into `spec.md`, -`/speckit.clarify` may clarify those requirement gaps from `spec.md`, but +When Design Requirement Intake or a Figma Evidence Packet has already been +written into `spec.md`, `/speckit.clarify` may clarify those requirement gaps +from `spec.md`, but extraction remains outside clarification. external design extraction is not a clarification responsibility. +Visual Fidelity readiness applies to design-derived and product-side visual requirements such as pixel-perfect, brand-critical, responsive visual, or UI visual acceptance requirements. The Visual Fidelity Evidence Matrix is the single visual readiness record and uses one row per visual requirement or visual proof obligation with Source `spec.md` section, Fidelity Scope, Screenshot Level, Evidence Refs, Visual Proof Required, Blocking Item ID, and Exception Rule. Responsive visual requirements block PASS only when they are complex, multi-state, or declare L2 or L3 visual proof. + ## Structured Artifact Rules Machine-readable JSON artifacts are contracts, not prose examples. Stable @@ -100,6 +107,7 @@ NFR readiness belongs in `spec.md` product requirements rather than downstream planning guesses. Keep domain model details in `data-model.md`, interface schemas in `contracts/`, and validation run guidance in `quickstart.md`. +For visual planning, research.md records visual validation decisions only and must not duplicate the Visual Fidelity Evidence Matrix; contracts formalize visual interaction and state constraints by referencing accepted visual items, visual proof refs, and accepted exception refs; contracts/sequences.md records visual state flow only when it affects cross-boundary sequencing, async callbacks, retry, rollback, compensation, or error propagation, and must not define visual style, tokens, layout breakpoints, screenshot matrices, or validation commands. ## Handoff Extension Rules Handoff extensions must update schema, validator, command, and cross-agent documentation together. diff --git a/presets/workflow-preset/preset.yml b/presets/workflow-preset/preset.yml index a33edb835e..7aa9c9f5fd 100644 --- a/presets/workflow-preset/preset.yml +++ b/presets/workflow-preset/preset.yml @@ -2,7 +2,7 @@ schema_version: '1.0' preset: id: workflow-preset name: Workflow Preset - version: 1.3.6 + version: 1.3.7 description: Behavior-first specification, design artifacts, and agent-native handoff orchestration author: bigsmartben @@ -33,9 +33,24 @@ provides: - type: template name: figma-intake-contract-template file: templates/figma-intake-contract.md - description: Define the Figma intake artifact contract for raw metadata readiness + description: Define the Figma provider source readiness contract for raw metadata + readiness replaces: figma-intake-contract-template strategy: replace + - type: template + name: design-requirement-intake-template + file: templates/design-requirement-intake-template.md + description: Define the Design Requirement Intake format for provider-neutral + design facts + replaces: design-requirement-intake-template + strategy: replace + - type: template + name: requirement-merge-report-template + file: templates/requirement-merge-report-template.md + description: Define the Requirement Merge report format before baseline spec.md + generation + replaces: requirement-merge-report-template + strategy: replace - type: command name: speckit.specify file: commands/speckit.specify.md @@ -51,7 +66,8 @@ provides: - type: command name: speckit.checklist file: commands/speckit.checklist.md - description: Wrap core checklist generation with BDD and NFR readiness gate + description: Wrap core checklist generation with BDD, NFR, and Visual Fidelity + readiness gate replaces: speckit.checklist strategy: wrap - type: command @@ -112,7 +128,7 @@ provides: - type: template name: behavior-testability-checklist-template file: templates/behavior/behavior-testability-checklist.md - description: Template for BDD and NFR readiness gate output + description: Template for BDD, NFR, and Visual Fidelity readiness gate output replaces: behavior-testability-checklist-template strategy: replace - type: template 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 5e0268469c..7ac37a3d01 100644 --- a/presets/workflow-preset/schemas/speckit.behavior.assertions.v1.schema.json +++ b/presets/workflow-preset/schemas/speckit.behavior.assertions.v1.schema.json @@ -19,7 +19,17 @@ "id": {"type": "string", "minLength": 1}, "target": {"type": "string", "minLength": 1}, "operator": {"enum": ["equals", "not_equals", "contains", "exists", "matches"]}, - "expected": {} + "expected": {}, + "intent": { + "enum": [ + "business_state", + "error_code", + "failure_feedback", + "state_invariant", + "rollback", + "compensation" + ] + } } } } 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 f39a5bef1f..baa30bfe6c 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 @@ -9,12 +9,90 @@ "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} + } + } + }, "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", @@ -43,16 +121,30 @@ "additionalProperties": true, "required": ["id"], "properties": { - "id": {"type": "string", "minLength": 1} + "id": {"type": "string", "minLength": 1}, + "case_kind": { + "enum": ["positive", "negative", "boundary", "permission", "validation", "state_conflict"] + }, + "outcome": {"enum": ["success", "failure"]}, + "trigger": {"type": "string", "minLength": 1} } }, "expected_response": { "type": "object", - "additionalProperties": true + "additionalProperties": true, + "properties": { + "business_code": {"type": "string", "minLength": 1}, + "status": {"type": ["integer", "string"]}, + "error_code": {"type": "string", "minLength": 1} + } }, "expected_feedback": { "type": "object", - "additionalProperties": true + "additionalProperties": true, + "properties": { + "type": {"type": "string", "minLength": 1}, + "message": {"type": "string", "minLength": 1} + } }, "assertion_ids": { "type": "array", diff --git a/presets/workflow-preset/templates/behavior/assertions.json b/presets/workflow-preset/templates/behavior/assertions.json index f88ace61b3..cef4516207 100644 --- a/presets/workflow-preset/templates/behavior/assertions.json +++ b/presets/workflow-preset/templates/behavior/assertions.json @@ -5,7 +5,15 @@ "id": "AST-001", "target": "entity.field", "operator": "equals", - "expected": "value" + "expected": "value", + "intent": "business_state" + }, + { + "id": "AST-ERR-001", + "target": "entity.field", + "operator": "equals", + "expected": "unchanged", + "intent": "state_invariant" } ] } diff --git a/presets/workflow-preset/templates/behavior/behavior-testability-checklist.md b/presets/workflow-preset/templates/behavior/behavior-testability-checklist.md index a8afd06445..4215018cd0 100644 --- a/presets/workflow-preset/templates/behavior/behavior-testability-checklist.md +++ b/presets/workflow-preset/templates/behavior/behavior-testability-checklist.md @@ -8,12 +8,27 @@ ## Acceptance Criteria Quality - [ ] Acceptance criteria are observable and verifiable from `spec.md`. - [ ] Acceptance criteria avoid implementation-only wording. -- [ ] Business rules include precise success, rejection, validation, permission, boundary, and state-conflict outcomes when applicable. +- [ ] Business rules include precise success, rejection, validation, permission, boundary, and state_conflict outcomes when applicable. ## Scenario Coverage - [ ] Primary success behavior is covered. - [ ] Alternate and exception behavior is covered when applicable. -- [ ] Boundary, permission, validation, and state-conflict behavior is covered when applicable. +- [ ] Boundary, permission, validation, and state_conflict behavior is covered when applicable. + +## Case Coverage Matrix +For each user story or capability, record one row per story or capability case type. Status: Required|Not Applicable|Unknown. + +| Case ID | Story/Capability | Case Type | Status | Source `spec.md` section | Blocking Item ID | Rationale | +| --- | --- | --- | --- | --- | --- | --- | +| CASE-PERMISSION-001 | Example | permission | Required | `spec.md#...` | | reason | +| CASE-BOUNDARY-001 | Example | boundary | Not Applicable | `spec.md#...` | | reason | +| CASE-VALIDATION-001 | Example | validation | Unknown | `spec.md#...` | BI-... | missing rule | + +- [ ] Required case type must cite the source `spec.md` section. +- [ ] Each row must have a stable Case ID. +- [ ] Scenario IDs and `case_coverage_blockers` are assigned during `/speckit.plan`. +- [ ] Not Applicable requires rationale. +- [ ] Unknown must appear in Blocking Items. ## Given Readiness - [ ] Required roles and permissions are explicit. @@ -25,7 +40,7 @@ - [ ] Required inputs, selections, uploads, and submitted values are explicit. ## Then Readiness -- [ ] Each outcome can become user feedback, business state, error semantics, or assertion intent. +- [ ] Each outcome maps to user feedback, business state, error semantics, or assertion intent. - [ ] Failure outcomes include precise feedback or error semantics. ## Non-Functional Requirement Readiness @@ -42,15 +57,38 @@ - [ ] Unknown NFR entries that affect downstream design are listed as blocking items. ## Visual Fidelity Readiness -- [ ] Figma-derived requirements identify the source Figma URL, frame or node IDs, and required fidelity. -- [ ] Figma intake ready gate evidence in `spec.md` shows raw metadata completeness, metadata index completeness proof, node inventory parity, and no blocker lint errors. +- [ ] Apply this section when `spec.md` contains design-derived requirements, a design source, provider evidence blockers, or provider-specific design evidence requests. Also apply it when `spec.md` contains product-side visual requirements such as pixel-perfect, brand-critical, responsive visual, or UI visual acceptance requirements. +- [ ] Design-derived requirements identify the design source, provider source refs, and required fidelity. +- [ ] Design-derived requirements record provider readiness status, evidence refs, and blockers when provider evidence is required. +- [ ] Visual Fidelity Evidence Matrix rows cite source `spec.md` sections, evidence refs, required screenshot level, blocking item IDs, and accepted exception rules. +- [ ] Use one Visual Fidelity Evidence Matrix as the single visual readiness record; do not duplicate visual evidence decisions outside the matrix and Blocking Items. +- [ ] Do not add historical visual rules or alternate visual decision paths. + +## Visual Fidelity Evidence Matrix + +| Visual Item ID | Source `spec.md` section | Fidelity Scope | Screenshot Level | Evidence Refs | Visual Proof Required | Blocking Item ID | Exception Rule | +| --- | --- | --- | --- | --- | --- | --- | --- | +| VIS-001 | `spec.md#...` | functional-equivalent|design-system-faithful|pixel-perfect|brand-critical|responsive-visual | L0|L1|L2|L3 | provider/screenshot refs or none | yes|no | BI-... or none | EX-... or none | + +- [ ] Screenshot evidence level is declared when screenshots are required: L0|L1|L2|L3. +- [ ] visual proof refs point to provider evidence or screenshot sources. +- [ ] declared visual proof required is recorded when `spec.md` makes screenshot-backed visual proof mandatory. +- [ ] Ordinary UI visual requirements may use L1 Key Screenshots; `spec.md` visual proof requirements require L1 or higher. +- [ ] L2 State + Viewport Matrix covers key page, state, and viewport combinations for complex UI, responsive, or multi-state requirements. +- [ ] L3 Visual Baseline is present for high-fidelity visual matching, pixel-perfect requirements, brand-critical pages, design systems, or visual regression. +- [ ] Missing screenshot evidence sets Gate Status: BLOCKED and lists the item in Blocking Items when visual proof is required. +- [ ] High-fidelity requirements without L3 screenshot evidence set Gate Status: BLOCKED and lists the item in Blocking Items. +- [ ] Pixel-perfect requirements without L3 screenshot evidence set Gate Status: BLOCKED and lists the item in Blocking Items. +- [ ] Responsive visual requirements block PASS only when they are complex, multi-state, or declare L2 or L3 visual proof; missing viewport-specific evidence then sets Gate Status: BLOCKED and lists the item in Blocking Items. +- [ ] Complex UI or multi-state requirements without L2 or L3 screenshot evidence set Gate Status: BLOCKED and lists the item in Blocking Items. - [ ] Layout, spacing, typography, colors, effects, assets, and clipping requirements are explicit. +- [ ] Required client visual assets have source refs, asset source strategy, required variants, fallback policy, and blocker status. - [ ] Required component mappings and variant coverage are explicit or marked as blocking clarification items. - [ ] Default, hover, focus, active, disabled, loading, empty, and error states are explicit or marked as missing. - [ ] Required breakpoints, reflow rules, scrolling, minimum widths, safe areas, and responsive behavior is explicit. - [ ] Copy, icons, images, fonts, numeric formats, and placeholder content are explicit. - [ ] Keyboard, focus, semantics, contrast, ARIA, form error behavior, and accessibility requirements are explicit. -- [ ] Visual differences that may be accepted are defined as traceable exception rules. +- [ ] Accepted visual differences are defined as traceable exception rules. ## Gate Status Gate Status: PASS|BLOCKED diff --git a/presets/workflow-preset/templates/behavior/scenario-instances.json b/presets/workflow-preset/templates/behavior/scenario-instances.json index 6413f5907b..5650fd5665 100644 --- a/presets/workflow-preset/templates/behavior/scenario-instances.json +++ b/presets/workflow-preset/templates/behavior/scenario-instances.json @@ -1,5 +1,15 @@ { "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", @@ -17,6 +27,28 @@ "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/design-requirement-intake-template.md b/presets/workflow-preset/templates/design-requirement-intake-template.md new file mode 100644 index 0000000000..8052e165f2 --- /dev/null +++ b/presets/workflow-preset/templates/design-requirement-intake-template.md @@ -0,0 +1,130 @@ +# Design Requirement Intake + +Purpose: normalize provider-neutral design requirements before Requirement Merge writes `spec.md`. + +## Design Sources + +- Source type: +- Source URL or path: +- Provider Evidence: +- Capture timestamp: +- Required fidelity: + +## Page Inventory + +- Page or screen: +- Purpose: +- Source refs: + +## Page Hierarchy + +- Navigation structure: +- Parent / child relationships: +- Modal, drawer, or overlay relationships: + +## User Paths + +- Entry point: +- Interaction path: +- Destination or outcome: +- Missing path evidence: + +## Component Inventory + +- Component: +- Required variants: +- Source refs: +- Existing code mapping: + +## Component States + +- Default: +- Hover / focus / active: +- Disabled: +- Loading: +- Empty: +- Error: +- Success: + +## Interaction Rules + +- User action: +- System response: +- Validation or guard: +- Feedback: + +## Visual Tokens + +- Typography: +- Color: +- Spacing: +- Radius: +- Effects: +- Assets: + +## Layout Rules + +- Grid or alignment: +- Sizing: +- Scroll behavior: +- Clipping: +- Safe areas: + +## Responsive Rules + +- Breakpoints: +- Reflow behavior: +- Minimum and maximum widths: +- Long-copy handling: + +## Motion Rules + +- Transition: +- Duration: +- Easing: +- Reduced-motion requirement: + +## State Coverage + +- Covered states: +- Missing states: +- Items marked `[NEEDS CLARIFICATION]`: + +## Visual Acceptance Requirements + +- Required fidelity: +- Visual difference tolerance: +- Accepted exceptions: +- Accessibility requirements: + +## Client Asset Contract + +- Asset ID: +- Required resource type: +- Source refs: +- Asset source strategy: +- Required variants: +- Fallback policy: +- Blocker status: + +## Screenshot Traceability + +Design Requirement Intake remains provider-neutral. + +Screenshot-derived visual facts must include screenshot refs; screenshots must not create product semantics. +Keep screenshot files in provider evidence or `sources/`; +record only requirement-level references here. + +- Requirement ID: +- Screenshot refs: +- Visual proof refs: +- Supported visual facts: +- Unsupported assumptions: +- Confidence: + +## Traceability + +- Requirement ID: +- Source refs: +- Confidence: +- Provider notes: diff --git a/presets/workflow-preset/templates/figma-evidence-packet-template.md b/presets/workflow-preset/templates/figma-evidence-packet-template.md index b1b8287ae7..a4f2903d64 100644 --- a/presets/workflow-preset/templates/figma-evidence-packet-template.md +++ b/presets/workflow-preset/templates/figma-evidence-packet-template.md @@ -1,8 +1,7 @@ # Figma Evidence Packet -Use this packet to normalize design evidence before `/speckit.specify` writes -Figma-derived requirements. Raw intake readiness is governed by -`templates/figma-intake-contract.md`. +Purpose: normalize Figma design evidence before Design Requirement Intake and +`/speckit.specify`. Readiness gate: `templates/figma-intake-contract.md`. ## Figma Source @@ -20,6 +19,34 @@ Figma-derived requirements. Raw intake readiness is governed by - Variables / styles captured: - Component metadata captured: +## Screenshot Evidence + +Screenshot evidence must declare L0-L3 coverage and coverage gaps. Screenshots are visual proof, not the primary Design Requirement Intake carrier. +Constraint: screenshot-derived visual facts require screenshot refs and must not replace Design Requirement Intake. + +- Screenshot level: L0|L1|L2|L3 +- L0: no screenshot evidence +- L1: static screenshot reference +- L2: viewport or state screenshot coverage +- L3: visual diff baseline or approved visual proof +- L3 applies to high-fidelity, pixel-perfect, brand-critical, design-system, or visual regression work + +## Screenshot Coverage Matrix + +- Requirement ID: +- Screenshot level: +- Screenshot refs: +- Frame / node refs: +- Viewport: +- State: +- Capture timestamp: +- Design version: +- Redaction required: +- Baseline usage: +- Missing coverage: +- Blocking item: +- Visual baseline usage: none|manual review|visual diff + ## Figma Intake Readiness - figma-metadata.part-*.xml: @@ -33,8 +60,7 @@ Figma-derived requirements. Raw intake readiness is governed by ## Evidence Record Format -Use this record for extracted facts in observed, inferred, missing, and -out-of-scope sections. +Record schema for observed, inferred, missing, and out-of-scope facts. - Fact ID: - Evidence type: Observed|Inferred|Missing|Out of Scope @@ -90,6 +116,18 @@ out-of-scope sections. - Accessibility evidence: - Accepted exceptions: +## Client Asset Inventory + +- Asset ID: +- Asset role: +- Resource type: image|icon|video|lottie|svg|font +- Figma node/component ref: +- Asset source strategy: figma_export_required|code_asset|existing_repo_asset|remote_runtime_asset +- Export/use contract: +- Required variants: +- Fallback policy: +- Blocker status: + ## Component Mapping - Figma component -> code component: @@ -98,7 +136,7 @@ out-of-scope sections. ## Spec Handoff Notes -- Requirement sections this evidence can support: +- Supported requirement sections: - Clarification items that must remain unresolved: - Source refs required in `spec.md`: diff --git a/presets/workflow-preset/templates/figma-intake-contract.md b/presets/workflow-preset/templates/figma-intake-contract.md index 65544bf993..c667425be3 100644 --- a/presets/workflow-preset/templates/figma-intake-contract.md +++ b/presets/workflow-preset/templates/figma-intake-contract.md @@ -1,7 +1,8 @@ # Figma Intake Contract -This preset defines the required artifact formats and gates. The runtime agent -or external Figma intake performs extraction. +Required Figma intake artifacts and readiness gates. The runtime agent or +external Figma intake performs extraction before Design Requirement Intake +consumes provider evidence. ## Raw Metadata Shards @@ -96,11 +97,11 @@ nodes, or marked ready without completeness proof. ## Preset Boundary -The preset defines the required artifact formats and gates. +Preset boundary: -- does not call Figma MCP -- does not fetch Figma URLs -- does not write `figma-metadata.part-*.xml` -- does not run adapter scripts -- does not authenticate to Figma -- does not generate artifact instances +- must not call Figma MCP +- must not fetch Figma URLs +- must not write `figma-metadata.part-*.xml` +- must not run adapter scripts +- must not authenticate to Figma +- must not generate artifact instances diff --git a/presets/workflow-preset/templates/plan-template.md b/presets/workflow-preset/templates/plan-template.md index 909919598b..81712f7444 100644 --- a/presets/workflow-preset/templates/plan-template.md +++ b/presets/workflow-preset/templates/plan-template.md @@ -11,3 +11,10 @@ - Data model: `./data-model.md` - Interface contracts: `./contracts/` - Validation path: `./quickstart.md` + +## Visual fidelity navigation + +- Visual validation decisions: `./research.md` +- Visual interaction contracts: `./contracts/uif/` and `./contracts/behavior/` +- Visual flow sequences: `./contracts/sequences.md` +- Visual proof execution: `./quickstart.md` diff --git a/presets/workflow-preset/templates/requirement-merge-report-template.md b/presets/workflow-preset/templates/requirement-merge-report-template.md new file mode 100644 index 0000000000..3e4222b409 --- /dev/null +++ b/presets/workflow-preset/templates/requirement-merge-report-template.md @@ -0,0 +1,71 @@ +# Requirement Merge Report + +Purpose: normalize Product Requirement and Design Requirement merge decisions for baseline `spec.md`. + +## Product Requirement Inputs + +- Source: +- Intake artifact: +- Confirmed product facts: +- Open product gaps: + +## Design Requirement Inputs + +- Source: +- Intake artifact: +- Confirmed design facts: +- Open design gaps: + +## Merge Rules + +Product Requirement owns: + +- business goals +- user roles and permissions +- data semantics +- validation rules +- interface semantics +- exception handling +- non-functional requirements + +Design Requirement owns: + +- page structure +- information hierarchy +- interaction paths +- component states +- visual tokens +- layout and responsive behavior +- motion behavior +- visual acceptance requirements + +## Design Requirement Promotion Rules + +- Promote observed design facts with source refs to design requirements. +- Promote confirmed design facts to design requirements. +- Inferred design facts remain assumptions or `[NEEDS CLARIFICATION]`. +- Missing design facts remain `[NEEDS CLARIFICATION]`. +- Promote screenshot-supported visual facts only as visual requirements with screenshot refs. +- Screenshot-implied business rules must remain `[NEEDS CLARIFICATION]`. + +## Conflict Resolution + +- Conflict ID: +- Product requirement source: +- Design requirement source: +- Resolution: +- Clarification needed: + +## Clarification Outputs + +- Item: +- Target `spec.md` section: +- Marker: `[NEEDS CLARIFICATION]` +- Blocking impact: + +## Baseline Spec Handoff + +- `spec.md` sections to create or update: +- Confirmed requirements: +- Unresolved requirement ambiguities: +- Source refs required in `spec.md`: diff --git a/presets/workflow-preset/tests/test_preset_contract.py b/presets/workflow-preset/tests/test_preset_contract.py index e7757d5fd7..2f24ca2b61 100644 --- a/presets/workflow-preset/tests/test_preset_contract.py +++ b/presets/workflow-preset/tests/test_preset_contract.py @@ -2,6 +2,7 @@ import unittest import json +import re from pathlib import Path import yaml @@ -9,6 +10,7 @@ from jsonschema.exceptions import ValidationError from validators.speckit_implement_contract import ( + validate_behavior_case_coverage, validate_behavior_contract_bundle, validate_behavior_draft_contract, validate_implement_contract, @@ -38,7 +40,32 @@ FIGMA_EVIDENCE_PACKET_TEMPLATE_PATH = ( REPO_ROOT / "templates" / "figma-evidence-packet-template.md" ) + +CANONICAL_RESPONSIVE_VISUAL_RULE = ( + "Responsive visual requirements block PASS only when they are complex, " + "multi-state, or declare L2 or L3 visual proof" +) +FORBIDDEN_VISUAL_COMPAT_TERMS = ( + "legacy visual", + "previous-version", + "previous version", + "backward-compatible", + "backward compatible", + "fallback visual", + "fallback visual rule", + "compatibility mode", + "历史版本", + "旧版兼容", + "兼容旧版", + "回退视觉规则", +) FIGMA_INTAKE_CONTRACT_TEMPLATE_PATH = REPO_ROOT / "templates" / "figma-intake-contract.md" +DESIGN_REQUIREMENT_INTAKE_TEMPLATE_PATH = ( + REPO_ROOT / "templates" / "design-requirement-intake-template.md" +) +REQUIREMENT_MERGE_REPORT_TEMPLATE_PATH = ( + REPO_ROOT / "templates" / "requirement-merge-report-template.md" +) REQUIREMENTS_DEV_PATH = REPO_ROOT / "requirements-dev.txt" MANIFEST_SCHEMA_PATH = REPO_ROOT / "schemas" / "speckit.implement.manifest.v1.schema.json" HANDOFF_SCHEMA_PATH = REPO_ROOT / "schemas" / "speckit.implement.handoff.v2.schema.json" @@ -284,15 +311,19 @@ def minimal_receipt( return receipt -def minimal_behavior_scenarios_draft() -> dict: +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": "SCN-001", + "id": scenario_id, "title": "Submit refund", - "type": "positive", + "type": scenario_type, "given": ["FIX-BUYER"], "when": ["click_refund", "submit_refund"], "then": ["show_refund_submitted"], @@ -368,6 +399,61 @@ def minimal_behavior_scenario_instances() -> dict: } +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", + } + ] + } + + def minimal_behavior_data_fixtures() -> dict: return { "contract_type": "speckit.behavior.data_fixtures.v1", @@ -398,6 +484,16 @@ def minimal_behavior_assertions() -> dict: } +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_preset_manifest_contract(self) -> None: data = yaml.safe_load(PRESET_PATH.read_text(encoding="utf-8")) @@ -405,7 +501,7 @@ def test_preset_manifest_contract(self) -> None: self.assertEqual("1.0", data["schema_version"]) self.assertEqual("workflow-preset", data["preset"]["id"]) self.assertEqual("Workflow Preset", data["preset"]["name"]) - self.assertEqual("1.3.6", data["preset"]["version"]) + self.assertEqual("1.3.7", data["preset"]["version"]) self.assertEqual( "Behavior-first specification, design artifacts, and agent-native handoff orchestration", data["preset"]["description"], @@ -423,7 +519,7 @@ def test_preset_manifest_contract(self) -> None: ) provides = data["provides"]["templates"] - self.assertEqual(32, len(provides)) + self.assertEqual(34, len(provides)) entries = {entry["name"]: entry for entry in provides} self.assertNotIn("behavior-open-questions-template", entries) self.assertNotIn("speckit-behavior-open-questions-v1-schema", entries) @@ -458,7 +554,33 @@ def test_preset_manifest_contract(self) -> None: self.assertEqual("templates/figma-intake-contract.md", figma_intake_contract["file"]) self.assertEqual("figma-intake-contract-template", figma_intake_contract["replaces"]) self.assertEqual("replace", figma_intake_contract["strategy"]) - self.assertIn("Figma intake artifact contract", figma_intake_contract["description"]) + self.assertIn("Figma provider source readiness contract", figma_intake_contract["description"]) + + design_intake_template = entries["design-requirement-intake-template"] + self.assertEqual("template", design_intake_template["type"]) + self.assertEqual( + "templates/design-requirement-intake-template.md", + design_intake_template["file"], + ) + self.assertEqual( + "design-requirement-intake-template", + design_intake_template["replaces"], + ) + self.assertEqual("replace", design_intake_template["strategy"]) + self.assertIn("Design Requirement Intake", design_intake_template["description"]) + + merge_report_template = entries["requirement-merge-report-template"] + self.assertEqual("template", merge_report_template["type"]) + self.assertEqual( + "templates/requirement-merge-report-template.md", + merge_report_template["file"], + ) + self.assertEqual( + "requirement-merge-report-template", + merge_report_template["replaces"], + ) + self.assertEqual("replace", merge_report_template["strategy"]) + self.assertIn("Requirement Merge", merge_report_template["description"]) for command_name in ("speckit.plan", "speckit.tasks"): command = entries[command_name] @@ -480,7 +602,7 @@ def test_preset_manifest_contract(self) -> None: entries["speckit.clarify"]["description"], ) self.assertEqual( - "Wrap core checklist generation with BDD and NFR readiness gate", + "Wrap core checklist generation with BDD, NFR, and Visual Fidelity readiness gate", entries["speckit.checklist"]["description"], ) @@ -567,7 +689,7 @@ def test_plan_command_wrapper_contract(self) -> None: self.assertIn("contracts/sequences.md", command) self.assertNotIn("test-plan.md", command) self.assertIn("strategy: wrap", command) - self.assertIn("Generate the two design artifacts only when useful", 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) @@ -587,6 +709,43 @@ def test_plan_template_navigation_contract(self) -> None: 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") + + for term in ( + "Visual Planning Responsibilities", + "Visual validation decisions", + "Visual Item ID", + "viewport/state coverage strategy", + "visual regression or baseline proof strategy", + "Do not copy the Visual Fidelity Evidence Matrix into `research.md`", + "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", + ): + self.assertIn(term, command) + + for term in ( + "Visual fidelity navigation", + "Visual validation decisions: `./research.md`", + "Visual interaction contracts: `./contracts/uif/` and `./contracts/behavior/`", + "Visual flow sequences: `./contracts/sequences.md`", + ): + self.assertIn(term, template) + + for document in (readme, governance): + self.assertIn("research.md records visual validation decisions", document) + self.assertIn("contracts formalize visual interaction and state constraints", document) + self.assertIn("contracts/sequences.md records visual state flow only when it affects cross-boundary sequencing", document) + 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") @@ -671,7 +830,7 @@ def test_tasks_command_wrapper_contract(self) -> None: self.assertIn("consistency_repairs", tasks) self.assertIn("deferred_validation_todos", tasks) self.assertIn("quickstart/contract validation command", tasks) - self.assertIn("`review_conclusion` and, when applicable", tasks) + self.assertIn("empty arrays or objects indicate no entries", tasks) self.assertNotIn("must require a `speckit.implement.receipt.v1` review receipt with `review_conclusion`, `consistency_repairs`, and `deferred_validation_todos`", tasks) def test_behavior_first_command_wrapper_contracts(self) -> None: @@ -682,37 +841,81 @@ def test_behavior_first_command_wrapper_contracts(self) -> None: for command in (specify, clarify, checklist): self.assertIn("{CORE_TEMPLATE}", command) self.assertIn("strategy: wrap", command) + 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("Spec-Only Requirement Policy", specify) - self.assertIn("produce or update `spec.md` only", specify) - self.assertIn("This command writes only `spec.md`", 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("report the `spec.md` sections created or updated", specify) for term in ( - "Figma URL Input Policy", + "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 ( + "Design Requirement Input Policy", + "Stage 0: Product Requirement Intake", + "Product intake input", + "Product intake output", + "Stage 1: Design Requirement Intake", + "Design intake input", + "Design intake output", + "recorded only in `spec.md`", + "provider-neutral design evidence", + "source refs", + "Stage 2: Requirement Merge", + "Merge input", + "Merge output", + "Design Requirement Promotion Rules", + "conflicts", + "provider blockers", + "Stage 3: Generate baseline spec.md", + "Baseline spec output", "Figma Evidence Packet", - "Figma intake contract", - "runtime agent has Figma MCP access", + "Figma provider source readiness contract", + "ready packet is supplied by a runtime agent or external Figma intake that has Figma MCP access", "runtime agent or external Figma intake", - "preset defines the required Figma intake artifact structure", + "does not call Figma MCP", + "preset defines the required design intake and provider readiness artifact structure", "does not generate the artifact instances", "ready gate", "not ready", - "do not write Figma-derived requirements", + "do not write design-derived requirements", + "metadata index completeness proof", + "Provider evidence readiness blockers", + "[BLOCKED: PROVIDER_EVIDENCE]", + "must not become product `[NEEDS CLARIFICATION]` items", "blocker lint errors", "Observed from Figma", "Inferred from Structure", "Missing / Needs Clarification", "Out of Scope", "[NEEDS CLARIFICATION]", + "Screenshots support visual facts only", + "screenshots must not create product semantics", + "Client Asset Contract facts", + "asset source strategy", + "required variants", + "fallback policy", + "blocker status", + "Screenshot-implied business rules", "Continue to write only `spec.md`", + "stage-wise report", ): self.assertIn(term, specify) - self.assertLessEqual(len(specify.splitlines()), 52) + self.assertLessEqual(len(specify.splitlines()), 70) for forbidden in ( "/speckit.plan", "/speckit.checklist", + "`[NEEDS CLARIFICATION]` item requesting a filled Figma Evidence Packet", "behavior/bdd.draft.feature", "behavior/behavior-scenarios.draft.json", "behavior/uif.intent.json", @@ -723,6 +926,9 @@ def test_behavior_first_command_wrapper_contracts(self) -> None: "validation commands", "task plans", "design artifacts", + "local asset path", + "asset hash", + "allowed_write_paths", ): self.assertNotIn(forbidden, specify) self.assertNotIn("contracts/bdd/", specify) @@ -734,13 +940,47 @@ def test_behavior_first_command_wrapper_contracts(self) -> None: self.assertIn("Product requirements stay in `spec.md`", clarify) self.assertIn("non-functional requirement assumptions", clarify) self.assertIn("only after user-provided answers", clarify) + self.assertIn("Design Requirement Clarification Strategy", clarify) + self.assertIn("Design Requirement Intake", clarify) self.assertIn("Figma Evidence Packet", clarify) - self.assertIn("Missing / Needs clarification", clarify) + self.assertIn("provider-specific evidence", clarify) + self.assertIn("Missing / Needs Clarification", clarify) self.assertIn("[NEEDS CLARIFICATION]", clarify) - self.assertIn("Inferred from structure", clarify) + self.assertIn("Inferred from Structure", clarify) self.assertIn("Do not call Figma MCP", clarify) self.assertIn("Do not re-extract design facts", clarify) + self.assertIn("qualified evidence-backed design-derived requirements and trace refs", clarify) + self.assertIn("does not write raw Figma evidence into `spec.md`", 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("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("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("Do not update checklist artifacts", clarify) + self.assertIn("report checklist impact as unresolved readiness context", 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 fidelity scope", clarify) self.assertIn("missing UI states", clarify) self.assertIn("responsive behavior", clarify) @@ -763,15 +1003,42 @@ def test_behavior_first_command_wrapper_contracts(self) -> None: ): self.assertNotIn(forbidden, clarify) + self.assertIn('Checklist Purpose: "Unit Tests for English"', checklist) + self.assertIn("NOT for verification/testing", checklist) + self.assertIn("CORE PRINCIPLE - Test the Requirements, Not the Implementation", checklist) + self.assertIn("Checklist questions must use requirement-quality forms", checklist) + self.assertIn("$ARGUMENTS", checklist) + self.assertIn("dynamic clarifying questions", checklist) + self.assertIn("no pre-baked catalog", checklist) + self.assertIn("Q1/Q2/Q3", checklist) + self.assertIn("Q4/Q5", checklist) + self.assertIn("create the file when absent", checklist) + self.assertIn("append or update without deleting existing checklist content", checklist) + self.assertIn("update mode", checklist) + self.assertIn("full path", checklist) + self.assertIn("item count", checklist) + self.assertIn("focus areas", checklist) + self.assertIn("depth level", checklist) + self.assertIn("actor/timing", checklist) + self.assertIn("must-have items", checklist) self.assertIn("BDD Readiness Gate", checklist) self.assertIn("checklists/behavior-testability.md", checklist) self.assertIn("directly from `spec.md`", checklist) self.assertIn("plan-entry quality gate", checklist) self.assertIn("Do not proceed to `/speckit.plan`", checklist) - self.assertIn("Return to `/speckit.clarify` or `/speckit.specify`", checklist) + self.assertIn("Requirement ambiguity returns to `/speckit.clarify` or `/speckit.specify`", checklist) self.assertIn("User Story Readiness", checklist) self.assertIn("Acceptance Criteria Quality", checklist) self.assertIn("Scenario Coverage", checklist) + self.assertIn("Case Coverage Matrix", checklist) + self.assertIn("one row per story or capability case type", checklist) + self.assertIn("case status: Required|Not Applicable|Unknown", checklist) + self.assertIn("Each row must have a stable Case ID", checklist) + self.assertIn("Required rows must cite the source `spec.md` section", checklist) + self.assertIn("Scenario IDs and `case_coverage_blockers` are assigned during `/speckit.plan`", checklist) + self.assertIn("Not Applicable requires rationale", checklist) + self.assertIn("Unknown must appear in Blocking Items", checklist) + self.assertIn("Required case type without observable acceptance behavior blocks PASS", checklist) self.assertIn("Given Readiness", checklist) self.assertIn("When Readiness", checklist) self.assertIn("Then Readiness", checklist) @@ -790,21 +1057,42 @@ def test_behavior_first_command_wrapper_contracts(self) -> None: self.assertIn("Unknown and affects downstream design", checklist) for term in ( "Visual Fidelity Readiness", - "Figma-derived requirements", + "design-derived requirements", + "design source, provider evidence blockers, or provider-specific design evidence requests", + "product-side visual requirements such as pixel-perfect, brand-critical, responsive visual, or UI visual acceptance requirements", + "Visual Fidelity Evidence Matrix", + "Use the behavior-testability checklist template as the visual gate authority", + "provider readiness status, evidence refs, and blockers", "source traceability", - "ready gate evidence", + "Screenshot evidence level", + "BDD, NFR, and Visual Fidelity readiness gate", + "declared visual proof required", + "Gate Status: BLOCKED", "state, responsive, accessibility, component mapping, and accepted exception", + "Responsive visual requirements block PASS only when they are complex, multi-state, or declare L2 or L3 visual proof", + "Use one Visual Fidelity Evidence Matrix as the single visual readiness record", + "Do not add historical visual rules or alternate visual decision paths", + ): + self.assertIn(term, checklist) + for term in ( + "| Visual Item ID | Source `spec.md` section | 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.assertIn(term, checklist) + self.assertNotIn(term, checklist) self.assertIn("Gate Status", checklist) self.assertIn("PASS", checklist) self.assertIn("BLOCKED", checklist) self.assertIn("Blocking Items", checklist) self.assertIn("checklist artifacts only", checklist) + self.assertIn("BDD, NFR, and Visual Fidelity readiness status", checklist) + self.assertIn( + "Provider evidence readiness blockers return to `/speckit.specify` or provider intake, not `/speckit.clarify`", + checklist, + ) def test_behavior_first_plan_and_tasks_awareness_contract(self) -> None: plan = PLAN_COMMAND_PATH.read_text(encoding="utf-8") @@ -831,6 +1119,13 @@ def test_behavior_first_plan_and_tasks_awareness_contract(self) -> None: "UIFPath", "FeedbackView", "BehaviorAssertion", + "Required case types from `checklists/behavior-testability.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", ): self.assertIn(term, plan) @@ -838,7 +1133,12 @@ def test_behavior_first_plan_and_tasks_awareness_contract(self) -> None: "Phase 0 Preflight", "Phase 0 Behavior Projection", "checklists/behavior-testability.md has passed", + "Blocking Items: none` or a `Blocking Items` section containing only `- none`", "before core research or design work", + "visual fidelity scope", + "screenshot refs", + "visual proof refs", + "Design Requirement trace refs", "behavior/behavior-scenarios.draft.json", "report-only/no-write failure", "must not create or update behavior artifacts", @@ -858,6 +1158,11 @@ def test_behavior_first_plan_and_tasks_awareness_contract(self) -> None: "contracts/bdd/", "contracts/uif/", "contracts/behavior/", + "`spec.md` visual acceptance requirements", + "`checklists/behavior-testability.md` Visual Fidelity Readiness", + "screenshot refs", + "visual proof refs", + "visual fidelity requirements", "test-first", "existing checklist format and user-story organization", "For each BehaviorScenarioInstance", @@ -867,10 +1172,23 @@ def test_behavior_first_plan_and_tasks_awareness_contract(self) -> None: "verification evidence task", "For each UIF user_event", "For each UIF api_call", + "UI implementation and acceptance tasks must be paired", + "UI acceptance task", + "state coverage", + "viewport coverage", + "visual proof ref", "For each quickstart validation path", "derive the test level", "fixture/mock/sandbox/real-system strategy", "inline evidence requirement", + "Client Asset Contract", + "derive asset preparation, binding, implementation, and validation tasks", + "Missing required client visual assets become readiness blockers", + "do not generate handoff fields or `allowed_write_paths`", + "Missing Required case scenarios must become blockers, not silently skipped tasks", + "negative, boundary, permission, validation, state_conflict, or error behavior", + "For each non-positive BehaviorScenarioInstance", + "derive fixture, contract or BDD test, implementation, and verification evidence tasks", ): self.assertIn(term, tasks) @@ -890,6 +1208,17 @@ def test_behavior_first_plan_and_tasks_awareness_contract(self) -> None: self.assertIn("behavior assertion", implement) self.assertIn("API contract", implement) self.assertIn("quickstart path", implement) + self.assertIn("visual fidelity requirements", implement) + self.assertIn("screenshot refs", implement) + self.assertIn("visual proof refs", implement) + self.assertIn("Design Requirement trace refs", implement) + self.assertIn("Client Asset Contract", implement) + self.assertIn("asset binding", implement) + self.assertIn("local asset paths or code asset mappings", implement) + self.assertIn("missing required client visual assets", implement) + self.assertIn("planned `U` design object and target component or module", implement) + self.assertIn("specific source, test, fixture, or configuration file paths", implement) + self.assertIn("If no concrete file path can be derived, record `context_gaps`", implement) def test_bdd_formalization_strengthens_reasoning_without_traceability_system(self) -> None: plan = PLAN_COMMAND_PATH.read_text(encoding="utf-8") @@ -936,6 +1265,24 @@ def test_analyze_command_owns_vertical_consistency_contract(self) -> None: 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-testability.md`", 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) @@ -979,6 +1326,24 @@ def test_behavior_first_templates_exist_and_are_decoupled(self) -> None: behavior_checklist_template = BEHAVIOR_TEMPLATE_PATHS[ "behavior-testability-checklist-template" ].read_text(encoding="utf-8") + self.assertIn("Case Coverage Matrix", behavior_checklist_template) + self.assertIn("one row per story or capability case type", behavior_checklist_template) + self.assertIn("Status: Required|Not Applicable|Unknown", behavior_checklist_template) + self.assertIn("| Case ID | Story/Capability | Case Type | Status | Source `spec.md` section | Blocking Item ID | Rationale |", behavior_checklist_template) + self.assertIn( + "Required case type must cite the source `spec.md` section", + behavior_checklist_template, + ) + self.assertIn( + "Each row must have a stable Case ID", + behavior_checklist_template, + ) + self.assertIn( + "Scenario IDs and `case_coverage_blockers` are assigned during `/speckit.plan`", + behavior_checklist_template, + ) + self.assertIn("Not Applicable requires rationale", behavior_checklist_template) + self.assertIn("Unknown must appear in Blocking Items", behavior_checklist_template) self.assertIn("Non-Functional Requirement Readiness", behavior_checklist_template) self.assertIn("Status: Required|Not Applicable|Unknown", behavior_checklist_template) self.assertIn("Performance", behavior_checklist_template) @@ -993,11 +1358,15 @@ def test_behavior_first_templates_exist_and_are_decoupled(self) -> None: self.assertIn("explicitly declared in `spec.md`", behavior_checklist_template) self.assertIn("without prescribing architecture", behavior_checklist_template) self.assertIn("Visual Fidelity Readiness", behavior_checklist_template) - self.assertIn("Figma-derived requirements", behavior_checklist_template) - self.assertIn("raw metadata completeness", behavior_checklist_template) - self.assertIn("metadata index completeness proof", behavior_checklist_template) - self.assertIn("node inventory parity", behavior_checklist_template) - self.assertIn("blocker lint errors", behavior_checklist_template) + self.assertIn("Design-derived requirements", behavior_checklist_template) + self.assertIn( + "provider readiness status, evidence refs, and blockers", + behavior_checklist_template, + ) + self.assertNotIn("raw metadata completeness", behavior_checklist_template) + self.assertNotIn("metadata index completeness proof", behavior_checklist_template) + self.assertNotIn("node inventory parity", behavior_checklist_template) + self.assertNotIn("blocker lint errors", behavior_checklist_template) self.assertIn("component mappings and variant coverage", behavior_checklist_template) self.assertIn("responsive behavior is explicit", behavior_checklist_template) self.assertIn("accessibility requirements are explicit", behavior_checklist_template) @@ -1031,6 +1400,20 @@ def test_behavior_first_templates_exist_and_are_decoupled(self) -> None: 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_figma_evidence_packet_template_contract(self) -> None: self.assertTrue(FIGMA_EVIDENCE_PACKET_TEMPLATE_PATH.exists()) document = FIGMA_EVIDENCE_PACKET_TEMPLATE_PATH.read_text(encoding="utf-8") @@ -1039,12 +1422,46 @@ def test_figma_evidence_packet_template_contract(self) -> None: "Figma Evidence Packet", "Figma Source", "Extraction Context", + "Screenshot Evidence", + "Screenshot Coverage Matrix", + "visual proof", + "Screenshot evidence must declare L0-L3 coverage and coverage gaps", + "not the primary Design Requirement Intake carrier", + "Screenshot level: L0|L1|L2|L3", + "L0: no screenshot evidence", + "L1: static screenshot reference", + "L2: viewport or state screenshot coverage", + "L3: visual diff baseline or approved visual proof", + "high-fidelity", + "pixel-perfect", + "brand-critical", + "visual regression", + "Screenshot refs", + "Viewport", + "State", + "Capture timestamp", + "Design version", + "Redaction required", + "Baseline usage", + "Missing coverage", + "Blocking item", + "Visual baseline usage: none|manual review|visual diff", "Observed from Figma", "Inferred from Structure", "Missing / Needs Clarification", "Out of Scope", "Figma Intake Readiness", "Visual Facts for Spec", + "Client Asset Inventory", + "Asset ID", + "Asset role", + "Resource type: image|icon|video|lottie|svg|font", + "Figma node/component ref", + "Asset source strategy: figma_export_required|code_asset|existing_repo_asset|remote_runtime_asset", + "Export/use contract", + "Required variants", + "Fallback policy", + "Blocker status", "Component Mapping", "Spec Handoff Notes", "Open Questions", @@ -1066,6 +1483,182 @@ def test_figma_evidence_packet_template_contract(self) -> None: for term in forbidden_terms: self.assertNotIn(term, document) + def test_design_requirement_intake_template_contract(self) -> None: + self.assertTrue(DESIGN_REQUIREMENT_INTAKE_TEMPLATE_PATH.exists()) + document = DESIGN_REQUIREMENT_INTAKE_TEMPLATE_PATH.read_text(encoding="utf-8") + + required_terms = [ + "Design Requirement Intake", + "Design Sources", + "Provider Evidence", + "Page Inventory", + "Page Hierarchy", + "User Paths", + "Component Inventory", + "Component States", + "Interaction Rules", + "Visual Tokens", + "Layout Rules", + "Responsive Rules", + "Motion Rules", + "State Coverage", + "Visual Acceptance Requirements", + "Client Asset Contract", + "Asset ID", + "Required resource type", + "Asset source strategy", + "Required variants", + "Fallback policy", + "Blocker status", + "Screenshot Traceability", + "Design Requirement Intake remains provider-neutral", + "Visual proof refs", + "Supported visual facts", + "Unsupported assumptions", + "Screenshot-derived visual facts must include screenshot refs", + "screenshots must not create product semantics", + "Traceability", + "Source refs", + "[NEEDS CLARIFICATION]", + ] + for term in required_terms: + self.assertIn(term, document) + + forbidden_terms = [ + "Figma MCP authentication", + "raw get_metadata", + "node coordinate dump", + "implementation test", + "test-plan.md", + "Endpoint / Client Requirements", + ] + for term in forbidden_terms: + self.assertNotIn(term, document) + + def test_requirement_merge_report_template_contract(self) -> None: + self.assertTrue(REQUIREMENT_MERGE_REPORT_TEMPLATE_PATH.exists()) + document = REQUIREMENT_MERGE_REPORT_TEMPLATE_PATH.read_text(encoding="utf-8") + + required_terms = [ + "Requirement Merge Report", + "Product Requirement Inputs", + "Design Requirement Inputs", + "Merge Rules", + "Product Requirement owns", + "Design Requirement owns", + "Conflict Resolution", + "Clarification Outputs", + "Baseline Spec Handoff", + "Design Requirement Promotion Rules", + "Promote screenshot-supported visual facts", + "Screenshot-implied business rules", + "Promote observed", + "Promote confirmed", + "Inferred", + "Missing", + "spec.md", + "[NEEDS CLARIFICATION]", + ] + for term in required_terms: + self.assertIn(term, document) + + forbidden_terms = [ + "Figma-only", + "directly call Figma MCP", + "generate tasks", + "write implementation", + "test-plan.md", + ] + for term in forbidden_terms: + self.assertNotIn(term, document) + + def test_visual_fidelity_screenshot_evidence_gate_contract(self) -> None: + command = CHECKLIST_COMMAND_PATH.read_text(encoding="utf-8") + template = BEHAVIOR_TEMPLATE_PATHS[ + "behavior-testability-checklist-template" + ].read_text(encoding="utf-8") + + for term in ( + "Use the behavior-testability checklist template as the visual gate authority", + "provider readiness status, evidence refs, and blockers", + CANONICAL_RESPONSIVE_VISUAL_RULE, + "Use one Visual Fidelity Evidence Matrix as the single visual readiness record", + "Do not add historical visual rules or alternate visual decision paths", + "Blocking Items", + ): + self.assertIn(term, command) + for term in ( + "| Visual Item ID | Source `spec.md` section | 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 ( + "Screenshot evidence level", + "visual proof refs", + "L0|L1|L2|L3", + "declared visual proof required", + "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", + CANONICAL_RESPONSIVE_VISUAL_RULE, + "Visual Fidelity Evidence Matrix", + "Source `spec.md` section", + "Evidence Refs", + "Exception Rule", + "lists the item in Blocking Items", + "Pixel-perfect", + "Blocking Items", + "provider readiness status, evidence refs, and blockers", + "Use one Visual Fidelity Evidence Matrix as the single visual readiness record", + "Do not add historical visual rules or alternate visual decision paths", + ): + self.assertIn(term, template) + self.assertIn( + "Required client visual assets have source refs, asset source strategy, required variants, fallback policy, and blocker status.", + 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 | Fidelity Scope | Screenshot Level | Evidence Refs | Visual Proof Required | Blocking Item ID | Exception Rule |" + ), + 1, + ) + self.assertEqual( + template.count( + "Use one Visual Fidelity Evidence Matrix as the single visual readiness record" + ), + 1, + ) + self.assertEqual(template.count(CANONICAL_RESPONSIVE_VISUAL_RULE), 1) + for forbidden in ( + "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.assertNotIn(forbidden, template) + + for document in (command, template): + lowered = document.lower() + for forbidden in FORBIDDEN_VISUAL_COMPAT_TERMS: + self.assertNotIn(forbidden, lowered) + def test_figma_intake_contract_metadata_lint_rules(self) -> None: self.assertTrue(FIGMA_INTAKE_CONTRACT_TEMPLATE_PATH.exists()) document = FIGMA_INTAKE_CONTRACT_TEMPLATE_PATH.read_text(encoding="utf-8") @@ -1107,9 +1700,9 @@ def test_figma_intake_contract_metadata_lint_rules(self) -> None: "FIGMA_METADATA_INDEX_MISSING", "FIGMA_METADATA_PARITY_FAILED", "FIGMA_READY_WITHOUT_COMPLETENESS_PROOF", - "preset defines the required artifact formats and gates", - "does not call Figma MCP", - "does not generate artifact instances", + "Required Figma intake artifacts and readiness gates", + "must not call Figma MCP", + "must not generate artifact instances", ] for field in metadata_fields: self.assertIn(field, document) @@ -1272,7 +1865,6 @@ def test_contract_schemas_are_decoupled_json_files(self) -> None: self.assertIn("checked_sources", review_conclusion["required"]) data_side_effect_review = receipt["properties"]["data_side_effect_review"] self.assertIn("reviewed_diff_paths", data_side_effect_review["required"]) - self.assertIn("runtime_data_writes_found", data_side_effect_review["required"]) self.assertIn("mutation_findings", data_side_effect_review["required"]) def test_manifest_schema_declares_runtime_neutral_execution_mode(self) -> None: @@ -1333,6 +1925,147 @@ def test_behavior_scenario_instances_schema_rejects_empty_contract_refs(self) -> 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" + + 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"] = [ + { + "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"} + + 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( @@ -1425,6 +2158,226 @@ def test_behavior_contract_validator_rejects_underspecified_uif_steps(self) -> N [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", + ): + validate_behavior_contract_bundle( + minimal_exception_behavior_scenario_instances(), + minimal_behavior_data_fixtures(), + minimal_behavior_assertions(), + [minimal_uif_expected()], + ) + + 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_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_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"): + validate_behavior_contract_bundle( + instances, + minimal_behavior_data_fixtures(), + minimal_exception_behavior_assertions(), + [minimal_uif_expected()], + ) + + 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_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", + ) + + 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_behavior_case_coverage_validator_accepts_formal_blocker_for_required_case(self) -> None: + instances = minimal_behavior_scenario_instances() + instances["case_coverage_blockers"] = [ + { + "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"] = [ + { + "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"] = [ + { + "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(), @@ -2037,95 +2990,6 @@ def test_validate_receipt_contract_rejects_unreviewed_diff_path_for_data_side_ef RECEIPT_PATH, ) - def test_validate_receipt_contract_requires_runtime_data_writes_found_for_code_review_task( - self, - ) -> None: - handoff = minimal_handoff(task_ids=["T099"], task_type="code_review") - handoff["allowed_read_paths"] = [TASKS_PATH, SERVICE_PATH] - - with self.assertRaisesRegex(ValueError, "runtime_data_writes_found"): - validate_receipt_contract( - handoff, - minimal_receipt( - task_ids=["T099"], - task_type="code_review", - review_conclusion={ - "status": "approved", - "summary": "Review complete.", - "checked_sources": [SERVICE_PATH], - "findings": [], - }, - data_side_effect_review={ - "reviewed_diff_paths": [SERVICE_PATH], - "mutation_findings": [], - }, - ), - RECEIPT_PATH, - ) - - def test_validate_receipt_contract_requires_mutation_findings_for_code_review_task( - self, - ) -> None: - handoff = minimal_handoff(task_ids=["T099"], task_type="code_review") - handoff["allowed_read_paths"] = [TASKS_PATH, SERVICE_PATH] - - with self.assertRaisesRegex(ValueError, "mutation_findings"): - validate_receipt_contract( - handoff, - minimal_receipt( - task_ids=["T099"], - task_type="code_review", - review_conclusion={ - "status": "approved", - "summary": "Review complete.", - "checked_sources": [SERVICE_PATH], - "findings": [], - }, - data_side_effect_review={ - "reviewed_diff_paths": [SERVICE_PATH], - "runtime_data_writes_found": False, - }, - ), - RECEIPT_PATH, - ) - - def test_validate_receipt_contract_rejects_malformed_data_side_effect_finding( - self, - ) -> None: - handoff = minimal_handoff(task_ids=["T099"], task_type="code_review") - handoff["allowed_read_paths"] = [TASKS_PATH, SERVICE_PATH] - - with self.assertRaisesRegex(ValueError, "mutation_findings\\[0\\] must include id"): - validate_receipt_contract( - handoff, - minimal_receipt( - task_ids=["T099"], - task_type="code_review", - review_conclusion={ - "status": "approved", - "summary": "Review complete.", - "checked_sources": [SERVICE_PATH], - "findings": [], - }, - data_side_effect_review={ - "reviewed_diff_paths": [SERVICE_PATH], - "runtime_data_writes_found": True, - "mutation_findings": [ - { - "severity": "low", - "category": "field_level_update", - "summary": "Missing id should fail validation.", - "operation": "update", - "tables_or_entities": ["orders"], - "fields": ["status"], - "resolution": "accepted", - } - ], - }, - ), - RECEIPT_PATH, - ) - def test_validate_receipt_contract_rejects_approved_with_unresolved_high_data_side_effect( self, ) -> None: @@ -2535,30 +3399,83 @@ def test_readme_contract(self) -> None: self.assertIn("speckit-cross-agent-subagents.md", readme) self.assertIn("Problem Addressed", readme) self.assertIn("reasoning quality", readme) + self.assertNotIn("compatible with the core workflow", readme) + self.assertNotIn("core compatibility fixes", readme) self.assertIn("must formalize", readme) self.assertIn("N/A or blocker", readme) self.assertIn("The preset has four goals:", readme) self.assertIn("BDD readiness gate", readme) self.assertIn("NFR readiness", readme) + self.assertIn("BDD/NFR/applicable Visual Fidelity", readme) + self.assertIn("Design Requirement Intake", readme) + self.assertIn("Requirement Merge", readme) + self.assertIn("Product Requirement + Design Requirement", readme) + self.assertIn("Figma is a Design Requirement provider", readme) self.assertIn("Figma Evidence Packet", readme) self.assertIn("direct Figma URL input", readme) self.assertIn("runtime agent has Figma MCP access", readme) self.assertIn("Visual Fidelity readiness gate", readme) - self.assertIn("preset defines the required Figma intake artifact structure", readme) + self.assertIn("Screenshot is evidence, not intake", readme) + self.assertIn("optional but strongly recommended provider evidence", readme) + self.assertIn("L0 No Screenshot", readme) + self.assertIn("L1 Key Screenshots", readme) + self.assertIn("L2 State + Viewport Matrix", readme) + self.assertIn("L3 Visual Baseline", readme) + self.assertIn("pixel-perfect", readme) + self.assertIn("Screenshots cannot upgrade product semantics", readme) + self.assertIn( + CANONICAL_RESPONSIVE_VISUAL_RULE, + readme, + ) + self.assertIn( + "product-side visual requirements such as pixel-perfect, brand-critical, responsive visual, or UI visual acceptance requirements", + readme, + ) + self.assertIn("Visual Fidelity Evidence Matrix", readme) + self.assertIn("visual requirement or visual proof obligation", readme) + self.assertIn("single visual readiness record", readme) + self.assertIn("preset defines the required design intake and provider readiness artifact structure", readme) self.assertIn("runtime agent or external Figma intake", readme) self.assertIn("does not generate the artifact instances", readme) + self.assertIn("[BLOCKED: PROVIDER_EVIDENCE]", readme) + self.assertIn("Provider evidence blockers do not become `[NEEDS CLARIFICATION]`", readme) + self.assertNotIn( + "writes or marks it as `[NEEDS CLARIFICATION]`", + readme, + ) self.assertIn("raw metadata completeness", readme) self.assertIn("node inventory parity", readme) self.assertIn("does not provide Figma MCP connection, authentication, or execution", readme) - self.assertIn("clarifies Figma-derived gaps already written in `spec.md`", readme) + self.assertIn("clarifies design-derived gaps already written in `spec.md`", readme) self.assertIn("does not call Figma", readme) self.assertIn("explicit non-functional requirement declarations", readme) self.assertIn("Required, Not Applicable, or Unknown", readme) self.assertIn("missing or unverifiable NFR assumptions", readme) self.assertIn("Phase 0 behavior projection", readme) + self.assertIn("Case Coverage Matrix", readme) + self.assertIn("case coverage", readme) + self.assertIn("Required, Not Applicable, or Unknown", readme) + lowered = readme.lower() + for forbidden in FORBIDDEN_VISUAL_COMPAT_TERMS: + self.assertNotIn(forbidden, lowered) + self.assertNotIn( + "Responsive visual readiness must record viewport-specific evidence or set Gate Status: BLOCKED", + readme, + ) + self.assertNotIn( + "Responsive visual readiness records viewport-specific evidence or sets Gate Status: BLOCKED", + readme, + ) + self.assertIn("failure scenarios", readme) + self.assertIn( + "error code, failure feedback, and state invariant, rollback, or compensation assertion", + readme, + ) self.assertIn("validation_evidence", readme) self.assertIn("Context-load controls", readme) self.assertIn("context-load controls", changelog) + self.assertIn("Case Coverage Matrix", changelog) + self.assertIn("failure behavior scenarios", changelog) self.assertIn("Change Scope Granularity", changelog) self.assertIn("/speckit.constitution", changelog) self.assertIn("Moved behavior draft generation from `/speckit.specify` to `/speckit.plan` Phase 0", changelog) @@ -2684,11 +3601,27 @@ def test_extension_governance_document_contract(self) -> None: "structured JSON artifacts require schemas", "validators/", "Do not put downstream prohibitions in upstream commands", + "Design Requirement Intake", + "Requirement Merge", + "Figma is a provider-specific design source", "Behavior-first extension rule", "BDD and UIF artifacts need independent templates", "`/speckit.constitution`: constitution governance and project principles only", - "`/speckit.checklist`: checklist artifacts and BDD/NFR readiness gates only", + "`/speckit.checklist`: checklist artifacts and BDD/NFR/Visual Fidelity readiness gates only", "Figma Evidence Packet", + "Screenshot is provider evidence", + "Screenshots must not become the primary Design Requirement Intake carrier", + "Visual Fidelity Evidence Matrix", + "one row per visual requirement or visual proof obligation", + "Source `spec.md` section", + "Fidelity Scope", + "Screenshot Level", + "Evidence Refs", + "Visual Proof Required", + "Blocking Item ID", + "Exception Rule", + CANONICAL_RESPONSIVE_VISUAL_RULE, + "single visual readiness record", "packaged evidence templates are allowed preset artifacts", "Figma MCP execution, hooks, adapter scripts, and authentication", "external design extraction is not a clarification responsibility", @@ -2712,6 +3645,17 @@ def test_extension_governance_document_contract(self) -> None: ] for term in forbidden_terms: self.assertNotIn(term, document) + lowered = document.lower() + for forbidden in FORBIDDEN_VISUAL_COMPAT_TERMS: + self.assertNotIn(forbidden, lowered) + self.assertNotIn( + "Responsive visual readiness must record viewport-specific evidence or set Gate Status: BLOCKED", + document, + ) + self.assertNotIn( + "Responsive visual readiness records viewport-specific evidence or sets Gate Status: BLOCKED", + document, + ) def test_agents_references_extension_governance(self) -> None: agents = AGENTS_PATH.read_text(encoding="utf-8") @@ -2724,8 +3668,7 @@ def _workflow_on(self, workflow: dict) -> dict: def test_github_actions_contract_workflow(self) -> None: workflow_path = REPO_ROOT / ".github" / "workflows" / "ci.yml" - if not workflow_path.exists(): - self.skipTest("GitHub Actions workflow is not bundled in spec-kit checkout") + self.assertTrue(workflow_path.exists()) workflow = yaml.safe_load(workflow_path.read_text(encoding="utf-8")) self.assertEqual("Preset Contract", workflow["name"]) @@ -2747,8 +3690,7 @@ def test_github_actions_contract_workflow(self) -> None: 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("GitHub Actions workflow is not bundled in spec-kit checkout") + self.assertTrue(workflow_path.exists()) workflow = yaml.safe_load(workflow_path.read_text(encoding="utf-8")) self.assertEqual("Preset Artifact", workflow["name"]) diff --git a/presets/workflow-preset/validators/speckit_implement_contract.py b/presets/workflow-preset/validators/speckit_implement_contract.py index 8ab19874cd..a82498f475 100644 --- a/presets/workflow-preset/validators/speckit_implement_contract.py +++ b/presets/workflow-preset/validators/speckit_implement_contract.py @@ -4,6 +4,8 @@ VALID_EXECUTION_MODES = {"isolated_subagent", "manual_fresh_worker_session"} +CASE_TYPES = {"positive", "negative", "boundary", "permission", "validation", "state_conflict"} +FAILURE_CASE_TYPES = {"negative", "permission", "validation", "state_conflict"} def _duplicate_ids(items: list[dict[str, Any]], *, key: str, context: str) -> set[str]: @@ -112,6 +114,147 @@ def _validate_expected_uif_contract(uif_contract: dict[str, Any]) -> None: raise ValueError(f"{context} user_event requires id or label") +def _validate_non_positive_behavior_scenario( + scenario: dict[str, Any], + assertions_by_id: dict[str, dict[str, Any]], +) -> None: + scenario_id = scenario.get("id", "") + scenario_type = scenario.get("type") + if scenario_type == "positive": + return + + request_case = scenario.get("request_case") + if not isinstance(request_case, dict): + raise ValueError(f"behavior scenario {scenario_id} must include request_case") + case_kind = request_case.get("case_kind") + if not case_kind: + raise ValueError(f"behavior scenario {scenario_id} missing case_kind") + if case_kind != scenario_type: + raise ValueError(f"behavior scenario {scenario_id} case_kind must match type") + outcome = request_case.get("outcome") + if outcome not in {"success", "failure"}: + raise ValueError(f"behavior scenario {scenario_id} missing outcome") + if not request_case.get("trigger"): + raise ValueError(f"behavior scenario {scenario_id} missing trigger") + + if scenario_type in FAILURE_CASE_TYPES and outcome != "failure": + raise ValueError(f"behavior scenario {scenario_id} failure case must declare failure outcome") + if outcome != "failure": + return + + expected_response = scenario.get("expected_response") + if not isinstance(expected_response, dict) or not expected_response: + raise ValueError(f"behavior scenario {scenario_id} missing expected_response") + if not expected_response.get("error_code"): + 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") + + invariant_intents = {"state_invariant", "rollback", "compensation"} + if not any( + assertions_by_id.get(assertion_id, {}).get("intent") in invariant_intents + for assertion_id in scenario.get("assertion_ids", []) + ): + raise ValueError( + f"behavior scenario {scenario_id} missing state_invariant_rollback_or_compensation_assertion" + ) + + +def _case_coverage_blockers_by_id(scenario_instances: dict[str, Any]) -> dict[str, dict[str, Any]]: + return { + blocker["id"]: blocker + for blocker in scenario_instances.get("case_coverage_blockers", []) + if "id" in blocker + } + + +def validate_behavior_case_coverage( + case_coverage: dict[str, Any], + scenarios_draft: dict[str, Any], + scenario_instances: dict[str, Any], + tasks_text: str, + quickstart_text: str, +) -> None: + draft_by_id = { + scenario.get("id"): scenario + for scenario in scenarios_draft.get("scenarios", []) + if "id" in scenario + } + formal_by_id = { + scenario.get("id"): scenario + for scenario in scenario_instances.get("scenarios", []) + if "id" in scenario + } + blockers_by_id = _case_coverage_blockers_by_id(scenario_instances) + + for row in case_coverage.get("case_coverage", []): + story = row.get("story", "") + case_type = row.get("case_type") + status = row.get("status") + context = f"{story} {case_type}" + + if case_type not in CASE_TYPES: + raise ValueError(f"case coverage row {context} has unknown case_type") + if status not in {"Required", "Not Applicable", "Unknown"}: + raise ValueError(f"case coverage row {context} has unknown status") + + if status == "Not Applicable": + if not row.get("rationale"): + raise ValueError(f"Not Applicable case {context} missing rationale") + continue + + if status == "Unknown": + if not row.get("blocker_id"): + raise ValueError(f"Unknown case {context} missing Blocking Items reference") + continue + + if status != "Required": + continue + + if not row.get("source"): + raise ValueError(f"Required case {context} missing source") + + scenario_id = row.get("scenario_id") + blocker_id = row.get("blocker_id") + if bool(scenario_id) == bool(blocker_id): + raise ValueError( + f"Required case {context} must name exactly one scenario_id or blocker_id" + ) + + if blocker_id: + blocker = blockers_by_id.get(blocker_id) + if blocker is None: + raise ValueError(f"Required case {context} references unknown blocker") + if blocker.get("case_id") != row.get("case_id"): + raise ValueError(f"Required case {context} blocker case_id mismatch") + if blocker.get("case_type") != case_type: + raise ValueError(f"Required case {context} blocker case_type mismatch") + if blocker.get("source") != row.get("source"): + raise ValueError(f"Required case {context} blocker source mismatch") + if blocker_id not in tasks_text: + raise ValueError(f"Required case {context} missing tasks.md blocker evidence") + if blocker_id not in quickstart_text: + raise ValueError(f"Required case {context} missing quickstart.md blocker evidence") + continue + + draft = draft_by_id.get(scenario_id) + formal = formal_by_id.get(scenario_id) + if draft is None or formal is None: + raise ValueError(f"Required case {context} missing draft or formal scenario") + if draft.get("type") != case_type or formal.get("type") != case_type: + raise ValueError(f"Required case {context} scenario type mismatch") + if scenario_id not in tasks_text: + raise ValueError(f"Required case {context} missing tasks.md evidence") + if scenario_id not in quickstart_text: + raise ValueError(f"Required case {context} missing quickstart.md evidence") + + def _handoff_has_behavior_contract_context(handoff: dict[str, Any]) -> bool: markers = ( "contracts/bdd/", @@ -185,26 +328,6 @@ def _unresolved_high_or_critical(finding: dict[str, Any]) -> bool: ) -def _validate_data_side_effect_finding(finding: Any, *, index: int) -> None: - if not isinstance(finding, dict): - raise ValueError( - f"data_side_effect_review mutation_findings[{index}] must be an object" - ) - - context = f"data_side_effect_review mutation_findings[{index}]" - for key in ("id", "severity", "category", "summary", "operation", "resolution"): - if not isinstance(finding.get(key), str) or not finding.get(key): - raise ValueError(f"{context} must include {key}") - - for key in ("tables_or_entities", "fields"): - values = finding.get(key) - if ( - not isinstance(values, list) - or any(not isinstance(value, str) or not value for value in values) - ): - raise ValueError(f"{context} must include {key}") - - def validate_behavior_draft_contract( scenarios_draft: dict[str, Any], data_fixtures_intent: dict[str, Any], @@ -263,6 +386,11 @@ def validate_behavior_contract_bundle( key="id", context="behavior assertions", ) + assertions_by_id = { + assertion["id"]: assertion + for assertion in assertions.get("assertions", []) + if "id" in assertion + } uif_path_ids = _duplicate_ids( uif_expected_contracts, key="id", @@ -295,6 +423,8 @@ def validate_behavior_contract_bundle( if assertion_id not in assertion_ids: raise ValueError(f"scenario references unknown assertion: {assertion_id}") + _validate_non_positive_behavior_scenario(scenario, assertions_by_id) + if len(scenario_ids) != len(scenario_instances.get("scenarios", [])): raise ValueError("behavior scenario instances contain duplicate ids") @@ -584,18 +714,6 @@ def validate_receipt_contract( f"allowed_read_paths or context_digest_path: {path}" ) - runtime_data_writes_found = data_side_effect_review.get( - "runtime_data_writes_found" - ) - if not isinstance(runtime_data_writes_found, bool): - raise ValueError( - "data_side_effect_review must include runtime_data_writes_found" - ) - - mutation_findings = data_side_effect_review.get("mutation_findings") - if not isinstance(mutation_findings, list): - raise ValueError("data_side_effect_review must include mutation_findings") - validation_commands = list(handoff.get("validation_commands", [])) if validation_commands and not _receipt_mentions_any_command( receipt, @@ -622,8 +740,7 @@ def validate_receipt_contract( "critical/high findings" ) - for index, finding in enumerate(mutation_findings): - _validate_data_side_effect_finding(finding, index=index) + for finding in data_side_effect_review.get("mutation_findings", []): if review_status == "approved" and _unresolved_high_or_critical(finding): raise ValueError( "approved code review receipt must not include unresolved " diff --git a/tests/test_presets.py b/tests/test_presets.py index fb0c0f80c1..5cb48aa18f 100644 --- a/tests/test_presets.py +++ b/tests/test_presets.py @@ -3490,7 +3490,7 @@ def test_workflow_preset_catalog_matches_manifest(self): template_count = sum(1 for item in provided if item["type"] == "template") assert entry["bundled"] is True - assert entry["version"] == "1.3.6" + assert entry["version"] == "1.3.7" assert entry["version"] == manifest["preset"]["version"] assert entry["repository"] == manifest["preset"]["repository"] assert entry["requires"]["speckit_version"] == manifest["requires"]["speckit_version"]