spexcode 0.6.2 → 0.6.5

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 (479) hide show
  1. package/README.md +3 -1
  2. package/bin/spex.mjs +4 -0
  3. package/node_modules/@spexcode/spec-cli/bin/spex.mjs +76 -0
  4. package/node_modules/@spexcode/spec-cli/dist/attach.d.ts +2 -0
  5. package/node_modules/@spexcode/spec-cli/dist/attach.js +48 -0
  6. package/node_modules/@spexcode/spec-cli/dist/claude-headless.d.ts +33 -0
  7. package/node_modules/@spexcode/spec-cli/dist/claude-headless.js +307 -0
  8. package/node_modules/@spexcode/spec-cli/dist/cli.d.ts +1 -0
  9. package/node_modules/@spexcode/spec-cli/dist/cli.js +1859 -0
  10. package/node_modules/@spexcode/spec-cli/dist/client.d.ts +111 -0
  11. package/node_modules/@spexcode/spec-cli/dist/client.js +476 -0
  12. package/node_modules/@spexcode/spec-cli/dist/cockpit.d.ts +7 -0
  13. package/node_modules/@spexcode/spec-cli/dist/cockpit.js +26 -0
  14. package/node_modules/@spexcode/spec-cli/dist/codex-headless.d.ts +1 -0
  15. package/{spec-cli/src/codex-headless.ts → node_modules/@spexcode/spec-cli/dist/codex-headless.js} +3 -9
  16. package/node_modules/@spexcode/spec-cli/dist/codex-runtime-generations.d.ts +53 -0
  17. package/node_modules/@spexcode/spec-cli/dist/codex-runtime-generations.js +686 -0
  18. package/node_modules/@spexcode/spec-cli/dist/commit-surgery.d.ts +1 -0
  19. package/node_modules/@spexcode/spec-cli/dist/commit-surgery.js +90 -0
  20. package/node_modules/@spexcode/spec-cli/dist/contract-filter.d.ts +14 -0
  21. package/node_modules/@spexcode/spec-cli/dist/contract-filter.js +219 -0
  22. package/node_modules/@spexcode/spec-cli/dist/dashboard-assets.d.ts +6 -0
  23. package/node_modules/@spexcode/spec-cli/dist/dashboard-assets.js +50 -0
  24. package/node_modules/@spexcode/spec-cli/dist/delivery-queue.d.ts +23 -0
  25. package/node_modules/@spexcode/spec-cli/dist/delivery-queue.js +179 -0
  26. package/node_modules/@spexcode/spec-cli/dist/doctor.d.ts +9 -0
  27. package/node_modules/@spexcode/spec-cli/dist/doctor.js +547 -0
  28. package/node_modules/@spexcode/spec-cli/dist/eval-host.d.ts +1 -0
  29. package/node_modules/@spexcode/spec-cli/dist/eval-host.js +19 -0
  30. package/node_modules/@spexcode/spec-cli/dist/execution-trace.d.ts +26 -0
  31. package/node_modules/@spexcode/spec-cli/dist/execution-trace.js +475 -0
  32. package/node_modules/@spexcode/spec-cli/dist/file-write.d.ts +2 -0
  33. package/node_modules/@spexcode/spec-cli/dist/file-write.js +26 -0
  34. package/node_modules/@spexcode/spec-cli/dist/flat.d.ts +89 -0
  35. package/node_modules/@spexcode/spec-cli/dist/flat.js +1076 -0
  36. package/node_modules/@spexcode/spec-cli/dist/gateway-auth.d.ts +56 -0
  37. package/node_modules/@spexcode/spec-cli/dist/gateway-auth.js +168 -0
  38. package/node_modules/@spexcode/spec-cli/dist/gateway-hub.d.ts +28 -0
  39. package/node_modules/@spexcode/spec-cli/dist/gateway-hub.js +361 -0
  40. package/node_modules/@spexcode/spec-cli/dist/gateway.d.ts +37 -0
  41. package/node_modules/@spexcode/spec-cli/dist/gateway.js +503 -0
  42. package/node_modules/@spexcode/spec-cli/dist/graphCache.d.ts +27 -0
  43. package/node_modules/@spexcode/spec-cli/dist/graphCache.js +716 -0
  44. package/node_modules/@spexcode/spec-cli/dist/graphSnapshot.d.ts +14 -0
  45. package/node_modules/@spexcode/spec-cli/dist/graphSnapshot.js +35 -0
  46. package/node_modules/@spexcode/spec-cli/dist/graphStream.d.ts +85 -0
  47. package/node_modules/@spexcode/spec-cli/dist/graphStream.js +1195 -0
  48. package/node_modules/@spexcode/spec-cli/dist/guidance-catalog.d.ts +64 -0
  49. package/node_modules/@spexcode/spec-cli/dist/guidance-catalog.js +166 -0
  50. package/node_modules/@spexcode/spec-cli/dist/guide.d.ts +8 -0
  51. package/{spec-cli/src/guide.ts → node_modules/@spexcode/spec-cli/dist/guide.js} +36 -32
  52. package/node_modules/@spexcode/spec-cli/dist/harness-select.d.ts +18 -0
  53. package/node_modules/@spexcode/spec-cli/dist/harness-select.js +62 -0
  54. package/node_modules/@spexcode/spec-cli/dist/harness.d.ts +325 -0
  55. package/node_modules/@spexcode/spec-cli/dist/harness.js +3026 -0
  56. package/node_modules/@spexcode/spec-cli/dist/headless-controller.d.ts +9 -0
  57. package/node_modules/@spexcode/spec-cli/dist/headless-controller.js +47 -0
  58. package/node_modules/@spexcode/spec-cli/dist/help.d.ts +10 -0
  59. package/node_modules/@spexcode/spec-cli/dist/help.js +574 -0
  60. package/node_modules/@spexcode/spec-cli/dist/hook-prompts.d.ts +18 -0
  61. package/node_modules/@spexcode/spec-cli/dist/hook-prompts.js +122 -0
  62. package/node_modules/@spexcode/spec-cli/dist/hooks.d.ts +1 -0
  63. package/{spec-cli/src/hooks.ts → node_modules/@spexcode/spec-cli/dist/hooks.js} +15 -15
  64. package/node_modules/@spexcode/spec-cli/dist/host-resources.d.ts +97 -0
  65. package/node_modules/@spexcode/spec-cli/dist/host-resources.js +795 -0
  66. package/node_modules/@spexcode/spec-cli/dist/host.d.ts +88 -0
  67. package/node_modules/@spexcode/spec-cli/dist/host.js +663 -0
  68. package/node_modules/@spexcode/spec-cli/dist/index.d.ts +1 -0
  69. package/node_modules/@spexcode/spec-cli/dist/index.js +899 -0
  70. package/node_modules/@spexcode/spec-cli/dist/init.d.ts +1 -0
  71. package/node_modules/@spexcode/spec-cli/dist/init.js +284 -0
  72. package/node_modules/@spexcode/spec-cli/dist/issues-cli.d.ts +6 -0
  73. package/node_modules/@spexcode/spec-cli/dist/issues-cli.js +334 -0
  74. package/node_modules/@spexcode/spec-cli/dist/issues.d.ts +69 -0
  75. package/node_modules/@spexcode/spec-cli/dist/issues.js +189 -0
  76. package/node_modules/@spexcode/spec-cli/dist/lint.d.ts +34 -0
  77. package/node_modules/@spexcode/spec-cli/dist/lint.js +476 -0
  78. package/node_modules/@spexcode/spec-cli/dist/listen.d.ts +7 -0
  79. package/{spec-cli/src/listen.ts → node_modules/@spexcode/spec-cli/dist/listen.js} +13 -17
  80. package/node_modules/@spexcode/spec-cli/dist/localIssues.d.ts +65 -0
  81. package/node_modules/@spexcode/spec-cli/dist/localIssues.js +582 -0
  82. package/node_modules/@spexcode/spec-cli/dist/login-page.d.ts +5 -0
  83. package/{spec-cli/src/login-page.ts → node_modules/@spexcode/spec-cli/dist/login-page.js} +7 -7
  84. package/node_modules/@spexcode/spec-cli/dist/loop-in.d.ts +23 -0
  85. package/{spec-cli/src/loop-in.ts → node_modules/@spexcode/spec-cli/dist/loop-in.js} +35 -45
  86. package/node_modules/@spexcode/spec-cli/dist/machine-peer.d.ts +69 -0
  87. package/node_modules/@spexcode/spec-cli/dist/machine-peer.js +603 -0
  88. package/node_modules/@spexcode/spec-cli/dist/materialize.d.ts +18 -0
  89. package/node_modules/@spexcode/spec-cli/dist/materialize.js +468 -0
  90. package/node_modules/@spexcode/spec-cli/dist/mentions.d.ts +50 -0
  91. package/node_modules/@spexcode/spec-cli/dist/mentions.js +117 -0
  92. package/node_modules/@spexcode/spec-cli/dist/opencode-headless.d.ts +4 -0
  93. package/node_modules/@spexcode/spec-cli/dist/opencode-headless.js +208 -0
  94. package/node_modules/@spexcode/spec-cli/dist/opencode.d.ts +3 -0
  95. package/{spec-cli/src/opencode.ts → node_modules/@spexcode/spec-cli/dist/opencode.js} +9 -13
  96. package/node_modules/@spexcode/spec-cli/dist/pi-harness.d.ts +3 -0
  97. package/{spec-cli/src/pi-harness.ts → node_modules/@spexcode/spec-cli/dist/pi-harness.js} +45 -40
  98. package/node_modules/@spexcode/spec-cli/dist/pi-headless.d.ts +25 -0
  99. package/node_modules/@spexcode/spec-cli/dist/pi-headless.js +196 -0
  100. package/node_modules/@spexcode/spec-cli/dist/plugin-harness.d.ts +21 -0
  101. package/node_modules/@spexcode/spec-cli/dist/plugin-harness.js +146 -0
  102. package/node_modules/@spexcode/spec-cli/dist/pty-bridge.d.ts +10 -0
  103. package/node_modules/@spexcode/spec-cli/dist/pty-bridge.js +404 -0
  104. package/node_modules/@spexcode/spec-cli/dist/pty-helper.d.mts +1 -0
  105. package/node_modules/@spexcode/spec-cli/dist/pty-helper.mjs +101 -0
  106. package/node_modules/@spexcode/spec-cli/dist/pty-native-helper.d.mts +2 -0
  107. package/node_modules/@spexcode/spec-cli/dist/pty-native-helper.mjs +23 -0
  108. package/node_modules/@spexcode/spec-cli/dist/public-graph.d.ts +46 -0
  109. package/node_modules/@spexcode/spec-cli/dist/public-graph.js +56 -0
  110. package/node_modules/@spexcode/spec-cli/dist/ranker.d.ts +15 -0
  111. package/node_modules/@spexcode/spec-cli/dist/ranker.js +188 -0
  112. package/node_modules/@spexcode/spec-cli/dist/reaper.d.ts +6 -0
  113. package/node_modules/@spexcode/spec-cli/dist/reaper.js +81 -0
  114. package/node_modules/@spexcode/spec-cli/dist/reviews.d.ts +189 -0
  115. package/node_modules/@spexcode/spec-cli/dist/reviews.js +350 -0
  116. package/node_modules/@spexcode/spec-cli/dist/runtime-guard.d.ts +6 -0
  117. package/node_modules/@spexcode/spec-cli/dist/runtime-guard.js +37 -0
  118. package/node_modules/@spexcode/spec-cli/dist/runtime-ownership.d.ts +21 -0
  119. package/node_modules/@spexcode/spec-cli/dist/runtime-ownership.js +84 -0
  120. package/node_modules/@spexcode/spec-cli/dist/search.bench.d.mts +1 -0
  121. package/node_modules/@spexcode/spec-cli/dist/search.bench.mjs +75 -0
  122. package/node_modules/@spexcode/spec-cli/dist/search.d.ts +20 -0
  123. package/node_modules/@spexcode/spec-cli/dist/search.js +61 -0
  124. package/node_modules/@spexcode/spec-cli/dist/session-cursors.d.ts +14 -0
  125. package/node_modules/@spexcode/spec-cli/dist/session-cursors.js +82 -0
  126. package/node_modules/@spexcode/spec-cli/dist/session-declarations.d.ts +13 -0
  127. package/node_modules/@spexcode/spec-cli/dist/session-declarations.js +126 -0
  128. package/node_modules/@spexcode/spec-cli/dist/session-execution.d.ts +10 -0
  129. package/node_modules/@spexcode/spec-cli/dist/session-execution.js +70 -0
  130. package/node_modules/@spexcode/spec-cli/dist/session-files.d.ts +28 -0
  131. package/node_modules/@spexcode/spec-cli/dist/session-files.js +130 -0
  132. package/node_modules/@spexcode/spec-cli/dist/session-follow.d.ts +31 -0
  133. package/node_modules/@spexcode/spec-cli/dist/session-follow.js +179 -0
  134. package/node_modules/@spexcode/spec-cli/dist/session-reparent.d.ts +10 -0
  135. package/node_modules/@spexcode/spec-cli/dist/session-reparent.js +33 -0
  136. package/node_modules/@spexcode/spec-cli/dist/session-timeline.d.ts +51 -0
  137. package/node_modules/@spexcode/spec-cli/dist/session-timeline.js +233 -0
  138. package/node_modules/@spexcode/spec-cli/dist/session-web.d.ts +24 -0
  139. package/node_modules/@spexcode/spec-cli/dist/session-web.js +141 -0
  140. package/node_modules/@spexcode/spec-cli/dist/sessions.d.ts +412 -0
  141. package/node_modules/@spexcode/spec-cli/dist/sessions.js +4239 -0
  142. package/node_modules/@spexcode/spec-cli/dist/sh.d.ts +1 -0
  143. package/node_modules/@spexcode/spec-cli/dist/sh.js +3 -0
  144. package/node_modules/@spexcode/spec-cli/dist/shim-runtime.d.ts +1 -0
  145. package/{spec-cli/src/shim-runtime.ts → node_modules/@spexcode/spec-cli/dist/shim-runtime.js} +3 -4
  146. package/node_modules/@spexcode/spec-cli/dist/slash-commands.d.ts +9 -0
  147. package/node_modules/@spexcode/spec-cli/dist/slash-commands.js +307 -0
  148. package/node_modules/@spexcode/spec-cli/dist/source-files.d.ts +9 -0
  149. package/node_modules/@spexcode/spec-cli/dist/source-files.js +113 -0
  150. package/node_modules/@spexcode/spec-cli/dist/supervise.d.ts +1 -0
  151. package/node_modules/@spexcode/spec-cli/dist/supervise.js +308 -0
  152. package/node_modules/@spexcode/spec-cli/dist/tree.d.ts +25 -0
  153. package/node_modules/@spexcode/spec-cli/dist/tree.js +98 -0
  154. package/node_modules/@spexcode/spec-cli/dist/tsx-bin.d.ts +3 -0
  155. package/node_modules/@spexcode/spec-cli/dist/tsx-bin.js +41 -0
  156. package/node_modules/@spexcode/spec-cli/dist/uninstall.d.ts +3 -0
  157. package/node_modules/@spexcode/spec-cli/dist/uninstall.js +177 -0
  158. package/node_modules/@spexcode/spec-cli/dist/uploads.d.ts +36 -0
  159. package/node_modules/@spexcode/spec-cli/dist/uploads.js +243 -0
  160. package/node_modules/@spexcode/spec-cli/dist/worktree-sources.d.ts +1 -0
  161. package/node_modules/@spexcode/spec-cli/dist/worktree-sources.js +47 -0
  162. package/{spec-cli → node_modules/@spexcode/spec-cli}/hooks/dispatch.sh +5 -5
  163. package/{spec-cli → node_modules/@spexcode/spec-cli}/hooks/harness.sh +1 -1
  164. package/node_modules/@spexcode/spec-cli/package.json +47 -0
  165. package/node_modules/@spexcode/spec-cli/templates/spec/project/.plugins/commands/supervisor/spec.md +8 -0
  166. package/{spec-cli → node_modules/@spexcode/spec-cli}/templates/spec/project/.plugins/core/spec-first/spec-first.sh +1 -1
  167. package/{spec-cli → node_modules/@spexcode/spec-cli}/templates/spec/project/.plugins/core/spec-of-file/spec-of-file.sh +1 -0
  168. package/{spec-cli → node_modules/@spexcode/spec-cli}/templates/spec/project/.plugins/core/stop-gate/spec.md +2 -2
  169. package/{spec-cli → node_modules/@spexcode/spec-cli}/templates/spec/project/.plugins/core/stop-gate/stop-gate.sh +31 -29
  170. package/node_modules/@spexcode/spec-core/dist/anchors.d.ts +94 -0
  171. package/node_modules/@spexcode/spec-core/dist/anchors.js +730 -0
  172. package/node_modules/@spexcode/spec-core/dist/git.d.ts +166 -0
  173. package/node_modules/@spexcode/spec-core/dist/git.js +2736 -0
  174. package/node_modules/@spexcode/spec-core/dist/graph-delta.d.ts +44 -0
  175. package/node_modules/@spexcode/spec-core/dist/graph-delta.js +72 -0
  176. package/node_modules/@spexcode/spec-core/dist/graph.d.ts +34 -0
  177. package/node_modules/@spexcode/spec-core/dist/graph.js +237 -0
  178. package/node_modules/@spexcode/spec-core/dist/graphDelta.d.ts +3 -0
  179. package/node_modules/@spexcode/spec-core/dist/graphDelta.js +13 -0
  180. package/node_modules/@spexcode/spec-core/dist/harness-identity.d.ts +31 -0
  181. package/node_modules/@spexcode/spec-core/dist/harness-identity.js +20 -0
  182. package/node_modules/@spexcode/spec-core/dist/identity-presets.d.ts +152 -0
  183. package/node_modules/@spexcode/spec-core/dist/identity-presets.js +132 -0
  184. package/node_modules/@spexcode/spec-core/dist/index.d.ts +45 -0
  185. package/node_modules/@spexcode/spec-core/dist/index.js +19 -0
  186. package/node_modules/@spexcode/spec-core/dist/layout.d.ts +183 -0
  187. package/node_modules/@spexcode/spec-core/dist/layout.js +548 -0
  188. package/node_modules/@spexcode/spec-core/dist/process-identity.d.ts +37 -0
  189. package/node_modules/@spexcode/spec-core/dist/process-identity.js +214 -0
  190. package/node_modules/@spexcode/spec-core/dist/project-identity.d.ts +12 -0
  191. package/node_modules/@spexcode/spec-core/dist/project-identity.js +71 -0
  192. package/node_modules/@spexcode/spec-core/dist/project-store.d.ts +3 -0
  193. package/node_modules/@spexcode/spec-core/dist/project-store.js +14 -0
  194. package/node_modules/@spexcode/spec-core/dist/resilience.d.ts +2 -0
  195. package/node_modules/@spexcode/spec-core/dist/resilience.js +40 -0
  196. package/node_modules/@spexcode/spec-core/dist/review/index.d.ts +3 -0
  197. package/node_modules/@spexcode/spec-core/dist/review/index.js +5 -0
  198. package/node_modules/@spexcode/spec-core/dist/review/reviewFilters.d.ts +77 -0
  199. package/node_modules/@spexcode/spec-core/dist/review/reviewFilters.js +308 -0
  200. package/node_modules/@spexcode/spec-core/dist/review/reviewQuery.d.ts +66 -0
  201. package/node_modules/@spexcode/spec-core/dist/review/reviewQuery.js +180 -0
  202. package/node_modules/@spexcode/spec-core/dist/review/session.d.ts +4 -0
  203. package/node_modules/@spexcode/spec-core/dist/review/session.js +8 -0
  204. package/node_modules/@spexcode/spec-core/dist/reviewSnapshot.d.ts +15 -0
  205. package/node_modules/@spexcode/spec-core/dist/reviewSnapshot.js +12 -0
  206. package/node_modules/@spexcode/spec-core/dist/root-lru.d.ts +4 -0
  207. package/{spec-cli/src/root-lru.ts → node_modules/@spexcode/spec-core/dist/root-lru.js} +26 -30
  208. package/node_modules/@spexcode/spec-core/dist/specs.d.ts +117 -0
  209. package/node_modules/@spexcode/spec-core/dist/specs.js +489 -0
  210. package/node_modules/@spexcode/spec-core/package.json +28 -0
  211. package/node_modules/@spexcode/spec-eval/dist/cache.d.ts +10 -0
  212. package/node_modules/@spexcode/spec-eval/dist/cache.js +50 -0
  213. package/node_modules/@spexcode/spec-eval/dist/cli.d.ts +22 -0
  214. package/node_modules/@spexcode/spec-eval/dist/cli.js +981 -0
  215. package/node_modules/@spexcode/spec-eval/dist/evaltab.d.ts +98 -0
  216. package/node_modules/@spexcode/spec-eval/dist/evaltab.js +176 -0
  217. package/node_modules/@spexcode/spec-eval/dist/filing.d.ts +16 -0
  218. package/node_modules/@spexcode/spec-eval/dist/filing.js +36 -0
  219. package/node_modules/@spexcode/spec-eval/dist/freshness.d.ts +48 -0
  220. package/node_modules/@spexcode/spec-eval/dist/freshness.js +799 -0
  221. package/node_modules/@spexcode/spec-eval/dist/host.d.ts +46 -0
  222. package/node_modules/@spexcode/spec-eval/dist/host.js +53 -0
  223. package/node_modules/@spexcode/spec-eval/dist/humanok.d.ts +11 -0
  224. package/node_modules/@spexcode/spec-eval/dist/humanok.js +27 -0
  225. package/node_modules/@spexcode/spec-eval/dist/index.d.ts +4 -0
  226. package/node_modules/@spexcode/spec-eval/dist/index.js +4 -0
  227. package/node_modules/@spexcode/spec-eval/dist/remarks.d.ts +31 -0
  228. package/node_modules/@spexcode/spec-eval/dist/remarks.js +1 -0
  229. package/node_modules/@spexcode/spec-eval/dist/scenariofresh.d.ts +13 -0
  230. package/node_modules/@spexcode/spec-eval/dist/scenariofresh.js +303 -0
  231. package/node_modules/@spexcode/spec-eval/dist/scenarios.d.ts +99 -0
  232. package/node_modules/@spexcode/spec-eval/dist/scenarios.js +650 -0
  233. package/node_modules/@spexcode/spec-eval/dist/sessioneval.d.ts +307 -0
  234. package/node_modules/@spexcode/spec-eval/dist/sessioneval.js +1883 -0
  235. package/node_modules/@spexcode/spec-eval/dist/sidecar.d.ts +55 -0
  236. package/node_modules/@spexcode/spec-eval/dist/sidecar.js +82 -0
  237. package/node_modules/@spexcode/spec-eval/dist/timeline.d.ts +25 -0
  238. package/node_modules/@spexcode/spec-eval/dist/timeline.js +65 -0
  239. package/node_modules/@spexcode/spec-eval/dist/ui-path.d.ts +1 -0
  240. package/node_modules/@spexcode/spec-eval/dist/ui-path.js +2 -0
  241. package/node_modules/@spexcode/spec-eval/package.json +36 -0
  242. package/node_modules/@spexcode/spec-forge/dist/cache.d.ts +29 -0
  243. package/node_modules/@spexcode/spec-forge/dist/cache.js +59 -0
  244. package/node_modules/@spexcode/spec-forge/dist/cli.d.ts +1 -0
  245. package/node_modules/@spexcode/spec-forge/dist/cli.js +100 -0
  246. package/node_modules/@spexcode/spec-forge/dist/drivers/github.d.ts +2 -0
  247. package/node_modules/@spexcode/spec-forge/dist/drivers/github.js +116 -0
  248. package/node_modules/@spexcode/spec-forge/dist/drivers/gitlab.d.ts +7 -0
  249. package/node_modules/@spexcode/spec-forge/dist/drivers/gitlab.js +144 -0
  250. package/node_modules/@spexcode/spec-forge/dist/drivers.d.ts +10 -0
  251. package/node_modules/@spexcode/spec-forge/dist/drivers.js +83 -0
  252. package/node_modules/@spexcode/spec-forge/dist/index.d.ts +4 -0
  253. package/node_modules/@spexcode/spec-forge/dist/index.js +4 -0
  254. package/node_modules/@spexcode/spec-forge/dist/links.d.ts +15 -0
  255. package/node_modules/@spexcode/spec-forge/dist/links.js +65 -0
  256. package/node_modules/@spexcode/spec-forge/dist/needs-eval.d.ts +10 -0
  257. package/node_modules/@spexcode/spec-forge/dist/needs-eval.js +20 -0
  258. package/node_modules/@spexcode/spec-forge/dist/port.d.ts +53 -0
  259. package/node_modules/@spexcode/spec-forge/dist/port.js +1 -0
  260. package/node_modules/@spexcode/spec-forge/dist/resident.d.ts +7 -0
  261. package/node_modules/@spexcode/spec-forge/dist/resident.js +43 -0
  262. package/node_modules/@spexcode/spec-forge/package.json +32 -0
  263. package/package.json +33 -19
  264. package/spec-cli/bin/spex.mjs +0 -69
  265. package/spec-cli/src/anchors.ts +0 -728
  266. package/spec-cli/src/attach.ts +0 -43
  267. package/spec-cli/src/claude-headless.ts +0 -321
  268. package/spec-cli/src/cli.ts +0 -1258
  269. package/spec-cli/src/client.ts +0 -414
  270. package/spec-cli/src/cockpit.ts +0 -43
  271. package/spec-cli/src/codex-runtime-generations.ts +0 -646
  272. package/spec-cli/src/commit-surgery.ts +0 -68
  273. package/spec-cli/src/contract-filter.ts +0 -202
  274. package/spec-cli/src/delivery-queue.ts +0 -165
  275. package/spec-cli/src/doctor.ts +0 -516
  276. package/spec-cli/src/execution-trace.ts +0 -444
  277. package/spec-cli/src/file-write.ts +0 -22
  278. package/spec-cli/src/gateway-auth.ts +0 -181
  279. package/spec-cli/src/gateway-hub.ts +0 -358
  280. package/spec-cli/src/gateway.ts +0 -461
  281. package/spec-cli/src/git.ts +0 -2556
  282. package/spec-cli/src/graph.ts +0 -271
  283. package/spec-cli/src/graphCache.ts +0 -710
  284. package/spec-cli/src/graphDelta.ts +0 -77
  285. package/spec-cli/src/graphStream.ts +0 -1019
  286. package/spec-cli/src/harness-select.ts +0 -77
  287. package/spec-cli/src/harness.ts +0 -3025
  288. package/spec-cli/src/headless-controller.ts +0 -63
  289. package/spec-cli/src/help.ts +0 -492
  290. package/spec-cli/src/host-resources.ts +0 -762
  291. package/spec-cli/src/host.ts +0 -611
  292. package/spec-cli/src/identity-presets.d.ts +0 -13
  293. package/spec-cli/src/identity-presets.js +0 -138
  294. package/spec-cli/src/index.ts +0 -818
  295. package/spec-cli/src/init.ts +0 -258
  296. package/spec-cli/src/issues-cli.ts +0 -276
  297. package/spec-cli/src/issues.ts +0 -237
  298. package/spec-cli/src/layout.ts +0 -640
  299. package/spec-cli/src/lint.ts +0 -488
  300. package/spec-cli/src/localIssues.ts +0 -538
  301. package/spec-cli/src/materialize.ts +0 -417
  302. package/spec-cli/src/mentions.ts +0 -81
  303. package/spec-cli/src/opencode-headless.ts +0 -205
  304. package/spec-cli/src/pi-headless.ts +0 -153
  305. package/spec-cli/src/plugin-harness.ts +0 -157
  306. package/spec-cli/src/process-identity.ts +0 -207
  307. package/spec-cli/src/project-identity.ts +0 -73
  308. package/spec-cli/src/project-store.ts +0 -17
  309. package/spec-cli/src/pty-bridge.ts +0 -396
  310. package/spec-cli/src/pty-helper.mjs +0 -96
  311. package/spec-cli/src/pty-native-helper.mjs +0 -22
  312. package/spec-cli/src/ranker.ts +0 -191
  313. package/spec-cli/src/reaper.ts +0 -125
  314. package/spec-cli/src/resilience.ts +0 -41
  315. package/spec-cli/src/reviewSnapshot.ts +0 -28
  316. package/spec-cli/src/reviews.ts +0 -434
  317. package/spec-cli/src/runtime-guard.ts +0 -35
  318. package/spec-cli/src/runtime-ownership.ts +0 -88
  319. package/spec-cli/src/search.bench.mjs +0 -80
  320. package/spec-cli/src/search.ts +0 -63
  321. package/spec-cli/src/session-cursors.ts +0 -91
  322. package/spec-cli/src/session-execution.ts +0 -68
  323. package/spec-cli/src/session-files.ts +0 -121
  324. package/spec-cli/src/session-follow.ts +0 -182
  325. package/spec-cli/src/session-reparent.ts +0 -33
  326. package/spec-cli/src/session-timeline.ts +0 -247
  327. package/spec-cli/src/session-web.ts +0 -135
  328. package/spec-cli/src/sessions.ts +0 -3883
  329. package/spec-cli/src/sh.ts +0 -3
  330. package/spec-cli/src/slash-commands.ts +0 -318
  331. package/spec-cli/src/source-files.ts +0 -104
  332. package/spec-cli/src/specs.ts +0 -498
  333. package/spec-cli/src/supervise.ts +0 -245
  334. package/spec-cli/src/tree.ts +0 -118
  335. package/spec-cli/src/tsx-bin.ts +0 -18
  336. package/spec-cli/src/uninstall.ts +0 -171
  337. package/spec-cli/src/uploads.ts +0 -253
  338. package/spec-cli/src/worktree-sources.ts +0 -42
  339. package/spec-cli/templates/spec/project/.plugins/commands/supervisor/spec.md +0 -8
  340. package/spec-dashboard/dist/assets/App-B6IbFbGu.js +0 -2
  341. package/spec-dashboard/dist/assets/Dashboard-C5ap-Sga.css +0 -1
  342. package/spec-dashboard/dist/assets/Dashboard-_sDk7Rcg.js +0 -27
  343. package/spec-dashboard/dist/assets/EvalsPage-BT357Lyt.js +0 -2
  344. package/spec-dashboard/dist/assets/IssuesPage-BG9rNOgV.js +0 -1
  345. package/spec-dashboard/dist/assets/KaTeX_AMS-Regular-BQhdFMY1.woff2 +0 -0
  346. package/spec-dashboard/dist/assets/KaTeX_AMS-Regular-DMm9YOAa.woff +0 -0
  347. package/spec-dashboard/dist/assets/KaTeX_AMS-Regular-DRggAlZN.ttf +0 -0
  348. package/spec-dashboard/dist/assets/KaTeX_Caligraphic-Bold-ATXxdsX0.ttf +0 -0
  349. package/spec-dashboard/dist/assets/KaTeX_Caligraphic-Bold-BEiXGLvX.woff +0 -0
  350. package/spec-dashboard/dist/assets/KaTeX_Caligraphic-Bold-Dq_IR9rO.woff2 +0 -0
  351. package/spec-dashboard/dist/assets/KaTeX_Caligraphic-Regular-CTRA-rTL.woff +0 -0
  352. package/spec-dashboard/dist/assets/KaTeX_Caligraphic-Regular-Di6jR-x-.woff2 +0 -0
  353. package/spec-dashboard/dist/assets/KaTeX_Caligraphic-Regular-wX97UBjC.ttf +0 -0
  354. package/spec-dashboard/dist/assets/KaTeX_Fraktur-Bold-BdnERNNW.ttf +0 -0
  355. package/spec-dashboard/dist/assets/KaTeX_Fraktur-Bold-BsDP51OF.woff +0 -0
  356. package/spec-dashboard/dist/assets/KaTeX_Fraktur-Bold-CL6g_b3V.woff2 +0 -0
  357. package/spec-dashboard/dist/assets/KaTeX_Fraktur-Regular-CB_wures.ttf +0 -0
  358. package/spec-dashboard/dist/assets/KaTeX_Fraktur-Regular-CTYiF6lA.woff2 +0 -0
  359. package/spec-dashboard/dist/assets/KaTeX_Fraktur-Regular-Dxdc4cR9.woff +0 -0
  360. package/spec-dashboard/dist/assets/KaTeX_Main-Bold-Cx986IdX.woff2 +0 -0
  361. package/spec-dashboard/dist/assets/KaTeX_Main-Bold-Jm3AIy58.woff +0 -0
  362. package/spec-dashboard/dist/assets/KaTeX_Main-Bold-waoOVXN0.ttf +0 -0
  363. package/spec-dashboard/dist/assets/KaTeX_Main-BoldItalic-DxDJ3AOS.woff2 +0 -0
  364. package/spec-dashboard/dist/assets/KaTeX_Main-BoldItalic-DzxPMmG6.ttf +0 -0
  365. package/spec-dashboard/dist/assets/KaTeX_Main-BoldItalic-SpSLRI95.woff +0 -0
  366. package/spec-dashboard/dist/assets/KaTeX_Main-Italic-3WenGoN9.ttf +0 -0
  367. package/spec-dashboard/dist/assets/KaTeX_Main-Italic-BMLOBm91.woff +0 -0
  368. package/spec-dashboard/dist/assets/KaTeX_Main-Italic-NWA7e6Wa.woff2 +0 -0
  369. package/spec-dashboard/dist/assets/KaTeX_Main-Regular-B22Nviop.woff2 +0 -0
  370. package/spec-dashboard/dist/assets/KaTeX_Main-Regular-Dr94JaBh.woff +0 -0
  371. package/spec-dashboard/dist/assets/KaTeX_Main-Regular-ypZvNtVU.ttf +0 -0
  372. package/spec-dashboard/dist/assets/KaTeX_Math-BoldItalic-B3XSjfu4.ttf +0 -0
  373. package/spec-dashboard/dist/assets/KaTeX_Math-BoldItalic-CZnvNsCZ.woff2 +0 -0
  374. package/spec-dashboard/dist/assets/KaTeX_Math-BoldItalic-iY-2wyZ7.woff +0 -0
  375. package/spec-dashboard/dist/assets/KaTeX_Math-Italic-DA0__PXp.woff +0 -0
  376. package/spec-dashboard/dist/assets/KaTeX_Math-Italic-flOr_0UB.ttf +0 -0
  377. package/spec-dashboard/dist/assets/KaTeX_Math-Italic-t53AETM-.woff2 +0 -0
  378. package/spec-dashboard/dist/assets/KaTeX_SansSerif-Bold-CFMepnvq.ttf +0 -0
  379. package/spec-dashboard/dist/assets/KaTeX_SansSerif-Bold-D1sUS0GD.woff2 +0 -0
  380. package/spec-dashboard/dist/assets/KaTeX_SansSerif-Bold-DbIhKOiC.woff +0 -0
  381. package/spec-dashboard/dist/assets/KaTeX_SansSerif-Italic-C3H0VqGB.woff2 +0 -0
  382. package/spec-dashboard/dist/assets/KaTeX_SansSerif-Italic-DN2j7dab.woff +0 -0
  383. package/spec-dashboard/dist/assets/KaTeX_SansSerif-Italic-YYjJ1zSn.ttf +0 -0
  384. package/spec-dashboard/dist/assets/KaTeX_SansSerif-Regular-BNo7hRIc.ttf +0 -0
  385. package/spec-dashboard/dist/assets/KaTeX_SansSerif-Regular-CS6fqUqJ.woff +0 -0
  386. package/spec-dashboard/dist/assets/KaTeX_SansSerif-Regular-DDBCnlJ7.woff2 +0 -0
  387. package/spec-dashboard/dist/assets/KaTeX_Script-Regular-C5JkGWo-.ttf +0 -0
  388. package/spec-dashboard/dist/assets/KaTeX_Script-Regular-D3wIWfF6.woff2 +0 -0
  389. package/spec-dashboard/dist/assets/KaTeX_Script-Regular-D5yQViql.woff +0 -0
  390. package/spec-dashboard/dist/assets/KaTeX_Size1-Regular-C195tn64.woff +0 -0
  391. package/spec-dashboard/dist/assets/KaTeX_Size1-Regular-Dbsnue_I.ttf +0 -0
  392. package/spec-dashboard/dist/assets/KaTeX_Size1-Regular-mCD8mA8B.woff2 +0 -0
  393. package/spec-dashboard/dist/assets/KaTeX_Size2-Regular-B7gKUWhC.ttf +0 -0
  394. package/spec-dashboard/dist/assets/KaTeX_Size2-Regular-Dy4dx90m.woff2 +0 -0
  395. package/spec-dashboard/dist/assets/KaTeX_Size2-Regular-oD1tc_U0.woff +0 -0
  396. package/spec-dashboard/dist/assets/KaTeX_Size3-Regular-CTq5MqoE.woff +0 -0
  397. package/spec-dashboard/dist/assets/KaTeX_Size3-Regular-DgpXs0kz.ttf +0 -0
  398. package/spec-dashboard/dist/assets/KaTeX_Size4-Regular-BF-4gkZK.woff +0 -0
  399. package/spec-dashboard/dist/assets/KaTeX_Size4-Regular-DWFBv043.ttf +0 -0
  400. package/spec-dashboard/dist/assets/KaTeX_Size4-Regular-Dl5lxZxV.woff2 +0 -0
  401. package/spec-dashboard/dist/assets/KaTeX_Typewriter-Regular-C0xS9mPB.woff +0 -0
  402. package/spec-dashboard/dist/assets/KaTeX_Typewriter-Regular-CO6r4hn1.woff2 +0 -0
  403. package/spec-dashboard/dist/assets/KaTeX_Typewriter-Regular-D3Ib7_Hf.ttf +0 -0
  404. package/spec-dashboard/dist/assets/MobileApp-D9yPMR3A.js +0 -2
  405. package/spec-dashboard/dist/assets/Modal-B3EcgeA3.js +0 -1
  406. package/spec-dashboard/dist/assets/PageScroll-CNZOugWV.js +0 -1
  407. package/spec-dashboard/dist/assets/ProjectsPage-C2CRHmvs.js +0 -1
  408. package/spec-dashboard/dist/assets/SessionInterface-6GBZ9nXN.css +0 -32
  409. package/spec-dashboard/dist/assets/SessionInterface-DOEjuT3l.js +0 -40
  410. package/spec-dashboard/dist/assets/SessionWindow-1nYgZ4CL.js +0 -1
  411. package/spec-dashboard/dist/assets/Settings-7WeTJn0V.js +0 -1
  412. package/spec-dashboard/dist/assets/Thread-CEbzAFQn.js +0 -13
  413. package/spec-dashboard/dist/assets/bindings-BC9vqpYU.js +0 -1
  414. package/spec-dashboard/dist/assets/data-SlvxNA2r.js +0 -1
  415. package/spec-dashboard/dist/assets/index-DLVeOHL-.js +0 -41
  416. package/spec-dashboard/dist/assets/index-DYLGfPjx.css +0 -1
  417. package/spec-dashboard/dist/assets/launch-Cp579UoJ.css +0 -1
  418. package/spec-dashboard/dist/assets/launch-yxCYr64x.js +0 -276
  419. package/spec-dashboard/dist/assets/sessionSurface--7l69h4F.js +0 -1
  420. package/spec-dashboard/dist/index.html +0 -29
  421. package/spec-dashboard/src/reviewFilters.js +0 -322
  422. package/spec-dashboard/src/reviewQuery.js +0 -174
  423. package/spec-dashboard/src/session.js +0 -199
  424. package/spec-eval/src/cache.ts +0 -57
  425. package/spec-eval/src/cli.ts +0 -894
  426. package/spec-eval/src/evaltab.ts +0 -264
  427. package/spec-eval/src/filing.ts +0 -39
  428. package/spec-eval/src/freshness.ts +0 -836
  429. package/spec-eval/src/humanok.ts +0 -28
  430. package/spec-eval/src/scenariofresh.ts +0 -244
  431. package/spec-eval/src/scenarios.ts +0 -672
  432. package/spec-eval/src/sessioneval.ts +0 -2220
  433. package/spec-eval/src/sidecar.ts +0 -93
  434. package/spec-eval/src/timeline.ts +0 -54
  435. package/spec-forge/src/__fixtures__/github-forge.json +0 -63
  436. package/spec-forge/src/cache.ts +0 -66
  437. package/spec-forge/src/cli.ts +0 -99
  438. package/spec-forge/src/drivers/github.ts +0 -132
  439. package/spec-forge/src/drivers/gitlab.ts +0 -153
  440. package/spec-forge/src/drivers.ts +0 -73
  441. package/spec-forge/src/links.ts +0 -72
  442. package/spec-forge/src/needs-eval.ts +0 -29
  443. package/spec-forge/src/port.ts +0 -36
  444. package/spec-forge/src/resident.ts +0 -46
  445. /package/{spec-cli → node_modules/@spexcode/spec-cli}/README.md +0 -0
  446. /package/{spec-cli → node_modules/@spexcode/spec-cli}/hooks/compat/mark-active-0.5.2-eef1.fixture +0 -0
  447. /package/{spec-cli → node_modules/@spexcode/spec-cli}/hooks/compat/mark-active-sed-v0.fixture +0 -0
  448. /package/{spec-cli → node_modules/@spexcode/spec-cli}/templates/hooks/commit-msg +0 -0
  449. /package/{spec-cli → node_modules/@spexcode/spec-cli}/templates/hooks/post-checkout +0 -0
  450. /package/{spec-cli → node_modules/@spexcode/spec-cli}/templates/hooks/post-merge +0 -0
  451. /package/{spec-cli → node_modules/@spexcode/spec-cli}/templates/hooks/pre-commit +0 -0
  452. /package/{spec-cli → node_modules/@spexcode/spec-cli}/templates/hooks/prepare-commit-msg +0 -0
  453. /package/{spec-cli → node_modules/@spexcode/spec-cli}/templates/hooks/reference-transaction +0 -0
  454. /package/{spec-cli → node_modules/@spexcode/spec-cli}/templates/spec/project/.plugins/commands/extract/spec.md +0 -0
  455. /package/{spec-cli → node_modules/@spexcode/spec-cli}/templates/spec/project/.plugins/commands/regroup/spec.md +0 -0
  456. /package/{spec-cli → node_modules/@spexcode/spec-cli}/templates/spec/project/.plugins/commands/rename/spec.md +0 -0
  457. /package/{spec-cli → node_modules/@spexcode/spec-cli}/templates/spec/project/.plugins/commands/spec.md +0 -0
  458. /package/{spec-cli → node_modules/@spexcode/spec-cli}/templates/spec/project/.plugins/commands/tidy/spec.md +0 -0
  459. /package/{spec-cli → node_modules/@spexcode/spec-cli}/templates/spec/project/.plugins/core/comment-altitude/spec.md +0 -0
  460. /package/{spec-cli → node_modules/@spexcode/spec-cli}/templates/spec/project/.plugins/core/idle/idle.sh +0 -0
  461. /package/{spec-cli → node_modules/@spexcode/spec-cli}/templates/spec/project/.plugins/core/idle/spec.md +0 -0
  462. /package/{spec-cli → node_modules/@spexcode/spec-cli}/templates/spec/project/.plugins/core/mark-active/mark-active.sh +0 -0
  463. /package/{spec-cli → node_modules/@spexcode/spec-cli}/templates/spec/project/.plugins/core/mark-active/spec.md +0 -0
  464. /package/{spec-cli → node_modules/@spexcode/spec-cli}/templates/spec/project/.plugins/core/session-fail/fail.sh +0 -0
  465. /package/{spec-cli → node_modules/@spexcode/spec-cli}/templates/spec/project/.plugins/core/session-fail/spec.md +0 -0
  466. /package/{spec-cli → node_modules/@spexcode/spec-cli}/templates/spec/project/.plugins/core/spec-first/spec.md +0 -0
  467. /package/{spec-cli → node_modules/@spexcode/spec-cli}/templates/spec/project/.plugins/core/spec-of-file/spec.md +0 -0
  468. /package/{spec-cli → node_modules/@spexcode/spec-cli}/templates/spec/project/.plugins/core/spec.md +0 -0
  469. /package/{spec-cli → node_modules/@spexcode/spec-cli}/templates/spec/project/.plugins/prompts/atomic-landing/spec.md +0 -0
  470. /package/{spec-cli → node_modules/@spexcode/spec-cli}/templates/spec/project/.plugins/prompts/forge-link/spec.md +0 -0
  471. /package/{spec-cli → node_modules/@spexcode/spec-cli}/templates/spec/project/.plugins/prompts/memory-hygiene/spec.md +0 -0
  472. /package/{spec-cli → node_modules/@spexcode/spec-cli}/templates/spec/project/.plugins/prompts/reproduce-before-fix/spec.md +0 -0
  473. /package/{spec-cli → node_modules/@spexcode/spec-cli}/templates/spec/project/.plugins/prompts/spec.md +0 -0
  474. /package/{spec-cli → node_modules/@spexcode/spec-cli}/templates/spec/project/.plugins/skills/distill/digest.mjs +0 -0
  475. /package/{spec-cli → node_modules/@spexcode/spec-cli}/templates/spec/project/.plugins/skills/distill/spec.md +0 -0
  476. /package/{spec-cli → node_modules/@spexcode/spec-cli}/templates/spec/project/.plugins/skills/spec.md +0 -0
  477. /package/{spec-cli → node_modules/@spexcode/spec-cli}/templates/spec/project/.plugins/spec.md +0 -0
  478. /package/{spec-cli → node_modules/@spexcode/spec-cli}/templates/spec/project/spec.md +0 -0
  479. /package/{spec-cli → node_modules/@spexcode/spec-core}/templates/spexcode.json +0 -0
