@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
|
@@ -54,9 +54,9 @@ describe('ConstraintChecker - 补充覆盖', () => {
|
|
|
54
54
|
|
|
55
55
|
const result = checker.findApplicableConstraints(context);
|
|
56
56
|
|
|
57
|
-
expect(result.
|
|
57
|
+
expect(result.errors.length).toBeGreaterThan(0);
|
|
58
58
|
expect(
|
|
59
|
-
result.
|
|
59
|
+
result.errors.some(c => c.id === 'no_completion_without_verification')
|
|
60
60
|
).toBe(true);
|
|
61
61
|
});
|
|
62
62
|
|
|
@@ -68,9 +68,9 @@ describe('ConstraintChecker - 补充覆盖', () => {
|
|
|
68
68
|
const result = checker.findApplicableConstraints(context);
|
|
69
69
|
|
|
70
70
|
// file_creation 可能没有任何约束匹配
|
|
71
|
-
//
|
|
72
|
-
expect(Array.isArray(result.
|
|
73
|
-
expect(Array.isArray(result.
|
|
71
|
+
// 这个测试的目的是验证按 trigger 过滤逻辑
|
|
72
|
+
expect(Array.isArray(result.errors)).toBe(true);
|
|
73
|
+
expect(Array.isArray(result.warnings)).toBe(true);
|
|
74
74
|
});
|
|
75
75
|
});
|
|
76
76
|
|
|
@@ -99,25 +99,25 @@ describe('ConstraintChecker - 补充覆盖', () => {
|
|
|
99
99
|
await expect(checkBeforeExecution(context)).rejects.toThrow();
|
|
100
100
|
});
|
|
101
101
|
|
|
102
|
-
it('customConfig
|
|
102
|
+
it('customConfig 应替换内置集(空配置不检查内置 error 级约束)', async () => {
|
|
103
103
|
const context: ConstraintContext = {
|
|
104
104
|
operation: 'code_implementation',
|
|
105
105
|
hasTest: false,
|
|
106
106
|
hasVerificationEvidence: false,
|
|
107
107
|
};
|
|
108
108
|
|
|
109
|
-
//
|
|
109
|
+
// 内置 error 级约束下违规抛错
|
|
110
110
|
await expect(checkBeforeExecution(context)).rejects.toThrow();
|
|
111
111
|
|
|
112
112
|
// 空 customConfig 替换内置集 → 不抛
|
|
113
113
|
await expect(
|
|
114
|
-
checkBeforeExecution(context, {
|
|
114
|
+
checkBeforeExecution(context, { constraints: {}, disabled: [] })
|
|
115
115
|
).resolves.not.toThrow();
|
|
116
116
|
});
|
|
117
117
|
});
|
|
118
118
|
|
|
119
119
|
describe('checkConstraints 完整流程', () => {
|
|
120
|
-
it('
|
|
120
|
+
it('应该返回完整的检查结果', async () => {
|
|
121
121
|
const context: ConstraintContext = {
|
|
122
122
|
operation: 'commit',
|
|
123
123
|
projectPath: tempDir,
|
|
@@ -127,8 +127,8 @@ describe('ConstraintChecker - 补充覆盖', () => {
|
|
|
127
127
|
|
|
128
128
|
const result = await checkConstraints(context);
|
|
129
129
|
|
|
130
|
-
expect(result.
|
|
131
|
-
expect(result.
|
|
130
|
+
expect(result.errors).toBeDefined();
|
|
131
|
+
expect(result.warnings).toBeDefined();
|
|
132
132
|
expect(typeof result.passed).toBe('boolean');
|
|
133
133
|
expect(typeof result.warningCount).toBe('number');
|
|
134
134
|
});
|
|
@@ -137,80 +137,57 @@ describe('ConstraintChecker - 补充覆盖', () => {
|
|
|
137
137
|
describe('自定义约束配置', () => {
|
|
138
138
|
it('per-request customConfig 应该生效', () => {
|
|
139
139
|
const constraints = checker.getConstraints({
|
|
140
|
-
|
|
141
|
-
guidelines: {},
|
|
140
|
+
constraints: {},
|
|
142
141
|
disabled: [],
|
|
143
|
-
custom: [],
|
|
144
142
|
});
|
|
145
143
|
|
|
146
|
-
expect(constraints
|
|
147
|
-
expect(constraints.guidelines).toEqual({});
|
|
144
|
+
expect(constraints).toEqual({});
|
|
148
145
|
});
|
|
149
146
|
|
|
150
|
-
it('checkConstraint 带 customConfig
|
|
151
|
-
const context: ConstraintContext = {
|
|
147
|
+
it('checkConstraint 带 customConfig 应命中配置内约束', async () => {
|
|
148
|
+
const context: ConstraintContext = {
|
|
149
|
+
operation: 'code_implementation',
|
|
150
|
+
hasVerificationEvidence: true,
|
|
151
|
+
};
|
|
152
152
|
const customConfig = {
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
id: '
|
|
156
|
-
kind: '
|
|
157
|
-
|
|
153
|
+
constraints: {
|
|
154
|
+
no_completion_without_verification: {
|
|
155
|
+
id: 'no_completion_without_verification',
|
|
156
|
+
kind: 'check' as const,
|
|
157
|
+
severity: 'error' as const,
|
|
158
158
|
rule: 'TEST',
|
|
159
159
|
message: 'test',
|
|
160
|
-
trigger: '
|
|
160
|
+
trigger: 'code_implementation',
|
|
161
161
|
enforcement: 'test',
|
|
162
162
|
},
|
|
163
163
|
},
|
|
164
|
-
guidelines: {},
|
|
165
164
|
disabled: [] as string[],
|
|
166
|
-
custom: [] as string[],
|
|
167
165
|
};
|
|
168
166
|
|
|
169
|
-
// 不带 customConfig
|
|
167
|
+
// 不带 customConfig:未生效的 id 不可见
|
|
170
168
|
const missing = await checkConstraint('test_only', context);
|
|
171
169
|
expect(missing.satisfied).toBe(false);
|
|
172
170
|
expect(missing.message).toContain('未知的约束');
|
|
173
171
|
|
|
174
|
-
// 带 customConfig
|
|
175
|
-
const hit = await checkConstraint('
|
|
176
|
-
expect(hit.id).toBe('
|
|
172
|
+
// 带 customConfig:命中配置内约束
|
|
173
|
+
const hit = await checkConstraint('no_completion_without_verification', context, customConfig);
|
|
174
|
+
expect(hit.id).toBe('no_completion_without_verification');
|
|
177
175
|
expect(hit.satisfied).toBe(true);
|
|
178
176
|
});
|
|
179
177
|
});
|
|
180
178
|
|
|
181
|
-
describe('
|
|
182
|
-
it('
|
|
183
|
-
const context: ConstraintContext = {
|
|
184
|
-
operation: 'code_implementation',
|
|
185
|
-
hasTest: true,
|
|
186
|
-
};
|
|
187
|
-
|
|
188
|
-
const result = await checker.check(
|
|
189
|
-
{
|
|
190
|
-
id: 'test_severity_iron',
|
|
191
|
-
kind: 'prompt',
|
|
192
|
-
level: 'iron_law',
|
|
193
|
-
rule: 'TEST',
|
|
194
|
-
message: 'test',
|
|
195
|
-
trigger: 'code_implementation',
|
|
196
|
-
enforcement: 'test',
|
|
197
|
-
},
|
|
198
|
-
context
|
|
199
|
-
);
|
|
200
|
-
|
|
201
|
-
expect(result).toBeDefined();
|
|
202
|
-
});
|
|
203
|
-
|
|
204
|
-
it('guideline 应该返回 warning', async () => {
|
|
179
|
+
describe('severity 透传', () => {
|
|
180
|
+
it('check 结果带约束定义的 severity', async () => {
|
|
205
181
|
const context: ConstraintContext = {
|
|
206
182
|
operation: 'code_implementation',
|
|
183
|
+
hasVerificationEvidence: true,
|
|
207
184
|
};
|
|
208
185
|
|
|
209
186
|
const result = await checker.check(
|
|
210
187
|
{
|
|
211
|
-
id: '
|
|
212
|
-
kind: '
|
|
213
|
-
|
|
188
|
+
id: 'no_completion_without_verification',
|
|
189
|
+
kind: 'check',
|
|
190
|
+
severity: 'error',
|
|
214
191
|
rule: 'TEST',
|
|
215
192
|
message: 'test',
|
|
216
193
|
trigger: 'code_implementation',
|
|
@@ -219,9 +196,8 @@ describe('ConstraintChecker - 补充覆盖', () => {
|
|
|
219
196
|
context
|
|
220
197
|
);
|
|
221
198
|
|
|
222
|
-
expect(result).
|
|
199
|
+
expect(result.severity).toBe('error');
|
|
223
200
|
});
|
|
224
|
-
|
|
225
201
|
});
|
|
226
202
|
|
|
227
203
|
describe('checkConstraint 快捷函数', () => {
|
|
@@ -251,8 +227,8 @@ describe('ConstraintChecker - 补充覆盖', () => {
|
|
|
251
227
|
});
|
|
252
228
|
});
|
|
253
229
|
|
|
254
|
-
describe('checkConstraints
|
|
255
|
-
it('
|
|
230
|
+
describe('checkConstraints error 级违规', () => {
|
|
231
|
+
it('error 级违规应该抛出 ConstraintViolationError', async () => {
|
|
256
232
|
const context: ConstraintContext = {
|
|
257
233
|
operation: 'code_implementation',
|
|
258
234
|
hasTest: false,
|
|
@@ -1,15 +1,13 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* checker.ts 测试
|
|
3
3
|
*
|
|
4
|
-
* ADR-
|
|
5
|
-
* prompt
|
|
6
|
-
* 已退役/被吸收约束的用例随之移除。
|
|
4
|
+
* ADR-0029:只覆盖存活 check 约束的 checker 行为;
|
|
5
|
+
* prompt 面已删除,其短路用例随之移除。
|
|
7
6
|
*/
|
|
8
7
|
|
|
9
8
|
import { ConstraintChecker, checkConstraint } from '../core/constraints/checker';
|
|
10
|
-
import { PROMPTS } from '../core/constraints/definitions';
|
|
11
9
|
import type { ConstraintContext } from '../types/constraint';
|
|
12
|
-
import {
|
|
10
|
+
import { rmSync, mkdtempSync } from 'fs';
|
|
13
11
|
import { join } from 'path';
|
|
14
12
|
import { tmpdir } from 'os';
|
|
15
13
|
|
|
@@ -31,42 +29,7 @@ describe('ConstraintChecker', () => {
|
|
|
31
29
|
}
|
|
32
30
|
});
|
|
33
31
|
|
|
34
|
-
describe('
|
|
35
|
-
it('should check no_bypass_checkpoint with skip patterns', async () => {
|
|
36
|
-
// 创建包含 skip 的文件
|
|
37
|
-
const skipFile = join(tempDir, 'skip-test.ts');
|
|
38
|
-
writeFileSync(skipFile, 'test.skip("skipped test", () => {});');
|
|
39
|
-
|
|
40
|
-
const context: ConstraintContext = {
|
|
41
|
-
operation: 'code_implementation',
|
|
42
|
-
changedFiles: [skipFile],
|
|
43
|
-
};
|
|
44
|
-
|
|
45
|
-
const result = await checker.check(
|
|
46
|
-
{ id: 'no_bypass_checkpoint', kind: 'check', level: 'guideline', rule: 'NO BYPASS', message: 'test', trigger: 'code_implementation', enforcement: 'test' },
|
|
47
|
-
context
|
|
48
|
-
);
|
|
49
|
-
|
|
50
|
-
expect(result.satisfied).toBe(false);
|
|
51
|
-
});
|
|
52
|
-
|
|
53
|
-
it('should pass no_bypass_checkpoint without skip patterns', async () => {
|
|
54
|
-
const normalFile = join(tempDir, 'normal-test.ts');
|
|
55
|
-
writeFileSync(normalFile, 'test("normal test", () => { expect(true).toBe(true); });');
|
|
56
|
-
|
|
57
|
-
const context: ConstraintContext = {
|
|
58
|
-
operation: 'code_implementation',
|
|
59
|
-
changedFiles: [normalFile],
|
|
60
|
-
};
|
|
61
|
-
|
|
62
|
-
const result = await checker.check(
|
|
63
|
-
{ id: 'no_bypass_checkpoint', kind: 'check', level: 'guideline', rule: 'NO BYPASS', message: 'test', trigger: 'code_implementation', enforcement: 'test' },
|
|
64
|
-
context
|
|
65
|
-
);
|
|
66
|
-
|
|
67
|
-
expect(result.satisfied).toBe(true);
|
|
68
|
-
});
|
|
69
|
-
|
|
32
|
+
describe('error 级约束', () => {
|
|
70
33
|
it('should check no_completion_without_verification', async () => {
|
|
71
34
|
const contextWithEvidence: ConstraintContext = {
|
|
72
35
|
operation: 'code_implementation',
|
|
@@ -79,71 +42,21 @@ describe('ConstraintChecker', () => {
|
|
|
79
42
|
};
|
|
80
43
|
|
|
81
44
|
const resultWithEvidence = await checker.check(
|
|
82
|
-
{ id: 'no_completion_without_verification', kind: 'check',
|
|
45
|
+
{ id: 'no_completion_without_verification', kind: 'check', severity: 'error', rule: 'NO COMPLETION', message: 'test', trigger: 'code_implementation', enforcement: 'test' },
|
|
83
46
|
contextWithEvidence
|
|
84
47
|
);
|
|
85
48
|
|
|
86
49
|
const resultWithoutEvidence = await checker.check(
|
|
87
|
-
{ id: 'no_completion_without_verification', kind: 'check',
|
|
50
|
+
{ id: 'no_completion_without_verification', kind: 'check', severity: 'error', rule: 'NO COMPLETION', message: 'test', trigger: 'code_implementation', enforcement: 'test' },
|
|
88
51
|
contextWithoutEvidence
|
|
89
52
|
);
|
|
90
53
|
|
|
91
54
|
expect(resultWithEvidence.satisfied).toBe(true);
|
|
92
55
|
expect(resultWithoutEvidence.satisfied).toBe(false);
|
|
93
56
|
});
|
|
94
|
-
|
|
95
|
-
it('should check incremental_progress', async () => {
|
|
96
|
-
const contextWithSingleTask: ConstraintContext = {
|
|
97
|
-
operation: 'code_implementation',
|
|
98
|
-
hasSingleTask: true,
|
|
99
|
-
};
|
|
100
|
-
|
|
101
|
-
const contextWithMultipleTasks: ConstraintContext = {
|
|
102
|
-
operation: 'code_implementation',
|
|
103
|
-
hasSingleTask: false,
|
|
104
|
-
};
|
|
105
|
-
|
|
106
|
-
const resultWith = await checker.check(
|
|
107
|
-
{ id: 'incremental_progress', kind: 'check', level: 'iron_law', rule: 'ONE TASK', message: 'test', trigger: 'code_implementation', enforcement: 'test' },
|
|
108
|
-
contextWithSingleTask
|
|
109
|
-
);
|
|
110
|
-
|
|
111
|
-
const resultWithout = await checker.check(
|
|
112
|
-
{ id: 'incremental_progress', kind: 'check', level: 'iron_law', rule: 'ONE TASK', message: 'test', trigger: 'code_implementation', enforcement: 'test' },
|
|
113
|
-
contextWithMultipleTasks
|
|
114
|
-
);
|
|
115
|
-
|
|
116
|
-
expect(resultWith.satisfied).toBe(true);
|
|
117
|
-
expect(resultWithout.satisfied).toBe(false);
|
|
118
|
-
});
|
|
119
|
-
|
|
120
|
-
it('should check no_implementation_without_requirement', async () => {
|
|
121
|
-
const contextWithRequirement: ConstraintContext = {
|
|
122
|
-
operation: 'code_implementation',
|
|
123
|
-
hasRequirement: true,
|
|
124
|
-
};
|
|
125
|
-
|
|
126
|
-
const contextWithoutRequirement: ConstraintContext = {
|
|
127
|
-
operation: 'code_implementation',
|
|
128
|
-
hasRequirement: false,
|
|
129
|
-
};
|
|
130
|
-
|
|
131
|
-
const resultWith = await checker.check(
|
|
132
|
-
{ id: 'no_implementation_without_requirement', kind: 'check', level: 'iron_law', rule: 'REQ EXISTS', message: 'test', trigger: 'code_implementation', enforcement: 'test' },
|
|
133
|
-
contextWithRequirement
|
|
134
|
-
);
|
|
135
|
-
|
|
136
|
-
const resultWithout = await checker.check(
|
|
137
|
-
{ id: 'no_implementation_without_requirement', kind: 'check', level: 'iron_law', rule: 'REQ EXISTS', message: 'test', trigger: 'code_implementation', enforcement: 'test' },
|
|
138
|
-
contextWithoutRequirement
|
|
139
|
-
);
|
|
140
|
-
|
|
141
|
-
expect(resultWith.satisfied).toBe(true);
|
|
142
|
-
expect(resultWithout.satisfied).toBe(false);
|
|
143
|
-
});
|
|
144
57
|
});
|
|
145
58
|
|
|
146
|
-
describe('
|
|
59
|
+
describe('warning 级约束', () => {
|
|
147
60
|
it('should check capability_sync without CAPABILITIES.md', async () => {
|
|
148
61
|
// 注意:tempDir 在 harness 仓库内,如果有缓存的变更,会检查到代码变更
|
|
149
62
|
// 所以需要创建一个无代码变更的场景或创建 CAPABILITIES.md
|
|
@@ -159,7 +72,7 @@ describe('ConstraintChecker', () => {
|
|
|
159
72
|
fs.writeFileSync(capabilitiesPath, '# Capabilities\n');
|
|
160
73
|
|
|
161
74
|
const result = await checker.check(
|
|
162
|
-
{ id: 'capability_sync', kind: 'check',
|
|
75
|
+
{ id: 'capability_sync', kind: 'check', severity: 'warning', rule: 'CAPABILITY SYNC', message: 'test', trigger: 'commit', enforcement: 'test' },
|
|
163
76
|
context
|
|
164
77
|
);
|
|
165
78
|
|
|
@@ -171,24 +84,6 @@ describe('ConstraintChecker', () => {
|
|
|
171
84
|
});
|
|
172
85
|
});
|
|
173
86
|
|
|
174
|
-
describe('Prompt constraints(ADR-0001:不执行 checker,check() 短路 satisfied)', () => {
|
|
175
|
-
it('所有内置 prompt 约束 check() 均短路通过', async () => {
|
|
176
|
-
const context: ConstraintContext = { operation: 'code_implementation' };
|
|
177
|
-
for (const prompt of Object.values(PROMPTS)) {
|
|
178
|
-
const result = await checker.check(prompt, context);
|
|
179
|
-
expect(result.satisfied).toBe(true);
|
|
180
|
-
}
|
|
181
|
-
});
|
|
182
|
-
|
|
183
|
-
it('prompt 短路不产生 requiredAction/message', async () => {
|
|
184
|
-
const context: ConstraintContext = { operation: 'code_implementation' };
|
|
185
|
-
const result = await checker.check(PROMPTS['no_fix_without_root_cause'], context);
|
|
186
|
-
expect(result.satisfied).toBe(true);
|
|
187
|
-
expect(result.message).toBeUndefined();
|
|
188
|
-
expect(result.requiredAction).toBeUndefined();
|
|
189
|
-
});
|
|
190
|
-
});
|
|
191
|
-
|
|
192
87
|
describe('Helper functions', () => {
|
|
193
88
|
it('should check single constraint via checkConstraint', async () => {
|
|
194
89
|
const context: ConstraintContext = {
|
|
@@ -205,8 +100,8 @@ describe('ConstraintChecker', () => {
|
|
|
205
100
|
operation: 'code_implementation',
|
|
206
101
|
};
|
|
207
102
|
|
|
208
|
-
const result = await checkConstraint('
|
|
209
|
-
expect(result.id).toBe('
|
|
103
|
+
const result = await checkConstraint('no_completion_without_verification', context);
|
|
104
|
+
expect(result.id).toBe('no_completion_without_verification');
|
|
210
105
|
expect(result.satisfied).toBe(true);
|
|
211
106
|
});
|
|
212
107
|
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* 生效约束集(getEffectiveConstraints / lintEffectiveConfig)测试
|
|
3
3
|
*
|
|
4
|
-
*
|
|
4
|
+
* 合并矩阵(ADR-0029):preset 裁剪 / config.yml 禁用 / 未知 id 诊断
|
|
5
|
+
* (custom 追加与 scenes 过滤已随文本注入层关停一并退役)
|
|
5
6
|
*/
|
|
6
7
|
|
|
7
8
|
import { describe, it, expect, beforeEach, afterEach } from '@jest/globals';
|
|
@@ -11,25 +12,19 @@ import {
|
|
|
11
12
|
lintEffectiveConfig,
|
|
12
13
|
} from '../core/effective-constraints';
|
|
13
14
|
import { createProjectFixture } from '../test-setup/project-fixture';
|
|
14
|
-
import {
|
|
15
|
+
import { CONSTRAINTS } from '../core/constraints/definitions';
|
|
15
16
|
|
|
16
|
-
const BUILTIN_TOTAL =
|
|
17
|
-
Object.keys(IRON_LAWS).length + Object.keys(GUIDELINES).length + Object.keys(PROMPTS).length;
|
|
18
|
-
/** 带 appliesTo 标签的内置 prompt(scenes 过滤对象) */
|
|
19
|
-
const SCENE_PROMPTS = Object.values(PROMPTS)
|
|
20
|
-
.filter(c => c.appliesTo && c.appliesTo.length > 0)
|
|
21
|
-
.map(c => c.id);
|
|
17
|
+
const BUILTIN_TOTAL = Object.keys(CONSTRAINTS).length;
|
|
22
18
|
|
|
23
19
|
describe('getEffectiveConstraints / lintEffectiveConfig', () => {
|
|
24
20
|
let tempDir: string;
|
|
25
21
|
let counter = 0;
|
|
26
22
|
|
|
27
|
-
const setupProject = (configYaml?: string
|
|
23
|
+
const setupProject = (configYaml?: string): string =>
|
|
28
24
|
createProjectFixture({
|
|
29
25
|
parentDir: tempDir,
|
|
30
26
|
name: `p${counter++}`,
|
|
31
27
|
config: configYaml,
|
|
32
|
-
files: customYaml === undefined ? undefined : { '.harness/custom-constraints.yml': customYaml },
|
|
33
28
|
});
|
|
34
29
|
|
|
35
30
|
beforeEach(() => {
|
|
@@ -42,65 +37,28 @@ describe('getEffectiveConstraints / lintEffectiveConfig', () => {
|
|
|
42
37
|
});
|
|
43
38
|
|
|
44
39
|
describe('合并矩阵', () => {
|
|
45
|
-
it('
|
|
40
|
+
it('无配置项目:内置全集(全部 kind=check,带 severity)', () => {
|
|
46
41
|
const dir = setupProject();
|
|
47
42
|
const constraints = getEffectiveConstraints(dir);
|
|
48
|
-
const ids = constraints.map(c => c.id);
|
|
49
|
-
|
|
50
|
-
expect(constraints.length).toBe(BUILTIN_TOTAL - SCENE_PROMPTS.length);
|
|
51
|
-
for (const id of SCENE_PROMPTS) {
|
|
52
|
-
expect(ids).not.toContain(id);
|
|
53
|
-
}
|
|
54
|
-
// check + prompt 均带 kind
|
|
55
|
-
expect(constraints.every(c => c.kind === 'check' || c.kind === 'prompt')).toBe(true);
|
|
56
|
-
expect(constraints.filter(c => c.kind === 'check')).toHaveLength(
|
|
57
|
-
Object.keys(IRON_LAWS).length + Object.keys(GUIDELINES).length
|
|
58
|
-
);
|
|
59
|
-
});
|
|
60
|
-
|
|
61
|
-
it('scenes 命中:对应场景 prompt 进入生效集,其余场景 prompt 仍排除', () => {
|
|
62
|
-
const dir = setupProject(`scenes:\n - agent-skill\n`);
|
|
63
|
-
const ids = getEffectiveConstraints(dir).map(c => c.id);
|
|
64
43
|
|
|
65
|
-
expect(
|
|
66
|
-
expect(
|
|
44
|
+
expect(constraints.length).toBe(BUILTIN_TOTAL);
|
|
45
|
+
expect(constraints.every(c => c.kind === 'check')).toBe(true);
|
|
67
46
|
});
|
|
68
47
|
|
|
69
|
-
it('config.yml
|
|
48
|
+
it('config.yml 禁用:条目从生效集移除', () => {
|
|
70
49
|
const dir = setupProject(`
|
|
71
50
|
constraints:
|
|
72
|
-
|
|
51
|
+
capability_sync:
|
|
73
52
|
enabled: false
|
|
74
|
-
|
|
53
|
+
docs_freshness:
|
|
75
54
|
enabled: false
|
|
76
55
|
`);
|
|
77
56
|
const constraints = getEffectiveConstraints(dir);
|
|
78
57
|
const ids = constraints.map(c => c.id);
|
|
79
58
|
|
|
80
|
-
expect(ids).not.toContain('
|
|
81
|
-
expect(ids).not.toContain('
|
|
82
|
-
expect(constraints.length).toBe(BUILTIN_TOTAL -
|
|
83
|
-
});
|
|
84
|
-
|
|
85
|
-
it('config.yml 禁用:custom 约束同样从生效集移除(retire 落 config.yml 的退役路径)', () => {
|
|
86
|
-
const dir = setupProject(
|
|
87
|
-
`
|
|
88
|
-
constraints:
|
|
89
|
-
my_project_rule:
|
|
90
|
-
enabled: false
|
|
91
|
-
`,
|
|
92
|
-
`
|
|
93
|
-
custom_constraints:
|
|
94
|
-
my_project_rule:
|
|
95
|
-
level: iron_law
|
|
96
|
-
rule: MY RULE
|
|
97
|
-
message: 项目自定义
|
|
98
|
-
`
|
|
99
|
-
);
|
|
100
|
-
const constraints = getEffectiveConstraints(dir);
|
|
101
|
-
const ids = constraints.map(c => c.id);
|
|
102
|
-
|
|
103
|
-
expect(ids).not.toContain('my_project_rule');
|
|
59
|
+
expect(ids).not.toContain('capability_sync');
|
|
60
|
+
expect(ids).not.toContain('docs_freshness');
|
|
61
|
+
expect(constraints.length).toBe(BUILTIN_TOTAL - 2);
|
|
104
62
|
});
|
|
105
63
|
|
|
106
64
|
it('preset: relaxed 裁剪生效集', () => {
|
|
@@ -111,33 +69,13 @@ custom_constraints:
|
|
|
111
69
|
expect(ids).toEqual(
|
|
112
70
|
expect.arrayContaining([
|
|
113
71
|
'no_completion_without_verification',
|
|
114
|
-
'incremental_progress',
|
|
115
|
-
'no_implementation_without_requirement',
|
|
116
|
-
'no_bypass_checkpoint',
|
|
117
72
|
'no_hardcoded_credentials',
|
|
118
73
|
])
|
|
119
74
|
);
|
|
120
|
-
expect(constraints).toHaveLength(
|
|
75
|
+
expect(constraints).toHaveLength(2);
|
|
121
76
|
expect(constraints.every(c => c.kind === 'check')).toBe(true);
|
|
122
77
|
});
|
|
123
78
|
|
|
124
|
-
it('custom-constraints.yml 追加自定义约束(kind=prompt)', () => {
|
|
125
|
-
const dir = setupProject(undefined, `
|
|
126
|
-
custom_constraints:
|
|
127
|
-
my_project_rule:
|
|
128
|
-
level: guideline
|
|
129
|
-
rule: MY RULE
|
|
130
|
-
message: 项目自定义
|
|
131
|
-
trigger: code_implementation
|
|
132
|
-
`);
|
|
133
|
-
const constraints = getEffectiveConstraints(dir);
|
|
134
|
-
const custom = constraints.find(c => c.id === 'my_project_rule');
|
|
135
|
-
|
|
136
|
-
expect(custom).toBeDefined();
|
|
137
|
-
expect(custom!.kind).toBe('prompt');
|
|
138
|
-
expect(custom!.level).toBe('guideline');
|
|
139
|
-
});
|
|
140
|
-
|
|
141
79
|
it('禁用未知 id(已移除约束的残留配置):不报错,lint 可诊断', () => {
|
|
142
80
|
const dir = setupProject(`
|
|
143
81
|
constraints:
|
|
@@ -146,19 +84,16 @@ constraints:
|
|
|
146
84
|
`);
|
|
147
85
|
// 生效集不受影响、不抛错
|
|
148
86
|
const constraints = getEffectiveConstraints(dir);
|
|
149
|
-
expect(constraints.length).toBe(BUILTIN_TOTAL
|
|
87
|
+
expect(constraints.length).toBe(BUILTIN_TOTAL);
|
|
150
88
|
|
|
151
89
|
const lint = lintEffectiveConfig(dir);
|
|
152
90
|
expect(lint.unknownIds).toContain('removed_legacy_constraint');
|
|
153
91
|
});
|
|
154
92
|
|
|
155
|
-
it('lintEffectiveConfig
|
|
156
|
-
const dir = setupProject(
|
|
93
|
+
it('lintEffectiveConfig:干净配置 unknownIds 为空', () => {
|
|
94
|
+
const dir = setupProject();
|
|
157
95
|
const lint = lintEffectiveConfig(dir);
|
|
158
96
|
|
|
159
|
-
expect(lint.scenes).toEqual(['llm-app']);
|
|
160
|
-
expect(lint.sceneExcluded).toContain('no_skill_without_test');
|
|
161
|
-
expect(lint.sceneExcluded).not.toContain('no_model_for_deterministic');
|
|
162
97
|
expect(lint.unknownIds).toEqual([]);
|
|
163
98
|
});
|
|
164
99
|
});
|