@specship/specship-win32-arm64 0.23.0 → 0.25.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 (143) hide show
  1. package/lib/dist/activation/steering.d.ts +4 -4
  2. package/lib/dist/activation/steering.d.ts.map +1 -1
  3. package/lib/dist/activation/steering.js +6 -6
  4. package/lib/dist/activation/steering.js.map +1 -1
  5. package/lib/dist/bin/specship.js +277 -40
  6. package/lib/dist/bin/specship.js.map +1 -1
  7. package/lib/dist/commands/specship/learn.md +5 -3
  8. package/lib/dist/commands/specship/spec.md +73 -15
  9. package/lib/dist/db/spec-queries.d.ts +73 -3
  10. package/lib/dist/db/spec-queries.d.ts.map +1 -1
  11. package/lib/dist/db/spec-queries.js +139 -5
  12. package/lib/dist/db/spec-queries.js.map +1 -1
  13. package/lib/dist/diagram/flow-diagram.d.ts +86 -0
  14. package/lib/dist/diagram/flow-diagram.d.ts.map +1 -0
  15. package/lib/dist/diagram/flow-diagram.js +0 -0
  16. package/lib/dist/diagram/flow-diagram.js.map +1 -0
  17. package/lib/dist/enforce/changed-files.d.ts +28 -0
  18. package/lib/dist/enforce/changed-files.d.ts.map +1 -0
  19. package/lib/dist/enforce/changed-files.js +72 -0
  20. package/lib/dist/enforce/changed-files.js.map +1 -0
  21. package/lib/dist/enforce/enforce.d.ts +28 -1
  22. package/lib/dist/enforce/enforce.d.ts.map +1 -1
  23. package/lib/dist/enforce/enforce.js +37 -5
  24. package/lib/dist/enforce/enforce.js.map +1 -1
  25. package/lib/dist/extraction/grammars.d.ts +10 -0
  26. package/lib/dist/extraction/grammars.d.ts.map +1 -1
  27. package/lib/dist/extraction/grammars.js +32 -3
  28. package/lib/dist/extraction/grammars.js.map +1 -1
  29. package/lib/dist/extraction/index.d.ts +7 -0
  30. package/lib/dist/extraction/index.d.ts.map +1 -1
  31. package/lib/dist/extraction/index.js.map +1 -1
  32. package/lib/dist/extraction/specs/markdown-spec-extractor.d.ts +7 -0
  33. package/lib/dist/extraction/specs/markdown-spec-extractor.d.ts.map +1 -1
  34. package/lib/dist/extraction/specs/markdown-spec-extractor.js +81 -8
  35. package/lib/dist/extraction/specs/markdown-spec-extractor.js.map +1 -1
  36. package/lib/dist/extraction/wasm/tree-sitter-pascal.wasm.gz +0 -0
  37. package/lib/dist/extraction/wasm/tree-sitter-scala.wasm.gz +0 -0
  38. package/lib/dist/graph/spec-coverage.d.ts +86 -0
  39. package/lib/dist/graph/spec-coverage.d.ts.map +1 -0
  40. package/lib/dist/graph/spec-coverage.js +152 -0
  41. package/lib/dist/graph/spec-coverage.js.map +1 -0
  42. package/lib/dist/index.d.ts +81 -6
  43. package/lib/dist/index.d.ts.map +1 -1
  44. package/lib/dist/index.js +178 -21
  45. package/lib/dist/index.js.map +1 -1
  46. package/lib/dist/installer/index.d.ts +33 -8
  47. package/lib/dist/installer/index.d.ts.map +1 -1
  48. package/lib/dist/installer/index.js +50 -50
  49. package/lib/dist/installer/index.js.map +1 -1
  50. package/lib/dist/installer/init-offer.d.ts +14 -9
  51. package/lib/dist/installer/init-offer.d.ts.map +1 -1
  52. package/lib/dist/installer/init-offer.js +13 -9
  53. package/lib/dist/installer/init-offer.js.map +1 -1
  54. package/lib/dist/installer/install-defaults.d.ts +62 -0
  55. package/lib/dist/installer/install-defaults.d.ts.map +1 -0
  56. package/lib/dist/installer/install-defaults.js +67 -0
  57. package/lib/dist/installer/install-defaults.js.map +1 -0
  58. package/lib/dist/installer/targets/claude.d.ts +14 -0
  59. package/lib/dist/installer/targets/claude.d.ts.map +1 -1
  60. package/lib/dist/installer/targets/claude.js +64 -0
  61. package/lib/dist/installer/targets/claude.js.map +1 -1
  62. package/lib/dist/installer/targets/shared.js +4 -4
  63. package/lib/dist/installer/targets/shared.js.map +1 -1
  64. package/lib/dist/mcp/fitness-tool.d.ts +5 -6
  65. package/lib/dist/mcp/fitness-tool.d.ts.map +1 -1
  66. package/lib/dist/mcp/fitness-tool.js +0 -16
  67. package/lib/dist/mcp/fitness-tool.js.map +1 -1
  68. package/lib/dist/mcp/health-tool.d.ts +26 -0
  69. package/lib/dist/mcp/health-tool.d.ts.map +1 -0
  70. package/lib/dist/mcp/health-tool.js +50 -0
  71. package/lib/dist/mcp/health-tool.js.map +1 -0
  72. package/lib/dist/mcp/maintainability-tool.d.ts +5 -7
  73. package/lib/dist/mcp/maintainability-tool.d.ts.map +1 -1
  74. package/lib/dist/mcp/maintainability-tool.js +0 -16
  75. package/lib/dist/mcp/maintainability-tool.js.map +1 -1
  76. package/lib/dist/mcp/model-context.d.ts +13 -2
  77. package/lib/dist/mcp/model-context.d.ts.map +1 -1
  78. package/lib/dist/mcp/model-context.js +39 -12
  79. package/lib/dist/mcp/model-context.js.map +1 -1
  80. package/lib/dist/mcp/proxy.d.ts.map +1 -1
  81. package/lib/dist/mcp/proxy.js +4 -1
  82. package/lib/dist/mcp/proxy.js.map +1 -1
  83. package/lib/dist/mcp/server-instructions.d.ts +1 -1
  84. package/lib/dist/mcp/server-instructions.d.ts.map +1 -1
  85. package/lib/dist/mcp/server-instructions.js +5 -4
  86. package/lib/dist/mcp/server-instructions.js.map +1 -1
  87. package/lib/dist/mcp/spec-tools.d.ts.map +1 -1
  88. package/lib/dist/mcp/spec-tools.js +213 -28
  89. package/lib/dist/mcp/spec-tools.js.map +1 -1
  90. package/lib/dist/mcp/tools.d.ts +12 -8
  91. package/lib/dist/mcp/tools.d.ts.map +1 -1
  92. package/lib/dist/mcp/tools.js +89 -48
  93. package/lib/dist/mcp/tools.js.map +1 -1
  94. package/lib/dist/resolution/spec-link-resolver.d.ts +44 -2
  95. package/lib/dist/resolution/spec-link-resolver.d.ts.map +1 -1
  96. package/lib/dist/resolution/spec-link-resolver.js +132 -0
  97. package/lib/dist/resolution/spec-link-resolver.js.map +1 -1
  98. package/lib/dist/resolution/test-title-links.d.ts +62 -0
  99. package/lib/dist/resolution/test-title-links.d.ts.map +1 -0
  100. package/lib/dist/resolution/test-title-links.js +108 -0
  101. package/lib/dist/resolution/test-title-links.js.map +1 -0
  102. package/lib/dist/search/query-parser.d.ts.map +1 -1
  103. package/lib/dist/search/query-parser.js +6 -1
  104. package/lib/dist/search/query-parser.js.map +1 -1
  105. package/lib/dist/server/project-registry.js +1 -1
  106. package/lib/dist/server/routes/claude.js +9 -36
  107. package/lib/dist/server/routes/graph.js +53 -7
  108. package/lib/dist/server/routes/mcp.js +4 -3
  109. package/lib/dist/server/routes/projects.js +4 -4
  110. package/lib/dist/server/routes/spec.js +49 -7
  111. package/lib/dist/server/server.js +29 -17
  112. package/lib/dist/skills/spec-author/SKILL.md +164 -0
  113. package/lib/dist/skills/spec-author/references/format.md +210 -0
  114. package/lib/dist/skills/spec-author/references/gap-questions.md +152 -0
  115. package/lib/dist/skills/spec-author/references/quality-rubric.md +125 -0
  116. package/lib/dist/skills/spec-author/references/review-checklist.md +175 -0
  117. package/lib/dist/spec-lint/lint.d.ts +82 -0
  118. package/lib/dist/spec-lint/lint.d.ts.map +1 -0
  119. package/lib/dist/spec-lint/lint.js +430 -0
  120. package/lib/dist/spec-lint/lint.js.map +1 -0
  121. package/lib/dist/statusline/index.js.map +1 -1
  122. package/lib/dist/statusline/render.d.ts +1 -1
  123. package/lib/dist/statusline/render.d.ts.map +1 -1
  124. package/lib/dist/statusline/render.js +3 -1
  125. package/lib/dist/statusline/render.js.map +1 -1
  126. package/lib/dist/ui/assets/index-BzuUWUAE.js +50 -0
  127. package/lib/dist/ui/index.html +1 -1
  128. package/lib/dist/verify/report-ingest.d.ts +88 -0
  129. package/lib/dist/verify/report-ingest.d.ts.map +1 -0
  130. package/lib/dist/verify/report-ingest.js +269 -0
  131. package/lib/dist/verify/report-ingest.js.map +1 -0
  132. package/lib/dist/workflows/defaults/spec-fix.yaml +103 -12
  133. package/lib/dist/workflows/defaults/spec-implement-mixed.yaml +264 -21
  134. package/lib/dist/workflows/defaults/spec-implement.yaml +260 -19
  135. package/lib/dist/workflows/defaults/spec-verify.yaml +60 -9
  136. package/lib/node_modules/.package-lock.json +1 -1
  137. package/lib/package.json +7 -3
  138. package/package.json +1 -1
  139. package/lib/dist/extraction/wasm/tree-sitter-pascal.wasm +0 -0
  140. package/lib/dist/extraction/wasm/tree-sitter-scala.wasm +0 -0
  141. package/lib/dist/server/ingest/impact-query.js +0 -343
  142. package/lib/dist/server/routes/events.js +0 -152
  143. package/lib/dist/ui/assets/index-CjjaftDw.js +0 -50
