@dommaker/harness 1.1.1 → 1.2.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 (348) hide show
  1. package/CHANGELOG.md +10 -0
  2. package/README.md +2 -4
  3. package/bin/harness.js +12 -37
  4. package/dist/cli/commands/check.d.ts +2 -2
  5. package/dist/cli/commands/check.d.ts.map +1 -1
  6. package/dist/cli/commands/check.js +7 -32
  7. package/dist/cli/commands/check.js.map +1 -1
  8. package/dist/cli/commands/definitions.d.ts +2 -0
  9. package/dist/cli/commands/definitions.d.ts.map +1 -1
  10. package/dist/cli/commands/definitions.js.map +1 -1
  11. package/dist/cli/commands/init.d.ts.map +1 -1
  12. package/dist/cli/commands/init.js +6 -37
  13. package/dist/cli/commands/init.js.map +1 -1
  14. package/dist/cli/commands/knowledge.d.ts.map +1 -1
  15. package/dist/cli/commands/knowledge.js +1 -12
  16. package/dist/cli/commands/knowledge.js.map +1 -1
  17. package/dist/cli/commands/report.d.ts.map +1 -1
  18. package/dist/cli/commands/report.js +0 -2
  19. package/dist/cli/commands/report.js.map +1 -1
  20. package/dist/cli/commands/status.js +2 -2
  21. package/dist/cli/commands/status.js.map +1 -1
  22. package/dist/context/index.d.ts +9 -6
  23. package/dist/context/index.d.ts.map +1 -1
  24. package/dist/context/index.js +18 -20
  25. package/dist/context/index.js.map +1 -1
  26. package/dist/core/constraints/capabilities-parser.d.ts +3 -2
  27. package/dist/core/constraints/capabilities-parser.d.ts.map +1 -1
  28. package/dist/core/constraints/capabilities-parser.js +25 -47
  29. package/dist/core/constraints/capabilities-parser.js.map +1 -1
  30. package/dist/core/constraints/checker.d.ts +29 -32
  31. package/dist/core/constraints/checker.d.ts.map +1 -1
  32. package/dist/core/constraints/checker.js +34 -126
  33. package/dist/core/constraints/checker.js.map +1 -1
  34. package/dist/core/constraints/checkers/index.d.ts +2 -1
  35. package/dist/core/constraints/checkers/index.d.ts.map +1 -1
  36. package/dist/core/constraints/checkers/index.js +3 -0
  37. package/dist/core/constraints/checkers/index.js.map +1 -1
  38. package/dist/core/constraints/checkers/types.d.ts +25 -0
  39. package/dist/core/constraints/checkers/types.d.ts.map +1 -1
  40. package/dist/core/constraints/checkers/types.js +24 -0
  41. package/dist/core/constraints/checkers/types.js.map +1 -1
  42. package/dist/core/constraints/definitions/guidelines.d.ts.map +1 -1
  43. package/dist/core/constraints/definitions/guidelines.js +0 -2
  44. package/dist/core/constraints/definitions/guidelines.js.map +1 -1
  45. package/dist/core/constraints/definitions/prompts.d.ts.map +1 -1
  46. package/dist/core/constraints/definitions/prompts.js +0 -5
  47. package/dist/core/constraints/definitions/prompts.js.map +1 -1
  48. package/dist/core/constraints/index.d.ts +1 -2
  49. package/dist/core/constraints/index.d.ts.map +1 -1
  50. package/dist/core/constraints/index.js +1 -7
  51. package/dist/core/constraints/index.js.map +1 -1
  52. package/dist/core/constraints/usage-report.d.ts +1 -2
  53. package/dist/core/constraints/usage-report.d.ts.map +1 -1
  54. package/dist/core/constraints/usage-report.js +2 -5
  55. package/dist/core/constraints/usage-report.js.map +1 -1
  56. package/dist/core/effective-constraints.d.ts +24 -4
  57. package/dist/core/effective-constraints.d.ts.map +1 -1
  58. package/dist/core/effective-constraints.js +28 -7
  59. package/dist/core/effective-constraints.js.map +1 -1
  60. package/dist/core/effective-set.d.ts +30 -0
  61. package/dist/core/effective-set.d.ts.map +1 -0
  62. package/dist/core/effective-set.js +35 -0
  63. package/dist/core/effective-set.js.map +1 -0
  64. package/dist/core/index.d.ts +13 -6
  65. package/dist/core/index.d.ts.map +1 -1
  66. package/dist/core/index.js +44 -21
  67. package/dist/core/index.js.map +1 -1
  68. package/dist/core/project-config-loader.d.ts +3 -6
  69. package/dist/core/project-config-loader.d.ts.map +1 -1
  70. package/dist/core/project-config-loader.js +47 -61
  71. package/dist/core/project-config-loader.js.map +1 -1
  72. package/dist/failure/constraint-handler.d.ts +1 -1
  73. package/dist/failure/constraint-handler.js +1 -1
  74. package/dist/gates/checker-gate.d.ts +1 -4
  75. package/dist/gates/checker-gate.d.ts.map +1 -1
  76. package/dist/gates/checker-gate.js +3 -11
  77. package/dist/gates/checker-gate.js.map +1 -1
  78. package/dist/gates/definitions.d.ts +9 -38
  79. package/dist/gates/definitions.d.ts.map +1 -1
  80. package/dist/gates/definitions.js +24 -13
  81. package/dist/gates/definitions.js.map +1 -1
  82. package/dist/gates/effective-gates.d.ts.map +1 -1
  83. package/dist/gates/effective-gates.js +14 -18
  84. package/dist/gates/effective-gates.js.map +1 -1
  85. package/dist/gates/index.d.ts +1 -1
  86. package/dist/gates/index.d.ts.map +1 -1
  87. package/dist/gates/index.js.map +1 -1
  88. package/dist/hooks/bootstrap.d.ts.map +1 -1
  89. package/dist/hooks/bootstrap.js +0 -3
  90. package/dist/hooks/bootstrap.js.map +1 -1
  91. package/dist/index.d.ts +43 -60
  92. package/dist/index.d.ts.map +1 -1
  93. package/dist/index.js +180 -143
  94. package/dist/index.js.map +1 -1
  95. package/dist/knowledge/query.d.ts +15 -0
  96. package/dist/knowledge/query.d.ts.map +1 -1
  97. package/dist/knowledge/query.js +23 -0
  98. package/dist/knowledge/query.js.map +1 -1
  99. package/dist/monitoring/analyzer-base.d.ts +0 -4
  100. package/dist/monitoring/analyzer-base.d.ts.map +1 -1
  101. package/dist/monitoring/analyzer-base.js +0 -21
  102. package/dist/monitoring/analyzer-base.js.map +1 -1
  103. package/dist/monitoring/index.d.ts +6 -12
  104. package/dist/monitoring/index.d.ts.map +1 -1
  105. package/dist/monitoring/index.js +13 -29
  106. package/dist/monitoring/index.js.map +1 -1
  107. package/dist/monitoring/trace-analyzer.d.ts.map +1 -1
  108. package/dist/monitoring/trace-analyzer.js +2 -68
  109. package/dist/monitoring/trace-analyzer.js.map +1 -1
  110. package/dist/monitoring/traces.d.ts +0 -4
  111. package/dist/monitoring/traces.d.ts.map +1 -1
  112. package/dist/monitoring/traces.js +0 -14
  113. package/dist/monitoring/traces.js.map +1 -1
  114. package/dist/presets/index.d.ts +2 -2
  115. package/dist/presets/index.d.ts.map +1 -1
  116. package/dist/presets/index.js +3 -4
  117. package/dist/presets/index.js.map +1 -1
  118. package/dist/presets/standard.d.ts +10 -22
  119. package/dist/presets/standard.d.ts.map +1 -1
  120. package/dist/presets/standard.js +15 -75
  121. package/dist/presets/standard.js.map +1 -1
  122. package/dist/types/constraint.d.ts +1 -29
  123. package/dist/types/constraint.d.ts.map +1 -1
  124. package/dist/types/constraint.js +1 -1
  125. package/dist/types/constraint.js.map +1 -1
  126. package/dist/types/index.d.ts +0 -3
  127. package/dist/types/index.d.ts.map +1 -1
  128. package/dist/types/index.js +0 -4
  129. package/dist/types/index.js.map +1 -1
  130. package/dist/types/project-config.d.ts +4 -8
  131. package/dist/types/project-config.d.ts.map +1 -1
  132. package/dist/types/trace.d.ts +5 -18
  133. package/dist/types/trace.d.ts.map +1 -1
  134. package/package.json +2 -2
  135. package/src/CONTEXT.md +4 -5
  136. package/src/__tests__/checker-extra.test.ts +33 -139
  137. package/src/__tests__/checker.test.ts +0 -376
  138. package/src/__tests__/project-config-loader-extra.test.ts +42 -29
  139. package/src/__tests__/public-exports.test.ts +138 -0
  140. package/src/__tests__/trace-analyzer-extra.test.ts +6 -86
  141. package/src/__tests__/trace-analyzer.test.ts +4 -5
  142. package/src/__tests__/trace-collector.test.ts +0 -11
  143. package/src/__tests__/traces.test.ts +3 -22
  144. package/src/cli/commands/CONTEXT.md +2 -2
  145. package/src/cli/commands/__tests__/check-skip-output.test.ts +10 -7
  146. package/src/cli/commands/__tests__/check.test.ts +29 -69
  147. package/src/cli/commands/__tests__/constraints-report.test.ts +1 -1
  148. package/src/cli/commands/__tests__/knowledge.test.ts +90 -1
  149. package/src/cli/commands/__tests__/registry.test.ts +8 -6
  150. package/src/cli/commands/__tests__/status-extra.test.ts +6 -6
  151. package/src/cli/commands/__tests__/status.test.ts +6 -6
  152. package/src/cli/commands/check.ts +8 -34
  153. package/src/cli/commands/definitions.ts +2 -0
  154. package/src/cli/commands/init.ts +6 -37
  155. package/src/cli/commands/knowledge.ts +1 -16
  156. package/src/cli/commands/report.ts +0 -2
  157. package/src/cli/commands/status.ts +2 -2
  158. package/src/context/index.ts +29 -6
  159. package/src/core/CONTEXT.md +4 -3
  160. package/src/core/__tests__/effective-set.test.ts +80 -0
  161. package/src/core/constraints/capabilities-parser.ts +43 -49
  162. package/src/core/constraints/checker.ts +51 -145
  163. package/src/core/constraints/checkers/index.ts +2 -1
  164. package/src/core/constraints/checkers/types.ts +42 -0
  165. package/src/core/constraints/definitions/guidelines.ts +0 -2
  166. package/src/core/constraints/definitions/prompts.ts +0 -5
  167. package/src/core/constraints/index.ts +2 -17
  168. package/src/core/constraints/usage-report.ts +5 -8
  169. package/src/core/effective-constraints.ts +35 -7
  170. package/src/core/effective-set.ts +56 -0
  171. package/src/core/index.ts +80 -6
  172. package/src/core/project-config-loader.ts +53 -68
  173. package/src/failure/CONTEXT.md +1 -1
  174. package/src/failure/constraint-handler.ts +1 -1
  175. package/src/gates/CONTEXT.md +3 -3
  176. package/src/gates/checker-gate.ts +4 -12
  177. package/src/gates/definitions.ts +27 -50
  178. package/src/gates/effective-gates.ts +14 -21
  179. package/src/gates/index.ts +1 -1
  180. package/src/hooks/bootstrap.ts +0 -3
  181. package/src/index.ts +384 -148
  182. package/src/knowledge/CONTEXT.md +2 -2
  183. package/src/knowledge/__tests__/query.test.ts +64 -0
  184. package/src/knowledge/query.ts +28 -0
  185. package/src/monitoring/CONTEXT.md +7 -13
  186. package/src/monitoring/__tests__/traces.test.ts +0 -10
  187. package/src/monitoring/analyzer-base.ts +0 -22
  188. package/src/monitoring/index.ts +6 -12
  189. package/src/monitoring/trace-analyzer.ts +2 -73
  190. package/src/monitoring/traces.ts +0 -20
  191. package/src/presets/__tests__/standard.test.ts +48 -69
  192. package/src/presets/index.ts +3 -5
  193. package/src/presets/standard.ts +14 -79
  194. package/src/spec/CONTEXT.md +5 -13
  195. package/src/types/constraint.ts +1 -51
  196. package/src/types/index.ts +0 -12
  197. package/src/types/project-config.ts +4 -10
  198. package/src/types/trace.ts +5 -30
  199. package/dist/core/constraints/interceptor.d.ts +0 -39
  200. package/dist/core/constraints/interceptor.d.ts.map +0 -1
  201. package/dist/core/constraints/interceptor.js +0 -166
  202. package/dist/core/constraints/interceptor.js.map +0 -1
  203. package/dist/dashboard/data.d.ts +0 -31
  204. package/dist/dashboard/data.d.ts.map +0 -1
  205. package/dist/dashboard/data.js +0 -81
  206. package/dist/dashboard/data.js.map +0 -1
  207. package/dist/dashboard/index.d.ts +0 -4
  208. package/dist/dashboard/index.d.ts.map +0 -1
  209. package/dist/dashboard/index.js +0 -20
  210. package/dist/dashboard/index.js.map +0 -1
  211. package/dist/dashboard/stats.d.ts +0 -20
  212. package/dist/dashboard/stats.d.ts.map +0 -1
  213. package/dist/dashboard/stats.js +0 -115
  214. package/dist/dashboard/stats.js.map +0 -1
  215. package/dist/dashboard/types.d.ts +0 -110
  216. package/dist/dashboard/types.d.ts.map +0 -1
  217. package/dist/dashboard/types.js +0 -8
  218. package/dist/dashboard/types.js.map +0 -1
  219. package/dist/monitoring/constraint-doctor.d.ts +0 -76
  220. package/dist/monitoring/constraint-doctor.d.ts.map +0 -1
  221. package/dist/monitoring/constraint-doctor.js +0 -189
  222. package/dist/monitoring/constraint-doctor.js.map +0 -1
  223. package/dist/monitoring/diagnosis-rules.d.ts +0 -18
  224. package/dist/monitoring/diagnosis-rules.d.ts.map +0 -1
  225. package/dist/monitoring/diagnosis-rules.js +0 -119
  226. package/dist/monitoring/diagnosis-rules.js.map +0 -1
  227. package/dist/monitoring/knowledge-doctor.d.ts +0 -78
  228. package/dist/monitoring/knowledge-doctor.d.ts.map +0 -1
  229. package/dist/monitoring/knowledge-doctor.js +0 -266
  230. package/dist/monitoring/knowledge-doctor.js.map +0 -1
  231. package/dist/monitoring/knowledge-evolver.d.ts +0 -72
  232. package/dist/monitoring/knowledge-evolver.d.ts.map +0 -1
  233. package/dist/monitoring/knowledge-evolver.js +0 -205
  234. package/dist/monitoring/knowledge-evolver.js.map +0 -1
  235. package/dist/monitoring/performance-analyzer.d.ts +0 -92
  236. package/dist/monitoring/performance-analyzer.d.ts.map +0 -1
  237. package/dist/monitoring/performance-analyzer.js +0 -343
  238. package/dist/monitoring/performance-analyzer.js.map +0 -1
  239. package/dist/monitoring/performance-collector.d.ts +0 -103
  240. package/dist/monitoring/performance-collector.d.ts.map +0 -1
  241. package/dist/monitoring/performance-collector.js +0 -298
  242. package/dist/monitoring/performance-collector.js.map +0 -1
  243. package/dist/safety/index.d.ts +0 -6
  244. package/dist/safety/index.d.ts.map +0 -1
  245. package/dist/safety/index.js +0 -22
  246. package/dist/safety/index.js.map +0 -1
  247. package/dist/safety/input-guardrail.d.ts +0 -36
  248. package/dist/safety/input-guardrail.d.ts.map +0 -1
  249. package/dist/safety/input-guardrail.js +0 -118
  250. package/dist/safety/input-guardrail.js.map +0 -1
  251. package/dist/safety/output-guardrail.d.ts +0 -39
  252. package/dist/safety/output-guardrail.d.ts.map +0 -1
  253. package/dist/safety/output-guardrail.js +0 -147
  254. package/dist/safety/output-guardrail.js.map +0 -1
  255. package/dist/safety/sandbox.d.ts +0 -45
  256. package/dist/safety/sandbox.d.ts.map +0 -1
  257. package/dist/safety/sandbox.js +0 -134
  258. package/dist/safety/sandbox.js.map +0 -1
  259. package/dist/safety/tool-guardrail.d.ts +0 -51
  260. package/dist/safety/tool-guardrail.d.ts.map +0 -1
  261. package/dist/safety/tool-guardrail.js +0 -147
  262. package/dist/safety/tool-guardrail.js.map +0 -1
  263. package/dist/safety/types.d.ts +0 -104
  264. package/dist/safety/types.d.ts.map +0 -1
  265. package/dist/safety/types.js +0 -6
  266. package/dist/safety/types.js.map +0 -1
  267. package/dist/spec/annotation-checker.d.ts +0 -47
  268. package/dist/spec/annotation-checker.d.ts.map +0 -1
  269. package/dist/spec/annotation-checker.js +0 -279
  270. package/dist/spec/annotation-checker.js.map +0 -1
  271. package/dist/types/enforcement.d.ts +0 -42
  272. package/dist/types/enforcement.d.ts.map +0 -1
  273. package/dist/types/enforcement.js +0 -6
  274. package/dist/types/enforcement.js.map +0 -1
  275. package/dist/types/monitoring-types.d.ts +0 -53
  276. package/dist/types/monitoring-types.d.ts.map +0 -1
  277. package/dist/types/monitoring-types.js +0 -10
  278. package/dist/types/monitoring-types.js.map +0 -1
  279. package/dist/types/performance.d.ts +0 -204
  280. package/dist/types/performance.d.ts.map +0 -1
  281. package/dist/types/performance.js +0 -12
  282. package/dist/types/performance.js.map +0 -1
  283. package/dist/verification/index.d.ts +0 -4
  284. package/dist/verification/index.d.ts.map +0 -1
  285. package/dist/verification/index.js +0 -20
  286. package/dist/verification/index.js.map +0 -1
  287. package/dist/verification/loop.d.ts +0 -30
  288. package/dist/verification/loop.d.ts.map +0 -1
  289. package/dist/verification/loop.js +0 -114
  290. package/dist/verification/loop.js.map +0 -1
  291. package/dist/verification/rules-based.d.ts +0 -40
  292. package/dist/verification/rules-based.d.ts.map +0 -1
  293. package/dist/verification/rules-based.js +0 -130
  294. package/dist/verification/rules-based.js.map +0 -1
  295. package/dist/verification/types.d.ts +0 -69
  296. package/dist/verification/types.d.ts.map +0 -1
  297. package/dist/verification/types.js +0 -6
  298. package/dist/verification/types.js.map +0 -1
  299. package/src/__tests__/annotation-checker.test.ts +0 -183
  300. package/src/__tests__/constraint-doctor.test.ts +0 -271
  301. package/src/__tests__/doctor-extra.test.ts +0 -155
  302. package/src/__tests__/extend-exceptions.test.ts +0 -149
  303. package/src/__tests__/interceptor.test.ts +0 -89
  304. package/src/__tests__/performance-analyzer.test.ts +0 -499
  305. package/src/__tests__/performance-collector.test.ts +0 -276
  306. package/src/__tests__/presets.test.ts +0 -65
  307. package/src/core/constraints/__tests__/interceptor.test.ts +0 -267
  308. package/src/core/constraints/interceptor.ts +0 -217
  309. package/src/dashboard/CONTEXT.md +0 -21
  310. package/src/dashboard/__tests__/data.test.ts +0 -111
  311. package/src/dashboard/__tests__/stats.test.ts +0 -159
  312. package/src/dashboard/data.ts +0 -103
  313. package/src/dashboard/index.ts +0 -3
  314. package/src/dashboard/stats.ts +0 -127
  315. package/src/dashboard/types.ts +0 -130
  316. package/src/monitoring/__tests__/knowledge-doctor.test.ts +0 -278
  317. package/src/monitoring/__tests__/knowledge-evolver.test.ts +0 -368
  318. package/src/monitoring/__tests__/performance-analyzer.test.ts +0 -137
  319. package/src/monitoring/__tests__/performance-collector.test.ts +0 -331
  320. package/src/monitoring/constraint-doctor.ts +0 -228
  321. package/src/monitoring/diagnosis-rules.ts +0 -132
  322. package/src/monitoring/knowledge-doctor.ts +0 -322
  323. package/src/monitoring/knowledge-evolver.ts +0 -289
  324. package/src/monitoring/performance-analyzer.ts +0 -400
  325. package/src/monitoring/performance-collector.ts +0 -326
  326. package/src/safety/CONTEXT.md +0 -23
  327. package/src/safety/__tests__/input-guardrail.test.ts +0 -105
  328. package/src/safety/__tests__/output-guardrail.test.ts +0 -108
  329. package/src/safety/__tests__/sandbox.test.ts +0 -158
  330. package/src/safety/__tests__/tool-guardrail.test.ts +0 -127
  331. package/src/safety/index.ts +0 -5
  332. package/src/safety/input-guardrail.ts +0 -133
  333. package/src/safety/output-guardrail.ts +0 -170
  334. package/src/safety/sandbox.ts +0 -148
  335. package/src/safety/tool-guardrail.ts +0 -176
  336. package/src/safety/types.ts +0 -124
  337. package/src/spec/__tests__/annotation-checker.test.ts +0 -294
  338. package/src/spec/annotation-checker.ts +0 -320
  339. package/src/types/enforcement.ts +0 -48
  340. package/src/types/monitoring-types.ts +0 -68
  341. package/src/types/performance.ts +0 -295
  342. package/src/verification/CONTEXT.md +0 -23
  343. package/src/verification/__tests__/loop.test.ts +0 -303
  344. package/src/verification/__tests__/rules-based.test.ts +0 -240
  345. package/src/verification/index.ts +0 -3
  346. package/src/verification/loop.ts +0 -137
  347. package/src/verification/rules-based.ts +0 -152
  348. package/src/verification/types.ts +0 -81
