@dommaker/harness 1.9.0 → 1.11.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 (285) hide show
  1. package/CHANGELOG.md +17 -0
  2. package/README.md +9 -12
  3. package/dist/cli/commands/check.d.ts +3 -3
  4. package/dist/cli/commands/check.d.ts.map +1 -1
  5. package/dist/cli/commands/check.js +41 -78
  6. package/dist/cli/commands/check.js.map +1 -1
  7. package/dist/cli/commands/constraints-report.d.ts +2 -10
  8. package/dist/cli/commands/constraints-report.d.ts.map +1 -1
  9. package/dist/cli/commands/constraints-report.js +4 -57
  10. package/dist/cli/commands/constraints-report.js.map +1 -1
  11. package/dist/cli/commands/constraints-retire.d.ts +9 -23
  12. package/dist/cli/commands/constraints-retire.d.ts.map +1 -1
  13. package/dist/cli/commands/constraints-retire.js +39 -121
  14. package/dist/cli/commands/constraints-retire.js.map +1 -1
  15. package/dist/cli/commands/constraints.d.ts +3 -8
  16. package/dist/cli/commands/constraints.d.ts.map +1 -1
  17. package/dist/cli/commands/constraints.js +8 -23
  18. package/dist/cli/commands/constraints.js.map +1 -1
  19. package/dist/cli/commands/definitions.d.ts.map +1 -1
  20. package/dist/cli/commands/definitions.js +7 -12
  21. package/dist/cli/commands/definitions.js.map +1 -1
  22. package/dist/cli/commands/init.d.ts +0 -32
  23. package/dist/cli/commands/init.d.ts.map +1 -1
  24. package/dist/cli/commands/init.js +7 -149
  25. package/dist/cli/commands/init.js.map +1 -1
  26. package/dist/cli/commands/report.d.ts.map +1 -1
  27. package/dist/cli/commands/report.js +14 -17
  28. package/dist/cli/commands/report.js.map +1 -1
  29. package/dist/cli/commands/scaffold-templates.d.ts +1 -3
  30. package/dist/cli/commands/scaffold-templates.d.ts.map +1 -1
  31. package/dist/cli/commands/scaffold-templates.js +2 -53
  32. package/dist/cli/commands/scaffold-templates.js.map +1 -1
  33. package/dist/cli/commands/scaffold.d.ts +0 -2
  34. package/dist/cli/commands/scaffold.d.ts.map +1 -1
  35. package/dist/cli/commands/scaffold.js +0 -10
  36. package/dist/cli/commands/scaffold.js.map +1 -1
  37. package/dist/cli/commands/status.js +10 -10
  38. package/dist/cli/commands/status.js.map +1 -1
  39. package/dist/cli/commands/sync-docs/agents-syncer.d.ts +0 -2
  40. package/dist/cli/commands/sync-docs/agents-syncer.d.ts.map +1 -1
  41. package/dist/cli/commands/sync-docs/agents-syncer.js +4 -23
  42. package/dist/cli/commands/sync-docs/agents-syncer.js.map +1 -1
  43. package/dist/core/constraints/capabilities-parser.d.ts.map +1 -1
  44. package/dist/core/constraints/capabilities-parser.js +16 -4
  45. package/dist/core/constraints/capabilities-parser.js.map +1 -1
  46. package/dist/core/constraints/checker.d.ts +15 -24
  47. package/dist/core/constraints/checker.d.ts.map +1 -1
  48. package/dist/core/constraints/checker.js +33 -72
  49. package/dist/core/constraints/checker.js.map +1 -1
  50. package/dist/core/constraints/checkers/docs-freshness.js +1 -1
  51. package/dist/core/constraints/checkers/docs-freshness.js.map +1 -1
  52. package/dist/core/constraints/checkers/governance-presence.d.ts +2 -1
  53. package/dist/core/constraints/checkers/governance-presence.d.ts.map +1 -1
  54. package/dist/core/constraints/checkers/governance-presence.js +4 -3
  55. package/dist/core/constraints/checkers/governance-presence.js.map +1 -1
  56. package/dist/core/constraints/checkers/index.d.ts +1 -1
  57. package/dist/core/constraints/checkers/index.d.ts.map +1 -1
  58. package/dist/core/constraints/checkers/index.js +4 -8
  59. package/dist/core/constraints/checkers/index.js.map +1 -1
  60. package/dist/core/constraints/checkers/iron-flags.d.ts +3 -4
  61. package/dist/core/constraints/checkers/iron-flags.d.ts.map +1 -1
  62. package/dist/core/constraints/checkers/iron-flags.js +4 -5
  63. package/dist/core/constraints/checkers/iron-flags.js.map +1 -1
  64. package/dist/core/constraints/checkers/no-test-simplification.js +1 -1
  65. package/dist/core/constraints/checkers/no-test-simplification.js.map +1 -1
  66. package/dist/core/constraints/checkers/types.d.ts +1 -1
  67. package/dist/core/constraints/checkers/types.js +1 -1
  68. package/dist/core/constraints/definitions/guidelines.d.ts +4 -4
  69. package/dist/core/constraints/definitions/guidelines.d.ts.map +1 -1
  70. package/dist/core/constraints/definitions/guidelines.js +9 -25
  71. package/dist/core/constraints/definitions/guidelines.js.map +1 -1
  72. package/dist/core/constraints/definitions/iron-laws.d.ts +4 -4
  73. package/dist/core/constraints/definitions/iron-laws.d.ts.map +1 -1
  74. package/dist/core/constraints/definitions/iron-laws.js +10 -81
  75. package/dist/core/constraints/definitions/iron-laws.js.map +1 -1
  76. package/dist/core/constraints/definitions.d.ts +7 -9
  77. package/dist/core/constraints/definitions.d.ts.map +1 -1
  78. package/dist/core/constraints/definitions.js +13 -16
  79. package/dist/core/constraints/definitions.js.map +1 -1
  80. package/dist/core/constraints/index.d.ts +2 -5
  81. package/dist/core/constraints/index.d.ts.map +1 -1
  82. package/dist/core/constraints/index.js +3 -13
  83. package/dist/core/constraints/index.js.map +1 -1
  84. package/dist/core/constraints/injection-writer.d.ts +10 -38
  85. package/dist/core/constraints/injection-writer.d.ts.map +1 -1
  86. package/dist/core/constraints/injection-writer.js +17 -54
  87. package/dist/core/constraints/injection-writer.js.map +1 -1
  88. package/dist/core/constraints/run-env.d.ts +1 -10
  89. package/dist/core/constraints/run-env.d.ts.map +1 -1
  90. package/dist/core/constraints/run-env.js +1 -22
  91. package/dist/core/constraints/run-env.js.map +1 -1
  92. package/dist/core/constraints/usage-report.d.ts +6 -8
  93. package/dist/core/constraints/usage-report.d.ts.map +1 -1
  94. package/dist/core/constraints/usage-report.js +6 -11
  95. package/dist/core/constraints/usage-report.js.map +1 -1
  96. package/dist/core/effective-constraints.d.ts +12 -22
  97. package/dist/core/effective-constraints.d.ts.map +1 -1
  98. package/dist/core/effective-constraints.js +13 -30
  99. package/dist/core/effective-constraints.js.map +1 -1
  100. package/dist/core/index.d.ts +2 -2
  101. package/dist/core/index.d.ts.map +1 -1
  102. package/dist/core/index.js +3 -9
  103. package/dist/core/index.js.map +1 -1
  104. package/dist/core/project-config-loader.d.ts +4 -42
  105. package/dist/core/project-config-loader.d.ts.map +1 -1
  106. package/dist/core/project-config-loader.js +16 -125
  107. package/dist/core/project-config-loader.js.map +1 -1
  108. package/dist/core/validators/passes-gate.d.ts +2 -2
  109. package/dist/core/validators/passes-gate.js +6 -6
  110. package/dist/core/validators/passes-gate.js.map +1 -1
  111. package/dist/index.d.ts +9 -10
  112. package/dist/index.d.ts.map +1 -1
  113. package/dist/index.js +21 -18
  114. package/dist/index.js.map +1 -1
  115. package/dist/knowledge/lint.d.ts +17 -0
  116. package/dist/knowledge/lint.d.ts.map +1 -1
  117. package/dist/knowledge/lint.js +52 -0
  118. package/dist/knowledge/lint.js.map +1 -1
  119. package/dist/knowledge/store.d.ts +7 -0
  120. package/dist/knowledge/store.d.ts.map +1 -1
  121. package/dist/knowledge/store.js +48 -13
  122. package/dist/knowledge/store.js.map +1 -1
  123. package/dist/knowledge/types.d.ts +5 -1
  124. package/dist/knowledge/types.d.ts.map +1 -1
  125. package/dist/knowledge/types.js +5 -1
  126. package/dist/knowledge/types.js.map +1 -1
  127. package/dist/monitoring/trace-analyzer.d.ts.map +1 -1
  128. package/dist/monitoring/trace-analyzer.js +11 -12
  129. package/dist/monitoring/trace-analyzer.js.map +1 -1
  130. package/dist/monitoring/traces.d.ts +4 -4
  131. package/dist/monitoring/traces.d.ts.map +1 -1
  132. package/dist/monitoring/traces.js +8 -8
  133. package/dist/monitoring/traces.js.map +1 -1
  134. package/dist/presets/standard.d.ts +8 -10
  135. package/dist/presets/standard.d.ts.map +1 -1
  136. package/dist/presets/standard.js +10 -16
  137. package/dist/presets/standard.js.map +1 -1
  138. package/dist/types/constraint.d.ts +24 -35
  139. package/dist/types/constraint.d.ts.map +1 -1
  140. package/dist/types/constraint.js +8 -7
  141. package/dist/types/constraint.js.map +1 -1
  142. package/dist/types/passes-gate.d.ts +5 -5
  143. package/dist/types/passes-gate.d.ts.map +1 -1
  144. package/dist/types/project-config.d.ts +8 -66
  145. package/dist/types/project-config.d.ts.map +1 -1
  146. package/dist/types/project-config.js +3 -2
  147. package/dist/types/project-config.js.map +1 -1
  148. package/dist/types/trace.d.ts +8 -10
  149. package/dist/types/trace.d.ts.map +1 -1
  150. package/package.json +2 -2
  151. package/src/CONTEXT.md +2 -2
  152. package/src/__tests__/checker-extra.test.ts +37 -61
  153. package/src/__tests__/checker.test.ts +10 -115
  154. package/src/__tests__/effective-constraints.test.ts +18 -83
  155. package/src/__tests__/iron-laws.test.ts +71 -90
  156. package/src/__tests__/passes-gate-check.test.ts +2 -2
  157. package/src/__tests__/project-config-loader-extra.test.ts +22 -123
  158. package/src/__tests__/project-config-loader.test.ts +5 -33
  159. package/src/__tests__/public-exports.test.ts +8 -7
  160. package/src/__tests__/public-type-surface.test.ts +7 -5
  161. package/src/__tests__/public-value-surface.test.ts +1 -7
  162. package/src/__tests__/skip-semantics.test.ts +21 -37
  163. package/src/__tests__/trace-analyzer-extra.test.ts +14 -14
  164. package/src/__tests__/trace-analyzer.test.ts +21 -21
  165. package/src/__tests__/trace-collector.test.ts +22 -22
  166. package/src/__tests__/traces.test.ts +27 -28
  167. package/src/__tests__/usage-report-public-export.test.ts +21 -0
  168. package/src/cli/commands/CONTEXT.md +12 -12
  169. package/src/cli/commands/__tests__/check-read-count.test.ts +4 -15
  170. package/src/cli/commands/__tests__/check-skip-output.test.ts +14 -14
  171. package/src/cli/commands/__tests__/check.test.ts +14 -31
  172. package/src/cli/commands/__tests__/constraints-report-json-flag.test.ts +101 -0
  173. package/src/cli/commands/__tests__/constraints-report.test.ts +12 -80
  174. package/src/cli/commands/__tests__/constraints-retire.test.ts +42 -231
  175. package/src/cli/commands/__tests__/init-governance-io.test.ts +12 -58
  176. package/src/cli/commands/__tests__/init-ondisk.test.ts +13 -21
  177. package/src/cli/commands/__tests__/init.test.ts +1 -16
  178. package/src/cli/commands/__tests__/numeric-flag-assembly.test.ts +1 -1
  179. package/src/cli/commands/__tests__/passes-gate-dead-options.test.ts +1 -1
  180. package/src/cli/commands/__tests__/project-path-anchoring.test.ts +2 -2
  181. package/src/cli/commands/__tests__/registry.test.ts +1 -1
  182. package/src/cli/commands/__tests__/report-extra.test.ts +4 -4
  183. package/src/cli/commands/__tests__/report.test.ts +1 -1
  184. package/src/cli/commands/__tests__/scaffold.test.ts +4 -14
  185. package/src/cli/commands/__tests__/status-analysis-wiring.test.ts +3 -3
  186. package/src/cli/commands/__tests__/status-extra.test.ts +14 -14
  187. package/src/cli/commands/__tests__/status.test.ts +12 -12
  188. package/src/cli/commands/__tests__/sync-docs-agents.test.ts +1 -1
  189. package/src/cli/commands/check.ts +43 -82
  190. package/src/cli/commands/constraints-report.ts +4 -59
  191. package/src/cli/commands/constraints-retire.ts +49 -154
  192. package/src/cli/commands/constraints.ts +11 -26
  193. package/src/cli/commands/definitions.ts +7 -12
  194. package/src/cli/commands/init.ts +8 -179
  195. package/src/cli/commands/report.ts +19 -23
  196. package/src/cli/commands/scaffold-templates.ts +1 -53
  197. package/src/cli/commands/scaffold.ts +0 -11
  198. package/src/cli/commands/status.ts +10 -10
  199. package/src/cli/commands/sync-docs/agents-syncer.ts +4 -25
  200. package/src/core/CONTEXT.md +9 -9
  201. package/src/core/constraints/__tests__/capability-counts.test.ts +5 -5
  202. package/src/core/constraints/__tests__/collect-constraints.test.ts +13 -8
  203. package/src/core/constraints/__tests__/git-evidence.test.ts +4 -7
  204. package/src/core/constraints/__tests__/injection-writer.test.ts +8 -61
  205. package/src/core/constraints/__tests__/run-env.test.ts +5 -34
  206. package/src/core/constraints/__tests__/trace-injection.test.ts +1 -1
  207. package/src/core/constraints/capabilities-parser.ts +17 -5
  208. package/src/core/constraints/checker.ts +39 -88
  209. package/src/core/constraints/checkers/__tests__/check-outcome.test.ts +5 -8
  210. package/src/core/constraints/checkers/__tests__/governance-presence.test.ts +1 -53
  211. package/src/core/constraints/checkers/__tests__/iron-flags.test.ts +5 -25
  212. package/src/core/constraints/checkers/docs-freshness.ts +1 -1
  213. package/src/core/constraints/checkers/governance-presence.ts +4 -3
  214. package/src/core/constraints/checkers/index.ts +4 -10
  215. package/src/core/constraints/checkers/iron-flags.ts +3 -12
  216. package/src/core/constraints/checkers/no-test-simplification.ts +1 -1
  217. package/src/core/constraints/checkers/types.ts +1 -1
  218. package/src/core/constraints/definitions/guidelines.ts +8 -25
  219. package/src/core/constraints/definitions/iron-laws.ts +9 -82
  220. package/src/core/constraints/definitions.ts +14 -15
  221. package/src/core/constraints/index.ts +4 -19
  222. package/src/core/constraints/injection-writer.ts +16 -61
  223. package/src/core/constraints/run-env.ts +1 -33
  224. package/src/core/constraints/usage-report.ts +9 -16
  225. package/src/core/effective-constraints.ts +14 -45
  226. package/src/core/index.ts +3 -10
  227. package/src/core/project-config-loader.ts +20 -145
  228. package/src/core/validators/passes-gate.ts +6 -6
  229. package/src/failure/CONTEXT.md +1 -1
  230. package/src/hooks/__tests__/bootstrap.test.ts +3 -5
  231. package/src/index.ts +28 -18
  232. package/src/knowledge/CONTEXT.md +4 -3
  233. package/src/knowledge/__tests__/lint.test.ts +84 -0
  234. package/src/knowledge/__tests__/store.test.ts +70 -0
  235. package/src/knowledge/lint.ts +60 -1
  236. package/src/knowledge/store.ts +48 -11
  237. package/src/knowledge/types.ts +7 -1
  238. package/src/monitoring/CONTEXT.md +1 -1
  239. package/src/monitoring/__tests__/trace-file-anchoring.test.ts +1 -1
  240. package/src/monitoring/__tests__/traces.test.ts +22 -22
  241. package/src/monitoring/trace-analyzer.ts +12 -13
  242. package/src/monitoring/traces.ts +8 -8
  243. package/src/presets/__tests__/standard.test.ts +12 -16
  244. package/src/presets/standard.ts +14 -23
  245. package/src/types/__tests__/types.test.ts +3 -3
  246. package/src/types/constraint.ts +26 -40
  247. package/src/types/passes-gate.ts +5 -5
  248. package/src/types/project-config.ts +8 -77
  249. package/src/types/trace.ts +8 -11
  250. package/dist/cli/commands/posteval-plan.d.ts +0 -15
  251. package/dist/cli/commands/posteval-plan.d.ts.map +0 -1
  252. package/dist/cli/commands/posteval-plan.js +0 -86
  253. package/dist/cli/commands/posteval-plan.js.map +0 -1
  254. package/dist/core/constraints/agent-prompt-renderer.d.ts +0 -32
  255. package/dist/core/constraints/agent-prompt-renderer.d.ts.map +0 -1
  256. package/dist/core/constraints/agent-prompt-renderer.js +0 -55
  257. package/dist/core/constraints/agent-prompt-renderer.js.map +0 -1
  258. package/dist/core/constraints/checkers/no-bypass-checkpoint.d.ts +0 -6
  259. package/dist/core/constraints/checkers/no-bypass-checkpoint.d.ts.map +0 -1
  260. package/dist/core/constraints/checkers/no-bypass-checkpoint.js +0 -40
  261. package/dist/core/constraints/checkers/no-bypass-checkpoint.js.map +0 -1
  262. package/dist/core/constraints/definitions/prompts.d.ts +0 -12
  263. package/dist/core/constraints/definitions/prompts.d.ts.map +0 -1
  264. package/dist/core/constraints/definitions/prompts.js +0 -400
  265. package/dist/core/constraints/definitions/prompts.js.map +0 -1
  266. package/dist/core/constraints/injection-drift.d.ts +0 -61
  267. package/dist/core/constraints/injection-drift.d.ts.map +0 -1
  268. package/dist/core/constraints/injection-drift.js +0 -131
  269. package/dist/core/constraints/injection-drift.js.map +0 -1
  270. package/dist/core/constraints/injection-renderer.d.ts +0 -23
  271. package/dist/core/constraints/injection-renderer.d.ts.map +0 -1
  272. package/dist/core/constraints/injection-renderer.js +0 -44
  273. package/dist/core/constraints/injection-renderer.js.map +0 -1
  274. package/src/cli/commands/__tests__/check-drift.test.ts +0 -158
  275. package/src/cli/commands/__tests__/init-injection.test.ts +0 -466
  276. package/src/cli/commands/__tests__/posteval-plan.test.ts +0 -204
  277. package/src/cli/commands/posteval-plan.ts +0 -98
  278. package/src/core/constraints/__tests__/agent-prompt-renderer.test.ts +0 -129
  279. package/src/core/constraints/__tests__/injection-drift.test.ts +0 -247
  280. package/src/core/constraints/agent-prompt-renderer.ts +0 -67
  281. package/src/core/constraints/checkers/__tests__/no-bypass-checkpoint.test.ts +0 -76
  282. package/src/core/constraints/checkers/no-bypass-checkpoint.ts +0 -40
  283. package/src/core/constraints/definitions/prompts.ts +0 -414
  284. package/src/core/constraints/injection-drift.ts +0 -142
  285. package/src/core/constraints/injection-renderer.ts +0 -44
