@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
@@ -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,228 +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
- /** 最大 traces 数量(用于构造诊断证据) */
23
- maxTraces?: number;
24
- }
25
-
26
- /**
27
- * Constraint Doctor - 约束诊断器
28
- *
29
- * 使用方式:
30
- * ```typescript
31
- * const doctor = new ConstraintDoctor(analyzer);
32
- * const diagnosis = await doctor.diagnose(anomaly);
33
- *
34
- * if (diagnosis.needsChange) {
35
- * const proposal = await evolver.propose(diagnosis);
36
- * }
37
- * ```
38
- */
39
- export class ConstraintDoctor {
40
- private config: ConstraintDoctorConfig;
41
- private traces: ExecutionTrace[];
42
-
43
- constructor(config?: ConstraintDoctorConfig) {
44
- this.config = {
45
- maxTraces: 20,
46
- ...config,
47
- };
48
- this.traces = [];
49
- }
50
-
51
- /**
52
- * 设置诊断数据
53
- */
54
- setData(traces: ExecutionTrace[], _summary?: TraceSummary): void {
55
- this.traces = traces;
56
- }
57
-
58
- /**
59
- * 诊断异常
60
- *
61
- * 如果 Agent 未启用,返回基于规则的诊断
62
- */
63
- async diagnose(anomaly: TraceAnomaly): Promise<Diagnosis> {
64
- // 过滤相关 traces
65
- const relevantTraces = this.filterRelevantTraces(anomaly);
66
-
67
- // 基于规则的诊断(不消耗 Token)
68
- return this.ruleBasedDiagnose(anomaly, relevantTraces);
69
- }
70
-
71
- /**
72
- * 基于规则的诊断(零 Token)
73
- *
74
- * 根据异常类型应用预设的诊断规则
75
- */
76
- private ruleBasedDiagnose(
77
- anomaly: TraceAnomaly,
78
- traces: ExecutionTrace[]
79
- ): Diagnosis {
80
- const diagnosis: Diagnosis = {
81
- anomalyId: `${anomaly.constraintId}-${anomaly.type}-${Date.now()}`,
82
- constraintId: anomaly.constraintId,
83
- diagnosedAt: Date.now(),
84
- rootCause: {
85
- primary: '',
86
- secondary: [],
87
- evidence: traces.slice(0, this.config.maxTraces!),
88
- },
89
- impact: {
90
- severity: 'medium',
91
- scope: 'single_project',
92
- userImpact: '',
93
- },
94
- recommendations: [],
95
- needsChange: false,
96
- urgency: 'low',
97
- };
98
-
99
- // 根据异常类型诊断(工单 23-B:规则数据化,见 diagnosis-rules.ts)
100
- const rule = DIAGNOSIS_RULES[anomaly.type];
101
- if (rule) {
102
- diagnosis.rootCause.primary = rule.primary;
103
- diagnosis.rootCause.secondary = rule.secondary;
104
- diagnosis.impact.userImpact = rule.userImpact;
105
- diagnosis.recommendations = rule.recommendations(anomaly);
106
- diagnosis.needsChange = rule.needsChange;
107
- diagnosis.urgency = rule.urgency(anomaly);
108
- }
109
-
110
- // 根据约束层级调整严重性
111
- if (anomaly.level === 'iron_law') {
112
- diagnosis.impact.severity = 'high';
113
- diagnosis.urgency = 'medium'; // Iron law 总是至少 medium
114
- }
115
-
116
- return diagnosis;
117
- }
118
-
119
- /**
120
- * 过滤相关 traces
121
- *
122
- * 只保留与异常相关的 traces,减少 prompt 内容
123
- */
124
- private filterRelevantTraces(anomaly: TraceAnomaly): ExecutionTrace[] {
125
- return this.traces.filter(t => {
126
- // 必须是同一个约束
127
- if (t.constraintId !== anomaly.constraintId) return false;
128
-
129
- // 失败或绕过的更有诊断价值
130
- if (t.result === 'fail' || t.result === 'bypassed') return true;
131
-
132
- // 保留少量通过的作为对比
133
- return true;
134
- });
135
- }
136
-
137
- /**
138
- * 批量诊断多个异常
139
- */
140
- async diagnoseBatch(anomalies: TraceAnomaly[]): Promise<Diagnosis[]> {
141
- return Promise.all(anomalies.map(a => this.diagnose(a)));
142
- }
143
-
144
- /**
145
- * 生成诊断报告(文本格式)
146
- */
147
- generateReport(diagnosis: Diagnosis): string {
148
- const lines: string[] = [];
149
-
150
- lines.push(`# Diagnosis Report`);
151
- lines.push(`Constraint: ${diagnosis.constraintId}`);
152
- lines.push(`Anomaly: ${diagnosis.anomalyId}`);
153
- lines.push(`Diagnosed: ${new Date(diagnosis.diagnosedAt).toISOString()}`);
154
- lines.push('');
155
-
156
- // 根因
157
- lines.push(`## Root Cause`);
158
- lines.push('');
159
- lines.push(`**Primary**: ${diagnosis.rootCause.primary}`);
160
- if (diagnosis.rootCause.secondary?.length) {
161
- lines.push('');
162
- lines.push('**Secondary**:');
163
- for (const s of diagnosis.rootCause.secondary) {
164
- lines.push(`- ${s}`);
165
- }
166
- }
167
- lines.push('');
168
-
169
- // 影响
170
- lines.push(`## Impact`);
171
- lines.push('');
172
- lines.push(`- Severity: ${diagnosis.impact.severity}`);
173
- lines.push(`- Scope: ${diagnosis.impact.scope}`);
174
- lines.push(`- User Impact: ${diagnosis.impact.userImpact}`);
175
- lines.push('');
176
-
177
- // 建议
178
- lines.push(`## Recommendations`);
179
- lines.push('');
180
-
181
- for (const r of diagnosis.recommendations) {
182
- lines.push(`### ${r.type}`);
183
- lines.push(`- Content: ${r.content}`);
184
- lines.push(`- Expected: ${r.expectedOutcome}`);
185
- lines.push(`- Cost: ${r.implementationCost}`);
186
- lines.push('');
187
- }
188
-
189
- // 决策
190
- lines.push(`## Decision`);
191
- lines.push('');
192
- lines.push(`- Needs Change: ${diagnosis.needsChange ? '✅ Yes' : '❌ No'}`);
193
- lines.push(`- Urgency: ${diagnosis.urgency}`);
194
-
195
- return lines.join('\n');
196
- }
197
-
198
- /**
199
- * 保存诊断结果
200
- */
201
- saveDiagnosis(diagnosis: Diagnosis, outputPath: string): void {
202
- const fs = require('fs');
203
- const path = require('path');
204
-
205
- const dir = path.dirname(outputPath);
206
- if (!fs.existsSync(dir)) {
207
- fs.mkdirSync(dir, { recursive: true });
208
- }
209
-
210
- fs.writeFileSync(outputPath, JSON.stringify(diagnosis, null, 2), 'utf-8');
211
- }
212
-
213
- /**
214
- * 加载诊断结果
215
- */
216
- loadDiagnosis(inputPath: string): Diagnosis {
217
- const fs = require('fs');
218
- const content = fs.readFileSync(inputPath, 'utf-8');
219
- return JSON.parse(content) as Diagnosis;
220
- }
221
- }
222
-
223
- /**
224
- * 创建诊断器
225
- */
226
- export function createDoctor(config?: ConstraintDoctorConfig): ConstraintDoctor {
227
- return new ConstraintDoctor(config);
228
- }
@@ -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
- };