@dommaker/harness 0.19.0 → 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +22 -9
- package/bin/harness.js +136 -557
- package/dist/cli/commands/check.d.ts.map +1 -1
- package/dist/cli/commands/check.js +2 -13
- package/dist/cli/commands/check.js.map +1 -1
- package/dist/cli/commands/definitions.d.ts +90 -0
- package/dist/cli/commands/definitions.d.ts.map +1 -0
- package/dist/cli/commands/definitions.js +519 -0
- package/dist/cli/commands/definitions.js.map +1 -0
- package/dist/cli/commands/knowledge.js.map +1 -1
- package/dist/cli/commands/sync-docs/agents-syncer.d.ts.map +1 -1
- package/dist/cli/commands/sync-docs/agents-syncer.js +25 -4
- package/dist/cli/commands/sync-docs/agents-syncer.js.map +1 -1
- package/dist/cli/commands/sync-docs/capabilities-syncer.d.ts +3 -25
- package/dist/cli/commands/sync-docs/capabilities-syncer.d.ts.map +1 -1
- package/dist/cli/commands/sync-docs/capabilities-syncer.js +3 -93
- package/dist/cli/commands/sync-docs/capabilities-syncer.js.map +1 -1
- package/dist/cli/commands/sync-docs/index.d.ts +1 -1
- package/dist/cli/commands/sync-docs/index.d.ts.map +1 -1
- package/dist/cli/commands/sync-docs/index.js +4 -4
- package/dist/cli/commands/sync-docs/index.js.map +1 -1
- package/dist/completion-checkers/classify.d.ts +23 -0
- package/dist/completion-checkers/classify.d.ts.map +1 -0
- package/dist/completion-checkers/classify.js +37 -0
- package/dist/completion-checkers/classify.js.map +1 -0
- package/dist/completion-checkers/contract-presence.d.ts +12 -0
- package/dist/completion-checkers/contract-presence.d.ts.map +1 -0
- package/dist/completion-checkers/contract-presence.js +36 -0
- package/dist/completion-checkers/contract-presence.js.map +1 -0
- package/dist/completion-checkers/glob-match.d.ts +13 -0
- package/dist/completion-checkers/glob-match.d.ts.map +1 -0
- package/dist/completion-checkers/glob-match.js +58 -0
- package/dist/completion-checkers/glob-match.js.map +1 -0
- package/dist/completion-checkers/index.d.ts +19 -0
- package/dist/completion-checkers/index.d.ts.map +1 -0
- package/dist/completion-checkers/index.js +47 -0
- package/dist/completion-checkers/index.js.map +1 -0
- package/dist/completion-checkers/phase-format.d.ts +15 -0
- package/dist/completion-checkers/phase-format.d.ts.map +1 -0
- package/dist/completion-checkers/phase-format.js +42 -0
- package/dist/completion-checkers/phase-format.js.map +1 -0
- package/dist/completion-checkers/tdd-chain.d.ts +16 -0
- package/dist/completion-checkers/tdd-chain.d.ts.map +1 -0
- package/dist/completion-checkers/tdd-chain.js +57 -0
- package/dist/completion-checkers/tdd-chain.js.map +1 -0
- package/dist/completion-checkers/types.d.ts +77 -0
- package/dist/completion-checkers/types.d.ts.map +1 -0
- package/dist/completion-checkers/types.js +10 -0
- package/dist/completion-checkers/types.js.map +1 -0
- package/dist/context/index.d.ts +1 -5
- package/dist/context/index.d.ts.map +1 -1
- package/dist/context/index.js +1 -5
- package/dist/context/index.js.map +1 -1
- package/dist/context/knowledge-injector.d.ts +3 -3
- package/dist/context/knowledge-injector.d.ts.map +1 -1
- package/dist/context/knowledge-injector.js +1 -1
- package/dist/context/session-manager.d.ts +1 -12
- package/dist/context/session-manager.d.ts.map +1 -1
- package/dist/context/session-manager.js +0 -63
- package/dist/context/session-manager.js.map +1 -1
- package/dist/context/types.d.ts +1 -39
- package/dist/context/types.d.ts.map +1 -1
- package/dist/context/types.js +2 -15
- package/dist/context/types.js.map +1 -1
- package/dist/core/constraints/agent-prompt-renderer.d.ts +1 -1
- package/dist/core/constraints/agent-prompt-renderer.js +1 -1
- package/dist/core/constraints/capabilities-parser.d.ts +17 -2
- package/dist/core/constraints/capabilities-parser.d.ts.map +1 -1
- package/dist/core/constraints/capabilities-parser.js +84 -2
- package/dist/core/constraints/capabilities-parser.js.map +1 -1
- package/dist/core/constraints/checker.d.ts +3 -17
- package/dist/core/constraints/checker.d.ts.map +1 -1
- package/dist/core/constraints/checker.js +7 -47
- package/dist/core/constraints/checker.js.map +1 -1
- package/dist/core/constraints/definitions.d.ts +0 -7
- package/dist/core/constraints/definitions.d.ts.map +1 -1
- package/dist/core/constraints/definitions.js +1 -8
- package/dist/core/constraints/definitions.js.map +1 -1
- package/dist/core/constraints/doc-freshness/runner.d.ts +0 -2
- package/dist/core/constraints/doc-freshness/runner.d.ts.map +1 -1
- package/dist/core/constraints/doc-freshness/runner.js.map +1 -1
- package/dist/core/constraints/index.d.ts +1 -1
- package/dist/core/constraints/index.d.ts.map +1 -1
- package/dist/core/constraints/index.js +2 -3
- package/dist/core/constraints/index.js.map +1 -1
- package/dist/core/constraints/interceptor.d.ts.map +1 -1
- package/dist/core/constraints/interceptor.js +1 -5
- package/dist/core/constraints/interceptor.js.map +1 -1
- package/dist/core/effective-constraints.d.ts.map +1 -1
- package/dist/core/effective-constraints.js +0 -1
- package/dist/core/effective-constraints.js.map +1 -1
- package/dist/core/project-config-loader.d.ts.map +1 -1
- package/dist/core/project-config-loader.js +1 -8
- package/dist/core/project-config-loader.js.map +1 -1
- package/dist/dashboard/stats.d.ts.map +1 -1
- package/dist/dashboard/stats.js.map +1 -1
- package/dist/dashboard/types.d.ts +2 -2
- package/dist/dashboard/types.d.ts.map +1 -1
- package/dist/failure/constraint-handler.d.ts.map +1 -1
- package/dist/failure/constraint-handler.js +0 -2
- package/dist/failure/constraint-handler.js.map +1 -1
- package/dist/gates/acceptance.d.ts +9 -1
- package/dist/gates/acceptance.d.ts.map +1 -1
- package/dist/gates/acceptance.js +21 -0
- package/dist/gates/acceptance.js.map +1 -1
- package/dist/gates/checker-gate.d.ts +23 -0
- package/dist/gates/checker-gate.d.ts.map +1 -0
- package/dist/gates/checker-gate.js +53 -0
- package/dist/gates/checker-gate.js.map +1 -0
- package/dist/gates/command.d.ts +9 -2
- package/dist/gates/command.d.ts.map +1 -1
- package/dist/gates/command.js +10 -0
- package/dist/gates/command.js.map +1 -1
- package/dist/gates/contract.d.ts +8 -2
- package/dist/gates/contract.d.ts.map +1 -1
- package/dist/gates/contract.js +9 -0
- package/dist/gates/contract.js.map +1 -1
- package/dist/gates/decision.d.ts +17 -0
- package/dist/gates/decision.d.ts.map +1 -0
- package/dist/gates/decision.js +25 -0
- package/dist/gates/decision.js.map +1 -0
- package/dist/gates/definitions.d.ts +63 -0
- package/dist/gates/definitions.d.ts.map +1 -0
- package/dist/gates/definitions.js +160 -0
- package/dist/gates/definitions.js.map +1 -0
- package/dist/gates/effective-gates.d.ts +30 -0
- package/dist/gates/effective-gates.d.ts.map +1 -0
- package/dist/gates/effective-gates.js +85 -0
- package/dist/gates/effective-gates.js.map +1 -0
- package/dist/gates/index.d.ts +25 -1
- package/dist/gates/index.d.ts.map +1 -1
- package/dist/gates/index.js +37 -1
- package/dist/gates/index.js.map +1 -1
- package/dist/gates/performance.d.ts +8 -2
- package/dist/gates/performance.d.ts.map +1 -1
- package/dist/gates/performance.js +9 -0
- package/dist/gates/performance.js.map +1 -1
- package/dist/gates/registry.d.ts +32 -0
- package/dist/gates/registry.d.ts.map +1 -0
- package/dist/gates/registry.js +90 -0
- package/dist/gates/registry.js.map +1 -0
- package/dist/gates/review.d.ts +8 -2
- package/dist/gates/review.d.ts.map +1 -1
- package/dist/gates/review.js +9 -0
- package/dist/gates/review.js.map +1 -1
- package/dist/gates/runner.d.ts +30 -0
- package/dist/gates/runner.d.ts.map +1 -0
- package/dist/gates/runner.js +38 -0
- package/dist/gates/runner.js.map +1 -0
- package/dist/gates/security.d.ts +8 -2
- package/dist/gates/security.d.ts.map +1 -1
- package/dist/gates/security.js +9 -0
- package/dist/gates/security.js.map +1 -1
- package/dist/gates/types.d.ts +41 -1
- package/dist/gates/types.d.ts.map +1 -1
- package/dist/hooks/bootstrap.d.ts.map +1 -1
- package/dist/hooks/bootstrap.js +0 -2
- package/dist/hooks/bootstrap.js.map +1 -1
- package/dist/hooks/config.d.ts +26 -0
- package/dist/hooks/config.d.ts.map +1 -0
- package/dist/hooks/config.js +30 -0
- package/dist/hooks/config.js.map +1 -0
- package/dist/hooks/index.d.ts +2 -1
- package/dist/hooks/index.d.ts.map +1 -1
- package/dist/hooks/index.js +4 -1
- package/dist/hooks/index.js.map +1 -1
- package/dist/hooks/registry.d.ts +18 -1
- package/dist/hooks/registry.d.ts.map +1 -1
- package/dist/hooks/registry.js +45 -0
- package/dist/hooks/registry.js.map +1 -1
- package/dist/hooks/types.d.ts +20 -0
- package/dist/hooks/types.d.ts.map +1 -1
- package/dist/index.d.ts +5 -8
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +11 -20
- package/dist/index.js.map +1 -1
- package/dist/knowledge/import.d.ts +2 -2
- package/dist/knowledge/import.d.ts.map +1 -1
- package/dist/knowledge/ingest.js.map +1 -1
- package/dist/knowledge/lifecycle-hooks.d.ts +2 -2
- package/dist/knowledge/lifecycle-hooks.d.ts.map +1 -1
- package/dist/knowledge/types.d.ts +5 -7
- package/dist/knowledge/types.d.ts.map +1 -1
- package/dist/knowledge/types.js +1 -1
- package/dist/knowledge/types.js.map +1 -1
- package/dist/monitoring/constraint-doctor.d.ts +4 -22
- package/dist/monitoring/constraint-doctor.d.ts.map +1 -1
- package/dist/monitoring/constraint-doctor.js +7 -94
- package/dist/monitoring/constraint-doctor.js.map +1 -1
- package/dist/presets/standard.d.ts +1 -1
- package/dist/presets/standard.d.ts.map +1 -1
- package/dist/presets/standard.js +1 -2
- package/dist/presets/standard.js.map +1 -1
- package/dist/pretool-use-hook.d.ts +16 -0
- package/dist/pretool-use-hook.d.ts.map +1 -0
- package/dist/pretool-use-hook.js +68 -0
- package/dist/pretool-use-hook.js.map +1 -0
- package/dist/spec/annotation-checker.d.ts.map +1 -1
- package/dist/spec/annotation-checker.js +6 -26
- package/dist/spec/annotation-checker.js.map +1 -1
- package/dist/tools/index.d.ts +0 -4
- package/dist/tools/index.d.ts.map +1 -1
- package/dist/tools/index.js +0 -4
- package/dist/tools/index.js.map +1 -1
- package/dist/types/constraint.d.ts +1 -12
- package/dist/types/constraint.d.ts.map +1 -1
- package/dist/types/constraint.js +0 -3
- package/dist/types/constraint.js.map +1 -1
- package/dist/types/project-config.d.ts +0 -2
- package/dist/types/project-config.d.ts.map +1 -1
- package/dist/utils/file-walk.d.ts +2 -2
- package/dist/utils/file-walk.js +2 -2
- package/package.json +6 -1
- package/src/CONTEXT.md +15 -0
- package/src/__tests__/checker-extra.test.ts +2 -59
- package/src/__tests__/constraint-doctor.test.ts +1 -140
- package/src/__tests__/constraint-handler.test.ts +0 -4
- package/src/__tests__/pretool-use-hook.test.ts +82 -0
- package/src/__tests__/project-config-loader-extra.test.ts +0 -20
- package/src/__tests__/project-config-loader.test.ts +0 -1
- package/src/cli/commands/CONTEXT.md +15 -7
- package/src/cli/commands/__tests__/check-drift.test.ts +0 -3
- package/src/cli/commands/__tests__/check-skip-output.test.ts +0 -5
- package/src/cli/commands/__tests__/check.test.ts +7 -75
- package/src/cli/commands/__tests__/registry.test.ts +201 -0
- package/src/cli/commands/__tests__/sync-docs-agents.test.ts +55 -0
- package/src/cli/commands/check.ts +2 -14
- package/src/cli/commands/definitions.ts +593 -0
- package/src/cli/commands/knowledge.ts +3 -3
- package/src/cli/commands/sync-docs/agents-syncer.ts +26 -6
- package/src/cli/commands/sync-docs/capabilities-syncer.ts +4 -110
- package/src/cli/commands/sync-docs/index.ts +8 -5
- package/src/completion-checkers/CONTEXT.md +25 -0
- package/src/completion-checkers/__tests__/contract-presence.test.ts +44 -0
- package/src/completion-checkers/__tests__/phase-format.test.ts +60 -0
- package/src/completion-checkers/__tests__/tdd-chain.test.ts +99 -0
- package/src/completion-checkers/classify.ts +47 -0
- package/src/completion-checkers/contract-presence.ts +40 -0
- package/src/completion-checkers/glob-match.ts +54 -0
- package/src/completion-checkers/index.ts +19 -0
- package/src/completion-checkers/phase-format.ts +48 -0
- package/src/completion-checkers/tdd-chain.ts +63 -0
- package/src/completion-checkers/types.ts +84 -0
- package/src/context/CONTEXT.md +2 -7
- package/src/context/__tests__/session-manager.test.ts +0 -91
- package/src/context/index.ts +1 -5
- package/src/context/knowledge-injector.ts +3 -3
- package/src/context/session-manager.ts +0 -74
- package/src/context/types.ts +2 -63
- package/src/core/constraints/__tests__/interceptor.test.ts +0 -2
- package/src/core/constraints/agent-prompt-renderer.ts +1 -1
- package/src/core/constraints/capabilities-parser.ts +95 -2
- package/src/core/constraints/checker.ts +8 -53
- package/src/core/constraints/definitions.ts +0 -8
- package/src/core/constraints/doc-freshness/runner.ts +1 -2
- package/src/core/constraints/index.ts +1 -2
- package/src/core/constraints/interceptor.ts +1 -6
- package/src/core/effective-constraints.ts +0 -1
- package/src/core/project-config-loader.ts +1 -7
- package/src/dashboard/stats.ts +2 -2
- package/src/dashboard/types.ts +2 -2
- package/src/failure/constraint-handler.ts +0 -2
- package/src/gates/CONTEXT.md +16 -13
- package/src/gates/__tests__/checker-gate.test.ts +45 -0
- package/src/gates/__tests__/contract.test.ts +73 -0
- package/src/gates/__tests__/effective-gates.test.ts +82 -0
- package/src/gates/__tests__/gate-interface.test.ts +202 -0
- package/src/gates/__tests__/registry.test.ts +87 -0
- package/src/gates/__tests__/runner.test.ts +81 -0
- package/src/gates/acceptance.ts +24 -1
- package/src/gates/checker-gate.ts +54 -0
- package/src/gates/command.ts +13 -2
- package/src/gates/contract.ts +12 -2
- package/src/gates/decision.ts +27 -0
- package/src/gates/definitions.ts +207 -0
- package/src/gates/effective-gates.ts +106 -0
- package/src/gates/index.ts +38 -0
- package/src/gates/performance.ts +12 -2
- package/src/gates/registry.ts +107 -0
- package/src/gates/review.ts +12 -2
- package/src/gates/runner.ts +57 -0
- package/src/gates/security.ts +12 -2
- package/src/gates/types.ts +48 -1
- package/src/hooks/CONTEXT.md +12 -2
- package/src/hooks/__tests__/bootstrap.test.ts +0 -1
- package/src/hooks/__tests__/config.test.ts +19 -0
- package/src/hooks/__tests__/pipeline.test.ts +79 -0
- package/src/hooks/__tests__/registry.test.ts +67 -0
- package/src/hooks/bootstrap.ts +0 -2
- package/src/hooks/config.ts +29 -0
- package/src/hooks/index.ts +2 -1
- package/src/hooks/registry.ts +60 -1
- package/src/hooks/types.ts +21 -0
- package/src/index.ts +11 -21
- package/src/knowledge/import.ts +3 -3
- package/src/knowledge/ingest.ts +4 -4
- package/src/knowledge/lifecycle-hooks.ts +3 -3
- package/src/knowledge/types.ts +5 -8
- package/src/monitoring/constraint-doctor.ts +7 -106
- package/src/presets/__tests__/standard.test.ts +0 -2
- package/src/presets/standard.ts +1 -2
- package/src/pretool-use-hook.ts +68 -0
- package/src/spec/__tests__/annotation-checker.test.ts +9 -3
- package/src/spec/annotation-checker.ts +7 -29
- package/src/tools/CONTEXT.md +7 -14
- package/src/tools/index.ts +0 -4
- package/src/types/constraint.ts +1 -14
- package/src/types/project-config.ts +0 -3
- package/src/utils/file-walk.ts +2 -2
- package/dist/architecture/constraint-engine.d.ts +0 -67
- package/dist/architecture/constraint-engine.d.ts.map +0 -1
- package/dist/architecture/constraint-engine.js +0 -295
- package/dist/architecture/constraint-engine.js.map +0 -1
- package/dist/architecture/cross-project-checker.d.ts +0 -54
- package/dist/architecture/cross-project-checker.d.ts.map +0 -1
- package/dist/architecture/cross-project-checker.js +0 -467
- package/dist/architecture/cross-project-checker.js.map +0 -1
- package/dist/cli/commands/index.d.ts +0 -30
- package/dist/cli/commands/index.d.ts.map +0 -1
- package/dist/cli/commands/index.js +0 -91
- package/dist/cli/commands/index.js.map +0 -1
- package/dist/context/file-budget.d.ts +0 -40
- package/dist/context/file-budget.d.ts.map +0 -1
- package/dist/context/file-budget.js +0 -147
- package/dist/context/file-budget.js.map +0 -1
- package/dist/context/progressive-loader.d.ts +0 -109
- package/dist/context/progressive-loader.d.ts.map +0 -1
- package/dist/context/progressive-loader.js +0 -166
- package/dist/context/progressive-loader.js.map +0 -1
- package/dist/context/token-pipeline.d.ts +0 -55
- package/dist/context/token-pipeline.d.ts.map +0 -1
- package/dist/context/token-pipeline.js +0 -220
- package/dist/context/token-pipeline.js.map +0 -1
- package/dist/context/tool-output-budget.d.ts +0 -59
- package/dist/context/tool-output-budget.d.ts.map +0 -1
- package/dist/context/tool-output-budget.js +0 -177
- package/dist/context/tool-output-budget.js.map +0 -1
- package/dist/core/constraints/default-executors.d.ts +0 -10
- package/dist/core/constraints/default-executors.d.ts.map +0 -1
- package/dist/core/constraints/default-executors.js +0 -120
- package/dist/core/constraints/default-executors.js.map +0 -1
- package/dist/llm/adapter.d.ts +0 -36
- package/dist/llm/adapter.d.ts.map +0 -1
- package/dist/llm/adapter.js +0 -118
- package/dist/llm/adapter.js.map +0 -1
- package/dist/llm/index.d.ts +0 -3
- package/dist/llm/index.d.ts.map +0 -1
- package/dist/llm/index.js +0 -19
- package/dist/llm/index.js.map +0 -1
- package/dist/llm/types.d.ts +0 -62
- package/dist/llm/types.d.ts.map +0 -1
- package/dist/llm/types.js +0 -8
- package/dist/llm/types.js.map +0 -1
- package/dist/tools/core/index.d.ts +0 -13
- package/dist/tools/core/index.d.ts.map +0 -1
- package/dist/tools/core/index.js +0 -198
- package/dist/tools/core/index.js.map +0 -1
- package/dist/tools/loader.d.ts +0 -21
- package/dist/tools/loader.d.ts.map +0 -1
- package/dist/tools/loader.js +0 -199
- package/dist/tools/loader.js.map +0 -1
- package/dist/tools/registry.d.ts +0 -59
- package/dist/tools/registry.d.ts.map +0 -1
- package/dist/tools/registry.js +0 -115
- package/dist/tools/registry.js.map +0 -1
- package/dist/tools/types.d.ts +0 -38
- package/dist/tools/types.d.ts.map +0 -1
- package/dist/tools/types.js +0 -6
- package/dist/tools/types.js.map +0 -1
- package/src/__tests__/architecture-engine.test.ts +0 -619
- package/src/__tests__/context/progressive-loader.test.ts +0 -131
- package/src/__tests__/cross-project-checker.test.ts +0 -318
- package/src/architecture/CONTEXT.md +0 -22
- package/src/architecture/constraint-engine.ts +0 -351
- package/src/architecture/cross-project-checker.ts +0 -545
- package/src/cli/commands/index.ts +0 -30
- package/src/context/__tests__/file-budget.test.ts +0 -142
- package/src/context/__tests__/progressive-loader.test.ts +0 -264
- package/src/context/__tests__/token-pipeline.test.ts +0 -178
- package/src/context/__tests__/tool-output-budget.test.ts +0 -95
- package/src/context/file-budget.ts +0 -142
- package/src/context/progressive-loader.ts +0 -260
- package/src/context/token-pipeline.ts +0 -265
- package/src/context/tool-output-budget.ts +0 -178
- package/src/core/constraints/__tests__/default-executors.test.ts +0 -57
- package/src/core/constraints/default-executors.ts +0 -92
- package/src/llm/CONTEXT.md +0 -21
- package/src/llm/__tests__/adapter.test.ts +0 -172
- package/src/llm/adapter.ts +0 -149
- package/src/llm/index.ts +0 -2
- package/src/llm/types.ts +0 -81
- package/src/tools/__tests__/loader.test.ts +0 -252
- package/src/tools/__tests__/registry.test.ts +0 -157
- package/src/tools/core/__tests__/core.test.ts +0 -122
- package/src/tools/core/index.ts +0 -201
- package/src/tools/loader.ts +0 -216
- package/src/tools/registry.ts +0 -128
- package/src/tools/types.ts +0 -46
|
@@ -17,7 +17,7 @@ import {
|
|
|
17
17
|
ConstraintViolationError,
|
|
18
18
|
} from '../../types/constraint';
|
|
19
19
|
import type { ExecutionTrace } from '../../types/trace';
|
|
20
|
-
import { IRON_LAWS, GUIDELINES,
|
|
20
|
+
import { IRON_LAWS, GUIDELINES, PROMPTS } from './definitions';
|
|
21
21
|
import type { MergedConstraintsConfig } from '../../types/project-config';
|
|
22
22
|
import { normalizeTriggers } from '../../utils/exec';
|
|
23
23
|
import { runCommand } from '../../utils/exec';
|
|
@@ -74,9 +74,6 @@ const NOOP_TRACE_RECORDER: TraceRecorder = { record: () => undefined };
|
|
|
74
74
|
export class ConstraintChecker {
|
|
75
75
|
private static instance: ConstraintChecker;
|
|
76
76
|
|
|
77
|
-
/** 自定义约束配置(项目级) */
|
|
78
|
-
private customConfig: MergedConstraintsConfig | null = null;
|
|
79
|
-
|
|
80
77
|
/** 检查结果缓存(S7:src 扫描等重复 I/O,TTL=1s 防跨测试污染) */
|
|
81
78
|
private cache: CheckCache = new CheckCache({ ttlMs: 1000 });
|
|
82
79
|
|
|
@@ -139,31 +136,20 @@ export class ConstraintChecker {
|
|
|
139
136
|
this.traceRecorder = recorder;
|
|
140
137
|
}
|
|
141
138
|
|
|
142
|
-
/**
|
|
143
|
-
* 设置项目级约束配置
|
|
144
|
-
*
|
|
145
|
-
* @deprecated 修改单例状态,多项目并发时存在污染风险。
|
|
146
|
-
* 请改用 checkConstraints() / beforeExecution() 的 customConfig 参数实现 per-request 隔离。
|
|
147
|
-
*/
|
|
148
|
-
setCustomConfig(config: MergedConstraintsConfig): void {
|
|
149
|
-
this.customConfig = config;
|
|
150
|
-
}
|
|
151
|
-
|
|
152
139
|
/**
|
|
153
140
|
* 获取当前的约束集合(内置 + 自定义)
|
|
154
141
|
*
|
|
155
|
-
* @param customConfig 可选,per-request
|
|
142
|
+
* @param customConfig 可选,per-request 自定义配置;不传则返回内置约束集
|
|
156
143
|
*/
|
|
157
144
|
getConstraints(customConfig?: MergedConstraintsConfig | null): {
|
|
158
145
|
ironLaws: Record<string, Constraint & { check: (ctx: ConstraintContext) => Promise<ConstraintResult> }>;
|
|
159
146
|
guidelines: Record<string, Constraint & { check: (ctx: ConstraintContext) => Promise<ConstraintResult> }>;
|
|
160
|
-
tips: Record<string, Constraint & { check: (ctx: ConstraintContext) => Promise<ConstraintResult> }>;
|
|
161
147
|
prompts: Record<string, Constraint & { check: (ctx: ConstraintContext) => Promise<ConstraintResult> }>;
|
|
162
148
|
} {
|
|
163
|
-
const config = customConfig
|
|
149
|
+
const config = customConfig;
|
|
164
150
|
const source = config
|
|
165
|
-
? { ironLaws: config.ironLaws, guidelines: config.guidelines,
|
|
166
|
-
: { ironLaws: IRON_LAWS, guidelines: GUIDELINES,
|
|
151
|
+
? { ironLaws: config.ironLaws, guidelines: config.guidelines, prompts: config.prompts ?? PROMPTS }
|
|
152
|
+
: { ironLaws: IRON_LAWS, guidelines: GUIDELINES, prompts: PROMPTS };
|
|
167
153
|
|
|
168
154
|
// Wire unified check() method on every constraint
|
|
169
155
|
const wire = (constraints: Record<string, Constraint>) => {
|
|
@@ -175,7 +161,6 @@ export class ConstraintChecker {
|
|
|
175
161
|
return {
|
|
176
162
|
ironLaws: wire(source.ironLaws),
|
|
177
163
|
guidelines: wire(source.guidelines),
|
|
178
|
-
tips: wire(source.tips),
|
|
179
164
|
prompts: wire(source.prompts),
|
|
180
165
|
};
|
|
181
166
|
}
|
|
@@ -260,7 +245,6 @@ export class ConstraintChecker {
|
|
|
260
245
|
case 'guideline':
|
|
261
246
|
return 'warning';
|
|
262
247
|
case 'prompt':
|
|
263
|
-
case 'tip':
|
|
264
248
|
return 'info';
|
|
265
249
|
default:
|
|
266
250
|
return 'warning';
|
|
@@ -350,7 +334,6 @@ export class ConstraintChecker {
|
|
|
350
334
|
): {
|
|
351
335
|
ironLaws: Constraint[];
|
|
352
336
|
guidelines: Constraint[];
|
|
353
|
-
tips: Constraint[];
|
|
354
337
|
prompts: Constraint[];
|
|
355
338
|
} {
|
|
356
339
|
const operations = [context.operation, ...(context.extraTriggers ?? [])];
|
|
@@ -368,17 +351,15 @@ export class ConstraintChecker {
|
|
|
368
351
|
return {
|
|
369
352
|
ironLaws: filterByTrigger(constraints.ironLaws),
|
|
370
353
|
guidelines: filterByTrigger(constraints.guidelines),
|
|
371
|
-
tips: filterByTrigger(constraints.tips),
|
|
372
354
|
prompts: filterByTrigger(constraints.prompts),
|
|
373
355
|
};
|
|
374
356
|
}
|
|
375
357
|
|
|
376
358
|
/**
|
|
377
|
-
*
|
|
359
|
+
* 执行约束检查
|
|
378
360
|
*
|
|
379
361
|
* - Iron Laws:检查失败立即抛出异常
|
|
380
362
|
* - Guidelines:检查失败记录警告
|
|
381
|
-
* - Tips:检查失败记录提示
|
|
382
363
|
*
|
|
383
364
|
* @param context 约束上下文
|
|
384
365
|
* @param customConfig 可选,per-request 自定义配置(避免多请求间的单例状态污染)
|
|
@@ -395,10 +376,8 @@ export class ConstraintChecker {
|
|
|
395
376
|
const result: ConstraintCheckResult = {
|
|
396
377
|
ironLaws: [],
|
|
397
378
|
guidelines: [],
|
|
398
|
-
tips: [],
|
|
399
379
|
passed: true,
|
|
400
380
|
warningCount: 0,
|
|
401
|
-
tipCount: 0,
|
|
402
381
|
};
|
|
403
382
|
|
|
404
383
|
const traceCollector = this.traceRecorder;
|
|
@@ -431,19 +410,6 @@ export class ConstraintChecker {
|
|
|
431
410
|
}
|
|
432
411
|
}
|
|
433
412
|
|
|
434
|
-
// 3. Tips: 仅记录
|
|
435
|
-
for (const constraint of Object.values(constraints.tips)) {
|
|
436
|
-
if (!this.matchesTrigger(constraint, context)) continue;
|
|
437
|
-
|
|
438
|
-
const checkResult = await this.check(constraint, context);
|
|
439
|
-
result.tips.push(checkResult);
|
|
440
|
-
this.recordTrace(traceCollector, constraint, checkResult, context);
|
|
441
|
-
|
|
442
|
-
if (!checkResult.satisfied) {
|
|
443
|
-
result.tipCount++;
|
|
444
|
-
}
|
|
445
|
-
}
|
|
446
|
-
|
|
447
413
|
return result;
|
|
448
414
|
} finally {
|
|
449
415
|
this.runCache = null;
|
|
@@ -451,7 +417,7 @@ export class ConstraintChecker {
|
|
|
451
417
|
}
|
|
452
418
|
|
|
453
419
|
/**
|
|
454
|
-
* S11: 安全全量检查 —
|
|
420
|
+
* S11: 安全全量检查 — 全部约束检查,不抛异常
|
|
455
421
|
*
|
|
456
422
|
* 区别 checkConstraints(): Iron Law 违规收集在结果中而不是抛异常。
|
|
457
423
|
* 调用方通过 result.passed + result.warningCount 判断状态。
|
|
@@ -468,10 +434,8 @@ export class ConstraintChecker {
|
|
|
468
434
|
const result: ConstraintCheckResult = {
|
|
469
435
|
ironLaws: [],
|
|
470
436
|
guidelines: [],
|
|
471
|
-
tips: [],
|
|
472
437
|
passed: true,
|
|
473
438
|
warningCount: 0,
|
|
474
|
-
tipCount: 0,
|
|
475
439
|
};
|
|
476
440
|
|
|
477
441
|
const traceCollector = this.traceRecorder;
|
|
@@ -493,14 +457,6 @@ export class ConstraintChecker {
|
|
|
493
457
|
if (!checkResult.satisfied) result.warningCount++;
|
|
494
458
|
}
|
|
495
459
|
|
|
496
|
-
for (const constraint of Object.values(constraints.tips)) {
|
|
497
|
-
if (!this.matchesTrigger(constraint, context)) continue;
|
|
498
|
-
const checkResult = await this.check(constraint, context);
|
|
499
|
-
result.tips.push(checkResult);
|
|
500
|
-
this.recordTrace(traceCollector, constraint, checkResult, context);
|
|
501
|
-
if (!checkResult.satisfied) result.tipCount++;
|
|
502
|
-
}
|
|
503
|
-
|
|
504
460
|
return result;
|
|
505
461
|
} finally {
|
|
506
462
|
this.runCache = null;
|
|
@@ -549,13 +505,12 @@ export async function checkConstraint(
|
|
|
549
505
|
const constraint =
|
|
550
506
|
constraints.ironLaws[constraintId] ||
|
|
551
507
|
constraints.guidelines[constraintId] ||
|
|
552
|
-
constraints.tips[constraintId] ||
|
|
553
508
|
constraints.prompts[constraintId];
|
|
554
509
|
|
|
555
510
|
if (!constraint) {
|
|
556
511
|
return {
|
|
557
512
|
id: constraintId,
|
|
558
|
-
level: '
|
|
513
|
+
level: 'guideline',
|
|
559
514
|
satisfied: false,
|
|
560
515
|
message: `未知的约束: ${constraintId}`,
|
|
561
516
|
checkedAt: new Date(),
|
|
@@ -4,8 +4,6 @@
|
|
|
4
4
|
* kind 二元模型(ADR-0001):
|
|
5
5
|
* - IRON_LAWS / GUIDELINES:check 层,必须带真实 checker(注册表闭环)
|
|
6
6
|
* - PROMPTS:prompt 层,纯文本注入,不占检查位
|
|
7
|
-
* - TIPS:已退役,导出空表仅为在途消费者(capabilities-syncer)编译兼容,
|
|
8
|
-
* 后续阶段随其改造一并删除
|
|
9
7
|
*
|
|
10
8
|
* 工单 20:字面量定义按层拆至 ./definitions/{iron-laws,guidelines,prompts}.ts;
|
|
11
9
|
* 本文件保持原路径做薄聚合(studio rule-scanner 按此路径解析,P0 #8)。
|
|
@@ -16,12 +14,6 @@ import { IRON_LAWS } from './definitions/iron-laws';
|
|
|
16
14
|
import { GUIDELINES } from './definitions/guidelines';
|
|
17
15
|
import { PROMPTS } from './definitions/prompts';
|
|
18
16
|
|
|
19
|
-
/**
|
|
20
|
-
* @deprecated TIPS 已退役(ADR-0001),恒为空表。
|
|
21
|
-
* 仅为在途 capabilities-syncer 编译兼容保留,后续阶段删除。
|
|
22
|
-
*/
|
|
23
|
-
export const TIPS: Record<string, Constraint> = {};
|
|
24
|
-
|
|
25
17
|
export { IRON_LAWS, GUIDELINES, PROMPTS };
|
|
26
18
|
|
|
27
19
|
// ========================================
|
|
@@ -44,7 +44,6 @@ interface RunContext {
|
|
|
44
44
|
constraintCounts?: {
|
|
45
45
|
ironLaws?: number;
|
|
46
46
|
guidelines?: number;
|
|
47
|
-
tips?: number;
|
|
48
47
|
};
|
|
49
48
|
}
|
|
50
49
|
|
|
@@ -58,7 +57,7 @@ export class FreshnessRunner {
|
|
|
58
57
|
runAll(
|
|
59
58
|
config: DocFreshnessConfig,
|
|
60
59
|
projectPath: string,
|
|
61
|
-
ctx?: { requiredDirs?: string[]; constraintCounts?: { ironLaws?: number; guidelines?: number
|
|
60
|
+
ctx?: { requiredDirs?: string[]; constraintCounts?: { ironLaws?: number; guidelines?: number } }
|
|
62
61
|
): FreshnessCheckResult[] {
|
|
63
62
|
if (config.enabled === false) return [];
|
|
64
63
|
if (!config.checks || config.checks.length === 0) return [];
|
|
@@ -115,14 +115,13 @@ export class ConstraintInterceptor {
|
|
|
115
115
|
const allConstraints = [
|
|
116
116
|
...Object.values(constraints.ironLaws),
|
|
117
117
|
...Object.values(constraints.guidelines),
|
|
118
|
-
...Object.values(constraints.tips),
|
|
119
118
|
].filter(c => c.kind !== 'prompt');
|
|
120
119
|
|
|
121
120
|
const applicableConstraints = allConstraints.filter(
|
|
122
121
|
(c) => normalizeTriggers<ConstraintTrigger>(c.trigger).includes(trigger)
|
|
123
122
|
);
|
|
124
123
|
|
|
125
|
-
const order: Record<ConstraintLevel, number> = { iron_law: 0, guideline: 1, prompt: 2
|
|
124
|
+
const order: Record<ConstraintLevel, number> = { iron_law: 0, guideline: 1, prompt: 2 };
|
|
126
125
|
const ordered = applicableConstraints.sort((a, b) => order[a.level] - order[b.level]);
|
|
127
126
|
|
|
128
127
|
for (const constraint of ordered) {
|
|
@@ -200,10 +199,6 @@ export class ConstraintInterceptor {
|
|
|
200
199
|
|
|
201
200
|
export const constraintInterceptor = ConstraintInterceptor.getInstance();
|
|
202
201
|
|
|
203
|
-
// 注册内置 executors
|
|
204
|
-
import { registerDefaultExecutors } from './default-executors';
|
|
205
|
-
registerDefaultExecutors();
|
|
206
|
-
|
|
207
202
|
export async function interceptOperation(
|
|
208
203
|
trigger: ConstraintTrigger,
|
|
209
204
|
context: ConstraintContext
|
|
@@ -172,7 +172,6 @@ export class ProjectConfigLoader {
|
|
|
172
172
|
const result: MergedConstraintsConfig = {
|
|
173
173
|
ironLaws: presetMerged.ironLaws,
|
|
174
174
|
guidelines: presetMerged.guidelines,
|
|
175
|
-
tips: {},
|
|
176
175
|
prompts: presetMerged.prompts ?? {},
|
|
177
176
|
disabled: [...presetMerged.disabled],
|
|
178
177
|
custom: [],
|
|
@@ -196,7 +195,6 @@ export class ProjectConfigLoader {
|
|
|
196
195
|
// 从对应层级中移除
|
|
197
196
|
delete result.ironLaws[constraintId];
|
|
198
197
|
delete result.guidelines[constraintId];
|
|
199
|
-
delete result.tips[constraintId];
|
|
200
198
|
delete result.prompts![constraintId];
|
|
201
199
|
}
|
|
202
200
|
}
|
|
@@ -225,7 +223,6 @@ export class ProjectConfigLoader {
|
|
|
225
223
|
];
|
|
226
224
|
if (result.ironLaws[id]) result.ironLaws[id] = constraint;
|
|
227
225
|
if (result.guidelines[id]) result.guidelines[id] = constraint;
|
|
228
|
-
if (result.tips[id]) result.tips[id] = constraint;
|
|
229
226
|
if (result.prompts![id]) result.prompts![id] = constraint;
|
|
230
227
|
continue;
|
|
231
228
|
}
|
|
@@ -255,9 +252,6 @@ export class ProjectConfigLoader {
|
|
|
255
252
|
case 'prompt':
|
|
256
253
|
result.prompts![id] = constraint;
|
|
257
254
|
break;
|
|
258
|
-
case 'tip':
|
|
259
|
-
result.tips[id] = constraint;
|
|
260
|
-
break;
|
|
261
255
|
}
|
|
262
256
|
}
|
|
263
257
|
|
|
@@ -285,7 +279,7 @@ export class ProjectConfigLoader {
|
|
|
285
279
|
id: string,
|
|
286
280
|
merged: MergedConstraintsConfig
|
|
287
281
|
): Constraint | undefined {
|
|
288
|
-
return merged.ironLaws[id] || merged.guidelines[id] || merged.
|
|
282
|
+
return merged.ironLaws[id] || merged.guidelines[id] || merged.prompts?.[id];
|
|
289
283
|
}
|
|
290
284
|
|
|
291
285
|
/**
|
package/src/dashboard/stats.ts
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* 纯函数,从原始数据计算统计指标
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
-
import type { KnowledgeEntry,
|
|
7
|
+
import type { KnowledgeEntry, KnowledgeSubsystem } from '../knowledge/types';
|
|
8
8
|
import type { KnowledgeOverview, KnowledgeFlow } from './types';
|
|
9
9
|
|
|
10
10
|
/**
|
|
@@ -36,7 +36,7 @@ export function computeKnowledgeOverview(entries: KnowledgeEntry[]): KnowledgeOv
|
|
|
36
36
|
* 按类型分组统计
|
|
37
37
|
*/
|
|
38
38
|
function computeByType(entries: KnowledgeEntry[]): KnowledgeOverview['byType'] {
|
|
39
|
-
const typeMap = new Map<
|
|
39
|
+
const typeMap = new Map<KnowledgeSubsystem, { total: number; proven: number; verified: number; draft: number }>();
|
|
40
40
|
|
|
41
41
|
for (const entry of entries) {
|
|
42
42
|
const existing = typeMap.get(entry.type) || { total: 0, proven: 0, verified: 0, draft: 0 };
|
package/src/dashboard/types.ts
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* harness 提供数据,studio 负责渲染
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
-
import type {
|
|
7
|
+
import type { KnowledgeSubsystem } from '../knowledge/types';
|
|
8
8
|
|
|
9
9
|
// ── 约束分层(原 src/constraints/types,约束分层子系统删除后归位 dashboard)──
|
|
10
10
|
|
|
@@ -28,7 +28,7 @@ export interface ConstraintStats {
|
|
|
28
28
|
|
|
29
29
|
export interface KnowledgeOverview {
|
|
30
30
|
byType: Array<{
|
|
31
|
-
type:
|
|
31
|
+
type: KnowledgeSubsystem;
|
|
32
32
|
total: number;
|
|
33
33
|
proven: number;
|
|
34
34
|
verified: number;
|
package/src/gates/CONTEXT.md
CHANGED
|
@@ -1,27 +1,30 @@
|
|
|
1
1
|
# gates/
|
|
2
2
|
|
|
3
3
|
## 职责
|
|
4
|
-
质量门禁系统:6
|
|
4
|
+
质量门禁系统:6 种门禁检查(验收/命令/契约/性能/审查/安全)。统一 Gate 三态协议 + 注册表闭环 + 声明式生效集(G1)。
|
|
5
5
|
|
|
6
6
|
## 核心导出
|
|
7
|
-
- `
|
|
8
|
-
-
|
|
9
|
-
-
|
|
10
|
-
-
|
|
11
|
-
- `
|
|
12
|
-
-
|
|
13
|
-
- `
|
|
14
|
-
-
|
|
7
|
+
- 统一协议:`Gate{id, order, evaluate(ctx)}` → `GateDecision{status: deny|abstain|ask, result: GateResult}`(`types.ts`;`decisionFromResult` 报告→决策映射,浅冻结)
|
|
8
|
+
- 注册表:`gateRegistry`(`registry.ts`)——定义即注册 + 构建期双向闭环(定义无实现/实现无定义/重复 id → 加载期抛错);`getGate`(未注册抛错)/`listRegisteredGates`/`registeredGateCount`
|
|
9
|
+
- 定义表:`GATE_DEFINITIONS`(`definitions.ts`)——id/description/默认 order/CLI 元数据;bin/harness.js 注册表驱动生成 6 门禁命令(纯数据模块,禁 import 实现,保 --help 懒加载;CLI 实现引用为 module+export,per-command 懒加载,H5)
|
|
10
|
+
- 执行器:`runGates`(`runner.ts`)——按 order 升序;deny 单调(下游 abstain 不得改回 allow)+ ask 枚举预留 fail-closed = deny;决策浅冻结
|
|
11
|
+
- 生效集:`getEffectiveGates(projectRoot)`(`effective-gates.ts`)——对齐 getEffectiveConstraints 裁剪模式:config.yml `gates.order` 重排 + `gates.<id>.enabled:false` 移除;引用未注册 id 抛错
|
|
12
|
+
- checker-as-guard 接线点:`createCheckerGate(check)`(`checker-gate.ts`)——ConstraintCheck → Gate(studio #129 随动);false→deny,true/'skip'→abstain
|
|
13
|
+
- 门禁类:`ReviewGate` / `SecurityGate` / `PerformanceGate` / `ContractGate` / `SpecAcceptanceGate` / `CommandGate`(各自执行细节私有,保留 `check()`/`scan()` 报告方法)
|
|
14
|
+
- `types.ts` — GateResult(报告结构,保留)/ GateContext / GateDecision 等公共类型
|
|
15
|
+
- 便捷工厂函数:createReviewGate / createSecurityGate / createPerformanceGate / createContractGate / createSpecAcceptanceGate / createCommandGate
|
|
15
16
|
|
|
16
17
|
## 依赖关系
|
|
17
|
-
- 依赖 `src/core
|
|
18
|
+
- 依赖 `src/core/`(effective-gates 读 project-config-loader;checker-gate 适配 checkers 注册表类型)
|
|
18
19
|
- 依赖 `src/types/` 公共类型
|
|
19
20
|
|
|
20
21
|
## 约定
|
|
21
|
-
-
|
|
22
|
-
-
|
|
23
|
-
-
|
|
22
|
+
- 新门禁必须:① 在 `definitions.ts` 补 GateDefinition(含 CLI 元数据)② 在 `registry.ts` IMPLEMENTATIONS 注册实现(缺一 → 加载期抛错)③ 实现统一 Gate 接口(evaluate 产三态决策,报告走 GateResult)④ 配 CLI 命令(命令实现文件 + CLI 元数据的 module+export 引用,bin 由定义表驱动生成,不再手写块)+ 测试文件
|
|
23
|
+
- deny 单调是接口契约:决策浅冻结,下游不得改写上游决策
|
|
24
|
+
- ask 枚举预留:暂无实现,runGates fail-closed 按 deny 计
|
|
24
25
|
|
|
25
26
|
## 注意事项
|
|
26
27
|
- 门禁系统不包含业务逻辑,只提供检查能力
|
|
28
|
+
- 统一的是决策协议(id/order/三态),执行细节(gh pr view/正则黑名单/OpenAPI diff/benchmark)私有——不要把执行细节塞进 Gate 接口
|
|
29
|
+
- config.yml gates 段为新增面:引用未注册 id 直接抛错(无历史残留配置需兼容)
|
|
27
30
|
- CommandGate 为命令黑名单检查(SEC-006)
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* checker-as-guard 接线点测试(G1 / studio #129 随动)
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { createCheckerGate } from '../checker-gate';
|
|
6
|
+
import { contextFlag, contextEvidenceFlag } from '../../core/constraints/checkers/types';
|
|
7
|
+
|
|
8
|
+
describe('createCheckerGate', () => {
|
|
9
|
+
const ctx = { projectId: 'p', projectPath: '/tmp/project' };
|
|
10
|
+
|
|
11
|
+
it('checker 通过(true)→ abstain', async () => {
|
|
12
|
+
const gate = createCheckerGate(contextFlag('flag-pass', () => true), 7);
|
|
13
|
+
expect(gate.id).toBe('flag-pass');
|
|
14
|
+
expect(gate.order).toBe(7);
|
|
15
|
+
|
|
16
|
+
const decision = await gate.evaluate(ctx);
|
|
17
|
+
expect(decision.status).toBe('abstain');
|
|
18
|
+
expect(decision.result.gate).toBe('flag-pass');
|
|
19
|
+
expect(decision.result.passed).toBe(true);
|
|
20
|
+
expect(decision.result.message).toContain('通过');
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
it('checker 违规(false)→ deny', async () => {
|
|
24
|
+
const gate = createCheckerGate(contextFlag('flag-fail', () => false));
|
|
25
|
+
const decision = await gate.evaluate(ctx);
|
|
26
|
+
expect(decision.status).toBe('deny');
|
|
27
|
+
expect(decision.result.passed).toBe(false);
|
|
28
|
+
expect(decision.result.message).toContain('判定违规');
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
it('证据未接线(skip)→ abstain(不阻断)', async () => {
|
|
32
|
+
const gate = createCheckerGate(
|
|
33
|
+
contextEvidenceFlag('flag-skip', () => undefined)
|
|
34
|
+
);
|
|
35
|
+
const decision = await gate.evaluate(ctx);
|
|
36
|
+
expect(decision.status).toBe('abstain');
|
|
37
|
+
expect(decision.result.message).toContain('跳过');
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
it('决策浅冻结(单调语义契约)', async () => {
|
|
41
|
+
const gate = createCheckerGate(contextFlag('flag-freeze', () => true));
|
|
42
|
+
const decision = await gate.evaluate(ctx);
|
|
43
|
+
expect(Object.isFrozen(decision)).toBe(true);
|
|
44
|
+
});
|
|
45
|
+
});
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ContractGate 测试(门禁规则:每个门禁必须有测试文件)
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import * as fs from 'fs';
|
|
6
|
+
import * as os from 'os';
|
|
7
|
+
import * as path from 'path';
|
|
8
|
+
import { ContractGate } from '../contract';
|
|
9
|
+
|
|
10
|
+
const VALID_SPEC = [
|
|
11
|
+
'openapi: 3.0.0',
|
|
12
|
+
'info:',
|
|
13
|
+
' title: Test API',
|
|
14
|
+
' version: 1.0.0',
|
|
15
|
+
'paths:',
|
|
16
|
+
' /users:',
|
|
17
|
+
' get: {}',
|
|
18
|
+
].join('\n');
|
|
19
|
+
|
|
20
|
+
const INVALID_SPEC = ['info:', ' title: Test API', 'paths:', ' /users:', ' get: {}'].join('\n');
|
|
21
|
+
|
|
22
|
+
describe('ContractGate', () => {
|
|
23
|
+
let dir: string;
|
|
24
|
+
|
|
25
|
+
beforeEach(() => {
|
|
26
|
+
dir = fs.mkdtempSync(path.join(os.tmpdir(), 'harness-gate-contract-'));
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
afterEach(() => {
|
|
30
|
+
fs.rmSync(dir, { recursive: true, force: true });
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
it('有效 OpenAPI → check 通过', async () => {
|
|
34
|
+
fs.writeFileSync(path.join(dir, 'openapi.yaml'), VALID_SPEC);
|
|
35
|
+
const result = await new ContractGate().check({ projectId: 'p', projectPath: dir });
|
|
36
|
+
expect(result.passed).toBe(true);
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
it('缺 openapi 版本 → check 失败', async () => {
|
|
40
|
+
fs.writeFileSync(path.join(dir, 'openapi.yaml'), INVALID_SPEC);
|
|
41
|
+
const result = await new ContractGate().check({ projectId: 'p', projectPath: dir });
|
|
42
|
+
expect(result.passed).toBe(false);
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
it('契约文件缺失 → 跳过(passed)', async () => {
|
|
46
|
+
const result = await new ContractGate().check({ projectId: 'p', projectPath: dir });
|
|
47
|
+
expect(result.passed).toBe(true);
|
|
48
|
+
expect(result.message).toContain('未找到契约文件');
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
describe('统一接口 evaluate', () => {
|
|
52
|
+
it('有效契约 → abstain(冻结决策)', async () => {
|
|
53
|
+
fs.writeFileSync(path.join(dir, 'openapi.yaml'), VALID_SPEC);
|
|
54
|
+
const decision = await new ContractGate().evaluate({ projectId: 'p', projectPath: dir });
|
|
55
|
+
expect(decision.status).toBe('abstain');
|
|
56
|
+
expect(decision.result.gate).toBe('contract');
|
|
57
|
+
expect(Object.isFrozen(decision)).toBe(true);
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
it('无效契约 → deny', async () => {
|
|
61
|
+
fs.writeFileSync(path.join(dir, 'openapi.yaml'), INVALID_SPEC);
|
|
62
|
+
const decision = await new ContractGate().evaluate({ projectId: 'p', projectPath: dir });
|
|
63
|
+
expect(decision.status).toBe('deny');
|
|
64
|
+
expect(decision.result.passed).toBe(false);
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
it('id/order 字段齐备', () => {
|
|
68
|
+
const gate = new ContractGate();
|
|
69
|
+
expect(gate.id).toBe('contract');
|
|
70
|
+
expect(gate.order).toBe(0);
|
|
71
|
+
});
|
|
72
|
+
});
|
|
73
|
+
});
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 生效门禁集测试(G1:config.yml order/enabled 裁剪,对齐 getEffectiveConstraints 模式)
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import * as fs from 'fs';
|
|
6
|
+
import * as os from 'os';
|
|
7
|
+
import * as path from 'path';
|
|
8
|
+
import { getEffectiveGates } from '../effective-gates';
|
|
9
|
+
|
|
10
|
+
const DEFAULT_ORDER = ['acceptance', 'command', 'contract', 'performance', 'review', 'security'];
|
|
11
|
+
|
|
12
|
+
describe('getEffectiveGates', () => {
|
|
13
|
+
let dir: string;
|
|
14
|
+
|
|
15
|
+
beforeEach(() => {
|
|
16
|
+
dir = fs.mkdtempSync(path.join(os.tmpdir(), 'harness-gates-'));
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
afterEach(() => {
|
|
20
|
+
fs.rmSync(dir, { recursive: true, force: true });
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
function writeConfig(yamlText: string): void {
|
|
24
|
+
const harnessDir = path.join(dir, '.harness');
|
|
25
|
+
fs.mkdirSync(harnessDir, { recursive: true });
|
|
26
|
+
fs.writeFileSync(path.join(harnessDir, 'config.yml'), yamlText);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
it('无 config.yml → 6 门禁按默认 order', () => {
|
|
30
|
+
expect(getEffectiveGates(dir).map(g => g.id)).toEqual(DEFAULT_ORDER);
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
it('gates.order 声明式重排:列出的按列表序,未列出的按默认序排后', () => {
|
|
34
|
+
writeConfig('gates:\n order:\n - security\n - review\n');
|
|
35
|
+
expect(getEffectiveGates(dir).map(g => g.id)).toEqual([
|
|
36
|
+
'security',
|
|
37
|
+
'review',
|
|
38
|
+
'acceptance',
|
|
39
|
+
'command',
|
|
40
|
+
'contract',
|
|
41
|
+
'performance',
|
|
42
|
+
]);
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
it('gates.<id>.enabled:false 裁剪(与 getEffectiveConstraints 同款删除语义)', () => {
|
|
46
|
+
writeConfig('gates:\n review:\n enabled: false\n');
|
|
47
|
+
const ids = getEffectiveGates(dir).map(g => g.id);
|
|
48
|
+
expect(ids).toHaveLength(5);
|
|
49
|
+
expect(ids).not.toContain('review');
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
it('order 与 enabled 组合:禁用胜出,其余按 order 重排', () => {
|
|
53
|
+
writeConfig('gates:\n order:\n - security\n - review\n review:\n enabled: false\n');
|
|
54
|
+
expect(getEffectiveGates(dir).map(g => g.id)).toEqual([
|
|
55
|
+
'security',
|
|
56
|
+
'acceptance',
|
|
57
|
+
'command',
|
|
58
|
+
'contract',
|
|
59
|
+
'performance',
|
|
60
|
+
]);
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
it('order 引用未注册门禁 → 抛错(闭环)', () => {
|
|
64
|
+
writeConfig('gates:\n order:\n - nope\n');
|
|
65
|
+
expect(() => getEffectiveGates(dir)).toThrow(/gates.order 引用了未注册的门禁 "nope"/);
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
it('enabled 段引用未注册门禁 → 抛错(闭环)', () => {
|
|
69
|
+
writeConfig('gates:\n nope:\n enabled: false\n');
|
|
70
|
+
expect(() => getEffectiveGates(dir)).toThrow(/gates.nope 引用了未注册的门禁/);
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
it('order 重复 id → 抛错', () => {
|
|
74
|
+
writeConfig('gates:\n order:\n - review\n - review\n');
|
|
75
|
+
expect(() => getEffectiveGates(dir)).toThrow(/门禁 "review" 重复/);
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
it('enabled:true 不裁剪', () => {
|
|
79
|
+
writeConfig('gates:\n review:\n enabled: true\n');
|
|
80
|
+
expect(getEffectiveGates(dir).map(g => g.id)).toEqual(DEFAULT_ORDER);
|
|
81
|
+
});
|
|
82
|
+
});
|