@@ -100,9 +100,137 @@ nodes:
100
100
 
101
101
  When done, output a short diff summary (which files changed and why).
102
102
 
103
- - id: verify
103
+ - id: quality_gate
104
104
  kind: bash
105
105
  depends_on: [implement]
106
+ output_type: quality_report
107
+ timeout: 600000
108
+ bash: |
109
+ # REQ-AUTHG-005.A1 — the project's own lint/typecheck, run in the
110
+ # worktree right after implement. The QUALITY_GATE marker is the
111
+ # contract the remediation node routes on: a project with neither a lint
112
+ # script nor a typecheck config says so explicitly, so "nothing ran" can
113
+ # never be mistaken for "clean" (same doctrine as VERIFY_RESULT=skipped).
114
+ QUALITY_CMD=""
115
+ if [ -f package.json ]; then
116
+ # The worktree may not have dependencies yet (verify installs them
117
+ # later) and lint needs them — bootstrap quietly, and treat a failed
118
+ # install as "no gate" rather than as a lint failure.
119
+ if [ ! -d node_modules ]; then
120
+ npm ci --no-audit --no-fund >/dev/null 2>&1 || npm install --no-audit --no-fund >/dev/null 2>&1 || true
121
+ fi
122
+ if node -e 'const s=(require("./package.json").scripts)||{};process.exit(s.lint?0:1)' 2>/dev/null; then
123
+ QUALITY_CMD="npm run lint --if-present"
124
+ elif [ -f tsconfig.json ]; then
125
+ QUALITY_CMD="npx --no-install tsc --noEmit"
126
+ fi
127
+ elif [ -f Cargo.toml ]; then
128
+ if command -v cargo >/dev/null 2>&1; then QUALITY_CMD="cargo clippy --no-deps"; fi
129
+ elif [ -f pyproject.toml ] || [ -f requirements.txt ]; then
130
+ if command -v ruff >/dev/null 2>&1; then QUALITY_CMD="ruff check ."; fi
131
+ fi
132
+ if [ -z "$QUALITY_CMD" ]; then
133
+ echo "QUALITY_GATE=skipped"
134
+ echo "QUALITY_REASON=no lint script and no typecheck configuration in this project"
135
+ exit 0
136
+ fi
137
+ echo "QUALITY_CMD=$QUALITY_CMD"
138
+ if OUT=$($QUALITY_CMD 2>&1); then
139
+ echo "QUALITY_GATE=passed"
140
+ else
141
+ echo "QUALITY_GATE=failed"
142
+ printf '%s\n' "$OUT" | tail -n 80
143
+ fi
144
+ # Exiting 0 on a lint failure is deliberate: the marker — not this node's
145
+ # exit code — drives the revise loop. quality_recheck is what halts.
146
+
147
+ - id: fix_quality
148
+ kind: prompt
149
+ depends_on: [quality_gate]
150
+ output_type: quality_fix
151
+ allowed_tools:
152
+ - Read
153
+ - Edit
154
+ - Write
155
+ - Bash
156
+ - mcp__specship__specship_explore
157
+ - mcp__specship__specship_node
158
+ prompt: |
159
+ Lint / typecheck result for the code you just wrote:
160
+
161
+ $quality_gate.output
162
+
163
+ Route on the QUALITY_GATE marker — do not re-judge the raw output:
164
+
165
+ - QUALITY_GATE=passed or QUALITY_GATE=skipped → change NOTHING. Output
166
+ exactly `FIX_QUALITY=not-needed` and stop.
167
+ - QUALITY_GATE=failed → fix every reported error, then re-run the
168
+ QUALITY_CMD shown above to confirm. Fix the code, never the lint
169
+ configuration; never silence or disable a rule to make it pass.
170
+
171
+ Output `FIX_QUALITY=fixed` (clean re-run) or `FIX_QUALITY=still-failing`
172
+ followed by the errors that remain.
173
+
174
+ - id: quality_recheck
175
+ kind: bash
176
+ depends_on: [fix_quality]
177
+ output_type: quality_result
178
+ timeout: 600000
179
+ bash: |
180
+ # REQ-AUTHG-005.A1 — the authoritative half of the gate. The fix node is
181
+ # a prompt, so its "FIX_QUALITY=fixed" claim is a claim, not evidence:
182
+ # this node re-runs the same command and halts the run while it still
183
+ # fails, so failing lint/typecheck never reaches verify, the link node,
184
+ # or the human gate.
185
+ GATE_OUT=$quality_gate.output
186
+ MARKER=$(printf '%s' "$GATE_OUT" | grep -oE 'QUALITY_GATE=[a-z-]+' | tail -n 1 | cut -d= -f2)
187
+ CMD=$(printf '%s' "$GATE_OUT" | grep -E '^QUALITY_CMD=' | tail -n 1 | cut -d= -f2-)
188
+ if [ "$MARKER" = "skipped" ] || [ -z "$CMD" ]; then
189
+ echo "QUALITY_RESULT=skipped"
190
+ echo "This project has no lint or typecheck gate — nothing was checked."
191
+ exit 0
192
+ fi
193
+ if OUT=$($CMD 2>&1); then
194
+ echo "QUALITY_RESULT=clean"
195
+ exit 0
196
+ fi
197
+ echo "QUALITY_RESULT=failed"
198
+ printf '%s\n' "$OUT" | tail -n 80
199
+ echo "HALT_CAUSE=lint/typecheck still fails after the fix attempt — the change is not reviewable yet"
200
+ exit 1
201
+
202
+ - id: diff
203
+ kind: bash
204
+ depends_on: [quality_recheck]
205
+ output_type: diff
206
+ bash: |
207
+ # REQ-AUTHG-004.A1 — the full diff, from git, into a run artifact. The
208
+ # human gate and the adversarial reviewer read THIS; the implement node's
209
+ # prose about what it believes it changed is not the change.
210
+ # `git add -A -N` registers new files as intent-to-add so untracked work
211
+ # appears in `git diff HEAD` without staging any content.
212
+ git add -A -N >/dev/null 2>&1 || true
213
+ echo "DIFF_BEGIN"
214
+ git --no-pager diff HEAD | head -c 400000
215
+ echo
216
+ echo "DIFF_END"
217
+
218
+ - id: diff_stat
219
+ kind: bash
220
+ depends_on: [diff]
221
+ output_type: diff_stat
222
+ bash: |
223
+ # REQ-AUTHG-004.A2 — the stat block the final gate shows: file names and
224
+ # insertions/deletions counted by git. Its own small node so the approval
225
+ # message carries the summary while the full diff stays in the `diff`
226
+ # artifact.
227
+ echo "DIFF_STAT_BEGIN"
228
+ git --no-pager diff HEAD --stat
229
+ echo "DIFF_STAT_END"
230
+
231
+ - id: verify
232
+ kind: bash
233
+ depends_on: [diff_stat]
106
234
  output_type: test_results
