@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
|
@@ -74,7 +74,7 @@ describe('ConstraintDoctor', () => {
|
|
|
74
74
|
it('应该返回诊断结果', async () => {
|
|
75
75
|
const anomaly: TraceAnomaly = {
|
|
76
76
|
type: 'rising_fail_rate',
|
|
77
|
-
constraintId: '
|
|
77
|
+
constraintId: 'no_completion_without_verification',
|
|
78
78
|
level: 'iron_law',
|
|
79
79
|
message: '失败率上升',
|
|
80
80
|
data: {
|
|
@@ -178,7 +178,7 @@ describe('ConstraintDoctor', () => {
|
|
|
178
178
|
it('应该诊断 low_pass_rate', async () => {
|
|
179
179
|
const anomaly: TraceAnomaly = {
|
|
180
180
|
type: 'low_pass_rate',
|
|
181
|
-
constraintId: '
|
|
181
|
+
constraintId: 'capability_sync',
|
|
182
182
|
level: 'guideline',
|
|
183
183
|
message: '通过率过低',
|
|
184
184
|
data: { currentRate: 0.1, threshold: 0.5 },
|
|
@@ -211,7 +211,7 @@ describe('ConstraintDoctor', () => {
|
|
|
211
211
|
it('rising_fail_rate 应该设置 severity 为 high 当 iron_law', async () => {
|
|
212
212
|
const anomaly: TraceAnomaly = {
|
|
213
213
|
type: 'rising_fail_rate',
|
|
214
|
-
constraintId: '
|
|
214
|
+
constraintId: 'no_completion_without_verification',
|
|
215
215
|
level: 'iron_law',
|
|
216
216
|
message: '失败率上升',
|
|
217
217
|
data: { currentRate: 0.7, threshold: 0.3, trend: 'rising' },
|
|
@@ -237,7 +237,7 @@ describe('ConstraintDoctor', () => {
|
|
|
237
237
|
},
|
|
238
238
|
{
|
|
239
239
|
type: 'low_pass_rate',
|
|
240
|
-
constraintId: '
|
|
240
|
+
constraintId: 'capability_sync',
|
|
241
241
|
level: 'guideline',
|
|
242
242
|
message: '通过率过低',
|
|
243
243
|
data: { currentRate: 0.1, threshold: 0.5 },
|
|
@@ -249,7 +249,7 @@ describe('ConstraintDoctor', () => {
|
|
|
249
249
|
|
|
250
250
|
expect(results.length).toBe(2);
|
|
251
251
|
expect(results[0].constraintId).toBe('no_fix_without_root_cause');
|
|
252
|
-
expect(results[1].constraintId).toBe('
|
|
252
|
+
expect(results[1].constraintId).toBe('capability_sync');
|
|
253
253
|
});
|
|
254
254
|
});
|
|
255
255
|
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ConstraintDoctor 测试
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import * as fs from 'fs';
|
|
6
|
+
import * as path from 'path';
|
|
7
|
+
import * as os from 'os';
|
|
8
|
+
import { TraceCollector } from '../monitoring/traces';
|
|
9
|
+
import { TraceAnalyzer } from '../monitoring/trace-analyzer';
|
|
10
|
+
import { ConstraintDoctor, createDoctor } from '../monitoring/constraint-doctor';
|
|
11
|
+
import type { ExecutionTrace, TraceAnomaly } from '../types/trace';
|
|
12
|
+
|
|
13
|
+
describe('ConstraintDoctor', () => {
|
|
14
|
+
let tempDir: string;
|
|
15
|
+
let collector: TraceCollector;
|
|
16
|
+
let analyzer: TraceAnalyzer;
|
|
17
|
+
let doctor: ConstraintDoctor;
|
|
18
|
+
|
|
19
|
+
beforeEach(() => {
|
|
20
|
+
tempDir = path.join(os.tmpdir(), `harness-traces-${Date.now()}`);
|
|
21
|
+
fs.mkdirSync(tempDir, { recursive: true });
|
|
22
|
+
collector = new TraceCollector({
|
|
23
|
+
traceFile: path.join(tempDir, 'execution.log'),
|
|
24
|
+
enabled: true,
|
|
25
|
+
});
|
|
26
|
+
analyzer = new TraceAnalyzer(collector);
|
|
27
|
+
doctor = createDoctor();
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
afterEach(() => {
|
|
31
|
+
if (fs.existsSync(tempDir)) {
|
|
32
|
+
fs.rmSync(tempDir, { recursive: true });
|
|
33
|
+
}
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
test('should diagnose high bypass rate anomaly', async () => {
|
|
37
|
+
// 创建异常
|
|
38
|
+
const anomaly: TraceAnomaly = {
|
|
39
|
+
type: 'high_bypass_rate',
|
|
40
|
+
constraintId: 'test_constraint',
|
|
41
|
+
level: 'guideline',
|
|
42
|
+
message: 'Bypass rate 40%',
|
|
43
|
+
data: {
|
|
44
|
+
currentRate: 0.4,
|
|
45
|
+
threshold: 0.3,
|
|
46
|
+
},
|
|
47
|
+
detectedAt: Date.now(),
|
|
48
|
+
suggestedAction: 'diagnose',
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
// 添加 traces
|
|
52
|
+
const traces: ExecutionTrace[] = [];
|
|
53
|
+
for (let i = 0; i < 10; i++) {
|
|
54
|
+
traces.push({
|
|
55
|
+
constraintId: 'test_constraint',
|
|
56
|
+
level: 'guideline',
|
|
57
|
+
timestamp: Date.now() - i * 60 * 1000,
|
|
58
|
+
result: i < 6 ? 'bypassed' : 'pass',
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
doctor.setData(traces);
|
|
62
|
+
|
|
63
|
+
// 诊断
|
|
64
|
+
const diagnosis = await doctor.diagnose(anomaly);
|
|
65
|
+
|
|
66
|
+
expect(diagnosis.constraintId).toBe('test_constraint');
|
|
67
|
+
expect(diagnosis.needsChange).toBe(true);
|
|
68
|
+
expect(diagnosis.rootCause.primary).toContain('过于严格');
|
|
69
|
+
expect(diagnosis.recommendations.length).toBeGreaterThan(0);
|
|
70
|
+
expect(diagnosis.recommendations[0].type).toBe('add_exception');
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
test('should diagnose rising fail rate anomaly', async () => {
|
|
74
|
+
const anomaly: TraceAnomaly = {
|
|
75
|
+
type: 'rising_fail_rate',
|
|
76
|
+
constraintId: 'another_constraint',
|
|
77
|
+
level: 'iron_law',
|
|
78
|
+
message: 'Fail rate rising',
|
|
79
|
+
data: {
|
|
80
|
+
currentRate: 0.6,
|
|
81
|
+
threshold: 0.5,
|
|
82
|
+
trend: 'rising',
|
|
83
|
+
},
|
|
84
|
+
detectedAt: Date.now(),
|
|
85
|
+
suggestedAction: 'diagnose',
|
|
86
|
+
};
|
|
87
|
+
|
|
88
|
+
doctor.setData([]);
|
|
89
|
+
const diagnosis = await doctor.diagnose(anomaly);
|
|
90
|
+
|
|
91
|
+
expect(diagnosis.needsChange).toBe(true);
|
|
92
|
+
expect(diagnosis.impact.severity).toBe('high'); // Iron law
|
|
93
|
+
expect(diagnosis.urgency).toBe('medium'); // Iron law 强制 medium+
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
test('should detect exception overuse', async () => {
|
|
97
|
+
const anomaly: TraceAnomaly = {
|
|
98
|
+
type: 'exception_overuse',
|
|
99
|
+
constraintId: 'constraint_with_exceptions',
|
|
100
|
+
level: 'guideline',
|
|
101
|
+
message: 'Exception rate 50%',
|
|
102
|
+
data: {
|
|
103
|
+
currentRate: 0.5,
|
|
104
|
+
threshold: 0.4,
|
|
105
|
+
},
|
|
106
|
+
detectedAt: Date.now(),
|
|
107
|
+
suggestedAction: 'add_exception',
|
|
108
|
+
};
|
|
109
|
+
|
|
110
|
+
doctor.setData([]);
|
|
111
|
+
const diagnosis = await doctor.diagnose(anomaly);
|
|
112
|
+
|
|
113
|
+
expect(diagnosis.rootCause.primary).toContain('过度使用');
|
|
114
|
+
expect(diagnosis.recommendations[0].type).toBe('modify_constraint');
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
test('should generate diagnosis report', async () => {
|
|
118
|
+
const anomaly: TraceAnomaly = {
|
|
119
|
+
type: 'high_bypass_rate',
|
|
120
|
+
constraintId: 'test',
|
|
121
|
+
level: 'guideline',
|
|
122
|
+
message: 'Test',
|
|
123
|
+
data: {
|
|
124
|
+
currentRate: 0.5,
|
|
125
|
+
threshold: 0.3,
|
|
126
|
+
},
|
|
127
|
+
detectedAt: Date.now(),
|
|
128
|
+
suggestedAction: 'diagnose',
|
|
129
|
+
};
|
|
130
|
+
|
|
131
|
+
doctor.setData([]);
|
|
132
|
+
const diagnosis = await doctor.diagnose(anomaly);
|
|
133
|
+
const report = doctor.generateReport(diagnosis);
|
|
134
|
+
|
|
135
|
+
expect(report).toContain('# Diagnosis Report');
|
|
136
|
+
expect(report).toContain('Root Cause');
|
|
137
|
+
expect(report).toContain('Recommendations');
|
|
138
|
+
expect(report).toContain('test');
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
test('should save and load diagnosis', async () => {
|
|
142
|
+
const anomaly: TraceAnomaly = {
|
|
143
|
+
type: 'high_bypass_rate',
|
|
144
|
+
constraintId: 'save_test',
|
|
145
|
+
level: 'guideline',
|
|
146
|
+
message: 'Test',
|
|
147
|
+
data: { currentRate: 0.5, threshold: 0.3 },
|
|
148
|
+
detectedAt: Date.now(),
|
|
149
|
+
suggestedAction: 'diagnose',
|
|
150
|
+
};
|
|
151
|
+
|
|
152
|
+
doctor.setData([]);
|
|
153
|
+
const diagnosis = await doctor.diagnose(anomaly);
|
|
154
|
+
|
|
155
|
+
const outputPath = path.join(tempDir, 'diagnosis.json');
|
|
156
|
+
doctor.saveDiagnosis(diagnosis, outputPath);
|
|
157
|
+
|
|
158
|
+
expect(fs.existsSync(outputPath)).toBe(true);
|
|
159
|
+
|
|
160
|
+
const loaded = doctor.loadDiagnosis(outputPath);
|
|
161
|
+
expect(loaded.constraintId).toBe('save_test');
|
|
162
|
+
expect(loaded.needsChange).toBe(true);
|
|
163
|
+
});
|
|
164
|
+
});
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 生效约束集(getEffectiveConstraints / lintEffectiveConfig)测试
|
|
3
|
+
*
|
|
4
|
+
* 合并矩阵:preset 裁剪 / config.yml 禁用 / custom 追加 / scenes 过滤 / 未知 id 诊断
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { describe, it, expect, beforeEach, afterEach } from '@jest/globals';
|
|
8
|
+
import * as fs from 'fs';
|
|
9
|
+
import * as path from 'path';
|
|
10
|
+
import {
|
|
11
|
+
getEffectiveConstraints,
|
|
12
|
+
lintEffectiveConfig,
|
|
13
|
+
} from '../core/effective-constraints';
|
|
14
|
+
import { IRON_LAWS, GUIDELINES, PROMPTS } from '../core/constraints/definitions';
|
|
15
|
+
|
|
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);
|
|
22
|
+
|
|
23
|
+
describe('getEffectiveConstraints / lintEffectiveConfig', () => {
|
|
24
|
+
let tempDir: string;
|
|
25
|
+
let counter = 0;
|
|
26
|
+
|
|
27
|
+
const setupProject = (configYaml?: string, customYaml?: string): string => {
|
|
28
|
+
const dir = path.join(tempDir, `p${counter++}`);
|
|
29
|
+
fs.mkdirSync(path.join(dir, '.harness'), { recursive: true });
|
|
30
|
+
if (configYaml !== undefined) {
|
|
31
|
+
fs.writeFileSync(path.join(dir, '.harness', 'config.yml'), configYaml);
|
|
32
|
+
}
|
|
33
|
+
if (customYaml !== undefined) {
|
|
34
|
+
fs.writeFileSync(path.join(dir, '.harness', 'custom-constraints.yml'), customYaml);
|
|
35
|
+
}
|
|
36
|
+
return dir;
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
beforeEach(() => {
|
|
40
|
+
tempDir = fs.mkdtempSync(path.join(process.cwd(), 'temp-test-effective-'));
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
afterEach(() => {
|
|
44
|
+
fs.rmSync(tempDir, { recursive: true, force: true });
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
describe('合并矩阵', () => {
|
|
48
|
+
it('无配置项目:内置全集减去场景专属 prompt(scenes 缺省为空)', () => {
|
|
49
|
+
const dir = setupProject();
|
|
50
|
+
const constraints = getEffectiveConstraints(dir);
|
|
51
|
+
const ids = constraints.map(c => c.id);
|
|
52
|
+
|
|
53
|
+
expect(constraints.length).toBe(BUILTIN_TOTAL - SCENE_PROMPTS.length);
|
|
54
|
+
for (const id of SCENE_PROMPTS) {
|
|
55
|
+
expect(ids).not.toContain(id);
|
|
56
|
+
}
|
|
57
|
+
// check + prompt 均带 kind
|
|
58
|
+
expect(constraints.every(c => c.kind === 'check' || c.kind === 'prompt')).toBe(true);
|
|
59
|
+
expect(constraints.filter(c => c.kind === 'check')).toHaveLength(
|
|
60
|
+
Object.keys(IRON_LAWS).length + Object.keys(GUIDELINES).length
|
|
61
|
+
);
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
it('scenes 命中:对应场景 prompt 进入生效集,其余场景 prompt 仍排除', () => {
|
|
65
|
+
const dir = setupProject(`scenes:\n - agent-skill\n`);
|
|
66
|
+
const ids = getEffectiveConstraints(dir).map(c => c.id);
|
|
67
|
+
|
|
68
|
+
expect(ids).toContain('no_skill_without_test');
|
|
69
|
+
expect(ids).not.toContain('no_model_for_deterministic');
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
it('config.yml 禁用:check 与 prompt 条目均从生效集移除', () => {
|
|
73
|
+
const dir = setupProject(`
|
|
74
|
+
constraints:
|
|
75
|
+
no_bypass_checkpoint:
|
|
76
|
+
enabled: false
|
|
77
|
+
no_fuzzy_completion_claim:
|
|
78
|
+
enabled: false
|
|
79
|
+
`);
|
|
80
|
+
const constraints = getEffectiveConstraints(dir);
|
|
81
|
+
const ids = constraints.map(c => c.id);
|
|
82
|
+
|
|
83
|
+
expect(ids).not.toContain('no_bypass_checkpoint');
|
|
84
|
+
expect(ids).not.toContain('no_fuzzy_completion_claim');
|
|
85
|
+
expect(constraints.length).toBe(BUILTIN_TOTAL - SCENE_PROMPTS.length - 2);
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
it('preset: relaxed 裁剪生效集', () => {
|
|
89
|
+
const dir = setupProject(`preset: relaxed\n`);
|
|
90
|
+
const constraints = getEffectiveConstraints(dir);
|
|
91
|
+
const ids = constraints.map(c => c.id);
|
|
92
|
+
|
|
93
|
+
expect(ids).toEqual(
|
|
94
|
+
expect.arrayContaining([
|
|
95
|
+
'no_completion_without_verification',
|
|
96
|
+
'incremental_progress',
|
|
97
|
+
'no_implementation_without_requirement',
|
|
98
|
+
'no_bypass_checkpoint',
|
|
99
|
+
'no_hardcoded_credentials',
|
|
100
|
+
])
|
|
101
|
+
);
|
|
102
|
+
expect(constraints).toHaveLength(5);
|
|
103
|
+
expect(constraints.every(c => c.kind === 'check')).toBe(true);
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
it('custom-constraints.yml 追加自定义约束(kind=prompt)', () => {
|
|
107
|
+
const dir = setupProject(undefined, `
|
|
108
|
+
custom_constraints:
|
|
109
|
+
my_project_rule:
|
|
110
|
+
level: guideline
|
|
111
|
+
rule: MY RULE
|
|
112
|
+
message: 项目自定义
|
|
113
|
+
trigger: code_implementation
|
|
114
|
+
`);
|
|
115
|
+
const constraints = getEffectiveConstraints(dir);
|
|
116
|
+
const custom = constraints.find(c => c.id === 'my_project_rule');
|
|
117
|
+
|
|
118
|
+
expect(custom).toBeDefined();
|
|
119
|
+
expect(custom!.kind).toBe('prompt');
|
|
120
|
+
expect(custom!.level).toBe('guideline');
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
it('禁用未知 id(已移除约束的残留配置):不报错,lint 可诊断', () => {
|
|
124
|
+
const dir = setupProject(`
|
|
125
|
+
constraints:
|
|
126
|
+
removed_legacy_constraint:
|
|
127
|
+
enabled: false
|
|
128
|
+
`);
|
|
129
|
+
// 生效集不受影响、不抛错
|
|
130
|
+
const constraints = getEffectiveConstraints(dir);
|
|
131
|
+
expect(constraints.length).toBe(BUILTIN_TOTAL - SCENE_PROMPTS.length);
|
|
132
|
+
|
|
133
|
+
const lint = lintEffectiveConfig(dir);
|
|
134
|
+
expect(lint.unknownIds).toContain('removed_legacy_constraint');
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
it('lintEffectiveConfig 报告 scenes 与被场景过滤的 prompt', () => {
|
|
138
|
+
const dir = setupProject(`scenes:\n - llm-app\n`);
|
|
139
|
+
const lint = lintEffectiveConfig(dir);
|
|
140
|
+
|
|
141
|
+
expect(lint.scenes).toEqual(['llm-app']);
|
|
142
|
+
expect(lint.sceneExcluded).toContain('no_skill_without_test');
|
|
143
|
+
expect(lint.sceneExcluded).not.toContain('no_model_for_deterministic');
|
|
144
|
+
expect(lint.unknownIds).toEqual([]);
|
|
145
|
+
});
|
|
146
|
+
});
|
|
147
|
+
});
|
|
@@ -38,8 +38,8 @@ custom_constraints:
|
|
|
38
38
|
loader.load();
|
|
39
39
|
const merged = loader.mergeConstraints();
|
|
40
40
|
|
|
41
|
-
// 注意:no_fix_without_root_cause 是
|
|
42
|
-
const constraint = merged.
|
|
41
|
+
// 注意:no_fix_without_root_cause 是 prompt 层约束(ADR-0001)
|
|
42
|
+
const constraint = merged.prompts!['no_fix_without_root_cause'];
|
|
43
43
|
expect(constraint).toBeDefined();
|
|
44
44
|
// 应该包含内置例外
|
|
45
45
|
expect(constraint?.exceptions).toContain('simple_typo');
|
|
@@ -135,7 +135,7 @@ custom_constraints:
|
|
|
135
135
|
loader.load();
|
|
136
136
|
const merged = loader.mergeConstraints();
|
|
137
137
|
|
|
138
|
-
const constraint = merged.
|
|
138
|
+
const constraint = merged.prompts!['no_fix_without_root_cause'];
|
|
139
139
|
expect(constraint).toBeDefined();
|
|
140
140
|
// 应该保留内置约束的 rule
|
|
141
141
|
expect(constraint?.rule).toBeTruthy();
|
|
@@ -1,55 +1,110 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* 约束系统测试(ADR-0001:kind 二元模型,25 条清单构成 + 注册表闭环)
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
5
|
import { describe, it, expect } from '@jest/globals';
|
|
6
|
-
import {
|
|
7
|
-
IRON_LAWS,
|
|
8
|
-
GUIDELINES,
|
|
9
|
-
|
|
10
|
-
getAllConstraints,
|
|
11
|
-
findConstraintsByTrigger,
|
|
12
|
-
getConstraint
|
|
6
|
+
import {
|
|
7
|
+
IRON_LAWS,
|
|
8
|
+
GUIDELINES,
|
|
9
|
+
PROMPTS,
|
|
10
|
+
getAllConstraints,
|
|
11
|
+
findConstraintsByTrigger,
|
|
12
|
+
getConstraint
|
|
13
13
|
} from '../core/constraints/definitions';
|
|
14
14
|
import { constraintChecker } from '../core/constraints/checker';
|
|
15
|
+
import { getConstraintCheck, registeredCheckCount } from '../core/constraints/checkers';
|
|
15
16
|
import type { ConstraintContext } from '../types/constraint';
|
|
16
17
|
|
|
17
18
|
describe('Constraint System', () => {
|
|
18
|
-
describe('
|
|
19
|
-
it('
|
|
20
|
-
|
|
19
|
+
describe('清单构成(ADR-0001:42 → 25)', () => {
|
|
20
|
+
it('getAllConstraints 返回 25 条:9 check + 16 prompt', () => {
|
|
21
|
+
const all = getAllConstraints();
|
|
22
|
+
expect(all).toHaveLength(25);
|
|
23
|
+
expect(all.filter(c => c.kind === 'check')).toHaveLength(9);
|
|
24
|
+
expect(all.filter(c => c.kind === 'prompt')).toHaveLength(16);
|
|
21
25
|
});
|
|
22
26
|
|
|
23
|
-
it('
|
|
24
|
-
Object.
|
|
25
|
-
|
|
27
|
+
it('check 层 = 5 iron + 4 guideline', () => {
|
|
28
|
+
expect(Object.keys(IRON_LAWS)).toHaveLength(5);
|
|
29
|
+
expect(Object.keys(GUIDELINES)).toHaveLength(4);
|
|
30
|
+
Object.values(IRON_LAWS).forEach(c => {
|
|
31
|
+
expect(c.kind).toBe('check');
|
|
32
|
+
expect(c.level).toBe('iron_law');
|
|
33
|
+
});
|
|
34
|
+
Object.values(GUIDELINES).forEach(c => {
|
|
35
|
+
expect(c.kind).toBe('check');
|
|
36
|
+
expect(c.level).toBe('guideline');
|
|
26
37
|
});
|
|
27
38
|
});
|
|
28
|
-
});
|
|
29
39
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
40
|
+
it('prompt 层 16 条,level 统一为 prompt', () => {
|
|
41
|
+
expect(Object.keys(PROMPTS)).toHaveLength(16);
|
|
42
|
+
Object.values(PROMPTS).forEach(c => {
|
|
43
|
+
expect(c.kind).toBe('prompt');
|
|
44
|
+
expect(c.level).toBe('prompt');
|
|
45
|
+
});
|
|
33
46
|
});
|
|
34
47
|
|
|
35
|
-
it('
|
|
36
|
-
const
|
|
37
|
-
|
|
48
|
+
it('被删除的 8 条不再存在', () => {
|
|
49
|
+
const retired = [
|
|
50
|
+
'no_any_type',
|
|
51
|
+
'test_coverage_required',
|
|
52
|
+
'no_coverage_decrease',
|
|
53
|
+
'readme_required',
|
|
54
|
+
'doc_required_for_public_api',
|
|
55
|
+
'two_stage_review_required',
|
|
56
|
+
'prefer_worktree',
|
|
57
|
+
'read_before_write',
|
|
58
|
+
];
|
|
59
|
+
for (const id of retired) {
|
|
60
|
+
expect(getConstraint(id)).toBeUndefined();
|
|
61
|
+
}
|
|
38
62
|
});
|
|
39
|
-
});
|
|
40
63
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
64
|
+
it('被吸收的成员不再单独存在', () => {
|
|
65
|
+
const absorbed = [
|
|
66
|
+
'no_self_approval',
|
|
67
|
+
'no_claim_without_evidence',
|
|
68
|
+
'no_excuse_patterns',
|
|
69
|
+
'no_fallback_without_root_cause',
|
|
70
|
+
'analysis_verification_gate',
|
|
71
|
+
'diagnosis_to_fix_gate',
|
|
72
|
+
'no_creation_without_reuse_check',
|
|
73
|
+
'yagni_check',
|
|
74
|
+
'no_implementation_without_requirement_review',
|
|
75
|
+
];
|
|
76
|
+
for (const id of absorbed) {
|
|
77
|
+
expect(getConstraint(id)).toBeUndefined();
|
|
78
|
+
}
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
it('场景标签:no_skill_without_test / no_model_for_deterministic', () => {
|
|
82
|
+
expect(PROMPTS['no_skill_without_test'].appliesTo).toEqual(['agent-skill']);
|
|
83
|
+
expect(PROMPTS['no_model_for_deterministic'].appliesTo).toEqual(['llm-app']);
|
|
44
84
|
});
|
|
45
85
|
});
|
|
46
86
|
|
|
47
|
-
describe('
|
|
48
|
-
it('
|
|
49
|
-
const
|
|
50
|
-
|
|
87
|
+
describe('注册表闭环', () => {
|
|
88
|
+
it('每条 kind=check 约束都有已注册 checker', () => {
|
|
89
|
+
const checks = getAllConstraints().filter(c => c.kind === 'check');
|
|
90
|
+
for (const c of checks) {
|
|
91
|
+
expect(getConstraintCheck(c.id)).toBeDefined();
|
|
92
|
+
}
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
it('注册表数量与 check 约束数量一致(无孤儿 checker)', () => {
|
|
96
|
+
const checkCount = getAllConstraints().filter(c => c.kind === 'check').length;
|
|
97
|
+
expect(registeredCheckCount()).toBe(checkCount);
|
|
51
98
|
});
|
|
52
99
|
|
|
100
|
+
it('prompt 约束不注册 checker', () => {
|
|
101
|
+
for (const c of Object.values(PROMPTS)) {
|
|
102
|
+
expect(getConstraintCheck(c.id)).toBeUndefined();
|
|
103
|
+
}
|
|
104
|
+
});
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
describe('Helper Functions', () => {
|
|
53
108
|
it('should find constraints by trigger', () => {
|
|
54
109
|
const constraints = findConstraintsByTrigger('code_implementation');
|
|
55
110
|
expect(constraints.length).toBeGreaterThan(0);
|
|
@@ -58,7 +113,8 @@ describe('Constraint System', () => {
|
|
|
58
113
|
it('should get single constraint by id', () => {
|
|
59
114
|
const constraint = getConstraint('no_fix_without_root_cause');
|
|
60
115
|
expect(constraint).toBeDefined();
|
|
61
|
-
expect(constraint?.
|
|
116
|
+
expect(constraint?.kind).toBe('prompt');
|
|
117
|
+
expect(constraint?.level).toBe('prompt');
|
|
62
118
|
});
|
|
63
119
|
|
|
64
120
|
it('should return undefined for unknown constraint', () => {
|
|
@@ -80,54 +136,59 @@ describe('Constraint Checker', () => {
|
|
|
80
136
|
};
|
|
81
137
|
|
|
82
138
|
const result = constraintChecker.findApplicableConstraints(context);
|
|
83
|
-
expect(result.ironLaws.length + result.guidelines.length
|
|
139
|
+
expect(result.ironLaws.length + result.guidelines.length).toBeGreaterThan(0);
|
|
84
140
|
});
|
|
85
141
|
|
|
86
|
-
it('
|
|
142
|
+
it('prompt 约束 check() 短路 satisfied,不查注册表', async () => {
|
|
87
143
|
const context: ConstraintContext = {
|
|
88
144
|
operation: 'code_implementation',
|
|
89
|
-
hasRootCauseInvestigation: false,
|
|
90
145
|
};
|
|
91
146
|
|
|
92
|
-
const result = await constraintChecker.check(
|
|
93
|
-
expect(result.satisfied).toBe(
|
|
147
|
+
const result = await constraintChecker.check(PROMPTS['no_fix_without_root_cause'], context);
|
|
148
|
+
expect(result.satisfied).toBe(true);
|
|
94
149
|
});
|
|
95
150
|
|
|
96
|
-
it('
|
|
151
|
+
it('kind=check 但未注册 checker 的约束应抛错(不许静默 pass)', async () => {
|
|
97
152
|
const context: ConstraintContext = {
|
|
98
153
|
operation: 'code_implementation',
|
|
99
|
-
hasRootCauseInvestigation: true,
|
|
100
154
|
};
|
|
101
155
|
|
|
102
|
-
|
|
103
|
-
|
|
156
|
+
await expect(
|
|
157
|
+
constraintChecker.check(
|
|
158
|
+
{
|
|
159
|
+
id: 'ghost_check_constraint',
|
|
160
|
+
kind: 'check',
|
|
161
|
+
level: 'guideline',
|
|
162
|
+
rule: 'GHOST',
|
|
163
|
+
message: 'test',
|
|
164
|
+
trigger: 'code_implementation',
|
|
165
|
+
enforcement: 'test',
|
|
166
|
+
},
|
|
167
|
+
context
|
|
168
|
+
)
|
|
169
|
+
).rejects.toThrow(/未注册 checker/);
|
|
104
170
|
});
|
|
105
171
|
|
|
106
172
|
it('should check all constraints', async () => {
|
|
107
173
|
const context: ConstraintContext = {
|
|
108
174
|
operation: 'code_implementation',
|
|
109
175
|
hasRequirement: true,
|
|
110
|
-
hasWorktree: true,
|
|
111
|
-
hasTest: true,
|
|
112
176
|
hasVerificationEvidence: true,
|
|
113
|
-
taskDescription: 'Test task',
|
|
114
177
|
hasSingleTask: true,
|
|
115
|
-
hasRequirementReview: true,
|
|
116
|
-
hasTwoStageReview: true,
|
|
117
|
-
completionClaimText: '142 tests passed, coverage 87%',
|
|
118
178
|
};
|
|
119
179
|
|
|
120
180
|
const result = await constraintChecker.checkConstraints(context);
|
|
121
|
-
expect(result.ironLaws.length + result.guidelines.length
|
|
181
|
+
expect(result.ironLaws.length + result.guidelines.length).toBeGreaterThan(0);
|
|
122
182
|
});
|
|
123
183
|
|
|
124
|
-
it('should
|
|
184
|
+
it('should skip incremental_progress when hasSingleTask is undefined(未接线不评估)', async () => {
|
|
125
185
|
const context: ConstraintContext = {
|
|
126
186
|
operation: 'code_implementation',
|
|
127
187
|
};
|
|
128
188
|
|
|
129
189
|
const result = await constraintChecker.check(IRON_LAWS['incremental_progress'], context);
|
|
130
|
-
expect(result.
|
|
190
|
+
expect(result.skipped).toBe(true);
|
|
191
|
+
expect(result.satisfied).toBe(true);
|
|
131
192
|
});
|
|
132
193
|
|
|
133
194
|
it('should pass incremental_progress when hasSingleTask is true', async () => {
|
|
@@ -140,51 +201,14 @@ describe('Constraint Checker', () => {
|
|
|
140
201
|
expect(result.satisfied).toBe(true);
|
|
141
202
|
});
|
|
142
203
|
|
|
143
|
-
it('should
|
|
144
|
-
const context: ConstraintContext = {
|
|
145
|
-
operation: 'api_change',
|
|
146
|
-
};
|
|
147
|
-
|
|
148
|
-
const result = await constraintChecker.check(IRON_LAWS['verify_external_capability'], context);
|
|
149
|
-
expect(result.satisfied).toBe(false);
|
|
150
|
-
});
|
|
151
|
-
|
|
152
|
-
it('should pass verify_external_capability when hasExternalCapabilityVerification is true', async () => {
|
|
153
|
-
const context: ConstraintContext = {
|
|
154
|
-
operation: 'api_change',
|
|
155
|
-
hasExternalCapabilityVerification: true,
|
|
156
|
-
};
|
|
157
|
-
|
|
158
|
-
const result = await constraintChecker.check(IRON_LAWS['verify_external_capability'], context);
|
|
159
|
-
expect(result.satisfied).toBe(true);
|
|
160
|
-
});
|
|
161
|
-
|
|
162
|
-
it('should fail no_implementation_without_requirement_review when hasRequirementReview is undefined', async () => {
|
|
163
|
-
const context: ConstraintContext = {
|
|
164
|
-
operation: 'code_implementation',
|
|
165
|
-
};
|
|
166
|
-
|
|
167
|
-
const result = await constraintChecker.check(IRON_LAWS['no_implementation_without_requirement_review'], context);
|
|
168
|
-
expect(result.satisfied).toBe(false);
|
|
169
|
-
});
|
|
170
|
-
|
|
171
|
-
it('should pass no_implementation_without_requirement_review when hasRequirementReview is true', async () => {
|
|
172
|
-
const context: ConstraintContext = {
|
|
173
|
-
operation: 'code_implementation',
|
|
174
|
-
hasRequirementReview: true,
|
|
175
|
-
};
|
|
176
|
-
|
|
177
|
-
const result = await constraintChecker.check(IRON_LAWS['no_implementation_without_requirement_review'], context);
|
|
178
|
-
expect(result.satisfied).toBe(true);
|
|
179
|
-
});
|
|
180
|
-
|
|
181
|
-
it('should fail no_implementation_without_requirement when hasRequirement is undefined', async () => {
|
|
204
|
+
it('should skip no_implementation_without_requirement when hasRequirement is undefined(未接线不评估)', async () => {
|
|
182
205
|
const context: ConstraintContext = {
|
|
183
206
|
operation: 'code_implementation',
|
|
184
207
|
};
|
|
185
208
|
|
|
186
209
|
const result = await constraintChecker.check(IRON_LAWS['no_implementation_without_requirement'], context);
|
|
187
|
-
expect(result.
|
|
210
|
+
expect(result.skipped).toBe(true);
|
|
211
|
+
expect(result.satisfied).toBe(true);
|
|
188
212
|
});
|
|
189
213
|
|
|
190
214
|
it('should pass no_implementation_without_requirement when hasRequirement is true', async () => {
|
|
@@ -211,9 +235,9 @@ describe('Constraint Levels', () => {
|
|
|
211
235
|
});
|
|
212
236
|
});
|
|
213
237
|
|
|
214
|
-
it('should have correct level for
|
|
215
|
-
Object.values(
|
|
216
|
-
expect(
|
|
238
|
+
it('should have correct level for prompts', () => {
|
|
239
|
+
Object.values(PROMPTS).forEach(prompt => {
|
|
240
|
+
expect(prompt.level).toBe('prompt');
|
|
217
241
|
});
|
|
218
242
|
});
|
|
219
243
|
});
|