@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,6 +3,7 @@
|
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
5
|
import { knowledgeAudit, knowledgeStats, knowledgeHealth, knowledgeSearch } from '../knowledge';
|
|
6
|
+
import { captureIO, type CapturingIO } from '../../command-contract';
|
|
6
7
|
|
|
7
8
|
// Mock chalk
|
|
8
9
|
jest.mock('chalk', () => ({
|
|
@@ -20,6 +21,8 @@ jest.mock('../../../knowledge/audit', () => ({
|
|
|
20
21
|
KnowledgeAudit: jest.fn().mockImplementation(() => ({
|
|
21
22
|
run: mockRun,
|
|
22
23
|
})),
|
|
24
|
+
// label 正本在 audit.ts 规则定义上,CLI 直接消费(#109)
|
|
25
|
+
AUDIT_RULE_LABELS: jest.requireActual('../../../knowledge/audit').AUDIT_RULE_LABELS,
|
|
23
26
|
}));
|
|
24
27
|
|
|
25
28
|
// Mock KnowledgeIndexGenerator (avoids real fs writes in CLI tests)
|
|
@@ -67,14 +70,24 @@ const MOCK_REPORT = {
|
|
|
67
70
|
healthScore: { before: 95, after: 95 },
|
|
68
71
|
};
|
|
69
72
|
|
|
73
|
+
let io: CapturingIO;
|
|
74
|
+
beforeEach(() => {
|
|
75
|
+
io = captureIO();
|
|
76
|
+
});
|
|
77
|
+
|
|
70
78
|
describe('getKnowledgeDir', () => {
|
|
79
|
+
const fs = require('fs');
|
|
71
80
|
const os = require('os');
|
|
72
81
|
const path = require('path');
|
|
73
82
|
const storeModule = require('../../../knowledge/store');
|
|
74
83
|
let storeCtorSpy: jest.SpyInstance;
|
|
84
|
+
let homedirSpy: jest.SpyInstance;
|
|
85
|
+
let tmpHome: string;
|
|
75
86
|
|
|
76
87
|
beforeEach(() => {
|
|
77
88
|
delete process.env.KNOWLEDGE_BASE_DIR;
|
|
89
|
+
tmpHome = fs.mkdtempSync(path.join(os.tmpdir(), 'harness-kdir-'));
|
|
90
|
+
homedirSpy = jest.spyOn(os, 'homedir').mockReturnValue(tmpHome);
|
|
78
91
|
storeCtorSpy = jest.spyOn(storeModule, 'FileKnowledgeStore').mockImplementation(() => ({
|
|
79
92
|
list: jest.fn().mockReturnValue([]),
|
|
80
93
|
}));
|
|
@@ -82,27 +95,51 @@ describe('getKnowledgeDir', () => {
|
|
|
82
95
|
|
|
83
96
|
afterEach(() => {
|
|
84
97
|
storeCtorSpy.mockRestore();
|
|
98
|
+
homedirSpy.mockRestore();
|
|
85
99
|
delete process.env.KNOWLEDGE_BASE_DIR;
|
|
100
|
+
fs.rmSync(tmpHome, { recursive: true, force: true });
|
|
86
101
|
});
|
|
87
102
|
|
|
88
103
|
it('should use KNOWLEDGE_BASE_DIR env var when set', async () => {
|
|
89
104
|
process.env.KNOWLEDGE_BASE_DIR = '/custom/knowledge';
|
|
90
|
-
await knowledgeStats({ json: true });
|
|
105
|
+
await knowledgeStats({ json: true }, io);
|
|
91
106
|
expect(storeCtorSpy).toHaveBeenCalledWith(expect.objectContaining({
|
|
92
107
|
baseDir: '/custom/knowledge',
|
|
93
108
|
}));
|
|
94
109
|
});
|
|
95
110
|
|
|
96
|
-
it('should default to ~/.
|
|
97
|
-
|
|
98
|
-
|
|
111
|
+
it('should default to ~/.harness/knowledge when no env var and no legacy data', async () => {
|
|
112
|
+
await knowledgeStats({ json: true }, io);
|
|
113
|
+
expect(storeCtorSpy).toHaveBeenCalledWith(expect.objectContaining({
|
|
114
|
+
baseDir: path.join(tmpHome, '.harness', 'knowledge'),
|
|
115
|
+
}));
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
it('should keep legacy ~/.studio/knowledge when it still has data, with deprecation notice shown once', async () => {
|
|
119
|
+
const legacyDir = path.join(tmpHome, '.studio', 'knowledge');
|
|
120
|
+
fs.mkdirSync(legacyDir, { recursive: true });
|
|
121
|
+
fs.writeFileSync(path.join(legacyDir, 'guideline-x.md'), 'data');
|
|
122
|
+
await knowledgeStats({ json: true }, io);
|
|
99
123
|
expect(storeCtorSpy).toHaveBeenCalledWith(expect.objectContaining({
|
|
100
|
-
baseDir:
|
|
124
|
+
baseDir: legacyDir,
|
|
125
|
+
}));
|
|
126
|
+
expect(io.errText()).toContain('旧目录');
|
|
127
|
+
// 闩锁:同进程重复解析只提示一次
|
|
128
|
+
await knowledgeStats({ json: true }, io);
|
|
129
|
+
const notices = io.errLines().filter(line => line.includes('旧目录'));
|
|
130
|
+
expect(notices).toHaveLength(1);
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
it('should ignore legacy dir when it exists but is empty', async () => {
|
|
134
|
+
fs.mkdirSync(path.join(tmpHome, '.studio', 'knowledge'), { recursive: true });
|
|
135
|
+
await knowledgeStats({ json: true }, io);
|
|
136
|
+
expect(storeCtorSpy).toHaveBeenCalledWith(expect.objectContaining({
|
|
137
|
+
baseDir: path.join(tmpHome, '.harness', 'knowledge'),
|
|
101
138
|
}));
|
|
102
139
|
});
|
|
103
140
|
|
|
104
141
|
it('should use projectPath when provided', async () => {
|
|
105
|
-
await knowledgeStats({ projectPath: '/my/project', json: true });
|
|
142
|
+
await knowledgeStats({ projectPath: '/my/project', json: true }, io);
|
|
106
143
|
expect(storeCtorSpy).toHaveBeenCalledWith(expect.objectContaining({
|
|
107
144
|
baseDir: '/my/project/.harness/knowledge',
|
|
108
145
|
}));
|
|
@@ -110,81 +147,105 @@ describe('getKnowledgeDir', () => {
|
|
|
110
147
|
});
|
|
111
148
|
|
|
112
149
|
describe('knowledgeAudit CLI', () => {
|
|
113
|
-
let consoleSpy: jest.SpyInstance;
|
|
114
150
|
|
|
115
151
|
beforeEach(() => {
|
|
116
152
|
jest.clearAllMocks();
|
|
117
|
-
consoleSpy = jest.spyOn(console, 'log').mockImplementation();
|
|
118
153
|
mockRun.mockReturnValue(MOCK_REPORT);
|
|
119
154
|
});
|
|
120
155
|
|
|
121
|
-
afterEach(() => {
|
|
122
|
-
consoleSpy.mockRestore();
|
|
123
|
-
});
|
|
124
|
-
|
|
125
156
|
it('should output JSON when --json is set', async () => {
|
|
126
|
-
await knowledgeAudit({ json: true });
|
|
127
|
-
|
|
157
|
+
await knowledgeAudit({ json: true }, io);
|
|
158
|
+
|
|
159
|
+
expect(io.outText()).toBe(JSON.stringify(MOCK_REPORT, null, 2) + '\n');
|
|
128
160
|
});
|
|
129
161
|
|
|
130
162
|
it('should call audit.run with autoFix=true when --fix is set', async () => {
|
|
131
|
-
await knowledgeAudit({ fix: true });
|
|
163
|
+
await knowledgeAudit({ fix: true }, io);
|
|
132
164
|
expect(mockRun).toHaveBeenCalledWith({ autoFix: true });
|
|
133
165
|
});
|
|
134
166
|
|
|
135
167
|
it('should call audit.run with autoFix falsy when --dry-run is set', async () => {
|
|
136
|
-
await knowledgeAudit({ dryRun: true });
|
|
168
|
+
await knowledgeAudit({ dryRun: true }, io);
|
|
137
169
|
expect(mockRun).toHaveBeenCalledWith({ autoFix: undefined });
|
|
138
170
|
});
|
|
139
171
|
|
|
140
172
|
it('should call audit.run with autoFix falsy by default', async () => {
|
|
141
|
-
await knowledgeAudit({});
|
|
173
|
+
await knowledgeAudit({}, io);
|
|
142
174
|
expect(mockRun).toHaveBeenCalledWith({ autoFix: undefined });
|
|
143
175
|
});
|
|
144
176
|
|
|
145
177
|
it('should display total entries and health score', async () => {
|
|
146
|
-
await knowledgeAudit({});
|
|
147
|
-
const output =
|
|
178
|
+
await knowledgeAudit({}, io);
|
|
179
|
+
const output = io.outText();
|
|
148
180
|
expect(output).toContain('总条目: 100');
|
|
149
181
|
expect(output).toContain('95/100');
|
|
150
182
|
});
|
|
151
183
|
|
|
152
184
|
it('should display dimension scores', async () => {
|
|
153
|
-
await knowledgeAudit({});
|
|
154
|
-
const output =
|
|
185
|
+
await knowledgeAudit({}, io);
|
|
186
|
+
const output = io.outText();
|
|
155
187
|
expect(output).toContain('D1 结构完整性');
|
|
156
188
|
expect(output).toContain('D6 飞轮验证');
|
|
157
189
|
});
|
|
158
190
|
|
|
159
191
|
it('should display autoFixed count when > 0', async () => {
|
|
160
192
|
mockRun.mockReturnValue({ ...MOCK_REPORT, autoFixed: 5, healthScore: { before: 90, after: 95 } });
|
|
161
|
-
await knowledgeAudit({ fix: true });
|
|
162
|
-
const output =
|
|
193
|
+
await knowledgeAudit({ fix: true }, io);
|
|
194
|
+
const output = io.outText();
|
|
163
195
|
expect(output).toContain('自动修复: 5');
|
|
164
196
|
expect(output).toContain('修复后: 95/100');
|
|
165
197
|
});
|
|
166
198
|
|
|
167
199
|
it('should pass custom dir to KnowledgeAudit', async () => {
|
|
168
200
|
const { KnowledgeAudit } = require('../../../knowledge/audit');
|
|
169
|
-
await knowledgeAudit({ dir: '/custom/path' });
|
|
201
|
+
await knowledgeAudit({ dir: '/custom/path' }, io);
|
|
170
202
|
expect(KnowledgeAudit).toHaveBeenCalledWith(expect.objectContaining({
|
|
171
203
|
baseDir: '/custom/path',
|
|
172
204
|
}));
|
|
173
205
|
});
|
|
206
|
+
|
|
207
|
+
it('规则 label 闭环:event-noise / deprecated-domain 显示中文 label,不回落英文键名(#109)', async () => {
|
|
208
|
+
mockRun.mockReturnValue({
|
|
209
|
+
...MOCK_REPORT,
|
|
210
|
+
issues: [],
|
|
211
|
+
summary: {
|
|
212
|
+
...MOCK_REPORT.summary,
|
|
213
|
+
'event-noise': 2,
|
|
214
|
+
'deprecated-domain': 1,
|
|
215
|
+
},
|
|
216
|
+
});
|
|
217
|
+
await knowledgeAudit({}, io);
|
|
218
|
+
const output = io.outText();
|
|
219
|
+
expect(output).toContain('运维事件噪音: 2');
|
|
220
|
+
expect(output).toContain('废弃领域残留: 1');
|
|
221
|
+
expect(output).not.toContain('event-noise');
|
|
222
|
+
expect(output).not.toContain('deprecated-domain');
|
|
223
|
+
});
|
|
224
|
+
|
|
225
|
+
it('维度 label 闭环:incremental 维度显示中文 label,不回落英文键名(#109)', async () => {
|
|
226
|
+
mockRun.mockReturnValue({
|
|
227
|
+
...MOCK_REPORT,
|
|
228
|
+
dimensions: {
|
|
229
|
+
...MOCK_REPORT.dimensions,
|
|
230
|
+
incremental: { score: 80, issues: 0, details: {} },
|
|
231
|
+
},
|
|
232
|
+
});
|
|
233
|
+
await knowledgeAudit({}, io);
|
|
234
|
+
const output = io.outText();
|
|
235
|
+
expect(output).toContain('D7 增量存活');
|
|
236
|
+
expect(output).not.toContain('incremental');
|
|
237
|
+
});
|
|
174
238
|
});
|
|
175
239
|
|
|
176
240
|
describe('knowledgeHealth CLI', () => {
|
|
177
241
|
const storeModule = require('../../../knowledge/store');
|
|
178
|
-
let consoleSpy: jest.SpyInstance;
|
|
179
242
|
let storeSpy: jest.SpyInstance;
|
|
180
243
|
|
|
181
244
|
beforeEach(() => {
|
|
182
245
|
jest.clearAllMocks();
|
|
183
|
-
consoleSpy = jest.spyOn(console, 'log').mockImplementation();
|
|
184
246
|
});
|
|
185
247
|
|
|
186
248
|
afterEach(() => {
|
|
187
|
-
consoleSpy.mockRestore();
|
|
188
249
|
if (storeSpy) storeSpy.mockRestore();
|
|
189
250
|
});
|
|
190
251
|
|
|
@@ -197,8 +258,8 @@ describe('knowledgeHealth CLI', () => {
|
|
|
197
258
|
getBaseDir: jest.fn().mockReturnValue('/tmp/knowledge'),
|
|
198
259
|
}));
|
|
199
260
|
|
|
200
|
-
await knowledgeHealth({ json: true });
|
|
201
|
-
const output =
|
|
261
|
+
await knowledgeHealth({ json: true }, io);
|
|
262
|
+
const output = io.outText();
|
|
202
263
|
const parsed = JSON.parse(output);
|
|
203
264
|
expect(parsed).toHaveProperty('healthScore');
|
|
204
265
|
expect(parsed.summary).toHaveProperty('total', 2);
|
|
@@ -212,8 +273,8 @@ describe('knowledgeHealth CLI', () => {
|
|
|
212
273
|
getBaseDir: jest.fn().mockReturnValue('/tmp/knowledge'),
|
|
213
274
|
}));
|
|
214
275
|
|
|
215
|
-
await knowledgeHealth({ json: true });
|
|
216
|
-
const output =
|
|
276
|
+
await knowledgeHealth({ json: true }, io);
|
|
277
|
+
const output = io.outText();
|
|
217
278
|
const parsed = JSON.parse(output);
|
|
218
279
|
expect(parsed.summary.lowRefEntries).toBe(1);
|
|
219
280
|
});
|
|
@@ -227,8 +288,8 @@ describe('knowledgeHealth CLI', () => {
|
|
|
227
288
|
getBaseDir: jest.fn().mockReturnValue('/tmp/knowledge'),
|
|
228
289
|
}));
|
|
229
290
|
|
|
230
|
-
await knowledgeHealth({ json: true });
|
|
231
|
-
const output =
|
|
291
|
+
await knowledgeHealth({ json: true }, io);
|
|
292
|
+
const output = io.outText();
|
|
232
293
|
const parsed = JSON.parse(output);
|
|
233
294
|
expect(parsed.summary.staleEntries).toBe(1);
|
|
234
295
|
});
|
|
@@ -247,7 +308,6 @@ describe('knowledgeSearch CLI (harness#63)', () => {
|
|
|
247
308
|
|
|
248
309
|
let tmpProject: string;
|
|
249
310
|
let knowledgeDir: string;
|
|
250
|
-
let consoleSpy: jest.SpyInstance;
|
|
251
311
|
|
|
252
312
|
const FILLER_COUNT = 25; // 25 × ~800 tokens ≈ 20000 > maxTokens 10000
|
|
253
313
|
const TARGET_ID = 'PIT-020';
|
|
@@ -279,7 +339,6 @@ describe('knowledgeSearch CLI (harness#63)', () => {
|
|
|
279
339
|
tmpProject = fs.mkdtempSync(path.join(os.tmpdir(), 'harness-k63-'));
|
|
280
340
|
knowledgeDir = path.join(tmpProject, '.harness', 'knowledge');
|
|
281
341
|
fs.mkdirSync(knowledgeDir, { recursive: true });
|
|
282
|
-
consoleSpy = jest.spyOn(console, 'log').mockImplementation();
|
|
283
342
|
|
|
284
343
|
const store = new FileKnowledgeStore({ baseDir: knowledgeDir });
|
|
285
344
|
// 高成熟度 + 有 lastReferenced 的填充条目排在前面,吃满 token budget
|
|
@@ -301,13 +360,12 @@ describe('knowledgeSearch CLI (harness#63)', () => {
|
|
|
301
360
|
});
|
|
302
361
|
|
|
303
362
|
afterEach(() => {
|
|
304
|
-
consoleSpy.mockRestore();
|
|
305
363
|
fs.rmSync(tmpProject, { recursive: true, force: true });
|
|
306
364
|
});
|
|
307
365
|
|
|
308
366
|
async function searchJson(query: string, limit: number): Promise<any> {
|
|
309
|
-
await knowledgeSearch(query, { projectPath: tmpProject, json: true, limit });
|
|
310
|
-
const output =
|
|
367
|
+
await knowledgeSearch(query, { projectPath: tmpProject, json: true, limit }, io);
|
|
368
|
+
const output = io.outText();
|
|
311
369
|
return JSON.parse(output);
|
|
312
370
|
}
|
|
313
371
|
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
5
|
import { runPassesGate, checkCoverage } from '../passes-gate';
|
|
6
|
+
import { captureIO, type CapturingIO } from '../../command-contract';
|
|
6
7
|
import * as fs from 'fs/promises';
|
|
7
8
|
import { PassesGate } from '../../../core/validators/passes-gate';
|
|
8
9
|
import { execAsync } from '../../../utils/exec';
|
|
@@ -18,9 +19,10 @@ jest.mock('../../../utils/exec', () => ({
|
|
|
18
19
|
execAsync: jest.fn(),
|
|
19
20
|
}));
|
|
20
21
|
|
|
21
|
-
// Mock PassesGate
|
|
22
|
+
// Mock PassesGate(detectTestCommand 用真实实现——它就是收口后的正本,fs 已被 mock 喂料)
|
|
22
23
|
jest.mock('../../../core/validators/passes-gate', () => ({
|
|
23
24
|
PassesGate: jest.fn(),
|
|
25
|
+
detectTestCommand: jest.requireActual('../../../core/validators/passes-gate').detectTestCommand,
|
|
24
26
|
}));
|
|
25
27
|
|
|
26
28
|
// Mock chalk
|
|
@@ -37,17 +39,12 @@ const MockPassesGate = PassesGate as jest.MockedClass<typeof PassesGate>;
|
|
|
37
39
|
const mockExecAsync = execAsync as jest.MockedFunction<typeof execAsync>;
|
|
38
40
|
|
|
39
41
|
describe('passes-gate command', () => {
|
|
40
|
-
let
|
|
42
|
+
let io: CapturingIO;
|
|
41
43
|
|
|
42
44
|
beforeEach(() => {
|
|
43
|
-
jest.clearAllMocks();
|
|
44
|
-
consoleSpy = jest.spyOn(console, 'log').mockImplementation();
|
|
45
|
-
process.exitCode = 0;
|
|
46
|
-
});
|
|
47
45
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
process.exitCode = 0;
|
|
46
|
+
io = captureIO();
|
|
47
|
+
jest.clearAllMocks();
|
|
51
48
|
});
|
|
52
49
|
|
|
53
50
|
describe('runPassesGate', () => {
|
|
@@ -55,8 +52,8 @@ describe('passes-gate command', () => {
|
|
|
55
52
|
mockFs.readFile.mockRejectedValue(new Error('no package.json'));
|
|
56
53
|
mockFs.access.mockRejectedValue(new Error('no file'));
|
|
57
54
|
|
|
58
|
-
await runPassesGate({});
|
|
59
|
-
expect(
|
|
55
|
+
await runPassesGate({}, io);
|
|
56
|
+
expect(io.outText()).toContain('未检测到测试命令');
|
|
60
57
|
});
|
|
61
58
|
|
|
62
59
|
it('应该通过测试门控', async () => {
|
|
@@ -76,8 +73,8 @@ describe('passes-gate command', () => {
|
|
|
76
73
|
runTests: mockRunTests,
|
|
77
74
|
}));
|
|
78
75
|
|
|
79
|
-
await runPassesGate({});
|
|
80
|
-
expect(
|
|
76
|
+
await runPassesGate({}, io);
|
|
77
|
+
expect(io.outText()).toContain('测试门控通过');
|
|
81
78
|
});
|
|
82
79
|
|
|
83
80
|
it('应该失败测试门控', async () => {
|
|
@@ -98,11 +95,10 @@ describe('passes-gate command', () => {
|
|
|
98
95
|
}));
|
|
99
96
|
|
|
100
97
|
// Mock process.exit
|
|
101
|
-
const mockExit = jest.spyOn(process, 'exit').mockImplementation(() => undefined as never);
|
|
102
98
|
|
|
103
|
-
await runPassesGate({});
|
|
104
|
-
|
|
105
|
-
|
|
99
|
+
const result = await runPassesGate({}, io);
|
|
100
|
+
|
|
101
|
+
expect(result).toEqual({ kind: 'fail', reason: 'passes-gate denied: 2/10 个测试失败' });
|
|
106
102
|
});
|
|
107
103
|
|
|
108
104
|
it('应该处理测试执行错误', async () => {
|
|
@@ -115,11 +111,9 @@ describe('passes-gate command', () => {
|
|
|
115
111
|
runTests: mockRunTests,
|
|
116
112
|
}));
|
|
117
113
|
|
|
118
|
-
const
|
|
114
|
+
const result = await runPassesGate({}, io);
|
|
119
115
|
|
|
120
|
-
|
|
121
|
-
expect(mockExit).toHaveBeenCalledWith(1);
|
|
122
|
-
mockExit.mockRestore();
|
|
116
|
+
expect(result).toEqual({ kind: 'fail', reason: expect.stringContaining('测试执行异常: test failed') });
|
|
123
117
|
});
|
|
124
118
|
|
|
125
119
|
it('应该使用自定义测试命令', async () => {
|
|
@@ -135,7 +129,7 @@ describe('passes-gate command', () => {
|
|
|
135
129
|
runTests: mockRunTests,
|
|
136
130
|
}));
|
|
137
131
|
|
|
138
|
-
await runPassesGate({ testCommand: 'pytest' });
|
|
132
|
+
await runPassesGate({ testCommand: 'pytest' }, io);
|
|
139
133
|
expect(MockPassesGate).toHaveBeenCalledWith(
|
|
140
134
|
expect.objectContaining({ testCommand: 'pytest' })
|
|
141
135
|
);
|
|
@@ -143,34 +137,37 @@ describe('passes-gate command', () => {
|
|
|
143
137
|
});
|
|
144
138
|
|
|
145
139
|
describe('checkCoverage', () => {
|
|
146
|
-
it('
|
|
140
|
+
it('覆盖率达标:ok', async () => {
|
|
147
141
|
mockExecAsync.mockResolvedValue({ stdout: '', stderr: '' });
|
|
148
142
|
mockFs.readFile.mockResolvedValue(JSON.stringify({
|
|
149
143
|
total: { lines: { pct: 85 } },
|
|
150
144
|
}));
|
|
151
145
|
|
|
152
|
-
const result = await checkCoverage('/project', 80);
|
|
153
|
-
|
|
154
|
-
expect(
|
|
146
|
+
const result = await checkCoverage('/project', 80, io);
|
|
147
|
+
|
|
148
|
+
expect(result).toEqual({ kind: 'ok' });
|
|
149
|
+
expect(io.outText()).toContain('覆盖率达标');
|
|
155
150
|
});
|
|
156
151
|
|
|
157
|
-
it('
|
|
152
|
+
it('覆盖率不足:skip(历史面:--coverage 路由不改退出码)', async () => {
|
|
158
153
|
mockExecAsync.mockResolvedValue({ stdout: '', stderr: '' });
|
|
159
154
|
mockFs.readFile.mockResolvedValue(JSON.stringify({
|
|
160
155
|
total: { lines: { pct: 60 } },
|
|
161
156
|
}));
|
|
162
157
|
|
|
163
|
-
const result = await checkCoverage('/project', 80);
|
|
164
|
-
|
|
165
|
-
expect(
|
|
158
|
+
const result = await checkCoverage('/project', 80, io);
|
|
159
|
+
|
|
160
|
+
expect(result).toEqual({ kind: 'skip', reason: '覆盖率不足: 60% < 80%' });
|
|
161
|
+
expect(io.outText()).toContain('覆盖率不足');
|
|
166
162
|
});
|
|
167
163
|
|
|
168
|
-
it('
|
|
164
|
+
it('取不到覆盖率:skip(不阻断)', async () => {
|
|
169
165
|
mockExecAsync.mockRejectedValue(new Error('command failed'));
|
|
170
166
|
|
|
171
|
-
const result = await checkCoverage('/project');
|
|
172
|
-
|
|
173
|
-
expect(
|
|
167
|
+
const result = await checkCoverage('/project', 80, io);
|
|
168
|
+
|
|
169
|
+
expect(result).toEqual({ kind: 'skip', reason: expect.stringContaining('无法获取覆盖率信息') });
|
|
170
|
+
expect(io.outText()).toContain('无法获取覆盖率信息');
|
|
174
171
|
});
|
|
175
172
|
|
|
176
173
|
it('应该使用默认阈值 80', async () => {
|
|
@@ -180,7 +177,8 @@ describe('passes-gate command', () => {
|
|
|
180
177
|
}));
|
|
181
178
|
|
|
182
179
|
const result = await checkCoverage('/project');
|
|
183
|
-
|
|
180
|
+
|
|
181
|
+
expect(result).toEqual({ kind: 'ok' });
|
|
184
182
|
});
|
|
185
183
|
|
|
186
184
|
it('应该处理 coverage.total 为 undefined', async () => {
|
|
@@ -188,7 +186,8 @@ describe('passes-gate command', () => {
|
|
|
188
186
|
mockFs.readFile.mockResolvedValue(JSON.stringify({}));
|
|
189
187
|
|
|
190
188
|
const result = await checkCoverage('/project', 80);
|
|
191
|
-
|
|
189
|
+
|
|
190
|
+
expect(result).toEqual({ kind: 'skip', reason: '覆盖率不足: 0% < 80%' });
|
|
192
191
|
});
|
|
193
192
|
});
|
|
194
193
|
|
|
@@ -196,17 +195,15 @@ describe('passes-gate command', () => {
|
|
|
196
195
|
it('应该检测 pytest 项目', async () => {
|
|
197
196
|
// package.json 不存在
|
|
198
197
|
mockFs.readFile.mockRejectedValue(new Error('no file'));
|
|
199
|
-
//
|
|
200
|
-
mockFs.access
|
|
201
|
-
.mockResolvedValueOnce(undefined) // pytest.ini
|
|
202
|
-
.mockRejectedValueOnce(new Error('no file')); // go.mod
|
|
198
|
+
// pyproject.toml 存在(Python 标记任一命中即 pytest)
|
|
199
|
+
mockFs.access.mockResolvedValueOnce(undefined); // pyproject.toml
|
|
203
200
|
|
|
204
201
|
const mockRunTests = jest.fn().mockResolvedValue({
|
|
205
202
|
passed: true, passedTests: 5, failedTests: 0, totalTests: 5, duration: 500, failures: [],
|
|
206
203
|
});
|
|
207
204
|
(MockPassesGate as any).mockImplementation(() => ({ runTests: mockRunTests }));
|
|
208
205
|
|
|
209
|
-
await runPassesGate({});
|
|
206
|
+
await runPassesGate({}, io);
|
|
210
207
|
expect(MockPassesGate).toHaveBeenCalledWith(expect.objectContaining({ testCommand: 'pytest' }));
|
|
211
208
|
});
|
|
212
209
|
|
|
@@ -215,6 +212,7 @@ describe('passes-gate command', () => {
|
|
|
215
212
|
// Reset access mock to avoid leaking from previous tests
|
|
216
213
|
mockFs.access.mockReset();
|
|
217
214
|
mockFs.access
|
|
215
|
+
.mockRejectedValueOnce(new Error('no file')) // pyproject.toml
|
|
218
216
|
.mockRejectedValueOnce(new Error('no file')) // pytest.ini
|
|
219
217
|
.mockResolvedValueOnce(undefined); // go.mod
|
|
220
218
|
|
|
@@ -223,7 +221,7 @@ describe('passes-gate command', () => {
|
|
|
223
221
|
});
|
|
224
222
|
(MockPassesGate as any).mockImplementation(() => ({ runTests: mockRunTests }));
|
|
225
223
|
|
|
226
|
-
await runPassesGate({});
|
|
224
|
+
await runPassesGate({}, io);
|
|
227
225
|
expect(MockPassesGate).toHaveBeenCalledWith(expect.objectContaining({ testCommand: 'go test ./...' }));
|
|
228
226
|
});
|
|
229
227
|
|
|
@@ -237,7 +235,7 @@ describe('passes-gate command', () => {
|
|
|
237
235
|
});
|
|
238
236
|
(MockPassesGate as any).mockImplementation(() => ({ runTests: mockRunTests }));
|
|
239
237
|
|
|
240
|
-
await runPassesGate({});
|
|
238
|
+
await runPassesGate({}, io);
|
|
241
239
|
expect(MockPassesGate).toHaveBeenCalledWith(expect.objectContaining({ testCommand: 'npm run test:ci' }));
|
|
242
240
|
});
|
|
243
241
|
|
|
@@ -249,8 +247,8 @@ describe('passes-gate command', () => {
|
|
|
249
247
|
mockFs.access.mockReset();
|
|
250
248
|
mockFs.access.mockRejectedValue(new Error('no file'));
|
|
251
249
|
|
|
252
|
-
await runPassesGate({});
|
|
253
|
-
expect(
|
|
250
|
+
await runPassesGate({}, io);
|
|
251
|
+
expect(io.outText()).toContain('未检测到测试命令');
|
|
254
252
|
});
|
|
255
253
|
});
|
|
256
254
|
});
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
5
|
import { performance } from '../performance';
|
|
6
|
+
import { captureIO, type CapturingIO } from '../../command-contract';
|
|
6
7
|
import { PerformanceGate } from '../../../gates/performance';
|
|
7
8
|
|
|
8
9
|
jest.mock('../../../gates/performance', () => ({
|
|
@@ -21,20 +22,12 @@ jest.mock('chalk', () => ({
|
|
|
21
22
|
const MockGate = PerformanceGate as jest.MockedClass<typeof PerformanceGate>;
|
|
22
23
|
|
|
23
24
|
describe('performance command', () => {
|
|
24
|
-
let
|
|
25
|
-
let exitSpy: jest.SpyInstance;
|
|
25
|
+
let io: CapturingIO;
|
|
26
26
|
|
|
27
27
|
beforeEach(() => {
|
|
28
|
-
jest.clearAllMocks();
|
|
29
|
-
consoleSpy = jest.spyOn(console, 'log').mockImplementation();
|
|
30
|
-
exitSpy = jest.spyOn(process, 'exit').mockImplementation(() => undefined as never);
|
|
31
|
-
process.exitCode = 0;
|
|
32
|
-
});
|
|
33
28
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
exitSpy.mockRestore();
|
|
37
|
-
process.exitCode = 0;
|
|
29
|
+
io = captureIO();
|
|
30
|
+
jest.clearAllMocks();
|
|
38
31
|
});
|
|
39
32
|
|
|
40
33
|
it('should print success when check passes', async () => {
|
|
@@ -47,10 +40,10 @@ describe('performance command', () => {
|
|
|
47
40
|
});
|
|
48
41
|
MockGate.mockImplementation(() => ({ check: mockCheck }) as any);
|
|
49
42
|
|
|
50
|
-
await performance({});
|
|
43
|
+
const result = await performance({}, io);
|
|
51
44
|
|
|
52
|
-
expect(
|
|
53
|
-
expect(
|
|
45
|
+
expect(io.outText()).toContain('性能门控检查通过');
|
|
46
|
+
expect(result.kind).toBe('ok');
|
|
54
47
|
});
|
|
55
48
|
|
|
56
49
|
it('should print failure and exit 1 when check fails', async () => {
|
|
@@ -61,27 +54,33 @@ describe('performance command', () => {
|
|
|
61
54
|
});
|
|
62
55
|
MockGate.mockImplementation(() => ({ check: mockCheck }) as any);
|
|
63
56
|
|
|
64
|
-
await performance({});
|
|
57
|
+
const result = await performance({}, io);
|
|
65
58
|
|
|
66
|
-
expect(
|
|
67
|
-
expect(
|
|
59
|
+
expect(io.outText()).toContain('性能门控检查失败');
|
|
60
|
+
expect(result).toEqual({
|
|
61
|
+
kind: 'fail',
|
|
62
|
+
reason: 'performance gate denied: threshold exceeded',
|
|
63
|
+
});
|
|
68
64
|
});
|
|
69
65
|
|
|
70
66
|
it('should handle errors and exit 1', async () => {
|
|
71
67
|
const mockCheck = jest.fn().mockRejectedValue(new Error('gate error'));
|
|
72
68
|
MockGate.mockImplementation(() => ({ check: mockCheck }) as any);
|
|
73
69
|
|
|
74
|
-
await performance({});
|
|
70
|
+
const result = await performance({}, io);
|
|
75
71
|
|
|
76
|
-
expect(
|
|
77
|
-
expect(
|
|
72
|
+
expect(io.outText()).toContain('性能门控检查出错');
|
|
73
|
+
expect(result).toEqual({
|
|
74
|
+
kind: 'fail',
|
|
75
|
+
reason: 'performance gate error: gate error',
|
|
76
|
+
});
|
|
78
77
|
});
|
|
79
78
|
|
|
80
79
|
it('should convert bundleThreshold from KB to bytes', async () => {
|
|
81
80
|
const mockCheck = jest.fn().mockResolvedValue({ passed: true, message: 'ok' });
|
|
82
81
|
MockGate.mockImplementation(() => ({ check: mockCheck }) as any);
|
|
83
82
|
|
|
84
|
-
await performance({ bundleThreshold: 500 });
|
|
83
|
+
await performance({ bundleThreshold: 500 }, io);
|
|
85
84
|
|
|
86
85
|
expect(MockGate).toHaveBeenCalledWith(
|
|
87
86
|
expect.objectContaining({
|
|
@@ -94,7 +93,7 @@ describe('performance command', () => {
|
|
|
94
93
|
const mockCheck = jest.fn().mockResolvedValue({ passed: true, message: 'ok' });
|
|
95
94
|
MockGate.mockImplementation(() => ({ check: mockCheck }) as any);
|
|
96
95
|
|
|
97
|
-
await performance({ benchmarkTimeout: 30 });
|
|
96
|
+
await performance({ benchmarkTimeout: 30 }, io);
|
|
98
97
|
|
|
99
98
|
expect(MockGate).toHaveBeenCalledWith(
|
|
100
99
|
expect.objectContaining({ benchmarkTimeout: 30000 }),
|
|
@@ -105,7 +104,7 @@ describe('performance command', () => {
|
|
|
105
104
|
const mockCheck = jest.fn().mockResolvedValue({ passed: true, message: 'ok' });
|
|
106
105
|
MockGate.mockImplementation(() => ({ check: mockCheck }) as any);
|
|
107
106
|
|
|
108
|
-
await performance({ coverage: true, coverageThreshold: 90 });
|
|
107
|
+
await performance({ coverage: true, coverageThreshold: 90 }, io);
|
|
109
108
|
|
|
110
109
|
expect(MockGate).toHaveBeenCalledWith(
|
|
111
110
|
expect.objectContaining({
|