@@ -17,6 +17,7 @@ import {
17
17
  ConstraintViolationError,
18
18
  } from '../../types/constraint';
19
19
  import type { ExecutionTrace } from '../../types/trace';
20
+ import { getTraceCollector } from '../../monitoring/traces';
20
21
  import { IRON_LAWS, GUIDELINES, PROMPTS } from './definitions';
21
22
  import type { MergedConstraintsConfig } from '../../types/project-config';
22
23
  import { normalizeTriggers } from '../../utils/exec';
@@ -24,50 +25,19 @@ import { runCommand } from '../../utils/exec';
24
25
  import { join, relative } from 'path';
25
26
  import { CheckCache } from './check-cache';
26
27
  import { findTsSourceFiles } from '../../utils/file-walk';
27
- import { getConstraintCheck, type CheckEnv, type CheckOutcome } from './checkers';
28
+ import { getConstraintCheck, buildCheckEnv, type CheckOutcome } from './checkers';
28
29
 
29
30
  /**
30
- * 例外名称ConstraintContext 中布尔字段的映射
31
- */
32
- const EXCEPTION_FIELD_MAP: Record<string, keyof ConstraintContext> = {
33
- scalability_required: 'scalabilityRequired',
34
- security_required: 'securityRequired',
35
- performance_required: 'performanceRequired',
36
- reliability_required: 'reliabilityRequired',
37
- simple_typo: 'isSimpleTypo',
38
- config_value_error: 'isConfigValueError',
39
- missing_config: 'isMissingConfig',
40
- config_file: 'isConfigFile',
41
- type_definition: 'isTypeDefinition',
42
- simple_accessor: 'isSimpleAccessor',
43
- pure_display_component: 'isPureDisplayComponent',
44
- json_parse_result: 'isJsonParseResult',
45
- third_party_no_types: 'isThirdPartyNoTypes',
46
- legacy_migration: 'isLegacyMigration',
47
- internal_refactor: 'isInternalRefactor',
48
- bug_fix_only: 'isBugFixOnly',
49
- performance_optimization: 'isPerformanceOptimization',
50
- redundant_code_cleanup: 'isRedundantCodeCleanup',
51
- same_effect_refactor: 'isSameEffectRefactor',
52
- unused_code_removal: 'isUnusedCodeRemoval',
53
- external_dependency: 'isExternalDependency',
54
- explicit_instruction: 'isExplicitInstruction',
55
- emergency_fix: 'isEmergencyFix',
56
- existing_design: 'isExistingDesign',
57
- };
58
-
59
- /**
60
- * trace 记录器最小接口(工单 15:core→monitoring 循环消除)
31
+ * trace 记录器最小接口(原工单 15 为消除 coremonitoring 循环;现 monitoring
32
+ * 不反向依赖 core,方向单向、无环,checker 可直接值引用 getTraceCollector)
61
33
  *
62
- * checker 不再值导入 monitoring/traces 的单例;生产调用方经
63
- * setTraceRecorder 注入真实收集器,默认 no-op。
34
+ * checker 不构造收集器;生产路径首次记录时惰性接线 getTraceCollector()
35
+ * (幂等单点,ADR-0003),测试可经 setTraceRecorder 注入替身。
64
36
  */
