@dommaker/harness 1.9.0 → 1.10.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 (253) hide show
  1. package/CHANGELOG.md +6 -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.js +2 -2
  20. package/dist/cli/commands/init.d.ts +0 -32
  21. package/dist/cli/commands/init.d.ts.map +1 -1
  22. package/dist/cli/commands/init.js +7 -149
  23. package/dist/cli/commands/init.js.map +1 -1
  24. package/dist/cli/commands/report.d.ts.map +1 -1
  25. package/dist/cli/commands/report.js +14 -17
  26. package/dist/cli/commands/report.js.map +1 -1
  27. package/dist/cli/commands/scaffold-templates.d.ts +0 -2
  28. package/dist/cli/commands/scaffold-templates.d.ts.map +1 -1
  29. package/dist/cli/commands/scaffold-templates.js +2 -37
  30. package/dist/cli/commands/scaffold-templates.js.map +1 -1
  31. package/dist/cli/commands/scaffold.d.ts +0 -2
  32. package/dist/cli/commands/scaffold.d.ts.map +1 -1
  33. package/dist/cli/commands/scaffold.js +0 -10
  34. package/dist/cli/commands/scaffold.js.map +1 -1
  35. package/dist/cli/commands/status.js +10 -10
  36. package/dist/cli/commands/status.js.map +1 -1
  37. package/dist/cli/commands/sync-docs/agents-syncer.d.ts +0 -2
  38. package/dist/cli/commands/sync-docs/agents-syncer.d.ts.map +1 -1
  39. package/dist/cli/commands/sync-docs/agents-syncer.js +4 -23
  40. package/dist/cli/commands/sync-docs/agents-syncer.js.map +1 -1
  41. package/dist/core/constraints/capabilities-parser.d.ts.map +1 -1
  42. package/dist/core/constraints/capabilities-parser.js +16 -4
  43. package/dist/core/constraints/capabilities-parser.js.map +1 -1
  44. package/dist/core/constraints/checker.d.ts +15 -24
  45. package/dist/core/constraints/checker.d.ts.map +1 -1
  46. package/dist/core/constraints/checker.js +33 -72
  47. package/dist/core/constraints/checker.js.map +1 -1
  48. package/dist/core/constraints/checkers/docs-freshness.js +1 -1
  49. package/dist/core/constraints/checkers/docs-freshness.js.map +1 -1
  50. package/dist/core/constraints/checkers/governance-presence.d.ts +2 -1
  51. package/dist/core/constraints/checkers/governance-presence.d.ts.map +1 -1
  52. package/dist/core/constraints/checkers/governance-presence.js +4 -3
  53. package/dist/core/constraints/checkers/governance-presence.js.map +1 -1
  54. package/dist/core/constraints/checkers/index.d.ts +1 -1
  55. package/dist/core/constraints/checkers/index.d.ts.map +1 -1
  56. package/dist/core/constraints/checkers/index.js +4 -8
  57. package/dist/core/constraints/checkers/index.js.map +1 -1
  58. package/dist/core/constraints/checkers/iron-flags.d.ts +3 -4
  59. package/dist/core/constraints/checkers/iron-flags.d.ts.map +1 -1
  60. package/dist/core/constraints/checkers/iron-flags.js +4 -5
  61. package/dist/core/constraints/checkers/iron-flags.js.map +1 -1
  62. package/dist/core/constraints/checkers/no-test-simplification.js +1 -1
  63. package/dist/core/constraints/checkers/no-test-simplification.js.map +1 -1
  64. package/dist/core/constraints/checkers/types.d.ts +1 -1
  65. package/dist/core/constraints/checkers/types.js +1 -1
  66. package/dist/core/constraints/definitions/guidelines.d.ts +4 -4
  67. package/dist/core/constraints/definitions/guidelines.d.ts.map +1 -1
  68. package/dist/core/constraints/definitions/guidelines.js +9 -25
  69. package/dist/core/constraints/definitions/guidelines.js.map +1 -1
  70. package/dist/core/constraints/definitions/iron-laws.d.ts +4 -4
  71. package/dist/core/constraints/definitions/iron-laws.d.ts.map +1 -1
  72. package/dist/core/constraints/definitions/iron-laws.js +10 -81
  73. package/dist/core/constraints/definitions/iron-laws.js.map +1 -1
  74. package/dist/core/constraints/definitions.d.ts +7 -9
  75. package/dist/core/constraints/definitions.d.ts.map +1 -1
  76. package/dist/core/constraints/definitions.js +13 -16
  77. package/dist/core/constraints/definitions.js.map +1 -1
  78. package/dist/core/constraints/index.d.ts +2 -5
  79. package/dist/core/constraints/index.d.ts.map +1 -1
  80. package/dist/core/constraints/index.js +3 -13
  81. package/dist/core/constraints/index.js.map +1 -1
  82. package/dist/core/constraints/injection-writer.d.ts +10 -38
  83. package/dist/core/constraints/injection-writer.d.ts.map +1 -1
  84. package/dist/core/constraints/injection-writer.js +17 -54
  85. package/dist/core/constraints/injection-writer.js.map +1 -1
  86. package/dist/core/constraints/run-env.d.ts +1 -10
  87. package/dist/core/constraints/run-env.d.ts.map +1 -1
  88. package/dist/core/constraints/run-env.js +1 -22
  89. package/dist/core/constraints/run-env.js.map +1 -1
  90. package/dist/core/constraints/usage-report.d.ts +5 -7
  91. package/dist/core/constraints/usage-report.d.ts.map +1 -1
  92. package/dist/core/constraints/usage-report.js +5 -10
  93. package/dist/core/constraints/usage-report.js.map +1 -1
  94. package/dist/core/effective-constraints.d.ts +12 -22
  95. package/dist/core/effective-constraints.d.ts.map +1 -1
  96. package/dist/core/effective-constraints.js +13 -30
  97. package/dist/core/effective-constraints.js.map +1 -1
  98. package/dist/core/index.d.ts +2 -2
  99. package/dist/core/index.d.ts.map +1 -1
  100. package/dist/core/index.js +3 -9
  101. package/dist/core/index.js.map +1 -1
  102. package/dist/core/project-config-loader.d.ts +4 -42
  103. package/dist/core/project-config-loader.d.ts.map +1 -1
  104. package/dist/core/project-config-loader.js +16 -125
  105. package/dist/core/project-config-loader.js.map +1 -1
  106. package/dist/core/validators/passes-gate.d.ts +2 -2
  107. package/dist/core/validators/passes-gate.js +6 -6
  108. package/dist/core/validators/passes-gate.js.map +1 -1
  109. package/dist/index.d.ts +7 -10
  110. package/dist/index.d.ts.map +1 -1
  111. package/dist/index.js +9 -18
  112. package/dist/index.js.map +1 -1
  113. package/dist/monitoring/trace-analyzer.d.ts.map +1 -1
  114. package/dist/monitoring/trace-analyzer.js +11 -12
  115. package/dist/monitoring/trace-analyzer.js.map +1 -1
  116. package/dist/monitoring/traces.d.ts +4 -4
  117. package/dist/monitoring/traces.d.ts.map +1 -1
  118. package/dist/monitoring/traces.js +8 -8
  119. package/dist/monitoring/traces.js.map +1 -1
  120. package/dist/presets/standard.d.ts +8 -10
  121. package/dist/presets/standard.d.ts.map +1 -1
  122. package/dist/presets/standard.js +10 -16
  123. package/dist/presets/standard.js.map +1 -1
  124. package/dist/types/constraint.d.ts +24 -35
  125. package/dist/types/constraint.d.ts.map +1 -1
  126. package/dist/types/constraint.js +8 -7
  127. package/dist/types/constraint.js.map +1 -1
  128. package/dist/types/passes-gate.d.ts +5 -5
  129. package/dist/types/passes-gate.d.ts.map +1 -1
  130. package/dist/types/project-config.d.ts +8 -66
  131. package/dist/types/project-config.d.ts.map +1 -1
  132. package/dist/types/project-config.js +3 -2
  133. package/dist/types/project-config.js.map +1 -1
  134. package/dist/types/trace.d.ts +8 -10
  135. package/dist/types/trace.d.ts.map +1 -1
  136. package/package.json +2 -2
  137. package/src/CONTEXT.md +2 -2
  138. package/src/__tests__/checker-extra.test.ts +37 -61
  139. package/src/__tests__/checker.test.ts +10 -115
  140. package/src/__tests__/effective-constraints.test.ts +18 -83
  141. package/src/__tests__/iron-laws.test.ts +71 -90
  142. package/src/__tests__/passes-gate-check.test.ts +2 -2
  143. package/src/__tests__/project-config-loader-extra.test.ts +22 -123
  144. package/src/__tests__/project-config-loader.test.ts +5 -33
  145. package/src/__tests__/public-exports.test.ts +1 -7
  146. package/src/__tests__/public-type-surface.test.ts +2 -5
  147. package/src/__tests__/public-value-surface.test.ts +1 -7
  148. package/src/__tests__/skip-semantics.test.ts +21 -37
  149. package/src/__tests__/trace-analyzer-extra.test.ts +14 -14
  150. package/src/__tests__/trace-analyzer.test.ts +21 -21
  151. package/src/__tests__/trace-collector.test.ts +22 -22
  152. package/src/__tests__/traces.test.ts +27 -28
  153. package/src/cli/commands/CONTEXT.md +9 -9
  154. package/src/cli/commands/__tests__/check-read-count.test.ts +4 -15
  155. package/src/cli/commands/__tests__/check-skip-output.test.ts +14 -14
  156. package/src/cli/commands/__tests__/check.test.ts +14 -31
  157. package/src/cli/commands/__tests__/constraints-report.test.ts +12 -80
  158. package/src/cli/commands/__tests__/constraints-retire.test.ts +42 -231
  159. package/src/cli/commands/__tests__/init-governance-io.test.ts +12 -58
  160. package/src/cli/commands/__tests__/init-ondisk.test.ts +12 -20
  161. package/src/cli/commands/__tests__/init.test.ts +0 -11
  162. package/src/cli/commands/__tests__/project-path-anchoring.test.ts +2 -2
  163. package/src/cli/commands/__tests__/report-extra.test.ts +4 -4
  164. package/src/cli/commands/__tests__/report.test.ts +1 -1
  165. package/src/cli/commands/__tests__/scaffold.test.ts +4 -14
  166. package/src/cli/commands/__tests__/status-analysis-wiring.test.ts +3 -3
  167. package/src/cli/commands/__tests__/status-extra.test.ts +14 -14
  168. package/src/cli/commands/__tests__/status.test.ts +12 -12
  169. package/src/cli/commands/__tests__/sync-docs-agents.test.ts +1 -1
  170. package/src/cli/commands/check.ts +43 -82
  171. package/src/cli/commands/constraints-report.ts +4 -59
  172. package/src/cli/commands/constraints-retire.ts +49 -154
  173. package/src/cli/commands/constraints.ts +11 -26
  174. package/src/cli/commands/definitions.ts +2 -2
  175. package/src/cli/commands/init.ts +8 -179
  176. package/src/cli/commands/report.ts +19 -23
  177. package/src/cli/commands/scaffold-templates.ts +1 -37
  178. package/src/cli/commands/scaffold.ts +0 -11
  179. package/src/cli/commands/status.ts +10 -10
  180. package/src/cli/commands/sync-docs/agents-syncer.ts +4 -25
  181. package/src/core/CONTEXT.md +9 -9
  182. package/src/core/constraints/__tests__/capability-counts.test.ts +5 -5
  183. package/src/core/constraints/__tests__/collect-constraints.test.ts +13 -8
  184. package/src/core/constraints/__tests__/git-evidence.test.ts +4 -7
  185. package/src/core/constraints/__tests__/injection-writer.test.ts +8 -61
  186. package/src/core/constraints/__tests__/run-env.test.ts +5 -34
  187. package/src/core/constraints/__tests__/trace-injection.test.ts +1 -1
  188. package/src/core/constraints/capabilities-parser.ts +17 -5
  189. package/src/core/constraints/checker.ts +39 -88
  190. package/src/core/constraints/checkers/__tests__/check-outcome.test.ts +5 -8
  191. package/src/core/constraints/checkers/__tests__/governance-presence.test.ts +1 -53
  192. package/src/core/constraints/checkers/__tests__/iron-flags.test.ts +5 -25
  193. package/src/core/constraints/checkers/docs-freshness.ts +1 -1
  194. package/src/core/constraints/checkers/governance-presence.ts +4 -3
  195. package/src/core/constraints/checkers/index.ts +4 -10
  196. package/src/core/constraints/checkers/iron-flags.ts +3 -12
  197. package/src/core/constraints/checkers/no-test-simplification.ts +1 -1
  198. package/src/core/constraints/checkers/types.ts +1 -1
  199. package/src/core/constraints/definitions/guidelines.ts +8 -25
  200. package/src/core/constraints/definitions/iron-laws.ts +9 -82
  201. package/src/core/constraints/definitions.ts +14 -15
  202. package/src/core/constraints/index.ts +4 -19
  203. package/src/core/constraints/injection-writer.ts +16 -61
  204. package/src/core/constraints/run-env.ts +1 -33
  205. package/src/core/constraints/usage-report.ts +8 -15
  206. package/src/core/effective-constraints.ts +14 -45
  207. package/src/core/index.ts +3 -10
  208. package/src/core/project-config-loader.ts +20 -145
  209. package/src/core/validators/passes-gate.ts +6 -6
  210. package/src/failure/CONTEXT.md +1 -1
  211. package/src/hooks/__tests__/bootstrap.test.ts +3 -5
  212. package/src/index.ts +8 -18
  213. package/src/monitoring/__tests__/trace-file-anchoring.test.ts +1 -1
  214. package/src/monitoring/__tests__/traces.test.ts +22 -22
  215. package/src/monitoring/trace-analyzer.ts +12 -13
  216. package/src/monitoring/traces.ts +8 -8
  217. package/src/presets/__tests__/standard.test.ts +12 -16
  218. package/src/presets/standard.ts +14 -23
  219. package/src/types/__tests__/types.test.ts +3 -3
  220. package/src/types/constraint.ts +26 -40
  221. package/src/types/passes-gate.ts +5 -5
  222. package/src/types/project-config.ts +8 -77
  223. package/src/types/trace.ts +8 -11
  224. package/dist/core/constraints/agent-prompt-renderer.d.ts +0 -32
  225. package/dist/core/constraints/agent-prompt-renderer.d.ts.map +0 -1
  226. package/dist/core/constraints/agent-prompt-renderer.js +0 -55
  227. package/dist/core/constraints/agent-prompt-renderer.js.map +0 -1
  228. package/dist/core/constraints/checkers/no-bypass-checkpoint.d.ts +0 -6
  229. package/dist/core/constraints/checkers/no-bypass-checkpoint.d.ts.map +0 -1
  230. package/dist/core/constraints/checkers/no-bypass-checkpoint.js +0 -40
  231. package/dist/core/constraints/checkers/no-bypass-checkpoint.js.map +0 -1
  232. package/dist/core/constraints/definitions/prompts.d.ts +0 -12
  233. package/dist/core/constraints/definitions/prompts.d.ts.map +0 -1
  234. package/dist/core/constraints/definitions/prompts.js +0 -400
  235. package/dist/core/constraints/definitions/prompts.js.map +0 -1
  236. package/dist/core/constraints/injection-drift.d.ts +0 -61
  237. package/dist/core/constraints/injection-drift.d.ts.map +0 -1
  238. package/dist/core/constraints/injection-drift.js +0 -131
  239. package/dist/core/constraints/injection-drift.js.map +0 -1
  240. package/dist/core/constraints/injection-renderer.d.ts +0 -23
  241. package/dist/core/constraints/injection-renderer.d.ts.map +0 -1
  242. package/dist/core/constraints/injection-renderer.js +0 -44
  243. package/dist/core/constraints/injection-renderer.js.map +0 -1
  244. package/src/cli/commands/__tests__/check-drift.test.ts +0 -158
  245. package/src/cli/commands/__tests__/init-injection.test.ts +0 -466
  246. package/src/core/constraints/__tests__/agent-prompt-renderer.test.ts +0 -129
  247. package/src/core/constraints/__tests__/injection-drift.test.ts +0 -247
  248. package/src/core/constraints/agent-prompt-renderer.ts +0 -67
  249. package/src/core/constraints/checkers/__tests__/no-bypass-checkpoint.test.ts +0 -76
  250. package/src/core/constraints/checkers/no-bypass-checkpoint.ts +0 -40
  251. package/src/core/constraints/definitions/prompts.ts +0 -414
  252. package/src/core/constraints/injection-drift.ts +0 -142
  253. package/src/core/constraints/injection-renderer.ts +0 -44
