@dommaker/harness 1.8.1 → 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 (299) hide show
  1. package/CHANGELOG.md +16 -0
  2. package/README.md +10 -13
  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/cli/commands/sync-docs/capabilities-syncer.d.ts +22 -0
  42. package/dist/cli/commands/sync-docs/capabilities-syncer.d.ts.map +1 -1
  43. package/dist/cli/commands/sync-docs/capabilities-syncer.js +113 -8
  44. package/dist/cli/commands/sync-docs/capabilities-syncer.js.map +1 -1
  45. package/dist/cli/commands/sync-docs/index.d.ts.map +1 -1
  46. package/dist/cli/commands/sync-docs/index.js +36 -4
  47. package/dist/cli/commands/sync-docs/index.js.map +1 -1
  48. package/dist/core/constraints/capabilities-parser.d.ts.map +1 -1
  49. package/dist/core/constraints/capabilities-parser.js +16 -4
  50. package/dist/core/constraints/capabilities-parser.js.map +1 -1
  51. package/dist/core/constraints/checker.d.ts +15 -24
  52. package/dist/core/constraints/checker.d.ts.map +1 -1
  53. package/dist/core/constraints/checker.js +33 -72
  54. package/dist/core/constraints/checker.js.map +1 -1
  55. package/dist/core/constraints/checkers/docs-freshness.js +1 -1
  56. package/dist/core/constraints/checkers/docs-freshness.js.map +1 -1
  57. package/dist/core/constraints/checkers/governance-presence.d.ts +2 -1
  58. package/dist/core/constraints/checkers/governance-presence.d.ts.map +1 -1
  59. package/dist/core/constraints/checkers/governance-presence.js +4 -3
  60. package/dist/core/constraints/checkers/governance-presence.js.map +1 -1
  61. package/dist/core/constraints/checkers/index.d.ts +1 -1
  62. package/dist/core/constraints/checkers/index.d.ts.map +1 -1
  63. package/dist/core/constraints/checkers/index.js +4 -8
  64. package/dist/core/constraints/checkers/index.js.map +1 -1
  65. package/dist/core/constraints/checkers/iron-flags.d.ts +3 -4
  66. package/dist/core/constraints/checkers/iron-flags.d.ts.map +1 -1
  67. package/dist/core/constraints/checkers/iron-flags.js +4 -5
  68. package/dist/core/constraints/checkers/iron-flags.js.map +1 -1
  69. package/dist/core/constraints/checkers/no-test-simplification.js +1 -1
  70. package/dist/core/constraints/checkers/no-test-simplification.js.map +1 -1
  71. package/dist/core/constraints/checkers/types.d.ts +1 -1
  72. package/dist/core/constraints/checkers/types.js +1 -1
  73. package/dist/core/constraints/definitions/guidelines.d.ts +4 -4
  74. package/dist/core/constraints/definitions/guidelines.d.ts.map +1 -1
  75. package/dist/core/constraints/definitions/guidelines.js +9 -25
  76. package/dist/core/constraints/definitions/guidelines.js.map +1 -1
  77. package/dist/core/constraints/definitions/iron-laws.d.ts +4 -4
  78. package/dist/core/constraints/definitions/iron-laws.d.ts.map +1 -1
  79. package/dist/core/constraints/definitions/iron-laws.js +10 -81
  80. package/dist/core/constraints/definitions/iron-laws.js.map +1 -1
  81. package/dist/core/constraints/definitions.d.ts +7 -9
  82. package/dist/core/constraints/definitions.d.ts.map +1 -1
  83. package/dist/core/constraints/definitions.js +13 -16
  84. package/dist/core/constraints/definitions.js.map +1 -1
  85. package/dist/core/constraints/index.d.ts +2 -5
  86. package/dist/core/constraints/index.d.ts.map +1 -1
  87. package/dist/core/constraints/index.js +3 -13
  88. package/dist/core/constraints/index.js.map +1 -1
  89. package/dist/core/constraints/injection-writer.d.ts +10 -38
  90. package/dist/core/constraints/injection-writer.d.ts.map +1 -1
  91. package/dist/core/constraints/injection-writer.js +17 -54
  92. package/dist/core/constraints/injection-writer.js.map +1 -1
  93. package/dist/core/constraints/run-env.d.ts +1 -10
  94. package/dist/core/constraints/run-env.d.ts.map +1 -1
  95. package/dist/core/constraints/run-env.js +1 -22
  96. package/dist/core/constraints/run-env.js.map +1 -1
  97. package/dist/core/constraints/usage-report.d.ts +5 -7
  98. package/dist/core/constraints/usage-report.d.ts.map +1 -1
  99. package/dist/core/constraints/usage-report.js +5 -10
  100. package/dist/core/constraints/usage-report.js.map +1 -1
  101. package/dist/core/effective-constraints.d.ts +12 -22
  102. package/dist/core/effective-constraints.d.ts.map +1 -1
  103. package/dist/core/effective-constraints.js +13 -30
  104. package/dist/core/effective-constraints.js.map +1 -1
  105. package/dist/core/index.d.ts +2 -2
  106. package/dist/core/index.d.ts.map +1 -1
  107. package/dist/core/index.js +3 -9
  108. package/dist/core/index.js.map +1 -1
  109. package/dist/core/project-config-loader.d.ts +4 -42
  110. package/dist/core/project-config-loader.d.ts.map +1 -1
  111. package/dist/core/project-config-loader.js +16 -125
  112. package/dist/core/project-config-loader.js.map +1 -1
  113. package/dist/core/validators/passes-gate.d.ts +2 -2
  114. package/dist/core/validators/passes-gate.js +6 -6
  115. package/dist/core/validators/passes-gate.js.map +1 -1
  116. package/dist/hooks/bootstrap.d.ts +9 -12
  117. package/dist/hooks/bootstrap.d.ts.map +1 -1
  118. package/dist/hooks/bootstrap.js +9 -31
  119. package/dist/hooks/bootstrap.js.map +1 -1
  120. package/dist/hooks/index.d.ts +5 -7
  121. package/dist/hooks/index.d.ts.map +1 -1
  122. package/dist/hooks/index.js +4 -10
  123. package/dist/hooks/index.js.map +1 -1
  124. package/dist/index.d.ts +9 -12
  125. package/dist/index.d.ts.map +1 -1
  126. package/dist/index.js +10 -23
  127. package/dist/index.js.map +1 -1
  128. package/dist/monitoring/trace-analyzer.d.ts.map +1 -1
  129. package/dist/monitoring/trace-analyzer.js +11 -12
  130. package/dist/monitoring/trace-analyzer.js.map +1 -1
  131. package/dist/monitoring/traces.d.ts +4 -4
  132. package/dist/monitoring/traces.d.ts.map +1 -1
  133. package/dist/monitoring/traces.js +8 -8
  134. package/dist/monitoring/traces.js.map +1 -1
  135. package/dist/presets/standard.d.ts +8 -10
  136. package/dist/presets/standard.d.ts.map +1 -1
  137. package/dist/presets/standard.js +10 -16
  138. package/dist/presets/standard.js.map +1 -1
  139. package/dist/types/constraint.d.ts +24 -35
  140. package/dist/types/constraint.d.ts.map +1 -1
  141. package/dist/types/constraint.js +8 -7
  142. package/dist/types/constraint.js.map +1 -1
  143. package/dist/types/passes-gate.d.ts +5 -5
  144. package/dist/types/passes-gate.d.ts.map +1 -1
  145. package/dist/types/project-config.d.ts +8 -66
  146. package/dist/types/project-config.d.ts.map +1 -1
  147. package/dist/types/project-config.js +3 -2
  148. package/dist/types/project-config.js.map +1 -1
  149. package/dist/types/trace.d.ts +8 -10
  150. package/dist/types/trace.d.ts.map +1 -1
  151. package/package.json +2 -2
  152. package/src/CONTEXT.md +3 -3
  153. package/src/__tests__/checker-extra.test.ts +37 -61
  154. package/src/__tests__/checker.test.ts +10 -115
  155. package/src/__tests__/effective-constraints.test.ts +18 -83
  156. package/src/__tests__/iron-laws.test.ts +71 -90
  157. package/src/__tests__/passes-gate-check.test.ts +2 -2
  158. package/src/__tests__/project-config-loader-extra.test.ts +22 -123
  159. package/src/__tests__/project-config-loader.test.ts +5 -33
  160. package/src/__tests__/public-exports.test.ts +31 -11
  161. package/src/__tests__/public-type-surface.test.ts +2 -13
  162. package/src/__tests__/public-value-surface.test.ts +1 -7
  163. package/src/__tests__/skip-semantics.test.ts +21 -37
  164. package/src/__tests__/trace-analyzer-extra.test.ts +14 -14
  165. package/src/__tests__/trace-analyzer.test.ts +21 -21
  166. package/src/__tests__/trace-collector.test.ts +22 -22
  167. package/src/__tests__/traces.test.ts +27 -28
  168. package/src/cli/commands/CONTEXT.md +12 -9
  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.test.ts +12 -80
  173. package/src/cli/commands/__tests__/constraints-retire.test.ts +42 -231
  174. package/src/cli/commands/__tests__/init-governance-io.test.ts +12 -58
  175. package/src/cli/commands/__tests__/init-ondisk.test.ts +12 -20
  176. package/src/cli/commands/__tests__/init.test.ts +0 -11
  177. package/src/cli/commands/__tests__/project-path-anchoring.test.ts +2 -2
  178. package/src/cli/commands/__tests__/registry.test.ts +39 -3
  179. package/src/cli/commands/__tests__/report-extra.test.ts +4 -4
  180. package/src/cli/commands/__tests__/report.test.ts +1 -1
  181. package/src/cli/commands/__tests__/scaffold.test.ts +4 -14
  182. package/src/cli/commands/__tests__/status-analysis-wiring.test.ts +3 -3
  183. package/src/cli/commands/__tests__/status-extra.test.ts +14 -14
  184. package/src/cli/commands/__tests__/status.test.ts +12 -12
  185. package/src/cli/commands/__tests__/sync-docs-agents.test.ts +1 -1
  186. package/src/cli/commands/__tests__/sync-docs-table-layout.test.ts +259 -0
  187. package/src/cli/commands/check.ts +43 -82
  188. package/src/cli/commands/constraints-report.ts +4 -59
  189. package/src/cli/commands/constraints-retire.ts +49 -154
  190. package/src/cli/commands/constraints.ts +11 -26
  191. package/src/cli/commands/definitions.ts +2 -2
  192. package/src/cli/commands/init.ts +8 -179
  193. package/src/cli/commands/report.ts +19 -23
  194. package/src/cli/commands/scaffold-templates.ts +1 -37
  195. package/src/cli/commands/scaffold.ts +0 -11
  196. package/src/cli/commands/status.ts +10 -10
  197. package/src/cli/commands/sync-docs/agents-syncer.ts +4 -25
  198. package/src/cli/commands/sync-docs/capabilities-syncer.ts +139 -9
  199. package/src/cli/commands/sync-docs/index.ts +41 -4
  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 +8 -15
  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/CONTEXT.md +12 -22
  231. package/src/hooks/__tests__/bootstrap.test.ts +24 -75
  232. package/src/hooks/bootstrap.ts +9 -48
  233. package/src/hooks/index.ts +5 -20
  234. package/src/index.ts +9 -31
  235. package/src/monitoring/__tests__/trace-file-anchoring.test.ts +1 -1
  236. package/src/monitoring/__tests__/traces.test.ts +22 -22
  237. package/src/monitoring/trace-analyzer.ts +12 -13
  238. package/src/monitoring/traces.ts +8 -8
  239. package/src/presets/__tests__/standard.test.ts +12 -16
  240. package/src/presets/standard.ts +14 -23
  241. package/src/types/__tests__/types.test.ts +3 -3
  242. package/src/types/constraint.ts +26 -40
  243. package/src/types/passes-gate.ts +5 -5
  244. package/src/types/project-config.ts +8 -77
  245. package/src/types/trace.ts +8 -11
  246. package/dist/core/constraints/agent-prompt-renderer.d.ts +0 -32
  247. package/dist/core/constraints/agent-prompt-renderer.d.ts.map +0 -1
  248. package/dist/core/constraints/agent-prompt-renderer.js +0 -55
  249. package/dist/core/constraints/agent-prompt-renderer.js.map +0 -1
  250. package/dist/core/constraints/checkers/no-bypass-checkpoint.d.ts +0 -6
  251. package/dist/core/constraints/checkers/no-bypass-checkpoint.d.ts.map +0 -1
  252. package/dist/core/constraints/checkers/no-bypass-checkpoint.js +0 -40
  253. package/dist/core/constraints/checkers/no-bypass-checkpoint.js.map +0 -1
  254. package/dist/core/constraints/definitions/prompts.d.ts +0 -12
  255. package/dist/core/constraints/definitions/prompts.d.ts.map +0 -1
  256. package/dist/core/constraints/definitions/prompts.js +0 -400
  257. package/dist/core/constraints/definitions/prompts.js.map +0 -1
  258. package/dist/core/constraints/injection-drift.d.ts +0 -61
  259. package/dist/core/constraints/injection-drift.d.ts.map +0 -1
  260. package/dist/core/constraints/injection-drift.js +0 -131
  261. package/dist/core/constraints/injection-drift.js.map +0 -1
  262. package/dist/core/constraints/injection-renderer.d.ts +0 -23
  263. package/dist/core/constraints/injection-renderer.d.ts.map +0 -1
  264. package/dist/core/constraints/injection-renderer.js +0 -44
  265. package/dist/core/constraints/injection-renderer.js.map +0 -1
  266. package/dist/hooks/config.d.ts +0 -30
  267. package/dist/hooks/config.d.ts.map +0 -1
  268. package/dist/hooks/config.js +0 -34
  269. package/dist/hooks/config.js.map +0 -1
  270. package/dist/hooks/pipeline.d.ts +0 -54
  271. package/dist/hooks/pipeline.d.ts.map +0 -1
  272. package/dist/hooks/pipeline.js +0 -173
  273. package/dist/hooks/pipeline.js.map +0 -1
  274. package/dist/hooks/registry.d.ts +0 -70
  275. package/dist/hooks/registry.d.ts.map +0 -1
  276. package/dist/hooks/registry.js +0 -141
  277. package/dist/hooks/registry.js.map +0 -1
  278. package/dist/hooks/types.d.ts +0 -113
  279. package/dist/hooks/types.d.ts.map +0 -1
  280. package/dist/hooks/types.js +0 -9
  281. package/dist/hooks/types.js.map +0 -1
  282. package/src/__tests__/hooks-pipeline.test.ts +0 -201
  283. package/src/cli/commands/__tests__/check-drift.test.ts +0 -158
  284. package/src/cli/commands/__tests__/init-injection.test.ts +0 -466
  285. package/src/core/constraints/__tests__/agent-prompt-renderer.test.ts +0 -129
  286. package/src/core/constraints/__tests__/injection-drift.test.ts +0 -247
  287. package/src/core/constraints/agent-prompt-renderer.ts +0 -67
  288. package/src/core/constraints/checkers/__tests__/no-bypass-checkpoint.test.ts +0 -76
  289. package/src/core/constraints/checkers/no-bypass-checkpoint.ts +0 -40
  290. package/src/core/constraints/definitions/prompts.ts +0 -414
  291. package/src/core/constraints/injection-drift.ts +0 -142
  292. package/src/core/constraints/injection-renderer.ts +0 -44
  293. package/src/hooks/__tests__/config.test.ts +0 -19
  294. package/src/hooks/__tests__/pipeline.test.ts +0 -195
  295. package/src/hooks/__tests__/registry.test.ts +0 -115
  296. package/src/hooks/config.ts +0 -33
  297. package/src/hooks/pipeline.ts +0 -202
  298. package/src/hooks/registry.ts +0 -166
  299. package/src/hooks/types.ts +0 -120
