@dommaker/harness 0.16.9 → 0.17.1

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 (337) hide show
  1. package/CHANGELOG.md +39 -0
  2. package/README.md +33 -32
  3. package/bin/harness.js +33 -20
  4. package/dist/cli/commands/check.d.ts +3 -2
  5. package/dist/cli/commands/check.d.ts.map +1 -1
  6. package/dist/cli/commands/check.js +69 -36
  7. package/dist/cli/commands/check.js.map +1 -1
  8. package/dist/cli/commands/constraints-report.d.ts +42 -0
  9. package/dist/cli/commands/constraints-report.d.ts.map +1 -0
  10. package/dist/cli/commands/constraints-report.js +233 -0
  11. package/dist/cli/commands/constraints-report.js.map +1 -0
  12. package/dist/cli/commands/constraints-retire.d.ts +71 -0
  13. package/dist/cli/commands/constraints-retire.d.ts.map +1 -0
  14. package/dist/cli/commands/constraints-retire.js +384 -0
  15. package/dist/cli/commands/constraints-retire.js.map +1 -0
  16. package/dist/cli/commands/constraints.d.ts +1 -1
  17. package/dist/cli/commands/constraints.d.ts.map +1 -1
  18. package/dist/cli/commands/constraints.js +4 -4
  19. package/dist/cli/commands/constraints.js.map +1 -1
  20. package/dist/cli/commands/index.d.ts +2 -1
  21. package/dist/cli/commands/index.d.ts.map +1 -1
  22. package/dist/cli/commands/index.js +10 -4
  23. package/dist/cli/commands/index.js.map +1 -1
  24. package/dist/cli/commands/init.d.ts +20 -0
  25. package/dist/cli/commands/init.d.ts.map +1 -1
  26. package/dist/cli/commands/init.js +81 -59
  27. package/dist/cli/commands/init.js.map +1 -1
  28. package/dist/cli/commands/report.js +4 -4
  29. package/dist/cli/commands/report.js.map +1 -1
  30. package/dist/cli/commands/status.d.ts.map +1 -1
  31. package/dist/cli/commands/status.js +3 -4
  32. package/dist/cli/commands/status.js.map +1 -1
  33. package/dist/cli/commands/sync-docs/capabilities-syncer.d.ts +5 -1
  34. package/dist/cli/commands/sync-docs/capabilities-syncer.d.ts.map +1 -1
  35. package/dist/cli/commands/sync-docs/capabilities-syncer.js +2 -10
  36. package/dist/cli/commands/sync-docs/capabilities-syncer.js.map +1 -1
  37. package/dist/cli/commands/validate.d.ts.map +1 -1
  38. package/dist/cli/commands/validate.js +1 -5
  39. package/dist/cli/commands/validate.js.map +1 -1
  40. package/dist/core/constraints/capabilities-parser.d.ts +7 -0
  41. package/dist/core/constraints/capabilities-parser.d.ts.map +1 -1
  42. package/dist/core/constraints/capabilities-parser.js +13 -0
  43. package/dist/core/constraints/capabilities-parser.js.map +1 -1
  44. package/dist/core/constraints/checker.d.ts +13 -5
  45. package/dist/core/constraints/checker.d.ts.map +1 -1
  46. package/dist/core/constraints/checker.js +62 -25
  47. package/dist/core/constraints/checker.js.map +1 -1
  48. package/dist/core/constraints/checkers/capability-sync.d.ts +9 -2
  49. package/dist/core/constraints/checkers/capability-sync.d.ts.map +1 -1
  50. package/dist/core/constraints/checkers/capability-sync.js +55 -22
  51. package/dist/core/constraints/checkers/capability-sync.js.map +1 -1
  52. package/dist/core/constraints/checkers/context-doc-sync.d.ts +3 -0
  53. package/dist/core/constraints/checkers/context-doc-sync.d.ts.map +1 -1
  54. package/dist/core/constraints/checkers/context-doc-sync.js +8 -2
  55. package/dist/core/constraints/checkers/context-doc-sync.js.map +1 -1
  56. package/dist/core/constraints/checkers/docs-freshness.d.ts +1 -0
  57. package/dist/core/constraints/checkers/docs-freshness.d.ts.map +1 -1
  58. package/dist/core/constraints/checkers/docs-freshness.js +39 -0
  59. package/dist/core/constraints/checkers/docs-freshness.js.map +1 -1
  60. package/dist/core/constraints/checkers/index.d.ts +8 -4
  61. package/dist/core/constraints/checkers/index.d.ts.map +1 -1
  62. package/dist/core/constraints/checkers/index.js +29 -46
  63. package/dist/core/constraints/checkers/index.js.map +1 -1
  64. package/dist/core/constraints/checkers/iron-flags.d.ts +6 -7
  65. package/dist/core/constraints/checkers/iron-flags.d.ts.map +1 -1
  66. package/dist/core/constraints/checkers/iron-flags.js +10 -17
  67. package/dist/core/constraints/checkers/iron-flags.js.map +1 -1
  68. package/dist/core/constraints/checkers/no-hardcoded-credentials.d.ts +13 -0
  69. package/dist/core/constraints/checkers/no-hardcoded-credentials.d.ts.map +1 -0
  70. package/dist/core/constraints/checkers/no-hardcoded-credentials.js +91 -0
  71. package/dist/core/constraints/checkers/no-hardcoded-credentials.js.map +1 -0
  72. package/dist/core/constraints/checkers/types.d.ts +15 -4
  73. package/dist/core/constraints/checkers/types.d.ts.map +1 -1
  74. package/dist/core/constraints/checkers/types.js +16 -4
  75. package/dist/core/constraints/checkers/types.js.map +1 -1
  76. package/dist/core/constraints/context-builder.d.ts +6 -3
  77. package/dist/core/constraints/context-builder.d.ts.map +1 -1
  78. package/dist/core/constraints/context-builder.js +29 -20
  79. package/dist/core/constraints/context-builder.js.map +1 -1
  80. package/dist/core/constraints/definitions/guidelines.d.ts +6 -2
  81. package/dist/core/constraints/definitions/guidelines.d.ts.map +1 -1
  82. package/dist/core/constraints/definitions/guidelines.js +29 -572
  83. package/dist/core/constraints/definitions/guidelines.js.map +1 -1
  84. package/dist/core/constraints/definitions/iron-laws.d.ts +6 -2
  85. package/dist/core/constraints/definitions/iron-laws.d.ts.map +1 -1
  86. package/dist/core/constraints/definitions/iron-laws.js +48 -231
  87. package/dist/core/constraints/definitions/iron-laws.js.map +1 -1
  88. package/dist/core/constraints/definitions/prompts.d.ts +12 -0
  89. package/dist/core/constraints/definitions/prompts.d.ts.map +1 -0
  90. package/dist/core/constraints/definitions/prompts.js +405 -0
  91. package/dist/core/constraints/definitions/prompts.js.map +1 -0
  92. package/dist/core/constraints/definitions.d.ts +15 -9
  93. package/dist/core/constraints/definitions.d.ts.map +1 -1
  94. package/dist/core/constraints/definitions.js +18 -12
  95. package/dist/core/constraints/definitions.js.map +1 -1
  96. package/dist/core/constraints/index.d.ts +3 -2
  97. package/dist/core/constraints/index.d.ts.map +1 -1
  98. package/dist/core/constraints/index.js +8 -2
  99. package/dist/core/constraints/index.js.map +1 -1
  100. package/dist/core/constraints/injection-drift.d.ts +48 -0
  101. package/dist/core/constraints/injection-drift.d.ts.map +1 -0
  102. package/dist/core/constraints/injection-drift.js +140 -0
  103. package/dist/core/constraints/injection-drift.js.map +1 -0
  104. package/dist/core/constraints/injection-renderer.d.ts +23 -0
  105. package/dist/core/constraints/injection-renderer.d.ts.map +1 -0
  106. package/dist/core/constraints/injection-renderer.js +44 -0
  107. package/dist/core/constraints/injection-renderer.js.map +1 -0
  108. package/dist/core/constraints/interceptor.d.ts.map +1 -1
  109. package/dist/core/constraints/interceptor.js +3 -2
  110. package/dist/core/constraints/interceptor.js.map +1 -1
  111. package/dist/core/constraints/usage-report.d.ts +95 -0
  112. package/dist/core/constraints/usage-report.d.ts.map +1 -0
  113. package/dist/core/constraints/usage-report.js +206 -0
  114. package/dist/core/constraints/usage-report.js.map +1 -0
  115. package/dist/core/effective-constraints.d.ts +47 -0
  116. package/dist/core/effective-constraints.d.ts.map +1 -0
  117. package/dist/core/effective-constraints.js +60 -0
  118. package/dist/core/effective-constraints.js.map +1 -0
  119. package/dist/core/index.d.ts +1 -0
  120. package/dist/core/index.d.ts.map +1 -1
  121. package/dist/core/index.js +2 -0
  122. package/dist/core/index.js.map +1 -1
  123. package/dist/core/project-config-loader.d.ts +17 -2
  124. package/dist/core/project-config-loader.d.ts.map +1 -1
  125. package/dist/core/project-config-loader.js +55 -8
  126. package/dist/core/project-config-loader.js.map +1 -1
  127. package/dist/dashboard/data.d.ts +1 -9
  128. package/dist/dashboard/data.d.ts.map +1 -1
  129. package/dist/dashboard/data.js +7 -15
  130. package/dist/dashboard/data.js.map +1 -1
  131. package/dist/dashboard/types.d.ts +13 -1
  132. package/dist/dashboard/types.d.ts.map +1 -1
  133. package/dist/index.d.ts +0 -4
  134. package/dist/index.d.ts.map +1 -1
  135. package/dist/index.js +1 -13
  136. package/dist/index.js.map +1 -1
  137. package/dist/monitoring/index.d.ts +0 -2
  138. package/dist/monitoring/index.d.ts.map +1 -1
  139. package/dist/monitoring/index.js +0 -2
  140. package/dist/monitoring/index.js.map +1 -1
  141. package/dist/monitoring/knowledge-evolver.d.ts +1 -1
  142. package/dist/monitoring/knowledge-evolver.js +1 -1
  143. package/dist/monitoring/trace-analyzer.d.ts +1 -1
  144. package/dist/monitoring/trace-analyzer.d.ts.map +1 -1
  145. package/dist/monitoring/trace-analyzer.js +16 -10
  146. package/dist/monitoring/trace-analyzer.js.map +1 -1
  147. package/dist/monitoring/traces.d.ts +2 -1
  148. package/dist/monitoring/traces.d.ts.map +1 -1
  149. package/dist/monitoring/traces.js +5 -3
  150. package/dist/monitoring/traces.js.map +1 -1
  151. package/dist/presets/standard.d.ts +3 -1
  152. package/dist/presets/standard.d.ts.map +1 -1
  153. package/dist/presets/standard.js +18 -8
  154. package/dist/presets/standard.js.map +1 -1
  155. package/dist/types/constraint.d.ts +51 -11
  156. package/dist/types/constraint.d.ts.map +1 -1
  157. package/dist/types/constraint.js +8 -4
  158. package/dist/types/constraint.js.map +1 -1
  159. package/dist/types/index.d.ts +1 -1
  160. package/dist/types/index.d.ts.map +1 -1
  161. package/dist/types/index.js.map +1 -1
  162. package/dist/types/monitoring-types.d.ts +2 -63
  163. package/dist/types/monitoring-types.d.ts.map +1 -1
  164. package/dist/types/monitoring-types.js +2 -2
  165. package/dist/types/project-config.d.ts +26 -1
  166. package/dist/types/project-config.d.ts.map +1 -1
  167. package/dist/types/trace.d.ts +16 -7
  168. package/dist/types/trace.d.ts.map +1 -1
  169. package/dist/types/trace.js +8 -0
  170. package/dist/types/trace.js.map +1 -1
  171. package/package.json +1 -1
  172. package/src/CONTEXT.md +2 -2
  173. package/src/__tests__/checker-extra.test.ts +228 -333
  174. package/src/__tests__/checker.test.ts +87 -724
  175. package/src/__tests__/constraint-doctor.test.ts +5 -5
  176. package/src/__tests__/doctor-extra.test.ts +164 -0
  177. package/src/__tests__/effective-constraints.test.ts +147 -0
  178. package/src/__tests__/extend-exceptions.test.ts +3 -3
  179. package/src/__tests__/iron-laws.test.ts +115 -91
  180. package/src/__tests__/no-hardcoded-credentials.test.ts +90 -0
  181. package/src/__tests__/presets.test.ts +7 -7
  182. package/src/__tests__/project-config-loader-extra.test.ts +41 -4
  183. package/src/__tests__/skip-semantics.test.ts +322 -0
  184. package/src/cli/commands/CONTEXT.md +5 -3
  185. package/src/cli/commands/__tests__/check-drift.test.ts +127 -0
  186. package/src/cli/commands/__tests__/check-skip-output.test.ts +128 -0
  187. package/src/cli/commands/__tests__/check.test.ts +14 -25
  188. package/src/cli/commands/__tests__/constraints-report.test.ts +327 -0
  189. package/src/cli/commands/__tests__/constraints-retire.test.ts +283 -0
  190. package/src/cli/commands/__tests__/init-injection.test.ts +234 -0
  191. package/src/cli/commands/__tests__/status-extra.test.ts +11 -11
  192. package/src/cli/commands/__tests__/status.test.ts +1 -1
  193. package/src/cli/commands/check.ts +72 -38
  194. package/src/cli/commands/constraints-report.ts +234 -0
  195. package/src/cli/commands/constraints-retire.ts +443 -0
  196. package/src/cli/commands/constraints.ts +5 -5
  197. package/src/cli/commands/index.ts +2 -1
  198. package/src/cli/commands/init.ts +94 -66
  199. package/src/cli/commands/report.ts +6 -6
  200. package/src/cli/commands/status.ts +3 -4
  201. package/src/cli/commands/sync-docs/capabilities-syncer.ts +5 -7
  202. package/src/cli/commands/validate.ts +1 -5
  203. package/src/core/CONTEXT.md +7 -4
  204. package/src/core/constraints/__tests__/injection-drift.test.ts +164 -0
  205. package/src/core/constraints/__tests__/interceptor.test.ts +4 -0
  206. package/src/core/constraints/capabilities-parser.ts +13 -0
  207. package/src/core/constraints/checker.ts +73 -28
  208. package/src/core/constraints/checkers/capability-sync.ts +72 -29
  209. package/src/core/constraints/checkers/context-doc-sync.ts +9 -2
  210. package/src/core/constraints/checkers/docs-freshness.ts +43 -0
  211. package/src/core/constraints/checkers/index.ts +38 -71
  212. package/src/core/constraints/checkers/iron-flags.ts +13 -49
  213. package/src/core/constraints/checkers/no-hardcoded-credentials.ts +93 -0
  214. package/src/core/constraints/checkers/types.ts +27 -5
  215. package/src/core/constraints/context-builder.ts +28 -19
  216. package/src/core/constraints/definitions/guidelines.ts +29 -597
  217. package/src/core/constraints/definitions/iron-laws.ts +48 -239
  218. package/src/core/constraints/definitions/prompts.ts +419 -0
  219. package/src/core/constraints/definitions.ts +18 -11
  220. package/src/core/constraints/index.ts +10 -1
  221. package/src/core/constraints/injection-drift.ts +139 -0
  222. package/src/core/constraints/injection-renderer.ts +44 -0
  223. package/src/core/constraints/interceptor.ts +3 -2
  224. package/src/core/constraints/usage-report.ts +254 -0
  225. package/src/core/effective-constraints.ts +85 -0
  226. package/src/core/index.ts +4 -1
  227. package/src/core/project-config-loader.ts +59 -9
  228. package/src/dashboard/__tests__/data.test.ts +5 -12
  229. package/src/dashboard/data.ts +9 -19
  230. package/src/dashboard/types.ts +18 -1
  231. package/src/index.ts +0 -12
  232. package/src/knowledge/CONTEXT.md +2 -2
  233. package/src/monitoring/CONTEXT.md +3 -4
  234. package/src/monitoring/index.ts +0 -2
  235. package/src/monitoring/knowledge-evolver.ts +1 -1
  236. package/src/monitoring/trace-analyzer.ts +15 -10
  237. package/src/monitoring/traces.ts +10 -6
  238. package/src/presets/__tests__/standard.test.ts +11 -9
  239. package/src/presets/standard.ts +24 -15
  240. package/src/types/constraint.ts +59 -14
  241. package/src/types/index.ts +1 -3
  242. package/src/types/monitoring-types.ts +2 -84
  243. package/src/types/project-config.ts +30 -1
  244. package/src/types/trace.ts +18 -7
  245. package/src/verification/CONTEXT.md +1 -1
  246. package/dist/cli/commands/flow.d.ts +0 -22
  247. package/dist/cli/commands/flow.d.ts.map +0 -1
  248. package/dist/cli/commands/flow.js +0 -275
  249. package/dist/cli/commands/flow.js.map +0 -1
  250. package/dist/constraints/lifecycle-runner.d.ts +0 -52
  251. package/dist/constraints/lifecycle-runner.d.ts.map +0 -1
  252. package/dist/constraints/lifecycle-runner.js +0 -151
  253. package/dist/constraints/lifecycle-runner.js.map +0 -1
  254. package/dist/constraints/registry.d.ts +0 -27
  255. package/dist/constraints/registry.d.ts.map +0 -1
  256. package/dist/constraints/registry.js +0 -128
  257. package/dist/constraints/registry.js.map +0 -1
  258. package/dist/constraints/types.d.ts +0 -53
  259. package/dist/constraints/types.d.ts.map +0 -1
  260. package/dist/constraints/types.js +0 -8
  261. package/dist/constraints/types.js.map +0 -1
  262. package/dist/core/constraints/checkers/always-pass.d.ts +0 -17
  263. package/dist/core/constraints/checkers/always-pass.d.ts.map +0 -1
  264. package/dist/core/constraints/checkers/always-pass.js +0 -23
  265. package/dist/core/constraints/checkers/always-pass.js.map +0 -1
  266. package/dist/core/constraints/checkers/guideline-flags.d.ts +0 -9
  267. package/dist/core/constraints/checkers/guideline-flags.d.ts.map +0 -1
  268. package/dist/core/constraints/checkers/guideline-flags.js +0 -13
  269. package/dist/core/constraints/checkers/guideline-flags.js.map +0 -1
  270. package/dist/core/constraints/checkers/no-any-type.d.ts +0 -6
  271. package/dist/core/constraints/checkers/no-any-type.d.ts.map +0 -1
  272. package/dist/core/constraints/checkers/no-any-type.js +0 -42
  273. package/dist/core/constraints/checkers/no-any-type.js.map +0 -1
  274. package/dist/core/constraints/checkers/no-excuse-patterns.d.ts +0 -6
  275. package/dist/core/constraints/checkers/no-excuse-patterns.d.ts.map +0 -1
  276. package/dist/core/constraints/checkers/no-excuse-patterns.js +0 -25
  277. package/dist/core/constraints/checkers/no-excuse-patterns.js.map +0 -1
  278. package/dist/core/constraints/checkers/no-fuzzy-completion-claim.d.ts +0 -6
  279. package/dist/core/constraints/checkers/no-fuzzy-completion-claim.d.ts.map +0 -1
  280. package/dist/core/constraints/checkers/no-fuzzy-completion-claim.js +0 -34
  281. package/dist/core/constraints/checkers/no-fuzzy-completion-claim.js.map +0 -1
  282. package/dist/core/constraints/checkers/no-performative-agreement.d.ts +0 -8
  283. package/dist/core/constraints/checkers/no-performative-agreement.d.ts.map +0 -1
  284. package/dist/core/constraints/checkers/no-performative-agreement.js +0 -23
  285. package/dist/core/constraints/checkers/no-performative-agreement.js.map +0 -1
  286. package/dist/core/constraints/checkers/no-simplification-without-approval.d.ts +0 -6
  287. package/dist/core/constraints/checkers/no-simplification-without-approval.d.ts.map +0 -1
  288. package/dist/core/constraints/checkers/no-simplification-without-approval.js +0 -30
  289. package/dist/core/constraints/checkers/no-simplification-without-approval.js.map +0 -1
  290. package/dist/core/constraints/checkers/test-coverage.d.ts +0 -6
  291. package/dist/core/constraints/checkers/test-coverage.d.ts.map +0 -1
  292. package/dist/core/constraints/checkers/test-coverage.js +0 -33
  293. package/dist/core/constraints/checkers/test-coverage.js.map +0 -1
  294. package/dist/core/constraints/checkers/yagni.d.ts +0 -8
  295. package/dist/core/constraints/checkers/yagni.d.ts.map +0 -1
  296. package/dist/core/constraints/checkers/yagni.js +0 -96
  297. package/dist/core/constraints/checkers/yagni.js.map +0 -1
  298. package/dist/core/constraints/definitions/tips.d.ts +0 -7
  299. package/dist/core/constraints/definitions/tips.d.ts.map +0 -1
  300. package/dist/core/constraints/definitions/tips.js +0 -62
  301. package/dist/core/constraints/definitions/tips.js.map +0 -1
  302. package/dist/evolution/auto-evolve.d.ts +0 -35
  303. package/dist/evolution/auto-evolve.d.ts.map +0 -1
  304. package/dist/evolution/auto-evolve.js +0 -64
  305. package/dist/evolution/auto-evolve.js.map +0 -1
  306. package/dist/evolution/index.d.ts +0 -2
  307. package/dist/evolution/index.d.ts.map +0 -1
  308. package/dist/evolution/index.js +0 -18
  309. package/dist/evolution/index.js.map +0 -1
  310. package/dist/monitoring/constraint-evolver.d.ts +0 -120
  311. package/dist/monitoring/constraint-evolver.d.ts.map +0 -1
  312. package/dist/monitoring/constraint-evolver.js +0 -410
  313. package/dist/monitoring/constraint-evolver.js.map +0 -1
  314. package/src/__tests__/constraint-evolver.test.ts +0 -405
  315. package/src/__tests__/doctor-evolver.test.ts +0 -381
  316. package/src/cli/commands/__tests__/flow.test.ts +0 -343
  317. package/src/cli/commands/flow.ts +0 -277
  318. package/src/constraints/__tests__/lifecycle-runner.test.ts +0 -246
  319. package/src/constraints/__tests__/registry.test.ts +0 -196
  320. package/src/constraints/lifecycle-runner.ts +0 -184
  321. package/src/constraints/registry.ts +0 -134
  322. package/src/constraints/types.ts +0 -59
  323. package/src/core/constraints/checkers/always-pass.ts +0 -22
  324. package/src/core/constraints/checkers/guideline-flags.ts +0 -30
  325. package/src/core/constraints/checkers/no-any-type.ts +0 -42
  326. package/src/core/constraints/checkers/no-excuse-patterns.ts +0 -24
  327. package/src/core/constraints/checkers/no-fuzzy-completion-claim.ts +0 -33
  328. package/src/core/constraints/checkers/no-performative-agreement.ts +0 -22
  329. package/src/core/constraints/checkers/no-simplification-without-approval.ts +0 -29
  330. package/src/core/constraints/checkers/test-coverage.ts +0 -35
  331. package/src/core/constraints/checkers/yagni.ts +0 -65
  332. package/src/core/constraints/definitions/tips.ts +0 -63
  333. package/src/evolution/CONTEXT.md +0 -22
  334. package/src/evolution/__tests__/auto-evolve.test.ts +0 -319
  335. package/src/evolution/auto-evolve.ts +0 -98
  336. package/src/evolution/index.ts +0 -1
  337. package/src/monitoring/constraint-evolver.ts +0 -501
