@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
|
@@ -5,9 +5,9 @@
|
|
|
5
5
|
import * as fs from 'fs/promises';
|
|
6
6
|
import { existsSync } from 'fs';
|
|
7
7
|
import * as path from 'path';
|
|
8
|
-
import { loadRawProjectConfig } from '../../../core/project-config-loader';
|
|
8
|
+
import { loadRawProjectConfig, resolveContextFiles } from '../../../core/project-config-loader';
|
|
9
9
|
import { detectSourceRoots } from '../../../utils/detect-source-roots';
|
|
10
|
-
import { findTsSourceFiles, isTsSourceFile } from '../../../utils/file-walk';
|
|
10
|
+
import { DEFAULT_SKIP_DIRS, findTsSourceFiles, isTsSourceFile } from '../../../utils/file-walk';
|
|
11
11
|
|
|
12
12
|
export interface ModuleInfo {
|
|
13
13
|
name: string;
|
|
@@ -65,28 +65,27 @@ export function formatScriptCommand(pm: 'pnpm' | 'yarn' | 'npm', name: string):
|
|
|
65
65
|
|
|
66
66
|
/**
|
|
67
67
|
* 获取需要 CONTEXT.md 的目录列表
|
|
68
|
+
*
|
|
69
|
+
* context_files 三态语义(工单 84):enabled 且非空 → 配置目录;
|
|
70
|
+
* enabled 但无目标 → 工具流兜底回落自动探测(回落保留在调用方,不进访问器);
|
|
71
|
+
* 未配置 → 空列表。
|
|
68
72
|
*/
|
|
69
73
|
export async function getRequiredContextDirs(projectPath: string): Promise<string[]> {
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
}
|
|
78
|
-
// Fallback: auto-discover from source roots
|
|
79
|
-
return detectSourceRoots(projectPath);
|
|
80
|
-
}
|
|
81
|
-
} catch {
|
|
82
|
-
// 配置不存在或无法解析
|
|
74
|
+
const resolution = resolveContextFiles(projectPath);
|
|
75
|
+
if (resolution.state === 'enabled') {
|
|
76
|
+
return resolution.dirs;
|
|
77
|
+
}
|
|
78
|
+
if (resolution.state === 'enabled-empty') {
|
|
79
|
+
// Fallback: auto-discover from source roots
|
|
80
|
+
return detectSourceRoots(projectPath);
|
|
83
81
|
}
|
|
84
82
|
return [];
|
|
85
83
|
}
|
|
86
84
|
|
|
87
85
|
/**
|
|
88
86
|
* 获取源码扫描目录列表
|
|
89
|
-
* 优先从 governance.context_files.required_dirs
|
|
87
|
+
* 优先从 governance.context_files.required_dirs 读取;未配置或配置为空时
|
|
88
|
+
* 回落自动探测(detectSourceRoots),并非固定 ['src']
|
|
90
89
|
*/
|
|
91
90
|
export async function getSourceDirs(projectPath: string): Promise<string[]> {
|
|
92
91
|
const requiredDirs = await getRequiredContextDirs(projectPath);
|
|
@@ -108,8 +107,8 @@ export async function scanSourceModules(srcDir: string, projectPath: string): Pr
|
|
|
108
107
|
}
|
|
109
108
|
|
|
110
109
|
for (const entry of entries) {
|
|
111
|
-
//
|
|
112
|
-
if (entry
|
|
110
|
+
// 跳过依赖/旁测/构建产物目录(名单正本见 utils/file-walk)
|
|
111
|
+
if (DEFAULT_SKIP_DIRS.includes(entry)) continue;
|
|
113
112
|
|
|
114
113
|
const entryPath = path.join(srcDir, entry);
|
|
115
114
|
const stat = await fs.stat(entryPath);
|
|
@@ -22,10 +22,12 @@ import * as path from 'path';
|
|
|
22
22
|
import * as os from 'os';
|
|
23
23
|
import {
|
|
24
24
|
readTranscriptSessions,
|
|
25
|
+
type TranscriptFilter,
|
|
25
26
|
extractCorrectionMatches,
|
|
26
27
|
cleanCorrectionConcept,
|
|
27
28
|
jaccardChinese,
|
|
28
29
|
} from '../session-mining';
|
|
30
|
+
import { log, processIO, type CommandIO, type CommandResult } from '../command-contract';
|
|
29
31
|
|
|
30
32
|
export interface UpdateUserModelOptions {
|
|
31
33
|
days?: number; // 只处理最近 N 天(自然日,含今天)的会话;缺省不过滤(向后兼容)
|
|
@@ -51,7 +53,7 @@ interface ModelState {
|
|
|
51
53
|
const STATE_FILE = path.join(os.homedir(), '.claude', 'user-model-state.json');
|
|
52
54
|
const PROFILE_FILE = path.join(os.homedir(), '.claude', 'projects', '-root-projects', 'memory', 'user_profile.md');
|
|
53
55
|
|
|
54
|
-
export async function updateUserModel(options: UpdateUserModelOptions): Promise<
|
|
56
|
+
export async function updateUserModel(options: UpdateUserModelOptions, io: CommandIO = processIO): Promise<CommandResult> {
|
|
55
57
|
const transcriptDir = process.env.CLAUDE_TRANSCRIPTS_DIR
|
|
56
58
|
|| path.join(os.homedir(), '.claude', 'projects', '-root--claude');
|
|
57
59
|
|
|
@@ -61,8 +63,8 @@ export async function updateUserModel(options: UpdateUserModelOptions): Promise<
|
|
|
61
63
|
// 2. Scan new data
|
|
62
64
|
const newSessions = findNewSessions(transcriptDir, state.sessionsProcessed, options.days);
|
|
63
65
|
if (newSessions.length === 0) {
|
|
64
|
-
|
|
65
|
-
return;
|
|
66
|
+
log(io, chalk.gray('No new sessions to process'));
|
|
67
|
+
return { kind: 'skip', reason: '没有新会话可处理' };
|
|
66
68
|
}
|
|
67
69
|
|
|
68
70
|
// 3. Extract signals from new data
|
|
@@ -94,12 +96,13 @@ export async function updateUserModel(options: UpdateUserModelOptions): Promise<
|
|
|
94
96
|
|
|
95
97
|
// 6. Output
|
|
96
98
|
if (options.json) {
|
|
97
|
-
|
|
98
|
-
return;
|
|
99
|
+
log(io, JSON.stringify({ newSessions: newSessions.length, changes }, null, 2));
|
|
100
|
+
return { kind: 'ok' };
|
|
99
101
|
}
|
|
100
102
|
|
|
101
|
-
|
|
102
|
-
printChanges(changes, signals);
|
|
103
|
+
log(io, chalk.blue(`📊 Processed ${newSessions.length} new sessions\n`));
|
|
104
|
+
printChanges(changes, signals, io);
|
|
105
|
+
return { kind: 'ok' };
|
|
103
106
|
}
|
|
104
107
|
|
|
105
108
|
// ── State I/O ──
|
|
@@ -139,17 +142,20 @@ interface SimpleSession {
|
|
|
139
142
|
}
|
|
140
143
|
|
|
141
144
|
function findNewSessions(dir: string, processed: string[], days?: number): SimpleSession[] {
|
|
142
|
-
|
|
143
|
-
|
|
145
|
+
// 过滤下推 seam(harness#112):excludeIds 走文件名、since 走 stat,未命中不 parse
|
|
146
|
+
const filter: TranscriptFilter = { excludeIds: processed };
|
|
144
147
|
|
|
145
148
|
// 「最近 N 天」:自然日窗口(含今天)。days<=0 视为不过滤,与缺省一致。
|
|
149
|
+
// date 是 mtime 的 UTC 日,date >= cutoff ⟺ mtimeMs >= cutoff 日 UTC 零点
|
|
146
150
|
if (days !== undefined && days > 0) {
|
|
147
151
|
const cutoff = new Date(Date.now() - (days - 1) * 86_400_000)
|
|
148
152
|
.toISOString()
|
|
149
153
|
.slice(0, 10);
|
|
150
|
-
|
|
154
|
+
filter.since = Date.parse(cutoff);
|
|
151
155
|
}
|
|
152
156
|
|
|
157
|
+
const sessions = readTranscriptSessions(dir, filter);
|
|
158
|
+
|
|
153
159
|
sessions.sort((a, b) => a.date.localeCompare(b.date));
|
|
154
160
|
|
|
155
161
|
return sessions.map(s => ({
|
|
@@ -329,6 +335,8 @@ function applySignals(state: ModelState, signals: SessionSignals[], mergedConcep
|
|
|
329
335
|
const now = new Date().toISOString().slice(0, 10);
|
|
330
336
|
|
|
331
337
|
// Update patterns from merged concept clusters
|
|
338
|
+
// occurrences 唯一来源:mergedConcepts 聚合值(含 correction phrase 的 ×3 加权,
|
|
339
|
+
// 见 buildMergedConcepts)。不再叠加 per-session 原始 count —— 那是 #113 裁决的双计 bug。
|
|
332
340
|
for (const [concept, agg] of Object.entries(mergedConcepts)) {
|
|
333
341
|
if (!state.patterns[concept]) {
|
|
334
342
|
state.patterns[concept] = {
|
|
@@ -344,26 +352,6 @@ function applySignals(state: ModelState, signals: SessionSignals[], mergedConcep
|
|
|
344
352
|
p.trend = p.occurrences >= 5 ? 'stable' : 'rising';
|
|
345
353
|
}
|
|
346
354
|
|
|
347
|
-
// Also track per-session concepts
|
|
348
|
-
for (const sig of signals) {
|
|
349
|
-
for (const [concept, count] of Object.entries(sig.concepts)) {
|
|
350
|
-
if (!state.patterns[concept]) {
|
|
351
|
-
state.patterns[concept] = {
|
|
352
|
-
firstSeen: now,
|
|
353
|
-
occurrences: 0,
|
|
354
|
-
sessions: [],
|
|
355
|
-
trend: 'new',
|
|
356
|
-
lastSeen: now,
|
|
357
|
-
};
|
|
358
|
-
}
|
|
359
|
-
const p = state.patterns[concept];
|
|
360
|
-
p.occurrences += count;
|
|
361
|
-
if (!p.sessions.includes(sig.sessionId)) p.sessions.push(sig.sessionId);
|
|
362
|
-
p.lastSeen = now;
|
|
363
|
-
p.trend = p.occurrences >= 5 ? 'stable' : 'rising';
|
|
364
|
-
}
|
|
365
|
-
}
|
|
366
|
-
|
|
367
355
|
// Update lens weights from correction signals
|
|
368
356
|
for (const sig of signals) {
|
|
369
357
|
if (sig.correctionPhrases.length > 0) {
|
|
@@ -430,9 +418,9 @@ function updateProfile(state: ModelState): void {
|
|
|
430
418
|
|
|
431
419
|
// ── Output ──
|
|
432
420
|
|
|
433
|
-
function printChanges(changes: Change[], signals: SessionSignals[]): void {
|
|
421
|
+
function printChanges(changes: Change[], signals: SessionSignals[], io: CommandIO): void {
|
|
434
422
|
if (changes.length === 0) {
|
|
435
|
-
|
|
423
|
+
log(io, chalk.green('No significant changes detected'));
|
|
436
424
|
return;
|
|
437
425
|
}
|
|
438
426
|
|
|
@@ -443,31 +431,31 @@ function printChanges(changes: Change[], signals: SessionSignals[]): void {
|
|
|
443
431
|
};
|
|
444
432
|
|
|
445
433
|
if (byType.new_pattern.length > 0) {
|
|
446
|
-
|
|
434
|
+
log(io, chalk.yellow(`🌱 New patterns: ${byType.new_pattern.length}`));
|
|
447
435
|
for (const c of byType.new_pattern.slice(0, 5)) {
|
|
448
|
-
|
|
436
|
+
log(io, chalk.gray(` ${c.key}: ${c.detail}`));
|
|
449
437
|
}
|
|
450
|
-
|
|
438
|
+
log(io);
|
|
451
439
|
}
|
|
452
440
|
|
|
453
441
|
if (byType.rising.length > 0) {
|
|
454
|
-
|
|
442
|
+
log(io, chalk.green(`📈 Re-emerging: ${byType.rising.length}`));
|
|
455
443
|
for (const c of byType.rising.slice(0, 5)) {
|
|
456
|
-
|
|
444
|
+
log(io, chalk.gray(` ${c.key}: ${c.detail}`));
|
|
457
445
|
}
|
|
458
|
-
|
|
446
|
+
log(io);
|
|
459
447
|
}
|
|
460
448
|
|
|
461
449
|
if (byType.lens_shift.length > 0) {
|
|
462
|
-
|
|
450
|
+
log(io, chalk.cyan(`🎯 Lens shifts:`));
|
|
463
451
|
for (const c of byType.lens_shift) {
|
|
464
|
-
|
|
452
|
+
log(io, chalk.gray(` ${c.key}: ${c.detail}`));
|
|
465
453
|
}
|
|
466
|
-
|
|
454
|
+
log(io);
|
|
467
455
|
}
|
|
468
456
|
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
457
|
+
log(io, chalk.bold(`Total signals processed:`));
|
|
458
|
+
log(io, ` Sessions: ${signals.length}`);
|
|
459
|
+
log(io, ` Correction phrases: ${signals.reduce((s, sig) => s + sig.correctionPhrases.length, 0)}`);
|
|
460
|
+
log(io, ` Concepts extracted: ${signals.reduce((s, sig) => s + Object.keys(sig.concepts).length, 0)}`);
|
|
473
461
|
}
|
|
@@ -10,6 +10,7 @@ import * as path from 'path';
|
|
|
10
10
|
import * as yaml from 'js-yaml';
|
|
11
11
|
import { CheckpointValidator } from '../../core/validators/checkpoint';
|
|
12
12
|
import type { Checkpoint, CheckpointContext } from '../../types/checkpoint';
|
|
13
|
+
import { log, processIO, type CommandIO, type CommandResult } from '../command-contract';
|
|
13
14
|
|
|
14
15
|
export interface ValidateOptions {
|
|
15
16
|
/** 检查点文件路径 */
|
|
@@ -28,13 +29,13 @@ const DEFAULT_CHECKPOINT_FILE = '.harness/checkpoints.yml';
|
|
|
28
29
|
/**
|
|
29
30
|
* 加载检查点配置
|
|
30
31
|
*/
|
|
31
|
-
async function loadCheckpoints(filePath: string): Promise<Checkpoint[]> {
|
|
32
|
+
async function loadCheckpoints(filePath: string, io: CommandIO): Promise<Checkpoint[]> {
|
|
32
33
|
try {
|
|
33
34
|
const content = await fs.readFile(filePath, 'utf-8');
|
|
34
35
|
const data = yaml.load(content) as { checkpoints?: Checkpoint[] };
|
|
35
36
|
return data.checkpoints || [];
|
|
36
37
|
} catch (error) {
|
|
37
|
-
|
|
38
|
+
log(io, chalk.yellow(`⚠️ 未找到检查点文件: ${filePath}`));
|
|
38
39
|
return [];
|
|
39
40
|
}
|
|
40
41
|
}
|
|
@@ -42,23 +43,26 @@ async function loadCheckpoints(filePath: string): Promise<Checkpoint[]> {
|
|
|
42
43
|
/**
|
|
43
44
|
* 执行检查点验证
|
|
44
45
|
*/
|
|
45
|
-
export async function validate(
|
|
46
|
-
|
|
46
|
+
export async function validate(
|
|
47
|
+
options: ValidateOptions,
|
|
48
|
+
io: CommandIO = processIO,
|
|
49
|
+
): Promise<CommandResult> {
|
|
50
|
+
log(io, chalk.blue('🔍 验证检查点...'));
|
|
47
51
|
|
|
48
52
|
const projectPath = options.projectPath || process.cwd();
|
|
49
53
|
const checkpointFile = options.file || path.join(projectPath, DEFAULT_CHECKPOINT_FILE);
|
|
50
54
|
|
|
51
55
|
// 加载检查点
|
|
52
|
-
const checkpoints = await loadCheckpoints(checkpointFile);
|
|
56
|
+
const checkpoints = await loadCheckpoints(checkpointFile, io);
|
|
53
57
|
|
|
54
58
|
if (checkpoints.length === 0) {
|
|
55
|
-
|
|
56
|
-
return;
|
|
59
|
+
log(io, chalk.gray('没有定义检查点,跳过验证'));
|
|
60
|
+
return { kind: 'skip', reason: `没有定义检查点: ${checkpointFile}` };
|
|
57
61
|
}
|
|
58
62
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
63
|
+
log(io, chalk.gray(`检查点文件: ${checkpointFile}`));
|
|
64
|
+
log(io, chalk.gray(`检查点数量: ${checkpoints.length}`));
|
|
65
|
+
log(io);
|
|
62
66
|
|
|
63
67
|
// 构建上下文
|
|
64
68
|
const context: CheckpointContext = {
|
|
@@ -69,20 +73,22 @@ export async function validate(options: ValidateOptions): Promise<void> {
|
|
|
69
73
|
// 执行验证
|
|
70
74
|
const validator = CheckpointValidator.getInstance();
|
|
71
75
|
const results = [];
|
|
76
|
+
const failedIds: string[] = [];
|
|
72
77
|
|
|
73
78
|
for (const checkpoint of checkpoints) {
|
|
74
79
|
const result = await validator.validate(checkpoint, context);
|
|
75
80
|
results.push(result);
|
|
76
81
|
|
|
77
82
|
if (result.passed) {
|
|
78
|
-
|
|
83
|
+
log(io, chalk.green(`✅ ${checkpoint.id}: 通过`));
|
|
79
84
|
} else {
|
|
80
|
-
|
|
85
|
+
log(io, chalk.red(`❌ ${checkpoint.id}: 失败`));
|
|
81
86
|
result.checks.forEach(check => {
|
|
82
87
|
if (!check.passed) {
|
|
83
|
-
|
|
88
|
+
log(io, chalk.red(` - ${check.checkId}: ${check.message || check.error}`));
|
|
84
89
|
}
|
|
85
90
|
});
|
|
91
|
+
failedIds.push(checkpoint.id);
|
|
86
92
|
}
|
|
87
93
|
}
|
|
88
94
|
|
|
@@ -90,16 +96,16 @@ export async function validate(options: ValidateOptions): Promise<void> {
|
|
|
90
96
|
const passed = results.filter(r => r.passed).length;
|
|
91
97
|
const failed = results.filter(r => !r.passed).length;
|
|
92
98
|
|
|
93
|
-
|
|
94
|
-
|
|
99
|
+
log(io);
|
|
100
|
+
log(io, chalk.gray(`通过: ${passed}/${results.length}`));
|
|
95
101
|
|
|
96
102
|
if (failed > 0) {
|
|
97
|
-
|
|
103
|
+
log(io, chalk.red(`\n🛑 ${failed} 个检查点未通过`));
|
|
98
104
|
// 工单 23:门控语义——检查点失败一律非零退出(此前仅 --strict 退出,钩子/CI 形同虚设)
|
|
99
|
-
|
|
100
|
-
} else {
|
|
101
|
-
console.log(chalk.green('\n✅ 所有检查点验证通过'));
|
|
105
|
+
return { kind: 'fail', reason: `${failed} 个检查点未通过: ${failedIds.join(', ')}` };
|
|
102
106
|
}
|
|
107
|
+
log(io, chalk.green('\n✅ 所有检查点验证通过'));
|
|
108
|
+
return { kind: 'ok' };
|
|
103
109
|
}
|
|
104
110
|
|
|
105
111
|
/**
|
|
@@ -138,14 +144,14 @@ export const DEFAULT_CHECKPOINTS: Checkpoint[] = [
|
|
|
138
144
|
/**
|
|
139
145
|
* 创建示例检查点文件
|
|
140
146
|
*/
|
|
141
|
-
export async function createExampleCheckpoint(projectPath: string): Promise<void> {
|
|
147
|
+
export async function createExampleCheckpoint(projectPath: string, io: CommandIO = processIO): Promise<void> {
|
|
142
148
|
const filePath = path.join(projectPath, DEFAULT_CHECKPOINT_FILE);
|
|
143
149
|
const dir = path.dirname(filePath);
|
|
144
150
|
|
|
145
151
|
// 如果已存在,不覆盖
|
|
146
152
|
try {
|
|
147
153
|
await fs.access(filePath);
|
|
148
|
-
|
|
154
|
+
log(io, chalk.gray(`checkpoints.yml 已存在,跳过`));
|
|
149
155
|
return;
|
|
150
156
|
} catch {
|
|
151
157
|
// 文件不存在,创建
|
|
@@ -156,7 +162,7 @@ export async function createExampleCheckpoint(projectPath: string): Promise<void
|
|
|
156
162
|
const content = yaml.dump({ checkpoints: DEFAULT_CHECKPOINTS }, { indent: 2 });
|
|
157
163
|
await fs.writeFile(filePath, content, 'utf-8');
|
|
158
164
|
|
|
159
|
-
|
|
165
|
+
log(io, chalk.green(`✅ 已创建示例检查点文件: ${filePath}`));
|
|
160
166
|
}
|
|
161
167
|
|
|
162
168
|
/**
|
|
@@ -180,14 +186,14 @@ const DEFAULT_RESOLUTIONS = {
|
|
|
180
186
|
/**
|
|
181
187
|
* 创建示例 Resolutions 文件(RKB dogfood)
|
|
182
188
|
*/
|
|
183
|
-
export async function createExampleResolutions(projectPath: string): Promise<void> {
|
|
189
|
+
export async function createExampleResolutions(projectPath: string, io: CommandIO = processIO): Promise<void> {
|
|
184
190
|
const harnessDir = path.join(projectPath, '.harness');
|
|
185
191
|
const filePath = path.join(harnessDir, 'resolutions.json');
|
|
186
192
|
|
|
187
193
|
// 如果已存在,不覆盖
|
|
188
194
|
try {
|
|
189
195
|
await fs.access(filePath);
|
|
190
|
-
|
|
196
|
+
log(io, chalk.gray(`resolutions.json 已存在,跳过`));
|
|
191
197
|
return;
|
|
192
198
|
} catch {
|
|
193
199
|
// 文件不存在,创建
|
|
@@ -198,5 +204,5 @@ export async function createExampleResolutions(projectPath: string): Promise<voi
|
|
|
198
204
|
const content = JSON.stringify(DEFAULT_RESOLUTIONS, null, 2);
|
|
199
205
|
await fs.writeFile(filePath, content, 'utf-8');
|
|
200
206
|
|
|
201
|
-
|
|
207
|
+
log(io, chalk.green(`✅ 已创建 Resolutions 文件: ${filePath}`));
|
|
202
208
|
}
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* readTranscriptSessions 坏行/null 记录容错测试(harness#82)
|
|
3
|
+
*
|
|
4
|
+
* transcript 行可 parse 但值为 null(或提取异常)时,
|
|
5
|
+
* 与原逐行 catch 实现一致:该条跳过,不影响其余记录。
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import * as fs from 'fs';
|
|
9
|
+
import * as os from 'os';
|
|
10
|
+
import * as path from 'path';
|
|
11
|
+
import { readTranscriptSessions } from '../transcript';
|
|
12
|
+
|
|
13
|
+
describe('readTranscriptSessions 坏行容错(harness#82)', () => {
|
|
14
|
+
let dir: string;
|
|
15
|
+
|
|
16
|
+
beforeEach(() => {
|
|
17
|
+
dir = fs.mkdtempSync(path.join(os.tmpdir(), 'harness-transcript-'));
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
afterEach(() => {
|
|
21
|
+
fs.rmSync(dir, { recursive: true, force: true });
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
it('null 行与坏行只丢该行,合法 turn 照常提取', () => {
|
|
25
|
+
const lines = [
|
|
26
|
+
JSON.stringify({ message: { role: 'user', content: 'hello' } }),
|
|
27
|
+
'null', // parse 合法但值为 null,原实现经逐行 catch 跳过
|
|
28
|
+
'{"broken',
|
|
29
|
+
JSON.stringify({ message: { role: 'assistant', content: [{ type: 'text', text: 'hi' }] } }),
|
|
30
|
+
];
|
|
31
|
+
fs.writeFileSync(path.join(dir, 'session-a.jsonl'), lines.join('\n') + '\n', 'utf-8');
|
|
32
|
+
|
|
33
|
+
const sessions = readTranscriptSessions(dir);
|
|
34
|
+
expect(sessions).toHaveLength(1);
|
|
35
|
+
expect(sessions[0].turns.map(t => t.role)).toEqual(['user', 'assistant']);
|
|
36
|
+
expect(sessions[0].turns[1].content).toBe('hi');
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
it('目录不可读返回空数组', () => {
|
|
40
|
+
expect(readTranscriptSessions(path.join(dir, 'missing'))).toEqual([]);
|
|
41
|
+
});
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
describe('readTranscriptSessions 过滤(harness#112)', () => {
|
|
45
|
+
let dir: string;
|
|
46
|
+
|
|
47
|
+
beforeEach(() => {
|
|
48
|
+
dir = fs.mkdtempSync(path.join(os.tmpdir(), 'harness-transcript-filter-'));
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
afterEach(() => {
|
|
52
|
+
fs.rmSync(dir, { recursive: true, force: true });
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
function writeSession(name: string, text: string, mtime?: Date): void {
|
|
56
|
+
const p = path.join(dir, name);
|
|
57
|
+
fs.writeFileSync(
|
|
58
|
+
p,
|
|
59
|
+
JSON.stringify({ message: { role: 'user', content: text } }) + '\n',
|
|
60
|
+
'utf-8',
|
|
61
|
+
);
|
|
62
|
+
if (mtime) fs.utimesSync(p, mtime, mtime);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
it('excludeIds:命中的会话不进结果(文件名级过滤,不 parse)', () => {
|
|
66
|
+
writeSession('session-a.jsonl', '甲会话内容');
|
|
67
|
+
writeSession('session-b.jsonl', '乙会话内容');
|
|
68
|
+
|
|
69
|
+
const sessions = readTranscriptSessions(dir, { excludeIds: ['session-a'] });
|
|
70
|
+
expect(sessions.map(s => s.id)).toEqual(['session-b']);
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
it('excludeIds 按截断 40 字符的会话 ID 匹配', () => {
|
|
74
|
+
const longName = 'a'.repeat(50);
|
|
75
|
+
writeSession(`${longName}.jsonl`, '长名会话内容');
|
|
76
|
+
writeSession('session-b.jsonl', '乙会话内容');
|
|
77
|
+
|
|
78
|
+
const sessions = readTranscriptSessions(dir, { excludeIds: [longName.slice(0, 40)] });
|
|
79
|
+
expect(sessions.map(s => s.id)).toEqual(['session-b']);
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
it('since:mtimeMs 早于窗口的文件不进结果(stat 级过滤,不 parse)', () => {
|
|
83
|
+
writeSession('old.jsonl', '旧会话内容', new Date(Date.now() - 30 * 86_400_000));
|
|
84
|
+
writeSession('recent.jsonl', '新会话内容');
|
|
85
|
+
|
|
86
|
+
const since = Date.now() - 7 * 86_400_000;
|
|
87
|
+
const sessions = readTranscriptSessions(dir, { since });
|
|
88
|
+
expect(sessions.map(s => s.id)).toEqual(['recent']);
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
it('since 边界含等于:mtimeMs == since 仍计入', () => {
|
|
92
|
+
const t = new Date('2026-08-01T12:00:00.000Z');
|
|
93
|
+
writeSession('edge.jsonl', '边界会话内容', t);
|
|
94
|
+
|
|
95
|
+
const sessions = readTranscriptSessions(dir, { since: t.getTime() });
|
|
96
|
+
expect(sessions.map(s => s.id)).toEqual(['edge']);
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
it('since 与 excludeIds 可叠加', () => {
|
|
100
|
+
writeSession('old.jsonl', '旧会话内容', new Date(Date.now() - 30 * 86_400_000));
|
|
101
|
+
writeSession('excluded.jsonl', '被排除会话');
|
|
102
|
+
writeSession('kept.jsonl', '保留会话内容');
|
|
103
|
+
|
|
104
|
+
const sessions = readTranscriptSessions(dir, {
|
|
105
|
+
since: Date.now() - 7 * 86_400_000,
|
|
106
|
+
excludeIds: ['excluded'],
|
|
107
|
+
});
|
|
108
|
+
expect(sessions.map(s => s.id)).toEqual(['kept']);
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
it('无 filter:全量读取(向后兼容)', () => {
|
|
112
|
+
writeSession('a.jsonl', '甲会话内容', new Date(Date.now() - 30 * 86_400_000));
|
|
113
|
+
writeSession('b.jsonl', '乙会话内容');
|
|
114
|
+
|
|
115
|
+
const sessions = readTranscriptSessions(dir);
|
|
116
|
+
expect(sessions).toHaveLength(2);
|
|
117
|
+
});
|
|
118
|
+
});
|
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
|
|
8
8
|
import * as fs from 'fs';
|
|
9
9
|
import * as path from 'path';
|
|
10
|
+
import { readJsonl } from '../../utils/jsonl';
|
|
10
11
|
|
|
11
12
|
export interface MinedTurn {
|
|
12
13
|
role: string;
|
|
@@ -41,8 +42,18 @@ function extractText(content: unknown): string {
|
|
|
41
42
|
*
|
|
42
43
|
* 不可读/损坏的行静默跳过(与原实现一致)。
|
|
43
44
|
*/
|
|
44
|
-
export
|
|
45
|
+
export interface TranscriptFilter {
|
|
46
|
+
/** 只读 mtimeMs >= since 的文件(stat 级过滤,命中即不 parse);缺省不过滤 */
|
|
47
|
+
since?: number;
|
|
48
|
+
/** 按会话 ID(文件名去 .jsonl、截断 40 字符)排除(文件名级过滤,不 stat 不 parse);缺省不排除 */
|
|
49
|
+
excludeIds?: string[];
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export function readTranscriptSessions(dir: string, filter: TranscriptFilter = {}): MinedSession[] {
|
|
45
53
|
const sessions: MinedSession[] = [];
|
|
54
|
+
const exclude = filter.excludeIds && filter.excludeIds.length > 0
|
|
55
|
+
? new Set(filter.excludeIds)
|
|
56
|
+
: undefined;
|
|
46
57
|
|
|
47
58
|
let files: string[];
|
|
48
59
|
try {
|
|
@@ -53,6 +64,8 @@ export function readTranscriptSessions(dir: string): MinedSession[] {
|
|
|
53
64
|
|
|
54
65
|
for (const file of files) {
|
|
55
66
|
if (!file.endsWith('.jsonl')) continue;
|
|
67
|
+
const id = file.replace('.jsonl', '').slice(0, 40);
|
|
68
|
+
if (exclude?.has(id)) continue;
|
|
56
69
|
const filePath = path.join(dir, file);
|
|
57
70
|
|
|
58
71
|
let stat: fs.Stats;
|
|
@@ -61,21 +74,26 @@ export function readTranscriptSessions(dir: string): MinedSession[] {
|
|
|
61
74
|
} catch {
|
|
62
75
|
continue;
|
|
63
76
|
}
|
|
77
|
+
if (filter.since !== undefined && stat.mtimeMs < filter.since) continue;
|
|
64
78
|
|
|
65
79
|
const turns: MinedTurn[] = [];
|
|
66
80
|
const toolCalls: string[] = [];
|
|
67
81
|
|
|
68
|
-
let
|
|
82
|
+
let records: Record<string, any>[];
|
|
69
83
|
try {
|
|
70
|
-
|
|
84
|
+
// 损坏行静默跳过(坏行策略 skip,与原实现一致,harness#82 走 jsonl 正本);
|
|
85
|
+
// 外层 catch 兜文件不可读,沿用原「跳过该文件」语义
|
|
86
|
+
// 计数去向:豁免(harness#100)——读的是外部 Claude Code transcript(非 harness 写入面)。
|
|
87
|
+
// 影响方向如实:坏行使该会话少几条 turn,而 analyze-sessions 的候选按次数阈值筛
|
|
88
|
+
// (frequency>=4 / 跨会话>=2 / confidence=len/10)→ 只会少出候选,不会造出候选,且候选须人审;
|
|
89
|
+
// 告知需改 MinedSession 形状并波及 analyze-sessions/update-user-model 两个调用方,另票收口
|
|
90
|
+
records = readJsonl<Record<string, any>>(filePath, 'skip').records;
|
|
71
91
|
} catch {
|
|
72
92
|
continue;
|
|
73
93
|
}
|
|
74
94
|
|
|
75
|
-
for (const
|
|
76
|
-
if (!line.trim()) continue;
|
|
95
|
+
for (const entry of records) {
|
|
77
96
|
try {
|
|
78
|
-
const entry = JSON.parse(line);
|
|
79
97
|
const msg = entry.message;
|
|
80
98
|
if (msg?.role && (msg.content || entry.type === 'user')) {
|
|
81
99
|
turns.push({ role: msg.role, content: extractText(msg.content) });
|
|
@@ -88,13 +106,13 @@ export function readTranscriptSessions(dir: string): MinedSession[] {
|
|
|
88
106
|
}
|
|
89
107
|
}
|
|
90
108
|
} catch {
|
|
91
|
-
//
|
|
109
|
+
// 单条记录提取异常跳过(原逐行 catch 语义:parse 已由 module skip,此处兜提取)
|
|
92
110
|
}
|
|
93
111
|
}
|
|
94
112
|
|
|
95
113
|
if (turns.length > 0) {
|
|
96
114
|
sessions.push({
|
|
97
|
-
id
|
|
115
|
+
id,
|
|
98
116
|
date: stat.mtime.toISOString().slice(0, 10),
|
|
99
117
|
mtimeMs: stat.mtimeMs,
|
|
100
118
|
turns,
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# completion-checkers/
|
|
2
2
|
|
|
3
3
|
## 职责
|
|
4
|
-
|
|
4
|
+
提交集收尾软观测三纯判定函数:tdd-chain 引用链 / phase-format subject 结构 / contract-presence 契约在场。纯函数,不碰文件系统与 git,commits 由调用方(completion-gates 类守卫)供给。
|
|
5
5
|
|
|
6
6
|
## 核心导出
|
|
7
7
|
- `verifyTddChain(commits, config)`(`tdd-chain.ts`)——实现 commit 须带 trailer `Tested-By: <sha>`(`TESTED_BY_RE` 写死);被引 sha 须在本提交集内、位置在前、文件清单命中 test_globs;`Tests: none`(`TESTS_NONE_RE`)→ waiver;纯测试/纯非代码 commit 天然免检
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* verifyTddChain
|
|
2
|
+
* verifyTddChain 测试(伪造引用三形态 / Tests: none waiver / 免检分类)
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
5
|
import { verifyTddChain } from '../tdd-chain';
|
|
@@ -34,7 +34,7 @@ describe('verifyTddChain', () => {
|
|
|
34
34
|
const commits = [commit({ sha: sha(2), body: 'Tested-By: ' + sha(9) })];
|
|
35
35
|
const result = verifyTddChain(commits);
|
|
36
36
|
expect(result.verdict).toBe('violation');
|
|
37
|
-
expect(result.commits[0].reason).toContain('
|
|
37
|
+
expect(result.commits[0].reason).toContain('不在本提交集内');
|
|
38
38
|
});
|
|
39
39
|
|
|
40
40
|
it('伪造引用:被引 commit 位置在本 commit 之后 → violation', () => {
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Completion Checkers
|
|
2
|
+
* Completion Checkers
|
|
3
3
|
*
|
|
4
|
-
*
|
|
4
|
+
* 提交集收尾软观测三件套——纯判定函数库:
|
|
5
5
|
* - verifyTddChain:实现 commit 的 Tested-By 引用链验证
|
|
6
6
|
* - verifyPhaseFormat:phase(...) subject 结构验证
|
|
7
7
|
* - verifyContractPresence:通用契约在场引擎
|
|
8
8
|
*
|
|
9
9
|
* 与 ConstraintCheck 闭环注册表无关:直接 export,不注册、不碰 checkers/index.ts。
|
|
10
|
-
* 函数不碰文件系统与 git;commits 由调用方(
|
|
10
|
+
* 函数不碰文件系统与 git;commits 由调用方(completion-gates 类守卫)供给。
|
|
11
11
|
*/
|
|
12
12
|
|
|
13
13
|
export * from './types';
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* phase-format
|
|
2
|
+
* phase-format:提交 subject 的 phase 结构验证(Q4 定稿)
|
|
3
3
|
*
|
|
4
4
|
* 协议(正则写死在函数里,不进配置):
|
|
5
5
|
* - 全部非 merge commit 的 subject 命中 `^phase\([a-z0-9-]+\):\s+\S`
|
|
@@ -32,7 +32,7 @@ export function verifyPhaseFormat(
|
|
|
32
32
|
|
|
33
33
|
const verdicts: CommitVerdict[] = commits.map((commit) => {
|
|
34
34
|
if (isMergeCommit(commit)) {
|
|
35
|
-
return { sha: commit.sha, verdict: 'violation', reason: '
|
|
35
|
+
return { sha: commit.sha, verdict: 'violation', reason: '提交集内出现 merge commit' };
|
|
36
36
|
}
|
|
37
37
|
if (!PHASE_SUBJECT_RE.test(commit.subject)) {
|
|
38
38
|
return { sha: commit.sha, verdict: 'violation', reason: `subject 不合规:${commit.subject}` };
|