@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
|
@@ -3,9 +3,12 @@
|
|
|
3
3
|
*
|
|
4
4
|
* 确保 task.passes 字段只能通过测试结果修改
|
|
5
5
|
* 禁止 Agent 自评通过
|
|
6
|
+
*
|
|
7
|
+
* 「过了没」的判定依据不在本文件:唯一入口是 test-output.ts 的 judgeTestRun(ADR-0014)
|
|
6
8
|
*/
|
|
7
9
|
|
|
8
10
|
import { execAsync, delay } from '../../utils/exec';
|
|
11
|
+
import { judgeTestRun } from './test-output';
|
|
9
12
|
import * as fs from 'fs/promises';
|
|
10
13
|
import * as path from 'path';
|
|
11
14
|
import type {
|
|
@@ -13,8 +16,6 @@ import type {
|
|
|
13
16
|
PassesGateResult,
|
|
14
17
|
TaskTestResult,
|
|
15
18
|
DynamicTask,
|
|
16
|
-
PassesGateExtension,
|
|
17
|
-
ExtensionTestResult,
|
|
18
19
|
TestResult,
|
|
19
20
|
PassesGateCheckResult,
|
|
20
21
|
PassesGateViolation,
|
|
@@ -47,6 +48,51 @@ const PROTECTED_TEST_PATTERNS = [
|
|
|
47
48
|
'**/__tests__/**',
|
|
48
49
|
];
|
|
49
50
|
|
|
51
|
+
/**
|
|
52
|
+
* 检测项目的测试命令(唯一正本,CLI 与库调用共消费,架构评审 A2)
|
|
53
|
+
*
|
|
54
|
+
* 探测顺序:test:ci → test(排除 npm 默认 echo 占位脚本)→ test:e2e → test:coverage
|
|
55
|
+
* → Python(pyproject.toml 或 pytest.ini 任一)→ go.mod。
|
|
56
|
+
* 探不到返回 undefined,兜底策略归调用方(CLI 映射为 skip;PassesGate 内部 fail-closed)。
|
|
57
|
+
*/
|
|
58
|
+
export async function detectTestCommand(projectPath: string): Promise<string | undefined> {
|
|
59
|
+
try {
|
|
60
|
+
const content = await fs.readFile(path.join(projectPath, 'package.json'), 'utf-8');
|
|
61
|
+
const pkg = JSON.parse(content);
|
|
62
|
+
|
|
63
|
+
if (pkg.scripts?.['test:ci']) {
|
|
64
|
+
return 'npm run test:ci';
|
|
65
|
+
}
|
|
66
|
+
if (pkg.scripts?.test && pkg.scripts.test !== 'echo "Error: no test specified"') {
|
|
67
|
+
return 'npm test';
|
|
68
|
+
}
|
|
69
|
+
if (pkg.scripts?.['test:e2e']) {
|
|
70
|
+
return 'npm run test:e2e';
|
|
71
|
+
}
|
|
72
|
+
if (pkg.scripts?.['test:coverage']) {
|
|
73
|
+
return 'npm run test:coverage';
|
|
74
|
+
}
|
|
75
|
+
} catch {
|
|
76
|
+
// 没有 package.json,继续探测其他项目类型
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// Python 项目:pyproject.toml 或 pytest.ini 任一命中
|
|
80
|
+
for (const marker of ['pyproject.toml', 'pytest.ini']) {
|
|
81
|
+
try {
|
|
82
|
+
await fs.access(path.join(projectPath, marker));
|
|
83
|
+
return 'pytest';
|
|
84
|
+
} catch {}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// Go 项目
|
|
88
|
+
try {
|
|
89
|
+
await fs.access(path.join(projectPath, 'go.mod'));
|
|
90
|
+
return 'go test ./...';
|
|
91
|
+
} catch {}
|
|
92
|
+
|
|
93
|
+
return undefined;
|
|
94
|
+
}
|
|
95
|
+
|
|
50
96
|
/**
|
|
51
97
|
* PassesGate 类
|
|
52
98
|
*/
|
|
@@ -219,8 +265,11 @@ export class PassesGate {
|
|
|
219
265
|
|
|
220
266
|
/**
|
|
221
267
|
* 运行测试(CLI 使用)
|
|
268
|
+
*
|
|
269
|
+
* `workDir` 必传:执行位置与证据落盘根都从这里来,不在本层取 cwd
|
|
270
|
+
* (projectPath 只在 CLI 入口兜底一次,见 src/cli/commands/CONTEXT.md)
|
|
222
271
|
*/
|
|
223
|
-
async runTests(): Promise<{
|
|
272
|
+
async runTests(workDir: string): Promise<{
|
|
224
273
|
passed: boolean;
|
|
225
274
|
passedTests: number;
|
|
226
275
|
failedTests: number;
|
|
@@ -230,10 +279,9 @@ export class PassesGate {
|
|
|
230
279
|
message?: string;
|
|
231
280
|
}> {
|
|
232
281
|
const startTime = Date.now();
|
|
233
|
-
|
|
234
|
-
|
|
282
|
+
|
|
235
283
|
try {
|
|
236
|
-
const testCommand = this.config.testCommand || await
|
|
284
|
+
const testCommand = this.config.testCommand || await detectTestCommand(workDir);
|
|
237
285
|
|
|
238
286
|
if (!testCommand) {
|
|
239
287
|
return {
|
|
@@ -246,7 +294,7 @@ export class PassesGate {
|
|
|
246
294
|
};
|
|
247
295
|
}
|
|
248
296
|
|
|
249
|
-
const result = await this.runTest(workDir);
|
|
297
|
+
const result = await this.runTest(workDir, undefined, testCommand);
|
|
250
298
|
const duration = Date.now() - startTime;
|
|
251
299
|
|
|
252
300
|
// 解析测试数量
|
|
@@ -281,155 +329,49 @@ export class PassesGate {
|
|
|
281
329
|
/**
|
|
282
330
|
* 运行测试
|
|
283
331
|
*/
|
|
284
|
-
private async runTest(workDir: string, _task?: DynamicTask): Promise<TaskTestResult> {
|
|
285
|
-
const testCommand =
|
|
332
|
+
private async runTest(workDir: string, _task?: DynamicTask, command?: string): Promise<TaskTestResult> {
|
|
333
|
+
const testCommand = command || this.config.testCommand || await detectTestCommand(workDir);
|
|
286
334
|
const timestamp = new Date();
|
|
287
335
|
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
cwd: workDir,
|
|
291
|
-
maxBuffer: 10 * 1024 * 1024, // 10MB buffer
|
|
292
|
-
});
|
|
293
|
-
|
|
294
|
-
const passed = true;
|
|
295
|
-
const coverage = this.extractCoverage(stdout);
|
|
296
|
-
const failures: string[] = [];
|
|
297
|
-
|
|
336
|
+
// 探测失败 fail-closed:与 runTests 的「未检测到测试命令」分支同形状
|
|
337
|
+
if (!testCommand) {
|
|
298
338
|
return {
|
|
299
|
-
passed,
|
|
300
|
-
command:
|
|
301
|
-
output:
|
|
302
|
-
failures,
|
|
303
|
-
coverage,
|
|
304
|
-
timestamp,
|
|
305
|
-
evidence: await this.generateEvidence(workDir, stdout),
|
|
306
|
-
};
|
|
307
|
-
} catch (error: any) {
|
|
308
|
-
const output = error.stdout || '';
|
|
309
|
-
const stderrOutput = error.stderr || '';
|
|
310
|
-
const combinedOutput = output + '\n' + stderrOutput;
|
|
311
|
-
|
|
312
|
-
const failures = this.extractFailures(combinedOutput);
|
|
313
|
-
const passed = this.config.allowPartialPass && failures.length === 0;
|
|
314
|
-
|
|
315
|
-
return {
|
|
316
|
-
passed,
|
|
317
|
-
command: testCommand,
|
|
318
|
-
output: combinedOutput,
|
|
319
|
-
failures,
|
|
320
|
-
coverage: this.extractCoverage(output),
|
|
339
|
+
passed: false,
|
|
340
|
+
command: '',
|
|
341
|
+
output: '未检测到测试命令',
|
|
321
342
|
timestamp,
|
|
322
|
-
evidence: await this.generateEvidence(workDir, combinedOutput),
|
|
323
343
|
};
|
|
324
344
|
}
|
|
325
|
-
}
|
|
326
|
-
|
|
327
|
-
/**
|
|
328
|
-
* 检测测试命令
|
|
329
|
-
*/
|
|
330
|
-
private async detectTestCommand(workDir: string): Promise<string> {
|
|
331
|
-
if (this.config.testCommand) {
|
|
332
|
-
return this.config.testCommand;
|
|
333
|
-
}
|
|
334
|
-
|
|
335
|
-
// 尝试读取 package.json
|
|
336
|
-
try {
|
|
337
|
-
const packageJsonPath = path.join(workDir, 'package.json');
|
|
338
|
-
const content = await fs.readFile(packageJsonPath, 'utf-8');
|
|
339
|
-
const packageJson = JSON.parse(content);
|
|
340
345
|
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
return 'npm run test:e2e';
|
|
344
|
-
}
|
|
345
|
-
if (packageJson.scripts?.['test:coverage']) {
|
|
346
|
-
return 'npm run test:coverage';
|
|
347
|
-
}
|
|
348
|
-
if (packageJson.scripts?.test) {
|
|
349
|
-
return 'npm test';
|
|
350
|
-
}
|
|
351
|
-
} catch {
|
|
352
|
-
// package.json 不存在,尝试其他检测
|
|
353
|
-
}
|
|
354
|
-
|
|
355
|
-
// 尝试 Python 项目
|
|
356
|
-
try {
|
|
357
|
-
const pyprojectPath = path.join(workDir, 'pyproject.toml');
|
|
358
|
-
await fs.access(pyprojectPath);
|
|
359
|
-
return 'pytest';
|
|
360
|
-
} catch {
|
|
361
|
-
// 不是 Python 项目
|
|
362
|
-
}
|
|
346
|
+
let exitCode = 0;
|
|
347
|
+
let output = '';
|
|
363
348
|
|
|
364
|
-
// 尝试 Go 项目
|
|
365
349
|
try {
|
|
366
|
-
const
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
}
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
}
|
|
376
|
-
|
|
377
|
-
/**
|
|
378
|
-
* 从输出中提取覆盖率
|
|
379
|
-
*/
|
|
380
|
-
private extractCoverage(output: string): number | undefined {
|
|
381
|
-
// Jest 格式: All files | 80.5 | 70.2 | ...
|
|
382
|
-
const jestMatch = output.match(/All files[|\s]+(\d+\.?\d*)/);
|
|
383
|
-
if (jestMatch?.[1]) {
|
|
384
|
-
return parseFloat(jestMatch[1]);
|
|
385
|
-
}
|
|
386
|
-
|
|
387
|
-
// Istanbul/nyc 格式: Statements : 80.5% ( 100/124 )
|
|
388
|
-
const istanbulMatch = output.match(/Statements\s*:\s*(\d+\.?\d*)%/);
|
|
389
|
-
if (istanbulMatch?.[1]) {
|
|
390
|
-
return parseFloat(istanbulMatch[1]);
|
|
391
|
-
}
|
|
392
|
-
|
|
393
|
-
// pytest-cov 格式: TOTAL 1234 80%
|
|
394
|
-
const pytestMatch = output.match(/TOTAL\s+\d+\s+(\d+)%/);
|
|
395
|
-
if (pytestMatch?.[1]) {
|
|
396
|
-
return parseInt(pytestMatch[1], 10);
|
|
397
|
-
}
|
|
398
|
-
|
|
399
|
-
return undefined;
|
|
400
|
-
}
|
|
401
|
-
|
|
402
|
-
/**
|
|
403
|
-
* 从输出中提取失败信息
|
|
404
|
-
*/
|
|
405
|
-
private extractFailures(output: string): string[] {
|
|
406
|
-
const failures: string[] = [];
|
|
407
|
-
|
|
408
|
-
// Jest 格式
|
|
409
|
-
const jestMatches = output.matchAll(/✕\s+(.+?)\s+\(/g);
|
|
410
|
-
for (const match of jestMatches) {
|
|
411
|
-
if (match[1]) failures.push(match[1]);
|
|
412
|
-
}
|
|
413
|
-
|
|
414
|
-
// Mocha 格式
|
|
415
|
-
const mochaMatches = output.matchAll(/\d+\)\s+(.+?):/g);
|
|
416
|
-
for (const match of mochaMatches) {
|
|
417
|
-
if (match[1]) failures.push(match[1]);
|
|
418
|
-
}
|
|
419
|
-
|
|
420
|
-
// pytest 格式
|
|
421
|
-
const pytestMatches = output.matchAll(/FAILED\s+(.+?)::/g);
|
|
422
|
-
for (const match of pytestMatches) {
|
|
423
|
-
if (match[1]) failures.push(match[1]);
|
|
350
|
+
const result = await execAsync(testCommand, {
|
|
351
|
+
cwd: workDir,
|
|
352
|
+
maxBuffer: 10 * 1024 * 1024, // 10MB buffer
|
|
353
|
+
});
|
|
354
|
+
output = result.stdout + result.stderr;
|
|
355
|
+
} catch (error: any) {
|
|
356
|
+
// 非零退出(超时 / buffer 溢出等执行失败也落这里):判定依据就是退出码,文本不参与
|
|
357
|
+
exitCode = typeof error.code === 'number' ? error.code : 1;
|
|
358
|
+
output = (error.stdout || '') + '\n' + (error.stderr || '');
|
|
424
359
|
}
|
|
425
360
|
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
}
|
|
361
|
+
const { passed, failures } = judgeTestRun({
|
|
362
|
+
exitCode,
|
|
363
|
+
output,
|
|
364
|
+
allowPartialPass: this.config.allowPartialPass,
|
|
365
|
+
});
|
|
431
366
|
|
|
432
|
-
return
|
|
367
|
+
return {
|
|
368
|
+
passed,
|
|
369
|
+
command: testCommand,
|
|
370
|
+
output,
|
|
371
|
+
failures,
|
|
372
|
+
timestamp,
|
|
373
|
+
evidence: await this.generateEvidence(workDir, output),
|
|
374
|
+
};
|
|
433
375
|
}
|
|
434
376
|
|
|
435
377
|
/**
|
|
@@ -499,110 +441,6 @@ export class PassesGate {
|
|
|
499
441
|
return this.testResults.get(taskId);
|
|
500
442
|
}
|
|
501
443
|
|
|
502
|
-
// ========================================
|
|
503
|
-
// 扩展点支持(Long-Running Agents)
|
|
504
|
-
// ========================================
|
|
505
|
-
|
|
506
|
-
/**
|
|
507
|
-
* 注册的扩展列表
|
|
508
|
-
*/
|
|
509
|
-
private extensions: Map<string, PassesGateExtension> = new Map();
|
|
510
|
-
|
|
511
|
-
/**
|
|
512
|
-
* 注册扩展
|
|
513
|
-
*
|
|
514
|
-
* 用于注册额外的测试类型(如 Puppeteer E2E)
|
|
515
|
-
*
|
|
516
|
-
* @param name 扩展名称
|
|
517
|
-
* @param extension 扩展实现
|
|
518
|
-
*
|
|
519
|
-
* @example
|
|
520
|
-
* ```typescript
|
|
521
|
-
* const puppeteerExtension = {
|
|
522
|
-
* name: 'puppeteer',
|
|
523
|
-
* run: async (workDir, task) => {
|
|
524
|
-
* // 运行 Puppeteer 测试
|
|
525
|
-
* return { passed: true, command: 'puppeteer', ... };
|
|
526
|
-
* }
|
|
527
|
-
* };
|
|
528
|
-
*
|
|
529
|
-
* passesGate.registerExtension('puppeteer', puppeteerExtension);
|
|
530
|
-
* ```
|
|
531
|
-
*/
|
|
532
|
-
registerExtension(name: string, extension: PassesGateExtension): void {
|
|
533
|
-
this.extensions.set(name, extension);
|
|
534
|
-
}
|
|
535
|
-
|
|
536
|
-
/**
|
|
537
|
-
* 注销扩展
|
|
538
|
-
*/
|
|
539
|
-
unregisterExtension(name: string): boolean {
|
|
540
|
-
return this.extensions.delete(name);
|
|
541
|
-
}
|
|
542
|
-
|
|
543
|
-
/**
|
|
544
|
-
* 获取所有已注册的扩展名称
|
|
545
|
-
*/
|
|
546
|
-
getExtensionNames(): string[] {
|
|
547
|
-
return Array.from(this.extensions.keys());
|
|
548
|
-
}
|
|
549
|
-
|
|
550
|
-
/**
|
|
551
|
-
* 运行所有测试(包括扩展测试)
|
|
552
|
-
*
|
|
553
|
-
* @param workDir 工作目录
|
|
554
|
-
* @param task 可选的任务信息
|
|
555
|
-
* @returns 所有测试结果
|
|
556
|
-
*/
|
|
557
|
-
async runAllTests(workDir: string, task?: DynamicTask): Promise<ExtensionTestResult[]> {
|
|
558
|
-
const results: ExtensionTestResult[] = [];
|
|
559
|
-
|
|
560
|
-
// 1. 运行单元测试(原有逻辑)
|
|
561
|
-
const unitTestResult = await this.runTest(workDir, task);
|
|
562
|
-
results.push(unitTestResult);
|
|
563
|
-
|
|
564
|
-
// 2. 运行所有扩展测试
|
|
565
|
-
for (const [name, extension] of this.extensions) {
|
|
566
|
-
try {
|
|
567
|
-
const extensionResult = await extension.run(workDir, task);
|
|
568
|
-
results.push({
|
|
569
|
-
...extensionResult,
|
|
570
|
-
type: name,
|
|
571
|
-
});
|
|
572
|
-
} catch (error: any) {
|
|
573
|
-
results.push({
|
|
574
|
-
passed: false,
|
|
575
|
-
command: `${name}-extension`,
|
|
576
|
-
error: error.message,
|
|
577
|
-
type: name,
|
|
578
|
-
timestamp: new Date(),
|
|
579
|
-
});
|
|
580
|
-
}
|
|
581
|
-
}
|
|
582
|
-
|
|
583
|
-
return results;
|
|
584
|
-
}
|
|
585
|
-
|
|
586
|
-
/**
|
|
587
|
-
* 检查所有测试是否通过
|
|
588
|
-
*/
|
|
589
|
-
async checkAllPasses(workDir: string, task?: DynamicTask): Promise<{
|
|
590
|
-
passed: boolean;
|
|
591
|
-
results: ExtensionTestResult[];
|
|
592
|
-
failedTypes: string[];
|
|
593
|
-
}> {
|
|
594
|
-
const results = await this.runAllTests(workDir, task);
|
|
595
|
-
const failedTypes = results
|
|
596
|
-
.filter(r => !r.passed)
|
|
597
|
-
.map(r => r.type || 'unit');
|
|
598
|
-
|
|
599
|
-
return {
|
|
600
|
-
passed: failedTypes.length === 0,
|
|
601
|
-
results,
|
|
602
|
-
failedTypes,
|
|
603
|
-
};
|
|
604
|
-
}
|
|
605
|
-
|
|
606
444
|
}
|
|
607
445
|
|
|
608
446
|
/**
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 测试产物解读——从 runner 的 stdout 读出「过了没 / 哪些失败」(ADR-0012,判定口径 ADR-0014)
|
|
3
|
+
*
|
|
4
|
+
* 纯函数,字符串进、值出,不做 IO、不跑进程。此前解析器分别 private 关在
|
|
5
|
+
* core/validators/passes-gate.ts(extractCoverage / extractFailures)与
|
|
6
|
+
* gates/acceptance.ts(parseTestOutput)体内:两处只有 jest 一个家族共同识别,用的信号
|
|
7
|
+
* 还不同(`✕` 行 vs `Test Suites:` 汇总行),同一份输出能在一处看出失败、在另一处看不出。
|
|
8
|
+
*
|
|
9
|
+
* 本模块是包内模块,不进 src/index.ts 导出面(对齐 ADR-0009 capabilities-reconcile /
|
|
10
|
+
* ADR-0011 injection-writer 先例)。
|
|
11
|
+
*
|
|
12
|
+
* #93 裁决(ADR-0014):判定依据统一为「退出码为主 + 文本交叉否决」,收在 judgeTestRun
|
|
13
|
+
* 一处,两个门禁都只从它取结论——原 parseTestOutput(纯文本判定)删除。
|
|
14
|
+
*
|
|
15
|
+
* #94 裁决 B(ADR-0015):本模块不读覆盖率。extractCoverage 与其唯一产出
|
|
16
|
+
* (TaskTestResult.coverage)已删除——harness 核心判定不管覆盖率执法,判覆盖率的
|
|
17
|
+
* 两处(`harness passes-gate --coverage`、performance 门禁)各自读 json-summary。
|
|
18
|
+
*
|
|
19
|
+
* extractFailures 的正则、分支顺序、返回形状逐字迁移自 ADR-0012;仍保持原样的已知粗糙处:
|
|
20
|
+
* - 一次跑完收集所有失败(首个失败即定的编排)→ #93 范围外,另票
|
|
21
|
+
*
|
|
22
|
+
* PR #117 实发修正(2026-09-08):四个 runner 正则由「可跨行」收紧为行首锚定/行内匹配。
|
|
23
|
+
* 原 mocha 正则 /\d+\)\s+(.+?):/g 的 \s+ 跨行——绿色运行的 console 噪音里一行以
|
|
24
|
+
* 数字+")"结尾(stack 帧 checker.ts:213:23)、init 日志 (v1.3.0))、下一行含 ":",
|
|
25
|
+
* 就被误捕为失败用例,governance 门禁对全绿 npm test 输出误判 24 条失败(exit 0 遭
|
|
26
|
+
* judgeTestRun 文本否决)。jest ✕ / pytest FAILED / go --- FAIL: 的 \s+ 同理收紧。
|
|
27
|
+
*/
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* 从输出中提取失败信息
|
|
31
|
+
*/
|
|
32
|
+
export function extractFailures(output: string): string[] {
|
|
33
|
+
const failures: string[] = [];
|
|
34
|
+
|
|
35
|
+
// Jest 格式(✕ 与用例名同行;\s 不跨行,防把下一行日志/堆栈捕进来)
|
|
36
|
+
const jestMatches = output.matchAll(/✕[ \t]+([^\n]+?)[ \t]+\(/g);
|
|
37
|
+
for (const match of jestMatches) {
|
|
38
|
+
if (match[1]) failures.push(match[1]);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// Mocha 格式(行首锚定:「N) 标题:」必须是一行的开头形状;
|
|
42
|
+
// 旧写法不锚行首且 \s+ 跨行,绿输出的 console 噪音会被误判——见文件头 PR #117 段)
|
|
43
|
+
const mochaMatches = output.matchAll(/^[ \t]*\d+\)[ \t]+([^\n]+?):/gm);
|
|
44
|
+
for (const match of mochaMatches) {
|
|
45
|
+
if (match[1]) failures.push(match[1]);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// pytest 格式
|
|
49
|
+
const pytestMatches = output.matchAll(/FAILED[ \t]+([^\n]+?)::/g);
|
|
50
|
+
for (const match of pytestMatches) {
|
|
51
|
+
if (match[1]) failures.push(match[1]);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// Go test 格式
|
|
55
|
+
const goMatches = output.matchAll(/--- FAIL:[ \t]+([^\n]+?)[ \t]+\(/g);
|
|
56
|
+
for (const match of goMatches) {
|
|
57
|
+
if (match[1]) failures.push(match[1]);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
return failures;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* 判定结果:过了没,以及文本里读出的失败用例名
|
|
65
|
+
*/
|
|
66
|
+
export interface TestRunVerdict {
|
|
67
|
+
passed: boolean;
|
|
68
|
+
/** 用例级失败名(判负时的可定位线索;退出码非零时也照常带出) */
|
|
69
|
+
failures: string[];
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* 行首锚定的结构化失败信号(ADR-0014)
|
|
74
|
+
*
|
|
75
|
+
* 只认 runner 自己打的汇总/套件行,不认子串:旧 `output.includes('FAIL')` 兜底会被通过的
|
|
76
|
+
* 用例名(`✓ FAIL 时应该重试`)或含 FAIL 的目录名反转成判负。
|
|
77
|
+
* - 套件行 `FAIL src/a.test.ts`(pytest 的 `FAILED x::`、go 的 `--- FAIL:` 行首不是 FAIL,
|
|
78
|
+
* 由 extractFailures 认)
|
|
79
|
+
* - 计数汇总行:jest `Test Suites: 2 failed, ...` / `Tests: 2 failed, ...`,
|
|
80
|
+
* playwright / mocha / pytest 的 ` 1 failed` / ` 1 failing` / `2 failed, 5 passed in 0.88s`。
|
|
81
|
+
* 计数为 0 是全过(`Test Suites: 0 failed, 2 total`),不构成信号
|
|
82
|
+
*/
|
|
83
|
+
const SUITE_FAILURE_LINE = /^FAIL\b.*$/m;
|
|
84
|
+
const SUMMARY_FAILURE_COUNTS: RegExp[] = [
|
|
85
|
+
/^(?:Test Suites|Suites|Tests):\s+(\d+)\s+failed\b/m,
|
|
86
|
+
/^\s*(\d+)\s+(?:failed|failing)\b/m,
|
|
87
|
+
];
|
|
88
|
+
|
|
89
|
+
function hasStructuredFailure(output: string): boolean {
|
|
90
|
+
if (SUITE_FAILURE_LINE.test(output)) return true;
|
|
91
|
+
|
|
92
|
+
return SUMMARY_FAILURE_COUNTS.some(pattern => {
|
|
93
|
+
const match = pattern.exec(output);
|
|
94
|
+
return match !== null && Number(match[1]) > 0;
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* 判一次测试跑动的结论:退出码为主 + 文本交叉否决(#93,ADR-0014)
|
|
100
|
+
*
|
|
101
|
+
* - 退出码非零 → 判负,文本救不回来
|
|
102
|
+
* - 退出码为零但文本有结构化失败信号 → 判负(runner 退出码撒谎的兜底)
|
|
103
|
+
* - 退出码为零且无失败信号 → 判过;「没跑出测试」不在此判定的否决范围内(另票)
|
|
104
|
+
*
|
|
105
|
+
* `allowPartialPass` 的落点是**文本否决**这一维(部分通过 = 允许个别用例失败但命令本身过了);
|
|
106
|
+
* 它不赦免非零退出码。
|
|
107
|
+
*/
|
|
108
|
+
export function judgeTestRun(input: {
|
|
109
|
+
/** 测试命令的退出码(0 = runner 自己说过了) */
|
|
110
|
+
exitCode: number;
|
|
111
|
+
/** runner 的 stdout + stderr 合并文本 */
|
|
112
|
+
output: string;
|
|
113
|
+
/** 允许部分通过:文本失败信号不否决零退出码,缺省 false */
|
|
114
|
+
allowPartialPass?: boolean;
|
|
115
|
+
}): TestRunVerdict {
|
|
116
|
+
const failures = extractFailures(input.output);
|
|
117
|
+
|
|
118
|
+
if (input.exitCode !== 0) {
|
|
119
|
+
return { passed: false, failures };
|
|
120
|
+
}
|
|
121
|
+
if (input.allowPartialPass) {
|
|
122
|
+
return { passed: true, failures };
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
const vetoed = failures.length > 0 || hasStructuredFailure(input.output);
|
|
126
|
+
|
|
127
|
+
return { passed: !vetoed, failures };
|
|
128
|
+
}
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* FailureRecorder 坏行容错测试(harness#96)
|
|
3
|
+
*
|
|
4
|
+
* fixtures 走真实临时文件(mkdtempSync,统一清理见 src/test-setup):
|
|
5
|
+
* failures.log 夹一行坏 JSON 时,failure list|stats 不崩栈、
|
|
6
|
+
* stdout(含 --json)逐字节不变、stderr 出现一次含坏行条数的警告。
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import * as fs from 'fs';
|
|
10
|
+
import { captureIO, type CapturingIO } from '../../cli/command-contract';
|
|
11
|
+
import * as os from 'os';
|
|
12
|
+
import * as path from 'path';
|
|
13
|
+
import { FailureRecorder } from '../recorder';
|
|
14
|
+
import { failureList, failureStats } from '../../cli/commands/failure';
|
|
15
|
+
import { ErrorType, FailureLevel, DEFAULT_FAILURE_LOG_FILE } from '../../types/failure';
|
|
16
|
+
|
|
17
|
+
const CORRUPT_LINE = '{"type":"TEST_FAILED","level":"L1","mess'; // 半写入截断
|
|
18
|
+
|
|
19
|
+
function makeRecorder(logFile: string): FailureRecorder {
|
|
20
|
+
return new FailureRecorder({ logFile });
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function writeLogFile(dir: string, lines: string[]): string {
|
|
24
|
+
// CLI 侧经 getRecorder 读 <projectPath>/<DEFAULT_FAILURE_LOG_FILE>,fixture 必须落在同一真实路径
|
|
25
|
+
const logFile = path.join(dir, DEFAULT_FAILURE_LOG_FILE);
|
|
26
|
+
fs.mkdirSync(path.dirname(logFile), { recursive: true });
|
|
27
|
+
fs.writeFileSync(logFile, lines.join('\n') + '\n', 'utf-8');
|
|
28
|
+
return logFile;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function validRecord(message: string): string {
|
|
32
|
+
return JSON.stringify({
|
|
33
|
+
type: ErrorType.TEST_FAILED,
|
|
34
|
+
level: FailureLevel.L1,
|
|
35
|
+
message,
|
|
36
|
+
timestamp: 1700000000000,
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function captureConsole(): { stdout: string[]; stderr: string[] } {
|
|
41
|
+
const captured = { stdout: [] as string[], stderr: [] as string[] };
|
|
42
|
+
jest.spyOn(console, 'log').mockImplementation((...args: unknown[]) => {
|
|
43
|
+
captured.stdout.push(args.map(String).join(' '));
|
|
44
|
+
});
|
|
45
|
+
jest.spyOn(console, 'error').mockImplementation((...args: unknown[]) => {
|
|
46
|
+
captured.stderr.push(args.map(String).join(' '));
|
|
47
|
+
});
|
|
48
|
+
return captured;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
let io: CapturingIO;
|
|
52
|
+
beforeEach(() => {
|
|
53
|
+
io = captureIO();
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
describe('FailureRecorder 坏行容错(harness#96)', () => {
|
|
57
|
+
let dir: string;
|
|
58
|
+
|
|
59
|
+
beforeEach(() => {
|
|
60
|
+
dir = fs.mkdtempSync(path.join(os.tmpdir(), 'harness-recorder-'));
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
afterEach(() => {
|
|
64
|
+
jest.restoreAllMocks();
|
|
65
|
+
fs.rmSync(dir, { recursive: true, force: true });
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
describe('getHistory()', () => {
|
|
69
|
+
it('坏行不抛:返回全部合法记录', async () => {
|
|
70
|
+
const logFile = writeLogFile(dir, [validRecord('a'), CORRUPT_LINE, validRecord('b')]);
|
|
71
|
+
const records = await makeRecorder(logFile).getHistory();
|
|
72
|
+
expect(records.map(r => r.message)).toEqual(['a', 'b']);
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
it('跳过坏行时 stderr 出现一次含条数的警告', async () => {
|
|
76
|
+
const logFile = writeLogFile(dir, [
|
|
77
|
+
validRecord('a'),
|
|
78
|
+
CORRUPT_LINE,
|
|
79
|
+
CORRUPT_LINE,
|
|
80
|
+
validRecord('b'),
|
|
81
|
+
]);
|
|
82
|
+
const captured = captureConsole();
|
|
83
|
+
await makeRecorder(logFile).getHistory();
|
|
84
|
+
expect(captured.stderr).toHaveLength(1);
|
|
85
|
+
expect(captured.stderr[0]).toContain('2');
|
|
86
|
+
expect(captured.stdout).toEqual([]);
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
it('文件不存在落到空态,无警告', async () => {
|
|
90
|
+
const captured = captureConsole();
|
|
91
|
+
const records = await makeRecorder(path.join(dir, 'missing.log')).getHistory();
|
|
92
|
+
expect(records).toEqual([]);
|
|
93
|
+
expect(captured.stderr).toEqual([]);
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
it('全部行损坏落到空态', async () => {
|
|
97
|
+
const logFile = writeLogFile(dir, [CORRUPT_LINE, 'not-json']);
|
|
98
|
+
const captured = captureConsole();
|
|
99
|
+
const records = await makeRecorder(logFile).getHistory();
|
|
100
|
+
expect(records).toEqual([]);
|
|
101
|
+
expect(captured.stderr).toHaveLength(1);
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
it('limit 只取最近 N 条合法记录', async () => {
|
|
105
|
+
const logFile = writeLogFile(dir, [validRecord('a'), CORRUPT_LINE, validRecord('b'), validRecord('c')]);
|
|
106
|
+
const records = await makeRecorder(logFile).getHistory(2);
|
|
107
|
+
expect(records.map(r => r.message)).toEqual(['b', 'c']);
|
|
108
|
+
});
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
describe('getStats()', () => {
|
|
112
|
+
it('统计口径只计合法记录', async () => {
|
|
113
|
+
const logFile = writeLogFile(dir, [validRecord('a'), CORRUPT_LINE, validRecord('b')]);
|
|
114
|
+
const stats = await makeRecorder(logFile).getStats();
|
|
115
|
+
expect(stats.total).toBe(2);
|
|
116
|
+
});
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
describe('failure list 命令', () => {
|
|
120
|
+
it('坏行不崩、stdout 与无坏行时逐字节一致、stderr 警告一次', async () => {
|
|
121
|
+
writeLogFile(path.join(dir, 'clean'), [validRecord('a'), validRecord('b')]);
|
|
122
|
+
writeLogFile(path.join(dir, 'dirty'), [validRecord('a'), CORRUPT_LINE, validRecord('b')]);
|
|
123
|
+
|
|
124
|
+
// 列表正文走注入 io;损坏行警告由 recorder 自身 console.error 打印
|
|
125
|
+
const cleanIo = captureIO();
|
|
126
|
+
await failureList({ projectPath: path.join(dir, 'clean') }, cleanIo);
|
|
127
|
+
|
|
128
|
+
const dirtyCapture = captureConsole();
|
|
129
|
+
const dirtyIo = captureIO();
|
|
130
|
+
const dirtyResult = await failureList({ projectPath: path.join(dir, 'dirty') }, dirtyIo);
|
|
131
|
+
expect(dirtyResult).toEqual({ kind: 'ok' });
|
|
132
|
+
|
|
133
|
+
expect(dirtyIo.outText()).toBe(cleanIo.outText());
|
|
134
|
+
expect(dirtyIo.outText()).not.toBe('');
|
|
135
|
+
expect(dirtyCapture.stderr).toHaveLength(1);
|
|
136
|
+
expect(dirtyCapture.stderr[0]).toContain('1');
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
it('--json 输出不受 stderr 警告污染且不含坏行', async () => {
|
|
140
|
+
writeLogFile(dir, [validRecord('a'), CORRUPT_LINE]);
|
|
141
|
+
const captured = captureConsole();
|
|
142
|
+
await failureList({ projectPath: dir, json: true }, io);
|
|
143
|
+
|
|
144
|
+
expect(captured.stderr).toHaveLength(1);
|
|
145
|
+
const parsed = JSON.parse(io.outText());
|
|
146
|
+
expect(parsed.total).toBe(1);
|
|
147
|
+
expect(parsed.records[0].message).toBe('a');
|
|
148
|
+
});
|
|
149
|
+
});
|
|
150
|
+
|
|
151
|
+
describe('failure stats 命令', () => {
|
|
152
|
+
it('坏行不崩、stdout 与无坏行时逐字节一致', async () => {
|
|
153
|
+
writeLogFile(path.join(dir, 'clean'), [validRecord('a'), validRecord('b')]);
|
|
154
|
+
writeLogFile(path.join(dir, 'dirty'), [CORRUPT_LINE, validRecord('a'), validRecord('b')]);
|
|
155
|
+
|
|
156
|
+
const cleanIo = captureIO();
|
|
157
|
+
await failureStats({ projectPath: path.join(dir, 'clean') }, cleanIo);
|
|
158
|
+
|
|
159
|
+
const dirtyCapture = captureConsole();
|
|
160
|
+
const dirtyIo = captureIO();
|
|
161
|
+
await failureStats({ projectPath: path.join(dir, 'dirty') }, dirtyIo);
|
|
162
|
+
|
|
163
|
+
expect(dirtyIo.outText()).toBe(cleanIo.outText());
|
|
164
|
+
expect(dirtyIo.outText()).not.toBe('');
|
|
165
|
+
expect(dirtyCapture.stderr).toHaveLength(1);
|
|
166
|
+
});
|
|
167
|
+
});
|
|
168
|
+
});
|