107
235
  timeout: 600000
108
236
  bash: |
@@ -111,25 +239,67 @@ nodes:
111
239
  # The VERIFY_RESULT marker is the machine-readable contract the link
112
240
  # node routes on (REQ-IMPLINT-001.A1): a skipped run must never be
113
241
  # mistaken for a passing one.
242
+ # Install and build failures fail fast with their own markers
243
+ # (REQ-REVINT-006.A1) — a broken environment or a build break is NOT a
244
+ # test failure, and must never be attributed to one.
114
245
  if [ -f package.json ]; then
115
246
  # A fresh worktree has no node_modules/ or build output — a verify
116
247
  # failure must mean the tests failed, not that the environment was
117
248
  # missing (WF-REJECT-DOC, REQ-WFREJ-005).
118
- if [ ! -d node_modules ]; then npm ci --no-audit --no-fund || npm install --no-audit --no-fund; fi
119
- npm run build --if-present
249
+ if [ ! -d node_modules ]; then
250
+ if ! { npm ci --no-audit --no-fund || npm install --no-audit --no-fund; }; then
251
+ echo "VERIFY_RESULT=env-failed"; exit 1
252
+ fi
253
+ fi
254
+ if ! npm run build --if-present; then echo "VERIFY_RESULT=build-failed"; exit 1; fi
120
255
  if npm test; then echo "VERIFY_RESULT=ran-and-passed"; else echo "VERIFY_RESULT=ran-and-failed"; exit 1; fi
