@dommaker/harness 1.1.0 → 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 (349) hide show
  1. package/CHANGELOG.md +19 -2
  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.d.ts.map +1 -1
  21. package/dist/cli/commands/status.js +2 -11
  22. package/dist/cli/commands/status.js.map +1 -1
  23. package/dist/context/index.d.ts +9 -6
  24. package/dist/context/index.d.ts.map +1 -1
  25. package/dist/context/index.js +18 -20
  26. package/dist/context/index.js.map +1 -1
  27. package/dist/core/constraints/capabilities-parser.d.ts +3 -2
  28. package/dist/core/constraints/capabilities-parser.d.ts.map +1 -1
  29. package/dist/core/constraints/capabilities-parser.js +25 -47
  30. package/dist/core/constraints/capabilities-parser.js.map +1 -1
  31. package/dist/core/constraints/checker.d.ts +32 -33
  32. package/dist/core/constraints/checker.d.ts.map +1 -1
  33. package/dist/core/constraints/checker.js +38 -128
  34. package/dist/core/constraints/checker.js.map +1 -1
  35. package/dist/core/constraints/checkers/index.d.ts +2 -1
  36. package/dist/core/constraints/checkers/index.d.ts.map +1 -1
  37. package/dist/core/constraints/checkers/index.js +3 -0
  38. package/dist/core/constraints/checkers/index.js.map +1 -1
  39. package/dist/core/constraints/checkers/types.d.ts +25 -0
  40. package/dist/core/constraints/checkers/types.d.ts.map +1 -1
  41. package/dist/core/constraints/checkers/types.js +24 -0
  42. package/dist/core/constraints/checkers/types.js.map +1 -1
  43. package/dist/core/constraints/definitions/guidelines.d.ts.map +1 -1
  44. package/dist/core/constraints/definitions/guidelines.js +0 -2
  45. package/dist/core/constraints/definitions/guidelines.js.map +1 -1
  46. package/dist/core/constraints/definitions/prompts.d.ts.map +1 -1
  47. package/dist/core/constraints/definitions/prompts.js +0 -5
  48. package/dist/core/constraints/definitions/prompts.js.map +1 -1
  49. package/dist/core/constraints/index.d.ts +1 -2
  50. package/dist/core/constraints/index.d.ts.map +1 -1
  51. package/dist/core/constraints/index.js +1 -7
  52. package/dist/core/constraints/index.js.map +1 -1
  53. package/dist/core/constraints/usage-report.d.ts +1 -2
  54. package/dist/core/constraints/usage-report.d.ts.map +1 -1
  55. package/dist/core/constraints/usage-report.js +2 -5
  56. package/dist/core/constraints/usage-report.js.map +1 -1
  57. package/dist/core/effective-constraints.d.ts +24 -4
  58. package/dist/core/effective-constraints.d.ts.map +1 -1
  59. package/dist/core/effective-constraints.js +28 -7
  60. package/dist/core/effective-constraints.js.map +1 -1
  61. package/dist/core/effective-set.d.ts +30 -0
  62. package/dist/core/effective-set.d.ts.map +1 -0
  63. package/dist/core/effective-set.js +35 -0
  64. package/dist/core/effective-set.js.map +1 -0
  65. package/dist/core/index.d.ts +13 -6
  66. package/dist/core/index.d.ts.map +1 -1
  67. package/dist/core/index.js +44 -21
  68. package/dist/core/index.js.map +1 -1
  69. package/dist/core/project-config-loader.d.ts +3 -6
  70. package/dist/core/project-config-loader.d.ts.map +1 -1
  71. package/dist/core/project-config-loader.js +47 -61
  72. package/dist/core/project-config-loader.js.map +1 -1
  73. package/dist/failure/constraint-handler.d.ts +1 -1
  74. package/dist/failure/constraint-handler.js +1 -1
  75. package/dist/gates/checker-gate.d.ts +1 -4
  76. package/dist/gates/checker-gate.d.ts.map +1 -1
  77. package/dist/gates/checker-gate.js +3 -11
  78. package/dist/gates/checker-gate.js.map +1 -1
  79. package/dist/gates/definitions.d.ts +9 -38
  80. package/dist/gates/definitions.d.ts.map +1 -1
  81. package/dist/gates/definitions.js +24 -13
  82. package/dist/gates/definitions.js.map +1 -1
  83. package/dist/gates/effective-gates.d.ts.map +1 -1
  84. package/dist/gates/effective-gates.js +14 -18
  85. package/dist/gates/effective-gates.js.map +1 -1
  86. package/dist/gates/index.d.ts +1 -1
  87. package/dist/gates/index.d.ts.map +1 -1
  88. package/dist/gates/index.js.map +1 -1
  89. package/dist/hooks/bootstrap.d.ts.map +1 -1
  90. package/dist/hooks/bootstrap.js +0 -3
  91. package/dist/hooks/bootstrap.js.map +1 -1
  92. package/dist/index.d.ts +43 -53
  93. package/dist/index.d.ts.map +1 -1
  94. package/dist/index.js +180 -138
  95. package/dist/index.js.map +1 -1
  96. package/dist/knowledge/query.d.ts +15 -0
  97. package/dist/knowledge/query.d.ts.map +1 -1
  98. package/dist/knowledge/query.js +23 -0
  99. package/dist/knowledge/query.js.map +1 -1
  100. package/dist/monitoring/analyzer-base.d.ts +0 -4
  101. package/dist/monitoring/analyzer-base.d.ts.map +1 -1
  102. package/dist/monitoring/analyzer-base.js +0 -21
  103. package/dist/monitoring/analyzer-base.js.map +1 -1
  104. package/dist/monitoring/index.d.ts +6 -12
  105. package/dist/monitoring/index.d.ts.map +1 -1
  106. package/dist/monitoring/index.js +13 -29
  107. package/dist/monitoring/index.js.map +1 -1
  108. package/dist/monitoring/trace-analyzer.d.ts.map +1 -1
  109. package/dist/monitoring/trace-analyzer.js +2 -68
  110. package/dist/monitoring/trace-analyzer.js.map +1 -1
  111. package/dist/monitoring/traces.d.ts +2 -6
  112. package/dist/monitoring/traces.d.ts.map +1 -1
  113. package/dist/monitoring/traces.js +0 -14
  114. package/dist/monitoring/traces.js.map +1 -1
  115. package/dist/presets/index.d.ts +2 -2
  116. package/dist/presets/index.d.ts.map +1 -1
  117. package/dist/presets/index.js +3 -4
  118. package/dist/presets/index.js.map +1 -1
  119. package/dist/presets/standard.d.ts +10 -22
  120. package/dist/presets/standard.d.ts.map +1 -1
  121. package/dist/presets/standard.js +15 -75
  122. package/dist/presets/standard.js.map +1 -1
  123. package/dist/types/constraint.d.ts +1 -29
  124. package/dist/types/constraint.d.ts.map +1 -1
  125. package/dist/types/constraint.js +1 -1
  126. package/dist/types/constraint.js.map +1 -1
  127. package/dist/types/index.d.ts +0 -3
  128. package/dist/types/index.d.ts.map +1 -1
  129. package/dist/types/index.js +0 -4
  130. package/dist/types/index.js.map +1 -1
  131. package/dist/types/project-config.d.ts +4 -8
  132. package/dist/types/project-config.d.ts.map +1 -1
  133. package/dist/types/trace.d.ts +9 -22
  134. package/dist/types/trace.d.ts.map +1 -1
  135. package/package.json +2 -2
  136. package/src/CONTEXT.md +5 -7
  137. package/src/__tests__/checker-extra.test.ts +80 -139
  138. package/src/__tests__/checker.test.ts +0 -376
  139. package/src/__tests__/project-config-loader-extra.test.ts +42 -29
  140. package/src/__tests__/public-exports.test.ts +138 -0
  141. package/src/__tests__/trace-analyzer-extra.test.ts +6 -86
  142. package/src/__tests__/trace-analyzer.test.ts +4 -5
  143. package/src/__tests__/trace-collector.test.ts +0 -11
  144. package/src/__tests__/traces.test.ts +3 -22
  145. package/src/cli/commands/CONTEXT.md +2 -2
  146. package/src/cli/commands/__tests__/check-skip-output.test.ts +10 -7
  147. package/src/cli/commands/__tests__/check.test.ts +29 -69
  148. package/src/cli/commands/__tests__/constraints-report.test.ts +1 -1
  149. package/src/cli/commands/__tests__/knowledge.test.ts +90 -1
  150. package/src/cli/commands/__tests__/registry.test.ts +8 -6
  151. package/src/cli/commands/__tests__/status-extra.test.ts +6 -28
  152. package/src/cli/commands/__tests__/status.test.ts +6 -21
  153. package/src/cli/commands/check.ts +8 -34
  154. package/src/cli/commands/definitions.ts +2 -0
  155. package/src/cli/commands/init.ts +6 -37
  156. package/src/cli/commands/knowledge.ts +1 -16
  157. package/src/cli/commands/report.ts +0 -2
  158. package/src/cli/commands/status.ts +2 -12
  159. package/src/context/index.ts +29 -6
  160. package/src/core/CONTEXT.md +4 -3
  161. package/src/core/__tests__/effective-set.test.ts +80 -0
  162. package/src/core/constraints/capabilities-parser.ts +43 -49
  163. package/src/core/constraints/checker.ts +56 -147
  164. package/src/core/constraints/checkers/index.ts +2 -1
  165. package/src/core/constraints/checkers/types.ts +42 -0
  166. package/src/core/constraints/definitions/guidelines.ts +0 -2
  167. package/src/core/constraints/definitions/prompts.ts +0 -5
  168. package/src/core/constraints/index.ts +2 -17
  169. package/src/core/constraints/usage-report.ts +5 -8
  170. package/src/core/effective-constraints.ts +35 -7
  171. package/src/core/effective-set.ts +56 -0
  172. package/src/core/index.ts +80 -6
  173. package/src/core/project-config-loader.ts +53 -68
  174. package/src/failure/CONTEXT.md +1 -1
  175. package/src/failure/constraint-handler.ts +1 -1
  176. package/src/gates/CONTEXT.md +3 -3
  177. package/src/gates/checker-gate.ts +4 -12
  178. package/src/gates/definitions.ts +27 -50
  179. package/src/gates/effective-gates.ts +14 -21
  180. package/src/gates/index.ts +1 -1
  181. package/src/hooks/bootstrap.ts +0 -3
  182. package/src/index.ts +384 -137
  183. package/src/knowledge/CONTEXT.md +2 -2
  184. package/src/knowledge/__tests__/query.test.ts +64 -0
  185. package/src/knowledge/query.ts +28 -0
  186. package/src/monitoring/CONTEXT.md +7 -13
  187. package/src/monitoring/__tests__/traces.test.ts +0 -10
  188. package/src/monitoring/analyzer-base.ts +0 -22
  189. package/src/monitoring/index.ts +6 -12
  190. package/src/monitoring/trace-analyzer.ts +2 -73
  191. package/src/monitoring/traces.ts +2 -22
  192. package/src/presets/__tests__/standard.test.ts +48 -69
  193. package/src/presets/index.ts +3 -5
  194. package/src/presets/standard.ts +14 -79
  195. package/src/spec/CONTEXT.md +5 -13
  196. package/src/types/constraint.ts +1 -51
  197. package/src/types/index.ts +0 -12
  198. package/src/types/project-config.ts +4 -10
  199. package/src/types/trace.ts +9 -34
  200. package/dist/core/constraints/interceptor.d.ts +0 -38
  201. package/dist/core/constraints/interceptor.d.ts.map +0 -1
  202. package/dist/core/constraints/interceptor.js +0 -166
  203. package/dist/core/constraints/interceptor.js.map +0 -1
  204. package/dist/dashboard/data.d.ts +0 -31
  205. package/dist/dashboard/data.d.ts.map +0 -1
  206. package/dist/dashboard/data.js +0 -81
  207. package/dist/dashboard/data.js.map +0 -1
  208. package/dist/dashboard/index.d.ts +0 -4
  209. package/dist/dashboard/index.d.ts.map +0 -1
  210. package/dist/dashboard/index.js +0 -20
  211. package/dist/dashboard/index.js.map +0 -1
  212. package/dist/dashboard/stats.d.ts +0 -20
  213. package/dist/dashboard/stats.d.ts.map +0 -1
  214. package/dist/dashboard/stats.js +0 -115
  215. package/dist/dashboard/stats.js.map +0 -1
  216. package/dist/dashboard/types.d.ts +0 -110
  217. package/dist/dashboard/types.d.ts.map +0 -1
  218. package/dist/dashboard/types.js +0 -8
  219. package/dist/dashboard/types.js.map +0 -1
  220. package/dist/monitoring/constraint-doctor.d.ts +0 -82
  221. package/dist/monitoring/constraint-doctor.d.ts.map +0 -1
  222. package/dist/monitoring/constraint-doctor.js +0 -191
  223. package/dist/monitoring/constraint-doctor.js.map +0 -1
  224. package/dist/monitoring/diagnosis-rules.d.ts +0 -18
  225. package/dist/monitoring/diagnosis-rules.d.ts.map +0 -1
  226. package/dist/monitoring/diagnosis-rules.js +0 -119
  227. package/dist/monitoring/diagnosis-rules.js.map +0 -1
  228. package/dist/monitoring/knowledge-doctor.d.ts +0 -78
  229. package/dist/monitoring/knowledge-doctor.d.ts.map +0 -1
  230. package/dist/monitoring/knowledge-doctor.js +0 -266
  231. package/dist/monitoring/knowledge-doctor.js.map +0 -1
  232. package/dist/monitoring/knowledge-evolver.d.ts +0 -72
  233. package/dist/monitoring/knowledge-evolver.d.ts.map +0 -1
  234. package/dist/monitoring/knowledge-evolver.js +0 -205
  235. package/dist/monitoring/knowledge-evolver.js.map +0 -1
  236. package/dist/monitoring/performance-analyzer.d.ts +0 -92
  237. package/dist/monitoring/performance-analyzer.d.ts.map +0 -1
  238. package/dist/monitoring/performance-analyzer.js +0 -343
  239. package/dist/monitoring/performance-analyzer.js.map +0 -1
  240. package/dist/monitoring/performance-collector.d.ts +0 -103
  241. package/dist/monitoring/performance-collector.d.ts.map +0 -1
  242. package/dist/monitoring/performance-collector.js +0 -298
  243. package/dist/monitoring/performance-collector.js.map +0 -1
  244. package/dist/safety/index.d.ts +0 -6
  245. package/dist/safety/index.d.ts.map +0 -1
  246. package/dist/safety/index.js +0 -22
  247. package/dist/safety/index.js.map +0 -1
  248. package/dist/safety/input-guardrail.d.ts +0 -36
  249. package/dist/safety/input-guardrail.d.ts.map +0 -1
  250. package/dist/safety/input-guardrail.js +0 -118
  251. package/dist/safety/input-guardrail.js.map +0 -1
  252. package/dist/safety/output-guardrail.d.ts +0 -39
  253. package/dist/safety/output-guardrail.d.ts.map +0 -1
  254. package/dist/safety/output-guardrail.js +0 -147
  255. package/dist/safety/output-guardrail.js.map +0 -1
  256. package/dist/safety/sandbox.d.ts +0 -45
  257. package/dist/safety/sandbox.d.ts.map +0 -1
  258. package/dist/safety/sandbox.js +0 -134
  259. package/dist/safety/sandbox.js.map +0 -1
  260. package/dist/safety/tool-guardrail.d.ts +0 -51
  261. package/dist/safety/tool-guardrail.d.ts.map +0 -1
  262. package/dist/safety/tool-guardrail.js +0 -147
  263. package/dist/safety/tool-guardrail.js.map +0 -1
  264. package/dist/safety/types.d.ts +0 -104
  265. package/dist/safety/types.d.ts.map +0 -1
  266. package/dist/safety/types.js +0 -6
  267. package/dist/safety/types.js.map +0 -1
  268. package/dist/spec/annotation-checker.d.ts +0 -47
  269. package/dist/spec/annotation-checker.d.ts.map +0 -1
  270. package/dist/spec/annotation-checker.js +0 -279
  271. package/dist/spec/annotation-checker.js.map +0 -1
  272. package/dist/types/enforcement.d.ts +0 -42
  273. package/dist/types/enforcement.d.ts.map +0 -1
  274. package/dist/types/enforcement.js +0 -6
  275. package/dist/types/enforcement.js.map +0 -1
  276. package/dist/types/monitoring-types.d.ts +0 -53
  277. package/dist/types/monitoring-types.d.ts.map +0 -1
  278. package/dist/types/monitoring-types.js +0 -10
  279. package/dist/types/monitoring-types.js.map +0 -1
  280. package/dist/types/performance.d.ts +0 -204
  281. package/dist/types/performance.d.ts.map +0 -1
  282. package/dist/types/performance.js +0 -12
  283. package/dist/types/performance.js.map +0 -1
  284. package/dist/verification/index.d.ts +0 -4
  285. package/dist/verification/index.d.ts.map +0 -1
  286. package/dist/verification/index.js +0 -20
  287. package/dist/verification/index.js.map +0 -1
  288. package/dist/verification/loop.d.ts +0 -30
  289. package/dist/verification/loop.d.ts.map +0 -1
  290. package/dist/verification/loop.js +0 -114
  291. package/dist/verification/loop.js.map +0 -1
  292. package/dist/verification/rules-based.d.ts +0 -40
  293. package/dist/verification/rules-based.d.ts.map +0 -1
  294. package/dist/verification/rules-based.js +0 -130
  295. package/dist/verification/rules-based.js.map +0 -1
  296. package/dist/verification/types.d.ts +0 -69
  297. package/dist/verification/types.d.ts.map +0 -1
  298. package/dist/verification/types.js +0 -6
  299. package/dist/verification/types.js.map +0 -1
  300. package/src/__tests__/annotation-checker.test.ts +0 -183
  301. package/src/__tests__/constraint-doctor.test.ts +0 -271
  302. package/src/__tests__/doctor-extra.test.ts +0 -155
  303. package/src/__tests__/extend-exceptions.test.ts +0 -149
  304. package/src/__tests__/interceptor.test.ts +0 -89
  305. package/src/__tests__/performance-analyzer.test.ts +0 -499
  306. package/src/__tests__/performance-collector.test.ts +0 -276
  307. package/src/__tests__/presets.test.ts +0 -65
  308. package/src/core/constraints/__tests__/interceptor.test.ts +0 -253
  309. package/src/core/constraints/interceptor.ts +0 -214
  310. package/src/dashboard/CONTEXT.md +0 -21
  311. package/src/dashboard/__tests__/data.test.ts +0 -111
  312. package/src/dashboard/__tests__/stats.test.ts +0 -159
  313. package/src/dashboard/data.ts +0 -103
  314. package/src/dashboard/index.ts +0 -3
  315. package/src/dashboard/stats.ts +0 -127
  316. package/src/dashboard/types.ts +0 -130
  317. package/src/monitoring/__tests__/knowledge-doctor.test.ts +0 -278
  318. package/src/monitoring/__tests__/knowledge-evolver.test.ts +0 -368
  319. package/src/monitoring/__tests__/performance-analyzer.test.ts +0 -137
  320. package/src/monitoring/__tests__/performance-collector.test.ts +0 -331
  321. package/src/monitoring/constraint-doctor.ts +0 -239
  322. package/src/monitoring/diagnosis-rules.ts +0 -132
  323. package/src/monitoring/knowledge-doctor.ts +0 -322
  324. package/src/monitoring/knowledge-evolver.ts +0 -289
  325. package/src/monitoring/performance-analyzer.ts +0 -400
  326. package/src/monitoring/performance-collector.ts +0 -326
  327. package/src/safety/CONTEXT.md +0 -23
  328. package/src/safety/__tests__/input-guardrail.test.ts +0 -105
  329. package/src/safety/__tests__/output-guardrail.test.ts +0 -108
  330. package/src/safety/__tests__/sandbox.test.ts +0 -158
  331. package/src/safety/__tests__/tool-guardrail.test.ts +0 -127
  332. package/src/safety/index.ts +0 -5
  333. package/src/safety/input-guardrail.ts +0 -133
  334. package/src/safety/output-guardrail.ts +0 -170
  335. package/src/safety/sandbox.ts +0 -148
  336. package/src/safety/tool-guardrail.ts +0 -176
  337. package/src/safety/types.ts +0 -124
  338. package/src/spec/__tests__/annotation-checker.test.ts +0 -294
  339. package/src/spec/annotation-checker.ts +0 -320
  340. package/src/types/enforcement.ts +0 -48
  341. package/src/types/monitoring-types.ts +0 -68
  342. package/src/types/performance.ts +0 -295
  343. package/src/verification/CONTEXT.md +0 -23
  344. package/src/verification/__tests__/loop.test.ts +0 -303
  345. package/src/verification/__tests__/rules-based.test.ts +0 -240
  346. package/src/verification/index.ts +0 -3
  347. package/src/verification/loop.ts +0 -137
  348. package/src/verification/rules-based.ts +0 -152
  349. package/src/verification/types.ts +0 -81
