Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 23 additions & 1 deletion presets/catalog.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"schema_version": "1.0",
"updated_at": "2026-08-04T00:00:00Z",
"updated_at": "2026-09-10T00:00:00Z",
"catalog_url": "https://raw.githubusercontent.com/github/spec-kit/main/presets/catalog.json",
"presets": {
"lean": {
Expand Down Expand Up @@ -48,6 +48,28 @@
"templates",
"compatibility"
]
},
"clarify-spec-gate": {
"name": "Clarify spec-stage gate",
"id": "clarify-spec-gate",
"version": "1.0.0",
"description": "Opt-in: wrap /speckit.clarify so spec-taxonomy items stay in the spec stage. Defer only implementation method, tech-stack comparison, or task breakdown.",
"author": "github",
"repository": "https://github.com/github/spec-kit",
"license": "MIT",
"bundled": true,
"requires": {
"speckit_version": ">=0.14.4"
},
"provides": {
"commands": 1,
"templates": 0
},
"tags": [
"clarify",
"spec",
"methodology"
]
}
}
}
28 changes: 28 additions & 0 deletions presets/clarify-spec-gate/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
# Clarify spec-stage gate

Opt-in wrap of `/speckit.clarify`. Core already lists the spec taxonomy. This preset adds a stage gate so agents do not dump NFRs, acceptance criteria, edge cases, and similar items into Plan.

Install it if you want that enforcement. Leave it off if core `/speckit.clarify` is enough.

## What it does

It wraps `speckit.clarify` with `{CORE_TEMPLATE}`, so core command updates still land. On top of that it:

- Treats a taxonomy hit as a spec-stage question
- Defers only implementation method, tech-stack comparison, or task breakdown
- Pauses when more than 60% of unresolved items would be deferred
- Adds a MUST-NOT on spec-taxonomy deferral

## Installation

```bash
specify preset add clarify-spec-gate
```

## Development

```bash
specify preset add --dev ./presets/clarify-spec-gate
specify preset resolve speckit.clarify
specify preset remove clarify-spec-gate
```
25 changes: 25 additions & 0 deletions presets/clarify-spec-gate/commands/speckit.clarify.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
---
description: Identify underspecified areas in the current feature spec, keeping spec-taxonomy items in this stage.
strategy: wrap
---

## Spec-vs-plan stage gate

This preset tightens deferral. Apply it while you scan the spec (the taxonomy lives in the core command below).

For every unchecked checklist item and every Partial/Missing taxonomy category, classify before considering deferral:

1. Match the item against the spec-oriented taxonomy in the core command.
2. A hit on any taxonomy category is a question candidate for this stage. Do not defer NFRs, acceptance/DoD testability, edge cases, UX empty states, domain constraints, or external-dependency failure modes.
3. Defer to planning only when the item is specifically about implementation method, tech-stack comparison, or task breakdown.
4. Mixed items (spec decision plus plan detail): split them and handle the spec part now.

If more than 60% of unresolved items would be marked Defer, pause, report the ratio, and re-check each against the taxonomy before continuing.

Do not use a vague "better deferred to planning" catch-all.

{CORE_TEMPLATE}

## Spec-taxonomy MUST-NOT

MUST NOT defer spec-taxonomy items to Plan. Concurrent-user volume, NFR quantification, acceptance-criteria testability, empty-state UX, and external-dependency failure modes are spec questions. How you implement pagination can wait. Whether the API paginates cannot.
26 changes: 26 additions & 0 deletions presets/clarify-spec-gate/preset.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
schema_version: "1.0"

preset:
id: "clarify-spec-gate"
name: "Clarify spec-stage gate"
version: "1.0.0"
description: "Opt-in: wrap /speckit.clarify so spec-taxonomy items stay in the spec stage. Defer only implementation method, tech-stack comparison, or task breakdown."
author: "github"
repository: "https://github.com/github/spec-kit"
license: "MIT"

requires:
speckit_version: ">=0.14.4"

provides:
templates:
- type: "command"
name: "speckit.clarify"
file: "commands/speckit.clarify.md"
description: "Wrap /speckit.clarify with a spec-vs-plan stage gate and defer-ratio audit"
strategy: "wrap"