121
256
  elif [ -f Cargo.toml ]; then
257
+ if ! cargo build; then echo "VERIFY_RESULT=build-failed"; exit 1; fi
122
258
  if cargo test; then echo "VERIFY_RESULT=ran-and-passed"; else echo "VERIFY_RESULT=ran-and-failed"; exit 1; fi
123
259
  elif [ -f pyproject.toml ] || [ -f requirements.txt ]; then
260
+ if ! python -c 'import pytest' 2>/dev/null; then echo "VERIFY_RESULT=env-failed"; exit 1; fi
124
261
  if python -m pytest; then echo "VERIFY_RESULT=ran-and-passed"; else echo "VERIFY_RESULT=ran-and-failed"; exit 1; fi
125
262
  else
126
263
  echo "no recognised test framework; tests did NOT run"
127
264
  echo "VERIFY_RESULT=skipped"
128
265
  fi
129
266
 
267
+ - id: route_verify
268
+ kind: bash
269
+ depends_on: [verify]
270
+ output_type: verify_route
271
+ bash: |
272
+ # Deterministic verify routing (REQ-VSTATE-005.A1): the VERIFY_RESULT
273
+ # marker is parsed HERE, in code, and turned into a LINK_ACTION the link
274
+ # node obeys. It used to be a prompt reading a test log — the one decision
275
+ # that governs whether links may be promoted was hallucinable.
276
+ VERIFY_OUT=$verify.output
277
+ MARKER=$(printf '%s' "$VERIFY_OUT" | grep -oE 'VERIFY_RESULT=[a-z-]+' | tail -n 1 | cut -d= -f2)
278
+ [ -n "$MARKER" ] || MARKER=missing
279
+ echo "VERIFY_RESULT=$MARKER"
280
+ # env-failed / build-failed halt BEFORE any link mutation, naming the
281
+ # environment cause (REQ-VSTATE-005.A3) — neither is a test failure.
282
+ case "$MARKER" in
283
+ ran-and-passed)
284
+ echo "LINK_ACTION=assert-and-verify-pass" ;;
285
+ ran-and-failed)
286
+ echo "LINK_ACTION=assert-and-verify-fail" ;;
287
+ skipped)
288
+ echo "LINK_ACTION=assert-only" ;;
289
+ env-failed)
290
+ echo "HALT_CAUSE=environment setup failed (dependency install) — nothing is known about the tests"
291
+ exit 1 ;;
292
+ build-failed)
293
+ echo "HALT_CAUSE=build failed — nothing is known about the tests"
294
+ exit 1 ;;
295
+ *)
296
+ echo "HALT_CAUSE=no VERIFY_RESULT marker found in the verify output — an unknown result is never a pass"
297
+ exit 1 ;;
298
+ esac
299
+
130
300
  - id: link