@@ -13,6 +13,7 @@
13
13
  import type { KnowledgeStore } from './store';
14
14
  import { ReferenceTracker } from './reference-tracker';
15
15
  import type { KnowledgeEntry, LintIssue, LintIssueType, MaturityLevel } from './types';
16
+ import { MATURITY_LEVELS, STORAGE_LAYERS } from './types';
16
17
 
17
18
  const MAX_SOURCE_REFS = 20;
18
19
 
@@ -46,6 +47,7 @@ export class KnowledgeLinter {
46
47
  issues.push(...this.checkOutdated(entries));
47
48
  issues.push(...this.checkDuplicates(entries));
48
49
  issues.push(...this.checkContradictions(entries));
50
+ issues.push(...this.checkInvalidEnums(entries));
49
51
 
50
52
  // 自动修复
51
53
  let fixed = 0;
@@ -60,6 +62,7 @@ export class KnowledgeLinter {
60
62
  outdated: 0,
61
63
  duplicate: 0,
62
64
  index_inconsistent: 0,
65
+ invalid_enum: 0,
63
66
  };
64
67
  for (const issue of issues) {
65
68
  summary[issue.type]++;
@@ -204,6 +207,55 @@ export class KnowledgeLinter {
204
207
  return issues;
205
208
  }
206
209
 
210
+ /**
211
+ * 检查存量条目的 maturity/layer 枚举值域(E1 复盘修正 M1 收口:
212
+ * 写入闸只挡新写,存量脏值由本检查进 lint 报告;缺字段在存量侧算脏值)。
213
+ * 与 validateEntry 共用同一判定正本(invalidEnumIssues),autoFix 不动此类
214
+ * issue——枚举映射需人工裁定,不自动改。
215
+ */
216
+ checkInvalidEnums(entries: KnowledgeEntry[]): LintIssue[] {
217
+ const issues: LintIssue[] = [];
218
+ for (const entry of entries) {
219
+ issues.push(...this.invalidEnumIssues(entry, true, entry.id));
220
+ }
221
+ return issues;
222
+ }
223
+
224
+ /**
225
+ * maturity/layer 枚举判定正本。missingIsDirty=false 时字段缺省跳过
226
+ * (pre-ingest 调用方只传四字段的兼容面);存量扫描传 true(缺字段即脏)。
227
+ */
228
+ private invalidEnumIssues(
229
+ fields: { maturity?: string; layer?: string },
230
+ missingIsDirty: boolean,
231
+ entryId?: string
232
+ ): LintIssue[] {
233
+ const issues: LintIssue[] = [];
234
+ if ((missingIsDirty || fields.maturity !== undefined) && !(MATURITY_LEVELS as readonly string[]).includes(fields.maturity as string)) {
235
+ issues.push({
236
+ type: 'invalid_enum',
237
+ entryId,
238
+ severity: 'high',
239
+ description: fields.maturity === undefined
240
+ ? `maturity is missing (declared values: ${MATURITY_LEVELS.join(' | ')}).`
241
+ : `maturity "${fields.maturity}" is not a declared value (${MATURITY_LEVELS.join(' | ')}).`,
242
+ suggestion: 'Map to a declared maturity (e.g. pending → draft) before writing; the store write gate rejects undeclared values.',
243
+ });
244
+ }
245
+ if ((missingIsDirty || fields.layer !== undefined) && !(STORAGE_LAYERS as readonly string[]).includes(fields.layer as string)) {
246
+ issues.push({
247
+ type: 'invalid_enum',
248
+ entryId,
249
+ severity: 'high',
250
+ description: fields.layer === undefined
251
+ ? `layer is missing (declared values: ${STORAGE_LAYERS.join(' | ')}).`
252
+ : `layer "${fields.layer}" is not a declared value (${STORAGE_LAYERS.join(' | ')}).`,
253
+ suggestion: 'Map to a declared layer or drop the field; the store write gate rejects undeclared values.',
254
+ });
255
+ }
256
+ return issues;
257
+ }
258
+
207
259
  /**
208
260
  * 检查矛盾(同主题相反结论)
209
261
  *
@@ -260,10 +312,17 @@ export class KnowledgeLinter {
260
312
  * - Title too vague (common stop words only) → flag
261
313
  * - Contradicts a proven entry with same tags → reject
262
314
  * - Near-duplicate of existing entry (title similarity > 80%) → flag merge
315
+ * - maturity/layer 未声明枚举值 → reject(E1 复盘修正 M1;字段缺省则跳过,
316
+ * 兼容只传 title/content/tags/type 的 pre-ingest 调用方;存量报告由调用方
317
+ * 把条目字段显式传入触发)
263
318
  */
264
- validateEntry(entry: { title: string; content: string; tags: string[]; type: string }): LintIssue[] {
319
+ validateEntry(entry: { title: string; content: string; tags: string[]; type: string; maturity?: string; layer?: string }): LintIssue[] {
265
320
  const issues: LintIssue[] = [];
266
321
 
322
+ // Undeclared maturity/layer enum values(判定正本 = invalidEnumIssues;字段缺省跳过,
323
+ // 兼容只传四字段的 pre-ingest 调用方;存量扫描走 run() → checkInvalidEnums)
324
+ issues.push(...this.invalidEnumIssues(entry, false));
325
+
267
326
  // Content too short
268
327
  if ((entry.content || '').length < 20) {
269
328
  issues.push({
@@ -18,6 +18,7 @@ import type {
18
18
  SnapshotSurvival,
19
19
  StoreUpdate,
20
20
  } from './types';
21
+ import { MATURITY_LEVELS, STORAGE_LAYERS } from './types';
21
22
 
22
23
  const DEFAULT_DIR = '.harness/knowledge';
23
24
  const INDEX_FILE = 'index.json';
@@ -109,12 +110,36 @@ export class FileKnowledgeStore implements KnowledgeStore {
109
110
  }
110
111
 
111
112
  save(entry: KnowledgeEntry): void {
113
+ this.assertEnumFields(entry);
112
114
  this.writeEntryFile(entry);
113
115
  this.updateIndexEntry(entry);
114
116
  }
115
117
 
118
+ /**
119
+ * 写入闸(E1 复盘修正 M1):maturity/layer 是声明枚举(值域正本在 types.ts),
120
+ * 未声明值拒写报错,不静默降级。save/saveAll/applyAll 三条落盘路径共用;
121
+ * update 经 save 继承同一闸。存量脏条目(绕过本 store 直写文件的)在被
122
+ * 任一写入路径触及时抛错——先修脏值再写。
123
+ */
124
+ private assertEnumFields(entry: KnowledgeEntry): void {
125
+ if (!MATURITY_LEVELS.includes(entry.maturity)) {
126
+ throw new Error(
127
+ `[harness] 知识条目 ${entry.id} 的 maturity 值 "${entry.maturity}" 未声明` +
128
+ `(允许: ${MATURITY_LEVELS.join(' | ')}),拒写`
129
+ );
130
+ }
131
+ if (!STORAGE_LAYERS.includes(entry.layer)) {
132
+ throw new Error(
133
+ `[harness] 知识条目 ${entry.id} 的 layer 值 "${entry.layer}" 未声明` +
134
+ `(允许: ${STORAGE_LAYERS.join(' | ')}),拒写`
135
+ );
136
+ }
137
+ }
138
+
116
139
  saveAll(entries: KnowledgeEntry[]): void {
117
140
  if (entries.length === 0) return;
141
+ // 整批预校验(写入闸):任一脏值全批拒写,不落半个批次
142
+ for (const entry of entries) this.assertEnumFields(entry);
118
143
  const index = this.readIndex();
119
144
  const position = new Map(index.map((e, i) => [e.id, i]));
120
145
  for (const entry of entries) {
@@ -133,25 +158,37 @@ export class FileKnowledgeStore implements KnowledgeStore {
133
158
 
134
159
  applyAll(updates: StoreUpdate[]): void {
135
160
  if (updates.length === 0) return;
161
+ // 先合并再整批预校验(写入闸):任一合并结果脏值全批拒写,不落半个批次。
162
+ // 批内同 id 多条按序累积合并(后者基于前者的合并结果,与逐条 update 同语义)。
163
+ const merged: KnowledgeEntry[] = [];
164
+ const pending = new Map<string, number>(); // id → merged 下标
165
+ for (const { id, partial } of updates) {
166
+ const idx = pending.get(id);
167
+ const base = idx !== undefined ? merged[idx] : this.get(id);
168
+ if (!base) continue;
169
+ const next: KnowledgeEntry = { ...base, ...partial, id };
170
+ if (idx !== undefined) {
171
+ merged[idx] = next;
172
+ } else {
173
+ pending.set(id, merged.length);
174
+ merged.push(next);
175
+ }
176
+ }
177
+ if (merged.length === 0) return;
178
+ for (const entry of merged) this.assertEnumFields(entry);
136
179
  const index = this.readIndex();
137
180
  const position = new Map(index.map((e, i) => [e.id, i]));
138
- let applied = 0;
139
- for (const { id, partial } of updates) {
140
- const existing = this.get(id);
141
- if (!existing) continue;
142
- const updated: KnowledgeEntry = { ...existing, ...partial, id };
143
- this.writeEntryFile(updated);
144
- const indexEntry = this.toIndexEntry(updated);
145
- const idx = position.get(id);
181
+ for (const entry of merged) {
182
+ this.writeEntryFile(entry);
183
+ const indexEntry = this.toIndexEntry(entry);
184
+ const idx = position.get(entry.id);
146
185
  if (idx !== undefined) {
147
186
  index[idx] = indexEntry;
148
187
  } else {
149
- position.set(id, index.length);
188
+ position.set(entry.id, index.length);
150
189
  index.push(indexEntry);
151
190
  }
152
- applied++;
153
191
  }
154
- if (applied === 0) return;
155
192
  this.writeIndex(index);
156
193
  }
157
194
 
@@ -15,6 +15,12 @@ export type MaturityLevel = 'draft' | 'verified' | 'proven' | 'archived' | 'acti
15
15
 
16
16
  export type StorageLayer = 'personal' | 'team' | 'tech' | 'domain' | 'project' | 'system';
17
17
 
18
+ /** MaturityLevel 的运行时值域正本(store 写入闸与 lint 枚举校验共用) */
19
+ export const MATURITY_LEVELS: readonly MaturityLevel[] = ['draft', 'verified', 'proven', 'archived', 'active', 'deprecated'];
20
+
21
+ /** StorageLayer 的运行时值域正本(store 写入闸与 lint 枚举校验共用) */
22
+ export const STORAGE_LAYERS: readonly StorageLayer[] = ['personal', 'team', 'tech', 'domain', 'project', 'system'];
23
+
18
24
  /** How agent consumes this knowledge — drives injection strategy and lifecycle */
19
25
  export type ConsumptionMode = 'rule' | 'reference' | 'context' | 'signal';
20
26
 
@@ -101,7 +107,7 @@ export interface QueryFilter {
101
107
 
102
108
  // ── Lint ────────────────────────────────────────────────────
103
109
 
104
- export type LintIssueType = 'orphan' | 'contradiction' | 'outdated' | 'duplicate' | 'index_inconsistent';
110
+ export type LintIssueType = 'orphan' | 'contradiction' | 'outdated' | 'duplicate' | 'index_inconsistent' | 'invalid_enum';
105
111
 
106
112
  export interface LintIssue {
107
113
  type: LintIssueType;
@@ -22,7 +22,7 @@
22
22
  - 零 Token 成本:不调用 LLM
23
23
  - **凡以 `skip` 策略读 JSONL,坏行计数必须到达该消费面的用户可见输出,或在该调用点显式记名豁免并写明理由(harness#100)**——静默吞掉「数据不全」不是一种可选项。承载方式纯增量:新报告入口带计数,旧入口退化为丢计数的薄包装(#82 裁决 4 的兼容约束继续成立),不另立第二份计数概念、不把 `JsonlReadResult` 提升进包根(ADR-0003)
24
24
  - 机器可检:`src/utils/__tests__/jsonl-skip-disposition.test.ts` 三道闸——每个 `readJsonl` 调用点必须判得出策略(判不出即形状逃逸,失败)、skip 读点集合冻结(新增未声明即失败,站点消失却不删条目也失败)、每个读点上方 6 行内有 `计数去向:` 声明且邻居声明不得顶替
25
- - 三类去向(逐条对得上代码):**报告体内部的降级位**随报告体走 stdout——`constraints report` 的坏行提示紧挨既有的 `traceFileExists` 说明;**不挤动既有输出的诊断告知**走 stderr——`status`(`logError`,stdout 的 `记录数` 口径与字节不变)、`failure list`(#96 定稿的 `console.error`)、`constraints retire`(交互与 `--yes` 直达两条路径各自告知);**结构化面用字段**——`constraints report --json` 的 `skippedLines`、`--export` markdown 的警示行、studio 端点响应体(配套票 studio#451)
25
+ - 三类去向(逐条对得上代码):**报告体内部的降级位**随报告体走 stdout——`constraints report` 的坏行提示紧挨既有的 `traceFileExists` 说明;**不挤动既有输出的诊断告知**走 stderr——`status`(`logError`,stdout 的 `记录数` 口径与字节不变)、`failure list`(#96 定稿的 `console.error`)、`constraints retire`(交互与 `--yes` 直达两条路径各自告知);**结构化面用字段**——`constraints report --json-output` 的 `skippedLines`、`--export` markdown 的警示行、studio 端点响应体(配套票 studio#451)
26
26
 
27
27
  ## 注意事项
28
28
  - 追踪数据供 `harness constraints report` 统计与退役候选诊断消费(观测用途,不做自动降级)
@@ -14,7 +14,7 @@ import { createProjectFixture } from '../../test-setup/project-fixture';
14
14
 
15
15
  const trace = (constraintId: string): ExecutionTrace => ({
16
16
  constraintId,
17
- level: 'guideline',
17
+ severity: 'warning',
18
18
  timestamp: 1700000000000,
19
19
  result: 'pass',
20
20
  });
@@ -42,7 +42,7 @@ describe('TraceCollector', () => {
42
42
  const disabledCollector = new TraceCollector({ enabled: false });
43
43
  disabledCollector.record({
44
44
  constraintId: 'test',
45
- level: 'iron_law',
45
+ severity: 'error',
46
46
  timestamp: Date.now(),
47
47
  result: 'pass',
48
48
  });
@@ -54,7 +54,7 @@ describe('TraceCollector', () => {
54
54
  it('should append trace to file', () => {
55
55
  collector.record({
56
56
  constraintId: 'test',
57
- level: 'iron_law',
57
+ severity: 'error',
58
58
  timestamp: Date.now(),
59
59
  result: 'pass',
60
60
  });
@@ -64,7 +64,7 @@ describe('TraceCollector', () => {
64
64
 
65
65
  describe('recordPass()', () => {
66
66
  it('should record pass trace', () => {
67
- collector.recordPass('test_constraint', 'iron_law');
67
+ collector.recordPass('test_constraint', 'error');
68
68
  const call = mockFs.appendFileSync.mock.calls[0];
69
69
  const content = JSON.parse(call[1] as string);
70
70
  expect(content.result).toBe('pass');
@@ -73,7 +73,7 @@ describe('TraceCollector', () => {
73
73
 
74
74
  describe('recordFail()', () => {
75
75
  it('should record fail trace', () => {
76
- collector.recordFail('test_constraint', 'iron_law');
76
+ collector.recordFail('test_constraint', 'error');
77
77
  const call = mockFs.appendFileSync.mock.calls[0];
78
78
  const content = JSON.parse(call[1] as string);
79
79
  expect(content.result).toBe('fail');
@@ -91,8 +91,8 @@ describe('TraceCollector', () => {
91
91
 
92
92
  it('should parse all traces from file', () => {
93
93
  mockFs.readFileSync.mockReturnValueOnce(
94
- '{"constraintId":"a","level":"iron_law","timestamp":100,"result":"pass"}\n' +
95
- '{"constraintId":"b","level":"guideline","timestamp":200,"result":"fail"}\n'
94
+ '{"constraintId":"a","severity":"error","timestamp":100,"result":"pass"}\n' +
95
+ '{"constraintId":"b","severity":"warning","timestamp":200,"result":"fail"}\n'
96
96
  );
97
97
  const traces = collector.read();
98
98
  expect(traces.length).toBe(2);
@@ -100,19 +100,19 @@ describe('TraceCollector', () => {
100
100
 
101
101
  it('should apply constraintId filter', () => {
102
102
  mockFs.readFileSync.mockReturnValueOnce(
103
- '{"constraintId":"a","level":"iron_law","timestamp":100,"result":"pass"}\n' +
104
- '{"constraintId":"b","level":"guideline","timestamp":200,"result":"fail"}\n'
103
+ '{"constraintId":"a","severity":"error","timestamp":100,"result":"pass"}\n' +
104
+ '{"constraintId":"b","severity":"warning","timestamp":200,"result":"fail"}\n'
105
105
  );
106
106
  const traces = collector.read({ constraintId: 'a' });
107
107
  expect(traces.length).toBe(1);
108
108
  });
109
109
 
110
- it('should apply level filter', () => {
110
+ it('should apply severity filter', () => {
111
111
  mockFs.readFileSync.mockReturnValueOnce(
112
- '{"constraintId":"a","level":"iron_law","timestamp":100,"result":"pass"}\n' +
113
- '{"constraintId":"b","level":"guideline","timestamp":200,"result":"fail"}\n'
112
+ '{"constraintId":"a","severity":"error","timestamp":100,"result":"pass"}\n' +
113
+ '{"constraintId":"b","severity":"warning","timestamp":200,"result":"fail"}\n'
114
114
  );
115
- const traces = collector.read({ level: 'guideline' });
115
+ const traces = collector.read({ severity: 'warning' });
116
116
  expect(traces.length).toBe(1);
117
117
  });
118
118
  });
@@ -121,7 +121,7 @@ describe('TraceCollector', () => {
121
121
  it('should read recent traces', () => {
122
122
  const now = Date.now();
123
123
  mockFs.readFileSync.mockReturnValueOnce(
124
- `{"constraintId":"new","level":"iron_law","timestamp":${now - 1000},"result":"pass"}\n`
124
+ `{"constraintId":"new","severity":"error","timestamp":${now - 1000},"result":"pass"}\n`
125
125
  );
126
126
  const traces = collector.readRecent(1);
127
127
  expect(traces.length).toBe(1);
@@ -131,7 +131,7 @@ describe('TraceCollector', () => {
131
131
  describe('readByConstraint()', () => {
132
132
  it('should read by constraint', () => {
133
133
  mockFs.readFileSync.mockReturnValueOnce(
134
- '{"constraintId":"a","level":"iron_law","timestamp":100,"result":"pass"}\n'
134
+ '{"constraintId":"a","severity":"error","timestamp":100,"result":"pass"}\n'
135
135
  );
136
136
  const traces = collector.readByConstraint('a');
137
137
  expect(traces.length).toBe(1);
@@ -143,7 +143,7 @@ describe('TraceCollector', () => {
143
143
  mockFs.statSync.mockReturnValueOnce({ size: 11 * 1024 * 1024 } as any);
144
144
  collector.record({
145
145
  constraintId: 'test',
146
- level: 'iron_law',
146
+ severity: 'error',
147
147
  timestamp: Date.now(),
148
148
  result: 'pass',
149
149
  });
@@ -155,7 +155,7 @@ describe('TraceCollector', () => {
155
155
  it('should return stats', () => {
156
156
  mockFs.existsSync.mockReturnValueOnce(true);
157
157
  mockFs.readFileSync.mockReturnValueOnce(
158
- '{"constraintId":"a","level":"iron_law","timestamp":100,"result":"pass"}\n'
158
+ '{"constraintId":"a","severity":"error","timestamp":100,"result":"pass"}\n'
159
159
  );
160
160
  mockFs.statSync.mockReturnValueOnce({ size: 1024 } as any);
161
161
  const stats = collector.getStats();
@@ -166,9 +166,9 @@ describe('TraceCollector', () => {
166
166
  describe('坏行容错(harness#82 裁决 4:原「抛」改 skip,签名与返回类型不变)', () => {
167
167
  it('read() 单行损坏只丢该行,合法 trace 照常返回', () => {
168
168
  mockFs.readFileSync.mockReturnValueOnce(
169
- '{"constraintId":"a","level":"iron_law","timestamp":100,"result":"pass"}\n' +
169
+ '{"constraintId":"a","severity":"error","timestamp":100,"result":"pass"}\n' +
170
170
  '{"constraintId":"b","broken"\n' +
171
- '{"constraintId":"c","level":"guideline","timestamp":200,"result":"fail"}\n'
171
+ '{"constraintId":"c","severity":"warning","timestamp":200,"result":"fail"}\n'
172
172
  );
173
173
  const traces = collector.read();
174
174
  expect(traces.map(t => t.constraintId)).toEqual(['a', 'c']);
@@ -176,9 +176,9 @@ describe('TraceCollector', () => {
176
176
 
177
177
  it('read() 坏行不阻断过滤', () => {
178
178
  mockFs.readFileSync.mockReturnValueOnce(
179
- '{"constraintId":"a","level":"iron_law","timestamp":100,"result":"pass"}\n' +
179
+ '{"constraintId":"a","severity":"error","timestamp":100,"result":"pass"}\n' +
180
180
  'not-json\n' +
181
- '{"constraintId":"a","level":"iron_law","timestamp":200,"result":"fail"}\n'
181
+ '{"constraintId":"a","severity":"error","timestamp":200,"result":"fail"}\n'
182
182
  );
183
183
  const traces = collector.read({ constraintId: 'a' });
184
184
  expect(traces).toHaveLength(2);
@@ -187,8 +187,8 @@ describe('TraceCollector', () => {
187
187
  it('getStats() 首末行坏时不抛:totalLines 保持原始行数,时间戳取合法记录', () => {
188
188
  mockFs.readFileSync.mockReturnValueOnce(
189
189
  'not-json\n' +
190
- '{"constraintId":"a","level":"iron_law","timestamp":100,"result":"pass"}\n' +
191
- '{"constraintId":"b","level":"guideline","timestamp":200,"result":"fail"}\n' +
190
+ '{"constraintId":"a","severity":"error","timestamp":100,"result":"pass"}\n' +
191
+ '{"constraintId":"b","severity":"warning","timestamp":200,"result":"fail"}\n' +
192
192
  'also-bad\n'
193
193
  );
194
194
  mockFs.statSync.mockReturnValueOnce({ size: 2048 } as any);
@@ -94,8 +94,8 @@ export function summarizeTraces(traces: ExecutionTrace[]): TraceSummary[] {
94
94
  const summaries: TraceSummary[] = [];
95
95
 
96
96
  for (const [constraintId, group] of grouped) {
97
- // 提取层级(从第一条 trace)
98
- const level = group[0].level;
97
+ // 提取严重性(从第一条 trace)
98
+ const severity = group[0].severity;
99
99
 
100
100
  // 计算时间范围
101
101
  const timeRange = timeRangeOf(group.map(t => t.timestamp));
@@ -120,7 +120,7 @@ export function summarizeTraces(traces: ExecutionTrace[]): TraceSummary[] {
120
120
 
121
121
  summaries.push({
122
122
  constraintId,
123
- level,
123
+ severity,
124
124
  timeRange,
125
125
  totalChecks,
126
126
  passCount,
@@ -155,7 +155,7 @@ export function detectTraceAnomalies(
155
155
  anomalies.push({
156
156
  type: 'rising_fail_rate',
157
157
  constraintId: summary.constraintId,
158
- level: summary.level,
158
+ severity: summary.severity,
159
159
  message: `约束 ${summary.constraintId} 失败率 ${Math.round(summary.failRate * 100)}% 且趋势上升`,
160
160
  data: {
161
161
  currentRate: summary.failRate,
@@ -172,7 +172,7 @@ export function detectTraceAnomalies(
172
172
  anomalies.push({
173
173
  type: 'low_pass_rate',
174
174
  constraintId: summary.constraintId,
175
- level: summary.level,
175
+ severity: summary.severity,
176
176
  message: `约束 ${summary.constraintId} 通过率 ${Math.round(summary.passRate * 100)}%,低于 30%`,
177
177
  data: {
178
178
  currentRate: summary.passRate,
@@ -358,14 +358,13 @@ export class TraceAnalyzer {
358
358
  lines.push('');
359
359
 
360
360
  for (const summary of summaries) {
361
- const levelEmoji = {
362
- iron_law: '🔴',
363
- guideline: '🟡',
364
- prompt: '🟣',
365
- tip: '🔵',
366
- }[summary.level];
367
-
368
- lines.push(`${levelEmoji} **${summary.constraintId}**`);
361
+ const severityEmoji = {
362
+ error: '🔴',
363
+ warning: '🟡',
364
+ info: '🔵',
365
+ }[summary.severity];
366
+
367
+ lines.push(`${severityEmoji} **${summary.constraintId}**`);
369
368
  lines.push(` - Checks: ${summary.totalChecks}`);
370
369
  lines.push(` - Pass: ${Math.round(summary.passRate * 100)}%`);
371
370
  lines.push(` - Fail: ${Math.round(summary.failRate * 100)}%`);
@@ -38,8 +38,8 @@ const DEFAULT_CONFIG: TraceCollectorConfig = {
38
38
  * ```typescript
39
39
  * const collector = new TraceCollector({ projectPath: '/path/to/project' });
40
40
  * collector.record({
41
- * constraintId: 'no_fix_without_root_cause',
42
- * level: 'iron_law',
41
+ * constraintId: 'no_completion_without_verification',
42
+ * severity: 'error',
43
43
  * timestamp: Date.now(),
44
44
  * result: 'fail',
45
45
  * });
@@ -92,12 +92,12 @@ export class TraceCollector {
92
92
  */
93
93
  recordPass(
94
94
  constraintId: string,
95
- level: 'iron_law' | 'guideline',
95
+ severity: 'error' | 'warning',
96
96
  options?: Partial<ExecutionTrace>
97
97
  ): void {
98
98
  this.record({
99
99
  constraintId,
100
- level,
100
+ severity,
101
101
  timestamp: Date.now(),
102
102
  result: 'pass',
103
103
  ...options,
@@ -109,12 +109,12 @@ export class TraceCollector {
109
109
  */
110
110
  recordFail(
111
111
  constraintId: string,
112
- level: 'iron_law' | 'guideline',
112
+ severity: 'error' | 'warning',
113
113
  options?: Partial<ExecutionTrace>
114
114
  ): void {
115
115
  this.record({
116
116
  constraintId,
117
- level,
117
+ severity,
118
118
  timestamp: Date.now(),
119
119
  result: 'fail',
120
120
  ...options,
@@ -184,8 +184,8 @@ export class TraceCollector {
184
184
  return false;
185
185
  }
186
186
 
187
- // 层级
188
- if (filter.level && trace.level !== filter.level) {
187
+ // 严重性
188
+ if (filter.severity && trace.severity !== filter.severity) {
189
189
  return false;
190
190
  }
191
191
 
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * presets 纯数据测试
3
3
  *
4
- * 预设只保留数据(ADR-0001:筛选逻辑统一在 mergeConstraints),
4
+ * 预设只保留数据(ADR-0029:键按 severity 命名;筛选逻辑统一在 mergeConstraints),
5
5
  * 这里锁定数据形状与 strict→standard 别名语义。
6
6
  */
7
7
 
@@ -16,43 +16,39 @@ describe('预设数据', () => {
16
16
  describe('STRICT_PRESET', () => {
17
17
  it('应该启用所有约束', () => {
18
18
  expect(STRICT_PRESET.name).toBe('strict');
19
- expect(STRICT_PRESET.ironLaws).toBeNull();
20
- expect(STRICT_PRESET.guidelines).toBeNull();
21
- expect(STRICT_PRESET.prompts).toBeNull();
19
+ expect(STRICT_PRESET.errors).toBeNull();
20
+ expect(STRICT_PRESET.warnings).toBeNull();
22
21
  });
23
22
  });
24
23
 
25
24
  describe('STANDARD_PRESET', () => {
26
25
  it('应该启用所有约束', () => {
27
26
  expect(STANDARD_PRESET.name).toBe('standard');
28
- expect(STANDARD_PRESET.ironLaws).toBeNull();
29
- expect(STANDARD_PRESET.guidelines).toBeNull();
30
- expect(STANDARD_PRESET.prompts).toBeNull();
27
+ expect(STANDARD_PRESET.errors).toBeNull();
28
+ expect(STANDARD_PRESET.warnings).toBeNull();
31
29
  });
32
30
  });
33
31
 
34
32
  describe('RELAXED_PRESET', () => {
35
33
  it('应该只启用核心约束', () => {
36
34
  expect(RELAXED_PRESET.name).toBe('relaxed');
37
- expect(RELAXED_PRESET.ironLaws).toHaveLength(3);
38
- expect(RELAXED_PRESET.guidelines).toHaveLength(2);
39
- expect(RELAXED_PRESET.prompts).toEqual([]);
35
+ expect(RELAXED_PRESET.errors).toHaveLength(1);
36
+ expect(RELAXED_PRESET.warnings).toHaveLength(1);
40
37
  });
41
38
 
42
39
  it('应该包含 no_completion_without_verification', () => {
43
- expect(RELAXED_PRESET.ironLaws).toContain('no_completion_without_verification');
40
+ expect(RELAXED_PRESET.errors).toContain('no_completion_without_verification');
44
41
  });
45
42
 
46
- it('应该包含 no_bypass_checkpoint', () => {
47
- expect(RELAXED_PRESET.guidelines).toContain('no_bypass_checkpoint');
43
+ it('应该包含 no_hardcoded_credentials', () => {
44
+ expect(RELAXED_PRESET.warnings).toContain('no_hardcoded_credentials');
48
45
  });
49
46
  });
50
47
 
51
48
  describe('PRESETS_BY_NAME', () => {
52
49
  it('strict 是 standard 的别名(同为全部启用)', () => {
53
- expect(PRESETS_BY_NAME['strict']?.ironLaws).toBeNull();
54
- expect(PRESETS_BY_NAME['strict']?.guidelines).toBeNull();
55
- expect(PRESETS_BY_NAME['strict']?.prompts).toBeNull();
50
+ expect(PRESETS_BY_NAME['strict']?.errors).toBeNull();
51
+ expect(PRESETS_BY_NAME['strict']?.warnings).toBeNull();
56
52
  });
57
53
 
58
54
  it('三个预设名均可解析', () => {
@@ -2,10 +2,10 @@
2
2
  * 约束预设(纯数据)
3
3
  *
4
4
  * 预设定义哪些内置约束被启用。筛选与合并逻辑统一在
5
- * core/project-config-loader.ts 的 mergeConstraints(ADR-0001 生效集链路),
5
+ * core/project-config-loader.ts 的 mergeConstraints(生效集链路),
6
6
  * 本文件只保留预设数据,不再含函数。
7
7
  *
8
- * ADR-0001:kind 二元(check + prompt 纯注入层)。
8
+ * ADR-0029:桶按 severity 命名(errors / warnings),取代三层命名。
9
9
  */
10
10
 
11
11
  /**
@@ -15,14 +15,11 @@ export interface PresetConfig {
15
15
  /** 预设名称 */
16
16
  name: string;
17
17
 
18
- /** 启用的铁律 ID 列表(null 表示全部启用) */
19
- ironLaws: string[] | null;
18
+ /** 启用的 severity='error' 约束 ID 列表(null 表示全部启用) */
19
+ errors: string[] | null;
20
20
 
21
- /** 启用的指导原则 ID 列表(null 表示全部启用) */
22
- guidelines: string[] | null;
23
-
24
- /** 启用的提示 ID 列表(null 表示全部启用) */
25
- prompts: string[] | null;
21
+ /** 启用的 severity='warning' 约束 ID 列表(null 表示全部启用) */
22
+ warnings: string[] | null;
26
23
  }
27
24
 
28
25
  /**
@@ -32,40 +29,34 @@ export interface PresetConfig {
32
29
  */
33
30
  export const STRICT_PRESET: PresetConfig = {
34
31
  name: 'strict',
35
- ironLaws: null, // 全部启用
36
- guidelines: null, // 全部启用
37
- prompts: null, // 全部启用
32
+ errors: null, // 全部启用
33
+ warnings: null, // 全部启用
38
34
  };
39
35
 
40
36
  /**
41
37
  * 标准预设
42
38
  *
43
- * 铁律全部启用,指导原则和提示选择性启用
39
+ * 全部约束启用
44
40
  */
45
41
  export const STANDARD_PRESET: PresetConfig = {
46
42
  name: 'standard',
47
- ironLaws: null, // 全部启用
48
- guidelines: null, // 全部启用
49
- prompts: null, // 全部启用
43
+ errors: null, // 全部启用
44
+ warnings: null, // 全部启用
50
45
  };
51
46
 
52
47
  /**
53
48
  * 宽松预设
54
49
  *
55
- * 仅启用核心铁律,禁用提示
50
+ * 仅启用核心 error 级与凭证扫描
56
51
  */
57
52
  export const RELAXED_PRESET: PresetConfig = {
58
53
  name: 'relaxed',
59
- ironLaws: [
54
+ errors: [
60
55
  'no_completion_without_verification',
61
- 'incremental_progress',
62
- 'no_implementation_without_requirement',
63
56
  ],
64
- guidelines: [
65
- 'no_bypass_checkpoint',
57
+ warnings: [
66
58
  'no_hardcoded_credentials',
67
59
  ],
68
- prompts: [], // 禁用提示
69
60
  };
70
61
 
71
62
  /**
@@ -17,7 +17,7 @@ describe('ConstraintViolationError', () => {
17
17
  it('应该使用 result.message 作为错误消息', () => {
18
18
  const error = new ConstraintViolationError({
19
19
  id: 'test',
20
- level: 'iron_law',
20
+ severity: 'error',
21
21
  satisfied: false,
22
22
  message: '测试违规消息',
23
23
  checkedAt: new Date(),
@@ -31,7 +31,7 @@ describe('ConstraintViolationError', () => {
31
31
  it('应该在 message 为空时使用默认消息', () => {
32
32
  const error = new ConstraintViolationError({
33
33
  id: 'test',
34
- level: 'iron_law',
34
+ severity: 'error',
35
35
  satisfied: false,
36
36
  checkedAt: new Date(),
37
37
  });
@@ -42,7 +42,7 @@ describe('ConstraintViolationError', () => {
42
42
  it('应该在 message 为空字符串时使用默认消息', () => {
43
43
  const error = new ConstraintViolationError({
44
44
  id: 'test',
45
- level: 'iron_law',
45
+ severity: 'error',
46
46
  satisfied: false,
47
47
  message: '',
48
48
  checkedAt: new Date(),