@dommaker/harness 0.19.0 → 1.1.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 (398) hide show
  1. package/CHANGELOG.md +22 -9
  2. package/bin/harness.js +136 -557
  3. package/dist/cli/commands/check.d.ts.map +1 -1
  4. package/dist/cli/commands/check.js +2 -13
  5. package/dist/cli/commands/check.js.map +1 -1
  6. package/dist/cli/commands/definitions.d.ts +90 -0
  7. package/dist/cli/commands/definitions.d.ts.map +1 -0
  8. package/dist/cli/commands/definitions.js +519 -0
  9. package/dist/cli/commands/definitions.js.map +1 -0
  10. package/dist/cli/commands/knowledge.js.map +1 -1
  11. package/dist/cli/commands/sync-docs/agents-syncer.d.ts.map +1 -1
  12. package/dist/cli/commands/sync-docs/agents-syncer.js +25 -4
  13. package/dist/cli/commands/sync-docs/agents-syncer.js.map +1 -1
  14. package/dist/cli/commands/sync-docs/capabilities-syncer.d.ts +3 -25
  15. package/dist/cli/commands/sync-docs/capabilities-syncer.d.ts.map +1 -1
  16. package/dist/cli/commands/sync-docs/capabilities-syncer.js +3 -93
  17. package/dist/cli/commands/sync-docs/capabilities-syncer.js.map +1 -1
  18. package/dist/cli/commands/sync-docs/index.d.ts +1 -1
  19. package/dist/cli/commands/sync-docs/index.d.ts.map +1 -1
  20. package/dist/cli/commands/sync-docs/index.js +4 -4
  21. package/dist/cli/commands/sync-docs/index.js.map +1 -1
  22. package/dist/completion-checkers/classify.d.ts +23 -0
  23. package/dist/completion-checkers/classify.d.ts.map +1 -0
  24. package/dist/completion-checkers/classify.js +37 -0
  25. package/dist/completion-checkers/classify.js.map +1 -0
  26. package/dist/completion-checkers/contract-presence.d.ts +12 -0
  27. package/dist/completion-checkers/contract-presence.d.ts.map +1 -0
  28. package/dist/completion-checkers/contract-presence.js +36 -0
  29. package/dist/completion-checkers/contract-presence.js.map +1 -0
  30. package/dist/completion-checkers/glob-match.d.ts +13 -0
  31. package/dist/completion-checkers/glob-match.d.ts.map +1 -0
  32. package/dist/completion-checkers/glob-match.js +58 -0
  33. package/dist/completion-checkers/glob-match.js.map +1 -0
  34. package/dist/completion-checkers/index.d.ts +19 -0
  35. package/dist/completion-checkers/index.d.ts.map +1 -0
  36. package/dist/completion-checkers/index.js +47 -0
  37. package/dist/completion-checkers/index.js.map +1 -0
  38. package/dist/completion-checkers/phase-format.d.ts +15 -0
  39. package/dist/completion-checkers/phase-format.d.ts.map +1 -0
  40. package/dist/completion-checkers/phase-format.js +42 -0
  41. package/dist/completion-checkers/phase-format.js.map +1 -0
  42. package/dist/completion-checkers/tdd-chain.d.ts +16 -0
  43. package/dist/completion-checkers/tdd-chain.d.ts.map +1 -0
  44. package/dist/completion-checkers/tdd-chain.js +57 -0
  45. package/dist/completion-checkers/tdd-chain.js.map +1 -0
  46. package/dist/completion-checkers/types.d.ts +77 -0
  47. package/dist/completion-checkers/types.d.ts.map +1 -0
  48. package/dist/completion-checkers/types.js +10 -0
  49. package/dist/completion-checkers/types.js.map +1 -0
  50. package/dist/context/index.d.ts +1 -5
  51. package/dist/context/index.d.ts.map +1 -1
  52. package/dist/context/index.js +1 -5
  53. package/dist/context/index.js.map +1 -1
  54. package/dist/context/knowledge-injector.d.ts +3 -3
  55. package/dist/context/knowledge-injector.d.ts.map +1 -1
  56. package/dist/context/knowledge-injector.js +1 -1
  57. package/dist/context/session-manager.d.ts +1 -12
  58. package/dist/context/session-manager.d.ts.map +1 -1
  59. package/dist/context/session-manager.js +0 -63
  60. package/dist/context/session-manager.js.map +1 -1
  61. package/dist/context/types.d.ts +1 -39
  62. package/dist/context/types.d.ts.map +1 -1
  63. package/dist/context/types.js +2 -15
  64. package/dist/context/types.js.map +1 -1
  65. package/dist/core/constraints/agent-prompt-renderer.d.ts +1 -1
  66. package/dist/core/constraints/agent-prompt-renderer.js +1 -1
  67. package/dist/core/constraints/capabilities-parser.d.ts +17 -2
  68. package/dist/core/constraints/capabilities-parser.d.ts.map +1 -1
  69. package/dist/core/constraints/capabilities-parser.js +84 -2
  70. package/dist/core/constraints/capabilities-parser.js.map +1 -1
  71. package/dist/core/constraints/checker.d.ts +3 -17
  72. package/dist/core/constraints/checker.d.ts.map +1 -1
  73. package/dist/core/constraints/checker.js +7 -47
  74. package/dist/core/constraints/checker.js.map +1 -1
  75. package/dist/core/constraints/definitions.d.ts +0 -7
  76. package/dist/core/constraints/definitions.d.ts.map +1 -1
  77. package/dist/core/constraints/definitions.js +1 -8
  78. package/dist/core/constraints/definitions.js.map +1 -1
  79. package/dist/core/constraints/doc-freshness/runner.d.ts +0 -2
  80. package/dist/core/constraints/doc-freshness/runner.d.ts.map +1 -1
  81. package/dist/core/constraints/doc-freshness/runner.js.map +1 -1
  82. package/dist/core/constraints/index.d.ts +1 -1
  83. package/dist/core/constraints/index.d.ts.map +1 -1
  84. package/dist/core/constraints/index.js +2 -3
  85. package/dist/core/constraints/index.js.map +1 -1
  86. package/dist/core/constraints/interceptor.d.ts.map +1 -1
  87. package/dist/core/constraints/interceptor.js +1 -5
  88. package/dist/core/constraints/interceptor.js.map +1 -1
  89. package/dist/core/effective-constraints.d.ts.map +1 -1
  90. package/dist/core/effective-constraints.js +0 -1
  91. package/dist/core/effective-constraints.js.map +1 -1
  92. package/dist/core/project-config-loader.d.ts.map +1 -1
  93. package/dist/core/project-config-loader.js +1 -8
  94. package/dist/core/project-config-loader.js.map +1 -1
  95. package/dist/dashboard/stats.d.ts.map +1 -1
  96. package/dist/dashboard/stats.js.map +1 -1
  97. package/dist/dashboard/types.d.ts +2 -2
  98. package/dist/dashboard/types.d.ts.map +1 -1
  99. package/dist/failure/constraint-handler.d.ts.map +1 -1
  100. package/dist/failure/constraint-handler.js +0 -2
  101. package/dist/failure/constraint-handler.js.map +1 -1
  102. package/dist/gates/acceptance.d.ts +9 -1
  103. package/dist/gates/acceptance.d.ts.map +1 -1
  104. package/dist/gates/acceptance.js +21 -0
  105. package/dist/gates/acceptance.js.map +1 -1
  106. package/dist/gates/checker-gate.d.ts +23 -0
  107. package/dist/gates/checker-gate.d.ts.map +1 -0
  108. package/dist/gates/checker-gate.js +53 -0
  109. package/dist/gates/checker-gate.js.map +1 -0
  110. package/dist/gates/command.d.ts +9 -2
  111. package/dist/gates/command.d.ts.map +1 -1
  112. package/dist/gates/command.js +10 -0
  113. package/dist/gates/command.js.map +1 -1
  114. package/dist/gates/contract.d.ts +8 -2
  115. package/dist/gates/contract.d.ts.map +1 -1
  116. package/dist/gates/contract.js +9 -0
  117. package/dist/gates/contract.js.map +1 -1
  118. package/dist/gates/decision.d.ts +17 -0
  119. package/dist/gates/decision.d.ts.map +1 -0
  120. package/dist/gates/decision.js +25 -0
  121. package/dist/gates/decision.js.map +1 -0
  122. package/dist/gates/definitions.d.ts +63 -0
  123. package/dist/gates/definitions.d.ts.map +1 -0
  124. package/dist/gates/definitions.js +160 -0
  125. package/dist/gates/definitions.js.map +1 -0
  126. package/dist/gates/effective-gates.d.ts +30 -0
  127. package/dist/gates/effective-gates.d.ts.map +1 -0
  128. package/dist/gates/effective-gates.js +85 -0
  129. package/dist/gates/effective-gates.js.map +1 -0
  130. package/dist/gates/index.d.ts +25 -1
  131. package/dist/gates/index.d.ts.map +1 -1
  132. package/dist/gates/index.js +37 -1
  133. package/dist/gates/index.js.map +1 -1
  134. package/dist/gates/performance.d.ts +8 -2
  135. package/dist/gates/performance.d.ts.map +1 -1
  136. package/dist/gates/performance.js +9 -0
  137. package/dist/gates/performance.js.map +1 -1
  138. package/dist/gates/registry.d.ts +32 -0
  139. package/dist/gates/registry.d.ts.map +1 -0
  140. package/dist/gates/registry.js +90 -0
  141. package/dist/gates/registry.js.map +1 -0
  142. package/dist/gates/review.d.ts +8 -2
  143. package/dist/gates/review.d.ts.map +1 -1
  144. package/dist/gates/review.js +9 -0
  145. package/dist/gates/review.js.map +1 -1
  146. package/dist/gates/runner.d.ts +30 -0
  147. package/dist/gates/runner.d.ts.map +1 -0
  148. package/dist/gates/runner.js +38 -0
  149. package/dist/gates/runner.js.map +1 -0
  150. package/dist/gates/security.d.ts +8 -2
  151. package/dist/gates/security.d.ts.map +1 -1
  152. package/dist/gates/security.js +9 -0
  153. package/dist/gates/security.js.map +1 -1
  154. package/dist/gates/types.d.ts +41 -1
  155. package/dist/gates/types.d.ts.map +1 -1
  156. package/dist/hooks/bootstrap.d.ts.map +1 -1
  157. package/dist/hooks/bootstrap.js +0 -2
  158. package/dist/hooks/bootstrap.js.map +1 -1
  159. package/dist/hooks/config.d.ts +26 -0
  160. package/dist/hooks/config.d.ts.map +1 -0
  161. package/dist/hooks/config.js +30 -0
  162. package/dist/hooks/config.js.map +1 -0
  163. package/dist/hooks/index.d.ts +2 -1
  164. package/dist/hooks/index.d.ts.map +1 -1
  165. package/dist/hooks/index.js +4 -1
  166. package/dist/hooks/index.js.map +1 -1
  167. package/dist/hooks/registry.d.ts +18 -1
  168. package/dist/hooks/registry.d.ts.map +1 -1
  169. package/dist/hooks/registry.js +45 -0
  170. package/dist/hooks/registry.js.map +1 -1
  171. package/dist/hooks/types.d.ts +20 -0
  172. package/dist/hooks/types.d.ts.map +1 -1
  173. package/dist/index.d.ts +5 -8
  174. package/dist/index.d.ts.map +1 -1
  175. package/dist/index.js +11 -20
  176. package/dist/index.js.map +1 -1
  177. package/dist/knowledge/import.d.ts +2 -2
  178. package/dist/knowledge/import.d.ts.map +1 -1
  179. package/dist/knowledge/ingest.js.map +1 -1
  180. package/dist/knowledge/lifecycle-hooks.d.ts +2 -2
  181. package/dist/knowledge/lifecycle-hooks.d.ts.map +1 -1
  182. package/dist/knowledge/types.d.ts +5 -7
  183. package/dist/knowledge/types.d.ts.map +1 -1
  184. package/dist/knowledge/types.js +1 -1
  185. package/dist/knowledge/types.js.map +1 -1
  186. package/dist/monitoring/constraint-doctor.d.ts +4 -22
  187. package/dist/monitoring/constraint-doctor.d.ts.map +1 -1
  188. package/dist/monitoring/constraint-doctor.js +7 -94
  189. package/dist/monitoring/constraint-doctor.js.map +1 -1
  190. package/dist/presets/standard.d.ts +1 -1
  191. package/dist/presets/standard.d.ts.map +1 -1
  192. package/dist/presets/standard.js +1 -2
  193. package/dist/presets/standard.js.map +1 -1
  194. package/dist/pretool-use-hook.d.ts +16 -0
  195. package/dist/pretool-use-hook.d.ts.map +1 -0
  196. package/dist/pretool-use-hook.js +68 -0
  197. package/dist/pretool-use-hook.js.map +1 -0
  198. package/dist/spec/annotation-checker.d.ts.map +1 -1
  199. package/dist/spec/annotation-checker.js +6 -26
  200. package/dist/spec/annotation-checker.js.map +1 -1
  201. package/dist/tools/index.d.ts +0 -4
  202. package/dist/tools/index.d.ts.map +1 -1
  203. package/dist/tools/index.js +0 -4
  204. package/dist/tools/index.js.map +1 -1
  205. package/dist/types/constraint.d.ts +1 -12
  206. package/dist/types/constraint.d.ts.map +1 -1
  207. package/dist/types/constraint.js +0 -3
  208. package/dist/types/constraint.js.map +1 -1
  209. package/dist/types/project-config.d.ts +0 -2
  210. package/dist/types/project-config.d.ts.map +1 -1
  211. package/dist/utils/file-walk.d.ts +2 -2
  212. package/dist/utils/file-walk.js +2 -2
  213. package/package.json +6 -1
  214. package/src/CONTEXT.md +15 -0
  215. package/src/__tests__/checker-extra.test.ts +2 -59
  216. package/src/__tests__/constraint-doctor.test.ts +1 -140
  217. package/src/__tests__/constraint-handler.test.ts +0 -4
  218. package/src/__tests__/pretool-use-hook.test.ts +82 -0
  219. package/src/__tests__/project-config-loader-extra.test.ts +0 -20
  220. package/src/__tests__/project-config-loader.test.ts +0 -1
  221. package/src/cli/commands/CONTEXT.md +15 -7
  222. package/src/cli/commands/__tests__/check-drift.test.ts +0 -3
  223. package/src/cli/commands/__tests__/check-skip-output.test.ts +0 -5
  224. package/src/cli/commands/__tests__/check.test.ts +7 -75
  225. package/src/cli/commands/__tests__/registry.test.ts +201 -0
  226. package/src/cli/commands/__tests__/sync-docs-agents.test.ts +55 -0
  227. package/src/cli/commands/check.ts +2 -14
  228. package/src/cli/commands/definitions.ts +593 -0
  229. package/src/cli/commands/knowledge.ts +3 -3
  230. package/src/cli/commands/sync-docs/agents-syncer.ts +26 -6
  231. package/src/cli/commands/sync-docs/capabilities-syncer.ts +4 -110
  232. package/src/cli/commands/sync-docs/index.ts +8 -5
  233. package/src/completion-checkers/CONTEXT.md +25 -0
  234. package/src/completion-checkers/__tests__/contract-presence.test.ts +44 -0
  235. package/src/completion-checkers/__tests__/phase-format.test.ts +60 -0
  236. package/src/completion-checkers/__tests__/tdd-chain.test.ts +99 -0
  237. package/src/completion-checkers/classify.ts +47 -0
  238. package/src/completion-checkers/contract-presence.ts +40 -0
  239. package/src/completion-checkers/glob-match.ts +54 -0
  240. package/src/completion-checkers/index.ts +19 -0
  241. package/src/completion-checkers/phase-format.ts +48 -0
  242. package/src/completion-checkers/tdd-chain.ts +63 -0
  243. package/src/completion-checkers/types.ts +84 -0
  244. package/src/context/CONTEXT.md +2 -7
  245. package/src/context/__tests__/session-manager.test.ts +0 -91
  246. package/src/context/index.ts +1 -5
  247. package/src/context/knowledge-injector.ts +3 -3
  248. package/src/context/session-manager.ts +0 -74
  249. package/src/context/types.ts +2 -63
  250. package/src/core/constraints/__tests__/interceptor.test.ts +0 -2
  251. package/src/core/constraints/agent-prompt-renderer.ts +1 -1
  252. package/src/core/constraints/capabilities-parser.ts +95 -2
  253. package/src/core/constraints/checker.ts +8 -53
  254. package/src/core/constraints/definitions.ts +0 -8
  255. package/src/core/constraints/doc-freshness/runner.ts +1 -2
  256. package/src/core/constraints/index.ts +1 -2
  257. package/src/core/constraints/interceptor.ts +1 -6
  258. package/src/core/effective-constraints.ts +0 -1
  259. package/src/core/project-config-loader.ts +1 -7
  260. package/src/dashboard/stats.ts +2 -2
  261. package/src/dashboard/types.ts +2 -2
  262. package/src/failure/constraint-handler.ts +0 -2
  263. package/src/gates/CONTEXT.md +16 -13
  264. package/src/gates/__tests__/checker-gate.test.ts +45 -0
  265. package/src/gates/__tests__/contract.test.ts +73 -0
  266. package/src/gates/__tests__/effective-gates.test.ts +82 -0
  267. package/src/gates/__tests__/gate-interface.test.ts +202 -0
  268. package/src/gates/__tests__/registry.test.ts +87 -0
  269. package/src/gates/__tests__/runner.test.ts +81 -0
  270. package/src/gates/acceptance.ts +24 -1
  271. package/src/gates/checker-gate.ts +54 -0
  272. package/src/gates/command.ts +13 -2
  273. package/src/gates/contract.ts +12 -2
  274. package/src/gates/decision.ts +27 -0
  275. package/src/gates/definitions.ts +207 -0
  276. package/src/gates/effective-gates.ts +106 -0
  277. package/src/gates/index.ts +38 -0
  278. package/src/gates/performance.ts +12 -2
  279. package/src/gates/registry.ts +107 -0
  280. package/src/gates/review.ts +12 -2
  281. package/src/gates/runner.ts +57 -0
  282. package/src/gates/security.ts +12 -2
  283. package/src/gates/types.ts +48 -1
  284. package/src/hooks/CONTEXT.md +12 -2
  285. package/src/hooks/__tests__/bootstrap.test.ts +0 -1
  286. package/src/hooks/__tests__/config.test.ts +19 -0
  287. package/src/hooks/__tests__/pipeline.test.ts +79 -0
  288. package/src/hooks/__tests__/registry.test.ts +67 -0
  289. package/src/hooks/bootstrap.ts +0 -2
  290. package/src/hooks/config.ts +29 -0
  291. package/src/hooks/index.ts +2 -1
  292. package/src/hooks/registry.ts +60 -1
  293. package/src/hooks/types.ts +21 -0
  294. package/src/index.ts +11 -21
  295. package/src/knowledge/import.ts +3 -3
  296. package/src/knowledge/ingest.ts +4 -4
  297. package/src/knowledge/lifecycle-hooks.ts +3 -3
  298. package/src/knowledge/types.ts +5 -8
  299. package/src/monitoring/constraint-doctor.ts +7 -106
  300. package/src/presets/__tests__/standard.test.ts +0 -2
  301. package/src/presets/standard.ts +1 -2
  302. package/src/pretool-use-hook.ts +68 -0
  303. package/src/spec/__tests__/annotation-checker.test.ts +9 -3
  304. package/src/spec/annotation-checker.ts +7 -29
  305. package/src/tools/CONTEXT.md +7 -14
  306. package/src/tools/index.ts +0 -4
  307. package/src/types/constraint.ts +1 -14
  308. package/src/types/project-config.ts +0 -3
  309. package/src/utils/file-walk.ts +2 -2
  310. package/dist/architecture/constraint-engine.d.ts +0 -67
  311. package/dist/architecture/constraint-engine.d.ts.map +0 -1
  312. package/dist/architecture/constraint-engine.js +0 -295
  313. package/dist/architecture/constraint-engine.js.map +0 -1
  314. package/dist/architecture/cross-project-checker.d.ts +0 -54
  315. package/dist/architecture/cross-project-checker.d.ts.map +0 -1
  316. package/dist/architecture/cross-project-checker.js +0 -467
  317. package/dist/architecture/cross-project-checker.js.map +0 -1
  318. package/dist/cli/commands/index.d.ts +0 -30
  319. package/dist/cli/commands/index.d.ts.map +0 -1
  320. package/dist/cli/commands/index.js +0 -91
  321. package/dist/cli/commands/index.js.map +0 -1
  322. package/dist/context/file-budget.d.ts +0 -40
  323. package/dist/context/file-budget.d.ts.map +0 -1
  324. package/dist/context/file-budget.js +0 -147
  325. package/dist/context/file-budget.js.map +0 -1
  326. package/dist/context/progressive-loader.d.ts +0 -109
  327. package/dist/context/progressive-loader.d.ts.map +0 -1
  328. package/dist/context/progressive-loader.js +0 -166
  329. package/dist/context/progressive-loader.js.map +0 -1
  330. package/dist/context/token-pipeline.d.ts +0 -55
  331. package/dist/context/token-pipeline.d.ts.map +0 -1
  332. package/dist/context/token-pipeline.js +0 -220
  333. package/dist/context/token-pipeline.js.map +0 -1
  334. package/dist/context/tool-output-budget.d.ts +0 -59
  335. package/dist/context/tool-output-budget.d.ts.map +0 -1
  336. package/dist/context/tool-output-budget.js +0 -177
  337. package/dist/context/tool-output-budget.js.map +0 -1
  338. package/dist/core/constraints/default-executors.d.ts +0 -10
  339. package/dist/core/constraints/default-executors.d.ts.map +0 -1
  340. package/dist/core/constraints/default-executors.js +0 -120
  341. package/dist/core/constraints/default-executors.js.map +0 -1
  342. package/dist/llm/adapter.d.ts +0 -36
  343. package/dist/llm/adapter.d.ts.map +0 -1
  344. package/dist/llm/adapter.js +0 -118
  345. package/dist/llm/adapter.js.map +0 -1
  346. package/dist/llm/index.d.ts +0 -3
  347. package/dist/llm/index.d.ts.map +0 -1
  348. package/dist/llm/index.js +0 -19
  349. package/dist/llm/index.js.map +0 -1
  350. package/dist/llm/types.d.ts +0 -62
  351. package/dist/llm/types.d.ts.map +0 -1
  352. package/dist/llm/types.js +0 -8
  353. package/dist/llm/types.js.map +0 -1
  354. package/dist/tools/core/index.d.ts +0 -13
  355. package/dist/tools/core/index.d.ts.map +0 -1
  356. package/dist/tools/core/index.js +0 -198
  357. package/dist/tools/core/index.js.map +0 -1
  358. package/dist/tools/loader.d.ts +0 -21
  359. package/dist/tools/loader.d.ts.map +0 -1
  360. package/dist/tools/loader.js +0 -199
  361. package/dist/tools/loader.js.map +0 -1
  362. package/dist/tools/registry.d.ts +0 -59
  363. package/dist/tools/registry.d.ts.map +0 -1
  364. package/dist/tools/registry.js +0 -115
  365. package/dist/tools/registry.js.map +0 -1
  366. package/dist/tools/types.d.ts +0 -38
  367. package/dist/tools/types.d.ts.map +0 -1
  368. package/dist/tools/types.js +0 -6
  369. package/dist/tools/types.js.map +0 -1
  370. package/src/__tests__/architecture-engine.test.ts +0 -619
  371. package/src/__tests__/context/progressive-loader.test.ts +0 -131
  372. package/src/__tests__/cross-project-checker.test.ts +0 -318
  373. package/src/architecture/CONTEXT.md +0 -22
  374. package/src/architecture/constraint-engine.ts +0 -351
  375. package/src/architecture/cross-project-checker.ts +0 -545
  376. package/src/cli/commands/index.ts +0 -30
  377. package/src/context/__tests__/file-budget.test.ts +0 -142
  378. package/src/context/__tests__/progressive-loader.test.ts +0 -264
  379. package/src/context/__tests__/token-pipeline.test.ts +0 -178
  380. package/src/context/__tests__/tool-output-budget.test.ts +0 -95
  381. package/src/context/file-budget.ts +0 -142
  382. package/src/context/progressive-loader.ts +0 -260
  383. package/src/context/token-pipeline.ts +0 -265
  384. package/src/context/tool-output-budget.ts +0 -178
  385. package/src/core/constraints/__tests__/default-executors.test.ts +0 -57
  386. package/src/core/constraints/default-executors.ts +0 -92
  387. package/src/llm/CONTEXT.md +0 -21
  388. package/src/llm/__tests__/adapter.test.ts +0 -172
  389. package/src/llm/adapter.ts +0 -149
  390. package/src/llm/index.ts +0 -2
  391. package/src/llm/types.ts +0 -81
  392. package/src/tools/__tests__/loader.test.ts +0 -252
  393. package/src/tools/__tests__/registry.test.ts +0 -157
  394. package/src/tools/core/__tests__/core.test.ts +0 -122
  395. package/src/tools/core/index.ts +0 -201
  396. package/src/tools/loader.ts +0 -216
  397. package/src/tools/registry.ts +0 -128
  398. package/src/tools/types.ts +0 -46
