@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,253 +0,0 @@
1
- /**
2
- * SessionStartup - Session 启动检查点验证
3
- *
4
- * 确保 Agent 启动时必须先读上下文,防止"失忆"乱搞。
5
- */
6
-
7
- import { execAsync } from '../../utils/exec';
8
- import * as fs from 'fs/promises';
9
- import * as path from 'path';
10
- import * as yaml from 'js-yaml';
11
- import type {
12
- StartupCheckpoints,
13
- StartupCheckpointResult,
14
- StartupCheckpointType,
15
- TaskListJson,
16
- } from '../../types/session';
17
-
18
-
19
- const DEFAULT_TIMEOUT = 30000; // 30 seconds
20
-
21
- export class SessionStartup {
22
- private checkpoints: StartupCheckpoints;
23
- private workDir: string;
24
-
25
- constructor(workDir: string, checkpoints: StartupCheckpoints) {
26
- this.workDir = workDir;
27
- this.checkpoints = checkpoints;
28
- }
29
-
30
- async run(): Promise<{ success: boolean; results: StartupCheckpointResult[]; errors: string[] }> {
31
- const results: StartupCheckpointResult[] = [];
32
- const errors: string[] = [];
33
-
34
- // 1. Run required checkpoints
35
- for (const type of this.checkpoints.required) {
36
- const result = await this.runCheckpoint(type);
37
- results.push(result);
38
- if (!result.success) {
39
- errors.push(`Required checkpoint failed: ${type} - ${result.error}`);
40
- }
41
- }
42
-
43
- // 2. Run optional checkpoints (don't fail on error)
44
- for (const type of this.checkpoints.optional || []) {
45
- const result = await this.runCheckpoint(type);
46
- results.push(result);
47
- }
48
-
49
- return {
50
- success: errors.length === 0,
51
- results,
52
- errors,
53
- };
54
- }
55
-
56
- private async runCheckpoint(type: StartupCheckpointType): Promise<StartupCheckpointResult> {
57
- const start = Date.now();
58
-
59
- try {
60
- let data: any;
61
-
62
- switch (type) {
63
- case 'pwd':
64
- data = await this.checkPwd();
65
- break;
66
- case 'git_log':
67
- data = await this.checkGitLog();
68
- break;
69
- case 'git_status':
70
- data = await this.checkGitStatus();
71
- break;
72
- case 'read_progress':
73
- data = await this.checkProgress();
74
- break;
75
- case 'read_task_list':
76
- data = await this.checkTaskList();
77
- break;
78
- case 'init_sh':
79
- data = await this.checkInitSh();
80
- break;
81
- case 'basic_verification':
82
- data = await this.checkBasicVerification();
83
- break;
84
- case 'load_context':
85
- data = await this.checkLoadContext();
86
- break;
87
- default:
88
- throw new Error(`Unknown checkpoint type: ${type}`);
89
- }
90
-
91
- return { type, success: true, data, duration: Date.now() - start };
92
- } catch (error: any) {
93
- return { type, success: false, error: error.message, duration: Date.now() - start };
94
- }
95
- }
96
-
97
- private async checkPwd(): Promise<string> {
98
- return this.workDir;
99
- }
100
-
101
- private async checkGitLog(): Promise<string[]> {
102
- try {
103
- const { stdout } = await execAsync('git log --oneline -10', { cwd: this.workDir });
104
- return stdout.split('\n').filter(Boolean);
105
- } catch {
106
- return [];
107
- }
108
- }
109
-
110
- private async checkGitStatus(): Promise<{ staged: string[]; unstaged: string[]; untracked: string[] }> {
111
- try {
112
- const { stdout } = await execAsync('git status --porcelain', { cwd: this.workDir });
113
- const staged: string[] = [], unstaged: string[] = [], untracked: string[] = [];
114
-
115
- for (const line of stdout.split('\n').filter(Boolean)) {
116
- const status = line.substring(0, 2);
117
- const file = line.substring(3);
118
- if (status.includes('??')) untracked.push(file);
119
- else if (status.match(/[MADRC]/)) staged.push(file);
120
- else unstaged.push(file);
121
- }
122
-
123
- return { staged, unstaged, untracked };
124
- } catch {
125
- return { staged: [], unstaged: [], untracked: [] };
126
- }
127
- }
128
-
129
- private async checkProgress(): Promise<any> {
130
- const progressPath = path.join(this.workDir, '.harness', 'progress.yml');
131
- try {
132
- const content = await fs.readFile(progressPath, 'utf-8');
133
- return yaml.load(content) as any;
134
- } catch {
135
- return null;
136
- }
137
- }
138
-
139
- private async checkTaskList(): Promise<TaskListJson | null> {
140
- // Try tasks.yml first (existing format)
141
- const tasksPath = path.join(this.workDir, 'tasks.yml');
142
- try {
143
- const content = await fs.readFile(tasksPath, 'utf-8');
144
- return yaml.load(content) as TaskListJson;
145
- } catch {
146
- // Try tasks.json
147
- const jsonPath = path.join(this.workDir, 'tasks.json');
148
- try {
149
- const content = await fs.readFile(jsonPath, 'utf-8');
150
- return JSON.parse(content);
151
- } catch {
152
- return null;
153
- }
154
- }
155
- }
156
-
157
- private async checkInitSh(): Promise<boolean> {
158
- const initPath = path.join(this.workDir, 'init.sh');
159
- try {
160
- await fs.access(initPath);
161
- return true;
162
- } catch {
163
- return false;
164
- }
165
- }
166
-
167
- private async checkBasicVerification(): Promise<{ hasPackageJson: boolean; hasTests: boolean; hasReadme: boolean }> {
168
- const [hasPackageJson, hasTests, hasReadme] = await Promise.all([
169
- fs.access(path.join(this.workDir, 'package.json')).then(() => true).catch(() => false),
170
- this.hasTestFiles(),
171
- fs.access(path.join(this.workDir, 'README.md')).then(() => true).catch(() => false),
172
- ]);
173
- return { hasPackageJson, hasTests, hasReadme };
174
- }
175
-
176
- private async hasTestFiles(): Promise<boolean> {
177
- const testPatterns = ['test', 'spec', '__tests__'];
178
- try {
179
- const files = await fs.readdir(this.workDir);
180
- return files.some(f => testPatterns.some(p => f.toLowerCase().includes(p)));
181
- } catch {
182
- return false;
183
- }
184
- }
185
-
186
- private async checkLoadContext(): Promise<any> {
187
- const context: Record<string, string> = {};
188
- const files = this.checkpoints.contextFiles || [];
189
-
190
- for (const file of files) {
191
- try {
192
- context[file] = await fs.readFile(path.join(this.workDir, file), 'utf-8');
193
- } catch {}
194
- }
195
-
196
- return context;
197
- }
198
-
199
- /**
200
- * Get current task from task list
201
- */
202
- async getCurrentTask(): Promise<{ task: any; index: number } | null> {
203
- const taskList = await this.checkTaskList();
204
- if (!taskList?.tasks) return null;
205
-
206
- for (let i = 0; i < taskList.tasks.length; i++) {
207
- const task = taskList.tasks[i];
208
- if (task && !task.passes) {
209
- return { task, index: i };
210
- }
211
- }
212
-
213
- return null;
214
- }
215
-
216
- /**
217
- * Generate startup report
218
- */
219
- generateReport(results: StartupCheckpointResult[]): string {
220
- const lines: string[] = ['## Session Startup Report\n'];
221
-
222
- for (const result of results) {
223
- const icon = result.success ? '✅' : '❌';
224
- lines.push(`${icon} ${result.type} (${result.duration}ms)`);
225
- if (result.error) {
226
- lines.push(` Error: ${result.error}`);
227
- }
228
- }
229
-
230
- return lines.join('\n');
231
- }
232
- }
233
-
234
- export const createSessionStartup = (workDir: string, checkpoints: StartupCheckpoints) =>
235
- new SessionStartup(workDir, checkpoints);
236
-
237
- /**
238
- * Default startup checkpoints for code workflows
239
- */
240
- export const DEFAULT_CODE_CHECKPOINTS: StartupCheckpoints = {
241
- required: ['pwd', 'git_log', 'read_task_list'],
242
- optional: ['read_progress', 'init_sh', 'basic_verification'],
243
- timeout: DEFAULT_TIMEOUT,
244
- };
245
-
246
- /**
247
- * Minimal startup checkpoints
248
- */
249
- export const MINIMAL_CHECKPOINTS: StartupCheckpoints = {
250
- required: ['pwd', 'read_task_list'],
251
- optional: [],
252
- timeout: DEFAULT_TIMEOUT,
253
- };
@@ -1,171 +0,0 @@
1
- /**
2
- * ConstraintViolationHandler — 约束违规统一处理(S4)
3
- *
4
- * 问题:ConstraintViolationError 在 4 个调用点有 4 种行为:
5
- * 1. 抛异常阻断(checker.ts)
6
- * 2. 返回 null(report.ts catch 后返回空数据)
7
- * 3. 返回 403 HTTP 状态
8
- * 4. console.warn 静默吞掉
9
- *
10
- * 解决:提供 3 种标准化处理策略,消除 ad-hoc catch 行为。
11
- */
12
-
13
- import type { ConstraintCheckResult } from '../types/constraint';
14
-
15
- /**
16
- * 违规处理策略
17
- */
18
- export type ViolationStrategy = 'BLOCK' | 'COLLECT' | 'SAFE_BOOLEAN';
19
-
20
- /**
21
- * 违规处理结果
22
- */
23
- export interface ViolationHandlingResult {
24
- /** 策略 */
25
- strategy: ViolationStrategy;
26
- /** 检查结果(COLLECT 策略时总是有值) */
27
- checkResult?: ConstraintCheckResult;
28
- /** 是否有违规(SAFE_BOOLEAN 策略时为 false) */
29
- hasViolation: boolean;
30
- /** 违规数量 */
31
- violationCount: number;
32
- }
33
-
34
- /**
35
- * 约束违规处理器
36
- *
37
- * 用法:
38
- * ```typescript
39
- * const handler = new ConstraintViolationHandler();
40
- *
41
- * // 策略 1: BLOCK — 抛异常阻断(Iron Laws 默认)
42
- * await handler.execute(checkFn, 'BLOCK');
43
- *
44
- * // 策略 2: COLLECT — 收集违规不抛异常(用于报告生成)
45
- * const result = await handler.execute(checkFn, 'COLLECT');
46
- * // result.checkResult 总是有值
47
- *
48
- * // 策略 3: SAFE_BOOLEAN — 返回真/假(用于 canProceed 类方法)
49
- * const ok = await handler.execute(checkFn, 'SAFE_BOOLEAN');
50
- * // ok.hasViolation 为 true/false
51
- * ```
52
- */
53
- export class ConstraintViolationHandler {
54
- /**
55
- * 执行约束检查并统一处理结果
56
- *
57
- * @param checkFn 约束检查函数(可能抛出 ConstraintViolationError)
58
- * @param strategy 处理策略
59
- * @returns 标准化结果
60
- */
61
- async execute(
62
- checkFn: () => Promise<ConstraintCheckResult>,
63
- strategy: ViolationStrategy
64
- ): Promise<ViolationHandlingResult> {
65
- switch (strategy) {
66
- case 'BLOCK':
67
- return this.handleBlock(checkFn);
68
- case 'COLLECT':
69
- return this.handleCollect(checkFn);
70
- case 'SAFE_BOOLEAN':
71
- return this.handleSafeBoolean(checkFn);
72
- }
73
- }
74
-
75
- /**
76
- * BLOCK 策略:违规即抛出,无违规返回结果
77
- */
78
- private async handleBlock(
79
- checkFn: () => Promise<ConstraintCheckResult>
80
- ): Promise<ViolationHandlingResult> {
81
- const result = await checkFn();
82
- return {
83
- strategy: 'BLOCK',
84
- checkResult: result,
85
- hasViolation: !result.passed,
86
- violationCount: result.ironLaws.filter(r => !r.satisfied).length,
87
- };
88
- }
89
-
90
- /**
91
- * COLLECT 策略:捕获异常,收集所有违规,不抛出
92
- */
93
- private async handleCollect(
94
- checkFn: () => Promise<ConstraintCheckResult>
95
- ): Promise<ViolationHandlingResult> {
96
- try {
97
- const result = await checkFn();
98
- const ironViolations = result.ironLaws.filter(r => !r.satisfied).length;
99
- const guideViolations = result.warningCount;
100
- return {
101
- strategy: 'COLLECT',
102
- checkResult: result,
103
- hasViolation: !result.passed || guideViolations > 0,
104
- violationCount: ironViolations + guideViolations,
105
- };
106
- } catch {
107
- // 捕获 ConstraintViolationError,返回失败结果
108
- const empty: ConstraintCheckResult = {
109
- ironLaws: [],
110
- guidelines: [],
111
- passed: false,
112
- warningCount: 0,
113
- };
114
- return {
115
- strategy: 'COLLECT',
116
- checkResult: empty,
117
- hasViolation: true,
118
- violationCount: 1,
119
- };
120
- }
121
- }
122
-
123
- /**
124
- * SAFE_BOOLEAN 策略:不抛异常,只返回 boolean
125
- */
126
- private async handleSafeBoolean(
127
- checkFn: () => Promise<ConstraintCheckResult>
128
- ): Promise<ViolationHandlingResult> {
129
- try {
130
- const result = await checkFn();
131
- return {
132
- strategy: 'SAFE_BOOLEAN',
133
- hasViolation: !result.passed,
134
- violationCount: result.ironLaws.filter(r => !r.satisfied).length,
135
- };
136
- } catch {
137
- return {
138
- strategy: 'SAFE_BOOLEAN',
139
- hasViolation: true,
140
- violationCount: 1,
141
- };
142
- }
143
- }
144
- }
145
-
146
- /**
147
- * 快捷:BLOCK 策略执行检查
148
- */
149
- export async function executeWithBlock(
150
- checkFn: () => Promise<ConstraintCheckResult>
151
- ): Promise<ViolationHandlingResult> {
152
- return new ConstraintViolationHandler().execute(checkFn, 'BLOCK');
153
- }
154
-
155
- /**
156
- * 快捷:COLLECT 策略执行检查
157
- */
158
- export async function executeWithCollect(
159
- checkFn: () => Promise<ConstraintCheckResult>
160
- ): Promise<ViolationHandlingResult> {
161
- return new ConstraintViolationHandler().execute(checkFn, 'COLLECT');
162
- }
163
-
164
- /**
165
- * 快捷:SAFE_BOOLEAN 策略执行检查
166
- */
167
- export async function executeWithSafeBoolean(
168
- checkFn: () => Promise<ConstraintCheckResult>
169
- ): Promise<ViolationHandlingResult> {
170
- return new ConstraintViolationHandler().execute(checkFn, 'SAFE_BOOLEAN');
171
- }
@@ -1,140 +0,0 @@
1
- /**
2
- * KnowledgeLifecycleHooks 测试
3
- */
4
-
5
- import { KnowledgeLifecycleHooks } from '../lifecycle-hooks';
6
- import { FileKnowledgeStore as KnowledgeStore } from '../store';
7
- import { KnowledgeQuery } from '../query';
8
- import * as fs from 'fs';
9
- import * as path from 'path';
10
- import * as os from 'os';
11
-
12
- describe('KnowledgeLifecycleHooks', () => {
13
- let store: KnowledgeStore;
14
- let query: KnowledgeQuery;
15
- let hooks: KnowledgeLifecycleHooks;
16
- let tmpDir: string;
17
-
18
- beforeEach(() => {
19
- tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'hooks-test-'));
20
- store = new KnowledgeStore({ baseDir: path.join(tmpDir, 'knowledge') });
21
- query = new KnowledgeQuery(store);
22
- hooks = new KnowledgeLifecycleHooks({ store, query });
23
- });
24
-
25
- afterEach(() => {
26
- fs.rmSync(tmpDir, { recursive: true, force: true });
27
- });
28
-
29
- function saveEntry(overrides: any = {}) {
30
- store.save({
31
- id: overrides.id || `test-${Math.random().toString(36).slice(2, 6)}`,
32
- type: 'decision',
33
- title: 'Test Decision',
34
- content: 'Some content',
35
- maturity: 'verified',
36
- layer: 'project',
37
- created: new Date().toISOString(),
38
- lastReferenced: new Date().toISOString(),
39
- contributors: [],
40
- projects: [],
41
- tags: [],
42
- applicablePhases: [],
43
- sourceReferences: [],
44
- referencedBy: [],
45
- executionResults: [],
46
- consumptionMode: 'reference',
47
- origin: 'agent',
48
- ...overrides,
49
- });
50
- }
51
-
52
- describe('onSessionStart', () => {
53
- it('应该返回空数组当无知识条目', () => {
54
- const sources = hooks.onSessionStart();
55
- expect(sources.length).toBe(0);
56
- });
57
-
58
- it('应该注入知识条目为 ContextSource', () => {
59
- saveEntry({ id: 'dec-001', title: 'Use TypeScript', content: 'Always use TS', tags: ['typescript', 'best-practice'] });
60
- const sources = hooks.onSessionStart({ budget: 8000 });
61
- expect(sources.length).toBe(1);
62
- expect(sources[0].type).toBe('knowledge');
63
- expect(sources[0].priority).toBe(3);
64
- expect(sources[0].content).toContain('Use TypeScript');
65
- expect(sources[0].content).toContain('typescript');
66
- });
67
-
68
- it('应该排除指定条目', () => {
69
- saveEntry({ id: 'ex-1', title: 'Excluded' });
70
- const sources = hooks.onSessionStart({ budget: 8000, exclude: ['ex-1'] });
71
- expect(sources.length).toBe(0);
72
- });
73
- });
74
-
75
- describe('onTaskComplete', () => {
76
- it('应该从错误中提取 pitfall', () => {
77
- const result = hooks.onTaskComplete({
78
- taskDescription: 'Fix login bug',
79
- errors: ['TypeError: Cannot read property of undefined'],
80
- });
81
-
82
- expect(result.entries.length).toBe(1);
83
- expect(result.entries[0].type).toBe('pitfall');
84
- expect(result.entries[0].tags).toContain('auto-extract');
85
- expect(result.source).toBe('archive');
86
- });
87
-
88
- it('应该从决策中提取 decision', () => {
89
- const result = hooks.onTaskComplete({
90
- taskDescription: 'Choose architecture',
91
- decisions: ['使用微服务架构因为需要独立部署'],
92
- });
93
-
94
- expect(result.entries.length).toBe(1);
95
- expect(result.entries[0].type).toBe('decision');
96
- });
97
-
98
- it('应该限制提取数量', () => {
99
- const errors = Array.from({ length: 10 }, (_, i) => `Error ${i}`);
100
- const result = hooks.onTaskComplete({
101
- taskDescription: 'Test',
102
- errors,
103
- });
104
-
105
- expect(result.entries.length).toBeLessThanOrEqual(3);
106
- });
107
-
108
- it('应该处理空上下文', () => {
109
- const result = hooks.onTaskComplete({ taskDescription: 'Empty' });
110
- expect(result.entries.length).toBe(0);
111
- });
112
- });
113
-
114
- describe('onError', () => {
115
- it('应该从错误中提取知识', () => {
116
- const result = hooks.onError(new Error('Connection timeout'), {
117
- taskDescription: 'API call',
118
- stackTrace: 'at line 42',
119
- });
120
-
121
- expect(result.entries.length).toBe(1);
122
- expect(result.entries[0].type).toBe('pitfall');
123
- expect(result.entries[0].content).toContain('Connection timeout');
124
- expect(result.entries[0].content).toContain('API call');
125
- expect(result.source).toBe('error');
126
- });
127
-
128
- it('应该保存到知识库', () => {
129
- hooks.onError(new Error('Test error'));
130
- const entries = store.list({ excludeArchived: false });
131
- expect(entries.length).toBe(1);
132
- expect(entries[0].title).toContain('Test error');
133
- });
134
-
135
- it('应该处理无上下文的错误', () => {
136
- const result = hooks.onError(new Error('Simple error'));
137
- expect(result.entries.length).toBe(1);
138
- });
139
- });
140
- });