@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.
- package/CHANGELOG.md +6 -0
- package/README.md +9 -12
- package/dist/cli/commands/check.d.ts +3 -3
- package/dist/cli/commands/check.d.ts.map +1 -1
- package/dist/cli/commands/check.js +41 -78
- package/dist/cli/commands/check.js.map +1 -1
- package/dist/cli/commands/constraints-report.d.ts +2 -10
- package/dist/cli/commands/constraints-report.d.ts.map +1 -1
- package/dist/cli/commands/constraints-report.js +4 -57
- package/dist/cli/commands/constraints-report.js.map +1 -1
- package/dist/cli/commands/constraints-retire.d.ts +9 -23
- package/dist/cli/commands/constraints-retire.d.ts.map +1 -1
- package/dist/cli/commands/constraints-retire.js +39 -121
- package/dist/cli/commands/constraints-retire.js.map +1 -1
- package/dist/cli/commands/constraints.d.ts +3 -8
- package/dist/cli/commands/constraints.d.ts.map +1 -1
- package/dist/cli/commands/constraints.js +8 -23
- package/dist/cli/commands/constraints.js.map +1 -1
- package/dist/cli/commands/definitions.js +2 -2
- package/dist/cli/commands/init.d.ts +0 -32
- package/dist/cli/commands/init.d.ts.map +1 -1
- package/dist/cli/commands/init.js +7 -149
- package/dist/cli/commands/init.js.map +1 -1
- package/dist/cli/commands/report.d.ts.map +1 -1
- package/dist/cli/commands/report.js +14 -17
- package/dist/cli/commands/report.js.map +1 -1
- package/dist/cli/commands/scaffold-templates.d.ts +0 -2
- package/dist/cli/commands/scaffold-templates.d.ts.map +1 -1
- package/dist/cli/commands/scaffold-templates.js +2 -37
- package/dist/cli/commands/scaffold-templates.js.map +1 -1
- package/dist/cli/commands/scaffold.d.ts +0 -2
- package/dist/cli/commands/scaffold.d.ts.map +1 -1
- package/dist/cli/commands/scaffold.js +0 -10
- package/dist/cli/commands/scaffold.js.map +1 -1
- package/dist/cli/commands/status.js +10 -10
- package/dist/cli/commands/status.js.map +1 -1
- package/dist/cli/commands/sync-docs/agents-syncer.d.ts +0 -2
- package/dist/cli/commands/sync-docs/agents-syncer.d.ts.map +1 -1
- package/dist/cli/commands/sync-docs/agents-syncer.js +4 -23
- package/dist/cli/commands/sync-docs/agents-syncer.js.map +1 -1
- package/dist/core/constraints/capabilities-parser.d.ts.map +1 -1
- package/dist/core/constraints/capabilities-parser.js +16 -4
- package/dist/core/constraints/capabilities-parser.js.map +1 -1
- package/dist/core/constraints/checker.d.ts +15 -24
- package/dist/core/constraints/checker.d.ts.map +1 -1
- package/dist/core/constraints/checker.js +33 -72
- package/dist/core/constraints/checker.js.map +1 -1
- package/dist/core/constraints/checkers/docs-freshness.js +1 -1
- package/dist/core/constraints/checkers/docs-freshness.js.map +1 -1
- package/dist/core/constraints/checkers/governance-presence.d.ts +2 -1
- package/dist/core/constraints/checkers/governance-presence.d.ts.map +1 -1
- package/dist/core/constraints/checkers/governance-presence.js +4 -3
- package/dist/core/constraints/checkers/governance-presence.js.map +1 -1
- package/dist/core/constraints/checkers/index.d.ts +1 -1
- package/dist/core/constraints/checkers/index.d.ts.map +1 -1
- package/dist/core/constraints/checkers/index.js +4 -8
- package/dist/core/constraints/checkers/index.js.map +1 -1
- package/dist/core/constraints/checkers/iron-flags.d.ts +3 -4
- package/dist/core/constraints/checkers/iron-flags.d.ts.map +1 -1
- package/dist/core/constraints/checkers/iron-flags.js +4 -5
- package/dist/core/constraints/checkers/iron-flags.js.map +1 -1
- package/dist/core/constraints/checkers/no-test-simplification.js +1 -1
- package/dist/core/constraints/checkers/no-test-simplification.js.map +1 -1
- package/dist/core/constraints/checkers/types.d.ts +1 -1
- package/dist/core/constraints/checkers/types.js +1 -1
- package/dist/core/constraints/definitions/guidelines.d.ts +4 -4
- package/dist/core/constraints/definitions/guidelines.d.ts.map +1 -1
- package/dist/core/constraints/definitions/guidelines.js +9 -25
- package/dist/core/constraints/definitions/guidelines.js.map +1 -1
- package/dist/core/constraints/definitions/iron-laws.d.ts +4 -4
- package/dist/core/constraints/definitions/iron-laws.d.ts.map +1 -1
- package/dist/core/constraints/definitions/iron-laws.js +10 -81
- package/dist/core/constraints/definitions/iron-laws.js.map +1 -1
- package/dist/core/constraints/definitions.d.ts +7 -9
- package/dist/core/constraints/definitions.d.ts.map +1 -1
- package/dist/core/constraints/definitions.js +13 -16
- package/dist/core/constraints/definitions.js.map +1 -1
- package/dist/core/constraints/index.d.ts +2 -5
- package/dist/core/constraints/index.d.ts.map +1 -1
- package/dist/core/constraints/index.js +3 -13
- package/dist/core/constraints/index.js.map +1 -1
- package/dist/core/constraints/injection-writer.d.ts +10 -38
- package/dist/core/constraints/injection-writer.d.ts.map +1 -1
- package/dist/core/constraints/injection-writer.js +17 -54
- package/dist/core/constraints/injection-writer.js.map +1 -1
- package/dist/core/constraints/run-env.d.ts +1 -10
- package/dist/core/constraints/run-env.d.ts.map +1 -1
- package/dist/core/constraints/run-env.js +1 -22
- package/dist/core/constraints/run-env.js.map +1 -1
- package/dist/core/constraints/usage-report.d.ts +5 -7
- package/dist/core/constraints/usage-report.d.ts.map +1 -1
- package/dist/core/constraints/usage-report.js +5 -10
- package/dist/core/constraints/usage-report.js.map +1 -1
- package/dist/core/effective-constraints.d.ts +12 -22
- package/dist/core/effective-constraints.d.ts.map +1 -1
- package/dist/core/effective-constraints.js +13 -30
- package/dist/core/effective-constraints.js.map +1 -1
- package/dist/core/index.d.ts +2 -2
- package/dist/core/index.d.ts.map +1 -1
- package/dist/core/index.js +3 -9
- package/dist/core/index.js.map +1 -1
- package/dist/core/project-config-loader.d.ts +4 -42
- package/dist/core/project-config-loader.d.ts.map +1 -1
- package/dist/core/project-config-loader.js +16 -125
- package/dist/core/project-config-loader.js.map +1 -1
- package/dist/core/validators/passes-gate.d.ts +2 -2
- package/dist/core/validators/passes-gate.js +6 -6
- package/dist/core/validators/passes-gate.js.map +1 -1
- package/dist/index.d.ts +7 -10
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +9 -18
- package/dist/index.js.map +1 -1
- package/dist/monitoring/trace-analyzer.d.ts.map +1 -1
- package/dist/monitoring/trace-analyzer.js +11 -12
- package/dist/monitoring/trace-analyzer.js.map +1 -1
- package/dist/monitoring/traces.d.ts +4 -4
- package/dist/monitoring/traces.d.ts.map +1 -1
- package/dist/monitoring/traces.js +8 -8
- package/dist/monitoring/traces.js.map +1 -1
- package/dist/presets/standard.d.ts +8 -10
- package/dist/presets/standard.d.ts.map +1 -1
- package/dist/presets/standard.js +10 -16
- package/dist/presets/standard.js.map +1 -1
- package/dist/types/constraint.d.ts +24 -35
- package/dist/types/constraint.d.ts.map +1 -1
- package/dist/types/constraint.js +8 -7
- package/dist/types/constraint.js.map +1 -1
- package/dist/types/passes-gate.d.ts +5 -5
- package/dist/types/passes-gate.d.ts.map +1 -1
- package/dist/types/project-config.d.ts +8 -66
- package/dist/types/project-config.d.ts.map +1 -1
- package/dist/types/project-config.js +3 -2
- package/dist/types/project-config.js.map +1 -1
- package/dist/types/trace.d.ts +8 -10
- package/dist/types/trace.d.ts.map +1 -1
- package/package.json +2 -2
- package/src/CONTEXT.md +2 -2
- package/src/__tests__/checker-extra.test.ts +37 -61
- package/src/__tests__/checker.test.ts +10 -115
- package/src/__tests__/effective-constraints.test.ts +18 -83
- package/src/__tests__/iron-laws.test.ts +71 -90
- package/src/__tests__/passes-gate-check.test.ts +2 -2
- package/src/__tests__/project-config-loader-extra.test.ts +22 -123
- package/src/__tests__/project-config-loader.test.ts +5 -33
- package/src/__tests__/public-exports.test.ts +1 -7
- package/src/__tests__/public-type-surface.test.ts +2 -5
- package/src/__tests__/public-value-surface.test.ts +1 -7
- package/src/__tests__/skip-semantics.test.ts +21 -37
- package/src/__tests__/trace-analyzer-extra.test.ts +14 -14
- package/src/__tests__/trace-analyzer.test.ts +21 -21
- package/src/__tests__/trace-collector.test.ts +22 -22
- package/src/__tests__/traces.test.ts +27 -28
- package/src/cli/commands/CONTEXT.md +9 -9
- package/src/cli/commands/__tests__/check-read-count.test.ts +4 -15
- package/src/cli/commands/__tests__/check-skip-output.test.ts +14 -14
- package/src/cli/commands/__tests__/check.test.ts +14 -31
- package/src/cli/commands/__tests__/constraints-report.test.ts +12 -80
- package/src/cli/commands/__tests__/constraints-retire.test.ts +42 -231
- package/src/cli/commands/__tests__/init-governance-io.test.ts +12 -58
- package/src/cli/commands/__tests__/init-ondisk.test.ts +12 -20
- package/src/cli/commands/__tests__/init.test.ts +0 -11
- package/src/cli/commands/__tests__/project-path-anchoring.test.ts +2 -2
- package/src/cli/commands/__tests__/report-extra.test.ts +4 -4
- package/src/cli/commands/__tests__/report.test.ts +1 -1
- package/src/cli/commands/__tests__/scaffold.test.ts +4 -14
- package/src/cli/commands/__tests__/status-analysis-wiring.test.ts +3 -3
- package/src/cli/commands/__tests__/status-extra.test.ts +14 -14
- package/src/cli/commands/__tests__/status.test.ts +12 -12
- package/src/cli/commands/__tests__/sync-docs-agents.test.ts +1 -1
- package/src/cli/commands/check.ts +43 -82
- package/src/cli/commands/constraints-report.ts +4 -59
- package/src/cli/commands/constraints-retire.ts +49 -154
- package/src/cli/commands/constraints.ts +11 -26
- package/src/cli/commands/definitions.ts +2 -2
- package/src/cli/commands/init.ts +8 -179
- package/src/cli/commands/report.ts +19 -23
- package/src/cli/commands/scaffold-templates.ts +1 -37
- package/src/cli/commands/scaffold.ts +0 -11
- package/src/cli/commands/status.ts +10 -10
- package/src/cli/commands/sync-docs/agents-syncer.ts +4 -25
- package/src/core/CONTEXT.md +9 -9
- package/src/core/constraints/__tests__/capability-counts.test.ts +5 -5
- package/src/core/constraints/__tests__/collect-constraints.test.ts +13 -8
- package/src/core/constraints/__tests__/git-evidence.test.ts +4 -7
- package/src/core/constraints/__tests__/injection-writer.test.ts +8 -61
- package/src/core/constraints/__tests__/run-env.test.ts +5 -34
- package/src/core/constraints/__tests__/trace-injection.test.ts +1 -1
- package/src/core/constraints/capabilities-parser.ts +17 -5
- package/src/core/constraints/checker.ts +39 -88
- package/src/core/constraints/checkers/__tests__/check-outcome.test.ts +5 -8
- package/src/core/constraints/checkers/__tests__/governance-presence.test.ts +1 -53
- package/src/core/constraints/checkers/__tests__/iron-flags.test.ts +5 -25
- package/src/core/constraints/checkers/docs-freshness.ts +1 -1
- package/src/core/constraints/checkers/governance-presence.ts +4 -3
- package/src/core/constraints/checkers/index.ts +4 -10
- package/src/core/constraints/checkers/iron-flags.ts +3 -12
- package/src/core/constraints/checkers/no-test-simplification.ts +1 -1
- package/src/core/constraints/checkers/types.ts +1 -1
- package/src/core/constraints/definitions/guidelines.ts +8 -25
- package/src/core/constraints/definitions/iron-laws.ts +9 -82
- package/src/core/constraints/definitions.ts +14 -15
- package/src/core/constraints/index.ts +4 -19
- package/src/core/constraints/injection-writer.ts +16 -61
- package/src/core/constraints/run-env.ts +1 -33
- package/src/core/constraints/usage-report.ts +8 -15
- package/src/core/effective-constraints.ts +14 -45
- package/src/core/index.ts +3 -10
- package/src/core/project-config-loader.ts +20 -145
- package/src/core/validators/passes-gate.ts +6 -6
- package/src/failure/CONTEXT.md +1 -1
- package/src/hooks/__tests__/bootstrap.test.ts +3 -5
- package/src/index.ts +8 -18
- package/src/monitoring/__tests__/trace-file-anchoring.test.ts +1 -1
- package/src/monitoring/__tests__/traces.test.ts +22 -22
- package/src/monitoring/trace-analyzer.ts +12 -13
- package/src/monitoring/traces.ts +8 -8
- package/src/presets/__tests__/standard.test.ts +12 -16
- package/src/presets/standard.ts +14 -23
- package/src/types/__tests__/types.test.ts +3 -3
- package/src/types/constraint.ts +26 -40
- package/src/types/passes-gate.ts +5 -5
- package/src/types/project-config.ts +8 -77
- package/src/types/trace.ts +8 -11
- package/dist/core/constraints/agent-prompt-renderer.d.ts +0 -32
- package/dist/core/constraints/agent-prompt-renderer.d.ts.map +0 -1
- package/dist/core/constraints/agent-prompt-renderer.js +0 -55
- package/dist/core/constraints/agent-prompt-renderer.js.map +0 -1
- package/dist/core/constraints/checkers/no-bypass-checkpoint.d.ts +0 -6
- package/dist/core/constraints/checkers/no-bypass-checkpoint.d.ts.map +0 -1
- package/dist/core/constraints/checkers/no-bypass-checkpoint.js +0 -40
- package/dist/core/constraints/checkers/no-bypass-checkpoint.js.map +0 -1
- package/dist/core/constraints/definitions/prompts.d.ts +0 -12
- package/dist/core/constraints/definitions/prompts.d.ts.map +0 -1
- package/dist/core/constraints/definitions/prompts.js +0 -400
- package/dist/core/constraints/definitions/prompts.js.map +0 -1
- package/dist/core/constraints/injection-drift.d.ts +0 -61
- package/dist/core/constraints/injection-drift.d.ts.map +0 -1
- package/dist/core/constraints/injection-drift.js +0 -131
- package/dist/core/constraints/injection-drift.js.map +0 -1
- package/dist/core/constraints/injection-renderer.d.ts +0 -23
- package/dist/core/constraints/injection-renderer.d.ts.map +0 -1
- package/dist/core/constraints/injection-renderer.js +0 -44
- package/dist/core/constraints/injection-renderer.js.map +0 -1
- package/src/cli/commands/__tests__/check-drift.test.ts +0 -158
- package/src/cli/commands/__tests__/init-injection.test.ts +0 -466
- package/src/core/constraints/__tests__/agent-prompt-renderer.test.ts +0 -129
- package/src/core/constraints/__tests__/injection-drift.test.ts +0 -247
- package/src/core/constraints/agent-prompt-renderer.ts +0 -67
- package/src/core/constraints/checkers/__tests__/no-bypass-checkpoint.test.ts +0 -76
- package/src/core/constraints/checkers/no-bypass-checkpoint.ts +0 -40
- package/src/core/constraints/definitions/prompts.ts +0 -414
- package/src/core/constraints/injection-drift.ts +0 -142
- package/src/core/constraints/injection-renderer.ts +0 -44
|
@@ -39,28 +39,33 @@ describe('collectConstraints(收集模式,不抛)', () => {
|
|
|
39
39
|
|
|
40
40
|
it('违规铁律如实进 ironLaws:satisfied=false 且 passed=false', async () => {
|
|
41
41
|
const result = await checker.collectConstraints(VIOLATING);
|
|
42
|
-
const violated = result.
|
|
42
|
+
const violated = result.errors.find((r: ConstraintResult) => r.id === 'no_completion_without_verification');
|
|
43
43
|
expect(violated).toBeDefined();
|
|
44
44
|
expect(violated!.satisfied).toBe(false);
|
|
45
45
|
expect(result.passed).toBe(false);
|
|
46
46
|
});
|
|
47
47
|
|
|
48
48
|
it('首个违规不截断:触发域内后续铁律照常评估', async () => {
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
49
|
+
// 首条铁律(no_completion_without_verification)违规后,extraTriggers 带入的
|
|
50
|
+
// no_test_simplification 仍须出现在收集结果里
|
|
51
|
+
const result = await checker.collectConstraints({
|
|
52
|
+
...VIOLATING,
|
|
53
|
+
extraTriggers: ['test_creation'],
|
|
54
|
+
});
|
|
55
|
+
const ids = result.errors.map((r: ConstraintResult) => r.id);
|
|
56
|
+
expect(ids).toContain('no_completion_without_verification');
|
|
57
|
+
expect(ids).toContain('no_test_simplification');
|
|
53
58
|
});
|
|
54
59
|
|
|
55
60
|
it('guidelines 照常执行,warningCount 与不满意条目同口径', async () => {
|
|
56
61
|
const result = await checker.collectConstraints(VIOLATING);
|
|
57
|
-
expect(result.
|
|
58
|
-
expect(result.warningCount).toBe(result.
|
|
62
|
+
expect(result.warnings.length).toBeGreaterThan(0);
|
|
63
|
+
expect(result.warningCount).toBe(result.warnings.filter((g: ConstraintResult) => !g.satisfied).length);
|
|
59
64
|
});
|
|
60
65
|
|
|
61
66
|
it('干净上下文对照:passed=true、ironLaws 全满意', async () => {
|
|
62
67
|
const result = await checker.collectConstraints(CLEAN);
|
|
63
68
|
expect(result.passed).toBe(true);
|
|
64
|
-
expect(result.
|
|
69
|
+
expect(result.errors.every((r: ConstraintResult) => r.satisfied)).toBe(true);
|
|
65
70
|
});
|
|
66
71
|
});
|
|
@@ -22,7 +22,7 @@ import {
|
|
|
22
22
|
} from '../git-evidence';
|
|
23
23
|
import { buildConstraintContext } from '../context-builder';
|
|
24
24
|
import { ConstraintChecker } from '../checker';
|
|
25
|
-
import {
|
|
25
|
+
import { CONSTRAINTS } from '../definitions';
|
|
26
26
|
import type { MergedConstraintsConfig } from '../../../types/project-config';
|
|
27
27
|
|
|
28
28
|
/** 建一个真 git fixture:committed src/existing.ts + 一个 staged 修改 */
|
|
@@ -181,10 +181,7 @@ describe('git 证据适配器(#87)', () => {
|
|
|
181
181
|
const context = await buildConstraintContext({ projectPath: dir, staged: true, evidence });
|
|
182
182
|
// capability_sync 经 CheckEnv.stagedDiffNames() 取同一份 git 证据
|
|
183
183
|
const merged: MergedConstraintsConfig = {
|
|
184
|
-
|
|
185
|
-
guidelines: { capability_sync: GUIDELINES['capability_sync'] },
|
|
186
|
-
prompts: {},
|
|
187
|
-
custom: [],
|
|
184
|
+
constraints: { capability_sync: CONSTRAINTS['capability_sync'] },
|
|
188
185
|
disabled: [],
|
|
189
186
|
unknownIds: [],
|
|
190
187
|
};
|
|
@@ -193,8 +190,8 @@ describe('git 证据适配器(#87)', () => {
|
|
|
193
190
|
const runResult = await checker.checkConstraints(context, merged, evidence);
|
|
194
191
|
|
|
195
192
|
// capability_sync 确实被评估(非 skip)→ 它经 env.stagedDiffNames() 取过证据
|
|
196
|
-
expect(runResult.
|
|
197
|
-
expect(runResult.
|
|
193
|
+
expect(runResult.warnings.map(r => r.id)).toEqual(['capability_sync']);
|
|
194
|
+
expect(runResult.warnings[0].skipped).toBeUndefined();
|
|
198
195
|
// 两个消费者都从注入的这一份实例取证据(checker 没有另起炉灶)
|
|
199
196
|
expect(calls).toContain('context:changedFileNames');
|
|
200
197
|
expect(calls).toContain('checker:changedFileNames');
|
|
@@ -1,32 +1,27 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* injection-writer 旁测(架构评审候选1,ADR-0011
|
|
2
|
+
* injection-writer 旁测(架构评审候选1,ADR-0011;ADR-0029 收窄)
|
|
3
3
|
*
|
|
4
|
-
* 测试面 = 纯函数(字符串进、字符串出)+
|
|
5
|
-
*
|
|
6
|
-
*
|
|
4
|
+
* 测试面 = 纯函数(字符串进、字符串出)+ 治理正本探测。
|
|
5
|
+
* 幂等/半标记守护/两种尾部形状在此测一次。
|
|
6
|
+
* ADR-0029:注入段落点路由(resolveInjectionTarget/resolveGovernanceLanding)
|
|
7
|
+
* 已随文本注入层关停删除,相关用例移除。
|
|
7
8
|
*/
|
|
8
9
|
|
|
9
|
-
import { describe, it, expect
|
|
10
|
-
import * as fs from 'fs';
|
|
11
|
-
import * as path from 'path';
|
|
10
|
+
import { describe, it, expect } from '@jest/globals';
|
|
12
11
|
import {
|
|
13
12
|
replaceStandaloneRange,
|
|
14
13
|
replaceEnclosedRange,
|
|
15
14
|
cutMarkerBlock,
|
|
16
|
-
resolveInjectionTarget,
|
|
17
|
-
resolveGovernanceLanding,
|
|
18
15
|
GOVERNANCE_HEADING,
|
|
19
16
|
hasGovernanceContract,
|
|
20
17
|
countGovernanceHeadings,
|
|
21
18
|
hasPreserveBlock,
|
|
22
19
|
} from '../injection-writer';
|
|
23
|
-
import {
|
|
24
|
-
CONSTRAINTS_START_MARKER as CS,
|
|
25
|
-
CONSTRAINTS_END_MARKER as CE,
|
|
26
|
-
} from '../injection-renderer';
|
|
27
20
|
|
|
28
21
|
const B = '<!-- B -->';
|
|
29
22
|
const E = '<!-- E -->';
|
|
23
|
+
const CS = '<!-- CS -->';
|
|
24
|
+
const CE = '<!-- CE -->';
|
|
30
25
|
|
|
31
26
|
describe('replaceStandaloneRange — 独立成节形状', () => {
|
|
32
27
|
it('替换含标记区间;尾文多换行折叠为恰好一个空行', () => {
|
|
@@ -97,54 +92,6 @@ describe('cutMarkerBlock', () => {
|
|
|
97
92
|
});
|
|
98
93
|
});
|
|
99
94
|
|
|
100
|
-
describe('落点路由(读写两侧,studio #307/#302)', () => {
|
|
101
|
-
let dir: string;
|
|
102
|
-
|
|
103
|
-
beforeEach(() => {
|
|
104
|
-
dir = fs.mkdtempSync(path.join(process.cwd(), 'temp-test-inj-writer-'));
|
|
105
|
-
});
|
|
106
|
-
afterEach(() => fs.rmSync(dir, { recursive: true, force: true }));
|
|
107
|
-
|
|
108
|
-
const write = (name: string, content: string) =>
|
|
109
|
-
fs.writeFileSync(path.join(dir, name), content, 'utf-8');
|
|
110
|
-
|
|
111
|
-
it('resolveInjectionTarget:CLAUDE.md 完整标记段优先于 AGENTS.md', () => {
|
|
112
|
-
write('CLAUDE.md', `x\n${CS}\nbody\n${CE}\n`);
|
|
113
|
-
write('AGENTS.md', `<!-- PRESERVE:governance -->\n${CS}\nbody2\n${CE}\n`);
|
|
114
|
-
expect(resolveInjectionTarget(dir)?.file).toBe('CLAUDE.md');
|
|
115
|
-
});
|
|
116
|
-
|
|
117
|
-
it('CLAUDE.md 标记残缺(单边)不算落点 → 落 AGENTS.md', () => {
|
|
118
|
-
write('CLAUDE.md', `x\n${CS}\nbroken\n`);
|
|
119
|
-
write('AGENTS.md', `${CS}\nbody\n${CE}\n`);
|
|
120
|
-
expect(resolveInjectionTarget(dir)?.file).toBe('AGENTS.md');
|
|
121
|
-
});
|
|
122
|
-
|
|
123
|
-
it('两处均无完整标记段 → null(未注入)', () => {
|
|
124
|
-
write('CLAUDE.md', '# 无约束标记\n');
|
|
125
|
-
expect(resolveInjectionTarget(dir)).toBeNull();
|
|
126
|
-
});
|
|
127
|
-
|
|
128
|
-
it('resolveGovernanceLanding:CLAUDE.md 有标记或有旧版 Governance Rules 块 → claude-md', () => {
|
|
129
|
-
write('CLAUDE.md', `${CS}\nbody\n${CE}\n`);
|
|
130
|
-
expect(resolveGovernanceLanding(dir).target).toBe('claude-md');
|
|
131
|
-
write('CLAUDE.md', '# CLAUDE.md\n\n## Governance Rules\n\n手写\n');
|
|
132
|
-
expect(resolveGovernanceLanding(dir).target).toBe('claude-md');
|
|
133
|
-
});
|
|
134
|
-
|
|
135
|
-
it('CLAUDE.md 与治理无关 / 不存在 → agents-md', () => {
|
|
136
|
-
write('CLAUDE.md', '# CLAUDE.md\n\n@AGENTS.md\n');
|
|
137
|
-
expect(resolveGovernanceLanding(dir).target).toBe('agents-md');
|
|
138
|
-
fs.rmSync(path.join(dir, 'CLAUDE.md'));
|
|
139
|
-
expect(resolveGovernanceLanding(dir).target).toBe('agents-md');
|
|
140
|
-
});
|
|
141
|
-
|
|
142
|
-
it('落点路由消费共享宽松谓词:双空格标题变体也路由 claude-md(fail-open)', () => {
|
|
143
|
-
write('CLAUDE.md', '# CLAUDE.md\n\n## Governance Rules\n\n手写\n');
|
|
144
|
-
expect(resolveGovernanceLanding(dir).target).toBe('claude-md');
|
|
145
|
-
});
|
|
146
|
-
});
|
|
147
|
-
|
|
148
95
|
describe('治理契约探测共享符号(#83,ADR-0011 补注:收口范围含探测器)', () => {
|
|
149
96
|
it('GOVERNANCE_HEADING 是标题字面量单一来源', () => {
|
|
150
97
|
expect(GOVERNANCE_HEADING).toBe('## Governance Rules');
|
|
@@ -47,7 +47,7 @@ function writeTraces(projectPath: string, lines: string[]): void {
|
|
|
47
47
|
}
|
|
48
48
|
|
|
49
49
|
const traceLine = (id: string, result: 'pass' | 'fail'): string =>
|
|
50
|
-
JSON.stringify({ constraintId: id,
|
|
50
|
+
JSON.stringify({ constraintId: id, severity: 'error', timestamp: 1, result });
|
|
51
51
|
|
|
52
52
|
const CORRUPT = '{"constraintId":"broken"';
|
|
53
53
|
|
|
@@ -125,18 +125,11 @@ describe('createRunEnv', () => {
|
|
|
125
125
|
});
|
|
126
126
|
});
|
|
127
127
|
|
|
128
|
-
describe('配置一次装载 — rawConfig
|
|
128
|
+
describe('配置一次装载 — rawConfig(ADR-0023 决策 2)', () => {
|
|
129
129
|
let dir: string;
|
|
130
130
|
|
|
131
131
|
const writeConfig = (content: string) => writeProjectConfig(dir, content);
|
|
132
132
|
|
|
133
|
-
/** 落 `.harness/<fileName>`(夹具可能还没有 .harness 目录,先补上) */
|
|
134
|
-
const writeHarnessFile = (fileName: string, body: string) => {
|
|
135
|
-
const target = path.join(dir, '.harness', fileName);
|
|
136
|
-
fs.mkdirSync(path.dirname(target), { recursive: true });
|
|
137
|
-
fs.writeFileSync(target, body, 'utf-8');
|
|
138
|
-
};
|
|
139
|
-
|
|
140
133
|
beforeEach(() => {
|
|
141
134
|
dir = createProjectFixture({ name: 'run-env-config' });
|
|
142
135
|
});
|
|
@@ -191,27 +184,6 @@ describe('配置一次装载 — rawConfig / customConstraints(ADR-0023 决策
|
|
|
191
184
|
expect(resolveRunEnv().projectPath).toBe(process.cwd());
|
|
192
185
|
});
|
|
193
186
|
|
|
194
|
-
it('customConstraints 取 custom_constraints 段,run 内同名文件至多读一次', () => {
|
|
195
|
-
const write = (body: string) => writeHarnessFile('custom-constraints.yml', body);
|
|
196
|
-
write('custom_constraints:\n a:\n level: iron_law\n rule: 只此一份\n');
|
|
197
|
-
const env = createRunEnv(dir);
|
|
198
|
-
expect(Object.keys(env.customConstraints('custom-constraints.yml'))).toEqual(['a']);
|
|
199
|
-
|
|
200
|
-
write('custom_constraints:\n b:\n level: guideline\n rule: 后来的\n');
|
|
201
|
-
expect(Object.keys(env.customConstraints('custom-constraints.yml'))).toEqual(['a']);
|
|
202
|
-
});
|
|
203
|
-
|
|
204
|
-
it('customConstraints:文件缺失 = {};不同文件名各自 memo', () => {
|
|
205
|
-
const env = createRunEnv(dir);
|
|
206
|
-
expect(env.customConstraints('missing.yml')).toEqual({});
|
|
207
|
-
|
|
208
|
-
writeHarnessFile(
|
|
209
|
-
'other.yml',
|
|
210
|
-
'custom_constraints:\n c:\n level: guideline\n rule: r\n'
|
|
211
|
-
);
|
|
212
|
-
expect(Object.keys(env.customConstraints('other.yml'))).toEqual(['c']);
|
|
213
|
-
expect(env.customConstraints('missing.yml')).toEqual({});
|
|
214
|
-
});
|
|
215
187
|
});
|
|
216
188
|
|
|
217
189
|
describe('context-builder 经注入的观察面取证据', () => {
|
|
@@ -235,7 +207,6 @@ describe('context-builder 经注入的观察面取证据', () => {
|
|
|
235
207
|
},
|
|
236
208
|
sourceRoots: () => ['src'],
|
|
237
209
|
rawConfig: () => undefined,
|
|
238
|
-
customConstraints: () => ({}),
|
|
239
210
|
capabilities: () => undefined,
|
|
240
211
|
};
|
|
241
212
|
return { env, limits };
|
|
@@ -251,8 +222,8 @@ describe('context-builder 经注入的观察面取证据', () => {
|
|
|
251
222
|
|
|
252
223
|
it('hasFailingTest / hasVerificationEvidence 各按自己的窗口向 env 取数,不另开读', async () => {
|
|
253
224
|
const { env, limits } = countingEnv([
|
|
254
|
-
{ constraintId: 'p',
|
|
255
|
-
{ constraintId: 'f',
|
|
225
|
+
{ constraintId: 'p', severity: 'error', timestamp: 1, result: 'pass' },
|
|
226
|
+
{ constraintId: 'f', severity: 'error', timestamp: 2, result: 'fail' },
|
|
256
227
|
]);
|
|
257
228
|
const ctx = await buildConstraintContext({
|
|
258
229
|
projectPath: dir,
|
|
@@ -268,7 +239,7 @@ describe('context-builder 经注入的观察面取证据', () => {
|
|
|
268
239
|
|
|
269
240
|
it('窗口里没有 pass 时验证证据为 false(合并读取不改判定)', async () => {
|
|
270
241
|
const { env } = countingEnv([
|
|
271
|
-
{ constraintId: 'f',
|
|
242
|
+
{ constraintId: 'f', severity: 'error', timestamp: 2, result: 'fail' },
|
|
272
243
|
]);
|
|
273
244
|
const ctx = await buildConstraintContext({
|
|
274
245
|
projectPath: dir,
|
|
@@ -51,7 +51,7 @@ describe('trace 记录器构造注入(harness#88)', () => {
|
|
|
51
51
|
await checker.checkConstraints(context());
|
|
52
52
|
|
|
53
53
|
expect(records.map(r => r.constraintId)).toEqual(
|
|
54
|
-
expect.arrayContaining(['
|
|
54
|
+
expect.arrayContaining(['no_completion_without_verification', 'no_hardcoded_credentials'])
|
|
55
55
|
);
|
|
56
56
|
expect(records.every(r => ['pass', 'fail', 'skip'].includes(r.result))).toBe(true);
|
|
57
57
|
});
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
*/
|
|
11
11
|
|
|
12
12
|
import * as fs from 'fs';
|
|
13
|
-
import {
|
|
13
|
+
import { CONSTRAINTS } from './definitions';
|
|
14
14
|
import { FreshnessRunner } from './doc-freshness/runner';
|
|
15
15
|
import type { DocFreshnessCheck } from '../../types/project-config';
|
|
16
16
|
|
|
@@ -79,10 +79,14 @@ export function readCapabilitiesEntries(capabilitiesPath: string, options: Capab
|
|
|
79
79
|
* checker 与 sync-docs 都应按此跳过条目级比对。
|
|
80
80
|
*/
|
|
81
81
|
export function isCapabilityListingFormat(content: string): boolean {
|
|
82
|
-
// 能力清单格式特征:包含 "CLI Commands (N)"
|
|
82
|
+
// 能力清单格式特征:包含 "CLI Commands (N)" 与约束计数行
|
|
83
|
+
// (ADR-0029 前为 "Iron Laws (N)" / "Guidelines (N)",现行为 "errors (N)" / "warnings (N)",
|
|
84
|
+
// 新旧行都认——旧文档不因此失去格式身份)
|
|
83
85
|
return /CLI Commands\s*\(\d+\)/.test(content) ||
|
|
84
86
|
/Iron Laws?\s*\(\d+\)/.test(content) ||
|
|
85
|
-
/Guidelines?\s*\(\d+\)/.test(content)
|
|
87
|
+
/Guidelines?\s*\(\d+\)/.test(content) ||
|
|
88
|
+
/errors?\s*\(\d+\)/.test(content) ||
|
|
89
|
+
/warnings?\s*\(\d+\)/.test(content);
|
|
86
90
|
}
|
|
87
91
|
|
|
88
92
|
/**
|
|
@@ -143,8 +147,16 @@ function capabilityCountRules(source: CapabilityDefinitionSource): CapabilityCou
|
|
|
143
147
|
actual: source.commands.length + source.gates.filter(g => g.cli).length,
|
|
144
148
|
},
|
|
145
149
|
{ label: 'Quality Gates', pattern: 'Quality Gates?\\s*\\((\\d+)\\)', actual: source.gates.length },
|
|
146
|
-
{
|
|
147
|
-
|
|
150
|
+
{
|
|
151
|
+
label: 'errors',
|
|
152
|
+
pattern: 'errors?\\s*\\((\\d+)\\)',
|
|
153
|
+
actual: Object.values(CONSTRAINTS).filter(c => c.severity === 'error').length,
|
|
154
|
+
},
|
|
155
|
+
{
|
|
156
|
+
label: 'warnings',
|
|
157
|
+
pattern: 'warnings?\\s*\\((\\d+)\\)',
|
|
158
|
+
actual: Object.values(CONSTRAINTS).filter(c => c.severity === 'warning').length,
|
|
159
|
+
},
|
|
148
160
|
];
|
|
149
161
|
}
|
|
150
162
|
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* 约束检查引擎
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
* -
|
|
6
|
-
* -
|
|
7
|
-
*
|
|
4
|
+
* severity 显式模型(ADR-0029):
|
|
5
|
+
* - severity='error':检查失败立即抛出异常(block 模式)
|
|
6
|
+
* - severity='warning':检查失败记录警告
|
|
7
|
+
* 全部约束 kind='check',必须带真实 checker(注册表闭环)。
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
10
|
import {
|
|
@@ -12,11 +12,10 @@ import {
|
|
|
12
12
|
ConstraintContext,
|
|
13
13
|
ConstraintResult,
|
|
14
14
|
ConstraintCheckResult,
|
|
15
|
-
ConstraintLevel,
|
|
16
15
|
ConstraintViolationError,
|
|
17
16
|
} from '../../types/constraint';
|
|
18
17
|
import type { ExecutionTrace } from '../../types/trace';
|
|
19
|
-
import {
|
|
18
|
+
import { CONSTRAINTS } from './definitions';
|
|
20
19
|
import type { MergedConstraintsConfig } from '../../types/project-config';
|
|
21
20
|
import { matchesTrigger } from './triggers';
|
|
22
21
|
import { join, relative } from 'path';
|
|
@@ -74,21 +73,8 @@ export class ConstraintChecker {
|
|
|
74
73
|
*
|
|
75
74
|
* @param customConfig 可选,per-request 自定义配置;不传则返回内置约束集
|
|
76
75
|
*/
|
|
77
|
-
getConstraints(customConfig?: MergedConstraintsConfig | null): {
|
|
78
|
-
|
|
79
|
-
guidelines: Record<string, Constraint>;
|
|
80
|
-
prompts: Record<string, Constraint>;
|
|
81
|
-
} {
|
|
82
|
-
const config = customConfig;
|
|
83
|
-
const source = config
|
|
84
|
-
? { ironLaws: config.ironLaws, guidelines: config.guidelines, prompts: config.prompts ?? PROMPTS }
|
|
85
|
-
: { ironLaws: IRON_LAWS, guidelines: GUIDELINES, prompts: PROMPTS };
|
|
86
|
-
|
|
87
|
-
return {
|
|
88
|
-
ironLaws: source.ironLaws,
|
|
89
|
-
guidelines: source.guidelines,
|
|
90
|
-
prompts: source.prompts,
|
|
91
|
-
};
|
|
76
|
+
getConstraints(customConfig?: MergedConstraintsConfig | null): Record<string, Constraint> {
|
|
77
|
+
return customConfig ? customConfig.constraints : CONSTRAINTS;
|
|
92
78
|
}
|
|
93
79
|
|
|
94
80
|
/**
|
|
@@ -103,17 +89,6 @@ export class ConstraintChecker {
|
|
|
103
89
|
evidence?: GitEvidence,
|
|
104
90
|
runEnv?: RunEnv
|
|
105
91
|
): Promise<ConstraintResult> {
|
|
106
|
-
// prompt 类约束不参与 checker 执行(ADR-0001:仅参与注入)
|
|
107
|
-
if (constraint.kind === 'prompt') {
|
|
108
|
-
return {
|
|
109
|
-
id: constraint.id,
|
|
110
|
-
level: constraint.level,
|
|
111
|
-
satisfied: true,
|
|
112
|
-
constraint,
|
|
113
|
-
checkedAt: new Date(),
|
|
114
|
-
};
|
|
115
|
-
}
|
|
116
|
-
|
|
117
92
|
// 检查前置条件('skip' = 约定未采用/证据未接线:satisfied 置 true 但不计 pass/fail)
|
|
118
93
|
const outcome = normalizeCheckOutcome(
|
|
119
94
|
await this.checkPrecondition(constraint, context, evidence, runEnv)
|
|
@@ -122,7 +97,7 @@ export class ConstraintChecker {
|
|
|
122
97
|
if (outcome.skipped) {
|
|
123
98
|
return {
|
|
124
99
|
id: constraint.id,
|
|
125
|
-
|
|
100
|
+
severity: constraint.severity,
|
|
126
101
|
satisfied: true,
|
|
127
102
|
skipped: true,
|
|
128
103
|
constraint,
|
|
@@ -135,7 +110,7 @@ export class ConstraintChecker {
|
|
|
135
110
|
|
|
136
111
|
return {
|
|
137
112
|
id: constraint.id,
|
|
138
|
-
|
|
113
|
+
severity: constraint.severity,
|
|
139
114
|
satisfied,
|
|
140
115
|
constraint,
|
|
141
116
|
message: satisfied ? undefined : constraint.message,
|
|
@@ -145,22 +120,6 @@ export class ConstraintChecker {
|
|
|
145
120
|
};
|
|
146
121
|
}
|
|
147
122
|
|
|
148
|
-
/**
|
|
149
|
-
* 根据约束层级获取严重性
|
|
150
|
-
*/
|
|
151
|
-
private getSeverity(level: ConstraintLevel): 'error' | 'warning' | 'info' {
|
|
152
|
-
switch (level) {
|
|
153
|
-
case 'iron_law':
|
|
154
|
-
return 'error';
|
|
155
|
-
case 'guideline':
|
|
156
|
-
return 'warning';
|
|
157
|
-
case 'prompt':
|
|
158
|
-
return 'info';
|
|
159
|
-
default:
|
|
160
|
-
return 'warning';
|
|
161
|
-
}
|
|
162
|
-
}
|
|
163
|
-
|
|
164
123
|
/**
|
|
165
124
|
* 记录约束检查的 trace
|
|
166
125
|
*/
|
|
@@ -171,11 +130,10 @@ export class ConstraintChecker {
|
|
|
171
130
|
): void {
|
|
172
131
|
this.traceRecorder.record({
|
|
173
132
|
constraintId: constraint.id,
|
|
174
|
-
|
|
133
|
+
severity: constraint.severity,
|
|
175
134
|
timestamp: Date.now(),
|
|
176
135
|
result: checkResult.skipped ? 'skip' : checkResult.satisfied ? 'pass' : 'fail',
|
|
177
136
|
operation: context.operation,
|
|
178
|
-
severity: this.getSeverity(constraint.level),
|
|
179
137
|
projectPath: context.projectPath,
|
|
180
138
|
sessionId: context.sessionId,
|
|
181
139
|
evidence: checkResult.evidence,
|
|
@@ -186,7 +144,7 @@ export class ConstraintChecker {
|
|
|
186
144
|
* 检查约束前置条件(工单 21:分发至 checkers/ 注册表)
|
|
187
145
|
*
|
|
188
146
|
* 注册表闭环(ADR-0001):kind='check' 未注册 checker 直接抛错,
|
|
189
|
-
* 不再有"未注册默认通过"
|
|
147
|
+
* 不再有"未注册默认通过"路径。
|
|
190
148
|
*
|
|
191
149
|
* git 证据(stagedDiff/stagedDiffNames)单一来源 = GitEvidence adapter(#87):
|
|
192
150
|
* 一次 run 内调用方传同一实例即至多取证一次,run 外调用独占一份。
|
|
@@ -202,7 +160,7 @@ export class ConstraintChecker {
|
|
|
202
160
|
if (!impl) {
|
|
203
161
|
throw new Error(
|
|
204
162
|
`[harness] 约束 "${constraint.id}" (kind='check') 未注册 checker,拒绝静默通过。` +
|
|
205
|
-
`请在 checkers/
|
|
163
|
+
`请在 checkers/ 注册实现(纯文本提示层已随 ADR-0029 关停,文本规则写入项目治理文档)。`
|
|
206
164
|
);
|
|
207
165
|
}
|
|
208
166
|
|
|
@@ -232,31 +190,27 @@ export class ConstraintChecker {
|
|
|
232
190
|
context: ConstraintContext,
|
|
233
191
|
customConfig?: MergedConstraintsConfig | null
|
|
234
192
|
): {
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
prompts: Constraint[];
|
|
193
|
+
errors: Constraint[];
|
|
194
|
+
warnings: Constraint[];
|
|
238
195
|
} {
|
|
239
196
|
const operations = [context.operation, ...(context.extraTriggers ?? [])];
|
|
240
197
|
const constraints = this.getConstraints(customConfig);
|
|
241
198
|
|
|
242
|
-
const
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
);
|
|
246
|
-
};
|
|
199
|
+
const applicable = Object.values(constraints).filter(constraint =>
|
|
200
|
+
matchesTrigger(constraint, operations)
|
|
201
|
+
);
|
|
247
202
|
|
|
248
203
|
return {
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
prompts: filterByTrigger(constraints.prompts),
|
|
204
|
+
errors: applicable.filter(c => c.severity === 'error'),
|
|
205
|
+
warnings: applicable.filter(c => c.severity === 'warning'),
|
|
252
206
|
};
|
|
253
207
|
}
|
|
254
208
|
|
|
255
209
|
/**
|
|
256
210
|
* 执行约束检查
|
|
257
211
|
*
|
|
258
|
-
* -
|
|
259
|
-
* -
|
|
212
|
+
* - severity='error':检查失败立即抛出异常
|
|
213
|
+
* - severity='warning':检查失败记录警告
|
|
260
214
|
*
|
|
261
215
|
* @param context 约束上下文
|
|
262
216
|
* @param customConfig 可选,per-request 自定义配置(避免多请求间的单例状态污染)
|
|
@@ -279,7 +233,7 @@ export class ConstraintChecker {
|
|
|
279
233
|
* 收集模式执行约束检查(harness report 的不抛出口,架构评审候选1)
|
|
280
234
|
*
|
|
281
235
|
* 与 checkConstraints 共享同一检查体,唯一差别是不 throw:
|
|
282
|
-
*
|
|
236
|
+
* error 级违规照进 result.errors、passed=false,后续 error 与 warning 级照常执行,
|
|
283
237
|
* trace 逐条照记。报告类消费者拿全量视图;阻断语义只属于 checkConstraints
|
|
284
238
|
* (throw 契约是 #119 判定证据的外溢面,逐字不动)。
|
|
285
239
|
*/
|
|
@@ -294,7 +248,7 @@ export class ConstraintChecker {
|
|
|
294
248
|
|
|
295
249
|
/**
|
|
296
250
|
* checkConstraints / collectConstraints 的共享检查体;
|
|
297
|
-
* mode='block'
|
|
251
|
+
* mode='block' 首个 error 级违规即抛,mode='collect' 全量跑完不抛
|
|
298
252
|
*/
|
|
299
253
|
private async runAllConstraints(
|
|
300
254
|
context: ConstraintContext,
|
|
@@ -311,8 +265,8 @@ export class ConstraintChecker {
|
|
|
311
265
|
const env = runEnv ?? createRunEnv(projectPath);
|
|
312
266
|
|
|
313
267
|
const result: ConstraintCheckResult = {
|
|
314
|
-
|
|
315
|
-
|
|
268
|
+
errors: [],
|
|
269
|
+
warnings: [],
|
|
316
270
|
passed: true,
|
|
317
271
|
warningCount: 0,
|
|
318
272
|
};
|
|
@@ -321,12 +275,12 @@ export class ConstraintChecker {
|
|
|
321
275
|
// context.operation 为主触发条件,extraTriggers 为次级推断(ADR-0001),任一命中即匹配
|
|
322
276
|
const operations = [context.operation, ...(context.extraTriggers ?? [])];
|
|
323
277
|
|
|
324
|
-
// 1.
|
|
325
|
-
for (const constraint of Object.values(constraints.
|
|
278
|
+
// 1. error 级: block 模式首个违规即抛;collect 模式全量收集
|
|
279
|
+
for (const constraint of Object.values(constraints).filter(c => c.severity === 'error')) {
|
|
326
280
|
if (!matchesTrigger(constraint, operations)) continue;
|
|
327
281
|
|
|
328
282
|
const checkResult = await this.check(constraint, context, run, env);
|
|
329
|
-
result.
|
|
283
|
+
result.errors.push(checkResult);
|
|
330
284
|
this.recordTrace(constraint, checkResult, context);
|
|
331
285
|
|
|
332
286
|
if (!checkResult.satisfied) {
|
|
@@ -337,12 +291,12 @@ export class ConstraintChecker {
|
|
|
337
291
|
}
|
|
338
292
|
}
|
|
339
293
|
|
|
340
|
-
// 2.
|
|
341
|
-
for (const constraint of Object.values(constraints.
|
|
294
|
+
// 2. warning 级: 记录警告
|
|
295
|
+
for (const constraint of Object.values(constraints).filter(c => c.severity === 'warning')) {
|
|
342
296
|
if (!matchesTrigger(constraint, operations)) continue;
|
|
343
297
|
|
|
344
298
|
const checkResult = await this.check(constraint, context, run, env);
|
|
345
|
-
result.
|
|
299
|
+
result.warnings.push(checkResult);
|
|
346
300
|
this.recordTrace(constraint, checkResult, context);
|
|
347
301
|
|
|
348
302
|
if (!checkResult.satisfied) {
|
|
@@ -354,12 +308,12 @@ export class ConstraintChecker {
|
|
|
354
308
|
}
|
|
355
309
|
|
|
356
310
|
/**
|
|
357
|
-
* 执行前检查(仅检查
|
|
311
|
+
* 执行前检查(仅检查 severity='error' 的约束)
|
|
358
312
|
*
|
|
359
313
|
* @param context 约束上下文
|
|
360
314
|
* @param customConfig 可选,per-request 自定义配置(避免多请求间的单例状态污染)
|
|
361
315
|
* @param evidence 可选,本次检查共用的 git 证据 adapter(#87,同 checkConstraints)
|
|
362
|
-
* @throws ConstraintViolationError
|
|
316
|
+
* @throws ConstraintViolationError 如果有 error 级违规
|
|
363
317
|
*/
|
|
364
318
|
async beforeExecution(
|
|
365
319
|
context: ConstraintContext,
|
|
@@ -373,7 +327,7 @@ export class ConstraintChecker {
|
|
|
373
327
|
const constraints = this.getConstraints(customConfig);
|
|
374
328
|
const operations = [context.operation, ...(context.extraTriggers ?? [])];
|
|
375
329
|
|
|
376
|
-
for (const constraint of Object.values(constraints.
|
|
330
|
+
for (const constraint of Object.values(constraints).filter(c => c.severity === 'error')) {
|
|
377
331
|
if (!matchesTrigger(constraint, operations)) continue;
|
|
378
332
|
|
|
379
333
|
const result = await this.check(constraint, context, run, env);
|
|
@@ -401,15 +355,12 @@ export async function checkConstraint(
|
|
|
401
355
|
const checker = ConstraintChecker.getInstance();
|
|
402
356
|
const constraints = checker.getConstraints(customConfig);
|
|
403
357
|
|
|
404
|
-
const constraint =
|
|
405
|
-
constraints.ironLaws[constraintId] ||
|
|
406
|
-
constraints.guidelines[constraintId] ||
|
|
407
|
-
constraints.prompts[constraintId];
|
|
358
|
+
const constraint = constraints[constraintId];
|
|
408
359
|
|
|
409
360
|
if (!constraint) {
|
|
410
361
|
return {
|
|
411
362
|
id: constraintId,
|
|
412
|
-
|
|
363
|
+
severity: 'warning',
|
|
413
364
|
satisfied: false,
|
|
414
365
|
message: `未知的约束: ${constraintId}`,
|
|
415
366
|
checkedAt: new Date(),
|
|
@@ -430,7 +381,7 @@ export interface CheckConstraintsOptions {
|
|
|
430
381
|
}
|
|
431
382
|
|
|
432
383
|
/**
|
|
433
|
-
*
|
|
384
|
+
* 快捷函数:执行约束检查
|
|
434
385
|
*
|
|
435
386
|
* @param context 约束上下文
|
|
436
387
|
* @param options.onTrace 每条约束检查后的回调(用于记录 trace 到外部存储)
|
|
@@ -442,8 +393,8 @@ export async function checkConstraints(
|
|
|
442
393
|
): Promise<ConstraintCheckResult> {
|
|
443
394
|
const result = await ConstraintChecker.getInstance().checkConstraints(context, options?.customConfig ?? null);
|
|
444
395
|
if (options?.onTrace) {
|
|
445
|
-
for (const r of result.
|
|
446
|
-
for (const r of result.
|
|
396
|
+
for (const r of result.errors) options.onTrace(r);
|
|
397
|
+
for (const r of result.warnings) options.onTrace(r);
|
|
447
398
|
}
|
|
448
399
|
return result;
|
|
449
400
|
}
|
|
@@ -23,7 +23,7 @@ import type { MergedConstraintsConfig } from '../../../../types/project-config';
|
|
|
23
23
|
const CAPABILITY_SYNC: Constraint = {
|
|
24
24
|
id: 'capability_sync',
|
|
25
25
|
kind: 'check',
|
|
26
|
-
|
|
26
|
+
severity: 'warning',
|
|
27
27
|
rule: 'CODE CHANGES MUST UPDATE CAPABILITIES.MD',
|
|
28
28
|
message: '核心模块变更必须同步功能清单',
|
|
29
29
|
trigger: ['module_modification'],
|
|
@@ -62,11 +62,8 @@ describe('normalizeCheckOutcome — 三态 + 证据归一', () => {
|
|
|
62
62
|
/** 只含 capability_sync 的生效集:一次 run 一条 trace,不牵动其他约束 */
|
|
63
63
|
function onlyCapabilitySync(): MergedConstraintsConfig {
|
|
64
64
|
return {
|
|
65
|
-
|
|
66
|
-
guidelines: { capability_sync: CAPABILITY_SYNC },
|
|
67
|
-
prompts: {},
|
|
65
|
+
constraints: { capability_sync: CAPABILITY_SYNC },
|
|
68
66
|
disabled: [],
|
|
69
|
-
custom: [],
|
|
70
67
|
};
|
|
71
68
|
}
|
|
72
69
|
|
|
@@ -81,7 +78,7 @@ async function runGuideline(dir: string): Promise<{
|
|
|
81
78
|
{ operation: 'module_modification', projectPath: dir },
|
|
82
79
|
onlyCapabilitySync()
|
|
83
80
|
);
|
|
84
|
-
const result = run.
|
|
81
|
+
const result = run.warnings.find(r => r.id === 'capability_sync')!;
|
|
85
82
|
const trace = traces.find(t => t.constraintId === 'capability_sync')!;
|
|
86
83
|
return { result, trace };
|
|
87
84
|
}
|
|
@@ -151,7 +148,7 @@ describe('证据随铁律异常外溢(CLI catch 分支只打印 error.message
|
|
|
151
148
|
it('ConstraintViolationError 文案带出证据行', () => {
|
|
152
149
|
const error = new ConstraintViolationError({
|
|
153
150
|
id: 'docs_freshness',
|
|
154
|
-
|
|
151
|
+
severity: 'error',
|
|
155
152
|
satisfied: false,
|
|
156
153
|
message: '文档新鲜度检查未通过',
|
|
157
154
|
evidence: ['CAPABILITIES.md 登记的条目不存在:', 'src/gone.ts'],
|
|
@@ -165,7 +162,7 @@ describe('证据随铁律异常外溢(CLI catch 分支只打印 error.message
|
|
|
165
162
|
it('无证据时文案保持原样(旧消费方断言不受影响)', () => {
|
|
166
163
|
const error = new ConstraintViolationError({
|
|
167
164
|
id: 'no_test_simplification',
|
|
168
|
-
|
|
165
|
+
severity: 'error',
|
|
169
166
|
satisfied: false,
|
|
170
167
|
message: '禁止简化测试绕过困难',
|
|
171
168
|
checkedAt: new Date(),
|