@dommaker/harness 1.1.1 → 1.2.1
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.
- package/CHANGELOG.md +17 -0
- package/README.md +2 -4
- package/bin/harness.js +12 -37
- package/dist/cli/commands/check.d.ts +2 -2
- package/dist/cli/commands/check.d.ts.map +1 -1
- package/dist/cli/commands/check.js +8 -33
- package/dist/cli/commands/check.js.map +1 -1
- package/dist/cli/commands/constraints-report.js +1 -1
- package/dist/cli/commands/constraints-report.js.map +1 -1
- package/dist/cli/commands/constraints-retire.d.ts +6 -3
- package/dist/cli/commands/constraints-retire.d.ts.map +1 -1
- package/dist/cli/commands/constraints-retire.js +34 -25
- package/dist/cli/commands/constraints-retire.js.map +1 -1
- package/dist/cli/commands/definitions.d.ts +2 -0
- package/dist/cli/commands/definitions.d.ts.map +1 -1
- package/dist/cli/commands/definitions.js.map +1 -1
- package/dist/cli/commands/init.d.ts +21 -1
- package/dist/cli/commands/init.d.ts.map +1 -1
- package/dist/cli/commands/init.js +118 -40
- package/dist/cli/commands/init.js.map +1 -1
- package/dist/cli/commands/knowledge.d.ts.map +1 -1
- package/dist/cli/commands/knowledge.js +1 -12
- package/dist/cli/commands/knowledge.js.map +1 -1
- package/dist/cli/commands/report.d.ts.map +1 -1
- package/dist/cli/commands/report.js +0 -2
- package/dist/cli/commands/report.js.map +1 -1
- package/dist/cli/commands/status.js +2 -2
- package/dist/cli/commands/status.js.map +1 -1
- package/dist/context/index.d.ts +9 -6
- package/dist/context/index.d.ts.map +1 -1
- package/dist/context/index.js +18 -20
- package/dist/context/index.js.map +1 -1
- package/dist/core/constraints/capabilities-parser.d.ts +3 -2
- package/dist/core/constraints/capabilities-parser.d.ts.map +1 -1
- package/dist/core/constraints/capabilities-parser.js +25 -47
- package/dist/core/constraints/capabilities-parser.js.map +1 -1
- package/dist/core/constraints/checker.d.ts +29 -32
- package/dist/core/constraints/checker.d.ts.map +1 -1
- package/dist/core/constraints/checker.js +34 -126
- package/dist/core/constraints/checker.js.map +1 -1
- package/dist/core/constraints/checkers/governance-presence.d.ts +20 -0
- package/dist/core/constraints/checkers/governance-presence.d.ts.map +1 -0
- package/dist/core/constraints/checkers/governance-presence.js +76 -0
- package/dist/core/constraints/checkers/governance-presence.js.map +1 -0
- package/dist/core/constraints/checkers/index.d.ts +2 -1
- package/dist/core/constraints/checkers/index.d.ts.map +1 -1
- package/dist/core/constraints/checkers/index.js +5 -0
- package/dist/core/constraints/checkers/index.js.map +1 -1
- package/dist/core/constraints/checkers/types.d.ts +25 -0
- package/dist/core/constraints/checkers/types.d.ts.map +1 -1
- package/dist/core/constraints/checkers/types.js +24 -0
- package/dist/core/constraints/checkers/types.js.map +1 -1
- package/dist/core/constraints/definitions/guidelines.d.ts.map +1 -1
- package/dist/core/constraints/definitions/guidelines.js +21 -2
- package/dist/core/constraints/definitions/guidelines.js.map +1 -1
- package/dist/core/constraints/definitions/prompts.d.ts.map +1 -1
- package/dist/core/constraints/definitions/prompts.js +0 -5
- package/dist/core/constraints/definitions/prompts.js.map +1 -1
- package/dist/core/constraints/index.d.ts +1 -2
- package/dist/core/constraints/index.d.ts.map +1 -1
- package/dist/core/constraints/index.js +1 -7
- package/dist/core/constraints/index.js.map +1 -1
- package/dist/core/constraints/injection-drift.d.ts +29 -4
- package/dist/core/constraints/injection-drift.d.ts.map +1 -1
- package/dist/core/constraints/injection-drift.js +52 -20
- package/dist/core/constraints/injection-drift.js.map +1 -1
- package/dist/core/constraints/usage-report.d.ts +1 -2
- package/dist/core/constraints/usage-report.d.ts.map +1 -1
- package/dist/core/constraints/usage-report.js +2 -5
- package/dist/core/constraints/usage-report.js.map +1 -1
- package/dist/core/effective-constraints.d.ts +24 -4
- package/dist/core/effective-constraints.d.ts.map +1 -1
- package/dist/core/effective-constraints.js +28 -7
- package/dist/core/effective-constraints.js.map +1 -1
- package/dist/core/effective-set.d.ts +30 -0
- package/dist/core/effective-set.d.ts.map +1 -0
- package/dist/core/effective-set.js +35 -0
- package/dist/core/effective-set.js.map +1 -0
- package/dist/core/index.d.ts +13 -6
- package/dist/core/index.d.ts.map +1 -1
- package/dist/core/index.js +44 -21
- package/dist/core/index.js.map +1 -1
- package/dist/core/project-config-loader.d.ts +3 -6
- package/dist/core/project-config-loader.d.ts.map +1 -1
- package/dist/core/project-config-loader.js +47 -61
- package/dist/core/project-config-loader.js.map +1 -1
- package/dist/failure/constraint-handler.d.ts +1 -1
- package/dist/failure/constraint-handler.js +1 -1
- package/dist/gates/checker-gate.d.ts +1 -4
- package/dist/gates/checker-gate.d.ts.map +1 -1
- package/dist/gates/checker-gate.js +3 -11
- package/dist/gates/checker-gate.js.map +1 -1
- package/dist/gates/definitions.d.ts +9 -38
- package/dist/gates/definitions.d.ts.map +1 -1
- package/dist/gates/definitions.js +24 -13
- package/dist/gates/definitions.js.map +1 -1
- package/dist/gates/effective-gates.d.ts.map +1 -1
- package/dist/gates/effective-gates.js +14 -18
- package/dist/gates/effective-gates.js.map +1 -1
- package/dist/gates/index.d.ts +1 -1
- package/dist/gates/index.d.ts.map +1 -1
- package/dist/gates/index.js.map +1 -1
- package/dist/hooks/bootstrap.d.ts.map +1 -1
- package/dist/hooks/bootstrap.js +0 -3
- package/dist/hooks/bootstrap.js.map +1 -1
- package/dist/index.d.ts +43 -60
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +180 -143
- package/dist/index.js.map +1 -1
- package/dist/knowledge/query.d.ts +15 -0
- package/dist/knowledge/query.d.ts.map +1 -1
- package/dist/knowledge/query.js +23 -0
- package/dist/knowledge/query.js.map +1 -1
- package/dist/monitoring/analyzer-base.d.ts +0 -4
- package/dist/monitoring/analyzer-base.d.ts.map +1 -1
- package/dist/monitoring/analyzer-base.js +0 -21
- package/dist/monitoring/analyzer-base.js.map +1 -1
- package/dist/monitoring/index.d.ts +6 -12
- package/dist/monitoring/index.d.ts.map +1 -1
- package/dist/monitoring/index.js +13 -29
- package/dist/monitoring/index.js.map +1 -1
- package/dist/monitoring/trace-analyzer.d.ts.map +1 -1
- package/dist/monitoring/trace-analyzer.js +2 -68
- package/dist/monitoring/trace-analyzer.js.map +1 -1
- package/dist/monitoring/traces.d.ts +0 -4
- package/dist/monitoring/traces.d.ts.map +1 -1
- package/dist/monitoring/traces.js +0 -14
- package/dist/monitoring/traces.js.map +1 -1
- package/dist/presets/index.d.ts +2 -2
- package/dist/presets/index.d.ts.map +1 -1
- package/dist/presets/index.js +3 -4
- package/dist/presets/index.js.map +1 -1
- package/dist/presets/standard.d.ts +10 -22
- package/dist/presets/standard.d.ts.map +1 -1
- package/dist/presets/standard.js +15 -75
- package/dist/presets/standard.js.map +1 -1
- package/dist/types/constraint.d.ts +1 -29
- package/dist/types/constraint.d.ts.map +1 -1
- package/dist/types/constraint.js +1 -1
- package/dist/types/constraint.js.map +1 -1
- package/dist/types/index.d.ts +0 -3
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/index.js +0 -4
- package/dist/types/index.js.map +1 -1
- package/dist/types/project-config.d.ts +4 -8
- package/dist/types/project-config.d.ts.map +1 -1
- package/dist/types/trace.d.ts +5 -18
- package/dist/types/trace.d.ts.map +1 -1
- package/package.json +2 -2
- package/src/CONTEXT.md +4 -5
- package/src/__tests__/checker-extra.test.ts +33 -139
- package/src/__tests__/checker.test.ts +0 -376
- package/src/__tests__/governance-presence.test.ts +129 -0
- package/src/__tests__/iron-laws.test.ts +7 -7
- package/src/__tests__/project-config-loader-extra.test.ts +42 -29
- package/src/__tests__/public-exports.test.ts +138 -0
- package/src/__tests__/trace-analyzer-extra.test.ts +6 -86
- package/src/__tests__/trace-analyzer.test.ts +4 -5
- package/src/__tests__/trace-collector.test.ts +0 -11
- package/src/__tests__/traces.test.ts +3 -22
- package/src/cli/commands/CONTEXT.md +6 -3
- package/src/cli/commands/__tests__/check-skip-output.test.ts +10 -7
- package/src/cli/commands/__tests__/check.test.ts +29 -69
- package/src/cli/commands/__tests__/constraints-report.test.ts +3 -3
- package/src/cli/commands/__tests__/constraints-retire.test.ts +45 -4
- package/src/cli/commands/__tests__/init-injection.test.ts +202 -0
- package/src/cli/commands/__tests__/knowledge.test.ts +90 -1
- package/src/cli/commands/__tests__/registry.test.ts +8 -6
- package/src/cli/commands/__tests__/status-extra.test.ts +6 -6
- package/src/cli/commands/__tests__/status.test.ts +6 -6
- package/src/cli/commands/__tests__/sync-docs-agents.test.ts +28 -0
- package/src/cli/commands/check.ts +9 -35
- package/src/cli/commands/constraints-report.ts +1 -1
- package/src/cli/commands/constraints-retire.ts +38 -27
- package/src/cli/commands/definitions.ts +2 -0
- package/src/cli/commands/init.ts +129 -40
- package/src/cli/commands/knowledge.ts +1 -16
- package/src/cli/commands/report.ts +0 -2
- package/src/cli/commands/status.ts +2 -2
- package/src/context/index.ts +29 -6
- package/src/core/CONTEXT.md +4 -3
- package/src/core/__tests__/effective-set.test.ts +80 -0
- package/src/core/constraints/__tests__/injection-drift.test.ts +90 -0
- package/src/core/constraints/capabilities-parser.ts +43 -49
- package/src/core/constraints/checker.ts +51 -145
- package/src/core/constraints/checkers/governance-presence.ts +74 -0
- package/src/core/constraints/checkers/index.ts +4 -1
- package/src/core/constraints/checkers/types.ts +42 -0
- package/src/core/constraints/definitions/guidelines.ts +22 -2
- package/src/core/constraints/definitions/prompts.ts +0 -5
- package/src/core/constraints/index.ts +2 -17
- package/src/core/constraints/injection-drift.ts +60 -21
- package/src/core/constraints/usage-report.ts +5 -8
- package/src/core/effective-constraints.ts +35 -7
- package/src/core/effective-set.ts +56 -0
- package/src/core/index.ts +80 -6
- package/src/core/project-config-loader.ts +53 -68
- package/src/failure/CONTEXT.md +1 -1
- package/src/failure/constraint-handler.ts +1 -1
- package/src/gates/CONTEXT.md +3 -3
- package/src/gates/checker-gate.ts +4 -12
- package/src/gates/definitions.ts +27 -50
- package/src/gates/effective-gates.ts +14 -21
- package/src/gates/index.ts +1 -1
- package/src/hooks/bootstrap.ts +0 -3
- package/src/index.ts +384 -148
- package/src/knowledge/CONTEXT.md +2 -2
- package/src/knowledge/__tests__/query.test.ts +64 -0
- package/src/knowledge/query.ts +28 -0
- package/src/monitoring/CONTEXT.md +7 -13
- package/src/monitoring/__tests__/traces.test.ts +0 -10
- package/src/monitoring/analyzer-base.ts +0 -22
- package/src/monitoring/index.ts +6 -12
- package/src/monitoring/trace-analyzer.ts +2 -73
- package/src/monitoring/traces.ts +0 -20
- package/src/presets/__tests__/standard.test.ts +48 -69
- package/src/presets/index.ts +3 -5
- package/src/presets/standard.ts +14 -79
- package/src/spec/CONTEXT.md +5 -13
- package/src/types/constraint.ts +1 -51
- package/src/types/index.ts +0 -12
- package/src/types/project-config.ts +4 -10
- package/src/types/trace.ts +5 -30
- package/dist/core/constraints/interceptor.d.ts +0 -39
- package/dist/core/constraints/interceptor.d.ts.map +0 -1
- package/dist/core/constraints/interceptor.js +0 -166
- package/dist/core/constraints/interceptor.js.map +0 -1
- package/dist/dashboard/data.d.ts +0 -31
- package/dist/dashboard/data.d.ts.map +0 -1
- package/dist/dashboard/data.js +0 -81
- package/dist/dashboard/data.js.map +0 -1
- package/dist/dashboard/index.d.ts +0 -4
- package/dist/dashboard/index.d.ts.map +0 -1
- package/dist/dashboard/index.js +0 -20
- package/dist/dashboard/index.js.map +0 -1
- package/dist/dashboard/stats.d.ts +0 -20
- package/dist/dashboard/stats.d.ts.map +0 -1
- package/dist/dashboard/stats.js +0 -115
- package/dist/dashboard/stats.js.map +0 -1
- package/dist/dashboard/types.d.ts +0 -110
- package/dist/dashboard/types.d.ts.map +0 -1
- package/dist/dashboard/types.js +0 -8
- package/dist/dashboard/types.js.map +0 -1
- package/dist/monitoring/constraint-doctor.d.ts +0 -76
- package/dist/monitoring/constraint-doctor.d.ts.map +0 -1
- package/dist/monitoring/constraint-doctor.js +0 -189
- package/dist/monitoring/constraint-doctor.js.map +0 -1
- package/dist/monitoring/diagnosis-rules.d.ts +0 -18
- package/dist/monitoring/diagnosis-rules.d.ts.map +0 -1
- package/dist/monitoring/diagnosis-rules.js +0 -119
- package/dist/monitoring/diagnosis-rules.js.map +0 -1
- package/dist/monitoring/knowledge-doctor.d.ts +0 -78
- package/dist/monitoring/knowledge-doctor.d.ts.map +0 -1
- package/dist/monitoring/knowledge-doctor.js +0 -266
- package/dist/monitoring/knowledge-doctor.js.map +0 -1
- package/dist/monitoring/knowledge-evolver.d.ts +0 -72
- package/dist/monitoring/knowledge-evolver.d.ts.map +0 -1
- package/dist/monitoring/knowledge-evolver.js +0 -205
- package/dist/monitoring/knowledge-evolver.js.map +0 -1
- package/dist/monitoring/performance-analyzer.d.ts +0 -92
- package/dist/monitoring/performance-analyzer.d.ts.map +0 -1
- package/dist/monitoring/performance-analyzer.js +0 -343
- package/dist/monitoring/performance-analyzer.js.map +0 -1
- package/dist/monitoring/performance-collector.d.ts +0 -103
- package/dist/monitoring/performance-collector.d.ts.map +0 -1
- package/dist/monitoring/performance-collector.js +0 -298
- package/dist/monitoring/performance-collector.js.map +0 -1
- package/dist/safety/index.d.ts +0 -6
- package/dist/safety/index.d.ts.map +0 -1
- package/dist/safety/index.js +0 -22
- package/dist/safety/index.js.map +0 -1
- package/dist/safety/input-guardrail.d.ts +0 -36
- package/dist/safety/input-guardrail.d.ts.map +0 -1
- package/dist/safety/input-guardrail.js +0 -118
- package/dist/safety/input-guardrail.js.map +0 -1
- package/dist/safety/output-guardrail.d.ts +0 -39
- package/dist/safety/output-guardrail.d.ts.map +0 -1
- package/dist/safety/output-guardrail.js +0 -147
- package/dist/safety/output-guardrail.js.map +0 -1
- package/dist/safety/sandbox.d.ts +0 -45
- package/dist/safety/sandbox.d.ts.map +0 -1
- package/dist/safety/sandbox.js +0 -134
- package/dist/safety/sandbox.js.map +0 -1
- package/dist/safety/tool-guardrail.d.ts +0 -51
- package/dist/safety/tool-guardrail.d.ts.map +0 -1
- package/dist/safety/tool-guardrail.js +0 -147
- package/dist/safety/tool-guardrail.js.map +0 -1
- package/dist/safety/types.d.ts +0 -104
- package/dist/safety/types.d.ts.map +0 -1
- package/dist/safety/types.js +0 -6
- package/dist/safety/types.js.map +0 -1
- package/dist/spec/annotation-checker.d.ts +0 -47
- package/dist/spec/annotation-checker.d.ts.map +0 -1
- package/dist/spec/annotation-checker.js +0 -279
- package/dist/spec/annotation-checker.js.map +0 -1
- package/dist/types/enforcement.d.ts +0 -42
- package/dist/types/enforcement.d.ts.map +0 -1
- package/dist/types/enforcement.js +0 -6
- package/dist/types/enforcement.js.map +0 -1
- package/dist/types/monitoring-types.d.ts +0 -53
- package/dist/types/monitoring-types.d.ts.map +0 -1
- package/dist/types/monitoring-types.js +0 -10
- package/dist/types/monitoring-types.js.map +0 -1
- package/dist/types/performance.d.ts +0 -204
- package/dist/types/performance.d.ts.map +0 -1
- package/dist/types/performance.js +0 -12
- package/dist/types/performance.js.map +0 -1
- package/dist/verification/index.d.ts +0 -4
- package/dist/verification/index.d.ts.map +0 -1
- package/dist/verification/index.js +0 -20
- package/dist/verification/index.js.map +0 -1
- package/dist/verification/loop.d.ts +0 -30
- package/dist/verification/loop.d.ts.map +0 -1
- package/dist/verification/loop.js +0 -114
- package/dist/verification/loop.js.map +0 -1
- package/dist/verification/rules-based.d.ts +0 -40
- package/dist/verification/rules-based.d.ts.map +0 -1
- package/dist/verification/rules-based.js +0 -130
- package/dist/verification/rules-based.js.map +0 -1
- package/dist/verification/types.d.ts +0 -69
- package/dist/verification/types.d.ts.map +0 -1
- package/dist/verification/types.js +0 -6
- package/dist/verification/types.js.map +0 -1
- package/src/__tests__/annotation-checker.test.ts +0 -183
- package/src/__tests__/constraint-doctor.test.ts +0 -271
- package/src/__tests__/doctor-extra.test.ts +0 -155
- package/src/__tests__/extend-exceptions.test.ts +0 -149
- package/src/__tests__/interceptor.test.ts +0 -89
- package/src/__tests__/performance-analyzer.test.ts +0 -499
- package/src/__tests__/performance-collector.test.ts +0 -276
- package/src/__tests__/presets.test.ts +0 -65
- package/src/core/constraints/__tests__/interceptor.test.ts +0 -267
- package/src/core/constraints/interceptor.ts +0 -217
- package/src/dashboard/CONTEXT.md +0 -21
- package/src/dashboard/__tests__/data.test.ts +0 -111
- package/src/dashboard/__tests__/stats.test.ts +0 -159
- package/src/dashboard/data.ts +0 -103
- package/src/dashboard/index.ts +0 -3
- package/src/dashboard/stats.ts +0 -127
- package/src/dashboard/types.ts +0 -130
- package/src/monitoring/__tests__/knowledge-doctor.test.ts +0 -278
- package/src/monitoring/__tests__/knowledge-evolver.test.ts +0 -368
- package/src/monitoring/__tests__/performance-analyzer.test.ts +0 -137
- package/src/monitoring/__tests__/performance-collector.test.ts +0 -331
- package/src/monitoring/constraint-doctor.ts +0 -228
- package/src/monitoring/diagnosis-rules.ts +0 -132
- package/src/monitoring/knowledge-doctor.ts +0 -322
- package/src/monitoring/knowledge-evolver.ts +0 -289
- package/src/monitoring/performance-analyzer.ts +0 -400
- package/src/monitoring/performance-collector.ts +0 -326
- package/src/safety/CONTEXT.md +0 -23
- package/src/safety/__tests__/input-guardrail.test.ts +0 -105
- package/src/safety/__tests__/output-guardrail.test.ts +0 -108
- package/src/safety/__tests__/sandbox.test.ts +0 -158
- package/src/safety/__tests__/tool-guardrail.test.ts +0 -127
- package/src/safety/index.ts +0 -5
- package/src/safety/input-guardrail.ts +0 -133
- package/src/safety/output-guardrail.ts +0 -170
- package/src/safety/sandbox.ts +0 -148
- package/src/safety/tool-guardrail.ts +0 -176
- package/src/safety/types.ts +0 -124
- package/src/spec/__tests__/annotation-checker.test.ts +0 -294
- package/src/spec/annotation-checker.ts +0 -320
- package/src/types/enforcement.ts +0 -48
- package/src/types/monitoring-types.ts +0 -68
- package/src/types/performance.ts +0 -295
- package/src/verification/CONTEXT.md +0 -23
- package/src/verification/__tests__/loop.test.ts +0 -303
- package/src/verification/__tests__/rules-based.test.ts +0 -240
- package/src/verification/index.ts +0 -3
- package/src/verification/loop.ts +0 -137
- package/src/verification/rules-based.ts +0 -152
- package/src/verification/types.ts +0 -81
|
@@ -3,6 +3,8 @@
|
|
|
3
3
|
*
|
|
4
4
|
* - 三类漂移各自检出:版本 / 内容(条目级 missing/extra)/ 重复章节
|
|
5
5
|
* - 无漂移静默;无标记段 = 未注入不算漂移;CLAUDE.md 不存在不算漂移
|
|
6
|
+
* - 落点路由(studio #307):AGENTS.md PRESERVE:governance 内注入段同样校验;
|
|
7
|
+
* 两文件均有标记段时 CLAUDE.md 优先(旧模型仓豁免)
|
|
6
8
|
* - config.yml 变更后未重跑 init → 内容漂移(extra)
|
|
7
9
|
*
|
|
8
10
|
* 使用真实临时目录(getEffectiveConstraints 读真实 fs)。
|
|
@@ -33,6 +35,17 @@ function writeSyncedClaudeMd(root: string, extra = ''): string {
|
|
|
33
35
|
return content;
|
|
34
36
|
}
|
|
35
37
|
|
|
38
|
+
/** 新模型仓注入段:AGENTS.md `PRESERVE:governance` 段内含标记段(版本 TEST_VERSION) */
|
|
39
|
+
function writeSyncedAgentsMd(root: string, extra = ''): string {
|
|
40
|
+
const section =
|
|
41
|
+
'<!-- PRESERVE:governance -->\n## Governance Rules\n' +
|
|
42
|
+
renderConstraintsSection(getEffectiveConstraints(root), TEST_VERSION) +
|
|
43
|
+
'<!-- /PRESERVE:governance -->\n';
|
|
44
|
+
const content = `# AGENTS.md\n\n${section}${extra}`;
|
|
45
|
+
fs.writeFileSync(path.join(root, 'AGENTS.md'), content, 'utf-8');
|
|
46
|
+
return content;
|
|
47
|
+
}
|
|
48
|
+
|
|
36
49
|
describe('detectInjectionDrift', () => {
|
|
37
50
|
it('无漂移:注入段与期望渲染一致 → hasDrift=false', () => {
|
|
38
51
|
const root = makeTmpProject();
|
|
@@ -161,4 +174,81 @@ describe('detectInjectionDrift', () => {
|
|
|
161
174
|
|
|
162
175
|
expect(drift.versionDrift).toEqual({ expected: TEST_VERSION, actual: '(缺失)' });
|
|
163
176
|
});
|
|
177
|
+
|
|
178
|
+
it('新模型仓:AGENTS.md PRESERVE:governance 注入段一致 → 无漂移,injectionFile=AGENTS.md', () => {
|
|
179
|
+
const root = makeTmpProject();
|
|
180
|
+
writeSyncedAgentsMd(root);
|
|
181
|
+
|
|
182
|
+
const drift = detectInjectionDrift(root, TEST_VERSION);
|
|
183
|
+
|
|
184
|
+
expect(drift.notInjected).toBe(false);
|
|
185
|
+
expect(drift.injectionFile).toBe('AGENTS.md');
|
|
186
|
+
expect(drift.hasDrift).toBe(false);
|
|
187
|
+
expect(drift.duplicateHeading).toBe(false);
|
|
188
|
+
});
|
|
189
|
+
|
|
190
|
+
it('新模型仓:版本漂移与内容漂移照常检出', () => {
|
|
191
|
+
const root = makeTmpProject();
|
|
192
|
+
const synced = writeSyncedAgentsMd(root);
|
|
193
|
+
const originalLine = synced.split('\n').find(l => l.startsWith('- **'))!;
|
|
194
|
+
const editedLine = originalLine.replace(/: .+$/, ': 手工篡改的注入文本');
|
|
195
|
+
fs.writeFileSync(
|
|
196
|
+
path.join(root, 'AGENTS.md'),
|
|
197
|
+
synced
|
|
198
|
+
.replace(`<!-- version: ${TEST_VERSION} -->`, '<!-- version: 0.0.1-old -->')
|
|
199
|
+
.replace(originalLine, editedLine),
|
|
200
|
+
'utf-8'
|
|
201
|
+
);
|
|
202
|
+
|
|
203
|
+
const drift = detectInjectionDrift(root, TEST_VERSION);
|
|
204
|
+
|
|
205
|
+
expect(drift.injectionFile).toBe('AGENTS.md');
|
|
206
|
+
expect(drift.hasDrift).toBe(true);
|
|
207
|
+
expect(drift.versionDrift).toEqual({ expected: TEST_VERSION, actual: '0.0.1-old' });
|
|
208
|
+
expect(drift.contentDrift!.missing).toEqual([originalLine]);
|
|
209
|
+
expect(drift.contentDrift!.extra).toEqual([editedLine]);
|
|
210
|
+
});
|
|
211
|
+
|
|
212
|
+
it('新模型仓:PRESERVE 段之外还有 ## Governance Rules 标题 → duplicateHeading', () => {
|
|
213
|
+
const root = makeTmpProject();
|
|
214
|
+
writeSyncedAgentsMd(root, '\n## Governance Rules\n\n旧版遗留的同名章节\n');
|
|
215
|
+
|
|
216
|
+
const drift = detectInjectionDrift(root, TEST_VERSION);
|
|
217
|
+
|
|
218
|
+
expect(drift.injectionFile).toBe('AGENTS.md');
|
|
219
|
+
expect(drift.duplicateHeading).toBe(true);
|
|
220
|
+
expect(drift.hasDrift).toBe(true);
|
|
221
|
+
});
|
|
222
|
+
|
|
223
|
+
it('两文件均有标记段:旧模型仓豁免,CLAUDE.md 优先(AGENTS.md 漂移不影响判定)', () => {
|
|
224
|
+
const root = makeTmpProject();
|
|
225
|
+
writeSyncedClaudeMd(root);
|
|
226
|
+
const agents = writeSyncedAgentsMd(root);
|
|
227
|
+
// AGENTS.md 注入段手改出漂移,CLAUDE.md 保持一致
|
|
228
|
+
fs.writeFileSync(
|
|
229
|
+
path.join(root, 'AGENTS.md'),
|
|
230
|
+
agents.replace(`<!-- version: ${TEST_VERSION} -->`, '<!-- version: 0.0.1-old -->'),
|
|
231
|
+
'utf-8'
|
|
232
|
+
);
|
|
233
|
+
|
|
234
|
+
const drift = detectInjectionDrift(root, TEST_VERSION);
|
|
235
|
+
|
|
236
|
+
expect(drift.injectionFile).toBe('CLAUDE.md');
|
|
237
|
+
expect(drift.hasDrift).toBe(false);
|
|
238
|
+
});
|
|
239
|
+
|
|
240
|
+
it('两处均无标记段但 AGENTS.md 有重复 Governance Rules 标题 → notInjected + duplicateHeading', () => {
|
|
241
|
+
const root = makeTmpProject();
|
|
242
|
+
fs.writeFileSync(
|
|
243
|
+
path.join(root, 'AGENTS.md'),
|
|
244
|
+
'# AGENTS.md\n\n## Governance Rules\n\n甲\n\n## Governance Rules\n\n乙\n',
|
|
245
|
+
'utf-8'
|
|
246
|
+
);
|
|
247
|
+
|
|
248
|
+
const drift = detectInjectionDrift(root, TEST_VERSION);
|
|
249
|
+
|
|
250
|
+
expect(drift.notInjected).toBe(true);
|
|
251
|
+
expect(drift.hasDrift).toBe(false);
|
|
252
|
+
expect(drift.duplicateHeading).toBe(true);
|
|
253
|
+
});
|
|
164
254
|
});
|
|
@@ -9,9 +9,16 @@
|
|
|
9
9
|
|
|
10
10
|
import * as fs from 'fs';
|
|
11
11
|
import { IRON_LAWS, GUIDELINES } from './definitions';
|
|
12
|
+
import { COMMAND_DEFINITIONS } from '../../cli/commands/definitions';
|
|
13
|
+
import { GATE_DEFINITIONS } from '../../gates/definitions';
|
|
12
14
|
import { FreshnessRunner } from './doc-freshness/runner';
|
|
13
15
|
import type { DocFreshnessCheck } from '../../types/project-config';
|
|
14
16
|
|
|
17
|
+
/** CLI 命令数 = 非门禁命令定义 + 门禁命令定义(ADR-0002:定义表是命令形状单一来源) */
|
|
18
|
+
function cliCommandCount(): number {
|
|
19
|
+
return COMMAND_DEFINITIONS.length + GATE_DEFINITIONS.filter(g => g.cli).length;
|
|
20
|
+
}
|
|
21
|
+
|
|
15
22
|
/** 匹配表格单元格中的源码文件条目(如 `src/foo.ts`、`foo.tsx`) */
|
|
16
23
|
const FILE_CELL_REGEX = /\|\s*([^|]+?\.(?:ts|tsx|js|jsx))\s*\|/g;
|
|
17
24
|
|
|
@@ -104,41 +111,40 @@ export function aggregateToSourceSubdir(root: string, file: string): string {
|
|
|
104
111
|
|
|
105
112
|
// ── 能力清单计数 ────────────────────────────────────────────
|
|
106
113
|
|
|
114
|
+
/**
|
|
115
|
+
* 能力清单统计规则单份定义(ADR-0008:check/write 两方向共用,
|
|
116
|
+
* 此前 label+pattern 在 buildCapabilityChecks 与 updateCapabilityCounts 各写一遍)
|
|
117
|
+
*
|
|
118
|
+
* - label:CAPABILITIES.md 计数行 label(check 的 DocFreshnessCheck.label +
|
|
119
|
+
* write 的替换文本前缀,如 `Quality Gates (6)`)
|
|
120
|
+
* - pattern:同时供 check(doc_regex_count 字符串 pattern,捕获组为文档计数)
|
|
121
|
+
* 与 write(编译为替换 regex)
|
|
122
|
+
* - actual:定义表实际计数(ADR-0002 单一来源)
|
|
123
|
+
*/
|
|
124
|
+
interface CapabilityCountRule {
|
|
125
|
+
label: string;
|
|
126
|
+
pattern: string;
|
|
127
|
+
actual: () => number;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
const CAPABILITY_COUNT_RULES: CapabilityCountRule[] = [
|
|
131
|
+
{ label: 'CLI Commands', pattern: 'CLI Commands\\s*\\((\\d+)\\)', actual: cliCommandCount },
|
|
132
|
+
{ label: 'Quality Gates', pattern: 'Quality Gates?\\s*\\((\\d+)\\)', actual: () => GATE_DEFINITIONS.length },
|
|
133
|
+
{ label: 'Iron Laws', pattern: 'Iron Laws?\\s*\\((\\d+)\\)', actual: () => Object.keys(IRON_LAWS).length },
|
|
134
|
+
{ label: 'Guidelines', pattern: 'Guidelines?\\s*\\((\\d+)\\)', actual: () => Object.keys(GUIDELINES).length },
|
|
135
|
+
];
|
|
136
|
+
|
|
107
137
|
/**
|
|
108
138
|
* 构建 CAPABILITIES.md 能力清单格式的检查配置
|
|
109
139
|
*/
|
|
110
140
|
function buildCapabilityChecks(): DocFreshnessCheck[] {
|
|
111
|
-
return
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
actual: { kind: 'dir_count', path: 'src/cli/commands', extension: '.ts', exclude: ['index.ts', 'definitions.ts'] },
|
|
119
|
-
},
|
|
120
|
-
{
|
|
121
|
-
type: 'doc_regex_count',
|
|
122
|
-
doc: 'CAPABILITIES.md',
|
|
123
|
-
label: 'Quality Gates',
|
|
124
|
-
pattern: 'Quality Gates?\\s*\\((\\d+)\\)',
|
|
125
|
-
actual: { kind: 'dir_count', path: 'src/gates', extension: '.ts', exclude: ['index.ts', 'types.ts'] },
|
|
126
|
-
},
|
|
127
|
-
{
|
|
128
|
-
type: 'doc_regex_count',
|
|
129
|
-
doc: 'CAPABILITIES.md',
|
|
130
|
-
label: 'Iron Laws',
|
|
131
|
-
pattern: 'Iron Laws?\\s*\\((\\d+)\\)',
|
|
132
|
-
actual: { kind: 'const_count', value: Object.keys(IRON_LAWS).length },
|
|
133
|
-
},
|
|
134
|
-
{
|
|
135
|
-
type: 'doc_regex_count',
|
|
136
|
-
doc: 'CAPABILITIES.md',
|
|
137
|
-
label: 'Guidelines',
|
|
138
|
-
pattern: 'Guidelines?\\s*\\((\\d+)\\)',
|
|
139
|
-
actual: { kind: 'const_count', value: Object.keys(GUIDELINES).length },
|
|
140
|
-
},
|
|
141
|
-
];
|
|
141
|
+
return CAPABILITY_COUNT_RULES.map(rule => ({
|
|
142
|
+
type: 'doc_regex_count' as const,
|
|
143
|
+
doc: 'CAPABILITIES.md',
|
|
144
|
+
label: rule.label,
|
|
145
|
+
pattern: rule.pattern,
|
|
146
|
+
actual: { kind: 'const_count' as const, value: rule.actual() },
|
|
147
|
+
}));
|
|
142
148
|
}
|
|
143
149
|
|
|
144
150
|
/**
|
|
@@ -164,26 +170,14 @@ export function checkCapabilityCounts(
|
|
|
164
170
|
/**
|
|
165
171
|
* 更新 CAPABILITIES.md 中的能力清单计数(write 模式)
|
|
166
172
|
*
|
|
167
|
-
*
|
|
173
|
+
* 命令/门禁计数取自定义表(ADR-0002 单一来源),regex 替换文档计数行;
|
|
174
|
+
* 规则与 check 方向共用 CAPABILITY_COUNT_RULES(ADR-0008)。
|
|
168
175
|
*/
|
|
169
|
-
export function updateCapabilityCounts(content: string,
|
|
170
|
-
const
|
|
171
|
-
|
|
172
|
-
const cliCount = runner.countFromDir('src/cli/commands', '.ts', ['index.ts', 'definitions.ts'], projectPath);
|
|
173
|
-
const gateCount = runner.countFromDir('src/gates', '.ts', ['index.ts', 'types.ts'], projectPath);
|
|
174
|
-
const ironCount = Object.keys(IRON_LAWS).length;
|
|
175
|
-
const guideCount = Object.keys(GUIDELINES).length;
|
|
176
|
-
|
|
177
|
-
const replacements: [RegExp, string][] = [
|
|
178
|
-
[/CLI Commands\s*\(\d+\)/, `CLI Commands (${cliCount})`],
|
|
179
|
-
[/Quality Gates?\s*\(\d+\)/, `Quality Gates (${gateCount})`],
|
|
180
|
-
[/Iron Laws?\s*\(\d+\)/, `Iron Laws (${ironCount})`],
|
|
181
|
-
[/Guidelines?\s*\(\d+\)/, `Guidelines (${guideCount})`],
|
|
182
|
-
];
|
|
183
|
-
|
|
184
|
-
for (const [regex, replacement] of replacements) {
|
|
176
|
+
export function updateCapabilityCounts(content: string, _projectPath: string): string {
|
|
177
|
+
for (const rule of CAPABILITY_COUNT_RULES) {
|
|
178
|
+
const regex = new RegExp(rule.pattern);
|
|
185
179
|
if (regex.test(content)) {
|
|
186
|
-
content = content.replace(regex,
|
|
180
|
+
content = content.replace(regex, `${rule.label} (${rule.actual()})`);
|
|
187
181
|
}
|
|
188
182
|
}
|
|
189
183
|
|
|
@@ -17,6 +17,7 @@ import {
|
|
|
17
17
|
ConstraintViolationError,
|
|
18
18
|
} from '../../types/constraint';
|
|
19
19
|
import type { ExecutionTrace } from '../../types/trace';
|
|
20
|
+
import { getTraceCollector } from '../../monitoring/traces';
|
|
20
21
|
import { IRON_LAWS, GUIDELINES, PROMPTS } from './definitions';
|
|
21
22
|
import type { MergedConstraintsConfig } from '../../types/project-config';
|
|
22
23
|
import { normalizeTriggers } from '../../utils/exec';
|
|
@@ -24,50 +25,19 @@ import { runCommand } from '../../utils/exec';
|
|
|
24
25
|
import { join, relative } from 'path';
|
|
25
26
|
import { CheckCache } from './check-cache';
|
|
26
27
|
import { findTsSourceFiles } from '../../utils/file-walk';
|
|
27
|
-
import { getConstraintCheck,
|
|
28
|
+
import { getConstraintCheck, buildCheckEnv, type CheckOutcome } from './checkers';
|
|
28
29
|
|
|
29
30
|
/**
|
|
30
|
-
*
|
|
31
|
-
|
|
32
|
-
const EXCEPTION_FIELD_MAP: Record<string, keyof ConstraintContext> = {
|
|
33
|
-
scalability_required: 'scalabilityRequired',
|
|
34
|
-
security_required: 'securityRequired',
|
|
35
|
-
performance_required: 'performanceRequired',
|
|
36
|
-
reliability_required: 'reliabilityRequired',
|
|
37
|
-
simple_typo: 'isSimpleTypo',
|
|
38
|
-
config_value_error: 'isConfigValueError',
|
|
39
|
-
missing_config: 'isMissingConfig',
|
|
40
|
-
config_file: 'isConfigFile',
|
|
41
|
-
type_definition: 'isTypeDefinition',
|
|
42
|
-
simple_accessor: 'isSimpleAccessor',
|
|
43
|
-
pure_display_component: 'isPureDisplayComponent',
|
|
44
|
-
json_parse_result: 'isJsonParseResult',
|
|
45
|
-
third_party_no_types: 'isThirdPartyNoTypes',
|
|
46
|
-
legacy_migration: 'isLegacyMigration',
|
|
47
|
-
internal_refactor: 'isInternalRefactor',
|
|
48
|
-
bug_fix_only: 'isBugFixOnly',
|
|
49
|
-
performance_optimization: 'isPerformanceOptimization',
|
|
50
|
-
redundant_code_cleanup: 'isRedundantCodeCleanup',
|
|
51
|
-
same_effect_refactor: 'isSameEffectRefactor',
|
|
52
|
-
unused_code_removal: 'isUnusedCodeRemoval',
|
|
53
|
-
external_dependency: 'isExternalDependency',
|
|
54
|
-
explicit_instruction: 'isExplicitInstruction',
|
|
55
|
-
emergency_fix: 'isEmergencyFix',
|
|
56
|
-
existing_design: 'isExistingDesign',
|
|
57
|
-
};
|
|
58
|
-
|
|
59
|
-
/**
|
|
60
|
-
* trace 记录器最小接口(工单 15:core→monitoring 循环消除)
|
|
31
|
+
* trace 记录器最小接口(原工单 15 为消除 core→monitoring 循环;现 monitoring
|
|
32
|
+
* 不反向依赖 core,方向单向、无环,checker 可直接值引用 getTraceCollector)
|
|
61
33
|
*
|
|
62
|
-
* checker
|
|
63
|
-
* setTraceRecorder
|
|
34
|
+
* checker 不构造收集器;生产路径首次记录时惰性接线 getTraceCollector()
|
|
35
|
+
* (幂等单点,ADR-0003),测试可经 setTraceRecorder 注入替身。
|
|
64
36
|
*/
|
|
65
37
|
export interface TraceRecorder {
|
|
66
38
|
record(trace: ExecutionTrace): void;
|
|
67
39
|
}
|
|
68
40
|
|
|
69
|
-
const NOOP_TRACE_RECORDER: TraceRecorder = { record: () => undefined };
|
|
70
|
-
|
|
71
41
|
/**
|
|
72
42
|
* 约束检查器
|
|
73
43
|
*/
|
|
@@ -78,16 +48,26 @@ export class ConstraintChecker {
|
|
|
78
48
|
private cache: CheckCache = new CheckCache({ ttlMs: 1000 });
|
|
79
49
|
|
|
80
50
|
/**
|
|
81
|
-
* run 级 memo(工单 18):一次 checkConstraints
|
|
51
|
+
* run 级 memo(工单 18):一次 checkConstraints 内
|
|
82
52
|
* git diff 等命令只执行一次;run 外直接调用私有检查方法时不 memo(恒新鲜)。
|
|
83
53
|
*/
|
|
84
54
|
private runCache: Map<string, Promise<string>> | null = null;
|
|
85
55
|
|
|
86
|
-
/** trace
|
|
87
|
-
private traceRecorder: TraceRecorder =
|
|
56
|
+
/** trace 记录器(注入式;null = 未显式注入,首次记录时惰性接线全局收集器) */
|
|
57
|
+
private traceRecorder: TraceRecorder | null = null;
|
|
88
58
|
|
|
89
59
|
private constructor() {}
|
|
90
60
|
|
|
61
|
+
/**
|
|
62
|
+
* 取 trace 记录器:未显式注入时惰性接线全局收集器(幂等,ADR-0003 副作用收敛)
|
|
63
|
+
*/
|
|
64
|
+
private getRecorder(): TraceRecorder {
|
|
65
|
+
if (!this.traceRecorder) {
|
|
66
|
+
this.traceRecorder = getTraceCollector();
|
|
67
|
+
}
|
|
68
|
+
return this.traceRecorder;
|
|
69
|
+
}
|
|
70
|
+
|
|
91
71
|
/**
|
|
92
72
|
* run 内 memoize;无活动 run 时直接执行(工单 18)
|
|
93
73
|
*/
|
|
@@ -130,7 +110,7 @@ export class ConstraintChecker {
|
|
|
130
110
|
}
|
|
131
111
|
|
|
132
112
|
/**
|
|
133
|
-
* 注入 trace
|
|
113
|
+
* 注入 trace 记录器(测试/定制场景;不调用则首次记录时惰性接线全局收集器)
|
|
134
114
|
*/
|
|
135
115
|
setTraceRecorder(recorder: TraceRecorder): void {
|
|
136
116
|
this.traceRecorder = recorder;
|
|
@@ -139,29 +119,25 @@ export class ConstraintChecker {
|
|
|
139
119
|
/**
|
|
140
120
|
* 获取当前的约束集合(内置 + 自定义)
|
|
141
121
|
*
|
|
122
|
+
* 纯查询,无副作用(ADR-0006:原 `.check` 装配已随唯一消费者
|
|
123
|
+
* interceptor 的删除一并移除,ADR-0004)。
|
|
124
|
+
*
|
|
142
125
|
* @param customConfig 可选,per-request 自定义配置;不传则返回内置约束集
|
|
143
126
|
*/
|
|
144
127
|
getConstraints(customConfig?: MergedConstraintsConfig | null): {
|
|
145
|
-
ironLaws: Record<string, Constraint
|
|
146
|
-
guidelines: Record<string, Constraint
|
|
147
|
-
prompts: Record<string, Constraint
|
|
128
|
+
ironLaws: Record<string, Constraint>;
|
|
129
|
+
guidelines: Record<string, Constraint>;
|
|
130
|
+
prompts: Record<string, Constraint>;
|
|
148
131
|
} {
|
|
149
132
|
const config = customConfig;
|
|
150
133
|
const source = config
|
|
151
134
|
? { ironLaws: config.ironLaws, guidelines: config.guidelines, prompts: config.prompts ?? PROMPTS }
|
|
152
135
|
: { ironLaws: IRON_LAWS, guidelines: GUIDELINES, prompts: PROMPTS };
|
|
153
136
|
|
|
154
|
-
// Wire unified check() method on every constraint
|
|
155
|
-
const wire = (constraints: Record<string, Constraint>) => {
|
|
156
|
-
for (const c of Object.values(constraints)) {
|
|
157
|
-
(c as any).check = (ctx: ConstraintContext) => this.check(c, ctx);
|
|
158
|
-
}
|
|
159
|
-
return constraints as Record<string, Constraint & { check: (ctx: ConstraintContext) => Promise<ConstraintResult> }>;
|
|
160
|
-
};
|
|
161
137
|
return {
|
|
162
|
-
ironLaws:
|
|
163
|
-
guidelines:
|
|
164
|
-
prompts:
|
|
138
|
+
ironLaws: source.ironLaws,
|
|
139
|
+
guidelines: source.guidelines,
|
|
140
|
+
prompts: source.prompts,
|
|
165
141
|
};
|
|
166
142
|
}
|
|
167
143
|
|
|
@@ -183,20 +159,6 @@ export class ConstraintChecker {
|
|
|
183
159
|
};
|
|
184
160
|
}
|
|
185
161
|
|
|
186
|
-
// 检查例外条件(仅 Guidelines 有效)
|
|
187
|
-
if (constraint.level === 'guideline' && constraint.exceptions) {
|
|
188
|
-
if (this.checkException(constraint, context)) {
|
|
189
|
-
return {
|
|
190
|
-
id: constraint.id,
|
|
191
|
-
level: constraint.level,
|
|
192
|
-
satisfied: true,
|
|
193
|
-
constraint,
|
|
194
|
-
message: `指导原则 ${constraint.id} 因例外条件被豁免`,
|
|
195
|
-
checkedAt: new Date(),
|
|
196
|
-
};
|
|
197
|
-
}
|
|
198
|
-
}
|
|
199
|
-
|
|
200
162
|
// 检查前置条件('skip' = 约定未采用/证据未接线:satisfied 置 true 但不计 pass/fail)
|
|
201
163
|
const outcome = await this.checkPrecondition(constraint, context);
|
|
202
164
|
|
|
@@ -225,16 +187,6 @@ export class ConstraintChecker {
|
|
|
225
187
|
};
|
|
226
188
|
}
|
|
227
189
|
|
|
228
|
-
/**
|
|
229
|
-
* 检查例外条件
|
|
230
|
-
*/
|
|
231
|
-
private checkException(constraint: Constraint, context: ConstraintContext): boolean {
|
|
232
|
-
if (!constraint.exceptions) return false;
|
|
233
|
-
return constraint.exceptions.some(
|
|
234
|
-
(ex) => context[EXCEPTION_FIELD_MAP[ex]] === true
|
|
235
|
-
);
|
|
236
|
-
}
|
|
237
|
-
|
|
238
190
|
/**
|
|
239
191
|
* 根据约束层级获取严重性
|
|
240
192
|
*/
|
|
@@ -279,9 +231,6 @@ export class ConstraintChecker {
|
|
|
279
231
|
result: checkResult.skipped ? 'skip' : checkResult.satisfied ? 'pass' : 'fail',
|
|
280
232
|
operation: context.operation,
|
|
281
233
|
severity: this.getSeverity(constraint.level),
|
|
282
|
-
exceptionApplied: checkResult.message?.includes('豁免')
|
|
283
|
-
? constraint.exceptions?.[0]
|
|
284
|
-
: undefined,
|
|
285
234
|
projectPath: context.projectPath,
|
|
286
235
|
sessionId: context.sessionId,
|
|
287
236
|
});
|
|
@@ -306,9 +255,7 @@ export class ConstraintChecker {
|
|
|
306
255
|
}
|
|
307
256
|
|
|
308
257
|
const projectPath = context.projectPath || process.cwd();
|
|
309
|
-
const env
|
|
310
|
-
context,
|
|
311
|
-
projectPath,
|
|
258
|
+
const env = buildCheckEnv(context, {
|
|
312
259
|
stagedDiff: () => this.getStagedDiff(projectPath),
|
|
313
260
|
stagedDiffNames: () => this.getStagedDiffNames(projectPath),
|
|
314
261
|
srcScan: (root: string) =>
|
|
@@ -317,7 +264,7 @@ export class ConstraintChecker {
|
|
|
317
264
|
relative(projectPath, f)
|
|
318
265
|
)
|
|
319
266
|
),
|
|
320
|
-
};
|
|
267
|
+
});
|
|
321
268
|
|
|
322
269
|
return await impl.evaluate(env);
|
|
323
270
|
}
|
|
@@ -380,7 +327,7 @@ export class ConstraintChecker {
|
|
|
380
327
|
warningCount: 0,
|
|
381
328
|
};
|
|
382
329
|
|
|
383
|
-
const traceCollector = this.
|
|
330
|
+
const traceCollector = this.getRecorder();
|
|
384
331
|
const constraints = this.getConstraints(customConfig);
|
|
385
332
|
|
|
386
333
|
// 1. Iron Laws: 必须全部通过
|
|
@@ -416,53 +363,6 @@ export class ConstraintChecker {
|
|
|
416
363
|
}
|
|
417
364
|
}
|
|
418
365
|
|
|
419
|
-
/**
|
|
420
|
-
* S11: 安全全量检查 — 全部约束检查,不抛异常
|
|
421
|
-
*
|
|
422
|
-
* 区别 checkConstraints(): Iron Law 违规收集在结果中而不是抛异常。
|
|
423
|
-
* 调用方通过 result.passed + result.warningCount 判断状态。
|
|
424
|
-
*/
|
|
425
|
-
async checkConstraintsSafe(
|
|
426
|
-
context: ConstraintContext,
|
|
427
|
-
customConfig?: MergedConstraintsConfig | null
|
|
428
|
-
): Promise<ConstraintCheckResult> {
|
|
429
|
-
// run 起始:重置 run 级缓存(工单 18)
|
|
430
|
-
this.cache.invalidate();
|
|
431
|
-
this.runCache = new Map();
|
|
432
|
-
|
|
433
|
-
try {
|
|
434
|
-
const result: ConstraintCheckResult = {
|
|
435
|
-
ironLaws: [],
|
|
436
|
-
guidelines: [],
|
|
437
|
-
passed: true,
|
|
438
|
-
warningCount: 0,
|
|
439
|
-
};
|
|
440
|
-
|
|
441
|
-
const traceCollector = this.traceRecorder;
|
|
442
|
-
const constraints = this.getConstraints(customConfig);
|
|
443
|
-
|
|
444
|
-
for (const constraint of Object.values(constraints.ironLaws)) {
|
|
445
|
-
if (!this.matchesTrigger(constraint, context)) continue;
|
|
446
|
-
const checkResult = await this.check(constraint, context);
|
|
447
|
-
result.ironLaws.push(checkResult);
|
|
448
|
-
this.recordTrace(traceCollector, constraint, checkResult, context);
|
|
449
|
-
if (!checkResult.satisfied) result.passed = false;
|
|
450
|
-
}
|
|
451
|
-
|
|
452
|
-
for (const constraint of Object.values(constraints.guidelines)) {
|
|
453
|
-
if (!this.matchesTrigger(constraint, context)) continue;
|
|
454
|
-
const checkResult = await this.check(constraint, context);
|
|
455
|
-
result.guidelines.push(checkResult);
|
|
456
|
-
this.recordTrace(traceCollector, constraint, checkResult, context);
|
|
457
|
-
if (!checkResult.satisfied) result.warningCount++;
|
|
458
|
-
}
|
|
459
|
-
|
|
460
|
-
return result;
|
|
461
|
-
} finally {
|
|
462
|
-
this.runCache = null;
|
|
463
|
-
}
|
|
464
|
-
}
|
|
465
|
-
|
|
466
366
|
/**
|
|
467
367
|
* 执行前检查(仅检查 Iron Laws)
|
|
468
368
|
*
|
|
@@ -524,26 +424,32 @@ export async function checkConstraint(
|
|
|
524
424
|
}
|
|
525
425
|
|
|
526
426
|
/**
|
|
527
|
-
*
|
|
528
|
-
*
|
|
529
|
-
* @param context 约束上下文
|
|
530
|
-
* @param customConfig 可选,per-request 自定义配置
|
|
427
|
+
* checkConstraints 选项(ADR-0003:包根/子路径统一 options 对象签名)
|
|
531
428
|
*/
|
|
532
|
-
export
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
429
|
+
export interface CheckConstraintsOptions {
|
|
430
|
+
/** 每条约束检查后的回调(用于记录 trace 到外部存储) */
|
|
431
|
+
onTrace?: (result: ConstraintResult) => void;
|
|
432
|
+
/** 可选,per-request 自定义约束配置;不传则用内置约束集 */
|
|
433
|
+
customConfig?: MergedConstraintsConfig | null;
|
|
537
434
|
}
|
|
538
435
|
|
|
539
436
|
/**
|
|
540
|
-
*
|
|
437
|
+
* 快捷函数:执行三层检查
|
|
438
|
+
*
|
|
439
|
+
* @param context 约束上下文
|
|
440
|
+
* @param options.onTrace 每条约束检查后的回调(用于记录 trace 到外部存储)
|
|
441
|
+
* @param options.customConfig 可选,per-request 自定义约束配置;不传则用内置约束集
|
|
541
442
|
*/
|
|
542
|
-
export async function
|
|
443
|
+
export async function checkConstraints(
|
|
543
444
|
context: ConstraintContext,
|
|
544
|
-
|
|
445
|
+
options?: CheckConstraintsOptions
|
|
545
446
|
): Promise<ConstraintCheckResult> {
|
|
546
|
-
|
|
447
|
+
const result = await ConstraintChecker.getInstance().checkConstraints(context, options?.customConfig ?? null);
|
|
448
|
+
if (options?.onTrace) {
|
|
449
|
+
for (const r of result.ironLaws) options.onTrace(r);
|
|
450
|
+
for (const r of result.guidelines) options.onTrace(r);
|
|
451
|
+
}
|
|
452
|
+
return result;
|
|
547
453
|
}
|
|
548
454
|
|
|
549
455
|
/**
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* governance_presence:治理契约在场守护(studio #302,ADR 2026-08-21 落点模型)
|
|
3
|
+
*
|
|
4
|
+
* 治理契约正本 = AGENTS.md 手写 `PRESERVE:governance` 段(旧模型仓 = CLAUDE.md
|
|
5
|
+
* Governance Rules / HARNESS_CONSTRAINTS 注入段)。PRESERVE 只保「存在」不保「在场」:
|
|
6
|
+
* 段被删除/掏空后 sync-docs 重新生成会静默丢失,此处补「在场」校验。
|
|
7
|
+
*
|
|
8
|
+
* ADR-0001 存在性探测:项目未采用 harness 治理(无 .harness/config.yml)→ skip。
|
|
9
|
+
* 向后兼容:旧模型仓(CLAUDE.md 有治理块、AGENTS.md 无 PRESERVE:governance)→ pass,
|
|
10
|
+
* 不强制迁移;两处都没有才报违规。
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import { readFileSync } from 'fs';
|
|
14
|
+
import { join } from 'path';
|
|
15
|
+
import { loadRawProjectConfig } from '../../project-config-loader';
|
|
16
|
+
import type { ConstraintCheck } from './types';
|
|
17
|
+
|
|
18
|
+
/** 治理契约 PRESERVE 段名(ADR 落点模型约定) */
|
|
19
|
+
export const GOVERNANCE_PRESERVE_NAME = 'governance';
|
|
20
|
+
|
|
21
|
+
const GOVERNANCE_BEGIN_RE = /^<!-- PRESERVE:governance -->\s*$/m;
|
|
22
|
+
|
|
23
|
+
/** AGENTS.md 是否存在非空 PRESERVE:governance 块(标记须独占一行,与 preserve-block.ts 同语义) */
|
|
24
|
+
export function hasGovernancePreserveBlock(agentsMdPath: string): boolean {
|
|
25
|
+
let content: string;
|
|
26
|
+
try {
|
|
27
|
+
content = readFileSync(agentsMdPath, 'utf-8');
|
|
28
|
+
} catch {
|
|
29
|
+
return false;
|
|
30
|
+
}
|
|
31
|
+
const beginMatch = GOVERNANCE_BEGIN_RE.exec(content);
|
|
32
|
+
if (!beginMatch) return false;
|
|
33
|
+
const rest = content.slice(beginMatch.index + beginMatch[0].length);
|
|
34
|
+
const endIdx = rest.indexOf(`<!-- /PRESERVE:${GOVERNANCE_PRESERVE_NAME} -->`);
|
|
35
|
+
if (endIdx === -1) return false;
|
|
36
|
+
// 块体(首尾标记之间)去空白后非空才算「在场」
|
|
37
|
+
return rest.slice(0, endIdx).trim().length > 0;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/** CLAUDE.md 是否存在治理块(与 agents-syncer getGovernanceInfo 同判定,旧模型仓豁免) */
|
|
41
|
+
export function hasClaudeGovernance(claudeMdPath: string): boolean {
|
|
42
|
+
let content: string;
|
|
43
|
+
try {
|
|
44
|
+
content = readFileSync(claudeMdPath, 'utf-8');
|
|
45
|
+
} catch {
|
|
46
|
+
return false;
|
|
47
|
+
}
|
|
48
|
+
return /^##\s+Governance Rules/m.test(content) || content.includes('HARNESS_CONSTRAINTS_START');
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export const governancePresence: ConstraintCheck = {
|
|
52
|
+
id: 'governance_presence',
|
|
53
|
+
async evaluate(env) {
|
|
54
|
+
const projectPath = env.projectPath;
|
|
55
|
+
|
|
56
|
+
// 存在性探测:无 harness 配置 = 未采用治理约定 → skip(不计 pass/fail)
|
|
57
|
+
let adopted = false;
|
|
58
|
+
try {
|
|
59
|
+
adopted = loadRawProjectConfig(projectPath) !== undefined;
|
|
60
|
+
} catch {
|
|
61
|
+
adopted = false;
|
|
62
|
+
}
|
|
63
|
+
if (!adopted) return 'skip';
|
|
64
|
+
|
|
65
|
+
if (hasGovernancePreserveBlock(join(projectPath, 'AGENTS.md'))) return true;
|
|
66
|
+
if (hasClaudeGovernance(join(projectPath, 'CLAUDE.md'))) return true;
|
|
67
|
+
|
|
68
|
+
console.error(
|
|
69
|
+
'[governance_presence] 治理契约缺失:AGENTS.md 无非空 PRESERVE:governance 段,' +
|
|
70
|
+
'且 CLAUDE.md 无 Governance Rules 块——约束正本静默丢失,请恢复其一'
|
|
71
|
+
);
|
|
72
|
+
return false;
|
|
73
|
+
},
|
|
74
|
+
};
|
|
@@ -22,6 +22,7 @@ import { capabilitySync } from './capability-sync';
|
|
|
22
22
|
import { contextDocSync } from './context-doc-sync';
|
|
23
23
|
import { docsFreshness } from './docs-freshness';
|
|
24
24
|
import { noHardcodedCredentials } from './no-hardcoded-credentials';
|
|
25
|
+
import { governancePresence } from './governance-presence';
|
|
25
26
|
|
|
26
27
|
const CHECKS: ConstraintCheck[] = [
|
|
27
28
|
// Iron Laws
|
|
@@ -35,6 +36,7 @@ const CHECKS: ConstraintCheck[] = [
|
|
|
35
36
|
noBypassCheckpoint,
|
|
36
37
|
capabilitySync,
|
|
37
38
|
contextDocSync,
|
|
39
|
+
governancePresence,
|
|
38
40
|
];
|
|
39
41
|
|
|
40
42
|
const registry = new Map<string, ConstraintCheck>(CHECKS.map(c => [c.id, c]));
|
|
@@ -78,4 +80,5 @@ export function registeredCheckCount(): number {
|
|
|
78
80
|
return registry.size;
|
|
79
81
|
}
|
|
80
82
|
|
|
81
|
-
export
|
|
83
|
+
export { buildCheckEnv } from './types';
|
|
84
|
+
export type { ConstraintCheck, CheckEnv, CheckOutcome, EvidenceProviders } from './types';
|