@@ -1,18 +1,14 @@
1
1
  /**
2
- * CAPABILITIES.md 同步器(工单 22)
2
+ * CAPABILITIES.md 文件表格同步器(工单 22)
3
3
  *
4
- * 支持两种格式:
5
- * - 文件表格格式:模块/文件/说明三列表格,增删行维护
6
- * - 能力清单格式:计数行(CLI Commands (N) 等),FreshnessRunner 校验与刷新
4
+ * 只负责文件表格格式:模块/文件/说明三列表格,增删行维护。
5
+ * 能力清单格式(计数行)的解析与计数已收敛到 core/constraints/capabilities-parser(H2 O10-R3)。
7
6
  */
8
7
 
9
8
  import * as fs from 'fs/promises';
10
9
  import * as path from 'path';
11
- import { IRON_LAWS, GUIDELINES, TIPS } from '../../../core/constraints/definitions';
12
- import { isCapabilityListingFormat, readCapabilitiesEntries } from '../../../core/constraints/capabilities-parser';
13
- import { FreshnessRunner } from '../../../core/constraints/doc-freshness/runner';
10
+ import { readCapabilitiesEntries } from '../../../core/constraints/capabilities-parser';
14
11
  import type { CapabilitiesMode } from '../../../core/project-config-loader';