@@ -0,0 +1,476 @@
1
+ import { readFileSync, existsSync, statSync } from 'node:fs';
2
+ import { join } from 'node:path';
3
+ import { repoRoot, git, sourceIndexes, rowsFor, treeFilePaths, treeFileText, withEventLedgerBuild } from '@spexcode/spec-core';
4
+ import { loadSpecs, parseFrontmatter } from '@spexcode/spec-core';
5
+ import { readJsonConfig } from '@spexcode/spec-core';
6
+ import { extractors, extractorFor, extOf, parseCodeEntry, relationClaimsPath, resolveSelectors, windowEvents, anchorHitQueries } from '@spexcode/spec-core';
7
+ import { DEFAULT_TEST_GLOBS, sourcePolicyDescription, trackedSourceFiles } from './source-files.js';
8
+ export const SPEC_LINT_REPORT_PROJECTION = 'spex.spec-lint.report';
9
+ export const SPEC_LINT_REPORT_SCHEMA_VERSION = 1;
10
+ const DEFAULT_CONFIG = {
11
+ governedRoots: ['spec-dashboard/src', 'spec-cli/src'],
12
+ sourceIncludeGlobs: null,
13
+ sourceExcludeGlobs: [],
14
+ sourceExtensions: null,
15
+ testGlobs: DEFAULT_TEST_GLOBS,
16
+ maxOwners: 3,
17
+ scenarioTags: ['frontend-e2e', 'backend-api', 'cli', 'desktop', 'mobile'],
18
+ scopedCodeMiss: 'warn',
19
+ };
20
+ export function loadConfig(root, pendingSource) {
21
+ // Absent spexcode.json → tuned defaults; a MALFORMED one throws LOUD (readJsonConfig) rather than
22
+ // silently reverting the author's budgets to defaults and green-washing the very warnings they tuned.
23
+ let parsed;
24
+ if (pendingSource === undefined)
25
+ parsed = readJsonConfig(join(root, 'spexcode.json'));
26
+ else if (pendingSource === null)
27
+ parsed = {};
28
+ else {
29
+ try {
30
+ parsed = JSON.parse(pendingSource);
31
+ }
32
+ catch (e) {
33
+ throw new Error(`invalid JSON in candidate spexcode.json: ${e?.message ?? e}`);
34
+ }
35
+ }
36
+ const c = parsed?.lint ?? {};
37
+ const merged = { ...DEFAULT_CONFIG, ...c };
38
+ return normalizeConfig(merged);
39
+ }
40
+ // Compile every author-facing source selector into one include-minus-exclude/test policy. In particular,
41
+ // sourceExtensions is compatibility syntax only: it contributes include globs and never reaches discovery.
42
+ export function normalizeConfig(cfg) {
43
+ // a mistyped enum silently reverting to the default would green-wash (or over-warn) exactly the
44
+ // advisory the author meant to tune — same fail-loud rule as a malformed spexcode.json.
45
+ if (cfg.scopedCodeMiss !== 'warn' && cfg.scopedCodeMiss !== 'ignore')
46
+ throw new Error(`spexcode.json lint.scopedCodeMiss must be "warn" or "ignore", got ${JSON.stringify(cfg.scopedCodeMiss)}`);
47
+ const dedot = (xs) => xs.map((x) => x.replace(/^\.+/, ''));
48
+ const anyDepth = (xs) => xs.map((g) => (g.includes('/') ? g : `**/${g}`));
49
+ const extensions = cfg.sourceExtensions === null ? null : dedot(cfg.sourceExtensions);
50
+ const includes = cfg.sourceIncludeGlobs === null && extensions === null
51
+ ? null
52
+ : [...new Set([...anyDepth(cfg.sourceIncludeGlobs ?? []), ...(extensions ?? []).map((ext) => `**/*.${ext}`)])];
53
+ return {
54
+ ...cfg,
55
+ sourceIncludeGlobs: includes,
56
+ sourceExcludeGlobs: anyDepth(cfg.sourceExcludeGlobs),
57
+ sourceExtensions: extensions,
58
+ testGlobs: anyDepth(cfg.testGlobs),
59
+ };
60
+ }
61
+ function untrackedAdoptionFiles(root) {
62
+ if (git(['-C', root, 'rev-parse', '--is-inside-work-tree']).trim() !== 'true')
63
+ return [];
64
+ const status = git([
65
+ '-C', root,
66
+ '-c', 'core.quotePath=false',
67
+ 'status', '--porcelain=v1', '-z', '--untracked-files=all',
68
+ '--', '.spec', 'spexcode.json',
69
+ ]);
70
+ return status.split('\0')
71
+ .filter((entry) => entry.startsWith('?? '))
72
+ .map((entry) => entry.slice(3))
73
+ .filter(Boolean);
74
+ }
75
+ function pendingChangedPaths(root, tip) {
76
+ try {
77
+ git(['-C', root, 'rev-parse', `${tip}^{commit}`]);
78
+ // `-m` compares a merge with every parent. An `ours` merge can leave the result tree
79
+ // identical to its first parent while still making an unacknowledged side-branch commit
80
+ // reachable; first-parent-only paths would filter that debt out of the pending anchor window.
81
+ const fields = git(['-C', root, '-c', 'core.quotePath=false', 'diff-tree', '--no-commit-id', '--name-status', '-z', '-r', '-m', '-M', tip])
82
+ .split('\0').filter(Boolean);
83
+ const changed = [];
84
+ for (let i = 0; i < fields.length;) {
85
+ const status = fields[i++];
86
+ if (/^[RC]/.test(status)) {
87
+ changed.push(fields[i++], fields[i++]);
88
+ }
89
+ else {
90
+ changed.push(fields[i++]);
91
+ }
92
+ }
93
+ return [...new Set(changed.filter(Boolean))];
94
+ }
95
+ catch {
96
+ return [];
97
+ }
98
+ }
99
+ // Cheap pending classification for the reference hook. It reads only the candidate tree and claims; it
100
+ // never constructs either history index. A normal lint call deliberately does not use this
101
+ // shortcut so its full findings/oracle contract remains unchanged.
102
+ export async function pendingTouchesGoverned(root, tip) {
103
+ // A merge can introduce reachable side-branch debt without changing the result tree. The first-parent
104
+ // diff is insufficient for a scope proof, so all multi-parent candidates stay on the full lint path.
105
+ const parentCount = git(['-C', root, 'rev-list', '--parents', '-n1', tip]).trim().split(/\s+/).length - 1;
106
+ if (parentCount > 1)
107
+ return true;
108
+ const changed = pendingChangedPaths(root, tip);
109
+ if (!changed.length)
110
+ return true;
111
+ // `governedRoots` is source discovery policy, not the set of actual code claims: a spec may deliberately
112
+ // govern a path outside those roots. Read only the candidate spec tree (no history/drift indexes) so the
113
+ // scope proof follows the same code:/related: declarations that lint later enforces.
114
+ const specs = await loadSpecs(root, { tip, history: null, drift: null });
115
+ const claims = specs.flatMap((spec) => [...spec.code, ...spec.related]);
116
+ return changed.some((path) => claims.some((claim) => relationClaimsPath(claim, path))
117
+ || path === 'spexcode.json' || path === 'spexcode.local.json'
118
+ || (path.startsWith('.spec/') && !path.startsWith('.spec/.issues/'))
119
+ || path === '.spec');
120
+ }
121
+ export async function specLint(root = repoRoot(), regs = extractors(root), options = {}) {
122
+ return (await specLintRun(root, regs, options)).findings;
123
+ }
124
+ export async function specLintReport(root = repoRoot(), regs = extractors(root), options = {}) {
125
+ const run = await specLintRun(root, regs, options);
126
+ return {
127
+ projection: SPEC_LINT_REPORT_PROJECTION,
128
+ schemaVersion: SPEC_LINT_REPORT_SCHEMA_VERSION,
129
+ sourceFiles: run.sourceFiles,
130
+ findings: run.findings,
131
+ };
132
+ }
133
+ function specLintRun(root, regs, options) {
134
+ return withEventLedgerBuild(root, () => specLintInLedger(root, regs, options));
135
+ }
136
+ async function specLintInLedger(root, regs, options) {
137
+ const tip = options.tip ?? 'HEAD';
138
+ const pending = tip !== 'HEAD';
139
+ const changed = pending ? pendingChangedPaths(root, tip) : [];
140
+ const files = pending ? treeFilePaths(root, tip) : null;
141
+ const directories = new Set();
142
+ for (const file of files ?? []) {
143
+ const parts = file.split('/');
144
+ for (let i = 1; i < parts.length; i++)
145
+ directories.add(parts.slice(0, i).join('/'));
146
+ }
147
+ const existsAtTip = (path) => files ? files.has(path) || directories.has(path.replace(/\/+$/, '')) : existsSync(join(root, path));
148
+ const isDirectoryAtTip = (path) => files
149
+ ? !files.has(path) && directories.has(path.replace(/\/+$/, ''))
150
+ : statSync(join(root, path)).isDirectory();
151
+ const textAtTip = (path) => pending ? treeFileText(root, tip, path) : readFileSync(join(root, path), 'utf8');
152
+ const cfg = loadConfig(root, pending ? treeFileText(root, tip, 'spexcode.json') : undefined);
153
+ const untracked = untrackedAdoptionFiles(root);
154
+ if (untracked.length) {
155
+ const shown = untracked.slice(0, 6);
156
+ const suffix = untracked.length > shown.length ? ` (+${untracked.length - shown.length} more)` : '';
157
+ return {
158
+ sourceFiles: [],
159
+ findings: [{
160
+ level: 'error',
161
+ rule: 'integrity',
162
+ msg: `project source of truth is untracked: ${shown.join(', ')}${suffix} — add it with \`git add .spec spexcode.json\` and commit it; generated harness files such as .codex/, .claude/, and AGENTS.md are machine-local`,
163
+ }],
164
+ };
165
+ }
166
+ const governed = trackedSourceFiles(root, cfg.governedRoots, cfg, tip);
167
+ const [hidx, didx] = await sourceIndexes(root, tip);
168
+ const specs = await loadSpecs(root, { tip, history: hidx, drift: didx });
169
+ const out = [];
170
+ // integrity + build the file -> owners map. A relation's STRUCTURAL problems (a duplicate entry,
171
+ // bare/scoped mixing on one base path, a selector on a glob — all from parseRelation,
172
+ // [[code-anchor]]) are integrity errors: malformed edges block like broken ones.
173
+ const owners = new Map();
174
+ const claimed = new Set();
175
+ const governedClaims = new Set();
176
+ for (const s of specs) {
177
+ for (const p of s.relationProblems)
178
+ out.push({ level: 'error', rule: 'integrity', spec: s.id, msg: `'${s.id}' ${p}` });
179
+ const scopedPaths = new Set(s.codeScoped.map((e) => e.path));
180
+ for (const f of s.code) {
181
+ if (!existsAtTip(f))
182
+ out.push({ level: 'error', rule: 'integrity', spec: s.id, file: f, msg: `spec '${s.id}' lists a missing file: ${f}` });
183
+ claimed.add(f);
184
+ governedClaims.add(f);
185
+ // a selector-SCOPED entry claims named units, not the whole file, so it stays out of the owners
186
+ // bound below ([[code-anchor]]) — `spex spec owner` still displays it as a (scoped) governor.
187
+ if (!scopedPaths.has(f))
188
+ owners.set(f, [...(owners.get(f) ?? []), s.id]);
189
+ }
190
+ // one-govern: a node is source of truth for at most ONE file — DISTINCT base paths; several
191
+ // `path#symbol` selectors on the same file are one subject — so drift/eval/ack have a single
192
+ // unambiguous subject (see [[governed-related]]). >1 is a defect — pick the true subject, demote the
193
+ // rest to related. ERROR (the node-side twin of too-many-owners' file-side bound). 0 is fine.
194
+ if (s.code.length > 1)
195
+ out.push({ level: 'error', rule: 'one-govern', spec: s.id, msg: `'${s.id}' governs ${s.code.length} files [${s.code.join(', ')}] — a node is source of truth for at most ONE. Keep the true subject in code:, move the rest to related:` });
196
+ }
197
+ // a file is COVERED if any node GOVERNS (code:) or merely REFERENCES (related:) it; integrity covers both.
198
+ // `related:` is the coverage net: govern is a sharp ideally-one-file pointer, so most files are reached by
199
+ // related, not govern (see [[governed-related]]). It carries coverage but never drift, never eval freshness.
200
+ for (const s of specs)
201
+ for (const f of s.related) {
202
+ if (!existsAtTip(f))
203
+ out.push({ level: 'error', rule: 'integrity', spec: s.id, file: f, msg: `spec '${s.id}' lists a missing related file: ${f}` });
204
+ claimed.add(f);
205
+ }
206
+ // A candidate may retire a node together with the implementation it owned, or transfer that path to a
207
+ // new node. It may not delete the governor while leaving the governed subject behind: ordinary coverage
208
+ // is advisory, so without this transition check a candidate could erase the very contract that would
209
+ // reject its code change. Read only deleted spec blobs from HEAD; no second history index is needed.
210
+ if (pending) {
211
+ let deleted = [];
212
+ try {
213
+ deleted = git(['-C', root, '-c', 'core.quotePath=false', 'diff', '--diff-filter=D', '--name-only', 'HEAD', tip, '--', '.spec'])
214
+ .split('\n').map((path) => path.trim()).filter((path) => path.endsWith('/spec.md'));
215
+ }
216
+ catch { /* an unreadable comparison is already surfaced by the candidate history reads */ }
217
+ for (const specPath of deleted) {
218
+ const oldSource = treeFileText(root, 'HEAD', specPath);
219
+ if (oldSource === null)
220
+ continue;
221
+ const value = parseFrontmatter(oldSource).fm.code;
222
+ const rows = Array.isArray(value) ? value : value ? [value] : [];
223
+ for (const row of rows) {
224
+ const path = parseCodeEntry(row).path;
225
+ if (existsAtTip(path) && !governedClaims.has(path))
226
+ out.push({ level: 'error', rule: 'integrity', file: path, msg: `candidate deletes governor '${specPath}' but leaves its governed subject '${path}' without a code: owner — delete the retired implementation too, or transfer it to a real node in this commit` });
227
+ }
228
+ }
229
+ }
230
+ // id-format: a node id (its leaf dir basename) passes an EXACT per-character whitelist — an ascii char
231
+ // must be [a-z0-9-]; a non-ascii char must be a unicode letter/number (judged on NFC, the mint's
232
+ // canonical form) — and is UNIQUE tree-wide (ERROR). This is THE id vocabulary, defined once (the
233
+ // spec-lint node's rule table) and referenced by [[mentions]] / [[id-url-safe]]: CJK and every other
234
+ // letter script is first-class, exactly what the script-agnostic resolve machinery already accepts.
235
+ // Everything else is forbidden by construction, no heuristics — space, '/', uppercase Latin (lowercase
236
+ // is the Latin norm), control chars, and '_' (reserved as the mint's parent-qualification join; a '_'
237
+ // inside a basename would make that join ambiguous). One optional leading dot is allowed — the
238
+ // reflexive plugin root `.plugins` is dot-prefixed by design. Uniqueness is what keeps the leaf THE id:
239
+ // on a collision the mint must parent-qualify with `_`, so every surface suddenly speaks a longer id
240
+ // than the dir name — legal to the machinery, illegible to people.
241
+ const ID_RE = /^\.?(?:[a-z0-9-]|(?![\x00-\x7F])[\p{L}\p{N}])+$/u;
242
+ const leafOf = (p) => { const segs = p.split('/'); return segs[segs.length - 2]; };
243
+ const byLeaf = new Map();
244
+ for (const s of specs) {
245
+ const leaf = leafOf(s.path);
246
+ byLeaf.set(leaf, [...(byLeaf.get(leaf) ?? []), s.path]);
247
+ if (!ID_RE.test(leaf.normalize('NFC')))
248
+ out.push({ level: 'error', rule: 'id-format', spec: s.id, msg: `node dir '${leaf}' is not a valid id — each char is ascii [a-z0-9-] or a non-ascii unicode letter/number (one optional leading dot); space, '/', '_', uppercase Latin and control chars are forbidden; rename the directory` });
249
+ }
250
+ for (const [leaf, paths] of byLeaf) {
251
+ if (paths.length > 1)
252
+ out.push({ level: 'error', rule: 'id-format', msg: `leaf id '${leaf}' names ${paths.length} nodes [${paths.map((p) => p.replace(/\/spec\.md$/, '')).join(', ')}] — a leaf id is unique tree-wide; rename all but one` });
253
+ }
254
+ // confusable-id: two leaf ids one edit apart read as the same word (WARN — a typo in either reaches a
255
+ // real, wrong node). Deliberately conservative: distance exactly 1, so hierarchy naming like
256
+ // graph/graph-delivery (a whole suffix apart) and verb pairs like evidence-put/evidence-get (distance 2)
257
+ // never warn — better to miss a borderline pair than to nag legitimate siblings. Distance is measured
258
+ // in CODE POINTS, script-agnostic: a CJK pair one character apart (节点/结点 — a classic homophone
259
+ // IME slip) warns like an ascii pair, an astral char counts as one edit (not two surrogate units), and
260
+ // a pure-CJK id can never sit one edit from a pure-ascii one.
261
+ const lev1 = (a, b) => {
262
+ const A = [...a], B = [...b];
263
+ if (Math.abs(A.length - B.length) > 1 || a === b)
264
+ return false;
265
+ let i = 0;
266
+ while (i < A.length && i < B.length && A[i] === B[i])
267
+ i++;
268
+ const rest = (x, k) => x.slice(k).join('');
269
+ return rest(A, i + 1) === rest(B, i + 1) || rest(A, i + 1) === rest(B, i) || rest(A, i) === rest(B, i + 1);
270
+ };
271
+ const leaves = [...byLeaf.keys()];
272
+ for (let i = 0; i < leaves.length; i++)
273
+ for (let j = i + 1; j < leaves.length; j++) {
274
+ if (lev1(leaves[i], leaves[j]))
275
+ out.push({ level: 'warn', rule: 'confusable-id', msg: `leaf ids '${leaves[i]}' and '${leaves[j]}' are one edit apart — easily confused; if they are distinct concepts, rename one to read apart` });
276
+ }
277
+ // mention: a `[[id]]` in a body must name a real node (ERROR — a dangling mention is a broken edge in
278
+ // the very graph the tree exists to keep honest). Checked against the SAME minted ids every other
279
+ // surface resolves ([[id-url-safe]]). Prose only: a fenced block or inline `code span` is sample text
280
+ // (`[[node]]`, `[[<id>]]` placeholders live there), not a reference.
281
+ const idSet = new Set(specs.map((s) => s.id));
282
+ const MENTION_RE = /\[\[(\.?[\p{L}\p{N}_-]+)\]\]/gu;
283
+ for (const s of specs) {
284
+ let inFence = false;
285
+ for (const rawLine of s.body.split('\n')) {
286
+ if (/^\s*```/.test(rawLine)) {
287
+ inFence = !inFence;
288
+ continue;
289
+ }
290
+ if (inFence)
291
+ continue;
292
+ const line = rawLine.replace(/`[^`]*`/g, '');
293
+ for (const m of line.matchAll(MENTION_RE)) {
294
+ if (!idSet.has(m[1]))
295
+ out.push({ level: 'error', rule: 'mention', spec: s.id, msg: `'${s.id}' mentions [[${m[1]}]] — no such node; retarget or drop it (backtick it if it is sample text)` });
296
+ }
297
+ }
298
+ }
299
+ // living: a spec body describes the node's CURRENT intent — it is not a changelog. Version history
300
+ // (every content commit, its reason/session/line-diff) is read from git and shown in the dashboard's
301
+ // recent/history tabs, so a `## vN`-style heading in the body is duplicated, drift-prone state.
302
+ // Reject it. Fence-aware — a `## v2` inside a ``` block is sample text, not a heading.
303
+ const VER_HEADING = /^#{1,6}\s+v\d+\b/;
304
+ for (const s of specs) {
305
+ let inFence = false;
306
+ for (const line of s.body.split('\n')) {
307
+ if (/^\s*```/.test(line)) {
308
+ inFence = !inFence;
309
+ continue;
310
+ }
311
+ if (!inFence && VER_HEADING.test(line))
312
+ out.push({ level: 'error', rule: 'living', spec: s.id, msg: `'${s.id}' has a changelog heading "${line.trim()}" — keep the body current-state; version history lives in git (recent/history tabs)` });
313
+ }
314
+ }
315
+ // coverage: every governed source file must be claimed by at least one spec.
316
+ if (governed.length === 0)
317
+ out.push({ level: 'warn', rule: 'coverage', msg: `governing NOTHING — 0 source candidates under governedRoots [${cfg.governedRoots.join(', ')}]; ${sourcePolicyDescription(cfg)}. Repair these knobs under the "lint" key in spexcode.json (top-level keys are ignored): governedRoots, sourceIncludeGlobs, sourceExcludeGlobs, testGlobs; sourceExtensions remains compatibility shorthand for include globs.` });
318
+ for (const f of governed)
319
+ if (!claimed.has(f))
320
+ out.push({ level: 'warn', rule: 'coverage', file: f, msg: `no spec governs: ${f}` });
321
+ // too-many-owners: many nodes governing one file is ordinary composition (drift fans to each — correct,
322
+ // every owner has a stake), so this is NOT flagged. Only an OVER-owned file is a smell — governed by more
323
+ // than maxOwners nodes, it has accreted more independently-specified functionality than one file should
324
+ // hold (see [[governed-related]]). ONE summary line (not one per file — its own wall of noise): the count,
325
+ // the worst offenders, and the remedy, which blames the FILE not the ownership — SPLIT it so each governor
326
+ // reclaims its own module; or merge the nodes if they're one concern; or give it a single foundation owner.
327
+ const over = [...owners].filter(([, ids]) => ids.length > cfg.maxOwners).sort((a, b) => b[1].length - a[1].length);
328
+ if (over.length) {
329
+ const top = over.slice(0, 5).map(([f, ids]) => `${f.split('/').pop()}(${ids.length})`).join(', ');
330
+ out.push({ level: 'warn', rule: 'owners', msg: `${over.length} file(s) are governed by > ${cfg.maxOwners} nodes — each holds more separately-specified functionality than one file should. Worst: ${top}. SPLIT the file so each governor owns its own module (or merge the nodes, or give it a single foundation owner + related:).` });
331
+ }
332
+ const readyWarned = new Set();
333
+ const anchorSteps = [];
334
+ for (const s of specs) {
335
+ for (const { relation, entries } of [{ relation: 'code', entries: s.codeScoped }, { relation: 'related', entries: s.relatedScoped }]) {
336
+ for (const { path, selectors } of entries) {
337
+ if (pending && !changed.some((file) => relationClaimsPath(path, file)))
338
+ continue;
339
+ const x = extractorFor(regs, extOf(path));
340
+ if (!x) {
341
+ anchorSteps.push({ finding: { level: 'error', rule: 'integrity', spec: s.id, file: path, msg: `'${s.id}' anchors ${path}#${selectors.join(', #')} (${relation}:), but no extractor is designated for '.${extOf(path)}' files — anchor validation was skipped and remains unverified; add a LangSpec row (anchors.ts) or drop the selector(s)` } });
342
+ continue;
343
+ }
344
+ const ready = x.ready();
345
+ if (ready !== true) {
346
+ // once per (extractor, reason), even across several anchored nodes — one repair, one message.
347
+ if (!readyWarned.has(x.id + ready)) {
348
+ readyWarned.add(x.id + ready);
349
+ anchorSteps.push({ finding: { level: 'error', rule: 'integrity', msg: `anchor extractor '${x.id}' cannot run: ${ready}` } });
350
+ }
351
+ continue;
352
+ }
353
+ if (!existsAtTip(path))
354
+ continue; // the missing FILE already errored above
355
+ if (isDirectoryAtTip(path)) {
356
+ anchorSteps.push({ finding: { level: 'error', rule: 'integrity', spec: s.id, file: path, msg: `'${s.id}' puts a selector on a directory (${relation}: ${path}#${selectors[0]}) — a selector scopes ONE real file` } });
357
+ continue;
358
+ }
359
+ let units;
360
+ try {
361
+ const source = textAtTip(path);
362
+ if (source === null)
363
+ throw new Error(`candidate tree has no file '${path}'`);
364
+ units = x.extract(source, path);
365
+ }
366
+ catch (e) {
367
+ anchorSteps.push({ finding: { level: 'error', rule: 'integrity', spec: s.id, file: path, msg: `anchor ${path}#${selectors.join(', #')} ('${s.id}') is unverifiable — the current file does not parse: ${e?.message ?? e}` } });
368
+ continue;
369
+ }
370
+ // each selector resolves (or errors) on its own; only the live ones feed the window engine.
371
+ // The dead/ambiguous verdict itself comes from the ONE shared classifier ([[code-anchor]]); only the
372
+ // wording of the gate's findings lives here.
373
+ const live = [];
374
+ for (const res of resolveSelectors(units, selectors)) {
375
+ const sym = res.selector;
376
+ if ('dead' in res) {
377
+ anchorSteps.push({ finding: { level: 'error', rule: 'integrity', spec: s.id, file: path, msg: `dead anchor: ${path}#${sym} ('${s.id}') names no unit on the current tree — the unit was deleted or renamed; update the spec's ${relation}: entry to follow it` } });
378
+ continue;
379
+ }
380
+ if ('ambiguous' in res) {
381
+ anchorSteps.push({ finding: { level: 'error', rule: 'integrity', spec: s.id, file: path, msg: `ambiguous anchor: ${path}#${sym} ('${s.id}') names ${res.ambiguous} same-named units in one file — an anchor must be unique; rename one unit` } });
382
+ continue;
383
+ }
384
+ if (res.ok.typeOnly)
385
+ anchorSteps.push({ finding: { level: 'warn', rule: 'anchor', spec: s.id, file: path, msg: `${path}#${sym} anchors a ${res.ok.kind} — anchoring a type is usually wrong (types reshape with every refactor); anchor the behaviour-bearing unit instead` } });
386
+ live.push(sym);
387
+ }
388
+ if (!live.length)
389
+ continue;
390
+ const since = rowsFor(hidx, s.path)[0]?.hash || '';
391
+ const win = windowEvents(didx, since, path, s.id);
392
+ if (!win.length)
393
+ continue;
394
+ anchorSteps.push({ query: { id: s.id, version: s.version, relation, path, symbols: live, win } });
395
+ }
396
+ }
397
+ }
398
+ const anchorHits = await anchorHitQueries(root, anchorSteps.flatMap((step) => 'query' in step ? [{ win: step.query.win, symbols: step.query.symbols }] : []), regs);
399
+ let hitIndex = 0;
400
+ for (const step of anchorSteps) {
401
+ if ('finding' in step) {
402
+ out.push(step.finding);
403
+ continue;
404
+ }
405
+ const { id, version, relation, path } = step.query;
406
+ const hits = anchorHits[hitIndex++];
407
+ if (!hits.length)
408
+ continue;
409
+ const hitSyms = [...new Set(hits.flatMap((h) => h.selectors))];
410
+ const shas = hits.map((h) => h.commit.slice(0, 8)).join(', ');
411
+ const unparseable = hits.filter((h) => h.unparseable);
412
+ const parseNote = unparseable.length ? ` (${unparseable.length} of these could not be parsed at that commit — counted as hits conservatively)` : '';
413
+ if (relation === 'code') {
414
+ const current = pending && hits.some((hit) => hit.commit === tip);
415
+ const older = pending && hits.some((hit) => hit.commit !== tip);
416
+ const remedy = !pending
417
+ ? `update the spec, or 'spex spec ack ${id} --reason "…"' if the contract still holds`
418
+ : current && older
419
+ ? `update the spec in this commit; its own hit can be declared by retrying with 'git commit --trailer "Spec-OK: ${id}" …', but the listed older debt must be cleared first (an in-commit declaration never pardons ancestors)`
420
+ : current
421
+ ? `update the spec in this commit, or retry with 'git commit --trailer "Spec-OK: ${id}" …'; a later empty ack cannot pre-author this candidate`
422
+ : `update the spec in this commit, or clear this older debt with 'spex spec ack ${id} --reason "…"' before retrying the candidate`;
423
+ out.push({ level: 'error', rule: 'anchor-drift', spec: id, file: path, msg: `${path}#${hitSyms.join(', #')} was changed by ${hits.length} commit(s) since spec '${id}' v${version} [${shas}]${parseNote} — the anchored contract's code moved: ${remedy}` });
424
+ }
425
+ else
426
+ out.push({ level: 'warn', rule: 'related-drift', spec: id, file: path, msg: `related ${path}#${hitSyms.join(', #')} ('${id}') was changed by ${hits.length} commit(s) since v${version} [${shas}]${parseNote} — a scoped dependency shifted, worth a glance (SOFT: never blocks, no ack, no eval staleness)` });
427
+ }
428
+ // drift: a governed file has commits NOT yet reflected in its spec. Judged by true git ancestry —
429
+ // loadSpecs computes `driftFiles` via driftFor() over the one cached driftIndex walk (git.ts): a
430
+ // commit to the file counts iff it is NOT reachable from the spec's latest version (in-memory
431
+ // parent-edge reachability, the equivalent of `rev-list <version>..HEAD -- <file>`), never a
432
+ // log-position or timestamp guess.
433
+ // A selector-SCOPED code file keeps this file-level advisory by default (a miss still nudges); the
434
+ // committed `lint.scopedCodeMiss: "ignore"` silences ONLY this advisory for scoped entries — the
435
+ // anchor engine's verdicts above (hit = block, resolution failures = integrity) are untouched.
436
+ for (const s of specs) {
437
+ const scopedPaths = new Set(s.codeScoped.map((e) => e.path));
438
+ for (const d of s.driftFiles) {
439
+ if (cfg.scopedCodeMiss === 'ignore' && scopedPaths.has(d.file))
440
+ continue;
441
+ out.push({ level: 'warn', rule: 'drift', spec: s.id, file: d.file, msg: `${d.file} is ${d.behind} commit(s) ahead of spec '${s.id}' (v${s.version}) — may be stale` });
442
+ }
443
+ }
444
+ // related drift: the SOFT tier ([[governed-related]]). A referenced file moved ahead of the node's
445
+ // version — a nudge that a dependency shifted. Same ancestry basis as govern drift, but WARN-only,
446
+ // never reaching the commit gate (driftGate reads govern) or eval freshness. It is COMMON (shared substrate and
447
+ // faces change often without re-versioning every referrer), so per-file it is a wall; like
448
+ // too-many-owners it collapses to ONE summary line, with the per-file detail riding the board
449
+ // (relatedDriftFiles). It stays a soft edge, never a per-file interruption.
450
+ const rd = specs.flatMap((s) => s.relatedDriftFiles.map((d) => ({ id: s.id, behind: d.behind })));
451
+ if (rd.length) {
452
+ const byNode = new Map();
453
+ for (const d of rd)
454
+ byNode.set(d.id, (byNode.get(d.id) ?? 0) + 1);
455
+ const worst = [...byNode].sort((a, b) => b[1] - a[1]).slice(0, 5).map(([id, n]) => `${id}(${n})`).join(', ');
456
+ out.push({ level: 'warn', rule: 'related-drift', msg: `${rd.length} related file(s) across ${byNode.size} node(s) drifted ahead of their spec (SOFT — a dependency shifted, worth a glance; never blocks, no ack, no eval staleness). Most: ${worst}` });
457
+ }
458
+ return { sourceFiles: governed.slice().sort(), findings: out };
459
+ }
460
+ export const DRIFT_GUIDANCE = `DRIFT — a governed file has moved ahead of its spec. A CHECKPOINT, not a chore: find WHERE the truth
461
+ broke along raw intent → expanded spec → code: link → code structure → implementation, fix THAT layer.
462
+
463
+ Inspect: spex spec lint which files, against which spec
464
+ the node's spec.md its raw source + expanded spec
465
+ git diff $(git log -1 --format=%H -- <spec.md>)..HEAD -- <file> the code delta since the spec
466
+
467
+ Diagnose, then apply the one honest remedy:
468
+ • contract changed → rewrite the spec body to the new intent, commit it (re-versions the node)
469
+ • only mechanics changed → spex spec ack <node> "checked — spec still valid" (give a real reason)
470
+ • implementation is WRONG → the spec is right; fix the CODE back toward it, then ack
471
+ • wrong code: link → the node shouldn't own this file (or owns it too broadly); fix frontmatter
472
+ • expanded spec ≠ raw → the spec drifted from human intent; fix the expanded spec to serve the raw
473
+ • structural mismatch → one file owned by many specs / a feature with no home: refactor so a file
474
+ maps to a node, or file an issue and link it (defer honestly)
475
+
476
+ Never patch. A reasoned ack or a real fix are recorded and re-judged at review; a blind ack is a lie.`;
@@ -0,0 +1,7 @@
1
+ import type { Server } from 'node:net';
2
+ export declare function listenOrExit(server: Server, port: number, opts: {
3
+ host?: string;
4
+ label: string;
5
+ cleanup?: () => void;
6
+ onListen: () => void;
7
+ }): void;
@@ -1,5 +1,3 @@
1
- import type { Server } from 'node:net'
2
-
3
1
  // @@@ listenOrExit - the shared "I own this port; if I cannot bind it, I have failed" contract for the two
