@specship/specship-win32-arm64 0.24.0 → 0.28.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.
- package/bin/specship.cmd +31 -1
- package/lib/dist/activation/cheatsheet.d.ts +17 -1
- package/lib/dist/activation/cheatsheet.d.ts.map +1 -1
- package/lib/dist/activation/cheatsheet.js +47 -3
- package/lib/dist/activation/cheatsheet.js.map +1 -1
- package/lib/dist/activation/hook-output.d.ts +80 -0
- package/lib/dist/activation/hook-output.d.ts.map +1 -0
- package/lib/dist/activation/hook-output.js +103 -0
- package/lib/dist/activation/hook-output.js.map +1 -0
- package/lib/dist/activation/search-intercept.d.ts +60 -29
- package/lib/dist/activation/search-intercept.d.ts.map +1 -1
- package/lib/dist/activation/search-intercept.js +159 -55
- package/lib/dist/activation/search-intercept.js.map +1 -1
- package/lib/dist/activation/steering.d.ts +11 -13
- package/lib/dist/activation/steering.d.ts.map +1 -1
- package/lib/dist/activation/steering.js +25 -10
- package/lib/dist/activation/steering.js.map +1 -1
- package/lib/dist/behaviour/behaviour-surface.d.ts +47 -0
- package/lib/dist/behaviour/behaviour-surface.d.ts.map +1 -1
- package/lib/dist/behaviour/behaviour-surface.js +108 -2
- package/lib/dist/behaviour/behaviour-surface.js.map +1 -1
- package/lib/dist/bin/node-version-check.d.ts +22 -7
- package/lib/dist/bin/node-version-check.d.ts.map +1 -1
- package/lib/dist/bin/node-version-check.js +39 -9
- package/lib/dist/bin/node-version-check.js.map +1 -1
- package/lib/dist/bin/specship.js +989 -116
- package/lib/dist/bin/specship.js.map +1 -1
- package/lib/dist/commands/specship/learn.md +27 -3
- package/lib/dist/commands/specship/spec.md +85 -15
- package/lib/dist/db/spec-queries.d.ts +73 -3
- package/lib/dist/db/spec-queries.d.ts.map +1 -1
- package/lib/dist/db/spec-queries.js +139 -5
- package/lib/dist/db/spec-queries.js.map +1 -1
- package/lib/dist/db/sqlite-adapter.d.ts +7 -5
- package/lib/dist/db/sqlite-adapter.d.ts.map +1 -1
- package/lib/dist/db/sqlite-adapter.js +11 -9
- package/lib/dist/db/sqlite-adapter.js.map +1 -1
- package/lib/dist/enforce/changed-files.d.ts +39 -0
- package/lib/dist/enforce/changed-files.d.ts.map +1 -0
- package/lib/dist/enforce/changed-files.js +114 -0
- package/lib/dist/enforce/changed-files.js.map +1 -0
- package/lib/dist/enforce/enforce.d.ts +53 -2
- package/lib/dist/enforce/enforce.d.ts.map +1 -1
- package/lib/dist/enforce/enforce.js +95 -8
- package/lib/dist/enforce/enforce.js.map +1 -1
- package/lib/dist/enforce/mutation.d.ts +46 -0
- package/lib/dist/enforce/mutation.d.ts.map +1 -0
- package/lib/dist/enforce/mutation.js +104 -0
- package/lib/dist/enforce/mutation.js.map +1 -0
- package/lib/dist/enforce/suppressions.d.ts +33 -0
- package/lib/dist/enforce/suppressions.d.ts.map +1 -0
- package/lib/dist/enforce/suppressions.js +159 -0
- package/lib/dist/enforce/suppressions.js.map +1 -0
- package/lib/dist/extraction/grammars.d.ts +10 -0
- package/lib/dist/extraction/grammars.d.ts.map +1 -1
- package/lib/dist/extraction/grammars.js +32 -3
- package/lib/dist/extraction/grammars.js.map +1 -1
- package/lib/dist/extraction/index.d.ts +7 -0
- package/lib/dist/extraction/index.d.ts.map +1 -1
- package/lib/dist/extraction/index.js.map +1 -1
- package/lib/dist/extraction/specs/markdown-spec-extractor.d.ts +7 -0
- package/lib/dist/extraction/specs/markdown-spec-extractor.d.ts.map +1 -1
- package/lib/dist/extraction/specs/markdown-spec-extractor.js +97 -10
- package/lib/dist/extraction/specs/markdown-spec-extractor.js.map +1 -1
- package/lib/dist/extraction/wasm/tree-sitter-pascal.wasm.gz +0 -0
- package/lib/dist/extraction/wasm/tree-sitter-scala.wasm.gz +0 -0
- package/lib/dist/graph/spec-coverage.d.ts +142 -0
- package/lib/dist/graph/spec-coverage.d.ts.map +1 -0
- package/lib/dist/graph/spec-coverage.js +276 -0
- package/lib/dist/graph/spec-coverage.js.map +1 -0
- package/lib/dist/health/smoke-check.js +2 -2
- package/lib/dist/health/smoke-check.js.map +1 -1
- package/lib/dist/index.d.ts +75 -7
- package/lib/dist/index.d.ts.map +1 -1
- package/lib/dist/index.js +191 -23
- package/lib/dist/index.js.map +1 -1
- package/lib/dist/installer/gemini-commands.d.ts +107 -0
- package/lib/dist/installer/gemini-commands.d.ts.map +1 -0
- package/lib/dist/installer/gemini-commands.js +264 -0
- package/lib/dist/installer/gemini-commands.js.map +1 -0
- package/lib/dist/installer/index.d.ts +55 -8
- package/lib/dist/installer/index.d.ts.map +1 -1
- package/lib/dist/installer/index.js +72 -53
- package/lib/dist/installer/index.js.map +1 -1
- package/lib/dist/installer/init-offer.d.ts +14 -9
- package/lib/dist/installer/init-offer.d.ts.map +1 -1
- package/lib/dist/installer/init-offer.js +13 -9
- package/lib/dist/installer/init-offer.js.map +1 -1
- package/lib/dist/installer/install-defaults.d.ts +81 -0
- package/lib/dist/installer/install-defaults.d.ts.map +1 -0
- package/lib/dist/installer/install-defaults.js +95 -0
- package/lib/dist/installer/install-defaults.js.map +1 -0
- package/lib/dist/installer/instructions-template.d.ts +22 -0
- package/lib/dist/installer/instructions-template.d.ts.map +1 -1
- package/lib/dist/installer/instructions-template.js +37 -1
- package/lib/dist/installer/instructions-template.js.map +1 -1
- package/lib/dist/installer/targets/claude.d.ts +18 -0
- package/lib/dist/installer/targets/claude.d.ts.map +1 -1
- package/lib/dist/installer/targets/claude.js +70 -23
- package/lib/dist/installer/targets/claude.js.map +1 -1
- package/lib/dist/installer/targets/gemini.d.ts +85 -12
- package/lib/dist/installer/targets/gemini.d.ts.map +1 -1
- package/lib/dist/installer/targets/gemini.js +527 -30
- package/lib/dist/installer/targets/gemini.js.map +1 -1
- package/lib/dist/installer/targets/shared.d.ts +13 -0
- package/lib/dist/installer/targets/shared.d.ts.map +1 -1
- package/lib/dist/installer/targets/shared.js +22 -4
- package/lib/dist/installer/targets/shared.js.map +1 -1
- package/lib/dist/installer/targets/types.d.ts +12 -0
- package/lib/dist/installer/targets/types.d.ts.map +1 -1
- package/lib/dist/jira/auto-publish.d.ts +71 -4
- package/lib/dist/jira/auto-publish.d.ts.map +1 -1
- package/lib/dist/jira/auto-publish.js +186 -17
- package/lib/dist/jira/auto-publish.js.map +1 -1
- package/lib/dist/jira/reconcile.d.ts +24 -1
- package/lib/dist/jira/reconcile.d.ts.map +1 -1
- package/lib/dist/jira/reconcile.js +46 -2
- package/lib/dist/jira/reconcile.js.map +1 -1
- package/lib/dist/jira/repo-config.d.ts +9 -0
- package/lib/dist/jira/repo-config.d.ts.map +1 -1
- package/lib/dist/jira/repo-config.js +11 -0
- package/lib/dist/jira/repo-config.js.map +1 -1
- package/lib/dist/jira/types.d.ts +7 -0
- package/lib/dist/jira/types.d.ts.map +1 -1
- package/lib/dist/jira/types.js.map +1 -1
- package/lib/dist/knowledge/enforce.d.ts +75 -0
- package/lib/dist/knowledge/enforce.d.ts.map +1 -0
- package/lib/dist/knowledge/enforce.js +250 -0
- package/lib/dist/knowledge/enforce.js.map +1 -0
- package/lib/dist/knowledge/facts.d.ts +33 -0
- package/lib/dist/knowledge/facts.d.ts.map +1 -0
- package/lib/dist/knowledge/facts.js +117 -0
- package/lib/dist/knowledge/facts.js.map +1 -0
- package/lib/dist/knowledge/proposals.d.ts +88 -0
- package/lib/dist/knowledge/proposals.d.ts.map +1 -0
- package/lib/dist/knowledge/proposals.js +360 -0
- package/lib/dist/knowledge/proposals.js.map +1 -0
- package/lib/dist/knowledge/stale.d.ts +64 -0
- package/lib/dist/knowledge/stale.d.ts.map +1 -0
- package/lib/dist/knowledge/stale.js +174 -0
- package/lib/dist/knowledge/stale.js.map +1 -0
- package/lib/dist/mcp/fitness-tool.d.ts +5 -6
- package/lib/dist/mcp/fitness-tool.d.ts.map +1 -1
- package/lib/dist/mcp/fitness-tool.js +0 -16
- package/lib/dist/mcp/fitness-tool.js.map +1 -1
- package/lib/dist/mcp/gemini-session.d.ts +50 -0
- package/lib/dist/mcp/gemini-session.d.ts.map +1 -0
- package/lib/dist/mcp/gemini-session.js +159 -0
- package/lib/dist/mcp/gemini-session.js.map +1 -0
- package/lib/dist/mcp/health-tool.d.ts +26 -0
- package/lib/dist/mcp/health-tool.d.ts.map +1 -0
- package/lib/dist/mcp/health-tool.js +50 -0
- package/lib/dist/mcp/health-tool.js.map +1 -0
- package/lib/dist/mcp/jira-tools.d.ts.map +1 -1
- package/lib/dist/mcp/jira-tools.js +25 -0
- package/lib/dist/mcp/jira-tools.js.map +1 -1
- package/lib/dist/mcp/maintainability-tool.d.ts +5 -7
- package/lib/dist/mcp/maintainability-tool.d.ts.map +1 -1
- package/lib/dist/mcp/maintainability-tool.js +0 -16
- package/lib/dist/mcp/maintainability-tool.js.map +1 -1
- package/lib/dist/mcp/model-context.d.ts +31 -7
- package/lib/dist/mcp/model-context.d.ts.map +1 -1
- package/lib/dist/mcp/model-context.js +70 -21
- package/lib/dist/mcp/model-context.js.map +1 -1
- package/lib/dist/mcp/proxy.d.ts.map +1 -1
- package/lib/dist/mcp/proxy.js +5 -2
- package/lib/dist/mcp/proxy.js.map +1 -1
- package/lib/dist/mcp/server-instructions.d.ts +16 -15
- package/lib/dist/mcp/server-instructions.d.ts.map +1 -1
- package/lib/dist/mcp/server-instructions.js +72 -5
- package/lib/dist/mcp/server-instructions.js.map +1 -1
- package/lib/dist/mcp/session.js +1 -1
- package/lib/dist/mcp/session.js.map +1 -1
- package/lib/dist/mcp/spec-tools.d.ts +9 -1
- package/lib/dist/mcp/spec-tools.d.ts.map +1 -1
- package/lib/dist/mcp/spec-tools.js +282 -49
- package/lib/dist/mcp/spec-tools.js.map +1 -1
- package/lib/dist/mcp/tools.d.ts +49 -9
- package/lib/dist/mcp/tools.d.ts.map +1 -1
- package/lib/dist/mcp/tools.js +210 -58
- package/lib/dist/mcp/tools.js.map +1 -1
- package/lib/dist/pulse/pulse.d.ts +93 -0
- package/lib/dist/pulse/pulse.d.ts.map +1 -0
- package/lib/dist/pulse/pulse.js +401 -0
- package/lib/dist/pulse/pulse.js.map +1 -0
- package/lib/dist/reflect/apply.d.ts.map +1 -1
- package/lib/dist/reflect/apply.js +38 -3
- package/lib/dist/reflect/apply.js.map +1 -1
- package/lib/dist/reflect/auto-sweep.d.ts +26 -0
- package/lib/dist/reflect/auto-sweep.d.ts.map +1 -0
- package/lib/dist/reflect/auto-sweep.js +90 -0
- package/lib/dist/reflect/auto-sweep.js.map +1 -0
- package/lib/dist/reflect/cli.d.ts +62 -0
- package/lib/dist/reflect/cli.d.ts.map +1 -0
- package/lib/dist/reflect/cli.js +163 -0
- package/lib/dist/reflect/cli.js.map +1 -0
- package/lib/dist/reflect/miner.d.ts.map +1 -1
- package/lib/dist/reflect/miner.js +119 -0
- package/lib/dist/reflect/miner.js.map +1 -1
- package/lib/dist/reflect/sweep.d.ts +1 -0
- package/lib/dist/reflect/sweep.d.ts.map +1 -1
- package/lib/dist/reflect/sweep.js +62 -13
- package/lib/dist/reflect/sweep.js.map +1 -1
- package/lib/dist/reflect/targets.d.ts +2 -0
- package/lib/dist/reflect/targets.d.ts.map +1 -1
- package/lib/dist/reflect/targets.js +33 -0
- package/lib/dist/reflect/targets.js.map +1 -1
- package/lib/dist/reflect/types.d.ts +19 -2
- package/lib/dist/reflect/types.d.ts.map +1 -1
- package/lib/dist/resolution/spec-link-resolver.d.ts +44 -2
- package/lib/dist/resolution/spec-link-resolver.d.ts.map +1 -1
- package/lib/dist/resolution/spec-link-resolver.js +132 -0
- package/lib/dist/resolution/spec-link-resolver.js.map +1 -1
- package/lib/dist/resolution/test-title-links.d.ts +62 -0
- package/lib/dist/resolution/test-title-links.d.ts.map +1 -0
- package/lib/dist/resolution/test-title-links.js +97 -0
- package/lib/dist/resolution/test-title-links.js.map +1 -0
- package/lib/dist/search/query-parser.d.ts.map +1 -1
- package/lib/dist/search/query-parser.js +6 -1
- package/lib/dist/search/query-parser.js.map +1 -1
- package/lib/dist/server/project-registry.js +1 -1
- package/lib/dist/server/routes/claude.js +24 -43
- package/lib/dist/server/routes/graph.js +53 -7
- package/lib/dist/server/routes/knowledge.js +118 -0
- package/lib/dist/server/routes/mcp.js +4 -3
- package/lib/dist/server/routes/projects.js +4 -4
- package/lib/dist/server/routes/spec.js +49 -7
- package/lib/dist/server/server.js +31 -17
- package/lib/dist/skills/spec-author/SKILL.md +164 -0
- package/lib/dist/skills/spec-author/references/format.md +210 -0
- package/lib/dist/skills/spec-author/references/gap-questions.md +152 -0
- package/lib/dist/skills/spec-author/references/quality-rubric.md +125 -0
- package/lib/dist/skills/spec-author/references/review-checklist.md +175 -0
- package/lib/dist/spec-lint/lint.d.ts +82 -0
- package/lib/dist/spec-lint/lint.d.ts.map +1 -0
- package/lib/dist/spec-lint/lint.js +491 -0
- package/lib/dist/spec-lint/lint.js.map +1 -0
- package/lib/dist/spec-quality/criterion-ids.d.ts +13 -0
- package/lib/dist/spec-quality/criterion-ids.d.ts.map +1 -0
- package/lib/dist/spec-quality/criterion-ids.js +38 -0
- package/lib/dist/spec-quality/criterion-ids.js.map +1 -0
- package/lib/dist/spec-quality/evidence-kind.d.ts +54 -0
- package/lib/dist/spec-quality/evidence-kind.d.ts.map +1 -0
- package/lib/dist/spec-quality/evidence-kind.js +183 -0
- package/lib/dist/spec-quality/evidence-kind.js.map +1 -0
- package/lib/dist/spec-quality/link-persist.d.ts +65 -0
- package/lib/dist/spec-quality/link-persist.d.ts.map +1 -0
- package/lib/dist/spec-quality/link-persist.js +249 -0
- package/lib/dist/spec-quality/link-persist.js.map +1 -0
- package/lib/dist/spec-quality/plan-scope.d.ts +38 -0
- package/lib/dist/spec-quality/plan-scope.d.ts.map +1 -0
- package/lib/dist/spec-quality/plan-scope.js +129 -0
- package/lib/dist/spec-quality/plan-scope.js.map +1 -0
- package/lib/dist/spec-quality/postcheck.d.ts +85 -0
- package/lib/dist/spec-quality/postcheck.d.ts.map +1 -0
- package/lib/dist/spec-quality/postcheck.js +390 -0
- package/lib/dist/spec-quality/postcheck.js.map +1 -0
- package/lib/dist/spec-quality/readiness.d.ts +72 -0
- package/lib/dist/spec-quality/readiness.d.ts.map +1 -0
- package/lib/dist/spec-quality/readiness.js +226 -0
- package/lib/dist/spec-quality/readiness.js.map +1 -0
- package/lib/dist/statusline/index.js.map +1 -1
- package/lib/dist/statusline/render.d.ts +1 -1
- package/lib/dist/statusline/render.d.ts.map +1 -1
- package/lib/dist/statusline/render.js +3 -1
- package/lib/dist/statusline/render.js.map +1 -1
- package/lib/dist/ui/assets/index-BzuUWUAE.js +50 -0
- package/lib/dist/ui/index.html +1 -1
- package/lib/dist/verify/report-ingest.d.ts +88 -0
- package/lib/dist/verify/report-ingest.d.ts.map +1 -0
- package/lib/dist/verify/report-ingest.js +269 -0
- package/lib/dist/verify/report-ingest.js.map +1 -0
- package/lib/dist/workflows/agents/claude.d.ts +16 -0
- package/lib/dist/workflows/agents/claude.d.ts.map +1 -0
- package/lib/dist/workflows/agents/claude.js +82 -0
- package/lib/dist/workflows/agents/claude.js.map +1 -0
- package/lib/dist/workflows/agents/gemini.d.ts +21 -0
- package/lib/dist/workflows/agents/gemini.d.ts.map +1 -0
- package/lib/dist/workflows/agents/gemini.js +124 -0
- package/lib/dist/workflows/agents/gemini.js.map +1 -0
- package/lib/dist/workflows/agents/index.d.ts +28 -0
- package/lib/dist/workflows/agents/index.d.ts.map +1 -0
- package/lib/dist/workflows/agents/index.js +104 -0
- package/lib/dist/workflows/agents/index.js.map +1 -0
- package/lib/dist/workflows/agents/types.d.ts +55 -0
- package/lib/dist/workflows/agents/types.d.ts.map +1 -0
- package/lib/dist/workflows/agents/types.js +46 -0
- package/lib/dist/workflows/agents/types.js.map +1 -0
- package/lib/dist/workflows/batch.d.ts +63 -0
- package/lib/dist/workflows/batch.d.ts.map +1 -0
- package/lib/dist/workflows/batch.js +151 -0
- package/lib/dist/workflows/batch.js.map +1 -0
- package/lib/dist/workflows/defaults/claude-design-implement.yaml +3 -4
- package/lib/dist/workflows/defaults/spec-author.yaml +11 -9
- package/lib/dist/workflows/defaults/spec-fix.yaml +103 -12
- package/lib/dist/workflows/defaults/spec-implement-mixed.yaml +21 -193
- package/lib/dist/workflows/defaults/spec-implement.yaml +701 -41
- package/lib/dist/workflows/defaults/spec-verify.yaml +60 -9
- package/lib/dist/workflows/discovery.d.ts +13 -0
- package/lib/dist/workflows/discovery.d.ts.map +1 -1
- package/lib/dist/workflows/discovery.js +79 -17
- package/lib/dist/workflows/discovery.js.map +1 -1
- package/lib/dist/workflows/executor.d.ts +18 -0
- package/lib/dist/workflows/executor.d.ts.map +1 -1
- package/lib/dist/workflows/executor.js +130 -33
- package/lib/dist/workflows/executor.js.map +1 -1
- package/lib/dist/workflows/gc.d.ts +50 -0
- package/lib/dist/workflows/gc.d.ts.map +1 -0
- package/lib/dist/workflows/gc.js +120 -0
- package/lib/dist/workflows/gc.js.map +1 -0
- package/lib/dist/workflows/merge-queue.d.ts +64 -0
- package/lib/dist/workflows/merge-queue.d.ts.map +1 -0
- package/lib/dist/workflows/merge-queue.js +162 -0
- package/lib/dist/workflows/merge-queue.js.map +1 -0
- package/lib/dist/workflows/runners/approval.d.ts +1 -1
- package/lib/dist/workflows/runners/approval.d.ts.map +1 -1
- package/lib/dist/workflows/runners/approval.js +11 -2
- package/lib/dist/workflows/runners/approval.js.map +1 -1
- package/lib/dist/workflows/runners/bash.d.ts +15 -0
- package/lib/dist/workflows/runners/bash.d.ts.map +1 -1
- package/lib/dist/workflows/runners/bash.js +50 -1
- package/lib/dist/workflows/runners/bash.js.map +1 -1
- package/lib/dist/workflows/runners/prompt.d.ts +16 -41
- package/lib/dist/workflows/runners/prompt.d.ts.map +1 -1
- package/lib/dist/workflows/runners/prompt.js +55 -120
- package/lib/dist/workflows/runners/prompt.js.map +1 -1
- package/lib/dist/workflows/runners/types.d.ts +12 -0
- package/lib/dist/workflows/runners/types.d.ts.map +1 -1
- package/lib/dist/workflows/schemas/workflow.d.ts +17 -0
- package/lib/dist/workflows/schemas/workflow.d.ts.map +1 -1
- package/lib/dist/workflows/schemas/workflow.js +27 -0
- package/lib/dist/workflows/schemas/workflow.js.map +1 -1
- package/lib/node_modules/.package-lock.json +1 -1
- package/lib/package.json +8 -4
- package/package.json +5 -3
- package/lib/dist/extraction/wasm/tree-sitter-pascal.wasm +0 -0
- package/lib/dist/extraction/wasm/tree-sitter-scala.wasm +0 -0
- package/lib/dist/server/ingest/impact-query.js +0 -343
- package/lib/dist/server/routes/events.js +0 -152
- package/lib/dist/ui/assets/index-CjjaftDw.js +0 -50
- package/node.exe +0 -4
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
# Spec format reference
|
|
2
|
+
|
|
3
|
+
The SpecShip parser at `src/extraction/specs/markdown-spec-extractor.ts` accepts a strict Markdown subset. This file is the contract — if your draft doesn't match what's described here, `specship sync` will emit `spec_missing_id`, `spec_stranded_id`, or `spec_bad_frontmatter` errors and the spec won't enter the graph.
|
|
4
|
+
|
|
5
|
+
## File location
|
|
6
|
+
|
|
7
|
+
```
|
|
8
|
+
<project-root>/specs/<slug>.md
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
The directory is configurable but `specs/` is the default. One file can hold one document with many requirements; you don't need one file per REQ.
|
|
12
|
+
|
|
13
|
+
## Top-to-bottom anatomy
|
|
14
|
+
|
|
15
|
+
```markdown
|
|
16
|
+
--- ← YAML frontmatter (optional)
|
|
17
|
+
id: AUTH-DOC
|
|
18
|
+
title: Authentication
|
|
19
|
+
owner: security-team
|
|
20
|
+
priority: high
|
|
21
|
+
version: 1
|
|
22
|
+
---
|
|
23
|
+
|
|
24
|
+
<!-- id: AUTH-DOC --> ← embedded ID for the document
|
|
25
|
+
# Authentication ← H1 = document body
|
|
26
|
+
|
|
27
|
+
This document covers login, session ← document body text
|
|
28
|
+
management, and rate-limiting.
|
|
29
|
+
|
|
30
|
+
<!-- id: REQ-AUTH-001 --> ← embedded ID for a requirement
|
|
31
|
+
## Failed logins MUST be rate-limited ← H2 = requirement
|
|
32
|
+
|
|
33
|
+
The login endpoint rejects more than 5
|
|
34
|
+
failed attempts per IP per minute.
|
|
35
|
+
|
|
36
|
+
implementations: ← optional code-link block
|
|
37
|
+
- src/auth/login.ts:authenticate
|
|
38
|
+
- src/auth/rate-limit.ts:enforce
|
|
39
|
+
|
|
40
|
+
verifies: ← optional test-evidence block
|
|
41
|
+
- __tests__/auth-rate-limit.test.ts:rejectsSixthAttempt
|
|
42
|
+
|
|
43
|
+
## Acceptance ← unnumbered subheading, OK
|
|
44
|
+
<!-- id: REQ-AUTH-001.A1 --> ← embedded ID for an acceptance bullet
|
|
45
|
+
- A 6th failed attempt within 60 s from
|
|
46
|
+
the same IP returns 429 with `Retry-After`.
|
|
47
|
+
<!-- id: REQ-AUTH-001.A2 -->
|
|
48
|
+
- A successful login resets the counter
|
|
49
|
+
for that IP.
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
## The five hard rules
|
|
53
|
+
|
|
54
|
+
### 1. Every heading needs an embedded ID immediately above it
|
|
55
|
+
|
|
56
|
+
```markdown
|
|
57
|
+
<!-- id: REQ-FOO-001 --> ← required
|
|
58
|
+
## Title here ← heading
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
If you forget the marker, the parser emits `spec_missing_id` (severity `error`) and the spec doesn't index. The marker must be on the line immediately above the heading — blank lines or other content between them break the pairing.
|
|
62
|
+
|
|
63
|
+
### 2. Two ID comments in a row produces a stranded-ID warning
|
|
64
|
+
|
|
65
|
+
```markdown
|
|
66
|
+
<!-- id: REQ-A -->
|
|
67
|
+
<!-- id: REQ-B --> ← REQ-A becomes "stranded"
|
|
68
|
+
## Title
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
This is `spec_stranded_id` (severity `warning`). The first ID is lost. Don't paste IDs from old drafts without their headings.
|
|
72
|
+
|
|
73
|
+
### 3. Hierarchy is heading depth
|
|
74
|
+
|
|
75
|
+
Levels nest naturally: H2 is a child of H1, H3 is a child of H2 (or H1 if no H2 above it), etc. The document spec (`kind: 'document'`) sits above all of them.
|
|
76
|
+
|
|
77
|
+
Practical translation:
|
|
78
|
+
- `# H1` → typically the document body (or a single requirement if there's no other heading)
|
|
79
|
+
- `## H2` → requirements
|
|
80
|
+
- `### H3` → child requirements OR acceptance bullets parent
|
|
81
|
+
- `#### H4+` → child acceptance bullets (rarely used)
|
|
82
|
+
|
|
83
|
+
### 4. Acceptance bullets get their own ID markers
|
|
84
|
+
|
|
85
|
+
The convention is `<REQ-ID>.A1`, `.A2`, etc.:
|
|
86
|
+
|
|
87
|
+
```markdown
|
|
88
|
+
<!-- id: REQ-AUTH-001 -->
|
|
89
|
+
## Failed logins MUST be rate-limited
|
|
90
|
+
|
|
91
|
+
## Acceptance
|
|
92
|
+
<!-- id: REQ-AUTH-001.A1 -->
|
|
93
|
+
- A 6th failed attempt within 60 s ...
|
|
94
|
+
<!-- id: REQ-AUTH-001.A2 -->
|
|
95
|
+
- A successful login resets the counter ...
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
The `## Acceptance` heading is conventional but not required by the parser — it just helps the human reader. The ID markers on the bullets ARE required if you want them queryable as individual spec nodes.
|
|
99
|
+
|
|
100
|
+
### 5. Frontmatter ID is optional but useful
|
|
101
|
+
|
|
102
|
+
If you provide an `id:` field in YAML frontmatter, the document spec uses that ID. If you don't, the document only emits a node when an H1 with an embedded ID is present. Most files should declare frontmatter `id:` so the document is always addressable.
|
|
103
|
+
|
|
104
|
+
## IDs must be unique within the file
|
|
105
|
+
|
|
106
|
+
Two markers declaring the same ID do not error — the specs are stored with `INSERT OR REPLACE`, so the **LAST** occurrence overwrites the earlier ones. The earlier heading's title, body, and links are silently gone. Scan for accidental duplicates before writing the file (a common cause: copy-pasting a REQ block and forgetting to bump the number).
|
|
107
|
+
|
|
108
|
+
## YAML frontmatter fields
|
|
109
|
+
|
|
110
|
+
All optional except as noted. The parser reads any string-typed field; unknown keys become `metadata`.
|
|
111
|
+
|
|
112
|
+
| Field | Type | Notes |
|
|
113
|
+
|---|---|---|
|
|
114
|
+
| `id` | string | Document-level spec ID. Conventional: `<AREA>-DOC` (e.g. `AUTH-DOC`). |
|
|
115
|
+
| `title` | string | Human title. Falls back to the H1 if absent. |
|
|
116
|
+
| `owner` | string | Team or person. Convention: `security`, `payments-team`, `@alice`. |
|
|
117
|
+
| `priority` | string | Free-form. Convention: `high` / `medium` / `low`. |
|
|
118
|
+
| `version` | string \| number | Spec version. Convention: integer that increments on contract changes. |
|
|
119
|
+
| `format` | string | Defaults to `markdown`. Don't set unless you know why. |
|
|
120
|
+
| `depends_on` | string \| list | Spec IDs this document depends on. Accepts one id, a comma-separated list, or `[a, b]`. |
|
|
121
|
+
| `metadata` | object | Arbitrary nested object. Reserved for future indexing. |
|
|
122
|
+
|
|
123
|
+
Stop the frontmatter with `---` on its own line. The parser tolerates a missing trailing `---` but emits `spec_bad_frontmatter` as a warning.
|
|
124
|
+
|
|
125
|
+
## The link blocks: `implementations:` and `verifies:`
|
|
126
|
+
|
|
127
|
+
Both blocks live in the **body of a requirement** (not at the file level), and both use the same bullet syntax. They differ only in the kind of link they create.
|
|
128
|
+
|
|
129
|
+
```markdown
|
|
130
|
+
<!-- id: REQ-FOO-001 -->
|
|
131
|
+
## Some requirement
|
|
132
|
+
|
|
133
|
+
Body text...
|
|
134
|
+
|
|
135
|
+
implementations:
|
|
136
|
+
- src/foo/bar.ts:doThing
|
|
137
|
+
- src/foo/baz.ts:Baz.handle
|
|
138
|
+
|
|
139
|
+
verifies:
|
|
140
|
+
- __tests__/foo.test.ts:doThingRejectsEmptyInput
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
Shared syntax rules:
|
|
144
|
+
|
|
145
|
+
- Each bullet is `<file-path>:<qualified-symbol>`.
|
|
146
|
+
- File paths are project-root-relative.
|
|
147
|
+
- Qualified symbols use dots: `Class.method`, `Class.staticMethod`, `module.function`.
|
|
148
|
+
- The pattern `[-*]\s+([^\s:]+)\s*:\s*([A-Za-z0-9_$.]+)` is what the parser matches.
|
|
149
|
+
- A bullet that has no `:symbol` (a bare path — e.g. a pointer at a slash-command markdown file) creates **no edge**. It's tolerated, doesn't end the block, and emits a `spec_bare_path_ref` warning naming the bullet. Use it only when there genuinely is no code symbol to point at.
|
|
150
|
+
- Only the FIRST block of each keyword in a requirement body is read. A block ends at the first non-blank, non-bullet line.
|
|
151
|
+
|
|
152
|
+
### `implementations:` → link kind `implements`
|
|
153
|
+
|
|
154
|
+
This block is the **backstop** for spec-to-code links. The agent SHOULD call `specship_link_assert` programmatically after implementing the spec — but if it forgets, the `implementations:` block re-asserts the link on every index. Either way, the link enters state `implemented`.
|
|
155
|
+
|
|
156
|
+
Leave `implementations:` empty (or omit it) in a fresh spec — the agent fills it in via `link_assert` once the code lands. Specify it up-front when you have a strong sense of where the implementation will live.
|
|
157
|
+
|
|
158
|
+
### `verifies:` → link kind `tests`
|
|
159
|
+
|
|
160
|
+
This is the **test-evidence** declaration, and it's what gates promotion from `implemented` to `verified`. A requirement with implementation code but no passing test evidence does not count as verified, no matter how complete the code is.
|
|
161
|
+
|
|
162
|
+
Point each bullet at a **real test symbol** — an exported test helper or a named test function. A bare `describe(...)` / `it(...)` string is not a symbol in the graph, so it can't be a link target; if the test file has no addressable symbol, either name a helper function the test calls or leave `verifies:` out until there is one.
|
|
163
|
+
|
|
164
|
+
Omit `verifies:` from a spec being authored before the tests exist. Adding it speculatively produces an orphan link that pollutes the drift queue.
|
|
165
|
+
|
|
166
|
+
## RFC 2119 keywords in titles + bodies
|
|
167
|
+
|
|
168
|
+
Use **MUST / SHOULD / MAY** in the requirement title and body. The parser doesn't validate these, but the review checklist does. Conventional usage:
|
|
169
|
+
|
|
170
|
+
| Keyword | Meaning |
|
|
171
|
+
|---|---|
|
|
172
|
+
| **MUST** | Required for compliance. Test failure means the spec is broken. |
|
|
173
|
+
| **SHOULD** | Strongly recommended. Deviation requires justification. |
|
|
174
|
+
| **MAY** | Optional. Choose freely; no compliance impact. |
|
|
175
|
+
| **MUST NOT** / **SHOULD NOT** | Negative forms. Same strength as positive. |
|
|
176
|
+
|
|
177
|
+
Avoid weasel words: "ought to", "could", "supports". They're un-testable and the review will flag them.
|
|
178
|
+
|
|
179
|
+
## Slug conventions
|
|
180
|
+
|
|
181
|
+
For the filename:
|
|
182
|
+
|
|
183
|
+
- **Kebab-case**, no spaces.
|
|
184
|
+
- **Feature name**, not date — git history records the when.
|
|
185
|
+
- **One file per logical area** when reasonable. `auth.md` is better than `auth-login.md` + `auth-logout.md` + `auth-rate-limit.md` if all three live under `AUTH-DOC`.
|
|
186
|
+
|
|
187
|
+
## What the parser explicitly DOES NOT support
|
|
188
|
+
|
|
189
|
+
- **No Markdown extensions** (footnotes, definition lists, task lists with `- [ ]`). They render fine, but the parser doesn't extract structure from them.
|
|
190
|
+
- **No HTML other than the `<!-- id: -->` comment.** Don't embed `<div>` or `<table>`.
|
|
191
|
+
- **No code blocks treated specially.** Triple-backtick fenced blocks render but are body content, not structure.
|
|
192
|
+
- **No links treated specially.** A `[REQ-AUTH-005](#req-auth-005)` link is rendered text, not a queryable cross-reference. Use the markers + heading text; the indexer's name-matcher handles cross-spec lookups.
|
|
193
|
+
- **No multi-file specs.** One spec file = one document. Cross-document parent references aren't supported (`depends_on` in frontmatter is the one cross-document relation).
|
|
194
|
+
|
|
195
|
+
If you find yourself wanting these, write a regular Markdown doc under `docs/` instead and let the spec layer focus on what it parses.
|
|
196
|
+
|
|
197
|
+
## Quick checklist before writing the file
|
|
198
|
+
|
|
199
|
+
- [ ] Frontmatter ID set (`id: <AREA>-DOC` or similar).
|
|
200
|
+
- [ ] Every heading has `<!-- id: ... -->` immediately above it.
|
|
201
|
+
- [ ] No two ID comments in a row (stranded ID).
|
|
202
|
+
- [ ] No ID declared twice in the file (the last one silently overwrites the first).
|
|
203
|
+
- [ ] Acceptance bullets each have their own `.A<N>` ID.
|
|
204
|
+
- [ ] `implementations:` paths (if specified) point at real symbols (verified via `specship_explore` or `specship_node`).
|
|
205
|
+
- [ ] `verifies:` bullets (if specified) point at real test symbols, not `describe`/`it` strings.
|
|
206
|
+
- [ ] RFC 2119 keywords used correctly (or not at all).
|
|
207
|
+
- [ ] No `<div>` / `<table>` / Markdown-extension syntax in the body.
|
|
208
|
+
- [ ] Filename is kebab-case, no date prefix, feature-named.
|
|
209
|
+
|
|
210
|
+
If all ten pass, the file will index cleanly.
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
# Gap-fill questions catalog
|
|
2
|
+
|
|
3
|
+
The user gave you a one-line feature description. Your draft fills in the obvious parts. This catalog is what you use to find the **non-obvious** parts you should ask about before writing the file.
|
|
4
|
+
|
|
5
|
+
## How to use this catalog
|
|
6
|
+
|
|
7
|
+
1. Read your draft.
|
|
8
|
+
2. For each category below, ask: "did the user's description or my draft cover this?"
|
|
9
|
+
3. If yes → move on; no question needed.
|
|
10
|
+
4. If no → either ask the user, or insert a `[needs user confirmation]` marker in the draft.
|
|
11
|
+
|
|
12
|
+
**Limit per round: 3–5 questions.** Don't dump the whole catalog on the user. Pick the ones that matter most for this spec; mark the rest as `[needs review]` and proceed.
|
|
13
|
+
|
|
14
|
+
The one exception is **Project invariants** at the end: always walk that section, because a spec that contradicts a house invariant gets rejected at review no matter how well-formed it is.
|
|
15
|
+
|
|
16
|
+
## The catalog
|
|
17
|
+
|
|
18
|
+
### Identity & ownership
|
|
19
|
+
|
|
20
|
+
| Q | Why it matters | Example phrasing |
|
|
21
|
+
|---|---|---|
|
|
22
|
+
| Owner team or person? | A spec without an owner drifts and nobody fixes it. | "Who owns this — `security`, `payments`, or someone else?" |
|
|
23
|
+
| Priority? | Helps when the drift queue is long. | "Priority — high, medium, low?" |
|
|
24
|
+
| Existing parent doc, or new one? | Decides whether to append or create a file. | "Should this go under your existing `AUTH-DOC`, or is this a new feature area?" |
|
|
25
|
+
|
|
26
|
+
### Trigger & scope
|
|
27
|
+
|
|
28
|
+
| Q | Why it matters | Example phrasing |
|
|
29
|
+
|---|---|---|
|
|
30
|
+
| What's the entry point? | An endpoint? A function call? A scheduled job? A webhook? | "Where does this kick off — REST endpoint, internal function, cron, webhook?" |
|
|
31
|
+
| Who/what can invoke it? | Authenticated users? System only? Admins? Anonymous? | "Who's allowed to invoke — authenticated only, admins, anonymous?" |
|
|
32
|
+
| Synchronous or async? | Affects the acceptance criteria around timing. | "Is the caller blocked waiting for the result, or is this fire-and-forget?" |
|
|
33
|
+
| Idempotent? | Critical for retry semantics. | "If the same input is sent twice, do we want the same result, or distinct ones?" |
|
|
34
|
+
|
|
35
|
+
### Success path
|
|
36
|
+
|
|
37
|
+
| Q | Why it matters | Example phrasing |
|
|
38
|
+
|---|---|---|
|
|
39
|
+
| What's the contract observable from outside? | This IS the spec. If you can't answer it, you don't have a spec yet. | "When this succeeds, what's the contract — the response shape, the side effects, the state change?" |
|
|
40
|
+
| What's the latency target, if any? | Often skipped, often relevant. | "Is there a latency expectation — sub-100ms, sub-second, eventually-consistent?" |
|
|
41
|
+
| Cache or memoize? | Affects idempotency and observability bullets. | "Should successful results be cached? For how long?" |
|
|
42
|
+
|
|
43
|
+
### Failure modes
|
|
44
|
+
|
|
45
|
+
| Q | Why it matters | Example phrasing |
|
|
46
|
+
|---|---|---|
|
|
47
|
+
| What happens on transient failure? | Retry semantics live here. | "If a downstream call fails transiently, do we retry, return an error, or queue?" |
|
|
48
|
+
| What happens on permanent failure? | The unhappy path is half the spec. | "If the operation can't succeed (validation fails, dependency dead), what does the caller see?" |
|
|
49
|
+
| What's the timeout? | Even "no timeout" is a decision worth stating. | "Is there a hard timeout? After how long?" |
|
|
50
|
+
| Partial-success semantics? | For multi-step operations. | "If step 3 of 5 fails, does the work roll back, or do we leave the partial state?" |
|
|
51
|
+
|
|
52
|
+
### Edge cases & ambiguity
|
|
53
|
+
|
|
54
|
+
| Q | Why it matters | Example phrasing |
|
|
55
|
+
|---|---|---|
|
|
56
|
+
| What about empty/null inputs? | Often unspecified, often the source of bugs. | "What happens on empty input — error, no-op, default?" |
|
|
57
|
+
| What about boundary values? | Off-by-one and rate-limit thresholds. | "Exactly 5 attempts allowed, or fewer-than-5?" |
|
|
58
|
+
| What about repeated identical inputs in a tight loop? | Race conditions. | "Two parallel requests with the same payload — same result, or do they race?" |
|
|
59
|
+
| What about very-large inputs? | Resource exhaustion. | "Is there a max payload size?" |
|
|
60
|
+
|
|
61
|
+
### Persistence & state
|
|
62
|
+
|
|
63
|
+
| Q | Why it matters | Example phrasing |
|
|
64
|
+
|---|---|---|
|
|
65
|
+
| Does this produce persisted state? | If yes, the schema is part of the contract. | "Does this write to a database / audit log / event store?" |
|
|
66
|
+
| TTL on persisted state? | Storage costs and privacy concerns. | "Do those records expire? After how long?" |
|
|
67
|
+
| Is the state queryable? | Determines whether queries are part of the spec. | "Are those records queryable by other services, or internal-only?" |
|
|
68
|
+
| Schema migration on existing data? | Hidden complexity. | "Does this affect any existing rows in the system?" |
|
|
69
|
+
|
|
70
|
+
### Security & access control
|
|
71
|
+
|
|
72
|
+
| Q | Why it matters | Example phrasing |
|
|
73
|
+
|---|---|---|
|
|
74
|
+
| AuthZ — who can call it? | Often implicit but should be stated. | "Authorization — what roles or scopes are required?" |
|
|
75
|
+
| Sensitive data in inputs/outputs? | Affects logging and storage requirements. | "Does this handle PII / payment data / secrets?" |
|
|
76
|
+
| Rate limiting required? | Even if "no", state it. | "Should this endpoint be rate-limited? Per-IP, per-user, per-tenant?" |
|
|
77
|
+
| Audit log requirement? | Compliance-relevant. | "Do we need an audit trail for this operation?" |
|
|
78
|
+
|
|
79
|
+
### Observability
|
|
80
|
+
|
|
81
|
+
| Q | Why it matters | Example phrasing |
|
|
82
|
+
|---|---|---|
|
|
83
|
+
| What metric should be emitted? | Counter / timer / gauge. | "What metric do we want — count, latency, error rate?" |
|
|
84
|
+
| What log lines? | Specific events to log. | "Any structured log events for this operation?" |
|
|
85
|
+
| What alert? | When does this wake somebody up? | "Should an alert fire if this fails repeatedly?" |
|
|
86
|
+
| Distributed tracing? | If your system uses it, the span name matters. | "Trace span name — what should it be?" |
|
|
87
|
+
|
|
88
|
+
### Compatibility & migration
|
|
89
|
+
|
|
90
|
+
| Q | Why it matters | Example phrasing |
|
|
91
|
+
|---|---|---|
|
|
92
|
+
| Backwards-compatible with prior version? | If you're changing existing behavior. | "Does this preserve the old behavior for existing callers?" |
|
|
93
|
+
| Migration path? | For schema or contract changes. | "How do existing consumers move to the new contract?" |
|
|
94
|
+
| Deprecation timeline? | If replacing something. | "Is the old endpoint deprecated? When does it sunset?" |
|
|
95
|
+
|
|
96
|
+
### Project invariants
|
|
97
|
+
|
|
98
|
+
Some projects carry invariants that a spec can violate without looking wrong. Walk this section against the **surface the spec touches** — these are questions to answer in the draft, not necessarily questions to put to the user.
|
|
99
|
+
|
|
100
|
+
In SpecShip itself (and in any project whose `CLAUDE.md` states equivalents), the surfaces are:
|
|
101
|
+
|
|
102
|
+
| Surface the spec touches | What the spec must account for |
|
|
103
|
+
|---|---|
|
|
104
|
+
| **MCP tools** (`src/mcp/`) — new tool, changed output, changed budget | Explore budgets are **monotonic**: a change may not shrink what a given budget returns. And `src/mcp/server-instructions.ts` is the **single source of truth** for agent-facing tool guidance — a spec that changes how the agent should use a tool must say that file is updated, not that guidance is duplicated elsewhere. |
|
|
105
|
+
| **Installer** (`src/installer/`) — new asset, new target, changed layout | Name **which targets** are affected (Claude Code is the only default target; Gemini CLI is opt-in and MCP-entry-only). Say how **markers** are handled, so install is idempotent and uninstall fully reverses. Require a **CHANGELOG entry** — installer regressions break every new install silently. |
|
|
106
|
+
| **Retrieval** — anything that changes how an answer is assembled | The budget is **tool-call count + wall-clock latency**, not tokens. A flow question must still resolve in 1–5 specship calls with zero Read/Grep. An acceptance bullet that lets the answer get slower or need more calls is a regression, however good the output looks. |
|
|
107
|
+
| **Extraction** (`src/extraction/`) — new language, new node/edge type | Use the **exact `NodeKind` / `EdgeKind` strings** from `src/types.ts`; inventing a kind silently drops the data. Any new SQL or grammar `.wasm` must be added to **`copy-assets`** or it won't ship in the package. |
|
|
108
|
+
|
|
109
|
+
Questions worth asking the user when the surface is ambiguous:
|
|
110
|
+
|
|
111
|
+
- "Does this change what an existing tool returns for the same input?" (→ monotonicity + a CHANGELOG-visible behavior change)
|
|
112
|
+
- "Should this ship on a default install, or behind an opt-in flag?" (→ installer tiering)
|
|
113
|
+
- "Is this allowed to cost an extra tool call to get a better answer?" (→ the retrieval budget; the answer is usually no)
|
|
114
|
+
|
|
115
|
+
## Decision framework — which to ask
|
|
116
|
+
|
|
117
|
+
You can't ask every question. Ranking:
|
|
118
|
+
|
|
119
|
+
1. **Trigger & scope** — without this, the spec has no boundary. **Always confirm.**
|
|
120
|
+
2. **Success path contract** — without this, there's nothing to test. **Always confirm.**
|
|
121
|
+
3. **Project invariants** — cheap to check, and a violation invalidates the spec. **Always walk.**
|
|
122
|
+
4. **Failure modes** — usually 2/3 of the spec body, often skipped by the user's initial description. **Ask if not covered.**
|
|
123
|
+
5. **Owner + priority** — quick to ask, valuable to record. **Ask if not in the description.**
|
|
124
|
+
6. **Edge cases** — pick the 1–2 most likely to be wrong; mark the rest `[needs review]`.
|
|
125
|
+
7. **Persistence, security, observability, compatibility** — ask only what the feature description suggests is relevant. Don't ask "is there a TTL" for a pure-function spec.
|
|
126
|
+
|
|
127
|
+
## Example application
|
|
128
|
+
|
|
129
|
+
User says: *"I want to add a spec for password reset via email."*
|
|
130
|
+
|
|
131
|
+
You draft a first pass covering: entry endpoint, success response, email content reference, link expiry. The catalog walk gives you:
|
|
132
|
+
|
|
133
|
+
- Trigger & scope — covered by "via email" (entry point inferred).
|
|
134
|
+
- Success path — covered.
|
|
135
|
+
- **Failure modes** — what if the email doesn't exist? rate-limited account? mailer down?
|
|
136
|
+
- **TTL** — how long is the link valid?
|
|
137
|
+
- **Rate limiting** — abuse vector if not rate-limited.
|
|
138
|
+
- **Sensitive data** — is the reset link a secret? logged anywhere?
|
|
139
|
+
- Observability — `[needs review]` (low priority for this spec).
|
|
140
|
+
- Compatibility — `[needs review]` (greenfield, no existing version).
|
|
141
|
+
- Owner — user said "auth team" implicitly via the feature name.
|
|
142
|
+
- Project invariants — no MCP / installer / retrieval / extraction surface touched; nothing to account for.
|
|
143
|
+
|
|
144
|
+
Your gap-fill round:
|
|
145
|
+
|
|
146
|
+
> Before I write the file, four things:
|
|
147
|
+
> 1. **Link TTL** — 1 hour? 24 hours? Until first use?
|
|
148
|
+
> 2. **Rate limit per email** — how many resets per day per address before we throttle?
|
|
149
|
+
> 3. **Non-existent email** — do we return success anyway (to prevent enumeration), or return 404?
|
|
150
|
+
> 4. **What's in the email** — link only, or also a code? Are there constraints on the link format (custom domain, length)?
|
|
151
|
+
|
|
152
|
+
That's 4 questions covering the high-impact gaps. The other catalog categories become `[needs review]` markers in the draft.
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
# Quality rubric
|
|
2
|
+
|
|
3
|
+
The spec parser is forgiving — it'll accept anything with the right markers. This rubric is what separates a spec that ships from a spec that wastes everyone's time. Apply it before writing the file.
|
|
4
|
+
|
|
5
|
+
## Seven properties of a good spec
|
|
6
|
+
|
|
7
|
+
### 1. Specifies the contract, not the implementation
|
|
8
|
+
|
|
9
|
+
A spec says **what**. Code says **how**. The split is load-bearing because:
|
|
10
|
+
|
|
11
|
+
- Implementation details belong to the team that owns the code, not the spec author. Forcing them through a spec process is friction.
|
|
12
|
+
- Specs that mirror implementation become brittle. Every refactor flags drift; the gate goes noisy; people stop trusting it.
|
|
13
|
+
- Implementation can evolve; the contract is what users depend on.
|
|
14
|
+
|
|
15
|
+
| ❌ Implementation-leak | ✅ Contract |
|
|
16
|
+
|---|---|
|
|
17
|
+
| MUST use bcrypt with cost factor 12 | Stored passwords MUST resist offline cracking with current GPU hardware |
|
|
18
|
+
| MUST be a JWT signed with RS256 and 24 h TTL | Session tokens MUST be cryptographically tamper-evident and expire within 24 h |
|
|
19
|
+
| MUST store events in PostgreSQL | Events MUST persist beyond process restart and remain queryable for ≥30 days |
|
|
20
|
+
| MUST use Redis for the counter | The rate-limit counter MUST survive a single-instance restart |
|
|
21
|
+
| MUST be at `POST /api/login` | MUST be addressable as a single POST endpoint at the auth-service boundary |
|
|
22
|
+
|
|
23
|
+
Test for implementation-leak: "Could I swap the implementation library tomorrow and still satisfy this?" If no, it's too prescriptive.
|
|
24
|
+
|
|
25
|
+
### 2. Testable acceptance criteria
|
|
26
|
+
|
|
27
|
+
Every acceptance bullet is something a test can verify mechanically. Vague verbs ("handles", "supports", "manages") are not testable.
|
|
28
|
+
|
|
29
|
+
| ❌ Untestable | ✅ Testable |
|
|
30
|
+
|---|---|
|
|
31
|
+
| Handles failed payments gracefully | A failed-payment webhook produces exactly one row in `billing_events` with `kind: 'retry_attempt'` |
|
|
32
|
+
| Validates user input | Email addresses without `@` return 400 with `code: invalid_email` |
|
|
33
|
+
| Supports concurrent requests | 100 concurrent requests to the same endpoint complete with no shared-state contamination (measured: distinct response IDs) |
|
|
34
|
+
| Logs everything | Every state transition appends a line to `audit.log` with `(timestamp, actor, before, after)` |
|
|
35
|
+
|
|
36
|
+
If you can't sketch how a test would assert against the bullet, rewrite it until you can. This matters twice over in SpecShip: the bullet is what a `verifies:` link's test has to actually assert, and an untestable bullet can never reach `verified`.
|
|
37
|
+
|
|
38
|
+
### 3. One concern per REQ
|
|
39
|
+
|
|
40
|
+
REQs are the unit of drift detection. When the implementation moves and a REQ's link goes `drifted`, you want to know exactly what to re-verify. A REQ that mixes concerns ("logs, validates, AND notifies on failure") means a small change to any one part flags the whole REQ — noise.
|
|
41
|
+
|
|
42
|
+
| ❌ Mixed concerns | ✅ Split |
|
|
43
|
+
|---|---|
|
|
44
|
+
| REQ-AUTH-001: Login MUST be rate-limited AND audit-logged | REQ-AUTH-001: Login MUST be rate-limited. + REQ-AUTH-002: Auth failures MUST be audit-logged. |
|
|
45
|
+
|
|
46
|
+
Test: read the REQ title. If you have to use "and" or "also", split.
|
|
47
|
+
|
|
48
|
+
### 4. RFC 2119 keyword used correctly
|
|
49
|
+
|
|
50
|
+
| Keyword | Use when |
|
|
51
|
+
|---|---|
|
|
52
|
+
| **MUST** / **MUST NOT** | Non-negotiable. Failing this is a bug or compliance breach. |
|
|
53
|
+
| **SHOULD** / **SHOULD NOT** | Strongly recommended. Deviation requires written justification. |
|
|
54
|
+
| **MAY** | Optional. Pure permission grant. No compliance impact either way. |
|
|
55
|
+
|
|
56
|
+
Common mistakes:
|
|
57
|
+
|
|
58
|
+
- **"MUST" inflation** — using MUST for everything dilutes the signal. Reserve it for things you genuinely won't ship without.
|
|
59
|
+
- **"SHOULD" hedging** — using SHOULD because you're nervous about MUST. Either it's required or it isn't; pick.
|
|
60
|
+
- **Weasel words mixed in** — "MUST ideally", "SHOULD probably", "ought to". Strike the modifiers.
|
|
61
|
+
|
|
62
|
+
### 5. Parent/child structure that mirrors the conceptual hierarchy
|
|
63
|
+
|
|
64
|
+
Document → requirements → acceptance bullets. Use heading depth to express this; don't flatten everything to H2.
|
|
65
|
+
|
|
66
|
+
| ❌ Flat | ✅ Structured |
|
|
67
|
+
|---|---|
|
|
68
|
+
| `## REQ-AUTH-001: Failed logins rate-limited` <br>`## REQ-AUTH-002: Rate limit returns 429` <br>`## REQ-AUTH-003: Rate limit has Retry-After header` <br>`## REQ-AUTH-004: Counter resets on success` | `# Authentication` <br>`## REQ-AUTH-001 Failed logins MUST be rate-limited` <br>`### Acceptance`<br>`- A1: returns 429` <br>`- A2: includes Retry-After` <br>`- A3: counter resets on success` |
|
|
69
|
+
|
|
70
|
+
The flat version makes drift detection useless (no concept of "this REQ has 4 acceptance criteria"); the structured version means changing the threshold is one drift event, not four.
|
|
71
|
+
|
|
72
|
+
### 6. Owner and priority set
|
|
73
|
+
|
|
74
|
+
Every spec needs:
|
|
75
|
+
- **Owner** — who decides on contract changes. A team, a person, or a role. "Engineering" is too broad.
|
|
76
|
+
- **Priority** — `high` / `medium` / `low` is fine. Helps when the drift queue is long.
|
|
77
|
+
|
|
78
|
+
These belong in the frontmatter:
|
|
79
|
+
|
|
80
|
+
```yaml
|
|
81
|
+
---
|
|
82
|
+
id: AUTH-DOC
|
|
83
|
+
owner: security-team
|
|
84
|
+
priority: high
|
|
85
|
+
---
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
A spec without an owner is a spec without a stakeholder. It'll drift and nobody will fix it.
|
|
89
|
+
|
|
90
|
+
### 7. Grounded in code that exists
|
|
91
|
+
|
|
92
|
+
If `mcp__specship__specship_explore` is available and the feature touches existing code, the draft references real symbols. Don't write:
|
|
93
|
+
|
|
94
|
+
```markdown
|
|
95
|
+
implementations:
|
|
96
|
+
- src/auth.ts:login
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
…if the actual code is at `src/services/auth-service.ts:AuthService.handleLogin`. The wrong path becomes a permanent orphan link, polluting the drift queue.
|
|
100
|
+
|
|
101
|
+
For greenfield work (no existing code yet), leave `implementations:` empty and omit `verifies:` entirely. The agent fills them in via `specship_link_assert` once the code and tests land.
|
|
102
|
+
|
|
103
|
+
## Anti-patterns
|
|
104
|
+
|
|
105
|
+
These are spec-author mistakes that produce specs the team will ignore.
|
|
106
|
+
|
|
107
|
+
- **Specs that read like product requirements.** "User clicks the login button and sees a friendly error message." Specs are contracts, not UX storyboards. Strip the narrative.
|
|
108
|
+
- **Specs that re-state the obvious.** "MUST not crash" applies to all code; it's not a spec, it's an axiom. Same for "MUST be secure" — too broad to test.
|
|
109
|
+
- **Specs that future-proof aggressively.** "MUST support OAuth, SAML, OIDC, magic link, and passkeys." If you only need email + password today, spec that. Extend the spec when the second method actually lands.
|
|
110
|
+
- **Specs written as TODO lists.** "MUST add a rate limiter, MUST write tests, MUST update docs." That's a task list, not a spec. The spec describes the contract after the work is done.
|
|
111
|
+
- **Specs that quote regulations verbatim.** Cite the regulation in the body, paraphrase the operational requirement in the acceptance bullets. Verbatim regulatory text is uninterpretable for testing.
|
|
112
|
+
|
|
113
|
+
## Sanity check before review
|
|
114
|
+
|
|
115
|
+
Answer these for your draft:
|
|
116
|
+
|
|
117
|
+
1. Could a teammate implement this in two different reasonable ways and both satisfy the spec? (If no, you've over-specified — implementation leak.)
|
|
118
|
+
2. Could a teammate read the acceptance criteria and write the tests without asking you what you meant? (If no, the criteria are vague.)
|
|
119
|
+
3. If a future refactor moves the linked symbol, does the spec still apply unchanged? (If no, the spec depends on implementation, not contract.)
|
|
120
|
+
4. Does each REQ title have one concern? (If you used "and", split.)
|
|
121
|
+
5. Is there at least one MUST and the right number of SHOULDs? (No MUSTs = the spec isn't required. All MUSTs = MUST is meaningless.)
|
|
122
|
+
6. Owner and priority in frontmatter? (If not, add.)
|
|
123
|
+
7. Do the `implementations:` paths exist (per `specship_explore` / `specship_node`)? (If unverified, mark `[needs review]`.)
|
|
124
|
+
|
|
125
|
+
Seven yeses → proceed to gap-fill.
|
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
# Review checklist
|
|
2
|
+
|
|
3
|
+
After gap-fill, run this checklist against the draft before writing the file. It's also the rubric `/specship:spec review` uses for review-only mode.
|
|
4
|
+
|
|
5
|
+
Output findings as a numbered list keyed to specific lines in the draft. Don't be polite — if something is broken, say so. The user will appreciate a sharp review more than a soft one.
|
|
6
|
+
|
|
7
|
+
## Structural checks (must pass before writing)
|
|
8
|
+
|
|
9
|
+
These produce HARD findings — block writing the file until fixed.
|
|
10
|
+
|
|
11
|
+
### S1. Every heading has an embedded ID immediately above it
|
|
12
|
+
|
|
13
|
+
Mental scan: for each heading, was the previous non-blank line `<!-- id: ... -->`?
|
|
14
|
+
|
|
15
|
+
Failure: spec won't parse. `specship sync` will reject with `spec_missing_id`.
|
|
16
|
+
|
|
17
|
+
Finding format: *"Line N: heading `## Foo` has no `<!-- id: -->` marker on line N-1."*
|
|
18
|
+
|
|
19
|
+
### S2. No stranded IDs (two ID comments in a row)
|
|
20
|
+
|
|
21
|
+
Mental scan: for each `<!-- id: ... -->`, is the next non-blank line a heading?
|
|
22
|
+
|
|
23
|
+
Failure: parser emits `spec_stranded_id` warning. The first ID is silently lost.
|
|
24
|
+
|
|
25
|
+
Finding format: *"Line N: `<!-- id: REQ-X -->` is followed by another `<!-- id: -->` on line N+1, not a heading. The first ID will be discarded."*
|
|
26
|
+
|
|
27
|
+
### S3. IDs are unique within the file
|
|
28
|
+
|
|
29
|
+
Two markers with the same ID do not error. Specs are stored with `INSERT OR REPLACE`, so the **LAST occurrence overwrites earlier ones** — the earlier heading's title, body, and links are silently gone, and nothing warns. This makes duplicates worse than they look: the REQ the reader sees first is not the REQ in the graph.
|
|
30
|
+
|
|
31
|
+
Finding format: *"Lines N and M both declare `<!-- id: REQ-AUTH-001 -->`. The occurrence at line M overwrites the one at line N — the earlier requirement's body and links are lost. Renumber one of them."*
|
|
32
|
+
|
|
33
|
+
### S4. Frontmatter is well-formed
|
|
34
|
+
|
|
35
|
+
Open `---`, close `---`, valid YAML between. Parser emits `spec_bad_frontmatter` warning on malformed but tolerates it (the document just won't have a doc-level ID).
|
|
36
|
+
|
|
37
|
+
Finding format: *"Frontmatter on lines N–M is missing the closing `---`."* / *"Frontmatter key `<key>` is not parseable as YAML."*
|
|
38
|
+
|
|
39
|
+
### S5. `implementations:` / `verifies:` block syntax matches the parser
|
|
40
|
+
|
|
41
|
+
Pattern: `[-*]\s+([^\s:]+)\s*:\s*([A-Za-z0-9_$.]+)`. Common breakages:
|
|
42
|
+
- Missing the leading `-` or `*`.
|
|
43
|
+
- Path contains spaces.
|
|
44
|
+
- Qualified symbol has parentheses or generics — keep it to identifier chars and dots.
|
|
45
|
+
- A bare path with no `:symbol` — tolerated, but creates **no edge** and emits `spec_bare_path_ref`. Only acceptable when there genuinely is no code symbol (e.g. a slash-command markdown file).
|
|
46
|
+
|
|
47
|
+
Finding format: *"Line N: implementations entry `…` doesn't match the parser pattern. The link won't be extracted."*
|
|
48
|
+
|
|
49
|
+
## Quality checks (block writing on critical, warn on minor)
|
|
50
|
+
|
|
51
|
+
### Q1. RFC 2119 keyword used (CRITICAL if missing)
|
|
52
|
+
|
|
53
|
+
Each requirement (H2-level node) should have at least one **MUST / SHOULD / MAY** in the title or body. Drafts without an RFC 2119 word read as wishful thinking, not as contracts.
|
|
54
|
+
|
|
55
|
+
Finding format: *"REQ-X has no MUST/SHOULD/MAY in title or body. Recommend MUST since this looks like a hard requirement."*
|
|
56
|
+
|
|
57
|
+
### Q2. Weasel words flagged
|
|
58
|
+
|
|
59
|
+
Strike on sight: *should probably*, *ought to*, *could*, *might*, *generally*, *normally*, *ideally*, *in most cases*, *handles*, *supports*, *manages*, *implements* (when used vaguely as in "MUST implement authentication").
|
|
60
|
+
|
|
61
|
+
Finding format: *"Line N uses `…ought to…`. RFC 2119 doesn't have a SHOULD-ish equivalent; pick MUST, SHOULD, or MAY."*
|
|
62
|
+
|
|
63
|
+
### Q3. Implementation leak (CRITICAL)
|
|
64
|
+
|
|
65
|
+
Strike on sight: specific library names, algorithm names, language versions, database systems, cache keys, internal class names. Specs describe the contract, not the choice.
|
|
66
|
+
|
|
67
|
+
| Leak signal | Why |
|
|
68
|
+
|---|---|
|
|
69
|
+
| `MUST use bcrypt` | Names an algorithm. |
|
|
70
|
+
| `MUST be stored in Redis` | Names a system. |
|
|
71
|
+
| `MUST inherit from BaseAuthService` | Names an internal class. |
|
|
72
|
+
| `MUST return JSON: { "ok": true, ... }` | Specifies the wire format precisely — only OK if the wire format IS the contract (public API). |
|
|
73
|
+
| `MUST use the existing rate-limiter` | Hedges; names an implementation choice without saying what behavior is required. |
|
|
74
|
+
|
|
75
|
+
Finding format: *"REQ-X line N specifies the implementation library `…`. Rewrite as the contract: `…cryptographically tamper-evident with a 24 h TTL…`."*
|
|
76
|
+
|
|
77
|
+
### Q4. Acceptance bullets are individually testable
|
|
78
|
+
|
|
79
|
+
Each `## Acceptance` bullet must be checkable by a single test assertion. If a bullet requires reading three other docs to know what to test, it's vague.
|
|
80
|
+
|
|
81
|
+
Finding format: *"REQ-X.A2 (`bullet text`) is not directly testable. Suggest splitting into Aa: `…` and Ab: `…`, each with a concrete assertion."*
|
|
82
|
+
|
|
83
|
+
### Q5. Title has one concern
|
|
84
|
+
|
|
85
|
+
Use "and"/"or" as a tripwire. Two concerns = two REQs.
|
|
86
|
+
|
|
87
|
+
Finding format: *"REQ-X title `Login MUST be rate-limited AND audit-logged` mixes two concerns. Split into REQ-X (rate-limit) and REQ-Y (audit-log)."*
|
|
88
|
+
|
|
89
|
+
### Q6. Acceptance bullets cover the failure path
|
|
90
|
+
|
|
91
|
+
Most drafts cover the happy path. Failure paths are half the spec.
|
|
92
|
+
|
|
93
|
+
Quick heuristic: count happy-path bullets vs failure-path bullets. If failure-path < 1, that's almost certainly a gap.
|
|
94
|
+
|
|
95
|
+
Finding format: *"REQ-X has 3 happy-path bullets (A1, A2, A3) but no failure-path bullets. What happens on: (a) invalid input, (b) downstream dependency failure, (c) timeout?"*
|
|
96
|
+
|
|
97
|
+
### Q7. `implementations:` block points at code that exists (when applicable)
|
|
98
|
+
|
|
99
|
+
When `mcp__specship__specship_explore` or `mcp__specship__specship_node` is available, verify each `implementations:` entry resolves. Skip for greenfield specs (where the code doesn't exist yet).
|
|
100
|
+
|
|
101
|
+
Finding format: *"REQ-X line N points at `src/auth/login.ts:authenticate`. `specship_node` returns no symbol at that path. Either the path is wrong (suggestion: `src/services/auth-service.ts:AuthService.handleLogin`) or this is a greenfield spec (then leave `implementations:` empty)."*
|
|
102
|
+
|
|
103
|
+
### Q8. `verifies:` block points at real test symbols (when present)
|
|
104
|
+
|
|
105
|
+
`verifies:` is the test-evidence declaration that gates promotion to `verified`, so a wrong entry means the REQ can never reach that state. Check that each bullet names an addressable symbol — a bare `describe(...)` / `it(...)` string is not one.
|
|
106
|
+
|
|
107
|
+
Finding format: *"REQ-X line N's `verifies:` bullet points at a test-case description, not a symbol. Either name a test helper function, or drop the block until the tests exist."*
|
|
108
|
+
|
|
109
|
+
### Q9. No project invariant is contradicted
|
|
110
|
+
|
|
111
|
+
Re-read the **Project invariants** section of `references/gap-questions.md` against the surface this spec touches (MCP tools, installer, retrieval, extraction). A spec that quietly shrinks an explore budget, adds an agent target, trades latency for output quality, or invents a NodeKind is broken regardless of how clean the markdown is.
|
|
112
|
+
|
|
113
|
+
Finding format: *"REQ-X.A2 allows the explore answer to take two extra tool calls. The retrieval budget is a tool-call count, not a token count — restate the bullet so the call count doesn't grow, or say explicitly why this surface is exempt."*
|
|
114
|
+
|
|
115
|
+
## Hygiene checks (warn, don't block)
|
|
116
|
+
|
|
117
|
+
### H1. Frontmatter owner + priority set
|
|
118
|
+
|
|
119
|
+
Not strictly required, but recommended. `[needs review]` markers are fine for a first draft.
|
|
120
|
+
|
|
121
|
+
Finding format: *"Frontmatter is missing `owner:` and `priority:`. Consider adding even if you mark them `[needs review]`."*
|
|
122
|
+
|
|
123
|
+
### H2. No stale `[needs review]` markers
|
|
124
|
+
|
|
125
|
+
If gap-fill ran and the user answered, the markers should be replaced with their answers. Markers that survive review are still legitimate (the user genuinely doesn't know yet) but flag them anyway.
|
|
126
|
+
|
|
127
|
+
Finding format: *"Lines N, M, P still carry `[needs review]` markers. List them so the user can address before commit."*
|
|
128
|
+
|
|
129
|
+
### H3. No TODO / FIXME / WIP in the body
|
|
130
|
+
|
|
131
|
+
These are work-in-progress signals that shouldn't ship in a spec.
|
|
132
|
+
|
|
133
|
+
Finding format: *"Line N contains `TODO`. Specs are contracts; address before committing."*
|
|
134
|
+
|
|
135
|
+
### H4. Body is concise
|
|
136
|
+
|
|
137
|
+
Spec bodies that read like essays usually contain narrative justification that belongs in the commit message, not the spec.
|
|
138
|
+
|
|
139
|
+
Finding format (subjective): *"REQ-X body is ~N words. Consider trimming the rationale — keep the spec to the contract; put motivation in the commit message."*
|
|
140
|
+
|
|
141
|
+
## Sample review output
|
|
142
|
+
|
|
143
|
+
For the password-reset draft from the gap-questions example:
|
|
144
|
+
|
|
145
|
+
```
|
|
146
|
+
Quality review of specs/password-reset.md:
|
|
147
|
+
|
|
148
|
+
STRUCTURAL — all pass
|
|
149
|
+
|
|
150
|
+
QUALITY:
|
|
151
|
+
1. Line 12 — REQ-AUTH-005 title uses "should ideally". Drop "ideally";
|
|
152
|
+
either MUST or SHOULD.
|
|
153
|
+
2. Line 18 — body specifies `JWT signed with HS256`. Implementation leak.
|
|
154
|
+
Rewrite as contract: "the reset token MUST be unguessable and
|
|
155
|
+
single-use".
|
|
156
|
+
3. Line 24 — REQ-AUTH-005.A2 ("Handles expired links gracefully") is
|
|
157
|
+
not testable. Split: A2 = "An expired link returns 410 Gone";
|
|
158
|
+
A2b = "An expired link logs an audit event with reason
|
|
159
|
+
'reset_link_expired'".
|
|
160
|
+
4. Line 31 — `implementations:` points at `src/auth/reset.ts:reset` but
|
|
161
|
+
`specship_node` returns no symbol there. This is a greenfield spec —
|
|
162
|
+
leave `implementations:` empty; the agent fills it in via
|
|
163
|
+
`link_assert` after implementation lands.
|
|
164
|
+
|
|
165
|
+
HYGIENE:
|
|
166
|
+
5. Frontmatter has owner but no priority. Suggest `priority: medium`.
|
|
167
|
+
6. Line 8 has a stale `[needs review]` marker on the link-TTL question
|
|
168
|
+
you answered during gap-fill — replace with the agreed value (1 hour).
|
|
169
|
+
|
|
170
|
+
Net: 4 quality issues, 2 hygiene. Recommend addressing items 2 + 3
|
|
171
|
+
before writing the file. Items 1, 4, 5, 6 can be addressed in this draft
|
|
172
|
+
or as a follow-up commit.
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
The review names line numbers, says what's wrong, suggests a fix. Don't waffle; users want sharp signal.
|