@dommaker/harness 1.6.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 +52 -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 +2 -1
- 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/core/validators/index.d.ts +1 -1
- package/dist/core/validators/index.d.ts.map +1 -1
- package/dist/core/validators/passes-gate.d.ts.map +1 -1
- package/dist/core/validators/passes-gate.js +2 -2
- package/dist/core/validators/passes-gate.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 +4 -4
- 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/passes-gate.d.ts +0 -13
- package/dist/types/passes-gate.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 +27 -6
- package/src/__tests__/public-type-surface.test.ts +360 -0
- 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__/passes-gate-dead-options.test.ts +89 -1
- 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 -1
- 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/core/validators/index.ts +0 -1
- package/src/core/validators/passes-gate.ts +2 -3
- 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 -3
- 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/passes-gate.ts +0 -17
- 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/monitoring/traces.ts
CHANGED
|
@@ -21,6 +21,9 @@ import {
|
|
|
21
21
|
|
|
22
22
|
/**
|
|
23
23
|
* 默认配置
|
|
24
|
+
*
|
|
25
|
+
* `traceFile` 这里是**未锚定的项目相对片段**(#95 病根形状),故本文件在
|
|
26
|
+
* `project-path-convention.test.ts` 闸 3 的豁免表里点名带理由;锚定动作在构造函数里做。
|
|
24
27
|
*/
|
|
25
28
|
const DEFAULT_CONFIG: TraceCollectorConfig = {
|
|
26
29
|
traceFile: DEFAULT_TRACE_FILE,
|
|
@@ -33,7 +36,7 @@ const DEFAULT_CONFIG: TraceCollectorConfig = {
|
|
|
33
36
|
*
|
|
34
37
|
* 使用方式:
|
|
35
38
|
* ```typescript
|
|
36
|
-
* const collector = new TraceCollector();
|
|
39
|
+
* const collector = new TraceCollector({ projectPath: '/path/to/project' });
|
|
37
40
|
* collector.record({
|
|
38
41
|
* constraintId: 'no_fix_without_root_cause',
|
|
39
42
|
* level: 'iron_law',
|
|
@@ -48,7 +51,11 @@ export class TraceCollector {
|
|
|
48
51
|
|
|
49
52
|
constructor(config?: Partial<TraceCollectorConfig>) {
|
|
50
53
|
this.config = { ...DEFAULT_CONFIG, ...config };
|
|
51
|
-
|
|
54
|
+
// 收根则相对落点按项目解析(harness#139:否则 -p 半失效——评估跑在 B、trace 写进 cwd);
|
|
55
|
+
// 没收根则保持 cwd 解析,这是跨仓消费者(studio)依赖的兼容面,不是遗漏
|
|
56
|
+
this.traceFile = this.config.projectPath
|
|
57
|
+
? path.resolve(this.config.projectPath, this.config.traceFile!)
|
|
58
|
+
: this.config.traceFile!;
|
|
52
59
|
this.ensureDirectory();
|
|
53
60
|
}
|
|
54
61
|
|
|
@@ -304,12 +311,17 @@ export class TraceCollector {
|
|
|
304
311
|
}
|
|
305
312
|
|
|
306
313
|
/**
|
|
307
|
-
*
|
|
314
|
+
* 全局单例(**cwd 锚定**的兼容面)
|
|
315
|
+
*
|
|
316
|
+
* harness#139 起本仓生产代码不再消费它:CLI check/report 与 bootstrap 四个组合根
|
|
317
|
+
* 各自 `new TraceCollector({ projectPath })` 锚根构造(守卫见 project-path-convention
|
|
318
|
+
* 闸 4)。保留是给跨仓调用方(studio)的既有形状——它依赖「API 进程 cwd 恰好是项目」。
|
|
319
|
+
* 传 `projectPath` 是它的锚根出口;不传则落点按 cwd 解析(行为逐字不变)。
|
|
308
320
|
*/
|
|
309
321
|
let globalCollector: TraceCollector | null = null;
|
|
310
322
|
|
|
311
323
|
/**
|
|
312
|
-
*
|
|
324
|
+
* 获取全局收集器(cwd 锚定,兼容语义见上)
|
|
313
325
|
*/
|
|
314
326
|
export function getTraceCollector(): TraceCollector {
|
|
315
327
|
if (!globalCollector) {
|
|
@@ -319,7 +331,9 @@ export function getTraceCollector(): TraceCollector {
|
|
|
319
331
|
}
|
|
320
332
|
|
|
321
333
|
/**
|
|
322
|
-
*
|
|
334
|
+
* 配置全局收集器(cwd 锚定,兼容语义见 `getTraceCollector()`)
|
|
335
|
+
*
|
|
336
|
+
* 需要把 trace 落进指定项目的调用方直接构造实例:`new TraceCollector({ projectPath })`。
|
|
323
337
|
*/
|
|
324
338
|
export function configureTraceCollector(config: Partial<TraceCollectorConfig>): void {
|
|
325
339
|
globalCollector = new TraceCollector(config);
|
package/src/release/CONTEXT.md
CHANGED
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
## 约定
|
|
17
17
|
- 清单维护模型:声明面推导为主(目录重构动到 package.json 时清单自动跟随)+ extras 随源码维护;**不新增第三处硬编码清单**
|
|
18
18
|
- 覆盖口径(有意裁决,#77):深度内部文件(tsc 产出的 dist/knowledge/*、core/constraints/checker.js 等)**不在清单**——非公开契约,存在性由 tsc 构建成功保证;历史上外部硬编码它们正是重构误判(studio 6cf3c329)的根因。旧 studio 清单 checker.js/doctor.js、旧 release 清单 13 项均被本清单取代,覆盖收窄是裁决而非遗漏
|
|
19
|
-
- `__tests__/integrity.test.ts`
|
|
19
|
+
- `__tests__/integrity.test.ts` 的「真实包根清单同步闸门」是重构同步闸门,对完整清单做显式期望 `toEqual` 精确对撞(#160 起双向成立):声明面(`exports`/`main`/`bin`)增、删、改任一条目都变红并指名差额项,动声明面必须同步测试内的显式清单;extras 由同文件另一条 `toEqual` 精确冻结,闸门内展开引用保持单一来源
|
|
20
20
|
- bin 新增引导期 require 的 dist 文件 → extras 加一条 + 测试同步
|
|
21
21
|
|
|
22
22
|
## 注意事项
|
|
@@ -139,19 +139,28 @@ describe('getCriticalArtifacts / verifyReleaseArtifacts(fixture 包根)', ()
|
|
|
139
139
|
});
|
|
140
140
|
|
|
141
141
|
describe('真实包根清单同步闸门', () => {
|
|
142
|
-
it('真实 package.json
|
|
142
|
+
it('真实 package.json 推导清单与显式期望精确相等(声明面增/删/改任一条目本测试强制同步,#160)', () => {
|
|
143
143
|
const root = resolvePackageRoot(__dirname);
|
|
144
144
|
const artifacts = getCriticalArtifacts(root);
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
145
|
+
// 双向对撞:toContain 只钉得住删/改,新增声明条目会静默多出——故用 toEqual 显式清单
|
|
146
|
+
// (ADR-0003 公共面冻结闸同形)。getCriticalArtifacts 返回已排序数组,期望侧同口径 .sort()。
|
|
147
|
+
expect(artifacts).toEqual(
|
|
148
|
+
[
|
|
149
|
+
// 声明面(main/exports/bin)推导的脚本入口:动 package.json 声明面须同步本清单
|
|
150
|
+
'bin/harness.js',
|
|
151
|
+
'dist/context/index.d.ts',
|
|
152
|
+
'dist/context/index.js',
|
|
153
|
+
'dist/core/index.d.ts',
|
|
154
|
+
'dist/core/index.js',
|
|
155
|
+
'dist/gates/index.d.ts',
|
|
156
|
+
'dist/gates/index.js',
|
|
157
|
+
'dist/index.d.ts',
|
|
158
|
+
'dist/index.js',
|
|
159
|
+
'dist/presets/index.d.ts',
|
|
160
|
+
'dist/presets/index.js',
|
|
161
|
+
// extras 单一来源在 integrity.ts,另由上方 toEqual 用例精确冻结,此处展开引用不复制
|
|
162
|
+
...EXTRA_CRITICAL_ARTIFACTS,
|
|
163
|
+
].sort(),
|
|
164
|
+
);
|
|
156
165
|
});
|
|
157
166
|
});
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 有界读 fs 假件测试(ADR-0023 决策 3 的夹具层)
|
|
3
|
+
*
|
|
4
|
+
* 钉的是夹具的契约:它给出的字节视图必须与 `readFile(path)` 的字符串**逐项等价**、且
|
|
5
|
+
* **跟着 openSync 的路径走**。两条读入口一旦各演一份世界,被测代码的容错分支会把不一致
|
|
6
|
+
* 吞成假象(本夹具存在的理由就是消灭这类假象)。
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import { jsonlBoundedReadChain } from '../jsonl-fake-fs';
|
|
10
|
+
|
|
11
|
+
describe('jsonlBoundedReadChain', () => {
|
|
12
|
+
it('fstatSync 的 size 是所开文件的 UTF-8 字节数,不是字符数', () => {
|
|
13
|
+
const text = '{"n":1,"文本":"约束"}\n';
|
|
14
|
+
const chain = jsonlBoundedReadChain(() => text);
|
|
15
|
+
const fd = chain.openSync('/x/a.jsonl', 'r');
|
|
16
|
+
expect(chain.fstatSync(fd).size).toBe(Buffer.byteLength(text, 'utf-8'));
|
|
17
|
+
expect(chain.fstatSync(fd).size).toBeGreaterThan(text.length);
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
it('readSync 按 position/length 取窗口并回填缓冲,返回实际读到的字节数', () => {
|
|
21
|
+
const chain = jsonlBoundedReadChain(() => 'abcdef\nghijkl\n');
|
|
22
|
+
const fd = chain.openSync('/x/a.jsonl');
|
|
23
|
+
const buf = Buffer.alloc(7);
|
|
24
|
+
expect(chain.readSync(fd, buf, 0, 7, 7)).toBe(7);
|
|
25
|
+
expect(buf.toString('utf-8')).toBe('ghijkl\n');
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
it('越界的 length 只拷到文件末尾,超出 EOF 读到 0 字节', () => {
|
|
29
|
+
const chain = jsonlBoundedReadChain(() => 'ab\n');
|
|
30
|
+
const fd = chain.openSync('/x/a.jsonl');
|
|
31
|
+
const tail = Buffer.alloc(4);
|
|
32
|
+
expect(chain.readSync(fd, tail, 0, 4, 1)).toBe(2);
|
|
33
|
+
expect(tail.subarray(0, 2).toString('utf-8')).toBe('b\n');
|
|
34
|
+
expect(chain.readSync(fd, Buffer.alloc(2), 0, 2, 99)).toBe(0);
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
it('offset 让调用方把多块拼进同一缓冲(分块读的形状)', () => {
|
|
38
|
+
const chain = jsonlBoundedReadChain(() => '0123456789');
|
|
39
|
+
const fd = chain.openSync('/x/a.jsonl');
|
|
40
|
+
const buf = Buffer.alloc(6);
|
|
41
|
+
chain.readSync(fd, buf, 0, 4, 0);
|
|
42
|
+
chain.readSync(fd, buf, 4, 2, 4);
|
|
43
|
+
expect(buf.toString('utf-8')).toBe('012345');
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
it('按路径分派内容:同一次运行开两个文件,各自读到自己的那份', () => {
|
|
47
|
+
const files: Record<string, string> = {
|
|
48
|
+
'/s/1/checkpoint.json': '{"id":"cp-1"}\n',
|
|
49
|
+
'/s/1/events.jsonl': '{"type":"user_message"}\n{"type":"assistant_message"}\n',
|
|
50
|
+
};
|
|
51
|
+
const chain = jsonlBoundedReadChain(p => files[p] ?? '');
|
|
52
|
+
const fdA = chain.openSync('/s/1/checkpoint.json');
|
|
53
|
+
const fdB = chain.openSync('/s/1/events.jsonl');
|
|
54
|
+
expect(chain.fstatSync(fdA).size).toBe(Buffer.byteLength(files['/s/1/checkpoint.json']));
|
|
55
|
+
expect(chain.fstatSync(fdB).size).toBe(Buffer.byteLength(files['/s/1/events.jsonl']));
|
|
56
|
+
const buf = Buffer.alloc(64);
|
|
57
|
+
const n = chain.readSync(fdB, buf, 0, 64, 0);
|
|
58
|
+
expect(buf.subarray(0, n).toString('utf-8')).toBe(files['/s/1/events.jsonl']);
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
it('未开过的 fd 与 close 掉的 fd 都按空文件处理,不互相串内容', () => {
|
|
62
|
+
// 内容源必须按路径分派:不看路径的源会让「路径丢失」这条失效模式测不出来
|
|
63
|
+
const files: Record<string, string> = { '/x/a.jsonl': 'x\ny\n' };
|
|
64
|
+
const chain = jsonlBoundedReadChain(p => files[p] ?? '');
|
|
65
|
+
const fd = chain.openSync('/x/a.jsonl');
|
|
66
|
+
expect(chain.fstatSync(fd).size).toBe(4);
|
|
67
|
+
chain.closeSync(fd);
|
|
68
|
+
expect(chain.fstatSync(fd).size).toBe(0);
|
|
69
|
+
expect(chain.fstatSync(999).size).toBe(0);
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
it('内容每次现取:改了 readFile 的返回,下一次读立即看到', () => {
|
|
73
|
+
let value = 'first\n';
|
|
74
|
+
const chain = jsonlBoundedReadChain(() => value);
|
|
75
|
+
const fd = chain.openSync('/x/a.jsonl');
|
|
76
|
+
expect(chain.fstatSync(fd).size).toBe(6);
|
|
77
|
+
value = 'second-longer\n';
|
|
78
|
+
expect(chain.fstatSync(fd).size).toBe(14);
|
|
79
|
+
const buf = Buffer.alloc(14);
|
|
80
|
+
chain.readSync(fd, buf, 0, 14, 0);
|
|
81
|
+
expect(buf.toString('utf-8')).toBe('second-longer\n');
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
it('非字符串内容按空文件处理(mock 未设置返回值时的缺省形状)', () => {
|
|
85
|
+
for (const raw of [undefined, null, 0]) {
|
|
86
|
+
const chain = jsonlBoundedReadChain(() => raw);
|
|
87
|
+
const fd = chain.openSync('/x/a.jsonl');
|
|
88
|
+
expect(chain.fstatSync(fd).size).toBe(0);
|
|
89
|
+
expect(chain.readSync(fd, Buffer.alloc(3), 0, 3, 0)).toBe(0);
|
|
90
|
+
}
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
it('openSync 每次发新 fd,多次开关不冲突', () => {
|
|
94
|
+
const chain = jsonlBoundedReadChain(() => 'a\n');
|
|
95
|
+
const fds = [chain.openSync('/x/a.jsonl'), chain.openSync('/x/a.jsonl')];
|
|
96
|
+
expect(new Set(fds).size).toBe(2);
|
|
97
|
+
});
|
|
98
|
+
});
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* JSONL 有界读的 fs 假件(测试夹具层)
|
|
3
|
+
*
|
|
4
|
+
* 背景:`utils/jsonl` 的 tail/head 是分块有界读(`openSync` + `fstatSync` + `readSync` +
|
|
5
|
+
* `closeSync`,ADR-0023 决策 3)。把整个 `fs` 模块 mock 掉的测试原先只提供了全文读入口
|
|
6
|
+
* `readFileSync`,于是走有界读的被测代码直接抛错,再被它自己的 `catch` 吞成「数据不存在」
|
|
7
|
+
* 一类的假象(session-manager 报 `Checkpoint 不存在`、context-tracker 读出空快照都是这样)。
|
|
8
|
+
*
|
|
9
|
+
* 口径一(同源):内容经 `readFile(path)` 现取,直接把 `readFileSync` 那个 mock 包一层即可——
|
|
10
|
+
* 测试改了它的返回值,两条读入口看到的就是同一个文件。
|
|
11
|
+
* 口径二(按路径分派):`openSync` 收到的路径必须传到后面三次调用上。有的假件按路径参数
|
|
12
|
+
* 冒充多个文件(`if (p.includes('events.jsonl')) ...`),丢了路径就会读到别的文件或直接空。
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
/** 有界读用到的四个 fs 入口,形状与 `fs` 对应函数一致 */
|
|
16
|
+
export interface BoundedReadChain {
|
|
17
|
+
openSync: (path: string, flags?: string) => number;
|
|
18
|
+
fstatSync: (fd: number) => { size: number };
|
|
19
|
+
readSync: (
|
|
20
|
+
fd: number,
|
|
21
|
+
buffer: Buffer,
|
|
22
|
+
offset: number,
|
|
23
|
+
length: number,
|
|
24
|
+
position: number
|
|
25
|
+
) => number;
|
|
26
|
+
closeSync: (fd: number) => void;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* 以 `readFile(path)` 为唯一内容源,凑出有界读所需的四个 fs 入口
|
|
31
|
+
*
|
|
32
|
+
* 非字符串(含 mock 未设置时的 `undefined`)按空文件处理。不用 `jest.fn()` 包:本夹具只需
|
|
33
|
+
* 行为正确;要数调用次数,在调用点自己套一层。
|
|
34
|
+
*/
|
|
35
|
+
export function jsonlBoundedReadChain(readFile: (path: string) => unknown): BoundedReadChain {
|
|
36
|
+
const bytesOf = (path: string): Buffer => {
|
|
37
|
+
const raw = readFile(path);
|
|
38
|
+
return Buffer.from(typeof raw === 'string' ? raw : '', 'utf-8');
|
|
39
|
+
};
|
|
40
|
+
const openPaths = new Map<number, string>();
|
|
41
|
+
let nextFd = 100;
|
|
42
|
+
return {
|
|
43
|
+
openSync: path => {
|
|
44
|
+
const fd = nextFd++;
|
|
45
|
+
openPaths.set(fd, String(path));
|
|
46
|
+
return fd;
|
|
47
|
+
},
|
|
48
|
+
fstatSync: fd => ({ size: bytesOf(openPaths.get(fd) ?? '').length }),
|
|
49
|
+
readSync: (fd, buffer, offset, length, position) => {
|
|
50
|
+
const bytes = bytesOf(openPaths.get(fd) ?? '');
|
|
51
|
+
// 真 readSync 在 position 越过 EOF 时返回 0;Buffer.copy 遇 sourceStart 超范围会抛
|
|
52
|
+
if (position >= bytes.length) return 0;
|
|
53
|
+
return bytes.copy(buffer, offset, position, position + length);
|
|
54
|
+
},
|
|
55
|
+
closeSync: fd => {
|
|
56
|
+
openPaths.delete(fd);
|
|
57
|
+
},
|
|
58
|
+
};
|
|
59
|
+
}
|
package/src/tools/index.ts
CHANGED
package/src/types/checkpoint.ts
CHANGED
|
@@ -171,4 +171,10 @@ export interface CheckpointContext {
|
|
|
171
171
|
|
|
172
172
|
/** 自定义检查处理器 */
|
|
173
173
|
customHandlers?: Map<string, (config: CheckConfig) => Promise<CheckResult>>;
|
|
174
|
+
|
|
175
|
+
/**
|
|
176
|
+
* 重试退避的等待注入(http_* 检查族消费)
|
|
177
|
+
* 缺省 = 真定时器;测试可注入零等待替身,据此断言重试语义而不必真等
|
|
178
|
+
*/
|
|
179
|
+
sleep?: (ms: number) => Promise<void>;
|
|
174
180
|
}
|
package/src/types/passes-gate.ts
CHANGED
|
@@ -55,23 +55,6 @@ export interface TaskTestResult {
|
|
|
55
55
|
evidence?: string;
|
|
56
56
|
}
|
|
57
57
|
|
|
58
|
-
/**
|
|
59
|
-
* 动态任务(用于 passes-gate)
|
|
60
|
-
*/
|
|
61
|
-
export interface DynamicTask {
|
|
62
|
-
/** 任务 ID */
|
|
63
|
-
id: string;
|
|
64
|
-
|
|
65
|
-
/** 任务名称 */
|
|
66
|
-
name?: string;
|
|
67
|
-
|
|
68
|
-
/** 是否通过 */
|
|
69
|
-
passes?: boolean;
|
|
70
|
-
|
|
71
|
-
/** 测试结果 */
|
|
72
|
-
testResult?: TaskTestResult;
|
|
73
|
-
}
|
|
74
|
-
|
|
75
58
|
// ========================================
|
|
76
59
|
// AS-006: 纯约束验证接口
|
|
77
60
|
// ========================================
|
|
@@ -224,6 +224,24 @@ export interface GovernanceConfig {
|
|
|
224
224
|
testing?: TestingGovernanceConfig;
|
|
225
225
|
}
|
|
226
226
|
|
|
227
|
+
/**
|
|
228
|
+
* CI 平台(harness#143):服务端门禁的接线目标
|
|
229
|
+
*
|
|
230
|
+
* 本地 hook 跑在推送者控制的机器上(`--no-verify` 可绕过),只能算自检;
|
|
231
|
+
* 真正的门禁必须抢到服务端 CI 这个执行时机。取值域另含 `'none'`(不接线),
|
|
232
|
+
* 那在本类型之外——命令层据此让 CI 站点干脆不进 plan,scaffold 只见有形状的平台。
|
|
233
|
+
*/
|
|
234
|
+
export type CiPlatform = 'github' | 'gitlab';
|
|
235
|
+
|
|
236
|
+
/**
|
|
237
|
+
* CI 接线配置(config.yml `ci` 段,harness#143)
|
|
238
|
+
*
|
|
239
|
+
* `platform` 缺省 = `github`;`'none'` = 不落任何 CI 文件。旧配置无此段即 `github`,零迁移。
|
|
240
|
+
*/
|
|
241
|
+
export interface CiConfig {
|
|
242
|
+
platform?: CiPlatform | 'none';
|
|
243
|
+
}
|
|
244
|
+
|
|
227
245
|
/**
|
|
228
246
|
* 项目配置
|
|
229
247
|
*/
|
|
@@ -236,6 +254,9 @@ export interface ProjectConfig {
|
|
|
236
254
|
/** 使用预设 */
|
|
237
255
|
preset?: 'strict' | 'standard' | 'relaxed';
|
|
238
256
|
|
|
257
|
+
/** CI 接线平台(harness#143,缺省 github) */
|
|
258
|
+
ci?: CiConfig;
|
|
259
|
+
|
|
239
260
|
/**
|
|
240
261
|
* 项目适用场景标签(ADR-0001,如 'agent-skill'、'llm-app')
|
|
241
262
|
*
|
package/src/types/trace.ts
CHANGED
|
@@ -5,10 +5,11 @@
|
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
7
|
/**
|
|
8
|
-
* 默认 trace
|
|
8
|
+
* 默认 trace 文件路径(项目相对片段——**不是**可直接打开的路径)
|
|
9
9
|
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
10
|
+
* 读写双方都必须把它锚到项目根(`path.resolve(projectPath, DEFAULT_TRACE_FILE)`,
|
|
11
|
+
* harness#139)。采集器侧的锚定在 `TraceCollector` 构造内完成(传 `projectPath` 即可);
|
|
12
|
+
* 需要自定义路径时使用 `TraceCollectorConfig.traceFile`(相对值同样按 projectPath 解析)。
|
|
12
13
|
*/
|
|
13
14
|
export const DEFAULT_TRACE_FILE = '.harness/logs/traces.log';
|
|
14
15
|
|
|
@@ -197,7 +198,16 @@ export interface TraceFilter {
|
|
|
197
198
|
* Trace 收集器配置
|
|
198
199
|
*/
|
|
199
200
|
export interface TraceCollectorConfig {
|
|
200
|
-
/**
|
|
201
|
+
/**
|
|
202
|
+
* 项目根(harness#139,#95 约定的落地形状)
|
|
203
|
+
*
|
|
204
|
+
* 给了 → 缺省/相对的 `traceFile` 按它解析(`path.resolve(projectPath, traceFile)`),
|
|
205
|
+
* 从别处带 `--project-path` 跑时 trace 落进目标项目;
|
|
206
|
+
* 不给 → 保持 cwd 解析现状(跨仓消费者的兼容面,不是遗漏)。
|
|
207
|
+
*/
|
|
208
|
+
projectPath?: string;
|
|
209
|
+
|
|
210
|
+
/** Trace 文件路径(绝对值原样;相对值按 projectPath 解析,无 projectPath 时按 cwd) */
|
|
201
211
|
traceFile?: string;
|
|
202
212
|
|
|
203
213
|
/** 最大文件大小(字节),超出则滚动 */
|
|
@@ -4,13 +4,14 @@
|
|
|
4
4
|
|
|
5
5
|
import * as fs from 'fs';
|
|
6
6
|
import * as path from 'path';
|
|
7
|
+
import * as os from 'os';
|
|
7
8
|
import { findTsSourceFiles } from '../file-walk';
|
|
8
9
|
|
|
9
10
|
describe('findTsSourceFiles', () => {
|
|
10
|
-
|
|
11
|
+
let tempDir: string;
|
|
11
12
|
|
|
12
13
|
beforeAll(() => {
|
|
13
|
-
fs.
|
|
14
|
+
tempDir = fs.mkdtempSync(path.join(os.tmpdir(), 'temp-test-file-walk-'));
|
|
14
15
|
});
|
|
15
16
|
|
|
16
17
|
afterAll(() => {
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 有界读的「不整读全文」闸(ADR-0023 决策 3:tail 倒读,并同型推广到 head 正读)
|
|
3
|
+
*
|
|
4
|
+
* 单独成文件的理由:Node 的 `fs` 导出属性不可 redefine,`jest.spyOn(fs, 'readFileSync')`
|
|
5
|
+
* 直接抛 `Cannot redefine property`——只能在模块解析层把 `readFileSync` 换成必炸实现,
|
|
6
|
+
* 其余 fs 行为经 requireActual 保真(两条有界读用的 openSync/fstatSync/readSync 都是真的)。
|
|
7
|
+
*
|
|
8
|
+
* 反向断言(全文读必须炸)是这道闸的自我检查:没有它,「有界读通过」可能只是 mock 没生效。
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
jest.mock('fs', () => ({
|
|
12
|
+
...jest.requireActual('fs'),
|
|
13
|
+
readFileSync: () => {
|
|
14
|
+
throw new Error('整读全文:有界读消费方不该走到这条路径');
|
|
15
|
+
},
|
|
16
|
+
}));
|
|
17
|
+
|
|
18
|
+
import * as path from 'path';
|
|
19
|
+
import { readJsonl } from '../jsonl';
|
|
20
|
+
|
|
21
|
+
interface Row {
|
|
22
|
+
n: number;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const realFs = jest.requireActual('fs');
|
|
26
|
+
const realOs = jest.requireActual('os');
|
|
27
|
+
|
|
28
|
+
describe('readJsonl 有界读', () => {
|
|
29
|
+
let dir: string;
|
|
30
|
+
let filePath: string;
|
|
31
|
+
|
|
32
|
+
beforeAll(() => {
|
|
33
|
+
dir = realFs.mkdtempSync(path.join(realOs.tmpdir(), 'harness-bounded-read-'));
|
|
34
|
+
// 20 万行 ≈ 4MB,远超 64KB 分块:整读要把它全捞进内存,有界读只需捞一端
|
|
35
|
+
const lines = Array.from({ length: 200000 }, (_, i) => `{"n":${i}}`);
|
|
36
|
+
filePath = path.join(dir, 'big.jsonl');
|
|
37
|
+
realFs.writeFileSync(filePath, lines.join('\n') + '\n', 'utf-8');
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
afterAll(() => {
|
|
41
|
+
realFs.rmSync(dir, { recursive: true, force: true });
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
it('mock 生效:全文读确实走 readFileSync(必炸)', () => {
|
|
45
|
+
expect(() => readJsonl<Row>(filePath, 'skip')).toThrow('整读全文');
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
it('tail 只要够数的尾部行,不整读全文', () => {
|
|
49
|
+
const { records, skippedLines } = readJsonl<Row>(filePath, 'throw', { tail: 3 });
|
|
50
|
+
expect(skippedLines).toBe(0);
|
|
51
|
+
expect(records.map(r => r.n)).toEqual([199997, 199998, 199999]);
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
it('head 只要够数的头部行,不整读全文', () => {
|
|
55
|
+
const { records, skippedLines } = readJsonl<Row>(filePath, 'throw', { head: 3 });
|
|
56
|
+
expect(skippedLines).toBe(0);
|
|
57
|
+
expect(records.map(r => r.n)).toEqual([0, 1, 2]);
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
it('head:跨 64KB 块边界的多字节字符不被切断', () => {
|
|
61
|
+
const wide = path.join(dir, 'wide.jsonl');
|
|
62
|
+
const lines = Array.from(
|
|
63
|
+
{ length: 1200 },
|
|
64
|
+
(_, i) => JSON.stringify({ n: i, 文本: '约束进化飞轮'.repeat(12) })
|
|
65
|
+
);
|
|
66
|
+
realFs.writeFileSync(wide, lines.join('\n') + '\n', 'utf-8');
|
|
67
|
+
expect(realFs.statSync(wide).size).toBeGreaterThan(64 * 1024);
|
|
68
|
+
const { records, skippedLines } = readJsonl<{ n: number; 文本: string }>(wide, 'throw', {
|
|
69
|
+
head: 2,
|
|
70
|
+
});
|
|
71
|
+
expect(skippedLines).toBe(0);
|
|
72
|
+
expect(records.map(r => r.n)).toEqual([0, 1]);
|
|
73
|
+
expect(records[1].文本).toBe('约束进化飞轮'.repeat(12));
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
it('两端超过总行数时取全部', () => {
|
|
77
|
+
const small = path.join(dir, 'small.jsonl');
|
|
78
|
+
realFs.writeFileSync(small, '{"n":1}\n{"n":2}\n', 'utf-8');
|
|
79
|
+
expect(readJsonl<Row>(small, 'throw', { tail: 50 }).records.map(r => r.n)).toEqual([1, 2]);
|
|
80
|
+
expect(readJsonl<Row>(small, 'throw', { head: 50 }).records.map(r => r.n)).toEqual([1, 2]);
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
it('head: 0 与缺文件返回空结果,不打开文件', () => {
|
|
84
|
+
const few = path.join(dir, 'few.jsonl');
|
|
85
|
+
realFs.writeFileSync(few, '{"n":1}\n', 'utf-8');
|
|
86
|
+
expect(readJsonl<Row>(few, 'skip', { head: 0 })).toEqual({ records: [], skippedLines: 0 });
|
|
87
|
+
expect(readJsonl<Row>(path.join(dir, 'missing.jsonl'), 'skip', { head: 5 })).toEqual({
|
|
88
|
+
records: [],
|
|
89
|
+
skippedLines: 0,
|
|
90
|
+
});
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
it('head 的槽位口径与整读一致:坏行占一个尾部槽位、不进 records', () => {
|
|
94
|
+
const dirty = path.join(dir, 'dirty.jsonl');
|
|
95
|
+
realFs.writeFileSync(dirty, 'not-json\n{"n":2}\n{"n":3}\n', 'utf-8');
|
|
96
|
+
const { records, skippedLines } = readJsonl<Row>(dirty, 'skip', { head: 2 });
|
|
97
|
+
expect(skippedLines).toBe(1);
|
|
98
|
+
expect(records.map(r => r.n)).toEqual([2]);
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
it('末行无换行时 head 也能取到它', () => {
|
|
102
|
+
const noEol = path.join(dir, 'no-eol.jsonl');
|
|
103
|
+
realFs.writeFileSync(noEol, '{"n":1}\n{"n":2}', 'utf-8');
|
|
104
|
+
expect(readJsonl<Row>(noEol, 'throw', { head: 2 }).records.map(r => r.n)).toEqual([1, 2]);
|
|
105
|
+
});
|
|
106
|
+
});
|
|
@@ -17,9 +17,11 @@
|
|
|
17
17
|
* 裁决——否则「改成 throw 就免掉告知」成了原则的绕行通道。`src/utils/jsonl.ts` 本身是策略
|
|
18
18
|
* 定义处,排除。
|
|
19
19
|
* `readJsonlEnds`(首尾读取)同样是 skip 读点,纳入同一识别正则——否则它成为本契约的
|
|
20
|
-
* 绕行通道(harness#114
|
|
21
|
-
*
|
|
22
|
-
*
|
|
20
|
+
* 绕行通道(harness#114)。`readJsonlWindow`(尾部窗口,ADR-0023 供一次运行内多个
|
|
21
|
+
* 尾部消费方共用一份行文本)同判据纳入。
|
|
22
|
+
* `countJsonlLines`(纯计数入口,把坏行算进条数却不 parse)已随其唯一消费点——`check` 的
|
|
23
|
+
* 提示阈值——改用有界 `head` 读而删除(ADR-0023 决策 3 推广);该消费点因此成为本表内的
|
|
24
|
+
* skip 站点,按契约就地记名豁免。
|
|
23
25
|
*/
|
|
24
26
|
|
|
25
27
|
import * as fs from 'fs';
|
|
@@ -41,9 +43,10 @@ const POLICY_WINDOW_LINES = 6;
|
|
|
41
43
|
* 每个读点的去向理由在调用点注释里,此表只钉「有哪些点、各几处」。
|
|
42
44
|
*/
|
|
43
45
|
const EXPECTED_SKIP_READ_SITES: Record<string, number> = {
|
|
46
|
+
'src/cli/commands/check.ts': 1,
|
|
44
47
|
'src/cli/commands/status.ts': 1,
|
|
45
48
|
'src/context/session-manager.ts': 2,
|
|
46
|
-
'src/core/constraints/
|
|
49
|
+
'src/core/constraints/run-env.ts': 1,
|
|
47
50
|
'src/core/constraints/usage-report.ts': 1,
|
|
48
51
|
'src/failure/recorder.ts': 1,
|
|
49
52
|
'src/knowledge/reference-tracker.ts': 1,
|
|
@@ -79,7 +82,7 @@ interface CallSite {
|
|
|
79
82
|
}
|
|
80
83
|
|
|
81
84
|
/**
|
|
82
|
-
* 找出一个文件里所有 readJsonl
|
|
85
|
+
* 找出一个文件里所有 readJsonl 系读点并判出坏行策略。
|
|
83
86
|
* 调用参数可能换行,故从 `readJsonl` 起拼若干行再判:先遇到 `'skip'` 记 skip、
|
|
84
87
|
* 先遇到 `'throw'` 记 throw,两者都不出现记 unknown。
|
|
85
88
|
*/
|
|
@@ -89,7 +92,8 @@ function callSites(file: string): CallSite[] {
|
|
|
89
92
|
|
|
90
93
|
for (let i = 0; i < raw.length; i++) {
|
|
91
94
|
const trimmed = raw[i].trim();
|
|
92
|
-
|
|
95
|
+
// Window 同纳入:否则「改用尾部窗口读」成为 skip 契约的逃逸通道(harness#114 同一手法)
|
|
96
|
+
if (!/readJsonl(?:Ends|Window)?\s*[<(]/.test(trimmed)) continue;
|
|
93
97
|
// 注释行里的示例不是站点(策略定义处 src/utils/jsonl.ts 已整体排除)
|
|
94
98
|
if (trimmed.startsWith('//') || trimmed.startsWith('*') || trimmed.startsWith('/*')) continue;
|
|
95
99
|
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
import * as fs from 'fs';
|
|
6
6
|
import * as os from 'os';
|
|
7
7
|
import * as path from 'path';
|
|
8
|
-
import { readJsonl,
|
|
8
|
+
import { readJsonl, readJsonlEnds, appendJsonl } from '../jsonl';
|
|
9
9
|
|
|
10
10
|
interface Row {
|
|
11
11
|
n: number;
|
|
@@ -88,24 +88,71 @@ describe('readJsonl', () => {
|
|
|
88
88
|
expect(readJsonl<Row>(filePath, 'skip', { tail: 10 }).records).toHaveLength(1);
|
|
89
89
|
expect(readJsonl<Row>(filePath, 'skip', { head: 10 }).records).toHaveLength(1);
|
|
90
90
|
});
|
|
91
|
-
});
|
|
92
91
|
|
|
93
|
-
|
|
94
|
-
|
|
92
|
+
// ===== tail 倒读(ADR-0023 决策 3):语义必须与「整读后 slice」逐字一致 =====
|
|
93
|
+
|
|
94
|
+
it('tail:与整读后截断的结果逐字等价', () => {
|
|
95
|
+
const filePath = write('equiv.jsonl', ['{"n":1}', CORRUPT, '', '{"n":4}', ' ', '{"n":6}']);
|
|
96
|
+
const whole = readJsonl<Row>(filePath, 'skip');
|
|
97
|
+
const tail = readJsonl<Row>(filePath, 'skip', { tail: 3 });
|
|
98
|
+
// 末尾 3 个非空行含 1 个坏行 → records 只剩 2 条,坏行照旧计入 skippedLines
|
|
99
|
+
expect(tail.records).toEqual(whole.records.slice(-2));
|
|
100
|
+
expect(tail.records.map(r => r.n)).toEqual([4, 6]);
|
|
101
|
+
expect(tail.skippedLines).toBe(1);
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
it('tail:跨 64KB 块边界的多字节字符不被切断', () => {
|
|
105
|
+
// 每行含 CJK,行数足够跨过若干个分块边界;残段按字节留到下一轮才解码
|
|
106
|
+
const lines = Array.from(
|
|
107
|
+
{ length: 1200 },
|
|
108
|
+
(_, i) => JSON.stringify({ n: i, 文本: '约束进化飞轮'.repeat(12) })
|
|
109
|
+
);
|
|
110
|
+
const filePath = write('wide.jsonl', lines);
|
|
111
|
+
expect(fs.statSync(filePath).size).toBeGreaterThan(64 * 1024);
|
|
112
|
+
const { records, skippedLines } = readJsonl<{ n: number; 文本: string }>(
|
|
113
|
+
filePath,
|
|
114
|
+
'throw',
|
|
115
|
+
{ tail: 5 }
|
|
116
|
+
);
|
|
117
|
+
expect(skippedLines).toBe(0);
|
|
118
|
+
expect(records.map(r => r.n)).toEqual([1195, 1196, 1197, 1198, 1199]);
|
|
119
|
+
expect(records[0].文本).toBe('约束进化飞轮'.repeat(12));
|
|
120
|
+
});
|
|
95
121
|
|
|
96
|
-
|
|
97
|
-
|
|
122
|
+
it('tail:末行无换行也能取到', () => {
|
|
123
|
+
const filePath = path.join(dir, 'no-eol.jsonl');
|
|
124
|
+
fs.writeFileSync(filePath, '{"n":1}\n{"n":2}', 'utf-8');
|
|
125
|
+
expect(readJsonl<Row>(filePath, 'skip', { tail: 1 }).records.map(r => r.n)).toEqual([2]);
|
|
98
126
|
});
|
|
99
127
|
|
|
100
|
-
|
|
101
|
-
|
|
128
|
+
it('tail:CRLF 的 \\r 随行进文本,parse 照旧通过', () => {
|
|
129
|
+
const filePath = path.join(dir, 'crlf.jsonl');
|
|
130
|
+
fs.writeFileSync(filePath, '{"n":1}\r\n{"n":2}\r\n', 'utf-8');
|
|
131
|
+
expect(readJsonl<Row>(filePath, 'throw', { tail: 2 }).records.map(r => r.n)).toEqual([1, 2]);
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
it('tail:文件尾部的纯空白行不计数,继续往前找够数', () => {
|
|
135
|
+
const filePath = write('blank-tail.jsonl', ['{"n":1}', '{"n":2}', '', ' ', '\t']);
|
|
136
|
+
expect(readJsonl<Row>(filePath, 'skip', { tail: 2 }).records.map(r => r.n)).toEqual([1, 2]);
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
it('tail:走倒读分块,不整读全文——见 jsonl-tail-seek.test.ts(fs 导出属性不可 redefine,只能在模块层 mock)', () => {
|
|
140
|
+
const filePath = write('seek.jsonl', Array.from({ length: 5000 }, (_, i) => `{"n":${i}}`));
|
|
141
|
+
expect(readJsonl<Row>(filePath, 'skip', { tail: 2 }).records.map(r => r.n)).toEqual([
|
|
142
|
+
4998, 4999,
|
|
143
|
+
]);
|
|
102
144
|
});
|
|
103
145
|
|
|
104
|
-
it('
|
|
105
|
-
const filePath =
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
146
|
+
it('tail: 0 与缺文件均返回空结果,不打开文件', () => {
|
|
147
|
+
const filePath = write('zero.jsonl', ['{"n":1}']);
|
|
148
|
+
expect(readJsonl<Row>(filePath, 'skip', { tail: 0 })).toEqual({
|
|
149
|
+
records: [],
|
|
150
|
+
skippedLines: 0,
|
|
151
|
+
});
|
|
152
|
+
expect(readJsonl<Row>(path.join(dir, 'missing.jsonl'), 'skip', { tail: 5 })).toEqual({
|
|
153
|
+
records: [],
|
|
154
|
+
skippedLines: 0,
|
|
155
|
+
});
|
|
109
156
|
});
|
|
110
157
|
});
|
|
111
158
|
|
|
@@ -172,7 +219,7 @@ describe('appendJsonl', () => {
|
|
|
172
219
|
appendJsonl(filePath, { n: 1 });
|
|
173
220
|
appendJsonl(filePath, { n: 2 });
|
|
174
221
|
|
|
175
|
-
expect(
|
|
222
|
+
expect(readJsonl<Row>(filePath, 'skip').records.map(r => r.n)).toEqual([1, 2]);
|
|
176
223
|
const lines = fs.readFileSync(filePath, 'utf-8').split('\n');
|
|
177
224
|
expect(lines[0]).toBe('{"n":1}');
|
|
178
225
|
expect(lines[1]).toBe('{"n":2}');
|