Skip to content

Commit bbbb41a

Browse files
committed
Add opt-in clarify-spec-gate wrap preset
Stage-gate procedure, defer-ratio audit, and MUST-NOT belong in a preset, not core /speckit.clarify. Teams that want that enforcement can install clarify-spec-gate.
1 parent 8b5ea90 commit bbbb41a

7 files changed

Lines changed: 176 additions & 1 deletion

File tree

‎presets/catalog.json‎

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"schema_version": "1.0",
3-
"updated_at": "2026-08-04T00:00:00Z",
3+
"updated_at": "2026-09-10T00:00:00Z",
44
"catalog_url": "https://raw.githubusercontent.com/github/spec-kit/main/presets/catalog.json",
55
"presets": {
66
"lean": {
@@ -48,6 +48,28 @@
4848
"templates",
4949
"compatibility"
5050
]
51+
},
52+
"clarify-spec-gate": {
53+
"name": "Clarify spec-stage gate",
54+
"id": "clarify-spec-gate",
55+
"version": "1.0.0",
56+
"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.",
57+
"author": "github",
58+
"repository": "https://github.com/github/spec-kit",
59+
"license": "MIT",
60+
"bundled": true,
61+
"requires": {
62+
"speckit_version": ">=0.14.4"
63+
},
64+
"provides": {
65+
"commands": 1,
66+
"templates": 0
67+
},
68+
"tags": [
69+
"clarify",
70+
"spec",
71+
"methodology"
72+
]
5173
}
5274
}
5375
}
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
# Clarify spec-stage gate
2+
3+
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.
4+
5+
Install it if you want that enforcement. Leave it off if core `/speckit.clarify` is enough.
6+
7+
## What it does
8+
9+
It wraps `speckit.clarify` with `{CORE_TEMPLATE}`, so core command updates still land. On top of that it:
10+
11+
- Treats a taxonomy hit as a spec-stage question
12+
- Defers only implementation method, tech-stack comparison, or task breakdown
13+
- Pauses when more than 60% of unresolved items would be deferred
14+
- Adds a MUST-NOT on spec-taxonomy deferral
15+
16+
## Installation
17+
18+
```bash
19+
specify preset add clarify-spec-gate
20+
```
21+
22+
## Development
23+
24+
```bash
25+
specify preset add --dev ./presets/clarify-spec-gate
26+
specify preset resolve speckit.clarify
27+
specify preset remove clarify-spec-gate
28+
```
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
---
2+
description: Identify underspecified areas in the current feature spec, keeping spec-taxonomy items in this stage.
3+
strategy: wrap
4+
---
5+
6+
## Spec-vs-plan stage gate
7+
8+
This preset tightens deferral. Apply it while you scan the spec (the taxonomy lives in the core command below).
9+
10+
For every unchecked checklist item and every Partial/Missing taxonomy category, classify before considering deferral:
11+
12+
1. Match the item against the spec-oriented taxonomy in the core command.
13+
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.
14+
3. Defer to planning only when the item is specifically about implementation method, tech-stack comparison, or task breakdown.
15+
4. Mixed items (spec decision plus plan detail): split them and handle the spec part now.
16+
17+
If more than 60% of unresolved items would be marked Defer, pause, report the ratio, and re-check each against the taxonomy before continuing.
18+
19+
Do not use a vague "better deferred to planning" catch-all.
20+
21+
{CORE_TEMPLATE}
22+
23+
## Spec-taxonomy MUST-NOT
24+
25+
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.
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
schema_version: "1.0"
2+
3+
preset:
4+
id: "clarify-spec-gate"
5+
name: "Clarify spec-stage gate"
6+
version: "1.0.0"
7+
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."
8+
author: "github"
9+
repository: "https://github.com/github/spec-kit"
10+
license: "MIT"
11+
12+
requires:
13+
speckit_version: ">=0.14.4"
14+
15+
provides:
16+
templates:
17+
- type: "command"
18+
name: "speckit.clarify"
19+
file: "commands/speckit.clarify.md"
20+
description: "Wrap /speckit.clarify with a spec-vs-plan stage gate and defer-ratio audit"
21+
strategy: "wrap"
22+
23+
tags:
24+
- "clarify"
25+
- "spec"
26+
- "methodology"

‎pyproject.toml‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ packages = ["src/specify_cli"]
5050
# Bundled presets (installable via `specify preset add <name>` or `specify init --preset <name>`)
5151
"presets/lean" = "specify_cli/core_pack/presets/lean"
5252
"presets/constitution-sync" = "specify_cli/core_pack/presets/constitution-sync"
53+
"presets/clarify-spec-gate" = "specify_cli/core_pack/presets/clarify-spec-gate"
5354
# Community bundle catalog snapshot (used for offline discovery)
5455
"bundles/catalog.community.json" = "specify_cli/core_pack/bundles/catalog.community.json"
5556

