@dommaker/harness 1.3.0 → 1.5.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 +20 -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 +104 -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 +27 -219
- 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 +17 -30
- package/dist/cli/commands/knowledge.d.ts.map +1 -1
- package/dist/cli/commands/knowledge.js +183 -273
- 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 +9 -3
- package/dist/cli/commands/update-user-model.d.ts.map +1 -1
- package/dist/cli/commands/update-user-model.js +56 -61
- 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 +68 -117
- package/dist/core/constraints/checker.js.map +1 -1
- package/dist/core/constraints/checkers/capability-sync.d.ts +20 -8
- package/dist/core/constraints/checkers/capability-sync.d.ts.map +1 -1
- package/dist/core/constraints/checkers/capability-sync.js +66 -77
- 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 +49 -66
- 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/index.d.ts +2 -2
- package/dist/core/constraints/checkers/index.d.ts.map +1 -1
- package/dist/core/constraints/checkers/index.js +2 -1
- package/dist/core/constraints/checkers/index.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 +43 -4
- package/dist/core/constraints/checkers/types.d.ts.map +1 -1
- package/dist/core/constraints/checkers/types.js +33 -0
- package/dist/core/constraints/checkers/types.js.map +1 -1
- 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 +2 -1
- package/dist/gates/checker-gate.d.ts.map +1 -1
- package/dist/gates/checker-gate.js +11 -13
- 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 +8 -18
- package/dist/types/constraint.d.ts.map +1 -1
- package/dist/types/constraint.js +6 -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/types/trace.d.ts +9 -0
- package/dist/types/trace.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 +8 -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 +130 -101
- 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 +23 -8
- 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 +107 -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 +448 -378
- 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 +231 -0
- package/src/cli/commands/__tests__/registry.test.ts +75 -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 +97 -34
- 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 +127 -66
- 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 +34 -227
- package/src/cli/commands/failure.ts +33 -26
- package/src/cli/commands/init.ts +166 -220
- package/src/cli/commands/knowledge.ts +192 -289
- 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 +64 -61
- 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 +10 -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 +82 -133
- package/src/core/constraints/checkers/__tests__/capability-sync.test.ts +273 -0
- package/src/core/constraints/checkers/__tests__/check-outcome.test.ts +176 -0
- package/src/core/constraints/checkers/__tests__/context-doc-sync.test.ts +77 -0
- package/src/core/constraints/checkers/__tests__/docs-freshness.test.ts +126 -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 +153 -0
- package/src/core/constraints/checkers/capability-sync.ts +75 -96
- package/src/core/constraints/checkers/context-doc-sync.ts +15 -25
- package/src/core/constraints/checkers/docs-freshness.ts +54 -70
- package/src/core/constraints/checkers/governance-presence.ts +7 -25
- package/src/core/constraints/checkers/index.ts +9 -2
- package/src/core/constraints/checkers/no-test-simplification.ts +4 -3
- package/src/core/constraints/checkers/types.ts +60 -4
- 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 +7 -4
- 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__/checker-gate.test.ts +24 -0
- 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 +12 -15
- 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 +16 -27
- 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/types/trace.ts +10 -0
- 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
|
@@ -10,32 +10,19 @@
|
|
|
10
10
|
*/
|
|
11
11
|
|
|
12
12
|
import * as fs from 'fs';
|
|
13
|
-
import
|
|
13
|
+
import { captureIO, type CapturingIO } from '../../command-contract';
|
|
14
14
|
import * as path from 'path';
|
|
15
15
|
import { PassThrough, Writable } from 'stream';
|
|
16
16
|
import * as yaml from 'js-yaml';
|
|
17
|
-
import type { ExecutionTrace } from '../../../types/trace';
|
|
18
17
|
import { getConstraint } from '../../../core/constraints/definitions';
|
|
19
18
|
import { getEffectiveConstraints } from '../../../core/effective-constraints';
|
|
20
19
|
import { renderConstraintsSection } from '../../../core/constraints/injection-renderer';
|
|
21
20
|
import { FileKnowledgeStore } from '../../../knowledge/store';
|
|
22
21
|
import { retireConstraint, constraintsRetire, runRetireInteractive, printRetireResult } from '../constraints-retire';
|
|
22
|
+
import { createProjectFixture, writeProjectTraces } from '../../../test-setup/project-fixture';
|
|
23
23
|
|
|
24
24
|
const FIXED_NOW = new Date('2026-08-08T12:00:00.000Z');
|
|
25
25
|
|
|
26
|
-
function makeTmpProject(): string {
|
|
27
|
-
return fs.mkdtempSync(path.join(os.tmpdir(), 'harness-retire-test-'));
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
function writeTraces(root: string, traces: Partial<ExecutionTrace>[]): void {
|
|
31
|
-
const dir = path.join(root, '.harness', 'logs');
|
|
32
|
-
fs.mkdirSync(dir, { recursive: true });
|
|
33
|
-
const lines = traces.map(t =>
|
|
34
|
-
JSON.stringify({ level: 'guideline', timestamp: 1700000000000, result: 'pass', ...t })
|
|
35
|
-
);
|
|
36
|
-
fs.writeFileSync(path.join(dir, 'traces.log'), lines.join('\n') + '\n', 'utf-8');
|
|
37
|
-
}
|
|
38
|
-
|
|
39
26
|
function readConfig(root: string): any {
|
|
40
27
|
return yaml.load(fs.readFileSync(path.join(root, '.harness', 'config.yml'), 'utf-8'));
|
|
41
28
|
}
|
|
@@ -58,20 +45,18 @@ function readCustom(root: string): any {
|
|
|
58
45
|
return yaml.load(fs.readFileSync(path.join(root, '.harness', 'custom-constraints.yml'), 'utf-8'));
|
|
59
46
|
}
|
|
60
47
|
|
|
61
|
-
let logSpy: jest.SpyInstance;
|
|
62
|
-
|
|
63
48
|
beforeEach(() => {
|
|
64
|
-
logSpy = jest.spyOn(console, 'log').mockImplementation(() => {});
|
|
65
49
|
});
|
|
66
50
|
|
|
67
|
-
|
|
68
|
-
|
|
51
|
+
let io: CapturingIO;
|
|
52
|
+
beforeEach(() => {
|
|
53
|
+
io = captureIO();
|
|
69
54
|
});
|
|
70
55
|
|
|
71
56
|
describe('retireConstraint 执行逻辑', () => {
|
|
72
57
|
it('config.yml 写入形态:enabled:false + retired 元数据(at/reason/stats)', () => {
|
|
73
|
-
const root =
|
|
74
|
-
|
|
58
|
+
const root = createProjectFixture({ name: 'harness-retire-test' });
|
|
59
|
+
writeProjectTraces(root, [
|
|
75
60
|
{ constraintId: 'no_hardcoded_credentials', result: 'pass' },
|
|
76
61
|
{ constraintId: 'no_hardcoded_credentials', result: 'pass', timestamp: 1700000001000 },
|
|
77
62
|
{ constraintId: 'no_hardcoded_credentials', result: 'fail', timestamp: 1700000002000 },
|
|
@@ -97,7 +82,7 @@ describe('retireConstraint 执行逻辑', () => {
|
|
|
97
82
|
});
|
|
98
83
|
|
|
99
84
|
it('config.yml 不存在时创建;已有其他配置时保留', () => {
|
|
100
|
-
const root =
|
|
85
|
+
const root = createProjectFixture({ name: 'harness-retire-test' });
|
|
101
86
|
fs.mkdirSync(path.join(root, '.harness'), { recursive: true });
|
|
102
87
|
fs.writeFileSync(path.join(root, '.harness', 'config.yml'), 'preset: strict\nscenes:\n - llm-app\n', 'utf-8');
|
|
103
88
|
|
|
@@ -112,8 +97,10 @@ describe('retireConstraint 执行逻辑', () => {
|
|
|
112
97
|
});
|
|
113
98
|
|
|
114
99
|
it('KnowledgeStore 写入退役记录:规则原文 + 原因 + 统计 + signal 模式', () => {
|
|
115
|
-
const root =
|
|
116
|
-
|
|
100
|
+
const root = createProjectFixture({
|
|
101
|
+
name: 'harness-retire-test',
|
|
102
|
+
traces: [{ constraintId: 'no_bypass_checkpoint', result: 'fail' }],
|
|
103
|
+
});
|
|
117
104
|
|
|
118
105
|
const result = retireConstraint(root, 'no_bypass_checkpoint', { reason: '流程已内置门禁', now: FIXED_NOW });
|
|
119
106
|
expect(result.status).toBe('retired');
|
|
@@ -137,7 +124,7 @@ describe('retireConstraint 执行逻辑', () => {
|
|
|
137
124
|
});
|
|
138
125
|
|
|
139
126
|
it('CLAUDE.md 含标记段时同步重渲染(退役条目消失、标记保留)', () => {
|
|
140
|
-
const root =
|
|
127
|
+
const root = createProjectFixture({ name: 'harness-retire-test' });
|
|
141
128
|
// 先用当前生效集渲染一个合法注入段
|
|
142
129
|
const before = renderConstraintsSection(getEffectiveConstraints(root), '0.0.0-test');
|
|
143
130
|
expect(before).toContain('no_bypass_checkpoint');
|
|
@@ -153,14 +140,14 @@ describe('retireConstraint 执行逻辑', () => {
|
|
|
153
140
|
expect(after).not.toContain('**no_bypass_checkpoint**');
|
|
154
141
|
expect(after).toContain('其他内容');
|
|
155
142
|
|
|
156
|
-
printRetireResult(result);
|
|
157
|
-
const output =
|
|
143
|
+
printRetireResult(result, io);
|
|
144
|
+
const output = io.outText();
|
|
158
145
|
expect(output).toContain('已同步 CLAUDE.md 注入段');
|
|
159
146
|
expect(output).toContain('恢复方法');
|
|
160
147
|
});
|
|
161
148
|
|
|
162
149
|
it('CLAUDE.md 无标记段或不存在时不创建、不同步', () => {
|
|
163
|
-
const root =
|
|
150
|
+
const root = createProjectFixture({ name: 'harness-retire-test' });
|
|
164
151
|
fs.writeFileSync(path.join(root, 'CLAUDE.md'), '# 用户自写\n', 'utf-8');
|
|
165
152
|
const result = retireConstraint(root, 'capability_sync', { now: FIXED_NOW });
|
|
166
153
|
expect(result.status).toBe('retired');
|
|
@@ -169,7 +156,7 @@ describe('retireConstraint 执行逻辑', () => {
|
|
|
169
156
|
});
|
|
170
157
|
|
|
171
158
|
it('新模型仓:AGENTS.md PRESERVE:governance 内含标记段时同步重渲染(无 CLAUDE.md)', () => {
|
|
172
|
-
const root =
|
|
159
|
+
const root = createProjectFixture({ name: 'harness-retire-test' });
|
|
173
160
|
const before = renderConstraintsSection(getEffectiveConstraints(root), '0.0.0-test');
|
|
174
161
|
expect(before).toContain('no_bypass_checkpoint');
|
|
175
162
|
fs.writeFileSync(
|
|
@@ -194,7 +181,7 @@ describe('retireConstraint 执行逻辑', () => {
|
|
|
194
181
|
});
|
|
195
182
|
|
|
196
183
|
it('两文件均有标记段时旧模型仓豁免:只同步 CLAUDE.md,AGENTS.md 不动', () => {
|
|
197
|
-
const root =
|
|
184
|
+
const root = createProjectFixture({ name: 'harness-retire-test' });
|
|
198
185
|
const before = renderConstraintsSection(getEffectiveConstraints(root), '0.0.0-test');
|
|
199
186
|
fs.writeFileSync(path.join(root, 'CLAUDE.md'), `# 项目\n\n## Governance Rules\n${before}`, 'utf-8');
|
|
200
187
|
const agentsMd = `# AGENTS.md\n\n<!-- PRESERVE:governance -->\n## Governance Rules\n${before}<!-- /PRESERVE:governance -->\n`;
|
|
@@ -210,7 +197,7 @@ describe('retireConstraint 执行逻辑', () => {
|
|
|
210
197
|
});
|
|
211
198
|
|
|
212
199
|
it('重复 retire:already_retired,不覆盖原 retired 元数据', () => {
|
|
213
|
-
const root =
|
|
200
|
+
const root = createProjectFixture({ name: 'harness-retire-test' });
|
|
214
201
|
const first = retireConstraint(root, 'capability_sync', { reason: '第一次', now: FIXED_NOW });
|
|
215
202
|
expect(first.status).toBe('retired');
|
|
216
203
|
|
|
@@ -224,28 +211,28 @@ describe('retireConstraint 执行逻辑', () => {
|
|
|
224
211
|
});
|
|
225
212
|
|
|
226
213
|
it('未知 id:unknown_id,不落盘任何文件', () => {
|
|
227
|
-
const root =
|
|
214
|
+
const root = createProjectFixture({ name: 'harness-retire-test' });
|
|
228
215
|
const result = retireConstraint(root, 'not_a_constraint', { now: FIXED_NOW });
|
|
229
216
|
expect(result.status).toBe('unknown_id');
|
|
230
217
|
expect(fs.existsSync(path.join(root, '.harness'))).toBe(false);
|
|
231
218
|
});
|
|
232
219
|
|
|
233
220
|
it('check 层 iron:isIronLaw 标记为 true(供交互模式二次确认)', () => {
|
|
234
|
-
const root =
|
|
221
|
+
const root = createProjectFixture({ name: 'harness-retire-test' });
|
|
235
222
|
const result = retireConstraint(root, 'docs_freshness', { now: FIXED_NOW });
|
|
236
223
|
expect(result.status).toBe('retired');
|
|
237
224
|
expect(result.isIronLaw).toBe(true);
|
|
238
225
|
});
|
|
239
226
|
|
|
240
227
|
it('退役后生效集不再包含该约束', () => {
|
|
241
|
-
const root =
|
|
228
|
+
const root = createProjectFixture({ name: 'harness-retire-test' });
|
|
242
229
|
expect(getEffectiveConstraints(root).some(c => c.id === 'capability_sync')).toBe(true);
|
|
243
230
|
retireConstraint(root, 'capability_sync', { now: FIXED_NOW });
|
|
244
231
|
expect(getEffectiveConstraints(root).some(c => c.id === 'capability_sync')).toBe(false);
|
|
245
232
|
});
|
|
246
233
|
|
|
247
234
|
it('custom 约束退役:落 custom-constraints.yml retired 段(不写 config.yml),生效集与注入段移除', () => {
|
|
248
|
-
const root =
|
|
235
|
+
const root = createProjectFixture({ name: 'harness-retire-test' });
|
|
249
236
|
writeCustom(root, CUSTOM_YML);
|
|
250
237
|
const before = renderConstraintsSection(getEffectiveConstraints(root), '0.0.0-test');
|
|
251
238
|
expect(before).toContain('**my_custom_rule**');
|
|
@@ -271,13 +258,13 @@ describe('retireConstraint 执行逻辑', () => {
|
|
|
271
258
|
expect(after).not.toContain('**my_custom_rule**');
|
|
272
259
|
expect(after).toContain('其他内容');
|
|
273
260
|
// 打印的恢复提示指向 yml
|
|
274
|
-
printRetireResult(result);
|
|
275
|
-
const output =
|
|
261
|
+
printRetireResult(result, io);
|
|
262
|
+
const output = io.outText();
|
|
276
263
|
expect(output).toContain('custom_constraints.my_custom_rule.retired');
|
|
277
264
|
});
|
|
278
265
|
|
|
279
266
|
it('custom 重复退役:yml 已带 retired → already_retired,不覆盖原元数据', () => {
|
|
280
|
-
const root =
|
|
267
|
+
const root = createProjectFixture({ name: 'harness-retire-test' });
|
|
281
268
|
writeCustom(root, CUSTOM_YML);
|
|
282
269
|
const first = retireConstraint(root, 'my_custom_rule', { reason: '第一次', now: FIXED_NOW });
|
|
283
270
|
expect(first.status).toBe('retired');
|
|
@@ -292,7 +279,7 @@ describe('retireConstraint 执行逻辑', () => {
|
|
|
292
279
|
});
|
|
293
280
|
|
|
294
281
|
it('custom 历史落点(config.yml enabled:false)仍判定 already_retired', () => {
|
|
295
|
-
const root =
|
|
282
|
+
const root = createProjectFixture({ name: 'harness-retire-test' });
|
|
296
283
|
writeCustom(root, CUSTOM_YML);
|
|
297
284
|
fs.writeFileSync(path.join(root, '.harness', 'config.yml'), 'constraints:\n my_custom_rule:\n enabled: false\n', 'utf-8');
|
|
298
285
|
const result = retireConstraint(root, 'my_custom_rule', { now: FIXED_NOW });
|
|
@@ -300,7 +287,7 @@ describe('retireConstraint 执行逻辑', () => {
|
|
|
300
287
|
});
|
|
301
288
|
|
|
302
289
|
it('custom 退役 KnowledgeStore 记录包含 promptInjection(规则原文完整)', () => {
|
|
303
|
-
const root =
|
|
290
|
+
const root = createProjectFixture({ name: 'harness-retire-test' });
|
|
304
291
|
writeCustom(root, CUSTOM_YML);
|
|
305
292
|
const result = retireConstraint(root, 'my_custom_rule', { reason: '由新机制覆盖', now: FIXED_NOW });
|
|
306
293
|
expect(result.status).toBe('retired');
|
|
@@ -313,7 +300,7 @@ describe('retireConstraint 执行逻辑', () => {
|
|
|
313
300
|
});
|
|
314
301
|
|
|
315
302
|
it('custom 恢复:删除 yml retired 段后回到生效集', () => {
|
|
316
|
-
const root =
|
|
303
|
+
const root = createProjectFixture({ name: 'harness-retire-test' });
|
|
317
304
|
writeCustom(root, CUSTOM_YML);
|
|
318
305
|
retireConstraint(root, 'my_custom_rule', { now: FIXED_NOW });
|
|
319
306
|
expect(getEffectiveConstraints(root).some(c => c.id === 'my_custom_rule')).toBe(false);
|
|
@@ -327,39 +314,25 @@ describe('retireConstraint 执行逻辑', () => {
|
|
|
327
314
|
});
|
|
328
315
|
|
|
329
316
|
describe('constraintsRetire 非交互直达', () => {
|
|
330
|
-
let originalExitCode: typeof process.exitCode;
|
|
331
|
-
|
|
332
|
-
beforeEach(() => {
|
|
333
|
-
originalExitCode = process.exitCode;
|
|
334
|
-
process.exitCode = 0;
|
|
335
|
-
});
|
|
336
|
-
|
|
337
|
-
afterEach(() => {
|
|
338
|
-
process.exitCode = originalExitCode;
|
|
339
|
-
});
|
|
340
|
-
|
|
341
317
|
it('无 --yes 直达:报错 + 非零退出码 + 不落盘任何文件(#24 人确认闸门)', async () => {
|
|
342
|
-
const root =
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
await constraintsRetire('docs_freshness', { projectPath: root, reason: '直接退役' });
|
|
318
|
+
const root = createProjectFixture({ name: 'harness-retire-test' });
|
|
319
|
+
{
|
|
320
|
+
const result = await constraintsRetire('docs_freshness', { projectPath: root, reason: '直接退役' }, io);
|
|
346
321
|
|
|
347
|
-
const output =
|
|
322
|
+
const output = io.errText();
|
|
348
323
|
expect(output).toContain('--yes');
|
|
349
324
|
expect(output).toContain('交互');
|
|
350
|
-
expect(
|
|
325
|
+
expect(result).toEqual({ kind: 'usage-error', reason: expect.stringContaining('缺少显式 --yes 人确认') });
|
|
351
326
|
// 无副作用:任何文件都不落盘
|
|
352
327
|
expect(fs.existsSync(path.join(root, '.harness'))).toBe(false);
|
|
353
|
-
} finally {
|
|
354
|
-
errSpy.mockRestore();
|
|
355
328
|
}
|
|
356
329
|
});
|
|
357
330
|
|
|
358
331
|
it('--yes 直达 iron 退役:打印额外警示并落盘', async () => {
|
|
359
|
-
const root =
|
|
360
|
-
await constraintsRetire('docs_freshness', { projectPath: root, reason: '直接退役', yes: true });
|
|
332
|
+
const root = createProjectFixture({ name: 'harness-retire-test' });
|
|
333
|
+
await constraintsRetire('docs_freshness', { projectPath: root, reason: '直接退役', yes: true }, io);
|
|
361
334
|
|
|
362
|
-
const output =
|
|
335
|
+
const output = io.outText();
|
|
363
336
|
expect(output).toContain('Iron Law');
|
|
364
337
|
expect(output).toContain('已退役');
|
|
365
338
|
|
|
@@ -368,10 +341,10 @@ describe('constraintsRetire 非交互直达', () => {
|
|
|
368
341
|
});
|
|
369
342
|
|
|
370
343
|
it('--yes 直达非 iron(guideline)退役:不打印 iron 警示并落盘', async () => {
|
|
371
|
-
const root =
|
|
372
|
-
await constraintsRetire('no_hardcoded_credentials', { projectPath: root, reason: '直接退役', yes: true });
|
|
344
|
+
const root = createProjectFixture({ name: 'harness-retire-test' });
|
|
345
|
+
await constraintsRetire('no_hardcoded_credentials', { projectPath: root, reason: '直接退役', yes: true }, io);
|
|
373
346
|
|
|
374
|
-
const output =
|
|
347
|
+
const output = io.outText();
|
|
375
348
|
expect(output).not.toContain('Iron Law');
|
|
376
349
|
expect(output).toContain('已退役');
|
|
377
350
|
|
|
@@ -380,18 +353,31 @@ describe('constraintsRetire 非交互直达', () => {
|
|
|
380
353
|
});
|
|
381
354
|
|
|
382
355
|
it('--yes 直达未知 id:明确提示', async () => {
|
|
383
|
-
const root =
|
|
384
|
-
await constraintsRetire('ghost', { projectPath: root, yes: true });
|
|
385
|
-
const output =
|
|
356
|
+
const root = createProjectFixture({ name: 'harness-retire-test' });
|
|
357
|
+
await constraintsRetire('ghost', { projectPath: root, yes: true }, io);
|
|
358
|
+
const output = io.outText();
|
|
386
359
|
expect(output).toContain('约束不存在');
|
|
387
360
|
});
|
|
388
361
|
});
|
|
389
362
|
|
|
390
363
|
describe('runRetireInteractive 交互流程(注入 IO 流)', () => {
|
|
391
|
-
/**
|
|
364
|
+
/**
|
|
365
|
+
* 交互正文仍走 console(RetireIO 只注入 readline 的 input/output 流,
|
|
366
|
+
* 且 retireConstraint 核心直接 console.warn 式打印)→ 用 console 捕获。
|
|
367
|
+
*/
|
|
368
|
+
function captureLog(): { text: () => string; restore: () => void } {
|
|
369
|
+
let out = '';
|
|
370
|
+
const spy = jest.spyOn(console, 'log').mockImplementation((...args: unknown[]) => {
|
|
371
|
+
out += String(args[0] ?? '') + '\n';
|
|
372
|
+
});
|
|
373
|
+
return { text: () => out, restore: () => spy.mockRestore() };
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
/** 逐行 drip-feed,等待 readline 消费上一行;output 收集写入文本 */
|
|
392
377
|
function makeIo(lines: string[]) {
|
|
393
378
|
const input = new PassThrough();
|
|
394
|
-
const
|
|
379
|
+
const chunks: string[] = [];
|
|
380
|
+
const output = new Writable({ write(c, _e, cb) { chunks.push(String(c)); cb(); } });
|
|
395
381
|
let i = 0;
|
|
396
382
|
const timer = setInterval(() => {
|
|
397
383
|
if (i < lines.length) {
|
|
@@ -401,18 +387,20 @@ describe('runRetireInteractive 交互流程(注入 IO 流)', () => {
|
|
|
401
387
|
clearInterval(timer);
|
|
402
388
|
}
|
|
403
389
|
}, 30);
|
|
404
|
-
return { input, output, done: () => clearInterval(timer) };
|
|
390
|
+
return { input, output, text: () => chunks.join(''), done: () => clearInterval(timer) };
|
|
405
391
|
}
|
|
406
392
|
|
|
407
393
|
it('候选编号选择 + iron 二次确认拒绝 → 不落盘', async () => {
|
|
408
|
-
const root =
|
|
394
|
+
const root = createProjectFixture({ name: 'harness-retire-test' });
|
|
409
395
|
// 无 trace → 候选全部为零触发,1 号候选是第一条 iron(no_completion_without_verification)
|
|
410
|
-
const
|
|
396
|
+
const streams = makeIo(['1', 'n']); // 选 1 号 → iron 确认拒绝
|
|
397
|
+
const printed = captureLog();
|
|
411
398
|
|
|
412
|
-
await runRetireInteractive(root,
|
|
413
|
-
|
|
399
|
+
await runRetireInteractive(root, streams);
|
|
400
|
+
streams.done();
|
|
401
|
+
printed.restore();
|
|
414
402
|
|
|
415
|
-
const output =
|
|
403
|
+
const output = printed.text() + streams.text();
|
|
416
404
|
expect(output).toContain('退役候选');
|
|
417
405
|
expect(output).toContain('已跳过 no_completion_without_verification');
|
|
418
406
|
expect(output).toContain('无可执行项');
|
|
@@ -420,7 +408,7 @@ describe('runRetireInteractive 交互流程(注入 IO 流)', () => {
|
|
|
420
408
|
});
|
|
421
409
|
|
|
422
410
|
it('无候选 → 手动输入 id → 确认执行 → 落盘', async () => {
|
|
423
|
-
const root =
|
|
411
|
+
const root = createProjectFixture({ name: 'harness-retire-test' });
|
|
424
412
|
// 所有 check 约束给少量健康数据(低于一切候选阈值)
|
|
425
413
|
const healthy = getEffectiveConstraints(root)
|
|
426
414
|
.filter(c => c.kind === 'check')
|
|
@@ -429,13 +417,15 @@ describe('runRetireInteractive 交互流程(注入 IO 流)', () => {
|
|
|
429
417
|
{ constraintId: c.id, result: 'pass' as const, timestamp: 1700000001000 },
|
|
430
418
|
{ constraintId: c.id, result: 'fail' as const, timestamp: 1700000002000 },
|
|
431
419
|
]);
|
|
432
|
-
|
|
420
|
+
writeProjectTraces(root, healthy);
|
|
433
421
|
|
|
434
|
-
const
|
|
435
|
-
|
|
436
|
-
|
|
422
|
+
const streams = makeIo(['no_hardcoded_credentials', '误报太多', 'y']);
|
|
423
|
+
const printed = captureLog();
|
|
424
|
+
await runRetireInteractive(root, streams);
|
|
425
|
+
streams.done();
|
|
426
|
+
printed.restore();
|
|
437
427
|
|
|
438
|
-
const output =
|
|
428
|
+
const output = printed.text() + streams.text();
|
|
439
429
|
expect(output).toContain('没有退役候选');
|
|
440
430
|
expect(output).toContain('已退役');
|
|
441
431
|
|
|
@@ -444,8 +434,73 @@ describe('runRetireInteractive 交互流程(注入 IO 流)', () => {
|
|
|
444
434
|
expect(config.constraints.no_hardcoded_credentials.retired.reason).toBe('误报太多');
|
|
445
435
|
});
|
|
446
436
|
|
|
437
|
+
it('坏行 fixture:候选列表前告知损坏行数(决策依据不完整不静默,harness#100)', async () => {
|
|
438
|
+
const root = createProjectFixture({ name: 'harness-retire-test' });
|
|
439
|
+
const dir = path.join(root, '.harness', 'logs');
|
|
440
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
441
|
+
fs.writeFileSync(
|
|
442
|
+
path.join(dir, 'traces.log'),
|
|
443
|
+
'{"constraintId":"a","level":"iron_law","timestamp":1700000000000,"result":"pass"}\n{bad json\n{also bad\n',
|
|
444
|
+
'utf-8'
|
|
445
|
+
);
|
|
446
|
+
|
|
447
|
+
const streams = makeIo(['']); // 直接取消,只验证告知行
|
|
448
|
+
const printed = captureLog();
|
|
449
|
+
const notices: string[] = [];
|
|
450
|
+
const errSpy = jest.spyOn(console, 'error').mockImplementation((...args: unknown[]) => {
|
|
451
|
+
notices.push(String(args[0] ?? ''));
|
|
452
|
+
});
|
|
453
|
+
await runRetireInteractive(root, streams);
|
|
454
|
+
streams.done();
|
|
455
|
+
errSpy.mockRestore();
|
|
456
|
+
printed.restore();
|
|
457
|
+
|
|
458
|
+
expect(notices.join('\n')).toContain('2 行损坏');
|
|
459
|
+
expect(printed.text()).toContain('退役候选');
|
|
460
|
+
});
|
|
461
|
+
|
|
462
|
+
it('trace 无损坏时不出现坏行提示(零噪声)', async () => {
|
|
463
|
+
const root = createProjectFixture({
|
|
464
|
+
name: 'harness-retire-test',
|
|
465
|
+
traces: [{ constraintId: 'a', result: 'pass' }],
|
|
466
|
+
});
|
|
467
|
+
|
|
468
|
+
const streams = makeIo(['']);
|
|
469
|
+
const printed = captureLog();
|
|
470
|
+
const notices: string[] = [];
|
|
471
|
+
const errSpy = jest.spyOn(console, 'error').mockImplementation((...args: unknown[]) => {
|
|
472
|
+
notices.push(String(args[0] ?? ''));
|
|
473
|
+
});
|
|
474
|
+
await runRetireInteractive(root, streams);
|
|
475
|
+
streams.done();
|
|
476
|
+
errSpy.mockRestore();
|
|
477
|
+
printed.restore();
|
|
478
|
+
|
|
479
|
+
expect(notices.join('\n')).not.toContain('损坏');
|
|
480
|
+
expect(printed.text()).toContain('退役候选');
|
|
481
|
+
});
|
|
482
|
+
|
|
483
|
+
it('直达 --yes 路径也在落盘前告知损坏行数(harness#100:不经过交互也有告知)', async () => {
|
|
484
|
+
const root = createProjectFixture({ name: 'harness-retire-test' });
|
|
485
|
+
const dir = path.join(root, '.harness', 'logs');
|
|
486
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
487
|
+
fs.writeFileSync(
|
|
488
|
+
path.join(dir, 'traces.log'),
|
|
489
|
+
'{"constraintId":"a","level":"iron_law","timestamp":1700000000000,"result":"pass"}\n{bad json\n',
|
|
490
|
+
'utf-8'
|
|
491
|
+
);
|
|
492
|
+
|
|
493
|
+
const directIo = captureIO();
|
|
494
|
+
await constraintsRetire('no_hardcoded_credentials', { projectPath: root, yes: true }, directIo);
|
|
495
|
+
|
|
496
|
+
expect(directIo.errText()).toContain('1 行损坏');
|
|
497
|
+
// 告知不挤动结果正文:退役结论仍在 stdout
|
|
498
|
+
expect(directIo.outText()).toContain('已退役');
|
|
499
|
+
expect(readConfig(root).constraints.no_hardcoded_credentials.enabled).toBe(false);
|
|
500
|
+
});
|
|
501
|
+
|
|
447
502
|
it('无候选 → 手动输入 custom id → 确认执行 → 落 custom-constraints.yml', async () => {
|
|
448
|
-
const root =
|
|
503
|
+
const root = createProjectFixture({ name: 'harness-retire-test' });
|
|
449
504
|
writeCustom(root, CUSTOM_YML);
|
|
450
505
|
// 所有 check 约束给少量健康数据(低于一切候选阈值)
|
|
451
506
|
const healthy = getEffectiveConstraints(root)
|
|
@@ -455,11 +510,11 @@ describe('runRetireInteractive 交互流程(注入 IO 流)', () => {
|
|
|
455
510
|
{ constraintId: c.id, result: 'pass' as const, timestamp: 1700000001000 },
|
|
456
511
|
{ constraintId: c.id, result: 'fail' as const, timestamp: 1700000002000 },
|
|
457
512
|
]);
|
|
458
|
-
|
|
513
|
+
writeProjectTraces(root, healthy);
|
|
459
514
|
|
|
460
|
-
const
|
|
461
|
-
await runRetireInteractive(root,
|
|
462
|
-
|
|
515
|
+
const streams = makeIo(['my_custom_rule', '作用对象消失', 'y']);
|
|
516
|
+
await runRetireInteractive(root, streams);
|
|
517
|
+
streams.done();
|
|
463
518
|
|
|
464
519
|
const custom = readCustom(root).custom_constraints.my_custom_rule;
|
|
465
520
|
expect(custom.retired.reason).toBe('作用对象消失');
|
|
@@ -467,17 +522,19 @@ describe('runRetireInteractive 交互流程(注入 IO 流)', () => {
|
|
|
467
522
|
});
|
|
468
523
|
|
|
469
524
|
it('无候选 → 手动输入未知 id → 提示不存在并取消', async () => {
|
|
470
|
-
const root =
|
|
525
|
+
const root = createProjectFixture({ name: 'harness-retire-test' });
|
|
471
526
|
const healthy = getEffectiveConstraints(root)
|
|
472
527
|
.filter(c => c.kind === 'check')
|
|
473
528
|
.flatMap(c => [{ constraintId: c.id, result: 'pass' as const }]);
|
|
474
|
-
|
|
529
|
+
writeProjectTraces(root, healthy);
|
|
475
530
|
|
|
476
|
-
const
|
|
477
|
-
|
|
478
|
-
|
|
531
|
+
const streams = makeIo(['ghost_id']);
|
|
532
|
+
const printed = captureLog();
|
|
533
|
+
await runRetireInteractive(root, streams);
|
|
534
|
+
streams.done();
|
|
535
|
+
printed.restore();
|
|
479
536
|
|
|
480
|
-
const output =
|
|
537
|
+
const output = printed.text() + streams.text();
|
|
481
538
|
expect(output).toContain('ghost_id');
|
|
482
539
|
expect(output).toContain('约束不存在');
|
|
483
540
|
expect(fs.existsSync(path.join(root, '.harness', 'config.yml'))).toBe(false);
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
5
|
import { contract, validateSchema } from '../contract';
|
|
6
|
+
import { captureIO, type CapturingIO } from '../../command-contract';
|
|
6
7
|
import * as fs from 'fs/promises';
|
|
7
8
|
import { ContractGate } from '../../../gates/contract';
|
|
8
9
|
|
|
@@ -34,20 +35,12 @@ const MockGate = ContractGate as jest.MockedClass<typeof ContractGate>;
|
|
|
34
35
|
const yaml = require('js-yaml');
|
|
35
36
|
|
|
36
37
|
describe('contract command', () => {
|
|
37
|
-
let
|
|
38
|
-
let exitSpy: jest.SpyInstance;
|
|
38
|
+
let io: CapturingIO;
|
|
39
39
|
|
|
40
40
|
beforeEach(() => {
|
|
41
|
-
jest.clearAllMocks();
|
|
42
|
-
consoleSpy = jest.spyOn(console, 'log').mockImplementation();
|
|
43
|
-
exitSpy = jest.spyOn(process, 'exit').mockImplementation(() => undefined as never);
|
|
44
|
-
process.exitCode = 0;
|
|
45
|
-
});
|
|
46
41
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
exitSpy.mockRestore();
|
|
50
|
-
process.exitCode = 0;
|
|
42
|
+
io = captureIO();
|
|
43
|
+
jest.clearAllMocks();
|
|
51
44
|
});
|
|
52
45
|
|
|
53
46
|
describe('contract', () => {
|
|
@@ -60,19 +53,19 @@ describe('contract command', () => {
|
|
|
60
53
|
});
|
|
61
54
|
MockGate.mockImplementation(() => ({ check: mockCheck }) as any);
|
|
62
55
|
|
|
63
|
-
await contract({});
|
|
56
|
+
const result = await contract({}, 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 exit 1 when file does not exist', async () => {
|
|
70
63
|
mockFs.access.mockRejectedValue(new Error('ENOENT'));
|
|
71
64
|
|
|
72
|
-
await contract({});
|
|
65
|
+
const result = await contract({}, io);
|
|
73
66
|
|
|
74
|
-
expect(
|
|
75
|
-
expect(
|
|
67
|
+
expect(io.outText()).toContain('契约文件不存在');
|
|
68
|
+
expect(result.kind).toBe('fail');
|
|
76
69
|
});
|
|
77
70
|
|
|
78
71
|
it('should print failure and exit 1 when check fails', async () => {
|
|
@@ -87,10 +80,10 @@ describe('contract command', () => {
|
|
|
87
80
|
});
|
|
88
81
|
MockGate.mockImplementation(() => ({ check: mockCheck }) as any);
|
|
89
82
|
|
|
90
|
-
await contract({});
|
|
83
|
+
const result = await contract({}, io);
|
|
91
84
|
|
|
92
|
-
expect(
|
|
93
|
-
expect(
|
|
85
|
+
expect(io.outText()).toContain('契约门控检查失败');
|
|
86
|
+
expect(result.kind).toBe('fail');
|
|
94
87
|
});
|
|
95
88
|
|
|
96
89
|
it('should handle thrown errors and exit 1', async () => {
|
|
@@ -98,10 +91,10 @@ describe('contract command', () => {
|
|
|
98
91
|
const mockCheck = jest.fn().mockRejectedValue(new Error('gate error'));
|
|
99
92
|
MockGate.mockImplementation(() => ({ check: mockCheck }) as any);
|
|
100
93
|
|
|
101
|
-
await contract({});
|
|
94
|
+
const result = await contract({}, io);
|
|
102
95
|
|
|
103
|
-
expect(
|
|
104
|
-
expect(
|
|
96
|
+
expect(io.outText()).toContain('契约门控检查出错');
|
|
97
|
+
expect(result.kind).toBe('fail');
|
|
105
98
|
});
|
|
106
99
|
});
|
|
107
100
|
|
|
@@ -114,38 +107,38 @@ describe('contract command', () => {
|
|
|
114
107
|
paths: { '/users': {} },
|
|
115
108
|
});
|
|
116
109
|
|
|
117
|
-
await validateSchema({});
|
|
110
|
+
await validateSchema({}, io);
|
|
118
111
|
|
|
119
|
-
expect(
|
|
112
|
+
expect(io.outText()).toContain('Schema 验证通过');
|
|
120
113
|
});
|
|
121
114
|
|
|
122
115
|
it('should report missing fields', async () => {
|
|
123
116
|
mockFs.readFile.mockResolvedValue('{}');
|
|
124
117
|
yaml.load.mockReturnValue({});
|
|
125
118
|
|
|
126
|
-
await validateSchema({});
|
|
119
|
+
const result = await validateSchema({}, io);
|
|
127
120
|
|
|
128
|
-
expect(
|
|
129
|
-
expect(
|
|
121
|
+
expect(io.outText()).toContain('Schema 验证失败');
|
|
122
|
+
expect(result.kind).toBe('fail');
|
|
130
123
|
});
|
|
131
124
|
|
|
132
125
|
it('should report YAML parse errors', async () => {
|
|
133
126
|
mockFs.readFile.mockResolvedValue('invalid: yaml: content:');
|
|
134
127
|
yaml.load.mockImplementation(() => { throw new Error('parse error'); });
|
|
135
128
|
|
|
136
|
-
await validateSchema({});
|
|
129
|
+
const result = await validateSchema({}, io);
|
|
137
130
|
|
|
138
|
-
expect(
|
|
139
|
-
expect(
|
|
131
|
+
expect(io.outText()).toContain('YAML 解析错误');
|
|
132
|
+
expect(result.kind).toBe('fail');
|
|
140
133
|
});
|
|
141
134
|
|
|
142
135
|
it('should handle file read errors', async () => {
|
|
143
136
|
mockFs.readFile.mockRejectedValue(new Error('ENOENT'));
|
|
144
137
|
|
|
145
|
-
await validateSchema({});
|
|
138
|
+
const result = await validateSchema({}, io);
|
|
146
139
|
|
|
147
|
-
expect(
|
|
148
|
-
expect(
|
|
140
|
+
expect(io.outText()).toContain('验证失败');
|
|
141
|
+
expect(result.kind).toBe('fail');
|
|
149
142
|
});
|
|
150
143
|
});
|
|
151
144
|
});
|