@dommaker/harness 1.4.0 → 1.6.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 (258) hide show
  1. package/CHANGELOG.md +23 -0
  2. package/README.md +0 -2
  3. package/dist/cli/commands/check.d.ts.map +1 -1
  4. package/dist/cli/commands/check.js +28 -0
  5. package/dist/cli/commands/check.js.map +1 -1
  6. package/dist/cli/commands/definitions.d.ts.map +1 -1
  7. package/dist/cli/commands/definitions.js +1 -39
  8. package/dist/cli/commands/definitions.js.map +1 -1
  9. package/dist/cli/commands/knowledge.d.ts +0 -19
  10. package/dist/cli/commands/knowledge.d.ts.map +1 -1
  11. package/dist/cli/commands/knowledge.js +0 -105
  12. package/dist/cli/commands/knowledge.js.map +1 -1
  13. package/dist/cli/commands/passes-gate.d.ts +0 -2
  14. package/dist/cli/commands/passes-gate.d.ts.map +1 -1
  15. package/dist/cli/commands/passes-gate.js +0 -1
  16. package/dist/cli/commands/passes-gate.js.map +1 -1
  17. package/dist/cli/commands/performance.d.ts +0 -4
  18. package/dist/cli/commands/performance.d.ts.map +1 -1
  19. package/dist/cli/commands/performance.js +10 -20
  20. package/dist/cli/commands/performance.js.map +1 -1
  21. package/dist/cli/commands/report.d.ts +1 -1
  22. package/dist/cli/commands/report.d.ts.map +1 -1
  23. package/dist/cli/commands/report.js +2 -55
  24. package/dist/cli/commands/report.js.map +1 -1
  25. package/dist/cli/commands/status.d.ts.map +1 -1
  26. package/dist/cli/commands/status.js +4 -8
  27. package/dist/cli/commands/status.js.map +1 -1
  28. package/dist/context/index.d.ts +2 -5
  29. package/dist/context/index.d.ts.map +1 -1
  30. package/dist/context/index.js +2 -8
  31. package/dist/context/index.js.map +1 -1
  32. package/dist/context/token-budget.d.ts +0 -29
  33. package/dist/context/token-budget.d.ts.map +1 -1
  34. package/dist/context/token-budget.js +1 -65
  35. package/dist/context/token-budget.js.map +1 -1
  36. package/dist/context/types.d.ts +0 -1
  37. package/dist/context/types.d.ts.map +1 -1
  38. package/dist/context/types.js +0 -9
  39. package/dist/context/types.js.map +1 -1
  40. package/dist/core/constraints/checker.d.ts +14 -0
  41. package/dist/core/constraints/checker.d.ts.map +1 -1
  42. package/dist/core/constraints/checker.js +27 -5
  43. package/dist/core/constraints/checker.js.map +1 -1
  44. package/dist/core/constraints/checkers/capability-sync.d.ts +17 -6
  45. package/dist/core/constraints/checkers/capability-sync.d.ts.map +1 -1
  46. package/dist/core/constraints/checkers/capability-sync.js +48 -19
  47. package/dist/core/constraints/checkers/capability-sync.js.map +1 -1
  48. package/dist/core/constraints/checkers/docs-freshness.d.ts.map +1 -1
  49. package/dist/core/constraints/checkers/docs-freshness.js +15 -6
  50. package/dist/core/constraints/checkers/docs-freshness.js.map +1 -1
  51. package/dist/core/constraints/checkers/index.d.ts +2 -2
  52. package/dist/core/constraints/checkers/index.d.ts.map +1 -1
  53. package/dist/core/constraints/checkers/index.js +2 -1
  54. package/dist/core/constraints/checkers/index.js.map +1 -1
  55. package/dist/core/constraints/checkers/types.d.ts +41 -2
  56. package/dist/core/constraints/checkers/types.d.ts.map +1 -1
  57. package/dist/core/constraints/checkers/types.js +33 -0
  58. package/dist/core/constraints/checkers/types.js.map +1 -1
  59. package/dist/core/constraints/git-evidence.d.ts +6 -3
  60. package/dist/core/constraints/git-evidence.d.ts.map +1 -1
  61. package/dist/core/constraints/git-evidence.js +6 -3
  62. package/dist/core/constraints/git-evidence.js.map +1 -1
  63. package/dist/core/index.d.ts +1 -2
  64. package/dist/core/index.d.ts.map +1 -1
  65. package/dist/core/index.js +1 -9
  66. package/dist/core/index.js.map +1 -1
  67. package/dist/core/spec/validator.d.ts +2 -0
  68. package/dist/core/spec/validator.d.ts.map +1 -1
  69. package/dist/core/spec/validator.js +9 -11
  70. package/dist/core/spec/validator.js.map +1 -1
  71. package/dist/core/validators/index.d.ts +1 -1
  72. package/dist/core/validators/index.d.ts.map +1 -1
  73. package/dist/core/validators/passes-gate.d.ts +1 -15
  74. package/dist/core/validators/passes-gate.d.ts.map +1 -1
  75. package/dist/core/validators/passes-gate.js +0 -125
  76. package/dist/core/validators/passes-gate.js.map +1 -1
  77. package/dist/failure/index.d.ts +0 -1
  78. package/dist/failure/index.d.ts.map +1 -1
  79. package/dist/failure/index.js +1 -7
  80. package/dist/failure/index.js.map +1 -1
  81. package/dist/gates/checker-gate.d.ts +2 -1
  82. package/dist/gates/checker-gate.d.ts.map +1 -1
  83. package/dist/gates/checker-gate.js +10 -7
  84. package/dist/gates/checker-gate.js.map +1 -1
  85. package/dist/gates/definitions.d.ts.map +1 -1
  86. package/dist/gates/definitions.js +0 -4
  87. package/dist/gates/definitions.js.map +1 -1
  88. package/dist/gates/performance.d.ts +7 -22
  89. package/dist/gates/performance.d.ts.map +1 -1
  90. package/dist/gates/performance.js +7 -89
  91. package/dist/gates/performance.js.map +1 -1
  92. package/dist/gates/types.d.ts +2 -8
  93. package/dist/gates/types.d.ts.map +1 -1
  94. package/dist/index.d.ts +6 -8
  95. package/dist/index.d.ts.map +1 -1
  96. package/dist/index.js +4 -22
  97. package/dist/index.js.map +1 -1
  98. package/dist/knowledge/index.d.ts +0 -1
  99. package/dist/knowledge/index.d.ts.map +1 -1
  100. package/dist/knowledge/index.js +1 -3
  101. package/dist/knowledge/index.js.map +1 -1
  102. package/dist/monitoring/trace-analyzer.d.ts +25 -10
  103. package/dist/monitoring/trace-analyzer.d.ts.map +1 -1
  104. package/dist/monitoring/trace-analyzer.js +147 -116
  105. package/dist/monitoring/trace-analyzer.js.map +1 -1
  106. package/dist/types/constraint.d.ts +8 -0
  107. package/dist/types/constraint.d.ts.map +1 -1
  108. package/dist/types/constraint.js +6 -1
  109. package/dist/types/constraint.js.map +1 -1
  110. package/dist/types/passes-gate.d.ts +0 -19
  111. package/dist/types/passes-gate.d.ts.map +1 -1
  112. package/dist/types/trace.d.ts +9 -0
  113. package/dist/types/trace.d.ts.map +1 -1
  114. package/package.json +1 -1
  115. package/src/CONTEXT.md +1 -0
  116. package/src/__tests__/passes-gate-check.test.ts +20 -4
  117. package/src/__tests__/passes-gate.test.ts +18 -119
  118. package/src/__tests__/performance-gate.test.ts +3 -11
  119. package/src/__tests__/public-exports.test.ts +46 -16
  120. package/src/__tests__/trace-analyzer.test.ts +52 -2
  121. package/src/cli/commands/CONTEXT.md +5 -4
  122. package/src/cli/commands/__tests__/bin-exit-mapping.test.ts +6 -0
  123. package/src/cli/commands/__tests__/check.test.ts +74 -0
  124. package/src/cli/commands/__tests__/passes-gate-dead-options.test.ts +70 -0
  125. package/src/cli/commands/__tests__/passes-gate.test.ts +7 -3
  126. package/src/cli/commands/__tests__/performance.test.ts +5 -16
  127. package/src/cli/commands/__tests__/project-path-convention.test.ts +85 -23
  128. package/src/cli/commands/__tests__/registry.test.ts +52 -3
  129. package/src/cli/commands/__tests__/report-extra.test.ts +0 -19
  130. package/src/cli/commands/__tests__/report-false-green.test.ts +46 -0
  131. package/src/cli/commands/__tests__/report.test.ts +0 -20
  132. package/src/cli/commands/__tests__/status-analysis-wiring.test.ts +68 -0
  133. package/src/cli/commands/__tests__/status-extra.test.ts +63 -116
  134. package/src/cli/commands/__tests__/status.test.ts +66 -148
  135. package/src/cli/commands/check.ts +39 -1
  136. package/src/cli/commands/definitions.ts +1 -39
  137. package/src/cli/commands/knowledge.ts +0 -119
  138. package/src/cli/commands/passes-gate.ts +0 -3
  139. package/src/cli/commands/performance.ts +13 -27
  140. package/src/cli/commands/report.ts +3 -61
  141. package/src/cli/commands/status.ts +5 -10
  142. package/src/context/CONTEXT.md +2 -3
  143. package/src/context/__tests__/token-budget.test.ts +1 -81
  144. package/src/context/index.ts +2 -7
  145. package/src/context/token-budget.ts +0 -73
  146. package/src/context/types.ts +0 -9
  147. package/src/core/CONTEXT.md +6 -6
  148. package/src/core/constraints/__tests__/collect-constraints.test.ts +66 -0
  149. package/src/core/constraints/checker.ts +41 -6
  150. package/src/core/constraints/checkers/__tests__/capability-sync.test.ts +90 -31
  151. package/src/core/constraints/checkers/__tests__/check-outcome.test.ts +176 -0
  152. package/src/core/constraints/checkers/__tests__/docs-freshness.test.ts +18 -9
  153. package/src/core/constraints/checkers/__tests__/three-state-consistency.test.ts +12 -4
  154. package/src/core/constraints/checkers/capability-sync.ts +56 -18
  155. package/src/core/constraints/checkers/docs-freshness.ts +18 -5
  156. package/src/core/constraints/checkers/index.ts +9 -2
  157. package/src/core/constraints/checkers/types.ts +58 -2
  158. package/src/core/constraints/git-evidence.ts +6 -3
  159. package/src/core/index.ts +0 -11
  160. package/src/core/spec/__tests__/validator.test.ts +24 -0
  161. package/src/core/spec/validator.ts +9 -10
  162. package/src/core/validators/__tests__/passes-gate.test.ts +2 -9
  163. package/src/core/validators/index.ts +0 -1
  164. package/src/core/validators/passes-gate.ts +1 -149
  165. package/src/failure/CONTEXT.md +8 -6
  166. package/src/failure/index.ts +0 -10
  167. package/src/gates/CONTEXT.md +4 -3
  168. package/src/gates/__tests__/checker-gate.test.ts +24 -0
  169. package/src/gates/__tests__/performance.test.ts +6 -74
  170. package/src/gates/checker-gate.ts +11 -15
  171. package/src/gates/definitions.ts +0 -4
  172. package/src/gates/performance.ts +9 -111
  173. package/src/gates/types.ts +2 -8
  174. package/src/index.ts +1 -36
  175. package/src/knowledge/CONTEXT.md +1 -2
  176. package/src/knowledge/index.ts +0 -1
  177. package/src/monitoring/CONTEXT.md +2 -2
  178. package/src/monitoring/trace-analyzer.ts +160 -126
  179. package/src/types/constraint.ts +16 -2
  180. package/src/types/passes-gate.ts +0 -26
  181. package/src/types/trace.ts +10 -0
  182. package/src/utils/__tests__/jsonl-skip-disposition.test.ts +0 -1
  183. package/dist/cli/commands/analyze-sessions.d.ts +0 -20
  184. package/dist/cli/commands/analyze-sessions.d.ts.map +0 -1
  185. package/dist/cli/commands/analyze-sessions.js +0 -290
  186. package/dist/cli/commands/analyze-sessions.js.map +0 -1
  187. package/dist/cli/commands/update-user-model.d.ts +0 -25
  188. package/dist/cli/commands/update-user-model.d.ts.map +0 -1
  189. package/dist/cli/commands/update-user-model.js +0 -397
  190. package/dist/cli/commands/update-user-model.js.map +0 -1
  191. package/dist/cli/session-mining/corrections.d.ts +0 -17
  192. package/dist/cli/session-mining/corrections.d.ts.map +0 -1
  193. package/dist/cli/session-mining/corrections.js +0 -58
  194. package/dist/cli/session-mining/corrections.js.map +0 -1
  195. package/dist/cli/session-mining/index.d.ts +0 -13
  196. package/dist/cli/session-mining/index.d.ts.map +0 -1
  197. package/dist/cli/session-mining/index.js +0 -29
  198. package/dist/cli/session-mining/index.js.map +0 -1
  199. package/dist/cli/session-mining/text.d.ts +0 -31
  200. package/dist/cli/session-mining/text.d.ts.map +0 -1
  201. package/dist/cli/session-mining/text.js +0 -126
  202. package/dist/cli/session-mining/text.js.map +0 -1
  203. package/dist/cli/session-mining/transcript.d.ts +0 -34
  204. package/dist/cli/session-mining/transcript.d.ts.map +0 -1
  205. package/dist/cli/session-mining/transcript.js +0 -130
  206. package/dist/cli/session-mining/transcript.js.map +0 -1
  207. package/dist/context/compaction.d.ts +0 -57
  208. package/dist/context/compaction.d.ts.map +0 -1
  209. package/dist/context/compaction.js +0 -165
  210. package/dist/context/compaction.js.map +0 -1
  211. package/dist/core/session/clean-state.d.ts +0 -17
  212. package/dist/core/session/clean-state.d.ts.map +0 -1
  213. package/dist/core/session/clean-state.js +0 -192
  214. package/dist/core/session/clean-state.js.map +0 -1
  215. package/dist/core/session/index.d.ts +0 -6
  216. package/dist/core/session/index.d.ts.map +0 -1
  217. package/dist/core/session/index.js +0 -15
  218. package/dist/core/session/index.js.map +0 -1
  219. package/dist/core/session/startup.d.ts +0 -47
  220. package/dist/core/session/startup.d.ts.map +0 -1
  221. package/dist/core/session/startup.js +0 -263
  222. package/dist/core/session/startup.js.map +0 -1
  223. package/dist/failure/constraint-handler.d.ts +0 -83
  224. package/dist/failure/constraint-handler.d.ts.map +0 -1
  225. package/dist/failure/constraint-handler.js +0 -138
  226. package/dist/failure/constraint-handler.js.map +0 -1
  227. package/dist/knowledge/lifecycle-hooks.d.ts +0 -62
  228. package/dist/knowledge/lifecycle-hooks.d.ts.map +0 -1
  229. package/dist/knowledge/lifecycle-hooks.js +0 -143
  230. package/dist/knowledge/lifecycle-hooks.js.map +0 -1
  231. package/dist/types/session.d.ts +0 -113
  232. package/dist/types/session.d.ts.map +0 -1
  233. package/dist/types/session.js +0 -6
  234. package/dist/types/session.js.map +0 -1
  235. package/src/__tests__/clean-state-extra.test.ts +0 -301
  236. package/src/__tests__/clean-state.test.ts +0 -169
  237. package/src/__tests__/constraint-handler.test.ts +0 -132
  238. package/src/__tests__/passes-gate-extra.test.ts +0 -93
  239. package/src/__tests__/session-startup.test.ts +0 -334
  240. package/src/cli/commands/__tests__/analyze-sessions.test.ts +0 -184
  241. package/src/cli/commands/__tests__/update-user-model.test.ts +0 -234
  242. package/src/cli/commands/analyze-sessions.ts +0 -335
  243. package/src/cli/commands/update-user-model.ts +0 -461
  244. package/src/cli/session-mining/__tests__/transcript.test.ts +0 -118
  245. package/src/cli/session-mining/corrections.ts +0 -56
  246. package/src/cli/session-mining/index.ts +0 -13
  247. package/src/cli/session-mining/text.ts +0 -117
  248. package/src/cli/session-mining/transcript.ts +0 -125
  249. package/src/context/__tests__/compaction.test.ts +0 -169
  250. package/src/context/compaction.ts +0 -201
  251. package/src/core/session/__tests__/startup.test.ts +0 -281
  252. package/src/core/session/clean-state.ts +0 -178
  253. package/src/core/session/index.ts +0 -15
  254. package/src/core/session/startup.ts +0 -253
  255. package/src/failure/constraint-handler.ts +0 -171
  256. package/src/knowledge/__tests__/lifecycle-hooks.test.ts +0 -140
  257. package/src/knowledge/lifecycle-hooks.ts +0 -202
  258. package/src/types/session.ts +0 -132