15
- import type { DocFreshnessCheck } from '../../../types/project-config';
16
12
  import type { ModuleInfo, SyncResult } from './project-reader';
17
13
 
18
14
  /**
@@ -33,108 +29,6 @@ export async function parseCapabilitiesFiles(capabilitiesPath: string): Promise<
33
29
  return files;
34
30
  }
35
31
 
36
- /**
37
- * 检测 CAPABILITIES.md 是否使用能力清单格式(计数行),而非文件表格格式
38
- *
39
- * @deprecated 实现已收敛到 core/constraints/capabilities-parser(与工单 19-B 同方向),
40
- * 此处保留 re-export 兼容现有调用方
41
- */
42
- export { isCapabilityListingFormat };
43
-
44
- /**
45
- * 构建 CAPABILITIES.md 能力清单格式的检查配置
46
- */
47
- function buildCapabilityChecks(): DocFreshnessCheck[] {
48
- return [
49
- {
50
- type: 'doc_regex_count',
51
- doc: 'CAPABILITIES.md',
52
- label: 'CLI Commands',
53
- pattern: 'CLI Commands\\s*\\((\\d+)\\)',
54
- actual: { kind: 'dir_count', path: 'src/cli/commands', extension: '.ts', exclude: ['index.ts'] },
55
- },
56
- {
57
- type: 'doc_regex_count',
58
- doc: 'CAPABILITIES.md',
59
- label: 'Quality Gates',
60
- pattern: 'Quality Gates?\\s*\\((\\d+)\\)',
61
- actual: { kind: 'dir_count', path: 'src/gates', extension: '.ts', exclude: ['index.ts', 'types.ts'] },
62
- },
63
- {
64
- type: 'doc_regex_count',
65
- doc: 'CAPABILITIES.md',
66
- label: 'Iron Laws',
67
- pattern: 'Iron Laws?\\s*\\((\\d+)\\)',
68
- actual: { kind: 'const_count', value: Object.keys(IRON_LAWS).length },
69
- },
70
- {
71
- type: 'doc_regex_count',
72
- doc: 'CAPABILITIES.md',
73
- label: 'Guidelines',
74
- pattern: 'Guidelines?\\s*\\((\\d+)\\)',
75
- actual: { kind: 'const_count', value: Object.keys(GUIDELINES).length },
76
- },
77
- {
78
- type: 'doc_regex_count',
79
- doc: 'CAPABILITIES.md',
80
- label: 'Tips',
81
- pattern: 'Tips?\\s*\\((\\d+)\\)',
82
- actual: { kind: 'const_count', value: Object.keys(TIPS).length },
83
- },
84
- ];
85
- }
86
-
87
- /**
88
- * 使用 FreshnessRunner 检查能力清单计数是否与代码一致(--check 模式)
89
- */
90
- export function checkCapabilityCounts(
91
- projectPath: string
92
- ): { match: boolean; mismatches: string[] } {
93
- const runner = new FreshnessRunner();
94
- const checks = buildCapabilityChecks();
95
- const results = runner.runAll({ checks }, projectPath);
96
-
97
- const mismatches: string[] = [];
98
- for (const r of results) {
99
- if (!r.pass && r.message) {
100
- mismatches.push(r.message);
101
- }
102
- }
103
-
104
- return { match: mismatches.length === 0, mismatches };
105
- }
106
-
107
- /**
108
- * 更新 CAPABILITIES.md 中的能力清单计数(write 模式)
109
- *
110
- * 使用 FreshnessRunner 获取实际计数,然后 regex 替换文档计数行。
111
- */
112
- export function updateCapabilityCounts(content: string, projectPath: string): string {
113
- const runner = new FreshnessRunner();
114
-
115
- const cliCount = runner.countFromDir('src/cli/commands', '.ts', ['index.ts'], projectPath);
116
- const gateCount = runner.countFromDir('src/gates', '.ts', ['index.ts', 'types.ts'], projectPath);
117
- const ironCount = Object.keys(IRON_LAWS).length;
118
- const guideCount = Object.keys(GUIDELINES).length;
119
- const tipsCount = Object.keys(TIPS).length;
120
-
121
- const replacements: [RegExp, string][] = [
122
- [/CLI Commands\s*\(\d+\)/, `CLI Commands (${cliCount})`],
123
- [/Quality Gates?\s*\(\d+\)/, `Quality Gates (${gateCount})`],
124
- [/Iron Laws?\s*\(\d+\)/, `Iron Laws (${ironCount})`],
125
- [/Guidelines?\s*\(\d+\)/, `Guidelines (${guideCount})`],
126
- [/Tips?\s*\(\d+\)/, `Tips (${tipsCount})`],
127
- ];
128
-
129
- for (const [regex, replacement] of replacements) {
130
- if (regex.test(content)) {
131
- content = content.replace(regex, replacement);
132
- }
133
- }
134
-
135
- return content;
136
- }
137
-
138
32
  /**
139
33
  * 更新 CAPABILITIES.md 文件
140
34
  *
@@ -6,7 +6,7 @@
6
6
  *
7
7
  * 实现拆分:
8
8
  * - project-reader.ts 项目信息读取(package.json/config.yml/源码扫描)
9
- * - capabilities-syncer.ts CAPABILITIES.md 两种格式的对比与维护
9
+ * - capabilities-syncer.ts CAPABILITIES.md 文件表格格式的对比与维护(能力清单格式解析/计数走 core/constraints/capabilities-parser)
10
10
  * - context-syncer.ts CONTEXT.md 模板生成/发现/过时判定
11
11
  * - agents-syncer.ts AGENTS.md 生成
12
12
  * - preserve-block.ts PRESERVE 标记块提取与组合
@@ -16,16 +16,19 @@ import chalk from 'chalk';
16
16
  import * as fs from 'fs/promises';
17
17
  import { existsSync } from 'fs';
18
18
  import * as path from 'path';
19
- import { aggregateToSourceSubdir, readCapabilitiesEntries } from '../../../core/constraints/capabilities-parser';
19
+ import {
20
+ aggregateToSourceSubdir,
21
+ readCapabilitiesEntries,
22
+ isCapabilityListingFormat,
23
+ checkCapabilityCounts,
24
+ updateCapabilityCounts,
25
+ } from '../../../core/constraints/capabilities-parser';
20
26
  import { detectSourceRoots } from '../../../utils/detect-source-roots';
21
27
  import { getCapabilitiesMode } from '../../../core/project-config-loader';
22
28
  import { getSourceDirs, scanSourceModules, getRequiredContextDirs } from './project-reader';
23
29
  import type { ModuleInfo, SyncResult } from './project-reader';
24
30
  import {
25
31
  parseCapabilitiesFiles,
26
- isCapabilityListingFormat,
27
- checkCapabilityCounts,
28
- updateCapabilityCounts,
29
32
  updateCapabilitiesFile,
30
33
  compactCapabilitiesContent,
31
34
  } from './capabilities-syncer';
@@ -0,0 +1,25 @@
1
+ # completion-checkers/
2
+
3
+ ## 职责
4
+ WU 收尾软观测三纯判定函数(T7-E1,studio#160):tdd-chain 引用链 / phase-format subject 结构 / contract-presence 契约在场。纯函数,不碰文件系统与 git,commits 由调用方(studio completion-gates 第四段守卫,T7-E2)供给。
5
+
6
+ ## 核心导出
7
+ - `verifyTddChain(commits, config)`(`tdd-chain.ts`)——实现 commit 须带 trailer `Tested-By: <sha>`(`TESTED_BY_RE` 写死);被引 sha 须在本提交集内、位置在前、文件清单命中 test_globs;`Tests: none`(`TESTS_NONE_RE`)→ waiver;纯测试/纯非代码 commit 天然免检
8
+ - `verifyPhaseFormat(commits, config)`(`phase-format.ts`)——非 merge commit subject 命中 `PHASE_SUBJECT_RE`(`^phase\([a-z0-9-]+\):\s+\S`,写死);merge commit 记违规(`CommitInput.isMerge` 优先,缺省 subject 启发式)
9
+ - `verifyContractPresence(type, context, config)`(`contract-presence.ts`)——yml contracts 清单查表,无表项 = skip;类型→判定方法映射是代码不是配置(review → `context.reviewReport` 在场);清单内无判定方法 = violation(配置与代码失配)
10
+ - `CompletionCheckersConfig`(`types.ts`)——enabled 总开关 / checkers 各开关 / testGlobs / noncodeGlobs / contracts;协议格式不进配置
11
+ - `classifyCommitFiles` / `resolveGlobs`(`classify.ts`)——文件分类,tdd-chain 与 phase-format 共享口径
12
+ - `matchGlob` / `matchAnyGlob` / `DEFAULT_TEST_GLOBS` / `DEFAULT_NONCODE_GLOBS`(`glob-match.ts`)——轻量 glob 匹配(`**`/`*`/`?`)
13
+
14
+ ## 依赖关系
15
+ - 无内部依赖(纯函数模块,自包含)
16
+
17
+ ## 约定
18
+ - 与 ConstraintCheck 闭环注册表无关:直接 export,禁止注册进 checkers/index.ts
19
+ - verdict 四态:pass / violation / waiver(豁免放行,commit 级)/ skip(不适用,不记台账)
20
+ - 新增契约类型 = `CONTRACT_JUDGMENTS` 加一条映射 + 测试
21
+ - 协议格式(Tested-By、Tests: none、phase 结构)写死为机制本体,不得配置化
22
+
23
+ ## 注意事项
24
+ - commits 输入有序(base..HEAD 升序),位置判定比索引不比时间戳
25
+ - Tested-By 引用支持 7-40 位 sha 前缀匹配
@@ -0,0 +1,44 @@
1
+ /**
2
+ * verifyContractPresence 测试(studio#160 验收:契约标记缺失 / 无表项 skip)
3
+ */
4
+
5
+ import { verifyContractPresence } from '../contract-presence';
6
+
7
+ describe('verifyContractPresence', () => {
8
+ it('无表项类型 → skip(不算违规)', () => {
9
+ const result = verifyContractPresence('review', {}, { contracts: [] });
10
+ expect(result.verdict).toBe('skip');
11
+ expect(result.detail).toContain('无 contracts 表项');
12
+ });
13
+
14
+ it('contracts 缺省(未配置)→ skip', () => {
15
+ expect(verifyContractPresence('review', { reviewReport: {} }).verdict).toBe('skip');
16
+ });
17
+
18
+ it('review 在场(reviewReport 已解析)→ pass', () => {
19
+ const result = verifyContractPresence(
20
+ 'review',
21
+ { reviewReport: { verdict: 'pass' } },
22
+ { contracts: ['review'] },
23
+ );
24
+ expect(result.verdict).toBe('pass');
25
+ });
26
+
27
+ it.each([undefined, null])('review 契约标记缺失(reviewReport=%s)→ violation', (reviewReport) => {
28
+ const result = verifyContractPresence('review', { reviewReport }, { contracts: ['review'] });
29
+ expect(result.verdict).toBe('violation');
30
+ expect(result.detail).toContain('契约标记缺失');
31
+ });
32
+
33
+ it('类型在清单内但无判定方法注册 → violation(配置与代码失配)', () => {
34
+ const result = verifyContractPresence('unknown-type', {}, { contracts: ['unknown-type'] });
35
+ expect(result.verdict).toBe('violation');
36
+ expect(result.detail).toContain('无判定方法注册');
37
+ });
38
+
39
+ it('checker 关闭 → skip', () => {
40
+ expect(
41
+ verifyContractPresence('review', {}, { contracts: ['review'], checkers: { contractPresence: false } }).verdict,
42
+ ).toBe('skip');
43
+ });
44
+ });
@@ -0,0 +1,60 @@
1
+ /**
2
+ * verifyPhaseFormat 测试(studio#160 验收:subject 不合规 / merge commit 违规)
3
+ */
4
+
5
+ import { verifyPhaseFormat } from '../phase-format';
6
+ import type { CommitInput } from '../types';
7
+
8
+ const sha = (n: number) => n.toString(16).padStart(40, '0');
9
+
10
+ function commit(partial: Partial<CommitInput> & { sha: string }): CommitInput {
11
+ return { subject: 'phase(impl): x', body: '', files: ['src/a.ts'], ...partial };
12
+ }
13
+
14
+ describe('verifyPhaseFormat', () => {
15
+ it('合规 subject → pass', () => {
16
+ const commits = [
17
+ commit({ sha: sha(1), subject: 'phase(test): add chain tests' }),
18
+ commit({ sha: sha(2), subject: 'phase(impl-2): wire it' }),
19
+ ];
20
+ const result = verifyPhaseFormat(commits);
21
+ expect(result.verdict).toBe('pass');
22
+ });
23
+
24
+ it.each([
25
+ 'feat: not a phase subject',
26
+ 'phase: missing scope',
27
+ 'phase(Impl): uppercase scope',
28
+ 'phase(impl):missing space after colon',
29
+ 'phase(impl): ',
30
+ ])('subject 不合规 → violation:%s', (subject) => {
31
+ const result = verifyPhaseFormat([commit({ sha: sha(1), subject })]);
32
+ expect(result.verdict).toBe('violation');
33
+ expect(result.commits[0].reason).toContain('subject 不合规');
34
+ });
35
+
36
+ it('merge commit(显式 isMerge)→ violation', () => {
37
+ const result = verifyPhaseFormat([commit({ sha: sha(1), isMerge: true })]);
38
+ expect(result.verdict).toBe('violation');
39
+ expect(result.commits[0].reason).toContain('merge commit');
40
+ });
41
+
42
+ it('merge commit(isMerge 缺省时 subject 启发式)→ violation', () => {
43
+ const result = verifyPhaseFormat([
44
+ commit({ sha: sha(1), subject: "Merge branch 'w/x'" }),
45
+ ]);
46
+ expect(result.verdict).toBe('violation');
47
+ });
48
+
49
+ it('显式 isMerge=false 覆盖 subject 启发式', () => {
50
+ const result = verifyPhaseFormat([
51
+ commit({ sha: sha(1), subject: 'Merge 字样但非 merge', isMerge: false }),
52
+ ]);
53
+ // subject 不含 phase 结构,仍因 subject 违规——但不应记 merge 违规
54
+ expect(result.commits[0].reason).not.toContain('merge commit');
55
+ });
56
+
57
+ it('checker 关闭 → skip', () => {
58
+ expect(verifyPhaseFormat([commit({ sha: sha(1) })], { checkers: { phaseFormat: false } }).verdict).toBe('skip');
59
+ });
60
+ });
@@ -0,0 +1,99 @@
1
+ /**
2
+ * verifyTddChain 测试(studio#160 验收:伪造引用三形态 / Tests: none waiver / 免检分类)
3
+ */
4
+
5
+ import { verifyTddChain } from '../tdd-chain';
6
+ import type { CommitInput } from '../types';
7
+
8
+ const sha = (n: number) => n.toString(16).padStart(40, '0');
9
+
10
+ function commit(partial: Partial<CommitInput> & { sha: string }): CommitInput {
11
+ return { subject: 'phase(impl): x', body: '', files: ['src/a.ts'], ...partial };
12
+ }
13
+
14
+ describe('verifyTddChain', () => {
15
+ it('合法引用链 → pass(测试 commit 在前,实现 commit 引用它)', () => {
16
+ const commits = [
17
+ commit({ sha: sha(1), files: ['src/a.test.ts'], subject: 'phase(test): add test' }),
18
+ commit({ sha: sha(2), files: ['src/a.ts'], body: 'impl\n\nTested-By: ' + sha(1) }),
19
+ ];
20
+ const result = verifyTddChain(commits);
21
+ expect(result.verdict).toBe('pass');
22
+ expect(result.commits.map((c) => c.verdict)).toEqual(['skip', 'pass']);
23
+ });
24
+
25
+ it('短 sha(7 位前缀)引用可命中', () => {
26
+ const commits = [
27
+ commit({ sha: sha(1), files: ['__tests__/a.ts'] }),
28
+ commit({ sha: sha(2), body: 'Tested-By: ' + sha(1).slice(0, 7) }),
29
+ ];
30
+ expect(verifyTddChain(commits).verdict).toBe('pass');
31
+ });
32
+
33
+ it('伪造引用:sha 不在提交集内 → violation', () => {
34
+ const commits = [commit({ sha: sha(2), body: 'Tested-By: ' + sha(9) })];
35
+ const result = verifyTddChain(commits);
36
+ expect(result.verdict).toBe('violation');
37
+ expect(result.commits[0].reason).toContain('不在本 WU 提交集内');
38
+ });
39
+
40
+ it('伪造引用:被引 commit 位置在本 commit 之后 → violation', () => {
41
+ const commits = [
42
+ commit({ sha: sha(2), body: 'Tested-By: ' + sha(1) }),
43
+ commit({ sha: sha(1), files: ['src/a.test.ts'] }),
44
+ ];
45
+ const result = verifyTddChain(commits);
46
+ expect(result.verdict).toBe('violation');
47
+ expect(result.commits[0].reason).toContain('位置不在本 commit 之前');
48
+ });
49
+
50
+ it('伪造引用:被引 commit 文件清单不含测试文件 → violation', () => {
51
+ const commits = [
52
+ commit({ sha: sha(1), files: ['src/b.ts'] }),
53
+ commit({ sha: sha(2), body: 'Tested-By: ' + sha(1) }),
54
+ ];
55
+ const result = verifyTddChain(commits);
56
+ expect(result.verdict).toBe('violation');
57
+ expect(result.commits[1].reason).toContain('未命中 test_globs');
58
+ });
59
+
60
+ it('缺 Tested-By trailer → violation', () => {
61
+ const result = verifyTddChain([commit({ sha: sha(1) })]);
62
+ expect(result.verdict).toBe('violation');
63
+ expect(result.commits[0].reason).toContain('缺 Tested-By trailer');
64
+ });
65
+
66
+ it('Tests: none trailer → waiver(放行,不算 violation)', () => {
67
+ const result = verifyTddChain([commit({ sha: sha(1), body: 'chore\n\nTests: none' })]);
68
+ expect(result.verdict).toBe('pass');
69
+ expect(result.commits[0].verdict).toBe('waiver');
70
+ });
71
+
72
+ it('纯测试 commit 与纯非代码 commit 天然免检', () => {
73
+ const commits = [
74
+ commit({ sha: sha(1), files: ['src/a.test.ts'] }),
75
+ commit({ sha: sha(2), files: ['README.md', 'docs/guide.md'] }),
76
+ ];
77
+ const result = verifyTddChain(commits);
78
+ expect(result.verdict).toBe('pass');
79
+ expect(result.commits.every((c) => c.verdict === 'skip')).toBe(true);
80
+ });
81
+
82
+ it('混合 commit(代码+测试同 commit)仍需引用链', () => {
83
+ const commits = [commit({ sha: sha(1), files: ['src/a.ts', 'src/a.test.ts'] })];
84
+ expect(verifyTddChain(commits).verdict).toBe('violation');
85
+ });
86
+
87
+ it('自定义 test_globs / noncode_globs 生效', () => {
88
+ const commits = [commit({ sha: sha(1), files: ['assets/logo.png'] })];
89
+ const withGlob = verifyTddChain(commits, { noncodeGlobs: ['**/*.png'] });
90
+ expect(withGlob.commits[0].verdict).toBe('skip');
91
+ expect(verifyTddChain(commits).commits[0].verdict).toBe('violation');
92
+ });
93
+
94
+ it('checker 关闭 / 总开关关闭 → skip', () => {
95
+ const commits = [commit({ sha: sha(1) })];
96
+ expect(verifyTddChain(commits, { checkers: { tddChain: false } }).verdict).toBe('skip');
97
+ expect(verifyTddChain(commits, { enabled: false }).verdict).toBe('skip');
98
+ });
99
+ });
@@ -0,0 +1,47 @@
1
+ /**
2
+ * commit 文件分类(tdd-chain 与 phase-format 共享同一份分类结果,Q4 定稿)
3
+ *
4
+ * - 纯非代码(全部命中 noncode_globs)与纯测试(全部命中 test_globs)commit 天然免检
5
+ * - 混合 commit 只要触到代码文件即需走引用链
6
+ * - 空文件清单按免检处理
7
+ */
8
+
9
+ import type { CommitInput, CompletionCheckersConfig } from './types';
10
+ import { DEFAULT_NONCODE_GLOBS, DEFAULT_TEST_GLOBS, matchAnyGlob } from './glob-match';
11
+
12
+ /** 单 commit 文件分类结果 */
13
+ export interface CommitFileClassification {
14
+ /** 无代码文件(纯测试 / 纯非代码 / 空清单)→ 天然免检 */
15
+ exempt: boolean;
16
+ /** 文件清单命中 test_globs(被引 commit 须满足) */
17
+ hasTests: boolean;
18
+ }
19
+
20
+ /** 从配置取 glob(缺省落默认) */
21
+ export function resolveGlobs(config: CompletionCheckersConfig): { testGlobs: string[]; noncodeGlobs: string[] } {
22
+ return {
23
+ testGlobs: config.testGlobs ?? DEFAULT_TEST_GLOBS,
24
+ noncodeGlobs: config.noncodeGlobs ?? DEFAULT_NONCODE_GLOBS,
25
+ };
26
+ }
27
+
28
+ /** 分类单 commit 的文件清单 */
29
+ export function classifyCommitFiles(
30
+ commit: CommitInput,
31
+ testGlobs: string[],
32
+ noncodeGlobs: string[],
33
+ ): CommitFileClassification {
34
+ if (commit.files.length === 0) {
35
+ return { exempt: true, hasTests: false };
36
+ }
37
+ let hasTests = false;
38
+ let hasCode = false;
39
+ for (const file of commit.files) {
40
+ if (matchAnyGlob(file, testGlobs)) {
41
+ hasTests = true;
42
+ } else if (!matchAnyGlob(file, noncodeGlobs)) {
43
+ hasCode = true;
44
+ }
45
+ }
46
+ return { exempt: !hasCode, hasTests };
47
+ }
@@ -0,0 +1,40 @@
1
+ /**
2
+ * contract-presence:通用契约在场引擎(Q5 定稿)
3
+ *
4
+ * - 按 yml contracts 类型清单查表:类型不在清单内 = skip(不算违规)
5
+ * - 类型 → 判定方法的映射是代码不是配置(CONTRACT_JUDGMENTS);
6
+ * 首个活跃条目 review → context.reviewReport 在场(studio agent-loop 已解析字段,不重复解析)
7
+ * - 类型在清单内但无判定方法注册 = violation(暴露配置与代码失配)
8
+ */
9
+
10
+ import type { CompletionCheckersConfig, ContractPresenceContext, ContractPresenceResult } from './types';
11
+
12
+ /** 类型 → 判定方法映射(代码,不是配置)。新增契约类型 = 在此加一条 + 测试 */
13
+ const CONTRACT_JUDGMENTS: Record<string, (context: ContractPresenceContext) => boolean> = {
14
+ review: (context) => context.reviewReport != null,
15
+ };
16
+
17
+ /** 契约在场判定 */
18
+ export function verifyContractPresence(
19
+ type: string,
20
+ context: ContractPresenceContext,
21
+ config: CompletionCheckersConfig = {},
22
+ ): ContractPresenceResult {
23
+ if (config.enabled === false || config.checkers?.contractPresence === false) {
24
+ return { checker: 'contract-presence', verdict: 'skip', detail: 'checker 已禁用' };
25
+ }
26
+ if (!(config.contracts ?? []).includes(type)) {
27
+ return { checker: 'contract-presence', verdict: 'skip', detail: `类型 ${type} 无 contracts 表项` };
28
+ }
29
+ const judge = CONTRACT_JUDGMENTS[type];
30
+ if (!judge) {
31
+ return {
32
+ checker: 'contract-presence',
33
+ verdict: 'violation',
34
+ detail: `类型 ${type} 已在 contracts 声明但无判定方法注册(配置与代码失配)`,
35
+ };
36
+ }
37
+ return judge(context)
38
+ ? { checker: 'contract-presence', verdict: 'pass' }
39
+ : { checker: 'contract-presence', verdict: 'violation', detail: `类型 ${type} 契约标记缺失` };
40
+ }
@@ -0,0 +1,54 @@
1
+ /**
2
+ * 轻量 glob 匹配(支持 `**`、`*`、`?`),用于 commits 文件清单分类。
3
+ * 不匹配文件系统,纯字符串判定。
4
+ */
5
+
6
+ /** 默认测试文件 glob(Q2 定稿) */
7
+ export const DEFAULT_TEST_GLOBS = ['**/*.test.ts', '**/*.spec.ts', '**/__tests__/**'];
8
+
9
+ /** 默认非代码文件 glob(兜底;权威清单由 yml noncode_globs 供给) */
10
+ export const DEFAULT_NONCODE_GLOBS = ['**/*.md', '**/*.mdx', '**/*.txt', 'docs/**'];
11
+
12
+ function escapeRegExp(s: string): string {
13
+ return s.replace(/[.+^${}()|[\]\\]/g, '\\$&');
14
+ }
15
+
16
+ /** glob → 锚定正则。前缀 `**` + `/` 匹配零或多段路径;`*` 不跨 `/`;`?` 匹配单个非 `/` 字符 */
17
+ function globToRegExp(glob: string): RegExp {
18
+ let re = '';
19
+ let i = 0;
20
+ while (i < glob.length) {
21
+ const c = glob[i];
22
+ if (c === '*') {
23
+ if (glob[i + 1] === '*') {
24
+ if (glob[i + 2] === '/') {
25
+ re += '(?:[^/]+/)*';
26
+ i += 3;
27
+ } else {
28
+ re += '.*';
29
+ i += 2;
30
+ }
31
+ } else {
32
+ re += '[^/]*';
33
+ i += 1;
34
+ }
35
+ } else if (c === '?') {
36
+ re += '[^/]';
37
+ i += 1;
38
+ } else {
39
+ re += escapeRegExp(c);
40
+ i += 1;
41
+ }
42
+ }
43
+ return new RegExp('^' + re + '$');
44
+ }
45
+
46
+ /** 单 glob 匹配 */
47
+ export function matchGlob(file: string, glob: string): boolean {
48
+ return globToRegExp(glob).test(file);
49
+ }
50
+
51
+ /** 命中任一 glob */
52
+ export function matchAnyGlob(file: string, globs: string[]): boolean {
53
+ return globs.some((g) => matchGlob(file, g));
54
+ }
@@ -0,0 +1,19 @@
1
+ /**
2
+ * Completion Checkers(T7-E1,studio#160)
3
+ *
4
+ * WU 收尾软观测三件套——纯判定函数库:
5
+ * - verifyTddChain:实现 commit 的 Tested-By 引用链验证
6
+ * - verifyPhaseFormat:phase(...) subject 结构验证
7
+ * - verifyContractPresence:通用契约在场引擎
8
+ *
9
+ * 与 ConstraintCheck 闭环注册表无关:直接 export,不注册、不碰 checkers/index.ts。
10
+ * 函数不碰文件系统与 git;commits 由调用方(studio 第四段守卫,T7-E2)供给。
11
+ */
12
+
13
+ export * from './types';
14
+ export { DEFAULT_TEST_GLOBS, DEFAULT_NONCODE_GLOBS, matchGlob, matchAnyGlob } from './glob-match';
15
+ export { classifyCommitFiles, resolveGlobs } from './classify';
16
+ export type { CommitFileClassification } from './classify';
17
+ export { verifyTddChain, TESTED_BY_RE, TESTS_NONE_RE } from './tdd-chain';
18
+ export { verifyPhaseFormat, PHASE_SUBJECT_RE } from './phase-format';
19
+ export { verifyContractPresence } from './contract-presence';
@@ -0,0 +1,48 @@
1
+ /**
2
+ * phase-format:WU 提交 subject 的 phase 结构验证(Q4 定稿)
3
+ *
4
+ * 协议(正则写死在函数里,不进配置):
5
+ * - 全部非 merge commit 的 subject 命中 `^phase\([a-z0-9-]+\):\s+\S`
6
+ * - merge commit 出现即记违规
7
+ * - 阶段名不维护词表不查
8
+ * - 文件分类口径与 tdd-chain 共享(classify.ts),本 checker 不做免检——subject 结构要求覆盖全部非 merge commit
9
+ */
10
+
11
+ import type { CommitInput, CommitVerdict, CompletionCheckersConfig, PhaseFormatResult } from './types';
12
+
13
+ /** phase subject 结构(写死) */
14
+ export const PHASE_SUBJECT_RE = /^phase\([a-z0-9-]+\):\s+\S/;
15
+
16
+ /** merge commit 的 subject 启发式(调用方未显式给 isMerge 时兜底) */
17
+ const MERGE_SUBJECT_RE = /^Merge\s/;
18
+
19
+ /** 判定 merge commit:优先 isMerge 显式字段,缺省按 subject 启发式 */
20
+ function isMergeCommit(commit: CommitInput): boolean {
21
+ return commit.isMerge ?? MERGE_SUBJECT_RE.test(commit.subject);
22
+ }
23
+
24
+ /** phase 格式验证 */
25
+ export function verifyPhaseFormat(
26
+ commits: CommitInput[],
27
+ config: CompletionCheckersConfig = {},
28
+ ): PhaseFormatResult {
29
+ if (config.enabled === false || config.checkers?.phaseFormat === false) {
30
+ return { checker: 'phase-format', verdict: 'skip', commits: [] };
31
+ }
32
+
33
+ const verdicts: CommitVerdict[] = commits.map((commit) => {
34
+ if (isMergeCommit(commit)) {
35
+ return { sha: commit.sha, verdict: 'violation', reason: 'WU 提交集内出现 merge commit' };
36
+ }
37
+ if (!PHASE_SUBJECT_RE.test(commit.subject)) {
38
+ return { sha: commit.sha, verdict: 'violation', reason: `subject 不合规:${commit.subject}` };
39
+ }
40
+ return { sha: commit.sha, verdict: 'pass' };
41
+ });
42
+
43
+ return {
44
+ checker: 'phase-format',
45
+ verdict: verdicts.some((v) => v.verdict === 'violation') ? 'violation' : 'pass',
46
+ commits: verdicts,
47
+ };
48
+ }
@@ -0,0 +1,63 @@
1
+ /**
2
+ * tdd-chain:实现 commit 的测试引用链验证(Q2/Q3 定稿)
3
+ *
4
+ * 协议(写死为机制本体,不进配置):
5
+ * - 实现 commit 必须带 trailer `Tested-By: <sha>`
6
+ * - 被引 sha 须 a) 在本 WU 提交集内 b) 序列位置在本 commit 之前(比位置不比时间戳)c) 其文件清单命中 test_globs
7
+ * - 豁免:trailer `Tests: none` → waiver(放行记台账);纯非代码/纯测试 commit 天然免检
8
+ */
9
+
10
+ import type { CommitInput, CommitVerdict, CompletionCheckersConfig, TddChainResult } from './types';
11
+ import { classifyCommitFiles, resolveGlobs } from './classify';
12
+
13
+ /** Tested-By 引用协议(写死) */
14
+ export const TESTED_BY_RE = /^Tested-By:\s*([0-9a-f]{7,40})\s*$/gim;
15
+
16
+ /** Tests: none 豁免协议(写死) */
17
+ export const TESTS_NONE_RE = /^Tests:\s*none\s*$/im;
18
+
19
+ /** 引用链验证:伪造引用(不存在 / 位置在后 / 不含测试文件)一律记 violation */
20
+ export function verifyTddChain(
21
+ commits: CommitInput[],
22
+ config: CompletionCheckersConfig = {},
23
+ ): TddChainResult {
24
+ if (config.enabled === false || config.checkers?.tddChain === false) {
25
+ return { checker: 'tdd-chain', verdict: 'skip', commits: [] };
26
+ }
27
+
28
+ const { testGlobs, noncodeGlobs } = resolveGlobs(config);
29
+ // 文件分类一次性算好,与同批 commit 的其他 checker 共享口径
30
+ const classifications = commits.map((c) => classifyCommitFiles(c, testGlobs, noncodeGlobs));
31
+
32
+ const verdicts: CommitVerdict[] = commits.map((commit, i) => {
33
+ if (classifications[i].exempt) {
34
+ return { sha: commit.sha, verdict: 'skip', reason: '纯测试/纯非代码 commit,天然免检' };
35
+ }
36
+ if (TESTS_NONE_RE.test(commit.body)) {
37
+ return { sha: commit.sha, verdict: 'waiver', reason: 'Tests: none 显式豁免' };
38
+ }
39
+ TESTED_BY_RE.lastIndex = 0;
40
+ const m = TESTED_BY_RE.exec(commit.body);
41
+ if (!m) {
42
+ return { sha: commit.sha, verdict: 'violation', reason: '缺 Tested-By trailer 且未声明 Tests: none' };
43
+ }
44
+ const ref = m[1].toLowerCase();
45
+ const j = commits.findIndex((c) => c.sha.toLowerCase().startsWith(ref));
46
+ if (j === -1) {
47
+ return { sha: commit.sha, verdict: 'violation', reason: `Tested-By 引用 ${ref} 不在本 WU 提交集内` };
48
+ }
49
+ if (j >= i) {
50
+ return { sha: commit.sha, verdict: 'violation', reason: `Tested-By 引用 ${ref} 位置不在本 commit 之前` };
51
+ }
52
+ if (!classifications[j].hasTests) {
53
+ return { sha: commit.sha, verdict: 'violation', reason: `被引 commit ${ref} 文件清单未命中 test_globs` };
54
+ }
55
+ return { sha: commit.sha, verdict: 'pass' };
56
+ });
57
+
58
+ return {
59
+ checker: 'tdd-chain',
60
+ verdict: verdicts.some((v) => v.verdict === 'violation') ? 'violation' : 'pass',
61
+ commits: verdicts,
62
+ };
63
+ }