131
301
  kind: prompt
132
- depends_on: [verify]
302
+ depends_on: [route_verify]
133
303
  output_type: link_summary
134
304
  allowed_tools:
135
305
  - mcp__specship__specship_link_assert
@@ -141,14 +311,31 @@ nodes:
141
311
  requirements), call specship_link_assert with the spec_id, the
142
312
  file path, and the qualified symbol name.
143
313
 
144
- Then route on the VERIFY_RESULT marker in the test results below
145
- (REQ-IMPLINT-001):
146
- - VERIFY_RESULT=ran-and-passed → after each assert, call
314
+ You MUST NOT assert a `tests`-kind link (REQ-REVINT-005.A1). Every
315
+ specship_link_assert call here is an implementation link — leave `kind`
316
+ at its default and never pass kind: 'tests'. Test evidence is
317
+ file-derived only: it comes from the spec's `verifies:` block or an
318
+ `@verifies` comment on the test symbol, never from this node. A workflow
319
+ that minted its own test evidence would be promoting itself past the
320
+ gate that exists to check it.
321
+
322
+ Then act on LINK_ACTION, which a bash node already computed from the
323
+ VERIFY_RESULT marker (REQ-VSTATE-005.A1). Do NOT re-derive it from the
324
+ test log — read it:
325
+
326
+ $route_verify.output
327
+
328
+ - LINK_ACTION=assert-and-verify-pass → after each assert, call
147
329
  specship_link_verify with result="pass".