‎tests/contract/test_wheel_bundled_presets.py‎

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,3 +51,10 @@ def test_constitution_sync_is_bundled_and_shipped():
5151
assert _force_include()["presets/constitution-sync"] == (
5252
"specify_cli/core_pack/presets/constitution-sync"
5353
)
54+
55+
56+
def test_clarify_spec_gate_is_bundled_and_shipped():
57+
assert "clarify-spec-gate" in _bundled_preset_ids()
58+
assert _force_include()["presets/clarify-spec-gate"] == (
59+
"specify_cli/core_pack/presets/clarify-spec-gate"
60+
)

‎tests/test_presets.py‎

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14730,3 +14730,69 @@ def test_resolved_content_embeds_core_and_sync_pass(self, project_dir):
1473014730
assert "## Constitution Template Sync" in content
1473114731
assert "supersedes the \"Scope Guard\" above" in content
1473214732
assert "plan-template.md" in content
14733+
14734+
14735+
class TestClarifySpecGatePreset:
14736+
"""Bundled opt-in wrap of ``/speckit.clarify`` (#1717).
14737+
14738+
Core already lists the spec taxonomy. This preset adds the stage-gate
14739+
procedure, defer-ratio audit, and MUST-NOT block so teams can opt in
14740+
without imposing that methodology on every install.
14741+
"""
14742+
14743+
PRESET_DIR = Path(__file__).parent.parent / "presets" / "clarify-spec-gate"
14744+
14745+
def test_manifest_provides_wrap_of_clarify(self):
14746+
manifest = yaml.safe_load((self.PRESET_DIR / "preset.yml").read_text())
14747+
assert manifest["preset"]["id"] == "clarify-spec-gate"
14748+
entries = manifest["provides"]["templates"]
14749+
assert len(entries) == 1
14750+
entry = entries[0]
14751+
assert entry["type"] == "command"
14752+
assert entry["name"] == "speckit.clarify"
14753+
assert entry["strategy"] == "wrap"
14754+
14755+
def test_wrapper_uses_core_template_and_stage_gate(self):
14756+
text = (self.PRESET_DIR / "commands" / "speckit.clarify.md").read_text()
14757+
assert text.startswith("---\n")
14758+
_, frontmatter_block, body = text.split("---", 2)
14759+
frontmatter = yaml.safe_load(frontmatter_block)
14760+
assert frontmatter["strategy"] == "wrap"
14761+
assert "{CORE_TEMPLATE}" in body
14762+
assert "strategy: wrap" not in body
14763+
assert "Spec-vs-plan stage gate" in body
14764+
assert "60%" in body
14765+
assert "MUST NOT defer spec-taxonomy items to Plan" in body
14766+
14767+
def test_catalog_lists_bundled_preset(self):
14768+
manifest = yaml.safe_load((self.PRESET_DIR / "preset.yml").read_text())
14769+
catalog = json.loads((self.PRESET_DIR.parent / "catalog.json").read_text())
14770+
entry = catalog["presets"]["clarify-spec-gate"]
14771+
assert entry["bundled"] is True
14772+
assert entry["version"] == manifest["preset"]["version"]
14773+
assert entry["provides"]["commands"] == 1
14774+
assert entry["provides"]["templates"] == 0
14775+
14776+
def test_wrap_composes_over_core_clarify(self, project_dir):
14777+
manager = PresetManager(project_dir)
14778+
manager.install_from_directory(self.PRESET_DIR, "0.15.0")
14779+
14780+
resolver = PresetResolver(project_dir)
14781+
layers = resolver.collect_all_layers("speckit.clarify", "command")
14782+
assert len(layers) >= 2, "expected preset wrap layer plus a core base"
14783+
assert layers[0]["strategy"] == "wrap"
14784+
assert any("clarify-spec-gate" in str(layer["path"]) for layer in layers)
14785+
assert layers[-1]["source"] == "core (bundled)"
14786+
14787+
def test_resolved_content_embeds_core_and_gate(self, project_dir):
14788+
manager = PresetManager(project_dir)
14789+
manager.install_from_directory(self.PRESET_DIR, "0.15.0")
14790+
14791+
resolver = PresetResolver(project_dir)
14792+
content = resolver.resolve_content("speckit.clarify", "command")
14793+
assert content is not None
14794+
assert "{CORE_TEMPLATE}" not in content
14795+
assert "## Outline" in content
14796+
assert "Functional Scope & Behavior" in content
14797+
assert "## Spec-vs-plan stage gate" in content
14798+
assert "MUST NOT defer spec-taxonomy items to Plan" in content

0 commit comments

Comments
 (0)