@@ -1,331 +0,0 @@
1
- /**
2
- * PerformanceCollector 测试
3
- */
4
-
5
- import { describe, it, expect, beforeEach } from '@jest/globals';
6
- import { PerformanceCollector } from '../performance-collector';
7
- import * as fs from 'fs';
8
-
9
- // Mock fs
10
- jest.mock('fs', () => ({
11
- existsSync: jest.fn(),
12
- mkdirSync: jest.fn(),
13
- appendFileSync: jest.fn(),
14
- statSync: jest.fn().mockReturnValue({ size: 100 }),
15
- renameSync: jest.fn(),
16
- writeFileSync: jest.fn(),
17
- readdirSync: jest.fn().mockReturnValue([]),
18
- readFileSync: jest.fn().mockReturnValue(''),
19
- }));
20
-
21
- const mockFs = fs as jest.Mocked<typeof fs>;
22
-
23
- describe('PerformanceCollector', () => {
24
- let collector: PerformanceCollector;
25
-
26
- beforeEach(() => {
27
- jest.clearAllMocks();
28
- mockFs.existsSync.mockReturnValue(true);
29
- mockFs.statSync.mockReturnValue({ size: 100 } as any);
30
- collector = new PerformanceCollector();
31
- });
32
-
33
- describe('constructor', () => {
34
- it('should create log directory if not exists', () => {
35
- mockFs.existsSync.mockReturnValueOnce(false);
36
- new PerformanceCollector();
37
- expect(mockFs.mkdirSync).toHaveBeenCalled();
38
- });
39
-
40
- it('should be disabled when enabled=false', () => {
41
- const disabledCollector = new PerformanceCollector({ enabled: false });
42
- disabledCollector.recordOk('test', 100);
43
- expect(mockFs.appendFileSync).not.toHaveBeenCalled();
44
- });
45
-
46
- it('should accept custom config', () => {
47
- const customCollector = new PerformanceCollector({
48
- enabled: true,
49
- logFile: '.harness/perf.log',
50
- maxFileSize: 5 * 1024 * 1024,
51
- });
52
- expect(customCollector).toBeDefined();
53
- });
54
-
55
- it('should use default config', () => {
56
- const defaultCollector = new PerformanceCollector();
57
- expect(defaultCollector).toBeDefined();
58
- });
59
- });
60
-
61
- describe('record()', () => {
62
- it('should append trace to file', () => {
63
- collector.record({
64
- operation: 'test',
65
- timestamp: Date.now(),
66
- duration: 100,
67
- result: 'ok',
68
- });
69
- expect(mockFs.appendFileSync).toHaveBeenCalled();
70
- });
71
-
72
- it('should not record when disabled', () => {
73
- const disabledCollector = new PerformanceCollector({ enabled: false });
74
- disabledCollector.record({
75
- operation: 'test',
76
- timestamp: Date.now(),
77
- duration: 100,
78
- result: 'ok',
79
- });
80
- expect(mockFs.appendFileSync).not.toHaveBeenCalled();
81
- });
82
-
83
- it('should record with all fields', () => {
84
- collector.record({
85
- operation: 'full-test',
86
- timestamp: 1234567890,
87
- duration: 500,
88
- result: 'exceeded',
89
- threshold: 300,
90
- metadata: { key: 'value' },
91
- });
92
- expect(mockFs.appendFileSync).toHaveBeenCalled();
93
- const call = mockFs.appendFileSync.mock.calls[0];
94
- const content = call[1] as string;
95
- expect(content).toContain('full-test');
96
- expect(content).toContain('exceeded');
97
- });
98
- });
99
-
100
- describe('recordOk()', () => {
101
- it('should record ok trace', () => {
102
- collector.recordOk('test', 150);
103
- const call = mockFs.appendFileSync.mock.calls[0];
104
- const content = call[1] as string;
105
- const trace = JSON.parse(content.trim());
106
- expect(trace.result).toBe('ok');
107
- expect(trace.operation).toBe('test');
108
- expect(trace.duration).toBe(150);
109
- });
110
-
111
- it('should record with metadata', () => {
112
- collector.recordOk('test', 100, { key: 'value' });
113
- const call = mockFs.appendFileSync.mock.calls[0];
114
- const content = call[1] as string;
115
- const trace = JSON.parse(content.trim());
116
- expect(trace.metadata).toEqual({ key: 'value' });
117
- });
118
-
119
- it('should auto-generate timestamp', () => {
120
- collector.recordOk('test', 100);
121
- const call = mockFs.appendFileSync.mock.calls[0];
122
- const content = call[1] as string;
123
- const trace = JSON.parse(content.trim());
124
- expect(trace.timestamp).toBeDefined();
125
- expect(typeof trace.timestamp).toBe('number');
126
- });
127
-
128
- it('should not record when disabled', () => {
129
- const disabledCollector = new PerformanceCollector({ enabled: false });
130
- disabledCollector.recordOk('test', 100);
131
- expect(mockFs.appendFileSync).not.toHaveBeenCalled();
132
- });
133
- });
134
-
135
- describe('recordExceeded()', () => {
136
- it('should record exceeded trace', () => {
137
- collector.recordExceeded('test', 35000, 30000);
138
- const call = mockFs.appendFileSync.mock.calls[0];
139
- const content = call[1] as string;
140
- const trace = JSON.parse(content.trim());
141
- expect(trace.result).toBe('exceeded');
142
- expect(trace.threshold).toBe(30000);
143
- });
144
-
145
- it('should include duration', () => {
146
- collector.recordExceeded('test', 35000, 30000);
147
- const call = mockFs.appendFileSync.mock.calls[0];
148
- const content = call[1] as string;
149
- const trace = JSON.parse(content.trim());
150
- expect(trace.duration).toBe(35000);
151
- });
152
-
153
- it('should not record when disabled', () => {
154
- const disabledCollector = new PerformanceCollector({ enabled: false });
155
- disabledCollector.recordExceeded('test', 35000, 30000);
156
- expect(mockFs.appendFileSync).not.toHaveBeenCalled();
157
- });
158
- });
159
-
160
- describe('recordError()', () => {
161
- it('should record error trace', () => {
162
- collector.recordError('test', 100, 'Test error');
163
- const call = mockFs.appendFileSync.mock.calls[0];
164
- const content = call[1] as string;
165
- const trace = JSON.parse(content.trim());
166
- expect(trace.result).toBe('error');
167
- expect(trace.metadata.error).toBe('Test error');
168
- });
169
-
170
- it('should include duration', () => {
171
- collector.recordError('test', 100, 'Test error');
172
- const call = mockFs.appendFileSync.mock.calls[0];
173
- const content = call[1] as string;
174
- const trace = JSON.parse(content.trim());
175
- expect(trace.duration).toBe(100);
176
- });
177
-
178
- it('should not record when disabled', () => {
179
- const disabledCollector = new PerformanceCollector({ enabled: false });
180
- disabledCollector.recordError('test', 100, 'Test error');
181
- expect(mockFs.appendFileSync).not.toHaveBeenCalled();
182
- });
183
- });
184
-
185
- describe('read()', () => {
186
- it('should return empty array if file not exists', () => {
187
- mockFs.existsSync.mockReturnValueOnce(false);
188
- const c = new PerformanceCollector();
189
- const traces = c.read();
190
- expect(traces).toEqual([]);
191
- });
192
-
193
- it('should return traces from file', () => {
194
- mockFs.existsSync.mockReturnValue(true);
195
- mockFs.readFileSync.mockReturnValueOnce(
196
- JSON.stringify({ operation: 'test1', duration: 100, result: 'ok', timestamp: 1 }) + '\n' +
197
- JSON.stringify({ operation: 'test2', duration: 200, result: 'ok', timestamp: 2 }) + '\n'
198
- );
199
-
200
- const c = new PerformanceCollector();
201
- const traces = c.read();
202
- expect(traces.length).toBe(2);
203
- expect(traces[0].operation).toBe('test1');
204
- expect(traces[1].operation).toBe('test2');
205
- });
206
-
207
- it('should handle empty file', () => {
208
- mockFs.existsSync.mockReturnValue(true);
209
- mockFs.readFileSync.mockReturnValueOnce('');
210
-
211
- const c = new PerformanceCollector();
212
- const traces = c.read();
213
- expect(traces).toEqual([]);
214
- });
215
-
216
- it('should filter by period', () => {
217
- mockFs.existsSync.mockReturnValue(true);
218
- mockFs.readFileSync.mockReturnValueOnce(
219
- JSON.stringify({ operation: 'test1', duration: 100, result: 'ok', timestamp: Date.now() - 100000 }) + '\n' +
220
- JSON.stringify({ operation: 'test2', duration: 200, result: 'ok', timestamp: Date.now() }) + '\n'
221
- );
222
-
223
- const c = new PerformanceCollector();
224
- const traces = c.read({ timeRange: { start: Date.now() - 50000, end: Date.now() } });
225
- expect(traces.length).toBe(1);
226
- expect(traces[0].operation).toBe('test2');
227
- });
228
-
229
- it('should filter by operation', () => {
230
- mockFs.existsSync.mockReturnValue(true);
231
- mockFs.readFileSync.mockReturnValueOnce(
232
- JSON.stringify({ operation: 'test1', duration: 100, result: 'ok', timestamp: Date.now() }) + '\n' +
233
- JSON.stringify({ operation: 'test2', duration: 200, result: 'ok', timestamp: Date.now() }) + '\n'
234
- );
235
-
236
- const c = new PerformanceCollector();
237
- const traces = c.read({ operation: 'test1' });
238
- expect(traces.length).toBe(1);
239
- expect(traces[0].operation).toBe('test1');
240
- });
241
-
242
- it('should filter by result', () => {
243
- mockFs.existsSync.mockReturnValue(true);
244
- mockFs.readFileSync.mockReturnValueOnce(
245
- JSON.stringify({ operation: 'test1', duration: 100, result: 'ok', timestamp: Date.now() }) + '\n' +
246
- JSON.stringify({ operation: 'test2', duration: 200, result: 'exceeded', timestamp: Date.now() }) + '\n'
247
- );
248
-
249
- const c = new PerformanceCollector();
250
- const traces = c.read({ result: 'exceeded' });
251
- expect(traces.length).toBe(1);
252
- expect(traces[0].result).toBe('exceeded');
253
- });
254
- });
255
-
256
- describe('getStats()', () => {
257
- it('should return stats', () => {
258
- mockFs.existsSync.mockReturnValue(true);
259
- mockFs.statSync.mockReturnValue({ size: 1024 } as any);
260
-
261
- const stats = collector.getStats();
262
- expect(stats).toHaveProperty('fileExists');
263
- });
264
-
265
- it('should return stats when file not exists', () => {
266
- // 先让目录检查通过,再让文件检查失败
267
- mockFs.existsSync
268
- .mockReturnValueOnce(true) // 目录检查
269
- .mockReturnValueOnce(false); // 文件检查
270
-
271
- const c = new PerformanceCollector();
272
- mockFs.existsSync.mockReturnValue(false); // getStats 检查
273
- const stats = c.getStats();
274
- expect(stats.fileExists).toBe(false);
275
- });
276
-
277
- it('should include file size', () => {
278
- mockFs.existsSync.mockReturnValue(true);
279
- mockFs.statSync.mockReturnValue({ size: 2048 } as any);
280
-
281
- const stats = collector.getStats();
282
- expect(stats.fileSize).toBe(2048);
283
- });
284
- });
285
-
286
- describe('file rolling', () => {
287
- it('should roll file when size exceeds limit', () => {
288
- mockFs.statSync.mockReturnValue({ size: 15 * 1024 * 1024 } as any); // 15MB
289
-
290
- collector.recordOk('test', 100);
291
- expect(mockFs.renameSync).toHaveBeenCalled();
292
- });
293
-
294
- it('should not roll when under limit', () => {
295
- mockFs.statSync.mockReturnValue({ size: 5 * 1024 * 1024 } as any); // 5MB
296
-
297
- collector.recordOk('test', 100);
298
- expect(mockFs.renameSync).not.toHaveBeenCalled();
299
- });
300
- });
301
-
302
- describe('clear()', () => {
303
- it('should exist if implemented', () => {
304
- // Check if method exists
305
- const hasClear = typeof (collector as any).clear === 'function';
306
- if (hasClear) {
307
- mockFs.existsSync.mockReturnValue(true);
308
- (collector as any).clear();
309
- expect(mockFs.writeFileSync).toHaveBeenCalled();
310
- }
311
- });
312
- });
313
-
314
- describe('getRecentTraces()', () => {
315
- it('should return recent traces', () => {
316
- mockFs.existsSync.mockReturnValue(true);
317
- mockFs.readFileSync.mockReturnValueOnce(
318
- JSON.stringify({ operation: 'test1', duration: 100, result: 'ok', timestamp: Date.now() }) + '\n' +
319
- JSON.stringify({ operation: 'test2', duration: 200, result: 'ok', timestamp: Date.now() }) + '\n' +
320
- JSON.stringify({ operation: 'test3', duration: 300, result: 'ok', timestamp: Date.now() }) + '\n'
321
- );
322
-
323
- // Check if method exists
324
- const hasRecent = typeof (collector as any).getRecentTraces === 'function';
325
- if (hasRecent) {
326
- const traces = (collector as any).getRecentTraces(2);
327
- expect(traces.length).toBe(2);
328
- }
329
- });
330
- });
331
- });
@@ -1,239 +0,0 @@
1
- /**
2
- * 约束诊断器
3
- *
4
- * 当检测到异常时,基于预设规则分析 traces 给出诊断(零 Token,不调用 LLM)。
5
- */
6
-
7
- import type {
8
- ExecutionTrace,
9
- TraceAnomaly,
10
- TraceSummary,
11
- } from '../types/trace';
12
- import type { Diagnosis } from '../types/monitoring-types';
13
- import { DIAGNOSIS_RULES } from './diagnosis-rules';
14
-
15
- // 类型定义已归位 types 层(工单 14),此处再导出保持模块公开面不变
16
- export type { Diagnosis };
17
-
18
- /**
19
- * 诊断配置
20
- */
21
- export interface ConstraintDoctorConfig {
22
- /** 是否启用 Agent 诊断(默认 false,需要显式启用) */
23
- enabled?: boolean;
24
-
25
- /** Agent 类型(预留,后续对接) */
26
- agentType?: 'claude-code' | 'codex' | 'openai';
27
-
28
- /** 最大 traces 数量(用于构造 prompt) */
29
- maxTracesInPrompt?: number;
30
-
31
- /** 是否自动生成提案 */
32
- autoGenerateProposal?: boolean;
33
- }
34
-
35
- /**
36
- * Constraint Doctor - 约束诊断器
37
- *
38
- * 使用方式:
39
- * ```typescript
40
- * const doctor = new ConstraintDoctor(analyzer);
41
- * const diagnosis = await doctor.diagnose(anomaly);
42
- *
43
- * if (diagnosis.needsChange) {
44
- * const proposal = await evolver.propose(diagnosis);
45
- * }
46
- * ```
47
- */
48
- export class ConstraintDoctor {
49
- private config: ConstraintDoctorConfig;
50
- private traces: ExecutionTrace[];
51
-
52
- constructor(config?: ConstraintDoctorConfig) {
53
- this.config = {
54
- enabled: false,
55
- maxTracesInPrompt: 20,
56
- autoGenerateProposal: false,
57
- ...config,
58
- };
59
- this.traces = [];
60
- }
61
-
62
- /**
63
- * 设置诊断数据
64
- */
65
- setData(traces: ExecutionTrace[], _summary?: TraceSummary): void {
66
- this.traces = traces;
67
- }
68
-
69
- /**
70
- * 诊断异常
71
- *
72
- * 如果 Agent 未启用,返回基于规则的诊断
73
- */
74
- async diagnose(anomaly: TraceAnomaly): Promise<Diagnosis> {
75
- // 过滤相关 traces
76
- const relevantTraces = this.filterRelevantTraces(anomaly);
77
-
78
- // 基于规则的诊断(不消耗 Token)
79
- return this.ruleBasedDiagnose(anomaly, relevantTraces);
80
- }
81
-
82
- /**
83
- * 基于规则的诊断(零 Token)
84
- *
85
- * 根据异常类型应用预设的诊断规则
86
- */
87
- private ruleBasedDiagnose(
88
- anomaly: TraceAnomaly,
89
- traces: ExecutionTrace[]
90
- ): Diagnosis {
91
- const diagnosis: Diagnosis = {
92
- anomalyId: `${anomaly.constraintId}-${anomaly.type}-${Date.now()}`,
93
- constraintId: anomaly.constraintId,
94
- diagnosedAt: Date.now(),
95
- rootCause: {
96
- primary: '',
97
- secondary: [],
98
- evidence: traces.slice(0, this.config.maxTracesInPrompt!),
99
- },
100
- impact: {
101
- severity: 'medium',
102
- scope: 'single_project',
103
- userImpact: '',
104
- },
105
- recommendations: [],
106
- needsChange: false,
107
- urgency: 'low',
108
- };
109
-
110
- // 根据异常类型诊断(工单 23-B:规则数据化,见 diagnosis-rules.ts)
111
- const rule = DIAGNOSIS_RULES[anomaly.type];
112
- if (rule) {
113
- diagnosis.rootCause.primary = rule.primary;
114
- diagnosis.rootCause.secondary = rule.secondary;
115
- diagnosis.impact.userImpact = rule.userImpact;
116
- diagnosis.recommendations = rule.recommendations(anomaly);
117
- diagnosis.needsChange = rule.needsChange;
118
- diagnosis.urgency = rule.urgency(anomaly);
119
- }
120
-
121
- // 根据约束层级调整严重性
122
- if (anomaly.level === 'iron_law') {
123
- diagnosis.impact.severity = 'high';
124
- diagnosis.urgency = 'medium'; // Iron law 总是至少 medium
125
- }
126
-
127
- return diagnosis;
128
- }
129
-
130
- /**
131
- * 过滤相关 traces
132
- *
133
- * 只保留与异常相关的 traces,减少 prompt 内容
134
- */
135
- private filterRelevantTraces(anomaly: TraceAnomaly): ExecutionTrace[] {
136
- return this.traces.filter(t => {
137
- // 必须是同一个约束
138
- if (t.constraintId !== anomaly.constraintId) return false;
139
-
140
- // 失败或绕过的更有诊断价值
141
- if (t.result === 'fail' || t.result === 'bypassed') return true;
142
-
143
- // 保留少量通过的作为对比
144
- return true;
145
- });
146
- }
147
-
148
- /**
149
- * 批量诊断多个异常
150
- */
151
- async diagnoseBatch(anomalies: TraceAnomaly[]): Promise<Diagnosis[]> {
152
- return Promise.all(anomalies.map(a => this.diagnose(a)));
153
- }
154
-
155
- /**
156
- * 生成诊断报告(文本格式)
157
- */
158
- generateReport(diagnosis: Diagnosis): string {
159
- const lines: string[] = [];
160
-
161
- lines.push(`# Diagnosis Report`);
162
- lines.push(`Constraint: ${diagnosis.constraintId}`);
163
- lines.push(`Anomaly: ${diagnosis.anomalyId}`);
164
- lines.push(`Diagnosed: ${new Date(diagnosis.diagnosedAt).toISOString()}`);
165
- lines.push('');
166
-
167
- // 根因
168
- lines.push(`## Root Cause`);
169
- lines.push('');
170
- lines.push(`**Primary**: ${diagnosis.rootCause.primary}`);
171
- if (diagnosis.rootCause.secondary?.length) {
172
- lines.push('');
173
- lines.push('**Secondary**:');
174
- for (const s of diagnosis.rootCause.secondary) {
175
- lines.push(`- ${s}`);
176
- }
177
- }
178
- lines.push('');
179
-
180
- // 影响
181
- lines.push(`## Impact`);
182
- lines.push('');
183
- lines.push(`- Severity: ${diagnosis.impact.severity}`);
184
- lines.push(`- Scope: ${diagnosis.impact.scope}`);
185
- lines.push(`- User Impact: ${diagnosis.impact.userImpact}`);
186
- lines.push('');
187
-
188
- // 建议
189
- lines.push(`## Recommendations`);
190
- lines.push('');
191
-
192
- for (const r of diagnosis.recommendations) {
193
- lines.push(`### ${r.type}`);
194
- lines.push(`- Content: ${r.content}`);
195
- lines.push(`- Expected: ${r.expectedOutcome}`);
196
- lines.push(`- Cost: ${r.implementationCost}`);
197
- lines.push('');
198
- }
199
-
200
- // 决策
201
- lines.push(`## Decision`);
202
- lines.push('');
203
- lines.push(`- Needs Change: ${diagnosis.needsChange ? '✅ Yes' : '❌ No'}`);
204
- lines.push(`- Urgency: ${diagnosis.urgency}`);
205
-
206
- return lines.join('\n');
207
- }
208
-
209
- /**
210
- * 保存诊断结果
211
- */
212
- saveDiagnosis(diagnosis: Diagnosis, outputPath: string): void {
213
- const fs = require('fs');
214
- const path = require('path');
215
-
216
- const dir = path.dirname(outputPath);
217
- if (!fs.existsSync(dir)) {
218
- fs.mkdirSync(dir, { recursive: true });
219
- }
220
-
221
- fs.writeFileSync(outputPath, JSON.stringify(diagnosis, null, 2), 'utf-8');
222
- }
223
-
224
- /**
225
- * 加载诊断结果
226
- */
227
- loadDiagnosis(inputPath: string): Diagnosis {
228
- const fs = require('fs');
229
- const content = fs.readFileSync(inputPath, 'utf-8');
230
- return JSON.parse(content) as Diagnosis;
231
- }
232
- }
233
-
234
- /**
235
- * 创建诊断器
236
- */
237
- export function createDoctor(config?: ConstraintDoctorConfig): ConstraintDoctor {
238
- return new ConstraintDoctor(config);
239
- }
@@ -1,132 +0,0 @@
1
- /**
2
- * 诊断规则表(工单 23-B)
3
- *
4
- * constraint-doctor.ruleBasedDiagnose 的巨型 switch 数据化:
5
- * 每种异常类型一条规则(根因/影响/建议/紧急度),新增异常类型只需加表项。
6
- */
7
-
8
- import type { TraceAnomaly } from '../types/trace';
9
- import type { Diagnosis } from '../types/monitoring-types';
10
-
11
- export interface DiagnosisRule {
12
- primary: string;
13
- secondary: string[];
14
- userImpact: string;
15
- recommendations: (anomaly: TraceAnomaly) => Diagnosis['recommendations'];
16
- needsChange: boolean;
17
- urgency: (anomaly: TraceAnomaly) => Diagnosis['urgency'];
18
- }
19
-
20
- export const DIAGNOSIS_RULES: Record<string, DiagnosisRule> = {
21
- high_bypass_rate: {
22
- primary: '约束过于严格,用户频繁绕过',
23
- secondary: [
24
- '约束触发条件过于宽泛',
25
- '缺乏必要的例外条件',
26
- '约束定义与实际场景不匹配',
27
- ],
28
- userImpact: '用户被迫绕过约束,降低信任度',
29
- recommendations: (anomaly) => [
30
- {
31
- type: 'add_exception',
32
- content: `为约束 ${anomaly.constraintId} 添加常见例外条件`,
33
- expectedOutcome: '绕过率降低至 20% 以下',
34
- implementationCost: 'low',
35
- },
36
- {
37
- type: 'adjust_threshold',
38
- content: '调整触发条件,减少误触发',
39
- expectedOutcome: '减少不必要的约束检查',
40
- implementationCost: 'medium',
41
- },
42
- ],
43
- needsChange: true,
44
- urgency: (anomaly) => ((anomaly.data.currentRate as number) > 0.5 ? 'high' : 'medium'),
45
- },
46
-
47
- rising_fail_rate: {
48
- primary: '约束失败率呈上升趋势',
49
- secondary: [
50
- '代码质量下降',
51
- '约束检查逻辑存在 bug',
52
- '环境变化导致约束不再适用',
53
- ],
54
- userImpact: '开发效率下降,约束频繁阻止正常操作',
55
- recommendations: () => [
56
- {
57
- type: 'modify_constraint',
58
- content: '审查约束逻辑,确认是否符合预期',
59
- expectedOutcome: '恢复正常的失败率水平',
60
- implementationCost: 'medium',
61
- },
62
- {
63
- type: 'user_training',
64
- content: '指导用户正确理解约束要求',
65
- expectedOutcome: '减少因误解导致的失败',
66
- implementationCost: 'low',
67
- },
68
- ],
69
- needsChange: true,
70
- urgency: () => 'medium',
71
- },
72
-
73
- rising_bypass_rate: {
74
- primary: '绕过率呈上升趋势',
75
- secondary: [
76
- '用户对约束的接受度下降',
77
- '约束规则逐渐不适应新的开发模式',
78
- '用户发现了绕过约束的"捷径"',
79
- ],
80
- userImpact: '约束逐渐失去约束力',
81
- recommendations: () => [
82
- {
83
- type: 'modify_constraint',
84
- content: '重新评估约束的必要性',
85
- expectedOutcome: '恢复约束的权威性',
86
- implementationCost: 'high',
87
- },
88
- ],
89
- needsChange: true,
90
- urgency: () => 'high',
91
- },
92
-
93
- low_pass_rate: {
94
- primary: '约束通过率过低',
95
- secondary: [
96
- '约束要求过于严格',
97
- '实际代码质量不达标',
98
- '约束定义存在歧义',
99
- ],
100
- userImpact: '几乎所有操作都被阻止,开发受阻',
101
- recommendations: () => [
102
- {
103
- type: 'adjust_threshold',
104
- content: '放宽约束要求,设置合理阈值',
105
- expectedOutcome: '通过率提升至 50% 以上',
106
- implementationCost: 'low',
107
- },
108
- ],
109
- needsChange: true,
110
- urgency: () => 'high',
111
- },
112
-
113
- exception_overuse: {
114
- primary: '例外条件被过度使用',
115
- secondary: [
116
- '例外条件过于宽泛',
117
- '例外条件定义不够精确',
118
- '用户习惯性申请例外',
119
- ],
120
- userImpact: '例外成为常态,约束失去意义',
121
- recommendations: () => [
122
- {
123
- type: 'modify_constraint',
124
- content: '缩小例外条件范围,提高例外门槛',
125
- expectedOutcome: '例外使用率降低至 20% 以下',
126
- implementationCost: 'medium',
127
- },
128
- ],
129
- needsChange: true,
130
- urgency: () => 'medium',
131
- },
132
- };