148
- - VERIFY_RESULT=skipped → do NOT call specship_link_verify at all;
330
+ - LINK_ACTION=assert-and-verify-fail → call specship_link_verify with
331
+ result="fail" and the failing test name as the reason.
332
+ - LINK_ACTION=assert-only → do NOT call specship_link_verify at all;
149
333
  the links stay in the `implemented` state. Tests that did not run
150
334
  are never reported as passing.
151
335
 
336
+ A run whose environment or build broke never reaches this node — the
337
+ routing step halts it first (REQ-VSTATE-005.A3).
338
+
152
339
  Implementation diff summary:
153
340
  $implement.output
154
341
 
@@ -160,34 +347,88 @@ nodes:
160
347
  skipped.
161
348
 
162
349
  - id: coverage
163
- kind: prompt
350
+ kind: bash
164
351
  depends_on: [link]
165
352
  output_type: coverage_summary
353
+ bash: |
354
+ # The acceptance-criteria coverage line is computed FROM THE DB
355
+ # (REQ-VSTATE-005.A2) — `specship coverage --line` renders the same
356
+ # `getSpecCoverage` report the MCP tool and the dashboard read, so the
357
+ # number on the approval gate cannot disagree with them. It used to be
358
+ # counted by a prompt.
359
+ if command -v specship >/dev/null 2>&1; then SS=specship
360
+ elif [ -x node_modules/.bin/specship ]; then SS=node_modules/.bin/specship
361
+ elif [ -x dist/bin/specship.js ]; then SS="node dist/bin/specship.js"
362
+ else SS=""; fi
363
+ if [ -z "$SS" ]; then
364
+ echo "COVERAGE_UNAVAILABLE=specship CLI not found — acceptance-criteria coverage was NOT computed"
365
+ exit 0
366
+ fi
367
+ LINE=$($SS coverage $INPUT.SPEC_ID --line) || {
368
+ echo "COVERAGE_UNAVAILABLE=specship coverage failed — coverage was NOT computed"
369
+ exit 0
370
+ }
371
+ echo "$LINE"
372
+ N=$(printf '%s' "$LINE" | awk '{print $1}')
373
+ M=$(printf '%s' "$LINE" | awk '{print $3}')
374
+ if [ "$N" != "$M" ]; then
375
+ echo "Close the gap with /specship:spec behaviour $INPUT.SPEC_ID."
376
+ fi
377
+
378
+ - id: adversarial_review
379
+ kind: prompt
380
+ depends_on: [coverage]
381
+ output_type: adversarial_review
166
382
  allowed_tools:
383
+ - Read
384
+ - Grep
167
385
  - mcp__specship__specship_spec
386
+ - mcp__specship__specship_explore
387
+ - mcp__specship__specship_node
388
+ - mcp__specship__specship_search
168
389
  prompt: |
169
- Report acceptance-criteria test coverage for $INPUT.SPEC_ID
170
- (REQ-IMPLINT-002). Call specship_spec with the spec_id, count its
171
- acceptance criteria (the .A<N> children; if the spec has none,
172
- treat the requirement itself as one criterion), and count how many
173
- of them carry a `tests`-kind link.
390
+ Adversarial review (REQ-AUTHG-005.A3). You are NOT the implementer — your
391
+ job is to find what the implementation missed. This node is read-only:
392
+ you have no Edit, Write or Bash tool and must not try to fix anything.
174
393
 