tags:
- "clarify"
- "spec"
- "methodology"
1 change: 1 addition & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ packages = ["src/specify_cli"]
# Bundled presets (installable via `specify preset add <name>` or `specify init --preset <name>`)
"presets/lean" = "specify_cli/core_pack/presets/lean"
"presets/constitution-sync" = "specify_cli/core_pack/presets/constitution-sync"
"presets/clarify-spec-gate" = "specify_cli/core_pack/presets/clarify-spec-gate"
# Community bundle catalog snapshot (used for offline discovery)
"bundles/catalog.community.json" = "specify_cli/core_pack/bundles/catalog.community.json"

Expand Down
7 changes: 7 additions & 0 deletions tests/contract/test_wheel_bundled_presets.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,3 +51,10 @@ def test_constitution_sync_is_bundled_and_shipped():
assert _force_include()["presets/constitution-sync"] == (
"specify_cli/core_pack/presets/constitution-sync"
)


def test_clarify_spec_gate_is_bundled_and_shipped():
assert "clarify-spec-gate" in _bundled_preset_ids()
assert _force_include()["presets/clarify-spec-gate"] == (
"specify_cli/core_pack/presets/clarify-spec-gate"
)
66 changes: 66 additions & 0 deletions tests/test_presets.py
Original file line number Diff line number Diff line change
Expand Up @@ -14730,3 +14730,69 @@ def test_resolved_content_embeds_core_and_sync_pass(self, project_dir):
assert "## Constitution Template Sync" in content
assert "supersedes the \"Scope Guard\" above" in content
assert "plan-template.md" in content


class TestClarifySpecGatePreset:
"""Bundled opt-in wrap of ``/speckit.clarify`` (#1717).

Core already lists the spec taxonomy. This preset adds the stage-gate
procedure, defer-ratio audit, and MUST-NOT block so teams can opt in
without imposing that methodology on every install.
"""

PRESET_DIR = Path(__file__).parent.parent / "presets" / "clarify-spec-gate"

def test_manifest_provides_wrap_of_clarify(self):
manifest = yaml.safe_load((self.PRESET_DIR / "preset.yml").read_text())
assert manifest["preset"]["id"] == "clarify-spec-gate"
entries = manifest["provides"]["templates"]
assert len(entries) == 1
entry = entries[0]
assert entry["type"] == "command"
assert entry["name"] == "speckit.clarify"
assert entry["strategy"] == "wrap"

def test_wrapper_uses_core_template_and_stage_gate(self):
text = (self.PRESET_DIR / "commands" / "speckit.clarify.md").read_text()
assert text.startswith("---\n")
_, frontmatter_block, body = text.split("---", 2)
frontmatter = yaml.safe_load(frontmatter_block)
assert frontmatter["strategy"] == "wrap"
assert "{CORE_TEMPLATE}" in body
assert "strategy: wrap" not in body
assert "Spec-vs-plan stage gate" in body
assert "60%" in body
assert "MUST NOT defer spec-taxonomy items to Plan" in body

def test_catalog_lists_bundled_preset(self):
manifest = yaml.safe_load((self.PRESET_DIR / "preset.yml").read_text())
catalog = json.loads((self.PRESET_DIR.parent / "catalog.json").read_text())
entry = catalog["presets"]["clarify-spec-gate"]
assert entry["bundled"] is True
assert entry["version"] == manifest["preset"]["version"]
assert entry["provides"]["commands"] == 1
assert entry["provides"]["templates"] == 0

def test_wrap_composes_over_core_clarify(self, project_dir):
manager = PresetManager(project_dir)
manager.install_from_directory(self.PRESET_DIR, "0.15.0")

resolver = PresetResolver(project_dir)
layers = resolver.collect_all_layers("speckit.clarify", "command")
assert len(layers) >= 2, "expected preset wrap layer plus a core base"
assert layers[0]["strategy"] == "wrap"
assert any("clarify-spec-gate" in str(layer["path"]) for layer in layers)
assert layers[-1]["source"] == "core (bundled)"

def test_resolved_content_embeds_core_and_gate(self, project_dir):
manager = PresetManager(project_dir)
manager.install_from_directory(self.PRESET_DIR, "0.15.0")

resolver = PresetResolver(project_dir)
content = resolver.resolve_content("speckit.clarify", "command")
assert content is not None
assert "{CORE_TEMPLATE}" not in content
assert "## Outline" in content
assert "Functional Scope & Behavior" in content
assert "## Spec-vs-plan stage gate" in content
assert "MUST NOT defer spec-taxonomy items to Plan" in content