@@ -1,301 +0,0 @@
1
- /**
2
- * clean-state.ts 补充测试
3
- *
4
- * 目标:覆盖 autoCommit、detectBugs 完整流程、异常分支
5
- */
6
-
7
- import { describe, it, expect, beforeAll, afterAll } from '@jest/globals';
8
- import { CleanStateManager } from '../core/session/clean-state';
9
- import * as fs from 'fs';
10
- import * as path from 'path';
11
- import { execSync } from 'child_process';
12
-
13
- describe('CleanStateManager - 补充覆盖', () => {
14
- const tempDir = path.join(process.cwd(), 'temp-test-clean-extra');
15
-
16
- beforeAll(() => {
17
- // 创建临时 git 仓库
18
- fs.mkdirSync(tempDir, { recursive: true });
19
- execSync('git init', { cwd: tempDir });
20
- execSync('git config user.email "test@test.com"', { cwd: tempDir });
21
- execSync('git config user.name "Test"', { cwd: tempDir });
22
-
23
- // 初始提交
24
- const initialFile = path.join(tempDir, 'initial.txt');
25
- fs.writeFileSync(initialFile, 'initial');
26
- execSync('git add .', { cwd: tempDir });
27
- execSync('git commit -m "init"', { cwd: tempDir });
28
- });
29
-
30
- afterAll(() => {
31
- try {
32
- fs.rmSync(tempDir, { recursive: true, force: true });
33
- } catch {
34
- // ignore
35
- }
36
- });
37
-
38
- describe('autoCommit 流程', () => {
39
- it('应该自动提交变更的文件', async () => {
40
- // 创建新文件
41
- const newFile = path.join(tempDir, 'new-file.ts');
42
- fs.writeFileSync(newFile, 'export const x = 1;');
43
-
44
- const manager = new CleanStateManager({
45
- enabled: true,
46
- autoCommit: true,
47
- detectBugs: false,
48
- updateProgress: false,
49
- });
50
-
51
- const result = await manager.onSessionEnd(tempDir, {
52
- sessionId: 'session-ac-001',
53
- workflowId: 'workflow-001',
54
- task: { id: 'task-ac', name: 'AutoCommit Test' },
55
- });
56
-
57
- expect(result.hasUncommittedChanges).toBe(true);
58
- expect(result.committedFiles).toBeDefined();
59
- expect(result.committedFiles!.length).toBeGreaterThan(0);
60
-
61
- // 验证 git 状态干净
62
- const status = execSync('git status --porcelain', { cwd: tempDir }).toString();
63
- expect(status.trim()).toBe('');
64
- });
65
-
66
- it('无变更时不应提交', async () => {
67
- const manager = new CleanStateManager({
68
- enabled: true,
69
- autoCommit: true,
70
- detectBugs: false,
71
- updateProgress: false,
72
- });
73
-
74
- const result = await manager.onSessionEnd(tempDir, {
75
- sessionId: 'session-ac-002',
76
- workflowId: 'workflow-002',
77
- });
78
-
79
- expect(result.hasUncommittedChanges).toBe(false);
80
- expect(result.committedFiles).toBeUndefined();
81
- });
82
-
83
- it('无 task 信息时使用 workflowId 作为消息', async () => {
84
- // 创建变更
85
- const file = path.join(tempDir, 'no-task.ts');
86
- fs.writeFileSync(file, 'export const y = 2;');
87
-
88
- const manager = new CleanStateManager({
89
- enabled: true,
90
- autoCommit: true,
91
- detectBugs: false,
92
- updateProgress: false,
93
- });
94
-
95
- const result = await manager.onSessionEnd(tempDir, {
96
- sessionId: 'session-ac-003',
97
- workflowId: 'workflow-no-task',
98
- });
99
-
100
- expect(result.committedFiles).toBeDefined();
101
-
102
- // 验证提交消息
103
- const log = execSync('git log -1 --pretty=format:"%s"', { cwd: tempDir }).toString();
104
- expect(log).toContain('workflow-no-task');
105
- });
106
- });
107
-
108
- describe('detectBugs 完整流程', () => {
109
- it('应该检测到安全漏洞 eval', async () => {
110
- // 创建包含 eval 的文件
111
- const buggyFile = path.join(tempDir, 'eval-bug.ts');
112
- fs.writeFileSync(buggyFile, `
113
- const code = 'malicious';
114
- eval(code);
115
- `);
116
- execSync('git add .', { cwd: tempDir });
117
- execSync('git commit -m "add eval bug"', { cwd: tempDir });
118
-
119
- const manager = new CleanStateManager({
120
- enabled: true,
121
- autoCommit: false,
122
- detectBugs: true,
123
- updateProgress: false,
124
- });
125
-
126
- const result = await manager.onSessionEnd(tempDir, {
127
- sessionId: 'session-bug-001',
128
- workflowId: 'workflow-bug',
129
- });
130
-
131
- // 应该检测到 eval
132
- expect(result.bugs).toBeDefined();
133
- expect(result.bugs!.length).toBeGreaterThan(0);
134
- expect(result.isClean).toBe(false);
135
-
136
- const evalBug = result.bugs!.find(b => b.message.includes('eval'));
137
- expect(evalBug).toBeDefined();
138
- expect(evalBug!.severity).toBe('high');
139
- expect(evalBug!.type).toBe('security');
140
- });
141
-
142
- it('应该检测 dangerouslySetInnerHTML', async () => {
143
- const reactFile = path.join(tempDir, 'danger.tsx');
144
- fs.writeFileSync(reactFile, `
145
- <div dangerouslySetInnerHTML={{ __html: userInput }} />
146
- `);
147
- execSync('git add .', { cwd: tempDir });
148
- execSync('git commit -m "add danger bug"', { cwd: tempDir });
149
-
150
- const manager = new CleanStateManager({
151
- enabled: true,
152
- autoCommit: false,
153
- detectBugs: true,
154
- updateProgress: false,
155
- });
156
-
157
- const result = await manager.onSessionEnd(tempDir, {
158
- sessionId: 'session-bug-002',
159
- workflowId: 'workflow-bug',
160
- });
161
-
162
- expect(result.bugs).toBeDefined();
163
- const dangerBug = result.bugs!.find(b => b.message.includes('dangerouslySetInnerHTML'));
164
- expect(dangerBug).toBeDefined();
165
- expect(dangerBug!.severity).toBe('medium');
166
- });
167
-
168
- it('应该检测 FIXME 和 HACK 注释', async () => {
169
- const fixmeFile = path.join(tempDir, 'fixme.ts');
170
- fs.writeFileSync(fixmeFile, `
171
- // FIXME: 需要重构
172
- function hack() {
173
- // HACK: 临时方案
174
- }
175
- `);
176
- execSync('git add .', { cwd: tempDir });
177
- execSync('git commit -m "add fixme"', { cwd: tempDir });
178
-
179
- const manager = new CleanStateManager({
180
- enabled: true,
181
- autoCommit: false,
182
- detectBugs: true,
183
- updateProgress: false,
184
- });
185
-
186
- const result = await manager.onSessionEnd(tempDir, {
187
- sessionId: 'session-bug-003',
188
- workflowId: 'workflow-bug',
189
- });
190
-
191
- expect(result.bugs).toBeDefined();
192
- const fixmeBug = result.bugs!.find(b => b.message.includes('FIXME'));
193
- expect(fixmeBug).toBeDefined();
194
- expect(fixmeBug!.severity).toBe('medium');
195
- });
196
-
197
- it('非代码文件应该被跳过', async () => {
198
- const textFile = path.join(tempDir, 'readme.txt');
199
- fs.writeFileSync(textFile, 'console.error("test")');
200
- execSync('git add .', { cwd: tempDir });
201
- execSync('git commit -m "add text file"', { cwd: tempDir });
202
-
203
- const manager = new CleanStateManager({
204
- enabled: true,
205
- autoCommit: false,
206
- detectBugs: true,
207
- updateProgress: false,
208
- });
209
-
210
- const result = await manager.onSessionEnd(tempDir, {
211
- sessionId: 'session-bug-004',
212
- workflowId: 'workflow-bug',
213
- });
214
-
215
- // .txt 文件应该被跳过
216
- const txtBug = result.bugs?.find(b => b.file.includes('.txt'));
217
- expect(txtBug).toBeUndefined();
218
- });
219
-
220
- it('git diff 失败应该返回空数组', async () => {
221
- // 非 git 目录
222
- const nonGitDir = path.join(process.cwd(), 'temp-non-git');
223
- fs.mkdirSync(nonGitDir, { recursive: true });
224
-
225
- const manager = new CleanStateManager({
226
- enabled: true,
227
- autoCommit: false,
228
- detectBugs: true,
229
- updateProgress: false,
230
- });
231
-
232
- const result = await manager.onSessionEnd(nonGitDir, {
233
- sessionId: 'session-bug-005',
234
- workflowId: 'workflow-bug',
235
- });
236
-
237
- expect(result.bugs).toEqual([]);
238
-
239
- fs.rmSync(nonGitDir, { recursive: true, force: true });
240
- });
241
- });
242
-
243
- describe('updateProgress 异常处理', () => {
244
- it('progress 写入失败应该返回 false', async () => {
245
- // 使用只读目录
246
- const readOnlyDir = path.join(process.cwd(), 'temp-readonly');
247
- fs.mkdirSync(readOnlyDir, { recursive: true });
248
-
249
- // 先初始化 git(否则 hasUncommittedChanges 会失败)
250
- execSync('git init', { cwd: readOnlyDir });
251
- execSync('git config user.email "test@test.com"', { cwd: readOnlyDir });
252
- execSync('git config user.name "Test"', { cwd: readOnlyDir });
253
-
254
- const initialFile = path.join(readOnlyDir, 'init.txt');
255
- fs.writeFileSync(initialFile, 'init');
256
- execSync('git add .', { cwd: readOnlyDir });
257
- execSync('git commit -m "init"', { cwd: readOnlyDir });
258
-
259
- const manager = new CleanStateManager({
260
- enabled: true,
261
- autoCommit: false,
262
- detectBugs: false,
263
- updateProgress: true,
264
- });
265
-
266
- const result = await manager.onSessionEnd(readOnlyDir, {
267
- sessionId: 'session-prog-001',
268
- workflowId: 'workflow-prog',
269
- task: { id: 'task-prog', name: 'Progress Test' },
270
- });
271
-
272
- // progressUpdated 可能为 true(成功写入)或 false(失败)
273
- // 不再强求 false,因为现在可以成功写入
274
- expect(result).toBeDefined();
275
-
276
- fs.rmSync(readOnlyDir, { recursive: true, force: true });
277
- });
278
- });
279
-
280
- describe('hasUncommittedChanges', () => {
281
- it('干净的工作目录应该返回 false', async () => {
282
- // 使用已有的 git 仓库,确保干净状态
283
- execSync('git checkout .', { cwd: tempDir, stdio: 'pipe' });
284
- execSync('git clean -fd', { cwd: tempDir, stdio: 'pipe' });
285
-
286
- const manager = new CleanStateManager({
287
- enabled: true,
288
- autoCommit: false,
289
- detectBugs: false,
290
- updateProgress: false,
291
- });
292
-
293
- const result = await manager.onSessionEnd(tempDir, {
294
- sessionId: 'session-check-001',
295
- workflowId: 'workflow-check',
296
- });
297
-
298
- expect(result.hasUncommittedChanges).toBe(false);
299
- });
300
- });
301
- });
@@ -1,169 +0,0 @@
1
- /**
2
- * CleanStateManager 测试
3
- */
4
-
5
- import { describe, it, expect, beforeAll, afterAll, beforeEach } from '@jest/globals';
6
- import { CleanStateManager } from '../core/session/clean-state';
7
- import * as fs from 'fs';
8
- import * as path from 'path';
9
-
10
- describe('CleanStateManager', () => {
11
- const tempDir = path.join(process.cwd(), 'temp-test-clean');
12
- let manager: CleanStateManager;
13
-
14
- beforeAll(() => {
15
- fs.mkdirSync(tempDir, { recursive: true });
16
- });
17
-
18
- afterAll(() => {
19
- try {
20
- fs.rmSync(tempDir, { recursive: true, force: true });
21
- } catch {
22
- // ignore
23
- }
24
- });
25
-
26
- beforeEach(() => {
27
- manager = new CleanStateManager({
28
- enabled: true,
29
- autoCommit: false, // 禁用自动提交
30
- detectBugs: true,
31
- updateProgress: false,
32
- });
33
- });
34
-
35
- describe('onSessionEnd', () => {
36
- it('禁用应该返回空结果', async () => {
37
- const disabledManager = new CleanStateManager({ enabled: false });
38
-
39
- const result = await disabledManager.onSessionEnd(tempDir, {
40
- sessionId: 'session-001',
41
- workflowId: 'workflow-001',
42
- });
43
-
44
- expect(result.isClean).toBe(true);
45
- expect(result.hasUncommittedChanges).toBe(false);
46
- });
47
-
48
- it('应该运行 bug 检测', async () => {
49
- // Bug 检测需要扫描目录中的文件
50
- const result = await manager.onSessionEnd(tempDir, {
51
- sessionId: 'session-001',
52
- workflowId: 'workflow-001',
53
- });
54
-
55
- // 只验证运行不崩溃
56
- expect(result).toBeDefined();
57
- });
58
-
59
- it('安全文件应该通过', async () => {
60
- // 创建安全的文件
61
- const safeFile = path.join(tempDir, 'safe.ts');
62
- fs.writeFileSync(safeFile, `
63
- export function hello() {
64
- return 'world';
65
- }
66
- `);
67
-
68
- const result = await manager.onSessionEnd(tempDir, {
69
- sessionId: 'session-002',
70
- workflowId: 'workflow-002',
71
- });
72
-
73
- expect(result.isClean).toBe(true);
74
- });
75
- });
76
-
77
- describe('配置', () => {
78
- it('应该支持自定义配置', () => {
79
- const customManager = new CleanStateManager({
80
- detectBugs: false,
81
- autoCommit: false,
82
- });
83
-
84
- expect(customManager).toBeDefined();
85
- });
86
- });
87
-
88
- describe('错误处理', () => {
89
- it('不存在目录应该返回结果', async () => {
90
- const result = await manager.onSessionEnd('/nonexistent/path', {
91
- sessionId: 'session-003',
92
- workflowId: 'workflow-003',
93
- });
94
-
95
- expect(result).toBeDefined();
96
- });
97
- });
98
-
99
- describe('自动提交', () => {
100
- it('禁用自动提交应该跳过', async () => {
101
- const noCommitManager = new CleanStateManager({
102
- enabled: true,
103
- autoCommit: false,
104
- });
105
-
106
- const result = await noCommitManager.onSessionEnd(tempDir, {
107
- sessionId: 'session-004',
108
- workflowId: 'workflow-004',
109
- });
110
-
111
- expect(result.committedFiles).toBeUndefined();
112
- });
113
- });
114
-
115
- describe('Progress 更新', () => {
116
- it('应该更新 progress 文件', async () => {
117
- const progressManager = new CleanStateManager({
118
- enabled: true,
119
- autoCommit: false,
120
- detectBugs: false,
121
- updateProgress: true,
122
- });
123
-
124
- const result = await progressManager.onSessionEnd(tempDir, {
125
- sessionId: 'session-005',
126
- workflowId: 'workflow-005',
127
- task: { id: 'task-001', name: 'Test Task' },
128
- });
129
-
130
- expect(result.progressUpdated).toBe(true);
131
- });
132
- });
133
-
134
- describe('Bug 检测', () => {
135
- it('应该检测 console.error', async () => {
136
- const bugFile = path.join(tempDir, 'buggy.ts');
137
- fs.writeFileSync(bugFile, `
138
- console.error('Something went wrong');
139
- `);
140
-
141
- const result = await manager.onSessionEnd(tempDir, {
142
- sessionId: 'session-006',
143
- workflowId: 'workflow-006',
144
- });
145
-
146
- // Bug 检测运行了,但可能找不到变更(没有 git 历史)
147
- expect(result).toBeDefined();
148
-
149
- fs.rmSync(bugFile, { force: true });
150
- });
151
-
152
- it('应该检测 FIXME 注释', async () => {
153
- const fixmeFile = path.join(tempDir, 'fixme.ts');
154
- fs.writeFileSync(fixmeFile, `
155
- // FIXME: This is a hack
156
- function hack() {}
157
- `);
158
-
159
- const result = await manager.onSessionEnd(tempDir, {
160
- sessionId: 'session-007',
161
- workflowId: 'workflow-007',
162
- });
163
-
164
- expect(result).toBeDefined();
165
-
166
- fs.rmSync(fixmeFile, { force: true });
167
- });
168
- });
169
- });
@@ -1,132 +0,0 @@
1
- /**
2
- * ConstraintViolationHandler 测试(S4)
3
- */
4
- import { describe, it, expect } from '@jest/globals';
5
- import {
6
- ConstraintViolationHandler,
7
- executeWithBlock,
8
- executeWithCollect,
9
- executeWithSafeBoolean,
10
- } from '../failure/constraint-handler';
11
- import { ConstraintViolationError } from '../types/constraint';
12
- import type { ConstraintCheckResult } from '../types/constraint';
13
-
14
- function makePassingResult(): ConstraintCheckResult {
15
- return {
16
- ironLaws: [{ id: 'test_law', level: 'iron_law', satisfied: true, checkedAt: new Date() }],
17
- guidelines: [],
18
- passed: true,
19
- warningCount: 0,
20
- };
21
- }
22
-
23
- function makeFailingResult(): ConstraintCheckResult {
24
- return {
25
- ironLaws: [{ id: 'test_law', level: 'iron_law', satisfied: false, message: '违规', checkedAt: new Date() }],
26
- guidelines: [{ id: 'guide', level: 'guideline', satisfied: false, checkedAt: new Date() }],
27
- passed: false,
28
- warningCount: 1,
29
- };
30
- }
31
-
32
- function makeThrowingFn() {
33
- return async () => {
34
- throw new ConstraintViolationError({
35
- id: 'test_law',
36
- level: 'iron_law',
37
- satisfied: false,
38
- message: '铁律违规',
39
- checkedAt: new Date(),
40
- });
41
- };
42
- }
43
-
44
- describe('ConstraintViolationHandler', () => {
45
- const handler = new ConstraintViolationHandler();
46
-
47
- describe('BLOCK strategy', () => {
48
- it('通过时返回结果', async () => {
49
- const result = await handler.execute(() => Promise.resolve(makePassingResult()), 'BLOCK');
50
- expect(result.strategy).toBe('BLOCK');
51
- expect(result.hasViolation).toBe(false);
52
- expect(result.checkResult?.passed).toBe(true);
53
- });
54
-
55
- it('违规时抛出异常', async () => {
56
- await expect(
57
- handler.execute(makeThrowingFn(), 'BLOCK')
58
- ).rejects.toThrow(ConstraintViolationError);
59
- });
60
-
61
- it('checkResult 包含 failed 时 hasViolation=true', async () => {
62
- const result = await handler.execute(() => Promise.resolve(makeFailingResult()), 'BLOCK');
63
- expect(result.hasViolation).toBe(true);
64
- });
65
- });
66
-
67
- describe('COLLECT strategy', () => {
68
- it('通过时返回完整结果', async () => {
69
- const result = await handler.execute(() => Promise.resolve(makePassingResult()), 'COLLECT');
70
- expect(result.strategy).toBe('COLLECT');
71
- expect(result.hasViolation).toBe(false);
72
- expect(result.checkResult).toBeDefined();
73
- });
74
-
75
- it('checkResult 有违规时 hasViolation=true', async () => {
76
- const result = await handler.execute(() => Promise.resolve(makeFailingResult()), 'COLLECT');
77
- expect(result.hasViolation).toBe(true);
78
- expect(result.violationCount).toBe(2); // 1 iron + 1 guideline
79
- });
80
-
81
- it('抛出异常时捕获并返回空结果', async () => {
82
- const result = await handler.execute(makeThrowingFn(), 'COLLECT');
83
- expect(result.strategy).toBe('COLLECT');
84
- expect(result.hasViolation).toBe(true);
85
- expect(result.checkResult).toBeDefined();
86
- expect(result.checkResult!.passed).toBe(false);
87
- });
88
- });
89
-
90
- describe('SAFE_BOOLEAN strategy', () => {
91
- it('通过时返回 false', async () => {
92
- const result = await handler.execute(() => Promise.resolve(makePassingResult()), 'SAFE_BOOLEAN');
93
- expect(result.strategy).toBe('SAFE_BOOLEAN');
94
- expect(result.hasViolation).toBe(false);
95
- });
96
-
97
- it('违规时返回 true', async () => {
98
- const result = await handler.execute(() => Promise.resolve(makeFailingResult()), 'SAFE_BOOLEAN');
99
- expect(result.hasViolation).toBe(true);
100
- });
101
-
102
- it('抛出异常时返回 true', async () => {
103
- const result = await handler.execute(makeThrowingFn(), 'SAFE_BOOLEAN');
104
- expect(result.hasViolation).toBe(true);
105
- });
106
- });
107
- });
108
-
109
- describe('快捷函数', () => {
110
- it('executeWithBlock 通过', async () => {
111
- const result = await executeWithBlock(() => Promise.resolve(makePassingResult()));
112
- expect(result.strategy).toBe('BLOCK');
113
- expect(result.hasViolation).toBe(false);
114
- });
115
-
116
- it('executeWithBlock 违规抛出', async () => {
117
- await expect(
118
- executeWithBlock(makeThrowingFn())
119
- ).rejects.toThrow(ConstraintViolationError);
120
- });
121
-
122
- it('executeWithCollect 异常不抛出', async () => {
123
- const result = await executeWithCollect(makeThrowingFn());
124
- expect(result.hasViolation).toBe(true);
125
- expect(result.checkResult).toBeDefined();
126
- });
127
-
128
- it('executeWithSafeBoolean 异常返回 true', async () => {
129
- const result = await executeWithSafeBoolean(makeThrowingFn());
130
- expect(result.hasViolation).toBe(true);
131
- });
132
- });
@@ -1,93 +0,0 @@
1
- /**
2
- * PassesGate 补充测试 - 覆盖重试逻辑和 runTests
3
- */
4
-
5
- import { describe, it, expect, beforeAll, afterAll, beforeEach } from '@jest/globals';
6
- import { PassesGate, createPassesGate } from '../core/validators/passes-gate';
7
- import { mkdirSync, rmSync, writeFileSync } from 'fs';
8
- import { join } from 'path';
9
-
10
- describe('PassesGate - 补充覆盖', () => {
11
- const tempDir = join(process.cwd(), 'temp-test-passes-extra');
12
- let gate: PassesGate;
13
-
14
- beforeAll(() => {
15
- mkdirSync(tempDir, { recursive: true });
16
- });
17
-
18
- afterAll(() => {
19
- try {
20
- rmSync(tempDir, { recursive: true, force: true });
21
- } catch {
22
- // ignore
23
- }
24
- });
25
-
26
- beforeEach(() => {
27
- gate = createPassesGate({
28
- enabled: true,
29
- testCommand: 'npm test',
30
- requireEvidence: false,
31
- maxRetries: 1,
32
- retryDelay: 100,
33
- });
34
- });
35
-
36
- describe('runTests', () => {
37
- // runTests 方法会实际执行测试命令,在测试环境中跳过
38
- // 这些测试在 passes-gate.test.ts 中已经有类似覆盖
39
- it.skip('应该返回测试结果统计', async () => {
40
- // 这个测试会实际运行 npm test,在 CI 环境中可能超时
41
- });
42
- });
43
-
44
- describe('setPasses 重试逻辑', () => {
45
- it('测试失败应该返回不允许', async () => {
46
- // 创建会失败的测试命令
47
- writeFileSync(join(tempDir, 'package.json'), JSON.stringify({
48
- name: 'fail-project',
49
- scripts: { test: 'exit 1' },
50
- }));
51
-
52
- const failGate = createPassesGate({
53
- enabled: true,
54
- testCommand: 'npm test',
55
- maxRetries: 0,
56
- retryDelay: 10,
57
- });
58
-
59
- const result = await failGate.setPasses('fail-task', true, tempDir);
60
-
61
- expect(result.allowed).toBe(false);
62
- expect(result.attempts).toBeGreaterThan(0);
63
- expect(result.error).toBeDefined();
64
- });
65
-
66
- it('证据不存在应该返回不允许', async () => {
67
- writeFileSync(join(tempDir, 'package.json'), JSON.stringify({
68
- name: 'evidence-project',
69
- scripts: { test: 'echo "1 passed"' },
70
- }));
71
-
72
- const evidenceGate = createPassesGate({
73
- enabled: true,
74
- testCommand: 'npm test',
75
- requireEvidence: true,
76
- });
77
-
78
- const result = await evidenceGate.setPasses('evidence-task', true, tempDir);
79
-
80
- // 证据会自动生成在 .agent/evidence 目录
81
- // requireEvidence=true 时需要验证证据存在
82
- expect(result).toBeDefined();
83
- });
84
- });
85
-
86
- describe('checkTestFileChanges', () => {
87
- it('应该检测测试文件变更', async () => {
88
- const changes = await gate.checkTestFileChanges(tempDir);
89
-
90
- expect(Array.isArray(changes)).toBe(true);
91
- });
92
- });
93
- });