175
- Output exactly one or two lines:
176
- - "N of M acceptance criteria for $INPUT.SPEC_ID have linked tests."
177
- - If N < M, add: "Close the gap with /specship:spec behaviour
178
- $INPUT.SPEC_ID."
394
+ Call specship_spec($INPUT.SPEC_ID) and read the spec body, including
395
+ every acceptance criterion.
396
+
397
+ The change under review (from git, not from the implementer's summary):
398
+
399
+ $diff.output
400
+
401
+ Report this and nothing else:
402
+
403
+ 1. One line per acceptance criterion:
404
+ `<AC id>: satisfied | partial | unaddressed — <one sentence, grounded
405
+ in the diff>`. Judge from the diff and the code you read; "the
406
+ implementer said so" is not evidence.
407
+ 2. `Untested new code paths:` the branches/functions this diff adds that
408
+ no test in the diff exercises (`none` if there are none).
409
+ 3. `Verdict:` one line — the most serious gap a reviewer should look at
410
+ first, or `no gaps found`.
179
411
 
180
412
  - id: final_review
181
413
  kind: approval
182
- depends_on: [coverage]
414
+ depends_on: [adversarial_review]
183
415
  message: |
184
416
  Implementation complete for $INPUT.SPEC_ID.
185
417
 
418
+ Changes (git diff --stat — measured, not summarized):
419
+ $diff_stat.output
420
+
421
+ Lint / typecheck:
422
+ $quality_recheck.output
423
+
186
424
  Links asserted:
187
425
  $link.output
188
426
 
189
427
  Acceptance-criteria test coverage:
190
428
  $coverage.output
191
429
 
430
+ Adversarial review:
431
+ $adversarial_review.output
432
+
192
433
  Approve to finalize the run (worktree is left for you to inspect
193
434
  and merge), or reject to abandon.
@@ -13,23 +13,58 @@ nodes:
13
13
  output_type: test_results
14
14
  timeout: 600000
15
15
  bash: |
16
+ # Same VERIFY_RESULT contract as spec-implement / spec-fix
17
+ # (REQ-REVINT-006, REQ-VSTATE-005): the marker is what the routing node
18
+ # reads. This node used to emit no marker at all and `exit 0` when it
19
+ # found no runner — a no-runner repo read as a clean verification pass.
16
20
  if [ -f package.json ]; then
17
21
  # Run against a built tree so a missing dist/ can't false-fail the
18
22
  # verify leg (WF-REJECT-DOC, REQ-WFREJ-005).
19
- npm run build --if-present
20
- npm test
23
+ if ! npm run build --if-present; then echo "VERIFY_RESULT=build-failed"; exit 1; fi
24
+ if npm test; then echo "VERIFY_RESULT=ran-and-passed"; else echo "VERIFY_RESULT=ran-and-failed"; exit 1; fi
21
25
  elif [ -f Cargo.toml ]; then
22
- cargo test
26
+ if ! cargo build; then echo "VERIFY_RESULT=build-failed"; exit 1; fi
27
+ if cargo test; then echo "VERIFY_RESULT=ran-and-passed"; else echo "VERIFY_RESULT=ran-and-failed"; exit 1; fi
23
28
  elif [ -f pyproject.toml ] || [ -f requirements.txt ]; then
24
- python -m pytest
29
+ if ! python -c 'import pytest' 2>/dev/null; then echo "VERIFY_RESULT=env-failed"; exit 1; fi
30
+ if python -m pytest; then echo "VERIFY_RESULT=ran-and-passed"; else echo "VERIFY_RESULT=ran-and-failed"; exit 1; fi
25
31
  else
26
- echo "no recognised test framework; skipping" >&2
27
- exit 0
32
+ echo "no recognised test framework; tests did NOT run" >&2
33
+ echo "VERIFY_RESULT=skipped"
28
34
  fi
29
35
 
36
+ - id: route_verify
37
+ kind: bash
38
+ depends_on: [run_tests]
39
+ output_type: verify_route
40
+ bash: |
41
+ # Deterministic routing (REQ-VSTATE-005.A1): code decides what the
42
+ # summarize node may do with link state.
43
+ VERIFY_OUT=$run_tests.output
44
+ MARKER=$(printf '%s' "$VERIFY_OUT" | grep -oE 'VERIFY_RESULT=[a-z-]+' | tail -n 1 | cut -d= -f2)
45
+ [ -n "$MARKER" ] || MARKER=missing
46
+ echo "VERIFY_RESULT=$MARKER"
47
+ case "$MARKER" in
48
+ ran-and-passed)
49
+ echo "LINK_ACTION=verify-evidenced" ;;
50
+ ran-and-failed)
51
+ echo "LINK_ACTION=verify-fail" ;;
52
+ skipped)
53
+ echo "LINK_ACTION=no-verify" ;;
54
+ env-failed)
55
+ echo "HALT_CAUSE=environment setup failed — nothing is known about the tests"
56
+ exit 1 ;;
57
+ build-failed)
58
+ echo "HALT_CAUSE=build failed — nothing is known about the tests"
59
+ exit 1 ;;
60
+ *)
61
+ echo "HALT_CAUSE=no VERIFY_RESULT marker found in the test output — an unknown result is never a pass"
62
+ exit 1 ;;
63
+ esac
64
+
30
65
  - id: summarize
31
66
  kind: prompt
32
- depends_on: [run_tests]
67
+ depends_on: [route_verify]
33
68
  output_type: link_summary
34
69
  trigger_rule: all_done
35
70
  allowed_tools:
@@ -41,12 +76,23 @@ nodes:
41
76
 
42
77
  $run_tests.output
43
78
 
79
+ Routing decision (computed in code — act on it, do not re-derive it
80
+ from the log above):
81
+
82
+ $route_verify.output
83
+
44
84
  Promotion is EVIDENCE-BASED (VERIFY-EVID-DOC): a spec may only become
45
85
  "verified" when a test linked to it as evidence passed. Evidence links
