@dommaker/harness 0.16.9 → 0.17.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 +39 -0
- package/README.md +33 -32
- package/bin/harness.js +33 -20
- package/dist/cli/commands/check.d.ts +3 -2
- package/dist/cli/commands/check.d.ts.map +1 -1
- package/dist/cli/commands/check.js +69 -36
- package/dist/cli/commands/check.js.map +1 -1
- package/dist/cli/commands/constraints-report.d.ts +42 -0
- package/dist/cli/commands/constraints-report.d.ts.map +1 -0
- package/dist/cli/commands/constraints-report.js +233 -0
- package/dist/cli/commands/constraints-report.js.map +1 -0
- package/dist/cli/commands/constraints-retire.d.ts +71 -0
- package/dist/cli/commands/constraints-retire.d.ts.map +1 -0
- package/dist/cli/commands/constraints-retire.js +384 -0
- package/dist/cli/commands/constraints-retire.js.map +1 -0
- package/dist/cli/commands/constraints.d.ts +1 -1
- package/dist/cli/commands/constraints.d.ts.map +1 -1
- package/dist/cli/commands/constraints.js +4 -4
- package/dist/cli/commands/constraints.js.map +1 -1
- package/dist/cli/commands/index.d.ts +2 -1
- package/dist/cli/commands/index.d.ts.map +1 -1
- package/dist/cli/commands/index.js +10 -4
- package/dist/cli/commands/index.js.map +1 -1
- package/dist/cli/commands/init.d.ts +20 -0
- package/dist/cli/commands/init.d.ts.map +1 -1
- package/dist/cli/commands/init.js +81 -59
- package/dist/cli/commands/init.js.map +1 -1
- package/dist/cli/commands/report.js +4 -4
- package/dist/cli/commands/report.js.map +1 -1
- package/dist/cli/commands/status.d.ts.map +1 -1
- package/dist/cli/commands/status.js +3 -4
- package/dist/cli/commands/status.js.map +1 -1
- package/dist/cli/commands/sync-docs/capabilities-syncer.d.ts +5 -1
- package/dist/cli/commands/sync-docs/capabilities-syncer.d.ts.map +1 -1
- package/dist/cli/commands/sync-docs/capabilities-syncer.js +2 -10
- package/dist/cli/commands/sync-docs/capabilities-syncer.js.map +1 -1
- package/dist/cli/commands/validate.d.ts.map +1 -1
- package/dist/cli/commands/validate.js +1 -5
- package/dist/cli/commands/validate.js.map +1 -1
- package/dist/core/constraints/capabilities-parser.d.ts +7 -0
- package/dist/core/constraints/capabilities-parser.d.ts.map +1 -1
- package/dist/core/constraints/capabilities-parser.js +13 -0
- package/dist/core/constraints/capabilities-parser.js.map +1 -1
- package/dist/core/constraints/checker.d.ts +13 -5
- package/dist/core/constraints/checker.d.ts.map +1 -1
- package/dist/core/constraints/checker.js +62 -25
- package/dist/core/constraints/checker.js.map +1 -1
- package/dist/core/constraints/checkers/capability-sync.d.ts +9 -2
- package/dist/core/constraints/checkers/capability-sync.d.ts.map +1 -1
- package/dist/core/constraints/checkers/capability-sync.js +55 -22
- package/dist/core/constraints/checkers/capability-sync.js.map +1 -1
- package/dist/core/constraints/checkers/context-doc-sync.d.ts +3 -0
- package/dist/core/constraints/checkers/context-doc-sync.d.ts.map +1 -1
- package/dist/core/constraints/checkers/context-doc-sync.js +8 -2
- package/dist/core/constraints/checkers/context-doc-sync.js.map +1 -1
- package/dist/core/constraints/checkers/docs-freshness.d.ts +1 -0
- package/dist/core/constraints/checkers/docs-freshness.d.ts.map +1 -1
- package/dist/core/constraints/checkers/docs-freshness.js +39 -0
- package/dist/core/constraints/checkers/docs-freshness.js.map +1 -1
- package/dist/core/constraints/checkers/index.d.ts +8 -4
- package/dist/core/constraints/checkers/index.d.ts.map +1 -1
- package/dist/core/constraints/checkers/index.js +29 -46
- package/dist/core/constraints/checkers/index.js.map +1 -1
- package/dist/core/constraints/checkers/iron-flags.d.ts +6 -7
- package/dist/core/constraints/checkers/iron-flags.d.ts.map +1 -1
- package/dist/core/constraints/checkers/iron-flags.js +10 -17
- package/dist/core/constraints/checkers/iron-flags.js.map +1 -1
- package/dist/core/constraints/checkers/no-hardcoded-credentials.d.ts +13 -0
- package/dist/core/constraints/checkers/no-hardcoded-credentials.d.ts.map +1 -0
- package/dist/core/constraints/checkers/no-hardcoded-credentials.js +91 -0
- package/dist/core/constraints/checkers/no-hardcoded-credentials.js.map +1 -0
- package/dist/core/constraints/checkers/types.d.ts +15 -4
- package/dist/core/constraints/checkers/types.d.ts.map +1 -1
- package/dist/core/constraints/checkers/types.js +16 -4
- package/dist/core/constraints/checkers/types.js.map +1 -1
- package/dist/core/constraints/context-builder.d.ts +6 -3
- package/dist/core/constraints/context-builder.d.ts.map +1 -1
- package/dist/core/constraints/context-builder.js +29 -20
- package/dist/core/constraints/context-builder.js.map +1 -1
- package/dist/core/constraints/definitions/guidelines.d.ts +6 -2
- package/dist/core/constraints/definitions/guidelines.d.ts.map +1 -1
- package/dist/core/constraints/definitions/guidelines.js +29 -572
- package/dist/core/constraints/definitions/guidelines.js.map +1 -1
- package/dist/core/constraints/definitions/iron-laws.d.ts +6 -2
- package/dist/core/constraints/definitions/iron-laws.d.ts.map +1 -1
- package/dist/core/constraints/definitions/iron-laws.js +48 -231
- package/dist/core/constraints/definitions/iron-laws.js.map +1 -1
- package/dist/core/constraints/definitions/prompts.d.ts +12 -0
- package/dist/core/constraints/definitions/prompts.d.ts.map +1 -0
- package/dist/core/constraints/definitions/prompts.js +405 -0
- package/dist/core/constraints/definitions/prompts.js.map +1 -0
- package/dist/core/constraints/definitions.d.ts +15 -9
- package/dist/core/constraints/definitions.d.ts.map +1 -1
- package/dist/core/constraints/definitions.js +18 -12
- package/dist/core/constraints/definitions.js.map +1 -1
- package/dist/core/constraints/index.d.ts +3 -2
- package/dist/core/constraints/index.d.ts.map +1 -1
- package/dist/core/constraints/index.js +8 -2
- package/dist/core/constraints/index.js.map +1 -1
- package/dist/core/constraints/injection-drift.d.ts +48 -0
- package/dist/core/constraints/injection-drift.d.ts.map +1 -0
- package/dist/core/constraints/injection-drift.js +140 -0
- package/dist/core/constraints/injection-drift.js.map +1 -0
- package/dist/core/constraints/injection-renderer.d.ts +23 -0
- package/dist/core/constraints/injection-renderer.d.ts.map +1 -0
- package/dist/core/constraints/injection-renderer.js +44 -0
- package/dist/core/constraints/injection-renderer.js.map +1 -0
- package/dist/core/constraints/interceptor.d.ts.map +1 -1
- package/dist/core/constraints/interceptor.js +3 -2
- package/dist/core/constraints/interceptor.js.map +1 -1
- package/dist/core/constraints/usage-report.d.ts +95 -0
- package/dist/core/constraints/usage-report.d.ts.map +1 -0
- package/dist/core/constraints/usage-report.js +206 -0
- package/dist/core/constraints/usage-report.js.map +1 -0
- package/dist/core/effective-constraints.d.ts +47 -0
- package/dist/core/effective-constraints.d.ts.map +1 -0
- package/dist/core/effective-constraints.js +60 -0
- package/dist/core/effective-constraints.js.map +1 -0
- package/dist/core/index.d.ts +1 -0
- package/dist/core/index.d.ts.map +1 -1
- package/dist/core/index.js +2 -0
- package/dist/core/index.js.map +1 -1
- package/dist/core/project-config-loader.d.ts +17 -2
- package/dist/core/project-config-loader.d.ts.map +1 -1
- package/dist/core/project-config-loader.js +55 -8
- package/dist/core/project-config-loader.js.map +1 -1
- package/dist/dashboard/data.d.ts +1 -9
- package/dist/dashboard/data.d.ts.map +1 -1
- package/dist/dashboard/data.js +7 -15
- package/dist/dashboard/data.js.map +1 -1
- package/dist/dashboard/types.d.ts +13 -1
- package/dist/dashboard/types.d.ts.map +1 -1
- package/dist/index.d.ts +0 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -13
- package/dist/index.js.map +1 -1
- package/dist/monitoring/index.d.ts +0 -2
- package/dist/monitoring/index.d.ts.map +1 -1
- package/dist/monitoring/index.js +0 -2
- package/dist/monitoring/index.js.map +1 -1
- package/dist/monitoring/knowledge-evolver.d.ts +1 -1
- package/dist/monitoring/knowledge-evolver.js +1 -1
- package/dist/monitoring/trace-analyzer.d.ts +1 -1
- package/dist/monitoring/trace-analyzer.d.ts.map +1 -1
- package/dist/monitoring/trace-analyzer.js +16 -10
- package/dist/monitoring/trace-analyzer.js.map +1 -1
- package/dist/monitoring/traces.d.ts +2 -1
- package/dist/monitoring/traces.d.ts.map +1 -1
- package/dist/monitoring/traces.js +5 -3
- package/dist/monitoring/traces.js.map +1 -1
- package/dist/presets/standard.d.ts +3 -1
- package/dist/presets/standard.d.ts.map +1 -1
- package/dist/presets/standard.js +18 -8
- package/dist/presets/standard.js.map +1 -1
- package/dist/types/constraint.d.ts +51 -11
- package/dist/types/constraint.d.ts.map +1 -1
- package/dist/types/constraint.js +8 -4
- package/dist/types/constraint.js.map +1 -1
- package/dist/types/index.d.ts +1 -1
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/index.js.map +1 -1
- package/dist/types/monitoring-types.d.ts +2 -63
- package/dist/types/monitoring-types.d.ts.map +1 -1
- package/dist/types/monitoring-types.js +2 -2
- package/dist/types/project-config.d.ts +26 -1
- package/dist/types/project-config.d.ts.map +1 -1
- package/dist/types/trace.d.ts +16 -7
- package/dist/types/trace.d.ts.map +1 -1
- package/dist/types/trace.js +8 -0
- package/dist/types/trace.js.map +1 -1
- package/package.json +1 -1
- package/src/CONTEXT.md +2 -2
- package/src/__tests__/checker-extra.test.ts +228 -333
- package/src/__tests__/checker.test.ts +87 -724
- package/src/__tests__/constraint-doctor.test.ts +5 -5
- package/src/__tests__/doctor-extra.test.ts +164 -0
- package/src/__tests__/effective-constraints.test.ts +147 -0
- package/src/__tests__/extend-exceptions.test.ts +3 -3
- package/src/__tests__/iron-laws.test.ts +115 -91
- package/src/__tests__/no-hardcoded-credentials.test.ts +90 -0
- package/src/__tests__/presets.test.ts +7 -7
- package/src/__tests__/project-config-loader-extra.test.ts +41 -4
- package/src/__tests__/skip-semantics.test.ts +322 -0
- package/src/cli/commands/CONTEXT.md +5 -3
- package/src/cli/commands/__tests__/check-drift.test.ts +127 -0
- package/src/cli/commands/__tests__/check-skip-output.test.ts +128 -0
- package/src/cli/commands/__tests__/check.test.ts +14 -25
- package/src/cli/commands/__tests__/constraints-report.test.ts +327 -0
- package/src/cli/commands/__tests__/constraints-retire.test.ts +283 -0
- package/src/cli/commands/__tests__/init-injection.test.ts +234 -0
- package/src/cli/commands/__tests__/status-extra.test.ts +11 -11
- package/src/cli/commands/__tests__/status.test.ts +1 -1
- package/src/cli/commands/check.ts +72 -38
- package/src/cli/commands/constraints-report.ts +234 -0
- package/src/cli/commands/constraints-retire.ts +443 -0
- package/src/cli/commands/constraints.ts +5 -5
- package/src/cli/commands/index.ts +2 -1
- package/src/cli/commands/init.ts +94 -66
- package/src/cli/commands/report.ts +6 -6
- package/src/cli/commands/status.ts +3 -4
- package/src/cli/commands/sync-docs/capabilities-syncer.ts +5 -7
- package/src/cli/commands/validate.ts +1 -5
- package/src/core/CONTEXT.md +7 -4
- package/src/core/constraints/__tests__/injection-drift.test.ts +164 -0
- package/src/core/constraints/__tests__/interceptor.test.ts +4 -0
- package/src/core/constraints/capabilities-parser.ts +13 -0
- package/src/core/constraints/checker.ts +73 -28
- package/src/core/constraints/checkers/capability-sync.ts +72 -29
- package/src/core/constraints/checkers/context-doc-sync.ts +9 -2
- package/src/core/constraints/checkers/docs-freshness.ts +43 -0
- package/src/core/constraints/checkers/index.ts +38 -71
- package/src/core/constraints/checkers/iron-flags.ts +13 -49
- package/src/core/constraints/checkers/no-hardcoded-credentials.ts +93 -0
- package/src/core/constraints/checkers/types.ts +27 -5
- package/src/core/constraints/context-builder.ts +28 -19
- package/src/core/constraints/definitions/guidelines.ts +29 -597
- package/src/core/constraints/definitions/iron-laws.ts +48 -239
- package/src/core/constraints/definitions/prompts.ts +419 -0
- package/src/core/constraints/definitions.ts +18 -11
- package/src/core/constraints/index.ts +10 -1
- package/src/core/constraints/injection-drift.ts +139 -0
- package/src/core/constraints/injection-renderer.ts +44 -0
- package/src/core/constraints/interceptor.ts +3 -2
- package/src/core/constraints/usage-report.ts +254 -0
- package/src/core/effective-constraints.ts +85 -0
- package/src/core/index.ts +4 -1
- package/src/core/project-config-loader.ts +59 -9
- package/src/dashboard/__tests__/data.test.ts +5 -12
- package/src/dashboard/data.ts +9 -19
- package/src/dashboard/types.ts +18 -1
- package/src/index.ts +0 -12
- package/src/knowledge/CONTEXT.md +2 -2
- package/src/monitoring/CONTEXT.md +3 -4
- package/src/monitoring/index.ts +0 -2
- package/src/monitoring/knowledge-evolver.ts +1 -1
- package/src/monitoring/trace-analyzer.ts +15 -10
- package/src/monitoring/traces.ts +10 -6
- package/src/presets/__tests__/standard.test.ts +11 -9
- package/src/presets/standard.ts +24 -15
- package/src/types/constraint.ts +59 -14
- package/src/types/index.ts +1 -3
- package/src/types/monitoring-types.ts +2 -84
- package/src/types/project-config.ts +30 -1
- package/src/types/trace.ts +18 -7
- package/src/verification/CONTEXT.md +1 -1
- package/dist/cli/commands/flow.d.ts +0 -22
- package/dist/cli/commands/flow.d.ts.map +0 -1
- package/dist/cli/commands/flow.js +0 -275
- package/dist/cli/commands/flow.js.map +0 -1
- package/dist/constraints/lifecycle-runner.d.ts +0 -52
- package/dist/constraints/lifecycle-runner.d.ts.map +0 -1
- package/dist/constraints/lifecycle-runner.js +0 -151
- package/dist/constraints/lifecycle-runner.js.map +0 -1
- package/dist/constraints/registry.d.ts +0 -27
- package/dist/constraints/registry.d.ts.map +0 -1
- package/dist/constraints/registry.js +0 -128
- package/dist/constraints/registry.js.map +0 -1
- package/dist/constraints/types.d.ts +0 -53
- package/dist/constraints/types.d.ts.map +0 -1
- package/dist/constraints/types.js +0 -8
- package/dist/constraints/types.js.map +0 -1
- package/dist/core/constraints/checkers/always-pass.d.ts +0 -17
- package/dist/core/constraints/checkers/always-pass.d.ts.map +0 -1
- package/dist/core/constraints/checkers/always-pass.js +0 -23
- package/dist/core/constraints/checkers/always-pass.js.map +0 -1
- package/dist/core/constraints/checkers/guideline-flags.d.ts +0 -9
- package/dist/core/constraints/checkers/guideline-flags.d.ts.map +0 -1
- package/dist/core/constraints/checkers/guideline-flags.js +0 -13
- package/dist/core/constraints/checkers/guideline-flags.js.map +0 -1
- package/dist/core/constraints/checkers/no-any-type.d.ts +0 -6
- package/dist/core/constraints/checkers/no-any-type.d.ts.map +0 -1
- package/dist/core/constraints/checkers/no-any-type.js +0 -42
- package/dist/core/constraints/checkers/no-any-type.js.map +0 -1
- package/dist/core/constraints/checkers/no-excuse-patterns.d.ts +0 -6
- package/dist/core/constraints/checkers/no-excuse-patterns.d.ts.map +0 -1
- package/dist/core/constraints/checkers/no-excuse-patterns.js +0 -25
- package/dist/core/constraints/checkers/no-excuse-patterns.js.map +0 -1
- package/dist/core/constraints/checkers/no-fuzzy-completion-claim.d.ts +0 -6
- package/dist/core/constraints/checkers/no-fuzzy-completion-claim.d.ts.map +0 -1
- package/dist/core/constraints/checkers/no-fuzzy-completion-claim.js +0 -34
- package/dist/core/constraints/checkers/no-fuzzy-completion-claim.js.map +0 -1
- package/dist/core/constraints/checkers/no-performative-agreement.d.ts +0 -8
- package/dist/core/constraints/checkers/no-performative-agreement.d.ts.map +0 -1
- package/dist/core/constraints/checkers/no-performative-agreement.js +0 -23
- package/dist/core/constraints/checkers/no-performative-agreement.js.map +0 -1
- package/dist/core/constraints/checkers/no-simplification-without-approval.d.ts +0 -6
- package/dist/core/constraints/checkers/no-simplification-without-approval.d.ts.map +0 -1
- package/dist/core/constraints/checkers/no-simplification-without-approval.js +0 -30
- package/dist/core/constraints/checkers/no-simplification-without-approval.js.map +0 -1
- package/dist/core/constraints/checkers/test-coverage.d.ts +0 -6
- package/dist/core/constraints/checkers/test-coverage.d.ts.map +0 -1
- package/dist/core/constraints/checkers/test-coverage.js +0 -33
- package/dist/core/constraints/checkers/test-coverage.js.map +0 -1
- package/dist/core/constraints/checkers/yagni.d.ts +0 -8
- package/dist/core/constraints/checkers/yagni.d.ts.map +0 -1
- package/dist/core/constraints/checkers/yagni.js +0 -96
- package/dist/core/constraints/checkers/yagni.js.map +0 -1
- package/dist/core/constraints/definitions/tips.d.ts +0 -7
- package/dist/core/constraints/definitions/tips.d.ts.map +0 -1
- package/dist/core/constraints/definitions/tips.js +0 -62
- package/dist/core/constraints/definitions/tips.js.map +0 -1
- package/dist/evolution/auto-evolve.d.ts +0 -35
- package/dist/evolution/auto-evolve.d.ts.map +0 -1
- package/dist/evolution/auto-evolve.js +0 -64
- package/dist/evolution/auto-evolve.js.map +0 -1
- package/dist/evolution/index.d.ts +0 -2
- package/dist/evolution/index.d.ts.map +0 -1
- package/dist/evolution/index.js +0 -18
- package/dist/evolution/index.js.map +0 -1
- package/dist/monitoring/constraint-evolver.d.ts +0 -120
- package/dist/monitoring/constraint-evolver.d.ts.map +0 -1
- package/dist/monitoring/constraint-evolver.js +0 -410
- package/dist/monitoring/constraint-evolver.js.map +0 -1
- package/src/__tests__/constraint-evolver.test.ts +0 -405
- package/src/__tests__/doctor-evolver.test.ts +0 -381
- package/src/cli/commands/__tests__/flow.test.ts +0 -343
- package/src/cli/commands/flow.ts +0 -277
- package/src/constraints/__tests__/lifecycle-runner.test.ts +0 -246
- package/src/constraints/__tests__/registry.test.ts +0 -196
- package/src/constraints/lifecycle-runner.ts +0 -184
- package/src/constraints/registry.ts +0 -134
- package/src/constraints/types.ts +0 -59
- package/src/core/constraints/checkers/always-pass.ts +0 -22
- package/src/core/constraints/checkers/guideline-flags.ts +0 -30
- package/src/core/constraints/checkers/no-any-type.ts +0 -42
- package/src/core/constraints/checkers/no-excuse-patterns.ts +0 -24
- package/src/core/constraints/checkers/no-fuzzy-completion-claim.ts +0 -33
- package/src/core/constraints/checkers/no-performative-agreement.ts +0 -22
- package/src/core/constraints/checkers/no-simplification-without-approval.ts +0 -29
- package/src/core/constraints/checkers/test-coverage.ts +0 -35
- package/src/core/constraints/checkers/yagni.ts +0 -65
- package/src/core/constraints/definitions/tips.ts +0 -63
- package/src/evolution/CONTEXT.md +0 -22
- package/src/evolution/__tests__/auto-evolve.test.ts +0 -319
- package/src/evolution/auto-evolve.ts +0 -98
- package/src/evolution/index.ts +0 -1
- package/src/monitoring/constraint-evolver.ts +0 -501
|
@@ -1,16 +1,17 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* checker.ts 补充测试
|
|
3
3
|
*
|
|
4
|
-
* 目标:覆盖
|
|
5
|
-
*
|
|
4
|
+
* 目标:覆盖 checkCapabilitySync 完整流程、beforeExecution、findApplicableConstraints
|
|
5
|
+
* (ADR-0001:已退役 checker 的用例随约束数据模型 v2 移除)
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
-
import { describe, it, expect, beforeAll, afterAll } from '@jest/globals';
|
|
8
|
+
import { describe, it, expect, beforeAll, afterAll, jest } from '@jest/globals';
|
|
9
9
|
import {
|
|
10
10
|
ConstraintChecker,
|
|
11
11
|
checkConstraints,
|
|
12
12
|
checkBeforeExecution,
|
|
13
13
|
} from '../core/constraints/checker';
|
|
14
|
+
import { getConstraintCheck, type CheckEnv } from '../core/constraints/checkers';
|
|
14
15
|
import type { ConstraintContext } from '../types/constraint';
|
|
15
16
|
import * as fs from 'fs';
|
|
16
17
|
import * as path from 'path';
|
|
@@ -42,199 +43,6 @@ describe('ConstraintChecker - 补充覆盖', () => {
|
|
|
42
43
|
}
|
|
43
44
|
});
|
|
44
45
|
|
|
45
|
-
describe('checkTestCoverage', () => {
|
|
46
|
-
it('无 coverage 报告应该通过', async () => {
|
|
47
|
-
const context: ConstraintContext = {
|
|
48
|
-
operation: 'commit',
|
|
49
|
-
projectPath: tempDir,
|
|
50
|
-
};
|
|
51
|
-
|
|
52
|
-
const result = await checker.check(
|
|
53
|
-
{
|
|
54
|
-
id: 'test_coverage_required',
|
|
55
|
-
level: 'guideline',
|
|
56
|
-
rule: 'COVERAGE',
|
|
57
|
-
message: 'test',
|
|
58
|
-
trigger: 'commit',
|
|
59
|
-
enforcement: 'test',
|
|
60
|
-
},
|
|
61
|
-
context
|
|
62
|
-
);
|
|
63
|
-
|
|
64
|
-
expect(result.satisfied).toBe(true);
|
|
65
|
-
});
|
|
66
|
-
|
|
67
|
-
it('有 coverage-summary.json 且达标应该通过', async () => {
|
|
68
|
-
// 创建 coverage 目录和报告
|
|
69
|
-
const coverageDir = path.join(tempDir, 'coverage');
|
|
70
|
-
fs.mkdirSync(coverageDir, { recursive: true });
|
|
71
|
-
|
|
72
|
-
const summaryPath = path.join(coverageDir, 'coverage-summary.json');
|
|
73
|
-
fs.writeFileSync(
|
|
74
|
-
summaryPath,
|
|
75
|
-
JSON.stringify({
|
|
76
|
-
total: {
|
|
77
|
-
lines: { pct: 80 },
|
|
78
|
-
statements: { pct: 80 },
|
|
79
|
-
branches: { pct: 75 },
|
|
80
|
-
functions: { pct: 85 },
|
|
81
|
-
},
|
|
82
|
-
})
|
|
83
|
-
);
|
|
84
|
-
|
|
85
|
-
const context: ConstraintContext = {
|
|
86
|
-
operation: 'commit',
|
|
87
|
-
projectPath: tempDir,
|
|
88
|
-
};
|
|
89
|
-
|
|
90
|
-
const result = await checker.check(
|
|
91
|
-
{
|
|
92
|
-
id: 'test_coverage_required',
|
|
93
|
-
level: 'guideline',
|
|
94
|
-
rule: 'COVERAGE',
|
|
95
|
-
message: 'test',
|
|
96
|
-
trigger: 'commit',
|
|
97
|
-
enforcement: 'test',
|
|
98
|
-
},
|
|
99
|
-
context
|
|
100
|
-
);
|
|
101
|
-
|
|
102
|
-
expect(result.satisfied).toBe(true);
|
|
103
|
-
|
|
104
|
-
// 清理
|
|
105
|
-
fs.rmSync(coverageDir, { recursive: true, force: true });
|
|
106
|
-
});
|
|
107
|
-
|
|
108
|
-
it('覆盖率低于 50% 应该失败', async () => {
|
|
109
|
-
const coverageDir = path.join(tempDir, 'coverage');
|
|
110
|
-
fs.mkdirSync(coverageDir, { recursive: true });
|
|
111
|
-
|
|
112
|
-
const summaryPath = path.join(coverageDir, 'coverage-summary.json');
|
|
113
|
-
fs.writeFileSync(
|
|
114
|
-
summaryPath,
|
|
115
|
-
JSON.stringify({
|
|
116
|
-
total: {
|
|
117
|
-
lines: { pct: 30 },
|
|
118
|
-
},
|
|
119
|
-
})
|
|
120
|
-
);
|
|
121
|
-
|
|
122
|
-
const context: ConstraintContext = {
|
|
123
|
-
operation: 'commit',
|
|
124
|
-
projectPath: tempDir,
|
|
125
|
-
};
|
|
126
|
-
|
|
127
|
-
const result = await checker.check(
|
|
128
|
-
{
|
|
129
|
-
id: 'test_coverage_required',
|
|
130
|
-
level: 'guideline',
|
|
131
|
-
rule: 'COVERAGE',
|
|
132
|
-
message: 'test',
|
|
133
|
-
trigger: 'commit',
|
|
134
|
-
enforcement: 'test',
|
|
135
|
-
},
|
|
136
|
-
context
|
|
137
|
-
);
|
|
138
|
-
|
|
139
|
-
expect(result.satisfied).toBe(false);
|
|
140
|
-
|
|
141
|
-
fs.rmSync(coverageDir, { recursive: true, force: true });
|
|
142
|
-
});
|
|
143
|
-
|
|
144
|
-
it('coverage-summary.json 解析失败应该通过', async () => {
|
|
145
|
-
const coverageDir = path.join(tempDir, 'coverage');
|
|
146
|
-
fs.mkdirSync(coverageDir, { recursive: true });
|
|
147
|
-
|
|
148
|
-
const summaryPath = path.join(coverageDir, 'coverage-summary.json');
|
|
149
|
-
fs.writeFileSync(summaryPath, 'invalid json {');
|
|
150
|
-
|
|
151
|
-
const context: ConstraintContext = {
|
|
152
|
-
operation: 'commit',
|
|
153
|
-
projectPath: tempDir,
|
|
154
|
-
};
|
|
155
|
-
|
|
156
|
-
const result = await checker.check(
|
|
157
|
-
{
|
|
158
|
-
id: 'test_coverage_required',
|
|
159
|
-
level: 'guideline',
|
|
160
|
-
rule: 'COVERAGE',
|
|
161
|
-
message: 'test',
|
|
162
|
-
trigger: 'commit',
|
|
163
|
-
enforcement: 'test',
|
|
164
|
-
},
|
|
165
|
-
context
|
|
166
|
-
);
|
|
167
|
-
|
|
168
|
-
// 解析失败默认通过
|
|
169
|
-
expect(result.satisfied).toBe(true);
|
|
170
|
-
|
|
171
|
-
fs.rmSync(coverageDir, { recursive: true, force: true });
|
|
172
|
-
});
|
|
173
|
-
});
|
|
174
|
-
|
|
175
|
-
describe('checkNoSimplificationWithoutApproval', () => {
|
|
176
|
-
it('正常 diff 应该通过', async () => {
|
|
177
|
-
// clean state
|
|
178
|
-
execSync('git checkout .', { cwd: tempDir, stdio: 'pipe' });
|
|
179
|
-
|
|
180
|
-
const normalFile = path.join(tempDir, 'normal.ts');
|
|
181
|
-
fs.writeFileSync(normalFile, 'export const x = 1;');
|
|
182
|
-
|
|
183
|
-
const context: ConstraintContext = {
|
|
184
|
-
operation: 'commit',
|
|
185
|
-
projectPath: tempDir,
|
|
186
|
-
};
|
|
187
|
-
|
|
188
|
-
const result = await checker.check(
|
|
189
|
-
{
|
|
190
|
-
id: 'no_simplification_without_approval',
|
|
191
|
-
level: 'guideline',
|
|
192
|
-
rule: 'NO SIMPLIFICATION',
|
|
193
|
-
message: 'test',
|
|
194
|
-
trigger: 'commit',
|
|
195
|
-
enforcement: 'test',
|
|
196
|
-
},
|
|
197
|
-
context
|
|
198
|
-
);
|
|
199
|
-
|
|
200
|
-
expect(result.satisfied).toBe(true);
|
|
201
|
-
});
|
|
202
|
-
|
|
203
|
-
it('包含简化关键词应该失败', async () => {
|
|
204
|
-
// 先完成之前的提交
|
|
205
|
-
execSync('git add .', { cwd: tempDir });
|
|
206
|
-
execSync('git commit -m "cleanup" --allow-empty', { cwd: tempDir });
|
|
207
|
-
|
|
208
|
-
// 创建包含简化关键词的文件并 stage
|
|
209
|
-
const simplifyFile = path.join(tempDir, 'simplify-msg.ts');
|
|
210
|
-
fs.writeFileSync(simplifyFile, '// removed test for simplicity\nexport const y = 2;');
|
|
211
|
-
execSync('git add .', { cwd: tempDir });
|
|
212
|
-
|
|
213
|
-
const context: ConstraintContext = {
|
|
214
|
-
operation: 'commit',
|
|
215
|
-
projectPath: tempDir,
|
|
216
|
-
};
|
|
217
|
-
|
|
218
|
-
const result = await checker.check(
|
|
219
|
-
{
|
|
220
|
-
id: 'no_simplification_without_approval',
|
|
221
|
-
level: 'guideline',
|
|
222
|
-
rule: 'NO SIMPLIFICATION',
|
|
223
|
-
message: 'test',
|
|
224
|
-
trigger: 'commit',
|
|
225
|
-
enforcement: 'test',
|
|
226
|
-
},
|
|
227
|
-
context
|
|
228
|
-
);
|
|
229
|
-
|
|
230
|
-
expect(result.satisfied).toBe(false);
|
|
231
|
-
|
|
232
|
-
// cleanup - unstage and remove
|
|
233
|
-
execSync('git reset HEAD -- ' + simplifyFile, { cwd: tempDir, stdio: 'pipe' });
|
|
234
|
-
fs.rmSync(simplifyFile, { force: true });
|
|
235
|
-
});
|
|
236
|
-
});
|
|
237
|
-
|
|
238
46
|
describe('checkCapabilitySync', () => {
|
|
239
47
|
it('无代码变更应该通过', async () => {
|
|
240
48
|
// 清理 staged changes
|
|
@@ -248,6 +56,7 @@ describe('ConstraintChecker - 补充覆盖', () => {
|
|
|
248
56
|
const result = await checker.check(
|
|
249
57
|
{
|
|
250
58
|
id: 'capability_sync',
|
|
59
|
+
kind: 'check',
|
|
251
60
|
level: 'guideline',
|
|
252
61
|
rule: 'CAPABILITY SYNC',
|
|
253
62
|
message: 'test',
|
|
@@ -278,6 +87,7 @@ describe('ConstraintChecker - 补充覆盖', () => {
|
|
|
278
87
|
const result = await checker.check(
|
|
279
88
|
{
|
|
280
89
|
id: 'capability_sync',
|
|
90
|
+
kind: 'check',
|
|
281
91
|
level: 'guideline',
|
|
282
92
|
rule: 'CAPABILITY SYNC',
|
|
283
93
|
message: 'test',
|
|
@@ -311,6 +121,7 @@ describe('ConstraintChecker - 补充覆盖', () => {
|
|
|
311
121
|
const result = await checker.check(
|
|
312
122
|
{
|
|
313
123
|
id: 'capability_sync',
|
|
124
|
+
kind: 'check',
|
|
314
125
|
level: 'guideline',
|
|
315
126
|
rule: 'CAPABILITY SYNC',
|
|
316
127
|
message: 'test',
|
|
@@ -327,13 +138,13 @@ describe('ConstraintChecker - 补充覆盖', () => {
|
|
|
327
138
|
execSync('git reset HEAD', { cwd: tempDir, stdio: 'pipe' });
|
|
328
139
|
});
|
|
329
140
|
|
|
330
|
-
it('有代码变更但无 CAPABILITIES.md
|
|
141
|
+
it('有代码变更但无 CAPABILITIES.md 应该跳过(ADR-0001 存在性探测)', async () => {
|
|
331
142
|
// 确保 CAPABILITIES.md 不存在
|
|
332
143
|
const capFile = path.join(tempDir, 'CAPABILITIES.md');
|
|
333
144
|
if (fs.existsSync(capFile)) {
|
|
334
145
|
fs.rmSync(capFile, { force: true });
|
|
335
146
|
}
|
|
336
|
-
|
|
147
|
+
|
|
337
148
|
// 创建新的代码文件并 stage
|
|
338
149
|
const newCodeFile = path.join(tempDir, 'newfeature.ts');
|
|
339
150
|
fs.writeFileSync(newCodeFile, 'export function newfeature() {}');
|
|
@@ -347,6 +158,7 @@ describe('ConstraintChecker - 补充覆盖', () => {
|
|
|
347
158
|
const result = await checker.check(
|
|
348
159
|
{
|
|
349
160
|
id: 'capability_sync',
|
|
161
|
+
kind: 'check',
|
|
350
162
|
level: 'guideline',
|
|
351
163
|
rule: 'CAPABILITY SYNC',
|
|
352
164
|
message: 'test',
|
|
@@ -356,8 +168,9 @@ describe('ConstraintChecker - 补充覆盖', () => {
|
|
|
356
168
|
context
|
|
357
169
|
);
|
|
358
170
|
|
|
359
|
-
//
|
|
360
|
-
expect(result.
|
|
171
|
+
// 未采用 CAPABILITIES.md 约定 → skip(不计 pass/fail,不阻断)
|
|
172
|
+
expect(result.skipped).toBe(true);
|
|
173
|
+
expect(result.satisfied).toBe(true);
|
|
361
174
|
|
|
362
175
|
// Cleanup
|
|
363
176
|
execSync('git reset HEAD', { cwd: tempDir, stdio: 'pipe' });
|
|
@@ -390,6 +203,7 @@ describe('ConstraintChecker - 补充覆盖', () => {
|
|
|
390
203
|
checker.check(
|
|
391
204
|
{
|
|
392
205
|
id: 'capability_sync',
|
|
206
|
+
kind: 'check',
|
|
393
207
|
level: 'guideline',
|
|
394
208
|
rule: 'CAPABILITY SYNC',
|
|
395
209
|
message: 'test',
|
|
@@ -494,17 +308,178 @@ describe('ConstraintChecker - 补充覆盖', () => {
|
|
|
494
308
|
});
|
|
495
309
|
});
|
|
496
310
|
|
|
311
|
+
describe('checkCapabilitySync 遗留问题回归(2026-08-08 评估稿第四节)', () => {
|
|
312
|
+
const capCheck = getConstraintCheck('capability_sync')!;
|
|
313
|
+
|
|
314
|
+
const setupDir = (name: string, capContent: string, files: string[] = []): string => {
|
|
315
|
+
const dir = path.join(tempDir, name);
|
|
316
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
317
|
+
fs.writeFileSync(path.join(dir, 'CAPABILITIES.md'), capContent);
|
|
318
|
+
for (const f of files) {
|
|
319
|
+
const fp = path.join(dir, f);
|
|
320
|
+
fs.mkdirSync(path.dirname(fp), { recursive: true });
|
|
321
|
+
fs.writeFileSync(fp, 'export const x = 1;');
|
|
322
|
+
}
|
|
323
|
+
return dir;
|
|
324
|
+
};
|
|
325
|
+
|
|
326
|
+
/** 直接构造 CheckEnv:stub stagedDiffNames / srcScan,不走真实 git */
|
|
327
|
+
const makeEnv = (
|
|
328
|
+
projectPath: string,
|
|
329
|
+
staged: string[],
|
|
330
|
+
scan: Record<string, string[]> = {}
|
|
331
|
+
): CheckEnv => ({
|
|
332
|
+
context: { operation: 'commit', projectPath },
|
|
333
|
+
projectPath,
|
|
334
|
+
stagedDiff: async () => '',
|
|
335
|
+
stagedDiffNames: async () => staged.join('\n'),
|
|
336
|
+
srcScan: (root: string) => scan[root] ?? [],
|
|
337
|
+
});
|
|
338
|
+
|
|
339
|
+
const tableWith = (row: string): string =>
|
|
340
|
+
'# Capabilities\n\n| 模块 | 文件 | 说明 |\n|------|------|------|\n' + row + '\n';
|
|
341
|
+
|
|
342
|
+
const EMPTY_TABLE = '# Capabilities\n\n| 模块 | 文件 | 说明 |\n|------|------|------|\n';
|
|
343
|
+
|
|
344
|
+
it('问题1:step1 应要求每个变更文件都被覆盖(every 而非 some)', async () => {
|
|
345
|
+
// 文档登记 src/foo.ts;同时 staged 已登记的 src/foo.ts 与未登记的 scripts/bar.ts
|
|
346
|
+
// some() 下只要有 foo.ts 命中即整体通过,bar.ts 漏网
|
|
347
|
+
const dir = setupDir('legacy-step1-some', tableWith('| foo | src/foo.ts | foo |'));
|
|
348
|
+
try {
|
|
349
|
+
expect(await capCheck.evaluate(makeEnv(dir, ['src/foo.ts', 'scripts/bar.ts']))).toBe(false);
|
|
350
|
+
} finally {
|
|
351
|
+
fs.rmSync(dir, { recursive: true, force: true });
|
|
352
|
+
}
|
|
353
|
+
});
|
|
354
|
+
|
|
355
|
+
it('问题3a:step1 不得因 endsWith 造成后缀碰撞(xfoo.ts 不应被 foo.ts 覆盖)', async () => {
|
|
356
|
+
const dir = setupDir('legacy-step1-endswith', tableWith('| foo | foo.ts | foo |'));
|
|
357
|
+
try {
|
|
358
|
+
expect(await capCheck.evaluate(makeEnv(dir, ['web/xfoo.ts']))).toBe(false);
|
|
359
|
+
} finally {
|
|
360
|
+
fs.rmSync(dir, { recursive: true, force: true });
|
|
361
|
+
}
|
|
362
|
+
});
|
|
363
|
+
|
|
364
|
+
it('问题3b:step1 不得因 includes 造成子串误配(docs/src/foo.tsx 不应被 src/foo.ts 覆盖)', async () => {
|
|
365
|
+
const dir = setupDir('legacy-step1-includes', tableWith('| foo | src/foo.ts | foo |'));
|
|
366
|
+
try {
|
|
367
|
+
expect(await capCheck.evaluate(makeEnv(dir, ['docs/src/foo.tsx']))).toBe(false);
|
|
368
|
+
} finally {
|
|
369
|
+
fs.rmSync(dir, { recursive: true, force: true });
|
|
370
|
+
}
|
|
371
|
+
});
|
|
372
|
+
|
|
373
|
+
it('兼容:basename 条目仍可按路径边界后缀匹配(src/foo.ts 被 foo.ts 覆盖)', async () => {
|
|
374
|
+
const dir = setupDir('legacy-step1-suffix-ok', tableWith('| foo | foo.ts | foo |'));
|
|
375
|
+
try {
|
|
376
|
+
expect(await capCheck.evaluate(makeEnv(dir, ['src/foo.ts']))).toBe(true);
|
|
377
|
+
} finally {
|
|
378
|
+
fs.rmSync(dir, { recursive: true, force: true });
|
|
379
|
+
}
|
|
380
|
+
});
|
|
381
|
+
|
|
382
|
+
it('问题2:空表格 + 有 staged 变更时不得直接放行', async () => {
|
|
383
|
+
const dir = setupDir('legacy-empty-table-diff', EMPTY_TABLE);
|
|
384
|
+
try {
|
|
385
|
+
expect(await capCheck.evaluate(makeEnv(dir, ['src/foo.ts']))).toBe(false);
|
|
386
|
+
} finally {
|
|
387
|
+
fs.rmSync(dir, { recursive: true, force: true });
|
|
388
|
+
}
|
|
389
|
+
});
|
|
390
|
+
|
|
391
|
+
it('问题2:空表格 + 源码根有文件时 step2 不得直接放行', async () => {
|
|
392
|
+
const dir = setupDir('legacy-empty-table-scan', EMPTY_TABLE, ['src/foo.ts']);
|
|
393
|
+
try {
|
|
394
|
+
expect(await capCheck.evaluate(makeEnv(dir, [], { src: ['src/foo.ts'] }))).toBe(false);
|
|
395
|
+
} finally {
|
|
396
|
+
fs.rmSync(dir, { recursive: true, force: true });
|
|
397
|
+
}
|
|
398
|
+
});
|
|
399
|
+
|
|
400
|
+
it('问题2 兼容:清单格式(计数行)无文件表,仍直接放行', async () => {
|
|
401
|
+
const dir = setupDir(
|
|
402
|
+
'legacy-listing-format',
|
|
403
|
+
'# Harness Capabilities\n\n## CLI Commands (25)\ncheck, validate\n'
|
|
404
|
+
);
|
|
405
|
+
try {
|
|
406
|
+
expect(await capCheck.evaluate(makeEnv(dir, ['src/foo.ts']))).toBe(true);
|
|
407
|
+
} finally {
|
|
408
|
+
fs.rmSync(dir, { recursive: true, force: true });
|
|
409
|
+
}
|
|
410
|
+
});
|
|
411
|
+
|
|
412
|
+
it('问题4:检查异常默认放行时应输出 warn 而非静默吞错', async () => {
|
|
413
|
+
const dir = setupDir('legacy-failopen', tableWith('| foo | src/foo.ts | foo |'));
|
|
414
|
+
const warnSpy = jest.spyOn(console, 'warn').mockImplementation(() => {});
|
|
415
|
+
try {
|
|
416
|
+
const env: CheckEnv = {
|
|
417
|
+
...makeEnv(dir, []),
|
|
418
|
+
stagedDiffNames: async () => {
|
|
419
|
+
throw new Error('git boom');
|
|
420
|
+
},
|
|
421
|
+
};
|
|
422
|
+
// fail-open 语义保留:异常时仍放行
|
|
423
|
+
expect(await capCheck.evaluate(env)).toBe(true);
|
|
424
|
+
expect(warnSpy).toHaveBeenCalled();
|
|
425
|
+
} finally {
|
|
426
|
+
warnSpy.mockRestore();
|
|
427
|
+
fs.rmSync(dir, { recursive: true, force: true });
|
|
428
|
+
}
|
|
429
|
+
});
|
|
430
|
+
|
|
431
|
+
it('step2(file 模式)与 step1 语义一致:basename 条目可按路径边界后缀覆盖', async () => {
|
|
432
|
+
// 文档第二列写 basename foo.ts;step1 认可它(边界后缀),
|
|
433
|
+
// step2 裸精确匹配却永远不命中 srcScan 返回的 src/foo.ts → 永远红
|
|
434
|
+
const dir = setupDir('legacy-step2-basename', tableWith('| foo | foo.ts | foo |'), [
|
|
435
|
+
'src/foo.ts',
|
|
436
|
+
]);
|
|
437
|
+
try {
|
|
438
|
+
expect(await capCheck.evaluate(makeEnv(dir, [], { src: ['src/foo.ts'] }))).toBe(true);
|
|
439
|
+
} finally {
|
|
440
|
+
fs.rmSync(dir, { recursive: true, force: true });
|
|
441
|
+
}
|
|
442
|
+
});
|
|
443
|
+
|
|
444
|
+
it('step2(file 模式):不同路径的同后缀文件不得被误覆盖(lib/foo.ts 不覆盖 src/foo.ts)', async () => {
|
|
445
|
+
const dir = setupDir('legacy-step2-boundary', tableWith('| foo | lib/foo.ts | foo |'), [
|
|
446
|
+
'src/foo.ts',
|
|
447
|
+
]);
|
|
448
|
+
try {
|
|
449
|
+
expect(await capCheck.evaluate(makeEnv(dir, [], { src: ['src/foo.ts'] }))).toBe(false);
|
|
450
|
+
} finally {
|
|
451
|
+
fs.rmSync(dir, { recursive: true, force: true });
|
|
452
|
+
}
|
|
453
|
+
});
|
|
454
|
+
|
|
455
|
+
it('step2(module 模式)文件条目同样按路径边界后缀覆盖', async () => {
|
|
456
|
+
const dir = setupDir('legacy-step2-module-suffix', tableWith('| foo | foo.ts | foo |'), [
|
|
457
|
+
'src/foo.ts',
|
|
458
|
+
]);
|
|
459
|
+
fs.mkdirSync(path.join(dir, '.harness'), { recursive: true });
|
|
460
|
+
fs.writeFileSync(
|
|
461
|
+
path.join(dir, '.harness', 'config.yml'),
|
|
462
|
+
'governance:\n capabilities:\n mode: module\n'
|
|
463
|
+
);
|
|
464
|
+
try {
|
|
465
|
+
expect(await capCheck.evaluate(makeEnv(dir, [], { src: ['src/foo.ts'] }))).toBe(true);
|
|
466
|
+
} finally {
|
|
467
|
+
fs.rmSync(dir, { recursive: true, force: true });
|
|
468
|
+
}
|
|
469
|
+
});
|
|
470
|
+
});
|
|
471
|
+
|
|
497
472
|
describe('findApplicableConstraints', () => {
|
|
498
473
|
it('应该过滤出匹配 trigger 的约束', () => {
|
|
499
474
|
const context: ConstraintContext = {
|
|
500
|
-
operation: '
|
|
475
|
+
operation: 'code_implementation',
|
|
501
476
|
};
|
|
502
477
|
|
|
503
478
|
const result = checker.findApplicableConstraints(context);
|
|
504
479
|
|
|
505
480
|
expect(result.ironLaws.length).toBeGreaterThan(0);
|
|
506
481
|
expect(
|
|
507
|
-
result.ironLaws.some((c: any) => c.id === '
|
|
482
|
+
result.ironLaws.some((c: any) => c.id === 'no_completion_without_verification')
|
|
508
483
|
).toBe(true);
|
|
509
484
|
});
|
|
510
485
|
|
|
@@ -604,6 +579,7 @@ describe('ConstraintChecker - 补充覆盖', () => {
|
|
|
604
579
|
const result = await checker.check(
|
|
605
580
|
{
|
|
606
581
|
id: 'test_severity_iron',
|
|
582
|
+
kind: 'prompt',
|
|
607
583
|
level: 'iron_law',
|
|
608
584
|
rule: 'TEST',
|
|
609
585
|
message: 'test',
|
|
@@ -624,6 +600,7 @@ describe('ConstraintChecker - 补充覆盖', () => {
|
|
|
624
600
|
const result = await checker.check(
|
|
625
601
|
{
|
|
626
602
|
id: 'test_severity_guideline',
|
|
603
|
+
kind: 'prompt',
|
|
627
604
|
level: 'guideline',
|
|
628
605
|
rule: 'TEST',
|
|
629
606
|
message: 'test',
|
|
@@ -644,6 +621,7 @@ describe('ConstraintChecker - 补充覆盖', () => {
|
|
|
644
621
|
const result = await checker.check(
|
|
645
622
|
{
|
|
646
623
|
id: 'test_severity_tip',
|
|
624
|
+
kind: 'prompt',
|
|
647
625
|
level: 'tip',
|
|
648
626
|
rule: 'TEST',
|
|
649
627
|
message: 'test',
|
|
@@ -666,9 +644,10 @@ describe('ConstraintChecker - 补充覆盖', () => {
|
|
|
666
644
|
|
|
667
645
|
const result = await checker.check(
|
|
668
646
|
{
|
|
669
|
-
id: '
|
|
647
|
+
id: 'incremental_progress',
|
|
648
|
+
kind: 'check',
|
|
670
649
|
level: 'guideline',
|
|
671
|
-
rule: '
|
|
650
|
+
rule: 'ONE TASK PER SESSION',
|
|
672
651
|
message: 'test',
|
|
673
652
|
trigger: 'code_implementation',
|
|
674
653
|
enforcement: 'test',
|
|
@@ -690,9 +669,10 @@ describe('ConstraintChecker - 补充覆盖', () => {
|
|
|
690
669
|
|
|
691
670
|
const result = await checker.check(
|
|
692
671
|
{
|
|
693
|
-
id: '
|
|
672
|
+
id: 'incremental_progress',
|
|
673
|
+
kind: 'check',
|
|
694
674
|
level: 'guideline',
|
|
695
|
-
rule: '
|
|
675
|
+
rule: 'ONE TASK PER SESSION',
|
|
696
676
|
message: 'test',
|
|
697
677
|
trigger: 'code_implementation',
|
|
698
678
|
enforcement: 'test',
|
|
@@ -701,20 +681,23 @@ describe('ConstraintChecker - 补充覆盖', () => {
|
|
|
701
681
|
context
|
|
702
682
|
);
|
|
703
683
|
|
|
704
|
-
|
|
684
|
+
// hasSingleTask 未接线(undefined)→ skip(ADR-0001:flag 未接线不评估,不误报违规)
|
|
685
|
+
expect(result.skipped).toBe(true);
|
|
686
|
+
expect(result.satisfied).toBe(true);
|
|
705
687
|
});
|
|
706
688
|
|
|
707
689
|
it('iron_law 不应该检查例外条件', async () => {
|
|
708
690
|
const context: ConstraintContext = {
|
|
709
691
|
operation: 'code_implementation',
|
|
710
|
-
|
|
692
|
+
isSimpleTypo: true,
|
|
711
693
|
};
|
|
712
694
|
|
|
713
695
|
const result = await checker.check(
|
|
714
696
|
{
|
|
715
|
-
id: '
|
|
697
|
+
id: 'incremental_progress',
|
|
698
|
+
kind: 'check',
|
|
716
699
|
level: 'iron_law',
|
|
717
|
-
rule: '
|
|
700
|
+
rule: 'ONE TASK PER SESSION',
|
|
718
701
|
message: 'test',
|
|
719
702
|
trigger: 'code_implementation',
|
|
720
703
|
enforcement: 'test',
|
|
@@ -723,20 +706,23 @@ describe('ConstraintChecker - 补充覆盖', () => {
|
|
|
723
706
|
context
|
|
724
707
|
);
|
|
725
708
|
|
|
726
|
-
|
|
709
|
+
// iron_law 不适用例外;hasSingleTask 未接线(undefined)→ skip(ADR-0001)
|
|
710
|
+
expect(result.skipped).toBe(true);
|
|
711
|
+
expect(result.satisfied).toBe(true);
|
|
727
712
|
});
|
|
728
713
|
|
|
729
714
|
it('无 exceptions 字段应该正常检查', async () => {
|
|
730
715
|
const context: ConstraintContext = {
|
|
731
716
|
operation: 'code_implementation',
|
|
732
|
-
|
|
717
|
+
hasVerificationEvidence: true,
|
|
733
718
|
};
|
|
734
719
|
|
|
735
720
|
const result = await checker.check(
|
|
736
721
|
{
|
|
737
|
-
id: '
|
|
738
|
-
|
|
739
|
-
|
|
722
|
+
id: 'no_completion_without_verification',
|
|
723
|
+
kind: 'check',
|
|
724
|
+
level: 'iron_law',
|
|
725
|
+
rule: 'NO COMPLETION WITHOUT VERIFICATION',
|
|
740
726
|
message: 'test',
|
|
741
727
|
trigger: 'code_implementation',
|
|
742
728
|
enforcement: 'test',
|
|
@@ -763,6 +749,7 @@ describe('ConstraintChecker - 补充覆盖', () => {
|
|
|
763
749
|
const result = await checker.check(
|
|
764
750
|
{
|
|
765
751
|
id: 'no_bypass_checkpoint',
|
|
752
|
+
kind: 'check',
|
|
766
753
|
level: 'iron_law',
|
|
767
754
|
rule: 'NO BYPASS',
|
|
768
755
|
message: 'test',
|
|
@@ -792,6 +779,7 @@ describe('ConstraintChecker - 补充覆盖', () => {
|
|
|
792
779
|
const result = await checker.check(
|
|
793
780
|
{
|
|
794
781
|
id: 'no_bypass_checkpoint',
|
|
782
|
+
kind: 'check',
|
|
795
783
|
level: 'iron_law',
|
|
796
784
|
rule: 'NO BYPASS',
|
|
797
785
|
message: 'test',
|
|
@@ -808,120 +796,6 @@ describe('ConstraintChecker - 补充覆盖', () => {
|
|
|
808
796
|
});
|
|
809
797
|
});
|
|
810
798
|
|
|
811
|
-
describe('checkNoAnyType 文件检查', () => {
|
|
812
|
-
it('.ts 文件包含 : any 应该失败', async () => {
|
|
813
|
-
const anyFile = path.join(tempDir, 'any-test.ts');
|
|
814
|
-
fs.writeFileSync(anyFile, 'const x: any = {};');
|
|
815
|
-
|
|
816
|
-
const context: ConstraintContext = {
|
|
817
|
-
operation: 'code_implementation',
|
|
818
|
-
projectPath: tempDir,
|
|
819
|
-
changedFiles: [anyFile],
|
|
820
|
-
};
|
|
821
|
-
|
|
822
|
-
const result = await checker.check(
|
|
823
|
-
{
|
|
824
|
-
id: 'no_any_type',
|
|
825
|
-
level: 'guideline',
|
|
826
|
-
rule: 'NO ANY',
|
|
827
|
-
message: 'test',
|
|
828
|
-
trigger: 'code_implementation',
|
|
829
|
-
enforcement: 'test',
|
|
830
|
-
},
|
|
831
|
-
context
|
|
832
|
-
);
|
|
833
|
-
|
|
834
|
-
expect(result.satisfied).toBe(false);
|
|
835
|
-
|
|
836
|
-
fs.unlinkSync(anyFile);
|
|
837
|
-
});
|
|
838
|
-
|
|
839
|
-
it('.ts 文件注释中的 : any 应该被忽略', async () => {
|
|
840
|
-
const commentFile = path.join(tempDir, 'comment-any.ts');
|
|
841
|
-
fs.writeFileSync(commentFile, '// const x: any = {};\nconst y: string = "ok";');
|
|
842
|
-
|
|
843
|
-
const context: ConstraintContext = {
|
|
844
|
-
operation: 'code_implementation',
|
|
845
|
-
projectPath: tempDir,
|
|
846
|
-
changedFiles: [commentFile],
|
|
847
|
-
};
|
|
848
|
-
|
|
849
|
-
const result = await checker.check(
|
|
850
|
-
{
|
|
851
|
-
id: 'no_any_type',
|
|
852
|
-
level: 'guideline',
|
|
853
|
-
rule: 'NO ANY',
|
|
854
|
-
message: 'test',
|
|
855
|
-
trigger: 'code_implementation',
|
|
856
|
-
enforcement: 'test',
|
|
857
|
-
},
|
|
858
|
-
context
|
|
859
|
-
);
|
|
860
|
-
|
|
861
|
-
expect(result.satisfied).toBe(true);
|
|
862
|
-
|
|
863
|
-
fs.unlinkSync(commentFile);
|
|
864
|
-
});
|
|
865
|
-
|
|
866
|
-
it('非 .ts 文件应该跳过检查', async () => {
|
|
867
|
-
const jsFile = path.join(tempDir, 'any-test.js');
|
|
868
|
-
fs.writeFileSync(jsFile, 'const x = {};');
|
|
869
|
-
|
|
870
|
-
const context: ConstraintContext = {
|
|
871
|
-
operation: 'code_implementation',
|
|
872
|
-
projectPath: tempDir,
|
|
873
|
-
changedFiles: [jsFile],
|
|
874
|
-
};
|
|
875
|
-
|
|
876
|
-
const result = await checker.check(
|
|
877
|
-
{
|
|
878
|
-
id: 'no_any_type',
|
|
879
|
-
level: 'guideline',
|
|
880
|
-
rule: 'NO ANY',
|
|
881
|
-
message: 'test',
|
|
882
|
-
trigger: 'code_implementation',
|
|
883
|
-
enforcement: 'test',
|
|
884
|
-
},
|
|
885
|
-
context
|
|
886
|
-
);
|
|
887
|
-
|
|
888
|
-
expect(result.satisfied).toBe(true);
|
|
889
|
-
|
|
890
|
-
fs.unlinkSync(jsFile);
|
|
891
|
-
});
|
|
892
|
-
});
|
|
893
|
-
|
|
894
|
-
describe('checkTestCoverage coverage-final.json', () => {
|
|
895
|
-
it('只有 coverage-final.json 应该通过', async () => {
|
|
896
|
-
const coverageDir = path.join(tempDir, 'coverage');
|
|
897
|
-
fs.mkdirSync(coverageDir, { recursive: true });
|
|
898
|
-
|
|
899
|
-
const finalPath = path.join(coverageDir, 'coverage-final.json');
|
|
900
|
-
fs.writeFileSync(finalPath, JSON.stringify({}));
|
|
901
|
-
|
|
902
|
-
const context: ConstraintContext = {
|
|
903
|
-
operation: 'commit',
|
|
904
|
-
projectPath: tempDir,
|
|
905
|
-
};
|
|
906
|
-
|
|
907
|
-
const result = await checker.check(
|
|
908
|
-
{
|
|
909
|
-
id: 'test_coverage_required',
|
|
910
|
-
level: 'guideline',
|
|
911
|
-
rule: 'COVERAGE',
|
|
912
|
-
message: 'test',
|
|
913
|
-
trigger: 'commit',
|
|
914
|
-
enforcement: 'test',
|
|
915
|
-
},
|
|
916
|
-
context
|
|
917
|
-
);
|
|
918
|
-
|
|
919
|
-
expect(result.satisfied).toBe(true);
|
|
920
|
-
|
|
921
|
-
fs.rmSync(coverageDir, { recursive: true, force: true });
|
|
922
|
-
});
|
|
923
|
-
});
|
|
924
|
-
|
|
925
799
|
describe('checkConstraint 快捷函数', () => {
|
|
926
800
|
it('未知约束应该返回不满足', async () => {
|
|
927
801
|
const { checkConstraint } = await import('../core/constraints/checker');
|
|
@@ -940,11 +814,11 @@ describe('ConstraintChecker - 补充覆盖', () => {
|
|
|
940
814
|
|
|
941
815
|
const context: ConstraintContext = {
|
|
942
816
|
operation: 'code_implementation',
|
|
943
|
-
|
|
817
|
+
hasVerificationEvidence: true,
|
|
944
818
|
};
|
|
945
819
|
|
|
946
|
-
const result = await checkConstraint('
|
|
947
|
-
expect(result.id).toBe('
|
|
820
|
+
const result = await checkConstraint('no_completion_without_verification', context);
|
|
821
|
+
expect(result.id).toBe('no_completion_without_verification');
|
|
948
822
|
expect(result.satisfied).toBe(true);
|
|
949
823
|
});
|
|
950
824
|
});
|
|
@@ -975,6 +849,7 @@ describe('ConstraintChecker - 补充覆盖', () => {
|
|
|
975
849
|
const result = await checker.check(
|
|
976
850
|
{
|
|
977
851
|
id: 'no_test_simplification',
|
|
852
|
+
kind: 'check',
|
|
978
853
|
level: 'iron_law',
|
|
979
854
|
rule: 'NO SIMPLIFY',
|
|
980
855
|
message: 'test',
|
|
@@ -1004,6 +879,7 @@ describe('ConstraintChecker - 补充覆盖', () => {
|
|
|
1004
879
|
const result = await checker.check(
|
|
1005
880
|
{
|
|
1006
881
|
id: 'context_doc_sync',
|
|
882
|
+
kind: 'check',
|
|
1007
883
|
level: 'guideline',
|
|
1008
884
|
rule: 'CONTEXT DOC SYNC',
|
|
1009
885
|
message: 'test',
|
|
@@ -1048,6 +924,7 @@ describe('ConstraintChecker - 补充覆盖', () => {
|
|
|
1048
924
|
const result = await checker.check(
|
|
1049
925
|
{
|
|
1050
926
|
id: 'context_doc_sync',
|
|
927
|
+
kind: 'check',
|
|
1051
928
|
level: 'guideline',
|
|
1052
929
|
rule: 'CONTEXT DOC SYNC',
|
|
1053
930
|
message: 'test',
|
|
@@ -1089,6 +966,7 @@ describe('ConstraintChecker - 补充覆盖', () => {
|
|
|
1089
966
|
const result = await checker.check(
|
|
1090
967
|
{
|
|
1091
968
|
id: 'context_doc_sync',
|
|
969
|
+
kind: 'check',
|
|
1092
970
|
level: 'guideline',
|
|
1093
971
|
rule: 'CONTEXT DOC SYNC',
|
|
1094
972
|
message: 'test',
|
|
@@ -1127,6 +1005,7 @@ describe('ConstraintChecker - 补充覆盖', () => {
|
|
|
1127
1005
|
const result = await checker.check(
|
|
1128
1006
|
{
|
|
1129
1007
|
id: 'context_doc_sync',
|
|
1008
|
+
kind: 'check',
|
|
1130
1009
|
level: 'guideline',
|
|
1131
1010
|
rule: 'CONTEXT DOC SYNC',
|
|
1132
1011
|
message: 'test',
|
|
@@ -1155,6 +1034,7 @@ describe('ConstraintChecker - 补充覆盖', () => {
|
|
|
1155
1034
|
const result = await checker.check(
|
|
1156
1035
|
{
|
|
1157
1036
|
id: 'docs_freshness',
|
|
1037
|
+
kind: 'check',
|
|
1158
1038
|
level: 'guideline',
|
|
1159
1039
|
rule: 'DOCS FRESHNESS',
|
|
1160
1040
|
message: 'test',
|
|
@@ -1182,6 +1062,7 @@ describe('ConstraintChecker - 补充覆盖', () => {
|
|
|
1182
1062
|
const result = await checker.check(
|
|
1183
1063
|
{
|
|
1184
1064
|
id: 'docs_freshness',
|
|
1065
|
+
kind: 'check',
|
|
1185
1066
|
level: 'guideline',
|
|
1186
1067
|
rule: 'DOCS FRESHNESS',
|
|
1187
1068
|
message: 'test',
|
|
@@ -1218,6 +1099,7 @@ describe('ConstraintChecker - 补充覆盖', () => {
|
|
|
1218
1099
|
const result = await checker.check(
|
|
1219
1100
|
{
|
|
1220
1101
|
id: 'docs_freshness',
|
|
1102
|
+
kind: 'check',
|
|
1221
1103
|
level: 'guideline',
|
|
1222
1104
|
rule: 'DOCS FRESHNESS',
|
|
1223
1105
|
message: 'test',
|
|
@@ -1255,6 +1137,7 @@ describe('ConstraintChecker - 补充覆盖', () => {
|
|
|
1255
1137
|
const result = await checker.check(
|
|
1256
1138
|
{
|
|
1257
1139
|
id: 'docs_freshness',
|
|
1140
|
+
kind: 'check',
|
|
1258
1141
|
level: 'guideline',
|
|
1259
1142
|
rule: 'DOCS FRESHNESS',
|
|
1260
1143
|
message: 'test',
|
|
@@ -1291,6 +1174,7 @@ describe('ConstraintChecker - 补充覆盖', () => {
|
|
|
1291
1174
|
const result = await checker.check(
|
|
1292
1175
|
{
|
|
1293
1176
|
id: 'docs_freshness',
|
|
1177
|
+
kind: 'check',
|
|
1294
1178
|
level: 'guideline',
|
|
1295
1179
|
rule: 'DOCS FRESHNESS',
|
|
1296
1180
|
message: 'test',
|
|
@@ -1324,6 +1208,7 @@ describe('ConstraintChecker - 补充覆盖', () => {
|
|
|
1324
1208
|
const result = await checker.check(
|
|
1325
1209
|
{
|
|
1326
1210
|
id: 'docs_freshness',
|
|
1211
|
+
kind: 'check',
|
|
1327
1212
|
level: 'guideline',
|
|
1328
1213
|
rule: 'DOCS FRESHNESS',
|
|
1329
1214
|
message: 'test',
|
|
@@ -1356,6 +1241,7 @@ describe('ConstraintChecker - 补充覆盖', () => {
|
|
|
1356
1241
|
const result = await checker.check(
|
|
1357
1242
|
{
|
|
1358
1243
|
id: 'docs_freshness',
|
|
1244
|
+
kind: 'check',
|
|
1359
1245
|
level: 'guideline',
|
|
1360
1246
|
rule: 'DOCS FRESHNESS',
|
|
1361
1247
|
message: 'test',
|
|
@@ -1391,6 +1277,7 @@ describe('ConstraintChecker - 补充覆盖', () => {
|
|
|
1391
1277
|
const result = await checker.check(
|
|
1392
1278
|
{
|
|
1393
1279
|
id: 'docs_freshness',
|
|
1280
|
+
kind: 'check',
|
|
1394
1281
|
level: 'guideline',
|
|
1395
1282
|
rule: 'DOCS FRESHNESS',
|
|
1396
1283
|
message: 'test',
|
|
@@ -1423,6 +1310,7 @@ describe('ConstraintChecker - 补充覆盖', () => {
|
|
|
1423
1310
|
const result = await checker.check(
|
|
1424
1311
|
{
|
|
1425
1312
|
id: 'docs_freshness',
|
|
1313
|
+
kind: 'check',
|
|
1426
1314
|
level: 'guideline',
|
|
1427
1315
|
rule: 'DOCS FRESHNESS',
|
|
1428
1316
|
message: 'test',
|
|
@@ -1455,6 +1343,7 @@ describe('ConstraintChecker - 补充覆盖', () => {
|
|
|
1455
1343
|
const result = await checker.check(
|
|
1456
1344
|
{
|
|
1457
1345
|
id: 'docs_freshness',
|
|
1346
|
+
kind: 'check',
|
|
1458
1347
|
level: 'guideline',
|
|
1459
1348
|
rule: 'DOCS FRESHNESS',
|
|
1460
1349
|
message: 'test',
|
|
@@ -1481,6 +1370,7 @@ describe('ConstraintChecker - 补充覆盖', () => {
|
|
|
1481
1370
|
const result = await checker.check(
|
|
1482
1371
|
{
|
|
1483
1372
|
id: 'docs_freshness',
|
|
1373
|
+
kind: 'check',
|
|
1484
1374
|
level: 'guideline',
|
|
1485
1375
|
rule: 'DOCS FRESHNESS',
|
|
1486
1376
|
message: 'test',
|
|
@@ -1549,6 +1439,7 @@ describe('ConstraintChecker - 补充覆盖', () => {
|
|
|
1549
1439
|
ironLaws: {
|
|
1550
1440
|
test_only: {
|
|
1551
1441
|
id: 'test_only',
|
|
1442
|
+
kind: 'prompt' as const,
|
|
1552
1443
|
level: 'iron_law' as const,
|
|
1553
1444
|
rule: 'TEST',
|
|
1554
1445
|
message: 'test',
|
|
@@ -1582,6 +1473,7 @@ describe('ConstraintChecker - 补充覆盖', () => {
|
|
|
1582
1473
|
ironLaws: {
|
|
1583
1474
|
rule_a: {
|
|
1584
1475
|
id: 'rule_a',
|
|
1476
|
+
kind: 'prompt' as const,
|
|
1585
1477
|
level: 'iron_law' as const,
|
|
1586
1478
|
rule: 'A',
|
|
1587
1479
|
message: 'project A',
|
|
@@ -1599,6 +1491,7 @@ describe('ConstraintChecker - 补充覆盖', () => {
|
|
|
1599
1491
|
ironLaws: {
|
|
1600
1492
|
rule_b: {
|
|
1601
1493
|
id: 'rule_b',
|
|
1494
|
+
kind: 'prompt' as const,
|
|
1602
1495
|
level: 'iron_law' as const,
|
|
1603
1496
|
rule: 'B',
|
|
1604
1497
|
message: 'project B',
|
|
@@ -1648,7 +1541,7 @@ describe('ConstraintChecker - 补充覆盖', () => {
|
|
|
1648
1541
|
const defaultKeys = Object.keys(defaultConstraints.ironLaws);
|
|
1649
1542
|
|
|
1650
1543
|
const customConfig = {
|
|
1651
|
-
ironLaws: { compat_test: { id: 'compat_test', level: 'iron_law' as const, rule: 'C', message: 'compat', trigger: 'file_modification', enforcement: 'test' } },
|
|
1544
|
+
ironLaws: { compat_test: { id: 'compat_test', kind: 'prompt' as const, level: 'iron_law' as const, rule: 'C', message: 'compat', trigger: 'file_modification', enforcement: 'test' } },
|
|
1652
1545
|
guidelines: {},
|
|
1653
1546
|
tips: {},
|
|
1654
1547
|
disabled: [] as string[],
|
|
@@ -1672,6 +1565,7 @@ describe('ConstraintChecker - 补充覆盖', () => {
|
|
|
1672
1565
|
ironLaws: {
|
|
1673
1566
|
custom_rule: {
|
|
1674
1567
|
id: 'custom_rule',
|
|
1568
|
+
kind: 'prompt' as const,
|
|
1675
1569
|
level: 'iron_law' as const,
|
|
1676
1570
|
rule: 'CUSTOM',
|
|
1677
1571
|
message: 'custom',
|
|
@@ -1710,6 +1604,7 @@ describe('ConstraintChecker - 补充覆盖', () => {
|
|
|
1710
1604
|
const result = await checker.check(
|
|
1711
1605
|
{
|
|
1712
1606
|
id: 'docs_freshness',
|
|
1607
|
+
kind: 'check',
|
|
1713
1608
|
level: 'guideline',
|
|
1714
1609
|
rule: 'DOCS FRESHNESS',
|
|
1715
1610
|
message: 'test',
|