65
37
  export interface TraceRecorder {
66
38
  record(trace: ExecutionTrace): void;
67
39
  }
68
40
 
69
- const NOOP_TRACE_RECORDER: TraceRecorder = { record: () => undefined };
70
-
71
41
  /**
72
42
  * 约束检查器
73
43
  */
@@ -78,16 +48,26 @@ export class ConstraintChecker {
78
48
  private cache: CheckCache = new CheckCache({ ttlMs: 1000 });
79
49
 
80
50
  /**
81
- * run 级 memo(工单 18):一次 checkConstraints/checkConstraintsSafe
51
+ * run 级 memo(工单 18):一次 checkConstraints 内
82
52
  * git diff 等命令只执行一次;run 外直接调用私有检查方法时不 memo(恒新鲜)。
83
53
  */
84
54
  private runCache: Map<string, Promise<string>> | null = null;
85
55
 
86
- /** trace 记录器(注入式,默认 no-op;生产入口经 setTraceRecorder 接入真实收集器) */
87
- private traceRecorder: TraceRecorder = NOOP_TRACE_RECORDER;
56
+ /** trace 记录器(注入式;null = 未显式注入,首次记录时惰性接线全局收集器) */
57
+ private traceRecorder: TraceRecorder | null = null;
88
58
 
89
59
  private constructor() {}
90
60
 
61
+ /**
62
+ * 取 trace 记录器:未显式注入时惰性接线全局收集器(幂等,ADR-0003 副作用收敛)
63
+ */
64
+ private getRecorder(): TraceRecorder {
65
+ if (!this.traceRecorder) {
66
+ this.traceRecorder = getTraceCollector();
67
+ }
68
+ return this.traceRecorder;
69
+ }
70
+
91
71
  /**
92
72
  * run 内 memoize;无活动 run 时直接执行(工单 18)
93
73
  */
@@ -130,7 +110,7 @@ export class ConstraintChecker {
130
110
  }
131
111
 
132
112
  /**
133
- * 注入 trace 记录器(工单 15:解耦 core 对 monitoring 的值依赖)
113
+ * 注入 trace 记录器(测试/定制场景;不调用则首次记录时惰性接线全局收集器)
134
114
  */
135
115
  setTraceRecorder(recorder: TraceRecorder): void {
136
116
  this.traceRecorder = recorder;
@@ -139,29 +119,25 @@ export class ConstraintChecker {
139
119
  /**
140
120
  * 获取当前的约束集合(内置 + 自定义)
141
121
  *
122
+ * 纯查询,无副作用(ADR-0006:原 `.check` 装配已随唯一消费者
123
+ * interceptor 的删除一并移除,ADR-0004)。
124
+ *
142
125
  * @param customConfig 可选,per-request 自定义配置;不传则返回内置约束集
143
126
  */
144
127
  getConstraints(customConfig?: MergedConstraintsConfig | null): {
145
- ironLaws: Record<string, Constraint & { check: (ctx: ConstraintContext) => Promise<ConstraintResult> }>;
146
- guidelines: Record<string, Constraint & { check: (ctx: ConstraintContext) => Promise<ConstraintResult> }>;
147
- prompts: Record<string, Constraint & { check: (ctx: ConstraintContext) => Promise<ConstraintResult> }>;
128
+ ironLaws: Record<string, Constraint>;
129
+ guidelines: Record<string, Constraint>;
130
+ prompts: Record<string, Constraint>;
148
131
  } {
149
132
  const config = customConfig;
150
133
  const source = config
151
134
  ? { ironLaws: config.ironLaws, guidelines: config.guidelines, prompts: config.prompts ?? PROMPTS }
152
135
  : { ironLaws: IRON_LAWS, guidelines: GUIDELINES, prompts: PROMPTS };
153
136
 
154
- // Wire unified check() method on every constraint
155
- const wire = (constraints: Record<string, Constraint>) => {
156
- for (const c of Object.values(constraints)) {
157
- (c as any).check = (ctx: ConstraintContext) => this.check(c, ctx);
158
- }
159
- return constraints as Record<string, Constraint & { check: (ctx: ConstraintContext) => Promise<ConstraintResult> }>;
160
- };
161
137
  return {
162
- ironLaws: wire(source.ironLaws),
163
- guidelines: wire(source.guidelines),
164
- prompts: wire(source.prompts),
138
+ ironLaws: source.ironLaws,
139
+ guidelines: source.guidelines,
140
+ prompts: source.prompts,
165
141
  };
166
142
  }
167
143
 
@@ -183,20 +159,6 @@ export class ConstraintChecker {
183
159
  };
184
160
  }
185
161
 
186
- // 检查例外条件(仅 Guidelines 有效)
187
- if (constraint.level === 'guideline' && constraint.exceptions) {
188
- if (this.checkException(constraint, context)) {
189
- return {
190
- id: constraint.id,
191
- level: constraint.level,
192
- satisfied: true,
193
- constraint,
194
- message: `指导原则 ${constraint.id} 因例外条件被豁免`,
195
- checkedAt: new Date(),
196
- };
197
- }
198
- }
199
-
200
162
  // 检查前置条件('skip' = 约定未采用/证据未接线:satisfied 置 true 但不计 pass/fail)
201
163
  const outcome = await this.checkPrecondition(constraint, context);
202
164
 
@@ -225,16 +187,6 @@ export class ConstraintChecker {
225
187
  };
226
188
  }
227
189
 
228
- /**
229
- * 检查例外条件
230
- */
231
- private checkException(constraint: Constraint, context: ConstraintContext): boolean {
232
- if (!constraint.exceptions) return false;
233
- return constraint.exceptions.some(
234
- (ex) => context[EXCEPTION_FIELD_MAP[ex]] === true
235
- );
236
- }
237
-
238
190
  /**
239
191
  * 根据约束层级获取严重性
240
192
  */
@@ -279,9 +231,6 @@ export class ConstraintChecker {
279
231
  result: checkResult.skipped ? 'skip' : checkResult.satisfied ? 'pass' : 'fail',
280
232
  operation: context.operation,
281
233
  severity: this.getSeverity(constraint.level),
282
- exceptionApplied: checkResult.message?.includes('豁免')
283
- ? constraint.exceptions?.[0]
284
- : undefined,
285
234
  projectPath: context.projectPath,
286
235
  sessionId: context.sessionId,
287
236
  });
