@dommaker/harness 1.3.0 → 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 +8 -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 +29 -214
- 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 +31 -25
- 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 +214 -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 +69 -59
- 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 +5 -2
- 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/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 +6 -0
- package/dist/failure/recorder.d.ts.map +1 -1
- package/dist/failure/recorder.js +12 -8
- 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 +1 -2
- package/dist/index.d.ts.map +1 -1
- 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 +17 -14
- 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 +2 -2
- package/dist/release/index.d.ts.map +1 -1
- package/dist/release/index.js +1 -5
- package/dist/release/index.js.map +1 -1
- 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/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__/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__/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 +69 -43
- 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 +209 -80
- 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 +77 -78
- 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 +36 -222
- package/src/cli/commands/failure.ts +33 -26
- package/src/cli/commands/init.ts +166 -220
- package/src/cli/commands/knowledge.ts +225 -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 +70 -59
- 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 +5 -2
- 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/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 +12 -10
- 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 +0 -4
- 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 +53 -0
- 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 +19 -14
- 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 +1 -0
- package/src/release/__tests__/barrel.test.ts +60 -0
- package/src/release/index.ts +2 -13
- 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/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,6 +70,11 @@ 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', () => {
|
|
71
79
|
const fs = require('fs');
|
|
72
80
|
const os = require('os');
|
|
@@ -74,14 +82,12 @@ describe('getKnowledgeDir', () => {
|
|
|
74
82
|
const storeModule = require('../../../knowledge/store');
|
|
75
83
|
let storeCtorSpy: jest.SpyInstance;
|
|
76
84
|
let homedirSpy: jest.SpyInstance;
|
|
77
|
-
let errorSpy: jest.SpyInstance;
|
|
78
85
|
let tmpHome: string;
|
|
79
86
|
|
|
80
87
|
beforeEach(() => {
|
|
81
88
|
delete process.env.KNOWLEDGE_BASE_DIR;
|
|
82
89
|
tmpHome = fs.mkdtempSync(path.join(os.tmpdir(), 'harness-kdir-'));
|
|
83
90
|
homedirSpy = jest.spyOn(os, 'homedir').mockReturnValue(tmpHome);
|
|
84
|
-
errorSpy = jest.spyOn(console, 'error').mockImplementation();
|
|
85
91
|
storeCtorSpy = jest.spyOn(storeModule, 'FileKnowledgeStore').mockImplementation(() => ({
|
|
86
92
|
list: jest.fn().mockReturnValue([]),
|
|
87
93
|
}));
|
|
@@ -90,21 +96,20 @@ describe('getKnowledgeDir', () => {
|
|
|
90
96
|
afterEach(() => {
|
|
91
97
|
storeCtorSpy.mockRestore();
|
|
92
98
|
homedirSpy.mockRestore();
|
|
93
|
-
errorSpy.mockRestore();
|
|
94
99
|
delete process.env.KNOWLEDGE_BASE_DIR;
|
|
95
100
|
fs.rmSync(tmpHome, { recursive: true, force: true });
|
|
96
101
|
});
|
|
97
102
|
|
|
98
103
|
it('should use KNOWLEDGE_BASE_DIR env var when set', async () => {
|
|
99
104
|
process.env.KNOWLEDGE_BASE_DIR = '/custom/knowledge';
|
|
100
|
-
await knowledgeStats({ json: true });
|
|
105
|
+
await knowledgeStats({ json: true }, io);
|
|
101
106
|
expect(storeCtorSpy).toHaveBeenCalledWith(expect.objectContaining({
|
|
102
107
|
baseDir: '/custom/knowledge',
|
|
103
108
|
}));
|
|
104
109
|
});
|
|
105
110
|
|
|
106
111
|
it('should default to ~/.harness/knowledge when no env var and no legacy data', async () => {
|
|
107
|
-
await knowledgeStats({ json: true });
|
|
112
|
+
await knowledgeStats({ json: true }, io);
|
|
108
113
|
expect(storeCtorSpy).toHaveBeenCalledWith(expect.objectContaining({
|
|
109
114
|
baseDir: path.join(tmpHome, '.harness', 'knowledge'),
|
|
110
115
|
}));
|
|
@@ -114,27 +119,27 @@ describe('getKnowledgeDir', () => {
|
|
|
114
119
|
const legacyDir = path.join(tmpHome, '.studio', 'knowledge');
|
|
115
120
|
fs.mkdirSync(legacyDir, { recursive: true });
|
|
116
121
|
fs.writeFileSync(path.join(legacyDir, 'guideline-x.md'), 'data');
|
|
117
|
-
await knowledgeStats({ json: true });
|
|
122
|
+
await knowledgeStats({ json: true }, io);
|
|
118
123
|
expect(storeCtorSpy).toHaveBeenCalledWith(expect.objectContaining({
|
|
119
124
|
baseDir: legacyDir,
|
|
120
125
|
}));
|
|
121
|
-
expect(
|
|
126
|
+
expect(io.errText()).toContain('旧目录');
|
|
122
127
|
// 闩锁:同进程重复解析只提示一次
|
|
123
|
-
await knowledgeStats({ json: true });
|
|
124
|
-
const notices =
|
|
128
|
+
await knowledgeStats({ json: true }, io);
|
|
129
|
+
const notices = io.errLines().filter(line => line.includes('旧目录'));
|
|
125
130
|
expect(notices).toHaveLength(1);
|
|
126
131
|
});
|
|
127
132
|
|
|
128
133
|
it('should ignore legacy dir when it exists but is empty', async () => {
|
|
129
134
|
fs.mkdirSync(path.join(tmpHome, '.studio', 'knowledge'), { recursive: true });
|
|
130
|
-
await knowledgeStats({ json: true });
|
|
135
|
+
await knowledgeStats({ json: true }, io);
|
|
131
136
|
expect(storeCtorSpy).toHaveBeenCalledWith(expect.objectContaining({
|
|
132
137
|
baseDir: path.join(tmpHome, '.harness', 'knowledge'),
|
|
133
138
|
}));
|
|
134
139
|
});
|
|
135
140
|
|
|
136
141
|
it('should use projectPath when provided', async () => {
|
|
137
|
-
await knowledgeStats({ projectPath: '/my/project', json: true });
|
|
142
|
+
await knowledgeStats({ projectPath: '/my/project', json: true }, io);
|
|
138
143
|
expect(storeCtorSpy).toHaveBeenCalledWith(expect.objectContaining({
|
|
139
144
|
baseDir: '/my/project/.harness/knowledge',
|
|
140
145
|
}));
|
|
@@ -142,81 +147,105 @@ describe('getKnowledgeDir', () => {
|
|
|
142
147
|
});
|
|
143
148
|
|
|
144
149
|
describe('knowledgeAudit CLI', () => {
|
|
145
|
-
let consoleSpy: jest.SpyInstance;
|
|
146
150
|
|
|
147
151
|
beforeEach(() => {
|
|
148
152
|
jest.clearAllMocks();
|
|
149
|
-
consoleSpy = jest.spyOn(console, 'log').mockImplementation();
|
|
150
153
|
mockRun.mockReturnValue(MOCK_REPORT);
|
|
151
154
|
});
|
|
152
155
|
|
|
153
|
-
afterEach(() => {
|
|
154
|
-
consoleSpy.mockRestore();
|
|
155
|
-
});
|
|
156
|
-
|
|
157
156
|
it('should output JSON when --json is set', async () => {
|
|
158
|
-
await knowledgeAudit({ json: true });
|
|
159
|
-
|
|
157
|
+
await knowledgeAudit({ json: true }, io);
|
|
158
|
+
|
|
159
|
+
expect(io.outText()).toBe(JSON.stringify(MOCK_REPORT, null, 2) + '\n');
|
|
160
160
|
});
|
|
161
161
|
|
|
162
162
|
it('should call audit.run with autoFix=true when --fix is set', async () => {
|
|
163
|
-
await knowledgeAudit({ fix: true });
|
|
163
|
+
await knowledgeAudit({ fix: true }, io);
|
|
164
164
|
expect(mockRun).toHaveBeenCalledWith({ autoFix: true });
|
|
165
165
|
});
|
|
166
166
|
|
|
167
167
|
it('should call audit.run with autoFix falsy when --dry-run is set', async () => {
|
|
168
|
-
await knowledgeAudit({ dryRun: true });
|
|
168
|
+
await knowledgeAudit({ dryRun: true }, io);
|
|
169
169
|
expect(mockRun).toHaveBeenCalledWith({ autoFix: undefined });
|
|
170
170
|
});
|
|
171
171
|
|
|
172
172
|
it('should call audit.run with autoFix falsy by default', async () => {
|
|
173
|
-
await knowledgeAudit({});
|
|
173
|
+
await knowledgeAudit({}, io);
|
|
174
174
|
expect(mockRun).toHaveBeenCalledWith({ autoFix: undefined });
|
|
175
175
|
});
|
|
176
176
|
|
|
177
177
|
it('should display total entries and health score', async () => {
|
|
178
|
-
await knowledgeAudit({});
|
|
179
|
-
const output =
|
|
178
|
+
await knowledgeAudit({}, io);
|
|
179
|
+
const output = io.outText();
|
|
180
180
|
expect(output).toContain('总条目: 100');
|
|
181
181
|
expect(output).toContain('95/100');
|
|
182
182
|
});
|
|
183
183
|
|
|
184
184
|
it('should display dimension scores', async () => {
|
|
185
|
-
await knowledgeAudit({});
|
|
186
|
-
const output =
|
|
185
|
+
await knowledgeAudit({}, io);
|
|
186
|
+
const output = io.outText();
|
|
187
187
|
expect(output).toContain('D1 结构完整性');
|
|
188
188
|
expect(output).toContain('D6 飞轮验证');
|
|
189
189
|
});
|
|
190
190
|
|
|
191
191
|
it('should display autoFixed count when > 0', async () => {
|
|
192
192
|
mockRun.mockReturnValue({ ...MOCK_REPORT, autoFixed: 5, healthScore: { before: 90, after: 95 } });
|
|
193
|
-
await knowledgeAudit({ fix: true });
|
|
194
|
-
const output =
|
|
193
|
+
await knowledgeAudit({ fix: true }, io);
|
|
194
|
+
const output = io.outText();
|
|
195
195
|
expect(output).toContain('自动修复: 5');
|
|
196
196
|
expect(output).toContain('修复后: 95/100');
|
|
197
197
|
});
|
|
198
198
|
|
|
199
199
|
it('should pass custom dir to KnowledgeAudit', async () => {
|
|
200
200
|
const { KnowledgeAudit } = require('../../../knowledge/audit');
|
|
201
|
-
await knowledgeAudit({ dir: '/custom/path' });
|
|
201
|
+
await knowledgeAudit({ dir: '/custom/path' }, io);
|
|
202
202
|
expect(KnowledgeAudit).toHaveBeenCalledWith(expect.objectContaining({
|
|
203
203
|
baseDir: '/custom/path',
|
|
204
204
|
}));
|
|
205
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
|
+
});
|
|
206
238
|
});
|
|
207
239
|
|
|
208
240
|
describe('knowledgeHealth CLI', () => {
|
|
209
241
|
const storeModule = require('../../../knowledge/store');
|
|
210
|
-
let consoleSpy: jest.SpyInstance;
|
|
211
242
|
let storeSpy: jest.SpyInstance;
|
|
212
243
|
|
|
213
244
|
beforeEach(() => {
|
|
214
245
|
jest.clearAllMocks();
|
|
215
|
-
consoleSpy = jest.spyOn(console, 'log').mockImplementation();
|
|
216
246
|
});
|
|
217
247
|
|
|
218
248
|
afterEach(() => {
|
|
219
|
-
consoleSpy.mockRestore();
|
|
220
249
|
if (storeSpy) storeSpy.mockRestore();
|
|
221
250
|
});
|
|
222
251
|
|
|
@@ -229,8 +258,8 @@ describe('knowledgeHealth CLI', () => {
|
|
|
229
258
|
getBaseDir: jest.fn().mockReturnValue('/tmp/knowledge'),
|
|
230
259
|
}));
|
|
231
260
|
|
|
232
|
-
await knowledgeHealth({ json: true });
|
|
233
|
-
const output =
|
|
261
|
+
await knowledgeHealth({ json: true }, io);
|
|
262
|
+
const output = io.outText();
|
|
234
263
|
const parsed = JSON.parse(output);
|
|
235
264
|
expect(parsed).toHaveProperty('healthScore');
|
|
236
265
|
expect(parsed.summary).toHaveProperty('total', 2);
|
|
@@ -244,8 +273,8 @@ describe('knowledgeHealth CLI', () => {
|
|
|
244
273
|
getBaseDir: jest.fn().mockReturnValue('/tmp/knowledge'),
|
|
245
274
|
}));
|
|
246
275
|
|
|
247
|
-
await knowledgeHealth({ json: true });
|
|
248
|
-
const output =
|
|
276
|
+
await knowledgeHealth({ json: true }, io);
|
|
277
|
+
const output = io.outText();
|
|
249
278
|
const parsed = JSON.parse(output);
|
|
250
279
|
expect(parsed.summary.lowRefEntries).toBe(1);
|
|
251
280
|
});
|
|
@@ -259,8 +288,8 @@ describe('knowledgeHealth CLI', () => {
|
|
|
259
288
|
getBaseDir: jest.fn().mockReturnValue('/tmp/knowledge'),
|
|
260
289
|
}));
|
|
261
290
|
|
|
262
|
-
await knowledgeHealth({ json: true });
|
|
263
|
-
const output =
|
|
291
|
+
await knowledgeHealth({ json: true }, io);
|
|
292
|
+
const output = io.outText();
|
|
264
293
|
const parsed = JSON.parse(output);
|
|
265
294
|
expect(parsed.summary.staleEntries).toBe(1);
|
|
266
295
|
});
|
|
@@ -279,7 +308,6 @@ describe('knowledgeSearch CLI (harness#63)', () => {
|
|
|
279
308
|
|
|
280
309
|
let tmpProject: string;
|
|
281
310
|
let knowledgeDir: string;
|
|
282
|
-
let consoleSpy: jest.SpyInstance;
|
|
283
311
|
|
|
284
312
|
const FILLER_COUNT = 25; // 25 × ~800 tokens ≈ 20000 > maxTokens 10000
|
|
285
313
|
const TARGET_ID = 'PIT-020';
|
|
@@ -311,7 +339,6 @@ describe('knowledgeSearch CLI (harness#63)', () => {
|
|
|
311
339
|
tmpProject = fs.mkdtempSync(path.join(os.tmpdir(), 'harness-k63-'));
|
|
312
340
|
knowledgeDir = path.join(tmpProject, '.harness', 'knowledge');
|
|
313
341
|
fs.mkdirSync(knowledgeDir, { recursive: true });
|
|
314
|
-
consoleSpy = jest.spyOn(console, 'log').mockImplementation();
|
|
315
342
|
|
|
316
343
|
const store = new FileKnowledgeStore({ baseDir: knowledgeDir });
|
|
317
344
|
// 高成熟度 + 有 lastReferenced 的填充条目排在前面,吃满 token budget
|
|
@@ -333,13 +360,12 @@ describe('knowledgeSearch CLI (harness#63)', () => {
|
|
|
333
360
|
});
|
|
334
361
|
|
|
335
362
|
afterEach(() => {
|
|
336
|
-
consoleSpy.mockRestore();
|
|
337
363
|
fs.rmSync(tmpProject, { recursive: true, force: true });
|
|
338
364
|
});
|
|
339
365
|
|
|
340
366
|
async function searchJson(query: string, limit: number): Promise<any> {
|
|
341
|
-
await knowledgeSearch(query, { projectPath: tmpProject, json: true, limit });
|
|
342
|
-
const output =
|
|
367
|
+
await knowledgeSearch(query, { projectPath: tmpProject, json: true, limit }, io);
|
|
368
|
+
const output = io.outText();
|
|
343
369
|
return JSON.parse(output);
|
|
344
370
|
}
|
|
345
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({
|