@dommaker/harness 1.7.0 → 1.8.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +44 -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 +19 -3
- package/dist/cli/commands/check.d.ts.map +1 -1
- package/dist/cli/commands/check.js +36 -22
- 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/status.d.ts +8 -0
- package/dist/cli/commands/status.d.ts.map +1 -1
- package/dist/cli/commands/status.js +5 -7
- package/dist/cli/commands/status.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/cli/state-io.d.ts +34 -0
- package/dist/cli/state-io.d.ts.map +1 -0
- package/dist/cli/state-io.js +71 -0
- package/dist/cli/state-io.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 +1 -0
- 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/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 +19 -1
- package/dist/hooks/pipeline.d.ts.map +1 -1
- package/dist/hooks/pipeline.js +42 -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 +25 -7
- package/dist/hooks/types.d.ts.map +1 -1
- package/dist/index.d.ts +3 -3
- 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/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 +0 -1
- package/src/__tests__/public-type-surface.test.ts +5 -4
- 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/__tests__/state-io.test.ts +50 -0
- package/src/cli/commands/CONTEXT.md +30 -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 +170 -0
- package/src/cli/commands/__tests__/check-skip-output.test.ts +4 -1
- package/src/cli/commands/__tests__/check.test.ts +63 -44
- 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__/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__/status-extra.test.ts +25 -8
- package/src/cli/commands/__tests__/status.test.ts +46 -18
- 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 +60 -35
- 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/status.ts +12 -7
- 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/cli/state-io.ts +53 -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 -0
- 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/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 +133 -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 +51 -6
- package/src/hooks/registry.ts +31 -12
- package/src/hooks/types.ts +26 -7
- package/src/index.ts +1 -2
- 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/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,170 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 一次 check 运行内「每个项目文件至多读一次」计数闸(ADR-0023 决策 5 ①)
|
|
3
|
+
*
|
|
4
|
+
* 形状照 `check.test.ts:78-106` 的 `recordingEvidence()`:真执行、只在中间记一笔,
|
|
5
|
+
* 不改任何行为。这里数的不是「观察口被请求几次」(memo 在观察面上,请求多次本就应当),
|
|
6
|
+
* 而是**底层文件读取发生了几次** —— 那才是本 ADR 承诺的口径。
|
|
7
|
+
*
|
|
8
|
+
* 夹具必须让两个吃能力表的 checker 都真评估(触发条件 module_modification +
|
|
9
|
+
* 有 CAPABILITIES.md + 有自定义约束文件),否则闸门是空跑的。
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import * as fs from 'fs';
|
|
13
|
+
import * as os from 'os';
|
|
14
|
+
import * as path from 'path';
|
|
15
|
+
import { execFileSync } from 'child_process';
|
|
16
|
+
import { check } from '../check';
|
|
17
|
+
import { captureIO, type CapturingIO } from '../../command-contract';
|
|
18
|
+
|
|
19
|
+
type ReadCall = { file: string; via: 'readFileSync' | 'open' };
|
|
20
|
+
|
|
21
|
+
// 计数器挂在 globalThis:jest.mock 工厂会被提前调用,闭包外提会撞 TDZ
|
|
22
|
+
jest.mock('fs', () => {
|
|
23
|
+
const actual = jest.requireActual<typeof import('fs')>('fs');
|
|
24
|
+
const g = globalThis as unknown as { __ADR0023_READS__?: ReadCall[] };
|
|
25
|
+
if (!g.__ADR0023_READS__) g.__ADR0023_READS__ = [];
|
|
26
|
+
const bucket = g.__ADR0023_READS__;
|
|
27
|
+
const name = (p: unknown): string =>
|
|
28
|
+
Buffer.isBuffer(p) ? p.toString('utf8') : String(p);
|
|
29
|
+
const passthrough = (fn: string) =>
|
|
30
|
+
(actual as unknown as Record<string, (...args: unknown[]) => unknown>)[fn];
|
|
31
|
+
|
|
32
|
+
return {
|
|
33
|
+
...actual,
|
|
34
|
+
readFileSync: jest.fn((p: unknown, ...rest: unknown[]) => {
|
|
35
|
+
bucket.push({ file: name(p), via: 'readFileSync' });
|
|
36
|
+
return passthrough('readFileSync')(p, ...rest);
|
|
37
|
+
}),
|
|
38
|
+
// 有界读走 open/fstat/read:只记只读打开(写 trace 的 append 不算读取)
|
|
39
|
+
openSync: jest.fn((p: unknown, flags?: unknown, ...rest: unknown[]) => {
|
|
40
|
+
if (flags === undefined || flags === 'r' || flags === 'rs') {
|
|
41
|
+
bucket.push({ file: name(p), via: 'open' });
|
|
42
|
+
}
|
|
43
|
+
return passthrough('openSync')(p, flags, ...rest);
|
|
44
|
+
}),
|
|
45
|
+
};
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
const reads = () =>
|
|
49
|
+
((globalThis as unknown as { __ADR0023_READS__?: ReadCall[] }).__ADR0023_READS__ ||= []);
|
|
50
|
+
|
|
51
|
+
jest.mock('chalk', () => ({
|
|
52
|
+
blue: (s: string) => s,
|
|
53
|
+
yellow: (s: string) => s,
|
|
54
|
+
green: (s: string) => s,
|
|
55
|
+
gray: (s: string) => s,
|
|
56
|
+
red: (s: string) => s,
|
|
57
|
+
}));
|
|
58
|
+
|
|
59
|
+
function git(dir: string, ...args: string[]): void {
|
|
60
|
+
execFileSync('git', args, { cwd: dir, stdio: 'pipe' });
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function write(dir: string, rel: string, content: string): void {
|
|
64
|
+
const target = path.join(dir, rel);
|
|
65
|
+
fs.mkdirSync(path.dirname(target), { recursive: true });
|
|
66
|
+
fs.writeFileSync(target, content, 'utf-8');
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
const TABLE = ['| 能力 | 路径 | 说明 |', '|---|---|---|'];
|
|
70
|
+
|
|
71
|
+
/** 一个「两 checker 都会真评估」的项目:源码根下两个已登记文件 + 自定义约束 + 一条 trace */
|
|
72
|
+
function fixtureRepo(): string {
|
|
73
|
+
const dir = fs.mkdtempSync(path.join(os.tmpdir(), 'harness-read-count-'));
|
|
74
|
+
git(dir, 'init', '-q');
|
|
75
|
+
git(dir, 'config', 'user.email', 'fixture@example.com');
|
|
76
|
+
git(dir, 'config', 'user.name', 'Fixture');
|
|
77
|
+
write(dir, 'README.md', '# fixture\n');
|
|
78
|
+
write(dir, '.harness/config.yml', 'preset: standard\n');
|
|
79
|
+
write(
|
|
80
|
+
dir,
|
|
81
|
+
'.harness/custom-constraints.yml',
|
|
82
|
+
'custom_constraints:\n ab_custom_rule:\n level: guideline\n rule: 自定义规则\n message: 自定义提示\n trigger: manual\n'
|
|
83
|
+
);
|
|
84
|
+
write(
|
|
85
|
+
dir,
|
|
86
|
+
'CAPABILITIES.md',
|
|
87
|
+
['# CAPABILITIES.md', '', ...TABLE, '| 既有 | src/existing.ts | 在 |', '| 嵌套 | src/nested/deep.ts | 在 |'].join('\n') + '\n'
|
|
88
|
+
);
|
|
89
|
+
write(dir, 'AGENTS.md', '<!-- PRESERVE:governance -->\n## Governance Rules\n\n条款\n\n<!-- /PRESERVE:governance -->\n');
|
|
90
|
+
write(dir, 'src/existing.ts', 'export const a = 1;\n');
|
|
91
|
+
write(dir, 'src/nested/deep.ts', 'export const d = 1;\n');
|
|
92
|
+
write(dir, '.harness/logs/traces.log', '{"constraintId":"fixture","result":"pass"}\n');
|
|
93
|
+
// ADR-0026:状态文件进夹具,智能提示的状态读取随之进计数表(一次运行至多读一次)
|
|
94
|
+
write(dir, '.harness/.state.json', '{}');
|
|
95
|
+
git(dir, 'add', '.');
|
|
96
|
+
git(dir, 'commit', '-q', '-m', 'baseline');
|
|
97
|
+
// 改一个已登记的源文件并 stage → 触发条件 module_modification
|
|
98
|
+
write(dir, 'src/existing.ts', 'export const a = 2;\n');
|
|
99
|
+
git(dir, 'add', '--', 'src/existing.ts');
|
|
100
|
+
return dir;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
describe('一次 check 的文件读取计数闸(ADR-0023 决策 5 ①)', () => {
|
|
104
|
+
let io: CapturingIO;
|
|
105
|
+
let dir: string;
|
|
106
|
+
|
|
107
|
+
/** 相对项目根的文件名 → 读取次数(只读语义的 readFileSync 与只读 open 都算一次) */
|
|
108
|
+
function readCounts(): Map<string, number> {
|
|
109
|
+
const counts = new Map<string, number>();
|
|
110
|
+
for (const call of reads()) {
|
|
111
|
+
const rel = path.relative(dir, call.file);
|
|
112
|
+
if (!rel || rel.startsWith('..')) continue; // 非本项目文件(模块加载等)不计
|
|
113
|
+
counts.set(rel, (counts.get(rel) ?? 0) + 1);
|
|
114
|
+
}
|
|
115
|
+
return counts;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
beforeEach(() => {
|
|
119
|
+
// 原地清空:mock 工厂在装载时就captured了这个数组引用,换新数组计数会落到旧的去向(假绿)
|
|
120
|
+
reads().length = 0;
|
|
121
|
+
io = captureIO();
|
|
122
|
+
dir = fixtureRepo();
|
|
123
|
+
// trace 自 #139 起锚在 projectPath 上写入(本夹具内),写入不经读取计数器
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
afterEach(() => {
|
|
127
|
+
fs.rmSync(dir, { recursive: true, force: true });
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
it('整张读取表如实冻结:本次运行读了哪些项目文件、各读几次', async () => {
|
|
131
|
+
const result = await check({ staged: true, projectPath: dir }, io);
|
|
132
|
+
expect(result.kind).toBe('ok');
|
|
133
|
+
// 正对照:两个吃能力表的 checker 确实跑了(否则下面的断言是空跑)
|
|
134
|
+
expect(io.outText()).toContain('自定义约束: 1 条');
|
|
135
|
+
expect(io.outText()).toContain('触发条件: module_modification');
|
|
136
|
+
|
|
137
|
+
expect([...readCounts().entries()].sort()).toEqual(
|
|
138
|
+
[
|
|
139
|
+
// —— 一次运行一份:本 ADR 的承诺面 ——
|
|
140
|
+
['.harness/config.yml', 1],
|
|
141
|
+
['.harness/custom-constraints.yml', 1],
|
|
142
|
+
['CAPABILITIES.md', 1],
|
|
143
|
+
['.harness/logs/traces.log', 2],
|
|
144
|
+
['.harness/.state.json', 1], // ADR-0026:智能提示经 StateIO 读状态,恰一次
|
|
145
|
+
// —— 已知例外(改动需明写理由)——
|
|
146
|
+
// traces.log = 2:head 50(智能提示的阈值判定,决策 3)与 tail 20(有无失败证据,
|
|
147
|
+
// 决策 4)是两个不同窗口的**有界**读;并成一个窗口就等于回到整读。
|
|
148
|
+
// AGENTS.md = 3 / CLAUDE.md = 2:决策表明示本 ADR 不处理治理文档的重复读
|
|
149
|
+
// (1.5–25KB,<1ms)。CLAUDE.md 在本夹具里并不存在,2 = 落点路由与旧落点兜底
|
|
150
|
+
// 各尝试读一次(读失败的尝试同样计入)。
|
|
151
|
+
['AGENTS.md', 3],
|
|
152
|
+
['CLAUDE.md', 2],
|
|
153
|
+
].sort()
|
|
154
|
+
);
|
|
155
|
+
});
|
|
156
|
+
|
|
157
|
+
it('除上表点名例外外的项目文件,一次运行内一律至多读一次', async () => {
|
|
158
|
+
await check({ staged: true, projectPath: dir }, io);
|
|
159
|
+
|
|
160
|
+
const except = new Set([
|
|
161
|
+
path.join('.harness', 'logs', 'traces.log'), // 两个窗口的有界读,见上表理由
|
|
162
|
+
'AGENTS.md', // 治理文档,ADR-0023 决策表明示不处理
|
|
163
|
+
'CLAUDE.md',
|
|
164
|
+
]);
|
|
165
|
+
const offenders = [...readCounts()].filter(
|
|
166
|
+
([file, n]) => !except.has(file) && n > 1
|
|
167
|
+
);
|
|
168
|
+
expect(offenders).toEqual([]);
|
|
169
|
+
});
|
|
170
|
+
});
|
|
@@ -34,8 +34,11 @@ jest.mock('../../../core/constraints/checker', () => ({
|
|
|
34
34
|
}),
|
|
35
35
|
}));
|
|
36
36
|
|
|
37
|
+
// 命令 per-run 锚根构造收集器(harness#139):构造替身即拦截 trace 落盘
|
|
37
38
|
jest.mock('../../../monitoring/traces', () => ({
|
|
38
|
-
|
|
39
|
+
TraceCollector: jest.fn(function () {
|
|
40
|
+
return { record: jest.fn() };
|
|
41
|
+
}),
|
|
39
42
|
}));
|
|
40
43
|
|
|
41
44
|
// Mock effective-constraints(ADR-0001:check 经 getMergedConstraintsConfig 走生效集链路)
|
|
@@ -7,8 +7,8 @@
|
|
|
7
7
|
* 真 ConstraintChecker,断言 CLI 输出形状与 CommandResult。
|
|
8
8
|
* 取证次数经 createGitEvidence 的计数 runner 观测——执行仍走真 git,不 mock 子进程。
|
|
9
9
|
*
|
|
10
|
-
* trace 记录器由命令侧组合根接线(harness#88
|
|
11
|
-
*
|
|
10
|
+
* trace 记录器由命令侧组合根接线(harness#88)并锚根构造(#139):落点在本用例的
|
|
11
|
+
* 临时项目里(`<projectPath>/.harness/logs/traces.log`),宿主仓 .harness/ 不受影响。
|
|
12
12
|
*/
|
|
13
13
|
|
|
14
14
|
import * as fs from 'fs';
|
|
@@ -18,7 +18,8 @@ import { execFileSync } from 'child_process';
|
|
|
18
18
|
|
|
19
19
|
import { check, listLaws } from '../check';
|
|
20
20
|
import { captureIO, type CapturingIO } from '../../command-contract';
|
|
21
|
-
import {
|
|
21
|
+
import type { HarnessState, StateIO } from '../../state-io';
|
|
22
|
+
import { DEFAULT_TRACE_FILE } from '../../../types/trace';
|
|
22
23
|
import {
|
|
23
24
|
createGitEvidence,
|
|
24
25
|
realGitCommandRunner,
|
|
@@ -75,6 +76,36 @@ function passTraces(dir: string, lines = 1): void {
|
|
|
75
76
|
);
|
|
76
77
|
}
|
|
77
78
|
|
|
79
|
+
/**
|
|
80
|
+
* 本项目的 trace 落点记录(harness#139:命令侧收集器锚根构造,写的就是 `<projectPath>/DEFAULT_TRACE_FILE` 正本)
|
|
81
|
+
*
|
|
82
|
+
* 含 `passTraces()` 预置的种子行,只看命令自己写入的用例按行数切掉前缀。
|
|
83
|
+
*/
|
|
84
|
+
function projectTraces(
|
|
85
|
+
dir: string
|
|
86
|
+
): Array<{ constraintId: string; result: string; projectPath?: string; evidence?: string[] }> {
|
|
87
|
+
const file = path.join(dir, DEFAULT_TRACE_FILE);
|
|
88
|
+
if (!fs.existsSync(file)) return [];
|
|
89
|
+
return fs
|
|
90
|
+
.readFileSync(file, 'utf-8')
|
|
91
|
+
.split('\n')
|
|
92
|
+
.filter(Boolean)
|
|
93
|
+
.map(line => JSON.parse(line));
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* 内存 StateIO 假件(ADR-0026):状态相关测试经注入面驱动,不碰真文件系统。
|
|
98
|
+
* `snapshot()` 给断言用——读的是假件当前持有的状态,不是磁盘。
|
|
99
|
+
*/
|
|
100
|
+
function memoryStateIO(initial: HarnessState = {}): StateIO & { snapshot(): HarnessState } {
|
|
101
|
+
let state = initial;
|
|
102
|
+
return {
|
|
103
|
+
read: () => state,
|
|
104
|
+
write: (next: HarnessState) => { state = next; },
|
|
105
|
+
snapshot: () => state,
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
|
|
78
109
|
/** 计数 git 证据:既记录证据方法请求,也记录实际 spawn 的 git 命令(执行走真 adapter) */
|
|
79
110
|
function recordingEvidence(projectPath: string): {
|
|
80
111
|
evidence: GitEvidence;
|
|
@@ -118,28 +149,9 @@ function stagedTestDeletion(): string {
|
|
|
118
149
|
|
|
119
150
|
describe('check command(真 git fixture)', () => {
|
|
120
151
|
let io: CapturingIO;
|
|
121
|
-
let traceDir: string;
|
|
122
|
-
let traceFile: string;
|
|
123
|
-
let traceSeq = 0;
|
|
124
|
-
|
|
125
|
-
beforeAll(() => {
|
|
126
|
-
traceDir = fs.mkdtempSync(path.join(os.tmpdir(), 'harness-check-traces-'));
|
|
127
|
-
});
|
|
128
|
-
|
|
129
|
-
afterAll(() => {
|
|
130
|
-
configureTraceCollector({});
|
|
131
|
-
try {
|
|
132
|
-
fs.rmSync(traceDir, { recursive: true, force: true });
|
|
133
|
-
} catch {
|
|
134
|
-
// ignore
|
|
135
|
-
}
|
|
136
|
-
});
|
|
137
152
|
|
|
138
153
|
beforeEach(() => {
|
|
139
154
|
io = captureIO();
|
|
140
|
-
// 命令侧接线的是全局收集器(默认 cwd 相对写入):重定向到套件临时文件,宿主仓 .harness/ 不受影响
|
|
141
|
-
traceFile = path.join(traceDir, `trace-${++traceSeq}.log`);
|
|
142
|
-
configureTraceCollector({ traceFile });
|
|
143
155
|
});
|
|
144
156
|
|
|
145
157
|
describe('判定与输出形状', () => {
|
|
@@ -240,6 +252,22 @@ describe('check command(真 git fixture)', () => {
|
|
|
240
252
|
expect(io.outText()).not.toContain('变更文件');
|
|
241
253
|
expect(result).toEqual({ kind: 'ok' });
|
|
242
254
|
});
|
|
255
|
+
|
|
256
|
+
it('不带 -p 时标注预设来源,带 -p 时打印所传值(ADR-0023 步骤 4.5)', async () => {
|
|
257
|
+
const dir = gitRepo();
|
|
258
|
+
stageChange(dir, 'src/existing.ts', 'export const a = 2;\n');
|
|
259
|
+
passTraces(dir);
|
|
260
|
+
|
|
261
|
+
await check({ staged: true, projectPath: dir, trigger: 'code_implementation' }, io);
|
|
262
|
+
await check(
|
|
263
|
+
{ preset: 'standard', staged: true, projectPath: dir, trigger: 'code_implementation' },
|
|
264
|
+
io
|
|
265
|
+
);
|
|
266
|
+
|
|
267
|
+
const out = io.outText();
|
|
268
|
+
expect(out).toContain('预设: (按 config.yml,缺省 standard)');
|
|
269
|
+
expect(out).toContain('预设: standard');
|
|
270
|
+
});
|
|
243
271
|
});
|
|
244
272
|
|
|
245
273
|
describe('判定证据外显(harness#119)', () => {
|
|
@@ -255,14 +283,9 @@ describe('check command(真 git fixture)', () => {
|
|
|
255
283
|
return dir;
|
|
256
284
|
}
|
|
257
285
|
|
|
258
|
-
/**
|
|
259
|
-
function traceOf(constraintId: string) {
|
|
260
|
-
return
|
|
261
|
-
.readFileSync(traceFile, 'utf-8')
|
|
262
|
-
.split('\n')
|
|
263
|
-
.filter(Boolean)
|
|
264
|
-
.map((line) => JSON.parse(line) as { constraintId: string; result: string; evidence?: string[] })
|
|
265
|
-
.find((l) => l.constraintId === constraintId);
|
|
286
|
+
/** 从本用例项目的 trace 正本里取某条约束的记录(#139:落点锚在 projectPath) */
|
|
287
|
+
function traceOf(dir: string, constraintId: string) {
|
|
288
|
+
return projectTraces(dir).find(l => l.constraintId === constraintId);
|
|
266
289
|
}
|
|
267
290
|
|
|
268
291
|
it('变更文件未登记 → 警告随附证据点名该文件', async () => {
|
|
@@ -308,7 +331,7 @@ describe('check command(真 git fixture)', () => {
|
|
|
308
331
|
io
|
|
309
332
|
);
|
|
310
333
|
|
|
311
|
-
const record = traceOf('capability_sync');
|
|
334
|
+
const record = traceOf(dir, 'capability_sync');
|
|
312
335
|
expect(record?.result).toBe('pass');
|
|
313
336
|
expect(record?.evidence?.join('\n')).toContain('src/nested/deep.ts');
|
|
314
337
|
});
|
|
@@ -364,17 +387,15 @@ describe('check command(真 git fixture)', () => {
|
|
|
364
387
|
const dir = gitRepo();
|
|
365
388
|
stageChange(dir, 'src/existing.ts', 'export const a = 2;\n');
|
|
366
389
|
passTraces(dir);
|
|
390
|
+
const seeded = projectTraces(dir).length;
|
|
367
391
|
|
|
368
392
|
await check(
|
|
369
393
|
{ preset: 'standard', staged: true, projectPath: dir, trigger: 'code_implementation' },
|
|
370
394
|
io
|
|
371
395
|
);
|
|
372
396
|
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
.split('\n')
|
|
376
|
-
.filter(Boolean)
|
|
377
|
-
.map(l => JSON.parse(l) as { constraintId: string; result: string; projectPath: string });
|
|
397
|
+
// #139:落点锚在本 run 的 projectPath 上,切掉夹具预置的证据行
|
|
398
|
+
const traces = projectTraces(dir).slice(seeded);
|
|
378
399
|
|
|
379
400
|
expect(traces.map(t => t.constraintId)).toEqual(
|
|
380
401
|
expect.arrayContaining(['incremental_progress', 'no_implementation_without_requirement'])
|
|
@@ -475,27 +496,25 @@ describe('check command(真 git fixture)', () => {
|
|
|
475
496
|
});
|
|
476
497
|
});
|
|
477
498
|
|
|
478
|
-
describe('智能提示(真 traces.log
|
|
499
|
+
describe('智能提示(真 traces.log + 注入 StateIO 假件)', () => {
|
|
479
500
|
it('记录数首次达到 50 时提示,并落盘已提示状态', async () => {
|
|
480
501
|
const dir = gitRepo();
|
|
481
502
|
passTraces(dir, 50);
|
|
503
|
+
const stateIO = memoryStateIO();
|
|
482
504
|
|
|
483
|
-
await check({ preset: 'standard', staged: true, projectPath: dir, trigger: 'manual' }, io);
|
|
505
|
+
await check({ preset: 'standard', staged: true, projectPath: dir, trigger: 'manual', stateIO }, io);
|
|
484
506
|
|
|
485
507
|
expect(io.outText()).toContain('记录已足够,运行 harness status 查看统计');
|
|
486
508
|
expect(io.outText()).toContain('────────────────');
|
|
487
|
-
|
|
488
|
-
fs.readFileSync(path.join(dir, '.harness', '.state.json'), 'utf-8')
|
|
489
|
-
);
|
|
490
|
-
expect(state.shownHints).toContain('trace_50');
|
|
509
|
+
expect(stateIO.snapshot().shownHints).toContain('trace_50');
|
|
491
510
|
});
|
|
492
511
|
|
|
493
512
|
it('已提示过的不再重复提示', async () => {
|
|
494
513
|
const dir = gitRepo();
|
|
495
514
|
passTraces(dir, 50);
|
|
496
|
-
|
|
515
|
+
const stateIO = memoryStateIO({ shownHints: ['trace_50'] });
|
|
497
516
|
|
|
498
|
-
await check({ preset: 'standard', staged: true, projectPath: dir, trigger: 'manual' }, io);
|
|
517
|
+
await check({ preset: 'standard', staged: true, projectPath: dir, trigger: 'manual', stateIO }, io);
|
|
499
518
|
|
|
500
519
|
expect(io.outText()).not.toContain('记录已足够');
|
|
501
520
|
expect(io.outText()).not.toContain('────────────────');
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* command 门禁的两个死配置位已删除(#135,架构评审候选 6)
|
|
3
|
+
*
|
|
4
|
+
* `CommandGateConfig.strict` 只在构造时读一次、任何判定路径从不使用——`harness command --strict`
|
|
5
|
+
* 与不带它判定完全一致;`CommandGateConfig.customBlacklist` 全仓零生产注入者,运行时扩展点是
|
|
6
|
+
* `addRule()`。triage 裁决两项都**删**(判据同 ADR-0018/0022:不接进判定、不留「配了不生效」),
|
|
7
|
+
* CLI `--strict` 旗帜随之 removal(属 breaking,见 ADR-0024)。
|
|
8
|
+
*
|
|
9
|
+
* 手法照 `passes-gate-dead-options.test.ts` / `init-dead-options.test.ts`:编译期
|
|
10
|
+
* `@ts-expect-error` 钉字段不存在 + 运行期钉定义表旗帜面与配置形状。
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import * as fs from 'fs';
|
|
14
|
+
import * as path from 'path';
|
|
15
|
+
import { GATE_DEFINITIONS } from '../../../gates/definitions';
|
|
16
|
+
import { CommandGate } from '../../../gates/command';
|
|
17
|
+
import type { CommandGateConfig } from '../../../gates/types';
|
|
18
|
+
import type { CommandCheckOptions } from '../command';
|
|
19
|
+
|
|
20
|
+
const commandGateCli = GATE_DEFINITIONS.find(gate => gate.id === 'command')!.cli;
|
|
21
|
+
|
|
22
|
+
describe('command 门禁的 strict / customBlacklist 已删除(#135)', () => {
|
|
23
|
+
it('定义表已注册 command 门禁(前提)', () => {
|
|
24
|
+
expect(commandGateCli).toBeDefined();
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
it('command 旗帜面逐字冻结,不含 --strict', () => {
|
|
28
|
+
expect(commandGateCli.options.map(option => option.flags)).toEqual([
|
|
29
|
+
'-l, --level',
|
|
30
|
+
'--list',
|
|
31
|
+
'--json',
|
|
32
|
+
]);
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
it('CommandGateConfig 与 CommandCheckOptions 的 strict / customBlacklist 已从类型面消失(编译期钉)', () => {
|
|
36
|
+
// @ts-expect-error strict 零消费者(只在构造时读取),随 #135 从 CommandGateConfig 删除
|
|
37
|
+
const removedStrict: CommandGateConfig = { strict: true };
|
|
38
|
+
// @ts-expect-error customBlacklist 零生产注入者,运行时扩展点是 addRule(),随 #135 删除
|
|
39
|
+
const removedCustom: CommandGateConfig = { customBlacklist: [] };
|
|
40
|
+
// @ts-expect-error CLI 同名字段随 --strict 旗帜与装配一并删除
|
|
41
|
+
const removedOption: CommandCheckOptions = { strict: true };
|
|
42
|
+
expect([removedStrict, removedCustom, removedOption]).toHaveLength(3);
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
it('活字段仍可赋值(防止删多)', () => {
|
|
46
|
+
const live: CommandGateConfig = { ignoreCategories: ['database'] };
|
|
47
|
+
const liveCli: CommandCheckOptions = { level: true, list: true, json: false };
|
|
48
|
+
expect(Object.keys(live)).toHaveLength(1);
|
|
49
|
+
expect(Object.keys(liveCli)).toHaveLength(3);
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
it('配置读取面只剩 ignoreCategories(运行期形状钉)', () => {
|
|
53
|
+
expect(Object.keys(new CommandGate().getConfig())).toEqual(['ignoreCategories']);
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
it('CLI 装配点不再引用模块级单例出口(--level 与默认分支同一实例来源)', () => {
|
|
57
|
+
const cliSource = fs.readFileSync(path.join(__dirname, '..', 'command.ts'), 'utf-8');
|
|
58
|
+
expect(cliSource).not.toMatch(/getCommandRiskLevel|getCommandGate\(/);
|
|
59
|
+
});
|
|
60
|
+
});
|
|
@@ -6,12 +6,12 @@
|
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
8
|
import { executeCommand } from '../command';
|
|
9
|
-
import { createCommandGate
|
|
9
|
+
import { createCommandGate } from '../../../gates';
|
|
10
10
|
import { captureIO, type CapturingIO } from '../../command-contract';
|
|
11
|
+
import { decide, fakeGate } from './gate-decision';
|
|
11
12
|
|
|
12
13
|
jest.mock('../../../gates', () => ({
|
|
13
14
|
createCommandGate: jest.fn(),
|
|
14
|
-
getCommandRiskLevel: jest.fn(),
|
|
15
15
|
DEFAULT_COMMAND_BLACKLIST: [
|
|
16
16
|
{ id: 'rule-1', level: 'block', message: 'No rm -rf', category: 'destructive', pattern: 'rm -rf' },
|
|
17
17
|
{ id: 'rule-2', level: 'warn', message: 'No DROP TABLE', category: 'database', pattern: 'DROP TABLE' },
|
|
@@ -27,10 +27,21 @@ jest.mock('chalk', () => ({
|
|
|
27
27
|
}));
|
|
28
28
|
|
|
29
29
|
const mockCreateGate = createCommandGate as jest.MockedFunction<typeof createCommandGate>;
|
|
30
|
-
const mockGetRisk = getCommandRiskLevel as jest.MockedFunction<typeof getCommandRiskLevel>;
|
|
31
30
|
|
|
32
|
-
|
|
33
|
-
|
|
31
|
+
/**
|
|
32
|
+
* 替身工厂:命令穿过统一接口,替身产决策而非裸报告。
|
|
33
|
+
* `--level` 与默认分支共用这一台实例(#135),故等级也从替身上取。
|
|
34
|
+
*/
|
|
35
|
+
function mockGate(
|
|
36
|
+
report: { passed: boolean; message: string; details?: Record<string, any> },
|
|
37
|
+
level: 'high' | 'medium' | 'low' = 'low',
|
|
38
|
+
): { evaluate: jest.Mock; getRiskLevel: jest.Mock } {
|
|
39
|
+
const gate = {
|
|
40
|
+
...fakeGate(decide('command', report.passed, report.message, report.details)),
|
|
41
|
+
getRiskLevel: jest.fn().mockReturnValue(level),
|
|
42
|
+
};
|
|
43
|
+
mockCreateGate.mockReturnValue(gate as any);
|
|
44
|
+
return gate;
|
|
34
45
|
}
|
|
35
46
|
|
|
36
47
|
/** 从捕获输出里取第一条可解析为该形状的 JSON 行 */
|
|
@@ -86,7 +97,7 @@ describe('command command', () => {
|
|
|
86
97
|
|
|
87
98
|
describe('default mode', () => {
|
|
88
99
|
it('放行:ok,打印 ✓', async () => {
|
|
89
|
-
mockGate(
|
|
100
|
+
mockGate({ passed: true, message: 'OK' });
|
|
90
101
|
|
|
91
102
|
const result = await executeCommand('safe-cmd', {}, io);
|
|
92
103
|
|
|
@@ -95,7 +106,7 @@ describe('command command', () => {
|
|
|
95
106
|
});
|
|
96
107
|
|
|
97
108
|
it('拒绝:fail + 裁决原因,打印 ✗', async () => {
|
|
98
|
-
mockGate(
|
|
109
|
+
mockGate({ passed: false, message: 'Blocked' });
|
|
99
110
|
|
|
100
111
|
const result = await executeCommand('rm -rf /', {}, io);
|
|
101
112
|
|
|
@@ -104,7 +115,7 @@ describe('command command', () => {
|
|
|
104
115
|
});
|
|
105
116
|
|
|
106
117
|
it('--json 输出裁决详情', async () => {
|
|
107
|
-
mockGate(
|
|
118
|
+
mockGate({ passed: true, message: 'OK', details: {} });
|
|
108
119
|
|
|
109
120
|
await executeCommand('cmd', { json: true }, io);
|
|
110
121
|
|
|
@@ -119,8 +130,7 @@ describe('command command', () => {
|
|
|
119
130
|
|
|
120
131
|
describe('--level', () => {
|
|
121
132
|
it('low:ok,打印等级', async () => {
|
|
122
|
-
|
|
123
|
-
mockGate(jest.fn().mockResolvedValue({ passed: true, message: 'OK' }));
|
|
133
|
+
mockGate({ passed: true, message: 'OK' }, 'low');
|
|
124
134
|
|
|
125
135
|
const result = await executeCommand('safe-cmd', { level: true }, io);
|
|
126
136
|
|
|
@@ -129,8 +139,7 @@ describe('command command', () => {
|
|
|
129
139
|
});
|
|
130
140
|
|
|
131
141
|
it('high:fail + 等级原因(严重级条件式译成 kind)', async () => {
|
|
132
|
-
|
|
133
|
-
mockGate(jest.fn().mockResolvedValue({ passed: true, message: 'OK' }));
|
|
142
|
+
mockGate({ passed: true, message: 'OK' }, 'high');
|
|
134
143
|
|
|
135
144
|
const result = await executeCommand('rm -rf /', { level: true }, io);
|
|
136
145
|
|
|
@@ -138,9 +147,7 @@ describe('command command', () => {
|
|
|
138
147
|
});
|
|
139
148
|
|
|
140
149
|
it('high 等级不再执行黑名单裁决分支', async () => {
|
|
141
|
-
|
|
142
|
-
mockGetRisk.mockReturnValue('high');
|
|
143
|
-
mockGate(mockCheck);
|
|
150
|
+
mockGate({ passed: true, message: 'OK' }, 'high');
|
|
144
151
|
|
|
145
152
|
const result = await executeCommand('rm -rf /', { level: true }, io);
|
|
146
153
|
|
|
@@ -150,8 +157,7 @@ describe('command command', () => {
|
|
|
150
157
|
});
|
|
151
158
|
|
|
152
159
|
it('--json 输出等级', async () => {
|
|
153
|
-
|
|
154
|
-
mockGate(jest.fn().mockResolvedValue({ passed: true, message: 'OK' }));
|
|
160
|
+
mockGate({ passed: true, message: 'OK' }, 'medium');
|
|
155
161
|
|
|
156
162
|
await executeCommand('cmd', { level: true, json: true }, io);
|
|
157
163
|
|
|
@@ -160,5 +166,15 @@ describe('command command', () => {
|
|
|
160
166
|
command: 'cmd',
|
|
161
167
|
});
|
|
162
168
|
});
|
|
169
|
+
|
|
170
|
+
it('#135:等级取自本命令创建的那台实例,全程只创建一次', async () => {
|
|
171
|
+
const gate = mockGate({ passed: false, message: 'Blocked' }, 'high');
|
|
172
|
+
|
|
173
|
+
const result = await executeCommand('danger-cmd', { level: true }, io);
|
|
174
|
+
|
|
175
|
+
expect(gate.getRiskLevel).toHaveBeenCalledWith('danger-cmd');
|
|
176
|
+
expect(mockCreateGate).toHaveBeenCalledTimes(1);
|
|
177
|
+
expect(result).toEqual({ kind: 'fail', reason: expect.stringContaining('risk level is high') });
|
|
178
|
+
});
|
|
163
179
|
});
|
|
164
180
|
});
|
|
@@ -313,6 +313,58 @@ describe('retireConstraint 执行逻辑', () => {
|
|
|
313
313
|
});
|
|
314
314
|
});
|
|
315
315
|
|
|
316
|
+
describe('落盘字节与装载次数冻结(harness#137)', () => {
|
|
317
|
+
/**
|
|
318
|
+
* YAML 读-改-写两段合并成 `setYamlEntry` 属内部重构:对外产物逐字不得漂移
|
|
319
|
+
* (票验收 6)。故此处钉的是**字节**而非 yaml.load 后的对象——键序、缩进、
|
|
320
|
+
* 行宽 120 的折叠口径、注释丢弃后的形态都在断言范围内。
|
|
321
|
+
*/
|
|
322
|
+
it('内置退役落 config.yml:逐字节冻结', () => {
|
|
323
|
+
const root = createProjectFixture({
|
|
324
|
+
name: 'harness-retire-test',
|
|
325
|
+
config: '# 项目配置\npreset: standard\nscenes:\n - llm-app\n',
|
|
326
|
+
});
|
|
327
|
+
|
|
328
|
+
retireConstraint(root, 'capability_sync', { reason: '逐字节冻结', now: FIXED_NOW });
|
|
329
|
+
|
|
330
|
+
expect(fs.readFileSync(path.join(root, '.harness', 'config.yml'), 'utf-8')).toBe(
|
|
331
|
+
'preset: standard\nscenes:\n - llm-app\nconstraints:\n capability_sync:\n enabled: false\n' +
|
|
332
|
+
" retired:\n at: '2026-08-08T12:00:00.000Z'\n reason: 逐字节冻结\n" +
|
|
333
|
+
' stats:\n total: 0\n fail: 0\n failRate: 0\n'
|
|
334
|
+
);
|
|
335
|
+
});
|
|
336
|
+
|
|
337
|
+
it('custom 退役落 custom-constraints.yml:逐字节冻结(规则原文保留、retired 段追加在末尾)', () => {
|
|
338
|
+
const root = createProjectFixture({ name: 'harness-retire-test' });
|
|
339
|
+
writeCustom(root, `# 自定义约束\n${CUSTOM_YML}`);
|
|
340
|
+
|
|
341
|
+
retireConstraint(root, 'my_custom_rule', { reason: '逐字节冻结', now: FIXED_NOW });
|
|
342
|
+
|
|
343
|
+
expect(fs.readFileSync(path.join(root, '.harness', 'custom-constraints.yml'), 'utf-8')).toBe(
|
|
344
|
+
'custom_constraints:\n my_custom_rule:\n level: iron_law\n rule: 禁止引入 X\n' +
|
|
345
|
+
' message: X 已由平台能力替代\n promptInjection: 禁止引入 X\n' +
|
|
346
|
+
" retired:\n at: '2026-08-08T12:00:00.000Z'\n reason: 逐字节冻结\n" +
|
|
347
|
+
' stats:\n total: 0\n fail: 0\n failRate: 0\n'
|
|
348
|
+
);
|
|
349
|
+
});
|
|
350
|
+
|
|
351
|
+
it('一次 retireConstraint 只装载一次项目配置(此前 custom 路径走 3 次 load)', async () => {
|
|
352
|
+
const { ProjectConfigLoader } = await import('../../../core/project-config-loader');
|
|
353
|
+
const loadSpy = jest.spyOn(ProjectConfigLoader.prototype, 'load');
|
|
354
|
+
const root = createProjectFixture({ name: 'harness-retire-test' });
|
|
355
|
+
writeCustom(root, CUSTOM_YML);
|
|
356
|
+
loadSpy.mockClear();
|
|
357
|
+
|
|
358
|
+
retireConstraint(root, 'my_custom_rule', { now: FIXED_NOW });
|
|
359
|
+
expect(loadSpy).toHaveBeenCalledTimes(1);
|
|
360
|
+
|
|
361
|
+
loadSpy.mockClear();
|
|
362
|
+
retireConstraint(root, 'my_custom_rule', { now: new Date('2026-08-09T00:00:00.000Z') });
|
|
363
|
+
expect(loadSpy).toHaveBeenCalledTimes(1);
|
|
364
|
+
loadSpy.mockRestore();
|
|
365
|
+
});
|
|
366
|
+
});
|
|
367
|
+
|
|
316
368
|
describe('constraintsRetire 非交互直达', () => {
|
|
317
369
|
it('无 --yes 直达:报错 + 非零退出码 + 不落盘任何文件(#24 人确认闸门)', async () => {
|
|
318
370
|
const root = createProjectFixture({ name: 'harness-retire-test' });
|