specpro-cli 0.1.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- specpro_cli/__init__.py +16 -0
- specpro_cli/assets/commands/specpro.analyze.md +1102 -0
- specpro_cli/assets/commands/specpro.checklist.md +335 -0
- specpro_cli/assets/commands/specpro.clarify.md +581 -0
- specpro_cli/assets/commands/specpro.constitution.md +488 -0
- specpro_cli/assets/commands/specpro.feature.md +115 -0
- specpro_cli/assets/commands/specpro.implement.md +1881 -0
- specpro_cli/assets/commands/specpro.manual-test.md +206 -0
- specpro_cli/assets/commands/specpro.plan.md +3284 -0
- specpro_cli/assets/commands/specpro.qc.md +1489 -0
- specpro_cli/assets/commands/specpro.scenarios.md +154 -0
- specpro_cli/assets/commands/specpro.specify.md +1449 -0
- specpro_cli/assets/commands/specpro.status.md +863 -0
- specpro_cli/assets/commands/specpro.tasks.md +1207 -0
- specpro_cli/assets/commands/specpro.test-implement.md +462 -0
- specpro_cli/assets/commands/specpro.test-plan.md +383 -0
- specpro_cli/assets/commands/specpro.user-manual.md +178 -0
- specpro_cli/assets/scripts/bash/check-anti-coupling.sh +293 -0
- specpro_cli/assets/scripts/bash/check-prerequisites.sh +176 -0
- specpro_cli/assets/scripts/bash/common.sh +88 -0
- specpro_cli/assets/scripts/bash/create-new-feature.sh +336 -0
- specpro_cli/assets/scripts/bash/qc-auto-fix.sh +121 -0
- specpro_cli/assets/scripts/bash/setup-plan.sh +60 -0
- specpro_cli/assets/scripts/bash/verify-cumulative-records.sh +203 -0
- specpro_cli/assets/scripts/bash/verify-deliverables-tracked.sh +147 -0
- specpro_cli/assets/scripts/bash/verify-deployment.sh +239 -0
- specpro_cli/assets/scripts/bash/verify-frontmatter-yaml.sh +63 -0
- specpro_cli/assets/scripts/bash/verify-ledger.sh +376 -0
- specpro_cli/assets/scripts/bash/verify-shapes.sh +1082 -0
- specpro_cli/assets/scripts/git-hooks/pre-commit +243 -0
- specpro_cli/assets/scripts/install-git-hooks.sh +67 -0
- specpro_cli/assets/scripts/powershell/check-anti-coupling.ps1 +249 -0
- specpro_cli/assets/scripts/powershell/check-prerequisites.ps1 +148 -0
- specpro_cli/assets/scripts/powershell/common.ps1 +95 -0
- specpro_cli/assets/scripts/powershell/create-new-feature.ps1 +229 -0
- specpro_cli/assets/scripts/powershell/qc-auto-fix.ps1 +110 -0
- specpro_cli/assets/scripts/powershell/setup-plan.ps1 +61 -0
- specpro_cli/assets/scripts/powershell/verify-cumulative-records.ps1 +133 -0
- specpro_cli/assets/scripts/powershell/verify-deliverables-tracked.ps1 +112 -0
- specpro_cli/assets/scripts/powershell/verify-deployment.ps1 +278 -0
- specpro_cli/assets/scripts/powershell/verify-frontmatter-yaml.ps1 +56 -0
- specpro_cli/assets/scripts/powershell/verify-ledger.ps1 +383 -0
- specpro_cli/assets/scripts/powershell/verify-shapes.ps1 +978 -0
- specpro_cli/assets/templates/agent-context-template.md +49 -0
- specpro_cli/assets/templates/assumptions-template.md +248 -0
- specpro_cli/assets/templates/checklist-template.md +40 -0
- specpro_cli/assets/templates/clarifications-template.md +155 -0
- specpro_cli/assets/templates/constitution-template.md +50 -0
- specpro_cli/assets/templates/feature-spec-template.md +66 -0
- specpro_cli/assets/templates/plan-overview-template.md +150 -0
- specpro_cli/assets/templates/plan-template.md +387 -0
- specpro_cli/assets/templates/protocol-golden-bytes-guide.md +195 -0
- specpro_cli/assets/templates/requirements-template.md +356 -0
- specpro_cli/assets/templates/spec-template.md +267 -0
- specpro_cli/assets/templates/tasks-template.md +252 -0
- specpro_cli/assets/templates/test-tasks-template.md +174 -0
- specpro_cli/cli/__init__.py +5 -0
- specpro_cli/cli/cmd_init.py +416 -0
- specpro_cli/cli/cmd_remove.py +122 -0
- specpro_cli/cli/entry.py +181 -0
- specpro_cli/integrations/__init__.py +36 -0
- specpro_cli/integrations/base.py +601 -0
- specpro_cli/integrations/claude/__init__.py +101 -0
- specpro_cli/integrations/copilot/__init__.py +153 -0
- specpro_cli/integrations/cursor_agent/__init__.py +51 -0
- specpro_cli/integrations/gemini/__init__.py +44 -0
- specpro_cli/integrations/opencode/__init__.py +48 -0
- specpro_cli/integrations/qodercli/__init__.py +54 -0
- specpro_cli/integrations/registry.py +88 -0
- specpro_cli/packaged/__init__.py +5 -0
- specpro_cli/packaged/sync.py +106 -0
- specpro_cli-0.1.0.dist-info/METADATA +117 -0
- specpro_cli-0.1.0.dist-info/RECORD +76 -0
- specpro_cli-0.1.0.dist-info/WHEEL +4 -0
- specpro_cli-0.1.0.dist-info/entry_points.txt +2 -0
- specpro_cli-0.1.0.dist-info/licenses/LICENSE +21 -0
|
@@ -0,0 +1,1449 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Create or update the feature specification from a natural language feature description.
|
|
3
|
+
handoffs:
|
|
4
|
+
- label: Build Technical Plan
|
|
5
|
+
agent: specpro-plan
|
|
6
|
+
prompt: Create a plan for the spec. I am building with...
|
|
7
|
+
- label: Clarify Spec Requirements
|
|
8
|
+
agent: specpro-clarify
|
|
9
|
+
prompt: Clarify specification requirements
|
|
10
|
+
send: true
|
|
11
|
+
writes:
|
|
12
|
+
# This command's write surface: only what it produces AS THE PRODUCER of that
|
|
13
|
+
# (artifact, unit) pair. A write this command makes on a non-producer path is a
|
|
14
|
+
# boundary violation by definition (FR-051) and MUST NOT be declared here.
|
|
15
|
+
# The full ownership map is the UNION of every command's writes: block.
|
|
16
|
+
- artifact: specs/spec.md
|
|
17
|
+
unit: "whole file - template structure and section order preserved; plus the per-item Lifecycle fields"
|
|
18
|
+
- artifact: specs/assumptions.md
|
|
19
|
+
unit: "A0NN entries (Status / Answer / Resolution) plus the Decision Path and Summary sections"
|
|
20
|
+
- artifact: specs/implement_issues.md
|
|
21
|
+
unit: "the [specify] section -> its own entries marked [x]; the statistics table (**its own row only** — seven commands declare this artifact); appended ISS-NNN entries in any section — registration is routing; only [x]-marking is section-scoped"
|
|
22
|
+
---
|
|
23
|
+
|
|
24
|
+
## User Input
|
|
25
|
+
|
|
26
|
+
```text
|
|
27
|
+
$ARGUMENTS
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
You **MUST** consider the user input before proceeding (if not empty).
|
|
31
|
+
|
|
32
|
+
**Rerun safety — detect the artifact, default to incremental** ⚠️ [settled 2026-09-13]:
|
|
33
|
+
|
|
34
|
+
**Before writing `specs/spec.md`, detect whether it already exists.** Use the SAME check in every command:
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
[ -s specs/spec.md ] && echo EXISTS || echo NEW # -s: exists AND non-empty (an empty placeholder counts as NEW)
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
| Detection | Mode |
|
|
41
|
+
|-----------|------|
|
|
42
|
+
| **NEW** (absent or empty) | **Initial** — generate from scratch |
|
|
43
|
+
| **EXISTS** | **Incremental** — evolve it; **never silently regenerate from scratch** |
|
|
44
|
+
|
|
45
|
+
**Overwriting an existing artifact requires explicit, confirmed intent:**
|
|
46
|
+
1. Only when the user *explicitly* asks (in their own words) does the initial path run on an existing artifact.
|
|
47
|
+
2. **Even then, confirm once more before writing** — name the artifact that will be replaced and what will be lost; wait for the answer.
|
|
48
|
+
3. **Silence is not consent.** An unspecified run on an existing artifact is ALWAYS incremental.
|
|
49
|
+
|
|
50
|
+
> **Why a shared rule rather than per-command courtesy**: nine of the twelve non-implementation commands already had some protection, but each wrote it its own way (`EXISTING_SPEC` check · "creates a NEW file" · `NEVER overwrite` · "incremental regeneration" · `AUTO_MODE=false`), and **three had none at all** — not by decision, but because the discipline had no shared carrier. Overwriting an artifact the user has been evolving is not recoverable within the session; the cost of asking is one prompt.
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
### Scope Resolution 🆕 (FR-063 / T050 · v0.23)
|
|
54
|
+
|
|
55
|
+
1. **作用域判定**: 当前工作目录位于 `specs/fNNN-简称/` 内 ⇒ **feature 作用域**(读写范围 = 本 feature 目录,由 `check-prerequisites.sh` 的作用域感知解析);位于仓库根或 `specs/` 根 ⇒ **母作用域**(读写母规格链)。feature 作用域内 MUST NOT 写母产物——唯一例外:**发现登记**(台账路由,`[specify]`/`[plan]` 分区)。
|
|
56
|
+
2. **新会话首次执行**: 若 `specs/features.md` 存在且含 `active` 行、而用户未指明作用域 ⇒ **询问用户**在母作用域还是某个 feature 内工作,MUST NOT 自行挑选。
|
|
57
|
+
3. 本命令的产物路径随之解析:feature 作用域下落 `<feature 目录>/`,母作用域下落 `specs/`。
|
|
58
|
+
|
|
59
|
+
## Outline
|
|
60
|
+
|
|
61
|
+
**Artifact Language Rule** 🌐 [CRITICAL — applies to ALL generated content]:
|
|
62
|
+
- **Prose content** (descriptions, rationale, scenario text, guidance) follows the project's **Artifact Language** setting — from `specs/constitution.md` → **Artifact Language** field; default `en` when absent
|
|
63
|
+
- **Structural anchors are ALWAYS English**, regardless of the artifact language: section headings from the template, item ID prefixes (US/FR), status enums, and table column names — exactly as written in the template
|
|
64
|
+
- **Entry field labels are structural anchors too** (`source:`, `Evidence:`, `Fix Direction:`) — never translated, even when the surrounding prose is not English. They exist to be **searched**: an entry's fields must be greppable across artifacts and projects, and a translated label silently drops out of every consumer's scan.
|
|
65
|
+
- Rationale: fixed anchors keep artifacts machine-parseable across specpro commands and keep instructions ↔ artifacts aligned for review
|
|
66
|
+
|
|
67
|
+
The text the user typed after `/specpro-specify` in the triggering message **is** the feature description. Assume you always have it available in this conversation even if `$ARGUMENTS` appears literally below. Do not ask the user to repeat it unless they provided an empty command.
|
|
68
|
+
|
|
69
|
+
Given that feature description, do this:
|
|
70
|
+
|
|
71
|
+
1. **Check for --review-issues argument** 🆕:
|
|
72
|
+
|
|
73
|
+
**Purpose**: Process implement issues submitted from the implement phase
|
|
74
|
+
|
|
75
|
+
a. **Parse arguments**:
|
|
76
|
+
- If `$ARGUMENTS` contains `--review-issues`:
|
|
77
|
+
* **Skip the question below**; proceed directly to step 1b (review issues)
|
|
78
|
+
- Else: **detect, then ask** ⚠️ [settled 2026-09-13]:
|
|
79
|
+
* Check the `[specify]` section of `specs/implement_issues.md` for open `[ ]` entries
|
|
80
|
+
* **None** → continue with normal specification generation (step 2+). **No prompt.**
|
|
81
|
+
* **Some** → **ask**: "N open `[specify]` issues in specs/implement_issues.md. Process them first, or run normally?"
|
|
82
|
+
- *Process first* → proceed to step 1b (review issues)
|
|
83
|
+
- *Run normally* → continue with normal specification generation (step 2+)
|
|
84
|
+
* **Rationale**: the flag used to be the whole switch — not passing it left pending issues **silently unprocessed**, passing it meant overriding the work the user actually wanted, and **which issues were pending was invisible until the command ran**. Detect-then-ask makes it an explicit choice; the flag now only means *"I already know — don't ask"*.
|
|
85
|
+
(Same model as `/specpro-implement` and `/specpro-test-plan` — see TOOL-008.)
|
|
86
|
+
|
|
87
|
+
b. **Review implement issues**:
|
|
88
|
+
- Read `specs/implement_issues.md`
|
|
89
|
+
- Extract all issues from `[specify]` section
|
|
90
|
+
- Filter for issues marked `[ ]` (pending) only
|
|
91
|
+
- If no pending issues found:
|
|
92
|
+
* Display: "✓ No pending [specify] issues to process"
|
|
93
|
+
* Exit
|
|
94
|
+
|
|
95
|
+
- Display issue summary:
|
|
96
|
+
```markdown
|
|
97
|
+
📋 [specify] Issues Review
|
|
98
|
+
|
|
99
|
+
Found N pending [specify] issues:
|
|
100
|
+
1. [ ] ISS-XXX: Issue description
|
|
101
|
+
2. [ ] ISS-XXX: Issue description
|
|
102
|
+
...
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
- For each pending issue:
|
|
106
|
+
* Read issue details (description, problem, suggestion)
|
|
107
|
+
* Determine appropriate action:
|
|
108
|
+
- **Add new requirement**: Insert new FR into spec.md
|
|
109
|
+
- **Clarify existing requirement**: Update existing FR description
|
|
110
|
+
- **Resolve conflict**: Modify conflicting FRs
|
|
111
|
+
* Apply the change to spec.md
|
|
112
|
+
* Mark issue as `[x]` in implement_issues.md
|
|
113
|
+
* Display: "✓ Processed ISS-XXX: [action taken]"
|
|
114
|
+
|
|
115
|
+
- Update the statistics table at the **top** of `implement_issues.md` (its position is deliberate — see the note below)
|
|
116
|
+
- **Edit order and idempotency check** ⚠️ [a silent false-skip, observed 2026-09-13]: write the **entry body first** (into its own section), then the derived summaries (statistics table + `Last Updated`) — never the reverse. A derived summary written first introduces the new ID into the file's text before the entry exists, so an idempotency guard that greps the file for that ID falsely concludes "already present" and **skips the entry**, while the summaries still claim it landed. For the same reason the guard MUST match the entry body's **line-start pattern**, never a full-text keyword search:
|
|
117
|
+
```bash
|
|
118
|
+
grep -qE '^- \[[x ]\] ISS-<N>:' specs/implement_issues.md # correct — matches an ENTRY, not a mention
|
|
119
|
+
# grep -q 'ISS-<N>' … # wrong — also matches the statistics Pending-Items column, the Last Updated line, cross-references
|
|
120
|
+
```
|
|
121
|
+
- **General rule: every grep/awk example shown in an instruction or template must itself obey the line-start pattern** (TOOL-009) — the executing side copies examples verbatim, so an example that uses a whole-file match propagates the same misjudgment to everyone who copies it. This file has already corrected its own sentinel self-check command under this rule.
|
|
122
|
+
`.specpro/scripts/bash/verify-ledger.sh` reports the resulting inconsistency as a count mismatch — but only if it is run, and by then the entry is already missing.
|
|
123
|
+
- **Entry placement**: append new entries to the end of their own section, never to the end of the file. The file's statistics block sits at the top precisely so that "append to the end" lands inside the last section rather than between sections; moving or duplicating a section header breaks that and produces an orphaned section no `--review-issues` run will read.
|
|
124
|
+
- **Verify mechanically after writing**: run `.specpro/scripts/bash/verify-ledger.sh` — it checks entry section membership, statistics-vs-actual counts, and the trailing newline in one pass (non-zero exit names the violation). Marking issues `[x]` changes the counts, so this applies to this command too, not only to commands that append. A pre-commit hook enforces the same check.
|
|
125
|
+
|
|
126
|
+
c. **Run quality validation on updated spec** 🆕:
|
|
127
|
+
```bash
|
|
128
|
+
echo ""
|
|
129
|
+
echo "📋 Running quality validation on updated spec.md..."
|
|
130
|
+
echo " Validating against the checklist's quality criteria..."
|
|
131
|
+
echo " QC rebuilds the checklist and queues every non-passing item for THIS command."
|
|
132
|
+
echo ""
|
|
133
|
+
|
|
134
|
+
# ⚠️ **No `--auto`, no authorisation parameter** (2026-09-19 ruling, FR-058).
|
|
135
|
+
# This command used to delegate the REPAIR to `/specpro-qc` and call it a
|
|
136
|
+
# quality gate. ⚠️ That made the judge the repairer, and it is why the caller
|
|
137
|
+
# had to say which artifact it owned — a parameter that existed only to make a
|
|
138
|
+
# boundary violation declareable. **The repair is this command's own job**, and
|
|
139
|
+
# the check's job is to produce the list of what needs repairing.
|
|
140
|
+
# ⚠️ **The loop closes here, and it is short on purpose**: qc queues its
|
|
141
|
+
# non-passing items into the `[specify]` section — **the very section this
|
|
142
|
+
# command consumes** — so the fix path is "read the queue, fix the spec,
|
|
143
|
+
# re-run the check", not "hope the checker also repairs".
|
|
144
|
+
/specpro-qc --scope="incremental"
|
|
145
|
+
|
|
146
|
+
QC_EXIT_CODE=$?
|
|
147
|
+
|
|
148
|
+
if [[ $QC_EXIT_CODE -eq 0 ]]; then
|
|
149
|
+
echo ""
|
|
150
|
+
echo "✅ Quality validation PASSED"
|
|
151
|
+
echo " All quality criteria met."
|
|
152
|
+
echo " specs/checklists/requirements.md rebuilt for this run"
|
|
153
|
+
else
|
|
154
|
+
echo ""
|
|
155
|
+
echo "❌ Quality validation FAILED"
|
|
156
|
+
echo " One or more criteria did not pass. **The non-passing ones are now"
|
|
157
|
+
echo " entries in the [specify] queue** (FR-059) — that is this command's"
|
|
158
|
+
echo " own queue, so they are fixed HERE, by the spec's producer."
|
|
159
|
+
echo ""
|
|
160
|
+
echo "Check specs/checklists/requirements.md for the per-item verdicts."
|
|
161
|
+
echo "Then process the queued entries with --review-issues."
|
|
162
|
+
exit 1
|
|
163
|
+
fi
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
d. **Display completion summary**:
|
|
167
|
+
```markdown
|
|
168
|
+
✅ Completed [specify] issues review and quality validation
|
|
169
|
+
|
|
170
|
+
Processed: N issues
|
|
171
|
+
Updated: specs/spec.md
|
|
172
|
+
Validated: Quality check passed — no criterion failed (`FR-061`)
|
|
173
|
+
Marked as [x] in implement_issues.md
|
|
174
|
+
|
|
175
|
+
⚠️ IMPORTANT: Next steps
|
|
176
|
+
|
|
177
|
+
1. Run /specpro-plan to update technical design
|
|
178
|
+
2. Run /specpro-tasks to update task list
|
|
179
|
+
3. Then you can process [plan] or [tasks] issues
|
|
180
|
+
|
|
181
|
+
Do NOT process [plan] or [tasks] issues yet.
|
|
182
|
+
They require the plan/tasks update first.
|
|
183
|
+
```
|
|
184
|
+
2. **Pre-flight Checks** ✨ (Fast-fail validation before core logic):
|
|
185
|
+
|
|
186
|
+
**Purpose**: Verify environment and prerequisites before executing specification generation
|
|
187
|
+
|
|
188
|
+
a. **Required files check**:
|
|
189
|
+
```bash
|
|
190
|
+
CONSTITUTION="specs/constitution.md"
|
|
191
|
+
SPEC_TEMPLATE=".specpro/templates/spec-template.md"
|
|
192
|
+
ASSUMP_TEMPLATE=".specpro/templates/assumptions-template.md"
|
|
193
|
+
|
|
194
|
+
if [[ ! -f "$CONSTITUTION" ]]; then
|
|
195
|
+
echo "❌ ERROR: Constitution not found at $CONSTITUTION"
|
|
196
|
+
echo "Please run /specpro-constitution to create the project constitution first."
|
|
197
|
+
exit 1
|
|
198
|
+
fi
|
|
199
|
+
|
|
200
|
+
if [[ ! -f "$SPEC_TEMPLATE" ]]; then
|
|
201
|
+
echo "❌ ERROR: Spec template not found at $SPEC_TEMPLATE"
|
|
202
|
+
exit 1
|
|
203
|
+
fi
|
|
204
|
+
|
|
205
|
+
if [[ ! -f "$ASSUMP_TEMPLATE" ]]; then
|
|
206
|
+
echo "❌ ERROR: Assumptions template not found at $ASSUMP_TEMPLATE"
|
|
207
|
+
exit 1
|
|
208
|
+
fi
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
b. **Directory permissions check**:
|
|
212
|
+
```bash
|
|
213
|
+
# After running create-new-feature.sh to get FEATURE_DIR
|
|
214
|
+
if [[ ! -d "$FEATURE_DIR" ]]; then
|
|
215
|
+
echo "❌ ERROR: Feature directory not writable: $FEATURE_DIR"
|
|
216
|
+
exit 1
|
|
217
|
+
fi
|
|
218
|
+
|
|
219
|
+
# Test write permission
|
|
220
|
+
touch "$FEATURE_DIR/.test_write" 2>/dev/null || {
|
|
221
|
+
echo "❌ ERROR: Cannot write to $FEATURE_DIR"
|
|
222
|
+
exit 1
|
|
223
|
+
}
|
|
224
|
+
rm -f "$FEATURE_DIR/.test_write"
|
|
225
|
+
```
|
|
226
|
+
|
|
227
|
+
c. **Constitution validation**:
|
|
228
|
+
```bash
|
|
229
|
+
# Verify Constitution is not empty
|
|
230
|
+
CONSTITUTION_SIZE=$(wc -c < "$CONSTITUTION")
|
|
231
|
+
if [[ $CONSTITUTION_SIZE -lt 100 ]]; then
|
|
232
|
+
echo "⚠️ WARNING: Constitution appears to be empty or very small"
|
|
233
|
+
echo "Please verify specs/constitution.md has content"
|
|
234
|
+
fi
|
|
235
|
+
|
|
236
|
+
# Count principles/articles
|
|
237
|
+
PRINCIPLE_COUNT=$(grep -c "^##\|^###\|^#" "$CONSTITUTION" 2>/dev/null || true)
|
|
238
|
+
: "${PRINCIPLE_COUNT:=0}"
|
|
239
|
+
if [[ $PRINCIPLE_COUNT -lt 2 ]]; then
|
|
240
|
+
echo "⚠️ WARNING: Constitution has fewer than 2 sections"
|
|
241
|
+
echo "Consider adding more principles before proceeding"
|
|
242
|
+
fi
|
|
243
|
+
```
|
|
244
|
+
|
|
245
|
+
d. **Report check completion**:
|
|
246
|
+
```markdown
|
|
247
|
+
✓ Pre-flight checks passed
|
|
248
|
+
- Constitution: ✅ ($PRINCIPLE_COUNT sections)
|
|
249
|
+
- Templates: ✅ (spec, assumptions)
|
|
250
|
+
- Permissions: ✅ (writable)
|
|
251
|
+
```
|
|
252
|
+
|
|
253
|
+
**Fast-fail**: If any check fails, abort immediately with clear error message
|
|
254
|
+
|
|
255
|
+
1. **Generate a concise short name** (2-4 words) for tracking:
|
|
256
|
+
- Analyze the feature description and extract the most meaningful keywords
|
|
257
|
+
- Create a 2-4 word short name that captures the essence of the feature
|
|
258
|
+
- Use action-noun format when possible (e.g., "user-auth", "plugin-architecture")
|
|
259
|
+
- Preserve technical terms and acronyms (OAuth2, API, JWT, protocol acronyms)
|
|
260
|
+
- Keep it concise but descriptive enough to understand the feature at a glance
|
|
261
|
+
|
|
262
|
+
2. **Check existing specification and determine operation mode**:
|
|
263
|
+
|
|
264
|
+
a. **Check if spec.md exists**:
|
|
265
|
+
```bash
|
|
266
|
+
SPEC_FILE="specs/spec.md"
|
|
267
|
+
|
|
268
|
+
if [[ -f "$SPEC_FILE" ]]; then
|
|
269
|
+
EXISTING_SPEC=true
|
|
270
|
+
# Read existing spec to gather statistics
|
|
271
|
+
# ⚠️ **All three of the old patterns were anchored on forms the template never
|
|
272
|
+
# emits** (T094/ISS-63) — measured on a spec built from `spec-template.md`:
|
|
273
|
+
# US 0 ≠ 11 · FR 0 ≠ 57. The User-Story heading is `### US1 - …`; the FR line is an
|
|
274
|
+
# INDENTED bold list item (` - **FR-XXX**: …`), so a line-start anchor `^- ` misses
|
|
275
|
+
# every one of them. Both now use the same patterns as `status.md` — that file
|
|
276
|
+
# documents this format contract in a comment and MUST stay consistent with `qc`.
|
|
277
|
+
US_COUNT=$(grep -cE '^### US[0-9]+ ' "$SPEC_FILE" 2>/dev/null || true)
|
|
278
|
+
FR_COUNT=$(grep -cE '^[[:space:]]*- \*\*FR-[0-9]+[A-E]?\*\*:' "$SPEC_FILE" 2>/dev/null || true)
|
|
279
|
+
: "${US_COUNT:=0}"
|
|
280
|
+
: "${FR_COUNT:=0}"
|
|
281
|
+
# ⚠️ A third counter, `ASSUMP_COUNT`, used to sit here. It is **removed, not
|
|
282
|
+
# fixed**: (a) its pattern (`^### **[A-`) matched nothing, (b) it scanned
|
|
283
|
+
# `$SPEC_FILE` while assumptions live in `specpro.assumptions.md` — the wrong
|
|
284
|
+
# artifact — and (c) it was **never read** by anything (not in the statistics
|
|
285
|
+
# display below). A wrong predicate inside a value nobody reads is pure liability
|
|
286
|
+
# (the shape this project keeps finding: 写了却从未被调用的东西).
|
|
287
|
+
|
|
288
|
+
# Extract current version
|
|
289
|
+
CURRENT_VERSION=$(grep "^**Version**:" "$SPEC_FILE" | head -1 | awk '{print $2}')
|
|
290
|
+
else
|
|
291
|
+
EXISTING_SPEC=false
|
|
292
|
+
fi
|
|
293
|
+
```
|
|
294
|
+
|
|
295
|
+
b. **Determine operation mode**:
|
|
296
|
+
|
|
297
|
+
**If NO existing spec** (`$EXISTING_SPEC = false`):
|
|
298
|
+
- This is **initial specification**
|
|
299
|
+
- Set `UPDATE_MODE=false`
|
|
300
|
+
- Set `SPEC_VERSION="0.1"`
|
|
301
|
+
- Run script: `.specpro/scripts/bash/create-new-feature.sh --json "$ARGUMENTS"`
|
|
302
|
+
- Proceed to step 3 (load Constitution, generate spec)
|
|
303
|
+
|
|
304
|
+
**If existing spec EXISTS** (`$EXISTING_SPEC = true`):
|
|
305
|
+
- This is an **evolution iteration**
|
|
306
|
+
- Ask user for intent:
|
|
307
|
+
```markdown
|
|
308
|
+
## Existing Specification Detected
|
|
309
|
+
|
|
310
|
+
You already have a specification for this application:
|
|
311
|
+
|
|
312
|
+
**Current Spec**: Version $CURRENT_VERSION
|
|
313
|
+
**Statistics**:
|
|
314
|
+
- User Stories: $US_COUNT
|
|
315
|
+
- Functional Requirements: $FR_COUNT
|
|
316
|
+
|
|
317
|
+
**Your Input**: "$ARGUMENTS"
|
|
318
|
+
|
|
319
|
+
**What do you want to do?**
|
|
320
|
+
|
|
321
|
+
1. **UPDATE** existing spec (evolve the application)
|
|
322
|
+
- Keep all existing content
|
|
323
|
+
- Add new features/requirements
|
|
324
|
+
- Update existing sections if specified
|
|
325
|
+
- Preserve validated constraints
|
|
326
|
+
- Generate Change Summary with [create]/[update]/[deprecated] tags (changed items only — unchanged items are not logged)
|
|
327
|
+
- Auto-commit to preserve history
|
|
328
|
+
|
|
329
|
+
2. **VIEW** current spec first
|
|
330
|
+
- Display summary of current specification
|
|
331
|
+
- Show User Stories, Requirements, Constraints
|
|
332
|
+
- Then ask again
|
|
333
|
+
|
|
334
|
+
Your choice (1/2):
|
|
335
|
+
```
|
|
336
|
+
|
|
337
|
+
- **If user chooses 1 (UPDATE)**:
|
|
338
|
+
- Set `UPDATE_MODE=true`
|
|
339
|
+
- Parse current version (e.g., "0.1")
|
|
340
|
+
- Increment version: `NEW_VERSION=$(echo "0.1" | awk -F. '{print $1"."$2+1}')`
|
|
341
|
+
- Set `SPEC_VERSION=$NEW_VERSION`
|
|
342
|
+
- Run script to get SPEC_FILE path: `.specpro/scripts/bash/create-new-feature.sh --json "$ARGUMENTS"`
|
|
343
|
+
- Proceed to step 3 with merge logic
|
|
344
|
+
|
|
345
|
+
- **If user chooses 2 (VIEW)**:
|
|
346
|
+
- Display current spec summary:
|
|
347
|
+
```markdown
|
|
348
|
+
## Current Specification Summary
|
|
349
|
+
|
|
350
|
+
### User Stories ($US_COUNT total)
|
|
351
|
+
[List first 10 stories with priorities]
|
|
352
|
+
|
|
353
|
+
### Key Functional Requirements
|
|
354
|
+
[List key requirements]
|
|
355
|
+
|
|
356
|
+
### Constitution Constraints
|
|
357
|
+
[List key constraints]
|
|
358
|
+
```
|
|
359
|
+
- Ask again for choice 1/2
|
|
360
|
+
|
|
361
|
+
2.5. **Dynamic assumption resolution** (for initial specification only):
|
|
362
|
+
|
|
363
|
+
**Purpose**: Identify and resolve CRITICAL architectural assumptions that MUST be clarified BEFORE spec generation. Unlike clarifications (which improve spec quality post-generation), these assumptions block spec generation because they fundamentally affect the architecture and direction.
|
|
364
|
+
|
|
365
|
+
**This step ONLY applies to UPDATE_MODE=false (initial specification)**. Skip this step if UPDATE_MODE=true.
|
|
366
|
+
|
|
367
|
+
a. **Initial analysis of user input**:
|
|
368
|
+
- Analyze the user's feature description for CRITICAL architectural decisions
|
|
369
|
+
- Identify decisions that affect: architecture, scope, platform, deployment, data model
|
|
370
|
+
- Ask: "What is the MOST CRITICAL unresolved decision without which we cannot proceed?"
|
|
371
|
+
- Examples of critical decisions:
|
|
372
|
+
* "Notepad" → Local vs Web? (affects architecture, data model, deployment)
|
|
373
|
+
* "Chat app" → Real-time vs Async? (affects protocol, infrastructure)
|
|
374
|
+
* "Multi-user" → Single-device vs Collaborative? (affects sync, backend)
|
|
375
|
+
* "High performance" → What's the target scale? (affects architecture choices)
|
|
376
|
+
- Load `.specpro/templates/assumptions-template.md` to understand the required format
|
|
377
|
+
|
|
378
|
+
b. **Generate first-level assumptions** (if any):
|
|
379
|
+
- If CRITICAL assumptions are identified:
|
|
380
|
+
1. Create `specs/assumptions.md` using the template
|
|
381
|
+
2. Add 1-3 initial assumptions to the file
|
|
382
|
+
3. For each assumption, include:
|
|
383
|
+
- Status: [ ] Pending
|
|
384
|
+
- Context: What led to this question (user input)
|
|
385
|
+
- Impact: What this affects (architecture, scope, platform, etc.)
|
|
386
|
+
- Question: Clear, specific question
|
|
387
|
+
- Options: 2-4 mutually exclusive options with implications
|
|
388
|
+
- Recommended: Best option based on context with reasoning
|
|
389
|
+
4. Display blocking message:
|
|
390
|
+
```markdown
|
|
391
|
+
## 🔑 Critical Assumptions Detected
|
|
392
|
+
|
|
393
|
+
Before I can generate the specification, I need to clarify these CRITICAL decisions
|
|
394
|
+
that affect the architecture and direction of the feature:
|
|
395
|
+
|
|
396
|
+
**Assumptions File**: specs/assumptions.md
|
|
397
|
+
- Total: N critical assumptions
|
|
398
|
+
|
|
399
|
+
These MUST be resolved before spec generation can proceed.
|
|
400
|
+
|
|
401
|
+
Let's resolve them one by one...
|
|
402
|
+
```
|
|
403
|
+
- If NO critical assumptions are identified (user input is clear):
|
|
404
|
+
- Skip assumption resolution
|
|
405
|
+
- Proceed directly to Step 3 (Load Constitution)
|
|
406
|
+
- Note: "No critical assumptions detected. User input is sufficiently clear to proceed."
|
|
407
|
+
|
|
408
|
+
c. **Interactive resolution loop** ✨ ENHANCED with progress tracking:
|
|
409
|
+
|
|
410
|
+
**WHILE** there are pending assumptions (status `[ ]`):
|
|
411
|
+
|
|
412
|
+
1. **Present ONE current assumption**:
|
|
413
|
+
- Display assumption with:
|
|
414
|
+
- Progress indicator: "### Question [current]/[total]: [ID]"
|
|
415
|
+
- ID, Question, Context, Impact
|
|
416
|
+
- Options table (if applicable)
|
|
417
|
+
- Recommended option with reasoning
|
|
418
|
+
- Format:
|
|
419
|
+
```markdown
|
|
420
|
+
### Question [ID]: [Question Summary]
|
|
421
|
+
|
|
422
|
+
**Context**: [What led to this question]
|
|
423
|
+
|
|
424
|
+
**Impact**: [What this affects]
|
|
425
|
+
|
|
426
|
+
**Question**:
|
|
427
|
+
[Full question text]
|
|
428
|
+
|
|
429
|
+
**Options** (if applicable):
|
|
430
|
+
| Option | Description | Implications |
|
|
431
|
+
|--------|-------------|--------------|
|
|
432
|
+
| A | [Option A] | [Implications] |
|
|
433
|
+
| B | [Option B] | [Implications] |
|
|
434
|
+
| C | [Option C] | [Implications] |
|
|
435
|
+
|
|
436
|
+
**Recommended**: Option [X] - [Reasoning]
|
|
437
|
+
|
|
438
|
+
Your answer (option letter or custom):
|
|
439
|
+
```
|
|
440
|
+
|
|
441
|
+
2. **Wait for user response**
|
|
442
|
+
- Accept: option letter (A/B/C), "yes"/"recommended" for recommendation, or custom answer
|
|
443
|
+
- Validate response maps to options or is appropriate
|
|
444
|
+
|
|
445
|
+
3. **Update assumptions.md immediately**:
|
|
446
|
+
- Mark assumption as `[x]` Resolved
|
|
447
|
+
- Fill in "Answer" field with user's response
|
|
448
|
+
- Fill in "Resolution" field with:
|
|
449
|
+
- How this answer guides spec generation
|
|
450
|
+
- Related architectural decisions
|
|
451
|
+
- Next assumptions that may emerge (see step 3d)
|
|
452
|
+
- Save file (atomic write)
|
|
453
|
+
- Display progress: "✓ Resolved [current]/[total] assumptions"
|
|
454
|
+
|
|
455
|
+
4. **Contextual re-analysis** (KEY STEP):
|
|
456
|
+
- Based on ALL previous answers + user input
|
|
457
|
+
- Ask: "Based on this answer, what's the NEXT critical decision?"
|
|
458
|
+
- Identify NEW assumptions that emerge:
|
|
459
|
+
* Check if previous decisions reveal new critical questions
|
|
460
|
+
* Check if decisions eliminate previously planned questions
|
|
461
|
+
- Generate next assumption (if any):
|
|
462
|
+
* Add to assumptions.md with updated context
|
|
463
|
+
* Include "Context": Previous decisions that led to this
|
|
464
|
+
* Mark as `[ ]` Pending
|
|
465
|
+
- If NO new assumptions emerge:
|
|
466
|
+
* Continue loop (there may be other pending assumptions)
|
|
467
|
+
|
|
468
|
+
5. **Check loop condition**:
|
|
469
|
+
- If there are still pending assumptions → Continue to next iteration (back to step c.1)
|
|
470
|
+
- If ALL assumptions resolved → Exit loop
|
|
471
|
+
|
|
472
|
+
d. **Completion of assumption resolution**:
|
|
473
|
+
|
|
474
|
+
1. **Display summary**:
|
|
475
|
+
```markdown
|
|
476
|
+
✓ All critical assumptions resolved
|
|
477
|
+
|
|
478
|
+
**Total Assumptions Resolved**: N
|
|
479
|
+
**Assumptions File**: specs/assumptions.md
|
|
480
|
+
|
|
481
|
+
**Decision Path**:
|
|
482
|
+
- A001: [Answer] → [Impact]
|
|
483
|
+
- A002: [Answer] → [Impact] (emerged after A001)
|
|
484
|
+
- A003: [Answer] → [Impact] (emerged after A001, A002)
|
|
485
|
+
|
|
486
|
+
**Architectural Direction**:
|
|
487
|
+
- [Summary of decisions]
|
|
488
|
+
- [Key implications for spec generation]
|
|
489
|
+
|
|
490
|
+
Proceeding with spec generation based on these decisions...
|
|
491
|
+
```
|
|
492
|
+
|
|
493
|
+
2. **Update assumptions.md**:
|
|
494
|
+
- Add "Decision Path" section showing complete decision history
|
|
495
|
+
- Add "Summary" section with totals
|
|
496
|
+
- Update "Status": Resolved
|
|
497
|
+
- Mark "Ready for Spec Generation": YES
|
|
498
|
+
|
|
499
|
+
3. **Store context for spec generation**:
|
|
500
|
+
- Keep all resolved assumptions accessible
|
|
501
|
+
- Use these decisions to guide Step 5 (specification generation)
|
|
502
|
+
- Reference when generating FRs, User Stories, architecture decisions
|
|
503
|
+
|
|
504
|
+
e. **Skip assumption resolution** (if UPDATE_MODE=true or no critical assumptions):
|
|
505
|
+
- This step is skipped for UPDATE_MODE=true (updating existing spec)
|
|
506
|
+
- For initial specs with no critical assumptions, skip and proceed
|
|
507
|
+
- Note: Existing specs should have already resolved assumptions during initial creation
|
|
508
|
+
|
|
509
|
+
3. **Load Constitution and identify applicable principles** ✨ ENHANCED with incremental caching:
|
|
510
|
+
|
|
511
|
+
**Purpose**: Ensure the feature specification complies with project principles defined in the Constitution, regardless of Constitution structure or version.
|
|
512
|
+
|
|
513
|
+
a. **Detect operation mode** ✨ NEW:
|
|
514
|
+
```bash
|
|
515
|
+
CACHE_FILE=".specpro/cache/constitution-matching.md"
|
|
516
|
+
|
|
517
|
+
# ⚠️ **The cache is keyed on the CONSTITUTION's CONTENT, not on the file's existence**
|
|
518
|
+
# (`T236` / `ISS-209`). The constitution is **amended** — its `**Version**` /
|
|
519
|
+
# `**Last Amended**` move — and a hit keyed on nothing would then serve a principle set
|
|
520
|
+
# that no longer holds, **silently**, because the steps that would have re-derived it are
|
|
521
|
+
# the ones being skipped. The key is what makes reuse safe: a moved constitution is a MISS.
|
|
522
|
+
# `shasum` (macOS/BSD) and `sha256sum` (GNU) — the repository's usual portability pair.
|
|
523
|
+
CONSTITUTION_HASH=$( (shasum -a 256 specs/constitution.md 2>/dev/null \
|
|
524
|
+
|| sha256sum specs/constitution.md 2>/dev/null) | cut -d' ' -f1)
|
|
525
|
+
|
|
526
|
+
if [[ "$UPDATE_MODE" = "false" ]]; then
|
|
527
|
+
# Initial spec: Full Constitution matching
|
|
528
|
+
CACHE_MODE="generate"
|
|
529
|
+
elif [[ -n "$CONSTITUTION_HASH" ]] && [ -f "$CACHE_FILE" ] \
|
|
530
|
+
&& grep -qF "**Constitution Hash**: $CONSTITUTION_HASH" "$CACHE_FILE"; then
|
|
531
|
+
# Hit AND the constitution is unchanged since the cache was written.
|
|
532
|
+
CACHE_MODE="reuse"
|
|
533
|
+
else
|
|
534
|
+
# Absent — or written against a constitution that has since moved ⇒ regenerate.
|
|
535
|
+
CACHE_MODE="generate"
|
|
536
|
+
fi
|
|
537
|
+
```
|
|
538
|
+
|
|
539
|
+
a2. **On a cache HIT, the matching is already done — read it, do not re-derive** ⚠️:
|
|
540
|
+
```bash
|
|
541
|
+
if [[ "$CACHE_MODE" = "reuse" ]]; then
|
|
542
|
+
echo "✓ Reusing Constitution matching from cache (constitution unchanged: ${CONSTITUTION_HASH:0:12})"
|
|
543
|
+
cat "$CACHE_FILE"
|
|
544
|
+
fi
|
|
545
|
+
```
|
|
546
|
+
|
|
547
|
+
⇒ **When `CACHE_MODE=reuse`, SKIP steps b through f.** The matched domains, principles and
|
|
548
|
+
constraints are exactly what the cache holds, so re-deriving them is the work the cache
|
|
549
|
+
exists to avoid. Continue at `g`, which on a reuse leaves the file untouched.
|
|
550
|
+
|
|
551
|
+
⚠️ **Before this step existed, `CACHE_MODE` was written and never read** (`T236` / `ISS-209`):
|
|
552
|
+
`g` decided only *whether to write*, so a hit could not change any behaviour, and the
|
|
553
|
+
`✓ Reusing Constitution matching from cache` line was the mechanism's only consumer. A cache
|
|
554
|
+
nothing reads is not a cache — it is a file that grows and a branch nothing takes. ⚠️ **The
|
|
555
|
+
repo names this shape directly** (`CLAUDE.md` → 「改机制前先核验它是否接在执行链上」: "写了却从未被调用的缓存").
|
|
556
|
+
|
|
557
|
+
b. **Read Constitution**:
|
|
558
|
+
- Load `specs/constitution.md`
|
|
559
|
+
- Parse ALL principles, requirements, and governance rules
|
|
560
|
+
- Do NOT assume any specific structure (Articles, Sections, etc.)
|
|
561
|
+
- Store for domain-based matching
|
|
562
|
+
|
|
563
|
+
c. **Identify feature domains** from the user's feature description:
|
|
564
|
+
- Analyze the user's input description for key terms
|
|
565
|
+
- Map to feature domains using these keyword patterns:
|
|
566
|
+
|
|
567
|
+
| Domain | Trigger Keywords | Constitution Search Terms |
|
|
568
|
+
|--------|-----------------|---------------------------|
|
|
569
|
+
| **Network/Connectivity** | connect, network, socket, remote, server, client, tcp, udp | network, async, blocking, I/O, performance, concurrent, connection |
|
|
570
|
+
| **Multiplatform** | Android, iOS, Desktop, cross-platform, platform, shared | multiplatform, platform, shared module, common, expect, actual |
|
|
571
|
+
| **Security/Privacy** | password, authentication, encrypt, credentials, token, secret, oauth, login | security, privacy, encryption, credential, logging, sensitive, secure |
|
|
572
|
+
| **Protocol/Codec** | encoding, decoding, protocol, codec, format, parse, serialize, compress | protocol, test, coverage, risk, high-risk, test-first, encoding, decoding |
|
|
573
|
+
| **UI/UX** | interface, display, render, screen, view, component, widget, button, dialog | UI, interface, user, experience, low-risk, interface component |
|
|
574
|
+
| **Data/Persistence** | database, storage, save, load, persist, cache, file, record | data, persistence, storage, database, serialization |
|
|
575
|
+
| **Performance** | speed, latency, throughput, concurrent, optimize, fast, slow | performance, resource, latency, throughput, optimization |
|
|
576
|
+
|
|
577
|
+
c. **Match feature domains to Constitution principles** (keyword-based, NOT article-number-based):
|
|
578
|
+
- For each identified domain, search Constitution content for related principles:
|
|
579
|
+
- Use the "Constitution Search Terms" from the table above
|
|
580
|
+
- Search in principle titles, requirement text, and governance sections
|
|
581
|
+
- Do NOT rely on article numbers or section names
|
|
582
|
+
- Example matching logic:
|
|
583
|
+
```
|
|
584
|
+
If "Network/Connectivity" domain detected:
|
|
585
|
+
Search for principles containing: "network", "async", "blocking", "I/O", "performance"
|
|
586
|
+
Match might be: "Article V: Performance & Resource Management"
|
|
587
|
+
OR: "Section 3: Network Operations"
|
|
588
|
+
OR: Any principle with "network I/O MUST use async patterns"
|
|
589
|
+
|
|
590
|
+
If "Protocol/Codec" domain detected:
|
|
591
|
+
Search for principles containing: "protocol", "test", "coverage", "risk", "high-risk"
|
|
592
|
+
Match might be: "Article IV: Risk-Based Quality Assurance"
|
|
593
|
+
OR: "Section: Testing Requirements"
|
|
594
|
+
OR: Any principle with "Protocol implementations require Test-First"
|
|
595
|
+
```
|
|
596
|
+
|
|
597
|
+
d. **Extract matched principle requirements**:
|
|
598
|
+
- For each matched principle, extract:
|
|
599
|
+
- Principle title/heading
|
|
600
|
+
- Specific MUST/SHOULD requirements
|
|
601
|
+
- Risk level (if defined, e.g., "High-Risk Module")
|
|
602
|
+
- Testing requirements (if defined, e.g., "Test-First MANDATORY")
|
|
603
|
+
- Create a constraint summary for spec.md
|
|
604
|
+
|
|
605
|
+
e. **Generate Constitution Constraints section** (to be inserted into spec.md):
|
|
606
|
+
- Format matched principles as constraints:
|
|
607
|
+
```markdown
|
|
608
|
+
## Constitution Constraints
|
|
609
|
+
|
|
610
|
+
This feature must comply with the following principles from the project Constitution:
|
|
611
|
+
|
|
612
|
+
### [Principle Title]
|
|
613
|
+
- **Requirement**: [Specific MUST/SHOULD statement from Constitution]
|
|
614
|
+
- **Source**: Constitution - [Principle Section]
|
|
615
|
+
- **Risk Level**: [If defined, e.g., "High-Risk Module (Network Transport)"]
|
|
616
|
+
- **Implications**: [What this means for the feature]
|
|
617
|
+
|
|
618
|
+
### [Another Principle Title]
|
|
619
|
+
- **Requirement**: [Specific requirement from Constitution]
|
|
620
|
+
- **Source**: Constitution - [Principle Section]
|
|
621
|
+
- **Risk Level**: [If defined]
|
|
622
|
+
- **Implications**: [What this means for the feature]
|
|
623
|
+
```
|
|
624
|
+
- If NO principles match the feature domains, document this:
|
|
625
|
+
```markdown
|
|
626
|
+
## Constitution Constraints
|
|
627
|
+
|
|
628
|
+
No specific Constitution principles were identified as applicable to this feature based on the domain analysis.
|
|
629
|
+
```
|
|
630
|
+
|
|
631
|
+
f. **Apply constraints to Functional Requirements** (during step 5.c generation):
|
|
632
|
+
- When generating functional requirements, incorporate Constitution constraints
|
|
633
|
+
- Example: If Constitution requires "Network I/O MUST use async patterns" and feature involves "connect to server":
|
|
634
|
+
- FR-xxx: System MUST connect to the server using non-blocking async I/O (per Constitution)
|
|
635
|
+
|
|
636
|
+
g. **Cache Constitution matching results** ✨ NEW:
|
|
637
|
+
```bash
|
|
638
|
+
CACHE_FILE=".specpro/cache/constitution-matching.md"
|
|
639
|
+
CACHE_DIR=".specpro/cache"
|
|
640
|
+
|
|
641
|
+
# Create cache directory if not exists
|
|
642
|
+
mkdir -p "$CACHE_DIR"
|
|
643
|
+
|
|
644
|
+
# If CACHE_MODE=generate, save matching results
|
|
645
|
+
# ⚠️ On `reuse` this block is SKIPPED — rewriting an unchanged cache would be churn, and
|
|
646
|
+
# the hash it carries is what `a` will key on next time (`T236` / `ISS-209`).
|
|
647
|
+
if [[ "$CACHE_MODE" = "generate" ]]; then
|
|
648
|
+
cat > "$CACHE_FILE" <<EOF
|
|
649
|
+
# Constitution Matching Cache
|
|
650
|
+
|
|
651
|
+
**Feature**: [FEATURE_NAME]
|
|
652
|
+
**Generated**: [DATE]
|
|
653
|
+
**Spec Version**: [VERSION]
|
|
654
|
+
**Constitution Hash**: $CONSTITUTION_HASH
|
|
655
|
+
|
|
656
|
+
## Matched Domains
|
|
657
|
+
[List of domains detected]
|
|
658
|
+
|
|
659
|
+
## Matched Principles
|
|
660
|
+
[List of matched Constitution principles with constraints]
|
|
661
|
+
|
|
662
|
+
## Last Modified
|
|
663
|
+
[TIMESTAMP]
|
|
664
|
+
EOF
|
|
665
|
+
echo "✓ Constitution matching cached to $CACHE_FILE"
|
|
666
|
+
else
|
|
667
|
+
echo "✓ Reusing Constitution matching from cache"
|
|
668
|
+
fi
|
|
669
|
+
```
|
|
670
|
+
|
|
671
|
+
4. Load `.specpro/templates/spec-template.md` to understand required sections.
|
|
672
|
+
|
|
673
|
+
5. **Generate or update specification** ✨ ENHANCED with progress tracking:
|
|
674
|
+
|
|
675
|
+
**If UPDATE_MODE=false** (initial specification):
|
|
676
|
+
|
|
677
|
+
a. **Parse user description from Input** 📋 [1/6]
|
|
678
|
+
b. **Extract key concepts** 📋 [2/6]
|
|
679
|
+
c. **Generate specification based on resolved assumptions** 📋 [3/6]:
|
|
680
|
+
- Load `specs/assumptions.md` (if exists, created in Step 2.5)
|
|
681
|
+
- Use resolved assumptions to guide:
|
|
682
|
+
* Architecture decisions
|
|
683
|
+
* Platform choices (e.g., local vs web, desktop vs mobile)
|
|
684
|
+
* Scope boundaries
|
|
685
|
+
* Feature priorities
|
|
686
|
+
* Technology direction
|
|
687
|
+
- For remaining unclear aspects (non-critical):
|
|
688
|
+
* Make informed guesses based on:
|
|
689
|
+
- Constitution principles
|
|
690
|
+
- Context from resolved assumptions
|
|
691
|
+
- Industry standards
|
|
692
|
+
- Common patterns
|
|
693
|
+
* Convert ALL assumptions (both resolved and inferred) into Functional Requirements
|
|
694
|
+
* Examples:
|
|
695
|
+
- Instead of "Assume LAN <10ms", write "System MUST operate in LAN environments with <10ms latency"
|
|
696
|
+
- Instead of "Assume 8GB RAM", write "System MUST support teacher devices with 8GB RAM minimum"
|
|
697
|
+
* DO NOT use [NEEDS CLARIFICATION] markers - all ambiguity detection is deferred to /specpro-clarify --scan
|
|
698
|
+
d. **Fill User Scenarios & Testing** 📋 [4/6]:
|
|
699
|
+
- Generate user stories with format:
|
|
700
|
+
```markdown
|
|
701
|
+
### US[ID] - [Title] (Priority: PX)
|
|
702
|
+
|
|
703
|
+
**Lifecycle**: [specify:create][plan:create][tasks:create]
|
|
704
|
+
|
|
705
|
+
[Description]
|
|
706
|
+
|
|
707
|
+
**Why this priority**: [Rationale]
|
|
708
|
+
|
|
709
|
+
**Independent Test**: [Test strategy]
|
|
710
|
+
|
|
711
|
+
**Acceptance Scenarios**:
|
|
712
|
+
1. Given...When...Then...
|
|
713
|
+
```
|
|
714
|
+
- Each new user story gets Lifecycle field: `[specify:create][plan:create][tasks:create]`
|
|
715
|
+
|
|
716
|
+
e. **Generate Functional Requirements** 📋 [5/5]:
|
|
717
|
+
- Generate FRs grouped by User Story with format:
|
|
718
|
+
```markdown
|
|
719
|
+
#### [User Story Title] (US[ID])
|
|
720
|
+
|
|
721
|
+
- **FR-XXX**: [Requirement Title]
|
|
722
|
+
**Lifecycle**: [specify:create][plan:create][tasks:create]
|
|
723
|
+
System MUST [requirement].
|
|
724
|
+
|
|
725
|
+
- **FR-XXX**: [Requirement Title]
|
|
726
|
+
**Lifecycle**: [specify:create][plan:create][tasks:create]
|
|
727
|
+
System MUST [requirement].
|
|
728
|
+
```
|
|
729
|
+
- Each new FR gets Lifecycle field: `[specify:create][plan:create][tasks:create]`
|
|
730
|
+
- Group FRs by their associated User Story for clear traceability
|
|
731
|
+
|
|
732
|
+
f. **Generate Change Summary** (for initial spec):
|
|
733
|
+
```markdown
|
|
734
|
+
## Change Summary
|
|
735
|
+
|
|
736
|
+
**Version**: 0.1
|
|
737
|
+
**Update Type**: Initial
|
|
738
|
+
**Changes Overview**:
|
|
739
|
+
- **Added**: Initial specification with N User Stories, M Requirements
|
|
740
|
+
```
|
|
741
|
+
|
|
742
|
+
g. **Return**: SUCCESS (spec ready for planning)
|
|
743
|
+
|
|
744
|
+
**If UPDATE_MODE=true** (update existing specification):
|
|
745
|
+
|
|
746
|
+
a. **Parse existing spec.md** 📋 [1/5]:
|
|
747
|
+
- Extract ALL User Stories with:
|
|
748
|
+
- ID (US1, US2, etc.)
|
|
749
|
+
- Title
|
|
750
|
+
- Priority (P1, P2, P3)
|
|
751
|
+
- Acceptance Criteria
|
|
752
|
+
- **Lifecycle**: [specify:<status>][plan:<status>][tasks:<status>]
|
|
753
|
+
- Extract ALL Functional Requirements
|
|
754
|
+
- ID (FR-xxx, FR-yyy, etc.)
|
|
755
|
+
- Title
|
|
756
|
+
- **Lifecycle**: [specify:<status>][plan:<status>][tasks:<status>]
|
|
757
|
+
- Extract ALL Constitution Constraints
|
|
758
|
+
|
|
759
|
+
b. **Analyze user input for changes** 📋 [2/5]:
|
|
760
|
+
- Identify **new features** to add (not in existing spec)
|
|
761
|
+
- Identify **modifications** to existing features
|
|
762
|
+
- Identify **deprecations** (features to mark as deprecated)
|
|
763
|
+
|
|
764
|
+
c. **Detect architectural conflicts** 📋 [3/5]:
|
|
765
|
+
- Load existing spec.md and extract critical architectural decisions:
|
|
766
|
+
* Application type: Local vs Web vs Hybrid
|
|
767
|
+
* Platform scope: Desktop vs Mobile vs Cross-platform
|
|
768
|
+
* Deployment model: Single-tenant vs Multi-tenant
|
|
769
|
+
* Data storage: Local vs Cloud vs Hybrid
|
|
770
|
+
* Collaboration model: Single-user vs Multi-user vs Real-time collaborative
|
|
771
|
+
* Performance model: Batch vs Real-time vs Stream processing
|
|
772
|
+
- Compare user's new input ($ARGUMENTS) against existing architectural decisions
|
|
773
|
+
- Detect **architectural conflicts** that would invalidate large portions of existing spec:
|
|
774
|
+
* **Local → Web** OR **Web → Local**: Completely different architecture, data model, deployment
|
|
775
|
+
* **Single-user → Collaborative**: Requires sync, conflict resolution, backend
|
|
776
|
+
* **Desktop → Mobile** OR **Mobile → Desktop**: Platform-specific changes, UI framework
|
|
777
|
+
* **Single-platform → Cross-platform**: Requires significant refactoring
|
|
778
|
+
* **Online → Offline** OR **Offline → Online**: Major architectural shift
|
|
779
|
+
- If architectural conflicts detected:
|
|
780
|
+
1. List specific conflicts identified
|
|
781
|
+
2. Calculate estimated impact (percentage of FRs that would be deprecated)
|
|
782
|
+
3. Display **RESTART recommended** message:
|
|
783
|
+
```markdown
|
|
784
|
+
## ⚠️ Architectural Conflict Detected
|
|
785
|
+
|
|
786
|
+
Your new input conflicts with the current architectural direction:
|
|
787
|
+
|
|
788
|
+
**Current Spec**: Version $CURRENT_VERSION
|
|
789
|
+
**Current Architecture**: [Summary of current architecture]
|
|
790
|
+
|
|
791
|
+
**Your Input**: "$ARGUMENTS"
|
|
792
|
+
|
|
793
|
+
**Conflicts Identified**:
|
|
794
|
+
- [Conflict 1]: [Current] → [Requested] (Impact: [X]% of FRs deprecated)
|
|
795
|
+
- [Conflict 2]: [Current] → [Requested] (Impact: [Y]% of FRs deprecated)
|
|
796
|
+
...
|
|
797
|
+
|
|
798
|
+
**Estimated Impact**:
|
|
799
|
+
- Approximately Z% of existing Functional Requirements would be deprecated
|
|
800
|
+
- Major architectural changes required
|
|
801
|
+
|
|
802
|
+
**Recommendations**:
|
|
803
|
+
1. **RESTART** (Recommended) - Start fresh with new architectural direction
|
|
804
|
+
- Clean slate, no deprecated baggage
|
|
805
|
+
- Version reset to 0.1
|
|
806
|
+
- Current spec preserved in Git history
|
|
807
|
+
|
|
808
|
+
2. **UPDATE** - Continue with evolution (will deprecate conflicting items)
|
|
809
|
+
- Many existing FRs marked as [deprecated]
|
|
810
|
+
- Change Summary will show extensive deprecations
|
|
811
|
+
- Version incremented to $NEW_VERSION
|
|
812
|
+
|
|
813
|
+
Your choice (1/2):
|
|
814
|
+
```
|
|
815
|
+
4. If user chooses 1 (RESTART):
|
|
816
|
+
- Ask for confirmation: "⚠️ Are you sure? This will DELETE the current spec.md and start fresh. Type 'RESTART' to confirm:"
|
|
817
|
+
- If confirmed:
|
|
818
|
+
* Delete existing spec.md
|
|
819
|
+
* Set `UPDATE_MODE=false`
|
|
820
|
+
* Set `SPEC_VERSION="0.1"` (reset to initial)
|
|
821
|
+
* Proceed to Step 2.5 (Dynamic Assumption Resolution) as if initial spec
|
|
822
|
+
- Else:
|
|
823
|
+
* Return to choice 1/2
|
|
824
|
+
5. If user chooses 2 (UPDATE):
|
|
825
|
+
* Set `UPDATE_MODE=true`
|
|
826
|
+
* Proceed to Step 5.d with extensive deprecations
|
|
827
|
+
* Note: User has been warned about impact
|
|
828
|
+
|
|
829
|
+
- If NO architectural conflicts:
|
|
830
|
+
* Proceed to Step 5.d (normal update flow)
|
|
831
|
+
|
|
832
|
+
d. **Generate Change Summary** 📋 [4/5] (if no conflicts or user chose UPDATE):
|
|
833
|
+
- Count changes:
|
|
834
|
+
- Added: N new items
|
|
835
|
+
- Modified: X items
|
|
836
|
+
- Deprecated: Y items
|
|
837
|
+
- Determine update type:
|
|
838
|
+
- Feature Addition: Added features without breaking changes
|
|
839
|
+
- Refactor: Restructured existing functionality
|
|
840
|
+
- Breaking Change: Incompatible changes
|
|
841
|
+
- Bug Fix: Fixed issues
|
|
842
|
+
- ⚠️ **不要产出逐项变更表**(`T239` / `ISS-212`):模板里那个 `### Detailed Changes` 块**已删除**,因为它**没有任何消费者**、且与每项的 `**Lifecycle**:` 字段和 `### Changes Overview` **复述同一事实**。要逐项细节,读**该项自己的 Lifecycle 字段**。
|
|
843
|
+
|
|
844
|
+
e. **Merge and generate updated spec.md** 📋 [5/5]:
|
|
845
|
+
|
|
846
|
+
**Lifecycle Update Rules**:
|
|
847
|
+
|
|
848
|
+
**For ALL items that CARRY a Lifecycle — which is User Stories and Functional Requirements, and nothing else** ⚠️ (`T239` / `ISS-212`):
|
|
849
|
+
⚠️ **`## Constitution Constraints` is NOT in this scope, and that is an adjudication, not an omission.** Evidence: ① `templates/spec-template.md`'s `## Constitution Constraints` section is `[Add constitution constraints if any]` — it **emits no `**Lifecycle**:` field**; ② the plan-layer reader (`commands/specpro.plan.md` → `4.a`) enumerates **two** classes (US, FR), not three; ③ measured on `specs/spec.md`: **73** `**Lifecycle**:` lines = 11 US + 62 FR — **zero** belong to a Constitution Constraint. ⇒ A rule written for a class that cannot carry the field is a rule nothing can follow; **this line used to name three**. ⚠️ **Do not "fix" it by adding Lifecycle fields to that section**: nothing reads them (no command parses a constraint's lifecycle), so the field would be a value with no consumer — and the section is a *reference* to the constitution, not a tracked requirement.
|
|
850
|
+
|
|
851
|
+
a. **New items** (not in existing spec):
|
|
852
|
+
- Add to spec with Lifecycle:
|
|
853
|
+
```markdown
|
|
854
|
+
**Lifecycle**: [specify:create][plan:create][tasks:create]
|
|
855
|
+
```
|
|
856
|
+
- specifyStatus = "create"
|
|
857
|
+
- planStatus = "create"
|
|
858
|
+
- tasksStatus = "create"
|
|
859
|
+
- In Change Summary: marked as **[create]**
|
|
860
|
+
|
|
861
|
+
b. **Modified items** (content changed):
|
|
862
|
+
- Update content in spec
|
|
863
|
+
- **Update Lifecycle** (all three stages):
|
|
864
|
+
```markdown
|
|
865
|
+
**Lifecycle**: [specify:update][plan:update][tasks:update]
|
|
866
|
+
```
|
|
867
|
+
- specifyStatus = "update"
|
|
868
|
+
- planStatus = "update" (triggers plan regeneration)
|
|
869
|
+
- tasksStatus = "update"
|
|
870
|
+
- **Note**: Do NOT check/preserve previous planStatus or tasksStatus
|
|
871
|
+
- **Rationale**: Any content change invalidates previous plan/tasks
|
|
872
|
+
- In Change Summary: marked as **[update]**
|
|
873
|
+
|
|
874
|
+
c. **Deprecated items** (no longer needed):
|
|
875
|
+
- Update content in spec
|
|
876
|
+
- **Update Lifecycle** (all three stages):
|
|
877
|
+
```markdown
|
|
878
|
+
**Lifecycle**: [specify:delete][plan:delete][tasks:delete]
|
|
879
|
+
```
|
|
880
|
+
- All three fields: specify="delete", plan="delete", tasks="delete"
|
|
881
|
+
- Move to Deprecated Items section at end of spec
|
|
882
|
+
- In Change Summary: marked as **[deprecated]**
|
|
883
|
+
- **Note**: DO NOT preserve old planStatus/tasksStatus values
|
|
884
|
+
|
|
885
|
+
d. **Preserved items** (no changes):
|
|
886
|
+
- **DO NOT update Lifecycle field** - preserve all three values as-is
|
|
887
|
+
- DO NOT include in Change Summary (no status marker)
|
|
888
|
+
|
|
889
|
+
**For User Stories**:
|
|
890
|
+
- **[create]**: Add new User Stories
|
|
891
|
+
- **[update]**: Update existing Story
|
|
892
|
+
- **[deprecated]**: Mark Story as deprecated
|
|
893
|
+
- (unchanged Stories: no Lifecycle update, NOT logged in Change Summary)
|
|
894
|
+
|
|
895
|
+
**For Functional Requirements**:
|
|
896
|
+
- **[create]**: Add new FRs
|
|
897
|
+
- **[update]**: Update existing FRs with new version
|
|
898
|
+
- **[deprecated]**: Mark FRs as deprecated with migration path
|
|
899
|
+
- (unchanged FRs: no Lifecycle update, NOT logged in Change Summary)
|
|
900
|
+
|
|
901
|
+
⚠️ **Constitution Constraints: no Lifecycle block here** (`T239` / `ISS-212`). A three-line `**For Constitution Constraints**:` block used to sit at this spot, mirroring the US/FR rules — but that section **carries no `**Lifecycle**:` field** and nothing reads one if it did (evidence in the note at the head of these rules). ⇒ **Constraints are added or updated as prose in `## Constitution Constraints`; they are never marked, never logged in the Change Summary, and never propagate downstream.** Re-adding the block would recreate a rule for a class that cannot follow it.
|
|
902
|
+
|
|
903
|
+
**For Deprecated Items section**:
|
|
904
|
+
- Move all deprecated items to this section
|
|
905
|
+
- Include:
|
|
906
|
+
- Original content
|
|
907
|
+
- Deprecation reason
|
|
908
|
+
- Migration path (if applicable)
|
|
909
|
+
|
|
910
|
+
f. **Write updated spec.md**:
|
|
911
|
+
- Include Change Summary at top
|
|
912
|
+
- Include all active (non-deprecated) items
|
|
913
|
+
- Include Deprecated Items section at end
|
|
914
|
+
|
|
915
|
+
g. **Update version metadata**:
|
|
916
|
+
```markdown
|
|
917
|
+
# Feature Specification: [FEATURE_NAME]
|
|
918
|
+
|
|
919
|
+
**Version**: 0.2
|
|
920
|
+
**Last Updated**: [DATE]
|
|
921
|
+
**Status**: Draft
|
|
922
|
+
```
|
|
923
|
+
|
|
924
|
+
h. **Return**: SUCCESS (spec updated and ready for planning)
|
|
925
|
+
|
|
926
|
+
6. **Write the specification to SPEC_FILE** using the template structure, replacing placeholders with concrete details derived from the feature description (arguments) while preserving section order and headings.
|
|
927
|
+
|
|
928
|
+
7. **Auto-scan for clarifications** ✨ ENHANCED with conditional logic:
|
|
929
|
+
|
|
930
|
+
a. **Determine scan necessity** ✨ NEW:
|
|
931
|
+
```bash
|
|
932
|
+
if [[ "$UPDATE_MODE" = "false" ]]; then
|
|
933
|
+
# Initial spec: Always run scan
|
|
934
|
+
RUN_SCAN=true
|
|
935
|
+
echo "📋 Running clarification scan on initial spec..."
|
|
936
|
+
else
|
|
937
|
+
# Update spec: Check if there are new/modified FRs
|
|
938
|
+
ADDED_FR_COUNT=$(grep -B1 '\*\*Lifecycle\*\*:.*\[specify:create\]' "$SPEC_FILE" 2>/dev/null | grep -cE "FR-[0-9]+[A-E]?" || true)
|
|
939
|
+
: "${ADDED_FR_COUNT:=0}"
|
|
940
|
+
MODIFIED_FR_COUNT=$(grep -B1 '\*\*Lifecycle\*\*:.*\[specify:update\]' "$SPEC_FILE" 2>/dev/null | grep -cE "FR-[0-9]+[A-E]?" || true)
|
|
941
|
+
: "${MODIFIED_FR_COUNT:=0}"
|
|
942
|
+
|
|
943
|
+
if [[ $ADDED_FR_COUNT -gt 0 ]] || [[ $MODIFIED_FR_COUNT -gt 0 ]]; then
|
|
944
|
+
RUN_SCAN=true
|
|
945
|
+
echo "📋 Running clarification scan ($ADDED_FR_COUNT added, $MODIFIED_FR_COUNT modified)..."
|
|
946
|
+
else
|
|
947
|
+
RUN_SCAN=false
|
|
948
|
+
echo "ℹ️ No new/modified FRs, skipping clarification scan"
|
|
949
|
+
fi
|
|
950
|
+
fi
|
|
951
|
+
```
|
|
952
|
+
|
|
953
|
+
b. **Invoke clarification scan** (conditional):
|
|
954
|
+
```bash
|
|
955
|
+
if [[ "$RUN_SCAN" = "true" ]]; then
|
|
956
|
+
/specpro-clarify --scan
|
|
957
|
+
else
|
|
958
|
+
# Load existing clarifications.md
|
|
959
|
+
if [[ -f "specs/clarifications.md" ]]; then
|
|
960
|
+
echo "✓ Loaded existing clarifications.md"
|
|
961
|
+
else
|
|
962
|
+
echo "⚠️ No clarifications.md found (expected for preserved-only update)"
|
|
963
|
+
fi
|
|
964
|
+
fi
|
|
965
|
+
```
|
|
966
|
+
This scans spec.md for ambiguities and generates/updates `specs/clarifications.md` with categorized questions.
|
|
967
|
+
|
|
968
|
+
c. **Wait for scan completion** (if scan was run): Do not proceed until the scan is complete and clarifications.md is generated.
|
|
969
|
+
|
|
970
|
+
d. **Verify clarifications.md** (if scan was run): Confirm that `specs/clarifications.md` was created successfully. If scan failed, warn user but proceed to commit.
|
|
971
|
+
|
|
972
|
+
8. **Auto-invoke clarify if needed** 🆕 REDESIGNED:
|
|
973
|
+
|
|
974
|
+
**Purpose**: Automatically check clarifications.md and invoke clarify if [Must Clarify] items exist
|
|
975
|
+
|
|
976
|
+
**Key Changes**:
|
|
977
|
+
- No user choice required - automatic based on clarifications.md content
|
|
978
|
+
- If [Must Clarify] items exist → automatically invoke clarify (interactive)
|
|
979
|
+
- If no [Must Clarify] items → skip to Step 9
|
|
980
|
+
- Seamless user experience - no decision point
|
|
981
|
+
|
|
982
|
+
a. **Read clarifications.md statistics**:
|
|
983
|
+
```bash
|
|
984
|
+
# ⚠️ **BIND THE PATH FIRST — this line is the whole fix** (`T236` / `ISS-209`): everything
|
|
985
|
+
# below reads `$CLARIFICATIONS_FILE`, and **nothing in this repository ever assigned it**
|
|
986
|
+
# (measured 2026-09-20: 5 uses, 0 assignments). With the variable unset, `[[ ! -f "" ]]` is
|
|
987
|
+
# **true**, so this step printed "✓ No clarifications needed" and returned **before**
|
|
988
|
+
# `$MUST_CLARIFY_COUNT` was ever computed ⇒ the `[Must Clarify]`-driven `/specpro-clarify`
|
|
989
|
+
# hand-off — the entire reason this step exists — was **unreachable by construction**.
|
|
990
|
+
# The literal below is the same one this file already uses at Step 7.c.
|
|
991
|
+
CLARIFICATIONS_FILE="specs/clarifications.md"
|
|
992
|
+
|
|
993
|
+
# Check if clarifications.md exists
|
|
994
|
+
if [[ ! -f "$CLARIFICATIONS_FILE" ]]; then
|
|
995
|
+
echo "✓ No clarifications needed (clarifications.md not found)"
|
|
996
|
+
echo " Proceeding to quality validation..."
|
|
997
|
+
# Skip to Step 9
|
|
998
|
+
return 0
|
|
999
|
+
fi
|
|
1000
|
+
|
|
1001
|
+
# Count items in each category — ⚠️ **ANCHORED ON THE ENTRY FORM, and that is the whole
|
|
1002
|
+
# fix** (`T244` / `ISS-217`): the pattern used to be the bare `"\[<label>\]"`, which
|
|
1003
|
+
# matches **every mention** — the template's own definitions block, its
|
|
1004
|
+
# `### [Must Clarify] Items` heading, the `## Progress Tracking` rows, the
|
|
1005
|
+
# `**Completion Criteria**` line. Measured on a freshly-generated artifact:
|
|
1006
|
+
# **Must Clarify 9 · Suggested Clarify 3 · Can Infer 8** against **1 item each**.
|
|
1007
|
+
# ⚠️ **That is not cosmetic**: `MUST_CLARIFY_COUNT > 0` is the trigger for the
|
|
1008
|
+
# auto-invoked `/specpro-clarify` hand-off below ⇒ an inflated count fires the hand-off
|
|
1009
|
+
# on an artifact with **no** must-clarify items, every time, and nothing reports it.
|
|
1010
|
+
# An entry is `- **[<label>] - <id>** - …` (note the ` - `, which the definition line
|
|
1011
|
+
# `- **[<label>]**: …` does not have, and no mention line starts with `- **`).
|
|
1012
|
+
# ⚠️ Per TOOL-009 this example is itself line-anchored: a copied unanchored pattern is
|
|
1013
|
+
# how the same misjudgment propagates.
|
|
1014
|
+
CLARIFY_ENTRY='^- \*\*\['
|
|
1015
|
+
MUST_CLARIFY_COUNT=$(grep -cE "${CLARIFY_ENTRY}Must Clarify\] - " "$CLARIFICATIONS_FILE" 2>/dev/null || true)
|
|
1016
|
+
: "${MUST_CLARIFY_COUNT:=0}"
|
|
1017
|
+
SUGGESTED_CLARIFY_COUNT=$(grep -cE "${CLARIFY_ENTRY}Suggested Clarify\] - " "$CLARIFICATIONS_FILE" 2>/dev/null || true)
|
|
1018
|
+
: "${SUGGESTED_CLARIFY_COUNT:=0}"
|
|
1019
|
+
CAN_INFER_COUNT=$(grep -cE "${CLARIFY_ENTRY}Can Infer\] - " "$CLARIFICATIONS_FILE" 2>/dev/null || true)
|
|
1020
|
+
: "${CAN_INFER_COUNT:=0}"
|
|
1021
|
+
TOTAL_ITEMS=$((MUST_CLARIFY_COUNT + SUGGESTED_CLARIFY_COUNT + CAN_INFER_COUNT))
|
|
1022
|
+
```
|
|
1023
|
+
|
|
1024
|
+
b. **Display clarification summary**:
|
|
1025
|
+
```markdown
|
|
1026
|
+
## Specification Created ✓
|
|
1027
|
+
|
|
1028
|
+
**Spec File**: [SPEC_FILE path]
|
|
1029
|
+
**Clarifications**: [specs/clarifications.md path]
|
|
1030
|
+
|
|
1031
|
+
**Clarification Summary**:
|
|
1032
|
+
- [Must Clarify]: M items
|
|
1033
|
+
- [Suggested Clarify]: N items
|
|
1034
|
+
- [Can Infer]: K items
|
|
1035
|
+
- **Total**: TOTAL items
|
|
1036
|
+
```
|
|
1037
|
+
|
|
1038
|
+
c. **Auto-invoke clarify if [Must Clarify] items exist**:
|
|
1039
|
+
```bash
|
|
1040
|
+
if [[ $MUST_CLARIFY_COUNT -gt 0 ]]; then
|
|
1041
|
+
echo ""
|
|
1042
|
+
echo "🔄 Found $MUST_CLARIFY_COUNT [Must Clarify] items"
|
|
1043
|
+
echo " Automatically starting clarification process..."
|
|
1044
|
+
echo ""
|
|
1045
|
+
echo " AI will ask questions one by one, integrating answers into spec.md"
|
|
1046
|
+
echo " You can also type 'skip' to skip specific questions"
|
|
1047
|
+
echo ""
|
|
1048
|
+
|
|
1049
|
+
# Auto-invoke clarify (interactive mode)
|
|
1050
|
+
/specpro-clarify
|
|
1051
|
+
|
|
1052
|
+
# After clarify completes, check if all [Must Clarify] items are resolved
|
|
1053
|
+
# ⚠️ **This is a BLOCK scan, not a line count** (`T244` / `ISS-217`, same round as the
|
|
1054
|
+
# counts above). Two things were wrong with the one-liner it replaces
|
|
1055
|
+
# (`grep -c "\[Must Clarify\]"`): ① it counted **mentions** (see above), and ② it
|
|
1056
|
+
# counted **entries**, not **unresolved** ones — so with any must-clarify item at all,
|
|
1057
|
+
# `REMAINING_MUST` was **always > 0** and the `✅ All … resolved` branch below was
|
|
1058
|
+
# **unreachable**. The stated intent ("check if all … are resolved") needs each
|
|
1059
|
+
# item's own `**Status**:` line, which is a per-item block read.
|
|
1060
|
+
# ⚠️ The status anchor carries **`[[:space:]]*`** and that is not cosmetic: the
|
|
1061
|
+
# template emits the line **indented** (` **Status**: [ ]`), so a line-start anchor
|
|
1062
|
+
# matches **nothing** and the count comes back a constant 0 — a false "all resolved"
|
|
1063
|
+
# that is indistinguishable from a real one. (Measured while writing this: the first
|
|
1064
|
+
# version of this very fix shipped with the wrong anchor and reported 0 on a fixture
|
|
1065
|
+
# holding an unresolved item.)
|
|
1066
|
+
REMAINING_MUST=$(awk '
|
|
1067
|
+
/^- \*\*\[Must Clarify\] - / { in_item = 1; next }
|
|
1068
|
+
in_item && /^[[:space:]]*\*\*Status\*\*:/ { if ($0 ~ /\[[ ]\]/) unresolved++; in_item = 0 }
|
|
1069
|
+
END { print unresolved + 0 }
|
|
1070
|
+
' "$CLARIFICATIONS_FILE" 2>/dev/null || true)
|
|
1071
|
+
: "${REMAINING_MUST:=0}"
|
|
1072
|
+
|
|
1073
|
+
if [[ $REMAINING_MUST -gt 0 ]]; then
|
|
1074
|
+
echo ""
|
|
1075
|
+
echo "⚠️ Clarification completed with $REMAINING_MUST unresolved [Must Clarify] items"
|
|
1076
|
+
echo " These will be addressed during quality validation (Step 9)"
|
|
1077
|
+
echo " Proceeding to quality validation..."
|
|
1078
|
+
else
|
|
1079
|
+
echo ""
|
|
1080
|
+
echo "✅ All [Must Clarify] items resolved"
|
|
1081
|
+
echo " Proceeding to quality validation..."
|
|
1082
|
+
fi
|
|
1083
|
+
else
|
|
1084
|
+
echo ""
|
|
1085
|
+
echo "✓ No [Must Clarify] items found"
|
|
1086
|
+
echo " Clarifications.md contains only [Suggested Clarify] and [Can Infer] items"
|
|
1087
|
+
echo " These can be resolved manually or during quality validation"
|
|
1088
|
+
echo " Proceeding to quality validation..."
|
|
1089
|
+
fi
|
|
1090
|
+
```
|
|
1091
|
+
|
|
1092
|
+
d. **Inform user about next steps**:
|
|
1093
|
+
```markdown
|
|
1094
|
+
**What to Expect Next**:
|
|
1095
|
+
- Step 9: Quality validation (specpro.qc)
|
|
1096
|
+
* Validates spec.md against the checklist's quality criteria
|
|
1097
|
+
* Rebuilds specs/checklists/requirements.md from the template, every run
|
|
1098
|
+
* Queues every non-passing criterion into the [specify] section of the ledger
|
|
1099
|
+
* Reports a verdict and stops — no repair, no loop (`FR-058` / `FR-060`)
|
|
1100
|
+
* ⚠️ The queued entries are THIS command's to fix, on the next --review-issues run
|
|
1101
|
+
|
|
1102
|
+
- After validation: Git commit with spec.md + clarifications.md + requirements.md
|
|
1103
|
+
|
|
1104
|
+
- Final step: Ready for `/specpro-plan` (technical planning)
|
|
1105
|
+
```
|
|
1106
|
+
|
|
1107
|
+
**User Experience**:
|
|
1108
|
+
- **No user intervention required**: clarify is invoked automatically when needed
|
|
1109
|
+
- **Seamless flow**: User watches as clarify asks questions and updates spec.md
|
|
1110
|
+
- **No decision point**: No need to choose "clarify now or later"
|
|
1111
|
+
- **Flexibility preserved**: User can still type 'skip' during clarify to skip questions
|
|
1112
|
+
|
|
1113
|
+
**Backward Compatibility**:
|
|
1114
|
+
- If user manually runs `/specpro-clarify` before this step, it will detect no [Must Clarify] items and skip
|
|
1115
|
+
- If user wants to review spec.md first, they can run `/specpro-specify --update` later
|
|
1116
|
+
9. **Specification Quality Validation** 🆕 REDESIGNED with specpro.qc [MANDATORY - CRITICAL GATE]:
|
|
1117
|
+
|
|
1118
|
+
**Purpose**: Validate the FINAL spec.md (after all clarifications) against the checklist's quality criteria using automated qc command
|
|
1119
|
+
|
|
1120
|
+
⚠️ **CRITICAL: This step is MANDATORY and NON-SKIPPABLE**
|
|
1121
|
+
- Quality validation CANNOT be bypassed
|
|
1122
|
+
- QC execution is enforced automatically
|
|
1123
|
+
- requirements.md generation is verified post-QC
|
|
1124
|
+
- Workflow BLOCKS if quality validation fails
|
|
1125
|
+
|
|
1126
|
+
**Key Changes** ⚠️ [rewritten 2026-09-19: two of the five lines below described the
|
|
1127
|
+
*repair* behaviour that `FR-058` removed — a command document claiming its callee
|
|
1128
|
+
repairs is how the delegation stayed invisible for as long as it did]:
|
|
1129
|
+
- Quality validation is a dedicated `/specpro-qc` command, not inline logic here
|
|
1130
|
+
- **qc checks and reports; THIS command repairs** — the spec's producer is the only
|
|
1131
|
+
party that writes the spec (`FR-058`)
|
|
1132
|
+
- **qc queues what did not pass into the `[specify]` section** (`FR-059`) — which is
|
|
1133
|
+
this command's own queue, so the fix path stays inside one chain
|
|
1134
|
+
- qc runs **once**, rebuilds the checklist, and stops (`FR-060`); there is no repair
|
|
1135
|
+
loop and therefore no "converged / not feasible" verdict for it to pronounce
|
|
1136
|
+
- No "continue despite issues" option - quality is not compromisable
|
|
1137
|
+
- Post-QC verification ensures requirements.md is generated successfully
|
|
1138
|
+
|
|
1139
|
+
a. **Detect validation scope**:
|
|
1140
|
+
```bash
|
|
1141
|
+
if [[ "$UPDATE_MODE" = "false" ]]; then
|
|
1142
|
+
# Initial spec: Full validation
|
|
1143
|
+
VALIDATION_SCOPE="full"
|
|
1144
|
+
echo "📋 Running full quality validation (initial spec)..."
|
|
1145
|
+
else
|
|
1146
|
+
# Update spec: Validate only modified/added items
|
|
1147
|
+
ADDED_FR_COUNT=$(grep -B1 '\*\*Lifecycle\*\*:.*\[specify:create\]' "$SPEC_FILE" 2>/dev/null | grep -cE "FR-[0-9]+[A-E]?" || true)
|
|
1148
|
+
: "${ADDED_FR_COUNT:=0}"
|
|
1149
|
+
MODIFIED_FR_COUNT=$(grep -B1 '\*\*Lifecycle\*\*:.*\[specify:update\]' "$SPEC_FILE" 2>/dev/null | grep -cE "FR-[0-9]+[A-E]?" || true)
|
|
1150
|
+
: "${MODIFIED_FR_COUNT:=0}"
|
|
1151
|
+
|
|
1152
|
+
if [[ $ADDED_FR_COUNT -gt 0 ]] || [[ $MODIFIED_FR_COUNT -gt 0 ]]; then
|
|
1153
|
+
VALIDATION_SCOPE="incremental"
|
|
1154
|
+
echo "📋 Running incremental validation ($ADDED_FR_COUNT added, $MODIFIED_FR_COUNT modified)..."
|
|
1155
|
+
else
|
|
1156
|
+
VALIDATION_SCOPE="skip"
|
|
1157
|
+
echo "ℹ️ No new/modified FRs, skipping quality validation"
|
|
1158
|
+
echo "✓ Loading existing quality checklist..."
|
|
1159
|
+
# Skip to Step 10
|
|
1160
|
+
fi
|
|
1161
|
+
fi
|
|
1162
|
+
```
|
|
1163
|
+
|
|
1164
|
+
b. **Run /specpro-qc** 🆕:
|
|
1165
|
+
```bash
|
|
1166
|
+
if [[ "$VALIDATION_SCOPE" != "skip" ]]; then
|
|
1167
|
+
echo "📋 Starting quality validation with specpro.qc..."
|
|
1168
|
+
echo " Validating against the checklist's quality criteria..."
|
|
1169
|
+
echo " QC rebuilds the checklist and queues every non-passing item for THIS command."
|
|
1170
|
+
|
|
1171
|
+
# ⚠️ **FR-062's obligation is discharged HERE**: the check runs on the spec's
|
|
1172
|
+
# first creation and on every update, and the trigger is the spec's change —
|
|
1173
|
+
# not the caller's memory. ⚠️ And it is run **flag-less**, because the repair
|
|
1174
|
+
# is not the checker's to perform (FR-058): qc queues the non-passing items
|
|
1175
|
+
# into `[specify]`, and THIS command — the spec's producer — resolves them.
|
|
1176
|
+
/specpro-qc --scope="$VALIDATION_SCOPE"
|
|
1177
|
+
|
|
1178
|
+
QC_EXIT_CODE=$?
|
|
1179
|
+
|
|
1180
|
+
if [[ $QC_EXIT_CODE -eq 0 ]]; then
|
|
1181
|
+
echo ""
|
|
1182
|
+
echo "✅ Quality validation PASSED"
|
|
1183
|
+
echo " No criterion failed."
|
|
1184
|
+
echo " specs/checklists/requirements.md: rebuilt for this run"
|
|
1185
|
+
else
|
|
1186
|
+
echo ""
|
|
1187
|
+
echo "❌ Quality validation FAILED"
|
|
1188
|
+
echo " The non-passing criteria are now entries in the [specify] queue"
|
|
1189
|
+
echo " (FR-059) — this command's own queue, to be fixed by this command."
|
|
1190
|
+
echo ""
|
|
1191
|
+
echo "Check specs/checklists/requirements.md for the per-item verdicts."
|
|
1192
|
+
echo "Then run --review-issues to process the queued entries."
|
|
1193
|
+
exit 1
|
|
1194
|
+
fi
|
|
1195
|
+
else
|
|
1196
|
+
echo "✓ Quality validation skipped (no changes to validate)"
|
|
1197
|
+
fi
|
|
1198
|
+
```
|
|
1199
|
+
|
|
1200
|
+
c. **Post-QC Verification** 🆕 [MANDATORY - CRITICAL GATE]:
|
|
1201
|
+
```bash
|
|
1202
|
+
# This step verifies that QC successfully generated requirements.md
|
|
1203
|
+
# If verification fails, the workflow is BLOCKED
|
|
1204
|
+
|
|
1205
|
+
echo ""
|
|
1206
|
+
echo "📋 Post-QC Verification: Checking requirements.md generation..."
|
|
1207
|
+
|
|
1208
|
+
# Derive from the spec file, not from an absolute specs path: in a layout where the
|
|
1209
|
+
# specs directory is its own repository, an absolute path can resolve outside the
|
|
1210
|
+
# tree this run actually wrote to.
|
|
1211
|
+
SPECS_DIR="$(cd "$(dirname "$SPEC_FILE")" && pwd)"
|
|
1212
|
+
REQUIREMENTS_FILE="$SPECS_DIR/checklists/requirements.md"
|
|
1213
|
+
|
|
1214
|
+
if [[ ! -f "$REQUIREMENTS_FILE" ]]; then
|
|
1215
|
+
echo ""
|
|
1216
|
+
echo "❌ CRITICAL: requirements.md not found after QC execution"
|
|
1217
|
+
echo " Expected: $REQUIREMENTS_FILE"
|
|
1218
|
+
echo " Current: File not found"
|
|
1219
|
+
echo ""
|
|
1220
|
+
echo "This indicates a critical workflow failure."
|
|
1221
|
+
echo "Quality validation (QC) should have generated requirements.md but failed to do so."
|
|
1222
|
+
echo ""
|
|
1223
|
+
echo "Workflow is BLOCKED. Please diagnose the issue:"
|
|
1224
|
+
echo "1. Run /specpro-qc manually to check for errors"
|
|
1225
|
+
echo "2. Check if QC is generating requirements.md correctly"
|
|
1226
|
+
echo "3. Verify SPECS_DIR path: $SPECS_DIR"
|
|
1227
|
+
exit 1
|
|
1228
|
+
fi
|
|
1229
|
+
|
|
1230
|
+
# 完成度按「Status 取值」判定,不按复选框——模板即 `- [ ] **Status**: …`,
|
|
1231
|
+
# 「诚实跳过」与「未做」在复选框上完全同形(工具缺陷 #10)
|
|
1232
|
+
QC_UNRESOLVED=$(grep -E '^- \[[ x]\] \*\*Status\*\*:' "$REQUIREMENTS_FILE" 2>/dev/null \
|
|
1233
|
+
| grep -vE '\*\*Status\*\*:[[:space:]]*(x|⊘[[:space:]]*Skipped)[[:space:]]*$' || true)
|
|
1234
|
+
|
|
1235
|
+
if [ -n "$QC_UNRESOLVED" ]; then
|
|
1236
|
+
echo ""
|
|
1237
|
+
echo "❌ CRITICAL: requirements.md has unresolved items after QC execution"
|
|
1238
|
+
echo " Expected: every item is either 'x' (passed) or '⊘ Skipped'"
|
|
1239
|
+
echo ""
|
|
1240
|
+
echo "This indicates QC did not complete successfully."
|
|
1241
|
+
echo ""
|
|
1242
|
+
echo "Unresolved items:"
|
|
1243
|
+
echo "$QC_UNRESOLVED" | head -5
|
|
1244
|
+
echo ""
|
|
1245
|
+
echo "Workflow is BLOCKED. Please diagnose the issue:"
|
|
1246
|
+
echo "1. Run /specpro-qc manually to check for errors"
|
|
1247
|
+
echo "2. Check specs/checklists/requirements.md for details"
|
|
1248
|
+
exit 1
|
|
1249
|
+
fi
|
|
1250
|
+
|
|
1251
|
+
echo "✅ Post-QC Verification PASSED"
|
|
1252
|
+
echo " requirements.md exists with every item marked [x]"
|
|
1253
|
+
echo " Quality validation artifact verified successfully"
|
|
1254
|
+
```
|
|
1255
|
+
|
|
1256
|
+
**What specpro.qc does** ⚠️ [corrected 2026-09-19 — every line below used to describe
|
|
1257
|
+
the *repair* behaviour that `FR-058` removed]:
|
|
1258
|
+
- Validates spec.md against the checklist's quality criteria:
|
|
1259
|
+
* P0: Content Quality (2 items) - No implementation details, functional not technical
|
|
1260
|
+
* P0: Completeness (3 items) - Mandatory sections, acceptance scenarios, scenarios are functional
|
|
1261
|
+
* P1: Consistency (4 items) - US consistency, FR consistency, US→FR mapping, reference integrity
|
|
1262
|
+
* P1: Traceability (2 items) - Constitution constraints, external dependencies
|
|
1263
|
+
- **Rebuilds** `specs/checklists/requirements.md` from the template, **every run** (`FR-060`)
|
|
1264
|
+
- **Queues every non-passing criterion** into the `[specify]` section of
|
|
1265
|
+
`specs/implement_issues.md` (`FR-059`) — deduped by check id, so repeated runs do
|
|
1266
|
+
not flood the queue
|
|
1267
|
+
- **Reports a verdict and stops** (`FR-061`): passing means "no `✗`"; a `⊘ Skipped`
|
|
1268
|
+
item is neither a pass nor a failure
|
|
1269
|
+
- ⚠️ **It does NOT fix anything, does NOT write `spec.md`, does NOT touch
|
|
1270
|
+
`clarifications.md`, and does NOT loop** — see the write-surface note at the top of
|
|
1271
|
+
`commands/specpro.qc.md`. The repair is **this** command's job.
|
|
1272
|
+
|
|
1273
|
+
**No user intervention required**:
|
|
1274
|
+
- User does not need to call /specpro-qc manually
|
|
1275
|
+
- User does not need to fix issues manually
|
|
1276
|
+
- All interactions happen through clarify (AI asks questions)
|
|
1277
|
+
- If qc cannot resolve issues, project is marked as not feasible
|
|
1278
|
+
|
|
1279
|
+
10. **Create Git commit** (after writing spec.md, clarifications.md, and checklists/requirements.md):
|
|
1280
|
+
|
|
1281
|
+
a. **Resolve the repository that owns the spec artifacts** ⚠️:
|
|
1282
|
+
The artifacts this step commits may not live in the same repository as the project root. A project may keep its plan/task artifacts in a **separate repository** — e.g. a nested repo mounted at the specs directory — and a commit issued from the project root would then see them as untracked-ignored or not at all.
|
|
1283
|
+
|
|
1284
|
+
Derive the specs directory from the spec file itself, then resolve the owning repository from that path — do not assume the project root, and do not rely on a `SPECS_DIR` variable this command never sets:
|
|
1285
|
+
```bash
|
|
1286
|
+
SPECS_DIR="$(cd "$(dirname "$SPEC_FILE")" && pwd)"
|
|
1287
|
+
SPECS_REPO="$(git -C "$SPECS_DIR" rev-parse --show-toplevel 2>/dev/null)" || SPECS_REPO=""
|
|
1288
|
+
if [ -z "$SPECS_REPO" ]; then
|
|
1289
|
+
echo "ℹ️ The specs directory is not under version control — skipping commit"
|
|
1290
|
+
SPEC_COMMIT_SKIP=true
|
|
1291
|
+
else
|
|
1292
|
+
SPEC_COMMIT_SKIP=false
|
|
1293
|
+
fi
|
|
1294
|
+
|
|
1295
|
+
# Relative-path helper. Use the Python form directly — macOS `realpath(1)` has no
|
|
1296
|
+
# `--relative-to`.
|
|
1297
|
+
#
|
|
1298
|
+
# ⚠️ **`os.path.realpath` on BOTH sides is the whole point, not a nicety** (ISS-144):
|
|
1299
|
+
# `os.path.relpath` only compares literal prefixes. `git rev-parse --show-toplevel` returns
|
|
1300
|
+
# the PHYSICAL path (symlinks resolved) while `$SPECS_DIR` below comes from `cd … && pwd`
|
|
1301
|
+
# (logical) — so when a project is reached through a symlink the two do not share a prefix
|
|
1302
|
+
# and relpath walks ABOVE the repository. Reproduced: with `/tmp` → `/private/tmp`,
|
|
1303
|
+
# `relpath` produced `../../../../tmp/.../specs/spec.md` for a file inside the repo, and
|
|
1304
|
+
# `git add` then refused it as outside the repository. Resolving both sides collapses the
|
|
1305
|
+
# difference. ⚠️ Copied verbatim in `specpro.implement.md` and `specpro.test-implement.md`
|
|
1306
|
+
# — change all three together.
|
|
1307
|
+
relpath() { python3 -c 'import os,sys;print(os.path.relpath(os.path.realpath(sys.argv[2]),os.path.realpath(sys.argv[1])))' "$1" "$2"; }
|
|
1308
|
+
```
|
|
1309
|
+
🔴 **`SPECS_REPO=""` means STOP — and the empty string is a trap, not a sentinel.** `git -C ""` does **not** mean "no repository"; it means **the current directory**, so it silently resolves to whatever repository the cwd happens to be in. Verified 2026-09-18: `git -C "" rev-parse --show-toplevel` → the cwd's repository, **exit 0**. ⇒ With `SPECS_REPO=""`, every step below would run against the **wrong** repository while the line above says "skipping" — the failure is not an error but a **silent commit into the wrong repo**. **If `SPEC_COMMIT_SKIP=true`, do NOT run steps 10b–10e at all.** Report it and end this step: the artifacts stay uncommitted, which is the honest outcome (**FR-030** — 明确报告并跳过,而非以一个不解析的路径失败).
|
|
1310
|
+
|
|
1311
|
+
All git commands below run against `$SPECS_REPO`, not `$REPO_ROOT`. Each artifact path is expressed **relative to that repository** — never as a literal `specs/...` path, which is only correct when the project root *is* the owning repository:
|
|
1312
|
+
```bash
|
|
1313
|
+
SPEC_FILE_REL="$(relpath "$SPECS_REPO" "$SPEC_FILE")"
|
|
1314
|
+
```
|
|
1315
|
+
|
|
1316
|
+
b. **Check if in Git repository** — ⛔ **GUARD FIRST: if `$SPEC_COMMIT_SKIP` is `true`, skip steps 10b–10e entirely** (see 10a's trap note — an empty `$SPECS_REPO` makes every `git -C` below act on the cwd's repository):
|
|
1317
|
+
```bash
|
|
1318
|
+
[ "${SPEC_COMMIT_SKIP:-false}" = true ] && echo "⛔ SPEC_COMMIT_SKIP — steps 10b–10e are skipped (FR-030)"
|
|
1319
|
+
# ...and do not run the lines below when that printed.
|
|
1320
|
+
if git -C "$SPECS_REPO" rev-parse --git-dir > /dev/null 2>&1; then
|
|
1321
|
+
IN_GIT_REPO=true
|
|
1322
|
+
else
|
|
1323
|
+
IN_GIT_REPO=false
|
|
1324
|
+
fi
|
|
1325
|
+
```
|
|
1326
|
+
|
|
1327
|
+
c. **Check if spec.md has changes**:
|
|
1328
|
+
```bash
|
|
1329
|
+
if git -C "$SPECS_REPO" diff --quiet -- "$SPEC_FILE_REL" 2>/dev/null; then
|
|
1330
|
+
echo "ℹ️ No changes to spec.md, skipping commit"
|
|
1331
|
+
else
|
|
1332
|
+
SPEC_CHANGED=true
|
|
1333
|
+
fi
|
|
1334
|
+
```
|
|
1335
|
+
|
|
1336
|
+
d. **Generate commit message**:
|
|
1337
|
+
```bash
|
|
1338
|
+
if [ "$UPDATE_MODE" = true ]; then
|
|
1339
|
+
# For updates, include version and summary
|
|
1340
|
+
CHANGE_SUMMARY=$(grep "^### Changes Overview" "$SPEC_FILE" -A 10 | head -15)
|
|
1341
|
+
COMMIT_MSG="spec: update to v${SPEC_VERSION}
|
|
1342
|
+
|
|
1343
|
+
${CHANGE_SUMMARY}"
|
|
1344
|
+
else
|
|
1345
|
+
# For initial spec
|
|
1346
|
+
COMMIT_MSG="spec: initial specification v${SPEC_VERSION}"
|
|
1347
|
+
fi
|
|
1348
|
+
```
|
|
1349
|
+
|
|
1350
|
+
e. **Create commit** (in the owning repository, with paths relative to it) — ⛔ **the write step, wrapped so an unowned path cannot reach it**:
|
|
1351
|
+
```bash
|
|
1352
|
+
if [ "${SPEC_COMMIT_SKIP:-false}" = true ]; then
|
|
1353
|
+
echo "⛔ refused: no owning repository — nothing is committed (FR-030)"
|
|
1354
|
+
else
|
|
1355
|
+
git -C "$SPECS_REPO" add -- \
|
|
1356
|
+
"$SPEC_FILE_REL" \
|
|
1357
|
+
"$(relpath "$SPECS_REPO" "$SPECS_DIR/clarifications.md")" \
|
|
1358
|
+
"$(relpath "$SPECS_REPO" "$SPECS_DIR/checklists/requirements.md")"
|
|
1359
|
+
git -C "$SPECS_REPO" commit -m "$COMMIT_MSG"
|
|
1360
|
+
fi
|
|
1361
|
+
```
|
|
1362
|
+
⚠️ **The `if` is the point, not decoration**: it makes "commit into the wrong repository" **unreachable by construction** rather than dependent on the reader having followed the prose in 10a. Branching on a flag set where the repository was resolved is what FR-030 asks for — 明确报告并跳过,而不是继续走到一个解析不出来的路径上。
|
|
1363
|
+
⚠️ If the artifacts span **more than one repository** (e.g. the spec lives in one repo and a checklist in another), issue **one commit per repository**, each with a message describing that repository's change. A single commit cannot span repositories.
|
|
1364
|
+
|
|
1365
|
+
f. **Optional: Create tag**:
|
|
1366
|
+
```bash
|
|
1367
|
+
echo "Consider creating a tag for this version:"
|
|
1368
|
+
echo " git tag -a v${SPEC_VERSION}-spec -m \"Specification v${SPEC_VERSION}\""
|
|
1369
|
+
```
|
|
1370
|
+
|
|
1371
|
+
g. **Commit successful message**:
|
|
1372
|
+
```markdown
|
|
1373
|
+
✓ Specification committed successfully
|
|
1374
|
+
|
|
1375
|
+
**Commit Hash**: [commit hash]
|
|
1376
|
+
**Message**: $COMMIT_MSG
|
|
1377
|
+
|
|
1378
|
+
**Documents Committed**:
|
|
1379
|
+
- specs/spec.md (specification)
|
|
1380
|
+
- specs/clarifications.md (clarification questions)
|
|
1381
|
+
- specs/checklists/requirements.md (quality validation)
|
|
1382
|
+
|
|
1383
|
+
**Clarification Summary**:
|
|
1384
|
+
- [Must Clarify]: M items
|
|
1385
|
+
- [Suggested Clarify]: N items
|
|
1386
|
+
- [Can Infer]: K items
|
|
1387
|
+
|
|
1388
|
+
**Quality Validation Summary** (fields verbatim from `specs/checklists/requirements.md`):
|
|
1389
|
+
- Status: [PASS | BLOCK]
|
|
1390
|
+
- Total Items: X
|
|
1391
|
+
- Passed: Y
|
|
1392
|
+
- Skipped: S
|
|
1393
|
+
- Failed: Z
|
|
1394
|
+
|
|
1395
|
+
**Important**:
|
|
1396
|
+
- If you ran clarification, your spec.md has been significantly updated
|
|
1397
|
+
- Please review specs/spec.md to verify the changes
|
|
1398
|
+
- Please review specs/checklists/requirements.md for quality issues
|
|
1399
|
+
|
|
1400
|
+
**Upstream status reconciliation** ⚠️ [MANDATORY before any "ready to continue" — Constraint 6 / FR-045]
|
|
1401
|
+
|
|
1402
|
+
**"Ready to continue" is a claim about the whole chain, not about the stage speaking.** So
|
|
1403
|
+
before this report states any "can continue / ready" conclusion, it MUST **read** each upstream
|
|
1404
|
+
stage's pending status on the entries that stage produced, and **state** what it found. The
|
|
1405
|
+
over-declaration this replaces: `/specpro-tasks` closed with "✅ Ready to continue
|
|
1406
|
+
implementation" while the design side had not taken a single step (`specs/plan.md` → Constraint 6).
|
|
1407
|
+
|
|
1408
|
+
**Two things, neither optional**:
|
|
1409
|
+
|
|
1410
|
+
1. **Say it when the status is clean too.** "Nothing was mentioned" and "nothing is pending"
|
|
1411
|
+
are different statements, and the first cannot be told apart from "forgot to look".
|
|
1412
|
+
2. **Skipped and processed counts are listed separately** — never merged into one "handled" number.
|
|
1413
|
+
|
|
1414
|
+
**Report shape** — every upstream gets a line, **including when it has nothing pending**
|
|
1415
|
+
(indented, not fenced: several of these blocks sit inside an enclosing fence, and a nested
|
|
1416
|
+
fence would close the outer one early):
|
|
1417
|
+
|
|
1418
|
+
Upstream reconciliation (Constraint 6)
|
|
1419
|
+
<upstream>: N pending · M processed
|
|
1420
|
+
Verdict: <clear to continue | upstream debts listed above>
|
|
1421
|
+
|
|
1422
|
+
⚠️ **Report — do not block.** The mechanism self-heals: an upstream that completes marks its
|
|
1423
|
+
downstream `update`. Blocking would need a judgement of "what counts as a debt", and that
|
|
1424
|
+
judgement *is* the downstream reading its upstreams — reporting is its only legitimate form.
|
|
1425
|
+
|
|
1426
|
+
⚠️ **Copied verbatim across `specpro.{specify,plan,tasks,test-plan,test-implement}.md`** (five
|
|
1427
|
+
sites; do not exist as one because each command document is deployed and read on its own).
|
|
1428
|
+
Only the "This stage's upstreams" line below differs per file — **change all five together**.
|
|
1429
|
+
|
|
1430
|
+
**This stage's upstreams**: `constitution` — pending means a constraint it states is not
|
|
1431
|
+
carried into this spec (`specs/constitution.md` → the constraint list).
|
|
1432
|
+
|
|
1433
|
+
**Next Steps**:
|
|
1434
|
+
1. Review specs/spec.md to verify the updates
|
|
1435
|
+
2. Review specs/checklists/requirements.md for quality issues
|
|
1436
|
+
3. Run /specpro-plan to create technical plan
|
|
1437
|
+
- Note: /specpro-plan will auto-invoke clarification if [Must Clarify] items remain
|
|
1438
|
+
```
|
|
1439
|
+
|
|
1440
|
+
---
|
|
1441
|
+
|
|
1442
|
+
## Protocol Codec Module Rules 🌐 [CONDITIONAL — FRs touching a wire format]
|
|
1443
|
+
|
|
1444
|
+
**Activation**: decided by the **Activation Gate** (`.specpro/templates/protocol-golden-bytes-guide.md` §6) — triggers T1/T2 (a byte-level protocol anchor or a named protocol in the requirement text) put an FR in scope for that field. "Partially protocol" is not a category: a requirement touching one wire field is in scope for that field. The verdict is recorded per §6.4, not left implicit — producing neither the artifacts nor an explicit not-active verdict is the failure the gate exists to catch. When activated, the FR text MUST meet the requirements below.
|
|
1445
|
+
|
|
1446
|
+
- A protocol FR MUST state three specifications explicitly: **stream semantics** (continuous single stream vs per-message independent stream); **byte-packing rules** (which layer owns bit-depth decisions, where dynamic branch selection happens, and known peer-implementation differences); and **peer diversity** (the measured list of differences between peer implementations).
|
|
1447
|
+
- The wording MUST NOT infer stream-internal byte counts by translating a parameter such as bits-per-pixel — stream-internal format follows what the encoder or peer actually emits, and every such claim MUST carry its source layer (L1 specification / L2 reference implementation / L3 captured stream; definitions in `.specpro/templates/protocol-golden-bytes-guide.md` §2).
|
|
1448
|
+
- The protocol-point inventory itself is a plan-layer design decision; the spec's job is to state the requirement that such an inventory exists.
|
|
1449
|
+
|