@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
@@ -1,247 +0,0 @@
1
- /**
2
- * 注入漂移校验测试(ADR-0001 决策 7,P6)
3
- *
4
- * - 三类漂移各自检出:版本 / 内容(条目级 missing/extra)/ 重复章节
5
- * - 无漂移静默;无标记段 = 未注入不算漂移;CLAUDE.md 不存在不算漂移
6
- * - 落点路由(studio #307):AGENTS.md PRESERVE:governance 内注入段同样校验;
7
- * 两文件均有标记段时 CLAUDE.md 优先(旧模型仓豁免)
8
- * - config.yml 变更后未重跑 init → 内容漂移(extra)
9
- *
10
- * 使用真实临时目录(getEffectiveConstraints 读真实 fs)。
11
- */
12
-
13
- import * as fs from 'fs';
14
- import * as path from 'path';
15
- import { detectInjectionDrift, INJECTION_DRIFT_FIX_HINT } from '../injection-drift';
16
- import {
17
- renderConstraintsSection,
18
- CONSTRAINTS_START_MARKER,
19
- } from '../injection-renderer';
20
- import { getEffectiveConstraints } from '../../effective-constraints';
21
- import { createProjectFixture, writeProjectConfig } from '../../../test-setup/project-fixture';
22
-
23
- const TEST_VERSION = '9.9.9-test';
24
-
25
- const makeTmpProject = (): string => createProjectFixture({ name: 'harness-drift-test' });
26
-
27
- /** 写入与当前生效集完全一致的注入段(版本 TEST_VERSION) */
28
- function writeSyncedClaudeMd(root: string, extra = ''): string {
29
- const section =
30
- '## Governance Rules\n' + renderConstraintsSection(getEffectiveConstraints(root), TEST_VERSION);
31
- const content = `# Test Project\n\n${section}${extra}`;
32
- fs.writeFileSync(path.join(root, 'CLAUDE.md'), content, 'utf-8');
33
- return content;
34
- }
35
-
36
- /** 新模型仓注入段:AGENTS.md `PRESERVE:governance` 段内含标记段(版本 TEST_VERSION) */
37
- function writeSyncedAgentsMd(root: string, extra = ''): string {
38
- const section =
39
- '<!-- PRESERVE:governance -->\n## Governance Rules\n' +
40
- renderConstraintsSection(getEffectiveConstraints(root), TEST_VERSION) +
41
- '<!-- /PRESERVE:governance -->\n';
42
- const content = `# AGENTS.md\n\n${section}${extra}`;
43
- fs.writeFileSync(path.join(root, 'AGENTS.md'), content, 'utf-8');
44
- return content;
45
- }
46
-
47
- describe('detectInjectionDrift', () => {
48
- it('无漂移:注入段与期望渲染一致 → hasDrift=false', () => {
49
- const root = makeTmpProject();
50
- writeSyncedClaudeMd(root);
51
-
52
- const drift = detectInjectionDrift(root, TEST_VERSION);
53
-
54
- expect(drift.notInjected).toBe(false);
55
- expect(drift.hasDrift).toBe(false);
56
- expect(drift.versionDrift).toBeUndefined();
57
- expect(drift.contentDrift).toBeUndefined();
58
- expect(drift.duplicateHeading).toBe(false);
59
- expect(drift.fixHint).toContain('npx @dommaker/harness init');
60
- expect(drift.fixHint).toBe(INJECTION_DRIFT_FIX_HINT);
61
- });
62
-
63
- it('版本漂移:标记段版本号改旧 → versionDrift', () => {
64
- const root = makeTmpProject();
65
- const synced = writeSyncedClaudeMd(root);
66
- fs.writeFileSync(
67
- path.join(root, 'CLAUDE.md'),
68
- synced.replace(`<!-- version: ${TEST_VERSION} -->`, '<!-- version: 0.0.1-old -->'),
69
- 'utf-8'
70
- );
71
-
72
- const drift = detectInjectionDrift(root, TEST_VERSION);
73
-
74
- expect(drift.hasDrift).toBe(true);
75
- expect(drift.versionDrift).toEqual({ expected: TEST_VERSION, actual: '0.0.1-old' });
76
- expect(drift.contentDrift).toBeUndefined();
77
- });
78
-
79
- it('内容漂移:手改标记段内一条 → 原条目 missing、改后条目 extra', () => {
80
- const root = makeTmpProject();
81
- const synced = writeSyncedClaudeMd(root);
82
- const originalLine = synced.split('\n').find(l => l.startsWith('- **'))!;
83
- const editedLine = originalLine.replace(/: .+$/, ': 手工篡改的注入文本');
84
- fs.writeFileSync(
85
- path.join(root, 'CLAUDE.md'),
86
- synced.replace(originalLine, editedLine),
87
- 'utf-8'
88
- );
89
-
90
- const drift = detectInjectionDrift(root, TEST_VERSION);
91
-
92
- expect(drift.hasDrift).toBe(true);
93
- expect(drift.versionDrift).toBeUndefined();
94
- expect(drift.contentDrift).toBeDefined();
95
- expect(drift.contentDrift!.missing).toEqual([originalLine]);
96
- expect(drift.contentDrift!.extra).toEqual([editedLine]);
97
- });
98
-
99
- it('内容漂移:config.yml 变更后未重跑 init → 被禁条目成为 extra', () => {
100
- const root = makeTmpProject();
101
- writeSyncedClaudeMd(root);
102
- // 注入后禁用一条 prompt,但不重跑 init
103
- writeProjectConfig(root, 'constraints:\n no_fuzzy_completion_claim:\n enabled: false\n');
104
-
105
- const drift = detectInjectionDrift(root, TEST_VERSION);
106
-
107
- expect(drift.hasDrift).toBe(true);
108
- expect(drift.contentDrift).toBeDefined();
109
- expect(drift.contentDrift!.missing).toEqual([]);
110
- expect(drift.contentDrift!.extra).toHaveLength(1);
111
- expect(drift.contentDrift!.extra[0]).toContain('no_fuzzy_completion_claim');
112
- });
113
-
114
- it('重复章节:标记段之外还有一个 ## Governance Rules 标题 → duplicateHeading', () => {
115
- const root = makeTmpProject();
116
- writeSyncedClaudeMd(root, '\n## Governance Rules\n\n旧版遗留的同名章节\n');
117
-
118
- const drift = detectInjectionDrift(root, TEST_VERSION);
119
-
120
- expect(drift.duplicateHeading).toBe(true);
121
- expect(drift.hasDrift).toBe(true);
122
- });
123
-
124
- it('无标记段 = 未注入,不算漂移', () => {
125
- const root = makeTmpProject();
126
- fs.writeFileSync(path.join(root, 'CLAUDE.md'), '# Test Project\n\n## Governance Rules\n\n用户自写内容\n', 'utf-8');
127
-
128
- const drift = detectInjectionDrift(root, TEST_VERSION);
129
-
130
- expect(drift.notInjected).toBe(true);
131
- expect(drift.hasDrift).toBe(false);
132
- });
133
-
134
- it('CLAUDE.md 不存在 = 未注入,不算漂移', () => {
135
- const root = makeTmpProject();
136
-
137
- const drift = detectInjectionDrift(root, TEST_VERSION);
138
-
139
- expect(drift.notInjected).toBe(true);
140
- expect(drift.hasDrift).toBe(false);
141
- });
142
-
143
- it('默认版本参数读 package.json:与真实版本一致的注入段无版本漂移', () => {
144
- // eslint-disable-next-line @typescript-eslint/no-var-requires
145
- const realVersion = require('../../../../package.json').version as string;
146
- const root = makeTmpProject();
147
- const section =
148
- '## Governance Rules\n' + renderConstraintsSection(getEffectiveConstraints(root), realVersion);
149
- fs.writeFileSync(path.join(root, 'CLAUDE.md'), section, 'utf-8');
150
-
151
- const drift = detectInjectionDrift(root);
152
-
153
- expect(drift.versionDrift).toBeUndefined();
154
- expect(drift.hasDrift).toBe(false);
155
- });
156
-
157
- it('标记段含 ' + CONSTRAINTS_START_MARKER + ' 但版本行缺失 → versionDrift actual=(缺失)', () => {
158
- const root = makeTmpProject();
159
- const synced = writeSyncedClaudeMd(root);
160
- fs.writeFileSync(
161
- path.join(root, 'CLAUDE.md'),
162
- synced.replace(`<!-- version: ${TEST_VERSION} -->\n`, ''),
163
- 'utf-8'
164
- );
165
-
166
- const drift = detectInjectionDrift(root, TEST_VERSION);
167
-
168
- expect(drift.versionDrift).toEqual({ expected: TEST_VERSION, actual: '(缺失)' });
169
- });
170
-
171
- it('新模型仓:AGENTS.md PRESERVE:governance 注入段一致 → 无漂移,injectionFile=AGENTS.md', () => {
172
- const root = makeTmpProject();
173
- writeSyncedAgentsMd(root);
174
-
175
- const drift = detectInjectionDrift(root, TEST_VERSION);
176
-
177
- expect(drift.notInjected).toBe(false);
178
- expect(drift.injectionFile).toBe('AGENTS.md');
179
- expect(drift.hasDrift).toBe(false);
180
- expect(drift.duplicateHeading).toBe(false);
181
- });
182
-
183
- it('新模型仓:版本漂移与内容漂移照常检出', () => {
184
- const root = makeTmpProject();
185
- const synced = writeSyncedAgentsMd(root);
186
- const originalLine = synced.split('\n').find(l => l.startsWith('- **'))!;
187
- const editedLine = originalLine.replace(/: .+$/, ': 手工篡改的注入文本');
188
- fs.writeFileSync(
189
- path.join(root, 'AGENTS.md'),
190
- synced
191
- .replace(`<!-- version: ${TEST_VERSION} -->`, '<!-- version: 0.0.1-old -->')
192
- .replace(originalLine, editedLine),
193
- 'utf-8'
194
- );
195
-
196
- const drift = detectInjectionDrift(root, TEST_VERSION);
197
-
198
- expect(drift.injectionFile).toBe('AGENTS.md');
199
- expect(drift.hasDrift).toBe(true);
200
- expect(drift.versionDrift).toEqual({ expected: TEST_VERSION, actual: '0.0.1-old' });
201
- expect(drift.contentDrift!.missing).toEqual([originalLine]);
202
- expect(drift.contentDrift!.extra).toEqual([editedLine]);
203
- });
204
-
205
- it('新模型仓:PRESERVE 段之外还有 ## Governance Rules 标题 → duplicateHeading', () => {
206
- const root = makeTmpProject();
207
- writeSyncedAgentsMd(root, '\n## Governance Rules\n\n旧版遗留的同名章节\n');
208
-
209
- const drift = detectInjectionDrift(root, TEST_VERSION);
210
-
211
- expect(drift.injectionFile).toBe('AGENTS.md');
212
- expect(drift.duplicateHeading).toBe(true);
213
- expect(drift.hasDrift).toBe(true);
214
- });
215
-
216
- it('两文件均有标记段:旧模型仓豁免,CLAUDE.md 优先(AGENTS.md 漂移不影响判定)', () => {
217
- const root = makeTmpProject();
218
- writeSyncedClaudeMd(root);
219
- const agents = writeSyncedAgentsMd(root);
220
- // AGENTS.md 注入段手改出漂移,CLAUDE.md 保持一致
221
- fs.writeFileSync(
222
- path.join(root, 'AGENTS.md'),
223
- agents.replace(`<!-- version: ${TEST_VERSION} -->`, '<!-- version: 0.0.1-old -->'),
224
- 'utf-8'
225
- );
226
-
227
- const drift = detectInjectionDrift(root, TEST_VERSION);
228
-
229
- expect(drift.injectionFile).toBe('CLAUDE.md');
230
- expect(drift.hasDrift).toBe(false);
231
- });
232
-
233
- it('两处均无标记段但 AGENTS.md 有重复 Governance Rules 标题 → notInjected + duplicateHeading', () => {
234
- const root = makeTmpProject();
235
- fs.writeFileSync(
236
- path.join(root, 'AGENTS.md'),
237
- '# AGENTS.md\n\n## Governance Rules\n\n甲\n\n## Governance Rules\n\n乙\n',
238
- 'utf-8'
239
- );
240
-
241
- const drift = detectInjectionDrift(root, TEST_VERSION);
242
-
243
- expect(drift.notInjected).toBe(true);
244
- expect(drift.hasDrift).toBe(false);
245
- expect(drift.duplicateHeading).toBe(true);
246
- });
247
- });
@@ -1,67 +0,0 @@
1
- /**
2
- * Agent prompt 约束段渲染(H6/G6:trigger 参数化分组渲染 API)
3
- *
4
- * 收编 studio prompt-injection.ts 的渲染层:role→trigger 路由留在 studio,
5
- * 本模块只做 trigger 参数化 + 生效集过滤 + 层级分组渲染。
6
- *
7
- * 数据源 = getEffectiveConstraints(内置 → preset → config.yml 禁用 →
8
- * custom 追加 → scenes 过滤),与 init 注入、harness check 同一来源。
9
- *
10
- * 输出按层级分组:铁律 → 指导原则 → 行为提示;无 promptInjection 的约束
11
- * (如 docs_freshness)不渲染。
12
- */
13
-
14
- import type { ConstraintLevel, ConstraintTrigger } from '../../types/constraint';
15
- import { matchesTrigger } from './triggers';
16
- import { getEffectiveConstraints } from '../effective-constraints';
17
-
18
- /**
19
- * renderConstraintsByTrigger 选项
20
- */
21
- export interface RenderConstraintsByTriggerOptions {
22
- /** 项目根路径(决定 config.yml 生效集),缺省 process.cwd() */
23
- projectRoot?: string;
24
- }
25
-
26
- /** 层级分组定义(渲染顺序即声明顺序) */
27
- const LEVEL_GROUPS: ReadonlyArray<{ level: ConstraintLevel; heading: string }> = [
28
- { level: 'iron_law', heading: '### 铁律(绝对禁止,无例外)' },
29
- { level: 'guideline', heading: '### 指导原则(优先建议)' },
30
- { level: 'prompt', heading: '### 行为提示' },
31
- ];
32
-
33
- /**
34
- * 按触发条件渲染约束分组文本(注入 Agent system prompt 用)
35
- *
36
- * 过滤:生效集内 trigger 与入参存在交集的约束;无 promptInjection 的不渲染。
37
- * 分组:按层级 iron_law → guideline → prompt 顺序渲染为标题 + 条目列表;
38
- * 无适用约束时返回空字符串。
39
- *
40
- * @param triggers 触发条件(单个或多个;空数组返回空字符串)
41
- * @param options.projectRoot 项目根路径(决定 config.yml 生效集)
42
- */
43
- export function renderConstraintsByTrigger(
44
- triggers: ConstraintTrigger | ConstraintTrigger[],
45
- options?: RenderConstraintsByTriggerOptions,
46
- ): string {
47
- const requested = Array.isArray(triggers) ? triggers : [triggers];
48
- if (requested.length === 0) return '';
49
-
50
- const constraints = getEffectiveConstraints(options?.projectRoot);
51
- const applicable = constraints.filter(
52
- c => c.promptInjection && matchesTrigger(c, requested)
53
- );
54
- if (applicable.length === 0) return '';
55
-
56
- const lines: string[] = ['\n## 行为约束(前置声明)\n'];
57
- for (const group of LEVEL_GROUPS) {
58
- const items = applicable.filter(c => c.level === group.level);
59
- if (items.length === 0) continue;
60
- lines.push(`${group.heading}\n`);
61
- for (const c of items) {
62
- lines.push(`- **${c.id}**: ${c.promptInjection}`);
63
- }
64
- lines.push('');
65
- }
66
- return lines.join('\n');
67
- }
@@ -1,76 +0,0 @@
1
- /**
2
- * no_bypass_checkpoint 检查器旁测(架构评审候选4,ADR-0009 同模式)
3
- *
4
- * 测试面 = ConstraintCheck.evaluate(env):判定只读 env.context.changedFiles
5
- * 的文件内容,无需 git staging(旧 facade 测用真实 git add 属多余 fixture)。
6
- * 自 checker-extra.test.ts 迁入并补齐 BYPASS_PATTERNS 全表用例。
7
- * 违规样例字符串一律拼接构造,避免本文件被该检查器自身命中。
8
- * 项目根由 src/test-setup/project-fixture 声明式构造(回收仍走 mkdtemp-cleanup)。
9
- */
10
-
11
- import { describe, it, expect } from '@jest/globals';
12
- import * as os from 'os';
13
- import * as path from 'path';
14
- import { noBypassCheckpoint } from '../no-bypass-checkpoint';
15
- import { buildCheckEnv } from '../types';
16
- import { createProjectFixture } from '../../../../test-setup/project-fixture';
17
- import type { ConstraintContext } from '../../../../types/constraint';
18
-
19
- /** 与 BYPASS_PATTERNS 对齐的违规样例(拼接构造,源文本不含完整模式串) */
20
- const VIOLATIONS: Array<[string, string]> = [
21
- ['jest/vitest skip 调用', 'test.' + 'skip("skipped", () => {});'],
22
- ['bypass 标志赋值', 'config.byp' + 'ass = true;'],
23
- ['skip 选项', '{ run: { skip' + ': true } }'],
24
- ['显式 bypass 注释', '# bypa' + 'ss checkpoint for perf'],
25
- ['行注释 skip', 'code(); // ' + 'skip this for now'],
26
- ];
27
-
28
- function writeTemp(name: string, content: string): string {
29
- const root = createProjectFixture({
30
- name: `no-bypass-${name}`,
31
- files: { 'subject.ts': content },
32
- });
33
- return path.join(root, 'subject.ts');
34
- }
35
-
36
- function evaluate(changedFiles: string[]) {
37
- const context: ConstraintContext = {
38
- operation: 'code_implementation',
39
- projectPath: process.cwd(),
40
- changedFiles,
41
- };
42
- return noBypassCheckpoint.evaluate(buildCheckEnv(context, 'none'));
43
- }
44
-
45
- describe('no_bypass_checkpoint', () => {
46
- it('无变更文件 → pass(默认放行)', async () => {
47
- expect(await evaluate([])).toBe(true);
48
- });
49
-
50
- for (const [label, content] of VIOLATIONS) {
51
- it(`命中 bypass 模式(${label})→ fail`, async () => {
52
- expect(await evaluate([writeTemp('hit', content)])).toBe(false);
53
- });
54
- }
55
-
56
- it('干净测试文件 → pass(旧 facade 对照用例)', async () => {
57
- expect(
58
- await evaluate([writeTemp('clean', 'test("clean", () => { expect(1).toBe(1); });')])
59
- ).toBe(true);
60
- });
61
-
62
- it('变更文件已删除(不在 fs)→ 跳过该文件继续', async () => {
63
- expect(await evaluate([path.join(os.tmpdir(), 'no-bypass-gone', 'ghost.ts')])).toBe(true);
64
- });
65
-
66
- it('文件不可读(目录当文件)→ 忽略不炸', async () => {
67
- const dir = createProjectFixture({ name: 'no-bypass-dir' });
68
- expect(await evaluate([dir])).toBe(true);
69
- });
70
-
71
- it('多文件任一命中即 fail', async () => {
72
- const clean = writeTemp('multi-clean', 'export const x = 1;');
73
- const dirty = writeTemp('multi-dirty', VIOLATIONS[0][1]);
74
- expect(await evaluate([clean, dirty])).toBe(false);
75
- });
76
- });
@@ -1,40 +0,0 @@
1
- /**
2
- * no_bypass_checkpoint:检查变更文件内容是否有 skip/bypass 关键词(工单 21)
3
- */
4
-
5
- import { existsSync, readFileSync } from 'fs';
6
- import type { ConstraintCheck } from './types';
7
-
8
- const BYPASS_PATTERNS = [
9
- /\.skip\(/, // Jest/Vitest skip
10
- /\.bypass\s*=/, // bypass flag
11
- /skip:\s*true/, // skip option
12
- /bypass\s*checkpoint/, // explicit bypass
13
- /\/\/\s*skip/, // comment skip
14
- ];
15
-
16
- export const noBypassCheckpoint: ConstraintCheck = {
17
- id: 'no_bypass_checkpoint',
18
- async evaluate(env) {
19
- const changedFiles = env.context.changedFiles || [];
20
- if (changedFiles.length === 0) {
21
- return true; // 无变更文件,默认通过
22
- }
23
-
24
- for (const file of changedFiles) {
25
- if (!existsSync(file)) continue;
26
- try {
27
- const content = readFileSync(file, 'utf-8');
28
- for (const pattern of BYPASS_PATTERNS) {
29
- if (pattern.test(content)) {
30
- return false; // 发现 bypass 关键词
31
- }
32
- }
33
- } catch {
34
- // 文件读取失败,忽略
35
- }
36
- }
37
-
38
- return true;
39
- },
40
- };