4
2
  // public-port listeners: the supervisor's raw-TCP proxy (supervise.ts) and the dashboard/public gateway
5
3
  // (gateway.ts). A bind failure is the ONE thing neither may survive — it is the opposite of the keep-serving
@@ -10,19 +8,17 @@ import type { Server } from 'node:net'
10
8
  // reap any child booted for this bind so none is orphaned, and exit non-zero.
11
9
  //
12
10
  // http.Server / https.Server both extend net.Server, so this one signature covers every caller.
13
- export function listenOrExit(
14
- server: Server,
15
- port: number,
16
- opts: { host?: string; label: string; cleanup?: () => void; onListen: () => void },
17
- ): void {
18
- server.once('error', (err: NodeJS.ErrnoException) => {
19
- opts.cleanup?.()
20
- const why = err.code === 'EADDRINUSE' ? `port ${port} is already in use`
21
- : err.code === 'EACCES' ? `permission denied binding port ${port}`
22
- : err.code ?? err.message
23
- console.error(`spec-cli: ${opts.label} cannot bind — ${why}. Free :${port} (e.g. lsof -i :${port}) or pick another port, then retry.`)
24
- process.exit(1)
25
- })
26
- if (opts.host) server.listen(port, opts.host, opts.onListen)
27
- else server.listen(port, opts.onListen)
11
+ export function listenOrExit(server, port, opts) {
12
+ server.once('error', (err) => {
13
+ opts.cleanup?.();
14
+ const why = err.code === 'EADDRINUSE' ? `port ${port} is already in use`
15
+ : err.code === 'EACCES' ? `permission denied binding port ${port}`
16
+ : err.code ?? err.message;
17
+ console.error(`spec-cli: ${opts.label} cannot bind — ${why}. Free :${port} (e.g. lsof -i :${port}) or pick another port, then retry.`);
18
+ process.exit(1);
19
+ });
20
+ if (opts.host)
21
+ server.listen(port, opts.host, opts.onListen);
22
+ else
23
+ server.listen(port, opts.onListen);
28
24
  }
@@ -0,0 +1,65 @@
1
+ import { type DispatchOutcome } from './mentions.js';
2
+ import type { Issue } from './issues.js';
3
+ export declare const issuesEnabled: () => boolean;
4
+ export declare const currentSession: () => string;
5
+ export declare function loadLocalIssues(): Issue[];
6
+ export declare function loadOne(id: string): Issue;
7
+ export declare function commitTrunkData(relPath: string, message: string): 'committed' | 'no-op' | 'not-primary';
8
+ export declare function openIssue(concern: string, opts?: {
9
+ nodes?: string[];
10
+ body?: string;
11
+ evidence?: string[];
12
+ author?: string;
13
+ }): Issue;
14
+ export declare function reply(id: string, body: string, author?: string, evidence?: string[], remark?: {
15
+ targetCodeSha: string;
16
+ }): Issue;
17
+ export declare function replyLocalIssue(id: string, body: string, author: string, evidence?: string[], remark?: {
18
+ targetCodeSha: string;
19
+ }): Promise<{
20
+ thread: Issue;
21
+ outcomes: DispatchOutcome[];
22
+ }>;
23
+ export declare function postLocalIssue(concern: string, opts: {
24
+ nodes?: string[];
25
+ body?: string;
26
+ evidence?: string[];
27
+ author: string;
28
+ }): Promise<{
29
+ thread: Issue;
30
+ outcomes: DispatchOutcome[];
31
+ }>;
32
+ export declare function closeLocalIssue(id: string): {
33
+ status: 'landed';
34
+ already: boolean;
35
+ };
36
+ export declare const parseEvalConcern: (concern: string) => {
37
+ node: string;
38
+ scenario: string;
39
+ } | null;
40
+ export declare function remarkOnHost(host: {
41
+ issue?: string;
42
+ node?: string;
43
+ scenario?: string;
44
+ }, body: string, opts?: {
45
+ codeSha?: string;
46
+ author?: string;
47
+ evidence?: string[];
48
+ }): Promise<{
49
+ ref: string;
50
+ rid: string;
51
+ codeSha: string;
52
+ thread: Issue;
53
+ author: string;
54
+ outcomes: DispatchOutcome[];
55
+ }>;
56
+ export declare function resolveRemark(ref: string, by: string): {
57
+ thread: Issue;
58
+ rid: string;
59
+ };
60
+ export declare function retractRemark(ref: string, by: string): {
61
+ thread: Issue;
62
+ rid: string;
63
+ };
64
+ export declare function nudge(node: string): string;
65
+ export declare function closeoutNudge(sessionId: string | null | undefined): string;