@dommaker/harness 1.7.0 → 1.8.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 +37 -0
- package/bin/harness.js +17 -1
- package/dist/agents/index.d.ts +6 -2
- package/dist/agents/index.d.ts.map +1 -1
- package/dist/agents/index.js +6 -16
- package/dist/agents/index.js.map +1 -1
- package/dist/cli/commands/__tests__/gate-decision.d.ts +18 -0
- package/dist/cli/commands/__tests__/gate-decision.d.ts.map +1 -0
- package/dist/cli/commands/__tests__/gate-decision.js +26 -0
- package/dist/cli/commands/__tests__/gate-decision.js.map +1 -0
- package/dist/cli/commands/acceptance.d.ts.map +1 -1
- package/dist/cli/commands/acceptance.js +17 -28
- package/dist/cli/commands/acceptance.js.map +1 -1
- package/dist/cli/commands/check.d.ts +11 -3
- package/dist/cli/commands/check.d.ts.map +1 -1
- package/dist/cli/commands/check.js +26 -11
- package/dist/cli/commands/check.js.map +1 -1
- package/dist/cli/commands/command.d.ts +0 -1
- package/dist/cli/commands/command.d.ts.map +1 -1
- package/dist/cli/commands/command.js +8 -6
- package/dist/cli/commands/command.js.map +1 -1
- package/dist/cli/commands/constraints-retire.d.ts.map +1 -1
- package/dist/cli/commands/constraints-retire.js +36 -57
- package/dist/cli/commands/constraints-retire.js.map +1 -1
- package/dist/cli/commands/contract.d.ts.map +1 -1
- package/dist/cli/commands/contract.js +36 -42
- package/dist/cli/commands/contract.js.map +1 -1
- package/dist/cli/commands/definitions.d.ts +2 -0
- package/dist/cli/commands/definitions.d.ts.map +1 -1
- package/dist/cli/commands/definitions.js +11 -6
- package/dist/cli/commands/definitions.js.map +1 -1
- package/dist/cli/commands/failure.d.ts.map +1 -1
- package/dist/cli/commands/failure.js +16 -2
- package/dist/cli/commands/failure.js.map +1 -1
- package/dist/cli/commands/init.d.ts +14 -8
- package/dist/cli/commands/init.d.ts.map +1 -1
- package/dist/cli/commands/init.js +172 -364
- package/dist/cli/commands/init.js.map +1 -1
- package/dist/cli/commands/knowledge-view.d.ts +90 -0
- package/dist/cli/commands/knowledge-view.d.ts.map +1 -0
- package/dist/cli/commands/knowledge-view.js +198 -0
- package/dist/cli/commands/knowledge-view.js.map +1 -0
- package/dist/cli/commands/knowledge.d.ts +137 -52
- package/dist/cli/commands/knowledge.d.ts.map +1 -1
- package/dist/cli/commands/knowledge.js +534 -400
- package/dist/cli/commands/knowledge.js.map +1 -1
- package/dist/cli/commands/passes-gate.d.ts +4 -1
- package/dist/cli/commands/passes-gate.d.ts.map +1 -1
- package/dist/cli/commands/passes-gate.js +11 -2
- package/dist/cli/commands/passes-gate.js.map +1 -1
- package/dist/cli/commands/performance.d.ts.map +1 -1
- package/dist/cli/commands/performance.js +19 -31
- package/dist/cli/commands/performance.js.map +1 -1
- package/dist/cli/commands/report.d.ts.map +1 -1
- package/dist/cli/commands/report.js +2 -1
- package/dist/cli/commands/report.js.map +1 -1
- package/dist/cli/commands/review.d.ts.map +1 -1
- package/dist/cli/commands/review.js +13 -25
- package/dist/cli/commands/review.js.map +1 -1
- package/dist/cli/commands/scaffold-templates.d.ts +73 -0
- package/dist/cli/commands/scaffold-templates.d.ts.map +1 -0
- package/dist/cli/commands/scaffold-templates.js +424 -0
- package/dist/cli/commands/scaffold-templates.js.map +1 -0
- package/dist/cli/commands/scaffold.d.ts +105 -0
- package/dist/cli/commands/scaffold.d.ts.map +1 -0
- package/dist/cli/commands/scaffold.js +263 -0
- package/dist/cli/commands/scaffold.js.map +1 -0
- package/dist/cli/commands/security.d.ts.map +1 -1
- package/dist/cli/commands/security.js +22 -24
- package/dist/cli/commands/security.js.map +1 -1
- package/dist/cli/commands/spec-baseline-check.d.ts +43 -0
- package/dist/cli/commands/spec-baseline-check.d.ts.map +1 -1
- package/dist/cli/commands/spec-baseline-check.js +109 -34
- package/dist/cli/commands/spec-baseline-check.js.map +1 -1
- package/dist/cli/commands/sync-docs/context-syncer.d.ts +21 -2
- package/dist/cli/commands/sync-docs/context-syncer.d.ts.map +1 -1
- package/dist/cli/commands/sync-docs/context-syncer.js +112 -28
- package/dist/cli/commands/sync-docs/context-syncer.js.map +1 -1
- package/dist/cli/commands/sync-docs/index.d.ts +1 -1
- package/dist/cli/commands/sync-docs/index.d.ts.map +1 -1
- package/dist/cli/commands/sync-docs/index.js +86 -27
- package/dist/cli/commands/sync-docs/index.js.map +1 -1
- package/dist/cli/commands/sync-docs/project-reader.d.ts +24 -1
- package/dist/cli/commands/sync-docs/project-reader.d.ts.map +1 -1
- package/dist/cli/commands/sync-docs/project-reader.js +33 -7
- package/dist/cli/commands/sync-docs/project-reader.js.map +1 -1
- package/dist/cli/commands/validate.d.ts +0 -16
- package/dist/cli/commands/validate.d.ts.map +1 -1
- package/dist/cli/commands/validate.js +2 -97
- package/dist/cli/commands/validate.js.map +1 -1
- package/dist/cli/gate-command.d.ts +37 -0
- package/dist/cli/gate-command.d.ts.map +1 -0
- package/dist/cli/gate-command.js +64 -0
- package/dist/cli/gate-command.js.map +1 -0
- package/dist/completion-checkers/index.d.ts +1 -1
- package/dist/completion-checkers/index.d.ts.map +1 -1
- package/dist/completion-checkers/index.js +0 -15
- package/dist/completion-checkers/index.js.map +1 -1
- package/dist/context/knowledge-injector.d.ts +4 -2
- package/dist/context/knowledge-injector.d.ts.map +1 -1
- package/dist/context/knowledge-injector.js +13 -6
- package/dist/context/knowledge-injector.js.map +1 -1
- package/dist/core/constraints/agent-prompt-renderer.js +2 -2
- package/dist/core/constraints/agent-prompt-renderer.js.map +1 -1
- package/dist/core/constraints/capabilities-reconcile.d.ts +20 -0
- package/dist/core/constraints/capabilities-reconcile.d.ts.map +1 -1
- package/dist/core/constraints/capabilities-reconcile.js +26 -4
- package/dist/core/constraints/capabilities-reconcile.js.map +1 -1
- package/dist/core/constraints/check-cache.d.ts +1 -1
- package/dist/core/constraints/check-cache.js +1 -1
- package/dist/core/constraints/checker.d.ts +8 -3
- package/dist/core/constraints/checker.d.ts.map +1 -1
- package/dist/core/constraints/checker.js +30 -19
- package/dist/core/constraints/checker.js.map +1 -1
- package/dist/core/constraints/checkers/capability-sync.d.ts +3 -1
- package/dist/core/constraints/checkers/capability-sync.d.ts.map +1 -1
- package/dist/core/constraints/checkers/capability-sync.js +17 -24
- package/dist/core/constraints/checkers/capability-sync.js.map +1 -1
- package/dist/core/constraints/checkers/context-doc-sync.d.ts +1 -1
- package/dist/core/constraints/checkers/context-doc-sync.d.ts.map +1 -1
- package/dist/core/constraints/checkers/context-doc-sync.js +9 -6
- 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 +24 -30
- package/dist/core/constraints/checkers/docs-freshness.js.map +1 -1
- package/dist/core/constraints/checkers/governance-presence.d.ts.map +1 -1
- package/dist/core/constraints/checkers/governance-presence.js +10 -4
- package/dist/core/constraints/checkers/governance-presence.js.map +1 -1
- package/dist/core/constraints/checkers/types.d.ts +10 -6
- package/dist/core/constraints/checkers/types.d.ts.map +1 -1
- package/dist/core/constraints/checkers/types.js +7 -3
- package/dist/core/constraints/checkers/types.js.map +1 -1
- package/dist/core/constraints/context-builder.d.ts +11 -10
- package/dist/core/constraints/context-builder.d.ts.map +1 -1
- package/dist/core/constraints/context-builder.js +31 -27
- package/dist/core/constraints/context-builder.js.map +1 -1
- package/dist/core/constraints/context-reconcile.d.ts +75 -0
- package/dist/core/constraints/context-reconcile.d.ts.map +1 -0
- package/dist/core/constraints/context-reconcile.js +161 -0
- package/dist/core/constraints/context-reconcile.js.map +1 -0
- package/dist/core/constraints/doc-freshness/runner.d.ts +7 -0
- package/dist/core/constraints/doc-freshness/runner.d.ts.map +1 -1
- package/dist/core/constraints/doc-freshness/runner.js +8 -3
- package/dist/core/constraints/doc-freshness/runner.js.map +1 -1
- package/dist/core/constraints/git-evidence.js +1 -1
- package/dist/core/constraints/git-evidence.js.map +1 -1
- package/dist/core/constraints/injection-drift.d.ts +7 -2
- package/dist/core/constraints/injection-drift.d.ts.map +1 -1
- package/dist/core/constraints/injection-drift.js +13 -7
- package/dist/core/constraints/injection-drift.js.map +1 -1
- package/dist/core/constraints/run-env.d.ts +92 -0
- package/dist/core/constraints/run-env.d.ts.map +1 -0
- package/dist/core/constraints/run-env.js +175 -0
- package/dist/core/constraints/run-env.js.map +1 -0
- package/dist/core/constraints/triggers.d.ts +22 -0
- package/dist/core/constraints/triggers.d.ts.map +1 -0
- package/dist/core/constraints/triggers.js +31 -0
- package/dist/core/constraints/triggers.js.map +1 -0
- package/dist/core/effective-constraints.d.ts +13 -4
- package/dist/core/effective-constraints.d.ts.map +1 -1
- package/dist/core/effective-constraints.js +16 -6
- package/dist/core/effective-constraints.js.map +1 -1
- package/dist/core/effective-set.d.ts +8 -15
- package/dist/core/effective-set.d.ts.map +1 -1
- package/dist/core/effective-set.js +7 -12
- package/dist/core/effective-set.js.map +1 -1
- package/dist/core/index.d.ts +1 -0
- package/dist/core/index.d.ts.map +1 -1
- package/dist/core/index.js.map +1 -1
- package/dist/core/project-config-loader.d.ts +39 -17
- package/dist/core/project-config-loader.d.ts.map +1 -1
- package/dist/core/project-config-loader.js +51 -122
- package/dist/core/project-config-loader.js.map +1 -1
- package/dist/core/validators/check-handlers/http.d.ts +2 -2
- package/dist/core/validators/check-handlers/http.d.ts.map +1 -1
- package/dist/core/validators/check-handlers/http.js +12 -7
- package/dist/core/validators/check-handlers/http.js.map +1 -1
- package/dist/failure/classifier.d.ts +1 -1
- package/dist/failure/classifier.d.ts.map +1 -1
- package/dist/failure/classifier.js +8 -8
- package/dist/failure/classifier.js.map +1 -1
- package/dist/failure/index.d.ts +2 -1
- package/dist/failure/index.d.ts.map +1 -1
- package/dist/failure/index.js +9 -17
- package/dist/failure/index.js.map +1 -1
- package/dist/failure/recorder.d.ts +1 -1
- package/dist/failure/recorder.d.ts.map +1 -1
- package/dist/gates/checker-gate.d.ts +2 -1
- package/dist/gates/checker-gate.d.ts.map +1 -1
- package/dist/gates/checker-gate.js +3 -2
- package/dist/gates/checker-gate.js.map +1 -1
- package/dist/gates/command.d.ts +10 -4
- package/dist/gates/command.d.ts.map +1 -1
- package/dist/gates/command.js +36 -62
- package/dist/gates/command.js.map +1 -1
- package/dist/gates/contract.d.ts.map +1 -1
- package/dist/gates/contract.js +0 -4
- package/dist/gates/contract.js.map +1 -1
- package/dist/gates/definitions.d.ts +2 -1
- package/dist/gates/definitions.d.ts.map +1 -1
- package/dist/gates/definitions.js +6 -5
- package/dist/gates/definitions.js.map +1 -1
- package/dist/gates/index.d.ts +0 -4
- package/dist/gates/index.d.ts.map +1 -1
- package/dist/gates/index.js +1 -6
- package/dist/gates/index.js.map +1 -1
- package/dist/gates/performance.d.ts.map +1 -1
- package/dist/gates/performance.js +0 -4
- package/dist/gates/performance.js.map +1 -1
- package/dist/gates/runner.d.ts +1 -1
- package/dist/gates/runner.js +1 -1
- package/dist/gates/security.d.ts +1 -1
- package/dist/gates/security.d.ts.map +1 -1
- package/dist/gates/security.js +5 -6
- package/dist/gates/security.js.map +1 -1
- package/dist/gates/types.d.ts +14 -17
- package/dist/gates/types.d.ts.map +1 -1
- package/dist/gates/types.js.map +1 -1
- package/dist/hooks/bootstrap.d.ts +6 -4
- package/dist/hooks/bootstrap.d.ts.map +1 -1
- package/dist/hooks/bootstrap.js +25 -13
- package/dist/hooks/bootstrap.js.map +1 -1
- package/dist/hooks/config.d.ts +6 -2
- package/dist/hooks/config.d.ts.map +1 -1
- package/dist/hooks/config.js +5 -1
- package/dist/hooks/config.js.map +1 -1
- package/dist/hooks/index.d.ts +1 -1
- package/dist/hooks/index.d.ts.map +1 -1
- package/dist/hooks/index.js +0 -15
- package/dist/hooks/index.js.map +1 -1
- package/dist/hooks/pipeline.d.ts.map +1 -1
- package/dist/hooks/pipeline.js +2 -4
- package/dist/hooks/pipeline.js.map +1 -1
- package/dist/hooks/registry.d.ts +11 -8
- package/dist/hooks/registry.d.ts.map +1 -1
- package/dist/hooks/registry.js +22 -7
- package/dist/hooks/registry.js.map +1 -1
- package/dist/hooks/types.d.ts +23 -7
- package/dist/hooks/types.d.ts.map +1 -1
- package/dist/index.d.ts +3 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -4
- package/dist/index.js.map +1 -1
- package/dist/knowledge/audit-scoring.d.ts +87 -0
- package/dist/knowledge/audit-scoring.d.ts.map +1 -0
- package/dist/knowledge/audit-scoring.js +470 -0
- package/dist/knowledge/audit-scoring.js.map +1 -0
- package/dist/knowledge/audit.d.ts +11 -59
- package/dist/knowledge/audit.d.ts.map +1 -1
- package/dist/knowledge/audit.js +47 -522
- package/dist/knowledge/audit.js.map +1 -1
- package/dist/knowledge/import.d.ts.map +1 -1
- package/dist/knowledge/import.js +2 -0
- package/dist/knowledge/import.js.map +1 -1
- package/dist/knowledge/index-generator.d.ts.map +1 -1
- package/dist/knowledge/index-generator.js +4 -5
- package/dist/knowledge/index-generator.js.map +1 -1
- package/dist/knowledge/index.d.ts +3 -2
- package/dist/knowledge/index.d.ts.map +1 -1
- package/dist/knowledge/index.js +3 -16
- package/dist/knowledge/index.js.map +1 -1
- package/dist/knowledge/ingest.js +1 -1
- package/dist/knowledge/ingest.js.map +1 -1
- package/dist/knowledge/lifecycle.d.ts.map +1 -1
- package/dist/knowledge/lifecycle.js +4 -2
- package/dist/knowledge/lifecycle.js.map +1 -1
- package/dist/knowledge/migration.d.ts +3 -0
- package/dist/knowledge/migration.d.ts.map +1 -1
- package/dist/knowledge/migration.js +8 -2
- package/dist/knowledge/migration.js.map +1 -1
- package/dist/knowledge/query.d.ts +6 -0
- package/dist/knowledge/query.d.ts.map +1 -1
- package/dist/knowledge/query.js +8 -2
- package/dist/knowledge/query.js.map +1 -1
- package/dist/knowledge/reference-tracker.d.ts.map +1 -1
- package/dist/knowledge/reference-tracker.js +4 -6
- package/dist/knowledge/reference-tracker.js.map +1 -1
- package/dist/knowledge/store.d.ts +22 -16
- package/dist/knowledge/store.d.ts.map +1 -1
- package/dist/knowledge/store.js +53 -12
- package/dist/knowledge/store.js.map +1 -1
- package/dist/knowledge/tree-walker.d.ts +19 -0
- package/dist/knowledge/tree-walker.d.ts.map +1 -0
- package/dist/knowledge/tree-walker.js +30 -0
- package/dist/knowledge/tree-walker.js.map +1 -0
- package/dist/knowledge/types.d.ts +16 -0
- package/dist/knowledge/types.d.ts.map +1 -1
- package/dist/monitoring/traces.d.ts +5 -3
- package/dist/monitoring/traces.d.ts.map +1 -1
- package/dist/monitoring/traces.js +19 -5
- package/dist/monitoring/traces.js.map +1 -1
- package/dist/test-setup/jsonl-fake-fs.d.ts +30 -0
- package/dist/test-setup/jsonl-fake-fs.d.ts.map +1 -0
- package/dist/test-setup/jsonl-fake-fs.js +49 -0
- package/dist/test-setup/jsonl-fake-fs.js.map +1 -0
- package/dist/tools/index.d.ts +4 -1
- package/dist/tools/index.d.ts.map +1 -1
- package/dist/tools/index.js +7 -15
- package/dist/tools/index.js.map +1 -1
- package/dist/types/checkpoint.d.ts +5 -0
- package/dist/types/checkpoint.d.ts.map +1 -1
- package/dist/types/project-config.d.ts +18 -0
- package/dist/types/project-config.d.ts.map +1 -1
- package/dist/types/trace.d.ts +13 -4
- package/dist/types/trace.d.ts.map +1 -1
- package/dist/types/trace.js +4 -3
- package/dist/types/trace.js.map +1 -1
- package/dist/utils/exec.d.ts +4 -27
- package/dist/utils/exec.d.ts.map +1 -1
- package/dist/utils/exec.js +4 -94
- package/dist/utils/exec.js.map +1 -1
- package/dist/utils/jsonl.d.ts +17 -7
- package/dist/utils/jsonl.d.ts.map +1 -1
- package/dist/utils/jsonl.js +144 -17
- package/dist/utils/jsonl.js.map +1 -1
- package/dist/utils/numeric-flag.d.ts +44 -0
- package/dist/utils/numeric-flag.d.ts.map +1 -0
- package/dist/utils/numeric-flag.js +62 -0
- package/dist/utils/numeric-flag.js.map +1 -0
- package/package.json +1 -1
- package/src/CONTEXT.md +5 -2
- package/src/__tests__/acceptance-gate.test.ts +7 -4
- package/src/__tests__/checker-extra.test.ts +3 -2
- package/src/__tests__/checker.test.ts +4 -3
- package/src/__tests__/checkpoint.test.ts +26 -9
- package/src/__tests__/context-files-resolution.test.ts +29 -15
- package/src/__tests__/contract-gate.test.ts +19 -40
- package/src/__tests__/error-classifier.test.ts +1 -1
- package/src/__tests__/exec.test.ts +45 -107
- package/src/__tests__/failure-recorder.test.ts +7 -5
- package/src/__tests__/hooks-pipeline.test.ts +34 -47
- package/src/__tests__/layering.test.ts +217 -25
- package/src/__tests__/passes-gate.test.ts +9 -4
- package/src/__tests__/performance-gate.test.ts +3 -15
- package/src/__tests__/project-config-loader-extra.test.ts +31 -57
- package/src/__tests__/project-config-loader.test.ts +6 -3
- package/src/__tests__/public-exports.test.ts +0 -1
- package/src/__tests__/public-type-surface.test.ts +5 -4
- package/src/__tests__/public-value-surface.test.ts +182 -0
- package/src/__tests__/review-gate.test.ts +4 -6
- package/src/__tests__/security-gate.test.ts +5 -20
- package/src/__tests__/skip-semantics.test.ts +5 -3
- package/src/__tests__/spec-validator.test.ts +3 -2
- package/src/__tests__/sub-barrels-explicit.test.ts +87 -0
- package/src/__tests__/test-fixture-hygiene.test.ts +53 -0
- package/src/__tests__/trace-analyzer-extra.test.ts +5 -3
- package/src/__tests__/trace-analyzer.test.ts +5 -3
- package/src/__tests__/trace-collector.test.ts +5 -3
- package/src/agents/index.ts +13 -2
- package/src/cli/__tests__/gate-command.test.ts +162 -0
- package/src/cli/commands/CONTEXT.md +29 -5
- package/src/cli/commands/__tests__/acceptance.test.ts +24 -25
- package/src/cli/commands/__tests__/bin-exit-mapping.test.ts +15 -0
- package/src/cli/commands/__tests__/check-drift.test.ts +42 -1
- package/src/cli/commands/__tests__/check-read-count.test.ts +167 -0
- package/src/cli/commands/__tests__/check-skip-output.test.ts +4 -1
- package/src/cli/commands/__tests__/check.test.ts +43 -36
- package/src/cli/commands/__tests__/command-gate-dead-options.test.ts +60 -0
- package/src/cli/commands/__tests__/command.test.ts +33 -17
- package/src/cli/commands/__tests__/constraints-retire.test.ts +52 -0
- package/src/cli/commands/__tests__/context-syncer.test.ts +141 -0
- package/src/cli/commands/__tests__/contract.test.ts +20 -20
- package/src/cli/commands/__tests__/failure.test.ts +5 -4
- package/src/cli/commands/__tests__/gate-decision.ts +30 -0
- package/src/cli/commands/__tests__/init-dead-options.test.ts +50 -0
- package/src/cli/commands/__tests__/init-governance-io.test.ts +139 -0
- package/src/cli/commands/__tests__/init-injection.test.ts +3 -3
- package/src/cli/commands/__tests__/init-ondisk.test.ts +867 -0
- package/src/cli/commands/__tests__/init.test.ts +82 -6
- package/src/cli/commands/__tests__/knowledge-audit-threshold.test.ts +146 -0
- package/src/cli/commands/__tests__/knowledge-flywheel-consistency.test.ts +1 -1
- package/src/cli/commands/__tests__/knowledge-tone-mapping.test.ts +244 -0
- package/src/cli/commands/__tests__/knowledge-view.test.ts +839 -0
- package/src/cli/commands/__tests__/knowledge.test.ts +33 -15
- package/src/cli/commands/__tests__/numeric-flag-assembly.test.ts +368 -0
- package/src/cli/commands/__tests__/performance.test.ts +19 -21
- package/src/cli/commands/__tests__/posteval-plan.test.ts +5 -3
- package/src/cli/commands/__tests__/pre-push-hook.test.ts +181 -0
- package/src/cli/commands/__tests__/project-path-anchoring.test.ts +95 -2
- package/src/cli/commands/__tests__/project-path-convention.test.ts +103 -25
- package/src/cli/commands/__tests__/registry.test.ts +2 -0
- package/src/cli/commands/__tests__/report.test.ts +23 -1
- package/src/cli/commands/__tests__/review.test.ts +10 -17
- package/src/cli/commands/__tests__/scaffold.test.ts +505 -0
- package/src/cli/commands/__tests__/security-default-scan.test.ts +75 -0
- package/src/cli/commands/__tests__/security.test.ts +21 -24
- package/src/cli/commands/__tests__/spec-baseline-check.test.ts +3 -2
- package/src/cli/commands/__tests__/spec-baseline-index-cap.test.ts +131 -0
- package/src/cli/commands/__tests__/spec-baseline-read-count.test.ts +325 -0
- package/src/cli/commands/__tests__/sync-docs-agents.test.ts +19 -4
- package/src/cli/commands/__tests__/sync-docs-context-drift.test.ts +232 -0
- package/src/cli/commands/__tests__/sync-docs-module-order.test.ts +111 -0
- package/src/cli/commands/__tests__/sync-docs-read-count.test.ts +192 -0
- package/src/cli/commands/__tests__/sync-docs.test.ts +3 -2
- package/src/cli/commands/__tests__/validate.test.ts +1 -48
- package/src/cli/commands/acceptance.ts +24 -29
- package/src/cli/commands/check.ts +40 -17
- package/src/cli/commands/command.ts +9 -8
- package/src/cli/commands/constraints-retire.ts +57 -70
- package/src/cli/commands/contract.ts +44 -47
- package/src/cli/commands/definitions.ts +12 -6
- package/src/cli/commands/failure.ts +16 -3
- package/src/cli/commands/init.ts +242 -389
- package/src/cli/commands/knowledge-view.ts +224 -0
- package/src/cli/commands/knowledge.ts +643 -448
- package/src/cli/commands/passes-gate.ts +12 -3
- package/src/cli/commands/performance.ts +31 -38
- package/src/cli/commands/report.ts +3 -2
- package/src/cli/commands/review.ts +22 -28
- package/src/cli/commands/scaffold-templates.ts +406 -0
- package/src/cli/commands/scaffold.ts +302 -0
- package/src/cli/commands/security.ts +27 -23
- package/src/cli/commands/spec-baseline-check.ts +158 -33
- package/src/cli/commands/sync-docs/__tests__/context-export-surface.test.ts +102 -0
- package/src/cli/commands/sync-docs/__tests__/module-descriptions.test.ts +196 -0
- package/src/cli/commands/sync-docs/context-syncer.ts +137 -31
- package/src/cli/commands/sync-docs/index.ts +103 -28
- package/src/cli/commands/sync-docs/project-reader.ts +51 -7
- package/src/cli/commands/validate.ts +1 -104
- package/src/cli/gate-command.ts +87 -0
- package/src/completion-checkers/index.ts +10 -1
- package/src/context/CONTEXT.md +2 -1
- package/src/context/__tests__/knowledge-injector.test.ts +27 -0
- package/src/context/__tests__/session-manager.test.ts +19 -12
- package/src/context/knowledge-injector.ts +14 -7
- package/src/core/CONTEXT.md +10 -7
- package/src/core/__tests__/effective-set.test.ts +36 -60
- package/src/core/constraints/__tests__/context-reconcile.test.ts +246 -0
- package/src/core/constraints/__tests__/run-env.test.ts +377 -0
- package/src/core/constraints/__tests__/triggers.test.ts +70 -0
- package/src/core/constraints/agent-prompt-renderer.ts +1 -1
- package/src/core/constraints/capabilities-reconcile.ts +28 -4
- package/src/core/constraints/check-cache.ts +1 -1
- package/src/core/constraints/checker.ts +30 -14
- package/src/core/constraints/checkers/__tests__/context-doc-sync.test.ts +8 -3
- package/src/core/constraints/checkers/__tests__/governance-presence.test.ts +16 -7
- package/src/core/constraints/checkers/__tests__/no-hardcoded-credentials.test.ts +2 -1
- package/src/core/constraints/checkers/capability-sync.ts +27 -28
- package/src/core/constraints/checkers/context-doc-sync.ts +9 -7
- package/src/core/constraints/checkers/docs-freshness.ts +28 -33
- package/src/core/constraints/checkers/governance-presence.ts +10 -6
- package/src/core/constraints/checkers/types.ts +14 -8
- package/src/core/constraints/context-builder.ts +35 -26
- package/src/core/constraints/context-reconcile.ts +200 -0
- package/src/core/constraints/doc-freshness/__tests__/runner.test.ts +25 -0
- package/src/core/constraints/doc-freshness/runner.ts +18 -4
- package/src/core/constraints/git-evidence.ts +1 -1
- package/src/core/constraints/injection-drift.ts +19 -8
- package/src/core/constraints/run-env.ts +207 -0
- package/src/core/constraints/triggers.ts +33 -0
- package/src/core/effective-constraints.ts +17 -6
- package/src/core/effective-set.ts +8 -24
- package/src/core/index.ts +2 -0
- package/src/core/project-config-loader.ts +54 -102
- package/src/core/validators/__tests__/http-retry-sleep.test.ts +231 -0
- package/src/core/validators/check-handlers/http.ts +18 -7
- package/src/failure/CONTEXT.md +2 -1
- package/src/failure/classifier.ts +2 -2
- package/src/failure/index.ts +14 -2
- package/src/failure/recorder.ts +1 -1
- package/src/gates/CONTEXT.md +13 -7
- package/src/gates/__tests__/checker-gate.test.ts +1 -1
- package/src/gates/__tests__/command-single-predicate.test.ts +135 -0
- package/src/gates/__tests__/command.test.ts +8 -15
- package/src/gates/__tests__/contract.test.ts +5 -5
- package/src/gates/__tests__/gate-interface.test.ts +0 -17
- package/src/gates/__tests__/performance.test.ts +0 -13
- package/src/gates/__tests__/review.test.ts +0 -1
- package/src/gates/__tests__/runner.test.ts +1 -1
- package/src/gates/__tests__/security-default-scan.test.ts +97 -0
- package/src/gates/__tests__/security.test.ts +0 -13
- package/src/gates/checker-gate.ts +3 -2
- package/src/gates/command.ts +51 -74
- package/src/gates/contract.ts +0 -5
- package/src/gates/definitions.ts +6 -5
- package/src/gates/index.ts +0 -5
- package/src/gates/performance.ts +1 -6
- package/src/gates/runner.ts +1 -1
- package/src/gates/security.ts +9 -11
- package/src/gates/types.ts +20 -23
- package/src/hooks/CONTEXT.md +10 -9
- package/src/hooks/__tests__/bootstrap.test.ts +29 -6
- package/src/hooks/__tests__/pipeline.test.ts +28 -17
- package/src/hooks/__tests__/registry.test.ts +51 -3
- package/src/hooks/bootstrap.ts +35 -15
- package/src/hooks/config.ts +6 -2
- package/src/hooks/index.ts +10 -1
- package/src/hooks/pipeline.ts +4 -6
- package/src/hooks/registry.ts +31 -12
- package/src/hooks/types.ts +24 -7
- package/src/index.ts +1 -2
- package/src/knowledge/CONTEXT.md +14 -7
- package/src/knowledge/__tests__/audit-labels.test.ts +8 -5
- package/src/knowledge/__tests__/audit-scoring.test.ts +230 -0
- package/src/knowledge/__tests__/audit-write-count.test.ts +131 -0
- package/src/knowledge/__tests__/audit.test.ts +291 -208
- package/src/knowledge/__tests__/doctor.test.ts +3 -2
- package/src/knowledge/__tests__/ingest.test.ts +3 -2
- package/src/knowledge/__tests__/lifecycle.test.ts +57 -2
- package/src/knowledge/__tests__/migration.test.ts +35 -6
- package/src/knowledge/__tests__/query.test.ts +3 -2
- package/src/knowledge/__tests__/reference-tracker.test.ts +23 -2
- package/src/knowledge/__tests__/store.test.ts +149 -4
- package/src/knowledge/__tests__/tree-walker.test.ts +132 -0
- package/src/knowledge/audit-scoring.ts +589 -0
- package/src/knowledge/audit.ts +55 -599
- package/src/knowledge/import.ts +2 -0
- package/src/knowledge/index-generator.ts +4 -6
- package/src/knowledge/index.ts +27 -2
- package/src/knowledge/ingest.ts +1 -1
- package/src/knowledge/lifecycle.ts +5 -2
- package/src/knowledge/migration.ts +8 -2
- package/src/knowledge/query.ts +8 -1
- package/src/knowledge/reference-tracker.ts +5 -7
- package/src/knowledge/store.ts +75 -16
- package/src/knowledge/tree-walker.ts +29 -0
- package/src/knowledge/types.ts +19 -0
- package/src/monitoring/CONTEXT.md +5 -2
- package/src/monitoring/__tests__/context-tracker.test.ts +16 -8
- package/src/monitoring/__tests__/trace-file-anchoring.test.ts +80 -0
- package/src/monitoring/traces.ts +19 -5
- package/src/release/CONTEXT.md +1 -1
- package/src/release/__tests__/integrity.test.ts +21 -12
- package/src/test-setup/__tests__/jsonl-fake-fs.test.ts +98 -0
- package/src/test-setup/jsonl-fake-fs.ts +59 -0
- package/src/tools/index.ts +5 -1
- package/src/types/checkpoint.ts +6 -0
- package/src/types/project-config.ts +21 -0
- package/src/types/trace.ts +14 -4
- package/src/utils/__tests__/file-walk.test.ts +3 -2
- package/src/utils/__tests__/jsonl-bounded-read.test.ts +106 -0
- package/src/utils/__tests__/jsonl-skip-disposition.test.ts +10 -6
- package/src/utils/__tests__/jsonl.test.ts +61 -14
- package/src/utils/__tests__/numeric-flag.test.ts +80 -0
- package/src/utils/__tests__/package-version.test.ts +2 -2
- package/src/utils/exec.ts +4 -64
- package/src/utils/jsonl.ts +159 -20
- package/src/utils/numeric-flag.ts +64 -0
- package/dist/failure/types.d.ts +0 -9
- package/dist/failure/types.d.ts.map +0 -1
- package/dist/failure/types.js +0 -25
- package/dist/failure/types.js.map +0 -1
- package/dist/gates/effective-gates.d.ts +0 -30
- package/dist/gates/effective-gates.d.ts.map +0 -1
- package/dist/gates/effective-gates.js +0 -81
- package/dist/gates/effective-gates.js.map +0 -1
- package/src/failure/types.ts +0 -9
- package/src/gates/__tests__/effective-gates.test.ts +0 -77
- package/src/gates/effective-gates.ts +0 -99
package/src/knowledge/import.ts
CHANGED
|
@@ -269,6 +269,8 @@ export class ColdStartImporter {
|
|
|
269
269
|
|
|
270
270
|
if (stat.isDirectory()) {
|
|
271
271
|
// 扫描目录下的 markdown 文件
|
|
272
|
+
// 口径记名(harness#134):本 walker 吃的是**项目文档树**(README/docs/),不是知识库树,
|
|
273
|
+
// 故不适用 tree-walker 的条目排除口径——知识树的 `_index.md`/`.snapshots` 在这里没有对应物。
|
|
272
274
|
const files = fs.readdirSync(fullPath)
|
|
273
275
|
.filter(f => f.endsWith('.md'))
|
|
274
276
|
.slice(0, 10); // 最多 10 个
|
|
@@ -10,9 +10,7 @@
|
|
|
10
10
|
import * as fs from 'fs';
|
|
11
11
|
import * as path from 'path';
|
|
12
12
|
import { splitFrontmatter } from '../utils/frontmatter';
|
|
13
|
-
|
|
14
|
-
const EXCLUDED_DIRS = ['.archive', 'archived', '.snapshots', 'resolutions'];
|
|
15
|
-
const INDEX_FILENAME = '_index.md';
|
|
13
|
+
import { INDEX_MD_FILE, isEntryFile, isInfraDir } from './tree-walker';
|
|
16
14
|
|
|
17
15
|
interface IndexEntry {
|
|
18
16
|
filename: string;
|
|
@@ -37,7 +35,7 @@ export class KnowledgeIndexGenerator {
|
|
|
37
35
|
regenerate(): string {
|
|
38
36
|
const output = this.generate();
|
|
39
37
|
fs.mkdirSync(this.baseDir, { recursive: true });
|
|
40
|
-
fs.writeFileSync(path.join(this.baseDir,
|
|
38
|
+
fs.writeFileSync(path.join(this.baseDir, INDEX_MD_FILE), output, 'utf-8');
|
|
41
39
|
return output;
|
|
42
40
|
}
|
|
43
41
|
|
|
@@ -112,10 +110,10 @@ export class KnowledgeIndexGenerator {
|
|
|
112
110
|
const relPath = relative ? path.join(relative, name) : name;
|
|
113
111
|
|
|
114
112
|
if (item.isDirectory()) {
|
|
115
|
-
if (
|
|
113
|
+
if (isInfraDir(name)) continue;
|
|
116
114
|
if (name.startsWith('.')) continue;
|
|
117
115
|
results.push(...this.scanFiles(fullPath, relPath));
|
|
118
|
-
} else if (
|
|
116
|
+
} else if (isEntryFile(name)) {
|
|
119
117
|
results.push(fullPath);
|
|
120
118
|
}
|
|
121
119
|
}
|
package/src/knowledge/index.ts
CHANGED
|
@@ -4,7 +4,32 @@
|
|
|
4
4
|
* Re-exports all knowledge module components.
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
-
export
|
|
7
|
+
export { DEFAULT_DECAY_CONFIG } from './types';
|
|
8
|
+
export type {
|
|
9
|
+
KnowledgeSubsystem,
|
|
10
|
+
MaturityLevel,
|
|
11
|
+
StorageLayer,
|
|
12
|
+
ConsumptionMode,
|
|
13
|
+
KnowledgeOrigin,
|
|
14
|
+
KnowledgeEntry,
|
|
15
|
+
SourceRef,
|
|
16
|
+
ExecutionResult,
|
|
17
|
+
KnowledgeReference,
|
|
18
|
+
QueryBudget,
|
|
19
|
+
QueryResult,
|
|
20
|
+
QueryFilter,
|
|
21
|
+
LintIssueType,
|
|
22
|
+
LintIssue,
|
|
23
|
+
IngestOptions,
|
|
24
|
+
MaturityChange,
|
|
25
|
+
DecayConfig,
|
|
26
|
+
DecisionRecord,
|
|
27
|
+
ReferenceRecord,
|
|
28
|
+
IndexEntry,
|
|
29
|
+
SnapshotSurvival,
|
|
30
|
+
ConsumptionStats,
|
|
31
|
+
StoreUpdate,
|
|
32
|
+
} from './types';
|
|
8
33
|
export type { KnowledgeStore } from './store';
|
|
9
34
|
export { FileKnowledgeStore } from './store';
|
|
10
35
|
export { KnowledgeQuery } from './query';
|
|
@@ -16,7 +41,7 @@ export { KnowledgeLinter } from './lint';
|
|
|
16
41
|
export { ColdStartImporter } from './import';
|
|
17
42
|
export { KnowledgeHealthScorer } from './doctor';
|
|
18
43
|
export { KnowledgeAudit } from './audit';
|
|
19
|
-
export type { AuditRuleName, AuditAction, AuditIssue, AuditReport, AuditOptions } from './audit';
|
|
44
|
+
export type { AuditRuleName, AuditAction, AuditIssue, AuditReport, AuditOptions } from './audit-scoring';
|
|
20
45
|
export { migrateKnowledgeEntries } from './migration';
|
|
21
46
|
export { extractCodeStructure } from './primitives/code-structure';
|
|
22
47
|
export type { CodeStructure, DeclarationInfo, ImportInfo } from './primitives/code-structure';
|
package/src/knowledge/ingest.ts
CHANGED
|
@@ -71,7 +71,7 @@ export class KnowledgeIngest {
|
|
|
71
71
|
const entry = this.buildEntry(partial, options);
|
|
72
72
|
|
|
73
73
|
// Quality gate: audit before saving
|
|
74
|
-
const audit = new KnowledgeAudit(
|
|
74
|
+
const audit = new KnowledgeAudit(this.store);
|
|
75
75
|
let issues = audit.validate(entry);
|
|
76
76
|
const critical = issues.filter(i => i.action === 'reject');
|
|
77
77
|
if (critical.length > 0) {
|
|
@@ -10,6 +10,7 @@ import type {
|
|
|
10
10
|
MaturityLevel,
|
|
11
11
|
MaturityChange,
|
|
12
12
|
DecayConfig,
|
|
13
|
+
StoreUpdate,
|
|
13
14
|
} from './types';
|
|
14
15
|
import { DEFAULT_DECAY_CONFIG } from './types';
|
|
15
16
|
import type { KnowledgeStore } from './store';
|
|
@@ -159,6 +160,7 @@ export class KnowledgeLifecycle {
|
|
|
159
160
|
runDecayCycle(): MaturityChange[] {
|
|
160
161
|
const entries = this.store.list({ excludeArchived: false });
|
|
161
162
|
const changes: MaturityChange[] = [];
|
|
163
|
+
const updates: StoreUpdate[] = [];
|
|
162
164
|
|
|
163
165
|
for (const entry of entries) {
|
|
164
166
|
const targetMaturity = this.checkEntryDecay(entry.id);
|
|
@@ -169,11 +171,12 @@ export class KnowledgeLifecycle {
|
|
|
169
171
|
to: targetMaturity,
|
|
170
172
|
reason: `Auto-decay: ${entry.maturity} → ${targetMaturity} (unreferenced for threshold)`,
|
|
171
173
|
};
|
|
172
|
-
|
|
174
|
+
updates.push({ id: entry.id, partial: { maturity: targetMaturity } });
|
|
173
175
|
changes.push(change);
|
|
174
176
|
}
|
|
175
177
|
}
|
|
176
178
|
|
|
179
|
+
this.store.applyAll(updates);
|
|
177
180
|
return changes;
|
|
178
181
|
}
|
|
179
182
|
|
|
@@ -350,7 +353,7 @@ export class KnowledgeLifecycle {
|
|
|
350
353
|
private checkRuleDecay(entry: KnowledgeEntry): MaturityLevel | undefined {
|
|
351
354
|
if (entry.maturity !== 'active') return undefined;
|
|
352
355
|
const rate = this.getExecutionSuccessRate(entry.id);
|
|
353
|
-
if (rate && rate.total >= RULE_MIN_RESULTS_FOR_DECAY && rate.rate
|
|
356
|
+
if (rate && rate.total >= RULE_MIN_RESULTS_FOR_DECAY && rate.rate <= RULE_FAIL_THRESHOLD) return 'deprecated';
|
|
354
357
|
return undefined;
|
|
355
358
|
}
|
|
356
359
|
|
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
import * as fs from 'fs';
|
|
9
9
|
import * as path from 'path';
|
|
10
10
|
import { splitFrontmatter, joinFrontmatter } from '../utils/frontmatter';
|
|
11
|
+
import { isEntryFile } from './tree-walker';
|
|
11
12
|
|
|
12
13
|
interface MigrationResult {
|
|
13
14
|
total: number;
|
|
@@ -20,6 +21,9 @@ interface MigrationResult {
|
|
|
20
21
|
* Migrate all knowledge .md files in a directory to include
|
|
21
22
|
* consumptionMode and origin fields if missing.
|
|
22
23
|
*
|
|
24
|
+
* 条目人口按 `tree-walker` 的排除口径判定(harness#134):`_index.md` 这类树生成物
|
|
25
|
+
* 既不是迁移对象,也不再计入 `total`/`errors`(此前被报成「no frontmatter found」假阳性)。
|
|
26
|
+
*
|
|
23
27
|
* Existing files without these fields get defaults:
|
|
24
28
|
* - consumptionMode: 'reference'
|
|
25
29
|
* - origin: 'agent'
|
|
@@ -33,7 +37,7 @@ export function migrateKnowledgeEntries(baseDir: string): MigrationResult {
|
|
|
33
37
|
return result;
|
|
34
38
|
}
|
|
35
39
|
|
|
36
|
-
const files = fs.readdirSync(baseDir).filter(
|
|
40
|
+
const files = fs.readdirSync(baseDir).filter(isEntryFile);
|
|
37
41
|
result.total = files.length;
|
|
38
42
|
|
|
39
43
|
for (const file of files) {
|
|
@@ -42,7 +46,9 @@ export function migrateKnowledgeEntries(baseDir: string): MigrationResult {
|
|
|
42
46
|
const raw = fs.readFileSync(filePath, 'utf-8');
|
|
43
47
|
const fm = splitFrontmatter(raw);
|
|
44
48
|
if (fm.state === 'absent') {
|
|
45
|
-
|
|
49
|
+
// 统一口径(harness#89 裁决 2 / harness#161):'absent' 是合法输入、不上报——
|
|
50
|
+
// 按非条目语义静默跳过,计入 skipped(非损坏、非迁移对象,仅保证 total 计数闭环)
|
|
51
|
+
result.skipped++;
|
|
46
52
|
continue;
|
|
47
53
|
}
|
|
48
54
|
if (fm.state === 'malformed') {
|
package/src/knowledge/query.ts
CHANGED
|
@@ -19,6 +19,13 @@ import { KnowledgeLifecycle } from './lifecycle';
|
|
|
19
19
|
// ── Constants ──────────────────────────────────────────────
|
|
20
20
|
|
|
21
21
|
const CACHE_TTL_MS = 5 * 60 * 1000; // 5 minutes
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* 外部来源条目的 prompt 标记(harness#161:三层防御第二层 marking 的唯一正本)。
|
|
25
|
+
* `KnowledgeQuery.formatForPrompt()` 与 `KnowledgeInjector` 的格式化共用,
|
|
26
|
+
* 仓内不允许存在第二份字面量。
|
|
27
|
+
*/
|
|
28
|
+
export const EXTERNAL_SOURCE_MARKER = '[External Source — verify before acting]';
|
|
22
29
|
const MATURITY_RANK: Record<MaturityLevel, number> = {
|
|
23
30
|
proven: 3,
|
|
24
31
|
verified: 2,
|
|
@@ -180,7 +187,7 @@ export class KnowledgeQuery {
|
|
|
180
187
|
*/
|
|
181
188
|
static formatForPrompt(entry: KnowledgeEntry): string {
|
|
182
189
|
const prefix = entry.origin === 'external'
|
|
183
|
-
?
|
|
190
|
+
? `${EXTERNAL_SOURCE_MARKER}\n`
|
|
184
191
|
: '';
|
|
185
192
|
return `${prefix}${entry.title}: ${entry.content}`;
|
|
186
193
|
}
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
|
|
8
8
|
import * as path from 'path';
|
|
9
9
|
import { appendJsonl, readJsonl } from '../utils/jsonl';
|
|
10
|
-
import type { ReferenceRecord } from './types';
|
|
10
|
+
import type { ReferenceRecord, StoreUpdate } from './types';
|
|
11
11
|
import type { KnowledgeStore } from './store';
|
|
12
12
|
|
|
13
13
|
const REFERENCES_FILE = 'references.jsonl';
|
|
@@ -63,6 +63,7 @@ export class ReferenceTracker {
|
|
|
63
63
|
updateReferencedBy(): void {
|
|
64
64
|
const records = this.readAll();
|
|
65
65
|
const entryToDecisions = new Map<string, Set<string>>();
|
|
66
|
+
const updates: StoreUpdate[] = [];
|
|
66
67
|
|
|
67
68
|
for (const record of records) {
|
|
68
69
|
for (const entryId of record.entryIds) {
|
|
@@ -74,13 +75,10 @@ export class ReferenceTracker {
|
|
|
74
75
|
}
|
|
75
76
|
|
|
76
77
|
for (const [entryId, decisions] of entryToDecisions) {
|
|
77
|
-
|
|
78
|
-
if (entry) {
|
|
79
|
-
this.store.update(entryId, {
|
|
80
|
-
referencedBy: [...decisions],
|
|
81
|
-
});
|
|
82
|
-
}
|
|
78
|
+
updates.push({ id: entryId, partial: { referencedBy: [...decisions] } });
|
|
83
79
|
}
|
|
80
|
+
// 未知 id 由 applyAll 逐条跳过(与原先的 store.get 存在性判定同语义)
|
|
81
|
+
this.store.applyAll(updates);
|
|
84
82
|
}
|
|
85
83
|
|
|
86
84
|
// ── Internal ───────────────────────────────────────────────
|
package/src/knowledge/store.ts
CHANGED
|
@@ -9,10 +9,19 @@
|
|
|
9
9
|
import * as fs from 'fs';
|
|
10
10
|
import * as path from 'path';
|
|
11
11
|
import { splitFrontmatter, joinFrontmatter } from '../utils/frontmatter';
|
|
12
|
-
import
|
|
12
|
+
import { isEntryFile, SNAPSHOTS_DIR } from './tree-walker';
|
|
13
|
+
import type {
|
|
14
|
+
KnowledgeEntry,
|
|
15
|
+
IndexEntry,
|
|
16
|
+
QueryFilter,
|
|
17
|
+
ConsumptionStats,
|
|
18
|
+
SnapshotSurvival,
|
|
19
|
+
StoreUpdate,
|
|
20
|
+
} from './types';
|
|
13
21
|
|
|
14
22
|
const DEFAULT_DIR = '.harness/knowledge';
|
|
15
23
|
const INDEX_FILE = 'index.json';
|
|
24
|
+
const CONSUMPTION_STATS_FILE = '.consumption-stats.json';
|
|
16
25
|
|
|
17
26
|
interface StoreConfig {
|
|
18
27
|
baseDir: string;
|
|
@@ -35,6 +44,11 @@ export interface KnowledgeStore {
|
|
|
35
44
|
* 消除逐条 save 的 O(K·N) 全量索引重写。空批为零读写 no-op。
|
|
36
45
|
*/
|
|
37
46
|
saveAll(entries: KnowledgeEntry[]): void;
|
|
47
|
+
/**
|
|
48
|
+
* 按 id 批量部分更新(harness#134):`update(id, partial)` 的批量形,语义与逐条一致
|
|
49
|
+
* (读不到该 id 即跳过),循环内只更新内存索引、结束一次 writeIndex。空批零读写。
|
|
50
|
+
*/
|
|
51
|
+
applyAll(updates: StoreUpdate[]): void;
|
|
38
52
|
delete(id: string): boolean;
|
|
39
53
|
update(id: string, partial: Partial<KnowledgeEntry>): KnowledgeEntry | undefined;
|
|
40
54
|
rebuildIndex(): void;
|
|
@@ -42,7 +56,12 @@ export interface KnowledgeStore {
|
|
|
42
56
|
readEntriesFromDisk(): KnowledgeEntry[];
|
|
43
57
|
snapshot(): string;
|
|
44
58
|
getSnapshot(date: string): IndexEntry[] | undefined;
|
|
45
|
-
getSurvivalRate(daysAgo: number):
|
|
59
|
+
getSurvivalRate(daysAgo: number): SnapshotSurvival | undefined;
|
|
60
|
+
/**
|
|
61
|
+
* 读 `.consumption-stats.json` 的当日消费计数(harness#134:打分核心的 IO 归 store 供给);
|
|
62
|
+
* 文件缺失或损坏 = undefined(无数据,不是零消费)
|
|
63
|
+
*/
|
|
64
|
+
getConsumptionStats(): ConsumptionStats | undefined;
|
|
46
65
|
}
|
|
47
66
|
|
|
48
67
|
/**
|
|
@@ -55,8 +74,8 @@ export class FileKnowledgeStore implements KnowledgeStore {
|
|
|
55
74
|
/**
|
|
56
75
|
* index.json 解析结果的实例级缓存(harness#106)
|
|
57
76
|
*
|
|
58
|
-
* mtimeMs+size
|
|
59
|
-
*
|
|
77
|
+
* mtimeMs+size 指纹(实例级,作用域限于本 store 的一次 list()):
|
|
78
|
+
* 文件未变则复用解析结果,消除一次调用内 N+1 次全量重读;
|
|
60
79
|
* 文件变更(含外部进程改写、删除)自动失效。
|
|
61
80
|
*/
|
|
62
81
|
private indexCache: { mtimeMs: number; size: number; entries: IndexEntry[] } | undefined;
|
|
@@ -90,9 +109,7 @@ export class FileKnowledgeStore implements KnowledgeStore {
|
|
|
90
109
|
}
|
|
91
110
|
|
|
92
111
|
save(entry: KnowledgeEntry): void {
|
|
93
|
-
|
|
94
|
-
const content = joinFrontmatter(this.toFrontmatter(entry), entry.content);
|
|
95
|
-
fs.writeFileSync(filePath, content, 'utf-8');
|
|
112
|
+
this.writeEntryFile(entry);
|
|
96
113
|
this.updateIndexEntry(entry);
|
|
97
114
|
}
|
|
98
115
|
|
|
@@ -101,9 +118,7 @@ export class FileKnowledgeStore implements KnowledgeStore {
|
|
|
101
118
|
const index = this.readIndex();
|
|
102
119
|
const position = new Map(index.map((e, i) => [e.id, i]));
|
|
103
120
|
for (const entry of entries) {
|
|
104
|
-
|
|
105
|
-
const content = joinFrontmatter(this.toFrontmatter(entry), entry.content);
|
|
106
|
-
fs.writeFileSync(filePath, content, 'utf-8');
|
|
121
|
+
this.writeEntryFile(entry);
|
|
107
122
|
const indexEntry = this.toIndexEntry(entry);
|
|
108
123
|
const idx = position.get(entry.id);
|
|
109
124
|
if (idx !== undefined) {
|
|
@@ -116,6 +131,30 @@ export class FileKnowledgeStore implements KnowledgeStore {
|
|
|
116
131
|
this.writeIndex(index);
|
|
117
132
|
}
|
|
118
133
|
|
|
134
|
+
applyAll(updates: StoreUpdate[]): void {
|
|
135
|
+
if (updates.length === 0) return;
|
|
136
|
+
const index = this.readIndex();
|
|
137
|
+
const position = new Map(index.map((e, i) => [e.id, i]));
|
|
138
|
+
let applied = 0;
|
|
139
|
+
for (const { id, partial } of updates) {
|
|
140
|
+
const existing = this.get(id);
|
|
141
|
+
if (!existing) continue;
|
|
142
|
+
const updated: KnowledgeEntry = { ...existing, ...partial, id };
|
|
143
|
+
this.writeEntryFile(updated);
|
|
144
|
+
const indexEntry = this.toIndexEntry(updated);
|
|
145
|
+
const idx = position.get(id);
|
|
146
|
+
if (idx !== undefined) {
|
|
147
|
+
index[idx] = indexEntry;
|
|
148
|
+
} else {
|
|
149
|
+
position.set(id, index.length);
|
|
150
|
+
index.push(indexEntry);
|
|
151
|
+
}
|
|
152
|
+
applied++;
|
|
153
|
+
}
|
|
154
|
+
if (applied === 0) return;
|
|
155
|
+
this.writeIndex(index);
|
|
156
|
+
}
|
|
157
|
+
|
|
119
158
|
delete(id: string): boolean {
|
|
120
159
|
const filePath = this.findFile(id);
|
|
121
160
|
if (!filePath) return false;
|
|
@@ -176,6 +215,15 @@ export class FileKnowledgeStore implements KnowledgeStore {
|
|
|
176
215
|
return path.join(this.baseDir, `${safeType}-${safeId}.md`);
|
|
177
216
|
}
|
|
178
217
|
|
|
218
|
+
/** 条目文件正文的唯一落盘写法(save / saveAll / applyAll 三条路径共用,#134) */
|
|
219
|
+
private writeEntryFile(entry: KnowledgeEntry): void {
|
|
220
|
+
fs.writeFileSync(
|
|
221
|
+
this.entryPath(entry),
|
|
222
|
+
joinFrontmatter(this.toFrontmatter(entry), entry.content),
|
|
223
|
+
'utf-8'
|
|
224
|
+
);
|
|
225
|
+
}
|
|
226
|
+
|
|
179
227
|
private findFile(id: string): string | undefined {
|
|
180
228
|
// 1. Fast path: index has id→type mapping, construct exact filename
|
|
181
229
|
const index = this.readIndex();
|
|
@@ -197,7 +245,7 @@ export class FileKnowledgeStore implements KnowledgeStore {
|
|
|
197
245
|
private listFiles(): string[] {
|
|
198
246
|
if (!fs.existsSync(this.baseDir)) return [];
|
|
199
247
|
return fs.readdirSync(this.baseDir)
|
|
200
|
-
.filter(
|
|
248
|
+
.filter(isEntryFile)
|
|
201
249
|
.map(f => path.join(this.baseDir, f));
|
|
202
250
|
}
|
|
203
251
|
|
|
@@ -324,13 +372,11 @@ export class FileKnowledgeStore implements KnowledgeStore {
|
|
|
324
372
|
|
|
325
373
|
// ── Snapshots ────────────────────────────────────────────
|
|
326
374
|
|
|
327
|
-
private static readonly SNAPSHOTS_DIR = '.snapshots';
|
|
328
|
-
|
|
329
375
|
/**
|
|
330
376
|
* 保存当日 index.json 快照
|
|
331
377
|
*/
|
|
332
378
|
snapshot(): string {
|
|
333
|
-
const snapDir = path.join(this.baseDir,
|
|
379
|
+
const snapDir = path.join(this.baseDir, SNAPSHOTS_DIR);
|
|
334
380
|
if (!fs.existsSync(snapDir)) {
|
|
335
381
|
fs.mkdirSync(snapDir, { recursive: true });
|
|
336
382
|
}
|
|
@@ -347,7 +393,7 @@ export class FileKnowledgeStore implements KnowledgeStore {
|
|
|
347
393
|
* 读取指定日期的快照
|
|
348
394
|
*/
|
|
349
395
|
getSnapshot(date: string): IndexEntry[] | undefined {
|
|
350
|
-
const snapPath = path.join(this.baseDir,
|
|
396
|
+
const snapPath = path.join(this.baseDir, SNAPSHOTS_DIR, `index-${date}.json`);
|
|
351
397
|
if (!fs.existsSync(snapPath)) return undefined;
|
|
352
398
|
try {
|
|
353
399
|
return JSON.parse(fs.readFileSync(snapPath, 'utf-8')) as IndexEntry[];
|
|
@@ -360,7 +406,7 @@ export class FileKnowledgeStore implements KnowledgeStore {
|
|
|
360
406
|
* 计算 N 天前快照中条目的存活率
|
|
361
407
|
* 存活 = 当前 index 中存在且 maturity !== 'archived'
|
|
362
408
|
*/
|
|
363
|
-
getSurvivalRate(daysAgo: number):
|
|
409
|
+
getSurvivalRate(daysAgo: number): SnapshotSurvival | undefined {
|
|
364
410
|
const target = new Date(Date.now() - daysAgo * 24 * 60 * 60 * 1000).toISOString().slice(0, 10);
|
|
365
411
|
const snapshot = this.getSnapshot(target);
|
|
366
412
|
if (!snapshot || snapshot.length === 0) return undefined;
|
|
@@ -382,6 +428,19 @@ export class FileKnowledgeStore implements KnowledgeStore {
|
|
|
382
428
|
};
|
|
383
429
|
}
|
|
384
430
|
|
|
431
|
+
/**
|
|
432
|
+
* 读 `.consumption-stats.json` 的当日消费计数(打分核心唯一的消费统计取数口,harness#134)
|
|
433
|
+
*/
|
|
434
|
+
getConsumptionStats(): ConsumptionStats | undefined {
|
|
435
|
+
const statsPath = path.join(this.baseDir, CONSUMPTION_STATS_FILE);
|
|
436
|
+
try {
|
|
437
|
+
const stats = JSON.parse(fs.readFileSync(statsPath, 'utf-8')) as { dailyEvents?: number };
|
|
438
|
+
return { dailyEvents: stats.dailyEvents ?? 0 };
|
|
439
|
+
} catch {
|
|
440
|
+
return undefined;
|
|
441
|
+
}
|
|
442
|
+
}
|
|
443
|
+
|
|
385
444
|
// ── Index I/O ────────────────────────────────────────────
|
|
386
445
|
|
|
387
446
|
readIndex(): IndexEntry[] {
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 知识树布局正本(harness#134:walker 排除口径单点化)
|
|
3
|
+
*
|
|
4
|
+
* 「什么算条目」的唯一判定。此前 store 顶层扫描、migration 顶层扫描、index-generator
|
|
5
|
+
* 递归扫描各有各的排除口径,后果是 `knowledge index` 落在 baseDir 的生成物 `_index.md`
|
|
6
|
+
* 被 `knowledge migrate` 判成「no frontmatter found」计入 errors——同一份树,两个命令两套人口。
|
|
7
|
+
*
|
|
8
|
+
* 本模块只管**排除口径**,不管遍历深度:store 与 migration 是顶层扫描器,index-generator
|
|
9
|
+
* 会下钻业务子目录(`skills/`、`arch-patterns/`),那是各自的功能而非口径分歧。
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
/** 索引生成物:由 `harness knowledge index` 落在 baseDir,是树的基建不是条目 */
|
|
13
|
+
export const INDEX_MD_FILE = '_index.md';
|
|
14
|
+
|
|
15
|
+
/** 快照目录:`store.snapshot()` 的落点 */
|
|
16
|
+
export const SNAPSHOTS_DIR = '.snapshots';
|
|
17
|
+
|
|
18
|
+
/** 树基建目录:归档区 / 快照区 / 决议区,任何 walker 都不把它们的内容当条目人口 */
|
|
19
|
+
const INFRA_DIRS = ['.archive', 'archived', SNAPSHOTS_DIR, 'resolutions'];
|
|
20
|
+
|
|
21
|
+
/** 目录名是否属于树基建 */
|
|
22
|
+
export function isInfraDir(name: string): boolean {
|
|
23
|
+
return INFRA_DIRS.includes(name);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/** 文件名是否是知识条目(.md 且非生成物) */
|
|
27
|
+
export function isEntryFile(name: string): boolean {
|
|
28
|
+
return name.endsWith('.md') && name !== INDEX_MD_FILE;
|
|
29
|
+
}
|
package/src/knowledge/types.ts
CHANGED
|
@@ -188,3 +188,22 @@ export interface IndexEntry {
|
|
|
188
188
|
consumptionMode: ConsumptionMode;
|
|
189
189
|
origin: KnowledgeOrigin;
|
|
190
190
|
}
|
|
191
|
+
|
|
192
|
+
/** `getSurvivalRate()` 的返回面:N 天前快照条目在当前索引里的存活计数 */
|
|
193
|
+
export interface SnapshotSurvival {
|
|
194
|
+
rate: number;
|
|
195
|
+
survived: number;
|
|
196
|
+
total: number;
|
|
197
|
+
snapshotDate: string;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
/** `getConsumptionStats()` 的返回面:`.consumption-stats.json` 里打分要用的计数(harness#134) */
|
|
201
|
+
export interface ConsumptionStats {
|
|
202
|
+
dailyEvents: number;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
/** `applyAll()` 的一条按 id 部分更新(harness#134:`update(id, partial)` 的批量形) */
|
|
206
|
+
export interface StoreUpdate {
|
|
207
|
+
id: string;
|
|
208
|
+
partial: Partial<KnowledgeEntry>;
|
|
209
|
+
}
|
|
@@ -4,17 +4,20 @@
|
|
|
4
4
|
运行时监控:Execution Trace(收集/分析)+ 上下文使用追踪。
|
|
5
5
|
|
|
6
6
|
## 核心导出
|
|
7
|
-
- `TraceCollector` — 执行追踪收集(append-only JSONL,`.harness/logs/traces.log
|
|
7
|
+
- `TraceCollector` — 执行追踪收集(append-only JSONL,`.harness/logs/traces.log`)。读入口三个:`readReport(filter?)` 报告入口(返回 `{ traces, skippedLines }`,harness#100)、`read()/readRecent()/readByConstraint()` 兼容包装(#82 裁决 4 冻结的签名,丢计数)、`getStats()` 文件级统计(`{ fileExists, fileSize, totalLines, oldestTrace?, newestTrace? }`,harness#114 起经 `readJsonlEnds` 只 parse 两端;坏行并进 `totalLines` 的原始行数口径,要单列坏行数用 `readReport()`)
|
|
8
|
+
- 模块级出口:`getTraceCollector()` / `configureTraceCollector(config)`——进程级单例面,**cwd 锚定**(构造不带 projectPath),本仓生产代码零消费、保留仅为跨仓兼容(锚根约定见「约定」第一条);`createAnalyzer(config?)` 同理,等价 `new TraceAnalyzer(new TraceCollector(), config)`
|
|
8
9
|
- `TraceAnalyzer` — 追踪统计分析 + 异常检测(共用 analyzer-base 纯函数)。`analyzeRecentReport(hours)` 带坏行数,`analyzeRecent()/analyzeConstraint()` 仍返回 `TraceSummary[]`。**ADR-0020**:`summarize`/`detectAnomalies` 的判定本体是 trace-analyzer.ts 的模块级纯函数 `summarizeTraces(traces)` / `detectTraceAnomalies(summaries, config?)`(阈值经参数传入,不再是实例状态),类壳只转发——类壳是 studio 的运行时消费面(`new TraceAnalyzer(c)` + `analyzeRecentReport`/`detectAnomalies`,签名逐字不动),已持有数据的消费端直调纯函数。两函数**不进包根导出**(ADR-0003 零扩张),仓内经相对 import
|
|
9
10
|
- `ContextTracker` — LLM 调用上下文使用快照记录
|
|
10
11
|
|
|
11
12
|
## 依赖关系
|
|
12
13
|
- 依赖 `src/types/trace` ExecutionTrace 类型
|
|
13
|
-
- 消费者:`src/cli/commands/{check,report}` 与 `src/hooks/bootstrap
|
|
14
|
+
- 消费者:`src/cli/commands/{check,report}` 与 `src/hooks/bootstrap`——组合根**锚根构造**收集器 `new TraceCollector({ projectPath })` 并注入 `ConstraintChecker` 构造参数(harness#88 接线,#139 收根:cwd 锚定的 `getTraceCollector()` 单例在本仓生产代码已零消费,保留仅为跨仓兼容面);`status` 按 projectPath 直读读链正本后**直调模块级纯函数**,不构造 collector/类壳(ADR-0020:collector 构造函数的 mkdir 副作用就此退出该路径)
|
|
15
|
+
- `ContextTracker` 的仓内持有者是 `src/context/session-manager`:构造时收 `basePath` 并经 `getTracker()` 交出。仓内**无** `record()` 调用点,`getRecent()/getAverages()/detectIssues()` 也无 CLI 接线(`getTracker()` 零仓内调用方),快照的写入方与读方都是库消费者(经包根 `ContextTracker` 出口,ADR-0003)——harness#142 核对补上这条消费边,此前只列了 trace 侧消费者
|
|
14
16
|
- core 不再依赖本模块(方向由 eslint no-restricted-imports 锁死,`src/__tests__/layering.test.ts` 守卫)
|
|
15
17
|
|
|
16
18
|
## 约定
|
|
17
19
|
- Trace 文件存储在 .harness/ 目录(不提交)
|
|
20
|
+
- **落点锚在 projectPath 上,不锚 cwd(harness#139,#95 约定的 trace 侧)**:`DEFAULT_TRACE_FILE` 是**项目相对片段**、不是可直接打开的路径,`TraceCollector` 构造收可选 `projectPath`——给了则缺省/相对的 `traceFile` 一律 `path.resolve(projectPath, …)`,没给则保持 cwd 解析(`getTraceCollector()`/`configureTraceCollector()` 是留给跨仓消费者的 cwd 锚定兼容面,本仓生产代码零消费)。写侧锚根与读侧(`status`/`constraints report` 按 projectPath 直读 jsonl 正本)必须同锚,否则从别处带 `--project-path` 跑一次 check 就是「评估真跑了 B、trace 写进 A」的假绿。可检面:`__tests__/trace-file-anchoring.test.ts`(两态落点 + 读侧同锚)、`src/cli/commands/__tests__/project-path-anchoring.test.ts` 站点 3(命令面写-读闭环)、`src/cli/commands/__tests__/project-path-convention.test.ts` 闸 3(`xxxPath|File|Log` 相对默认值冻结,含常量引用臂)与闸 4(组合根不复活单例取用口)
|
|
18
21
|
- 追加写入,单行 JSON,自动滚动
|
|
19
22
|
- 零 Token 成本:不调用 LLM
|
|
20
23
|
- **凡以 `skip` 策略读 JSONL,坏行计数必须到达该消费面的用户可见输出,或在该调用点显式记名豁免并写明理由(harness#100)**——静默吞掉「数据不全」不是一种可选项。承载方式纯增量:新报告入口带计数,旧入口退化为丢计数的薄包装(#82 裁决 4 的兼容约束继续成立),不另立第二份计数概念、不把 `JsonlReadResult` 提升进包根(ADR-0003)
|
|
@@ -6,14 +6,22 @@ import { ContextTracker } from '../context-tracker';
|
|
|
6
6
|
import * as fs from 'fs';
|
|
7
7
|
import type { ContextUsageSnapshot } from '../../context/types';
|
|
8
8
|
|
|
9
|
-
jest.mock('fs', () =>
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
9
|
+
jest.mock('fs', () => {
|
|
10
|
+
// 两条读入口必须同源:utils/jsonl 的 tail 走倒读分块(openSync/fstatSync/readSync,
|
|
11
|
+
// ADR-0023 决策 3),head 与全文走 readFileSync。假件收在 test-setup/jsonl-fake-fs
|
|
12
|
+
// 承接两处消费(jest.mock 工厂被提升,只能 require)。
|
|
13
|
+
const { jsonlBoundedReadChain } = require('../../test-setup/jsonl-fake-fs');
|
|
14
|
+
const readFileSync = jest.fn().mockReturnValue('');
|
|
15
|
+
return {
|
|
16
|
+
existsSync: jest.fn().mockReturnValue(true),
|
|
17
|
+
mkdirSync: jest.fn(),
|
|
18
|
+
readFileSync,
|
|
19
|
+
appendFileSync: jest.fn(),
|
|
20
|
+
statSync: jest.fn().mockReturnValue({ size: 0 }),
|
|
21
|
+
renameSync: jest.fn(),
|
|
22
|
+
...jsonlBoundedReadChain((p: string) => readFileSync(p, 'utf-8')),
|
|
23
|
+
};
|
|
24
|
+
});
|
|
17
25
|
|
|
18
26
|
const mockFs = fs as jest.Mocked<typeof fs>;
|
|
19
27
|
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* TraceCollector 落点锚根(harness#139,#95 约定的 trace 侧)
|
|
3
|
+
*
|
|
4
|
+
* 不 mock IO:落点本身就是被测行为,只有 **cwd ≠ projectPath** 才看得出锚到了哪一侧
|
|
5
|
+
* (前提形状照 `src/cli/commands/__tests__/project-path-anchoring.test.ts`)。
|
|
6
|
+
* 两态都要钉:给了 projectPath → 锚项目根;没给 → 保持 cwd 解析(跨仓消费者的兼容面)。
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import * as fs from 'fs';
|
|
10
|
+
import * as path from 'path';
|
|
11
|
+
import { TraceCollector } from '../traces';
|
|
12
|
+
import { DEFAULT_TRACE_FILE, type ExecutionTrace } from '../../types/trace';
|
|
13
|
+
import { createProjectFixture } from '../../test-setup/project-fixture';
|
|
14
|
+
|
|
15
|
+
const trace = (constraintId: string): ExecutionTrace => ({
|
|
16
|
+
constraintId,
|
|
17
|
+
level: 'guideline',
|
|
18
|
+
timestamp: 1700000000000,
|
|
19
|
+
result: 'pass',
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
const traceLines = (projectRoot: string): string[] =>
|
|
23
|
+
fs.readFileSync(path.join(projectRoot, DEFAULT_TRACE_FILE), 'utf-8').split('\n').filter(Boolean);
|
|
24
|
+
|
|
25
|
+
describe('TraceCollector 落点锚根(harness#139)', () => {
|
|
26
|
+
const originalCwd = process.cwd();
|
|
27
|
+
let cwdSide: string;
|
|
28
|
+
let projectSide: string;
|
|
29
|
+
|
|
30
|
+
beforeEach(() => {
|
|
31
|
+
cwdSide = createProjectFixture({ name: 'traceanchor-cwd' });
|
|
32
|
+
projectSide = createProjectFixture({ name: 'traceanchor-project' });
|
|
33
|
+
process.chdir(cwdSide);
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
afterEach(() => {
|
|
37
|
+
process.chdir(originalCwd);
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
it('给了 projectPath → 缺省落点锚到 <projectPath> 下,cwd 侧不产生文件', () => {
|
|
41
|
+
new TraceCollector({ projectPath: projectSide }).record(trace('anchored'));
|
|
42
|
+
|
|
43
|
+
expect(traceLines(projectSide).map(l => JSON.parse(l).constraintId)).toEqual(['anchored']);
|
|
44
|
+
expect(fs.existsSync(path.join(cwdSide, DEFAULT_TRACE_FILE))).toBe(false);
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
it('没给 projectPath → 缺省落点仍按 cwd 解析(兼容语义,行为逐字不变)', () => {
|
|
48
|
+
new TraceCollector().record(trace('cwd_default'));
|
|
49
|
+
|
|
50
|
+
expect(traceLines(cwdSide).map(l => JSON.parse(l).constraintId)).toEqual(['cwd_default']);
|
|
51
|
+
expect(fs.existsSync(path.join(projectSide, DEFAULT_TRACE_FILE))).toBe(false);
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
it('显式绝对 traceFile 优先,不被 projectPath 改写', () => {
|
|
55
|
+
const custom = path.join(cwdSide, 'custom.log');
|
|
56
|
+
|
|
57
|
+
new TraceCollector({ projectPath: projectSide, traceFile: custom }).record(trace('explicit'));
|
|
58
|
+
|
|
59
|
+
expect(fs.existsSync(custom)).toBe(true);
|
|
60
|
+
expect(fs.existsSync(path.join(projectSide, DEFAULT_TRACE_FILE))).toBe(false);
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
it('projectPath + 相对 traceFile → 相对落点在 projectPath 之下(自定义片段的锚定形状)', () => {
|
|
64
|
+
new TraceCollector({ projectPath: projectSide, traceFile: 'logs/custom.log' }).record(
|
|
65
|
+
trace('relative')
|
|
66
|
+
);
|
|
67
|
+
|
|
68
|
+
// 自定义片段跟缺省片段同一条锚定规则:给了根就锚根,不给仍按 cwd(兼容面逐字不变)
|
|
69
|
+
expect(fs.existsSync(path.join(projectSide, 'logs', 'custom.log'))).toBe(true);
|
|
70
|
+
expect(fs.existsSync(path.join(cwdSide, 'logs', 'custom.log'))).toBe(false);
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
it('读侧同锚:另一实例按同一 projectPath 构造,read()/getStats() 读到刚写的记录', () => {
|
|
74
|
+
new TraceCollector({ projectPath: projectSide }).record(trace('roundtrip'));
|
|
75
|
+
|
|
76
|
+
const reader = new TraceCollector({ projectPath: projectSide });
|
|
77
|
+
expect(reader.read().map(t => t.constraintId)).toEqual(['roundtrip']);
|
|
78
|
+
expect(reader.getStats().totalLines).toBe(1);
|
|
79
|
+
});
|
|
80
|
+
});
|