From 1248fa9b149214bd6542df9ab591d0b04c42ad34 Mon Sep 17 00:00:00 2001 From: bigsmartben <30429295+bigsmartben@users.noreply.github.com> Date: Tue, 23 Jun 2026 17:52:52 +0800 Subject: [PATCH 1/3] update preview extension to v1.1.0 --- README.md | 14 +- docs/community/extensions.md | 2 +- docs/quickstart.md | 10 +- extensions/catalog.community.json | 14 +- extensions/catalog.json | 12 +- extensions/preview/CATALOG-SUBMISSION.md | 35 ++- extensions/preview/CHANGELOG.md | 9 + extensions/preview/README.md | 87 ++++-- .../commands/speckit.preview.high-html.md | 91 ++++++ .../commands/speckit.preview.high-md.md | 86 ++++++ .../preview/commands/speckit.preview.html.md | 112 ------- .../commands/speckit.preview.low-html.md | 87 ++++++ .../commands/speckit.preview.low-md.md | 85 ++++++ .../commands/speckit.preview.mid-html.md | 89 ++++++ .../commands/speckit.preview.mid-md.md | 86 ++++++ extensions/preview/extension.yml | 29 +- .../preview/schemas/preview/contract.json | 276 ++++++++++++++++++ .../schemas/preview/contract.schema.json | 109 +++++++ .../preview/templates/preview/high.html | 229 +++++++++++++++ extensions/preview/templates/preview/high.md | 53 ++++ extensions/preview/templates/preview/low.html | 149 ++++++++++ extensions/preview/templates/preview/low.md | 41 +++ extensions/preview/templates/preview/mid.html | 191 ++++++++++++ extensions/preview/templates/preview/mid.md | 47 +++ .../preview/tests/validate-extension.py | 252 ++++++++++++++-- tests/integrations/community_defaults.py | 7 +- tests/test_extensions.py | 30 ++ 27 files changed, 2027 insertions(+), 205 deletions(-) create mode 100644 extensions/preview/commands/speckit.preview.high-html.md create mode 100644 extensions/preview/commands/speckit.preview.high-md.md delete mode 100644 extensions/preview/commands/speckit.preview.html.md create mode 100644 extensions/preview/commands/speckit.preview.low-html.md create mode 100644 extensions/preview/commands/speckit.preview.low-md.md create mode 100644 extensions/preview/commands/speckit.preview.mid-html.md create mode 100644 extensions/preview/commands/speckit.preview.mid-md.md create mode 100644 extensions/preview/schemas/preview/contract.json create mode 100644 extensions/preview/schemas/preview/contract.schema.json create mode 100644 extensions/preview/templates/preview/high.html create mode 100644 extensions/preview/templates/preview/high.md create mode 100644 extensions/preview/templates/preview/low.html create mode 100644 extensions/preview/templates/preview/low.md create mode 100644 extensions/preview/templates/preview/mid.html create mode 100644 extensions/preview/templates/preview/mid.md diff --git a/README.md b/README.md index 2e4debc5e2..9e5fa63446 100644 --- a/README.md +++ b/README.md @@ -156,7 +156,7 @@ specify integration list | --- | --- | --- | --- | | ARCH SSOT | `arch` 扩展 | `/speckit.arch.generate`、`/speckit.arch.reverse`、`.specify/memory/architecture*.md` | 把项目级 4+1 架构视图沉淀为稳定记忆,避免每个 feature 重新猜边界、约束和部署假设。 | | 仓库治理规范 | `repository-governance` 扩展 | `/speckit.repository-governance.refresh`、受管 `SPECKIT GOVERNANCE` 段 | 统一 agent 的 SSOT 读取顺序、目录责任、平台适配和仓库事实证据。 | -| UI/UX 需求规格 | `workflow-preset` + `preview` 扩展 | `spec.md` 用户旅程、`behavior/uif.intent.json`、`contracts/uif/`、`/speckit.preview.html` | 先把界面状态、断点、用户路径和交互假设写成规格/契约,再生成可打开的 HTML 原型评审。 | +| UI/UX 需求规格 | `workflow-preset` + `preview` 扩展 | `spec.md` 用户旅程、`behavior/uif.intent.json`、`contracts/uif/`、`/speckit.preview.mid-html` | 先把界面状态、断点、用户路径和交互假设写成规格/契约,再生成可打开的预览产物评审。 | | BDD 引入 | `workflow-preset` | `/speckit.checklist`、`checklists/behavior-testability.md`、`contracts/bdd/` | 在规划前检查 Given/When/Then、可观察结果、边界和 NFR 声明,避免不可验证需求进入实现。 | | 任务期验证策略 | `workflow-preset` | `/speckit.tasks`、`tasks.md` 内联测试层级和证据要求 | 从 BDD、UIF、行为契约、接口契约、`research.md`、`quickstart.md` 派生验证任务,不再依赖散落的测试策略说明。 | | imp subagent 矩阵 | `workflow-preset` | `/speckit.implement`、Core/Vertical Planner/Worker、`handoffs/implement//` | 把大实现拆成按 vertical capability 隔离的 handoff,让 Codex、Claude、Gemini、Copilot 等 runtime 使用各自 subagent/worker 模式执行。 | @@ -168,7 +168,7 @@ specify integration list | 扩展 | 默认状态 | 你会用它做什么 | | --- | --- | --- | | `arch` | `specify init` 默认安装 | 生成或反向生成项目级 4+1 架构视图,形成 `.specify/memory/architecture*.md` 架构记忆。 | -| `preview` | `specify init` 默认安装 | 根据当前 feature 的规格和计划生成 `specs//preview/index.html`,用于实现前验证 UI 和交互假设。 | +| `preview` | `specify init` 默认安装 | 根据当前 feature 的规格和计划生成低/中/高保真 Markdown wireflow 或自包含 HTML 预览,用于实现前验证 UI 和交互假设。 | | `repository-governance` | `specify init` 默认安装 | 生成 Repository Governance Framework 治理说明,包含垂直 SSOT 注册、读取顺序、缺失 SSOT 处理和仓库事实证据。 | | `git` | 初始化时默认安装,传 `--no-git` 可跳过 | 初始化 Git、创建 feature branch、校验分支、检测 remote,并可配置自动提交。 | | `template` | 开发模板 | 给扩展作者复制使用,不是普通项目必装扩展。 | @@ -195,7 +195,9 @@ specify extension info arch ```text /speckit.arch.generate /speckit.arch.reverse -/speckit.preview.html +/speckit.preview.low-md +/speckit.preview.mid-html +/speckit.preview.high-html /speckit.repository-governance.refresh ``` @@ -228,7 +230,7 @@ specify preset info workflow-preset - 接手旧仓库:跑 `/speckit.arch.reverse`,先从仓库事实反推架构记忆。 - 团队使用多个 agent 或新人频繁接手:跑 `/speckit.repository-governance.refresh`,把目录责任、SSOT 边界和 agent 执行规则写入上下文。 - 希望引入 BDD:保留默认 `workflow-preset`,让 `/speckit.checklist` 在规划前检查 BDD readiness,让 `/speckit.plan` 生成 BDD/UIF/fixture 行为草稿和正式契约。 -- 做前端或交互功能:在 `/speckit.specify` 或 `/speckit.plan` 后跑 `/speckit.preview.html`,先看原型再实现。 +- 做前端或交互功能:在 `/speckit.specify` 或 `/speckit.plan` 后跑 `/speckit.preview.mid-html` 或对应保真的 `*-md`/`*-html` 命令,先看预览再实现。 - 中大型实现或跨模块改动:使用默认 `/speckit.implement`,让 Core Agent 生成 handoff manifest,Vertical Planner 拆分能力维度,Worker 按允许路径执行并写 receipt。 - 需要实现后复核:保留 Final Code Review task,要求 code review receipt 检查设计、sequence、contract、quickstart 和数据副作用。 - 希望 feature 分支和提交更规范:保留 `git` 扩展,按需要配置 `.specify/extensions/git/git-config.yml`。 @@ -312,7 +314,7 @@ AGENTS.md /speckit.arch.generate /speckit.specify /speckit.clarify -/speckit.preview.html +/speckit.preview.mid-html /speckit.plan /speckit.tasks /speckit.analyze @@ -385,7 +387,7 @@ specify preset remove lean - 写 `/speckit.specify` 时只写需求和业务规则,技术栈留给 `/speckit.plan`。 - 在 `/speckit.plan` 前运行 `/speckit.clarify`,可以减少实现阶段反复改规格。 -- 对 UI 或流程不确定的功能,先用 `/speckit.preview.html` 看一个可打开的 HTML 原型。 +- 对 UI 或流程不确定的功能,先用 `/speckit.preview.low-md`、`/speckit.preview.mid-html` 或 `/speckit.preview.high-html` 看一个保真度合适的预览。 - 对中大型功能,保留默认 `workflow-preset`,让任务和实现阶段通过验证证据、handoff 和 receipt 留下可审查记录。 - 对非常小的实验,使用 `lean`,减少模板负担。 - 如果 agent 生成了你没要求的复杂设计,让它回到 `spec.md`、`plan.md` 和 `.specify/memory/constitution.md` 逐条解释依据。 diff --git a/docs/community/extensions.md b/docs/community/extensions.md index 46c79a9f4b..394097b3c4 100644 --- a/docs/community/extensions.md +++ b/docs/community/extensions.md @@ -57,7 +57,6 @@ The following community-contributed extensions are available in [`catalog.commun | GitHub Issues Integration 2 | Creates and syncs local specs from an existing GitHub issue | `integration` | Read+Write | [spec-kit-issue](https://github.com/aaronrsun/spec-kit-issue) | | Improve Extension | Audits any codebase as a senior advisor and writes prioritized, self-contained spec prompts under specs/ that the spec-kit lifecycle can process | `process` | Read+Write | [spec-kit-improve](https://github.com/d0whc3r/spec-kit-improve) | | Intake | Extract and validate SDD-ready intake artifacts from PRDs, visual designs, test cases, and other software sources | `docs` | Read+Write | [spec-kit-intake](https://github.com/bigsmartben/spec-kit-intake) | -| Interactive HTML Preview | Generate self-contained interactive HTML prototypes from Spec Kit artifacts | `docs` | Read+Write | [spec-kit-preview](https://github.com/bigsmartben/spec-kit-preview) | | Intelligent Agent Orchestrator | Cross-catalog agent discovery and intelligent prompt-to-command routing | `process` | Read+Write | [spec-kit-orchestrator](https://github.com/pragya247/spec-kit-orchestrator) | | Iterate | Iterate on spec documents with a two-phase define-and-apply workflow — refine specs mid-implementation and go straight back to building | `docs` | Read+Write | [spec-kit-iterate](https://github.com/imviancagrace/spec-kit-iterate) | | Jira Integration | Create Jira Epics, Stories, and Issues from spec-kit specifications and task breakdowns with configurable hierarchy and custom field support | `integration` | Read+Write | [spec-kit-jira](https://github.com/mbachorik/spec-kit-jira) | @@ -111,6 +110,7 @@ The following community-contributed extensions are available in [`catalog.commun | Spec Changelog | Auto-generate changelogs and release notes from spec git history and requirement diffs | `docs` | Read-only | [spec-kit-changelog](https://github.com/Quratulain-bilal/spec-kit-changelog) | | Spec Critique Extension | Dual-lens critical review of spec and plan from product strategy and engineering risk perspectives | `docs` | Read-only | [spec-kit-critique](https://github.com/arunt14/spec-kit-critique) | | Spec Diagram | Auto-generate Mermaid diagrams of SDD workflow state, feature progress, and task dependencies | `visibility` | Read-only | [spec-kit-diagram-](https://github.com/Quratulain-bilal/spec-kit-diagram-) | +| Spec Kit Preview | Generate evidence-backed low, mid, or high fidelity previews from Spec Kit artifacts as Markdown or self-contained HTML | `docs` | Read+Write | [spec-kit-preview](https://github.com/bigsmartben/spec-kit-preview) | | Spec Kit Schedule | Optimal multi-agent task scheduling via CP-SAT — DAG precedence, hallucination-aware caps, file-conflict avoidance, stochastic durations, replanning, and interactive HTML output | `process` | Read+Write | [spec-kit-schedule](https://github.com/jfranc38/spec-kit-schedule) | | Spec Kit TLDR | Render a feature's spec.md / plan.md into a review-oriented TLDR (self-contained HTML dashboard + PR-native Markdown) that surfaces risks for faster PR review. | `visibility` | Read+Write | [speckit-tldr](https://github.com/qurore/speckit-tldr) | | Spec Orchestrator | Cross-feature orchestration — track state, select tasks, and detect conflicts across parallel specs | `process` | Read-only | [spec-kit-orchestrator](https://github.com/Quratulain-bilal/spec-kit-orchestrator) | diff --git a/docs/quickstart.md b/docs/quickstart.md index ab7e9fa1a2..ade342b7ec 100644 --- a/docs/quickstart.md +++ b/docs/quickstart.md @@ -19,7 +19,7 @@ /speckit.specify /speckit.clarify /speckit.checklist -/speckit.preview.html +/speckit.preview.mid-html /speckit.plan /speckit.tasks /speckit.analyze @@ -178,19 +178,19 @@ specs//checklists/behavior-testability.md ## 6. 预览 UI/UX 规格 -对 UI、流程或交互有不确定性时,先生成自包含 HTML 原型: +对 UI、流程或交互有不确定性时,先生成保真度合适的预览产物: ```text -/speckit.preview.html mobile album browsing and reorder flow +/speckit.preview.mid-html mobile album browsing and reorder flow ``` 打开输出文件评审: ```text -specs//preview/index.html +specs//preview/wireflow-mid.html ``` -这个文件只用于实现前评审 flow、信息架构、状态和交互假设,不会修改生产代码。 +这个文件只用于实现前评审 flow、信息架构、状态和交互假设,不会修改生产代码。也可以按需要使用 `/speckit.preview.low-md`、`/speckit.preview.low-html`、`/speckit.preview.high-md` 或 `/speckit.preview.high-html`。 ## 7. 生成计划与行为契约 diff --git a/extensions/catalog.community.json b/extensions/catalog.community.json index 425b1fb42f..83c64de837 100644 --- a/extensions/catalog.community.json +++ b/extensions/catalog.community.json @@ -2391,12 +2391,12 @@ "updated_at": "2026-03-18T00:00:00Z" }, "preview": { - "name": "Interactive HTML Preview", + "name": "Spec Kit Preview", "id": "preview", - "description": "Generate self-contained interactive HTML prototypes from Spec Kit artifacts", + "description": "Generate evidence-backed low, mid, or high fidelity previews from Spec Kit artifacts as Markdown or self-contained HTML", "author": "bigsmartben", - "version": "1.0.0", - "download_url": "https://github.com/bigsmartben/spec-kit-preview/archive/refs/tags/v1.0.0.zip", + "version": "1.1.0", + "download_url": "https://github.com/bigsmartben/spec-kit-preview/archive/refs/tags/v1.1.0.zip", "repository": "https://github.com/bigsmartben/spec-kit-preview", "homepage": "https://github.com/bigsmartben/spec-kit-preview", "documentation": "https://github.com/bigsmartben/spec-kit-preview/blob/main/README.md", @@ -2408,20 +2408,22 @@ "speckit_version": ">=0.8.10.dev0" }, "provides": { - "commands": 1, + "commands": 6, "hooks": 0 }, "tags": [ "preview", "prototype", "html", + "markdown", + "wireflow", "ux" ], "verified": false, "downloads": 0, "stars": 0, "created_at": "2026-05-15T00:00:00Z", - "updated_at": "2026-05-15T00:00:00Z" + "updated_at": "2026-06-23T00:00:00Z" }, "product": { "name": "Product Spec Extension", diff --git a/extensions/catalog.json b/extensions/catalog.json index ac161d3712..b65a784f01 100644 --- a/extensions/catalog.json +++ b/extensions/catalog.json @@ -1,6 +1,6 @@ { "schema_version": "1.0", - "updated_at": "2026-06-05T00:00:00Z", + "updated_at": "2026-06-23T00:00:00Z", "catalog_url": "https://raw.githubusercontent.com/github/spec-kit/main/extensions/catalog.json", "extensions": { "agent-context": { @@ -70,10 +70,10 @@ ] }, "preview": { - "name": "Interactive HTML Preview", + "name": "Spec Kit Preview", "id": "preview", - "version": "1.0.0", - "description": "Generate self-contained interactive HTML prototypes from Spec Kit artifacts", + "version": "1.1.0", + "description": "Generate evidence-backed low, mid, or high fidelity previews from Spec Kit artifacts as Markdown or self-contained HTML", "author": "bigsmartben", "repository": "https://github.com/bigsmartben/spec-kit-preview", "license": "MIT", @@ -82,12 +82,14 @@ "speckit_version": ">=0.8.10.dev0" }, "provides": { - "commands": 1 + "commands": 6 }, "tags": [ "preview", "prototype", "html", + "markdown", + "wireflow", "ux" ] }, diff --git a/extensions/preview/CATALOG-SUBMISSION.md b/extensions/preview/CATALOG-SUBMISSION.md index 414072d68c..6ffd5f2eef 100644 --- a/extensions/preview/CATALOG-SUBMISSION.md +++ b/extensions/preview/CATALOG-SUBMISSION.md @@ -1,34 +1,41 @@ # Spec Kit Extension Submission Extension ID: preview -Name: Interactive HTML Preview -Version: 1.0.0 -Description: Generate self-contained interactive HTML prototypes from Spec Kit artifacts +Name: Spec Kit Preview +Version: 1.1.0 +Description: Generate evidence-backed low, mid, or high fidelity previews from Spec Kit artifacts as Markdown or self-contained HTML Author: bigsmartben Repository URL: https://github.com/bigsmartben/spec-kit-preview -Download URL: https://github.com/bigsmartben/spec-kit-preview/archive/refs/tags/v1.0.0.zip +Download URL: https://github.com/bigsmartben/spec-kit-preview/archive/refs/tags/v1.1.0.zip Documentation URL: https://github.com/bigsmartben/spec-kit-preview/blob/main/README.md License: MIT Required Spec Kit version: >=0.8.10.dev0 -Commands count: 1 +Commands count: 6 Hooks count: 0 -Tags: preview, prototype, html, ux +Tags: preview, prototype, html, markdown, wireflow, ux ## Key Features -- Adds `speckit.preview.html`. -- Generates `specs//preview/index.html`. -- Keeps prototypes self-contained with inline CSS and JavaScript. +- Adds `speckit.preview.low-md` and `speckit.preview.low-html`. +- Adds `speckit.preview.mid-md` and `speckit.preview.mid-html`. +- Adds `speckit.preview.high-md` and `speckit.preview.high-html`. +- Generates `specs//preview/wireflow-low.md`, `wireflow-mid.md`, or `wireflow-high.md`. +- Generates matching `wireflow-low.html`, `wireflow-mid.html`, or `wireflow-high.html`. +- Uses fixed templates under `templates/preview/` for output sections, table schemas, HTML shells, and preserved-review slots. +- Uses `schemas/preview/contract.json` and `schemas/preview/contract.schema.json` as the structural validation source. +- Keeps previews self-contained with inline CSS and fidelity-appropriate JavaScript. - Explicitly avoids production source, spec, plan, and task file changes. -- Captures prototype assumptions and unresolved questions. +- Captures coverage evidence, inferred assumptions, unsupported items, and unresolved questions. ## Testing Performed - `python3 -m py_compile tests/validate-extension.py` - `python3 tests/validate-extension.py` - Installed locally with `specify extension add --dev /home/administrator/github/spec-kit-preview` in a fresh Spec Kit project. -- Verified `.qwen/commands/speckit.preview.html.md` was registered. +- Verified six `.qwen/commands/speckit.preview.*.md` commands were registered. - Verified installed extension contents excluded `tests/` via `.extensionignore`. -- Created release `v1.0.0`. -- Installed release ZIP in a fresh Spec Kit project: - `specify extension add preview --from https://github.com/bigsmartben/spec-kit-preview/archive/refs/tags/v1.0.0.zip` +## Release Checklist + +- Create release `v1.1.0` from this revision. +- Install release ZIP in a fresh Spec Kit project: + `specify extension add preview --from https://github.com/bigsmartben/spec-kit-preview/archive/refs/tags/v1.1.0.zip` diff --git a/extensions/preview/CHANGELOG.md b/extensions/preview/CHANGELOG.md index 7d20e4309d..1ef3f12c32 100644 --- a/extensions/preview/CHANGELOG.md +++ b/extensions/preview/CHANGELOG.md @@ -1,5 +1,14 @@ # Changelog +## v1.1.0 + +- Adds six preview commands: `speckit.preview.low-md`, `speckit.preview.low-html`, `speckit.preview.mid-md`, `speckit.preview.mid-html`, `speckit.preview.high-md`, and `speckit.preview.high-html`. +- Adds separate Markdown and HTML output designs for low, mid, and high fidelity preview artifacts. +- Adds fixed preview templates under `templates/preview/` so command prompts no longer define output structure inline. +- Adds schema-backed validation contracts under `schemas/preview/` for command/template structure checks. +- Adds consistent active feature detection and update-preservation guidance across preview commands. +- Strengthens package validation for command/template responsibility separation, output boundaries, and fidelity-specific contracts. + ## v1.0.0 - Initial release. diff --git a/extensions/preview/README.md b/extensions/preview/README.md index 72e0bde2a6..74e2de958b 100644 --- a/extensions/preview/README.md +++ b/extensions/preview/README.md @@ -1,37 +1,52 @@ -# Interactive HTML Preview Extension +# Spec Kit Preview Extension -Generate self-contained interactive HTML prototypes from Spec Kit feature artifacts. +Generate evidence-backed low, mid, or high fidelity previews from Spec Kit feature artifacts as Markdown or self-contained HTML. ## Overview -This extension adds a review artifact between specification and implementation. It helps teams validate product flows, layout assumptions, UI states, and interaction details before production code is planned or built. +This extension adds review artifacts between specification and implementation. It helps teams validate product flows, page/state structure, coverage evidence, layout assumptions, UI states, and interaction details before production code is planned or built. -The generated prototype is intentionally standalone: +Commands act as execution orchestrators: they resolve the active feature, load Spec Kit artifacts, apply evidence policy, and fill fixed templates under `templates/preview/`. The templates are the source of truth for output sections, table schemas, HTML shells, and preserved-review slots. Structural validation is driven by `schemas/preview/contract.json`, validated against `schemas/preview/contract.schema.json`. + +Generated HTML previews are intentionally standalone: - one HTML file -- inline CSS and JavaScript +- inline CSS +- JavaScript only where the selected fidelity allows it - no network dependencies - no build step - no production source changes -## Command +Markdown previews are evidence-backed wireflows with requirement coverage, unknowns, and review questions. + +## Commands | Command | Description | |---------|-------------| -| `speckit.preview.html` | Generate `specs//preview/index.html` from the active feature artifacts | +| `speckit.preview.low-md` | Generate `specs//preview/wireflow-low.md` | +| `speckit.preview.low-html` | Generate `specs//preview/wireflow-low.html` | +| `speckit.preview.mid-md` | Generate `specs//preview/wireflow-mid.md` | +| `speckit.preview.mid-html` | Generate `specs//preview/wireflow-mid.html` | +| `speckit.preview.high-md` | Generate `specs//preview/wireflow-high.md` | +| `speckit.preview.high-html` | Generate `specs//preview/wireflow-high.html` | ## Usage ```text -/speckit.preview.html +/speckit.preview.low-md +/speckit.preview.mid-html +/speckit.preview.high-html ``` Optional focus examples: ```text -/speckit.preview.html mobile onboarding flow -/speckit.preview.html admin dashboard empty and error states -/speckit.preview.html checkout review flow, desktop first +/speckit.preview.low-md onboarding happy path +/speckit.preview.low-html onboarding happy path +/speckit.preview.mid-md admin dashboard empty and error states +/speckit.preview.mid-html admin dashboard empty and error states +/speckit.preview.high-md checkout review flow, desktop first +/speckit.preview.high-html checkout review flow, desktop first ``` ## Inputs @@ -50,21 +65,32 @@ It also reads `.specify/memory/constitution.md` when present. ## Output ```text -specs//preview/index.html +specs//preview/wireflow-low.md +specs//preview/wireflow-low.html +specs//preview/wireflow-mid.md +specs//preview/wireflow-mid.html +specs//preview/wireflow-high.md +specs//preview/wireflow-high.html ``` -The HTML file can be opened directly in a browser. +HTML files can be opened directly in a browser. + +## Fidelity + +- Low fidelity: early feasibility review, abstract nodes, core path, branch points, and major missing scope. +- Mid fidelity: product, UX, and engineering review with source-defined labels, page/state inventory, basic layout relationships, and state coverage. +- High fidelity: final confirmation with interaction matrix, state matrix, validation feedback, permissions, responsive states, and clickable HTML simulation in the `.html` command. ## Boundaries -This extension does not modify app source code, tests, build files, specs, plans, or tasks. The preview is not a production implementation and should not replace `/speckit.plan`, `/speckit.tasks`, or `/speckit.implement`. +This extension does not modify app source code, tests, build files, specs, plans, or tasks. Previews are not production implementation and should not replace `/speckit.plan`, `/speckit.tasks`, or `/speckit.implement`. ## Installation -Install from the v1.0.0 release ZIP: +Install from the v1.1.0 release ZIP: ```bash -specify extension add preview --from https://github.com/bigsmartben/spec-kit-preview/archive/refs/tags/v1.0.0.zip +specify extension add preview --from https://github.com/bigsmartben/spec-kit-preview/archive/refs/tags/v1.1.0.zip ``` For local development from this repository root: @@ -78,7 +104,8 @@ specify extension add --dev . When the command runs in a Spec Kit project, it writes only: ```text -specs//preview/index.html +specs//preview/wireflow-*.md +specs//preview/wireflow-*.html ``` Installing the extension copies this repository into: @@ -86,3 +113,29 @@ Installing the extension copies this repository into: ```text .specify/extensions/preview/ ``` + +## Template Sources + +Command prompts load one matching template per output: + +```text +templates/preview/low.md +templates/preview/low.html +templates/preview/mid.md +templates/preview/mid.html +templates/preview/high.md +templates/preview/high.html +``` + +The command layer owns context loading, evidence policy, and file-write boundaries. The template layer owns sections, required tables, HTML structure, and the preserved review records slot. + +## Validation Contract + +Repository validation uses a schema-backed JSON contract: + +```text +schemas/preview/contract.schema.json +schemas/preview/contract.json +``` + +`tests/validate-extension.py` validates the contract shape with a standard-library JSON Schema subset, then uses the contract to verify manifest command mappings, command/template files, required slots, forbidden legacy phrases, and documentation alignment. diff --git a/extensions/preview/commands/speckit.preview.high-html.md b/extensions/preview/commands/speckit.preview.high-html.md new file mode 100644 index 0000000000..108806a8b5 --- /dev/null +++ b/extensions/preview/commands/speckit.preview.high-html.md @@ -0,0 +1,91 @@ +--- +description: Generate a high-fidelity evidence-backed interactive HTML preview from Spec Kit artifacts. +--- + +## User Input + +```text +$ARGUMENTS +``` + +You **MUST** evaluate the user input before proceeding. Treat it as preview focus, audience, device, flow, interaction, or review criteria. + +If `$ARGUMENTS` requests a page, role, device, state, interaction, validation rule, permission, or business rule that is not supported by the loaded artifacts, do not invent it. Mark it as `输入未说明`, or use `推理补齐` only for the minimal traceable inference required to preserve preview coherence. + +## Goal + +Generate or update `specs//preview/wireflow-high.html`. + +High-fidelity HTML is an interactive review artifact for implementation handoff validation. It simulates documented interactions and observable state transitions without emitting production implementation code. + +## Command Responsibilities + +- Resolve the active feature and load source artifacts. +- Apply evidence policy and fidelity-specific extraction rules. +- Load the output template from `.specify/extensions/preview/templates/preview/high.html`; when running from this extension repository, use `templates/preview/high.html`. +- Populate template slots with evidence-backed content only. +- Write only `specs//preview/wireflow-high.html`. Create `specs//preview/` if it is missing; do not create any other files or directories. + +Do not redefine template sections, table columns, CSS shell, JavaScript hook names, or output structure in this command. The template is the source of truth for HTML shape and required tables. + +## Boundaries + +- Do not modify source code, tests, app assets, package manifests, build configuration, feature specs, plans, tasks, or memory files. +- Do not create Markdown, Figma files, images, screenshots, or production UI. +- Do not invent business rules, roles, fields, states, copy, data rules, or interactions. Mark unsupported items as `输入未说明`; mark only minimal traceable inferences as `推理补齐`. +- HTML output must be self-contained and network-free. Use inline CSS and JavaScript only to simulate documented interactions and state changes. +- If an interaction is not documented or minimally inferable from source evidence, show it as a review question instead of making it clickable. +- If the template file cannot be read, stop with an error explaining that the preview extension template is missing. + +## Context Loading + +1. Verify the current directory is a Spec Kit project by checking for `.specify/`. +2. Identify the active feature: + - Prefer `SPECIFY_FEATURE` when set. + - Otherwise use the current Git branch name when it exactly matches a directory under `specs/`. + - Otherwise inspect `specs/` and use it only when there is exactly one unambiguous candidate directory. + - Do not choose by most recent timestamp when multiple feature directories exist. + - If the feature cannot be identified, stop and ask the user to set `SPECIFY_FEATURE` or run from the feature branch. +3. Read these files when present: + - `specs//spec.md` (required) + - `specs//plan.md` + - `specs//research.md` + - `specs//data-model.md` + - `specs//contracts/` + - `specs//quickstart.md` +4. Read `.specify/memory/constitution.md` if present. +5. If `spec.md` is missing, stop with an error explaining that `/speckit.specify` must run first. +6. If `specs//preview/wireflow-high.html` already exists, read it before writing. Preserve user-authored review notes, decisions, and unresolved questions when they remain consistent with current source artifacts; label changed items as `UPDATED` and superseded items as `SUPERSEDED`. + +## Evidence Policy + +Use these coverage labels exactly: `已覆盖`, `部分覆盖`, `未覆盖`, `输入未说明`, `推理补齐`. + +Every requirement, screen, node, field, interaction, state, branch, permission, validation rule, and system response included in the preview must include a coverage label and provenance. Provenance must be either a source anchor or a `推理补齐` explanation. + +Coverage evidence must identify the source with a precise anchor: file path, heading, section id, line number, table row, or short quoted source excerpt. Use `输入未说明` when no source supports the requested item. Use `推理补齐` only when a minimal inference connects two supported facts; include the reasoning bridge and keep it non-authoritative. + +## High-Fidelity Extraction Policy + +Extract only: + +- Artifact-defined data conditions, roles, permissions, validation rules, and user-visible feedback. +- Interaction matrix content reflected in HTML behavior or visible documentation. +- State matrix content for hover, focus, click, loading, success, failure, disabled, empty, validation, timeout, retry, cancellation, and permission-denied states where relevant. +- Toast, inline error, confirmation dialog, retry, cancellation, timeout, and loading feedback rules when supported by evidence. +- Responsive mobile and desktop review states when the feature is user-facing; mark unsupported device assumptions as `推理补齐`. +- Motion or timing notes only when artifacts state them or when explicitly marked as `推理补齐`. +- Coverage evidence mapped from requirements to preview screens, interactions, states, or review questions. + +Do not claim that HTML is production-ready. Do not generate app components, production framework code, remote assets, analytics, or API calls. + +## Procedure + +1. Summarize the feature goal, personas, primary scenarios, and constraints from loaded artifacts. +2. Select the preview focus from `$ARGUMENTS`; if absent, cover the highest-priority user story and primary alternate flow state. +3. Extract evidence-backed pages, tasks, fields, controls, roles, permissions, data conditions, states, decisions, and system responses. +4. Fill the high-fidelity HTML template slots for metadata, initial screen, wireflow, node inventory, interaction matrix, state matrix, branches, coverage, preserved review records, and unresolved review questions. +5. Simulate only documented interactions. Label minimally inferred behavior as `推理补齐`, and record unsupported behavior as a review question instead of making it interactive. +6. Escape user-provided content and source excerpts before inserting them into HTML. +7. Write `specs//preview/wireflow-high.html`, preserving existing review content as described above. +8. Report output path, fidelity, input sources, flows covered, covered interactions, inferred assumptions, unsupported items, and unresolved review questions. diff --git a/extensions/preview/commands/speckit.preview.high-md.md b/extensions/preview/commands/speckit.preview.high-md.md new file mode 100644 index 0000000000..eba9b9ec84 --- /dev/null +++ b/extensions/preview/commands/speckit.preview.high-md.md @@ -0,0 +1,86 @@ +--- +description: Generate a high-fidelity evidence-backed Markdown interaction preview from Spec Kit artifacts. +--- + +## User Input + +```text +$ARGUMENTS +``` + +You **MUST** evaluate the user input before proceeding. Treat it as preview focus, audience, device, flow, interaction, or review criteria. + +If `$ARGUMENTS` requests a page, role, device, state, interaction, validation rule, permission, or business rule that is not supported by the loaded artifacts, do not invent it. Mark it as `输入未说明`, or use `推理补齐` only for the minimal traceable inference required to preserve preview coherence. + +## Goal + +Generate or update `specs//preview/wireflow-high.md`. + +High-fidelity Markdown is a final product validation artifact for implementation handoff validation. It documents evidence-backed interaction feedback, state transitions, validation, permissions, and edge cases without emitting production implementation code. + +## Command Responsibilities + +- Resolve the active feature and load source artifacts. +- Apply evidence policy and fidelity-specific extraction rules. +- Load the output template from `.specify/extensions/preview/templates/preview/high.md`; when running from this extension repository, use `templates/preview/high.md`. +- Populate template slots with evidence-backed content only. +- Write only `specs//preview/wireflow-high.md`. Create `specs//preview/` if it is missing; do not create any other files or directories. + +Do not redefine template sections, table columns, or output structure in this command. The template is the source of truth for Markdown shape and required tables. + +## Boundaries + +- Do not modify source code, tests, app assets, package manifests, build configuration, feature specs, plans, tasks, or memory files. +- Do not create HTML, Figma files, images, screenshots, or production UI. +- Do not invent business rules, roles, fields, states, copy, data rules, or interactions. Mark unsupported items as `输入未说明`; mark only minimal traceable inferences as `推理补齐`. +- If the template file cannot be read, stop with an error explaining that the preview extension template is missing. + +## Context Loading + +1. Verify the current directory is a Spec Kit project by checking for `.specify/`. +2. Identify the active feature: + - Prefer `SPECIFY_FEATURE` when set. + - Otherwise use the current Git branch name when it exactly matches a directory under `specs/`. + - Otherwise inspect `specs/` and use it only when there is exactly one unambiguous candidate directory. + - Do not choose by most recent timestamp when multiple feature directories exist. + - If the feature cannot be identified, stop and ask the user to set `SPECIFY_FEATURE` or run from the feature branch. +3. Read these files when present: + - `specs//spec.md` (required) + - `specs//plan.md` + - `specs//research.md` + - `specs//data-model.md` + - `specs//contracts/` + - `specs//quickstart.md` +4. Read `.specify/memory/constitution.md` if present. +5. If `spec.md` is missing, stop with an error explaining that `/speckit.specify` must run first. +6. If `specs//preview/wireflow-high.md` already exists, read it before writing. Preserve user-authored review notes, decisions, and unresolved questions when they remain consistent with current source artifacts; label changed items as `UPDATED` and superseded items as `SUPERSEDED`. + +## Evidence Policy + +Use these coverage labels exactly: `已覆盖`, `部分覆盖`, `未覆盖`, `输入未说明`, `推理补齐`. + +Every requirement, page, node, field, interaction, state, branch, permission, validation rule, and system response included in the preview must include a coverage label and provenance. Provenance must be either a source anchor or a `推理补齐` explanation. + +Coverage evidence must identify the source with a precise anchor: file path, heading, section id, line number, table row, or short quoted source excerpt. Use `输入未说明` when no source supports the requested item. Use `推理补齐` only when a minimal inference connects two supported facts; include the reasoning bridge and keep it non-authoritative. + +## High-Fidelity Extraction Policy + +Extract only: + +- Artifact-defined data conditions, roles, permissions, validation rules, and user-visible feedback. +- Interaction matrix content: event, target, precondition, feedback, state change, error handling, coverage, and evidence. +- State matrix content for hover, focus, click, loading, success, failure, disabled, empty, validation, timeout, retry, cancellation, and permission-denied states where relevant. +- Toast, inline error, confirmation dialog, retry, cancellation, timeout, and loading feedback rules when supported by evidence. +- Motion or timing notes only when artifacts state them or when explicitly marked as `推理补齐`. +- Coverage evidence mapped from requirements to preview nodes, interactions, states, or review questions. + +Do not claim that Markdown is a clickable prototype or production-ready. + +## Procedure + +1. Summarize the feature goal, personas, primary scenarios, and constraints from loaded artifacts. +2. Select the preview focus from `$ARGUMENTS`; if absent, cover the highest-priority user story and primary alternate flow state. +3. Extract evidence-backed pages, tasks, fields, controls, roles, permissions, data conditions, states, decisions, and system responses. +4. Fill the high-fidelity Markdown template slots for metadata, evidence summary, wireflow, node inventory, interaction matrix, state matrix, branches, coverage, preserved review records, and unresolved review questions. +5. Write `specs//preview/wireflow-high.md`, preserving existing review content as described above. +6. Report output path, fidelity, input sources, flows covered, covered interactions, inferred assumptions, unsupported items, and unresolved review questions. diff --git a/extensions/preview/commands/speckit.preview.html.md b/extensions/preview/commands/speckit.preview.html.md deleted file mode 100644 index 73ca1213fe..0000000000 --- a/extensions/preview/commands/speckit.preview.html.md +++ /dev/null @@ -1,112 +0,0 @@ ---- -description: Generate a self-contained interactive HTML prototype from Spec Kit artifacts. ---- - -## User Input - -```text -$ARGUMENTS -``` - -You **MUST** consider the user input before proceeding (if not empty). Treat it as prototype focus, audience, device, flow, fidelity, or style guidance. - -## Goal - -Generate or update a self-contained HTML prototype for the active Spec Kit feature: - -- Output: `specs//preview/index.html` -- Purpose: make product flow, information architecture, and interaction assumptions reviewable before implementation. -- Fidelity: interactive prototype only; this is not a production implementation. - -## Positioning - -Use this command after `/speckit.specify` and `/speckit.clarify` when a feature has user-facing flows that benefit from visual review. It may also run after `/speckit.plan` to reflect technical or platform constraints already captured in the plan. - -Do not run this as a substitute for `/speckit.plan`, `/speckit.tasks`, or `/speckit.implement`. The prototype is an exploratory artifact for alignment, not source code for the application. - -## Operating Boundaries - -- Write only `specs//preview/index.html`. -- Do not modify source code, tests, app assets, package manifests, build configuration, feature specs, plans, tasks, or memory files. -- Do not create additional files unless the user explicitly asks for separate assets. -- No external network dependencies: do not load fonts, icons, images, scripts, stylesheets, analytics, or APIs from the network. -- Use inline CSS and JavaScript. The HTML file must open directly in a browser without a build step. -- Do not use production framework code, generated application components, or repository source files. -- Do not invent business rules. If the artifacts do not specify a behavior, use a clearly labeled prototype assumption. -- Preserve an existing preview only when it contains user-authored decisions not contradicted by the current artifacts; otherwise update it to match the current feature. - -## Context Loading - -1. Verify the current directory is a Spec Kit project by checking for `.specify/`. -2. Identify the active feature: - - Prefer `SPECIFY_FEATURE` when set. - - Otherwise use the current Git branch name when it matches a directory under `specs/`. - - Otherwise inspect `specs/` and choose the most recent feature directory only if there is a single clear candidate. - - If the feature cannot be identified, stop and ask the user to set `SPECIFY_FEATURE` or run from the feature branch. -3. Read these files when present: - - `specs//spec.md` (required) - - `specs//plan.md` - - `specs//research.md` - - `specs//data-model.md` - - `specs//contracts/` - - `specs//quickstart.md` -4. Read `.specify/memory/constitution.md` if present for product, accessibility, and quality constraints. -5. If `spec.md` is missing, stop with an error explaining that `/speckit.specify` must run first. - -## Prototype Requirements - -The generated HTML must include: - -- A realistic first screen for the feature, not a marketing landing page. -- At least one primary user flow that can be clicked through end to end. -- Meaningful state changes from user actions, such as selection, filtering, validation, navigation, save confirmation, or simulated data changes. -- empty, loading, error, and success states when relevant to the feature. -- responsive layout for mobile and desktop widths. -- keyboard reachable controls and visible focus states. -- Accessible labels or text for interactive controls. -- Sample data grounded in the feature spec. Mark any invented sample data as prototype-only. -- A visible "Prototype Assumptions" section or panel that lists inferred behaviors and unresolved questions. - -For operational tools, dashboards, admin screens, and B2B workflows, prefer dense, scannable interfaces over marketing-style hero pages. For consumer or creative experiences, match the domain while keeping the feature workflow reviewable. - -## HTML Construction Rules - -- Produce valid HTML with inline ` + + +
+
+

High-Fidelity Preview

+ +
+ +
+

输入来源与读取范围

+ {{input_sources}} +
+ +
+

需求证据摘要

+ {{evidence_summary}} +
+ +
+

首屏与交互预览

+
+
{{initial_screen}}
+
{{interaction_controls}}
+
+
+ +
+

Wireflow

+ {{wireflow}} +
+ +
+

页面/节点清单

+ + + + + + + + + + + + + + + {{node_inventory_rows}} +
ID屏幕/节点目的可见元素用户动作系统响应状态覆盖标签证据/推理
+
+ +
+

交互矩阵

+ + + + + + + + + + + + + + {{interaction_matrix_rows}} +
事件目标前置条件反馈状态变化错误处理覆盖标签证据/推理
+
+ +
+

状态矩阵

+ + + + + + + + + + + + {{state_matrix_rows}} +
状态触发条件用户可见反馈恢复/下一步覆盖标签证据/推理
+
+ +
+

分支、状态与异常路径

+ {{branches_states_exceptions}} +
+ +
+

覆盖度证据结论

+ + + + + + + + + + + {{coverage_rows}} +
需求/场景预览位置覆盖标签证据/推理缺口或问题
+
+ +
+

已保留评审记录

+ {{preserved_review_records}} +
+ +
+

未覆盖项、未知项与评审问题

+ {{review_questions}} +
+
+ + + diff --git a/extensions/preview/templates/preview/high.md b/extensions/preview/templates/preview/high.md new file mode 100644 index 0000000000..ba741015a5 --- /dev/null +++ b/extensions/preview/templates/preview/high.md @@ -0,0 +1,53 @@ +# High-Fidelity Preview + +## 保真级别与评审场景 + +{{metadata}} + +## 输入来源与读取范围 + +{{input_sources}} + +## 需求证据摘要 + +{{evidence_summary}} + +## Wireflow + +{{wireflow}} + +## 页面/节点清单 + +| ID | 页面/节点 | 目的 | 可见元素 | 用户动作 | 系统响应 | 状态 | 覆盖标签 | 证据/推理 | +|----|-----------|------|----------|----------|----------|------|----------|-----------| +{{node_inventory_rows}} + +## 交互矩阵 + +| 事件 | 目标 | 前置条件 | 反馈 | 状态变化 | 错误处理 | 覆盖标签 | 证据/推理 | +|------|------|----------|------|----------|----------|----------|-----------| +{{interaction_matrix_rows}} + +## 状态矩阵 + +| 状态 | 触发条件 | 用户可见反馈 | 恢复/下一步 | 覆盖标签 | 证据/推理 | +|------|----------|--------------|-------------|----------|-----------| +{{state_matrix_rows}} + +## 分支、状态与异常路径 + +{{branches_states_exceptions}} + +## 覆盖度证据结论 + +| 需求/场景 | 预览位置 | 覆盖标签 | 证据/推理 | 缺口或问题 | +|-----------|----------|----------|-----------|------------| +{{coverage_rows}} + +## 已保留评审记录 + +{{preserved_review_records}} + +## 未覆盖项、未知项与评审问题 + +{{review_questions}} diff --git a/extensions/preview/templates/preview/low.html b/extensions/preview/templates/preview/low.html new file mode 100644 index 0000000000..1c273fe7e7 --- /dev/null +++ b/extensions/preview/templates/preview/low.html @@ -0,0 +1,149 @@ + + + + + + Low-Fidelity Preview + + + +
+
+

Low-Fidelity Preview

+ +
+ +
+

输入来源与读取范围

+ {{input_sources}} +
+ +
+

需求证据摘要

+ {{evidence_summary}} +
+ +
+

Wireflow

+
{{wireflow}}
+
+ +
+

页面/节点清单

+ + + + + + + + + + + + + + {{node_inventory_rows}} + +
ID节点目的用户动作系统响应覆盖标签证据/推理
+
+ +
+

分支、状态与异常路径

+ {{branches_states_exceptions}} +
+ +
+

覆盖度证据结论

+ + + + + + + + + + + + {{coverage_rows}} + +
需求/场景预览位置覆盖标签证据/推理缺口或问题
+
+ +
+

已保留评审记录

+ {{preserved_review_records}} +
+ +
+

未覆盖项、未知项与评审问题

+ {{review_questions}} +
+
+ + diff --git a/extensions/preview/templates/preview/low.md b/extensions/preview/templates/preview/low.md new file mode 100644 index 0000000000..a872c04fdb --- /dev/null +++ b/extensions/preview/templates/preview/low.md @@ -0,0 +1,41 @@ +# Low-Fidelity Preview + +## 保真级别与评审场景 + +{{metadata}} + +## 输入来源与读取范围 + +{{input_sources}} + +## 需求证据摘要 + +{{evidence_summary}} + +## Wireflow + +{{wireflow}} + +## 页面/节点清单 + +| ID | 节点 | 目的 | 用户动作 | 系统响应 | 覆盖标签 | 证据/推理 | +|----|------|------|----------|----------|----------|-----------| +{{node_inventory_rows}} + +## 分支、状态与异常路径 + +{{branches_states_exceptions}} + +## 覆盖度证据结论 + +| 需求/场景 | 预览位置 | 覆盖标签 | 证据/推理 | 缺口或问题 | +|-----------|----------|----------|-----------|------------| +{{coverage_rows}} + +## 已保留评审记录 + +{{preserved_review_records}} + +## 未覆盖项、未知项与评审问题 + +{{review_questions}} diff --git a/extensions/preview/templates/preview/mid.html b/extensions/preview/templates/preview/mid.html new file mode 100644 index 0000000000..fba64ec668 --- /dev/null +++ b/extensions/preview/templates/preview/mid.html @@ -0,0 +1,191 @@ + + + + + + Mid-Fidelity Preview + + + +
+
+

Mid-Fidelity Preview

+ +
+ +
+

输入来源与读取范围

+ {{input_sources}} +
+ +
+

需求证据摘要

+ {{evidence_summary}} +
+ +
+

Wireflow

+
{{wireflow}}
+
+ +
+

页面/节点清单

+ + + + + + + + + + + + + + + {{node_inventory_rows}} +
ID页面/节点目的可见元素用户动作系统响应状态覆盖标签证据/推理
+
+ +
+

状态清单

+ +
+ {{state_examples}} +
+ + + + + + + + + + + + {{state_inventory_rows}} +
状态触发条件用户可见反馈恢复/下一步覆盖标签证据/推理
+
+ +
+

分支、状态与异常路径

+ {{branches_states_exceptions}} +
+ +
+

覆盖度证据结论

+ + + + + + + + + + + {{coverage_rows}} +
需求/场景预览位置覆盖标签证据/推理缺口或问题
+
+ +
+

已保留评审记录

+ {{preserved_review_records}} +
+ +
+

未覆盖项、未知项与评审问题

+ {{review_questions}} +
+
+ + + diff --git a/extensions/preview/templates/preview/mid.md b/extensions/preview/templates/preview/mid.md new file mode 100644 index 0000000000..b880ed5cd9 --- /dev/null +++ b/extensions/preview/templates/preview/mid.md @@ -0,0 +1,47 @@ +# Mid-Fidelity Preview + +## 保真级别与评审场景 + +{{metadata}} + +## 输入来源与读取范围 + +{{input_sources}} + +## 需求证据摘要 + +{{evidence_summary}} + +## Wireflow + +{{wireflow}} + +## 页面/节点清单 + +| ID | 页面/节点 | 目的 | 可见元素 | 用户动作 | 系统响应 | 状态 | 覆盖标签 | 证据/推理 | +|----|-----------|------|----------|----------|----------|------|----------|-----------| +{{node_inventory_rows}} + +## 状态清单 + +| 状态 | 触发条件 | 用户可见反馈 | 恢复/下一步 | 覆盖标签 | 证据/推理 | +|------|----------|--------------|-------------|----------|-----------| +{{state_inventory_rows}} + +## 分支、状态与异常路径 + +{{branches_states_exceptions}} + +## 覆盖度证据结论 + +| 需求/场景 | 预览位置 | 覆盖标签 | 证据/推理 | 缺口或问题 | +|-----------|----------|----------|-----------|------------| +{{coverage_rows}} + +## 已保留评审记录 + +{{preserved_review_records}} + +## 未覆盖项、未知项与评审问题 + +{{review_questions}} diff --git a/extensions/preview/tests/validate-extension.py b/extensions/preview/tests/validate-extension.py index 5a0f8a6436..c8fde511a4 100644 --- a/extensions/preview/tests/validate-extension.py +++ b/extensions/preview/tests/validate-extension.py @@ -3,13 +3,18 @@ from __future__ import annotations +import json +import re import sys from pathlib import Path +from typing import Any import yaml ROOT = Path(__file__).resolve().parent.parent +CONTRACT_PATH = ROOT / "schemas" / "preview" / "contract.json" +CONTRACT_SCHEMA_PATH = ROOT / "schemas" / "preview" / "contract.schema.json" def fail(message: str) -> None: @@ -17,51 +22,234 @@ def fail(message: str) -> None: raise SystemExit(1) +def load_json(path: Path) -> Any: + if not path.is_file(): + fail(f"{path.relative_to(ROOT)} is missing") + return json.loads(path.read_text(encoding="utf-8")) + + +def require_phrases(name: str, content: str, phrases: list[str]) -> None: + missing = [phrase for phrase in phrases if phrase not in content] + if missing: + fail(f"{name} missing required phrases: {missing}") + + +def reject_phrases(name: str, content: str, phrases: list[str]) -> None: + stale = [phrase for phrase in phrases if phrase in content] + if stale: + fail(f"{name} contains disallowed phrases: {stale}") + + +def resolve_ref(schema: dict[str, Any], ref: str) -> dict[str, Any]: + if not ref.startswith("#/"): + fail(f"unsupported JSON schema ref: {ref}") + current: Any = schema + for part in ref[2:].split("/"): + current = current[part] + if not isinstance(current, dict): + fail(f"JSON schema ref does not resolve to an object: {ref}") + return current + + +def validate_json_schema_subset( + value: Any, + node: dict[str, Any], + root_schema: dict[str, Any], + path: str = "$", +) -> None: + if "$ref" in node: + validate_json_schema_subset(value, resolve_ref(root_schema, node["$ref"]), root_schema, path) + return + + expected_type = node.get("type") + if expected_type == "object": + if not isinstance(value, dict): + fail(f"{path} must be an object") + required = node.get("required", []) + missing = [key for key in required if key not in value] + if missing: + fail(f"{path} missing required keys: {missing}") + properties = node.get("properties", {}) + if node.get("additionalProperties") is False: + extra = [key for key in value if key not in properties] + if extra: + fail(f"{path} contains unsupported keys: {extra}") + for key, child in properties.items(): + if key in value: + validate_json_schema_subset(value[key], child, root_schema, f"{path}.{key}") + return + + if expected_type == "array": + if not isinstance(value, list): + fail(f"{path} must be an array") + min_items = node.get("minItems") + if min_items is not None and len(value) < min_items: + fail(f"{path} must contain at least {min_items} items") + if node.get("uniqueItems"): + serialized = [json.dumps(item, sort_keys=True, ensure_ascii=False) for item in value] + if len(serialized) != len(set(serialized)): + fail(f"{path} must not contain duplicate items") + item_schema = node.get("items") + if item_schema: + for index, item in enumerate(value): + validate_json_schema_subset(item, item_schema, root_schema, f"{path}[{index}]") + return + + if expected_type == "string": + if not isinstance(value, str): + fail(f"{path} must be a string") + pattern = node.get("pattern") + if pattern and not re.search(pattern, value): + fail(f"{path} does not match pattern {pattern!r}") + + enum = node.get("enum") + if enum is not None and value not in enum: + fail(f"{path} must be one of {enum}") + + +def validate_contract(contract: dict[str, Any], schema: dict[str, Any]) -> None: + validate_json_schema_subset(contract, schema, schema) + + +def render_catalog_phrase(template: str, manifest: dict[str, Any]) -> str: + extension = manifest["extension"] + commands = manifest["provides"]["commands"] + tags = manifest["tags"] + replacements = { + "{extension.id}": extension["id"], + "{extension.name}": extension["name"], + "{extension.version}": extension["version"], + "{extension.description}": extension["description"], + "{extension.repository}": extension["repository"], + "{requires.speckit_version}": manifest["requires"]["speckit_version"], + "{commands.count}": str(len(commands)), + "{tags.csv}": ", ".join(tags), + } + phrase = template + for token, value in replacements.items(): + phrase = phrase.replace(token, value) + return phrase + + def main() -> None: - manifest_path = ROOT / "extension.yml" - command_path = ROOT / "commands" / "speckit.preview.html.md" + contract = load_json(CONTRACT_PATH) + contract_schema = load_json(CONTRACT_SCHEMA_PATH) + validate_contract(contract, contract_schema) + manifest_path = ROOT / "extension.yml" if not manifest_path.is_file(): fail("extension.yml is missing") - if not command_path.is_file(): - fail("commands/speckit.preview.html.md is missing") - manifest = yaml.safe_load(manifest_path.read_text(encoding="utf-8")) extension = manifest.get("extension", {}) commands = manifest.get("provides", {}).get("commands", []) - if extension.get("id") != "preview": - fail("extension.id must be preview") - if extension.get("version") != "1.0.0": - fail("extension.version must be 1.0.0") - if extension.get("repository") != "https://github.com/bigsmartben/spec-kit-preview": + expected_extension = contract["extension"] + if extension.get("id") != expected_extension["id"]: + fail(f"extension.id must be {expected_extension['id']}") + if extension.get("version") != expected_extension["version"]: + fail(f"extension.version must be {expected_extension['version']}") + if extension.get("repository") != expected_extension["repository"]: fail("extension.repository must point to the standalone repository") - if len(commands) != 1: - fail("expected exactly one command") - - command = commands[0] - if command.get("name") != "speckit.preview.html": - fail("command name must be speckit.preview.html") - if command.get("file") != "commands/speckit.preview.html.md": - fail("command file path is incorrect") - - content = command_path.read_text(encoding="utf-8") - required_phrases = [ - "specs//preview/index.html", - "self-contained HTML", - "inline CSS and JavaScript", - "No external network dependencies", - "Prototype Assumptions", - "Do not modify source code", - "not a production implementation", + + command_contracts = contract["commands"] + command_paths = { + item["name"]: ROOT / item["file"] + for item in command_contracts + } + template_paths = { + item["path"]: ROOT / item["path"] + for item in contract["templates"] + } + + for path in [*command_paths.values(), *template_paths.values()]: + if not path.is_file(): + fail(f"{path.relative_to(ROOT)} is missing") + + actual_command_files = sorted( + str(path.relative_to(ROOT)).replace("\\", "/") + for path in (ROOT / "commands").glob("speckit.preview.*.md") + ) + expected_command_files = sorted(item["file"] for item in command_contracts) + if actual_command_files != expected_command_files: + fail(f"unexpected command files: {actual_command_files}") + + actual_template_files = sorted( + str(path.relative_to(ROOT)).replace("\\", "/") + for path in (ROOT / "templates" / "preview").glob("*.*") + ) + expected_template_files = sorted(item["path"] for item in contract["templates"]) + if actual_template_files != expected_template_files: + fail(f"unexpected template files: {actual_template_files}") + + actual_schema_files = sorted( + str(path.relative_to(ROOT)).replace("\\", "/") + for path in (ROOT / "schemas" / "preview").glob("*.json") + ) + expected_schema_files = [ + "schemas/preview/contract.json", + "schemas/preview/contract.schema.json", ] - missing = [phrase for phrase in required_phrases if phrase not in content] - if missing: - fail(f"command file missing required phrases: {missing}") + if actual_schema_files != expected_schema_files: + fail(f"unexpected schema files: {actual_schema_files}") + + command_names = [command.get("name") for command in commands] + command_files = [command.get("file") for command in commands] + if len(command_names) != len(set(command_names)): + fail(f"duplicate command names: {command_names}") + if len(command_files) != len(set(command_files)): + fail(f"duplicate command files: {command_files}") + + expected_commands = { + item["name"]: item["file"] + for item in command_contracts + } + actual_commands = { + command.get("name"): command.get("file") + for command in commands + } + if actual_commands != expected_commands: + fail(f"commands mismatch: {actual_commands}") + + shared_required = contract["sharedCommandRequiredPhrases"] + shared_forbidden = contract["sharedCommandForbiddenPhrases"] + for item in command_contracts: + name = item["name"] + content = command_paths[name].read_text(encoding="utf-8") + required = [ + *shared_required, + *item["requiredPhrases"], + f"Write only `{item['target']}`.", + f"Write `{item['target']}`", + item["target"], + item["template"], + f"Load the output template from `.specify/extensions/preview/{item['template']}`", + ] + forbidden = [*shared_forbidden, *item["forbiddenPhrases"]] + require_phrases(name, content, required) + reject_phrases(name, content, forbidden) + + for item in contract["templates"]: + content = template_paths[item["path"]].read_text(encoding="utf-8") + require_phrases(item["path"], content, item["requiredPhrases"]) + + docs = { + path: (ROOT / path).read_text(encoding="utf-8") + for path in contract["docs"]["paths"] + } + for path, content in docs.items(): + reject_phrases(path, content, contract["docs"]["forbiddenPhrases"]) + for item in command_contracts: + require_phrases(path, content, [item["name"], Path(item["target"]).name]) + + catalog = docs.get("CATALOG-SUBMISSION.md", "") + catalog_phrases = [ + render_catalog_phrase(phrase, manifest) + for phrase in contract["catalogRequiredPhrases"] + ] + require_phrases("CATALOG-SUBMISSION.md", catalog, catalog_phrases) print("preview extension package is valid") if __name__ == "__main__": main() - diff --git a/tests/integrations/community_defaults.py b/tests/integrations/community_defaults.py index c6dff2f3cc..40ce5191da 100644 --- a/tests/integrations/community_defaults.py +++ b/tests/integrations/community_defaults.py @@ -13,7 +13,12 @@ DEFAULT_EXTENSION_COMMANDS = ( "speckit.arch.generate", "speckit.arch.reverse", - "speckit.preview.html", + "speckit.preview.low-md", + "speckit.preview.low-html", + "speckit.preview.mid-md", + "speckit.preview.mid-html", + "speckit.preview.high-md", + "speckit.preview.high-html", "speckit.repository-governance.refresh", ) DEFAULT_PRESET_ID = "workflow-preset" diff --git a/tests/test_extensions.py b/tests/test_extensions.py index e063571b14..b4d39fef9d 100644 --- a/tests/test_extensions.py +++ b/tests/test_extensions.py @@ -227,6 +227,36 @@ def test_core_command_names_match_bundled_templates(self): assert CORE_COMMAND_NAMES == expected + def test_bundled_preview_catalogs_match_manifest(self): + """Preview's bundled package, catalogs, and default command list stay aligned.""" + from tests.integrations.community_defaults import DEFAULT_EXTENSION_COMMANDS + + repo_root = Path(__file__).resolve().parent.parent + manifest = ExtensionManifest(repo_root / "extensions" / "preview" / "extension.yml") + bundled_catalog = json.loads( + (repo_root / "extensions" / "catalog.json").read_text(encoding="utf-8") + ) + community_catalog = json.loads( + (repo_root / "extensions" / "catalog.community.json").read_text(encoding="utf-8") + ) + bundled_entry = bundled_catalog["extensions"]["preview"] + community_entry = community_catalog["extensions"]["preview"] + manifest_repository = manifest.data["extension"]["repository"] + manifest_commands = tuple(command["name"] for command in manifest.commands) + preview_default_commands = tuple( + command for command in DEFAULT_EXTENSION_COMMANDS if command.startswith("speckit.preview.") + ) + + for entry in (bundled_entry, community_entry): + assert entry["name"] == manifest.name + assert entry["version"] == manifest.version + assert entry["description"] == manifest.description + assert entry["repository"] == manifest_repository + assert entry["provides"]["commands"] == len(manifest.commands) + + assert community_entry["download_url"].endswith(f"/refs/tags/v{manifest.version}.zip") + assert preview_default_commands == manifest_commands + def test_missing_required_field(self, temp_dir): """Test manifest missing required field.""" import yaml From ba4e5a0ce1857f98a2a885e7834cd684a81f9db7 Mon Sep 17 00:00:00 2001 From: bigsmartben <30429295+bigsmartben@users.noreply.github.com> Date: Tue, 23 Jun 2026 18:26:59 +0800 Subject: [PATCH 2/3] fix preview smoke checks --- .github/workflows/community-smoke.yml | 32 +++++++++++++++++++++++---- 1 file changed, 28 insertions(+), 4 deletions(-) diff --git a/.github/workflows/community-smoke.yml b/.github/workflows/community-smoke.yml index 38d85fbb78..f957a6f873 100644 --- a/.github/workflows/community-smoke.yml +++ b/.github/workflows/community-smoke.yml @@ -92,7 +92,15 @@ jobs: test -f .specify/presets/workflow-preset/.composed/speckit.tasks.md test -f .claude/skills/speckit-arch-generate/SKILL.md - test -f .claude/skills/speckit-preview-html/SKILL.md + for preview_skill in \ + speckit-preview-low-md \ + speckit-preview-low-html \ + speckit-preview-mid-md \ + speckit-preview-mid-html \ + speckit-preview-high-md \ + speckit-preview-high-html; do + test -f ".claude/skills/$preview_skill/SKILL.md" + done test -f .claude/skills/speckit-repository-governance-refresh/SKILL.md test -f .claude/skills/speckit-specify/SKILL.md test -f .claude/skills/speckit-clarify/SKILL.md @@ -119,7 +127,15 @@ jobs: test ! -d .claude/skills/speckit-arch-reverse ;; preview) - test ! -d .claude/skills/speckit-preview-html + for preview_skill in \ + speckit-preview-low-md \ + speckit-preview-low-html \ + speckit-preview-mid-md \ + speckit-preview-mid-html \ + speckit-preview-high-md \ + speckit-preview-high-html; do + test ! -d ".claude/skills/$preview_skill" + done ;; repository-governance) test ! -d .claude/skills/speckit-repository-governance-refresh @@ -136,8 +152,16 @@ jobs: grep -q "4+1 architecture workflow" .claude/skills/speckit-arch-generate/SKILL.md ;; preview) - test -f .claude/skills/speckit-preview-html/SKILL.md - grep -q "self-contained interactive HTML prototype" .claude/skills/speckit-preview-html/SKILL.md + for preview_skill in \ + speckit-preview-low-md \ + speckit-preview-low-html \ + speckit-preview-mid-md \ + speckit-preview-mid-html \ + speckit-preview-high-md \ + speckit-preview-high-html; do + test -f ".claude/skills/$preview_skill/SKILL.md" + grep -q "evidence-backed" ".claude/skills/$preview_skill/SKILL.md" + done ;; repository-governance) test -f .claude/skills/speckit-repository-governance-refresh/SKILL.md From dec83e8513bdd8657871acf395124365a7078f6a Mon Sep 17 00:00:00 2001 From: bigsmartben <30429295+bigsmartben@users.noreply.github.com> Date: Tue, 23 Jun 2026 18:50:31 +0800 Subject: [PATCH 3/3] update preview smoke test expectations --- tests/test_presets.py | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/tests/test_presets.py b/tests/test_presets.py index 0266a5af37..a36bc7c2ed 100644 --- a/tests/test_presets.py +++ b/tests/test_presets.py @@ -4539,7 +4539,18 @@ def test_community_smoke_checks_wheel_assets_and_extension_dev_reinstall(self): ) assert "test ! -d .claude/skills/speckit-arch-generate" in verify_run assert "test ! -d .claude/skills/speckit-arch-reverse" in verify_run - assert "test ! -d .claude/skills/speckit-preview-html" in verify_run + for preview_skill in ( + "speckit-preview-low-md", + "speckit-preview-low-html", + "speckit-preview-mid-md", + "speckit-preview-mid-html", + "speckit-preview-high-md", + "speckit-preview-high-html", + ): + assert preview_skill in verify_run + assert f'test ! -d ".claude/skills/$preview_skill"' in verify_run + assert f'test -f ".claude/skills/$preview_skill/SKILL.md"' in verify_run + assert 'grep -q "evidence-backed" ".claude/skills/$preview_skill/SKILL.md"' in verify_run assert ( "test ! -d .claude/skills/speckit-repository-governance-refresh" in verify_run