@@ -306,9 +255,7 @@ export class ConstraintChecker {
306
255
  }
307
256
 
308
257
  const projectPath = context.projectPath || process.cwd();
309
- const env: CheckEnv = {
310
- context,
311
- projectPath,
258
+ const env = buildCheckEnv(context, {
312
259
  stagedDiff: () => this.getStagedDiff(projectPath),
313
260
  stagedDiffNames: () => this.getStagedDiffNames(projectPath),
314
261
  srcScan: (root: string) =>
@@ -317,7 +264,7 @@ export class ConstraintChecker {
317
264
  relative(projectPath, f)
318
265
  )
319
266
  ),
320
- };
267
+ });
321
268
 
322
269
  return await impl.evaluate(env);
323
270
  }
@@ -380,7 +327,7 @@ export class ConstraintChecker {
380
327
  warningCount: 0,
381
328
  };
382
329
 
383
- const traceCollector = this.traceRecorder;
330
+ const traceCollector = this.getRecorder();
384
331
  const constraints = this.getConstraints(customConfig);
385
332
 
386
333
  // 1. Iron Laws: 必须全部通过
@@ -416,53 +363,6 @@ export class ConstraintChecker {
416
363
  }
417
364
  }
418
365
 
419
- /**
420
- * S11: 安全全量检查 — 全部约束检查,不抛异常
421
- *
422
- * 区别 checkConstraints(): Iron Law 违规收集在结果中而不是抛异常。
423
- * 调用方通过 result.passed + result.warningCount 判断状态。
424
- */
425
- async checkConstraintsSafe(
426
- context: ConstraintContext,
427
- customConfig?: MergedConstraintsConfig | null
428
- ): Promise<ConstraintCheckResult> {
429
- // run 起始:重置 run 级缓存(工单 18)
430
- this.cache.invalidate();
431
- this.runCache = new Map();
432
-
433
- try {
434
- const result: ConstraintCheckResult = {
435
- ironLaws: [],
436
- guidelines: [],
437
- passed: true,
438
- warningCount: 0,
439
- };
440
-
441
- const traceCollector = this.traceRecorder;
442
- const constraints = this.getConstraints(customConfig);
443
-
444
- for (const constraint of Object.values(constraints.ironLaws)) {
445
- if (!this.matchesTrigger(constraint, context)) continue;
446
- const checkResult = await this.check(constraint, context);
447
- result.ironLaws.push(checkResult);
448
- this.recordTrace(traceCollector, constraint, checkResult, context);
449
- if (!checkResult.satisfied) result.passed = false;
450
- }
451
-
452
- for (const constraint of Object.values(constraints.guidelines)) {
453
- if (!this.matchesTrigger(constraint, context)) continue;
454
- const checkResult = await this.check(constraint, context);
455
- result.guidelines.push(checkResult);
456
- this.recordTrace(traceCollector, constraint, checkResult, context);
457
- if (!checkResult.satisfied) result.warningCount++;
458
- }
459
-
460
- return result;
461
- } finally {
462
- this.runCache = null;
463
- }
464
- }
465
-
466
366
  /**
467
367
  * 执行前检查(仅检查 Iron Laws)
468
368
  *
@@ -524,26 +424,32 @@ export async function checkConstraint(
524
424
  }
525
425
 
526
426
  /**
527
- * 快捷函数:执行三层检查
528
- *
529
- * @param context 约束上下文
530
- * @param customConfig 可选,per-request 自定义配置
427
+ * checkConstraints 选项(ADR-0003:包根/子路径统一 options 对象签名)
531
428
  */
