@dommaker/harness 1.6.0 → 1.8.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 +52 -0
- package/bin/harness.js +17 -1
- package/dist/agents/index.d.ts +6 -2
- package/dist/agents/index.d.ts.map +1 -1
- package/dist/agents/index.js +6 -16
- package/dist/agents/index.js.map +1 -1
- package/dist/cli/commands/__tests__/gate-decision.d.ts +18 -0
- package/dist/cli/commands/__tests__/gate-decision.d.ts.map +1 -0
- package/dist/cli/commands/__tests__/gate-decision.js +26 -0
- package/dist/cli/commands/__tests__/gate-decision.js.map +1 -0
- package/dist/cli/commands/acceptance.d.ts.map +1 -1
- package/dist/cli/commands/acceptance.js +17 -28
- package/dist/cli/commands/acceptance.js.map +1 -1
- package/dist/cli/commands/check.d.ts +11 -3
- package/dist/cli/commands/check.d.ts.map +1 -1
- package/dist/cli/commands/check.js +26 -11
- package/dist/cli/commands/check.js.map +1 -1
- package/dist/cli/commands/command.d.ts +0 -1
- package/dist/cli/commands/command.d.ts.map +1 -1
- package/dist/cli/commands/command.js +8 -6
- package/dist/cli/commands/command.js.map +1 -1
- package/dist/cli/commands/constraints-retire.d.ts.map +1 -1
- package/dist/cli/commands/constraints-retire.js +36 -57
- package/dist/cli/commands/constraints-retire.js.map +1 -1
- package/dist/cli/commands/contract.d.ts.map +1 -1
- package/dist/cli/commands/contract.js +36 -42
- package/dist/cli/commands/contract.js.map +1 -1
- package/dist/cli/commands/definitions.d.ts +2 -0
- package/dist/cli/commands/definitions.d.ts.map +1 -1
- package/dist/cli/commands/definitions.js +11 -6
- package/dist/cli/commands/definitions.js.map +1 -1
- package/dist/cli/commands/failure.d.ts.map +1 -1
- package/dist/cli/commands/failure.js +16 -2
- package/dist/cli/commands/failure.js.map +1 -1
- package/dist/cli/commands/init.d.ts +14 -8
- package/dist/cli/commands/init.d.ts.map +1 -1
- package/dist/cli/commands/init.js +172 -364
- package/dist/cli/commands/init.js.map +1 -1
- package/dist/cli/commands/knowledge-view.d.ts +90 -0
- package/dist/cli/commands/knowledge-view.d.ts.map +1 -0
- package/dist/cli/commands/knowledge-view.js +198 -0
- package/dist/cli/commands/knowledge-view.js.map +1 -0
- package/dist/cli/commands/knowledge.d.ts +137 -52
- package/dist/cli/commands/knowledge.d.ts.map +1 -1
- package/dist/cli/commands/knowledge.js +534 -400
- package/dist/cli/commands/knowledge.js.map +1 -1
- package/dist/cli/commands/passes-gate.d.ts +4 -1
- package/dist/cli/commands/passes-gate.d.ts.map +1 -1
- package/dist/cli/commands/passes-gate.js +11 -2
- package/dist/cli/commands/passes-gate.js.map +1 -1
- package/dist/cli/commands/performance.d.ts.map +1 -1
- package/dist/cli/commands/performance.js +19 -31
- package/dist/cli/commands/performance.js.map +1 -1
- package/dist/cli/commands/report.d.ts.map +1 -1
- package/dist/cli/commands/report.js +2 -1
- package/dist/cli/commands/report.js.map +1 -1
- package/dist/cli/commands/review.d.ts.map +1 -1
- package/dist/cli/commands/review.js +13 -25
- package/dist/cli/commands/review.js.map +1 -1
- package/dist/cli/commands/scaffold-templates.d.ts +73 -0
- package/dist/cli/commands/scaffold-templates.d.ts.map +1 -0
- package/dist/cli/commands/scaffold-templates.js +424 -0
- package/dist/cli/commands/scaffold-templates.js.map +1 -0
- package/dist/cli/commands/scaffold.d.ts +105 -0
- package/dist/cli/commands/scaffold.d.ts.map +1 -0
- package/dist/cli/commands/scaffold.js +263 -0
- package/dist/cli/commands/scaffold.js.map +1 -0
- package/dist/cli/commands/security.d.ts.map +1 -1
- package/dist/cli/commands/security.js +22 -24
- package/dist/cli/commands/security.js.map +1 -1
- package/dist/cli/commands/spec-baseline-check.d.ts +43 -0
- package/dist/cli/commands/spec-baseline-check.d.ts.map +1 -1
- package/dist/cli/commands/spec-baseline-check.js +109 -34
- package/dist/cli/commands/spec-baseline-check.js.map +1 -1
- package/dist/cli/commands/sync-docs/context-syncer.d.ts +21 -2
- package/dist/cli/commands/sync-docs/context-syncer.d.ts.map +1 -1
- package/dist/cli/commands/sync-docs/context-syncer.js +112 -28
- package/dist/cli/commands/sync-docs/context-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 +86 -27
- package/dist/cli/commands/sync-docs/index.js.map +1 -1
- package/dist/cli/commands/sync-docs/project-reader.d.ts +24 -1
- package/dist/cli/commands/sync-docs/project-reader.d.ts.map +1 -1
- package/dist/cli/commands/sync-docs/project-reader.js +33 -7
- package/dist/cli/commands/sync-docs/project-reader.js.map +1 -1
- package/dist/cli/commands/validate.d.ts +0 -16
- package/dist/cli/commands/validate.d.ts.map +1 -1
- package/dist/cli/commands/validate.js +2 -97
- package/dist/cli/commands/validate.js.map +1 -1
- package/dist/cli/gate-command.d.ts +37 -0
- package/dist/cli/gate-command.d.ts.map +1 -0
- package/dist/cli/gate-command.js +64 -0
- package/dist/cli/gate-command.js.map +1 -0
- package/dist/completion-checkers/index.d.ts +1 -1
- package/dist/completion-checkers/index.d.ts.map +1 -1
- package/dist/completion-checkers/index.js +0 -15
- package/dist/completion-checkers/index.js.map +1 -1
- package/dist/context/knowledge-injector.d.ts +4 -2
- package/dist/context/knowledge-injector.d.ts.map +1 -1
- package/dist/context/knowledge-injector.js +13 -6
- package/dist/context/knowledge-injector.js.map +1 -1
- package/dist/core/constraints/agent-prompt-renderer.js +2 -2
- package/dist/core/constraints/agent-prompt-renderer.js.map +1 -1
- package/dist/core/constraints/capabilities-reconcile.d.ts +20 -0
- package/dist/core/constraints/capabilities-reconcile.d.ts.map +1 -1
- package/dist/core/constraints/capabilities-reconcile.js +26 -4
- package/dist/core/constraints/capabilities-reconcile.js.map +1 -1
- package/dist/core/constraints/check-cache.d.ts +1 -1
- package/dist/core/constraints/check-cache.js +1 -1
- package/dist/core/constraints/checker.d.ts +8 -3
- package/dist/core/constraints/checker.d.ts.map +1 -1
- package/dist/core/constraints/checker.js +30 -19
- package/dist/core/constraints/checker.js.map +1 -1
- package/dist/core/constraints/checkers/capability-sync.d.ts +3 -1
- package/dist/core/constraints/checkers/capability-sync.d.ts.map +1 -1
- package/dist/core/constraints/checkers/capability-sync.js +17 -24
- package/dist/core/constraints/checkers/capability-sync.js.map +1 -1
- package/dist/core/constraints/checkers/context-doc-sync.d.ts +1 -1
- package/dist/core/constraints/checkers/context-doc-sync.d.ts.map +1 -1
- package/dist/core/constraints/checkers/context-doc-sync.js +9 -6
- package/dist/core/constraints/checkers/context-doc-sync.js.map +1 -1
- package/dist/core/constraints/checkers/docs-freshness.d.ts.map +1 -1
- package/dist/core/constraints/checkers/docs-freshness.js +24 -30
- package/dist/core/constraints/checkers/docs-freshness.js.map +1 -1
- package/dist/core/constraints/checkers/governance-presence.d.ts.map +1 -1
- package/dist/core/constraints/checkers/governance-presence.js +10 -4
- package/dist/core/constraints/checkers/governance-presence.js.map +1 -1
- package/dist/core/constraints/checkers/types.d.ts +10 -6
- package/dist/core/constraints/checkers/types.d.ts.map +1 -1
- package/dist/core/constraints/checkers/types.js +7 -3
- package/dist/core/constraints/checkers/types.js.map +1 -1
- package/dist/core/constraints/context-builder.d.ts +11 -10
- package/dist/core/constraints/context-builder.d.ts.map +1 -1
- package/dist/core/constraints/context-builder.js +31 -27
- package/dist/core/constraints/context-builder.js.map +1 -1
- package/dist/core/constraints/context-reconcile.d.ts +75 -0
- package/dist/core/constraints/context-reconcile.d.ts.map +1 -0
- package/dist/core/constraints/context-reconcile.js +161 -0
- package/dist/core/constraints/context-reconcile.js.map +1 -0
- package/dist/core/constraints/doc-freshness/runner.d.ts +7 -0
- package/dist/core/constraints/doc-freshness/runner.d.ts.map +1 -1
- package/dist/core/constraints/doc-freshness/runner.js +8 -3
- package/dist/core/constraints/doc-freshness/runner.js.map +1 -1
- package/dist/core/constraints/git-evidence.js +1 -1
- package/dist/core/constraints/git-evidence.js.map +1 -1
- package/dist/core/constraints/injection-drift.d.ts +7 -2
- package/dist/core/constraints/injection-drift.d.ts.map +1 -1
- package/dist/core/constraints/injection-drift.js +13 -7
- package/dist/core/constraints/injection-drift.js.map +1 -1
- package/dist/core/constraints/run-env.d.ts +92 -0
- package/dist/core/constraints/run-env.d.ts.map +1 -0
- package/dist/core/constraints/run-env.js +175 -0
- package/dist/core/constraints/run-env.js.map +1 -0
- package/dist/core/constraints/triggers.d.ts +22 -0
- package/dist/core/constraints/triggers.d.ts.map +1 -0
- package/dist/core/constraints/triggers.js +31 -0
- package/dist/core/constraints/triggers.js.map +1 -0
- package/dist/core/effective-constraints.d.ts +13 -4
- package/dist/core/effective-constraints.d.ts.map +1 -1
- package/dist/core/effective-constraints.js +16 -6
- package/dist/core/effective-constraints.js.map +1 -1
- package/dist/core/effective-set.d.ts +8 -15
- package/dist/core/effective-set.d.ts.map +1 -1
- package/dist/core/effective-set.js +7 -12
- package/dist/core/effective-set.js.map +1 -1
- package/dist/core/index.d.ts +2 -1
- package/dist/core/index.d.ts.map +1 -1
- package/dist/core/index.js.map +1 -1
- package/dist/core/project-config-loader.d.ts +39 -17
- package/dist/core/project-config-loader.d.ts.map +1 -1
- package/dist/core/project-config-loader.js +51 -122
- package/dist/core/project-config-loader.js.map +1 -1
- package/dist/core/validators/check-handlers/http.d.ts +2 -2
- package/dist/core/validators/check-handlers/http.d.ts.map +1 -1
- package/dist/core/validators/check-handlers/http.js +12 -7
- package/dist/core/validators/check-handlers/http.js.map +1 -1
- package/dist/core/validators/index.d.ts +1 -1
- package/dist/core/validators/index.d.ts.map +1 -1
- package/dist/core/validators/passes-gate.d.ts.map +1 -1
- package/dist/core/validators/passes-gate.js +2 -2
- package/dist/core/validators/passes-gate.js.map +1 -1
- package/dist/failure/classifier.d.ts +1 -1
- package/dist/failure/classifier.d.ts.map +1 -1
- package/dist/failure/classifier.js +8 -8
- package/dist/failure/classifier.js.map +1 -1
- package/dist/failure/index.d.ts +2 -1
- package/dist/failure/index.d.ts.map +1 -1
- package/dist/failure/index.js +9 -17
- package/dist/failure/index.js.map +1 -1
- package/dist/failure/recorder.d.ts +1 -1
- package/dist/failure/recorder.d.ts.map +1 -1
- package/dist/gates/checker-gate.d.ts +2 -1
- package/dist/gates/checker-gate.d.ts.map +1 -1
- package/dist/gates/checker-gate.js +3 -2
- package/dist/gates/checker-gate.js.map +1 -1
- package/dist/gates/command.d.ts +10 -4
- package/dist/gates/command.d.ts.map +1 -1
- package/dist/gates/command.js +36 -62
- package/dist/gates/command.js.map +1 -1
- package/dist/gates/contract.d.ts.map +1 -1
- package/dist/gates/contract.js +0 -4
- package/dist/gates/contract.js.map +1 -1
- package/dist/gates/definitions.d.ts +2 -1
- package/dist/gates/definitions.d.ts.map +1 -1
- package/dist/gates/definitions.js +6 -5
- package/dist/gates/definitions.js.map +1 -1
- package/dist/gates/index.d.ts +0 -4
- package/dist/gates/index.d.ts.map +1 -1
- package/dist/gates/index.js +1 -6
- package/dist/gates/index.js.map +1 -1
- package/dist/gates/performance.d.ts.map +1 -1
- package/dist/gates/performance.js +0 -4
- package/dist/gates/performance.js.map +1 -1
- package/dist/gates/runner.d.ts +1 -1
- package/dist/gates/runner.js +1 -1
- package/dist/gates/security.d.ts +1 -1
- package/dist/gates/security.d.ts.map +1 -1
- package/dist/gates/security.js +5 -6
- package/dist/gates/security.js.map +1 -1
- package/dist/gates/types.d.ts +14 -17
- package/dist/gates/types.d.ts.map +1 -1
- package/dist/gates/types.js.map +1 -1
- package/dist/hooks/bootstrap.d.ts +6 -4
- package/dist/hooks/bootstrap.d.ts.map +1 -1
- package/dist/hooks/bootstrap.js +25 -13
- package/dist/hooks/bootstrap.js.map +1 -1
- package/dist/hooks/config.d.ts +6 -2
- package/dist/hooks/config.d.ts.map +1 -1
- package/dist/hooks/config.js +5 -1
- package/dist/hooks/config.js.map +1 -1
- package/dist/hooks/index.d.ts +1 -1
- package/dist/hooks/index.d.ts.map +1 -1
- package/dist/hooks/index.js +0 -15
- package/dist/hooks/index.js.map +1 -1
- package/dist/hooks/pipeline.d.ts.map +1 -1
- package/dist/hooks/pipeline.js +2 -4
- package/dist/hooks/pipeline.js.map +1 -1
- package/dist/hooks/registry.d.ts +11 -8
- package/dist/hooks/registry.d.ts.map +1 -1
- package/dist/hooks/registry.js +22 -7
- package/dist/hooks/registry.js.map +1 -1
- package/dist/hooks/types.d.ts +23 -7
- package/dist/hooks/types.d.ts.map +1 -1
- package/dist/index.d.ts +4 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -4
- package/dist/index.js.map +1 -1
- package/dist/knowledge/audit-scoring.d.ts +87 -0
- package/dist/knowledge/audit-scoring.d.ts.map +1 -0
- package/dist/knowledge/audit-scoring.js +470 -0
- package/dist/knowledge/audit-scoring.js.map +1 -0
- package/dist/knowledge/audit.d.ts +11 -59
- package/dist/knowledge/audit.d.ts.map +1 -1
- package/dist/knowledge/audit.js +47 -522
- package/dist/knowledge/audit.js.map +1 -1
- package/dist/knowledge/import.d.ts.map +1 -1
- package/dist/knowledge/import.js +2 -0
- package/dist/knowledge/import.js.map +1 -1
- package/dist/knowledge/index-generator.d.ts.map +1 -1
- package/dist/knowledge/index-generator.js +4 -5
- package/dist/knowledge/index-generator.js.map +1 -1
- package/dist/knowledge/index.d.ts +3 -2
- package/dist/knowledge/index.d.ts.map +1 -1
- package/dist/knowledge/index.js +3 -16
- package/dist/knowledge/index.js.map +1 -1
- package/dist/knowledge/ingest.js +1 -1
- package/dist/knowledge/ingest.js.map +1 -1
- package/dist/knowledge/lifecycle.d.ts.map +1 -1
- package/dist/knowledge/lifecycle.js +4 -2
- package/dist/knowledge/lifecycle.js.map +1 -1
- package/dist/knowledge/migration.d.ts +3 -0
- package/dist/knowledge/migration.d.ts.map +1 -1
- package/dist/knowledge/migration.js +8 -2
- package/dist/knowledge/migration.js.map +1 -1
- package/dist/knowledge/query.d.ts +6 -0
- package/dist/knowledge/query.d.ts.map +1 -1
- package/dist/knowledge/query.js +8 -2
- package/dist/knowledge/query.js.map +1 -1
- package/dist/knowledge/reference-tracker.d.ts.map +1 -1
- package/dist/knowledge/reference-tracker.js +4 -6
- package/dist/knowledge/reference-tracker.js.map +1 -1
- package/dist/knowledge/store.d.ts +22 -16
- package/dist/knowledge/store.d.ts.map +1 -1
- package/dist/knowledge/store.js +53 -12
- package/dist/knowledge/store.js.map +1 -1
- package/dist/knowledge/tree-walker.d.ts +19 -0
- package/dist/knowledge/tree-walker.d.ts.map +1 -0
- package/dist/knowledge/tree-walker.js +30 -0
- package/dist/knowledge/tree-walker.js.map +1 -0
- package/dist/knowledge/types.d.ts +16 -0
- package/dist/knowledge/types.d.ts.map +1 -1
- package/dist/monitoring/traces.d.ts +5 -3
- package/dist/monitoring/traces.d.ts.map +1 -1
- package/dist/monitoring/traces.js +19 -5
- package/dist/monitoring/traces.js.map +1 -1
- package/dist/test-setup/jsonl-fake-fs.d.ts +30 -0
- package/dist/test-setup/jsonl-fake-fs.d.ts.map +1 -0
- package/dist/test-setup/jsonl-fake-fs.js +49 -0
- package/dist/test-setup/jsonl-fake-fs.js.map +1 -0
- package/dist/tools/index.d.ts +4 -1
- package/dist/tools/index.d.ts.map +1 -1
- package/dist/tools/index.js +7 -15
- package/dist/tools/index.js.map +1 -1
- package/dist/types/checkpoint.d.ts +5 -0
- package/dist/types/checkpoint.d.ts.map +1 -1
- package/dist/types/passes-gate.d.ts +0 -13
- package/dist/types/passes-gate.d.ts.map +1 -1
- package/dist/types/project-config.d.ts +18 -0
- package/dist/types/project-config.d.ts.map +1 -1
- package/dist/types/trace.d.ts +13 -4
- package/dist/types/trace.d.ts.map +1 -1
- package/dist/types/trace.js +4 -3
- package/dist/types/trace.js.map +1 -1
- package/dist/utils/exec.d.ts +4 -27
- package/dist/utils/exec.d.ts.map +1 -1
- package/dist/utils/exec.js +4 -94
- package/dist/utils/exec.js.map +1 -1
- package/dist/utils/jsonl.d.ts +17 -7
- package/dist/utils/jsonl.d.ts.map +1 -1
- package/dist/utils/jsonl.js +144 -17
- package/dist/utils/jsonl.js.map +1 -1
- package/dist/utils/numeric-flag.d.ts +44 -0
- package/dist/utils/numeric-flag.d.ts.map +1 -0
- package/dist/utils/numeric-flag.js +62 -0
- package/dist/utils/numeric-flag.js.map +1 -0
- package/package.json +1 -1
- package/src/CONTEXT.md +5 -2
- package/src/__tests__/acceptance-gate.test.ts +7 -4
- package/src/__tests__/checker-extra.test.ts +3 -2
- package/src/__tests__/checker.test.ts +4 -3
- package/src/__tests__/checkpoint.test.ts +26 -9
- package/src/__tests__/context-files-resolution.test.ts +29 -15
- package/src/__tests__/contract-gate.test.ts +19 -40
- package/src/__tests__/error-classifier.test.ts +1 -1
- package/src/__tests__/exec.test.ts +45 -107
- package/src/__tests__/failure-recorder.test.ts +7 -5
- package/src/__tests__/hooks-pipeline.test.ts +34 -47
- package/src/__tests__/layering.test.ts +217 -25
- package/src/__tests__/passes-gate.test.ts +9 -4
- package/src/__tests__/performance-gate.test.ts +3 -15
- package/src/__tests__/project-config-loader-extra.test.ts +31 -57
- package/src/__tests__/project-config-loader.test.ts +6 -3
- package/src/__tests__/public-exports.test.ts +27 -6
- package/src/__tests__/public-type-surface.test.ts +360 -0
- package/src/__tests__/public-value-surface.test.ts +182 -0
- package/src/__tests__/review-gate.test.ts +4 -6
- package/src/__tests__/security-gate.test.ts +5 -20
- package/src/__tests__/skip-semantics.test.ts +5 -3
- package/src/__tests__/spec-validator.test.ts +3 -2
- package/src/__tests__/sub-barrels-explicit.test.ts +87 -0
- package/src/__tests__/test-fixture-hygiene.test.ts +53 -0
- package/src/__tests__/trace-analyzer-extra.test.ts +5 -3
- package/src/__tests__/trace-analyzer.test.ts +5 -3
- package/src/__tests__/trace-collector.test.ts +5 -3
- package/src/agents/index.ts +13 -2
- package/src/cli/__tests__/gate-command.test.ts +162 -0
- package/src/cli/commands/CONTEXT.md +29 -5
- package/src/cli/commands/__tests__/acceptance.test.ts +24 -25
- package/src/cli/commands/__tests__/bin-exit-mapping.test.ts +15 -0
- package/src/cli/commands/__tests__/check-drift.test.ts +42 -1
- package/src/cli/commands/__tests__/check-read-count.test.ts +167 -0
- package/src/cli/commands/__tests__/check-skip-output.test.ts +4 -1
- package/src/cli/commands/__tests__/check.test.ts +43 -36
- package/src/cli/commands/__tests__/command-gate-dead-options.test.ts +60 -0
- package/src/cli/commands/__tests__/command.test.ts +33 -17
- package/src/cli/commands/__tests__/constraints-retire.test.ts +52 -0
- package/src/cli/commands/__tests__/context-syncer.test.ts +141 -0
- package/src/cli/commands/__tests__/contract.test.ts +20 -20
- package/src/cli/commands/__tests__/failure.test.ts +5 -4
- package/src/cli/commands/__tests__/gate-decision.ts +30 -0
- package/src/cli/commands/__tests__/init-dead-options.test.ts +50 -0
- package/src/cli/commands/__tests__/init-governance-io.test.ts +139 -0
- package/src/cli/commands/__tests__/init-injection.test.ts +3 -3
- package/src/cli/commands/__tests__/init-ondisk.test.ts +867 -0
- package/src/cli/commands/__tests__/init.test.ts +82 -6
- package/src/cli/commands/__tests__/knowledge-audit-threshold.test.ts +146 -0
- package/src/cli/commands/__tests__/knowledge-flywheel-consistency.test.ts +1 -1
- package/src/cli/commands/__tests__/knowledge-tone-mapping.test.ts +244 -0
- package/src/cli/commands/__tests__/knowledge-view.test.ts +839 -0
- package/src/cli/commands/__tests__/knowledge.test.ts +33 -15
- package/src/cli/commands/__tests__/numeric-flag-assembly.test.ts +368 -0
- package/src/cli/commands/__tests__/passes-gate-dead-options.test.ts +89 -1
- package/src/cli/commands/__tests__/performance.test.ts +19 -21
- package/src/cli/commands/__tests__/posteval-plan.test.ts +5 -3
- package/src/cli/commands/__tests__/pre-push-hook.test.ts +181 -0
- package/src/cli/commands/__tests__/project-path-anchoring.test.ts +95 -2
- package/src/cli/commands/__tests__/project-path-convention.test.ts +103 -25
- package/src/cli/commands/__tests__/registry.test.ts +2 -0
- package/src/cli/commands/__tests__/report.test.ts +23 -1
- package/src/cli/commands/__tests__/review.test.ts +10 -17
- package/src/cli/commands/__tests__/scaffold.test.ts +505 -0
- package/src/cli/commands/__tests__/security-default-scan.test.ts +75 -0
- package/src/cli/commands/__tests__/security.test.ts +21 -24
- package/src/cli/commands/__tests__/spec-baseline-check.test.ts +3 -2
- package/src/cli/commands/__tests__/spec-baseline-index-cap.test.ts +131 -0
- package/src/cli/commands/__tests__/spec-baseline-read-count.test.ts +325 -0
- package/src/cli/commands/__tests__/sync-docs-agents.test.ts +19 -4
- package/src/cli/commands/__tests__/sync-docs-context-drift.test.ts +232 -0
- package/src/cli/commands/__tests__/sync-docs-module-order.test.ts +111 -0
- package/src/cli/commands/__tests__/sync-docs-read-count.test.ts +192 -0
- package/src/cli/commands/__tests__/sync-docs.test.ts +3 -2
- package/src/cli/commands/__tests__/validate.test.ts +1 -48
- package/src/cli/commands/acceptance.ts +24 -29
- package/src/cli/commands/check.ts +40 -17
- package/src/cli/commands/command.ts +9 -8
- package/src/cli/commands/constraints-retire.ts +57 -70
- package/src/cli/commands/contract.ts +44 -47
- package/src/cli/commands/definitions.ts +12 -6
- package/src/cli/commands/failure.ts +16 -3
- package/src/cli/commands/init.ts +242 -389
- package/src/cli/commands/knowledge-view.ts +224 -0
- package/src/cli/commands/knowledge.ts +643 -448
- package/src/cli/commands/passes-gate.ts +12 -3
- package/src/cli/commands/performance.ts +31 -38
- package/src/cli/commands/report.ts +3 -2
- package/src/cli/commands/review.ts +22 -28
- package/src/cli/commands/scaffold-templates.ts +406 -0
- package/src/cli/commands/scaffold.ts +302 -0
- package/src/cli/commands/security.ts +27 -23
- package/src/cli/commands/spec-baseline-check.ts +158 -33
- package/src/cli/commands/sync-docs/__tests__/context-export-surface.test.ts +102 -0
- package/src/cli/commands/sync-docs/__tests__/module-descriptions.test.ts +196 -0
- package/src/cli/commands/sync-docs/context-syncer.ts +137 -31
- package/src/cli/commands/sync-docs/index.ts +103 -28
- package/src/cli/commands/sync-docs/project-reader.ts +51 -7
- package/src/cli/commands/validate.ts +1 -104
- package/src/cli/gate-command.ts +87 -0
- package/src/completion-checkers/index.ts +10 -1
- package/src/context/CONTEXT.md +2 -1
- package/src/context/__tests__/knowledge-injector.test.ts +27 -0
- package/src/context/__tests__/session-manager.test.ts +19 -12
- package/src/context/knowledge-injector.ts +14 -7
- package/src/core/CONTEXT.md +10 -7
- package/src/core/__tests__/effective-set.test.ts +36 -60
- package/src/core/constraints/__tests__/context-reconcile.test.ts +246 -0
- package/src/core/constraints/__tests__/run-env.test.ts +377 -0
- package/src/core/constraints/__tests__/triggers.test.ts +70 -0
- package/src/core/constraints/agent-prompt-renderer.ts +1 -1
- package/src/core/constraints/capabilities-reconcile.ts +28 -4
- package/src/core/constraints/check-cache.ts +1 -1
- package/src/core/constraints/checker.ts +30 -14
- package/src/core/constraints/checkers/__tests__/context-doc-sync.test.ts +8 -3
- package/src/core/constraints/checkers/__tests__/governance-presence.test.ts +16 -7
- package/src/core/constraints/checkers/__tests__/no-hardcoded-credentials.test.ts +2 -1
- package/src/core/constraints/checkers/capability-sync.ts +27 -28
- package/src/core/constraints/checkers/context-doc-sync.ts +9 -7
- package/src/core/constraints/checkers/docs-freshness.ts +28 -33
- package/src/core/constraints/checkers/governance-presence.ts +10 -6
- package/src/core/constraints/checkers/types.ts +14 -8
- package/src/core/constraints/context-builder.ts +35 -26
- package/src/core/constraints/context-reconcile.ts +200 -0
- package/src/core/constraints/doc-freshness/__tests__/runner.test.ts +25 -0
- package/src/core/constraints/doc-freshness/runner.ts +18 -4
- package/src/core/constraints/git-evidence.ts +1 -1
- package/src/core/constraints/injection-drift.ts +19 -8
- package/src/core/constraints/run-env.ts +207 -0
- package/src/core/constraints/triggers.ts +33 -0
- package/src/core/effective-constraints.ts +17 -6
- package/src/core/effective-set.ts +8 -24
- package/src/core/index.ts +2 -1
- package/src/core/project-config-loader.ts +54 -102
- package/src/core/validators/__tests__/http-retry-sleep.test.ts +231 -0
- package/src/core/validators/check-handlers/http.ts +18 -7
- package/src/core/validators/index.ts +0 -1
- package/src/core/validators/passes-gate.ts +2 -3
- package/src/failure/CONTEXT.md +2 -1
- package/src/failure/classifier.ts +2 -2
- package/src/failure/index.ts +14 -2
- package/src/failure/recorder.ts +1 -1
- package/src/gates/CONTEXT.md +13 -7
- package/src/gates/__tests__/checker-gate.test.ts +1 -1
- package/src/gates/__tests__/command-single-predicate.test.ts +135 -0
- package/src/gates/__tests__/command.test.ts +8 -15
- package/src/gates/__tests__/contract.test.ts +5 -5
- package/src/gates/__tests__/gate-interface.test.ts +0 -17
- package/src/gates/__tests__/performance.test.ts +0 -13
- package/src/gates/__tests__/review.test.ts +0 -1
- package/src/gates/__tests__/runner.test.ts +1 -1
- package/src/gates/__tests__/security-default-scan.test.ts +97 -0
- package/src/gates/__tests__/security.test.ts +0 -13
- package/src/gates/checker-gate.ts +3 -2
- package/src/gates/command.ts +51 -74
- package/src/gates/contract.ts +0 -5
- package/src/gates/definitions.ts +6 -5
- package/src/gates/index.ts +0 -5
- package/src/gates/performance.ts +1 -6
- package/src/gates/runner.ts +1 -1
- package/src/gates/security.ts +9 -11
- package/src/gates/types.ts +20 -23
- package/src/hooks/CONTEXT.md +10 -9
- package/src/hooks/__tests__/bootstrap.test.ts +29 -6
- package/src/hooks/__tests__/pipeline.test.ts +28 -17
- package/src/hooks/__tests__/registry.test.ts +51 -3
- package/src/hooks/bootstrap.ts +35 -15
- package/src/hooks/config.ts +6 -2
- package/src/hooks/index.ts +10 -1
- package/src/hooks/pipeline.ts +4 -6
- package/src/hooks/registry.ts +31 -12
- package/src/hooks/types.ts +24 -7
- package/src/index.ts +1 -3
- package/src/knowledge/CONTEXT.md +14 -7
- package/src/knowledge/__tests__/audit-labels.test.ts +8 -5
- package/src/knowledge/__tests__/audit-scoring.test.ts +230 -0
- package/src/knowledge/__tests__/audit-write-count.test.ts +131 -0
- package/src/knowledge/__tests__/audit.test.ts +291 -208
- package/src/knowledge/__tests__/doctor.test.ts +3 -2
- package/src/knowledge/__tests__/ingest.test.ts +3 -2
- package/src/knowledge/__tests__/lifecycle.test.ts +57 -2
- package/src/knowledge/__tests__/migration.test.ts +35 -6
- package/src/knowledge/__tests__/query.test.ts +3 -2
- package/src/knowledge/__tests__/reference-tracker.test.ts +23 -2
- package/src/knowledge/__tests__/store.test.ts +149 -4
- package/src/knowledge/__tests__/tree-walker.test.ts +132 -0
- package/src/knowledge/audit-scoring.ts +589 -0
- package/src/knowledge/audit.ts +55 -599
- package/src/knowledge/import.ts +2 -0
- package/src/knowledge/index-generator.ts +4 -6
- package/src/knowledge/index.ts +27 -2
- package/src/knowledge/ingest.ts +1 -1
- package/src/knowledge/lifecycle.ts +5 -2
- package/src/knowledge/migration.ts +8 -2
- package/src/knowledge/query.ts +8 -1
- package/src/knowledge/reference-tracker.ts +5 -7
- package/src/knowledge/store.ts +75 -16
- package/src/knowledge/tree-walker.ts +29 -0
- package/src/knowledge/types.ts +19 -0
- package/src/monitoring/CONTEXT.md +5 -2
- package/src/monitoring/__tests__/context-tracker.test.ts +16 -8
- package/src/monitoring/__tests__/trace-file-anchoring.test.ts +80 -0
- package/src/monitoring/traces.ts +19 -5
- package/src/release/CONTEXT.md +1 -1
- package/src/release/__tests__/integrity.test.ts +21 -12
- package/src/test-setup/__tests__/jsonl-fake-fs.test.ts +98 -0
- package/src/test-setup/jsonl-fake-fs.ts +59 -0
- package/src/tools/index.ts +5 -1
- package/src/types/checkpoint.ts +6 -0
- package/src/types/passes-gate.ts +0 -17
- package/src/types/project-config.ts +21 -0
- package/src/types/trace.ts +14 -4
- package/src/utils/__tests__/file-walk.test.ts +3 -2
- package/src/utils/__tests__/jsonl-bounded-read.test.ts +106 -0
- package/src/utils/__tests__/jsonl-skip-disposition.test.ts +10 -6
- package/src/utils/__tests__/jsonl.test.ts +61 -14
- package/src/utils/__tests__/numeric-flag.test.ts +80 -0
- package/src/utils/__tests__/package-version.test.ts +2 -2
- package/src/utils/exec.ts +4 -64
- package/src/utils/jsonl.ts +159 -20
- package/src/utils/numeric-flag.ts +64 -0
- package/dist/failure/types.d.ts +0 -9
- package/dist/failure/types.d.ts.map +0 -1
- package/dist/failure/types.js +0 -25
- package/dist/failure/types.js.map +0 -1
- package/dist/gates/effective-gates.d.ts +0 -30
- package/dist/gates/effective-gates.d.ts.map +0 -1
- package/dist/gates/effective-gates.js +0 -81
- package/dist/gates/effective-gates.js.map +0 -1
- package/src/failure/types.ts +0 -9
- package/src/gates/__tests__/effective-gates.test.ts +0 -77
- package/src/gates/effective-gates.ts +0 -99
|
@@ -0,0 +1,377 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 运行级观察面测试(ADR-0023 决策 1/4)
|
|
3
|
+
*
|
|
4
|
+
* 钉五件事:① 一次运行内同一份 trace 至多读一次(懒建 + memo);② 不同尾部窗口的口径
|
|
5
|
+
* 与 `readJsonl(..., {tail})` 逐字一致(含坏行占槽位)——这是"合并两处读"不改变判定的前提;
|
|
6
|
+
* ③ context-builder 用注入的 env,而不是自己再读一遍;
|
|
7
|
+
* ④ 项目配置(config.yml 与自定义约束文件)一次运行一份快照(ADR-0023 决策 2:
|
|
8
|
+
* 进程级缓存撤销后的口径);
|
|
9
|
+
* ⑤ 能力表一次解析多消费——两个 checker 共用同一枚 env 时文档只读一次、判定不变
|
|
10
|
+
* (ADR-0023 决策 4)。
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import { jest } from '@jest/globals';
|
|
14
|
+
import * as fs from 'fs';
|
|
15
|
+
import * as os from 'os';
|
|
16
|
+
import * as path from 'path';
|
|
17
|
+
import { createRunEnv, resolveRunEnv, TRACE_TAIL_WINDOW, type RunEnv } from '../run-env';
|
|
18
|
+
import { buildConstraintContext } from '../context-builder';
|
|
19
|
+
import { buildCheckEnv, normalizeCheckOutcome } from '../checkers/types';
|
|
20
|
+
import { docsFreshness } from '../checkers/docs-freshness';
|
|
21
|
+
import { capabilitySync } from '../checkers/capability-sync';
|
|
22
|
+
import { collectSourceFiles, reconcileCapabilities } from '../capabilities-reconcile';
|
|
23
|
+
import { loadRawProjectConfig } from '../../project-config-loader';
|
|
24
|
+
import { createProjectFixture, writeProjectConfig } from '../../../test-setup/project-fixture';
|
|
25
|
+
import { readJsonl } from '../../../utils/jsonl';
|
|
26
|
+
import { DEFAULT_TRACE_FILE, type ExecutionTrace } from '../../../types/trace';
|
|
27
|
+
import type { GitEvidence } from '../git-evidence';
|
|
28
|
+
|
|
29
|
+
// ts-jest 的 namespace 导入属性不可重定义(jest.spyOn 会抛),只包一层 readFileSync
|
|
30
|
+
// 做读计数——其余 fs 能力用真实实现,夹具搭建与被检代码读取都不受影响
|
|
31
|
+
// (同一手法先例:src/__tests__/context-files-resolution.test.ts)
|
|
32
|
+
jest.mock('fs', () => {
|
|
33
|
+
const actual = jest.requireActual<typeof import('fs')>('fs');
|
|
34
|
+
return { ...actual, readFileSync: jest.fn(actual.readFileSync) };
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
const readSpy = (fs as unknown as { readFileSync: jest.Mock }).readFileSync;
|
|
38
|
+
const capsReads = () =>
|
|
39
|
+
readSpy.mock.calls.filter(call => String(call[0]).endsWith('CAPABILITIES.md')).length;
|
|
40
|
+
|
|
41
|
+
const tracePathOf = (projectPath: string) => path.join(projectPath, DEFAULT_TRACE_FILE);
|
|
42
|
+
|
|
43
|
+
function writeTraces(projectPath: string, lines: string[]): void {
|
|
44
|
+
const file = tracePathOf(projectPath);
|
|
45
|
+
fs.mkdirSync(path.dirname(file), { recursive: true });
|
|
46
|
+
fs.writeFileSync(file, lines.join('\n') + '\n', 'utf-8');
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
const traceLine = (id: string, result: 'pass' | 'fail'): string =>
|
|
50
|
+
JSON.stringify({ constraintId: id, level: 'iron_law', timestamp: 1, result });
|
|
51
|
+
|
|
52
|
+
const CORRUPT = '{"constraintId":"broken"';
|
|
53
|
+
|
|
54
|
+
describe('createRunEnv', () => {
|
|
55
|
+
let dir: string;
|
|
56
|
+
|
|
57
|
+
beforeEach(() => {
|
|
58
|
+
dir = fs.mkdtempSync(path.join(os.tmpdir(), 'harness-run-env-'));
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
afterEach(() => {
|
|
62
|
+
fs.rmSync(dir, { recursive: true, force: true });
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
it('一次运行内同一份 trace 至多读一次:取过之后再改文件,仍是老内容', () => {
|
|
66
|
+
writeTraces(dir, [traceLine('a', 'pass')]);
|
|
67
|
+
const env = createRunEnv(dir);
|
|
68
|
+
expect(env.traceTail(1).records.map(r => r.constraintId)).toEqual(['a']);
|
|
69
|
+
|
|
70
|
+
// 追加一条 fail:若没有 memo,第二次取会读到新尾部并判出 fail
|
|
71
|
+
writeTraces(dir, [traceLine('a', 'pass'), traceLine('b', 'fail')]);
|
|
72
|
+
expect(env.traceTail(2).records.map(r => r.constraintId)).toEqual(['a']);
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
it('懒建:造 env 不碰文件,之后落的数据才读得到', () => {
|
|
76
|
+
const env = createRunEnv(dir);
|
|
77
|
+
writeTraces(dir, [traceLine('late', 'fail')]);
|
|
78
|
+
expect(env.traceTail(5).records.map(r => r.constraintId)).toEqual(['late']);
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
it('缺 trace 文件返回空窗口,不抛', () => {
|
|
82
|
+
expect(createRunEnv(dir).traceTail(10)).toEqual({ records: [], skippedLines: 0 });
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
it('窗口口径与 readJsonl 的 tail 逐字一致(含坏行占槽位)', () => {
|
|
86
|
+
const lines = [
|
|
87
|
+
traceLine('old', 'fail'),
|
|
88
|
+
traceLine('x1', 'pass'),
|
|
89
|
+
CORRUPT,
|
|
90
|
+
traceLine('x2', 'pass'),
|
|
91
|
+
traceLine('x3', 'pass'),
|
|
92
|
+
];
|
|
93
|
+
writeTraces(dir, lines);
|
|
94
|
+
const env = createRunEnv(dir);
|
|
95
|
+
const file = tracePathOf(dir);
|
|
96
|
+
|
|
97
|
+
for (const limit of [1, 2, 3, 4, 5]) {
|
|
98
|
+
expect(env.traceTail(limit)).toEqual(readJsonl<ExecutionTrace>(file, 'skip', { tail: limit }));
|
|
99
|
+
}
|
|
100
|
+
// 坏行占住尾部槽位:limit=3 时最近三条含一条坏行 → 有效记录只剩 2 条
|
|
101
|
+
expect(env.traceTail(3).records.map(r => r.constraintId)).toEqual(['x2', 'x3']);
|
|
102
|
+
expect(env.traceTail(3).skippedLines).toBe(1);
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
it('超过窗口上限直接抛,不静默少给', () => {
|
|
106
|
+
writeTraces(dir, Array.from({ length: 40 }, (_, i) => traceLine(`t${i}`, 'pass')));
|
|
107
|
+
const env = createRunEnv(dir);
|
|
108
|
+
expect(() => env.traceTail(TRACE_TAIL_WINDOW + 1)).toThrow(`尾部窗口上限 ${TRACE_TAIL_WINDOW} 行`);
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
it('projectPath 原样带出(每个 IO 点锚同一个根,harness#95)', () => {
|
|
112
|
+
expect(createRunEnv(dir).projectPath).toBe(dir);
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
it('sourceRoots 一次运行内至多探测一次', () => {
|
|
116
|
+
fs.mkdirSync(path.join(dir, 'src'), { recursive: true });
|
|
117
|
+
fs.writeFileSync(path.join(dir, 'src', 'a.ts'), 'export const a = 1;\n');
|
|
118
|
+
const env = createRunEnv(dir);
|
|
119
|
+
expect(env.sourceRoots()).toContain('src');
|
|
120
|
+
|
|
121
|
+
// 运行中途新增源码根:memo 生效,同一份结果不再走盘
|
|
122
|
+
fs.mkdirSync(path.join(dir, 'packages'), { recursive: true });
|
|
123
|
+
fs.writeFileSync(path.join(dir, 'packages', 'b.ts'), 'export const b = 1;\n');
|
|
124
|
+
expect(env.sourceRoots()).not.toContain('packages');
|
|
125
|
+
});
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
describe('配置一次装载 — rawConfig / customConstraints(ADR-0023 决策 2)', () => {
|
|
129
|
+
let dir: string;
|
|
130
|
+
|
|
131
|
+
const writeConfig = (content: string) => writeProjectConfig(dir, content);
|
|
132
|
+
|
|
133
|
+
/** 落 `.harness/<fileName>`(夹具可能还没有 .harness 目录,先补上) */
|
|
134
|
+
const writeHarnessFile = (fileName: string, body: string) => {
|
|
135
|
+
const target = path.join(dir, '.harness', fileName);
|
|
136
|
+
fs.mkdirSync(path.dirname(target), { recursive: true });
|
|
137
|
+
fs.writeFileSync(target, body, 'utf-8');
|
|
138
|
+
};
|
|
139
|
+
|
|
140
|
+
beforeEach(() => {
|
|
141
|
+
dir = createProjectFixture({ name: 'run-env-config' });
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
afterEach(() => {
|
|
145
|
+
fs.rmSync(dir, { recursive: true, force: true });
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
it('run 内至多读一次:取过之后再改文件,仍是老内容', () => {
|
|
149
|
+
writeConfig('preset: standard\n');
|
|
150
|
+
const env = createRunEnv(dir);
|
|
151
|
+
expect(env.rawConfig()).toEqual({ preset: 'standard' });
|
|
152
|
+
|
|
153
|
+
writeConfig('preset: strict\n');
|
|
154
|
+
expect(env.rawConfig()).toEqual({ preset: 'standard' });
|
|
155
|
+
});
|
|
156
|
+
|
|
157
|
+
it('懒建:造 env 不碰文件,之后落的配置才读得到', () => {
|
|
158
|
+
const env = createRunEnv(dir);
|
|
159
|
+
writeConfig('preset: minimal\n');
|
|
160
|
+
expect(env.rawConfig()).toEqual({ preset: 'minimal' });
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
it('无 config.yml = undefined(并记住这个结论,不反复探测)', () => {
|
|
164
|
+
const env = createRunEnv(dir);
|
|
165
|
+
expect(env.rawConfig()).toBeUndefined();
|
|
166
|
+
expect(env.rawConfig()).toBeUndefined();
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
it('空文件回落 {},与改前 loadRawProjectConfig 逐字一致', () => {
|
|
170
|
+
writeConfig('');
|
|
171
|
+
expect(createRunEnv(dir).rawConfig()).toEqual({});
|
|
172
|
+
});
|
|
173
|
+
|
|
174
|
+
it('YAML 解析失败照抛——兜与否属消费方的判定,读面不替它决定', () => {
|
|
175
|
+
writeConfig('governance: [\n broken: {{\n');
|
|
176
|
+
const env = createRunEnv(dir);
|
|
177
|
+
expect(() => env.rawConfig()).toThrow();
|
|
178
|
+
// 抛错不入 memo:修好文件后同一枚 env 也能读到正确内容
|
|
179
|
+
writeConfig('preset: standard\n');
|
|
180
|
+
expect(env.rawConfig()).toEqual({ preset: 'standard' });
|
|
181
|
+
});
|
|
182
|
+
|
|
183
|
+
it('resolveRunEnv:传 env 原样复用,传路径各自读当下内容(无进程级残留)', () => {
|
|
184
|
+
writeConfig('preset: standard\n');
|
|
185
|
+
const env = createRunEnv(dir);
|
|
186
|
+
expect(resolveRunEnv(env)).toBe(env);
|
|
187
|
+
expect(resolveRunEnv(dir).rawConfig()).toEqual({ preset: 'standard' });
|
|
188
|
+
|
|
189
|
+
writeConfig('preset: strict\n');
|
|
190
|
+
expect(loadRawProjectConfig(dir)).toEqual({ preset: 'strict' });
|
|
191
|
+
expect(resolveRunEnv().projectPath).toBe(process.cwd());
|
|
192
|
+
});
|
|
193
|
+
|
|
194
|
+
it('customConstraints 取 custom_constraints 段,run 内同名文件至多读一次', () => {
|
|
195
|
+
const write = (body: string) => writeHarnessFile('custom-constraints.yml', body);
|
|
196
|
+
write('custom_constraints:\n a:\n level: iron_law\n rule: 只此一份\n');
|
|
197
|
+
const env = createRunEnv(dir);
|
|
198
|
+
expect(Object.keys(env.customConstraints('custom-constraints.yml'))).toEqual(['a']);
|
|
199
|
+
|
|
200
|
+
write('custom_constraints:\n b:\n level: guideline\n rule: 后来的\n');
|
|
201
|
+
expect(Object.keys(env.customConstraints('custom-constraints.yml'))).toEqual(['a']);
|
|
202
|
+
});
|
|
203
|
+
|
|
204
|
+
it('customConstraints:文件缺失 = {};不同文件名各自 memo', () => {
|
|
205
|
+
const env = createRunEnv(dir);
|
|
206
|
+
expect(env.customConstraints('missing.yml')).toEqual({});
|
|
207
|
+
|
|
208
|
+
writeHarnessFile(
|
|
209
|
+
'other.yml',
|
|
210
|
+
'custom_constraints:\n c:\n level: guideline\n rule: r\n'
|
|
211
|
+
);
|
|
212
|
+
expect(Object.keys(env.customConstraints('other.yml'))).toEqual(['c']);
|
|
213
|
+
expect(env.customConstraints('missing.yml')).toEqual({});
|
|
214
|
+
});
|
|
215
|
+
});
|
|
216
|
+
|
|
217
|
+
describe('context-builder 经注入的观察面取证据', () => {
|
|
218
|
+
let dir: string;
|
|
219
|
+
|
|
220
|
+
const fakeGit = (projectPath: string, changed: string[]): GitEvidence => ({
|
|
221
|
+
projectPath,
|
|
222
|
+
stagedDiff: () => '',
|
|
223
|
+
changedFileNames: () => changed.join('\n'),
|
|
224
|
+
headDirs: () => new Set<string>(),
|
|
225
|
+
});
|
|
226
|
+
|
|
227
|
+
/** 只记次数的假观察面:records 固定,用于断言两个证据探测都从 env 取数 */
|
|
228
|
+
function countingEnv(records: ExecutionTrace[]) {
|
|
229
|
+
const limits: number[] = [];
|
|
230
|
+
const env: RunEnv = {
|
|
231
|
+
projectPath: dir,
|
|
232
|
+
traceTail: (limit: number) => {
|
|
233
|
+
limits.push(limit);
|
|
234
|
+
return { records: limit >= records.length ? records : records.slice(-limit), skippedLines: 0 };
|
|
235
|
+
},
|
|
236
|
+
sourceRoots: () => ['src'],
|
|
237
|
+
rawConfig: () => undefined,
|
|
238
|
+
customConstraints: () => ({}),
|
|
239
|
+
capabilities: () => undefined,
|
|
240
|
+
};
|
|
241
|
+
return { env, limits };
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
beforeEach(() => {
|
|
245
|
+
dir = fs.mkdtempSync(path.join(os.tmpdir(), 'harness-ctxbuilder-env-'));
|
|
246
|
+
});
|
|
247
|
+
|
|
248
|
+
afterEach(() => {
|
|
249
|
+
fs.rmSync(dir, { recursive: true, force: true });
|
|
250
|
+
});
|
|
251
|
+
|
|
252
|
+
it('hasFailingTest / hasVerificationEvidence 各按自己的窗口向 env 取数,不另开读', async () => {
|
|
253
|
+
const { env, limits } = countingEnv([
|
|
254
|
+
{ constraintId: 'p', level: 'iron_law', timestamp: 1, result: 'pass' },
|
|
255
|
+
{ constraintId: 'f', level: 'iron_law', timestamp: 2, result: 'fail' },
|
|
256
|
+
]);
|
|
257
|
+
const ctx = await buildConstraintContext({
|
|
258
|
+
projectPath: dir,
|
|
259
|
+
staged: false,
|
|
260
|
+
evidence: fakeGit(dir, ['README.md']),
|
|
261
|
+
runEnv: env,
|
|
262
|
+
});
|
|
263
|
+
|
|
264
|
+
expect(limits).toEqual([TRACE_TAIL_WINDOW, 10]);
|
|
265
|
+
expect(ctx.hasFailingTest).toBe(true);
|
|
266
|
+
expect(ctx.hasVerificationEvidence).toBe(true);
|
|
267
|
+
});
|
|
268
|
+
|
|
269
|
+
it('窗口里没有 pass 时验证证据为 false(合并读取不改判定)', async () => {
|
|
270
|
+
const { env } = countingEnv([
|
|
271
|
+
{ constraintId: 'f', level: 'iron_law', timestamp: 2, result: 'fail' },
|
|
272
|
+
]);
|
|
273
|
+
const ctx = await buildConstraintContext({
|
|
274
|
+
projectPath: dir,
|
|
275
|
+
staged: false,
|
|
276
|
+
evidence: fakeGit(dir, ['README.md']),
|
|
277
|
+
runEnv: env,
|
|
278
|
+
});
|
|
279
|
+
expect(ctx.hasFailingTest).toBe(true);
|
|
280
|
+
expect(ctx.hasVerificationEvidence).toBe(false);
|
|
281
|
+
});
|
|
282
|
+
|
|
283
|
+
it('不注入 env 时自造一份并按 projectPath 锚定读取', async () => {
|
|
284
|
+
writeTraces(dir, [traceLine('a', 'fail')]);
|
|
285
|
+
const ctx = await buildConstraintContext({
|
|
286
|
+
projectPath: dir,
|
|
287
|
+
staged: false,
|
|
288
|
+
evidence: fakeGit(dir, ['README.md']),
|
|
289
|
+
});
|
|
290
|
+
expect(ctx.hasFailingTest).toBe(true);
|
|
291
|
+
});
|
|
292
|
+
});
|
|
293
|
+
|
|
294
|
+
describe('capabilities — 能力表一次解析多消费(ADR-0023 决策 4)', () => {
|
|
295
|
+
const TABLE = '| 能力 | 路径 | 说明 |\n|---|---|---|\n';
|
|
296
|
+
/** 幽灵条目 src/gone.ts + 一个真实源文件 src/live.ts */
|
|
297
|
+
const CAPS_DOC = `# C\n\n${TABLE}| 幽灵 | src/gone.ts | 已删 |\n`;
|
|
298
|
+
|
|
299
|
+
let dir: string;
|
|
300
|
+
|
|
301
|
+
beforeEach(() => {
|
|
302
|
+
dir = createProjectFixture({
|
|
303
|
+
name: 'run-env-caps',
|
|
304
|
+
files: { 'CAPABILITIES.md': CAPS_DOC, 'src/live.ts': 'export const x = 1;\n' },
|
|
305
|
+
});
|
|
306
|
+
readSpy.mockClear();
|
|
307
|
+
});
|
|
308
|
+
|
|
309
|
+
afterEach(() => {
|
|
310
|
+
fs.rmSync(dir, { recursive: true, force: true });
|
|
311
|
+
});
|
|
312
|
+
|
|
313
|
+
/** 两个 checker 共用同一枚 env(= 一次 check run 的形状) */
|
|
314
|
+
async function evaluateBoth(env: RunEnv) {
|
|
315
|
+
const scan = collectSourceFiles(dir, env.sourceRoots());
|
|
316
|
+
const checkEnv = buildCheckEnv(
|
|
317
|
+
{ operation: 'module_modification', projectPath: dir },
|
|
318
|
+
{ stagedDiff: async () => '', stagedDiffNames: async () => '', srcScan: () => scan },
|
|
319
|
+
env
|
|
320
|
+
);
|
|
321
|
+
return {
|
|
322
|
+
docs: normalizeCheckOutcome(await docsFreshness.evaluate(checkEnv)),
|
|
323
|
+
caps: normalizeCheckOutcome(await capabilitySync.evaluate(checkEnv)),
|
|
324
|
+
};
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
it('一次 run 内两个 checker 共用同一份读取:CAPABILITIES.md 只读一次', async () => {
|
|
328
|
+
const { docs, caps } = await evaluateBoth(createRunEnv(dir));
|
|
329
|
+
|
|
330
|
+
// 判定照旧:幽灵条目由 docs_freshness 点名,capability_sync 正常评估不 skip
|
|
331
|
+
expect(docs.satisfied).toBe(false);
|
|
332
|
+
expect(docs.evidence.join('\n')).toContain('src/gone.ts');
|
|
333
|
+
expect(caps.skipped).toBe(false);
|
|
334
|
+
expect(capsReads()).toBe(1);
|
|
335
|
+
});
|
|
336
|
+
|
|
337
|
+
it('取过之后再改文档 = 同一份内容(run 内 memo,不重复解析)', () => {
|
|
338
|
+
const env = createRunEnv(dir);
|
|
339
|
+
const first = env.capabilities(['src/live.ts']);
|
|
340
|
+
expect(capsReads()).toBe(1);
|
|
341
|
+
|
|
342
|
+
fs.writeFileSync(path.join(dir, 'CAPABILITIES.md'), `# C\n\n${TABLE}| 新 | src/live.ts | 在 |\n`, 'utf-8');
|
|
343
|
+
expect(env.capabilities(['src/live.ts'])).toBe(first);
|
|
344
|
+
expect(capsReads()).toBe(1);
|
|
345
|
+
});
|
|
346
|
+
|
|
347
|
+
it('无 CAPABILITIES.md → undefined;有文档时判定与直调对照模块同输入同结果', () => {
|
|
348
|
+
const noDoc = createProjectFixture({ name: 'run-env-caps-none', files: { 'src/a.ts': 'x\n' } });
|
|
349
|
+
try {
|
|
350
|
+
expect(createRunEnv(noDoc).capabilities(['src/a.ts'])).toBeUndefined();
|
|
351
|
+
} finally {
|
|
352
|
+
fs.rmSync(noDoc, { recursive: true, force: true });
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
const env = createRunEnv(dir);
|
|
356
|
+
const caps = env.capabilities(['src/live.ts']);
|
|
357
|
+
expect(caps?.verdict.deadEntries).toEqual(
|
|
358
|
+
reconcileCapabilities({
|
|
359
|
+
content: caps!.content,
|
|
360
|
+
populationFiles: ['src/live.ts'],
|
|
361
|
+
sourceRoots: caps!.sourceRoots,
|
|
362
|
+
fileExists: rel => fs.existsSync(path.join(dir, rel)),
|
|
363
|
+
}).deadEntries
|
|
364
|
+
);
|
|
365
|
+
// 增量那一维不在上行数据面(变更清单属 git 证据 #87),共享形状里恒空
|
|
366
|
+
expect(caps?.verdict.uncoveredChanges).toEqual([]);
|
|
367
|
+
});
|
|
368
|
+
|
|
369
|
+
it('源根探测与能力表共用同一份 memo:取过之后再改目录树不影响', () => {
|
|
370
|
+
const env = createRunEnv(dir);
|
|
371
|
+
const before = env.capabilities(['src/live.ts'])?.sourceRoots;
|
|
372
|
+
|
|
373
|
+
fs.mkdirSync(path.join(dir, 'packages'), { recursive: true });
|
|
374
|
+
fs.writeFileSync(path.join(dir, 'packages', 'p.ts'), 'export const p = 1;\n', 'utf-8');
|
|
375
|
+
expect(env.capabilities(['src/live.ts'])?.sourceRoots).toBe(before);
|
|
376
|
+
});
|
|
377
|
+
});
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 约束 trigger 归一与匹配谓词测试(harness#105 语义单点)
|
|
3
|
+
*
|
|
4
|
+
* 用例自 `src/__tests__/exec.test.ts` 随迁(harness#137):二者是约束域谓词,
|
|
5
|
+
* 归位到 core/constraints 后测试跟着正本走。
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { describe, it, expect } from '@jest/globals';
|
|
9
|
+
import { normalizeTriggers, matchesTrigger } from '../triggers';
|
|
10
|
+
|
|
11
|
+
describe('normalizeTriggers', () => {
|
|
12
|
+
it('应该返回 fallback 当值为 null', () => {
|
|
13
|
+
const result = normalizeTriggers(null, ['default']);
|
|
14
|
+
expect(result).toEqual(['default']);
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
it('应该返回 fallback 当值为 undefined', () => {
|
|
18
|
+
const result = normalizeTriggers(undefined, ['default']);
|
|
19
|
+
expect(result).toEqual(['default']);
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
it('应该将单值包装为数组', () => {
|
|
23
|
+
const result = normalizeTriggers('single');
|
|
24
|
+
expect(result).toEqual(['single']);
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
it('应该直接返回数组', () => {
|
|
28
|
+
const result = normalizeTriggers(['a', 'b']);
|
|
29
|
+
expect(result).toEqual(['a', 'b']);
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
it('应该使用空数组作为默认 fallback', () => {
|
|
33
|
+
const result = normalizeTriggers(undefined);
|
|
34
|
+
expect(result).toEqual([]);
|
|
35
|
+
});
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
describe('matchesTrigger', () => {
|
|
39
|
+
it('单值 trigger 命中操作返回 true', () => {
|
|
40
|
+
expect(matchesTrigger({ trigger: 'a' }, ['a'])).toBe(true);
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
it('单值 trigger 未命中返回 false', () => {
|
|
44
|
+
expect(matchesTrigger({ trigger: 'a' }, ['b'])).toBe(false);
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
it('数组 trigger 任一命中即返回 true', () => {
|
|
48
|
+
expect(matchesTrigger({ trigger: ['a', 'b'] }, ['b'])).toBe(true);
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
it('数组 trigger 全部未命中返回 false', () => {
|
|
52
|
+
expect(matchesTrigger({ trigger: ['a', 'b'] }, ['c', 'd'])).toBe(false);
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
it('operations 中任一命中即返回 true', () => {
|
|
56
|
+
expect(matchesTrigger({ trigger: 'a' }, ['x', 'a'])).toBe(true);
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
it('trigger 为 undefined 时不匹配任何操作', () => {
|
|
60
|
+
expect(matchesTrigger({ trigger: undefined }, ['a'])).toBe(false);
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
it('trigger 为空数组时不匹配任何操作', () => {
|
|
64
|
+
expect(matchesTrigger({ trigger: [] }, ['a'])).toBe(false);
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
it('operations 为空数组时返回 false', () => {
|
|
68
|
+
expect(matchesTrigger({ trigger: 'a' }, [])).toBe(false);
|
|
69
|
+
});
|
|
70
|
+
});
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
*/
|
|
13
13
|
|
|
14
14
|
import type { ConstraintLevel, ConstraintTrigger } from '../../types/constraint';
|
|
15
|
-
import { matchesTrigger } from '
|
|
15
|
+
import { matchesTrigger } from './triggers';
|
|
16
16
|
import { getEffectiveConstraints } from '../effective-constraints';
|
|
17
17
|
|
|
18
18
|
/**
|
|
@@ -16,6 +16,7 @@
|
|
|
16
16
|
* module 读 uncoveredDirs(目录聚合),file 读 uncoveredFiles(逐文件)。
|
|
17
17
|
*/
|
|
18
18
|
|
|
19
|
+
import { existsSync } from 'fs';
|
|
19
20
|
import { join, relative } from 'path';
|
|
20
21
|
import {
|
|
21
22
|
aggregateToSourceSubdir,
|
|
@@ -67,8 +68,11 @@ export interface CapabilityVerdict {
|
|
|
67
68
|
* 目录条目(以 / 结尾)前缀匹配;文件条目精确匹配或路径边界后缀匹配
|
|
68
69
|
* (兼容 basename 条目,但拒绝 xfoo.ts 命中 foo.ts、docs/src/foo.tsx 命中 src/foo.ts
|
|
69
70
|
* 这类 endsWith/includes 模糊匹配造成的碰撞)
|
|
71
|
+
*
|
|
72
|
+
* 对外导出(ADR-0023 步骤 4):共享对照判定的消费方需要按自己的增量清单再算一次覆盖,
|
|
73
|
+
* 这条规则不得在别处重写。
|
|
70
74
|
*/
|
|
71
|
-
function
|
|
75
|
+
export function isCoveredByEntries(entries: string[], file: string): boolean {
|
|
72
76
|
return entries.some((listed) =>
|
|
73
77
|
listed.endsWith('/')
|
|
74
78
|
? file.startsWith(listed)
|
|
@@ -100,7 +104,7 @@ export function reconcileCapabilities(input: ReconcileInput): CapabilityVerdict
|
|
|
100
104
|
const dirEntries = allEntries.filter((e) => e.endsWith('/'));
|
|
101
105
|
const coverageEntries = [...fileEntries, ...dirEntries];
|
|
102
106
|
|
|
103
|
-
const uncoveredFiles = populationFiles.filter((f) => !
|
|
107
|
+
const uncoveredFiles = populationFiles.filter((f) => !isCoveredByEntries(coverageEntries, f));
|
|
104
108
|
const uncoveredDirs = [...new Set(
|
|
105
109
|
uncoveredFiles.map((file) => {
|
|
106
110
|
// 与 sync-docs 历史聚合同语义:归属第一个能前缀匹配的来源根,兜底首根
|
|
@@ -111,7 +115,7 @@ export function reconcileCapabilities(input: ReconcileInput): CapabilityVerdict
|
|
|
111
115
|
|
|
112
116
|
// 条目在代码实况中仍有对应文件即算活(裸文件名条目无法做存在性判定的兜底)
|
|
113
117
|
const aliveByPopulation = (entry: string): boolean =>
|
|
114
|
-
populationFiles.some((f) =>
|
|
118
|
+
populationFiles.some((f) => isCoveredByEntries([entry], f));
|
|
115
119
|
|
|
116
120
|
const deadEntries = allEntries.filter((entry) => {
|
|
117
121
|
if (entryExistsOnDisk(entry, fileExists, sourceRoots)) return false;
|
|
@@ -124,7 +128,7 @@ export function reconcileCapabilities(input: ReconcileInput): CapabilityVerdict
|
|
|
124
128
|
coverageEntries,
|
|
125
129
|
hasTable: TABLE_SEPARATOR_REGEX.test(content),
|
|
126
130
|
listingFormat: isCapabilityListingFormat(content),
|
|
127
|
-
uncoveredChanges: changedFiles.filter((f) => !
|
|
131
|
+
uncoveredChanges: changedFiles.filter((f) => !isCoveredByEntries(coverageEntries, f)),
|
|
128
132
|
uncoveredFiles,
|
|
129
133
|
uncoveredDirs,
|
|
130
134
|
deadEntries,
|
|
@@ -164,3 +168,23 @@ export function collectSourceFiles(
|
|
|
164
168
|
}
|
|
165
169
|
return files;
|
|
166
170
|
}
|
|
171
|
+
|
|
172
|
+
/**
|
|
173
|
+
* 「代码实况清单」的唯一取法:只收真实存在的源码根下的文件(项目相对路径)
|
|
174
|
+
*
|
|
175
|
+
* 抽出的理由(ADR-0023 步骤 4):capability_sync 与 docs_freshness 现在把同一份清单
|
|
176
|
+
* 喂给 run 内共享的对照判定(`RunEnv.capabilities()`,memo 只记第一次的输入),
|
|
177
|
+
* 两边必须逐字相同,否则共享的就是一份错的东西。扫描本身仍经调用方注入的 `scan`
|
|
178
|
+
* (保留 checker 侧 srcScan 证据接缝与测试可替身性)。
|
|
179
|
+
*/
|
|
180
|
+
export function collectPopulationFiles(
|
|
181
|
+
projectPath: string,
|
|
182
|
+
roots: string[],
|
|
183
|
+
scan: (root: string) => string[]
|
|
184
|
+
): string[] {
|
|
185
|
+
const files: string[] = [];
|
|
186
|
+
for (const root of roots) {
|
|
187
|
+
if (existsSync(join(projectPath, root))) files.push(...scan(root));
|
|
188
|
+
}
|
|
189
|
+
return files;
|
|
190
|
+
}
|
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
* ```typescript
|
|
15
15
|
* const cache = new CheckCache({ ttlMs: 5000 });
|
|
16
16
|
* // 普通 TTL 缓存
|
|
17
|
-
* const diff = await cache.get('git_diff', projectPath, () =>
|
|
17
|
+
* const diff = await cache.get('git_diff', projectPath, () => createGitEvidence(projectPath).stagedDiff());
|
|
18
18
|
* // 计数采样:每 3 次调用执行 1 次完整检查
|
|
19
19
|
* const ok = await cache.get('goal_check', key, check, { sampleRate: 3, defaultValueOnMiss: true });
|
|
20
20
|
* ```
|
|
@@ -18,12 +18,13 @@ import {
|
|
|
18
18
|
import type { ExecutionTrace } from '../../types/trace';
|
|
19
19
|
import { IRON_LAWS, GUIDELINES, PROMPTS } from './definitions';
|
|
20
20
|
import type { MergedConstraintsConfig } from '../../types/project-config';
|
|
21
|
-
import { matchesTrigger } from '
|
|
21
|
+
import { matchesTrigger } from './triggers';
|
|
22
22
|
import { join, relative } from 'path';
|
|
23
23
|
import { CheckCache } from './check-cache';
|
|
24
24
|
import { findTsSourceFiles } from '../../utils/file-walk';
|
|
25
25
|
import { getConstraintCheck, buildCheckEnv, normalizeCheckOutcome, type CheckOutcome } from './checkers';
|
|
26
26
|
import { createGitEvidence, type GitEvidence } from './git-evidence';
|
|
27
|
+
import { createRunEnv, type RunEnv } from './run-env';
|
|
27
28
|
|
|
28
29
|
/**
|
|
29
30
|
* trace 记录器最小接口(工单 15 decycle 收尾,harness#88)
|
|
@@ -94,11 +95,13 @@ export class ConstraintChecker {
|
|
|
94
95
|
* 检查单个约束
|
|
95
96
|
*
|
|
96
97
|
* @param evidence 可选,本 run 的 git 证据实例(#87);不传 = 本次调用独占一份真 git 证据
|
|
98
|
+
* @param runEnv 可选,本 run 的运行级观察面(ADR-0023);不传 = 本次调用独占一份
|
|
97
99
|
*/
|
|
98
100
|
async check(
|
|
99
101
|
constraint: Constraint,
|
|
100
102
|
context: ConstraintContext,
|
|
101
|
-
evidence?: GitEvidence
|
|
103
|
+
evidence?: GitEvidence,
|
|
104
|
+
runEnv?: RunEnv
|
|
102
105
|
): Promise<ConstraintResult> {
|
|
103
106
|
// prompt 类约束不参与 checker 执行(ADR-0001:仅参与注入)
|
|
104
107
|
if (constraint.kind === 'prompt') {
|
|
@@ -113,7 +116,7 @@ export class ConstraintChecker {
|
|
|
113
116
|
|
|
114
117
|
// 检查前置条件('skip' = 约定未采用/证据未接线:satisfied 置 true 但不计 pass/fail)
|
|
115
118
|
const outcome = normalizeCheckOutcome(
|
|
116
|
-
await this.checkPrecondition(constraint, context, evidence)
|
|
119
|
+
await this.checkPrecondition(constraint, context, evidence, runEnv)
|
|
117
120
|
);
|
|
118
121
|
|
|
119
122
|
if (outcome.skipped) {
|
|
@@ -187,11 +190,13 @@ export class ConstraintChecker {
|
|
|
187
190
|
*
|
|
188
191
|
* git 证据(stagedDiff/stagedDiffNames)单一来源 = GitEvidence adapter(#87):
|
|
189
192
|
* 一次 run 内调用方传同一实例即至多取证一次,run 外调用独占一份。
|
|
193
|
+
* 上行数据(config.yml / trace 尾部 / 源根)同一形状 = RunEnv(ADR-0023)。
|
|
190
194
|
*/
|
|
191
195
|
private async checkPrecondition(
|
|
192
196
|
constraint: Constraint,
|
|
193
197
|
context: ConstraintContext,
|
|
194
|
-
evidence?: GitEvidence
|
|
198
|
+
evidence?: GitEvidence,
|
|
199
|
+
runEnv?: RunEnv
|
|
195
200
|
): Promise<CheckOutcome> {
|
|
196
201
|
const impl = getConstraintCheck(constraint.id);
|
|
197
202
|
if (!impl) {
|
|
@@ -212,7 +217,7 @@ export class ConstraintChecker {
|
|
|
212
217
|
relative(projectPath, f)
|
|
213
218
|
)
|
|
214
219
|
),
|
|
215
|
-
});
|
|
220
|
+
}, runEnv);
|
|
216
221
|
|
|
217
222
|
return await impl.evaluate(env);
|
|
218
223
|
}
|
|
@@ -258,13 +263,16 @@ export class ConstraintChecker {
|
|
|
258
263
|
* @param evidence 可选,本 run 的 git 证据 adapter(#87)。调用方(CLI check)与
|
|
259
264
|
* buildConstraintContext 传同一实例 → 一次 run 内 git 取证单一来源;
|
|
260
265
|
* 不传则本 run 自建一份,run 级 memo 照样生效(memo 在实例层,非隐式全局)。
|
|
266
|
+
* @param runEnv 可选,本 run 的运行级观察面(ADR-0023)。调用方(CLI check)与
|
|
267
|
+
* buildConstraintContext 传同一实例 → 一次 run 内同一项目文件至多读一次。
|
|
261
268
|
*/
|
|
262
269
|
async checkConstraints(
|
|
263
270
|
context: ConstraintContext,
|
|
264
271
|
customConfig?: MergedConstraintsConfig | null,
|
|
265
|
-
evidence?: GitEvidence
|
|
272
|
+
evidence?: GitEvidence,
|
|
273
|
+
runEnv?: RunEnv
|
|
266
274
|
): Promise<ConstraintCheckResult> {
|
|
267
|
-
return this.runAllConstraints(context, customConfig, evidence, 'block');
|
|
275
|
+
return this.runAllConstraints(context, customConfig, evidence, runEnv, 'block');
|
|
268
276
|
}
|
|
269
277
|
|
|
270
278
|
/**
|
|
@@ -278,9 +286,10 @@ export class ConstraintChecker {
|
|
|
278
286
|
async collectConstraints(
|
|
279
287
|
context: ConstraintContext,
|
|
280
288
|
customConfig?: MergedConstraintsConfig | null,
|
|
281
|
-
evidence?: GitEvidence
|
|
289
|
+
evidence?: GitEvidence,
|
|
290
|
+
runEnv?: RunEnv
|
|
282
291
|
): Promise<ConstraintCheckResult> {
|
|
283
|
-
return this.runAllConstraints(context, customConfig, evidence, 'collect');
|
|
292
|
+
return this.runAllConstraints(context, customConfig, evidence, runEnv, 'collect');
|
|
284
293
|
}
|
|
285
294
|
|
|
286
295
|
/**
|
|
@@ -291,11 +300,15 @@ export class ConstraintChecker {
|
|
|
291
300
|
context: ConstraintContext,
|
|
292
301
|
customConfig: MergedConstraintsConfig | null | undefined,
|
|
293
302
|
evidence: GitEvidence | undefined,
|
|
303
|
+
runEnv: RunEnv | undefined,
|
|
294
304
|
mode: 'block' | 'collect'
|
|
295
305
|
): Promise<ConstraintCheckResult> {
|
|
296
306
|
// run 起始:重置 src 扫描缓存(S7);git 证据 = 本 run 独占的 adapter 实例(工单 18 → #87)
|
|
297
307
|
this.cache.invalidate();
|
|
298
|
-
const
|
|
308
|
+
const projectPath = context.projectPath || process.cwd();
|
|
309
|
+
const run = evidence ?? createGitEvidence(projectPath);
|
|
310
|
+
// 观察面同一形状(ADR-0023):调用方传了就共用,没传也只造一枚——逐条 checker 不再各读一遍配置
|
|
311
|
+
const env = runEnv ?? createRunEnv(projectPath);
|
|
299
312
|
|
|
300
313
|
const result: ConstraintCheckResult = {
|
|
301
314
|
ironLaws: [],
|
|
@@ -312,7 +325,7 @@ export class ConstraintChecker {
|
|
|
312
325
|
for (const constraint of Object.values(constraints.ironLaws)) {
|
|
313
326
|
if (!matchesTrigger(constraint, operations)) continue;
|
|
314
327
|
|
|
315
|
-
const checkResult = await this.check(constraint, context, run);
|
|
328
|
+
const checkResult = await this.check(constraint, context, run, env);
|
|
316
329
|
result.ironLaws.push(checkResult);
|
|
317
330
|
this.recordTrace(constraint, checkResult, context);
|
|
318
331
|
|
|
@@ -328,7 +341,7 @@ export class ConstraintChecker {
|
|
|
328
341
|
for (const constraint of Object.values(constraints.guidelines)) {
|
|
329
342
|
if (!matchesTrigger(constraint, operations)) continue;
|
|
330
343
|
|
|
331
|
-
const checkResult = await this.check(constraint, context, run);
|
|
344
|
+
const checkResult = await this.check(constraint, context, run, env);
|
|
332
345
|
result.guidelines.push(checkResult);
|
|
333
346
|
this.recordTrace(constraint, checkResult, context);
|
|
334
347
|
|
|
@@ -353,14 +366,17 @@ export class ConstraintChecker {
|
|
|
353
366
|
customConfig?: MergedConstraintsConfig | null,
|
|
354
367
|
evidence?: GitEvidence
|
|
355
368
|
): Promise<void> {
|
|
356
|
-
const
|
|
369
|
+
const projectPath = context.projectPath || process.cwd();
|
|
370
|
+
const run = evidence ?? createGitEvidence(projectPath);
|
|
371
|
+
// 独立 run 入口(ADR-0023):本函数内所有 checker 共用一枚观察面
|
|
372
|
+
const env = createRunEnv(projectPath);
|
|
357
373
|
const constraints = this.getConstraints(customConfig);
|
|
358
374
|
const operations = [context.operation, ...(context.extraTriggers ?? [])];
|
|
359
375
|
|
|
360
376
|
for (const constraint of Object.values(constraints.ironLaws)) {
|
|
361
377
|
if (!matchesTrigger(constraint, operations)) continue;
|
|
362
378
|
|
|
363
|
-
const result = await this.check(constraint, context, run);
|
|
379
|
+
const result = await this.check(constraint, context, run, env);
|
|
364
380
|
if (!result.satisfied) {
|
|
365
381
|
throw new ConstraintViolationError(result);
|
|
366
382
|
}
|