@@ -1,12 +1,10 @@
1
1
  /**
2
- * 约束系统测试(ADR-0001kind 二元模型,26 条清单构成 + 注册表闭环)
2
+ * 约束系统测试(ADR-0029severity 显式模型——三层命名废弃、prompt 面删除、注册表闭环保留)
3
3
  */
4
4
 
5
5
  import { describe, it, expect } from '@jest/globals';
6
6
  import {
7
- IRON_LAWS,
8
- GUIDELINES,
9
- PROMPTS,
7
+ CONSTRAINTS,
10
8
  getAllConstraints,
11
9
  findConstraintsByTrigger,
12
10
  getConstraint
@@ -16,32 +14,28 @@ import { getConstraintCheck, registeredCheckCount } from '../core/constraints/ch
16
14
  import type { ConstraintContext } from '../types/constraint';
17
15
 
18
16
  describe('Constraint System', () => {
19
- describe('清单构成(ADR-00014226)', () => {
20
- it('getAllConstraints 返回 26 条:10 check + 16 prompt', () => {
17
+ describe('清单构成(ADR-0029167,全部 kind=check)', () => {
18
+ it('getAllConstraints 返回 7 check 约束', () => {
21
19
  const all = getAllConstraints();
22
- expect(all).toHaveLength(26);
23
- expect(all.filter(c => c.kind === 'check')).toHaveLength(10);
24
- expect(all.filter(c => c.kind === 'prompt')).toHaveLength(16);
20
+ expect(all).toHaveLength(7);
21
+ expect(all.every(c => c.kind === 'check')).toBe(true);
25
22
  });
26
23
 
27
- it('check = 5 iron + 5 guideline', () => {
28
- expect(Object.keys(IRON_LAWS)).toHaveLength(5);
29
- expect(Object.keys(GUIDELINES)).toHaveLength(5);
30
- Object.values(IRON_LAWS).forEach(c => {
24
+ it('severity 分桶 = 3 error + 4 warning', () => {
25
+ const errors = Object.values(CONSTRAINTS).filter(c => c.severity === 'error');
26
+ const warnings = Object.values(CONSTRAINTS).filter(c => c.severity === 'warning');
27
+ expect(errors).toHaveLength(3);
28
+ expect(warnings).toHaveLength(4);
29
+ Object.values(CONSTRAINTS).forEach(c => {
31
30
  expect(c.kind).toBe('check');
32
- expect(c.level).toBe('iron_law');
33
- });
34
- Object.values(GUIDELINES).forEach(c => {
35
- expect(c.kind).toBe('check');
36
- expect(c.level).toBe('guideline');
37
31
  });
38
32
  });
39
33
 
40
- it('prompt 16 条,level 统一为 prompt', () => {
41
- expect(Object.keys(PROMPTS)).toHaveLength(16);
42
- Object.values(PROMPTS).forEach(c => {
43
- expect(c.kind).toBe('prompt');
44
- expect(c.level).toBe('prompt');
34
+ it('prompt 面字段已删除(promptInjection / injectPrompt / appliesTo 不存在)', () => {
35
+ Object.values(CONSTRAINTS).forEach(c => {
36
+ expect('promptInjection' in c).toBe(false);
37
+ expect('injectPrompt' in c).toBe(false);
38
+ expect('appliesTo' in c).toBe(false);
45
39
  });
46
40
  });
47
41
 
@@ -78,20 +72,48 @@ describe('Constraint System', () => {
78
72
  }
79
73
  });
80
74
 
81
- it('场景标签:no_skill_without_test / no_model_for_deterministic', () => {
82
- expect(PROMPTS['no_skill_without_test'].appliesTo).toEqual(['agent-skill']);
83
- expect(PROMPTS['no_model_for_deterministic'].appliesTo).toEqual(['llm-app']);
75
+ it('harness#174 删除的 10 条不再存在', () => {
76
+ const removed = [
77
+ 'incremental_progress',
78
+ 'no_implementation_without_requirement',
79
+ 'no_bypass_checkpoint',
80
+ 'simplest_solution_first',
81
+ 'no_simplification_without_approval',
82
+ 'follow_conventions',
83
+ 'first_principles_first',
84
+ 'no_conflict_blending',
85
+ 'no_performative_agreement',
86
+ 'no_skill_without_test',
87
+ ];
88
+ for (const id of removed) {
89
+ expect(getConstraint(id)).toBeUndefined();
90
+ expect(getConstraintCheck(id)).toBeUndefined();
91
+ }
92
+ });
93
+
94
+ it('ADR-0029:prompt 类 9 条随文本注入层关停删除', () => {
95
+ const removed = [
96
+ 'no_fuzzy_completion_claim',
97
+ 'no_fix_without_root_cause',
98
+ 'no_code_without_test',
99
+ 'fix_the_problem_not_the_gate',
100
+ 'verify_external_capability',
101
+ 'no_delete_without_context',
102
+ 'design_decision_requires_discussion',
103
+ 'surgical_changes_only',
104
+ 'no_model_for_deterministic',
105
+ ];
106
+ for (const id of removed) {
107
+ expect(getConstraint(id)).toBeUndefined();
108
+ expect(getConstraintCheck(id)).toBeUndefined();
109
+ }
84
110
  });
85
111
 
86
112
  it('no_completion_without_verification 不硬编码具体命令示例(#25:口径由项目声明)', () => {
87
- const law = IRON_LAWS['no_completion_without_verification'];
88
- expect(law.promptInjection).not.toMatch(/npm test|npm run build/);
113
+ const law = CONSTRAINTS['no_completion_without_verification'];
89
114
  expect(law.description).not.toMatch(/npm test|npm run build/);
90
- expect(law.promptInjection).not.toContain('完整');
91
115
  expect(law.description).not.toContain('完整');
92
- expect(law.promptInjection).toContain('项目声明的测试');
93
116
  expect(law.description).toContain('项目声明的测试');
94
- expect(law.promptInjection).toContain('不得凭记忆声称完成');
95
117
  });
96
118
  });
97
119
 
@@ -107,12 +129,6 @@ describe('Constraint System', () => {
107
129
  const checkCount = getAllConstraints().filter(c => c.kind === 'check').length;
108
130
  expect(registeredCheckCount()).toBe(checkCount);
109
131
  });
110
-
111
- it('prompt 约束不注册 checker', () => {
112
- for (const c of Object.values(PROMPTS)) {
113
- expect(getConstraintCheck(c.id)).toBeUndefined();
114
- }
115
- });
116
132
  });
117
133
 
118
134
  describe('Helper Functions', () => {
@@ -122,10 +138,10 @@ describe('Constraint System', () => {
122
138
  });
123
139
 
124
140
  it('should get single constraint by id', () => {
125
- const constraint = getConstraint('no_fix_without_root_cause');
141
+ const constraint = getConstraint('no_completion_without_verification');
126
142
  expect(constraint).toBeDefined();
127
- expect(constraint?.kind).toBe('prompt');
128
- expect(constraint?.level).toBe('prompt');
143
+ expect(constraint?.kind).toBe('check');
144
+ expect(constraint?.severity).toBe('error');
129
145
  });
130
146
 
131
147
  it('should return undefined for unknown constraint', () => {
@@ -147,16 +163,7 @@ describe('Constraint Checker', () => {
147
163
  };
148
164
 
149
165
  const result = constraintChecker.findApplicableConstraints(context);
150
- expect(result.ironLaws.length + result.guidelines.length).toBeGreaterThan(0);
151
- });
152
-
153
- it('prompt 约束 check() 短路 satisfied,不查注册表', async () => {
154
- const context: ConstraintContext = {
155
- operation: 'code_implementation',
156
- };
157
-
158
- const result = await constraintChecker.check(PROMPTS['no_fix_without_root_cause'], context);
159
- expect(result.satisfied).toBe(true);
166
+ expect(result.errors.length + result.warnings.length).toBeGreaterThan(0);
160
167
  });
161
168
 
162
169
  it('kind=check 但未注册 checker 的约束应抛错(不许静默 pass)', async () => {
@@ -169,7 +176,7 @@ describe('Constraint Checker', () => {
169
176
  {
170
177
  id: 'ghost_check_constraint',
171
178
  kind: 'check',
172
- level: 'guideline',
179
+ severity: 'warning',
173
180
  rule: 'GHOST',
174
181
  message: 'test',
175
182
  trigger: 'code_implementation',
@@ -189,66 +196,40 @@ describe('Constraint Checker', () => {
189
196
  };
190
197
 
191
198
  const result = await constraintChecker.checkConstraints(context);
192
- expect(result.ironLaws.length + result.guidelines.length).toBeGreaterThan(0);
199
+ expect(result.errors.length + result.warnings.length).toBeGreaterThan(0);
193
200
  });
194
201
 
195
- it('should skip incremental_progress when hasSingleTask is undefined(未接线不评估)', async () => {
202
+ it('should skip no_completion_without_verification when hasVerificationEvidence is undefined(未接线不评估)', async () => {
196
203
  const context: ConstraintContext = {
197
204
  operation: 'code_implementation',
198
205
  };
199
206
 
200
- const result = await constraintChecker.check(IRON_LAWS['incremental_progress'], context);
207
+ const result = await constraintChecker.check(CONSTRAINTS['no_completion_without_verification'], context);
201
208
  expect(result.skipped).toBe(true);
202
209
  expect(result.satisfied).toBe(true);
203
210
  });
204
211
 
205
- it('should pass incremental_progress when hasSingleTask is true', async () => {
212
+ it('should pass no_completion_without_verification when hasVerificationEvidence is true', async () => {
206
213
  const context: ConstraintContext = {
207
214
  operation: 'code_implementation',
208
- hasSingleTask: true,
209
- };
210
-
211
- const result = await constraintChecker.check(IRON_LAWS['incremental_progress'], context);
212
- expect(result.satisfied).toBe(true);
213
- });
214
-
215
- it('should skip no_implementation_without_requirement when hasRequirement is undefined(未接线不评估)', async () => {
216
- const context: ConstraintContext = {
217
- operation: 'code_implementation',
218
- };
219
-
220
- const result = await constraintChecker.check(IRON_LAWS['no_implementation_without_requirement'], context);
221
- expect(result.skipped).toBe(true);
222
- expect(result.satisfied).toBe(true);
223
- });
224
-
225
- it('should pass no_implementation_without_requirement when hasRequirement is true', async () => {
226
- const context: ConstraintContext = {
227
- operation: 'code_implementation',
228
- hasRequirement: true,
215
+ hasVerificationEvidence: true,
229
216
  };
230
217
 
231
- const result = await constraintChecker.check(IRON_LAWS['no_implementation_without_requirement'], context);
218
+ const result = await constraintChecker.check(CONSTRAINTS['no_completion_without_verification'], context);
232
219
  expect(result.satisfied).toBe(true);
233
220
  });
234
221
  });
235
222
 
236
- describe('Constraint Levels', () => {
237
- it('should have correct level for iron laws', () => {
238
- Object.values(IRON_LAWS).forEach(law => {
239
- expect(law.level).toBe('iron_law');
240
- });
241
- });
242
-
243
- it('should have correct level for guidelines', () => {
244
- Object.values(GUIDELINES).forEach(guideline => {
245
- expect(guideline.level).toBe('guideline');
223
+ describe('Constraint Severity', () => {
224
+ it('error 级约束 severity 均为 error', () => {
225
+ ['no_completion_without_verification', 'no_test_simplification', 'docs_freshness'].forEach(id => {
226
+ expect(CONSTRAINTS[id].severity).toBe('error');
246
227
  });
247
228
  });
248
229
 
249
- it('should have correct level for prompts', () => {
250
- Object.values(PROMPTS).forEach(prompt => {
251
- expect(prompt.level).toBe('prompt');
230
+ it('warning 级约束 severity 均为 warning', () => {
231
+ ['no_hardcoded_credentials', 'capability_sync', 'context_doc_sync', 'governance_presence'].forEach(id => {
232
+ expect(CONSTRAINTS[id].severity).toBe('warning');
252
233
  });
253
234
  });
254
235
  });
@@ -30,7 +30,7 @@ describe('PassesGate.check()', () => {
30
30
  expect(result.violations).toBeDefined();
31
31
  expect(result.violations?.length).toBe(1);
32
32
  expect(result.violations?.[0]?.id).toBe('no_self_approval');
33
- expect(result.violations?.[0]?.level).toBe('iron_law');
33
+ expect(result.violations?.[0]?.severity).toBe('error');
34
34
  expect(result.testResult).toEqual(testResult);
35
35
  });
36
36
 
@@ -47,7 +47,7 @@ describe('PassesGate.check()', () => {
47
47
  expect(result.violations).toBeDefined();
48
48
  expect(result.violations?.length).toBe(1);
49
49
  expect(result.violations?.[0]?.id).toBe('no_completion_without_verification');
50
- expect(result.violations?.[0]?.level).toBe('iron_law');
50
+ expect(result.violations?.[0]?.severity).toBe('error');
51
51
  expect(result.testResult).toEqual(testResult);
52
52
  });
53
53
 
@@ -1,9 +1,12 @@
1
1
  /**
2
2
  * ProjectConfigLoader 补充测试
3
+ *
4
+ * ADR-0029:custom 纯文本约束面(custom_constraints / getCustomConstraints /
5
+ * isConstraintRetired 的 custom 落点)已随文本注入层关停一并退役,相关用例移除。
3
6
  */
4
7
 
5
8
  import { describe, it, expect, beforeAll, afterAll, jest } from '@jest/globals';
6
- import { ProjectConfigLoader, isConstraintRetired } from '../core/project-config-loader';
9
+ import { ProjectConfigLoader } from '../core/project-config-loader';
7
10
  import * as fs from 'fs';
8
11
  import * as path from 'path';
9
12
  import * as os from 'os';
@@ -27,7 +30,7 @@ describe('ProjectConfigLoader - 补充覆盖', () => {
27
30
  });
28
31
 
29
32
  describe('禁用约束', () => {
30
- it('应该禁用指定约束', () => {
33
+ it('未知 id 的禁用配置静默忽略(生效集不变)', () => {
31
34
  fs.writeFileSync(
32
35
  path.join(harnessDir, 'config.yml'),
33
36
  `
@@ -43,69 +46,18 @@ constraints:
43
46
  loader.load();
44
47
  const merged = loader.mergeConstraints();
45
48
 
46
- expect(merged.disabled).toContain('legacy_disabled_a');
47
- expect(merged.disabled).toContain('legacy_disabled_b');
49
+ // 未知 id 进 unknownIds 供诊断(filterEnabledEntries 历史口径:未知但 enabled:false 的 id 同时也进 disabled
50
+ expect(merged.unknownIds).toContain('legacy_disabled_a');
51
+ expect(merged.unknownIds).toContain('legacy_disabled_b');
48
52
  });
49
- });
50
-
51
- describe('自定义约束层级', () => {
52
- it('应该正确分类 iron_law 级别约束', () => {
53
- fs.writeFileSync(
54
- path.join(harnessDir, 'config.yml'),
55
- `
56
- custom_constraints:
57
- my_iron_law:
58
- rule: MY IRON LAW
59
- message: Iron law message
60
- level: iron_law
61
- trigger: commit
62
- `
63
- );
64
-
65
- const loader = new ProjectConfigLoader(tempDir);
66
- loader.load();
67
- const merged = loader.mergeConstraints();
68
-
69
- expect(merged.ironLaws['my_iron_law']).toBeDefined();
70
- });
71
-
72
- it('默认应该分类为 guideline', () => {
73
- fs.writeFileSync(
74
- path.join(harnessDir, 'config.yml'),
75
- `
76
- custom_constraints:
77
- my_guideline:
78
- rule: MY GUIDELINE
79
- message: Guideline message
80
- trigger: commit
81
- `
82
- );
83
-
84
- const loader = new ProjectConfigLoader(tempDir);
85
- loader.load();
86
- const merged = loader.mergeConstraints();
87
-
88
- expect(merged.guidelines['my_guideline']).toBeDefined();
89
- });
90
- });
91
53
 
92
- describe('自定义约束 promptInjection', () => {
93
- it('应该透传 promptInjection 并进入注入段渲染', async () => {
54
+ it('内置 id 的禁用配置从生效集移除并进 disabled', () => {
94
55
  fs.writeFileSync(
95
56
  path.join(harnessDir, 'config.yml'),
96
57
  `
97
- custom_constraints:
98
- my_prompt_rule:
99
- rule: MY PROMPT RULE
100
- message: Prompt rule message
101
- level: guideline
102
- trigger: commit
103
- promptInjection: 我的自定义注入文本
104
- my_silent_rule:
105
- rule: MY SILENT RULE
106
- message: No injection text
107
- level: guideline
108
- trigger: commit
58
+ constraints:
59
+ capability_sync:
60
+ enabled: false
109
61
  `
110
62
  );
111
63
 
@@ -113,16 +65,8 @@ custom_constraints:
113
65
  loader.load();
114
66
  const merged = loader.mergeConstraints();
115
67
 
116
- // 字段透传
117
- expect(merged.guidelines['my_prompt_rule']?.promptInjection).toBe('我的自定义注入文本');
118
- // 未定义 promptInjection 的自定义约束保持不注入(与内置无注入文本条目一致)
119
- expect(merged.guidelines['my_silent_rule']?.promptInjection).toBeUndefined();
120
-
121
- // 端到端:进入 CLAUDE.md 注入段渲染
122
- const { renderConstraintsSection } = await import('../core/constraints/injection-renderer');
123
- const section = renderConstraintsSection(Object.values(merged.guidelines), '0.0.0-test');
124
- expect(section).toContain('- **my_prompt_rule**: 我的自定义注入文本');
125
- expect(section).not.toContain('my_silent_rule');
68
+ expect(merged.disabled).toContain('capability_sync');
69
+ expect(merged.constraints['capability_sync']).toBeUndefined();
126
70
  });
127
71
  });
128
72
 
@@ -130,7 +74,7 @@ custom_constraints:
130
74
  /**
131
75
  * `isConstraintEnabled` / `getConstraintSource` 属 ADR-0022 型漏收:
132
76
  * 除本文件与自身测试外双仓零消费者,生效集判定唯一来源是
133
- * `mergeConstraints` → `getEffectiveConstraints`(ADR-0001),这两个查询函数是它的平行复印。
77
+ * `mergeConstraints` → `getEffectiveConstraints`,这两个查询函数是它的平行复印。
134
78
  */
135
79
  it('isConstraintEnabled / getConstraintSource 已从类面删除', () => {
136
80
  const loader = new ProjectConfigLoader(tempDir);
@@ -145,26 +89,6 @@ custom_constraints:
145
89
  });
146
90
  });
147
91
 
148
- describe('isConstraintRetired(退役判定单点,harness#137)', () => {
149
- it('config.yml enabled:false 即已退役,与 custom 条目无关', () => {
150
- expect(isConstraintRetired(undefined, true)).toBe(true);
151
- });
152
-
153
- it('custom 条目带 retired 元数据即已退役(studio#82 D6 落点在条目自身)', () => {
154
- expect(isConstraintRetired({ retired: { at: '2026-08-08T12:00:00.000Z' } }, false)).toBe(true);
155
- });
156
-
157
- it('两个落点都没命中 → 未退役', () => {
158
- expect(isConstraintRetired(undefined, false)).toBe(false);
159
- expect(isConstraintRetired({ retired: undefined }, false)).toBe(false);
160
- });
161
-
162
- it('retired 段的假值(null / 空串)不算退役——mergeConstraints 用的是 truthiness', () => {
163
- expect(isConstraintRetired({ retired: null }, false)).toBe(false);
164
- expect(isConstraintRetired({ retired: '' }, false)).toBe(false);
165
- });
166
- });
167
-
168
92
  describe('hasCustomConfig', () => {
169
93
  it('无自定义配置应该返回 false', () => {
170
94
  // 清空配置
@@ -179,15 +103,13 @@ custom_constraints:
179
103
  expect(loader.hasCustomConfig()).toBe(false);
180
104
  });
181
105
 
182
- it('有自定义约束应该返回 true', () => {
106
+ it(' constraints 段应该返回 true', () => {
183
107
  fs.writeFileSync(
184
108
  path.join(harnessDir, 'config.yml'),
185
109
  `
186
- custom_constraints:
187
- custom_for_has:
188
- rule: CUSTOM
189
- message: Custom
190
- trigger: commit
110
+ constraints:
111
+ capability_sync:
112
+ enabled: false
191
113
  `
192
114
  );
193
115
 
@@ -220,10 +142,8 @@ custom_constraints:
220
142
  loader.load();
221
143
  const merged = loader.mergeConstraints({ preset: 'relaxed' });
222
144
 
223
- // relaxed 仅启用 3 条铁律 + 2 条指导原则,禁用提示
224
- expect(Object.keys(merged.ironLaws)).toHaveLength(3);
225
- expect(Object.keys(merged.guidelines)).toHaveLength(2);
226
- expect(Object.keys(merged.prompts ?? {})).toHaveLength(0);
145
+ // relaxed 仅启用 1 error 级 + 1 条 warning 级
146
+ expect(Object.keys(merged.constraints)).toHaveLength(2);
227
147
  expect(merged.disabled.length).toBeGreaterThan(0);
228
148
  });
229
149
 
@@ -249,25 +169,4 @@ custom_constraints:
249
169
  }
250
170
  });
251
171
  });
252
-
253
- describe('getCustomConstraints', () => {
254
- it('应该返回自定义约束', () => {
255
- fs.writeFileSync(
256
- path.join(harnessDir, 'config.yml'),
257
- `
258
- custom_constraints:
259
- get_custom_test:
260
- rule: TEST
261
- message: Test
262
- trigger: commit
263
- `
264
- );
265
-
266
- const loader = new ProjectConfigLoader(tempDir);
267
- loader.load();
268
-
269
- const customs = loader.getCustomConstraints();
270
- expect(customs['get_custom_test']).toBeDefined();
271
- });
272
- });
273
- });
172
+ });
@@ -32,23 +32,18 @@ describe('ProjectConfigLoader', () => {
32
32
  const config = loader.load();
33
33
 
34
34
  expect(config.preset).toBe('standard');
35
- expect(config.custom_constraints_file).toBeDefined();
36
35
  });
37
36
 
38
37
  it('应该加载 config.yml', () => {
39
38
  fs.writeFileSync(
40
39
  path.join(harnessDir, 'config.yml'),
41
- `
42
- preset: custom
43
- custom_constraints_file: my-constraints.yml
44
- `
40
+ `preset: custom`
45
41
  );
46
42
 
47
43
  const loader = new ProjectConfigLoader(tempDir);
48
44
  const config = loader.load();
49
45
 
50
46
  expect(config.preset).toBe('custom');
51
- expect(config.custom_constraints_file).toBe('my-constraints.yml');
52
47
  });
53
48
  });
54
49
 
@@ -59,29 +54,8 @@ custom_constraints_file: my-constraints.yml
59
54
 
60
55
  const merged = loader.mergeConstraints();
61
56
 
62
- expect(merged.ironLaws).toBeDefined();
63
- expect(merged.guidelines).toBeDefined();
64
- expect(Object.keys(merged.ironLaws).length).toBeGreaterThan(0);
65
- });
66
-
67
- it('应该加载自定义约束', () => {
68
- fs.writeFileSync(
69
- path.join(harnessDir, 'custom-constraints.yml'),
70
- `
71
- custom_rules:
72
- my_rule:
73
- rule: MY CUSTOM RULE
74
- message: Custom message
75
- level: guideline
76
- trigger: commit
77
- `
78
- );
79
-
80
- const loader = new ProjectConfigLoader(tempDir);
81
- loader.load();
82
-
83
- const merged = loader.mergeConstraints();
84
- expect(merged.custom).toBeDefined();
57
+ expect(merged.constraints).toBeDefined();
58
+ expect(Object.keys(merged.constraints).length).toBeGreaterThan(0);
85
59
  });
86
60
  });
87
61
 
@@ -96,8 +70,6 @@ custom_rules:
96
70
  const config = loader.load();
97
71
 
98
72
  expect(config.preset).toBe('minimal');
99
- // 默认值应该保留
100
- expect(config.custom_constraints_file).toBe('custom-constraints.yml');
101
73
  });
102
74
  });
103
75
 
@@ -113,10 +85,10 @@ custom_rules:
113
85
  expect(() => loader.load()).toThrow();
114
86
  });
115
87
 
116
- it('自定义约束文件不存在应该不崩溃', () => {
88
+ it('config.yml 只有无关键时 mergeConstraints 不崩溃', () => {
117
89
  fs.writeFileSync(
118
90
  path.join(harnessDir, 'config.yml'),
119
- `custom_constraints_file: nonexistent.yml`
91
+ `preset: standard`
120
92
  );
121
93
 
122
94
  const loader = new ProjectConfigLoader(tempDir);
@@ -19,8 +19,7 @@ import * as path from 'path';
19
19
 
20
20
  const EXPECTED_RUNTIME_EXPORTS = [
21
21
  'AgentLifecycle',
22
- 'CONSTRAINTS_END_MARKER',
23
- 'CONSTRAINTS_START_MARKER',
22
+ 'CONSTRAINTS',
24
23
  'CSOValidator',
25
24
  'CheckCache',
26
25
  'CheckpointValidator',
@@ -43,10 +42,6 @@ const EXPECTED_RUNTIME_EXPORTS = [
43
42
  'FailureRecorder',
44
43
  'FileKnowledgeStore',
45
44
  'GATE_DEFINITIONS',
46
- 'GUIDELINES',
47
- 'HookPipeline',
48
- 'HookRegistry',
49
- 'IRON_LAWS',
50
45
  'KnowledgeAudit',
51
46
  'KnowledgeHealthScorer',
52
47
  'KnowledgeIngest',
@@ -55,7 +50,6 @@ const EXPECTED_RUNTIME_EXPORTS = [
55
50
  'KnowledgeLinter',
56
51
  'KnowledgeQuery',
57
52
  'PHASE_SUBJECT_RE',
58
- 'PROMPTS',
59
53
  'PassesGate',
60
54
  'PerformanceGate',
61
55
  'ReferenceTracker',
@@ -71,7 +65,6 @@ const EXPECTED_RUNTIME_EXPORTS = [
71
65
  'TraceAnalyzer',
72
66
  'TraceCollector',
73
67
  'assertGateRegistryClosed',
74
- 'assertHookRegistryClosed',
75
68
  'bootstrapHarness',
76
69
  'bootstrapHarnessSync',
77
70
  'checkBeforeExecution',
@@ -112,12 +105,9 @@ const EXPECTED_RUNTIME_EXPORTS = [
112
105
  'matchGlob',
113
106
  'migrateKnowledgeEntries',
114
107
  'registeredGateCount',
115
- 'renderConstraintsByTrigger',
116
- 'renderConstraintsSection',
117
108
  'resolveGlobs',
118
109
  'runGates',
119
110
  'sanitizeExternalContent',
120
- 'toErrorStrategy',
121
111
  'validateAllSpecs',
122
112
  'validateSpec',
123
113
  'verifyContractPresence',
@@ -189,4 +179,34 @@ describe('包根类型面(ADR-0022 关联类型随迁)', () => {
189
179
  expect(liveCheck.allowed).toBe(true);
190
180
  expect(liveTask.command).toBe('npm test');
191
181
  });
182
+
183
+ /**
184
+ * ADR-0027(#170)管线面删除的可达性负钉:4 值符号 + 8 类型。
185
+ *
186
+ * 两道全量清单闸钉的是「码与名单不符」,本钉直接钉「barrel 源里不再出现这些名字」——
187
+ * 回灌时红名指到 barrel 本身,不必从清单 diff 反推。barrel 源形状即全可达面:
188
+ * `export *` 已被 `sub-barrels-explicit.test.ts` 禁到 src 下全部目录 barrel,
189
+ * 且 `./hooks` 不在 `package.json` 的 `exports` 内(无子路径入口可绕)。
190
+ */
191
+ const ADR0027_DELETED_SYMBOLS = [
192
+ 'HookRegistry',
193
+ 'HookPipeline',
194
+ 'assertHookRegistryClosed',
195
+ 'toErrorStrategy',
196
+ 'HookDefinition',
197
+ 'HookConfig',
198
+ 'EffectiveHook',
199
+ 'HookErrorStrategy',
200
+ 'HookExecutionRecord',
201
+ 'HookPhase',
202
+ 'HookResult',
203
+ 'PipelineResult',
204
+ ];
205
+
206
+ for (const rel of ['../hooks/index.ts', '../index.ts']) {
207
+ it(`${rel} 不再提及 ADR-0027 已删的管线面符号(保留面 bootstrapHarness* 不受影响)`, () => {
208
+ const source = fs.readFileSync(path.join(__dirname, rel), 'utf-8');
209
+ expect(ADR0027_DELETED_SYMBOLS.filter((name) => source.includes(name))).toEqual([]);
210
+ });
211
+ }
192
212
  });