codeforge-dev 1.9.0 → 1.10.0

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.
Files changed (32) hide show
  1. package/.devcontainer/.env +3 -0
  2. package/.devcontainer/CHANGELOG.md +56 -0
  3. package/.devcontainer/CLAUDE.md +29 -8
  4. package/.devcontainer/README.md +61 -2
  5. package/.devcontainer/config/defaults/main-system-prompt.md +162 -128
  6. package/.devcontainer/config/defaults/rules/spec-workflow.md +10 -2
  7. package/.devcontainer/connect-external-terminal.sh +17 -17
  8. package/.devcontainer/devcontainer.json +143 -144
  9. package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/agents/architect.md +4 -3
  10. package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/agents/doc-writer.md +3 -3
  11. package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/agents/spec-writer.md +21 -11
  12. package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/hooks/hooks.json +1 -1
  13. package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/scripts/advisory-test-runner.py +186 -13
  14. package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/scripts/spec-reminder.py +2 -1
  15. package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/skills/documentation-patterns/SKILL.md +1 -1
  16. package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/skills/spec-check/SKILL.md +22 -10
  17. package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/skills/spec-init/SKILL.md +7 -5
  18. package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/skills/spec-init/references/backlog-template.md +19 -3
  19. package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/skills/spec-init/references/roadmap-template.md +28 -8
  20. package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/skills/spec-new/SKILL.md +15 -6
  21. package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/skills/spec-new/references/template.md +24 -5
  22. package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/skills/spec-refine/SKILL.md +194 -0
  23. package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/skills/spec-update/SKILL.md +19 -1
  24. package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/skills/specification-writing/SKILL.md +19 -12
  25. package/.devcontainer/scripts/check-setup.sh +24 -25
  26. package/.devcontainer/scripts/setup-aliases.sh +88 -76
  27. package/.devcontainer/scripts/setup-projects.sh +172 -131
  28. package/.devcontainer/scripts/setup-terminal.sh +48 -0
  29. package/.devcontainer/scripts/setup-update-claude.sh +49 -107
  30. package/.devcontainer/scripts/setup.sh +4 -17
  31. package/README.md +2 -2
  32. package/package.json +1 -1
@@ -2,9 +2,10 @@
2
2
  """
3
3
  Advisory test runner — Stop hook that injects test results as context.
4
4
 
5
- Detects the project's test framework and runs the test suite. Results are
6
- returned as additionalContext so Claude sees pass/fail info without being
7
- blocked. If tests fail, Claude's next response will naturally address them.
5
+ Reads the list of files edited this session (written by collect-edited-files.py),
6
+ maps them to affected test files, and runs only those tests. Skips entirely
7
+ if no files were edited. Results are returned as additionalContext so Claude
8
+ sees pass/fail info without being blocked.
8
9
 
9
10
  Reads hook input from stdin (JSON). Returns JSON on stdout.
10
11
  Always exits 0 (advisory, never blocking).
@@ -15,15 +16,37 @@ import os
15
16
  import subprocess
16
17
  import sys
17
18
 
19
+ TIMEOUT_SECONDS = 15
20
+
21
+
22
+ def get_edited_files(session_id: str) -> list[str]:
23
+ """Read the list of files edited this session.
24
+
25
+ Relies on collect-edited-files.py writing paths to a temp file.
26
+ Returns deduplicated list of paths that still exist on disk.
27
+ """
28
+ tmp_path = f"/tmp/claude-edited-files-{session_id}"
29
+ try:
30
+ with open(tmp_path, "r") as f:
31
+ raw = f.read()
32
+ except OSError:
33
+ return []
34
+
35
+ seen: set[str] = set()
36
+ result: list[str] = []
37
+ for line in raw.strip().splitlines():
38
+ path = line.strip()
39
+ if path and path not in seen and os.path.isfile(path):
40
+ seen.add(path)
41
+ result.append(path)
42
+ return result
43
+
18
44
 
19
45
  def detect_test_framework(cwd: str) -> tuple[str, list[str]]:
20
46
  """Detect which test framework is available in the project.
21
47
 
22
- Checks for: pytest, vitest, jest, mocha, go test, cargo test.
23
- Falls back to npm test if a test script is defined.
24
-
25
48
  Returns:
