@dommaker/harness 1.2.3 → 1.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +15 -0
- package/README.md +0 -2
- package/bin/harness.js +86 -32
- package/dist/cli/command-contract.d.ts +83 -0
- package/dist/cli/command-contract.d.ts.map +1 -0
- package/dist/cli/command-contract.js +66 -0
- package/dist/cli/command-contract.js.map +1 -0
- package/dist/cli/commands/acceptance.d.ts +3 -2
- package/dist/cli/commands/acceptance.d.ts.map +1 -1
- package/dist/cli/commands/acceptance.js +28 -24
- package/dist/cli/commands/acceptance.js.map +1 -1
- package/dist/cli/commands/analyze-sessions.d.ts +2 -1
- package/dist/cli/commands/analyze-sessions.d.ts.map +1 -1
- package/dist/cli/commands/analyze-sessions.js +25 -24
- package/dist/cli/commands/analyze-sessions.js.map +1 -1
- package/dist/cli/commands/check.d.ts +14 -2
- package/dist/cli/commands/check.d.ts.map +1 -1
- package/dist/cli/commands/check.js +76 -61
- package/dist/cli/commands/check.js.map +1 -1
- package/dist/cli/commands/command.d.ts +5 -1
- package/dist/cli/commands/command.d.ts.map +1 -1
- package/dist/cli/commands/command.js +26 -17
- package/dist/cli/commands/command.js.map +1 -1
- package/dist/cli/commands/constraints-report.d.ts +2 -1
- package/dist/cli/commands/constraints-report.d.ts.map +1 -1
- package/dist/cli/commands/constraints-report.js +37 -33
- package/dist/cli/commands/constraints-report.js.map +1 -1
- package/dist/cli/commands/constraints-retire.d.ts +6 -5
- package/dist/cli/commands/constraints-retire.d.ts.map +1 -1
- package/dist/cli/commands/constraints-retire.js +50 -52
- package/dist/cli/commands/constraints-retire.js.map +1 -1
- package/dist/cli/commands/constraints.d.ts +2 -1
- package/dist/cli/commands/constraints.d.ts.map +1 -1
- package/dist/cli/commands/constraints.js +10 -27
- package/dist/cli/commands/constraints.js.map +1 -1
- package/dist/cli/commands/contract.d.ts +3 -2
- package/dist/cli/commands/contract.d.ts.map +1 -1
- package/dist/cli/commands/contract.js +45 -42
- package/dist/cli/commands/contract.js.map +1 -1
- package/dist/cli/commands/definitions.d.ts +11 -11
- package/dist/cli/commands/definitions.d.ts.map +1 -1
- package/dist/cli/commands/definitions.js +30 -215
- package/dist/cli/commands/definitions.js.map +1 -1
- package/dist/cli/commands/failure.d.ts +5 -4
- package/dist/cli/commands/failure.d.ts.map +1 -1
- package/dist/cli/commands/failure.js +33 -26
- package/dist/cli/commands/failure.js.map +1 -1
- package/dist/cli/commands/init.d.ts +7 -5
- package/dist/cli/commands/init.d.ts.map +1 -1
- package/dist/cli/commands/init.js +147 -211
- package/dist/cli/commands/init.js.map +1 -1
- package/dist/cli/commands/knowledge.d.ts +21 -15
- package/dist/cli/commands/knowledge.d.ts.map +1 -1
- package/dist/cli/commands/knowledge.js +237 -199
- package/dist/cli/commands/knowledge.js.map +1 -1
- package/dist/cli/commands/passes-gate.d.ts +8 -2
- package/dist/cli/commands/passes-gate.d.ts.map +1 -1
- package/dist/cli/commands/passes-gate.js +44 -66
- package/dist/cli/commands/passes-gate.js.map +1 -1
- package/dist/cli/commands/performance.d.ts +2 -1
- package/dist/cli/commands/performance.d.ts.map +1 -1
- package/dist/cli/commands/performance.js +20 -18
- package/dist/cli/commands/performance.js.map +1 -1
- package/dist/cli/commands/posteval-plan.d.ts +5 -1
- package/dist/cli/commands/posteval-plan.d.ts.map +1 -1
- package/dist/cli/commands/posteval-plan.js +26 -15
- package/dist/cli/commands/posteval-plan.js.map +1 -1
- package/dist/cli/commands/release.d.ts +6 -1
- package/dist/cli/commands/release.d.ts.map +1 -1
- package/dist/cli/commands/release.js +73 -77
- package/dist/cli/commands/release.js.map +1 -1
- package/dist/cli/commands/report.d.ts +2 -1
- package/dist/cli/commands/report.d.ts.map +1 -1
- package/dist/cli/commands/report.js +9 -5
- package/dist/cli/commands/report.js.map +1 -1
- package/dist/cli/commands/review.d.ts +3 -2
- package/dist/cli/commands/review.d.ts.map +1 -1
- package/dist/cli/commands/review.js +33 -29
- package/dist/cli/commands/review.js.map +1 -1
- package/dist/cli/commands/sdd.d.ts +2 -1
- package/dist/cli/commands/sdd.d.ts.map +1 -1
- package/dist/cli/commands/sdd.js +7 -5
- package/dist/cli/commands/sdd.js.map +1 -1
- package/dist/cli/commands/security.d.ts +3 -2
- package/dist/cli/commands/security.d.ts.map +1 -1
- package/dist/cli/commands/security.js +29 -26
- package/dist/cli/commands/security.js.map +1 -1
- package/dist/cli/commands/spec-baseline-check.d.ts +2 -1
- package/dist/cli/commands/spec-baseline-check.d.ts.map +1 -1
- package/dist/cli/commands/spec-baseline-check.js +15 -11
- package/dist/cli/commands/spec-baseline-check.js.map +1 -1
- package/dist/cli/commands/spec.d.ts +7 -2
- package/dist/cli/commands/spec.d.ts.map +1 -1
- package/dist/cli/commands/spec.js +61 -51
- package/dist/cli/commands/spec.js.map +1 -1
- package/dist/cli/commands/status.d.ts +2 -1
- package/dist/cli/commands/status.d.ts.map +1 -1
- package/dist/cli/commands/status.js +57 -53
- package/dist/cli/commands/status.js.map +1 -1
- package/dist/cli/commands/sync-docs/agents-syncer.d.ts.map +1 -1
- package/dist/cli/commands/sync-docs/agents-syncer.js +31 -16
- package/dist/cli/commands/sync-docs/agents-syncer.js.map +1 -1
- package/dist/cli/commands/sync-docs/context-syncer.d.ts.map +1 -1
- package/dist/cli/commands/sync-docs/context-syncer.js +3 -2
- package/dist/cli/commands/sync-docs/context-syncer.js.map +1 -1
- package/dist/cli/commands/sync-docs/index.d.ts +2 -1
- package/dist/cli/commands/sync-docs/index.d.ts.map +1 -1
- package/dist/cli/commands/sync-docs/index.js +68 -88
- package/dist/cli/commands/sync-docs/index.js.map +1 -1
- package/dist/cli/commands/sync-docs/project-reader.d.ts +6 -1
- package/dist/cli/commands/sync-docs/project-reader.d.ts.map +1 -1
- package/dist/cli/commands/sync-docs/project-reader.js +14 -16
- package/dist/cli/commands/sync-docs/project-reader.js.map +1 -1
- package/dist/cli/commands/update-user-model.d.ts +2 -1
- package/dist/cli/commands/update-user-model.d.ts.map +1 -1
- package/dist/cli/commands/update-user-model.js +31 -45
- package/dist/cli/commands/update-user-model.js.map +1 -1
- package/dist/cli/commands/validate.d.ts +4 -3
- package/dist/cli/commands/validate.d.ts.map +1 -1
- package/dist/cli/commands/validate.js +28 -26
- package/dist/cli/commands/validate.js.map +1 -1
- package/dist/cli/session-mining/transcript.d.ts +7 -1
- package/dist/cli/session-mining/transcript.d.ts.map +1 -1
- package/dist/cli/session-mining/transcript.js +21 -14
- package/dist/cli/session-mining/transcript.js.map +1 -1
- package/dist/completion-checkers/index.d.ts +3 -3
- package/dist/completion-checkers/index.js +3 -3
- package/dist/completion-checkers/phase-format.d.ts +1 -1
- package/dist/completion-checkers/phase-format.js +2 -2
- package/dist/completion-checkers/phase-format.js.map +1 -1
- package/dist/completion-checkers/tdd-chain.d.ts +1 -1
- package/dist/completion-checkers/tdd-chain.js +2 -2
- package/dist/completion-checkers/tdd-chain.js.map +1 -1
- package/dist/completion-checkers/types.d.ts +5 -5
- package/dist/completion-checkers/types.d.ts.map +1 -1
- package/dist/completion-checkers/types.js +2 -2
- package/dist/context/session-manager.d.ts.map +1 -1
- package/dist/context/session-manager.js +19 -32
- package/dist/context/session-manager.js.map +1 -1
- package/dist/core/constraints/agent-prompt-renderer.d.ts.map +1 -1
- package/dist/core/constraints/agent-prompt-renderer.js +1 -4
- package/dist/core/constraints/agent-prompt-renderer.js.map +1 -1
- package/dist/core/constraints/capabilities-parser.d.ts +24 -7
- package/dist/core/constraints/capabilities-parser.d.ts.map +1 -1
- package/dist/core/constraints/capabilities-parser.js +32 -28
- package/dist/core/constraints/capabilities-parser.js.map +1 -1
- package/dist/core/constraints/capabilities-reconcile.d.ts +67 -0
- package/dist/core/constraints/capabilities-reconcile.d.ts.map +1 -0
- package/dist/core/constraints/capabilities-reconcile.js +105 -0
- package/dist/core/constraints/capabilities-reconcile.js.map +1 -0
- package/dist/core/constraints/checker.d.ts +21 -43
- package/dist/core/constraints/checker.d.ts.map +1 -1
- package/dist/core/constraints/checker.js +64 -115
- package/dist/core/constraints/checker.js.map +1 -1
- package/dist/core/constraints/checkers/capability-sync.d.ts +4 -3
- package/dist/core/constraints/checkers/capability-sync.d.ts.map +1 -1
- package/dist/core/constraints/checkers/capability-sync.js +29 -69
- package/dist/core/constraints/checkers/capability-sync.js.map +1 -1
- package/dist/core/constraints/checkers/context-doc-sync.d.ts +4 -2
- package/dist/core/constraints/checkers/context-doc-sync.d.ts.map +1 -1
- package/dist/core/constraints/checkers/context-doc-sync.js +13 -22
- 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 +37 -63
- package/dist/core/constraints/checkers/docs-freshness.js.map +1 -1
- package/dist/core/constraints/checkers/governance-presence.d.ts +2 -2
- package/dist/core/constraints/checkers/governance-presence.d.ts.map +1 -1
- package/dist/core/constraints/checkers/governance-presence.js +7 -28
- package/dist/core/constraints/checkers/governance-presence.js.map +1 -1
- package/dist/core/constraints/checkers/no-test-simplification.d.ts.map +1 -1
- package/dist/core/constraints/checkers/no-test-simplification.js +4 -3
- package/dist/core/constraints/checkers/no-test-simplification.js.map +1 -1
- package/dist/core/constraints/checkers/types.d.ts +2 -2
- package/dist/core/constraints/context-builder.d.ts +15 -14
- package/dist/core/constraints/context-builder.d.ts.map +1 -1
- package/dist/core/constraints/context-builder.js +32 -96
- package/dist/core/constraints/context-builder.js.map +1 -1
- package/dist/core/constraints/doc-freshness/runner.d.ts.map +1 -1
- package/dist/core/constraints/doc-freshness/runner.js +2 -1
- package/dist/core/constraints/doc-freshness/runner.js.map +1 -1
- package/dist/core/constraints/git-evidence.d.ts +60 -0
- package/dist/core/constraints/git-evidence.d.ts.map +1 -0
- package/dist/core/constraints/git-evidence.js +88 -0
- package/dist/core/constraints/git-evidence.js.map +1 -0
- package/dist/core/constraints/injection-drift.d.ts +3 -20
- package/dist/core/constraints/injection-drift.d.ts.map +1 -1
- package/dist/core/constraints/injection-drift.js +10 -57
- package/dist/core/constraints/injection-drift.js.map +1 -1
- package/dist/core/constraints/injection-writer.d.ts +113 -0
- package/dist/core/constraints/injection-writer.d.ts.map +1 -0
- package/dist/core/constraints/injection-writer.js +228 -0
- package/dist/core/constraints/injection-writer.js.map +1 -0
- package/dist/core/constraints/usage-report.d.ts +22 -1
- package/dist/core/constraints/usage-report.d.ts.map +1 -1
- package/dist/core/constraints/usage-report.js +27 -20
- package/dist/core/constraints/usage-report.js.map +1 -1
- package/dist/core/index.d.ts +2 -2
- package/dist/core/index.d.ts.map +1 -1
- package/dist/core/index.js +4 -2
- package/dist/core/index.js.map +1 -1
- package/dist/core/project-config-loader.d.ts +32 -1
- package/dist/core/project-config-loader.d.ts.map +1 -1
- package/dist/core/project-config-loader.js +39 -9
- package/dist/core/project-config-loader.js.map +1 -1
- package/dist/core/validators/passes-gate.d.ts +15 -64
- package/dist/core/validators/passes-gate.d.ts.map +1 -1
- package/dist/core/validators/passes-gate.js +84 -222
- package/dist/core/validators/passes-gate.js.map +1 -1
- package/dist/core/validators/test-output.d.ts +58 -0
- package/dist/core/validators/test-output.d.ts.map +1 -0
- package/dist/core/validators/test-output.js +109 -0
- package/dist/core/validators/test-output.js.map +1 -0
- package/dist/failure/recorder.d.ts +7 -1
- package/dist/failure/recorder.d.ts.map +1 -1
- package/dist/failure/recorder.js +13 -9
- package/dist/failure/recorder.js.map +1 -1
- package/dist/gates/acceptance.d.ts +1 -48
- package/dist/gates/acceptance.d.ts.map +1 -1
- package/dist/gates/acceptance.js +6 -38
- package/dist/gates/acceptance.js.map +1 -1
- package/dist/gates/checker-gate.d.ts.map +1 -1
- package/dist/gates/checker-gate.js +6 -11
- package/dist/gates/checker-gate.js.map +1 -1
- package/dist/gates/command.d.ts.map +1 -1
- package/dist/gates/command.js +10 -22
- package/dist/gates/command.js.map +1 -1
- package/dist/gates/contract.d.ts.map +1 -1
- package/dist/gates/contract.js +22 -61
- package/dist/gates/contract.js.map +1 -1
- package/dist/gates/performance.d.ts.map +1 -1
- package/dist/gates/performance.js +13 -31
- package/dist/gates/performance.js.map +1 -1
- package/dist/gates/review.d.ts.map +1 -1
- package/dist/gates/review.js +21 -53
- package/dist/gates/review.js.map +1 -1
- package/dist/gates/security.d.ts.map +1 -1
- package/dist/gates/security.js +14 -32
- package/dist/gates/security.js.map +1 -1
- package/dist/gates/types.d.ts +28 -5
- package/dist/gates/types.d.ts.map +1 -1
- package/dist/gates/types.js +38 -1
- package/dist/gates/types.js.map +1 -1
- package/dist/hooks/bootstrap.d.ts +2 -0
- package/dist/hooks/bootstrap.d.ts.map +1 -1
- package/dist/hooks/bootstrap.js +5 -2
- package/dist/hooks/bootstrap.js.map +1 -1
- package/dist/index.d.ts +4 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +13 -4
- package/dist/index.js.map +1 -1
- package/dist/knowledge/audit.d.ts +7 -2
- package/dist/knowledge/audit.d.ts.map +1 -1
- package/dist/knowledge/audit.js +59 -79
- package/dist/knowledge/audit.js.map +1 -1
- package/dist/knowledge/flywheel-metrics.d.ts +39 -0
- package/dist/knowledge/flywheel-metrics.d.ts.map +1 -0
- package/dist/knowledge/flywheel-metrics.js +47 -0
- package/dist/knowledge/flywheel-metrics.js.map +1 -0
- package/dist/knowledge/import.d.ts.map +1 -1
- package/dist/knowledge/import.js +4 -14
- package/dist/knowledge/import.js.map +1 -1
- package/dist/knowledge/index-generator.d.ts.map +1 -1
- package/dist/knowledge/index-generator.js +18 -15
- package/dist/knowledge/index-generator.js.map +1 -1
- package/dist/knowledge/ingest.d.ts.map +1 -1
- package/dist/knowledge/ingest.js +9 -4
- package/dist/knowledge/ingest.js.map +1 -1
- package/dist/knowledge/migration.d.ts.map +1 -1
- package/dist/knowledge/migration.js +11 -8
- package/dist/knowledge/migration.js.map +1 -1
- package/dist/knowledge/reference-tracker.d.ts.map +1 -1
- package/dist/knowledge/reference-tracker.js +12 -9
- package/dist/knowledge/reference-tracker.js.map +1 -1
- package/dist/knowledge/store.d.ts +15 -0
- package/dist/knowledge/store.d.ts.map +1 -1
- package/dist/knowledge/store.js +59 -10
- package/dist/knowledge/store.js.map +1 -1
- package/dist/monitoring/context-tracker.d.ts.map +1 -1
- package/dist/monitoring/context-tracker.js +11 -20
- package/dist/monitoring/context-tracker.js.map +1 -1
- package/dist/monitoring/index.d.ts +1 -1
- package/dist/monitoring/index.d.ts.map +1 -1
- package/dist/monitoring/index.js +1 -2
- package/dist/monitoring/index.js.map +1 -1
- package/dist/monitoring/trace-analyzer.d.ts +15 -0
- package/dist/monitoring/trace-analyzer.d.ts.map +1 -1
- package/dist/monitoring/trace-analyzer.js +19 -2
- package/dist/monitoring/trace-analyzer.js.map +1 -1
- package/dist/monitoring/traces.d.ts +21 -2
- package/dist/monitoring/traces.d.ts.map +1 -1
- package/dist/monitoring/traces.js +38 -29
- package/dist/monitoring/traces.js.map +1 -1
- package/dist/release/index.d.ts +3 -0
- package/dist/release/index.d.ts.map +1 -0
- package/dist/release/index.js +7 -0
- package/dist/release/index.js.map +1 -0
- package/dist/release/integrity.d.ts +65 -0
- package/dist/release/integrity.d.ts.map +1 -0
- package/dist/release/integrity.js +151 -0
- package/dist/release/integrity.js.map +1 -0
- package/dist/sdd/index-generator.js +10 -11
- package/dist/sdd/index-generator.js.map +1 -1
- package/dist/test-setup/project-fixture.d.ts +48 -0
- package/dist/test-setup/project-fixture.d.ts.map +1 -0
- package/dist/test-setup/project-fixture.js +106 -0
- package/dist/test-setup/project-fixture.js.map +1 -0
- package/dist/types/constraint.d.ts +0 -18
- package/dist/types/constraint.d.ts.map +1 -1
- package/dist/types/constraint.js.map +1 -1
- package/dist/types/failure.d.ts +7 -0
- package/dist/types/failure.d.ts.map +1 -1
- package/dist/types/failure.js +8 -1
- package/dist/types/failure.js.map +1 -1
- package/dist/types/passes-gate.d.ts +10 -25
- package/dist/types/passes-gate.d.ts.map +1 -1
- package/dist/types/project-config.d.ts +1 -1
- package/dist/types/session.d.ts +6 -5
- package/dist/types/session.d.ts.map +1 -1
- package/dist/utils/exec.d.ts +10 -0
- package/dist/utils/exec.d.ts.map +1 -1
- package/dist/utils/exec.js +12 -0
- package/dist/utils/exec.js.map +1 -1
- package/dist/utils/file-walk.d.ts +9 -1
- package/dist/utils/file-walk.d.ts.map +1 -1
- package/dist/utils/file-walk.js +10 -1
- package/dist/utils/file-walk.js.map +1 -1
- package/dist/utils/frontmatter.d.ts +73 -0
- package/dist/utils/frontmatter.d.ts.map +1 -0
- package/dist/utils/frontmatter.js +141 -0
- package/dist/utils/frontmatter.js.map +1 -0
- package/dist/utils/jsonl.d.ts +92 -0
- package/dist/utils/jsonl.d.ts.map +1 -0
- package/dist/utils/jsonl.js +166 -0
- package/dist/utils/jsonl.js.map +1 -0
- package/dist/utils/package-version.d.ts +16 -0
- package/dist/utils/package-version.d.ts.map +1 -0
- package/dist/utils/package-version.js +63 -0
- package/dist/utils/package-version.js.map +1 -0
- package/package.json +1 -1
- package/src/CONTEXT.md +7 -2
- package/src/__tests__/acceptance-gate.test.ts +3 -1
- package/src/__tests__/checker-extra.test.ts +7 -1218
- package/src/__tests__/context-files-resolution.test.ts +116 -0
- package/src/__tests__/effective-constraints.test.ts +10 -13
- package/src/__tests__/exec.test.ts +35 -1
- package/src/__tests__/failure-recorder.test.ts +9 -0
- package/src/__tests__/layering.test.ts +152 -0
- package/src/__tests__/passes-gate-extra.test.ts +0 -145
- package/src/__tests__/passes-gate.test.ts +142 -106
- package/src/__tests__/public-exports.test.ts +3 -0
- package/src/__tests__/skip-semantics.test.ts +5 -9
- package/src/cli/__tests__/command-contract.test.ts +170 -0
- package/src/cli/command-contract.ts +108 -0
- package/src/cli/commands/CONTEXT.md +22 -7
- package/src/cli/commands/__tests__/acceptance.test.ts +40 -33
- package/src/cli/commands/__tests__/analyze-sessions.test.ts +35 -29
- package/src/cli/commands/__tests__/bin-exit-mapping.test.ts +101 -0
- package/src/cli/commands/__tests__/check-drift.test.ts +27 -34
- package/src/cli/commands/__tests__/check-skip-output.test.ts +21 -24
- package/src/cli/commands/__tests__/check.test.ts +378 -382
- package/src/cli/commands/__tests__/command.test.ts +85 -70
- package/src/cli/commands/__tests__/constraints-report.test.ts +100 -71
- package/src/cli/commands/__tests__/constraints-retire.test.ts +152 -95
- package/src/cli/commands/__tests__/contract.test.ts +27 -34
- package/src/cli/commands/__tests__/failure.test.ts +31 -34
- package/src/cli/commands/__tests__/init-injection.test.ts +58 -28
- package/src/cli/commands/__tests__/init.test.ts +77 -48
- package/src/cli/commands/__tests__/knowledge-flywheel-consistency.test.ts +108 -0
- package/src/cli/commands/__tests__/knowledge.test.ts +96 -38
- package/src/cli/commands/__tests__/passes-gate.test.ts +43 -45
- package/src/cli/commands/__tests__/performance.test.ts +22 -23
- package/src/cli/commands/__tests__/posteval-plan.test.ts +39 -38
- package/src/cli/commands/__tests__/project-path-anchoring.test.ts +157 -0
- package/src/cli/commands/__tests__/project-path-convention.test.ts +169 -0
- package/src/cli/commands/__tests__/registry.test.ts +69 -35
- package/src/cli/commands/__tests__/release.test.ts +224 -74
- package/src/cli/commands/__tests__/report-extra.test.ts +16 -16
- package/src/cli/commands/__tests__/report.test.ts +25 -25
- package/src/cli/commands/__tests__/review.test.ts +33 -31
- package/src/cli/commands/__tests__/security.test.ts +23 -27
- package/src/cli/commands/__tests__/spec-baseline-check.test.ts +20 -20
- package/src/cli/commands/__tests__/spec.test.ts +41 -29
- package/src/cli/commands/__tests__/status-extra.test.ts +18 -18
- package/src/cli/commands/__tests__/status-skipped-lines.test.ts +72 -0
- package/src/cli/commands/__tests__/status.test.ts +34 -34
- package/src/cli/commands/__tests__/sync-docs-agents.test.ts +108 -86
- package/src/cli/commands/__tests__/sync-docs.test.ts +72 -74
- package/src/cli/commands/__tests__/update-user-model.test.ts +62 -33
- package/src/cli/commands/__tests__/validate.test.ts +27 -29
- package/src/cli/commands/acceptance.ts +35 -24
- package/src/cli/commands/analyze-sessions.ts +25 -24
- package/src/cli/commands/check.ts +88 -65
- package/src/cli/commands/command.ts +30 -17
- package/src/cli/commands/constraints-report.ts +37 -34
- package/src/cli/commands/constraints-retire.ts +58 -55
- package/src/cli/commands/constraints.ts +10 -27
- package/src/cli/commands/contract.ts +51 -42
- package/src/cli/commands/definitions.ts +37 -223
- package/src/cli/commands/failure.ts +35 -27
- package/src/cli/commands/init.ts +166 -220
- package/src/cli/commands/knowledge.ts +250 -203
- package/src/cli/commands/passes-gate.ts +58 -68
- package/src/cli/commands/performance.ts +23 -18
- package/src/cli/commands/posteval-plan.ts +29 -15
- package/src/cli/commands/release.ts +75 -78
- package/src/cli/commands/report.ts +9 -5
- package/src/cli/commands/review.ts +39 -29
- package/src/cli/commands/sdd.ts +7 -5
- package/src/cli/commands/security.ts +35 -26
- package/src/cli/commands/spec-baseline-check.ts +16 -11
- package/src/cli/commands/spec.ts +66 -53
- package/src/cli/commands/status.ts +59 -53
- package/src/cli/commands/sync-docs/agents-syncer.ts +31 -15
- package/src/cli/commands/sync-docs/context-syncer.ts +3 -2
- package/src/cli/commands/sync-docs/index.ts +73 -100
- package/src/cli/commands/sync-docs/project-reader.ts +17 -18
- package/src/cli/commands/update-user-model.ts +33 -45
- package/src/cli/commands/validate.ts +31 -25
- package/src/cli/session-mining/__tests__/transcript.test.ts +118 -0
- package/src/cli/session-mining/transcript.ts +26 -8
- package/src/completion-checkers/CONTEXT.md +1 -1
- package/src/completion-checkers/__tests__/contract-presence.test.ts +1 -1
- package/src/completion-checkers/__tests__/phase-format.test.ts +1 -1
- package/src/completion-checkers/__tests__/tdd-chain.test.ts +2 -2
- package/src/completion-checkers/index.ts +3 -3
- package/src/completion-checkers/phase-format.ts +2 -2
- package/src/completion-checkers/tdd-chain.ts +2 -2
- package/src/completion-checkers/types.ts +5 -5
- package/src/context/session-manager.ts +19 -33
- package/src/core/CONTEXT.md +9 -6
- package/src/core/constraints/__tests__/capabilities-reconcile.test.ts +200 -0
- package/src/core/constraints/__tests__/capability-counts.test.ts +94 -0
- package/src/core/constraints/__tests__/git-evidence.test.ts +208 -0
- package/src/core/constraints/__tests__/injection-drift.test.ts +3 -10
- package/src/core/constraints/__tests__/injection-writer.test.ts +231 -0
- package/src/core/constraints/__tests__/trace-injection.test.ts +71 -0
- package/src/core/constraints/__tests__/usage-report.test.ts +112 -0
- package/src/core/constraints/agent-prompt-renderer.ts +2 -6
- package/src/core/constraints/capabilities-parser.ts +53 -35
- package/src/core/constraints/capabilities-reconcile.ts +166 -0
- package/src/core/constraints/checker.ts +75 -130
- package/src/core/constraints/checkers/__tests__/capability-sync.test.ts +214 -0
- package/src/core/constraints/checkers/__tests__/context-doc-sync.test.ts +77 -0
- package/src/core/constraints/checkers/__tests__/docs-freshness.test.ts +117 -0
- package/src/{__tests__ → core/constraints/checkers/__tests__}/governance-presence.test.ts +83 -9
- package/src/core/constraints/checkers/__tests__/iron-flags.test.ts +50 -0
- package/src/core/constraints/checkers/__tests__/no-bypass-checkpoint.test.ts +76 -0
- package/src/{__tests__ → core/constraints/checkers/__tests__}/no-hardcoded-credentials.test.ts +4 -3
- package/src/core/constraints/checkers/__tests__/no-test-simplification.test.ts +58 -0
- package/src/core/constraints/checkers/__tests__/three-state-consistency.test.ts +145 -0
- package/src/core/constraints/checkers/capability-sync.ts +29 -88
- package/src/core/constraints/checkers/context-doc-sync.ts +15 -25
- package/src/core/constraints/checkers/docs-freshness.ts +40 -69
- package/src/core/constraints/checkers/governance-presence.ts +7 -25
- package/src/core/constraints/checkers/no-test-simplification.ts +4 -3
- package/src/core/constraints/checkers/types.ts +2 -2
- package/src/core/constraints/context-builder.ts +39 -91
- package/src/core/constraints/doc-freshness/runner.ts +2 -1
- package/src/core/constraints/git-evidence.ts +120 -0
- package/src/core/constraints/injection-drift.ts +13 -60
- package/src/core/constraints/injection-writer.ts +212 -0
- package/src/core/constraints/usage-report.ts +36 -17
- package/src/core/index.ts +4 -2
- package/src/core/project-config-loader.ts +49 -9
- package/src/core/validators/__tests__/passes-gate.test.ts +2 -56
- package/src/core/validators/__tests__/test-output.test.ts +372 -0
- package/src/core/validators/passes-gate.ts +86 -248
- package/src/core/validators/test-output.ts +128 -0
- package/src/failure/__tests__/recorder.test.ts +168 -0
- package/src/failure/recorder.ts +13 -11
- package/src/gates/CONTEXT.md +6 -3
- package/src/gates/__tests__/acceptance-types-single-source.test.ts +68 -0
- package/src/gates/__tests__/acceptance.test.ts +4 -86
- package/src/gates/__tests__/effective-gates.test.ts +3 -8
- package/src/gates/__tests__/test-verdict-consistency.test.ts +140 -0
- package/src/gates/acceptance.ts +19 -84
- package/src/gates/checker-gate.ts +8 -7
- package/src/gates/command.ts +10 -16
- package/src/gates/contract.ts +22 -61
- package/src/gates/performance.ts +10 -22
- package/src/gates/review.ts +21 -41
- package/src/gates/security.ts +10 -22
- package/src/gates/types.ts +70 -5
- package/src/hooks/CONTEXT.md +1 -1
- package/src/hooks/bootstrap.ts +5 -2
- package/src/index.ts +11 -6
- package/src/knowledge/CONTEXT.md +8 -3
- package/src/knowledge/__tests__/audit-labels.test.ts +44 -0
- package/src/knowledge/__tests__/flywheel-metrics.test.ts +135 -0
- package/src/knowledge/__tests__/import.test.ts +44 -0
- package/src/knowledge/__tests__/index-generator.test.ts +55 -2
- package/src/knowledge/__tests__/ingest.test.ts +85 -0
- package/src/knowledge/__tests__/migration.test.ts +47 -0
- package/src/knowledge/__tests__/reference-tracker.test.ts +16 -0
- package/src/knowledge/__tests__/store.test.ts +194 -0
- package/src/knowledge/audit.ts +67 -71
- package/src/knowledge/flywheel-metrics.ts +71 -0
- package/src/knowledge/import.ts +4 -18
- package/src/knowledge/index-generator.ts +20 -15
- package/src/knowledge/ingest.ts +9 -4
- package/src/knowledge/migration.ts +11 -8
- package/src/knowledge/reference-tracker.ts +12 -8
- package/src/knowledge/store.ts +64 -11
- package/src/monitoring/CONTEXT.md +8 -3
- package/src/monitoring/__tests__/skipped-lines-report.test.ts +178 -0
- package/src/monitoring/__tests__/traces.test.ts +57 -0
- package/src/monitoring/context-tracker.ts +11 -23
- package/src/monitoring/index.ts +1 -1
- package/src/monitoring/trace-analyzer.ts +21 -2
- package/src/monitoring/traces.ts +39 -36
- package/src/release/CONTEXT.md +24 -0
- package/src/release/__tests__/barrel.test.ts +60 -0
- package/src/release/__tests__/integrity.test.ts +157 -0
- package/src/release/index.ts +2 -0
- package/src/release/integrity.ts +141 -0
- package/src/sdd/__tests__/index-generator.test.ts +71 -0
- package/src/sdd/index-generator.ts +9 -9
- package/src/test-setup/__tests__/project-fixture.test.ts +178 -0
- package/src/test-setup/project-fixture.ts +87 -0
- package/src/tools/CONTEXT.md +6 -0
- package/src/tools/__tests__/registry.test.ts +221 -0
- package/src/types/__tests__/types.test.ts +4 -5
- package/src/types/constraint.ts +0 -25
- package/src/types/failure.ts +8 -0
- package/src/types/passes-gate.ts +10 -30
- package/src/types/project-config.ts +1 -1
- package/src/types/session.ts +5 -5
- package/src/utils/__tests__/frontmatter.test.ts +206 -0
- package/src/utils/__tests__/jsonl-skip-disposition.test.ts +175 -0
- package/src/utils/__tests__/jsonl.test.ts +187 -0
- package/src/utils/__tests__/package-version.test.ts +97 -0
- package/src/utils/exec.ts +15 -0
- package/src/utils/file-walk.ts +11 -2
- package/src/utils/frontmatter.ts +121 -0
- package/src/utils/jsonl.ts +167 -0
- package/src/utils/package-version.ts +27 -0
- package/dist/cli/commands/doc-freshness-check.d.ts +0 -53
- package/dist/cli/commands/doc-freshness-check.d.ts.map +0 -1
- package/dist/cli/commands/doc-freshness-check.js +0 -352
- package/dist/cli/commands/doc-freshness-check.js.map +0 -1
- package/dist/types/index.d.ts +0 -14
- package/dist/types/index.d.ts.map +0 -1
- package/dist/types/index.js +0 -39
- package/dist/types/index.js.map +0 -1
- package/src/__tests__/long-running.test.ts +0 -17
- package/src/__tests__/passes-gate-extension.test.ts +0 -151
- package/src/cli/commands/__tests__/doc-freshness-check.test.ts +0 -168
- package/src/cli/commands/doc-freshness-check.ts +0 -386
- package/src/spec/CONTEXT.md +0 -11
- package/src/types/index.ts +0 -47
|
@@ -3,15 +3,18 @@
|
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
5
|
import * as fs from 'fs';
|
|
6
|
+
import { captureIO, type CapturingIO } from '../../command-contract';
|
|
6
7
|
import * as path from 'path';
|
|
7
8
|
|
|
9
|
+
let io: CapturingIO;
|
|
10
|
+
beforeEach(() => {
|
|
11
|
+
io = captureIO();
|
|
12
|
+
});
|
|
13
|
+
|
|
8
14
|
describe('postevalPlan', () => {
|
|
9
15
|
jest.setTimeout(30000); // Retry-based tests with exponential backoff need longer timeout
|
|
10
16
|
const tempDir = path.join(process.cwd(), 'temp-test-posteval');
|
|
11
17
|
const planPath = path.join(tempDir, 'plan.md');
|
|
12
|
-
let consoleErrorSpy: jest.SpyInstance;
|
|
13
|
-
let consoleLogSpy: jest.SpyInstance;
|
|
14
|
-
let mockExit: jest.SpyInstance;
|
|
15
18
|
let originalFetch: typeof fetch;
|
|
16
19
|
let originalEnv: NodeJS.ProcessEnv;
|
|
17
20
|
|
|
@@ -34,19 +37,11 @@ describe('postevalPlan', () => {
|
|
|
34
37
|
originalEnv = { ...process.env };
|
|
35
38
|
delete process.env.API_PORT;
|
|
36
39
|
delete process.env.POSTEVAL_GRACE_MS;
|
|
37
|
-
consoleErrorSpy = jest.spyOn(console, 'error').mockImplementation();
|
|
38
|
-
consoleLogSpy = jest.spyOn(console, 'log').mockImplementation();
|
|
39
|
-
mockExit = jest.spyOn(process, 'exit').mockImplementation((() => {
|
|
40
|
-
// Don't actually exit
|
|
41
|
-
}) as any);
|
|
42
40
|
});
|
|
43
41
|
|
|
44
42
|
afterEach(() => {
|
|
45
43
|
global.fetch = originalFetch;
|
|
46
44
|
process.env = originalEnv;
|
|
47
|
-
consoleErrorSpy.mockRestore();
|
|
48
|
-
consoleLogSpy.mockRestore();
|
|
49
|
-
mockExit.mockRestore();
|
|
50
45
|
jest.useRealTimers();
|
|
51
46
|
});
|
|
52
47
|
|
|
@@ -61,12 +56,12 @@ describe('postevalPlan', () => {
|
|
|
61
56
|
}) as any;
|
|
62
57
|
|
|
63
58
|
const { postevalPlan } = await import('../posteval-plan');
|
|
64
|
-
await postevalPlan({ planPath });
|
|
59
|
+
await postevalPlan({ planPath }, io);
|
|
65
60
|
|
|
66
|
-
expect(
|
|
61
|
+
expect(io.outText()).toContain('100%');
|
|
67
62
|
});
|
|
68
63
|
|
|
69
|
-
it('completeness < 1
|
|
64
|
+
it('completeness < 1:fail(覆盖率不足,含缺失项)', async () => {
|
|
70
65
|
global.fetch = jest.fn().mockResolvedValue({
|
|
71
66
|
ok: true,
|
|
72
67
|
json: () => Promise.resolve({
|
|
@@ -77,10 +72,10 @@ describe('postevalPlan', () => {
|
|
|
77
72
|
}) as any;
|
|
78
73
|
|
|
79
74
|
const { postevalPlan } = await import('../posteval-plan');
|
|
80
|
-
await postevalPlan({ planPath });
|
|
75
|
+
const result = await postevalPlan({ planPath }, io);
|
|
81
76
|
|
|
82
|
-
expect(
|
|
83
|
-
expect(
|
|
77
|
+
expect(result).toEqual({ kind: 'fail', reason: expect.stringContaining('plan coverage 50% < 100%') });
|
|
78
|
+
expect(io.errText()).toContain('50%');
|
|
84
79
|
});
|
|
85
80
|
|
|
86
81
|
it('completeness < 1 时应该列出缺失项', async () => {
|
|
@@ -94,10 +89,13 @@ describe('postevalPlan', () => {
|
|
|
94
89
|
}) as any;
|
|
95
90
|
|
|
96
91
|
const { postevalPlan } = await import('../posteval-plan');
|
|
97
|
-
await postevalPlan({ planPath });
|
|
92
|
+
const result = await postevalPlan({ planPath }, io);
|
|
98
93
|
|
|
99
|
-
expect(
|
|
100
|
-
|
|
94
|
+
expect(result).toEqual({
|
|
95
|
+
kind: 'fail',
|
|
96
|
+
reason: 'plan coverage 33% < 100%,缺 2 项: AC-002; AC-003',
|
|
97
|
+
});
|
|
98
|
+
expect(io.errText()).toContain('AC-002');
|
|
101
99
|
});
|
|
102
100
|
|
|
103
101
|
it('4xx 错误应该 exit(1)', async () => {
|
|
@@ -108,13 +106,13 @@ describe('postevalPlan', () => {
|
|
|
108
106
|
}) as any;
|
|
109
107
|
|
|
110
108
|
const { postevalPlan } = await import('../posteval-plan');
|
|
111
|
-
await postevalPlan({ planPath });
|
|
109
|
+
const result = await postevalPlan({ planPath }, io);
|
|
112
110
|
|
|
113
|
-
expect(
|
|
114
|
-
expect(
|
|
111
|
+
expect(result).toEqual({ kind: 'fail', reason: 'PostEval API error: 400 Bad Request' });
|
|
112
|
+
expect(io.errText()).toContain('400');
|
|
115
113
|
});
|
|
116
114
|
|
|
117
|
-
it('5xx
|
|
115
|
+
it('5xx 错误:skip(未判定,允许提交;退出码面 0 不变)', async () => {
|
|
118
116
|
global.fetch = jest.fn().mockResolvedValue({
|
|
119
117
|
ok: false,
|
|
120
118
|
status: 503,
|
|
@@ -122,20 +120,20 @@ describe('postevalPlan', () => {
|
|
|
122
120
|
}) as any;
|
|
123
121
|
|
|
124
122
|
const { postevalPlan } = await import('../posteval-plan');
|
|
125
|
-
await postevalPlan({ planPath });
|
|
123
|
+
const result = await postevalPlan({ planPath }, io);
|
|
126
124
|
|
|
127
|
-
expect(
|
|
128
|
-
expect(
|
|
125
|
+
expect(result).toEqual({ kind: 'skip', reason: expect.stringContaining('服务端错误 503') });
|
|
126
|
+
expect(io.errText()).toContain('unavailable');
|
|
129
127
|
});
|
|
130
128
|
|
|
131
|
-
it('
|
|
129
|
+
it('网络错误重试耗尽:skip(未判定,允许提交)', async () => {
|
|
132
130
|
global.fetch = jest.fn().mockRejectedValue(new Error('ECONNREFUSED'));
|
|
133
131
|
|
|
134
132
|
const { postevalPlan } = await import('../posteval-plan');
|
|
135
|
-
await postevalPlan({ planPath });
|
|
133
|
+
const result = await postevalPlan({ planPath }, io);
|
|
136
134
|
|
|
137
|
-
expect(
|
|
138
|
-
expect(
|
|
135
|
+
expect(result).toEqual({ kind: 'skip', reason: expect.stringContaining('Studio API 不可达') });
|
|
136
|
+
expect(io.errText()).toContain('unreachable');
|
|
139
137
|
});
|
|
140
138
|
|
|
141
139
|
it('应该支持自定义 API_PORT', async () => {
|
|
@@ -150,7 +148,7 @@ describe('postevalPlan', () => {
|
|
|
150
148
|
}) as any;
|
|
151
149
|
|
|
152
150
|
const { postevalPlan } = await import('../posteval-plan');
|
|
153
|
-
await postevalPlan({ planPath });
|
|
151
|
+
await postevalPlan({ planPath }, io);
|
|
154
152
|
|
|
155
153
|
// Verify the fetch was called with port 3999
|
|
156
154
|
const fetchCall = (global.fetch as jest.Mock).mock.calls[0][0];
|
|
@@ -174,12 +172,12 @@ describe('postevalPlan', () => {
|
|
|
174
172
|
global.fetch = mockFetch as any;
|
|
175
173
|
|
|
176
174
|
const { postevalPlan } = await import('../posteval-plan');
|
|
177
|
-
await postevalPlan({ planPath });
|
|
175
|
+
const result = await postevalPlan({ planPath }, io);
|
|
178
176
|
|
|
179
177
|
// Should have been called 3 times
|
|
180
178
|
expect(mockFetch).toHaveBeenCalledTimes(3);
|
|
181
|
-
expect(
|
|
182
|
-
expect(
|
|
179
|
+
expect(io.outText()).toContain('100%');
|
|
180
|
+
expect(result.kind).toBe('ok');
|
|
183
181
|
});
|
|
184
182
|
|
|
185
183
|
it('completeness 为 0 时应该正确显示 0%', async () => {
|
|
@@ -193,9 +191,12 @@ describe('postevalPlan', () => {
|
|
|
193
191
|
}) as any;
|
|
194
192
|
|
|
195
193
|
const { postevalPlan } = await import('../posteval-plan');
|
|
196
|
-
await postevalPlan({ planPath });
|
|
194
|
+
const result = await postevalPlan({ planPath }, io);
|
|
197
195
|
|
|
198
|
-
expect(
|
|
199
|
-
|
|
196
|
+
expect(result).toEqual({
|
|
197
|
+
kind: 'fail',
|
|
198
|
+
reason: 'plan coverage 0% < 100%,缺 1 项: AC-001',
|
|
199
|
+
});
|
|
200
|
+
expect(io.errText()).toContain('0%');
|
|
200
201
|
});
|
|
201
202
|
});
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* -p/--project-path 锚定回归(harness#95)
|
|
3
|
+
*
|
|
4
|
+
* 两条门禁命令都接受 -p,但真正执行/IO 的一侧过去取 process.cwd():不 cd 到目标工程调用,
|
|
5
|
+
* 就会拿 A 工程的命令、在 B 目录跑、把证据写进 B;acceptance 的「无 tasks.yml 即跳过」
|
|
6
|
+
* 本身是 passed:true,于是 -p 失效直接表现为假绿。
|
|
7
|
+
*
|
|
8
|
+
* 既有单测全部 mock fs/exec + 假 projectPath,恰好抹平两种取径的差异。本文件不 mock 任何 IO,
|
|
9
|
+
* 前提统一为 **cwd ≠ projectPath**(cwd = A,-p = B),断言读写位置落在 B。
|
|
10
|
+
* 站点 2 从两面各断言一次:CLI 命令面(`acceptance -p`)与门禁实例面(默认构造 = 注册表单例形状)。
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import * as fs from 'fs';
|
|
14
|
+
import * as path from 'path';
|
|
15
|
+
import { createProjectFixture } from '../../../test-setup/project-fixture';
|
|
16
|
+
import { SpecAcceptanceGate } from '../../../gates/acceptance';
|
|
17
|
+
import { captureIO } from '../../command-contract';
|
|
18
|
+
import { runPassesGate } from '../passes-gate';
|
|
19
|
+
import { acceptance } from '../acceptance';
|
|
20
|
+
|
|
21
|
+
/** 在自身 cwd 落标记文件:哪一侧被真正调用,标记就出现在哪一侧的目录里 */
|
|
22
|
+
function markScript(marker: string): string {
|
|
23
|
+
return [
|
|
24
|
+
'const fs = require("fs");',
|
|
25
|
+
`fs.writeFileSync("${marker}-exec-location.txt", process.cwd());`,
|
|
26
|
+
'console.log("1 passed");',
|
|
27
|
+
'',
|
|
28
|
+
].join('\n');
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const PACKAGE_JSON = JSON.stringify({
|
|
32
|
+
name: 'fixture',
|
|
33
|
+
version: '1.0.0',
|
|
34
|
+
scripts: { test: 'node ./mark.js' },
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
/** acceptance_criteria 形状:checked 决定判定,两侧只差 checked 值 → 读错文件必然改变结论 */
|
|
38
|
+
function tasksYml(checked: boolean): string {
|
|
39
|
+
return [
|
|
40
|
+
'tasks:',
|
|
41
|
+
' - id: TASK-001',
|
|
42
|
+
' title: 锚定用例',
|
|
43
|
+
' acceptance_criteria:',
|
|
44
|
+
' - id: crit-1',
|
|
45
|
+
' description: 需要人工确认的验收条件',
|
|
46
|
+
' type: manual',
|
|
47
|
+
' required: true',
|
|
48
|
+
` checked: ${checked}`,
|
|
49
|
+
'',
|
|
50
|
+
].join('\n');
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/** passes-gate 两侧夹具:A = 调用方所在目录(cwd),B = -p 指向的目标工程 */
|
|
54
|
+
function createPassesGateSides(): { cwdSide: string; projectSide: string } {
|
|
55
|
+
return {
|
|
56
|
+
cwdSide: createProjectFixture({
|
|
57
|
+
name: 'pganchor-cwd-side',
|
|
58
|
+
files: { 'package.json': PACKAGE_JSON, 'mark.js': markScript('cwd-side') },
|
|
59
|
+
}),
|
|
60
|
+
projectSide: createProjectFixture({
|
|
61
|
+
name: 'pganchor-project-side',
|
|
62
|
+
files: { 'package.json': PACKAGE_JSON, 'mark.js': markScript('project-side') },
|
|
63
|
+
}),
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
describe('-p 锚定:执行与 IO 必须落在 projectPath(cwd ≠ projectPath)', () => {
|
|
68
|
+
const originalCwd = process.cwd();
|
|
69
|
+
|
|
70
|
+
afterEach(() => {
|
|
71
|
+
process.chdir(originalCwd);
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
describe('passes-gate(站点 1:执行 + 证据落盘)', () => {
|
|
75
|
+
it('-p 指向 B → B 的 package.json 测试命令在 B 执行,证据落 B/.harness/evidence', async () => {
|
|
76
|
+
const { cwdSide, projectSide } = createPassesGateSides();
|
|
77
|
+
process.chdir(cwdSide);
|
|
78
|
+
|
|
79
|
+
const result = await runPassesGate({ projectPath: projectSide }, captureIO());
|
|
80
|
+
|
|
81
|
+
expect(result).toEqual({ kind: 'ok' });
|
|
82
|
+
expect(fs.readFileSync(path.join(projectSide, 'project-side-exec-location.txt'), 'utf-8'))
|
|
83
|
+
.toBe(projectSide);
|
|
84
|
+
// 跑在 B 的判据同时要求 A 侧什么都没被执行、没被写入
|
|
85
|
+
expect(fs.existsSync(path.join(cwdSide, 'cwd-side-exec-location.txt'))).toBe(false);
|
|
86
|
+
expect(fs.existsSync(path.join(cwdSide, '.harness', 'evidence'))).toBe(false);
|
|
87
|
+
|
|
88
|
+
const evidenceDir = path.join(projectSide, '.harness', 'evidence');
|
|
89
|
+
expect(fs.existsSync(evidenceDir)).toBe(true);
|
|
90
|
+
expect(fs.readdirSync(evidenceDir).filter(f => f.startsWith('test-'))).toHaveLength(1);
|
|
91
|
+
});
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
describe('acceptance(站点 2:tasks.yml 解析)', () => {
|
|
95
|
+
it('默认构造的门禁实例(registry / getEffectiveGates 形状)→ 读 B/tasks.yml,不读 cwd 的', async () => {
|
|
96
|
+
const cwdSide = createProjectFixture({
|
|
97
|
+
name: 'acccfg-cwd-side',
|
|
98
|
+
files: { 'tasks.yml': tasksYml(true) },
|
|
99
|
+
});
|
|
100
|
+
const projectSide = createProjectFixture({
|
|
101
|
+
name: 'acccfg-project-side',
|
|
102
|
+
files: { 'tasks.yml': tasksYml(false) },
|
|
103
|
+
});
|
|
104
|
+
process.chdir(cwdSide);
|
|
105
|
+
|
|
106
|
+
// 无 config 构造 = 注册表单例的形状(./tasks.yml 相对默认值在这一支按 cwd 解析)
|
|
107
|
+
const result = await new SpecAcceptanceGate().check({
|
|
108
|
+
projectPath: projectSide,
|
|
109
|
+
taskId: 'TASK-001',
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
expect(result.passed).toBe(false);
|
|
113
|
+
expect(result.message).toContain('1 unchecked acceptance criteria');
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
it('-p 指向 B → 读 B/tasks.yml,不因 cwd 无文件而假绿', async () => {
|
|
117
|
+
const cwdSide = createProjectFixture({
|
|
118
|
+
name: 'accanchor-cwd-side',
|
|
119
|
+
files: { 'tasks.yml': tasksYml(true) },
|
|
120
|
+
});
|
|
121
|
+
const projectSide = createProjectFixture({
|
|
122
|
+
name: 'accanchor-project-side',
|
|
123
|
+
files: { 'tasks.yml': tasksYml(false) },
|
|
124
|
+
});
|
|
125
|
+
process.chdir(cwdSide);
|
|
126
|
+
|
|
127
|
+
const result = await acceptance({ projectPath: projectSide, taskId: 'TASK-001' }, captureIO());
|
|
128
|
+
|
|
129
|
+
expect(result).toEqual({
|
|
130
|
+
kind: 'fail',
|
|
131
|
+
reason: expect.stringContaining('1 unchecked acceptance criteria'),
|
|
132
|
+
});
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
it('--tasks-path 相对路径按 projectPath 解析,不按 cwd', async () => {
|
|
136
|
+
const cwdSide = createProjectFixture({
|
|
137
|
+
name: 'accanchorrel-cwd-side',
|
|
138
|
+
files: { 'config/tasks.yml': tasksYml(true) },
|
|
139
|
+
});
|
|
140
|
+
const projectSide = createProjectFixture({
|
|
141
|
+
name: 'accanchorrel-project-side',
|
|
142
|
+
files: { 'config/tasks.yml': tasksYml(false) },
|
|
143
|
+
});
|
|
144
|
+
process.chdir(cwdSide);
|
|
145
|
+
|
|
146
|
+
const result = await acceptance(
|
|
147
|
+
{ projectPath: projectSide, taskId: 'TASK-001', tasksPath: 'config/tasks.yml' },
|
|
148
|
+
captureIO(),
|
|
149
|
+
);
|
|
150
|
+
|
|
151
|
+
expect(result).toEqual({
|
|
152
|
+
kind: 'fail',
|
|
153
|
+
reason: expect.stringContaining('1 unchecked acceptance criteria'),
|
|
154
|
+
});
|
|
155
|
+
});
|
|
156
|
+
});
|
|
157
|
+
});
|
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* projectPath 传递约定守护(harness#95)
|
|
3
|
+
*
|
|
4
|
+
* 约定(正本见本目录 CONTEXT.md「约定」段):**projectPath 只在 CLI 入口做一次 `|| process.cwd()`
|
|
5
|
+
* 兜底,之后必须传到每个 IO/执行点**——下游禁止再取一次 cwd,也禁止用相对路径默认值
|
|
6
|
+
* (两者都会让 `-p` 半失效:读写/执行位置悄悄回到调用方的 cwd)。
|
|
7
|
+
*
|
|
8
|
+
* 本文件是这条约定的机器可检面,三道闸:
|
|
9
|
+
* 1. CLI 入口层:cwd 只能以 `xxx || process.cwd()` 的兜底形状出现,例外逐个点名并记理由
|
|
10
|
+
* 2. 下游层(core / gates / context / monitoring / hooks):cwd 站点集合冻结,
|
|
11
|
+
* 新增即失败——要么把根传下去,要么在此记下豁免理由
|
|
12
|
+
* 3. `xxxPath: '相对路径'` 默认值同理冻结(acceptance 的 `tasksPath: './tasks.yml'` 就是这一类)
|
|
13
|
+
*
|
|
14
|
+
* 冻结集合还要求**每条豁免仍然成立**(站点消失却不删条目 → 同样失败),豁免不会烂成化石。
|
|
15
|
+
* 「根参数已传入却又取 cwd」这类静态看不出的漂移,由 project-path-anchoring.test.ts 的真实 IO 行为用例守。
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
import * as fs from 'fs';
|
|
19
|
+
import * as path from 'path';
|
|
20
|
+
|
|
21
|
+
const SRC_ROOT = path.join(__dirname, '..', '..', '..', '..', 'src');
|
|
22
|
+
|
|
23
|
+
/** 注释行不参与匹配:文档里「缺省 process.cwd()」这类描述不是站点 */
|
|
24
|
+
function codeLines(file: string): string[] {
|
|
25
|
+
return fs
|
|
26
|
+
.readFileSync(file, 'utf-8')
|
|
27
|
+
.split('\n')
|
|
28
|
+
.map(line => line.trim())
|
|
29
|
+
.filter(line => !line.startsWith('*') && !line.startsWith('//') && !line.startsWith('/*'));
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function listTsFiles(dir: string, skipDirs: string[] = []): string[] {
|
|
33
|
+
const found: string[] = [];
|
|
34
|
+
for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
|
|
35
|
+
const full = path.join(dir, entry.name);
|
|
36
|
+
if (entry.isDirectory()) {
|
|
37
|
+
if (entry.name === '__tests__' || skipDirs.includes(entry.name)) continue;
|
|
38
|
+
found.push(...listTsFiles(full, skipDirs));
|
|
39
|
+
} else if (entry.name.endsWith('.ts') && !entry.name.endsWith('.d.ts')) {
|
|
40
|
+
found.push(full);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
return found.sort();
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/** 仓库相对路径(豁免表以此为准,跨机器稳定) */
|
|
47
|
+
function repoPath(abs: string): string {
|
|
48
|
+
return path.relative(path.join(SRC_ROOT, '..'), abs).split(path.sep).join('/');
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function cwdSites(files: string[]): Map<string, string[]> {
|
|
52
|
+
const sites = new Map<string, string[]>();
|
|
53
|
+
for (const file of files) {
|
|
54
|
+
const lines = codeLines(file).filter(line => line.includes('process.cwd()'));
|
|
55
|
+
if (lines.length > 0) sites.set(repoPath(file), lines);
|
|
56
|
+
}
|
|
57
|
+
return sites;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// ========================================
|
|
61
|
+
// 闸 1:CLI 入口层的非兜底形状 cwd
|
|
62
|
+
// ========================================
|
|
63
|
+
|
|
64
|
+
/** CLI 层的合法形状:入口对 `-p/--project-path` 做一次性兜底 */
|
|
65
|
+
const ENTRY_FALLBACK_SHAPE = /\|\|\s*process\.cwd\(\)/;
|
|
66
|
+
|
|
67
|
+
/** 兜底形状之外的 CLI cwd 站点:逐个裁决过,理由与站点原文一起冻结 */
|
|
68
|
+
const CLI_CWD_EXEMPTIONS: Record<string, { lines: string[]; reason: string }> = {
|
|
69
|
+
'src/cli/commands/release.ts': {
|
|
70
|
+
lines: ['const pkgPath = process.cwd();'],
|
|
71
|
+
reason:
|
|
72
|
+
'release 定义表里没有 -p/--project-path(只收 --bump/--dry-run),pkgPath 就是该命令唯一的根,' +
|
|
73
|
+
'且已逐个传给每条 run(cmd, pkgPath) 与 verifyReleaseArtifacts(pkgPath)——不构成 -p 半失效。' +
|
|
74
|
+
'给发布流水线新增 -p 属新能力(可指向任意目录做 push/publish),不在 #95 范围。',
|
|
75
|
+
},
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
// ========================================
|
|
79
|
+
// 闸 2:下游层的 cwd 站点
|
|
80
|
+
// ========================================
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* 下游豁免的共同理由:这些是**库层公开 API 的可选根参数默认值**(调用方没给根时的缺省),
|
|
84
|
+
* 不是「根已经传进来了,取 IO 时又绕回 cwd」。core 不能 import cli,兜底只能留在各自入口,
|
|
85
|
+
* 故冻结而非删除;新增站点必须在此逐个点名并单独说明。
|
|
86
|
+
*/
|
|
87
|
+
const DOWNSTREAM_CWD_EXEMPTIONS: Record<string, string> = {
|
|
88
|
+
'src/context/session-manager.ts': '构造参数 basePath 缺省(会话存储根,CLI 已显式传 projectPath)',
|
|
89
|
+
'src/core/constraints/checker.ts': 'check 上下文缺 projectPath 时的根兜底(3 处同一形状)',
|
|
90
|
+
'src/core/constraints/checkers/types.ts': 'checker 环境的根兜底(context.projectPath 可选)',
|
|
91
|
+
'src/core/constraints/context-builder.ts': 'buildCheckEnv 的 options.projectPath 缺省兜底(2 处)',
|
|
92
|
+
'src/core/constraints/usage-report.ts': 'projectRoot 形参默认值(库层可选根)',
|
|
93
|
+
'src/core/effective-constraints.ts': '生效集三函数 projectRoot 形参默认值(库层可选根,JSDoc 已声明)',
|
|
94
|
+
'src/core/project-config-loader.ts': 'config 加载器 projectPath 构造参数缺省兜底',
|
|
95
|
+
'src/core/spec/validator.ts': 'validateAll 的可选 projectPath 兜底(其相对 schemaPath 默认值见闸 3 豁免)',
|
|
96
|
+
'src/gates/checker-gate.ts': 'GateContext.projectPath 缺省兜底(门禁适配器的根入口)',
|
|
97
|
+
'src/gates/effective-gates.ts': 'projectRoot 形参默认值(库层可选根)',
|
|
98
|
+
'src/hooks/bootstrap.ts': 'bootstrapHarness 的可选 projectPath 兜底(组合根入口,2 处)',
|
|
99
|
+
'src/monitoring/context-tracker.ts': 'basePath 构造参数缺省兜底',
|
|
100
|
+
};
|
|
101
|
+
|
|
102
|
+
// ========================================
|
|
103
|
+
// 闸 3:core/gates 的相对路径默认值
|
|
104
|
+
// ========================================
|
|
105
|
+
|
|
106
|
+
/** key 必须 camelCase 以 Path 结尾,值为相对字面量——这类默认值按 cwd 解析,即 #95 的病根形状 */
|
|
107
|
+
const RELATIVE_PATH_DEFAULT = /\b\w+Path\s*:\s*'(?!\/)([^']+)'/;
|
|
108
|
+
|
|
109
|
+
const RELATIVE_PATH_DEFAULT_EXEMPTIONS: Record<string, string> = {
|
|
110
|
+
'src/core/spec/validator.ts':
|
|
111
|
+
'schemaPath: "./specs/schemas" 确实按 cwd 解析(同型病灶)。不随 #95 修:validateFile()/loadSchema() ' +
|
|
112
|
+
'签名里没有根,补齐要把根穿透整个 spec 域(属 spec 重构,非本票两门禁)。CLI 侧 `spec -s` 已 ' +
|
|
113
|
+
'path.resolve(projectPath, schema),给了 -s 时口径正确。留待 spec 域单票收口。',
|
|
114
|
+
};
|
|
115
|
+
|
|
116
|
+
describe('projectPath 传递约定(harness#95)', () => {
|
|
117
|
+
const cliFiles = listTsFiles(path.join(SRC_ROOT, 'cli'));
|
|
118
|
+
const downstreamFiles = listTsFiles(SRC_ROOT).filter(f => !f.startsWith(path.join(SRC_ROOT, 'cli')));
|
|
119
|
+
|
|
120
|
+
describe('闸 1:CLI 入口层的 cwd 只能是一次性兜底', () => {
|
|
121
|
+
it('非兜底形状的 cwd 站点 = 冻结豁免表,且每条豁免仍然成立', () => {
|
|
122
|
+
const offenders = new Map<string, string[]>();
|
|
123
|
+
for (const [rel, lines] of cwdSites(cliFiles)) {
|
|
124
|
+
const notFallback = lines.filter(line => !ENTRY_FALLBACK_SHAPE.test(line));
|
|
125
|
+
if (notFallback.length > 0) offenders.set(rel, notFallback);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
expect([...offenders.keys()].sort()).toEqual(Object.keys(CLI_CWD_EXEMPTIONS).sort());
|
|
129
|
+
for (const [rel, exemption] of Object.entries(CLI_CWD_EXEMPTIONS)) {
|
|
130
|
+
expect({ rel, lines: offenders.get(rel) }).toEqual({ rel, lines: exemption.lines });
|
|
131
|
+
}
|
|
132
|
+
});
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
describe('闸 2:下游层不得二次取 cwd', () => {
|
|
136
|
+
it('下游 cwd 站点集合 = 冻结豁免表(新增即失败;站点消失却不删条目也失败)', () => {
|
|
137
|
+
const sites = cwdSites(downstreamFiles);
|
|
138
|
+
|
|
139
|
+
expect([...sites.keys()].sort()).toEqual(Object.keys(DOWNSTREAM_CWD_EXEMPTIONS).sort());
|
|
140
|
+
expect(sites.size).toBe(Object.keys(DOWNSTREAM_CWD_EXEMPTIONS).length);
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
it('PassesGate 执行侧不再取 cwd(站点 1 的收口形状:workDir 由调用方传入)', () => {
|
|
144
|
+
const lines = codeLines(path.join(SRC_ROOT, 'core', 'validators', 'passes-gate.ts'));
|
|
145
|
+
|
|
146
|
+
expect(lines.some(line => line.includes('process.cwd()'))).toBe(false);
|
|
147
|
+
expect(lines.find(line => line.includes('async runTests'))).toContain('workDir');
|
|
148
|
+
});
|
|
149
|
+
});
|
|
150
|
+
|
|
151
|
+
describe('闸 3:相对路径默认值不再按 cwd 解析', () => {
|
|
152
|
+
it('core/gates 里 xxxPath: 相对字面量 的站点 = 冻结豁免表', () => {
|
|
153
|
+
const hits = new Set<string>();
|
|
154
|
+
for (const file of listTsFiles(path.join(SRC_ROOT, 'core')).concat(listTsFiles(path.join(SRC_ROOT, 'gates')))) {
|
|
155
|
+
if (codeLines(file).some(line => RELATIVE_PATH_DEFAULT.test(line))) hits.add(repoPath(file));
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
expect([...hits].sort()).toEqual(Object.keys(RELATIVE_PATH_DEFAULT_EXEMPTIONS).sort());
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
it('acceptance 的 tasks.yml 默认不再有相对路径常量(站点 2 的收口形状)', () => {
|
|
162
|
+
// 多行调用 → 拼成空白归一的整段文本再断言形状
|
|
163
|
+
const body = codeLines(path.join(SRC_ROOT, 'gates', 'acceptance.ts')).join(' ').replace(/\s+/g, ' ');
|
|
164
|
+
|
|
165
|
+
expect(body).not.toMatch(/tasksPath\s*:\s*'/);
|
|
166
|
+
expect(body).toMatch(/path\.resolve\(\s*context\.projectPath\s*,\s*[^)]*'tasks\.yml'/);
|
|
167
|
+
});
|
|
168
|
+
});
|
|
169
|
+
});
|
|
@@ -36,7 +36,7 @@ const EXPECTED_TOP_LEVEL_COMMANDS = [
|
|
|
36
36
|
'check', 'validate', 'passes-gate', 'init', 'report', 'status', 'spec',
|
|
37
37
|
'sync-docs', 'knowledge', 'sdd', 'failure', 'posteval-plan',
|
|
38
38
|
'update-user-model', 'release', 'analyze-sessions', 'constraints',
|
|
39
|
-
'
|
|
39
|
+
'spec-baseline-check',
|
|
40
40
|
// 6 门禁命令(GATE_DEFINITIONS 驱动)
|
|
41
41
|
'acceptance', 'command', 'contract', 'performance', 'review', 'security',
|
|
42
42
|
];
|
|
@@ -66,6 +66,28 @@ describe('命令注册表闭环', () => {
|
|
|
66
66
|
}
|
|
67
67
|
});
|
|
68
68
|
|
|
69
|
+
it('全部命令实现声明 CommandResult 返回(候选7:判定经返回值外溢)', () => {
|
|
70
|
+
const refs = collectRefs([
|
|
71
|
+
...COMMAND_DEFINITIONS,
|
|
72
|
+
...GATE_DEFINITIONS.map(d => d.cli),
|
|
73
|
+
]);
|
|
74
|
+
for (const ref of refs) {
|
|
75
|
+
const file = fs.existsSync(path.join(repoCommandsDir, ref.module + '.ts'))
|
|
76
|
+
? path.join(repoCommandsDir, ref.module + '.ts')
|
|
77
|
+
: path.join(repoCommandsDir, ref.module, 'index.ts');
|
|
78
|
+
const source = fs.readFileSync(file, 'utf-8');
|
|
79
|
+
const declAt = source.search(new RegExp(`function\\s+${ref.export}\\s*\\(`));
|
|
80
|
+
expect(declAt).toBeGreaterThanOrEqual(0);
|
|
81
|
+
const decl = source.slice(declAt, declAt + 900);
|
|
82
|
+
expect(decl).toMatch(/\)\s*:\s*(?:Promise<)?CommandResult/);
|
|
83
|
+
// bin 的调用约定:任何路由/子命令/action 至少传 options(mapActionArgs 编组)
|
|
84
|
+
// → 首参不得是 io,否则 options 会被当成 io 传入(写流即崩,退出码 1)
|
|
85
|
+
const params = source.slice(declAt + ref.export.length, declAt + 900);
|
|
86
|
+
const firstParam = (params.slice(params.indexOf('(') + 1).match(/^\s*([A-Za-z_$][\w$]*)/) || [''])[1];
|
|
87
|
+
expect(['io', '_io']).not.toContain(firstParam);
|
|
88
|
+
}
|
|
89
|
+
});
|
|
90
|
+
|
|
69
91
|
it('顶层命令面与预期 24 命令一致(含 6 门禁)', () => {
|
|
70
92
|
const names = [
|
|
71
93
|
...COMMAND_DEFINITIONS.map(d => d.command.split(' ')[0]),
|
|
@@ -80,45 +102,42 @@ describe('命令注册表闭环', () => {
|
|
|
80
102
|
expect(new Set(names).size).toBe(names.length);
|
|
81
103
|
});
|
|
82
104
|
|
|
83
|
-
it('
|
|
84
|
-
const
|
|
85
|
-
|
|
86
|
-
const
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
entry.args(['positional'], {});
|
|
94
|
-
entry.args([], {});
|
|
95
|
-
}
|
|
96
|
-
}
|
|
97
|
-
for (const route of def.optionRoutes || []) {
|
|
98
|
-
if (route.args) {
|
|
99
|
-
route.args([], {});
|
|
100
|
-
route.args(['positional'], {});
|
|
101
|
-
}
|
|
102
|
-
}
|
|
103
|
-
if (def.afterRun) {
|
|
104
|
-
def.afterRun(true, {});
|
|
105
|
-
def.afterRun(false, { check: true });
|
|
105
|
+
it('子命令别名唯一:别名不与主名/其他别名碰撞(候选7:别名是数据,冲突即行为歧义)', () => {
|
|
106
|
+
const walk = (def: CommandDefinition) => {
|
|
107
|
+
const entries = Object.entries(def.subcommands || {});
|
|
108
|
+
const seen = new Set<string>();
|
|
109
|
+
for (const [name, entry] of entries) {
|
|
110
|
+
expect(seen.has(name)).toBe(false);
|
|
111
|
+
seen.add(name);
|
|
112
|
+
for (const alias of entry.aliases || []) {
|
|
113
|
+
expect(seen.has(alias)).toBe(false);
|
|
114
|
+
seen.add(alias);
|
|
106
115
|
}
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
116
|
+
}
|
|
117
|
+
for (const child of def.children || []) walk(child);
|
|
118
|
+
};
|
|
119
|
+
for (const def of [...COMMAND_DEFINITIONS, ...GATE_DEFINITIONS.map(d => d.cli)]) walk(def);
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
it('全部命令定义的路由构造可执行(mapActionArgs 冒烟;候选7 后定义表零副作用闭包)', () => {
|
|
123
|
+
const walk = (def: CommandDefinition) => {
|
|
124
|
+
if (def.mapActionArgs) {
|
|
125
|
+
def.mapActionArgs(['positional'], {});
|
|
126
|
+
def.mapActionArgs([], {});
|
|
127
|
+
}
|
|
128
|
+
// 候选7:定义表不得再持有退出码处理闭包(afterRun 已废除,判定进返回值)
|
|
129
|
+
expect((def as unknown as Record<string, unknown>).afterRun).toBeUndefined();
|
|
130
|
+
for (const child of def.children || []) walk(child);
|
|
131
|
+
};
|
|
132
|
+
for (const def of [
|
|
133
|
+
...COMMAND_DEFINITIONS,
|
|
134
|
+
...GATE_DEFINITIONS.map(d => d.cli),
|
|
135
|
+
]) walk(def);
|
|
118
136
|
});
|
|
119
137
|
});
|
|
120
138
|
|
|
121
139
|
const repoRoot = path.join(__dirname, '..', '..', '..', '..');
|
|
140
|
+
const repoCommandsDir = path.join(repoRoot, 'src', 'cli', 'commands');
|
|
122
141
|
const distBin = path.join(repoRoot, 'bin', 'harness.js');
|
|
123
142
|
const distCommands = path.join(repoRoot, 'dist', 'cli', 'commands');
|
|
124
143
|
const hasDist = fs.existsSync(distCommands);
|
|
@@ -200,4 +219,19 @@ smoke('bin/harness.js 端到端(dist 存在时)', () => {
|
|
|
200
219
|
expect(r.status).toBe(1);
|
|
201
220
|
expect(r.stderr).toContain('未知子命令: bogus');
|
|
202
221
|
});
|
|
222
|
+
|
|
223
|
+
it('别名子命令解析到同一实现(knowledge ls = list,候选7)', () => {
|
|
224
|
+
const r = runWithModuleProbe(['knowledge', 'ls', '--json']);
|
|
225
|
+
expect(r.status).toBe(0);
|
|
226
|
+
expect(JSON.parse(r.stdout)).toHaveProperty('total');
|
|
227
|
+
expect(r.implModules).toEqual([
|
|
228
|
+
expect.stringContaining('/dist/cli/commands/knowledge.js'),
|
|
229
|
+
]);
|
|
230
|
+
});
|
|
231
|
+
|
|
232
|
+
it('search 编组闸门随别名命令名生效且提示逐字不变(kb s 缺参 → exit 1)', () => {
|
|
233
|
+
const r = runWithModuleProbe(['kb', 's']);
|
|
234
|
+
expect(r.status).toBe(1);
|
|
235
|
+
expect(r.stderr).toContain('请提供搜索关键词');
|
|
236
|
+
});
|
|
203
237
|
});
|