@dommaker/harness 1.3.0 → 1.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +8 -0
- package/README.md +0 -2
- package/bin/harness.js +86 -32
- package/dist/cli/command-contract.d.ts +83 -0
- package/dist/cli/command-contract.d.ts.map +1 -0
- package/dist/cli/command-contract.js +66 -0
- package/dist/cli/command-contract.js.map +1 -0
- package/dist/cli/commands/acceptance.d.ts +3 -2
- package/dist/cli/commands/acceptance.d.ts.map +1 -1
- package/dist/cli/commands/acceptance.js +28 -24
- package/dist/cli/commands/acceptance.js.map +1 -1
- package/dist/cli/commands/analyze-sessions.d.ts +2 -1
- package/dist/cli/commands/analyze-sessions.d.ts.map +1 -1
- package/dist/cli/commands/analyze-sessions.js +25 -24
- package/dist/cli/commands/analyze-sessions.js.map +1 -1
- package/dist/cli/commands/check.d.ts +14 -2
- package/dist/cli/commands/check.d.ts.map +1 -1
- package/dist/cli/commands/check.js +76 -61
- package/dist/cli/commands/check.js.map +1 -1
- package/dist/cli/commands/command.d.ts +5 -1
- package/dist/cli/commands/command.d.ts.map +1 -1
- package/dist/cli/commands/command.js +26 -17
- package/dist/cli/commands/command.js.map +1 -1
- package/dist/cli/commands/constraints-report.d.ts +2 -1
- package/dist/cli/commands/constraints-report.d.ts.map +1 -1
- package/dist/cli/commands/constraints-report.js +37 -33
- package/dist/cli/commands/constraints-report.js.map +1 -1
- package/dist/cli/commands/constraints-retire.d.ts +6 -5
- package/dist/cli/commands/constraints-retire.d.ts.map +1 -1
- package/dist/cli/commands/constraints-retire.js +50 -52
- package/dist/cli/commands/constraints-retire.js.map +1 -1
- package/dist/cli/commands/constraints.d.ts +2 -1
- package/dist/cli/commands/constraints.d.ts.map +1 -1
- package/dist/cli/commands/constraints.js +10 -27
- package/dist/cli/commands/constraints.js.map +1 -1
- package/dist/cli/commands/contract.d.ts +3 -2
- package/dist/cli/commands/contract.d.ts.map +1 -1
- package/dist/cli/commands/contract.js +45 -42
- package/dist/cli/commands/contract.js.map +1 -1
- package/dist/cli/commands/definitions.d.ts +11 -11
- package/dist/cli/commands/definitions.d.ts.map +1 -1
- package/dist/cli/commands/definitions.js +29 -214
- package/dist/cli/commands/definitions.js.map +1 -1
- package/dist/cli/commands/failure.d.ts +5 -4
- package/dist/cli/commands/failure.d.ts.map +1 -1
- package/dist/cli/commands/failure.js +31 -25
- package/dist/cli/commands/failure.js.map +1 -1
- package/dist/cli/commands/init.d.ts +7 -5
- package/dist/cli/commands/init.d.ts.map +1 -1
- package/dist/cli/commands/init.js +147 -211
- package/dist/cli/commands/init.js.map +1 -1
- package/dist/cli/commands/knowledge.d.ts +21 -15
- package/dist/cli/commands/knowledge.d.ts.map +1 -1
- package/dist/cli/commands/knowledge.js +214 -199
- package/dist/cli/commands/knowledge.js.map +1 -1
- package/dist/cli/commands/passes-gate.d.ts +8 -2
- package/dist/cli/commands/passes-gate.d.ts.map +1 -1
- package/dist/cli/commands/passes-gate.js +44 -66
- package/dist/cli/commands/passes-gate.js.map +1 -1
- package/dist/cli/commands/performance.d.ts +2 -1
- package/dist/cli/commands/performance.d.ts.map +1 -1
- package/dist/cli/commands/performance.js +20 -18
- package/dist/cli/commands/performance.js.map +1 -1
- package/dist/cli/commands/posteval-plan.d.ts +5 -1
- package/dist/cli/commands/posteval-plan.d.ts.map +1 -1
- package/dist/cli/commands/posteval-plan.js +26 -15
- package/dist/cli/commands/posteval-plan.js.map +1 -1
- package/dist/cli/commands/release.d.ts +6 -1
- package/dist/cli/commands/release.d.ts.map +1 -1
- package/dist/cli/commands/release.js +69 -59
- package/dist/cli/commands/release.js.map +1 -1
- package/dist/cli/commands/report.d.ts +2 -1
- package/dist/cli/commands/report.d.ts.map +1 -1
- package/dist/cli/commands/report.js +9 -5
- package/dist/cli/commands/report.js.map +1 -1
- package/dist/cli/commands/review.d.ts +3 -2
- package/dist/cli/commands/review.d.ts.map +1 -1
- package/dist/cli/commands/review.js +33 -29
- package/dist/cli/commands/review.js.map +1 -1
- package/dist/cli/commands/sdd.d.ts +2 -1
- package/dist/cli/commands/sdd.d.ts.map +1 -1
- package/dist/cli/commands/sdd.js +7 -5
- package/dist/cli/commands/sdd.js.map +1 -1
- package/dist/cli/commands/security.d.ts +3 -2
- package/dist/cli/commands/security.d.ts.map +1 -1
- package/dist/cli/commands/security.js +29 -26
- package/dist/cli/commands/security.js.map +1 -1
- package/dist/cli/commands/spec-baseline-check.d.ts +2 -1
- package/dist/cli/commands/spec-baseline-check.d.ts.map +1 -1
- package/dist/cli/commands/spec-baseline-check.js +15 -11
- package/dist/cli/commands/spec-baseline-check.js.map +1 -1
- package/dist/cli/commands/spec.d.ts +7 -2
- package/dist/cli/commands/spec.d.ts.map +1 -1
- package/dist/cli/commands/spec.js +61 -51
- package/dist/cli/commands/spec.js.map +1 -1
- package/dist/cli/commands/status.d.ts +2 -1
- package/dist/cli/commands/status.d.ts.map +1 -1
- package/dist/cli/commands/status.js +57 -53
- package/dist/cli/commands/status.js.map +1 -1
- package/dist/cli/commands/sync-docs/agents-syncer.d.ts.map +1 -1
- package/dist/cli/commands/sync-docs/agents-syncer.js +5 -2
- package/dist/cli/commands/sync-docs/agents-syncer.js.map +1 -1
- package/dist/cli/commands/sync-docs/context-syncer.d.ts.map +1 -1
- package/dist/cli/commands/sync-docs/context-syncer.js +3 -2
- package/dist/cli/commands/sync-docs/context-syncer.js.map +1 -1
- package/dist/cli/commands/sync-docs/index.d.ts +2 -1
- package/dist/cli/commands/sync-docs/index.d.ts.map +1 -1
- package/dist/cli/commands/sync-docs/index.js +68 -88
- package/dist/cli/commands/sync-docs/index.js.map +1 -1
- package/dist/cli/commands/sync-docs/project-reader.d.ts +6 -1
- package/dist/cli/commands/sync-docs/project-reader.d.ts.map +1 -1
- package/dist/cli/commands/sync-docs/project-reader.js +14 -16
- package/dist/cli/commands/sync-docs/project-reader.js.map +1 -1
- package/dist/cli/commands/update-user-model.d.ts +2 -1
- package/dist/cli/commands/update-user-model.d.ts.map +1 -1
- package/dist/cli/commands/update-user-model.js +31 -45
- package/dist/cli/commands/update-user-model.js.map +1 -1
- package/dist/cli/commands/validate.d.ts +4 -3
- package/dist/cli/commands/validate.d.ts.map +1 -1
- package/dist/cli/commands/validate.js +28 -26
- package/dist/cli/commands/validate.js.map +1 -1
- package/dist/cli/session-mining/transcript.d.ts +7 -1
- package/dist/cli/session-mining/transcript.d.ts.map +1 -1
- package/dist/cli/session-mining/transcript.js +21 -14
- package/dist/cli/session-mining/transcript.js.map +1 -1
- package/dist/context/session-manager.d.ts.map +1 -1
- package/dist/context/session-manager.js +19 -32
- package/dist/context/session-manager.js.map +1 -1
- package/dist/core/constraints/agent-prompt-renderer.d.ts.map +1 -1
- package/dist/core/constraints/agent-prompt-renderer.js +1 -4
- package/dist/core/constraints/agent-prompt-renderer.js.map +1 -1
- package/dist/core/constraints/capabilities-parser.d.ts +24 -7
- package/dist/core/constraints/capabilities-parser.d.ts.map +1 -1
- package/dist/core/constraints/capabilities-parser.js +32 -28
- package/dist/core/constraints/capabilities-parser.js.map +1 -1
- package/dist/core/constraints/capabilities-reconcile.d.ts +67 -0
- package/dist/core/constraints/capabilities-reconcile.d.ts.map +1 -0
- package/dist/core/constraints/capabilities-reconcile.js +105 -0
- package/dist/core/constraints/capabilities-reconcile.js.map +1 -0
- package/dist/core/constraints/checker.d.ts +21 -43
- package/dist/core/constraints/checker.d.ts.map +1 -1
- package/dist/core/constraints/checker.js +64 -115
- package/dist/core/constraints/checker.js.map +1 -1
- package/dist/core/constraints/checkers/capability-sync.d.ts +4 -3
- package/dist/core/constraints/checkers/capability-sync.d.ts.map +1 -1
- package/dist/core/constraints/checkers/capability-sync.js +29 -69
- package/dist/core/constraints/checkers/capability-sync.js.map +1 -1
- package/dist/core/constraints/checkers/context-doc-sync.d.ts +4 -2
- package/dist/core/constraints/checkers/context-doc-sync.d.ts.map +1 -1
- package/dist/core/constraints/checkers/context-doc-sync.js +13 -22
- package/dist/core/constraints/checkers/context-doc-sync.js.map +1 -1
- package/dist/core/constraints/checkers/docs-freshness.d.ts.map +1 -1
- package/dist/core/constraints/checkers/docs-freshness.js +37 -63
- package/dist/core/constraints/checkers/docs-freshness.js.map +1 -1
- package/dist/core/constraints/checkers/governance-presence.d.ts +2 -2
- package/dist/core/constraints/checkers/governance-presence.d.ts.map +1 -1
- package/dist/core/constraints/checkers/governance-presence.js +7 -28
- package/dist/core/constraints/checkers/governance-presence.js.map +1 -1
- package/dist/core/constraints/checkers/no-test-simplification.d.ts.map +1 -1
- package/dist/core/constraints/checkers/no-test-simplification.js +4 -3
- package/dist/core/constraints/checkers/no-test-simplification.js.map +1 -1
- package/dist/core/constraints/checkers/types.d.ts +2 -2
- package/dist/core/constraints/context-builder.d.ts +15 -14
- package/dist/core/constraints/context-builder.d.ts.map +1 -1
- package/dist/core/constraints/context-builder.js +32 -96
- package/dist/core/constraints/context-builder.js.map +1 -1
- package/dist/core/constraints/doc-freshness/runner.d.ts.map +1 -1
- package/dist/core/constraints/doc-freshness/runner.js +2 -1
- package/dist/core/constraints/doc-freshness/runner.js.map +1 -1
- package/dist/core/constraints/git-evidence.d.ts +60 -0
- package/dist/core/constraints/git-evidence.d.ts.map +1 -0
- package/dist/core/constraints/git-evidence.js +88 -0
- package/dist/core/constraints/git-evidence.js.map +1 -0
- package/dist/core/constraints/injection-drift.d.ts +3 -20
- package/dist/core/constraints/injection-drift.d.ts.map +1 -1
- package/dist/core/constraints/injection-drift.js +10 -57
- package/dist/core/constraints/injection-drift.js.map +1 -1
- package/dist/core/constraints/injection-writer.d.ts +113 -0
- package/dist/core/constraints/injection-writer.d.ts.map +1 -0
- package/dist/core/constraints/injection-writer.js +228 -0
- package/dist/core/constraints/injection-writer.js.map +1 -0
- package/dist/core/constraints/usage-report.d.ts +22 -1
- package/dist/core/constraints/usage-report.d.ts.map +1 -1
- package/dist/core/constraints/usage-report.js +27 -20
- package/dist/core/constraints/usage-report.js.map +1 -1
- package/dist/core/index.d.ts +2 -2
- package/dist/core/index.d.ts.map +1 -1
- package/dist/core/index.js +4 -2
- package/dist/core/index.js.map +1 -1
- package/dist/core/project-config-loader.d.ts +32 -1
- package/dist/core/project-config-loader.d.ts.map +1 -1
- package/dist/core/project-config-loader.js +39 -9
- package/dist/core/project-config-loader.js.map +1 -1
- package/dist/core/validators/passes-gate.d.ts +15 -64
- package/dist/core/validators/passes-gate.d.ts.map +1 -1
- package/dist/core/validators/passes-gate.js +84 -222
- package/dist/core/validators/passes-gate.js.map +1 -1
- package/dist/core/validators/test-output.d.ts +58 -0
- package/dist/core/validators/test-output.d.ts.map +1 -0
- package/dist/core/validators/test-output.js +109 -0
- package/dist/core/validators/test-output.js.map +1 -0
- package/dist/failure/recorder.d.ts +6 -0
- package/dist/failure/recorder.d.ts.map +1 -1
- package/dist/failure/recorder.js +12 -8
- package/dist/failure/recorder.js.map +1 -1
- package/dist/gates/acceptance.d.ts +1 -48
- package/dist/gates/acceptance.d.ts.map +1 -1
- package/dist/gates/acceptance.js +6 -38
- package/dist/gates/acceptance.js.map +1 -1
- package/dist/gates/checker-gate.d.ts.map +1 -1
- package/dist/gates/checker-gate.js +6 -11
- package/dist/gates/checker-gate.js.map +1 -1
- package/dist/gates/command.d.ts.map +1 -1
- package/dist/gates/command.js +10 -22
- package/dist/gates/command.js.map +1 -1
- package/dist/gates/contract.d.ts.map +1 -1
- package/dist/gates/contract.js +22 -61
- package/dist/gates/contract.js.map +1 -1
- package/dist/gates/performance.d.ts.map +1 -1
- package/dist/gates/performance.js +13 -31
- package/dist/gates/performance.js.map +1 -1
- package/dist/gates/review.d.ts.map +1 -1
- package/dist/gates/review.js +21 -53
- package/dist/gates/review.js.map +1 -1
- package/dist/gates/security.d.ts.map +1 -1
- package/dist/gates/security.js +14 -32
- package/dist/gates/security.js.map +1 -1
- package/dist/gates/types.d.ts +28 -5
- package/dist/gates/types.d.ts.map +1 -1
- package/dist/gates/types.js +38 -1
- package/dist/gates/types.js.map +1 -1
- package/dist/hooks/bootstrap.d.ts +2 -0
- package/dist/hooks/bootstrap.d.ts.map +1 -1
- package/dist/hooks/bootstrap.js +5 -2
- package/dist/hooks/bootstrap.js.map +1 -1
- package/dist/index.d.ts +1 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js.map +1 -1
- package/dist/knowledge/audit.d.ts +7 -2
- package/dist/knowledge/audit.d.ts.map +1 -1
- package/dist/knowledge/audit.js +59 -79
- package/dist/knowledge/audit.js.map +1 -1
- package/dist/knowledge/flywheel-metrics.d.ts +39 -0
- package/dist/knowledge/flywheel-metrics.d.ts.map +1 -0
- package/dist/knowledge/flywheel-metrics.js +47 -0
- package/dist/knowledge/flywheel-metrics.js.map +1 -0
- package/dist/knowledge/import.d.ts.map +1 -1
- package/dist/knowledge/import.js +4 -14
- package/dist/knowledge/import.js.map +1 -1
- package/dist/knowledge/index-generator.d.ts.map +1 -1
- package/dist/knowledge/index-generator.js +17 -14
- package/dist/knowledge/index-generator.js.map +1 -1
- package/dist/knowledge/ingest.d.ts.map +1 -1
- package/dist/knowledge/ingest.js +9 -4
- package/dist/knowledge/ingest.js.map +1 -1
- package/dist/knowledge/migration.d.ts.map +1 -1
- package/dist/knowledge/migration.js +11 -8
- package/dist/knowledge/migration.js.map +1 -1
- package/dist/knowledge/reference-tracker.d.ts.map +1 -1
- package/dist/knowledge/reference-tracker.js +12 -9
- package/dist/knowledge/reference-tracker.js.map +1 -1
- package/dist/knowledge/store.d.ts +15 -0
- package/dist/knowledge/store.d.ts.map +1 -1
- package/dist/knowledge/store.js +59 -10
- package/dist/knowledge/store.js.map +1 -1
- package/dist/monitoring/context-tracker.d.ts.map +1 -1
- package/dist/monitoring/context-tracker.js +11 -20
- package/dist/monitoring/context-tracker.js.map +1 -1
- package/dist/monitoring/index.d.ts +1 -1
- package/dist/monitoring/index.d.ts.map +1 -1
- package/dist/monitoring/index.js +1 -2
- package/dist/monitoring/index.js.map +1 -1
- package/dist/monitoring/trace-analyzer.d.ts +15 -0
- package/dist/monitoring/trace-analyzer.d.ts.map +1 -1
- package/dist/monitoring/trace-analyzer.js +19 -2
- package/dist/monitoring/trace-analyzer.js.map +1 -1
- package/dist/monitoring/traces.d.ts +21 -2
- package/dist/monitoring/traces.d.ts.map +1 -1
- package/dist/monitoring/traces.js +38 -29
- package/dist/monitoring/traces.js.map +1 -1
- package/dist/release/index.d.ts +2 -2
- package/dist/release/index.d.ts.map +1 -1
- package/dist/release/index.js +1 -5
- package/dist/release/index.js.map +1 -1
- package/dist/sdd/index-generator.js +10 -11
- package/dist/sdd/index-generator.js.map +1 -1
- package/dist/test-setup/project-fixture.d.ts +48 -0
- package/dist/test-setup/project-fixture.d.ts.map +1 -0
- package/dist/test-setup/project-fixture.js +106 -0
- package/dist/test-setup/project-fixture.js.map +1 -0
- package/dist/types/constraint.d.ts +0 -18
- package/dist/types/constraint.d.ts.map +1 -1
- package/dist/types/constraint.js.map +1 -1
- package/dist/types/passes-gate.d.ts +10 -25
- package/dist/types/passes-gate.d.ts.map +1 -1
- package/dist/types/project-config.d.ts +1 -1
- package/dist/types/session.d.ts +6 -5
- package/dist/types/session.d.ts.map +1 -1
- package/dist/utils/exec.d.ts +10 -0
- package/dist/utils/exec.d.ts.map +1 -1
- package/dist/utils/exec.js +12 -0
- package/dist/utils/exec.js.map +1 -1
- package/dist/utils/file-walk.d.ts +9 -1
- package/dist/utils/file-walk.d.ts.map +1 -1
- package/dist/utils/file-walk.js +10 -1
- package/dist/utils/file-walk.js.map +1 -1
- package/dist/utils/frontmatter.d.ts +73 -0
- package/dist/utils/frontmatter.d.ts.map +1 -0
- package/dist/utils/frontmatter.js +141 -0
- package/dist/utils/frontmatter.js.map +1 -0
- package/dist/utils/jsonl.d.ts +92 -0
- package/dist/utils/jsonl.d.ts.map +1 -0
- package/dist/utils/jsonl.js +166 -0
- package/dist/utils/jsonl.js.map +1 -0
- package/dist/utils/package-version.d.ts +16 -0
- package/dist/utils/package-version.d.ts.map +1 -0
- package/dist/utils/package-version.js +63 -0
- package/dist/utils/package-version.js.map +1 -0
- package/package.json +1 -1
- package/src/CONTEXT.md +7 -2
- package/src/__tests__/acceptance-gate.test.ts +3 -1
- package/src/__tests__/checker-extra.test.ts +7 -1218
- package/src/__tests__/context-files-resolution.test.ts +116 -0
- package/src/__tests__/effective-constraints.test.ts +10 -13
- package/src/__tests__/exec.test.ts +35 -1
- package/src/__tests__/layering.test.ts +152 -0
- package/src/__tests__/passes-gate-extra.test.ts +0 -145
- package/src/__tests__/passes-gate.test.ts +142 -106
- package/src/__tests__/skip-semantics.test.ts +5 -9
- package/src/cli/__tests__/command-contract.test.ts +170 -0
- package/src/cli/command-contract.ts +108 -0
- package/src/cli/commands/CONTEXT.md +22 -7
- package/src/cli/commands/__tests__/acceptance.test.ts +40 -33
- package/src/cli/commands/__tests__/analyze-sessions.test.ts +35 -29
- package/src/cli/commands/__tests__/bin-exit-mapping.test.ts +101 -0
- package/src/cli/commands/__tests__/check-drift.test.ts +27 -34
- package/src/cli/commands/__tests__/check-skip-output.test.ts +21 -24
- package/src/cli/commands/__tests__/check.test.ts +378 -382
- package/src/cli/commands/__tests__/command.test.ts +85 -70
- package/src/cli/commands/__tests__/constraints-report.test.ts +100 -71
- package/src/cli/commands/__tests__/constraints-retire.test.ts +152 -95
- package/src/cli/commands/__tests__/contract.test.ts +27 -34
- package/src/cli/commands/__tests__/failure.test.ts +31 -34
- package/src/cli/commands/__tests__/init-injection.test.ts +58 -28
- package/src/cli/commands/__tests__/init.test.ts +77 -48
- package/src/cli/commands/__tests__/knowledge-flywheel-consistency.test.ts +108 -0
- package/src/cli/commands/__tests__/knowledge.test.ts +69 -43
- package/src/cli/commands/__tests__/passes-gate.test.ts +43 -45
- package/src/cli/commands/__tests__/performance.test.ts +22 -23
- package/src/cli/commands/__tests__/posteval-plan.test.ts +39 -38
- package/src/cli/commands/__tests__/project-path-anchoring.test.ts +157 -0
- package/src/cli/commands/__tests__/project-path-convention.test.ts +169 -0
- package/src/cli/commands/__tests__/registry.test.ts +69 -35
- package/src/cli/commands/__tests__/release.test.ts +209 -80
- package/src/cli/commands/__tests__/report-extra.test.ts +16 -16
- package/src/cli/commands/__tests__/report.test.ts +25 -25
- package/src/cli/commands/__tests__/review.test.ts +33 -31
- package/src/cli/commands/__tests__/security.test.ts +23 -27
- package/src/cli/commands/__tests__/spec-baseline-check.test.ts +20 -20
- package/src/cli/commands/__tests__/spec.test.ts +41 -29
- package/src/cli/commands/__tests__/status-extra.test.ts +18 -18
- package/src/cli/commands/__tests__/status-skipped-lines.test.ts +72 -0
- package/src/cli/commands/__tests__/status.test.ts +34 -34
- package/src/cli/commands/__tests__/sync-docs-agents.test.ts +77 -78
- package/src/cli/commands/__tests__/sync-docs.test.ts +72 -74
- package/src/cli/commands/__tests__/update-user-model.test.ts +62 -33
- package/src/cli/commands/__tests__/validate.test.ts +27 -29
- package/src/cli/commands/acceptance.ts +35 -24
- package/src/cli/commands/analyze-sessions.ts +25 -24
- package/src/cli/commands/check.ts +88 -65
- package/src/cli/commands/command.ts +30 -17
- package/src/cli/commands/constraints-report.ts +37 -34
- package/src/cli/commands/constraints-retire.ts +58 -55
- package/src/cli/commands/constraints.ts +10 -27
- package/src/cli/commands/contract.ts +51 -42
- package/src/cli/commands/definitions.ts +36 -222
- package/src/cli/commands/failure.ts +33 -26
- package/src/cli/commands/init.ts +166 -220
- package/src/cli/commands/knowledge.ts +225 -203
- package/src/cli/commands/passes-gate.ts +58 -68
- package/src/cli/commands/performance.ts +23 -18
- package/src/cli/commands/posteval-plan.ts +29 -15
- package/src/cli/commands/release.ts +70 -59
- package/src/cli/commands/report.ts +9 -5
- package/src/cli/commands/review.ts +39 -29
- package/src/cli/commands/sdd.ts +7 -5
- package/src/cli/commands/security.ts +35 -26
- package/src/cli/commands/spec-baseline-check.ts +16 -11
- package/src/cli/commands/spec.ts +66 -53
- package/src/cli/commands/status.ts +59 -53
- package/src/cli/commands/sync-docs/agents-syncer.ts +5 -2
- package/src/cli/commands/sync-docs/context-syncer.ts +3 -2
- package/src/cli/commands/sync-docs/index.ts +73 -100
- package/src/cli/commands/sync-docs/project-reader.ts +17 -18
- package/src/cli/commands/update-user-model.ts +33 -45
- package/src/cli/commands/validate.ts +31 -25
- package/src/cli/session-mining/__tests__/transcript.test.ts +118 -0
- package/src/cli/session-mining/transcript.ts +26 -8
- package/src/context/session-manager.ts +19 -33
- package/src/core/CONTEXT.md +9 -6
- package/src/core/constraints/__tests__/capabilities-reconcile.test.ts +200 -0
- package/src/core/constraints/__tests__/capability-counts.test.ts +94 -0
- package/src/core/constraints/__tests__/git-evidence.test.ts +208 -0
- package/src/core/constraints/__tests__/injection-drift.test.ts +3 -10
- package/src/core/constraints/__tests__/injection-writer.test.ts +231 -0
- package/src/core/constraints/__tests__/trace-injection.test.ts +71 -0
- package/src/core/constraints/__tests__/usage-report.test.ts +112 -0
- package/src/core/constraints/agent-prompt-renderer.ts +2 -6
- package/src/core/constraints/capabilities-parser.ts +53 -35
- package/src/core/constraints/capabilities-reconcile.ts +166 -0
- package/src/core/constraints/checker.ts +75 -130
- package/src/core/constraints/checkers/__tests__/capability-sync.test.ts +214 -0
- package/src/core/constraints/checkers/__tests__/context-doc-sync.test.ts +77 -0
- package/src/core/constraints/checkers/__tests__/docs-freshness.test.ts +117 -0
- package/src/{__tests__ → core/constraints/checkers/__tests__}/governance-presence.test.ts +83 -9
- package/src/core/constraints/checkers/__tests__/iron-flags.test.ts +50 -0
- package/src/core/constraints/checkers/__tests__/no-bypass-checkpoint.test.ts +76 -0
- package/src/{__tests__ → core/constraints/checkers/__tests__}/no-hardcoded-credentials.test.ts +4 -3
- package/src/core/constraints/checkers/__tests__/no-test-simplification.test.ts +58 -0
- package/src/core/constraints/checkers/__tests__/three-state-consistency.test.ts +145 -0
- package/src/core/constraints/checkers/capability-sync.ts +29 -88
- package/src/core/constraints/checkers/context-doc-sync.ts +15 -25
- package/src/core/constraints/checkers/docs-freshness.ts +40 -69
- package/src/core/constraints/checkers/governance-presence.ts +7 -25
- package/src/core/constraints/checkers/no-test-simplification.ts +4 -3
- package/src/core/constraints/checkers/types.ts +2 -2
- package/src/core/constraints/context-builder.ts +39 -91
- package/src/core/constraints/doc-freshness/runner.ts +2 -1
- package/src/core/constraints/git-evidence.ts +120 -0
- package/src/core/constraints/injection-drift.ts +13 -60
- package/src/core/constraints/injection-writer.ts +212 -0
- package/src/core/constraints/usage-report.ts +36 -17
- package/src/core/index.ts +4 -2
- package/src/core/project-config-loader.ts +49 -9
- package/src/core/validators/__tests__/passes-gate.test.ts +2 -56
- package/src/core/validators/__tests__/test-output.test.ts +372 -0
- package/src/core/validators/passes-gate.ts +86 -248
- package/src/core/validators/test-output.ts +128 -0
- package/src/failure/__tests__/recorder.test.ts +168 -0
- package/src/failure/recorder.ts +12 -10
- package/src/gates/CONTEXT.md +6 -3
- package/src/gates/__tests__/acceptance-types-single-source.test.ts +68 -0
- package/src/gates/__tests__/acceptance.test.ts +4 -86
- package/src/gates/__tests__/effective-gates.test.ts +3 -8
- package/src/gates/__tests__/test-verdict-consistency.test.ts +140 -0
- package/src/gates/acceptance.ts +19 -84
- package/src/gates/checker-gate.ts +8 -7
- package/src/gates/command.ts +10 -16
- package/src/gates/contract.ts +22 -61
- package/src/gates/performance.ts +10 -22
- package/src/gates/review.ts +21 -41
- package/src/gates/security.ts +10 -22
- package/src/gates/types.ts +70 -5
- package/src/hooks/CONTEXT.md +1 -1
- package/src/hooks/bootstrap.ts +5 -2
- package/src/index.ts +0 -4
- package/src/knowledge/CONTEXT.md +8 -3
- package/src/knowledge/__tests__/audit-labels.test.ts +44 -0
- package/src/knowledge/__tests__/flywheel-metrics.test.ts +135 -0
- package/src/knowledge/__tests__/import.test.ts +44 -0
- package/src/knowledge/__tests__/index-generator.test.ts +53 -0
- package/src/knowledge/__tests__/ingest.test.ts +85 -0
- package/src/knowledge/__tests__/migration.test.ts +47 -0
- package/src/knowledge/__tests__/reference-tracker.test.ts +16 -0
- package/src/knowledge/__tests__/store.test.ts +194 -0
- package/src/knowledge/audit.ts +67 -71
- package/src/knowledge/flywheel-metrics.ts +71 -0
- package/src/knowledge/import.ts +4 -18
- package/src/knowledge/index-generator.ts +19 -14
- package/src/knowledge/ingest.ts +9 -4
- package/src/knowledge/migration.ts +11 -8
- package/src/knowledge/reference-tracker.ts +12 -8
- package/src/knowledge/store.ts +64 -11
- package/src/monitoring/CONTEXT.md +8 -3
- package/src/monitoring/__tests__/skipped-lines-report.test.ts +178 -0
- package/src/monitoring/__tests__/traces.test.ts +57 -0
- package/src/monitoring/context-tracker.ts +11 -23
- package/src/monitoring/index.ts +1 -1
- package/src/monitoring/trace-analyzer.ts +21 -2
- package/src/monitoring/traces.ts +39 -36
- package/src/release/CONTEXT.md +1 -0
- package/src/release/__tests__/barrel.test.ts +60 -0
- package/src/release/index.ts +2 -13
- package/src/sdd/__tests__/index-generator.test.ts +71 -0
- package/src/sdd/index-generator.ts +9 -9
- package/src/test-setup/__tests__/project-fixture.test.ts +178 -0
- package/src/test-setup/project-fixture.ts +87 -0
- package/src/tools/CONTEXT.md +6 -0
- package/src/tools/__tests__/registry.test.ts +221 -0
- package/src/types/__tests__/types.test.ts +4 -5
- package/src/types/constraint.ts +0 -25
- package/src/types/passes-gate.ts +10 -30
- package/src/types/project-config.ts +1 -1
- package/src/types/session.ts +5 -5
- package/src/utils/__tests__/frontmatter.test.ts +206 -0
- package/src/utils/__tests__/jsonl-skip-disposition.test.ts +175 -0
- package/src/utils/__tests__/jsonl.test.ts +187 -0
- package/src/utils/__tests__/package-version.test.ts +97 -0
- package/src/utils/exec.ts +15 -0
- package/src/utils/file-walk.ts +11 -2
- package/src/utils/frontmatter.ts +121 -0
- package/src/utils/jsonl.ts +167 -0
- package/src/utils/package-version.ts +27 -0
- package/dist/cli/commands/doc-freshness-check.d.ts +0 -53
- package/dist/cli/commands/doc-freshness-check.d.ts.map +0 -1
- package/dist/cli/commands/doc-freshness-check.js +0 -352
- package/dist/cli/commands/doc-freshness-check.js.map +0 -1
- package/dist/types/index.d.ts +0 -14
- package/dist/types/index.d.ts.map +0 -1
- package/dist/types/index.js +0 -39
- package/dist/types/index.js.map +0 -1
- package/src/__tests__/long-running.test.ts +0 -17
- package/src/__tests__/passes-gate-extension.test.ts +0 -151
- package/src/cli/commands/__tests__/doc-freshness-check.test.ts +0 -168
- package/src/cli/commands/doc-freshness-check.ts +0 -386
- package/src/spec/CONTEXT.md +0 -11
- package/src/types/index.ts +0 -47
|
@@ -3,15 +3,18 @@
|
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
5
|
import * as fs from 'fs';
|
|
6
|
+
import { captureIO, type CapturingIO } from '../../command-contract';
|
|
6
7
|
import * as path from 'path';
|
|
7
8
|
|
|
9
|
+
let io: CapturingIO;
|
|
10
|
+
beforeEach(() => {
|
|
11
|
+
io = captureIO();
|
|
12
|
+
});
|
|
13
|
+
|
|
8
14
|
describe('postevalPlan', () => {
|
|
9
15
|
jest.setTimeout(30000); // Retry-based tests with exponential backoff need longer timeout
|
|
10
16
|
const tempDir = path.join(process.cwd(), 'temp-test-posteval');
|
|
11
17
|
const planPath = path.join(tempDir, 'plan.md');
|
|
12
|
-
let consoleErrorSpy: jest.SpyInstance;
|
|
13
|
-
let consoleLogSpy: jest.SpyInstance;
|
|
14
|
-
let mockExit: jest.SpyInstance;
|
|
15
18
|
let originalFetch: typeof fetch;
|
|
16
19
|
let originalEnv: NodeJS.ProcessEnv;
|
|
17
20
|
|
|
@@ -34,19 +37,11 @@ describe('postevalPlan', () => {
|
|
|
34
37
|
originalEnv = { ...process.env };
|
|
35
38
|
delete process.env.API_PORT;
|
|
36
39
|
delete process.env.POSTEVAL_GRACE_MS;
|
|
37
|
-
consoleErrorSpy = jest.spyOn(console, 'error').mockImplementation();
|
|
38
|
-
consoleLogSpy = jest.spyOn(console, 'log').mockImplementation();
|
|
39
|
-
mockExit = jest.spyOn(process, 'exit').mockImplementation((() => {
|
|
40
|
-
// Don't actually exit
|
|
41
|
-
}) as any);
|
|
42
40
|
});
|
|
43
41
|
|
|
44
42
|
afterEach(() => {
|
|
45
43
|
global.fetch = originalFetch;
|
|
46
44
|
process.env = originalEnv;
|
|
47
|
-
consoleErrorSpy.mockRestore();
|
|
48
|
-
consoleLogSpy.mockRestore();
|
|
49
|
-
mockExit.mockRestore();
|
|
50
45
|
jest.useRealTimers();
|
|
51
46
|
});
|
|
52
47
|
|
|
@@ -61,12 +56,12 @@ describe('postevalPlan', () => {
|
|
|
61
56
|
}) as any;
|
|
62
57
|
|
|
63
58
|
const { postevalPlan } = await import('../posteval-plan');
|
|
64
|
-
await postevalPlan({ planPath });
|
|
59
|
+
await postevalPlan({ planPath }, io);
|
|
65
60
|
|
|
66
|
-
expect(
|
|
61
|
+
expect(io.outText()).toContain('100%');
|
|
67
62
|
});
|
|
68
63
|
|
|
69
|
-
it('completeness < 1
|
|
64
|
+
it('completeness < 1:fail(覆盖率不足,含缺失项)', async () => {
|
|
70
65
|
global.fetch = jest.fn().mockResolvedValue({
|
|
71
66
|
ok: true,
|
|
72
67
|
json: () => Promise.resolve({
|
|
@@ -77,10 +72,10 @@ describe('postevalPlan', () => {
|
|
|
77
72
|
}) as any;
|
|
78
73
|
|
|
79
74
|
const { postevalPlan } = await import('../posteval-plan');
|
|
80
|
-
await postevalPlan({ planPath });
|
|
75
|
+
const result = await postevalPlan({ planPath }, io);
|
|
81
76
|
|
|
82
|
-
expect(
|
|
83
|
-
expect(
|
|
77
|
+
expect(result).toEqual({ kind: 'fail', reason: expect.stringContaining('plan coverage 50% < 100%') });
|
|
78
|
+
expect(io.errText()).toContain('50%');
|
|
84
79
|
});
|
|
85
80
|
|
|
86
81
|
it('completeness < 1 时应该列出缺失项', async () => {
|
|
@@ -94,10 +89,13 @@ describe('postevalPlan', () => {
|
|
|
94
89
|
}) as any;
|
|
95
90
|
|
|
96
91
|
const { postevalPlan } = await import('../posteval-plan');
|
|
97
|
-
await postevalPlan({ planPath });
|
|
92
|
+
const result = await postevalPlan({ planPath }, io);
|
|
98
93
|
|
|
99
|
-
expect(
|
|
100
|
-
|
|
94
|
+
expect(result).toEqual({
|
|
95
|
+
kind: 'fail',
|
|
96
|
+
reason: 'plan coverage 33% < 100%,缺 2 项: AC-002; AC-003',
|
|
97
|
+
});
|
|
98
|
+
expect(io.errText()).toContain('AC-002');
|
|
101
99
|
});
|
|
102
100
|
|
|
103
101
|
it('4xx 错误应该 exit(1)', async () => {
|
|
@@ -108,13 +106,13 @@ describe('postevalPlan', () => {
|
|
|
108
106
|
}) as any;
|
|
109
107
|
|
|
110
108
|
const { postevalPlan } = await import('../posteval-plan');
|
|
111
|
-
await postevalPlan({ planPath });
|
|
109
|
+
const result = await postevalPlan({ planPath }, io);
|
|
112
110
|
|
|
113
|
-
expect(
|
|
114
|
-
expect(
|
|
111
|
+
expect(result).toEqual({ kind: 'fail', reason: 'PostEval API error: 400 Bad Request' });
|
|
112
|
+
expect(io.errText()).toContain('400');
|
|
115
113
|
});
|
|
116
114
|
|
|
117
|
-
it('5xx
|
|
115
|
+
it('5xx 错误:skip(未判定,允许提交;退出码面 0 不变)', async () => {
|
|
118
116
|
global.fetch = jest.fn().mockResolvedValue({
|
|
119
117
|
ok: false,
|
|
120
118
|
status: 503,
|
|
@@ -122,20 +120,20 @@ describe('postevalPlan', () => {
|
|
|
122
120
|
}) as any;
|
|
123
121
|
|
|
124
122
|
const { postevalPlan } = await import('../posteval-plan');
|
|
125
|
-
await postevalPlan({ planPath });
|
|
123
|
+
const result = await postevalPlan({ planPath }, io);
|
|
126
124
|
|
|
127
|
-
expect(
|
|
128
|
-
expect(
|
|
125
|
+
expect(result).toEqual({ kind: 'skip', reason: expect.stringContaining('服务端错误 503') });
|
|
126
|
+
expect(io.errText()).toContain('unavailable');
|
|
129
127
|
});
|
|
130
128
|
|
|
131
|
-
it('
|
|
129
|
+
it('网络错误重试耗尽:skip(未判定,允许提交)', async () => {
|
|
132
130
|
global.fetch = jest.fn().mockRejectedValue(new Error('ECONNREFUSED'));
|
|
133
131
|
|
|
134
132
|
const { postevalPlan } = await import('../posteval-plan');
|
|
135
|
-
await postevalPlan({ planPath });
|
|
133
|
+
const result = await postevalPlan({ planPath }, io);
|
|
136
134
|
|
|
137
|
-
expect(
|
|
138
|
-
expect(
|
|
135
|
+
expect(result).toEqual({ kind: 'skip', reason: expect.stringContaining('Studio API 不可达') });
|
|
136
|
+
expect(io.errText()).toContain('unreachable');
|
|
139
137
|
});
|
|
140
138
|
|
|
141
139
|
it('应该支持自定义 API_PORT', async () => {
|
|
@@ -150,7 +148,7 @@ describe('postevalPlan', () => {
|
|
|
150
148
|
}) as any;
|
|
151
149
|
|
|
152
150
|
const { postevalPlan } = await import('../posteval-plan');
|
|
153
|
-
await postevalPlan({ planPath });
|
|
151
|
+
await postevalPlan({ planPath }, io);
|
|
154
152
|
|
|
155
153
|
// Verify the fetch was called with port 3999
|
|
156
154
|
const fetchCall = (global.fetch as jest.Mock).mock.calls[0][0];
|
|
@@ -174,12 +172,12 @@ describe('postevalPlan', () => {
|
|
|
174
172
|
global.fetch = mockFetch as any;
|
|
175
173
|
|
|
176
174
|
const { postevalPlan } = await import('../posteval-plan');
|
|
177
|
-
await postevalPlan({ planPath });
|
|
175
|
+
const result = await postevalPlan({ planPath }, io);
|
|
178
176
|
|
|
179
177
|
// Should have been called 3 times
|
|
180
178
|
expect(mockFetch).toHaveBeenCalledTimes(3);
|
|
181
|
-
expect(
|
|
182
|
-
expect(
|
|
179
|
+
expect(io.outText()).toContain('100%');
|
|
180
|
+
expect(result.kind).toBe('ok');
|
|
183
181
|
});
|
|
184
182
|
|
|
185
183
|
it('completeness 为 0 时应该正确显示 0%', async () => {
|
|
@@ -193,9 +191,12 @@ describe('postevalPlan', () => {
|
|
|
193
191
|
}) as any;
|
|
194
192
|
|
|
195
193
|
const { postevalPlan } = await import('../posteval-plan');
|
|
196
|
-
await postevalPlan({ planPath });
|
|
194
|
+
const result = await postevalPlan({ planPath }, io);
|
|
197
195
|
|
|
198
|
-
expect(
|
|
199
|
-
|
|
196
|
+
expect(result).toEqual({
|
|
197
|
+
kind: 'fail',
|
|
198
|
+
reason: 'plan coverage 0% < 100%,缺 1 项: AC-001',
|
|
199
|
+
});
|
|
200
|
+
expect(io.errText()).toContain('0%');
|
|
200
201
|
});
|
|
201
202
|
});
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* -p/--project-path 锚定回归(harness#95)
|
|
3
|
+
*
|
|
4
|
+
* 两条门禁命令都接受 -p,但真正执行/IO 的一侧过去取 process.cwd():不 cd 到目标工程调用,
|
|
5
|
+
* 就会拿 A 工程的命令、在 B 目录跑、把证据写进 B;acceptance 的「无 tasks.yml 即跳过」
|
|
6
|
+
* 本身是 passed:true,于是 -p 失效直接表现为假绿。
|
|
7
|
+
*
|
|
8
|
+
* 既有单测全部 mock fs/exec + 假 projectPath,恰好抹平两种取径的差异。本文件不 mock 任何 IO,
|
|
9
|
+
* 前提统一为 **cwd ≠ projectPath**(cwd = A,-p = B),断言读写位置落在 B。
|
|
10
|
+
* 站点 2 从两面各断言一次:CLI 命令面(`acceptance -p`)与门禁实例面(默认构造 = 注册表单例形状)。
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import * as fs from 'fs';
|
|
14
|
+
import * as path from 'path';
|
|
15
|
+
import { createProjectFixture } from '../../../test-setup/project-fixture';
|
|
16
|
+
import { SpecAcceptanceGate } from '../../../gates/acceptance';
|
|
17
|
+
import { captureIO } from '../../command-contract';
|
|
18
|
+
import { runPassesGate } from '../passes-gate';
|
|
19
|
+
import { acceptance } from '../acceptance';
|
|
20
|
+
|
|
21
|
+
/** 在自身 cwd 落标记文件:哪一侧被真正调用,标记就出现在哪一侧的目录里 */
|
|
22
|
+
function markScript(marker: string): string {
|
|
23
|
+
return [
|
|
24
|
+
'const fs = require("fs");',
|
|
25
|
+
`fs.writeFileSync("${marker}-exec-location.txt", process.cwd());`,
|
|
26
|
+
'console.log("1 passed");',
|
|
27
|
+
'',
|
|
28
|
+
].join('\n');
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const PACKAGE_JSON = JSON.stringify({
|
|
32
|
+
name: 'fixture',
|
|
33
|
+
version: '1.0.0',
|
|
34
|
+
scripts: { test: 'node ./mark.js' },
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
/** acceptance_criteria 形状:checked 决定判定,两侧只差 checked 值 → 读错文件必然改变结论 */
|
|
38
|
+
function tasksYml(checked: boolean): string {
|
|
39
|
+
return [
|
|
40
|
+
'tasks:',
|
|
41
|
+
' - id: TASK-001',
|
|
42
|
+
' title: 锚定用例',
|
|
43
|
+
' acceptance_criteria:',
|
|
44
|
+
' - id: crit-1',
|
|
45
|
+
' description: 需要人工确认的验收条件',
|
|
46
|
+
' type: manual',
|
|
47
|
+
' required: true',
|
|
48
|
+
` checked: ${checked}`,
|
|
49
|
+
'',
|
|
50
|
+
].join('\n');
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/** passes-gate 两侧夹具:A = 调用方所在目录(cwd),B = -p 指向的目标工程 */
|
|
54
|
+
function createPassesGateSides(): { cwdSide: string; projectSide: string } {
|
|
55
|
+
return {
|
|
56
|
+
cwdSide: createProjectFixture({
|
|
57
|
+
name: 'pganchor-cwd-side',
|
|
58
|
+
files: { 'package.json': PACKAGE_JSON, 'mark.js': markScript('cwd-side') },
|
|
59
|
+
}),
|
|
60
|
+
projectSide: createProjectFixture({
|
|
61
|
+
name: 'pganchor-project-side',
|
|
62
|
+
files: { 'package.json': PACKAGE_JSON, 'mark.js': markScript('project-side') },
|
|
63
|
+
}),
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
describe('-p 锚定:执行与 IO 必须落在 projectPath(cwd ≠ projectPath)', () => {
|
|
68
|
+
const originalCwd = process.cwd();
|
|
69
|
+
|
|
70
|
+
afterEach(() => {
|
|
71
|
+
process.chdir(originalCwd);
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
describe('passes-gate(站点 1:执行 + 证据落盘)', () => {
|
|
75
|
+
it('-p 指向 B → B 的 package.json 测试命令在 B 执行,证据落 B/.harness/evidence', async () => {
|
|
76
|
+
const { cwdSide, projectSide } = createPassesGateSides();
|
|
77
|
+
process.chdir(cwdSide);
|
|
78
|
+
|
|
79
|
+
const result = await runPassesGate({ projectPath: projectSide }, captureIO());
|
|
80
|
+
|
|
81
|
+
expect(result).toEqual({ kind: 'ok' });
|
|
82
|
+
expect(fs.readFileSync(path.join(projectSide, 'project-side-exec-location.txt'), 'utf-8'))
|
|
83
|
+
.toBe(projectSide);
|
|
84
|
+
// 跑在 B 的判据同时要求 A 侧什么都没被执行、没被写入
|
|
85
|
+
expect(fs.existsSync(path.join(cwdSide, 'cwd-side-exec-location.txt'))).toBe(false);
|
|
86
|
+
expect(fs.existsSync(path.join(cwdSide, '.harness', 'evidence'))).toBe(false);
|
|
87
|
+
|
|
88
|
+
const evidenceDir = path.join(projectSide, '.harness', 'evidence');
|
|
89
|
+
expect(fs.existsSync(evidenceDir)).toBe(true);
|
|
90
|
+
expect(fs.readdirSync(evidenceDir).filter(f => f.startsWith('test-'))).toHaveLength(1);
|
|
91
|
+
});
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
describe('acceptance(站点 2:tasks.yml 解析)', () => {
|
|
95
|
+
it('默认构造的门禁实例(registry / getEffectiveGates 形状)→ 读 B/tasks.yml,不读 cwd 的', async () => {
|
|
96
|
+
const cwdSide = createProjectFixture({
|
|
97
|
+
name: 'acccfg-cwd-side',
|
|
98
|
+
files: { 'tasks.yml': tasksYml(true) },
|
|
99
|
+
});
|
|
100
|
+
const projectSide = createProjectFixture({
|
|
101
|
+
name: 'acccfg-project-side',
|
|
102
|
+
files: { 'tasks.yml': tasksYml(false) },
|
|
103
|
+
});
|
|
104
|
+
process.chdir(cwdSide);
|
|
105
|
+
|
|
106
|
+
// 无 config 构造 = 注册表单例的形状(./tasks.yml 相对默认值在这一支按 cwd 解析)
|
|
107
|
+
const result = await new SpecAcceptanceGate().check({
|
|
108
|
+
projectPath: projectSide,
|
|
109
|
+
taskId: 'TASK-001',
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
expect(result.passed).toBe(false);
|
|
113
|
+
expect(result.message).toContain('1 unchecked acceptance criteria');
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
it('-p 指向 B → 读 B/tasks.yml,不因 cwd 无文件而假绿', async () => {
|
|
117
|
+
const cwdSide = createProjectFixture({
|
|
118
|
+
name: 'accanchor-cwd-side',
|
|
119
|
+
files: { 'tasks.yml': tasksYml(true) },
|
|
120
|
+
});
|
|
121
|
+
const projectSide = createProjectFixture({
|
|
122
|
+
name: 'accanchor-project-side',
|
|
123
|
+
files: { 'tasks.yml': tasksYml(false) },
|
|
124
|
+
});
|
|
125
|
+
process.chdir(cwdSide);
|
|
126
|
+
|
|
127
|
+
const result = await acceptance({ projectPath: projectSide, taskId: 'TASK-001' }, captureIO());
|
|
128
|
+
|
|
129
|
+
expect(result).toEqual({
|
|
130
|
+
kind: 'fail',
|
|
131
|
+
reason: expect.stringContaining('1 unchecked acceptance criteria'),
|
|
132
|
+
});
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
it('--tasks-path 相对路径按 projectPath 解析,不按 cwd', async () => {
|
|
136
|
+
const cwdSide = createProjectFixture({
|
|
137
|
+
name: 'accanchorrel-cwd-side',
|
|
138
|
+
files: { 'config/tasks.yml': tasksYml(true) },
|
|
139
|
+
});
|
|
140
|
+
const projectSide = createProjectFixture({
|
|
141
|
+
name: 'accanchorrel-project-side',
|
|
142
|
+
files: { 'config/tasks.yml': tasksYml(false) },
|
|
143
|
+
});
|
|
144
|
+
process.chdir(cwdSide);
|
|
145
|
+
|
|
146
|
+
const result = await acceptance(
|
|
147
|
+
{ projectPath: projectSide, taskId: 'TASK-001', tasksPath: 'config/tasks.yml' },
|
|
148
|
+
captureIO(),
|
|
149
|
+
);
|
|
150
|
+
|
|
151
|
+
expect(result).toEqual({
|
|
152
|
+
kind: 'fail',
|
|
153
|
+
reason: expect.stringContaining('1 unchecked acceptance criteria'),
|
|
154
|
+
});
|
|
155
|
+
});
|
|
156
|
+
});
|
|
157
|
+
});
|
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* projectPath 传递约定守护(harness#95)
|
|
3
|
+
*
|
|
4
|
+
* 约定(正本见本目录 CONTEXT.md「约定」段):**projectPath 只在 CLI 入口做一次 `|| process.cwd()`
|
|
5
|
+
* 兜底,之后必须传到每个 IO/执行点**——下游禁止再取一次 cwd,也禁止用相对路径默认值
|
|
6
|
+
* (两者都会让 `-p` 半失效:读写/执行位置悄悄回到调用方的 cwd)。
|
|
7
|
+
*
|
|
8
|
+
* 本文件是这条约定的机器可检面,三道闸:
|
|
9
|
+
* 1. CLI 入口层:cwd 只能以 `xxx || process.cwd()` 的兜底形状出现,例外逐个点名并记理由
|
|
10
|
+
* 2. 下游层(core / gates / context / monitoring / hooks):cwd 站点集合冻结,
|
|
11
|
+
* 新增即失败——要么把根传下去,要么在此记下豁免理由
|
|
12
|
+
* 3. `xxxPath: '相对路径'` 默认值同理冻结(acceptance 的 `tasksPath: './tasks.yml'` 就是这一类)
|
|
13
|
+
*
|
|
14
|
+
* 冻结集合还要求**每条豁免仍然成立**(站点消失却不删条目 → 同样失败),豁免不会烂成化石。
|
|
15
|
+
* 「根参数已传入却又取 cwd」这类静态看不出的漂移,由 project-path-anchoring.test.ts 的真实 IO 行为用例守。
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
import * as fs from 'fs';
|
|
19
|
+
import * as path from 'path';
|
|
20
|
+
|
|
21
|
+
const SRC_ROOT = path.join(__dirname, '..', '..', '..', '..', 'src');
|
|
22
|
+
|
|
23
|
+
/** 注释行不参与匹配:文档里「缺省 process.cwd()」这类描述不是站点 */
|
|
24
|
+
function codeLines(file: string): string[] {
|
|
25
|
+
return fs
|
|
26
|
+
.readFileSync(file, 'utf-8')
|
|
27
|
+
.split('\n')
|
|
28
|
+
.map(line => line.trim())
|
|
29
|
+
.filter(line => !line.startsWith('*') && !line.startsWith('//') && !line.startsWith('/*'));
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function listTsFiles(dir: string, skipDirs: string[] = []): string[] {
|
|
33
|
+
const found: string[] = [];
|
|
34
|
+
for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
|
|
35
|
+
const full = path.join(dir, entry.name);
|
|
36
|
+
if (entry.isDirectory()) {
|
|
37
|
+
if (entry.name === '__tests__' || skipDirs.includes(entry.name)) continue;
|
|
38
|
+
found.push(...listTsFiles(full, skipDirs));
|
|
39
|
+
} else if (entry.name.endsWith('.ts') && !entry.name.endsWith('.d.ts')) {
|
|
40
|
+
found.push(full);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
return found.sort();
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/** 仓库相对路径(豁免表以此为准,跨机器稳定) */
|
|
47
|
+
function repoPath(abs: string): string {
|
|
48
|
+
return path.relative(path.join(SRC_ROOT, '..'), abs).split(path.sep).join('/');
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function cwdSites(files: string[]): Map<string, string[]> {
|
|
52
|
+
const sites = new Map<string, string[]>();
|
|
53
|
+
for (const file of files) {
|
|
54
|
+
const lines = codeLines(file).filter(line => line.includes('process.cwd()'));
|
|
55
|
+
if (lines.length > 0) sites.set(repoPath(file), lines);
|
|
56
|
+
}
|
|
57
|
+
return sites;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// ========================================
|
|
61
|
+
// 闸 1:CLI 入口层的非兜底形状 cwd
|
|
62
|
+
// ========================================
|
|
63
|
+
|
|
64
|
+
/** CLI 层的合法形状:入口对 `-p/--project-path` 做一次性兜底 */
|
|
65
|
+
const ENTRY_FALLBACK_SHAPE = /\|\|\s*process\.cwd\(\)/;
|
|
66
|
+
|
|
67
|
+
/** 兜底形状之外的 CLI cwd 站点:逐个裁决过,理由与站点原文一起冻结 */
|
|
68
|
+
const CLI_CWD_EXEMPTIONS: Record<string, { lines: string[]; reason: string }> = {
|
|
69
|
+
'src/cli/commands/release.ts': {
|
|
70
|
+
lines: ['const pkgPath = process.cwd();'],
|
|
71
|
+
reason:
|
|
72
|
+
'release 定义表里没有 -p/--project-path(只收 --bump/--dry-run),pkgPath 就是该命令唯一的根,' +
|
|
73
|
+
'且已逐个传给每条 run(cmd, pkgPath) 与 verifyReleaseArtifacts(pkgPath)——不构成 -p 半失效。' +
|
|
74
|
+
'给发布流水线新增 -p 属新能力(可指向任意目录做 push/publish),不在 #95 范围。',
|
|
75
|
+
},
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
// ========================================
|
|
79
|
+
// 闸 2:下游层的 cwd 站点
|
|
80
|
+
// ========================================
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* 下游豁免的共同理由:这些是**库层公开 API 的可选根参数默认值**(调用方没给根时的缺省),
|
|
84
|
+
* 不是「根已经传进来了,取 IO 时又绕回 cwd」。core 不能 import cli,兜底只能留在各自入口,
|
|
85
|
+
* 故冻结而非删除;新增站点必须在此逐个点名并单独说明。
|
|
86
|
+
*/
|
|
87
|
+
const DOWNSTREAM_CWD_EXEMPTIONS: Record<string, string> = {
|
|
88
|
+
'src/context/session-manager.ts': '构造参数 basePath 缺省(会话存储根,CLI 已显式传 projectPath)',
|
|
89
|
+
'src/core/constraints/checker.ts': 'check 上下文缺 projectPath 时的根兜底(3 处同一形状)',
|
|
90
|
+
'src/core/constraints/checkers/types.ts': 'checker 环境的根兜底(context.projectPath 可选)',
|
|
91
|
+
'src/core/constraints/context-builder.ts': 'buildCheckEnv 的 options.projectPath 缺省兜底(2 处)',
|
|
92
|
+
'src/core/constraints/usage-report.ts': 'projectRoot 形参默认值(库层可选根)',
|
|
93
|
+
'src/core/effective-constraints.ts': '生效集三函数 projectRoot 形参默认值(库层可选根,JSDoc 已声明)',
|
|
94
|
+
'src/core/project-config-loader.ts': 'config 加载器 projectPath 构造参数缺省兜底',
|
|
95
|
+
'src/core/spec/validator.ts': 'validateAll 的可选 projectPath 兜底(其相对 schemaPath 默认值见闸 3 豁免)',
|
|
96
|
+
'src/gates/checker-gate.ts': 'GateContext.projectPath 缺省兜底(门禁适配器的根入口)',
|
|
97
|
+
'src/gates/effective-gates.ts': 'projectRoot 形参默认值(库层可选根)',
|
|
98
|
+
'src/hooks/bootstrap.ts': 'bootstrapHarness 的可选 projectPath 兜底(组合根入口,2 处)',
|
|
99
|
+
'src/monitoring/context-tracker.ts': 'basePath 构造参数缺省兜底',
|
|
100
|
+
};
|
|
101
|
+
|
|
102
|
+
// ========================================
|
|
103
|
+
// 闸 3:core/gates 的相对路径默认值
|
|
104
|
+
// ========================================
|
|
105
|
+
|
|
106
|
+
/** key 必须 camelCase 以 Path 结尾,值为相对字面量——这类默认值按 cwd 解析,即 #95 的病根形状 */
|
|
107
|
+
const RELATIVE_PATH_DEFAULT = /\b\w+Path\s*:\s*'(?!\/)([^']+)'/;
|
|
108
|
+
|
|
109
|
+
const RELATIVE_PATH_DEFAULT_EXEMPTIONS: Record<string, string> = {
|
|
110
|
+
'src/core/spec/validator.ts':
|
|
111
|
+
'schemaPath: "./specs/schemas" 确实按 cwd 解析(同型病灶)。不随 #95 修:validateFile()/loadSchema() ' +
|
|
112
|
+
'签名里没有根,补齐要把根穿透整个 spec 域(属 spec 重构,非本票两门禁)。CLI 侧 `spec -s` 已 ' +
|
|
113
|
+
'path.resolve(projectPath, schema),给了 -s 时口径正确。留待 spec 域单票收口。',
|
|
114
|
+
};
|
|
115
|
+
|
|
116
|
+
describe('projectPath 传递约定(harness#95)', () => {
|
|
117
|
+
const cliFiles = listTsFiles(path.join(SRC_ROOT, 'cli'));
|
|
118
|
+
const downstreamFiles = listTsFiles(SRC_ROOT).filter(f => !f.startsWith(path.join(SRC_ROOT, 'cli')));
|
|
119
|
+
|
|
120
|
+
describe('闸 1:CLI 入口层的 cwd 只能是一次性兜底', () => {
|
|
121
|
+
it('非兜底形状的 cwd 站点 = 冻结豁免表,且每条豁免仍然成立', () => {
|
|
122
|
+
const offenders = new Map<string, string[]>();
|
|
123
|
+
for (const [rel, lines] of cwdSites(cliFiles)) {
|
|
124
|
+
const notFallback = lines.filter(line => !ENTRY_FALLBACK_SHAPE.test(line));
|
|
125
|
+
if (notFallback.length > 0) offenders.set(rel, notFallback);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
expect([...offenders.keys()].sort()).toEqual(Object.keys(CLI_CWD_EXEMPTIONS).sort());
|
|
129
|
+
for (const [rel, exemption] of Object.entries(CLI_CWD_EXEMPTIONS)) {
|
|
130
|
+
expect({ rel, lines: offenders.get(rel) }).toEqual({ rel, lines: exemption.lines });
|
|
131
|
+
}
|
|
132
|
+
});
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
describe('闸 2:下游层不得二次取 cwd', () => {
|
|
136
|
+
it('下游 cwd 站点集合 = 冻结豁免表(新增即失败;站点消失却不删条目也失败)', () => {
|
|
137
|
+
const sites = cwdSites(downstreamFiles);
|
|
138
|
+
|
|
139
|
+
expect([...sites.keys()].sort()).toEqual(Object.keys(DOWNSTREAM_CWD_EXEMPTIONS).sort());
|
|
140
|
+
expect(sites.size).toBe(Object.keys(DOWNSTREAM_CWD_EXEMPTIONS).length);
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
it('PassesGate 执行侧不再取 cwd(站点 1 的收口形状:workDir 由调用方传入)', () => {
|
|
144
|
+
const lines = codeLines(path.join(SRC_ROOT, 'core', 'validators', 'passes-gate.ts'));
|
|
145
|
+
|
|
146
|
+
expect(lines.some(line => line.includes('process.cwd()'))).toBe(false);
|
|
147
|
+
expect(lines.find(line => line.includes('async runTests'))).toContain('workDir');
|
|
148
|
+
});
|
|
149
|
+
});
|
|
150
|
+
|
|
151
|
+
describe('闸 3:相对路径默认值不再按 cwd 解析', () => {
|
|
152
|
+
it('core/gates 里 xxxPath: 相对字面量 的站点 = 冻结豁免表', () => {
|
|
153
|
+
const hits = new Set<string>();
|
|
154
|
+
for (const file of listTsFiles(path.join(SRC_ROOT, 'core')).concat(listTsFiles(path.join(SRC_ROOT, 'gates')))) {
|
|
155
|
+
if (codeLines(file).some(line => RELATIVE_PATH_DEFAULT.test(line))) hits.add(repoPath(file));
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
expect([...hits].sort()).toEqual(Object.keys(RELATIVE_PATH_DEFAULT_EXEMPTIONS).sort());
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
it('acceptance 的 tasks.yml 默认不再有相对路径常量(站点 2 的收口形状)', () => {
|
|
162
|
+
// 多行调用 → 拼成空白归一的整段文本再断言形状
|
|
163
|
+
const body = codeLines(path.join(SRC_ROOT, 'gates', 'acceptance.ts')).join(' ').replace(/\s+/g, ' ');
|
|
164
|
+
|
|
165
|
+
expect(body).not.toMatch(/tasksPath\s*:\s*'/);
|
|
166
|
+
expect(body).toMatch(/path\.resolve\(\s*context\.projectPath\s*,\s*[^)]*'tasks\.yml'/);
|
|
167
|
+
});
|
|
168
|
+
});
|
|
169
|
+
});
|
|
@@ -36,7 +36,7 @@ const EXPECTED_TOP_LEVEL_COMMANDS = [
|
|
|
36
36
|
'check', 'validate', 'passes-gate', 'init', 'report', 'status', 'spec',
|
|
37
37
|
'sync-docs', 'knowledge', 'sdd', 'failure', 'posteval-plan',
|
|
38
38
|
'update-user-model', 'release', 'analyze-sessions', 'constraints',
|
|
39
|
-
'
|
|
39
|
+
'spec-baseline-check',
|
|
40
40
|
// 6 门禁命令(GATE_DEFINITIONS 驱动)
|
|
41
41
|
'acceptance', 'command', 'contract', 'performance', 'review', 'security',
|
|
42
42
|
];
|
|
@@ -66,6 +66,28 @@ describe('命令注册表闭环', () => {
|
|
|
66
66
|
}
|
|
67
67
|
});
|
|
68
68
|
|
|
69
|
+
it('全部命令实现声明 CommandResult 返回(候选7:判定经返回值外溢)', () => {
|
|
70
|
+
const refs = collectRefs([
|
|
71
|
+
...COMMAND_DEFINITIONS,
|
|
72
|
+
...GATE_DEFINITIONS.map(d => d.cli),
|
|
73
|
+
]);
|
|
74
|
+
for (const ref of refs) {
|
|
75
|
+
const file = fs.existsSync(path.join(repoCommandsDir, ref.module + '.ts'))
|
|
76
|
+
? path.join(repoCommandsDir, ref.module + '.ts')
|
|
77
|
+
: path.join(repoCommandsDir, ref.module, 'index.ts');
|
|
78
|
+
const source = fs.readFileSync(file, 'utf-8');
|
|
79
|
+
const declAt = source.search(new RegExp(`function\\s+${ref.export}\\s*\\(`));
|
|
80
|
+
expect(declAt).toBeGreaterThanOrEqual(0);
|
|
81
|
+
const decl = source.slice(declAt, declAt + 900);
|
|
82
|
+
expect(decl).toMatch(/\)\s*:\s*(?:Promise<)?CommandResult/);
|
|
83
|
+
// bin 的调用约定:任何路由/子命令/action 至少传 options(mapActionArgs 编组)
|
|
84
|
+
// → 首参不得是 io,否则 options 会被当成 io 传入(写流即崩,退出码 1)
|
|
85
|
+
const params = source.slice(declAt + ref.export.length, declAt + 900);
|
|
86
|
+
const firstParam = (params.slice(params.indexOf('(') + 1).match(/^\s*([A-Za-z_$][\w$]*)/) || [''])[1];
|
|
87
|
+
expect(['io', '_io']).not.toContain(firstParam);
|
|
88
|
+
}
|
|
89
|
+
});
|
|
90
|
+
|
|
69
91
|
it('顶层命令面与预期 24 命令一致(含 6 门禁)', () => {
|
|
70
92
|
const names = [
|
|
71
93
|
...COMMAND_DEFINITIONS.map(d => d.command.split(' ')[0]),
|
|
@@ -80,45 +102,42 @@ describe('命令注册表闭环', () => {
|
|
|
80
102
|
expect(new Set(names).size).toBe(names.length);
|
|
81
103
|
});
|
|
82
104
|
|
|
83
|
-
it('
|
|
84
|
-
const
|
|
85
|
-
|
|
86
|
-
const
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
entry.args(['positional'], {});
|
|
94
|
-
entry.args([], {});
|
|
95
|
-
}
|
|
96
|
-
}
|
|
97
|
-
for (const route of def.optionRoutes || []) {
|
|
98
|
-
if (route.args) {
|
|
99
|
-
route.args([], {});
|
|
100
|
-
route.args(['positional'], {});
|
|
101
|
-
}
|
|
102
|
-
}
|
|
103
|
-
if (def.afterRun) {
|
|
104
|
-
def.afterRun(true, {});
|
|
105
|
-
def.afterRun(false, { check: true });
|
|
105
|
+
it('子命令别名唯一:别名不与主名/其他别名碰撞(候选7:别名是数据,冲突即行为歧义)', () => {
|
|
106
|
+
const walk = (def: CommandDefinition) => {
|
|
107
|
+
const entries = Object.entries(def.subcommands || {});
|
|
108
|
+
const seen = new Set<string>();
|
|
109
|
+
for (const [name, entry] of entries) {
|
|
110
|
+
expect(seen.has(name)).toBe(false);
|
|
111
|
+
seen.add(name);
|
|
112
|
+
for (const alias of entry.aliases || []) {
|
|
113
|
+
expect(seen.has(alias)).toBe(false);
|
|
114
|
+
seen.add(alias);
|
|
106
115
|
}
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
116
|
+
}
|
|
117
|
+
for (const child of def.children || []) walk(child);
|
|
118
|
+
};
|
|
119
|
+
for (const def of [...COMMAND_DEFINITIONS, ...GATE_DEFINITIONS.map(d => d.cli)]) walk(def);
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
it('全部命令定义的路由构造可执行(mapActionArgs 冒烟;候选7 后定义表零副作用闭包)', () => {
|
|
123
|
+
const walk = (def: CommandDefinition) => {
|
|
124
|
+
if (def.mapActionArgs) {
|
|
125
|
+
def.mapActionArgs(['positional'], {});
|
|
126
|
+
def.mapActionArgs([], {});
|
|
127
|
+
}
|
|
128
|
+
// 候选7:定义表不得再持有退出码处理闭包(afterRun 已废除,判定进返回值)
|
|
129
|
+
expect((def as unknown as Record<string, unknown>).afterRun).toBeUndefined();
|
|
130
|
+
for (const child of def.children || []) walk(child);
|
|
131
|
+
};
|
|
132
|
+
for (const def of [
|
|
133
|
+
...COMMAND_DEFINITIONS,
|
|
134
|
+
...GATE_DEFINITIONS.map(d => d.cli),
|
|
135
|
+
]) walk(def);
|
|
118
136
|
});
|
|
119
137
|
});
|
|
120
138
|
|
|
121
139
|
const repoRoot = path.join(__dirname, '..', '..', '..', '..');
|
|
140
|
+
const repoCommandsDir = path.join(repoRoot, 'src', 'cli', 'commands');
|
|
122
141
|
const distBin = path.join(repoRoot, 'bin', 'harness.js');
|
|
123
142
|
const distCommands = path.join(repoRoot, 'dist', 'cli', 'commands');
|
|
124
143
|
const hasDist = fs.existsSync(distCommands);
|
|
@@ -200,4 +219,19 @@ smoke('bin/harness.js 端到端(dist 存在时)', () => {
|
|
|
200
219
|
expect(r.status).toBe(1);
|
|
201
220
|
expect(r.stderr).toContain('未知子命令: bogus');
|
|
202
221
|
});
|
|
222
|
+
|
|
223
|
+
it('别名子命令解析到同一实现(knowledge ls = list,候选7)', () => {
|
|
224
|
+
const r = runWithModuleProbe(['knowledge', 'ls', '--json']);
|
|
225
|
+
expect(r.status).toBe(0);
|
|
226
|
+
expect(JSON.parse(r.stdout)).toHaveProperty('total');
|
|
227
|
+
expect(r.implModules).toEqual([
|
|
228
|
+
expect.stringContaining('/dist/cli/commands/knowledge.js'),
|
|
229
|
+
]);
|
|
230
|
+
});
|
|
231
|
+
|
|
232
|
+
it('search 编组闸门随别名命令名生效且提示逐字不变(kb s 缺参 → exit 1)', () => {
|
|
233
|
+
const r = runWithModuleProbe(['kb', 's']);
|
|
234
|
+
expect(r.status).toBe(1);
|
|
235
|
+
expect(r.stderr).toContain('请提供搜索关键词');
|
|
236
|
+
});
|
|
203
237
|
});
|