46
- have kind='tests' and come from a `verifies:` block in the spec or an
47
- `@verifies REQ-X` comment on the test. A green suite alone proves
86
+ have kind='tests' and come from a `verifies:` block in the spec, an
87
+ `@verifies REQ-X` comment on the test, or a spec id inside an `it()`
88
+ title in a test file (REQ-VSTATE-002). A green suite alone proves
48
89
  nothing about a spec with no linked tests — never blanket-promote.
49
90
 
91
+ - LINK_ACTION=verify-evidenced → proceed with steps 1–4 below.
92
+ - LINK_ACTION=verify-fail → only demote (step 3). Promote nothing.
93
+ - LINK_ACTION=no-verify → no test ran. Mutate no link state at all;
94
+ report that verification was skipped and stop.
95
+
50
96
  1. Call specship_drifted to see any drifted/broken/orphaned links.
51
97
  2. For each 'implemented' link whose spec HAS test-evidence links:
52
98
  if all of that spec's evidence tests passed in the output above,
@@ -61,5 +107,10 @@ nodes:
61
107
  "unevidenced" in your summary so the author can add `verifies:`
62
108
  blocks (REQ-VEVID-003).
63
109
 
110
+ For a fully deterministic alternative to steps 2–3, run
111
+ `specship verify --report <vitest-json>` (REQ-VSTATE-004): it maps
112
+ reported test cases to `tests` links by file + title and moves the
113
+ state itself, with the run recorded as evidence.
114
+
64
115
  Output: a short summary — how many links moved to verified, how many
65
116
  to broken, which specs are unevidenced, and which are still drifted.
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@specship/specship",
3
- "version": "0.23.0",
3
+ "version": "0.25.0",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
package/lib/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@specship/specship",
3
- "version": "0.23.0",
3
+ "version": "0.25.0",
4
4
  "description": "Supercharge Claude Code with semantic code intelligence. 94% fewer tool calls • 77% faster exploration • 100% local.",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -9,12 +9,13 @@
9
9
  },
10
10
  "files": [
11
11
  "dist",
12
- "scripts",
12
+ "!dist/**/*.map",
13
13
  "README.md",
14
14
  ".claude-plugin",
15
15
  "commands",
16
16
  "agents",
17
17
  "hooks",
18
+ "skills",
18
19
  "selectors.json"
19
20
  ],
20
21
  "scripts": {
@@ -22,8 +23,9 @@
22
23
  "build:core": "tsc && tsc -p src/designer/tsconfig.json && npm run copy-assets && node -e \"require('fs').chmodSync('dist/bin/specship.js', 0o755)\"",
23
24
  "build:server": "node scripts/build-server-bundle.mjs",
24
25
  "preuninstall": "node dist/bin/uninstall.js",
25
- "copy-assets": "node -e \"const fs=require('fs');fs.mkdirSync('dist/db',{recursive:true});fs.copyFileSync('src/db/schema.sql','dist/db/schema.sql');fs.mkdirSync('dist/extraction/wasm',{recursive:true});fs.readdirSync('src/extraction/wasm').filter(f=>f.endsWith('.wasm')).forEach(f=>fs.copyFileSync('src/extraction/wasm/'+f,'dist/extraction/wasm/'+f));try{fs.mkdirSync('dist/workflows/defaults',{recursive:true});fs.readdirSync('src/workflows/defaults').filter(f=>/\\.ya?ml$/i.test(f)).forEach(f=>fs.copyFileSync('src/workflows/defaults/'+f,'dist/workflows/defaults/'+f))}catch(e){};['commands','agents','hooks','.claude-plugin'].forEach(d=>{try{fs.cpSync(d,'dist/'+d,{recursive:true})}catch(e){}})\"",
26
+ "copy-assets": "node scripts/copy-assets.mjs",
26
27
  "dev": "tsc --watch",
28
+ "lint": "tsc --noEmit && tsc --noEmit -p server/tsconfig.json",
27
29
  "cli": "npm run build && node dist/bin/specship.js",
28
30
  "test": "vitest run",
29
31
  "test:watch": "vitest",
@@ -63,6 +65,8 @@
63
65
  "@types/cross-spawn": "^6.0.6",
64
66
  "@types/node": "^20.19.30",
65
67
  "@types/picomatch": "^4.0.2",
68
+ "@vitest/coverage-v8": "^2.1.9",
69
+ "fast-check": "^4.10.2",
66
70
  "typescript": "^5.0.0",
67
71
  "vitest": "^2.1.9"
68
72
  },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@specship/specship-win32-arm64",
3
- "version": "0.23.0",
3
+ "version": "0.25.0",
4
4
  "description": "SpecShip self-contained bundle for win32-arm64",
5
5
  "os": [
6
6
  "win32"