532
- export async function checkConstraints(
533
- context: ConstraintContext,
534
- customConfig?: MergedConstraintsConfig | null
535
- ): Promise<ConstraintCheckResult> {
536
- return ConstraintChecker.getInstance().checkConstraints(context, customConfig);
429
+ export interface CheckConstraintsOptions {
430
+ /** 每条约束检查后的回调(用于记录 trace 到外部存储) */
431
+ onTrace?: (result: ConstraintResult) => void;
432
+ /** 可选,per-request 自定义约束配置;不传则用内置约束集 */
433
+ customConfig?: MergedConstraintsConfig | null;
537
434
  }
538
435
 
539
436
  /**
540
- * S11: 快捷函数 — 安全全量检查(不抛异常)
437
+ * 快捷函数:执行三层检查
438
+ *
439
+ * @param context 约束上下文
440
+ * @param options.onTrace 每条约束检查后的回调(用于记录 trace 到外部存储)
441
+ * @param options.customConfig 可选,per-request 自定义约束配置;不传则用内置约束集
541
442
  */
542
- export async function checkConstraintsSafe(
443
+ export async function checkConstraints(
543
444
  context: ConstraintContext,
544
- customConfig?: MergedConstraintsConfig | null
445
+ options?: CheckConstraintsOptions
545
446
  ): Promise<ConstraintCheckResult> {
546
- return ConstraintChecker.getInstance().checkConstraintsSafe(context, customConfig);
447
+ const result = await ConstraintChecker.getInstance().checkConstraints(context, options?.customConfig ?? null);
448
+ if (options?.onTrace) {
449
+ for (const r of result.ironLaws) options.onTrace(r);
450
+ for (const r of result.guidelines) options.onTrace(r);
451
+ }
452
+ return result;
547
453
  }
548
454
 
549
455
  /**
@@ -78,4 +78,5 @@ export function registeredCheckCount(): number {
78
78
  return registry.size;
79
79
  }
80
80
 
81
- export type { ConstraintCheck, CheckEnv, CheckOutcome } from './types';
81
+ export { buildCheckEnv } from './types';
82
+ export type { ConstraintCheck, CheckEnv, CheckOutcome, EvidenceProviders } from './types';
@@ -26,6 +26,48 @@ export interface CheckEnv {
26
26
  srcScan(root: string): string[];
27
27
  }
28
28
 
29
+ /**
30
+ * 证据提供者组:git diff / 源码扫描的 memoized I/O
31
+ *
32
+ * 由调用方绑定后传入 buildCheckEnv(如 ConstraintChecker 绑实例级
33
+ * run 缓存的方法);工厂本身不做 memoization。
34
+ */
35
+ export interface EvidenceProviders {
36
+ /** staged 全量 diff */
37
+ stagedDiff(): Promise<string>;
38
+ /** staged 变更文件名列表 */
39
+ stagedDiffNames(): Promise<string>;
40
+ /** 源码根相对路径文件列表 */
41
+ srcScan(root: string): string[];
42
+ }
43
+
44
+ /**
45
+ * 构造 CheckEnv:生产侧唯一构造点
46
+ *
47
+ * - providers 传入 = 证据接线,checker 正常评估
48
+ * - 'none' = 显式不接证据:证据函数返回空;evidence flag 未接线的
49
+ * checker 按契约返回 'skip'(见 contextEvidenceFlag),直接读空
50
+ * 证据的 checker 在自然输入下判定
51
+ *
52
+ * studio 侧若需 git 证据(#129),传真实 providers 即可。
53
+ */
54
+ export function buildCheckEnv(
55
+ context: ConstraintContext,
56
+ evidence: EvidenceProviders | 'none'
57
+ ): CheckEnv {
58
+ const projectPath = context.projectPath || process.cwd();
59
+ if (evidence === 'none') {
60
+ return {
61
+ context,
62
+ projectPath,
63
+ stagedDiff: async () => '',
64
+ stagedDiffNames: async () => '',
65
+ srcScan: () => [],
66
+ };
67
+ }
68
+ return { context, projectPath, ...evidence };
69
+ }
70
+
29
71
  /**
30
72
  * 检查结果三态(ADR-0001 存在性探测 / flag 未接线):
31
73
  * - true = 满足(pass)
@@ -68,7 +68,6 @@ export const GUIDELINES: Record<string, Constraint> = {
68
68
  - 性能优化不改变接口
69
69
 
70
70
  支持模块级目录条目登记:governance.capabilities.mode=module 时按目录覆盖判定(如 src/core/ 一条目录条目覆盖该目录下所有源文件)。`,
71
- exceptions: ['internal_refactor', 'bug_fix_only', 'performance_optimization'],
72
71
  },
73
72
 
74
73
  /**
@@ -97,6 +96,5 @@ export const GUIDELINES: Record<string, Constraint> = {
97
96
  - 临时目录(tmp、temp)
98
97
  - 测试目录(__tests__、test)
99
98
  - 生成代码目录(dist、build、generated)`,
100
- exceptions: ['temp_dir', 'test_dir', 'generated_code'],
101
99
  },
102
100
  };
@@ -76,7 +76,6 @@ export const PROMPTS: Record<string, Constraint> = {
76
76
  - 用 fallback/兜底值掩盖上游数据异常而不追根因
77
77
  - Read 定位后直接 Edit,不对照设计原型确认意图
78
78
  - 从"数字异常"直接跳到根因结论,不验证假设的含义与量纲`,
79
- exceptions: ['simple_typo', 'config_value_error', 'missing_config'],
80
79
  promptInjection: '修复问题前必须先诊断根因——不止"哪里出错",而是"为什么设计成这样":用 Read/Grep 定位后禁止直接 Edit,先对照设计原型(CLAUDE.md、类型定义、commit message)确认原设计意图,呈现确认的根因+方案草案后才能动手。遇到空值/异常/不完整数据,禁止用 fallback/兜底/try-catch 掩盖——先追上游:数据谁产生的?为什么是空的?选择防御性兜底时必须在注释中说明根因;同一位置连续兜底 2+ 次是上游 bug 信号,停止修下游、追踪源头。从数据到结论必须先验证关键假设:数字的含义(累积/单次?量纲?)、正常范围、同类场景对比与反例,禁止"数字异常→直接定根因→直接改"的跳级推理。不绕过问题、不遮掩症状、不用临时方案代替根本修复。',
81
80
  },
82
81
 
@@ -107,7 +106,6 @@ export const PROMPTS: Record<string, Constraint> = {
107
106
  - reliability_required: 需要持久化/高可用
108
107
 
109
108
  YAGNI:不为"未来可能需要"添加抽象层、接口、配置项或插件系统;一个抽象只有一个实现者时删除该抽象。`,
110
- exceptions: ['scalability_required', 'security_required', 'performance_required', 'reliability_required'],
111
109
  promptInjection: '最简方案优先:用最少代码解决当前问题,不添加"以防万一"的冗余功能,不为仅用一次的代码强行设计抽象。创建新模块/文件/能力前,先查现有能力索引确认无可复用——优先级:直接复用 > 扩展现有 > 组合现有 > 新建。遵循 YAGNI:不为"未来可能需要"添加抽象层、接口、配置项或插件系统;一个 interface/abstract class 只有一个实现者时,删除这个抽象。自检:资深工程师是否会认为此实现过度复杂?若是,立即简化。',
112
110
  injectPrompt: true,
113
111
  },
@@ -137,7 +135,6 @@ YAGNI:不为"未来可能需要"添加抽象层、接口、配置项或插件
137
135
  - 类型定义文件(.d.ts、interface)
138
136
  - 简单 getter/setter
139
137
  - 纯展示 UI 组件(无交互逻辑)`,
140
- exceptions: ['config_file', 'type_definition', 'simple_accessor', 'pure_display_component'],
141
138
  promptInjection: '新代码必须同时编写测试。实现功能前先写测试用例(RED),然后实现让测试通过(GREEN)。不得提交无测试覆盖的实现代码。',
142
139
  },
143
140
 
@@ -164,7 +161,6 @@ YAGNI:不为"未来可能需要"添加抽象层、接口、配置项或插件
164
161
  - 发现冗余代码后的重构优化
165
162
  - 用更简洁的实现达到相同效果
166
163
  - 删除不再使用的遗留代码`,
167
- exceptions: ['redundant_code_cleanup', 'same_effect_refactor', 'unused_code_removal'],
168
164
  promptInjection: '不得擅自简化或删除测试、lint 规则、类型检查或约束。如需降低检查标准,必须先提案并获明确批准。',
169
165
  },
170
166
 
@@ -272,7 +268,6 @@ YAGNI:不为"未来可能需要"添加抽象层、接口、配置项或插件
272
268
  - 用户明确说"直接做"、"不用问"
273
269
  - 紧急修复
274
270
  - 已有明确设计文档`,
275
- exceptions: ['explicit_instruction', 'emergency_fix', 'existing_design'],
276
271
  promptInjection: '涉及架构变更、新增依赖、API 设计等重大决策时,必须先提出讨论获得确认,再开始实现。不要凭单方面判断做架构决策。',
277
272
  },
278
273
 
@@ -20,6 +20,7 @@ export {
20
20
  checkBeforeExecution,
21
21
  constraintChecker,
22
22
  } from './checker';
23
+ export type { CheckConstraintsOptions, TraceRecorder } from './checker';
23
24
 
24
25
  // CLAUDE.md 约束注入段渲染(纯函数,init 注入与漂移校验共用)
25
26
  export {
@@ -51,20 +52,4 @@ export type {
51
52
  IronLawContext,
52
53
  } from '../../types/constraint';
53
54
 
54
- export { ConstraintViolationError } from '../../types/constraint';
55
-
56
- // 约束拦截器
57
- export {
58
- ConstraintInterceptor,
59
- constraintInterceptor,
60
- interceptOperation,
61
- claimOperation,
62
- } from './interceptor';
63
-
64
- export type {
65
- EnforcementId,
66
- EnforcementExecutor,
67
- EnforcementContext,
68
- EnforcementResult,
69
- InterceptionResult,
70
- } from '../../types/enforcement';
55
+ export { ConstraintViolationError } from '../../types/constraint';
@@ -33,12 +33,11 @@ export interface ConstraintUsageStats {
33
33
  id: string;
34
34
  level: Constraint['level'];
35
35
 
36
- /** trace 总行数(含 skip/bypassed) */
36
+ /** trace 总行数(含 skip) */
37
37
  total: number;
38
38
  pass: number;
39
39
  fail: number;
40
40
  skip: number;
41
- bypassed: number;
42
41
 
43
42
  /** 实际评估次数(total - skip;skip 未评估不计入,与 TraceAnalyzer 一致) */
44
43
  evaluated: number;
@@ -129,26 +128,25 @@ export function readProjectTraces(projectRoot: string): ExecutionTrace[] {
129
128
  export function collectUsageByConstraint(
130
129
  traces: ExecutionTrace[]
131
130
  ): Map<string, Omit<ConstraintUsageStats, 'id' | 'level' | 'evaluated' | 'failRate'>> {
132
- const map = new Map<string, { total: number; pass: number; fail: number; skip: number; bypassed: number; firstAt?: number; lastAt?: number }>();
131
+ const map = new Map<string, { total: number; pass: number; fail: number; skip: number; firstAt?: number; lastAt?: number }>();
133
132
  for (const t of traces) {
134
133
  let agg = map.get(t.constraintId);
135
134
  if (!agg) {
136
- agg = { total: 0, pass: 0, fail: 0, skip: 0, bypassed: 0 };
135
+ agg = { total: 0, pass: 0, fail: 0, skip: 0 };
137
136
  map.set(t.constraintId, agg);
138
137
  }
139
138
  agg.total++;
140
139
  if (t.result === 'pass') agg.pass++;
141
140
  else if (t.result === 'fail') agg.fail++;
142
141
  else if (t.result === 'skip') agg.skip++;
143
- else if (t.result === 'bypassed') agg.bypassed++;
144
142
  if (agg.firstAt === undefined || t.timestamp < agg.firstAt) agg.firstAt = t.timestamp;
145
143
  if (agg.lastAt === undefined || t.timestamp > agg.lastAt) agg.lastAt = t.timestamp;
146
144
  }
147
145
  return map;
148
146
  }
149
147
 
150
- function toStats(id: string, level: Constraint['level'], agg: { total: number; pass: number; fail: number; skip: number; bypassed: number; firstAt?: number; lastAt?: number } | undefined): ConstraintUsageStats {
151
- const a = agg ?? { total: 0, pass: 0, fail: 0, skip: 0, bypassed: 0 };
148
+ function toStats(id: string, level: Constraint['level'], agg: { total: number; pass: number; fail: number; skip: number; firstAt?: number; lastAt?: number } | undefined): ConstraintUsageStats {
149
+ const a = agg ?? { total: 0, pass: 0, fail: 0, skip: 0 };
152
150
  const evaluated = a.total - a.skip;
153
151
  return {
154
152
  id,
@@ -157,7 +155,6 @@ function toStats(id: string, level: Constraint['level'], agg: { total: number; p
157
155
  pass: a.pass,
158
156
  fail: a.fail,
159
157
  skip: a.skip,
160
- bypassed: a.bypassed,
161
158
  evaluated,
162
159
  failRate: evaluated > 0 ? a.fail / evaluated : 0,
163
160
  firstAt: a.firstAt,
@@ -8,24 +8,28 @@
8
8
  */
9
9
 
10
10
  import type { Constraint } from '../types/constraint';
11
+ import type { MergedConstraintsConfig } from '../types/project-config';
11
12
  import { PROMPTS } from './constraints/definitions';
12
13
  import { ProjectConfigLoader } from './project-config-loader';
13
14
 
14
15
  /**
15
16
  * 获取项目当前生效的约束集(check + prompt,带 kind)
16
17
  *
17
- * 内部完成完整合并:内置 → preset 裁剪 → config.yml `constraints.<id>.enabled:false`
18
- * 删除(内置与 custom 同效)→ custom-constraints 追加/extend_exceptions
19
- * (禁用/已退役的 custom 不追加)→ scenes 过滤
18
+ * 生效集链路同 getMergedConstraintsConfig:内置 → preset 裁剪 → config.yml
19
+ * `constraints.<id>.enabled:false` 删除(内置与 custom 同效)→ custom-constraints
20
+ * 追加(禁用/已退役的 custom 不追加)→ scenes 过滤
20
21
  * (带 appliesTo 的 prompt 仅当 config.yml `scenes` 与其交集非空时保留,
21
22
  * 缺省 scenes=[] 即场景专属 prompt 默认不进入生效集)。
22
23
  *
23
24
  * @param projectRoot 项目根路径(缺省 process.cwd())
25
+ * @param options.preset 覆盖 config.yml 的 preset;仅在项目无自定义配置时生效
26
+ * (与 getMergedConstraintsConfig 同一优先级规则),不传则尊重 config.yml
24
27
  */
25
- export function getEffectiveConstraints(projectRoot: string = process.cwd()): Constraint[] {
26
- const loader = new ProjectConfigLoader(projectRoot);
27
- loader.load();
28
- const merged = loader.mergeConstraints();
28
+ export function getEffectiveConstraints(
29
+ projectRoot: string = process.cwd(),
30
+ options?: { preset?: string }
31
+ ): Constraint[] {
32
+ const merged = getMergedConstraintsConfig(projectRoot, options);
29
33
  return [
30
34
  ...Object.values(merged.ironLaws),
31
35
  ...Object.values(merged.guidelines),
@@ -33,6 +37,30 @@ export function getEffectiveConstraints(projectRoot: string = process.cwd()): Co
33
37
  ];
34
38
  }
35
39
 
40
+ /**
41
+ * 获取项目当前生效的合并约束配置(ADR-0001 唯一来源的完整形状)
42
+ *
43
+ * 与 getEffectiveConstraints 同一生效集链路,返回完整 MergedConstraintsConfig
44
+ * (含 disabled/custom/unknownIds),供 check 等需要诊断信息的消费方使用。
45
+ *
46
+ * options.preset(CLI --preset)仅在项目无自定义配置时覆盖 config.yml 的
47
+ * preset(工单 23 语义:项目自定义配置优先于 CLI 预设);不传 preset 时
48
+ * 完全尊重 config.yml 的 preset 键。
49
+ *
50
+ * @param projectRoot 项目根路径(缺省 process.cwd())
51
+ */
52
+ export function getMergedConstraintsConfig(
53
+ projectRoot: string = process.cwd(),
54
+ options?: { preset?: string }
55
+ ): MergedConstraintsConfig {
56
+ const loader = new ProjectConfigLoader(projectRoot);
57
+ loader.load();
58
+ if (loader.hasCustomConfig() || !options?.preset) {
59
+ return loader.mergeConstraints();
60
+ }
61
+ return loader.mergeConstraints({ preset: options.preset });
62
+ }
63
+
36
64
  /**
37
65
  * 生效集配置诊断结果
38
66
  */
@@ -0,0 +1,56 @@
1
+ /**
2
+ * 生效集共享筛选器(ADR-0001 收口)
3
+ *
4
+ * 「config.yml 引用了哪些 id、哪些被禁用」的判定,约束侧与门禁侧共用一份:
5
+ * - 约束侧(ProjectConfigLoader.mergeConstraints):未知 id 静默忽略,
6
+ * 收集进结果 unknownIds 供诊断(历史配置可能存在已移除约束的禁用残留)
7
+ * - 门禁侧(getEffectiveGates):未知 id 抛错(闭环校验;gates 配置段为
8
+ * 新增面,不存在历史残留配置)
9
+ *
10
+ * 行为差异仅收敛为 onUnknownId 一个开关,筛选语义两侧一致。
11
+ */
12
+
13
+ export interface FilterEnabledEntriesOptions {
14
+ /** 未知 id 处理:'collect' 收集进结果 unknownIds;'throw' 立即抛错 */
15
+ onUnknownId: 'collect' | 'throw';
16
+
17
+ /** onUnknownId='throw' 时的错误消息定制(如附带可用 id 清单) */
18
+ unknownIdError?: (id: string) => string;
19
+ }
20
+
21
+ export interface FilterEnabledEntriesResult {
22
+ /** enabled === false 的 id(未知 id 若带 enabled:false 同样计入,与历史行为一致) */
23
+ disabledIds: string[];
24
+
25
+ /** 未知 id(仅 onUnknownId='collect' 时非空) */
26
+ unknownIds: string[];
27
+ }
28
+
29
+ /**
30
+ * 按 config 条目筛选:未知 id 按 onUnknownId 处理,enabled:false 收进 disabledIds
31
+ */
32
+ export function filterEnabledEntries(
33
+ knownIds: ReadonlySet<string>,
34
+ entries: Record<string, { enabled?: boolean } | undefined>,
35
+ options: FilterEnabledEntriesOptions
36
+ ): FilterEnabledEntriesResult {
37
+ const disabledIds: string[] = [];
38
+ const unknownIds: string[] = [];
39
+
40
+ for (const [id, entry] of Object.entries(entries)) {
41
+ if (!knownIds.has(id)) {
42
+ if (options.onUnknownId === 'throw') {
43
+ throw new Error(
44
+ options.unknownIdError?.(id) ??
45
+ `[harness] 配置引用了未注册的 id "${id}"。`
46
+ );
47
+ }
48
+ unknownIds.push(id);
49
+ }
50
+ if (entry?.enabled === false) {
51
+ disabledIds.push(id);
52
+ }
53
+ }
54
+
55
+ return { disabledIds, unknownIds };
56
+ }