@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
|
@@ -1,18 +1,19 @@
|
|
|
1
1
|
# cli/commands/
|
|
2
2
|
|
|
3
3
|
## 职责
|
|
4
|
-
CLI 子命令实现:
|
|
4
|
+
CLI 子命令实现:23 个顶层命令 + constraints 治理子命令 report/retire,覆盖约束检查/门禁验证/文档同步/知识管理/失败诊断/发布/会话分析等。
|
|
5
5
|
|
|
6
6
|
H5(#44)起:
|
|
7
7
|
- **命令定义即注册**:`definitions.ts` 的 `COMMAND_DEFINITIONS` 是命令形状(名称/别名/位置参数/选项/子命令/实现引用)的单一来源;bin/harness.js 遍历本表注册表驱动生成,不再手写 commander 命令块,不再有 index.ts barrel 两处同步(R6)
|
|
8
8
|
- **per-command 懒加载**(O2):定义表存实现引用 `{ module, export }`,bin 只在 action 执行时 require 对应命令模块,任一命令执行不再加载全部命令实现
|
|
9
9
|
|
|
10
10
|
## 核心导出
|
|
11
|
-
- `COMMAND_DEFINITIONS`(definitions.ts)—
|
|
12
|
-
-
|
|
11
|
+
- `COMMAND_DEFINITIONS`(definitions.ts)— 全部非门禁命令定义(纯数据模块、零闭包,禁止 import 命令实现;ADR-0010)
|
|
12
|
+
- 命令契约(`src/cli/command-contract.ts`,上层目录)— `CommandResult` / `CommandKind` / `CommandIO` / `processIO` / `captureIO` / `lastJsonOutput` / `log` / `logError`
|
|
13
|
+
- 各命令文件:check / validate / passes-gate / init / report / status / spec / sync-docs / knowledge / sdd / failure / posteval-plan / release / analyze-sessions / update-user-model / constraints / spec-baseline-check
|
|
13
14
|
- 6 门禁命令实现在 `acceptance` / `command` / `contract` / `performance` / `review` / `security`(其 CLI 元数据在 `src/gates/definitions.ts`,形状同为 `CommandDefinition`,ADR-0007)
|
|
14
15
|
|
|
15
|
-
`constraints` 下挂治理子命令:`constraints report`(使用统计 + 退役候选诊断 + 配置健康 + 注入漂移,`--export` 脱敏)、`constraints retire`(交互选择 + 人确认退役;带 id 直达需显式 `--yes`(#24 人确认闸门),无 `--yes` 报错 + 非零退出码且不落盘;内置落 config.yml retired 元数据,custom 落 custom-constraints.yml 条目 retired
|
|
16
|
+
`constraints` 下挂治理子命令:`constraints report`(使用统计 + 退役候选诊断 + 配置健康 + 注入漂移,`--export` 脱敏)、`constraints retire`(交互选择 + 人确认退役;带 id 直达需显式 `--yes`(#24 人确认闸门),无 `--yes` 报错 + 非零退出码且不落盘;内置落 config.yml retired 元数据,custom 落 custom-constraints.yml 条目 retired 段(studio#82 D6 一处真相)+ KnowledgeStore 沉淀 + 治理注入段同步)。
|
|
16
17
|
|
|
17
18
|
## 依赖关系
|
|
18
19
|
- 依赖 `src/core/constraints/` 约束引擎
|
|
@@ -25,13 +26,27 @@ H5(#44)起:
|
|
|
25
26
|
## 约定
|
|
26
27
|
- 每个命令一个实现文件,命名与命令名一致(sync-docs 为模块族目录)
|
|
27
28
|
- **新增命令 = 命令实现文件 + definitions.ts 一条定义(含 CLI 元数据与实现引用)+ 测试**,不再改 bin/harness.js
|
|
28
|
-
- **definitions.ts
|
|
29
|
+
- **definitions.ts 是纯数据模块(ADR-0010 后零闭包)**:禁止 import 任何命令实现/运行时依赖(保 --help/--version 懒加载);子命令别名是数据(条目 `aliases: string[]`,不复印整块),实参编组(缺参闸门/强转/兜底)归各命令模块的具名导出(如 `knowledgeSearchCommand`/`coverageCheck`);实现引用可解析性由 `__tests__/registry.test.ts` 构建/测试期断言
|
|
29
30
|
- 命令选项类型命名规范:XxxOptions
|
|
31
|
+
- **本层是上行数据的注入方(harness#88)**:core 对 cli/gates/monitoring 零值导入,故 `check`/`report` 自己 `new ConstraintChecker(getTraceCollector())`(组合根接线 trace 记录器;用 `constraintChecker`/`getInstance()` 拿到的默认实例不写 trace),`sync-docs` 把 `COMMAND_DEFINITIONS` + `GATE_DEFINITIONS` 组装成 `CapabilityDefinitionSource` 注入 `capabilities-parser` 供能力清单计数
|
|
32
|
+
- **projectPath 只在入口兜底一次,并传到每个 IO/执行点(harness#95)**:`-p/--project-path` 的 cwd 兜底唯一落点是命令模块入口(`const projectPath = options.projectPath || process.cwd()`);往下每层只能收参数,**禁止再取一次 `process.cwd()`、也禁止用相对路径默认值**——两者都让 `-p` 半失效:执行/读写位置悄悄回到调用方 cwd,而 acceptance 的「无 tasks.yml 即跳过」本身就是 `passed:true`,于是失效表现为假绿。落地形状:执行/IO 根用**必传形参**表达(`PassesGate.runTests(workDir)`),门禁内的相对子路径锚到 projectPath:`ContractGate` 一直是 `path.join(context.projectPath, contractPath)`,`SpecAcceptanceGate` 的 tasksPath 随 #95 对齐成 `path.resolve(projectPath, 相对值)`(绝对值原样,缺省 `<projectPath>/tasks.yml`)
|
|
33
|
+
- 机器可检:`__tests__/project-path-convention.test.ts` 三道闸——CLI 层 cwd 必须是 `xxx || process.cwd()` 兜底形状、下游层(core/gates/context/monitoring/hooks)cwd 站点集合冻结、`xxxPath: '相对值'` 默认值冻结;豁免逐条带理由,站点消失却不删条目同样失败
|
|
34
|
+
- 行为可检:`__tests__/project-path-anchoring.test.ts` 前提统一 **cwd ≠ projectPath**、不 mock 任何 IO,断言测试真在 B 执行、证据落 `B/.harness/evidence/`、acceptance 读 `B/tasks.yml`。「根已传入却又取 cwd」这类静态看不出的漂移由它守,冻结表守的是不再新增站点
|
|
35
|
+
- **读到部分结果必须告知(harness#100)**:命令面以 `skip` 策略读 JSONL(traces.log / failures.log)时,坏行计数要到用户可见输出,**不允许默默丢弃**。共同约束:无损坏零噪声(计数为 0 不出行)、不改任何既有计数口径(`status` 的 `记录数` 仍是原始非空行数,逐字节不变)、计数一律取自 `src/utils/jsonl` 读链返回的 `skippedLines`(经报告入口透传或在读点就地取用),**禁止**用「原始行数 − 统计条数」反推(窗过滤掉的合法行与坏行分不开)。三处落点各自对得上一行代码:
|
|
36
|
+
- `status`:告知**走 stderr**(`logError(io, …)`),因为 stdout 是报告体、其字节已被 `记录数` 那一行定死,挤进去等于改输出
|
|
37
|
+
- `constraints report`:提示属于**报告体内的降级位**,与既有的 `traceFileExists` 说明同在 stdout;结构化面另两份——`--json` 的 `skippedLines` 字段、`--export` markdown 的警示行(只报条数,不报路径与坏行内容)
|
|
38
|
+
- `constraints retire`:两条路径都**走 stderr**——交互模式在候选列表前一行(沿用本函数既有的 console 例外,`console.error`),`--yes` 直达分支在落盘前 `logError(io, …)`(`retireConstraint` 本身按契约「纯执行无交互」不打印)
|
|
39
|
+
- 原则正本与逐点去向见 `src/monitoring/CONTEXT.md`「约定」段;机器可检面是 `src/utils/__tests__/jsonl-skip-disposition.test.ts`(冻结全仓 skip 读点集合 + 要求每个读点记名声明 `计数去向:`)
|
|
30
40
|
|
|
31
41
|
## 注意事项
|
|
32
42
|
- 新增命令需同步更新 CLAUDE.md / CAPABILITIES.md / src/CONTEXT.md
|
|
33
43
|
- init 治理约束段写入落点(studio #302,ADR 2026-08-21 落点模型):新仓 → AGENTS.md `PRESERVE:governance` 段(sync-docs 重新生成时保留);旧模型仓(CLAUDE.md 已有 HARNESS_CONSTRAINTS 标记或 `## Governance Rules` 块)→ 续写 CLAUDE.md,幂等重跑不制造双份正本。治理段「在场守护」由 core 侧 governance_presence checker 承担(段缺失/为空时 check 报警)
|
|
34
44
|
- PRESERVE:governance 段内写入纪律:机器管理的只有 HARNESS_CONSTRAINTS 标记区间——有标记只换标记区间,无标记(纯手写段)在段尾追加注入段;段内其余手写内容(治理契约引言/流程/纪律等)必须原样保留(曾整段替换清空手写内容的回归,init-injection.test 有防回归用例)
|
|
35
|
-
-
|
|
45
|
+
- 注入段落点路由与 marker-range 替换收口在 `core/constraints/injection-writer`(ADR-0011):读侧 `resolveInjectionTarget`(漂移检测/retire 同步共用「CLAUDE.md 有标记优先,否则 AGENTS.md」)+ 写侧 `resolveGovernanceLanding`(init 落点选择);init 三个治理段 writer 与 retire 注入同步均为「渲染 body + 调 writer」,半标记(单边/乱序)一律拒写告警;未注入 = 两处均无完整标记段
|
|
36
46
|
- knowledge 命令包含 13 个子操作(list/search/import/decay/stats/upsert/sync-status/sync-rag/audit/snapshot/migrate/index/health)
|
|
37
|
-
-
|
|
47
|
+
- `stats` / `health` 的飞轮数字不在 CLI 内计算:分子口径唯一实现是 `knowledge/flywheel-metrics.ts`(`evaluateFlywheel`,ADR-0013),本层只做百分比取整/一位小数与字段名映射;`.consumption-stats.json` 的读取留在各命令(module 零 IO)
|
|
48
|
+
- 特殊路由(选项条件、子命令兜底、裸跑语义)表达在定义表的 optionRoutes / subcommands / subcommandStrict / bareRunsAction 字段,bin 是纯通用引擎、不含单命令知识
|
|
49
|
+
- **命令 interface = `CommandResult` + 注入 io(架构评审候选7)**:命令实现一律声明 `Promise<CommandResult>`(判别联合 `ok|skip|fail|usage-error`,`fail`/`usage-error` 必附可定位的 `reason`,多闸门命令 reason 含 `gate <id>`),末位可选形参 `io: CommandIO = processIO`;类型与写入面在 `src/cli/command-contract.ts`
|
|
50
|
+
- **本目录零 `process.exit` / `process.exitCode`**:kind → 退出码的唯一映射在 `bin/harness.js`(`ok`/`skip` → 0,`fail`/`usage-error` → 1,未知 kind fail-closed)。历史上两处条件式(`command --level` 按严重级、门禁按 passed)已由命令侧译成 kind;`sync-docs --check` 的漂移改由实现返回 `fail`(定义表的 `afterRun` 逃生门随之废除)
|
|
51
|
+
- **输出不得直接用 console**:流式打印走 `log(io, …)` / `logError(io, …)`(`util.format` + 换行,与 console 逐字节等价,见 `src/cli/__tests__/command-contract.test.ts`);测试断言输出用 `captureIO()`,不再 `spyOn(process, 'exit')`;`--json` 输出断言用 `lastJsonOutput(io)`(JSON.parse 正本,harness#108)。例外:`constraints retire` 交互正文仍走 console(其 `RetireIO` 只注入 readline 流),退役结果打印已接注入流
|
|
52
|
+
- **#95 同型扫荡结论**:两门禁站点已修(`passes-gate` 执行/证据落 projectPath、`acceptance` 去掉 `tasksPath: './tasks.yml'` 相对默认值)。逐点判定后的豁免三处,理由与站点原文一起冻结在 `__tests__/project-path-convention.test.ts` 的豁免表:`release` 的 `pkgPath`(定义表本无 `-p`,pkgPath 就是该命令唯一的根且已逐个传给每条 `run(cmd, pkgPath)`,不构成半失效;给发布流水线新增 `-p` 属新能力)、`constraints` 的 `join(process.cwd(), 'package.json')`(报的是 harness 自身包版本,主锚 `__dirname`,cwd 仅兜底)、`core/spec/validator.ts` 的 `schemaPath: './specs/schemas'`(**确是同型病灶**:`validateAll(projectPath)` 用 projectPath 找 spec 文件、却用 cwd 找 schema;但 `validateFile`/`loadSchema` 签名里没有根,补齐要穿透整个 spec 域,留待 spec 单票收口)
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
5
|
import { acceptance, listAcceptanceCriteria } from '../acceptance';
|
|
6
|
+
import { captureIO, type CapturingIO } from '../../command-contract';
|
|
6
7
|
import * as fs from 'fs/promises';
|
|
7
8
|
import { SpecAcceptanceGate } from '../../../gates/acceptance';
|
|
8
9
|
|
|
@@ -35,20 +36,12 @@ const MockGate = SpecAcceptanceGate as jest.MockedClass<typeof SpecAcceptanceGat
|
|
|
35
36
|
const yaml = require('js-yaml');
|
|
36
37
|
|
|
37
38
|
describe('acceptance command', () => {
|
|
38
|
-
let
|
|
39
|
-
let exitSpy: jest.SpyInstance;
|
|
39
|
+
let io: CapturingIO;
|
|
40
40
|
|
|
41
41
|
beforeEach(() => {
|
|
42
|
-
jest.clearAllMocks();
|
|
43
|
-
consoleSpy = jest.spyOn(console, 'log').mockImplementation();
|
|
44
|
-
exitSpy = jest.spyOn(process, 'exit').mockImplementation(() => undefined as never);
|
|
45
|
-
process.exitCode = 0;
|
|
46
|
-
});
|
|
47
42
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
exitSpy.mockRestore();
|
|
51
|
-
process.exitCode = 0;
|
|
43
|
+
io = captureIO();
|
|
44
|
+
jest.clearAllMocks();
|
|
52
45
|
});
|
|
53
46
|
|
|
54
47
|
describe('acceptance', () => {
|
|
@@ -60,10 +53,10 @@ describe('acceptance command', () => {
|
|
|
60
53
|
});
|
|
61
54
|
MockGate.mockImplementation(() => ({ check: mockCheck }) as any);
|
|
62
55
|
|
|
63
|
-
await acceptance({});
|
|
56
|
+
const result = await acceptance({}, io);
|
|
64
57
|
|
|
65
|
-
expect(
|
|
66
|
-
expect(
|
|
58
|
+
expect(io.outText()).toContain('验收标准检查通过');
|
|
59
|
+
expect(result.kind).toBe('ok');
|
|
67
60
|
});
|
|
68
61
|
|
|
69
62
|
it('should print failure and exit 1 when check fails', async () => {
|
|
@@ -74,27 +67,33 @@ describe('acceptance command', () => {
|
|
|
74
67
|
});
|
|
75
68
|
MockGate.mockImplementation(() => ({ check: mockCheck }) as any);
|
|
76
69
|
|
|
77
|
-
await acceptance({});
|
|
70
|
+
const result = await acceptance({}, io);
|
|
78
71
|
|
|
79
|
-
expect(
|
|
80
|
-
expect(
|
|
72
|
+
expect(io.outText()).toContain('验收标准检查失败');
|
|
73
|
+
expect(result).toEqual({
|
|
74
|
+
kind: 'fail',
|
|
75
|
+
reason: 'acceptance gate denied: criteria not met',
|
|
76
|
+
});
|
|
81
77
|
});
|
|
82
78
|
|
|
83
79
|
it('should handle errors and exit 1', async () => {
|
|
84
80
|
const mockCheck = jest.fn().mockRejectedValue(new Error('gate error'));
|
|
85
81
|
MockGate.mockImplementation(() => ({ check: mockCheck }) as any);
|
|
86
82
|
|
|
87
|
-
await acceptance({});
|
|
83
|
+
const result = await acceptance({}, io);
|
|
88
84
|
|
|
89
|
-
expect(
|
|
90
|
-
expect(
|
|
85
|
+
expect(io.outText()).toContain('验收标准检查出错');
|
|
86
|
+
expect(result).toEqual({
|
|
87
|
+
kind: 'fail',
|
|
88
|
+
reason: 'acceptance gate error: gate error',
|
|
89
|
+
});
|
|
91
90
|
});
|
|
92
91
|
|
|
93
92
|
it('should use cwd as default projectPath', async () => {
|
|
94
93
|
const mockCheck = jest.fn().mockResolvedValue({ passed: true, message: 'ok' });
|
|
95
94
|
MockGate.mockImplementation(() => ({ check: mockCheck }) as any);
|
|
96
95
|
|
|
97
|
-
await acceptance({});
|
|
96
|
+
await acceptance({}, io);
|
|
98
97
|
|
|
99
98
|
expect(mockCheck).toHaveBeenCalledWith(
|
|
100
99
|
expect.objectContaining({ projectPath: process.cwd() }),
|
|
@@ -105,7 +104,7 @@ describe('acceptance command', () => {
|
|
|
105
104
|
const mockCheck = jest.fn().mockResolvedValue({ passed: true, message: 'ok' });
|
|
106
105
|
MockGate.mockImplementation(() => ({ check: mockCheck }) as any);
|
|
107
106
|
|
|
108
|
-
await acceptance({ taskId: 'TASK-001' });
|
|
107
|
+
await acceptance({ taskId: 'TASK-001' }, io);
|
|
109
108
|
|
|
110
109
|
expect(mockCheck).toHaveBeenCalledWith(
|
|
111
110
|
expect.objectContaining({ taskId: 'TASK-001' }),
|
|
@@ -116,7 +115,7 @@ describe('acceptance command', () => {
|
|
|
116
115
|
const mockCheck = jest.fn().mockResolvedValue({ passed: true, message: 'ok' });
|
|
117
116
|
MockGate.mockImplementation(() => ({ check: mockCheck }) as any);
|
|
118
117
|
|
|
119
|
-
await acceptance({ runE2e: true });
|
|
118
|
+
await acceptance({ runE2e: true }, io);
|
|
120
119
|
|
|
121
120
|
expect(MockGate).toHaveBeenCalledWith(
|
|
122
121
|
expect.objectContaining({ e2eTestCommand: 'npx playwright test' }),
|
|
@@ -127,7 +126,7 @@ describe('acceptance command', () => {
|
|
|
127
126
|
const mockCheck = jest.fn().mockResolvedValue({ passed: true, message: 'ok' });
|
|
128
127
|
MockGate.mockImplementation(() => ({ check: mockCheck }) as any);
|
|
129
128
|
|
|
130
|
-
await acceptance({});
|
|
129
|
+
await acceptance({}, io);
|
|
131
130
|
|
|
132
131
|
expect(MockGate).toHaveBeenCalledWith(
|
|
133
132
|
expect.objectContaining({ e2eTestCommand: undefined }),
|
|
@@ -143,10 +142,10 @@ describe('acceptance command', () => {
|
|
|
143
142
|
'task-2': { acceptanceCriteria: ['Criteria A'] },
|
|
144
143
|
});
|
|
145
144
|
|
|
146
|
-
await listAcceptanceCriteria({});
|
|
145
|
+
await listAcceptanceCriteria({}, io);
|
|
147
146
|
|
|
148
|
-
expect(
|
|
149
|
-
expect(
|
|
147
|
+
expect(io.outText()).toContain('task-1');
|
|
148
|
+
expect(io.outText()).toContain('Criteria 1');
|
|
150
149
|
});
|
|
151
150
|
|
|
152
151
|
it('should handle task with no criteria', async () => {
|
|
@@ -155,26 +154,34 @@ describe('acceptance command', () => {
|
|
|
155
154
|
'task-1': {},
|
|
156
155
|
});
|
|
157
156
|
|
|
158
|
-
await listAcceptanceCriteria({});
|
|
157
|
+
await listAcceptanceCriteria({}, io);
|
|
159
158
|
|
|
160
|
-
expect(
|
|
159
|
+
expect(io.outText()).toContain('无验收标准');
|
|
161
160
|
});
|
|
162
161
|
|
|
163
162
|
it('should handle empty tasks object', async () => {
|
|
164
163
|
mockFs.readFile.mockResolvedValue('yaml-content');
|
|
165
164
|
yaml.load.mockReturnValue(null);
|
|
166
165
|
|
|
167
|
-
await listAcceptanceCriteria({});
|
|
166
|
+
const result = await listAcceptanceCriteria({}, io);
|
|
168
167
|
|
|
169
|
-
expect(
|
|
168
|
+
expect(io.outText()).toContain('未找到任务定义');
|
|
169
|
+
expect(result).toEqual({
|
|
170
|
+
kind: 'skip',
|
|
171
|
+
reason: expect.stringContaining('未找到任务定义'),
|
|
172
|
+
});
|
|
170
173
|
});
|
|
171
174
|
|
|
172
175
|
it('should handle file read error', async () => {
|
|
173
176
|
mockFs.readFile.mockRejectedValue(new Error('ENOENT'));
|
|
174
177
|
|
|
175
|
-
await listAcceptanceCriteria({});
|
|
178
|
+
const result = await listAcceptanceCriteria({}, io);
|
|
176
179
|
|
|
177
|
-
expect(
|
|
180
|
+
expect(io.outText()).toContain('读取 tasks.yml 失败');
|
|
181
|
+
expect(result).toEqual({
|
|
182
|
+
kind: 'skip',
|
|
183
|
+
reason: expect.stringContaining('读取 tasks.yml 失败'),
|
|
184
|
+
});
|
|
178
185
|
});
|
|
179
186
|
});
|
|
180
187
|
});
|
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
*/
|
|
10
10
|
|
|
11
11
|
import * as fs from 'fs';
|
|
12
|
+
import { captureIO, lastJsonOutput, type CapturingIO } from '../../command-contract';
|
|
12
13
|
import * as path from 'path';
|
|
13
14
|
import { analyzeSessions } from '../analyze-sessions';
|
|
14
15
|
import { readTranscriptSessions, type MinedSession } from '../../session-mining';
|
|
@@ -69,13 +70,12 @@ function mkSession(partial: Partial<MinedSession> = {}): MinedSession {
|
|
|
69
70
|
};
|
|
70
71
|
}
|
|
71
72
|
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
}
|
|
73
|
+
let io: CapturingIO;
|
|
74
|
+
beforeEach(() => {
|
|
75
|
+
io = captureIO();
|
|
76
|
+
});
|
|
76
77
|
|
|
77
78
|
describe('analyze-sessions command', () => {
|
|
78
|
-
let consoleSpy: jest.SpyInstance;
|
|
79
79
|
|
|
80
80
|
beforeEach(() => {
|
|
81
81
|
jest.clearAllMocks();
|
|
@@ -83,42 +83,48 @@ describe('analyze-sessions command', () => {
|
|
|
83
83
|
fs.mkdirSync(path.join(TEST_HOME, '.claude', 'projects', '-root-projects', 'memory'), { recursive: true });
|
|
84
84
|
fs.mkdirSync(TRANSCRIPTS_DIR, { recursive: true });
|
|
85
85
|
process.env.CLAUDE_TRANSCRIPTS_DIR = TRANSCRIPTS_DIR;
|
|
86
|
-
consoleSpy = jest.spyOn(console, 'log').mockImplementation();
|
|
87
86
|
});
|
|
88
87
|
|
|
89
88
|
afterEach(() => {
|
|
90
|
-
consoleSpy.mockRestore();
|
|
91
89
|
delete process.env.CLAUDE_TRANSCRIPTS_DIR;
|
|
92
90
|
});
|
|
93
91
|
|
|
94
92
|
test('transcripts 目录不存在:提示 No transcripts directory found', async () => {
|
|
95
93
|
process.env.CLAUDE_TRANSCRIPTS_DIR = path.join(TEST_HOME, 'missing-dir');
|
|
96
94
|
|
|
97
|
-
await analyzeSessions({});
|
|
95
|
+
const result = await analyzeSessions({}, io);
|
|
98
96
|
|
|
99
|
-
expect(
|
|
97
|
+
expect(io.outText()).toContain('No transcripts directory found');
|
|
98
|
+
expect(result).toEqual({ kind: 'skip', reason: expect.stringContaining('会话记录目录不存在') });
|
|
100
99
|
expect(mockReadTranscriptSessions).not.toHaveBeenCalled();
|
|
101
100
|
});
|
|
102
101
|
|
|
103
102
|
test('窗口内无会话:提示 No sessions found(默认最近 7 天)', async () => {
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
expect(
|
|
103
|
+
const before = Date.now() - 7 * 86_400_000;
|
|
104
|
+
mockReadTranscriptSessions.mockReturnValue([]);
|
|
105
|
+
|
|
106
|
+
const result = await analyzeSessions({}, io);
|
|
107
|
+
const after = Date.now() - 7 * 86_400_000;
|
|
108
|
+
|
|
109
|
+
expect(io.outText()).toContain('No sessions found in the last 7 days');
|
|
110
|
+
expect(result).toEqual({ kind: 'skip', reason: '最近 7 天没有会话' });
|
|
111
|
+
// since 过滤下推到 seam:默认窗口 7 天
|
|
112
|
+
const filter = mockReadTranscriptSessions.mock.calls[0][1] as { since?: number };
|
|
113
|
+
expect(filter.since).toBeGreaterThanOrEqual(before);
|
|
114
|
+
expect(filter.since).toBeLessThanOrEqual(after);
|
|
111
115
|
});
|
|
112
116
|
|
|
113
|
-
test('--days 1
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
mkSession({ id: 'two-days-ago', mtimeMs: Date.now() - 2 * 86_400_000 }),
|
|
117
|
-
]);
|
|
117
|
+
test('--days 1:since 过滤下推到 seam(mtimeMs 窗口)', async () => {
|
|
118
|
+
const before = Date.now() - 86_400_000;
|
|
119
|
+
mockReadTranscriptSessions.mockReturnValue([mkSession({ id: 'today' })]);
|
|
118
120
|
|
|
119
|
-
await analyzeSessions({ days: 1 });
|
|
121
|
+
await analyzeSessions({ days: 1 }, io);
|
|
122
|
+
const after = Date.now() - 86_400_000;
|
|
120
123
|
|
|
121
|
-
expect(
|
|
124
|
+
expect(io.outText()).toContain('Analyzing 1 sessions (last 1 days)');
|
|
125
|
+
const filter = mockReadTranscriptSessions.mock.calls[0][1] as { since?: number };
|
|
126
|
+
expect(filter.since).toBeGreaterThanOrEqual(before);
|
|
127
|
+
expect(filter.since).toBeLessThanOrEqual(after);
|
|
122
128
|
});
|
|
123
129
|
|
|
124
130
|
test('缺省 --days:窗口为 7 天,两天前会话仍计入', async () => {
|
|
@@ -127,9 +133,9 @@ describe('analyze-sessions command', () => {
|
|
|
127
133
|
mkSession({ id: 'two-days-ago', mtimeMs: Date.now() - 2 * 86_400_000 }),
|
|
128
134
|
]);
|
|
129
135
|
|
|
130
|
-
await analyzeSessions({});
|
|
136
|
+
await analyzeSessions({}, io);
|
|
131
137
|
|
|
132
|
-
expect(
|
|
138
|
+
expect(io.outText()).toContain('Analyzing 2 sessions (last 7 days)');
|
|
133
139
|
});
|
|
134
140
|
|
|
135
141
|
test('--json:纠正句跨 3 会话聚合为 correction 候选', async () => {
|
|
@@ -139,9 +145,9 @@ describe('analyze-sessions command', () => {
|
|
|
139
145
|
mkSession({ id: 's3' }),
|
|
140
146
|
]);
|
|
141
147
|
|
|
142
|
-
await analyzeSessions({ json: true });
|
|
148
|
+
await analyzeSessions({ json: true }, io);
|
|
143
149
|
|
|
144
|
-
const output = lastJsonOutput(
|
|
150
|
+
const output = lastJsonOutput(io);
|
|
145
151
|
expect(output.sessions).toBe(3);
|
|
146
152
|
expect(output.corrections).toBe(3);
|
|
147
153
|
expect(output.candidates).toEqual(
|
|
@@ -168,9 +174,9 @@ describe('analyze-sessions command', () => {
|
|
|
168
174
|
}),
|
|
169
175
|
]);
|
|
170
176
|
|
|
171
|
-
await analyzeSessions({ json: true });
|
|
177
|
+
await analyzeSessions({ json: true }, io);
|
|
172
178
|
|
|
173
|
-
const output = lastJsonOutput(
|
|
179
|
+
const output = lastJsonOutput(io);
|
|
174
180
|
const ngramCandidates = (output.candidates as Array<{ source: string }>)
|
|
175
181
|
.filter(c => c.source === 'ngram');
|
|
176
182
|
expect(ngramCandidates.length).toBeGreaterThan(0);
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* bin 侧退出码映射测试(架构评审候选7)
|
|
3
|
+
*
|
|
4
|
+
* 命令实现只返回 CommandResult,`process.exit` 的唯一出口在 bin/harness.js。
|
|
5
|
+
* 两层证据:
|
|
6
|
+
* 1. exitCodeFor 纯映射:四种 kind + 契约违规(未知/缺失 kind)fail-closed;
|
|
7
|
+
* 2. 端到端 spawn bin/harness.js(dist 存在时):真实命令的对外退出码
|
|
8
|
+
* ——断言进程 status,不用 spyOn(process,'exit')。
|
|
9
|
+
* 另加零 process.exit 的源码护栏(命令实现/定义表目录)。
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import * as fs from 'fs';
|
|
13
|
+
import * as path from 'path';
|
|
14
|
+
import { spawnSync } from 'child_process';
|
|
15
|
+
|
|
16
|
+
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
17
|
+
const { exitCodeFor } = require('../../../../bin/harness.js');
|
|
18
|
+
|
|
19
|
+
const repoRoot = path.join(__dirname, '..', '..', '..', '..');
|
|
20
|
+
const distBin = path.join(repoRoot, 'bin', 'harness.js');
|
|
21
|
+
const distCommands = path.join(repoRoot, 'dist', 'cli', 'commands');
|
|
22
|
+
const hasDist = fs.existsSync(distCommands);
|
|
23
|
+
|
|
24
|
+
describe('exitCodeFor:kind → 退出码唯一映射', () => {
|
|
25
|
+
it('ok / skip → 0(现状对外码面只有 0/1,skip 不新增码值)', () => {
|
|
26
|
+
expect(exitCodeFor({ kind: 'ok' })).toBe(0);
|
|
27
|
+
expect(exitCodeFor({ kind: 'skip' })).toBe(0);
|
|
28
|
+
expect(exitCodeFor({ kind: 'skip', reason: '没有定义检查点' })).toBe(0);
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
it('fail / usage-error → 1', () => {
|
|
32
|
+
expect(exitCodeFor({ kind: 'fail', reason: 'gate branch: 非 master' })).toBe(1);
|
|
33
|
+
expect(exitCodeFor({ kind: 'usage-error', reason: '缺少关键词' })).toBe(1);
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
it('未知或缺失 kind → fail-closed(命令契约违规不得静默通过)', () => {
|
|
37
|
+
const errSpy = jest.spyOn(console, 'error').mockImplementation();
|
|
38
|
+
try {
|
|
39
|
+
expect(exitCodeFor({ kind: 'exploded' })).toBe(1);
|
|
40
|
+
expect(exitCodeFor(undefined)).toBe(1);
|
|
41
|
+
expect(exitCodeFor({})).toBe(1);
|
|
42
|
+
expect(errSpy).toHaveBeenCalledTimes(3);
|
|
43
|
+
} finally {
|
|
44
|
+
errSpy.mockRestore();
|
|
45
|
+
}
|
|
46
|
+
});
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
describe('命令实现/定义表目录零 process.exit(唯一出口在 bin)', () => {
|
|
50
|
+
const dirs = [
|
|
51
|
+
path.join(repoRoot, 'src', 'cli', 'commands'),
|
|
52
|
+
path.join(repoRoot, 'src', 'cli', 'commands', 'sync-docs'),
|
|
53
|
+
];
|
|
54
|
+
|
|
55
|
+
it.each(dirs.map(d => [d]))('%s', (dir: string) => {
|
|
56
|
+
const offenders = fs.readdirSync(dir)
|
|
57
|
+
.filter(f => f.endsWith('.ts') && !f.includes('.test.'))
|
|
58
|
+
.filter(f => fs.readFileSync(path.join(dir, f), 'utf-8').includes('process.exit'))
|
|
59
|
+
.map(f => `${dir}/${f}`);
|
|
60
|
+
expect(offenders).toEqual([]);
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
it('bin/harness.js 只有一处 process.exit 调用点', () => {
|
|
64
|
+
const source = fs.readFileSync(distBin, 'utf-8');
|
|
65
|
+
const calls = [...source.matchAll(/process\.exit\(/g)];
|
|
66
|
+
expect(calls).toHaveLength(1);
|
|
67
|
+
});
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
const smoke = hasDist ? describe : describe.skip;
|
|
71
|
+
|
|
72
|
+
smoke('端到端:真实命令经 bin 映射后的对外退出码', () => {
|
|
73
|
+
const run = (argv: string[]) => spawnSync(process.execPath, [distBin, ...argv], {
|
|
74
|
+
cwd: repoRoot,
|
|
75
|
+
encoding: 'utf-8',
|
|
76
|
+
timeout: 60000,
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
it('放行命令 → 0(返回 ok,进程自然退出)', () => {
|
|
80
|
+
const r = run(['command', 'ls']);
|
|
81
|
+
expect(r.status).toBe(0);
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
it('门禁否决 → 1(返回 fail,bin 映射)', () => {
|
|
85
|
+
const r = run(['command', 'rm -rf /']);
|
|
86
|
+
expect(r.status).toBe(1);
|
|
87
|
+
expect(r.stdout).toContain('✗');
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
it('用法错误 → 1(返回 usage-error)', () => {
|
|
91
|
+
const r = run(['kb', 's']);
|
|
92
|
+
expect(r.status).toBe(1);
|
|
93
|
+
expect(r.stderr).toContain('请提供搜索关键词');
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
it('bin 自身子命令路由错误 → 1', () => {
|
|
97
|
+
const r = run(['knowledge', 'bogus']);
|
|
98
|
+
expect(r.status).toBe(1);
|
|
99
|
+
expect(r.stderr).toContain('未知子命令: bogus');
|
|
100
|
+
});
|
|
101
|
+
});
|
|
@@ -7,18 +7,23 @@
|
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
9
|
import * as fs from 'fs';
|
|
10
|
-
import
|
|
10
|
+
import { captureIO, type CapturingIO } from '../../command-contract';
|
|
11
11
|
import * as path from 'path';
|
|
12
12
|
import { check } from '../check';
|
|
13
|
-
import { constraintChecker } from '../../../core/constraints/checker';
|
|
14
13
|
import { renderConstraintsSection } from '../../../core/constraints/injection-renderer';
|
|
15
14
|
import { getEffectiveConstraints } from '../../../core/effective-constraints';
|
|
15
|
+
import { createProjectFixture } from '../../../test-setup/project-fixture';
|
|
16
16
|
|
|
17
|
+
// 命令 per-run 构造 checker(harness#88):构造替身即控制 checkConstraints 返回值
|
|
18
|
+
const mockChecker = { checkConstraints: jest.fn() };
|
|
17
19
|
jest.mock('../../../core/constraints/checker', () => ({
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
20
|
+
ConstraintChecker: jest.fn(function () {
|
|
21
|
+
return mockChecker;
|
|
22
|
+
}),
|
|
23
|
+
}));
|
|
24
|
+
|
|
25
|
+
jest.mock('../../../monitoring/traces', () => ({
|
|
26
|
+
getTraceCollector: jest.fn(() => ({ record: jest.fn() })),
|
|
22
27
|
}));
|
|
23
28
|
|
|
24
29
|
jest.mock('../../../utils/exec', () => ({
|
|
@@ -30,15 +35,9 @@ jest.mock('child_process', () => ({
|
|
|
30
35
|
execSync: jest.fn(() => Buffer.from('')),
|
|
31
36
|
}));
|
|
32
37
|
|
|
33
|
-
const mockChecker = constraintChecker as jest.Mocked<typeof constraintChecker>;
|
|
34
|
-
|
|
35
38
|
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
36
39
|
const REAL_VERSION = require('../../../../package.json').version as string;
|
|
37
40
|
|
|
38
|
-
function makeTmpProject(): string {
|
|
39
|
-
return fs.mkdtempSync(path.join(os.tmpdir(), 'harness-check-drift-test-'));
|
|
40
|
-
}
|
|
41
|
-
|
|
42
41
|
function writeClaudeMd(root: string, version: string): void {
|
|
43
42
|
const section =
|
|
44
43
|
'## Governance Rules\n' + renderConstraintsSection(getEffectiveConstraints(root), version);
|
|
@@ -46,13 +45,12 @@ function writeClaudeMd(root: string, version: string): void {
|
|
|
46
45
|
}
|
|
47
46
|
|
|
48
47
|
describe('check 命令注入漂移警告', () => {
|
|
49
|
-
let
|
|
50
|
-
let exitSpy: jest.SpyInstance;
|
|
48
|
+
let io: CapturingIO;
|
|
51
49
|
|
|
52
50
|
beforeEach(() => {
|
|
51
|
+
|
|
52
|
+
io = captureIO();
|
|
53
53
|
jest.clearAllMocks();
|
|
54
|
-
consoleSpy = jest.spyOn(console, 'log').mockImplementation(() => {});
|
|
55
|
-
exitSpy = jest.spyOn(process, 'exit').mockImplementation(() => undefined as never);
|
|
56
54
|
mockChecker.checkConstraints.mockResolvedValue({
|
|
57
55
|
passed: true,
|
|
58
56
|
ironLaws: [],
|
|
@@ -61,18 +59,13 @@ describe('check 命令注入漂移警告', () => {
|
|
|
61
59
|
});
|
|
62
60
|
});
|
|
63
61
|
|
|
64
|
-
|
|
65
|
-
consoleSpy.mockRestore();
|
|
66
|
-
exitSpy.mockRestore();
|
|
67
|
-
});
|
|
68
|
-
|
|
69
|
-
const outputText = () => consoleSpy.mock.calls.map(c => String(c[0])).join('\n');
|
|
62
|
+
const outputText = () => io.outText();
|
|
70
63
|
|
|
71
64
|
it('版本漂移:黄色警告块 + ⚠️⚠️ 版本行,但不阻断(exit 未调用,仍判通过)', async () => {
|
|
72
|
-
const root =
|
|
65
|
+
const root = createProjectFixture({ name: 'harness-check-drift-test' });
|
|
73
66
|
writeClaudeMd(root, '0.0.1-old');
|
|
74
67
|
|
|
75
|
-
await check({ preset: 'standard', staged: false, projectPath: root });
|
|
68
|
+
const result = await check({ preset: 'standard', staged: false, projectPath: root }, io);
|
|
76
69
|
|
|
77
70
|
const output = outputText();
|
|
78
71
|
expect(output).toContain('约束注入漂移');
|
|
@@ -82,43 +75,43 @@ describe('check 命令注入漂移警告', () => {
|
|
|
82
75
|
expect(output).toContain('npx @dommaker/harness init');
|
|
83
76
|
// 不阻断:检查仍通过、未调用 process.exit
|
|
84
77
|
expect(output).toContain('约束检查通过');
|
|
85
|
-
expect(
|
|
78
|
+
expect(result.kind).toBe('ok');
|
|
86
79
|
});
|
|
87
80
|
|
|
88
81
|
it('内容漂移:手改一条 → 警告块含缺失/多余计数,exit 未调用', async () => {
|
|
89
|
-
const root =
|
|
82
|
+
const root = createProjectFixture({ name: 'harness-check-drift-test' });
|
|
90
83
|
writeClaudeMd(root, REAL_VERSION);
|
|
91
84
|
const claudeMdPath = path.join(root, 'CLAUDE.md');
|
|
92
85
|
const content = fs.readFileSync(claudeMdPath, 'utf-8');
|
|
93
86
|
const originalLine = content.split('\n').find(l => l.startsWith('- **'))!;
|
|
94
87
|
fs.writeFileSync(claudeMdPath, content.replace(originalLine, originalLine.replace(/: .+$/, ': 篡改')), 'utf-8');
|
|
95
88
|
|
|
96
|
-
await check({ preset: 'standard', staged: false, projectPath: root });
|
|
89
|
+
const result = await check({ preset: 'standard', staged: false, projectPath: root }, io);
|
|
97
90
|
|
|
98
91
|
const output = outputText();
|
|
99
92
|
expect(output).toContain('内容漂移: 缺失 1 条 / 多余 1 条');
|
|
100
93
|
expect(output).toContain('约束检查通过');
|
|
101
|
-
expect(
|
|
94
|
+
expect(result.kind).toBe('ok');
|
|
102
95
|
});
|
|
103
96
|
|
|
104
97
|
it('无漂移:零警告输出(不增加噪音)', async () => {
|
|
105
|
-
const root =
|
|
98
|
+
const root = createProjectFixture({ name: 'harness-check-drift-test' });
|
|
106
99
|
writeClaudeMd(root, REAL_VERSION);
|
|
107
100
|
|
|
108
|
-
await check({ preset: 'standard', staged: false, projectPath: root });
|
|
101
|
+
const result = await check({ preset: 'standard', staged: false, projectPath: root }, io);
|
|
109
102
|
|
|
110
103
|
const output = outputText();
|
|
111
104
|
expect(output).not.toContain('注入漂移');
|
|
112
105
|
expect(output).toContain('约束检查通过');
|
|
113
|
-
expect(
|
|
106
|
+
expect(result.kind).toBe('ok');
|
|
114
107
|
});
|
|
115
108
|
|
|
116
109
|
it('未注入(无 CLAUDE.md):不警告', async () => {
|
|
117
|
-
const root =
|
|
110
|
+
const root = createProjectFixture({ name: 'harness-check-drift-test' });
|
|
118
111
|
|
|
119
|
-
await check({ preset: 'standard', staged: false, projectPath: root });
|
|
112
|
+
const result = await check({ preset: 'standard', staged: false, projectPath: root }, io);
|
|
120
113
|
|
|
121
114
|
expect(outputText()).not.toContain('注入漂移');
|
|
122
|
-
expect(
|
|
115
|
+
expect(result.kind).toBe('ok');
|
|
123
116
|
});
|
|
124
117
|
});
|