@@ -0,0 +1,443 @@
1
+ /**
2
+ * harness constraints retire —— 约束退役(ADR-0001 决策 2/5)
3
+ *
4
+ * 建议层全自动(候选诊断复用 report 数据层),执行层保留一次人确认。
5
+ * 落盘形态 = config.yml `enabled: false` + `retired` 元数据(不发明第二套状态):
6
+ *
7
+ * constraints:
8
+ * <id>:
9
+ * enabled: false
10
+ * retired: { at, reason, stats: { total, fail, failRate } }
11
+ *
12
+ * 每条同时写一条 KnowledgeStore 记录(consumptionMode: 'signal'),
13
+ * 并同步 CLAUDE.md 注入段(存在 HARNESS_CONSTRAINTS 标记时)。
14
+ *
15
+ * retire 不是删除——恢复方法:删除 config.yml 中 constraints.<id> 段。
16
+ *
17
+ * 交互与执行分离:retireConstraint 为纯执行逻辑(同步、可测),
18
+ * runRetireInteractive 只做 readline 交互,IO 流可注入。
19
+ */
20
+
21
+ import * as fs from 'fs';
22
+ import * as path from 'path';
23
+ import * as readline from 'readline';
24
+ import * as yaml from 'js-yaml';
25
+ import chalk from 'chalk';
26
+ import { getConstraint } from '../../core/constraints/definitions';
27
+ import { renderConstraintsSection, CONSTRAINTS_START_MARKER, CONSTRAINTS_END_MARKER } from '../../core/constraints/injection-renderer';
28
+ import { getEffectiveConstraints } from '../../core/effective-constraints';
29
+ import { loadRawProjectConfig, ProjectConfigLoader } from '../../core/project-config-loader';
30
+ import { FileKnowledgeStore } from '../../knowledge/store';
31
+ import type { KnowledgeEntry } from '../../knowledge/types';
32
+ import type { Constraint } from '../../types/constraint';
33
+ import {
34
+ buildConstraintsUsageReport,
35
+ collectUsageByConstraint,
36
+ readProjectTraces,
37
+ type RetireCandidate,
38
+ } from '../../core/constraints/usage-report';
39
+ import { getConstraintsMeta } from './constraints';
40
+
41
+ export interface RetireExecuteOptions {
42
+ /** 退役原因(可空) */
43
+ reason?: string;
44
+ /** 注入当前时间(测试用) */
45
+ now?: Date;
46
+ }
47
+
48
+ export type RetireStatus = 'retired' | 'already_retired' | 'unknown_id';
49
+
50
+ export interface RetireResult {
51
+ id: string;
52
+ status: RetireStatus;
53
+ /** 是否 check 层 iron(交互模式据此追加确认) */
54
+ isIronLaw: boolean;
55
+ stats: { total: number; fail: number; failRate: number };
56
+ /** CLAUDE.md 注入段是否已同步 */
57
+ claudeMdSynced: boolean;
58
+ /** KnowledgeStore 条目 id(status='retired' 时存在) */
59
+ knowledgeEntryId?: string;
60
+ }
61
+
62
+ export interface ConstraintsRetireOptions {
63
+ projectPath?: string;
64
+ reason?: string;
65
+ }
66
+
67
+ interface RetireTargetInfo {
68
+ isIronLaw: boolean;
69
+ level: Constraint['level'];
70
+ description?: string;
71
+ rule?: string;
72
+ message?: string;
73
+ promptInjection?: string;
74
+ }
75
+
76
+ /**
77
+ * 查找约束定义(内置 definitions + 项目 custom-constraints)
78
+ */
79
+ function findRetireTarget(projectRoot: string, id: string): RetireTargetInfo | undefined {
80
+ const builtIn = getConstraint(id);
81
+ if (builtIn) {
82
+ return {
83
+ isIronLaw: builtIn.kind === 'check' && builtIn.level === 'iron_law',
84
+ level: builtIn.level,
85
+ description: builtIn.description,
86
+ rule: builtIn.rule,
87
+ message: builtIn.message,
88
+ promptInjection: builtIn.promptInjection,
89
+ };
90
+ }
91
+
92
+ const loader = new ProjectConfigLoader(projectRoot);
93
+ loader.load();
94
+ const custom = loader.getCustomConstraints()[id];
95
+ if (custom) {
96
+ const level = custom.level || 'guideline';
97
+ return {
98
+ isIronLaw: false, // custom 约束统一 kind='prompt'(无 checker),不存在 check 层 iron
99
+ level,
100
+ description: custom.description,
101
+ rule: custom.rule,
102
+ message: custom.message,
103
+ };
104
+ }
105
+
106
+ return undefined;
107
+ }
108
+
109
+ /**
110
+ * 读取 harness 版本(与 init.ts 同路径策略)
111
+ */
112
+ function getHarnessVersion(): string {
113
+ try {
114
+ const pkgPath = path.join(__dirname, '..', '..', '..', 'package.json');
115
+ return JSON.parse(fs.readFileSync(pkgPath, 'utf-8')).version ?? 'unknown';
116
+ } catch {
117
+ return 'unknown';
118
+ }
119
+ }
120
+
121
+ /**
122
+ * 写 config.yml 退役段(ADR-0001 决策 5)
123
+ *
124
+ * js-yaml 不保留注释:原文件含注释行时重写会丢失,console 说明。
125
+ */
126
+ function writeRetireConfig(
127
+ projectRoot: string,
128
+ id: string,
129
+ retiredMeta: { at: string; reason: string; stats: { total: number; fail: number; failRate: number } }
130
+ ): void {
131
+ const configPath = path.join(projectRoot, '.harness', 'config.yml');
132
+
133
+ let raw: Record<string, unknown> = {};
134
+ let hadComments = false;
135
+ if (fs.existsSync(configPath)) {
136
+ const original = fs.readFileSync(configPath, 'utf-8');
137
+ hadComments = original.split('\n').some(l => l.trimStart().startsWith('#'));
138
+ raw = (yaml.load(original) as Record<string, unknown>) ?? {};
139
+ }
140
+
141
+ const constraints = (raw.constraints ?? {}) as Record<string, unknown>;
142
+ const prev = (constraints[id] ?? {}) as Record<string, unknown>;
143
+ constraints[id] = { ...prev, enabled: false, retired: retiredMeta };
144
+ raw.constraints = constraints;
145
+
146
+ fs.mkdirSync(path.dirname(configPath), { recursive: true });
147
+ fs.writeFileSync(configPath, yaml.dump(raw, { lineWidth: 120 }), 'utf-8');
148
+
149
+ if (hadComments) {
150
+ console.log(chalk.yellow(' ⚠️ config.yml 已重写:js-yaml 不保留原文件注释'));
151
+ }
152
+ }
153
+
154
+ /**
155
+ * 写 KnowledgeStore 退役记录(consumptionMode: 'signal')
156
+ */
157
+ function saveRetireKnowledge(
158
+ projectRoot: string,
159
+ id: string,
160
+ target: RetireTargetInfo,
161
+ reason: string,
162
+ stats: { total: number; fail: number; failRate: number },
163
+ iso: string
164
+ ): string {
165
+ const entryId = `constraint-retired-${id}`;
166
+ const contentLines = [
167
+ `# 约束退役:${id}`,
168
+ '',
169
+ '## 规则原文',
170
+ '',
171
+ target.description ? `description: ${target.description}` : undefined,
172
+ target.rule ? `rule: ${target.rule}` : undefined,
173
+ target.message ? `message: ${target.message}` : undefined,
174
+ target.promptInjection ? `promptInjection: ${target.promptInjection}` : undefined,
175
+ '',
176
+ '## 退役原因',
177
+ '',
178
+ reason || '(未填写)',
179
+ '',
180
+ '## 历史统计',
181
+ '',
182
+ `- total: ${stats.total}`,
183
+ `- fail: ${stats.fail}`,
184
+ `- failRate: ${Math.round(stats.failRate * 100)}%`,
185
+ '',
186
+ `退役日期: ${iso}`,
187
+ ].filter((l): l is string => l !== undefined);
188
+
189
+ const entry: KnowledgeEntry = {
190
+ id: entryId,
191
+ type: 'decision',
192
+ title: `约束退役:${id}`,
193
+ content: contentLines.join('\n'),
194
+ maturity: 'verified',
195
+ layer: 'project',
196
+ created: iso,
197
+ lastReferenced: iso,
198
+ contributors: [],
199
+ projects: [],
200
+ tags: ['constraint-retired', `constraint:${id}`, `level:${target.level}`],
201
+ applicablePhases: [],
202
+ sourceReferences: [{ timestamp: iso }],
203
+ referencedBy: [],
204
+ executionResults: [],
205
+ consumptionMode: 'signal',
206
+ origin: 'human',
207
+ };
208
+
209
+ const store = new FileKnowledgeStore({ baseDir: path.join(projectRoot, '.harness', 'knowledge') });
210
+ store.save(entry);
211
+ return entryId;
212
+ }
213
+
214
+ /**
215
+ * 同步 CLAUDE.md 注入段(存在 HARNESS_CONSTRAINTS 标记时)
216
+ *
217
+ * 复用 renderConstraintsSection(P3 导出纯函数)按退役后生效集重渲染。
218
+ * 无标记段不动(不追加——retire 不承担 init 职责)。
219
+ */
220
+ function syncClaudeMdInjection(projectRoot: string): boolean {
221
+ const claudeMdPath = path.join(projectRoot, 'CLAUDE.md');
222
+ if (!fs.existsSync(claudeMdPath)) return false;
223
+
224
+ const content = fs.readFileSync(claudeMdPath, 'utf-8');
225
+ const startIdx = content.indexOf(CONSTRAINTS_START_MARKER);
226
+ const endIdx = content.indexOf(CONSTRAINTS_END_MARKER);
227
+ if (startIdx === -1 || endIdx === -1 || endIdx <= startIdx) return false;
228
+
229
+ const body = renderConstraintsSection(getEffectiveConstraints(projectRoot), getHarnessVersion());
230
+ const after = content.slice(endIdx + CONSTRAINTS_END_MARKER.length).replace(/^\n+/, '');
231
+ const newContent = content.slice(0, startIdx) + body + (after ? '\n' + after : '');
232
+ if (newContent !== content) {
233
+ fs.writeFileSync(claudeMdPath, newContent, 'utf-8');
234
+ }
235
+ return true;
236
+ }
237
+
238
+ /**
239
+ * 执行单条约束退役(纯执行逻辑,无交互)
240
+ *
241
+ * 不存在的 id / 已退役的 id 通过 status 返回,由调用方提示。
242
+ */
243
+ export function retireConstraint(
244
+ projectRoot: string,
245
+ id: string,
246
+ options: RetireExecuteOptions = {}
247
+ ): RetireResult {
248
+ const now = options.now ?? new Date();
249
+ const iso = now.toISOString();
250
+ const reason = options.reason ?? '';
251
+
252
+ const target = findRetireTarget(projectRoot, id);
253
+ const emptyStats = { total: 0, fail: 0, failRate: 0 };
254
+ if (!target) {
255
+ return { id, status: 'unknown_id', isIronLaw: false, stats: emptyStats, claudeMdSynced: false };
256
+ }
257
+
258
+ // 已退役保护(config.yml constraints.<id>.enabled === false)
259
+ const rawConfig = loadRawProjectConfig(projectRoot);
260
+ const existing = (rawConfig?.constraints as Record<string, { enabled?: boolean; retired?: unknown }> | undefined)?.[id];
261
+ if (existing?.enabled === false) {
262
+ return { id, status: 'already_retired', isIronLaw: target.isIronLaw, stats: emptyStats, claudeMdSynced: false };
263
+ }
264
+
265
+ // 历史统计(来自 traces.log)
266
+ const usage = collectUsageByConstraint(readProjectTraces(projectRoot)).get(id);
267
+ const evaluated = usage ? usage.total - usage.skip : 0;
268
+ const stats = {
269
+ total: usage?.total ?? 0,
270
+ fail: usage?.fail ?? 0,
271
+ failRate: evaluated > 0 ? (usage!.fail / evaluated) : 0,
272
+ };
273
+
274
+ // 1. config.yml
275
+ writeRetireConfig(projectRoot, id, { at: iso, reason, stats });
276
+
277
+ // 2. KnowledgeStore
278
+ const knowledgeEntryId = saveRetireKnowledge(projectRoot, id, target, reason, stats, iso);
279
+
280
+ // 3. CLAUDE.md 注入段同步
281
+ const claudeMdSynced = syncClaudeMdInjection(projectRoot);
282
+
283
+ return { id, status: 'retired', isIronLaw: target.isIronLaw, stats, claudeMdSynced, knowledgeEntryId };
284
+ }
285
+
286
+ /**
287
+ * 打印单条退役结果(含回滚语义提示)
288
+ */
289
+ export function printRetireResult(result: RetireResult): void {
290
+ switch (result.status) {
291
+ case 'unknown_id':
292
+ console.log(chalk.red(`❌ ${result.id}: 约束不存在(既非内置也非 custom-constraints),未做任何变更`));
293
+ return;
294
+ case 'already_retired':
295
+ console.log(chalk.yellow(`⚠️ ${result.id}: 已处于退役状态(config.yml 中 enabled: false),跳过`));
296
+ return;
297
+ case 'retired':
298
+ console.log(chalk.green(`✅ ${result.id}: 已退役`));
299
+ console.log(` 历史统计: total=${result.stats.total} fail=${result.stats.fail} fail率=${Math.round(result.stats.failRate * 100)}%`);
300
+ console.log(` 知识沉淀: ${result.knowledgeEntryId}(.harness/knowledge)`);
301
+ if (result.claudeMdSynced) {
302
+ console.log(' 已同步 CLAUDE.md 注入段');
303
+ }
304
+ console.log(chalk.gray(` retire 不是删除——恢复方法:删除 config.yml 中 constraints.${result.id} 段`));
305
+ }
306
+ }
307
+
308
+ // ========================================
309
+ // 交互模式
310
+ // ========================================
311
+
312
+ interface RetireIO {
313
+ input: NodeJS.ReadableStream;
314
+ output: NodeJS.WritableStream;
315
+ }
316
+
317
+ function createAsk(io: RetireIO): { ask: (q: string) => Promise<string>; close: () => void } {
318
+ const rl = readline.createInterface({ input: io.input, output: io.output });
319
+ return {
320
+ ask: (q: string) => new Promise(resolve => rl.question(q, ans => resolve(ans.trim()))),
321
+ close: () => rl.close(),
322
+ };
323
+ }
324
+
325
+ const CANDIDATE_KIND_LABEL: Record<RetireCandidate['kind'], string> = {
326
+ zero_trigger: '零触发',
327
+ unevaluable: '不可评估',
328
+ high_noise: '高噪',
329
+ zero_intercept: '零拦截',
330
+ };
331
+
332
+ /**
333
+ * 交互式退役(候选列表 → 编号多选 → 摘要 → 确认 → 执行)
334
+ *
335
+ * IO 流可注入(测试用),默认 process.stdin/stdout。
336
+ */
337
+ export async function runRetireInteractive(
338
+ projectRoot: string,
339
+ io: RetireIO = { input: process.stdin, output: process.stdout }
340
+ ): Promise<void> {
341
+ const report = buildConstraintsUsageReport(projectRoot);
342
+ const { ask, close } = createAsk(io);
343
+
344
+ try {
345
+ let selectedIds: string[] = [];
346
+
347
+ if (report.candidates.length === 0) {
348
+ console.log(chalk.green('✅ 当前没有退役候选(所有 check 约束信号正常)'));
349
+ const manual = await ask('可手动输入要退役的约束 id(留空取消): ');
350
+ if (!manual) {
351
+ console.log('已取消');
352
+ return;
353
+ }
354
+ selectedIds = [manual];
355
+ } else {
356
+ console.log(chalk.bold(`退役候选(${report.candidates.length} 条):`));
357
+ report.candidates.forEach((c, i) => {
358
+ console.log(` ${i + 1}. [${CANDIDATE_KIND_LABEL[c.kind]}] ${c.id} — ${c.reason}`);
359
+ });
360
+ console.log();
361
+ const answer = await ask('输入编号(逗号分隔多选)或约束 id,留空取消: ');
362
+ if (!answer) {
363
+ console.log('已取消');
364
+ return;
365
+ }
366
+
367
+ for (const token of answer.split(',').map(s => s.trim()).filter(Boolean)) {
368
+ const idx = Number(token);
369
+ if (Number.isInteger(idx) && idx >= 1 && idx <= report.candidates.length) {
370
+ selectedIds.push(report.candidates[idx - 1].id);
371
+ } else {
372
+ selectedIds.push(token); // 视为手动输入的 id,由 retireConstraint 校验
373
+ }
374
+ }
375
+ selectedIds = [...new Set(selectedIds)];
376
+ }
377
+
378
+ // 逐条收集 reason + iron 二次确认
379
+ const plan: { id: string; reason: string }[] = [];
380
+ for (const id of selectedIds) {
381
+ const target = findRetireTarget(projectRoot, id);
382
+ if (!target) {
383
+ console.log(chalk.red(`❌ ${id}: 约束不存在,跳过`));
384
+ continue;
385
+ }
386
+ if (target.isIronLaw) {
387
+ const confirm = await ask(chalk.yellow(`⚠️ ${id} 是一条 Iron Law,确认退役?(y/N) `));
388
+ if (confirm.toLowerCase() !== 'y' && confirm.toLowerCase() !== 'yes') {
389
+ console.log(` 已跳过 ${id}`);
390
+ continue;
391
+ }
392
+ }
393
+ const reason = await ask(`退役原因(${id},可留空): `);
394
+ plan.push({ id, reason });
395
+ }
396
+
397
+ if (plan.length === 0) {
398
+ console.log('无可执行项,已取消');
399
+ return;
400
+ }
401
+
402
+ // 变更摘要 → 最终确认
403
+ console.log();
404
+ console.log(chalk.bold('将执行以下变更:'));
405
+ for (const p of plan) {
406
+ console.log(` - config.yml: constraints.${p.id}.enabled=false + retired 元数据(原因: ${p.reason || '(空)'})`);
407
+ console.log(` - KnowledgeStore: 写入 constraint-retired-${p.id}`);
408
+ }
409
+ console.log(' - CLAUDE.md: 若含 HARNESS_CONSTRAINTS 标记段则同步重渲染');
410
+ const finalConfirm = await ask('确认执行?(y/N) ');
411
+ if (finalConfirm.toLowerCase() !== 'y' && finalConfirm.toLowerCase() !== 'yes') {
412
+ console.log('已取消');
413
+ return;
414
+ }
415
+
416
+ console.log();
417
+ for (const p of plan) {
418
+ const result = retireConstraint(projectRoot, p.id, { reason: p.reason });
419
+ printRetireResult(result);
420
+ }
421
+ } finally {
422
+ close();
423
+ }
424
+ }
425
+
426
+ /**
427
+ * CLI handler: harness constraints retire [id]
428
+ */
429
+ export async function constraintsRetire(id?: string, options: ConstraintsRetireOptions = {}): Promise<void> {
430
+ const projectRoot = options.projectPath || process.cwd();
431
+
432
+ if (id) {
433
+ // 非交互直达
434
+ const result = retireConstraint(projectRoot, id, { reason: options.reason });
435
+ if (result.status === 'retired' && result.isIronLaw) {
436
+ console.log(chalk.yellow(`⚠️ ${id} 是一条 Iron Law,已通过命令行直接退役(交互模式会要求二次确认)`));
437
+ }
438
+ printRetireResult(result);
439
+ return;
440
+ }
441
+
442
+ await runRetireInteractive(projectRoot);
443
+ }
@@ -13,7 +13,7 @@ import { getAllConstraints } from '../../core/constraints/definitions';
13
13
  export interface ConstraintsMeta {
14
14
  version: string;
15
15
  hash: string;
16
- counts: { ironLaws: number; guidelines: number; tips: number };
16
+ counts: { ironLaws: number; guidelines: number; prompts: number };
17
17
  textSize: { total: number; perConstraint: number };
18
18
  }