26
- Tuple of (framework_name, command_list) or ("", []) if none found.
49
+ Tuple of (framework_name, base_command) or ("", []) if none found.
27
50
  """
28
51
  try:
29
52
  entries = set(os.listdir(cwd))
@@ -102,7 +125,7 @@ def detect_test_framework(cwd: str) -> tuple[str, list[str]]:
102
125
 
103
126
  # --- Go ---
104
127
  if "go.mod" in entries:
105
- return ("go", ["go", "test", "./...", "-count=1"])
128
+ return ("go", ["go", "test", "-count=1"])
106
129
 
107
130
  # --- Rust ---
108
131
  if "Cargo.toml" in entries:
@@ -111,6 +134,139 @@ def detect_test_framework(cwd: str) -> tuple[str, list[str]]:
111
134
  return ("", [])
112
135
 
113
136
 
137
+ def resolve_pytest_tests(edited_files: list[str], cwd: str) -> tuple[list[str], bool]:
138
+ """Map edited Python files to their corresponding pytest test files.
139
+
140
+ Returns:
141
+ (test_files, run_all) — if run_all is True, run the whole suite
142
+ (e.g. conftest.py was edited).
143
+ """
144
+ test_files: list[str] = []
145
+
146
+ for path in edited_files:
147
+ if not path.endswith(".py"):
148
+ continue
149
+
150
+ basename = os.path.basename(path)
151
+
152
+ # conftest changes can affect anything — run full suite
153
+ if basename == "conftest.py":
154
+ return ([], True)
155
+
156
+ # Already a test file — include directly
157
+ if basename.startswith("test_") or "/tests/" in path:
158
+ if os.path.isfile(path):
159
+ test_files.append(path)
160
+ continue
161
+
162
+ # Map source → test via directory mirroring
163
+ # e.g. src/engine/db/sessions.py → tests/engine/db/test_sessions.py
164
+ # e.g. src/engine/api/routes/github.py → tests/engine/api/test_routes_github.py
165
+ rel = os.path.relpath(path, cwd)
166
+ parts = rel.split(os.sep)
167
+
168
+ # Strip leading "src/" if present
169
+ if parts and parts[0] == "src":
170
+ parts = parts[1:]
171
+
172
+ if not parts:
173
+ continue
174
+
175
+ module = parts[-1] # e.g. "sessions.py"
176
+ module_name = module.removesuffix(".py")
177
+ parent_parts = parts[:-1] # e.g. ["engine", "db"]
178
+
179
+ # Standard mapping: tests/<parent>/test_<module>.py
180
+ test_path = os.path.join(cwd, "tests", *parent_parts, f"test_{module_name}.py")
181
+ if os.path.isfile(test_path):
182
+ test_files.append(test_path)
183
+ continue
184
+
185
+ # Routes mapping: src/engine/api/routes/github.py
186
+ # → tests/engine/api/test_routes_github.py
187
+ if len(parent_parts) >= 2 and parent_parts[-1] == "routes":
188
+ route_test = os.path.join(
189
+ cwd,
190
+ "tests",
191
+ *parent_parts[:-1],
192
+ f"test_routes_{module_name}.py",
193
+ )
194
+ if os.path.isfile(route_test):
195
+ test_files.append(route_test)
196
+
197
+ # Deduplicate while preserving order
198
+ seen: set[str] = set()
199
+ unique: list[str] = []
200
+ for t in test_files:
201
+ if t not in seen:
202
+ seen.add(t)
203
+ unique.append(t)
204
+
205
+ return (unique, False)
206
+
207
+
208
+ def resolve_affected_tests(
209
+ edited_files: list[str], cwd: str, framework: str
210
+ ) -> tuple[list[str], bool]:
211
+ """Resolve edited files to framework-specific test arguments.
212
+
213
+ Returns:
214
+ (extra_args, run_all) — extra_args to append to the base command.
215
+ If run_all is True, run the whole suite (no extra args needed).
216
+ If extra_args is empty and run_all is False, skip testing entirely.
217
+ """
218
+ if framework == "pytest":
219
+ test_files, run_all = resolve_pytest_tests(edited_files, cwd)
220
+ return (test_files, run_all)
221
+
222
+ if framework == "vitest":
223
+ # vitest --related does dep-graph analysis natively
224
+ source_files = [
225
+ f
226
+ for f in edited_files
227
+ if not f.endswith(
228
+ (".md", ".json", ".yaml", ".yml", ".toml", ".txt", ".css")
229
+ )
230
+ ]
231
+ if not source_files:
232
+ return ([], False)
233
+ return (["--related"] + source_files, False)
234
+
235
+ if framework == "jest":
236
+ source_files = [
237
+ f
238
+ for f in edited_files
239
+ if not f.endswith(
240
+ (".md", ".json", ".yaml", ".yml", ".toml", ".txt", ".css")
241
+ )
242
+ ]
243
+ if not source_files:
244
+ return ([], False)
245
+ return (["--findRelatedTests"] + source_files, False)
246
+
247
+ if framework == "go":
248
+ # Map edited .go files to their package directories
249
+ pkgs: set[str] = set()
250
+ for path in edited_files:
251
+ if path.endswith(".go"):
252
+ pkg_dir = os.path.dirname(path)
253
+ rel = os.path.relpath(pkg_dir, cwd)
254
+ pkgs.add(f"./{rel}")
255
+ if not pkgs:
256
+ return ([], False)
257
+ return (sorted(pkgs), False)
258
+
259
+ # cargo, mocha, npm-test — no granular selection, run full suite
260
+ code_files = [
261
+ f
262
+ for f in edited_files
263
+ if not f.endswith((".md", ".json", ".yaml", ".yml", ".toml", ".txt"))
264
+ ]
265
+ if not code_files:
266
+ return ([], False)
267
+ return ([], True)
268
+
269
+
114
270
  def main():
115
271
  try:
116
272
  input_data = json.load(sys.stdin)
@@ -121,34 +277,51 @@ def main():
121
277
  if input_data.get("stop_hook_active"):
122
278
  sys.exit(0)
123
279
 
280
+ session_id = input_data.get("session_id", "")
281
+ if not session_id:
282
+ sys.exit(0)
283
+
284
+ # No files edited this session — nothing to test
285
+ edited_files = get_edited_files(session_id)
286
+ if not edited_files:
287
+ sys.exit(0)
288
+
124
289
  cwd = os.getcwd()
125
- framework, cmd = detect_test_framework(cwd)
290
+ framework, base_cmd = detect_test_framework(cwd)
126
291
 
127
292
  if not framework:
128
293
  sys.exit(0)
129
294
 
295
+ extra_args, run_all = resolve_affected_tests(edited_files, cwd, framework)
296
+
297
+ # No affected tests and not a run-all situation — skip
298
+ if not extra_args and not run_all:
299
+ sys.exit(0)
300
+
301
+ cmd = base_cmd + extra_args
302
+
130
303
  try:
131
304
  result = subprocess.run(
132
305
  cmd,
133
306
  cwd=cwd,
134
307
  capture_output=True,
135
308
  text=True,
136
- timeout=60,
309
+ timeout=TIMEOUT_SECONDS,
137
310
  )
138
311
  except subprocess.TimeoutExpired:
139
312
  json.dump(
140
- {"additionalContext": f"[Tests] {framework} timed out after 60s"},
313
+ {
314
+ "additionalContext": f"[Tests] {framework} timed out after {TIMEOUT_SECONDS}s"
315
+ },
141
316
  sys.stdout,
142
317
  )
143
318
  sys.exit(0)
144
319
  except (FileNotFoundError, OSError):
145
- # Test runner not installed or not accessible
146
320
  sys.exit(0)
147
321
 
148
322
  output = (result.stdout + "\n" + result.stderr).strip()
149
323
 
150
324
  if result.returncode == 0:
151
- # Extract test count from output if possible
152
325
  json.dump(
153
326
  {"additionalContext": f"[Tests] All tests passed ({framework})"},
154
327
  sys.stdout,
@@ -110,7 +110,8 @@ def main():
110
110
  f"[Spec Reminder] Code was modified in {dirs_str} "
111
111
  "but no specs were updated. "
112
112
  "Use /spec-update to update the relevant spec, "
113
- "or /spec-new if no spec exists for this feature."
113
+ "/spec-new if no spec exists for this feature, "
114
+ "or /spec-refine if the spec is still in draft status."
114
115
  )
115
116
 
116
117
  json.dump({"additionalContext": message}, sys.stdout)
@@ -65,7 +65,7 @@ Development setup, how to run tests, how to submit changes. Link to CONTRIBUTING
65
65
 
66
66
  ## Sizing Rules
67
67
 
68
- Documentation files consumed by AI tools (CLAUDE.md, specs, architecture docs) should be **≤200 lines** each. Split large documents by concern. Each file should be independently useful.
68
+ Documentation files consumed by AI tools (CLAUDE.md, specs, architecture docs) should aim for **~200 lines** each. Split large documents by concern when practical. Each file should be independently useful.
69
69
 
70
70
  For human-facing docs (README, API reference), there is no hard limit, but prefer shorter docs that link to detailed sub-pages over monolithic documents.
71
71
 
@@ -38,9 +38,11 @@ For each spec file, extract:
38
38
  - **Version** from the `**Version:**` field
39
39
  - **Status** from the `**Status:**` field
40
40
  - **Last Updated** from the `**Last Updated:**` field
41
+ - **Approval** from the `**Approval:**` field (default `draft` if missing)
41
42
  - **Line count** (wc -l)
42
43
  - **Sections present** — check for each required section header
43
44
  - **Acceptance criteria** — count total, count checked `[x]`
45
+ - **Requirements** — count total, count `[assumed]`, count `[user-approved]`
44
46
  - **Discrepancies** — check if section has content
45
47
 
46
48
  ### Step 3: Flag Issues
@@ -49,13 +51,17 @@ For each spec, check these conditions:
49
51
 
50
52
  | Issue | Condition | Severity |
51
53
  |-------|-----------|----------|
54
+ | **Unapproved** | Approval is `draft` or missing | High |
55
+ | **Assumed requirements** | Has requirements tagged `[assumed]` | Medium |
52
56
  | **Stale** | Status is `planned` but Last Updated is >30 days ago | High |
53
57
  | **Incomplete** | Missing required sections (Intent, Acceptance Criteria, Key Files, Requirements, Out of Scope) | High |
54
- | **Oversized** | Exceeds 200 lines | Medium |
58
+ | **Long spec** | Exceeds ~200 lines — consider splitting | Info |
55
59
  | **No criteria** | Acceptance Criteria section is empty or has no checkboxes | High |
56
60
  | **Open discrepancies** | Discrepancies section has content | Medium |
57
61
  | **Missing as-built** | Status is `implemented` but Implementation Notes is empty | Medium |
58
62
  | **Stale paths** | Key Files references paths that don't exist | Low |
63
+ | **Draft + implemented** | Status is `implemented` but Approval is `draft` — approval gate was bypassed | High |
64
+ | **Inconsistent approval** | Approval is `user-approved` but spec has `[assumed]` requirements | High |
59
65
 
60
66
  ### Step 4: Report
61
67
 
@@ -64,11 +70,11 @@ Output a summary table:
64
70
  ```
65
71
  ## Spec Health Report
66
72
 
67
- | Feature | Version | Status | Updated | Lines | Issues |
68
- |---------|---------|--------|---------|-------|--------|
69
- | Session History | v0.2.0 | implemented | 2026-02-08 | 74 | None |
70
- | Auth Flow | v0.3.0 | planned | 2026-01-15 | 45 | Stale (26 days) |
71
- | Settings Page | v0.2.0 | partial | 2026-02-05 | 210 | Oversized |
73
+ | Feature | Version | Status | Approval | Updated | Lines | Issues |
74
+ |---------|---------|--------|----------|---------|-------|--------|
75
+ | Session History | v0.2.0 | implemented | user-approved | 2026-02-08 | 74 | None |
76
+ | Auth Flow | v0.3.0 | planned | draft | 2026-01-15 | 45 | Unapproved, Stale (26 days) |
77
+ | Settings Page | v0.2.0 | partial | draft | 2026-02-05 | 210 | Unapproved, Long spec |
72
78
 
73
79
  ## Issues Found
74
80
 
@@ -76,11 +82,17 @@ Output a summary table:
76
82
  - **Auth Flow** (`.specs/v0.3.0/auth-flow.md`): Status is `planned` but last updated 26 days ago. Either implementation is stalled or the spec needs an as-built update.
77
83
 
78
84
  ### Medium Priority
79
- - **Settings Page** (`.specs/v0.2.0/settings-page.md`): 210 lines exceeds the 200-line limit. Split into sub-specs.
85
+ - **Settings Page** (`.specs/v0.2.0/settings-page.md`): 210 lines consider splitting into sub-specs for easier consumption.
80
86
 
81
87
  ### Suggested Actions
82
- 1. Run `/spec-update auth-flow` to update the auth flow spec
83
- 2. Split settings-page.md into sub-specs
88
+ 1. Run `/spec-refine auth-flow` to validate assumptions and get user approval
89
+ 2. Run `/spec-update auth-flow` to update the auth flow spec
90
+ 3. Split settings-page.md into sub-specs
91
+
92
+ ### Approval Summary
93
+ - **User-approved:** 1 spec
94
+ - **Draft (needs /spec-refine):** 2 specs
95
+ - **Assumed requirements across all specs:** 8
84
96
  ```
85
97
 
86
- If no issues are found, report: "All specs healthy. N specs across M versions."
98
+ If no issues are found, report: "All specs healthy. N specs across M versions. All user-approved."
@@ -67,18 +67,20 @@ Created:
67
67
  - `.specs/BACKLOG.md` — deferred items list
68
68
 
69
69
  Next steps:
70
- - Use `/spec-new <feature-name> <version>` to create your first feature spec
70
+ - Add features to `BACKLOG.md` with priority grades (P0–P3)
71
+ - Pull features into a version in `ROADMAP.md` when ready to scope
72
+ - Use `/spec-new <feature-name> <version>` to create a spec
73
+ - Use `/spec-refine <feature-name>` to validate before implementation
74
+ - After implementing, use `/spec-update` to close the loop
71
75
  - Use `/spec-check` to audit spec health at any time
72
76
  ```
73
77
 
74
78
  ---
75
79
 
76
- ## Hard Constraints
80
+ ## Constraints
77
81
 
78
82
  - **Never overwrite** an existing `.specs/` directory or its contents.
79
- - **ROADMAP.md** must stay under 30 lines (it's a summary, not a plan document).
80
- - **BACKLOG.md** must stay under 15 lines (it grows as items are added).
81
- - Templates are starting points — the user will extend them.
83
+ - Templates are starting points the user will extend them as the project grows.
82
84
 
83
85
  ---
84
86
 
@@ -1,7 +1,23 @@
1
1
  # Backlog
2
2
 
3
- Deferred items not yet scheduled for a version.
3
+ Priority-graded feature and infrastructure backlog. Items are pulled into versions when ready to scope and spec. See `ROADMAP.md` for the versioning workflow.
4
4
 
5
- ## Items
5
+ ## P0 — High Priority
6
6
 
7
- - [ ] [Item description] — [context/rationale]
7
+ - [ ] [Feature] — [Description]
8
+
9
+ ## P1 — Important
10
+
11
+ - [ ] [Feature] — [Description]
12
+
13
+ ## P2 — Desired
14
+
15
+ - [ ] [Feature] — [Description]
16
+
17
+ ## P3 — Nice to Have
18
+
19
+ - [ ] [Feature] — [Description]
20
+
21
+ ## Infrastructure & CI
22
+
23
+ - [ ] [Item] — [Description]
@@ -1,13 +1,33 @@
1
1
  # Roadmap
2
2
 
3
- What each version delivers and why.
3
+ > Features live in the priority-graded backlog until pulled into a version.
4
+ > Versions are scoped and spec'd when ready to build — not pre-assigned.
5
+ > See `BACKLOG.md` for the feature backlog.
4
6
 
5
- | Version | Status | Summary |
6
- |---------|--------|---------|
7
- | v0.1.0 | planned | [Initial release — describe core features] |
7
+ ## How Versioning Works
8
8
 
9
- ## Versioning
9
+ 1. **Backlog** — All desired features live in `BACKLOG.md`, graded by priority.
10
+ 2. **Version scoping** — When ready to start a new version, pull features from the backlog.
11
+ 3. **Spec first** — Each feature in a version gets a spec before implementation begins.
12
+ 4. **Ship** — Version is done when all its specs are implemented and verified.
10
13
 
11
- - **Major**: Breaking changes to public APIs or data models
12
- - **Minor**: New features, non-breaking changes
13
- - **Patch**: Bug fixes, documentation, internal refactors
14
+ Only the **next version** is defined in detail. Everything else is backlog.
15
+
16
+ ## Released
17
+
18
+ _None yet._
19
+
20
+ ## Current
21
+
22
+ ### v0.1.0 — [Name] 🔧
23
+
24
+ - [ ] [Feature pulled from backlog]
25
+ - [ ] [Feature pulled from backlog]
26
+
27
+ ## Next
28
+
29
+ > Scoped from `BACKLOG.md` when current version is complete.
30
+
31
+ ## Out of Scope
32
+
33
+ - [Items explicitly not planned]
@@ -14,7 +14,7 @@ version: 0.1.0
14
14
 
15
15
  A specification is a contract between the person requesting a feature and the person building it. Writing the spec BEFORE implementation forces you to think through edge cases, acceptance criteria, and scope boundaries while changes are cheap — before any code exists.
16
16
 
17
- Every project uses `.specs/` as the specification directory. Specs are version-organized, independently loadable, and capped at 200 lines.
17
+ Every project uses `.specs/` as the specification directory. Specs are version-organized, independently loadable, and should aim for ~200 lines.
18
18
 
19
19
  ---
20
20
 
@@ -30,6 +30,8 @@ If arguments are missing, ask the user for:
30
30
  1. Feature name (what is being built)
31
31
  2. Target version (which release this belongs to)
32
32
 
33
+ **Note:** Features should be pulled from the project's backlog (`BACKLOG.md`) into a version before creating a spec. If the feature isn't in the backlog yet, add it first, then assign it to a version.
34
+
33
35
  ### Step 2: Determine File Path
34
36
 
35
37
  - **Multi-feature version** (directory already exists or multiple features planned):
@@ -49,6 +51,7 @@ Pre-fill:
49
51
  - **Version**: from arguments
50
52
  - **Status**: `planned`
51
53
  - **Last Updated**: today's date (YYYY-MM-DD)
54
+ - **Approval**: `draft`
52
55
  - **Feature name**: from arguments
53
56
 
54
57
  Leave all other sections as placeholders for the user to fill.
@@ -62,24 +65,30 @@ After creating the file, guide the user through filling it out:
62
65
  3. **Key Files** — Glob the codebase to identify existing files relevant to this feature
63
66
  4. **Schema / Data Model** — Reference file paths only, never inline schemas
64
67
  5. **API Endpoints** — Table format: Method | Path | Description
65
- 6. **Requirements** — EARS format, numbered FR-1, FR-2, NFR-1, etc.
68
+ 6. **Requirements** — EARS format, numbered FR-1, FR-2, NFR-1, etc. Tag all requirements `[assumed]` at creation time — they become `[user-approved]` only after explicit user validation via `/spec-refine`.
66
69
  7. **Dependencies** — What this feature depends on
67
70
  8. **Out of Scope** — Explicit non-goals to prevent scope creep
71
+ 9. **Resolved Questions** — Leave empty at creation; populated by `/spec-refine`
68
72
 
69
73
  ### Step 5: Validate
70
74
 
71
75
  Before finishing:
72
- - [ ] File is 200 lines
76
+ - [ ] If the file exceeds ~200 lines, consider splitting into sub-specs
73
77
  - [ ] No source code, SQL, or type definitions reproduced inline
74
- - [ ] Status is `planned`
78
+ - [ ] Status is `planned` and Approval is `draft`
75
79
  - [ ] All required sections present (even if some are "N/A" or "TBD")
76
80
  - [ ] Acceptance criteria are testable
81
+ - [ ] All requirements are tagged `[assumed]`
82
+
83
+ After validation, inform the user: **"This spec MUST go through `/spec-refine` before implementation begins.** All requirements are marked `[assumed]` until explicitly validated."
84
+
85
+ The `/spec-refine` skill walks through every `[assumed]` requirement with the user, validates tech decisions and scope boundaries, and upgrades approved items to `[user-approved]`. The spec's `**Approval:**` becomes `user-approved` only after all requirements pass review.
77
86
 
78
87
  ---
79
88
 
80
- ## Hard Constraints
89
+ ## Sizing Guidelines
81
90
 
82
- - **≤200 lines per spec.** If a feature needs more, split into sub-specs with a parent `_overview.md` (≤50 lines) linking them.
91
+ - **Aim for ~200 lines per spec.** If a feature needs more, consider splitting into sub-specs with a parent `_overview.md` linking them.
83
92
  - **Reference, don't reproduce.** Write `see src/engine/db/migrations/002.sql lines 48-70` — never paste the SQL.
84
93
  - **Independently loadable.** Each spec file must be useful without loading any other file.
85
94
  - **EARS format for requirements.** Use the `specification-writing` skill for templates and examples.
@@ -12,6 +12,7 @@ Standard template for all feature specifications. Copy this structure when creat
12
12
  **Version:** v0.X.0
13
13
  **Status:** planned
14
14
  **Last Updated:** YYYY-MM-DD
15
+ **Approval:** draft
15
16
 
16
17
  ## Intent
17
18
 
@@ -56,14 +57,14 @@ Standard template for all feature specifications. Copy this structure when creat
56
57
 
57
58
  ### Functional Requirements
58
59
 
59
- - FR-1: [EARS format requirement — see specification-writing skill for templates]
60
- - FR-2: When [event], the system shall [action].
61
- - FR-3: If [unwanted condition], then the system shall [action].
60
+ - FR-1 [assumed]: [EARS format requirement — see specification-writing skill for templates]
61
+ - FR-2 [assumed]: When [event], the system shall [action].
62
+ - FR-3 [assumed]: If [unwanted condition], then the system shall [action].
62
63
 
63
64
  ### Non-Functional Requirements
64
65
 
65
- - NFR-1: The system shall respond to [endpoint] within [N]ms at the [percentile] percentile.
66
- - NFR-2: [Security, accessibility, scalability requirement]
66
+ - NFR-1 [assumed]: The system shall respond to [endpoint] within [N]ms at the [percentile] percentile.
67
+ - NFR-2 [assumed]: [Security, accessibility, scalability requirement]
67
68
 
68
69
  ## Dependencies
69
70
 
@@ -75,6 +76,10 @@ Standard template for all feature specifications. Copy this structure when creat
75
76
  - [Explicit non-goal 1 — prevents scope creep]
76
77
  - [Explicit non-goal 2]
77
78
 
79
+ ## Resolved Questions
80
+
81
+ [Decisions explicitly approved by the user via `/spec-refine`. Each entry: decision topic, chosen option, date, brief rationale.]
82
+
78
83
  ## Implementation Notes
79
84
 
80
85
  [Post-implementation only. Leave empty in planned specs. After building, document what actually shipped vs. what was planned.]
@@ -108,3 +113,17 @@ Standard template for all feature specifications. Copy this structure when creat
108
113
  | `planned` | Spec written, implementation not started |
109
114
  | `partial` | Some acceptance criteria implemented, work ongoing |
110
115
  | `implemented` | All acceptance criteria met, as-built notes complete |
116
+
117
+ ## Approval Workflow
118
+
119
+ | Tag | Meaning |
120
+ |-----|---------|
121
+ | `[assumed]` | Requirement was drafted by AI or inferred — treated as a hypothesis |
122
+ | `[user-approved]` | Requirement was explicitly reviewed and approved by the user via `/spec-refine` |
123
+
124
+ | Approval Status | Meaning |
125
+ |-----------------|---------|
126
+ | `draft` | Spec has unvalidated assumptions — NOT approved for implementation |
127
+ | `user-approved` | All requirements are `[user-approved]` — ready for implementation |
128
+
129
+ **Workflow:** `/spec-new` creates → `/spec-refine` validates → implementation begins → `/spec-update` closes the loop.