@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,181 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* pre-push 钩子的真 push 行为(harness#144)
|
|
3
|
+
*
|
|
4
|
+
* scaffold/init 测试能看到「落盘了什么字节」,看不到 git 到底怎么待见这份字节。
|
|
5
|
+
* 本文件不 mock 任何 IO、也不重写一份钩子正文:在临时 git 仓库里把 `init` 落盘的
|
|
6
|
+
* 那份正文(`renderPrePushHook()` 正本)连可执行位一起装上,接一个本地 bare 远端,
|
|
7
|
+
* 把 PATH 上的 `npx` 换成记账替身,真跑 `git push`。按票验收标准逐条钉住:
|
|
8
|
+
* 1. 整仓全量兜底生效:任一失败(check 或 validate)→ push 非零退出、远端收不到东西、
|
|
9
|
+
* 输出指明是哪一道挡的;
|
|
10
|
+
* 2. 两道门禁全过 → push 成功,且调用序列就是 check → validate;
|
|
11
|
+
* 3. 不内建逃生机制:唯一的口子是 git 原生 `--no-verify`,它一开钩子整个人不被调用;
|
|
12
|
+
* 4. 不做增量:stdin 里没有待推 ref 清单(删远端分支)时照样整仓过一遍。
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
import { execFileSync, spawnSync, type SpawnSyncReturns } from 'child_process';
|
|
16
|
+
import * as fs from 'fs';
|
|
17
|
+
import * as os from 'os';
|
|
18
|
+
import * as path from 'path';
|
|
19
|
+
import { renderPrePushHook } from '../scaffold-templates';
|
|
20
|
+
|
|
21
|
+
/** 替身 npx:把每次调用的参数记进日志,按环境变量决定各自退出码 */
|
|
22
|
+
const STUB = `#!/bin/sh
|
|
23
|
+
printf '%s\\n' "$*" >> "$HARNESS_NPX_LOG"
|
|
24
|
+
case "$*" in
|
|
25
|
+
*validate*) exit \${HARNESS_VALIDATE_EXIT:-0} ;;
|
|
26
|
+
*) exit \${HARNESS_CHECK_EXIT:-0} ;;
|
|
27
|
+
esac
|
|
28
|
+
`;
|
|
29
|
+
|
|
30
|
+
/** 两道门禁各自的退出码(缺省 0 = 通过) */
|
|
31
|
+
interface Knobs {
|
|
32
|
+
check?: number;
|
|
33
|
+
validate?: number;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
interface Fixture {
|
|
37
|
+
/** 工作仓库(钩子装在它的 .git/hooks/pre-push) */
|
|
38
|
+
repo: string;
|
|
39
|
+
branch: string;
|
|
40
|
+
/** 真跑 git push(argv 数组,不经 shell) */
|
|
41
|
+
push: (knobs: Knobs, ...args: string[]) => SpawnSyncReturns<string>;
|
|
42
|
+
/** 替身 npx 被调用的参数清单 = 钩子里 harness 命令的真实调用序列 */
|
|
43
|
+
calls: () => string[];
|
|
44
|
+
resetCalls: () => void;
|
|
45
|
+
remoteHeads: () => string[];
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function git(dir: string, ...args: string[]): void {
|
|
49
|
+
execFileSync('git', args, { cwd: dir, stdio: 'pipe' });
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function fixture(): Fixture {
|
|
53
|
+
const root = fs.mkdtempSync(path.join(os.tmpdir(), 'harness-prepush-'));
|
|
54
|
+
const repo = path.join(root, 'work');
|
|
55
|
+
const remote = path.join(root, 'remote.git');
|
|
56
|
+
const stubDir = path.join(root, 'stub');
|
|
57
|
+
const log = path.join(root, 'npx.log');
|
|
58
|
+
|
|
59
|
+
fs.mkdirSync(stubDir, { recursive: true });
|
|
60
|
+
const stub = path.join(stubDir, 'npx');
|
|
61
|
+
fs.writeFileSync(stub, STUB, 'utf-8');
|
|
62
|
+
fs.chmodSync(stub, 0o755);
|
|
63
|
+
|
|
64
|
+
fs.mkdirSync(repo, { recursive: true });
|
|
65
|
+
git(repo, 'init', '-q');
|
|
66
|
+
git(repo, 'config', 'user.email', 'fixture@example.com');
|
|
67
|
+
git(repo, 'config', 'user.name', 'Fixture');
|
|
68
|
+
// 仓库级钉死钩子目录:全局 core.hooksPath 会把测试变成「测别人家的钩子」
|
|
69
|
+
git(repo, 'config', 'core.hooksPath', path.join(repo, '.git', 'hooks'));
|
|
70
|
+
fs.writeFileSync(path.join(repo, 'README.md'), '# fixture\n');
|
|
71
|
+
git(repo, 'add', '.');
|
|
72
|
+
git(repo, 'commit', '-q', '-m', 'baseline');
|
|
73
|
+
|
|
74
|
+
// 装上 init 会落的那份正文(正本,不手抄),连可执行位一起(mode 与站点同值 0o755)
|
|
75
|
+
const hook = path.join(repo, '.git', 'hooks', 'pre-push');
|
|
76
|
+
fs.writeFileSync(hook, renderPrePushHook(), 'utf-8');
|
|
77
|
+
fs.chmodSync(hook, 0o755);
|
|
78
|
+
|
|
79
|
+
git(repo, 'init', '-q', '--bare', remote);
|
|
80
|
+
git(repo, 'remote', 'add', 'origin', remote);
|
|
81
|
+
const branch = execFileSync('git', ['rev-parse', '--abbrev-ref', 'HEAD'], {
|
|
82
|
+
cwd: repo,
|
|
83
|
+
encoding: 'utf-8',
|
|
84
|
+
}).trim();
|
|
85
|
+
|
|
86
|
+
return {
|
|
87
|
+
repo,
|
|
88
|
+
branch,
|
|
89
|
+
push: (knobs, ...args) =>
|
|
90
|
+
spawnSync('git', ['push', ...args], {
|
|
91
|
+
cwd: repo,
|
|
92
|
+
encoding: 'utf-8',
|
|
93
|
+
env: {
|
|
94
|
+
...process.env,
|
|
95
|
+
PATH: `${stubDir}:${process.env.PATH}`,
|
|
96
|
+
HARNESS_NPX_LOG: log,
|
|
97
|
+
HARNESS_CHECK_EXIT: String(knobs.check ?? 0),
|
|
98
|
+
HARNESS_VALIDATE_EXIT: String(knobs.validate ?? 0),
|
|
99
|
+
},
|
|
100
|
+
}),
|
|
101
|
+
calls: () =>
|
|
102
|
+
fs.existsSync(log)
|
|
103
|
+
? fs
|
|
104
|
+
.readFileSync(log, 'utf-8')
|
|
105
|
+
.split('\n')
|
|
106
|
+
.map(line => line.trim())
|
|
107
|
+
.filter(Boolean)
|
|
108
|
+
: [],
|
|
109
|
+
resetCalls: () => {
|
|
110
|
+
try {
|
|
111
|
+
fs.unlinkSync(log);
|
|
112
|
+
} catch {
|
|
113
|
+
// 首趟就没跑过钩子,无日志可清
|
|
114
|
+
}
|
|
115
|
+
},
|
|
116
|
+
remoteHeads: () =>
|
|
117
|
+
execFileSync('git', ['ls-remote', '--heads', remote], { encoding: 'utf-8' })
|
|
118
|
+
.split('\n')
|
|
119
|
+
.map(line => line.trim())
|
|
120
|
+
.filter(Boolean),
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
const output = (r: SpawnSyncReturns<string>): string => `${r.stdout}${r.stderr}`;
|
|
125
|
+
|
|
126
|
+
describe('pre-push 钩子真挡得住 push(harness#144)', () => {
|
|
127
|
+
it('两道门禁全过 → push 成功,钩子里 check 与 validate 各跑一次(check 不带 --staged)', () => {
|
|
128
|
+
const f = fixture();
|
|
129
|
+
|
|
130
|
+
const r = f.push({}, 'origin', 'HEAD');
|
|
131
|
+
|
|
132
|
+
expect(r.status).toBe(0);
|
|
133
|
+
expect(f.remoteHeads()).toHaveLength(1);
|
|
134
|
+
expect(f.calls()).toEqual(['@dommaker/harness check', '@dommaker/harness validate']);
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
it('check 失败 → push 被挡、退出非零、输出指明是铁律检查、远端一格未收', () => {
|
|
138
|
+
const f = fixture();
|
|
139
|
+
|
|
140
|
+
const r = f.push({ check: 1 }, 'origin', 'HEAD');
|
|
141
|
+
|
|
142
|
+
expect(r.status).not.toBe(0);
|
|
143
|
+
expect(output(r)).toContain('🔍 Running harness pre-push checks (whole repo)...');
|
|
144
|
+
expect(output(r)).toContain('❌ Iron law check failed');
|
|
145
|
+
expect(f.calls()).toEqual(['@dommaker/harness check']);
|
|
146
|
+
expect(f.remoteHeads()).toEqual([]);
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
it('validate 失败同样挡住 push(不是只看 check),输出指明是哪一道', () => {
|
|
150
|
+
const f = fixture();
|
|
151
|
+
|
|
152
|
+
const r = f.push({ check: 0, validate: 1 }, 'origin', 'HEAD');
|
|
153
|
+
|
|
154
|
+
expect(r.status).not.toBe(0);
|
|
155
|
+
expect(output(r)).toContain('❌ Validate failed');
|
|
156
|
+
expect(f.calls()).toEqual(['@dommaker/harness check', '@dommaker/harness validate']);
|
|
157
|
+
expect(f.remoteHeads()).toEqual([]);
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
it('唯一逃生口是 git 原生 --no-verify:失败门禁下 push 照过,钩子整个人没被调用', () => {
|
|
161
|
+
const f = fixture();
|
|
162
|
+
|
|
163
|
+
const r = f.push({ check: 1 }, '--no-verify', 'origin', 'HEAD');
|
|
164
|
+
|
|
165
|
+
expect(r.status).toBe(0);
|
|
166
|
+
expect(f.remoteHeads()).toHaveLength(1);
|
|
167
|
+
expect(f.calls()).toEqual([]);
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
it('不做增量:删远端分支时 stdin 里没有待推 ref 清单,仍整仓跑一遍 check', () => {
|
|
171
|
+
const f = fixture();
|
|
172
|
+
expect(f.push({}, 'origin', 'HEAD').status).toBe(0);
|
|
173
|
+
f.resetCalls();
|
|
174
|
+
|
|
175
|
+
const r = f.push({ check: 1 }, '--delete', 'origin', f.branch);
|
|
176
|
+
|
|
177
|
+
expect(r.status).not.toBe(0);
|
|
178
|
+
expect(f.calls()).toEqual(['@dommaker/harness check']);
|
|
179
|
+
expect(f.remoteHeads()).toHaveLength(1);
|
|
180
|
+
});
|
|
181
|
+
});
|
|
@@ -1,22 +1,30 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* -p/--project-path 锚定回归(harness#95)
|
|
3
3
|
*
|
|
4
|
-
*
|
|
4
|
+
* 各站点的命令都接受 -p,但真正执行/IO 的一侧过去取 process.cwd():不 cd 到目标工程调用,
|
|
5
5
|
* 就会拿 A 工程的命令、在 B 目录跑、把证据写进 B;acceptance 的「无 tasks.yml 即跳过」
|
|
6
6
|
* 本身是 passed:true,于是 -p 失效直接表现为假绿。
|
|
7
7
|
*
|
|
8
8
|
* 既有单测全部 mock fs/exec + 假 projectPath,恰好抹平两种取径的差异。本文件不 mock 任何 IO,
|
|
9
9
|
* 前提统一为 **cwd ≠ projectPath**(cwd = A,-p = B),断言读写位置落在 B。
|
|
10
10
|
* 站点 2 从两面各断言一次:CLI 命令面(`acceptance -p`)与门禁实例面(默认构造 = 注册表单例形状)。
|
|
11
|
+
*
|
|
12
|
+
* 站点 3(harness#139)是 trace 落点:`check --project-path B` 的约束评估真跑了 B,
|
|
13
|
+
* 但 trace 写进了 A——写侧不锚,按 projectPath 直读的读侧(`status`)就读不到刚写的记录,
|
|
14
|
+
* 即 #95 点名的假绿族。故本站点从写、读两面各断言一次。
|
|
11
15
|
*/
|
|
12
16
|
|
|
13
17
|
import * as fs from 'fs';
|
|
14
18
|
import * as path from 'path';
|
|
19
|
+
import { execFileSync } from 'child_process';
|
|
15
20
|
import { createProjectFixture } from '../../../test-setup/project-fixture';
|
|
16
21
|
import { SpecAcceptanceGate } from '../../../gates/acceptance';
|
|
22
|
+
import { DEFAULT_TRACE_FILE } from '../../../types/trace';
|
|
17
23
|
import { captureIO } from '../../command-contract';
|
|
18
24
|
import { runPassesGate } from '../passes-gate';
|
|
19
25
|
import { acceptance } from '../acceptance';
|
|
26
|
+
import { check } from '../check';
|
|
27
|
+
import { status } from '../status';
|
|
20
28
|
|
|
21
29
|
/** 在自身 cwd 落标记文件:哪一侧被真正调用,标记就出现在哪一侧的目录里 */
|
|
22
30
|
function markScript(marker: string): string {
|
|
@@ -92,7 +100,7 @@ describe('-p 锚定:执行与 IO 必须落在 projectPath(cwd ≠ projectPat
|
|
|
92
100
|
});
|
|
93
101
|
|
|
94
102
|
describe('acceptance(站点 2:tasks.yml 解析)', () => {
|
|
95
|
-
it('
|
|
103
|
+
it('默认构造的门禁实例(注册表单例形状)→ 读 B/tasks.yml,不读 cwd 的', async () => {
|
|
96
104
|
const cwdSide = createProjectFixture({
|
|
97
105
|
name: 'acccfg-cwd-side',
|
|
98
106
|
files: { 'tasks.yml': tasksYml(true) },
|
|
@@ -154,4 +162,89 @@ describe('-p 锚定:执行与 IO 必须落在 projectPath(cwd ≠ projectPat
|
|
|
154
162
|
});
|
|
155
163
|
});
|
|
156
164
|
});
|
|
165
|
+
|
|
166
|
+
describe('check / status(站点 3:trace 落点与读-写闭环)', () => {
|
|
167
|
+
/** 在 fixture 里跑 git(argv 数组,不经 shell) */
|
|
168
|
+
function git(dir: string, ...args: string[]): void {
|
|
169
|
+
execFileSync('git', args, { cwd: dir, stdio: 'pipe' });
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
/** check 能真跑完的项目:已提交基线 + staged 改动 + 一条 pass trace(验证证据) */
|
|
173
|
+
function checkProject(name: string): string {
|
|
174
|
+
const dir = createProjectFixture({
|
|
175
|
+
name,
|
|
176
|
+
files: { 'README.md': '# fixture\n', 'src/existing.ts': 'export const a = 1;\n' },
|
|
177
|
+
traces: [{ constraintId: 'fixture', result: 'pass' }],
|
|
178
|
+
});
|
|
179
|
+
git(dir, 'init', '-q');
|
|
180
|
+
git(dir, 'config', 'user.email', 'fixture@example.com');
|
|
181
|
+
git(dir, 'config', 'user.name', 'Fixture');
|
|
182
|
+
git(dir, 'add', '.');
|
|
183
|
+
git(dir, 'commit', '-q', '-m', 'baseline');
|
|
184
|
+
fs.writeFileSync(path.join(dir, 'src/existing.ts'), 'export const a = 2;\n', 'utf-8');
|
|
185
|
+
git(dir, 'add', '--', 'src/existing.ts');
|
|
186
|
+
return dir;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
/** trace 正本的行(按生产落点 DEFAULT_TRACE_FILE 解析,不另立路径口径) */
|
|
190
|
+
function traceLinesOf(projectRoot: string): string[] {
|
|
191
|
+
return fs
|
|
192
|
+
.readFileSync(path.join(projectRoot, DEFAULT_TRACE_FILE), 'utf-8')
|
|
193
|
+
.split('\n')
|
|
194
|
+
.filter(Boolean);
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
it('--project-path B → trace 落 B/.harness/logs/traces.log,A 侧不长出文件', async () => {
|
|
198
|
+
const cwdSide = createProjectFixture({ name: 'traceanchor-cwd' });
|
|
199
|
+
const projectSide = checkProject('traceanchor-project');
|
|
200
|
+
process.chdir(cwdSide);
|
|
201
|
+
const seeded = traceLinesOf(projectSide).length;
|
|
202
|
+
|
|
203
|
+
const result = await check(
|
|
204
|
+
{ staged: true, projectPath: projectSide, trigger: 'code_implementation' },
|
|
205
|
+
captureIO()
|
|
206
|
+
);
|
|
207
|
+
expect(result).toEqual({ kind: 'ok' });
|
|
208
|
+
|
|
209
|
+
const written = traceLinesOf(projectSide)
|
|
210
|
+
.slice(seeded)
|
|
211
|
+
.map(l => JSON.parse(l) as { constraintId: string; projectPath?: string });
|
|
212
|
+
expect(written.map(t => t.constraintId)).toEqual(
|
|
213
|
+
expect.arrayContaining(['incremental_progress', 'no_implementation_without_requirement'])
|
|
214
|
+
);
|
|
215
|
+
expect(written.every(t => t.projectPath === projectSide)).toBe(true);
|
|
216
|
+
// 病灶:修复前这一支落在调用方 cwd(A),B 侧 mtime 不动
|
|
217
|
+
expect(fs.existsSync(path.join(cwdSide, DEFAULT_TRACE_FILE))).toBe(false);
|
|
218
|
+
});
|
|
219
|
+
|
|
220
|
+
it('读-写闭环:status --project-path B 读得到上一步 check 刚写的 trace', async () => {
|
|
221
|
+
const cwdSide = createProjectFixture({ name: 'traceanchor-rw-cwd' });
|
|
222
|
+
const projectSide = checkProject('traceanchor-rw-project');
|
|
223
|
+
process.chdir(cwdSide);
|
|
224
|
+
|
|
225
|
+
await check(
|
|
226
|
+
{ staged: true, projectPath: projectSide, trigger: 'code_implementation' },
|
|
227
|
+
captureIO()
|
|
228
|
+
);
|
|
229
|
+
|
|
230
|
+
const io = captureIO();
|
|
231
|
+
expect(await status({ projectPath: projectSide }, io)).toEqual({ kind: 'ok' });
|
|
232
|
+
// 记录数取自 B 的正本行数:写侧漏到 A 就必然对不上(假绿),读到 A 也读不到 check 的落点
|
|
233
|
+
expect(io.outText()).toContain(`记录数: ${traceLinesOf(projectSide).length} 条`);
|
|
234
|
+
expect(io.outText()).toContain('no_implementation_without_requirement');
|
|
235
|
+
});
|
|
236
|
+
|
|
237
|
+
it('不带 --project-path 的项目内使用:落点仍是当下 cwd(存量使用者无感)', async () => {
|
|
238
|
+
const projectSide = checkProject('traceanchor-nop');
|
|
239
|
+
process.chdir(projectSide);
|
|
240
|
+
const seeded = traceLinesOf(projectSide).length;
|
|
241
|
+
|
|
242
|
+
const result = await check(
|
|
243
|
+
{ staged: true, trigger: 'code_implementation' },
|
|
244
|
+
captureIO()
|
|
245
|
+
);
|
|
246
|
+
expect(result).toEqual({ kind: 'ok' });
|
|
247
|
+
expect(traceLinesOf(projectSide).length).toBeGreaterThan(seeded);
|
|
248
|
+
});
|
|
249
|
+
});
|
|
157
250
|
});
|
|
@@ -5,19 +5,23 @@
|
|
|
5
5
|
* 兜底,之后必须传到每个 IO/执行点**——下游禁止再取一次 cwd,也禁止用相对路径默认值
|
|
6
6
|
* (两者都会让 `-p` 半失效:读写/执行位置悄悄回到调用方的 cwd)。
|
|
7
7
|
*
|
|
8
|
-
*
|
|
8
|
+
* 本文件是这条约定的机器可检面,四道闸:
|
|
9
9
|
* 1. CLI 入口层:cwd 只能以 `xxx || process.cwd()` 的兜底形状出现,例外逐个点名并记理由
|
|
10
10
|
* 2. 下游层(= src 减 cli,含 core / gates / context / monitoring / hooks 等):cwd 站点**逐行**冻结
|
|
11
11
|
* (harness#98,不再只冻文件键集——豁免文件内新增站点/行变形同样失败),
|
|
12
12
|
* 要么把根传下去,要么在此记下豁免理由
|
|
13
13
|
* 3. 相对路径默认值同理冻结(harness#98 收紧:对象字面量 `xxxPath: '相对'` 之外,
|
|
14
|
-
* 参数默认值形 `xxxPath = '相对'
|
|
14
|
+
* 参数默认值形 `xxxPath = '相对'`(含类型标注)与模板字面量同罪;harness#139 再扩两形:
|
|
15
|
+
* 键名后缀 `Path` → `Path|File|Log`,以及引用常量的 `xxxFile: SOME_CONST` 臂;扫描域 = 整个下游层)
|
|
16
|
+
* 4. 组合根自己锚根构造,不再消费 cwd 锚定的全局单例(harness#139:src/cli、src/hooks 生产代码
|
|
17
|
+
* 对 `getTraceCollector()` 零消费——单例是留给跨仓消费者的兼容面,不是本仓的取用口)
|
|
15
18
|
*
|
|
16
19
|
* 冻结集合还要求**每条豁免仍然成立**(站点消失却不删条目 → 同样失败),豁免不会烂成化石。
|
|
17
20
|
* 「根参数已传入却又取 cwd」这类静态看不出的漂移,由 project-path-anchoring.test.ts 的真实 IO 行为用例守。
|
|
18
21
|
*/
|
|
19
22
|
|
|
20
23
|
import * as fs from 'fs';
|
|
24
|
+
import * as os from 'os';
|
|
21
25
|
import * as path from 'path';
|
|
22
26
|
|
|
23
27
|
const SRC_ROOT = path.join(__dirname, '..', '..', '..', '..', 'src');
|
|
@@ -59,6 +63,15 @@ function cwdSites(files: string[]): Map<string, string[]> {
|
|
|
59
63
|
return sites;
|
|
60
64
|
}
|
|
61
65
|
|
|
66
|
+
/** 闸 3 的取站点本体(反证用例与冻结表用例共用同一判据) */
|
|
67
|
+
function relativePathDefaultSites(files: string[]): Set<string> {
|
|
68
|
+
const hits = new Set<string>();
|
|
69
|
+
for (const file of files) {
|
|
70
|
+
if (codeLines(file).some(line => RELATIVE_PATH_DEFAULT.test(line))) hits.add(repoPath(file));
|
|
71
|
+
}
|
|
72
|
+
return hits;
|
|
73
|
+
}
|
|
74
|
+
|
|
62
75
|
// ========================================
|
|
63
76
|
// 闸 1:CLI 入口层的非兜底形状 cwd
|
|
64
77
|
// ========================================
|
|
@@ -68,6 +81,14 @@ const ENTRY_FALLBACK_SHAPE = /\|\|\s*process\.cwd\(\)/;
|
|
|
68
81
|
|
|
69
82
|
/** 兜底形状之外的 CLI cwd 站点:逐个裁决过,理由与站点原文一起冻结 */
|
|
70
83
|
const CLI_CWD_EXEMPTIONS: Record<string, { lines: string[]; reason: string }> = {
|
|
84
|
+
'src/cli/commands/command.ts': {
|
|
85
|
+
lines: ['const decision = await gate.evaluate({ projectPath: process.cwd(), command: cmd });'],
|
|
86
|
+
reason:
|
|
87
|
+
'统一门禁接口的 GateContext 要求项目根,而 CommandGate 只做命令串的正则判定、零文件读写;' +
|
|
88
|
+
'本命令定义表里没有 -p/--project-path(只有 --level/--list/--json),' +
|
|
89
|
+
'取真实 cwd 仅为满足上下文形状,不存在「给了 -p 却被 IO 绕回 cwd」的半失效路径。' +
|
|
90
|
+
'架构评审候选1 步骤 2:六个门禁的判定统一穿过 evaluate()。',
|
|
91
|
+
},
|
|
71
92
|
'src/cli/commands/release.ts': {
|
|
72
93
|
lines: ['const pkgPath = process.cwd();'],
|
|
73
94
|
reason:
|
|
@@ -97,10 +118,12 @@ const DOWNSTREAM_CWD_EXEMPTIONS: Record<string, { lines: string[]; reason: strin
|
|
|
97
118
|
'src/core/constraints/checker.ts': {
|
|
98
119
|
lines: [
|
|
99
120
|
'const projectPath = context.projectPath || process.cwd();',
|
|
100
|
-
'const
|
|
101
|
-
'const
|
|
121
|
+
'const projectPath = context.projectPath || process.cwd();',
|
|
122
|
+
'const projectPath = context.projectPath || process.cwd();',
|
|
102
123
|
],
|
|
103
|
-
reason:
|
|
124
|
+
reason:
|
|
125
|
+
'check 三个 run 入口(checkPrecondition / runAllConstraints / beforeExecution)各取一次根兜底,' +
|
|
126
|
+
'git 证据与运行级观察面都从该变量派生、不再内联取 cwd(ADR-0023 步骤 3)',
|
|
104
127
|
},
|
|
105
128
|
'src/core/constraints/checkers/types.ts': {
|
|
106
129
|
lines: ['const projectPath = context.projectPath || process.cwd();'],
|
|
@@ -113,21 +136,23 @@ const DOWNSTREAM_CWD_EXEMPTIONS: Record<string, { lines: string[]; reason: strin
|
|
|
113
136
|
],
|
|
114
137
|
reason: 'buildCheckEnv 的 options.projectPath 缺省兜底(2 处)',
|
|
115
138
|
},
|
|
139
|
+
'src/core/constraints/run-env.ts': {
|
|
140
|
+
lines: ['return createRunEnv(target || process.cwd());'],
|
|
141
|
+
reason:
|
|
142
|
+
'resolveRunEnv 的根兜底:RunTarget 只给了路径或什么也没给时自造一枚一次性观察面' +
|
|
143
|
+
'(配置访问器族与 ProjectConfigLoader 的入参归一点,ADR-0023 决策 2)',
|
|
144
|
+
},
|
|
116
145
|
'src/core/constraints/usage-report.ts': {
|
|
117
146
|
lines: ['projectRoot: string = process.cwd(),'],
|
|
118
147
|
reason: 'projectRoot 形参默认值(库层可选根)',
|
|
119
148
|
},
|
|
120
149
|
'src/core/effective-constraints.ts': {
|
|
121
150
|
lines: [
|
|
122
|
-
'
|
|
123
|
-
'
|
|
151
|
+
'target: RunTarget = process.cwd(),',
|
|
152
|
+
'target: RunTarget = process.cwd(),',
|
|
124
153
|
'export function lintEffectiveConfig(projectRoot: string = process.cwd()): EffectiveConfigLint {',
|
|
125
154
|
],
|
|
126
|
-
reason: '
|
|
127
|
-
},
|
|
128
|
-
'src/core/project-config-loader.ts': {
|
|
129
|
-
lines: ['this.projectPath = projectPath || process.cwd();'],
|
|
130
|
-
reason: 'config 加载器 projectPath 构造参数缺省兜底',
|
|
155
|
+
reason: '生效集三函数根形参默认值(库层可选根,JSDoc 已声明;前两者入参含 RunEnv,ADR-0023)',
|
|
131
156
|
},
|
|
132
157
|
'src/core/spec/validator.ts': {
|
|
133
158
|
lines: ['const cwd = projectPath || process.cwd();'],
|
|
@@ -137,10 +162,6 @@ const DOWNSTREAM_CWD_EXEMPTIONS: Record<string, { lines: string[]; reason: strin
|
|
|
137
162
|
lines: ['const projectPath = ctx.projectPath || process.cwd();'],
|
|
138
163
|
reason: 'GateContext.projectPath 缺省兜底(门禁适配器的根入口)',
|
|
139
164
|
},
|
|
140
|
-
'src/gates/effective-gates.ts': {
|
|
141
|
-
lines: ['export function getEffectiveGates(projectRoot: string = process.cwd()): Gate[] {'],
|
|
142
|
-
reason: 'projectRoot 形参默认值(库层可选根)',
|
|
143
|
-
},
|
|
144
165
|
'src/hooks/bootstrap.ts': {
|
|
145
166
|
lines: [
|
|
146
167
|
'const resolvedPath = projectPath || process.cwd();',
|
|
@@ -159,18 +180,38 @@ const DOWNSTREAM_CWD_EXEMPTIONS: Record<string, { lines: string[]; reason: strin
|
|
|
159
180
|
// ========================================
|
|
160
181
|
|
|
161
182
|
/**
|
|
162
|
-
*
|
|
183
|
+
* 键名必须是 camelCase 且以 Path/File/Log 结尾,值为相对字面量或引用常量——这类默认值按 cwd 解析,
|
|
184
|
+
* 即 #95 的病根形状。
|
|
185
|
+
*
|
|
163
186
|
* harness#98 收紧匹配形状(原为只认对象字面量 `xxxPath: '相对'`):
|
|
164
187
|
* 参数默认值形 `xxxPath = '相对'`、带类型标注的 `xxxPath: string = '相对'`、模板字面量、
|
|
165
188
|
* 双引号形同罪(仓内 lint 不强制引号风格,三种引号都得认)。
|
|
189
|
+
*
|
|
190
|
+
* harness#139 再扩两形(traceFile 病灶的形状是「键名不带 Path + 值引用常量」,原闸两头都不认):
|
|
191
|
+
* - 臂 1 的键名后缀 `Path` → `Path|File|Log`(落点类键名的全集,Log 覆盖 `xxxLog` 一族)
|
|
192
|
+
* - 臂 2 新增常量引用形 `xxxFile: SOME_CONSTANT`(值不在本行,是相对片段正本的另一半)
|
|
193
|
+
*
|
|
194
|
+
* 只扫**使用点**,不冻结常量定义本体(`export const DEFAULT_TRACE_FILE = '.harness/…'` 是正本,
|
|
195
|
+
* 锚定责任在消费点)。扩形后下游层新增命中恰好 2 处(本票病灶 traces.ts + trace-analyzer.ts 的
|
|
196
|
+
* summaryFile),连同既有的 validator 豁免共 3 条,零假阳性——见下方豁免表。
|
|
166
197
|
*/
|
|
167
|
-
const RELATIVE_PATH_DEFAULT =
|
|
198
|
+
const RELATIVE_PATH_DEFAULT =
|
|
199
|
+
/\b\w+(?:Path|File|Log)\s*(?::\s*[^=,)]+?)?[:=]\s*['"`](?!\/)|\b\w+(?:Path|File|Log)\s*[:=]\s*[A-Z][A-Z0-9_]+\b/;
|
|
168
200
|
|
|
169
201
|
const RELATIVE_PATH_DEFAULT_EXEMPTIONS: Record<string, string> = {
|
|
170
202
|
'src/core/spec/validator.ts':
|
|
171
203
|
'schemaPath: "./specs/schemas" 确实按 cwd 解析(同型病灶)。不随 #95 修:validateFile()/loadSchema() ' +
|
|
172
204
|
'签名里没有根,补齐要把根穿透整个 spec 域(属 spec 重构,非本票两门禁)。CLI 侧 `spec -s` 已 ' +
|
|
173
205
|
'path.resolve(projectPath, schema),给了 -s 时口径正确。留待 spec 域单票收口。',
|
|
206
|
+
'src/monitoring/traces.ts':
|
|
207
|
+
'DEFAULT_CONFIG.traceFile 引用的是**项目相对片段**正本(#139 明确其语义),锚定在构造函数里做:' +
|
|
208
|
+
'给了 projectPath → path.resolve(projectPath, traceFile),没给 → 保持 cwd 解析(跨仓消费者的兼容面)。' +
|
|
209
|
+
'本行是「相对片段」的声明处,不是「按 cwd 打开」的执行处;锚定行为由 monitoring/__tests__/' +
|
|
210
|
+
'trace-file-anchoring.test.ts 与 cli/commands/__tests__/project-path-anchoring.test.ts 站点 3 钉。',
|
|
211
|
+
'src/monitoring/trace-analyzer.ts':
|
|
212
|
+
'summaryFile: ".harness/logs/traces-summary.json" 同型病灶(#139 实测的第二处命中)。不在本票修:' +
|
|
213
|
+
'harness 侧无生产写点(`status` 自 ADR-0020 起直调纯函数,不构造 analyzer),修它等于给无人消费的面' +
|
|
214
|
+
'新增根参数。与 trace 的 failure/summary 三件套同型病灶一并由 studio 侧迁移票带走(#139 Out of scope)。',
|
|
174
215
|
};
|
|
175
216
|
|
|
176
217
|
describe('projectPath 传递约定(harness#95)', () => {
|
|
@@ -211,13 +252,10 @@ describe('projectPath 传递约定(harness#95)', () => {
|
|
|
211
252
|
});
|
|
212
253
|
|
|
213
254
|
describe('闸 3:相对路径默认值不再按 cwd 解析', () => {
|
|
214
|
-
it('下游层(= src 减 cli
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
}
|
|
219
|
-
|
|
220
|
-
expect([...hits].sort()).toEqual(Object.keys(RELATIVE_PATH_DEFAULT_EXEMPTIONS).sort());
|
|
255
|
+
it('下游层(= src 减 cli)的相对字面量/常量引用路径默认值 = 冻结豁免表', () => {
|
|
256
|
+
expect([...relativePathDefaultSites(downstreamFiles)].sort()).toEqual(
|
|
257
|
+
Object.keys(RELATIVE_PATH_DEFAULT_EXEMPTIONS).sort()
|
|
258
|
+
);
|
|
221
259
|
});
|
|
222
260
|
|
|
223
261
|
it('acceptance 的 tasks.yml 默认不再有相对路径常量(站点 2 的收口形状)', () => {
|
|
@@ -227,5 +265,45 @@ describe('projectPath 传递约定(harness#95)', () => {
|
|
|
227
265
|
expect(body).not.toMatch(/tasksPath\s*:\s*'/);
|
|
228
266
|
expect(body).toMatch(/path\.resolve\(\s*context\.projectPath\s*,\s*[^)]*'tasks\.yml'/);
|
|
229
267
|
});
|
|
268
|
+
|
|
269
|
+
it('反证(#139):未锚的 xxxFile 相对默认值顶闸,锚定与绝对形状不误报', () => {
|
|
270
|
+
const dir = fs.mkdtempSync(path.join(os.tmpdir(), 'gate3probe-'));
|
|
271
|
+
const write = (name: string, body: string): string => {
|
|
272
|
+
const file = path.join(dir, name);
|
|
273
|
+
fs.writeFileSync(file, body, 'utf-8');
|
|
274
|
+
return file;
|
|
275
|
+
};
|
|
276
|
+
|
|
277
|
+
// 臂 2(常量引用)与臂 1(File/Log 后缀的相对字面量)各自要真打得中,冻结表才不是空转
|
|
278
|
+
expect([
|
|
279
|
+
write('unanchored-const.ts', 'export const cfg = { traceFile: DEFAULT_TRACE_FILE };\n'),
|
|
280
|
+
write('unanchored-literal.ts', "export const cfg = { errorLog: 'logs/error.log' };\n"),
|
|
281
|
+
write('unanchored-param.ts', "function f({ summaryPath = './x/y.json' }: { summaryPath?: string }) {}\n"),
|
|
282
|
+
].map(f => relativePathDefaultSites([f]).size)).toEqual([1, 1, 1]);
|
|
283
|
+
|
|
284
|
+
// 锚定动作、绝对值、值非大写常量、键名不在后缀集内 → 都不算站点
|
|
285
|
+
expect([
|
|
286
|
+
write('anchored.ts', 'export const cfg = { traceFile: path.resolve(projectPath, DEFAULT_TRACE_FILE) };\n'),
|
|
287
|
+
write('absolute.ts', "export const cfg = { traceFile: '/abs/traces.log' };\n"),
|
|
288
|
+
write('lowercase-value.ts', 'export const cfg = { traceFile: resolvedFile };\n'),
|
|
289
|
+
write('other-suffix.ts', 'export const cfg = { maxFileSize: 10 * 1024 * 1024 };\n'),
|
|
290
|
+
].map(f => relativePathDefaultSites([f]).size)).toEqual([0, 0, 0, 0]);
|
|
291
|
+
});
|
|
292
|
+
});
|
|
293
|
+
|
|
294
|
+
describe('闸 4:组合根锚根构造,不消费 cwd 锚定的单例(harness#139)', () => {
|
|
295
|
+
it('src/cli 与 src/hooks 的生产代码对 getTraceCollector() 零消费', () => {
|
|
296
|
+
const offenders: string[] = [];
|
|
297
|
+
for (const dir of ['cli', 'hooks']) {
|
|
298
|
+
for (const file of listTsFiles(path.join(SRC_ROOT, dir))) {
|
|
299
|
+
const lines = codeLines(file).filter(line => /getTraceCollector\s*\(/.test(line));
|
|
300
|
+
if (lines.length > 0) offenders.push(`${repoPath(file)}: ${lines.join(' | ')}`);
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
// 单例保留是给跨仓消费者的兼容面(#139 裁决:公共面不摘除);本仓四个组合根各自
|
|
305
|
+
// new TraceCollector({ projectPath }),取用口一旦复活,-p 就又半失效一次
|
|
306
|
+
expect(offenders).toEqual([]);
|
|
307
|
+
});
|
|
230
308
|
});
|
|
231
309
|
});
|
|
@@ -274,6 +274,8 @@ smoke('bin/harness.js 端到端(dist 存在时)', () => {
|
|
|
274
274
|
expect(r.status).toBe(0);
|
|
275
275
|
expect(JSON.parse(r.stdout)).toHaveProperty('total');
|
|
276
276
|
expect(r.implModules).toEqual([
|
|
277
|
+
// #133 起 knowledge 的投影面随命令模块一并懒加载(探针按字典序列出加载项)
|
|
278
|
+
expect.stringContaining('/dist/cli/commands/knowledge-view.js'),
|
|
277
279
|
expect.stringContaining('/dist/cli/commands/knowledge.js'),
|
|
278
280
|
]);
|
|
279
281
|
});
|
|
@@ -4,6 +4,10 @@
|
|
|
4
4
|
|
|
5
5
|
import { report } from '../report';
|
|
6
6
|
import { captureIO, type CapturingIO } from '../../command-contract';
|
|
7
|
+
import { DEFAULT_TRACE_FILE } from '../../../types/trace';
|
|
8
|
+
import * as nodeFs from 'fs';
|
|
9
|
+
import * as os from 'os';
|
|
10
|
+
import * as path from 'path';
|
|
7
11
|
import * as fs from 'fs/promises';
|
|
8
12
|
|
|
9
13
|
// Mock fs/promises
|
|
@@ -52,9 +56,27 @@ describe('report command', () => {
|
|
|
52
56
|
expect(io.outText()).toContain('报告已保存');
|
|
53
57
|
});
|
|
54
58
|
|
|
59
|
+
// 自定义 projectPath 用例的临时目录:afterEach 兜底清理,用例失败也不留垃圾
|
|
60
|
+
let projectPath: string;
|
|
61
|
+
afterEach(() => {
|
|
62
|
+
if (projectPath) {
|
|
63
|
+
nodeFs.rmSync(projectPath, { recursive: true, force: true });
|
|
64
|
+
projectPath = '';
|
|
65
|
+
}
|
|
66
|
+
});
|
|
67
|
+
|
|
55
68
|
it('应该使用自定义项目路径', async () => {
|
|
56
|
-
|
|
69
|
+
// 原断言传的是假绝对路径 '/custom/path':#139 之前 report 不在该路径落任何文件,假路径无感;
|
|
70
|
+
// #139 之后 TraceCollector 按 projectPath 锚根创建 trace 目录,这条路径变成真实写请求——
|
|
71
|
+
// 以 root 身份跑恰好 mkdir 成功(并在文件系统根下留下目录),非 root 的 CI runner 直接 EACCES。
|
|
72
|
+
// 换成现场创建的临时目录:环境无关,并把 #139 的锚根行为写成断言(原来只验「有输出」)。
|
|
73
|
+
projectPath = nodeFs.mkdtempSync(path.join(os.tmpdir(), 'harness-report-pp-'));
|
|
74
|
+
|
|
75
|
+
await report({ format: 'json', projectPath }, io);
|
|
57
76
|
expect(io.outText()).not.toBe('');
|
|
77
|
+
expect(
|
|
78
|
+
nodeFs.existsSync(path.join(projectPath, path.dirname(DEFAULT_TRACE_FILE)))
|
|
79
|
+
).toBe(true);
|
|
58
80
|
});
|
|
59
81
|
|
|
60
82
|
it('Markdown 报告应包含表格结构', async () => {
|
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
import { review, reviewStatus } from '../review';
|
|
6
6
|
import { captureIO, type CapturingIO } from '../../command-contract';
|
|
7
|
+
import { decide, fakeGate, throwingGate } from './gate-decision';
|
|
7
8
|
import { execAsync } from '../../../utils/exec';
|
|
8
9
|
import { ReviewGate } from '../../../gates/review';
|
|
9
10
|
|
|
@@ -13,7 +14,7 @@ jest.mock('../../../utils/exec', () => ({
|
|
|
13
14
|
|
|
14
15
|
jest.mock('../../../gates/review', () => ({
|
|
15
16
|
ReviewGate: jest.fn().mockImplementation(() => ({
|
|
16
|
-
|
|
17
|
+
evaluate: jest.fn(),
|
|
17
18
|
})),
|
|
18
19
|
}));
|
|
19
20
|
|
|
@@ -41,12 +42,9 @@ describe('review command', () => {
|
|
|
41
42
|
describe('review', () => {
|
|
42
43
|
it('should print success when check passes', async () => {
|
|
43
44
|
mockExec.mockResolvedValue({ stdout: 'main\n', stderr: '' });
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
details: { approvals: 2, changesRequested: 0 },
|
|
48
|
-
});
|
|
49
|
-
MockGate.mockImplementation(() => ({ check: mockCheck }) as any);
|
|
45
|
+
MockGate.mockImplementation(() =>
|
|
46
|
+
fakeGate(decide('review', true, 'ok', { approvals: 2, changesRequested: 0 })) as any
|
|
47
|
+
);
|
|
50
48
|
|
|
51
49
|
const result = await review({}, io);
|
|
52
50
|
|
|
@@ -56,12 +54,9 @@ describe('review command', () => {
|
|
|
56
54
|
|
|
57
55
|
it('should print failure and exit 1 when check fails', async () => {
|
|
58
56
|
mockExec.mockResolvedValue({ stdout: 'main\n', stderr: '' });
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
details: { suggestion: 'Request review from a teammate' },
|
|
63
|
-
});
|
|
64
|
-
MockGate.mockImplementation(() => ({ check: mockCheck }) as any);
|
|
57
|
+
MockGate.mockImplementation(() =>
|
|
58
|
+
fakeGate(decide('review', false, 'needs approval', { suggestion: 'Request review from a teammate' })) as any
|
|
59
|
+
);
|
|
65
60
|
|
|
66
61
|
const result = await review({}, io);
|
|
67
62
|
|
|
@@ -74,8 +69,7 @@ describe('review command', () => {
|
|
|
74
69
|
|
|
75
70
|
it('should handle errors and exit 1', async () => {
|
|
76
71
|
mockExec.mockResolvedValue({ stdout: 'main\n', stderr: '' });
|
|
77
|
-
|
|
78
|
-
MockGate.mockImplementation(() => ({ check: mockCheck }) as any);
|
|
72
|
+
MockGate.mockImplementation(() => throwingGate(new Error('gate error')) as any);
|
|
79
73
|
|
|
80
74
|
const result = await review({}, io);
|
|
81
75
|
|
|
@@ -100,8 +94,7 @@ describe('review command', () => {
|
|
|
100
94
|
|
|
101
95
|
it('should parse allowedReviewers from comma-separated string', async () => {
|
|
102
96
|
mockExec.mockResolvedValue({ stdout: 'main\n', stderr: '' });
|
|
103
|
-
|
|
104
|
-
MockGate.mockImplementation(() => ({ check: mockCheck }) as any);
|
|
97
|
+
MockGate.mockImplementation(() => fakeGate(decide('review', true, 'ok')) as any);
|
|
105
98
|
|
|
106
99
|
await review({ allowedReviewers: 'alice, bob, charlie' }, io);
|
|
107
100
|
|