19
19
 
@@ -40,7 +40,7 @@ export function getConstraintsMeta(): ConstraintsMeta {
40
40
 
41
41
  const ironLaws = constraints.filter(c => c.level === 'iron_law');
42
42
  const guidelines = constraints.filter(c => c.level === 'guideline');
43
- const tips = constraints.filter(c => c.level === 'tip');
43
+ const prompts = constraints.filter(c => c.level === 'prompt');
44
44
 
45
45
  // 计算总 promptInjection 文本长度(字符数,可估算 token)
46
46
  const totalTextSize = constraints
@@ -51,7 +51,7 @@ export function getConstraintsMeta(): ConstraintsMeta {
51
51
  const hashInput = JSON.stringify({
52
52
  ironLaws: ironLaws.map(c => ({ id: c.id, promptInjection: c.promptInjection })),
53
53
  guidelines: guidelines.map(c => ({ id: c.id, promptInjection: c.promptInjection })),
54
- tips: tips.map(c => ({ id: c.id, promptInjection: c.promptInjection })),
54
+ prompts: prompts.map(c => ({ id: c.id, promptInjection: c.promptInjection })),
55
55
  });
56
56
  const hash = createHash('sha256').update(hashInput).digest('hex');
57
57
 
@@ -61,7 +61,7 @@ export function getConstraintsMeta(): ConstraintsMeta {
61
61
  counts: {
62
62
  ironLaws: ironLaws.length,
63
63
  guidelines: guidelines.length,
64
- tips: tips.length,
64
+ prompts: prompts.length,
65
65
  },
66
66
  textSize: {
67
67
  total: totalTextSize,
@@ -80,7 +80,7 @@ export async function constraints(options: { json?: boolean }): Promise<void> {
80
80
  } else {
81
81
  console.log(`version: ${meta.version}`);
82
82
  console.log(`hash: ${meta.hash}`);
83
- console.log(`ironLaws: ${meta.counts.ironLaws}, guidelines: ${meta.counts.guidelines}, tips: ${meta.counts.tips}`);
83
+ console.log(`ironLaws: ${meta.counts.ironLaws}, guidelines: ${meta.counts.guidelines}, prompts: ${meta.counts.prompts}`);
84
84
  console.log(`textSize: ${meta.textSize.total} chars total, ~${meta.textSize.perConstraint} chars/constraint`);
85
85
  }
86
86
  }
@@ -8,7 +8,6 @@ export { runPassesGate, checkCoverage, type PassesGateOptions } from './passes-g
8
8
  export { init, type InitOptions } from './init';
9
9
  export { report, type ReportOptions } from './report';
10
10
  export { status, type StatusOptions } from './status';
11
- export { flow, type FlowOptions } from './flow';
12
11
  export { specValidate, listSpecTypes, type SpecValidateOptions } from './spec';
13
12
  export { acceptance, listAcceptanceCriteria, type AcceptanceOptions } from './acceptance';
14
13
  export { performance, type PerformanceOptions } from './performance';
@@ -24,6 +23,8 @@ export { release, type ReleaseOptions } from './release';
24
23
  export { analyzeSessions, type AnalyzeSessionsOptions } from './analyze-sessions';
25
24
  export { updateUserModel, type UpdateUserModelOptions } from './update-user-model';
26
25
  export { constraints, getConstraintsMeta, type ConstraintsMeta } from './constraints';
26
+ export { constraintsReport, renderExportMarkdown, type ConstraintsReportOptions } from './constraints-report';
27
+ export { constraintsRetire, retireConstraint, runRetireInteractive, printRetireResult, type ConstraintsRetireOptions, type RetireResult, type RetireStatus } from './constraints-retire';
27
28
  export { docFreshnessCheck, extractClaims, verifyClaims, type DocFreshnessCheckOptions, type ClaimResult, type ClaimType } from './doc-freshness-check';
28
29
  export { specBaselineCheck, extractBaselineSection, type SpecBaselineCheckOptions, type PrerequisiteResult } from './spec-baseline-check';
29
30
  export { sddIndex, type SDDOptions } from './sdd';