@@ -31,11 +31,10 @@ describe('TraceCollector', () => {
31
31
  test('should record a trace', () => {
32
32
  collector.record({
33
33
  constraintId: 'no_fix_without_root_cause',
34
- level: 'iron_law',
34
+ severity: 'error',
35
35
  timestamp: Date.now(),
36
36
  result: 'fail',
37
37
  operation: 'code_implementation',
38
- severity: 'error',
39
38
  });
40
39
 
41
40
  const traces = collector.read();
@@ -46,7 +45,7 @@ describe('TraceCollector', () => {
46
45
 
47
46
  test('should record multiple traces', () => {
48
47
  for (let i = 0; i < 5; i++) {
49
- collector.recordPass('test_constraint', 'guideline');
48
+ collector.recordPass('test_constraint', 'warning');
50
49
  }
51
50
 
52
51
  const traces = collector.read();
@@ -55,9 +54,9 @@ describe('TraceCollector', () => {
55
54
  });
56
55
 
57
56
  test('should filter traces by constraint ID', () => {
58
- collector.recordPass('constraint_a', 'iron_law');
59
- collector.recordFail('constraint_b', 'guideline');
60
- collector.recordFail('constraint_a', 'iron_law');
57
+ collector.recordPass('constraint_a', 'error');
58
+ collector.recordFail('constraint_b', 'warning');
59
+ collector.recordFail('constraint_a', 'error');
61
60
 
62
61
  const traces = collector.read({ constraintId: 'constraint_a' });
63
62
  expect(traces.length).toBe(2);
@@ -70,14 +69,14 @@ describe('TraceCollector', () => {
70
69
 
71
70
  collector.record({
72
71
  constraintId: 'old_trace',
73
- level: 'guideline',
72
+ severity: 'warning',
74
73
  timestamp: oneHourAgo - 1000, // 1小时前更早
75
74
  result: 'pass',
76
75
  });
77
76
 
78
77
  collector.record({
79
78
  constraintId: 'new_trace',
80
- level: 'guideline',
79
+ severity: 'warning',
81
80
  timestamp: now,
82
81
  result: 'pass',
83
82
  });
@@ -95,7 +94,7 @@ describe('TraceCollector', () => {
95
94
  for (let i = 0; i < 10; i++) {
96
95
  collector.record({
97
96
  constraintId: `constraint_${i}`,
98
- level: 'guideline',
97
+ severity: 'warning',
99
98
  timestamp: Date.now() - i * 60 * 1000, // 每分钟一个
100
99
  result: i % 2 === 0 ? 'pass' : 'fail',
101
100
  });
@@ -108,7 +107,7 @@ describe('TraceCollector', () => {
108
107
 
109
108
  test('should provide stats', () => {
110
109
  for (let i = 0; i < 10; i++) {
111
- collector.recordPass(`constraint_${i}`, 'guideline');
110
+ collector.recordPass(`constraint_${i}`, 'warning');
112
111
  }
113
112
 
114
113
  const stats = collector.getStats();
@@ -117,8 +116,8 @@ describe('TraceCollector', () => {
117
116
  });
118
117
 
119
118
  test('should filter traces by result', () => {
120
- collector.recordFail('constraint_a', 'guideline');
121
- collector.recordPass('constraint_b', 'guideline');
119
+ collector.recordFail('constraint_a', 'warning');
120
+ collector.recordPass('constraint_b', 'warning');
122
121
 
123
122
  const traces = collector.read({ result: 'fail' });
124
123
  expect(traces).toHaveLength(1);
@@ -127,12 +126,12 @@ describe('TraceCollector', () => {
127
126
 
128
127
  test('should filter traces by projectPath', () => {
129
128
  collector.record({
130
- constraintId: 'c1', level: 'guideline', timestamp: Date.now(),
131
- result: 'pass', projectPath: '/project/a', severity: 'info',
129
+ constraintId: 'c1', severity: 'warning', timestamp: Date.now(),
130
+ result: 'pass', projectPath: '/project/a',
132
131
  });
133
132
  collector.record({
134
- constraintId: 'c2', level: 'guideline', timestamp: Date.now(),
135
- result: 'pass', projectPath: '/project/b', severity: 'info',
133
+ constraintId: 'c2', severity: 'warning', timestamp: Date.now(),
134
+ result: 'pass', projectPath: '/project/b',
136
135
  });
137
136
 
138
137
  const traces = collector.read({ projectPath: '/project/a' });
@@ -142,12 +141,12 @@ describe('TraceCollector', () => {
142
141
 
143
142
  test('should filter traces by sessionId', () => {
144
143
  collector.record({
145
- constraintId: 'c1', level: 'guideline', timestamp: Date.now(),
146
- result: 'pass', sessionId: 'session-1', severity: 'info',
144
+ constraintId: 'c1', severity: 'warning', timestamp: Date.now(),
145
+ result: 'pass', sessionId: 'session-1',
147
146
  });
148
147
  collector.record({
149
- constraintId: 'c2', level: 'guideline', timestamp: Date.now(),
150
- result: 'pass', sessionId: 'session-2', severity: 'info',
148
+ constraintId: 'c2', severity: 'warning', timestamp: Date.now(),
149
+ result: 'pass', sessionId: 'session-2',
151
150
  });
152
151
 
153
152
  const traces = collector.read({ sessionId: 'session-2' });
@@ -215,7 +214,7 @@ describe('TraceAnalyzer', () => {
215
214
  for (let i = 0; i < 10; i++) {
216
215
  collector.record({
217
216
  constraintId: 'constraint_a',
218
- level: 'guideline',
217
+ severity: 'warning',
219
218
  timestamp: Date.now() - i * 60 * 1000,
220
219
  result: i < 7 ? 'pass' : 'fail',
221
220
  });
@@ -224,7 +223,7 @@ describe('TraceAnalyzer', () => {
224
223
  for (let i = 0; i < 5; i++) {
225
224
  collector.record({
226
225
  constraintId: 'constraint_b',
227
- level: 'iron_law',
226
+ severity: 'error',
228
227
  timestamp: Date.now() - i * 60 * 1000,
229
228
  result: i < 3 ? 'pass' : 'fail',
230
229
  });
@@ -253,7 +252,7 @@ describe('TraceAnalyzer', () => {
253
252
  for (let i = 0; i < 20; i++) {
254
253
  collector.record({
255
254
  constraintId: 'rising_fail_constraint',
256
- level: 'iron_law',
255
+ severity: 'error',
257
256
  timestamp: now - (40 - i) * 60 * 1000,
258
257
  result: 'pass',
259
258
  });
@@ -263,7 +262,7 @@ describe('TraceAnalyzer', () => {
263
262
  for (let i = 0; i < 20; i++) {
264
263
  collector.record({
265
264
  constraintId: 'rising_fail_constraint',
266
- level: 'iron_law',
265
+ severity: 'error',
267
266
  timestamp: now - i * 60 * 1000,
268
267
  result: i < 15 ? 'fail' : 'pass', // 75% 失败率
269
268
  });
@@ -284,7 +283,7 @@ describe('TraceAnalyzer', () => {
284
283
  for (let i = 0; i < 10; i++) {
285
284
  collector.record({
286
285
  constraintId: 'stable_constraint',
287
- level: 'guideline',
286
+ severity: 'warning',
288
287
  timestamp: now - i * 60 * 1000,
289
288
  result: 'pass',
290
289
  });
@@ -298,7 +297,7 @@ describe('TraceAnalyzer', () => {
298
297
 
299
298
  test('should save and load summary', () => {
300
299
  for (let i = 0; i < 5; i++) {
301
- collector.recordPass('test_constraint', 'guideline');
300
+ collector.recordPass('test_constraint', 'warning');
302
301
  }
303
302
 
304
303
  const summaries = analyzer.analyzeRecent(1);
@@ -312,8 +311,8 @@ describe('TraceAnalyzer', () => {
312
311
 
313
312
  test('should generate report', () => {
314
313
  for (let i = 0; i < 5; i++) {
315
- collector.recordPass('constraint_a', 'guideline');
316
- collector.recordFail('constraint_b', 'iron_law');
314
+ collector.recordPass('constraint_a', 'warning');
315
+ collector.recordFail('constraint_b', 'error');
317
316
  }
318
317
 
319
318
  const summaries = analyzer.analyzeRecent(1);
@@ -12,13 +12,13 @@ H5(#44)起:
12
12
  - 命令契约(`src/cli/command-contract.ts`,上层目录)— `CommandResult` / `CommandKind` / `CommandIO` / `processIO` / `captureIO` / `lastJsonOutput` / `log` / `logError`
13
13
  - 状态文件接缝(`src/cli/state-io.ts`,上层目录)— `.harness/.state.json` 读-改-写的唯一入口 `StateIO`(`read()`/`write()`)+ 状态类型 `HarnessState` + 缺省真实 fs 实现 `fileStateIO(projectPath)`;`check` / `status` 经可选 `stateIO` 参数注入(ADR-0026,harness#148)
14
14
  - 门禁命令共享面(`src/cli/gate-command.ts`,上层目录)— `GateDecision → CommandResult` 的唯一映射 `gateCommandResult`,加 ✓/✗ 输出骨架 `reportGateDecision` 与出错横幅 `reportGateError`。同一句失败措辞此前抄在 6 个 handler 里(「`<id>` gate denied」×6 / 「`<id>` gate error」×4),门禁特有的指标行经 `onPass`/`onFail` 闭包传入(架构评审候选1)
15
- - 脚手架落盘面(`src/cli/commands/scaffold.ts`)— 受管文件(managed file)三态判定 `writeManagedFile` / 一组落盘 `runPlan` + 9 个站点工厂(含对外文案);模板正文住 `scaffold-templates.ts`,落盘注入面 `ScaffoldFileSystem` 可换内存替身(harness#132);CI 站点工厂 `harnessCheckCiFile` 带平台维度(github / gitlab,harness#143);两道 Git hook 工厂 `preCommitHookFile` / `prePushHookFile`(后者 harness#144,带可执行位)
15
+ - 脚手架落盘面(`src/cli/commands/scaffold.ts`)— 受管文件(managed file)三态判定 `writeManagedFile` / 一组落盘 `runPlan` + 8 个站点工厂(含对外文案;ADR-0029:custom-constraints.yml 示例站随 custom 约束面退役移除);模板正文住 `scaffold-templates.ts`,落盘注入面 `ScaffoldFileSystem` 可换内存替身(harness#132);CI 站点工厂 `harnessCheckCiFile` 带平台维度(github / gitlab,harness#143);两道 Git hook 工厂 `preCommitHookFile` / `prePushHookFile`(后者 harness#144,带可执行位)
16
16
  - knowledge 投影面(`src/cli/commands/knowledge-view.ts`)— 11 个 knowledge 子操作的**display model** 渲染与出口:`emitKnowledgeView`(json/人读唯一分派 + 退出码)、`announce`(取数期进度行,`--json` 下静默)、`TONE_STYLES` 角色→样式单表、维度/规则 label 与 `toneForMaturity`/`toneForScore`/`toneForSeverity` 三张映射、`resolveKnowledgeBaseDir` + `openKnowledgeStore`(路径兜底与 store 构造单点)(harness#133)
17
17
  - 各命令文件:check / validate / passes-gate / init / report / status / spec / sync-docs / knowledge / sdd / failure / posteval-plan / release / constraints / spec-baseline-check
18
18
  - 6 门禁命令实现在 `acceptance` / `command` / `contract` / `performance` / `review` / `security`(其 CLI 元数据在 `src/gates/definitions.ts`,形状同为 `CommandDefinition`,ADR-0007);**判定一律穿过统一接口 `evaluate()`**,成败与措辞由共享面映射。`command` 只复用映射、保留自己的单行 ✓/✗ 输出(已对外的机器友好形状);`security audit` / `acceptance list` / `contract validate-schema` / `review status` / `command --list/--level` 是只展示不判断的子命令,直读报告面(`scan()` 等),不套骨架
19
19
  - CLI 子命令 `command` 的两个分支共用同一台 gate 实例(#135/ADR-0024):默认分支经 `evaluate()` 取裁决、`--level` 经同一实例的 `getRiskLevel()` 取等级,命令侧不再引用模块级单例出口——「配置传进去却到不了判定」的那条路已封。`--strict` 旗帜随 `CommandGateConfig.strict`(零消费者、从不生效)一并删除
20
20
 
21
- `constraints` 下挂治理子命令:`constraints report`(使用统计 + 退役候选诊断 + 配置健康 + 注入漂移,`--export` 脱敏)、`constraints retire`(交互选择 + 人确认退役;带 id 直达需显式 `--yes`(#24 人确认闸门),无 `--yes` 报错 + 非零退出码且不落盘;内置落 config.yml retired 元数据,custom custom-constraints.yml 条目 retired 段(studio#82 D6 一处真相)+ KnowledgeStore 沉淀 + 治理注入段同步)。
21
+ `constraints` 下挂治理子命令:`constraints report`(使用统计 + 退役候选诊断 + 配置健康,`--export` 脱敏)、`constraints retire`(交互选择 + 人确认退役;带 id 直达需显式 `--yes`(#24 人确认闸门),无 `--yes` 报错 + 非零退出码且不落盘;落 config.yml retired 元数据 + KnowledgeStore 沉淀。ADR-0029:注入漂移小节、prompt 注入清单、custom 落点与治理注入段同步已随文本注入层关停移除)。
22
22
 
23
23
  ## 依赖关系
24
24
  - 依赖 `src/core/constraints/` 约束引擎
@@ -51,15 +51,15 @@ H5(#44)起:
51
51
 
52
52
  ## 注意事项
53
53
  - 新增命令需同步更新 CLAUDE.md / CAPABILITIES.md / src/CONTEXT.md
54
- - **scaffold 三态模型(harness#132 已落地)**:init/validate 的「目标在不在场→写或告知」判定(9 站点 = init 7 + validate 2,#132 收口时 8 处)收口为 scaffold 模块。术语:**managed file**(受管文件)= harness 模板拥有正本、用户可持有的落盘文件;三态 = `created`(不在场→落盘)/ `exists`(在场→告知跳过,**不细分内容是否等于模板**,内容比较是独立特性)/ `manual`(在场→打印片段请用户手工合并)。纪律:scaffold 不持有覆盖/跳过策略(`--no-git-hooks`/`--no-github-actions` 由命令层翻成 plan 列表,scaffold 只见 plan);模板留代码内住 `scaffold-templates.ts`,不落 `templates/`(其无运行时消费者、integrity 清单不覆盖);**打印给用户的片段必须是落盘正文的一部分**(GH Actions 站点冲突分支打印完整 workflow 全文而非 job 片段,#103 判据);`--print-snippets` 的片段视图同判据且**由正本裁剪派生**(harness#153:`GITHUB_ACTIONS_SNIPPET` = `HARNESS_CHECK_WORKFLOW` 的 `jobs:` 段之后,形状与 GitLab 侧「打印的是落盘正文的尾巴」对仗。它曾是手抄的第二份文本,比落盘正本少 `validate` / `passes-gate` 两道门禁——照抄的用户拿到比同版本 `harness init` 弱一半的 CI;引导语随之改准为「以下正文取自 harness-check.yml 的 job 段」,`--print-snippets` 旗帜本身保留);gitlab 形同判据由 harness#157 补齐——`printSnippets` 收 `governanceLevel`、打 `renderGitLabCiJobs(governanceLevel)`,与落盘 / 冲突分支同一个正本函数(此前打无 level 的 `GITLAB_CI_SNIPPET`,`-g` 档照抄的用户静默少拿治理与 docs 新鲜度两个任务);CLAUDE.md/AGENTS.md 标记化幂等写(读-改-写形状)不纳入 scaffold;死选项 `-t/--type` 已随本票删除。GitLab CI 接线决议见下条;pre-push 站点已随 harness#144 落地,见「本地防线两道」条
55
- - 9 站点的落点(#132 收口时 8 处,pre-push 是 harness#144 加的第 9 处):`scaffold.ts` 存 9 个 `ManagedFile` 工厂(目标路径 + 三态文案 + 冲突片段),`init.ts` 只留「前置告知(无 .git / 目录不在场 / 已有 workflow 覆盖治理命令)+ runPlan」;原住 `validate.ts` 的 `createExampleCheckpoint`/`createExampleResolutions` 随工厂迁入 scaffold,`init.ts` 对 `validate` 的反向 import 因此消失,`validate.ts` 反向消费 `scaffold-templates` 的检查点文件路径约定。新增受管文件 = 一个工厂 + 一条 plan。**#132 口径外的第 9 处同形写手**(`sync-docs/context-syncer.ts:createContextMd` 自带一份 CONTEXT.md 正文、与正本已漂移)已于 harness#150 归位——现走 `contextDocFile` + `runPlan`,与 init 共用同一模板正本(`scaffold-templates.ts:renderContextDoc`)
56
- - 可检面:`__tests__/scaffold.test.ts`(注入内存 fs 替身,零真实文件系统测全三态 + 9 站点文案逐字冻结 + 同源闸及其反证探针(两道 hook 各一条)+ 片段视图的「是正本裁出来的一段」包含闸)、`__tests__/init-ondisk.test.ts`(临时目录真跑 init:第一遍逐字节断言落盘内容并对齐改造前的整屏输出(#149 起这份整屏多出治理段 writer 的一行落盘提示——那行本就一直在真机上,只是此前 io 被丢、不进捕获面),第二遍断言 9 站点第三态与「打印即磁盘正文」;另有「`--print-snippets` 的 GitHub Actions 片段视图」两道闸——同源闸对 `harness-check.yml` 的字节级冻结基线、steps 集合闸按 YAML 解析枚举 job 的 `run:` 命令;harness#157 起「`--print-snippets` 的 GitLab CI 片段视图」四道闸——`-g standard` 同源闸(打印正文逐字等于 `GITLAB_GOVERNED` 基线的任务段 + 与真落盘 `.gitlab-ci.yml` 按 YAML 解析比对 harness-* 任务集合,正本删一个任务即红)、script 集合闸、不传 `-g` 单任务回归、`-g` 非法值 usage-error 零打印)
54
+ - **scaffold 三态模型(harness#132 已落地)**:init/validate 的「目标在不在场→写或告知」判定(站点数历经 8→9→8:#132 收口时 8 处,harness#144 加 pre-push 成 9,ADR-0029 撤 custom-constraints.yml 示例站回 8 = init 6 + validate 2)收口为 scaffold 模块。术语:**managed file**(受管文件)= harness 模板拥有正本、用户可持有的落盘文件;三态 = `created`(不在场→落盘)/ `exists`(在场→告知跳过,**不细分内容是否等于模板**,内容比较是独立特性)/ `manual`(在场→打印片段请用户手工合并)。纪律:scaffold 不持有覆盖/跳过策略(`--no-git-hooks`/`--no-github-actions` 由命令层翻成 plan 列表,scaffold 只见 plan);模板留代码内住 `scaffold-templates.ts`,不落 `templates/`(其无运行时消费者、integrity 清单不覆盖);**打印给用户的片段必须是落盘正文的一部分**(GH Actions 站点冲突分支打印完整 workflow 全文而非 job 片段,#103 判据);`--print-snippets` 的片段视图同判据且**由正本裁剪派生**(harness#153:`GITHUB_ACTIONS_SNIPPET` = `HARNESS_CHECK_WORKFLOW` 的 `jobs:` 段之后,形状与 GitLab 侧「打印的是落盘正文的尾巴」对仗。它曾是手抄的第二份文本,比落盘正本少 `validate` / `passes-gate` 两道门禁——照抄的用户拿到比同版本 `harness init` 弱一半的 CI;引导语随之改准为「以下正文取自 harness-check.yml 的 job 段」,`--print-snippets` 旗帜本身保留);gitlab 形同判据由 harness#157 补齐——`printSnippets` 收 `governanceLevel`、打 `renderGitLabCiJobs(governanceLevel)`,与落盘 / 冲突分支同一个正本函数(此前打无 level 的 `GITLAB_CI_SNIPPET`,`-g` 档照抄的用户静默少拿治理与 docs 新鲜度两个任务);CLAUDE.md/AGENTS.md 标记化幂等写(读-改-写形状)不纳入 scaffold;死选项 `-t/--type` 已随本票删除。GitLab CI 接线决议见下条;pre-push 站点已随 harness#144 落地,见「本地防线两道」条
55
+ - 站点的落点(#132 收口时 8 处,pre-push 是 harness#144 加的第 9 处,ADR-0029 撤 custom-constraints.yml 示例站后回到 8 处):`scaffold.ts` 存 8 个 `ManagedFile` 工厂(目标路径 + 三态文案 + 冲突片段),`init.ts` 只留「前置告知(无 .git / 目录不在场 / 已有 workflow 覆盖治理命令)+ runPlan」;原住 `validate.ts` 的 `createExampleCheckpoint`/`createExampleResolutions` 随工厂迁入 scaffold,`init.ts` 对 `validate` 的反向 import 因此消失,`validate.ts` 反向消费 `scaffold-templates` 的检查点文件路径约定。新增受管文件 = 一个工厂 + 一条 plan。**#132 口径外的第 9 处同形写手**(`sync-docs/context-syncer.ts:createContextMd` 自带一份 CONTEXT.md 正文、与正本已漂移)已于 harness#150 归位——现走 `contextDocFile` + `runPlan`,与 init 共用同一模板正本(`scaffold-templates.ts:renderContextDoc`)
56
+ - 可检面:`__tests__/scaffold.test.ts`(注入内存 fs 替身,零真实文件系统测全三态 + 8 站点文案逐字冻结 + 同源闸及其反证探针(两道 hook 各一条)+ 片段视图的「是正本裁出来的一段」包含闸)、`__tests__/init-ondisk.test.ts`(临时目录真跑 init:第一遍逐字节断言落盘内容并对齐改造前的整屏输出(#149 起这份整屏多出治理段 writer 的一行落盘提示——那行本就一直在真机上,只是此前 io 被丢、不进捕获面),第二遍断言 8 站点第三态与「打印即磁盘正文」;另有「`--print-snippets` 的 GitHub Actions 片段视图」两道闸——同源闸对 `harness-check.yml` 的字节级冻结基线、steps 集合闸按 YAML 解析枚举 job 的 `run:` 命令;harness#157 起「`--print-snippets` 的 GitLab CI 片段视图」四道闸——`-g standard` 同源闸(打印正文逐字等于 `GITLAB_GOVERNED` 基线的任务段 + 与真落盘 `.gitlab-ci.yml` 按 YAML 解析比对 harness-* 任务集合,正本删一个任务即红)、script 集合闸、不传 `-g` 单任务回归、`-g` 非法值 usage-error 零打印)
57
57
  - 同族不同面:#150(已落地)是同一份 CONTEXT.md 模板被 `init` 与 `sync-docs` 两个命令各抄一份(`sync-docs/context-syncer.ts:createContextMd` 自带一份、与正本 `renderContextDoc` 已漂移),本条 #153 是同一命令的两个出口(打印 / 落盘)各抄一份——判据同一个(#103),落点不同,两票不并。#150 收口后 `createContextMd` 走 `contextDocFile` + `runPlan`(含 io / 注入 fs,正常只落 `created`),可检面 `__tests__/context-syncer.test.ts`(对撞闸:sync-docs 落点与 init 站点的骨架逐字相同;正本并入两行引导 + 注释形占位钉住;present→exists 不覆盖;#158 补骨架正文逐字冻结字面量——对撞闸只钉「两侧同源」,正本被改由冻结字面量撞红)与 `__tests__/init-ondisk.test.ts`(#158 起 `src/CONTEXT.md` 落盘从 `toContain` 升为 `CONTEXT_MD_BYTES` 字节级基线,同 `PRE_PUSH_HOOK_BYTES` 手法)
58
58
  - **CI 平台维度(harness#143 已落地)**:init 的服务端 CI 接线带平台维度。术语:**CI 平台(ci platform)** = 服务端门禁的接线目标(`github` / `gitlab` / `none`,`none` = 不接线,即旧 `--no-github-actions`),一次平台选择贯通三个面(harness-check 站点、治理 CI 面、`--print-snippets` 的 CI 片段)。决议 ①–⑥ 的落地形状:① 显式 `--ci <github|gitlab|none>`、缺省 `github`、**不做 remote URL 自动检测**(init 常跑在首次 push 前,检测错是静默写错文件);② 最小参数化——CI 站点工厂 `harnessCheckCiFile(projectPath, platform, existingCiWorkflows, governanceLevel?)` 收平台参数,GitLab 正文住 `scaffold-templates.ts`(`GITLAB_CI_SNIPPET` / `renderGitLabCiJobs` / `renderGitLabCiFile`),`renderGovernanceWorkflow(level, platform)` 同步参数化,不建注册表抽象;③ 解析链 = flag > `.harness/config.yml` 的 `ci.platform` > `github`(读点 `readConfiguredCiPlatform`,缺失/脏值按未配置),**只有非默认平台才写 `ci.platform`**——github 不写键,而 init 每次都重写 config.yml,故「不写」即等于清除,旧配置零迁移、validate 不报错;④ `--no-github-actions` 保留为 `--ci none` 别名并打 deprecation 警告,与显式 `--ci <非 none>` 同时出现判 `usage-error` 且完全不落盘(不猜意图);⑤ `.gitlab-ci.yml` 三态:不在场 → `created`、在场 → `manual`(打印 harness 任务正文,它是落盘全文的尾巴 → #103 判据成立),GH 侧冲突判定逐字不变;⑥ GitLab 模板与 GH 对仗(同三条命令 + `image: node:20` + `stage: test` + `rules:` 触发 MR 与 main/master push + 无缓存),治理档 `sync-docs --check` 的非阻断对应 `allow_failure: true`
59
59
  - 平台维度带来的两处形状差(GH 侧行为逐字节不变):**一个平台一份 CI 文件**——GitLab 没有「第二个 CI 文件位」,`-g` 档的治理任务并入同一份 `.gitlab-ci.yml` 正文,gitlab 形下 `governanceWorkflowFile` 无站点;**`none` = 不接线任何服务端 CI、不创建任何 CI 文件,治理 CI 面随之不建**(harness#156 裁决 F1,推翻 #143「`none` 只摘 harness-check 站点、`-g` 档照建 GH 治理 workflow」的有意保留——旧边界让 `config.yml` 写 `ci.platform: none` 而磁盘上躺着一份 GitHub Actions 文件,状态自相矛盾);治理文档面(CHANGELOG、约束段、CONTEXT.md)由 `-g` 独立决定,`none` 下照常生成。`--print-snippets` 照出 GH 片段(非落盘面,#156 未动)。切回 `github` 时 init 不删既有的 `.gitlab-ci.yml`——scaffold 只有「落盘 / 告知」两种动作,删除不是它的能力
60
60
  - `-g` 值域校验在命令入口(harness#156 裁决 F2,与 #152 脏输入 fail-loud 同判据):合法值域 = `GOVERNANCE_PRESETS` 键集合(入口校验与装配共用同一份表),非法值判 `usage-error` 退 1、先于任何落盘,github / gitlab / none 三平台同判(旧行为:gitlab 侧非法值透传模板落三个任务、github 侧查表 miss 静默不建——同一输入两种后果)。隶属判定用 `Object.hasOwn` 而非 `in`(harness#164):`in` 走原型链,`-g constructor`/`toString` 曾穿透校验、把 `Object` 构造函数写进 `configData` 并在 `yaml.dump` 抛 `YAMLException`
61
61
  - 可检面:`__tests__/scaffold.test.ts`「CI 站点的平台维度」+ 同源闸两形覆盖、`__tests__/init-ondisk.test.ts`「init --ci 平台维度」(`.gitlab-ci.yml` 逐字节 / 第二趟 manual / 治理并入 / 持久化回读 / 别名与冲突与非法值)与「init --ci × -g 组合边界(harness#156)」(`--ci none -g standard` 落盘文件集合 + config.yml 两投影逐字冻结、`-g` 非法值三平台一致性钉、合法值回归)
62
- - **本地防线两道(harness#144 已落地)**:术语:**本地防线** = init 落的两个 Git hook 站点,分工是设计而非冗余——**pre-commit** 查暂存的(`check --staged` + plan 覆盖,增量、快反馈),**pre-push** 查整仓的(第 9 站点 `prePushHookFile` → `.git/hooks/pre-push`,`mode 0o755`,全量 `harness check` 不带 `--staged` + `harness validate`,任一失败 `exit 1` 挡住 push),兜的是 `git commit --no-verify` 绕出去的内容。三道决定按 #132 grilling 定论钉死:① **不做增量**——不解析 pre-push 从 stdin 收到的 `<local_ref> <local_oid> <remote_ref> <remote_oid>` 清单,无论推什么(含删远端分支这种 stdin 空清单的场景)整仓过一遍,钩子内子进程一律 `< /dev/null`、不让它吃掉 ref 清单;② **不内建逃生机制**——无环境变量开关、无超时、无「慢则降级」,唯一口子是 git 原生 `git push --no-verify`;③ **定位不变**——本地 hook = 自检与提醒,服务端 CI(见「CI 平台维度」条)才是门禁,pre-push 不替代它。`--no-git-hooks` 一并覆盖本站点(策略仍在命令层翻成 plan,scaffold 不持策略、不新增跳过旗帜);`--print-snippets` 与冲突分支打印的片段 = 落盘正文去 shebang 头(#103 判据);init 收尾提示与本站点引导语写明「查暂存的 / 查整仓的,重复是设计使然」。仓库自身的 `bin/install-precommit-hook.sh`(`npm run hooks:install`)仍是 pre-commit 单道,本票不动它
62
+ - **本地防线两道(harness#144 已落地)**:术语:**本地防线** = init 落的两个 Git hook 站点,分工是设计而非冗余——**pre-commit** 查暂存的(`check --staged` + plan 覆盖,增量、快反馈),**pre-push** 查整仓的(`prePushHookFile` → `.git/hooks/pre-push`,`mode 0o755`,全量 `harness check` 不带 `--staged` + `harness validate`,任一失败 `exit 1` 挡住 push),兜的是 `git commit --no-verify` 绕出去的内容。三道决定按 #132 grilling 定论钉死:① **不做增量**——不解析 pre-push 从 stdin 收到的 `<local_ref> <local_oid> <remote_ref> <remote_oid>` 清单,无论推什么(含删远端分支这种 stdin 空清单的场景)整仓过一遍,钩子内子进程一律 `< /dev/null`、不让它吃掉 ref 清单;② **不内建逃生机制**——无环境变量开关、无超时、无「慢则降级」,唯一口子是 git 原生 `git push --no-verify`;③ **定位不变**——本地 hook = 自检与提醒,服务端 CI(见「CI 平台维度」条)才是门禁,pre-push 不替代它。`--no-git-hooks` 一并覆盖本站点(策略仍在命令层翻成 plan,scaffold 不持策略、不新增跳过旗帜);`--print-snippets` 与冲突分支打印的片段 = 落盘正文去 shebang 头(#103 判据);init 收尾提示与本站点引导语写明「查暂存的 / 查整仓的,重复是设计使然」。仓库自身的 `bin/install-precommit-hook.sh`(`npm run hooks:install`)仍是 pre-commit 单道,本票不动它
63
63
  - **knowledge view 收口模型(harness#133 已落地)**:knowledge 11 个子操作的「取数 + json 投影 + 人读排版 + 退出码」四件事拆开——命令模块只产 `{ data, human() }`(`data` 是 `--json` 正文的唯一正本,11 个 `knowledgeXxxView` 具名导出即投影本体),`knowledge-view.ts` 收口排版渲染、上色、label 映射、json/人读分派与退出码、路径解析与 store 构造。术语:**display model** = `human()` 返回的结构化人读视图 `{ sections: [{ title, rows: [{ cells }] }] }`,一格(cell)三选一:`label`(纯文案)/ `field`(点分路径声明它投影自 json 面哪个字段,数组下标与 `.length` 合法)/ `derived`(json 面没有的派生量,须在测试豁免表登记理由)。空行是显式的一行(`blankLine()`),不再靠 `\n` 前后缀藏排版。颜色只经 `DisplayTone` 角色查 `TONE_STYLES` 一张表,命令侧零 `chalk` 调用。可检面:`__tests__/knowledge-view.test.ts`(四道闸:源形状 / 11·11 json 字段清单冻结 / 两投影一致性 + 派生量双向对撞 / 满态与空态人读逐行冻结,基线取自改造前实现实测捕获)+ `__tests__/knowledge-tone-mapping.test.ts`(#151 加:tone 映射的纯函数级冻结,判据见下两条)。
64
64
  - **冻结类测试的两条判据(#133 收尾须知自报第一条,#151 补完其推论)**:
65
65
  - **fixture 走不到的分支须逐个建形,不共用一个空根**。闸 4b 的 `emptyRoot()` 是裸 `mkdtemp` 目录,天然没有 `.harness/knowledge-docs/`,于是 `syncRagRows` 的两个空态分支只有第一个(`!docsPresent`)进过基线,第二个(目录在场、无 `.md`)在测试里走不到——文案改字全绿。而 json 面两分支恒同形(`data = {directory, files}`),`--json` 断言钉不住人读第二行。落点:闸 4c 的平行表 `EXTRA_EMPTY_HUMAN_CASES`(每 case 自带 `makeRoot` + 人读逐行基线 + json 形状;不改 `EMPTY_HUMAN_BASELINE: Record<SubName, string[]>` 的形状,一个子命令只装一种形状的约束留在原表)。
@@ -67,14 +67,14 @@ H5(#44)起:
67
67
  - **json 面现状冻结**:11 个顶层形状逐字不动(双仓核实 `knowledge --json` 零程序化消费者,studio 走 library 直调;统一包壳是纯 churn,删除 `--json` 死面按 ADR-0022 口径另票评估)。三处有意偏离,均不改字段集合:① `snapshot`/`index` 的 `--json` 由单行紧凑改为两空格缩进(分派单点化的直接结果,与其余 9 个子操作同形);② `sync-rag` 的两个空态此前把人类文案直接打到 `--json` 的 stdout(吐出的不是 JSON),现由 display model 承担,json 面恒为 `{directory, files}`;③ `stats` 人读面 `verified` 成熟度的颜色随 `toneForMaturity` 单表变为青色(此前 list 青 / stats 黄两处手抄不一致,文字不变)。
68
68
  - 与 #134(KnowledgeAudit 收 store)独立落地,唯一交点 `knowledgeAudit` 命令函数——#134 是后到方并已适配:`knowledgeAuditView` 经 `openKnowledgeStore` 取 store 作引擎第一形参、`shortContentThreshold` 作第二形参(构造期不再自建 store)。审计后重建索引「只在人读路径发生」是本票保留的既有策略(`--json` 不写盘),也是 `knowledge.ts` 内被逐行冻结的唯一 `options.json` 分支。
69
69
  - init 治理约束段写入落点(studio #302,ADR 2026-08-21 落点模型):新仓 → AGENTS.md `PRESERVE:governance` 段(sync-docs 重新生成时保留);旧模型仓(CLAUDE.md 已有 HARNESS_CONSTRAINTS 标记或 `## Governance Rules` 块)→ 续写 CLAUDE.md,幂等重跑不制造双份正本。治理段「在场守护」由 core 侧 governance_presence checker 承担(段缺失/为空时 check 报警)
70
- - PRESERVE:governance 段内写入纪律:机器管理的只有 HARNESS_CONSTRAINTS 标记区间——有标记只换标记区间,无标记(纯手写段)在段尾追加注入段;段内其余手写内容(治理契约引言/流程/纪律等)必须原样保留(曾整段替换清空手写内容的回归,init-injection.test 有防回归用例)
71
- - 注入段落点路由与 marker-range 替换收口在 `core/constraints/injection-writer`(ADR-0011):读侧 `resolveInjectionTarget`(漂移检测/retire 同步共用「CLAUDE.md 有标记优先,否则 AGENTS.md」)+ 写侧 `resolveGovernanceLanding`(init 落点选择);init 三个治理段 writer retire 注入同步均为「渲染 body + 调 writer」,半标记(单边/乱序)一律拒写告警;未注入 = 两处均无完整标记段
70
+ - ADR-0029(文本注入层关停):init 的约束注入段 writer(setupAgentsMdConstraints / setupClaudeMdConstraints / setupGovernanceConstraints)、check 的注入漂移警告块、report 的漂移小节与 prompt 注入清单、retire 的注入段同步全部移除;sync-docs 不再生成 HARNESS_CONSTRAINTS 注入段。PRESERVE 标记保留机制本身保留(通用机制,非约束注入);历史 HARNESS_CONSTRAINTS 段在 `hasGovernanceContract` 中仅作治理契约在场的历史兼容判据
71
+ - marker-range 替换与治理正本探测收口在 `core/constraints/injection-writer`(ADR-0011):init Output Style writer 为「渲染 body + 调 writer」,半标记(单边/乱序)一律拒写告警;注入段落点路由(resolveInjectionTarget / resolveGovernanceLanding)已随 ADR-0029 删除
72
72
  - knowledge 命令包含 11 个子操作(list/search/import/decay/stats/sync-rag/audit/snapshot/migrate/index/health);upsert/sync-status 已迁至 studio CLI(harness#110,二者硬编码 localhost Studio 内部端点,不属 harness「通用框架、文件驱动」定位)
73
73
  - `stats` / `health` 的飞轮数字不在 CLI 内计算:分子口径唯一实现是 `knowledge/flywheel-metrics.ts`(`evaluateFlywheel`,ADR-0013),本层只做百分比取整/一位小数与字段名映射;`.consumption-stats.json` 的读取留在各命令(module 零 IO)
74
74
  - 特殊路由(选项条件、子命令兜底、裸跑语义)表达在定义表的 optionRoutes / subcommands / subcommandStrict / bareRunsAction 字段,bin 是纯通用引擎、不含单命令知识
75
75
  - **数值旗帜装配口径(harness#152)**:`<n>` / `<rate>` 这类数值旗帜经 commander 到命令侧**恒是字符串**(`defaultValue: '50'` 也是),所以入参声明必须跟着运行时形状走——装配点之前声明 `string`(或双形 `string | number`,如 `failure --limit`),数值只在装配点转一次。装配点有两处:**有 `mapActionArgs` 的命令在定义表装配**(`status --hours`、`constraints report` 三阈值、门禁 `--coverage-threshold`/`--bundle-threshold`/`--min-reviewers`),**knowledge 族是「纯投影条目」故编组归命令模块**(`knowledgeSearchCommand` 的 `--limit`、`knowledgeAudit` 的 `--threshold`);两形都对,判据是「声明与运行时一致」,因此 `as any` 不该出现在这条链上(它替编译器记住了旗帜的实际形状,去掉 `parseInt` 就是往数值槽塞字符串)。**转不出来必须 fail-loud**(`logError` + `usage-error`,非零退出):下游兜底写的多是 `?? 缺省值`,而 `??` 只兜 `null`/`undefined`、NaN 照单全收,于是一句 `len < NaN` 恒 false 就把一道质量判定静默关掉、退出码仍是 0(#152 病灶正本:`knowledge audit --threshold abc`)。上游按口径挡住的,引擎侧不再重复兜(库面向的守卫已落 harness#163:`KnowledgeAudit` 构造器对 `shortContentThreshold`/`staleDays`/`promotionBlockDays` 三槽的显式脏值(NaN/±Infinity/负数)构造期抛 `TypeError`,未传/显式 `undefined` 走缺省、显式 `0` 合法)。可检面:`__tests__/knowledge-audit-threshold.test.ts`(脏值 fail-loud / 缺省 '50' 与未传同果 / 显式 '0' 不被当未传 / `knowledge.ts` 零 `as any`)+ `__tests__/bin-exit-mapping.test.ts`(对外退出码 1 的端到端证据);knowledge 的 `kind:` 逐行冻结豁免表在 `__tests__/knowledge-view.test.ts` 闸 1(#152 起两行、#154 起三行,理由随表注)。其余数值旗帜的脏输入后果(fail-closed 与静默按缺省两类)逐项判定见 #152 票面,#152 只收 `knowledge audit --threshold` 一处;**harness#154 已把口径推广到全部数值旗帜**(已推广清单如下),**唯一解析器 = `src/utils/numeric-flag.ts`**(本条目指名的正本:`parseNumericFlag` 判别 + `numericFlagMessage` 文案模板 + `requireNumericFlag` 抛出 + `NumericFlagError`;`parseInt`/`parseFloat` 的前缀截断 `'30abc'→30`、`'1e2'→1` 同属静默误读,禁回)。两消费形同一条判定规则:定义表 `mapActionArgs` 站点(`constraints report` 三阈值、`performance --coverage-threshold`/`--bundle-threshold`、`review --min-reviewers`)走 `requireNumericFlag` 抛出、bin 在 `runDefinition` 捕获映射 usage-error——定义表是纯数据模块返回不了 CommandResult,装配失败的通道只此一条,bin 不因此新增单命令知识;命令模块站点(`knowledge search --limit`、`failure list --limit`、`passes-gate --coverage-threshold` 的 `coverageCheck`)走 `parseNumericFlag` 就地 `logError` + `usage-error`,且都先于 store/recorder 构造与任何执行(脏值零落盘)。#152 的 `assembleShortContentThreshold` 改为委派 `parseNumericFlag`(函数面与 #152 报错文案保留、冻结测试钉死,未新建第二套解析器)。**#152 判为 fail-closed 的两处不保留、一并收敛 fail-loud**(#154 裁定):`passes-gate --coverage-threshold` 的「覆盖率不足 (x% < NaN%)」自证异常文案与 `review --min-reviewers` 的恒不通过——脏输入属用法错误、自证异常的文案即缺陷;合法输入路径逐字不变,退出码面 review 1→1、passes-gate coverage 脏值 0→1 属缺陷修正(定级与 CHANGELOG 入账见该票)。边界不动:门禁/引擎构造器侧的 falsy/`??` 约定是库面向语义(显式 `0` 在 performance 门禁内仍 = 不检查该项),与 #163 同例不重复兜;`status --hours` 是死旗帜,另族另票。可检面新增:`src/utils/__tests__/numeric-flag.test.ts`(解析规则矩阵)+ `src/cli/commands/__tests__/numeric-flag-assembly.test.ts`(定义表逐旗帜装配 + 命令入口 + 端到端 spawn:脏输入 / 合法路径对拍 / 显式 `0`)
76
76
  - **命令 interface = `CommandResult` + 注入 io(架构评审候选7)**:命令实现一律声明 `Promise<CommandResult>`(判别联合 `ok|skip|fail|usage-error`,`fail`/`usage-error` 必附可定位的 `reason`,多闸门命令 reason 含 `gate <id>`),末位可选形参 `io: CommandIO = processIO`;类型与写入面在 `src/cli/command-contract.ts`
77
- - **writer 层 `io` 必传,缺省只留在对外命令入口(harness#149)**:`= processIO` 的缺省是给 bin 不注入时的对外入口用的(每个命令模块一处,如 `init()`)。命令内部的落盘 writer 与它们的路由一律收**必传** `io`——缺省留在链路上时,调用点漏传不报错,只是静默把提示兜到真实 stdout:`captureIO()` 的测试面因此天然缺那批行,`⚠️ …标记残缺…请人工修复` 这类唯一提示写坏了没有测试会红。init 治理段的三个 writer(Output Style / AGENTS.md / CLAUDE.md,共 14 条 `log(io, …)`)与落点路由 `setupGovernanceConstraints` 已按此收口;机器可检:`__tests__/init-governance-io.test.ts`(源形状闸:`init.ts` 内该缺省只剩 `init()` 一处 + 四个 writer 签名必传 + `setupGovernance` 调用点逐个传 io)与行为面(临时目录真跑 init,断言这批提示落进注入面;#158 把 `⚠️ …标记残缺…请人工修复` 四处分支补齐——CLAUDE.md 的 Output Style / AGENTS.md 的 PRESERVE 外层单边 / 段内 HARNESS_CONSTRAINTS 单边 / CLAUDE.md 的 HARNESS_CONSTRAINTS 单边各一条捕获面断言,fixture 各自单建)
77
+ - **writer 层 `io` 必传,缺省只留在对外命令入口(harness#149)**:`= processIO` 的缺省是给 bin 不注入时的对外入口用的(每个命令模块一处,如 `init()`)。命令内部的落盘 writer 与它们的路由一律收**必传** `io`——缺省留在链路上时,调用点漏传不报错,只是静默把提示兜到真实 stdout:`captureIO()` 的测试面因此天然缺那批行,`⚠️ …标记残缺…请人工修复` 这类唯一提示写坏了没有测试会红。init 治理段的 writer 已按此收口(ADR-0029 后只剩 Output Style 段一个 writer);机器可检:`__tests__/init-governance-io.test.ts`(源形状闸:`init.ts` 内该缺省只剩 `init()` 一处 + writer 签名必传 + `setupGovernance` 调用点逐个传 io)与行为面(临时目录真跑 init,断言 Output Style 段提示与其 `⚠️ …标记残缺…请人工修复` 告警落进注入面)
78
78
  - **本目录零 `process.exit` / `process.exitCode`**:kind → 退出码的唯一映射在 `bin/harness.js`(`ok`/`skip` → 0,`fail`/`usage-error` → 1,未知 kind fail-closed)。历史上两处条件式(`command --level` 按严重级、门禁按 passed)已由命令侧译成 kind;`sync-docs --check` 的漂移改由实现返回 `fail`(定义表的 `afterRun` 逃生门随之废除)
79
79
  - **输出不得直接用 console**:流式打印走 `log(io, …)` / `logError(io, …)`(`util.format` + 换行,与 console 逐字节等价,见 `src/cli/__tests__/command-contract.test.ts`);测试断言输出用 `captureIO()`,不再 `spyOn(process, 'exit')`;`--json` 输出断言用 `lastJsonOutput(io)`(JSON.parse 正本,harness#108)。例外:`constraints retire` 交互正文仍走 console(其 `RetireIO` 只注入 readline 流),退役结果打印已接注入流
80
80
  - **#95 同型扫荡结论**:两门禁站点已修(`passes-gate` 执行/证据落 projectPath、`acceptance` 去掉 `tasksPath: './tasks.yml'` 相对默认值)。逐点判定后的豁免四处,理由与站点原文一起冻结在 `__tests__/project-path-convention.test.ts` 的豁免表:`release` 的 `pkgPath`(定义表本无 `-p`,pkgPath 就是该命令唯一的根且已逐个传给每条 `run(cmd, pkgPath)`,不构成半失效;给发布流水线新增 `-p` 属新能力)、`command` 的 `evaluate({ projectPath: process.cwd(), … })`(`CommandGate` 只做命令串正则判定、零文件读写,且该命令定义表里没有 `-p`,取 cwd 仅为满足 `GateContext` 形状,架构评审候选1)、`constraints` 的 `join(process.cwd(), 'package.json')`(报的是 harness 自身包版本,主锚 `__dirname`,cwd 仅兜底)、`core/spec/validator.ts` 的 `schemaPath: './specs/schemas'`(**确是同型病灶**:`validateAll(projectPath)` 用 projectPath 找 spec 文件、却用 cwd 找 schema;但 `validateFile`/`loadSchema` 签名里没有根,补齐要穿透整个 spec 域,留待 spec 单票收口)
@@ -68,7 +68,7 @@ function write(dir: string, rel: string, content: string): void {
68
68
 
69
69
  const TABLE = ['| 能力 | 路径 | 说明 |', '|---|---|---|'];
70
70
 
71
- /** 一个「两 checker 都会真评估」的项目:源码根下两个已登记文件 + 自定义约束 + 一条 trace */
71
+ /** 一个「两 checker 都会真评估」的项目:源码根下两个已登记文件 + 一条 trace */
72
72
  function fixtureRepo(): string {
73
73
  const dir = fs.mkdtempSync(path.join(os.tmpdir(), 'harness-read-count-'));
74
74
  git(dir, 'init', '-q');
@@ -76,11 +76,6 @@ function fixtureRepo(): string {
76
76
  git(dir, 'config', 'user.name', 'Fixture');
77
77
  write(dir, 'README.md', '# fixture\n');
78
78
  write(dir, '.harness/config.yml', 'preset: standard\n');
79
- write(
80
- dir,
81
- '.harness/custom-constraints.yml',
82
- 'custom_constraints:\n ab_custom_rule:\n level: guideline\n rule: 自定义规则\n message: 自定义提示\n trigger: manual\n'
83
- );
84
79
  write(
85
80
  dir,
86
81
  'CAPABILITIES.md',
@@ -131,25 +126,21 @@ describe('一次 check 的文件读取计数闸(ADR-0023 决策 5 ①)', ()
131
126
  const result = await check({ staged: true, projectPath: dir }, io);
132
127
  expect(result.kind).toBe('ok');
133
128
  // 正对照:两个吃能力表的 checker 确实跑了(否则下面的断言是空跑)
134
- expect(io.outText()).toContain('自定义约束: 1 条');
135
129
  expect(io.outText()).toContain('触发条件: module_modification');
136
130
 
137
131
  expect([...readCounts().entries()].sort()).toEqual(
138
132
  [
139
133
  // —— 一次运行一份:本 ADR 的承诺面 ——
140
134
  ['.harness/config.yml', 1],
141
- ['.harness/custom-constraints.yml', 1],
142
135
  ['CAPABILITIES.md', 1],
143
136
  ['.harness/logs/traces.log', 2],
144
137
  ['.harness/.state.json', 1], // ADR-0026:智能提示经 StateIO 读状态,恰一次
138
+ ['AGENTS.md', 1],
145
139
  // —— 已知例外(改动需明写理由)——
146
140
  // traces.log = 2:head 50(智能提示的阈值判定,决策 3)与 tail 20(有无失败证据,
147
141
  // 决策 4)是两个不同窗口的**有界**读;并成一个窗口就等于回到整读。
148
- // AGENTS.md = 3 / CLAUDE.md = 2:决策表明示本 ADR 不处理治理文档的重复读
149
- // (1.5–25KB,<1ms)。CLAUDE.md 在本夹具里并不存在,2 = 落点路由与旧落点兜底
150
- // 各尝试读一次(读失败的尝试同样计入)。
151
- ['AGENTS.md', 3],
152
- ['CLAUDE.md', 2],
142
+ // AGENTS.md = 1:ADR-0029 注入漂移检测与 custom 约束面关停后,治理文档只剩
143
+ // governance_presence 一处读者(夹具 CLAUDE.md 不存在且不再被探测,归零)。
153
144
  ].sort()
154
145
  );
155
146
  });
@@ -159,8 +150,6 @@ describe('一次 check 的文件读取计数闸(ADR-0023 决策 5 ①)', ()
159
150
 
160
151
  const except = new Set([
161
152
  path.join('.harness', 'logs', 'traces.log'), // 两个窗口的有界读,见上表理由
162
- 'AGENTS.md', // 治理文档,ADR-0023 决策表明示不处理
163
- 'CLAUDE.md',
164
153
  ]);
165
154
  const offenders = [...readCounts()].filter(
166
155
  ([file, n]) => !except.has(file) && n > 1
@@ -62,13 +62,13 @@ jest.mock('chalk', () => ({
62
62
  red: jest.fn((str: string) => str),
63
63
  }));
64
64
 
65
- function fakeConstraint(id: string, level: 'iron_law' | 'guideline') {
65
+ function fakeConstraint(id: string, severity: 'error' | 'warning') {
66
66
  return {
67
67
  kind: 'check' as const,
68
68
  id,
69
69
  rule: 'test',
70
70
  message: `msg-${id}`,
71
- level,
71
+ severity,
72
72
  trigger: 'code_implementation',
73
73
  enforcement: 'test',
74
74
  };
@@ -83,21 +83,21 @@ describe('check 命令 skip 输出', () => {
83
83
  jest.clearAllMocks();
84
84
  });
85
85
 
86
- it('skipped 单独列示,不计入铁律通过条数', async () => {
86
+ it('skipped 单独列示,不计入 error 级通过条数', async () => {
87
87
  mockChecker.checkConstraints.mockResolvedValue({
88
88
  passed: true,
89
- ironLaws: [
90
- { id: 'law_pass', level: 'iron_law', satisfied: true, checkedAt: new Date(), constraint: fakeConstraint('law_pass', 'iron_law') },
91
- { id: 'law_skip', level: 'iron_law', satisfied: true, skipped: true, checkedAt: new Date(), constraint: fakeConstraint('law_skip', 'iron_law') },
89
+ errors: [
90
+ { id: 'law_pass', severity: 'error', satisfied: true, checkedAt: new Date(), constraint: fakeConstraint('law_pass', 'error') },
91
+ { id: 'law_skip', severity: 'error', satisfied: true, skipped: true, checkedAt: new Date(), constraint: fakeConstraint('law_skip', 'error') },
92
92
  ],
93
- guidelines: [],
93
+ warnings: [],
94
94
  warningCount: 0,
95
95
  });
96
96
 
97
97
  await check({ preset: 'default', staged: false }, io);
98
98
 
99
99
  // 通过条数只计实际评估的 1 条
100
- expect(io.outText()).toContain('铁律: 全部通过 (1 条)');
100
+ expect(io.outText()).toContain('error 级约束: 全部通过 (1 条)');
101
101
  // skipped 单独列示
102
102
  expect(io.outText()).toContain('跳过评估: 1 条');
103
103
  expect(io.outText()).toContain('- law_skip');
@@ -105,20 +105,20 @@ describe('check 命令 skip 输出', () => {
105
105
  expect(io.outText()).toContain('约束检查通过');
106
106
  });
107
107
 
108
- it('指导原则通过计数排除 skipped', async () => {
108
+ it('warning 级通过计数排除 skipped', async () => {
109
109
  mockChecker.checkConstraints.mockResolvedValue({
110
110
  passed: true,
111
- ironLaws: [],
112
- guidelines: [
113
- { id: 'g_pass', level: 'guideline', satisfied: true, checkedAt: new Date(), constraint: fakeConstraint('g_pass', 'guideline') },
114
- { id: 'g_skip', level: 'guideline', satisfied: true, skipped: true, checkedAt: new Date(), constraint: fakeConstraint('g_skip', 'guideline') },
111
+ errors: [],
112
+ warnings: [
113
+ { id: 'g_pass', severity: 'warning', satisfied: true, checkedAt: new Date(), constraint: fakeConstraint('g_pass', 'warning') },
114
+ { id: 'g_skip', severity: 'warning', satisfied: true, skipped: true, checkedAt: new Date(), constraint: fakeConstraint('g_skip', 'warning') },
115
115
  ],
116
116
  warningCount: 0,
117
117
  });
118
118
 
119
119
  await check({ preset: 'default', staged: false }, io);
120
120
 
121
- expect(io.outText()).toContain('指导原则: 1/1 通过');
121
+ expect(io.outText()).toContain('warning 级约束: 1/1 通过');
122
122
  expect(io.outText()).toContain('跳过评估: 1 条');
123
123
  });
124
124
  });
@@ -155,7 +155,7 @@ describe('check command(真 git fixture)', () => {
155
155
  });
156
156
 
157
157
  describe('判定与输出形状', () => {
158
- it('真证据全部满足时逐层通过,未接线证据单独列示', async () => {
158
+ it('真证据全部满足时逐层通过', async () => {
159
159
  const dir = gitRepo();
160
160
  stageChange(dir, 'src/existing.ts', 'export const a = 2;\n');
161
161
  passTraces(dir);
@@ -165,16 +165,13 @@ describe('check command(真 git fixture)', () => {
165
165
  io
166
166
  );
167
167
 
168
- // 3 code_implementation 铁律:incremental_progress 的 hasSingleTask 未接线 → skip
169
- expect(io.outText()).toContain('✅ 铁律: 全部通过 (2 条)');
170
- expect(io.outText()).toContain('✅ 指导原则: 2/2 通过');
171
- expect(io.outText()).toContain('⏭️ 跳过评估: 1 条(约定未采用或证据未接线,不计通过/失败)');
172
- expect(io.outText()).toContain('- incremental_progress');
168
+ expect(io.outText()).toContain('✅ error 级约束: 全部通过 (1 条)');
169
+ expect(io.outText()).toContain('✅ warning 级约束: 1/1 通过');
173
170
  expect(io.outText()).toContain('✅ 约束检查通过');
174
171
  expect(result).toEqual({ kind: 'ok' });
175
172
  });
176
173
 
177
- it('无验证证据 → 真铁律拦截,经异常外溢为 fail', async () => {
174
+ it('无验证证据 → error 级拦截,经异常外溢为 fail', async () => {
178
175
  const dir = gitRepo();
179
176
  stageChange(dir, 'src/existing.ts', 'export const a = 2;\n');
180
177
  write(dir, '.harness/logs/traces.log', '{"constraintId":"fixture","result":"fail"}\n');
@@ -207,7 +204,7 @@ describe('check command(真 git fixture)', () => {
207
204
  expect(result.kind).toBe('fail');
208
205
  });
209
206
 
210
- it('staged 硬编码凭证 → 指导原则警告但不阻断', async () => {
207
+ it('staged 硬编码凭证 → warning 级警告但不阻断', async () => {
211
208
  const dir = gitRepo();
212
209
  stageChange(
213
210
  dir,
@@ -223,7 +220,7 @@ describe('check command(真 git fixture)', () => {
223
220
  io
224
221
  );
225
222
 
226
- expect(io.outText()).toContain('⚠️ 指导原则警告: 1 条');
223
+ expect(io.outText()).toContain('⚠️ warning 级约束警告: 1 条');
227
224
  expect(io.outText()).toContain('- no_hardcoded_credentials');
228
225
  expect(io.outText()).toContain('✅ 约束检查通过');
229
226
  expect(result).toEqual({ kind: 'ok' });
@@ -313,8 +310,8 @@ describe('check command(真 git fixture)', () => {
313
310
  );
314
311
 
315
312
  const out = io.outText();
316
- expect(out).toContain('✅ 指导原则: 1/1 通过');
317
- expect(out).not.toContain('指导原则警告');
313
+ expect(out).toContain('✅ warning 级约束: 1/1 通过');
314
+ expect(out).not.toContain('warning 级约束警告');
318
315
  expect(out).toContain('💡 提示: 1 条(不判违规,供参考)');
319
316
  expect(out).toContain('- capability_sync:');
320
317
  expect(out).toContain('src/nested/deep.ts');
@@ -398,7 +395,7 @@ describe('check command(真 git fixture)', () => {
398
395
  const traces = projectTraces(dir).slice(seeded);
399
396
 
400
397
  expect(traces.map(t => t.constraintId)).toEqual(
401
- expect.arrayContaining(['incremental_progress', 'no_implementation_without_requirement'])
398
+ expect.arrayContaining(['no_completion_without_verification', 'no_hardcoded_credentials'])
402
399
  );
403
400
  expect(traces.every(t => ['pass', 'fail', 'skip'].includes(t.result))).toBe(true);
404
401
  expect(traces.every(t => t.projectPath === dir)).toBe(true);
@@ -462,7 +459,7 @@ describe('check command(真 git fixture)', () => {
462
459
  });
463
460
 
464
461
  describe('生效集来自真 .harness/config.yml', () => {
465
- it('报告自定义、禁用与未知约束 id', async () => {
462
+ it('报告禁用与未知约束 id', async () => {
466
463
  const dir = gitRepo();
467
464
  stageChange(dir, 'src/existing.ts', 'export const a = 2;\n');
468
465
  passTraces(dir);
@@ -470,12 +467,6 @@ describe('check command(真 git fixture)', () => {
470
467
  dir,
471
468
  '.harness/config.yml',
472
469
  [
473
- 'custom_constraints:',
474
- ' fixture_rule:',
475
- ' id: fixture_rule',
476
- ' level: guideline',
477
- ' trigger: manual',
478
- ' message: fixture 自定义约束',
479
470
  'constraints:',
480
471
  ' capability_sync:',
481
472
  ' enabled: false',
@@ -490,7 +481,6 @@ describe('check command(真 git fixture)', () => {
490
481
  io
491
482
  );
492
483
 
493
- expect(io.outText()).toContain('自定义约束: 1 条');
494
484
  expect(io.outText()).toContain('已禁用约束: capability_sync, no_such_rule');
495
485
  expect(io.outText()).toContain('配置中存在未知约束 id(已忽略,可清理): no_such_rule');
496
486
  });
@@ -546,24 +536,17 @@ describe('check command(真 git fixture)', () => {
546
536
  expect(result).toEqual({ kind: 'ok' });
547
537
  });
548
538
 
549
- it('应该列出铁律', () => {
539
+ it('应该列出 error 级约束', () => {
550
540
  const result = listLaws({}, io);
551
541
 
552
- expect(io.outText()).toContain('铁律');
542
+ expect(io.outText()).toContain('error 级');
553
543
  expect(result).toEqual({ kind: 'ok' });
554
544
  });
555
545
 
556
- it('应该列出指导原则', () => {
546
+ it('应该列出 warning 级约束', () => {
557
547
  const result = listLaws({}, io);
558
548
 
559
- expect(io.outText()).toContain('指导原则');
560
- expect(result).toEqual({ kind: 'ok' });
561
- });
562
-
563
- it('应该列出提示', () => {
564
- const result = listLaws({}, io);
565
-
566
- expect(io.outText()).toContain('提示');
549
+ expect(io.outText()).toContain('warning 级');
567
550
  expect(result).toEqual({ kind: 'ok' });
568
551
  });
569
552
  });