@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
|
@@ -8,14 +8,25 @@
|
|
|
8
8
|
* D4: promotion-blocked, orphan-draft
|
|
9
9
|
* D5: stale-entry
|
|
10
10
|
* D6: flywheel (computed from referencedBy)
|
|
11
|
+
*
|
|
12
|
+
* 构造收 store(harness#134):打分离开文件系统即可测——本文件除了「修复动作是否真落盘」
|
|
13
|
+
* 那几条用真 FileKnowledgeStore(落盘可见性就是它的判定面),其余全部走内存 store 双。
|
|
11
14
|
*/
|
|
12
15
|
|
|
13
16
|
import * as fs from 'fs';
|
|
14
17
|
import * as path from 'path';
|
|
15
18
|
import * as os from 'os';
|
|
16
19
|
import { KnowledgeAudit } from '../audit';
|
|
17
|
-
import { FileKnowledgeStore
|
|
18
|
-
import type {
|
|
20
|
+
import { FileKnowledgeStore } from '../store';
|
|
21
|
+
import type { KnowledgeStore } from '../store';
|
|
22
|
+
import type {
|
|
23
|
+
ConsumptionStats,
|
|
24
|
+
IndexEntry,
|
|
25
|
+
KnowledgeEntry,
|
|
26
|
+
QueryFilter,
|
|
27
|
+
SnapshotSurvival,
|
|
28
|
+
StoreUpdate,
|
|
29
|
+
} from '../types';
|
|
19
30
|
|
|
20
31
|
function makeTmpDir(): string {
|
|
21
32
|
return fs.mkdtempSync(path.join(os.tmpdir(), 'audit-test-'));
|
|
@@ -44,8 +55,58 @@ function makeEntry(overrides: Partial<KnowledgeEntry> = {}): KnowledgeEntry {
|
|
|
44
55
|
};
|
|
45
56
|
}
|
|
46
57
|
|
|
47
|
-
|
|
48
|
-
|
|
58
|
+
/**
|
|
59
|
+
* 内存 store 双:只服务 audit 的取数形状(list / get / applyAll / 环境数据),
|
|
60
|
+
* 与 FileKnowledgeStore 同实现 KnowledgeStore 接口——接口变动即编译期报错。
|
|
61
|
+
*/
|
|
62
|
+
class MemoryKnowledgeStore implements KnowledgeStore {
|
|
63
|
+
private entries = new Map<string, KnowledgeEntry>();
|
|
64
|
+
consumptionStats?: ConsumptionStats;
|
|
65
|
+
survival?: SnapshotSurvival;
|
|
66
|
+
applyAllCalls = 0;
|
|
67
|
+
|
|
68
|
+
constructor(entries: KnowledgeEntry[] = []) {
|
|
69
|
+
for (const e of entries) this.entries.set(e.id, e);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
getBaseDir(): string { return 'memory://knowledge'; }
|
|
73
|
+
get(id: string): KnowledgeEntry | undefined { return this.entries.get(id); }
|
|
74
|
+
|
|
75
|
+
list(filter?: QueryFilter): KnowledgeEntry[] {
|
|
76
|
+
const all = [...this.entries.values()];
|
|
77
|
+
if (filter?.excludeArchived === false) return all;
|
|
78
|
+
return all.filter(e => e.maturity !== 'archived' && e.maturity !== 'deprecated');
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
save(entry: KnowledgeEntry): void { this.entries.set(entry.id, entry); }
|
|
82
|
+
saveAll(entries: KnowledgeEntry[]): void { for (const e of entries) this.save(e); }
|
|
83
|
+
applyAll(updates: StoreUpdate[]): void {
|
|
84
|
+
if (updates.length === 0) return; // 接口契约:空批零读写(真实现由 store.test.ts 钉住)
|
|
85
|
+
this.applyAllCalls++;
|
|
86
|
+
for (const { id, partial } of updates) {
|
|
87
|
+
const existing = this.entries.get(id);
|
|
88
|
+
if (existing) this.entries.set(id, { ...existing, ...partial, id });
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
delete(id: string): boolean { return this.entries.delete(id); }
|
|
92
|
+
update(id: string, partial: Partial<KnowledgeEntry>): KnowledgeEntry | undefined {
|
|
93
|
+
const existing = this.entries.get(id);
|
|
94
|
+
if (!existing) return undefined;
|
|
95
|
+
const updated = { ...existing, ...partial, id };
|
|
96
|
+
this.entries.set(id, updated);
|
|
97
|
+
return updated;
|
|
98
|
+
}
|
|
99
|
+
rebuildIndex(): void { /* 内存双无索引 */ }
|
|
100
|
+
readIndex(): IndexEntry[] { return []; }
|
|
101
|
+
readEntriesFromDisk(): KnowledgeEntry[] { return this.list({ excludeArchived: false }); }
|
|
102
|
+
snapshot(): string { return 'memory://knowledge/.snapshots'; }
|
|
103
|
+
getSnapshot(): IndexEntry[] | undefined { return undefined; }
|
|
104
|
+
getSurvivalRate(): SnapshotSurvival | undefined { return this.survival; }
|
|
105
|
+
getConsumptionStats(): ConsumptionStats | undefined { return this.consumptionStats; }
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
function setupStore(dir: string, entries: Partial<KnowledgeEntry>[]): FileKnowledgeStore {
|
|
109
|
+
const store = new FileKnowledgeStore({ baseDir: dir });
|
|
49
110
|
for (const partial of entries) {
|
|
50
111
|
const entry = makeEntry(partial);
|
|
51
112
|
store.save(entry);
|
|
@@ -53,36 +114,39 @@ function setupStore(dir: string, entries: Partial<KnowledgeEntry>[]): KnowledgeS
|
|
|
53
114
|
return store;
|
|
54
115
|
}
|
|
55
116
|
|
|
117
|
+
/** 打分用例的共用形状:内存 store + 已落条目 + 阈值 */
|
|
118
|
+
function memoryAudit(
|
|
119
|
+
entries: KnowledgeEntry[],
|
|
120
|
+
options?: { shortContentThreshold?: number; staleDays?: number; promotionBlockDays?: number }
|
|
121
|
+
): { audit: KnowledgeAudit; store: MemoryKnowledgeStore } {
|
|
122
|
+
const store = new MemoryKnowledgeStore(entries);
|
|
123
|
+
return { audit: new KnowledgeAudit(store, options), store };
|
|
124
|
+
}
|
|
125
|
+
|
|
56
126
|
// ── D1: frontmatter-missing ─────────────────────────────
|
|
57
127
|
|
|
58
128
|
describe('D1: frontmatter-missing', () => {
|
|
59
129
|
it('rejects entry with missing id', () => {
|
|
60
|
-
const
|
|
61
|
-
const audit = new KnowledgeAudit({ baseDir: dir });
|
|
130
|
+
const { audit } = memoryAudit([]);
|
|
62
131
|
const entry = makeEntry({ id: '' as any });
|
|
63
132
|
const issues = audit.validate(entry);
|
|
64
133
|
const fm = issues.filter(i => i.rule === 'frontmatter-missing');
|
|
65
134
|
expect(fm.length).toBe(1);
|
|
66
135
|
expect(fm[0].action).toBe('reject');
|
|
67
|
-
fs.rmSync(dir, { recursive: true });
|
|
68
136
|
});
|
|
69
137
|
|
|
70
138
|
it('rejects entry with missing type', () => {
|
|
71
|
-
const
|
|
72
|
-
const audit = new KnowledgeAudit({ baseDir: dir });
|
|
139
|
+
const { audit } = memoryAudit([]);
|
|
73
140
|
const entry = makeEntry({ type: undefined as any });
|
|
74
141
|
const issues = audit.validate(entry);
|
|
75
142
|
expect(issues.some(i => i.rule === 'frontmatter-missing')).toBe(true);
|
|
76
|
-
fs.rmSync(dir, { recursive: true });
|
|
77
143
|
});
|
|
78
144
|
|
|
79
145
|
it('passes with all required fields present', () => {
|
|
80
|
-
const
|
|
81
|
-
const audit = new KnowledgeAudit({ baseDir: dir });
|
|
146
|
+
const { audit } = memoryAudit([]);
|
|
82
147
|
const entry = makeEntry();
|
|
83
148
|
const issues = audit.validate(entry);
|
|
84
149
|
expect(issues.filter(i => i.rule === 'frontmatter-missing')).toHaveLength(0);
|
|
85
|
-
fs.rmSync(dir, { recursive: true });
|
|
86
150
|
});
|
|
87
151
|
});
|
|
88
152
|
|
|
@@ -90,48 +154,38 @@ describe('D1: frontmatter-missing', () => {
|
|
|
90
154
|
|
|
91
155
|
describe('D2: test-data-pollution', () => {
|
|
92
156
|
it('archives entries with test-scope tags', () => {
|
|
93
|
-
const
|
|
94
|
-
const audit = new KnowledgeAudit({ baseDir: dir });
|
|
157
|
+
const { audit } = memoryAudit([]);
|
|
95
158
|
const entry = makeEntry({ tags: ['test-scope-abc'] });
|
|
96
159
|
const issues = audit.validate(entry);
|
|
97
160
|
expect(issues.some(i => i.rule === 'test-data-pollution' && i.action === 'archive')).toBe(true);
|
|
98
|
-
fs.rmSync(dir, { recursive: true });
|
|
99
161
|
});
|
|
100
162
|
|
|
101
163
|
it('archives entries with test title', () => {
|
|
102
|
-
const
|
|
103
|
-
const audit = new KnowledgeAudit({ baseDir: dir });
|
|
164
|
+
const { audit } = memoryAudit([]);
|
|
104
165
|
const entry = makeEntry({ title: 'Test Entry' });
|
|
105
166
|
const issues = audit.validate(entry);
|
|
106
167
|
expect(issues.some(i => i.rule === 'test-data-pollution')).toBe(true);
|
|
107
|
-
fs.rmSync(dir, { recursive: true });
|
|
108
168
|
});
|
|
109
169
|
|
|
110
170
|
it('archives entries with test-* ID', () => {
|
|
111
|
-
const
|
|
112
|
-
const audit = new KnowledgeAudit({ baseDir: dir });
|
|
171
|
+
const { audit } = memoryAudit([]);
|
|
113
172
|
const entry = makeEntry({ id: 'test-search-1780414709970-pattern' });
|
|
114
173
|
const issues = audit.validate(entry);
|
|
115
174
|
expect(issues.some(i => i.rule === 'test-data-pollution' && i.detail.includes('test-search'))).toBe(true);
|
|
116
|
-
fs.rmSync(dir, { recursive: true });
|
|
117
175
|
});
|
|
118
176
|
|
|
119
177
|
it('archives entries with inj-test-* ID', () => {
|
|
120
|
-
const
|
|
121
|
-
const audit = new KnowledgeAudit({ baseDir: dir });
|
|
178
|
+
const { audit } = memoryAudit([]);
|
|
122
179
|
const entry = makeEntry({ id: 'inj-test-1780414711344' });
|
|
123
180
|
const issues = audit.validate(entry);
|
|
124
181
|
expect(issues.some(i => i.rule === 'test-data-pollution' && i.detail.includes('inj-test'))).toBe(true);
|
|
125
|
-
fs.rmSync(dir, { recursive: true });
|
|
126
182
|
});
|
|
127
183
|
|
|
128
184
|
it('skips archived entries', () => {
|
|
129
|
-
const
|
|
130
|
-
const audit = new KnowledgeAudit({ baseDir: dir });
|
|
185
|
+
const { audit } = memoryAudit([]);
|
|
131
186
|
const entry = makeEntry({ maturity: 'archived', tags: ['test-scope-abc'] });
|
|
132
187
|
const issues = audit.validate(entry);
|
|
133
188
|
expect(issues.filter(i => i.rule === 'test-data-pollution')).toHaveLength(0);
|
|
134
|
-
fs.rmSync(dir, { recursive: true });
|
|
135
189
|
});
|
|
136
190
|
});
|
|
137
191
|
|
|
@@ -139,21 +193,17 @@ describe('D2: test-data-pollution', () => {
|
|
|
139
193
|
|
|
140
194
|
describe('D2: daily-audit-noise', () => {
|
|
141
195
|
it('archives entries with [Auditor] Daily audit title', () => {
|
|
142
|
-
const
|
|
143
|
-
const audit = new KnowledgeAudit({ baseDir: dir });
|
|
196
|
+
const { audit } = memoryAudit([]);
|
|
144
197
|
const entry = makeEntry({ title: '[Auditor] Daily audit 2026-06-01' });
|
|
145
198
|
const issues = audit.validate(entry);
|
|
146
199
|
expect(issues.some(i => i.rule === 'daily-audit-noise' && i.action === 'archive')).toBe(true);
|
|
147
|
-
fs.rmSync(dir, { recursive: true });
|
|
148
200
|
});
|
|
149
201
|
|
|
150
202
|
it('passes for normal titles', () => {
|
|
151
|
-
const
|
|
152
|
-
const audit = new KnowledgeAudit({ baseDir: dir });
|
|
203
|
+
const { audit } = memoryAudit([]);
|
|
153
204
|
const entry = makeEntry({ title: 'Normal knowledge entry' });
|
|
154
205
|
const issues = audit.validate(entry);
|
|
155
206
|
expect(issues.filter(i => i.rule === 'daily-audit-noise')).toHaveLength(0);
|
|
156
|
-
fs.rmSync(dir, { recursive: true });
|
|
157
207
|
});
|
|
158
208
|
});
|
|
159
209
|
|
|
@@ -161,57 +211,45 @@ describe('D2: daily-audit-noise', () => {
|
|
|
161
211
|
|
|
162
212
|
describe('D2: event-noise', () => {
|
|
163
213
|
it('rejects [Monitor] event titles', () => {
|
|
164
|
-
const
|
|
165
|
-
const audit = new KnowledgeAudit({ baseDir: dir });
|
|
214
|
+
const { audit } = memoryAudit([]);
|
|
166
215
|
const entry = makeEntry({ title: '[Monitor] stuck_goals: GoalExecution abc123' });
|
|
167
216
|
const issues = audit.validate(entry);
|
|
168
217
|
expect(issues.some(i => i.rule === 'event-noise' && i.action === 'archive')).toBe(true);
|
|
169
|
-
fs.rmSync(dir, { recursive: true });
|
|
170
218
|
});
|
|
171
219
|
|
|
172
220
|
it('rejects KnowledgeSync cycle titles', () => {
|
|
173
|
-
const
|
|
174
|
-
const audit = new KnowledgeAudit({ baseDir: dir });
|
|
221
|
+
const { audit } = memoryAudit([]);
|
|
175
222
|
const entry = makeEntry({ title: 'KnowledgeSync cycle: 0 stale, 0 unmonitored' });
|
|
176
223
|
const issues = audit.validate(entry);
|
|
177
224
|
expect(issues.some(i => i.rule === 'event-noise' && i.action === 'archive')).toBe(true);
|
|
178
|
-
fs.rmSync(dir, { recursive: true });
|
|
179
225
|
});
|
|
180
226
|
|
|
181
227
|
it('rejects [Triage Fix] titles', () => {
|
|
182
|
-
const
|
|
183
|
-
const audit = new KnowledgeAudit({ baseDir: dir });
|
|
228
|
+
const { audit } = memoryAudit([]);
|
|
184
229
|
const entry = makeEntry({ title: '[Triage Fix] timeout (critical)' });
|
|
185
230
|
const issues = audit.validate(entry);
|
|
186
231
|
expect(issues.some(i => i.rule === 'event-noise' && i.action === 'archive')).toBe(true);
|
|
187
|
-
fs.rmSync(dir, { recursive: true });
|
|
188
232
|
});
|
|
189
233
|
|
|
190
234
|
it('rejects [Session Feature] titles', () => {
|
|
191
|
-
const
|
|
192
|
-
const audit = new KnowledgeAudit({ baseDir: dir });
|
|
235
|
+
const { audit } = memoryAudit([]);
|
|
193
236
|
const entry = makeEntry({ title: '[Session Feature] feat: studio release' });
|
|
194
237
|
const issues = audit.validate(entry);
|
|
195
238
|
expect(issues.some(i => i.rule === 'event-noise' && i.action === 'archive')).toBe(true);
|
|
196
|
-
fs.rmSync(dir, { recursive: true });
|
|
197
239
|
});
|
|
198
240
|
|
|
199
241
|
it('passes for normal knowledge titles', () => {
|
|
200
|
-
const
|
|
201
|
-
const audit = new KnowledgeAudit({ baseDir: dir });
|
|
242
|
+
const { audit } = memoryAudit([]);
|
|
202
243
|
const entry = makeEntry({ title: 'SQLite WAL mode pattern' });
|
|
203
244
|
const issues = audit.validate(entry);
|
|
204
245
|
expect(issues.filter(i => i.rule === 'event-noise')).toHaveLength(0);
|
|
205
|
-
fs.rmSync(dir, { recursive: true });
|
|
206
246
|
});
|
|
207
247
|
|
|
208
248
|
it('skips archived entries', () => {
|
|
209
|
-
const
|
|
210
|
-
const audit = new KnowledgeAudit({ baseDir: dir });
|
|
249
|
+
const { audit } = memoryAudit([]);
|
|
211
250
|
const entry = makeEntry({ maturity: 'archived', title: '[Monitor] stuck_goals: test' });
|
|
212
251
|
const issues = audit.validate(entry);
|
|
213
252
|
expect(issues.filter(i => i.rule === 'event-noise')).toHaveLength(0);
|
|
214
|
-
fs.rmSync(dir, { recursive: true });
|
|
215
253
|
});
|
|
216
254
|
});
|
|
217
255
|
|
|
@@ -219,21 +257,17 @@ describe('D2: event-noise', () => {
|
|
|
219
257
|
|
|
220
258
|
describe('D2: zero-content-proven', () => {
|
|
221
259
|
it('demotes proven entries with <20 chars content', () => {
|
|
222
|
-
const
|
|
223
|
-
const audit = new KnowledgeAudit({ baseDir: dir });
|
|
260
|
+
const { audit } = memoryAudit([]);
|
|
224
261
|
const entry = makeEntry({ maturity: 'proven', content: 'short' });
|
|
225
262
|
const issues = audit.validate(entry);
|
|
226
263
|
expect(issues.some(i => i.rule === 'zero-content-proven' && i.action === 'demote')).toBe(true);
|
|
227
|
-
fs.rmSync(dir, { recursive: true });
|
|
228
264
|
});
|
|
229
265
|
|
|
230
266
|
it('passes for proven entries with sufficient content', () => {
|
|
231
|
-
const
|
|
232
|
-
const audit = new KnowledgeAudit({ baseDir: dir });
|
|
267
|
+
const { audit } = memoryAudit([]);
|
|
233
268
|
const entry = makeEntry({ maturity: 'proven', content: 'a'.repeat(50) });
|
|
234
269
|
const issues = audit.validate(entry);
|
|
235
270
|
expect(issues.filter(i => i.rule === 'zero-content-proven')).toHaveLength(0);
|
|
236
|
-
fs.rmSync(dir, { recursive: true });
|
|
237
271
|
});
|
|
238
272
|
});
|
|
239
273
|
|
|
@@ -241,12 +275,10 @@ describe('D2: zero-content-proven', () => {
|
|
|
241
275
|
|
|
242
276
|
describe('D2: maturity-inflation', () => {
|
|
243
277
|
it('demotes verified entries with <20 chars content', () => {
|
|
244
|
-
const
|
|
245
|
-
const audit = new KnowledgeAudit({ baseDir: dir });
|
|
278
|
+
const { audit } = memoryAudit([]);
|
|
246
279
|
const entry = makeEntry({ maturity: 'verified', content: 'tiny' });
|
|
247
280
|
const issues = audit.validate(entry);
|
|
248
281
|
expect(issues.some(i => i.rule === 'maturity-inflation' && i.action === 'demote')).toBe(true);
|
|
249
|
-
fs.rmSync(dir, { recursive: true });
|
|
250
282
|
});
|
|
251
283
|
});
|
|
252
284
|
|
|
@@ -254,22 +286,18 @@ describe('D2: maturity-inflation', () => {
|
|
|
254
286
|
|
|
255
287
|
describe('D2: short-content', () => {
|
|
256
288
|
it('flags entries with content between 20-50 chars', () => {
|
|
257
|
-
const
|
|
258
|
-
const audit = new KnowledgeAudit({ baseDir: dir });
|
|
289
|
+
const { audit } = memoryAudit([]);
|
|
259
290
|
// 25 chars — above zero threshold (20) but below short threshold (50)
|
|
260
291
|
const entry = makeEntry({ content: 'a'.repeat(25), maturity: 'draft' });
|
|
261
292
|
const issues = audit.validate(entry);
|
|
262
293
|
expect(issues.some(i => i.rule === 'short-content' && i.action === 'flag')).toBe(true);
|
|
263
|
-
fs.rmSync(dir, { recursive: true });
|
|
264
294
|
});
|
|
265
295
|
|
|
266
296
|
it('respects custom threshold', () => {
|
|
267
|
-
const
|
|
268
|
-
const audit = new KnowledgeAudit({ baseDir: dir, shortContentThreshold: 100 });
|
|
297
|
+
const { audit } = memoryAudit([], { shortContentThreshold: 100 });
|
|
269
298
|
const entry = makeEntry({ content: 'a'.repeat(60), maturity: 'draft' });
|
|
270
299
|
const issues = audit.validate(entry);
|
|
271
300
|
expect(issues.some(i => i.rule === 'short-content')).toBe(true);
|
|
272
|
-
fs.rmSync(dir, { recursive: true });
|
|
273
301
|
});
|
|
274
302
|
});
|
|
275
303
|
|
|
@@ -277,60 +305,49 @@ describe('D2: short-content', () => {
|
|
|
277
305
|
|
|
278
306
|
describe('D3: title-duplicate', () => {
|
|
279
307
|
it('flags entries with duplicate titles of same type', () => {
|
|
280
|
-
const
|
|
281
|
-
|
|
282
|
-
{ id: 'A-
|
|
283
|
-
{ id: 'A-002', title: 'Same Title', type: 'guideline' },
|
|
308
|
+
const { audit } = memoryAudit([
|
|
309
|
+
makeEntry({ id: 'A-001', title: 'Same Title', type: 'guideline' }),
|
|
310
|
+
makeEntry({ id: 'A-002', title: 'Same Title', type: 'guideline' }),
|
|
284
311
|
]);
|
|
285
|
-
const audit = new KnowledgeAudit({ baseDir: dir });
|
|
286
312
|
const report = audit.run();
|
|
287
313
|
const dupes = report.issues.filter(i => i.rule === 'title-duplicate');
|
|
288
314
|
expect(dupes.length).toBe(2);
|
|
289
|
-
fs.rmSync(dir, { recursive: true });
|
|
290
315
|
});
|
|
291
316
|
|
|
292
317
|
it('ignores archived entries in dedup', () => {
|
|
293
|
-
const
|
|
294
|
-
|
|
295
|
-
{ id: 'A-
|
|
296
|
-
{ id: 'A-002', title: 'Same Title', type: 'guideline' },
|
|
318
|
+
const { audit } = memoryAudit([
|
|
319
|
+
makeEntry({ id: 'A-001', title: 'Same Title', type: 'guideline', maturity: 'archived' }),
|
|
320
|
+
makeEntry({ id: 'A-002', title: 'Same Title', type: 'guideline' }),
|
|
297
321
|
]);
|
|
298
|
-
const audit = new KnowledgeAudit({ baseDir: dir });
|
|
299
322
|
const report = audit.run();
|
|
300
323
|
const dupes = report.issues.filter(i => i.rule === 'title-duplicate');
|
|
301
324
|
expect(dupes.length).toBe(0);
|
|
302
|
-
fs.rmSync(dir, { recursive: true });
|
|
303
325
|
});
|
|
304
326
|
});
|
|
305
327
|
|
|
306
328
|
// ── D3: source-refs-bloat ──────────────────────────────
|
|
307
329
|
|
|
308
330
|
describe('D3: source-refs-bloat', () => {
|
|
331
|
+
const bloatRefs = () => Array.from({ length: 25 }, (_, i) => ({
|
|
332
|
+
workflow: `wf-${i}`,
|
|
333
|
+
timestamp: new Date().toISOString(),
|
|
334
|
+
}));
|
|
335
|
+
|
|
309
336
|
it('flags entries with >20 source references', () => {
|
|
310
|
-
const
|
|
311
|
-
const refs = Array.from({ length: 25 }, (_, i) => ({
|
|
312
|
-
workflow: `wf-${i}`,
|
|
313
|
-
timestamp: new Date().toISOString(),
|
|
314
|
-
}));
|
|
315
|
-
setupStore(dir, [{ id: 'B-001', sourceReferences: refs }]);
|
|
316
|
-
const audit = new KnowledgeAudit({ baseDir: dir });
|
|
337
|
+
const { audit } = memoryAudit([makeEntry({ id: 'B-001', sourceReferences: bloatRefs() })]);
|
|
317
338
|
const report = audit.run();
|
|
318
339
|
expect(report.issues.some(i => i.rule === 'source-refs-bloat')).toBe(true);
|
|
319
|
-
fs.rmSync(dir, { recursive: true });
|
|
320
340
|
});
|
|
321
341
|
|
|
322
342
|
it('trims source references on auto-fix', () => {
|
|
323
343
|
const dir = makeTmpDir();
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
}));
|
|
328
|
-
setupStore(dir, [{ id: 'B-001', sourceReferences: refs, title: 'Valid Knowledge Title' }]);
|
|
329
|
-
const audit = new KnowledgeAudit({ baseDir: dir });
|
|
344
|
+
setupStore(dir, [{ id: 'B-001', sourceReferences: bloatRefs(), title: 'Valid Knowledge Title' }]);
|
|
345
|
+
const store = new FileKnowledgeStore({ baseDir: dir });
|
|
346
|
+
const audit = new KnowledgeAudit(store);
|
|
330
347
|
const report = audit.run({ autoFix: true });
|
|
331
348
|
expect(report.autoFixed).toBeGreaterThan(0);
|
|
332
349
|
// re-read from disk after fix
|
|
333
|
-
const store2 = new
|
|
350
|
+
const store2 = new FileKnowledgeStore({ baseDir: dir });
|
|
334
351
|
const updated = store2.get('B-001');
|
|
335
352
|
expect(updated!.sourceReferences.length).toBe(20);
|
|
336
353
|
fs.rmSync(dir, { recursive: true });
|
|
@@ -341,33 +358,22 @@ describe('D3: source-refs-bloat', () => {
|
|
|
341
358
|
|
|
342
359
|
describe('D4: promotion-blocked', () => {
|
|
343
360
|
it('flags draft entries older than 30 days with no references', () => {
|
|
344
|
-
const dir = makeTmpDir();
|
|
345
361
|
const oldDate = new Date(Date.now() - 60 * 24 * 3600_000).toISOString();
|
|
346
|
-
|
|
347
|
-
id: 'C-001',
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
lastReferenced: '',
|
|
351
|
-
}]);
|
|
352
|
-
const audit = new KnowledgeAudit({ baseDir: dir, promotionBlockDays: 30 });
|
|
362
|
+
const { audit } = memoryAudit(
|
|
363
|
+
[makeEntry({ id: 'C-001', maturity: 'draft', created: oldDate, lastReferenced: '' })],
|
|
364
|
+
{ promotionBlockDays: 30 }
|
|
365
|
+
);
|
|
353
366
|
const report = audit.run();
|
|
354
367
|
expect(report.issues.some(i => i.rule === 'promotion-blocked')).toBe(true);
|
|
355
|
-
fs.rmSync(dir, { recursive: true });
|
|
356
368
|
});
|
|
357
369
|
|
|
358
370
|
it('ignores non-draft entries', () => {
|
|
359
|
-
const dir = makeTmpDir();
|
|
360
371
|
const oldDate = new Date(Date.now() - 60 * 24 * 3600_000).toISOString();
|
|
361
|
-
|
|
362
|
-
id: 'C-002',
|
|
363
|
-
|
|
364
|
-
created: oldDate,
|
|
365
|
-
lastReferenced: '',
|
|
366
|
-
}]);
|
|
367
|
-
const audit = new KnowledgeAudit({ baseDir: dir });
|
|
372
|
+
const { audit } = memoryAudit([
|
|
373
|
+
makeEntry({ id: 'C-002', maturity: 'verified', created: oldDate, lastReferenced: '' }),
|
|
374
|
+
]);
|
|
368
375
|
const report = audit.run();
|
|
369
376
|
expect(report.issues.filter(i => i.rule === 'promotion-blocked')).toHaveLength(0);
|
|
370
|
-
fs.rmSync(dir, { recursive: true });
|
|
371
377
|
});
|
|
372
378
|
});
|
|
373
379
|
|
|
@@ -375,33 +381,19 @@ describe('D4: promotion-blocked', () => {
|
|
|
375
381
|
|
|
376
382
|
describe('D4: orphan-draft', () => {
|
|
377
383
|
it('flags drafts with no contributors/projects/references', () => {
|
|
378
|
-
const
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
maturity: 'draft',
|
|
382
|
-
contributors: [],
|
|
383
|
-
projects: [],
|
|
384
|
-
referencedBy: [],
|
|
385
|
-
}]);
|
|
386
|
-
const audit = new KnowledgeAudit({ baseDir: dir });
|
|
384
|
+
const { audit } = memoryAudit([
|
|
385
|
+
makeEntry({ id: 'D-001', maturity: 'draft', contributors: [], projects: [], referencedBy: [] }),
|
|
386
|
+
]);
|
|
387
387
|
const report = audit.run();
|
|
388
388
|
expect(report.issues.some(i => i.rule === 'orphan-draft')).toBe(true);
|
|
389
|
-
fs.rmSync(dir, { recursive: true });
|
|
390
389
|
});
|
|
391
390
|
|
|
392
391
|
it('passes for drafts with contributors', () => {
|
|
393
|
-
const
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
maturity: 'draft',
|
|
397
|
-
contributors: ['user-a'],
|
|
398
|
-
projects: [],
|
|
399
|
-
referencedBy: [],
|
|
400
|
-
}]);
|
|
401
|
-
const audit = new KnowledgeAudit({ baseDir: dir });
|
|
392
|
+
const { audit } = memoryAudit([
|
|
393
|
+
makeEntry({ id: 'D-002', maturity: 'draft', contributors: ['user-a'], projects: [], referencedBy: [] }),
|
|
394
|
+
]);
|
|
402
395
|
const report = audit.run();
|
|
403
396
|
expect(report.issues.filter(i => i.rule === 'orphan-draft')).toHaveLength(0);
|
|
404
|
-
fs.rmSync(dir, { recursive: true });
|
|
405
397
|
});
|
|
406
398
|
});
|
|
407
399
|
|
|
@@ -409,31 +401,22 @@ describe('D4: orphan-draft', () => {
|
|
|
409
401
|
|
|
410
402
|
describe('D5: stale-entry', () => {
|
|
411
403
|
it('flags entries older than staleDays threshold', () => {
|
|
412
|
-
const dir = makeTmpDir();
|
|
413
404
|
const oldDate = new Date(Date.now() - 120 * 24 * 3600_000).toISOString();
|
|
414
|
-
|
|
415
|
-
id: 'E-001',
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
}]);
|
|
419
|
-
const audit = new KnowledgeAudit({ baseDir: dir, staleDays: 90 });
|
|
405
|
+
const { audit } = memoryAudit(
|
|
406
|
+
[makeEntry({ id: 'E-001', maturity: 'verified', lastReferenced: oldDate })],
|
|
407
|
+
{ staleDays: 90 }
|
|
408
|
+
);
|
|
420
409
|
const report = audit.run();
|
|
421
410
|
expect(report.issues.some(i => i.rule === 'stale-entry')).toBe(true);
|
|
422
|
-
fs.rmSync(dir, { recursive: true });
|
|
423
411
|
});
|
|
424
412
|
|
|
425
413
|
it('ignores archived entries', () => {
|
|
426
|
-
const dir = makeTmpDir();
|
|
427
414
|
const oldDate = new Date(Date.now() - 120 * 24 * 3600_000).toISOString();
|
|
428
|
-
|
|
429
|
-
id: 'E-002',
|
|
430
|
-
|
|
431
|
-
lastReferenced: oldDate,
|
|
432
|
-
}]);
|
|
433
|
-
const audit = new KnowledgeAudit({ baseDir: dir });
|
|
415
|
+
const { audit } = memoryAudit([
|
|
416
|
+
makeEntry({ id: 'E-002', maturity: 'archived', lastReferenced: oldDate }),
|
|
417
|
+
]);
|
|
434
418
|
const report = audit.run();
|
|
435
419
|
expect(report.issues.filter(i => i.rule === 'stale-entry')).toHaveLength(0);
|
|
436
|
-
fs.rmSync(dir, { recursive: true });
|
|
437
420
|
});
|
|
438
421
|
});
|
|
439
422
|
|
|
@@ -441,23 +424,20 @@ describe('D5: stale-entry', () => {
|
|
|
441
424
|
|
|
442
425
|
describe('D6: flywheel dimension', () => {
|
|
443
426
|
it('computes refCoverage from referencedBy', () => {
|
|
444
|
-
const
|
|
445
|
-
|
|
446
|
-
{ id: 'F-
|
|
447
|
-
{ id: 'F-
|
|
448
|
-
{ id: 'F-003', maturity: 'verified', referencedBy: ['agent-1'] },
|
|
427
|
+
const { audit } = memoryAudit([
|
|
428
|
+
makeEntry({ id: 'F-001', maturity: 'verified', referencedBy: ['agent-1', 'agent-2'] }),
|
|
429
|
+
makeEntry({ id: 'F-002', maturity: 'verified', referencedBy: [] }),
|
|
430
|
+
makeEntry({ id: 'F-003', maturity: 'verified', referencedBy: ['agent-1'] }),
|
|
449
431
|
]);
|
|
450
|
-
const audit = new KnowledgeAudit({ baseDir: dir });
|
|
451
432
|
const report = audit.run();
|
|
452
433
|
// 2/3 entries have refs → 67% coverage
|
|
453
434
|
expect(report.dimensions.flywheel.details.refCoverage).toBe(67);
|
|
454
435
|
expect(report.dimensions.flywheel.score).toBeGreaterThan(0);
|
|
455
|
-
fs.rmSync(dir, { recursive: true });
|
|
456
436
|
});
|
|
457
437
|
|
|
458
438
|
it('gives 0 score for empty store (no entries to measure)', () => {
|
|
459
439
|
const dir = makeTmpDir();
|
|
460
|
-
const audit = new KnowledgeAudit({ baseDir: dir });
|
|
440
|
+
const audit = new KnowledgeAudit(new FileKnowledgeStore({ baseDir: dir }));
|
|
461
441
|
const report = audit.run();
|
|
462
442
|
// 0/0 = no data → score 0 (no flywheel activity)
|
|
463
443
|
expect(report.dimensions.flywheel.score).toBe(0);
|
|
@@ -473,7 +453,7 @@ describe('D6: flywheel dimension', () => {
|
|
|
473
453
|
]);
|
|
474
454
|
|
|
475
455
|
// Without consumption stats → low D6 (no refs)
|
|
476
|
-
const audit1 = new KnowledgeAudit({ baseDir: dir });
|
|
456
|
+
const audit1 = new KnowledgeAudit(new FileKnowledgeStore({ baseDir: dir }));
|
|
477
457
|
const report1 = audit1.run();
|
|
478
458
|
const baseD6 = report1.dimensions.flywheel.score;
|
|
479
459
|
|
|
@@ -483,8 +463,8 @@ describe('D6: flywheel dimension', () => {
|
|
|
483
463
|
JSON.stringify({ date: '2026-06-02', dailyEvents: 10, searchHits: 5 }),
|
|
484
464
|
);
|
|
485
465
|
|
|
486
|
-
// With consumption stats → higher D6
|
|
487
|
-
const audit2 = new KnowledgeAudit({ baseDir: dir });
|
|
466
|
+
// With consumption stats → higher D6(取数口是 store.getConsumptionStats,#134)
|
|
467
|
+
const audit2 = new KnowledgeAudit(new FileKnowledgeStore({ baseDir: dir }));
|
|
488
468
|
const report2 = audit2.run();
|
|
489
469
|
expect(report2.dimensions.flywheel.score).toBeGreaterThan(baseD6);
|
|
490
470
|
expect(report2.dimensions.flywheel.details.dailyConsumptionEvents).toBe(10);
|
|
@@ -493,30 +473,49 @@ describe('D6: flywheel dimension', () => {
|
|
|
493
473
|
});
|
|
494
474
|
});
|
|
495
475
|
|
|
476
|
+
// ── D7: incremental dimension ──────────────────────────
|
|
477
|
+
|
|
478
|
+
describe('D7: incremental dimension(环境数据经 store 供给,#134)', () => {
|
|
479
|
+
it('无可用快照 → 满分并说明无数据', () => {
|
|
480
|
+
const { audit } = memoryAudit([makeEntry({ id: 'S-001', maturity: 'verified' })]);
|
|
481
|
+
const report = audit.run();
|
|
482
|
+
expect(report.dimensions.incremental).toEqual({
|
|
483
|
+
score: 100,
|
|
484
|
+
issues: 0,
|
|
485
|
+
details: { note: 'no 30d snapshot available' },
|
|
486
|
+
});
|
|
487
|
+
});
|
|
488
|
+
|
|
489
|
+
it('有 30 天快照 → 分数即存活率,问题数即流失数', () => {
|
|
490
|
+
const { audit, store } = memoryAudit([makeEntry({ id: 'S-001', maturity: 'verified' })]);
|
|
491
|
+
store.survival = { rate: 75, survived: 3, total: 4, snapshotDate: '2026-08-15' };
|
|
492
|
+
const report = audit.run();
|
|
493
|
+
expect(report.dimensions.incremental).toEqual({
|
|
494
|
+
score: 75,
|
|
495
|
+
issues: 1,
|
|
496
|
+
details: { survivalRate: 75, survived: 3, total: 4, snapshotDate: '2026-08-15' },
|
|
497
|
+
});
|
|
498
|
+
});
|
|
499
|
+
});
|
|
500
|
+
|
|
496
501
|
// ── Health score ────────────────────────────────────────
|
|
497
502
|
|
|
498
503
|
describe('health score', () => {
|
|
499
504
|
it('returns 100 for clean store', () => {
|
|
500
|
-
const
|
|
501
|
-
|
|
502
|
-
{ id: 'G-
|
|
503
|
-
{ id: 'G-002', maturity: 'proven', content: 'b'.repeat(100), title: 'Unique Title B' },
|
|
505
|
+
const { audit } = memoryAudit([
|
|
506
|
+
makeEntry({ id: 'G-001', maturity: 'verified', content: 'a'.repeat(100), title: 'Unique Title A' }),
|
|
507
|
+
makeEntry({ id: 'G-002', maturity: 'proven', content: 'b'.repeat(100), title: 'Unique Title B' }),
|
|
504
508
|
]);
|
|
505
|
-
const audit = new KnowledgeAudit({ baseDir: dir });
|
|
506
509
|
const report = audit.run();
|
|
507
510
|
expect(report.healthScore.before).toBe(100);
|
|
508
|
-
fs.rmSync(dir, { recursive: true });
|
|
509
511
|
});
|
|
510
512
|
|
|
511
513
|
it('decreases with critical issues', () => {
|
|
512
|
-
const
|
|
513
|
-
|
|
514
|
-
{ id: 'G-003', maturity: 'proven', content: 'tiny', title: 'Test Entry' },
|
|
514
|
+
const { audit } = memoryAudit([
|
|
515
|
+
makeEntry({ id: 'G-003', maturity: 'proven', content: 'tiny', title: 'Test Entry' }),
|
|
515
516
|
]);
|
|
516
|
-
const audit = new KnowledgeAudit({ baseDir: dir });
|
|
517
517
|
const report = audit.run();
|
|
518
518
|
expect(report.healthScore.before).toBeLessThan(100);
|
|
519
|
-
fs.rmSync(dir, { recursive: true });
|
|
520
519
|
});
|
|
521
520
|
});
|
|
522
521
|
|
|
@@ -524,37 +523,58 @@ describe('health score', () => {
|
|
|
524
523
|
|
|
525
524
|
describe('auto-fix', () => {
|
|
526
525
|
it('archives test data entries', () => {
|
|
527
|
-
const
|
|
528
|
-
|
|
529
|
-
{ id: 'H-001', tags: ['test-scope-abc'], maturity: 'verified' },
|
|
526
|
+
const { audit, store } = memoryAudit([
|
|
527
|
+
makeEntry({ id: 'H-001', tags: ['test-scope-abc'], maturity: 'verified' }),
|
|
530
528
|
]);
|
|
531
|
-
const audit = new KnowledgeAudit({ baseDir: dir });
|
|
532
529
|
const report = audit.run({ autoFix: true });
|
|
533
530
|
expect(report.autoFixed).toBeGreaterThan(0);
|
|
534
531
|
expect(store.get('H-001')!.maturity).toBe('archived');
|
|
535
|
-
fs.rmSync(dir, { recursive: true });
|
|
536
532
|
});
|
|
537
533
|
|
|
538
534
|
it('demotes zero-content proven entries', () => {
|
|
539
|
-
const
|
|
540
|
-
|
|
541
|
-
{ id: 'H-002', maturity: 'proven', content: 'tiny', title: 'Normal Title' },
|
|
535
|
+
const { audit, store } = memoryAudit([
|
|
536
|
+
makeEntry({ id: 'H-002', maturity: 'proven', content: 'tiny', title: 'Normal Title' }),
|
|
542
537
|
]);
|
|
543
|
-
const audit = new KnowledgeAudit({ baseDir: dir });
|
|
544
538
|
audit.run({ autoFix: true });
|
|
545
539
|
expect(store.get('H-002')!.maturity).toBe('draft');
|
|
546
|
-
fs.rmSync(dir, { recursive: true });
|
|
547
540
|
});
|
|
548
541
|
|
|
549
542
|
it('flags short content entries with low_quality tag', () => {
|
|
550
|
-
const
|
|
551
|
-
|
|
552
|
-
{ id: 'H-003', maturity: 'draft', content: 'a'.repeat(30), title: 'Normal Title 2' },
|
|
543
|
+
const { audit, store } = memoryAudit([
|
|
544
|
+
makeEntry({ id: 'H-003', maturity: 'draft', content: 'a'.repeat(30), title: 'Normal Title 2' }),
|
|
553
545
|
]);
|
|
554
|
-
const audit = new KnowledgeAudit({ baseDir: dir });
|
|
555
546
|
audit.run({ autoFix: true });
|
|
556
547
|
expect(store.get('H-003')!.tags).toContain('low_quality');
|
|
557
|
-
|
|
548
|
+
});
|
|
549
|
+
|
|
550
|
+
it('整批修复走一次 applyAll(不再逐条 update)', () => {
|
|
551
|
+
const { audit, store } = memoryAudit([
|
|
552
|
+
makeEntry({ id: 'H-004', tags: ['test-scope-a'], maturity: 'verified', title: 'Noise One' }),
|
|
553
|
+
makeEntry({ id: 'H-005', tags: ['test-scope-b'], maturity: 'verified', title: 'Noise Two' }),
|
|
554
|
+
makeEntry({ id: 'H-006', tags: ['test-scope-c'], maturity: 'verified', title: 'Noise Three' }),
|
|
555
|
+
]);
|
|
556
|
+
const report = audit.run({ autoFix: true });
|
|
557
|
+
expect(report.autoFixed).toBe(3); // 正对照:3 条真修复
|
|
558
|
+
expect(store.applyAllCalls).toBe(1);
|
|
559
|
+
});
|
|
560
|
+
|
|
561
|
+
it('无修复项时不触发批量写', () => {
|
|
562
|
+
const { audit, store } = memoryAudit([
|
|
563
|
+
makeEntry({ id: 'H-007', maturity: 'verified', content: 'a'.repeat(100), title: 'Clean Title' }),
|
|
564
|
+
]);
|
|
565
|
+
const report = audit.run({ autoFix: true });
|
|
566
|
+
expect(report.autoFixed).toBe(0);
|
|
567
|
+
expect(store.applyAllCalls).toBe(0);
|
|
568
|
+
});
|
|
569
|
+
|
|
570
|
+
it('reject 类问题(frontmatter 缺失)只报告、不产生任何修复动作', () => {
|
|
571
|
+
const { audit, store } = memoryAudit([
|
|
572
|
+
makeEntry({ id: '' as any, maturity: 'verified', title: 'Blank Id Title' }),
|
|
573
|
+
]);
|
|
574
|
+
const report = audit.run({ autoFix: true });
|
|
575
|
+
expect(report.issues.some(i => i.action === 'reject')).toBe(true);
|
|
576
|
+
expect(report.autoFixed).toBe(0);
|
|
577
|
+
expect(store.applyAllCalls).toBe(0);
|
|
558
578
|
});
|
|
559
579
|
});
|
|
560
580
|
|
|
@@ -562,54 +582,72 @@ describe('auto-fix', () => {
|
|
|
562
582
|
|
|
563
583
|
describe('D2b: deprecated-domain', () => {
|
|
564
584
|
it('flags entry with "pipeline" tag', () => {
|
|
565
|
-
const
|
|
566
|
-
const audit = new KnowledgeAudit({ baseDir: dir });
|
|
585
|
+
const { audit } = memoryAudit([]);
|
|
567
586
|
const entry = makeEntry({ tags: ['pipeline', 'deployment'] });
|
|
568
587
|
const issues = audit.validate(entry);
|
|
569
588
|
const dd = issues.filter(i => i.rule === 'deprecated-domain');
|
|
570
589
|
expect(dd.length).toBe(1);
|
|
571
590
|
expect(dd[0].action).toBe('archive');
|
|
572
591
|
expect(dd[0].severity).toBe('high');
|
|
573
|
-
fs.rmSync(dir, { recursive: true });
|
|
574
592
|
});
|
|
575
593
|
|
|
576
594
|
it('flags entry with "pipeline" in title', () => {
|
|
577
|
-
const
|
|
578
|
-
const audit = new KnowledgeAudit({ baseDir: dir });
|
|
595
|
+
const { audit } = memoryAudit([]);
|
|
579
596
|
const entry = makeEntry({ title: 'Pipeline 工作单元不可见导致协作效率低' });
|
|
580
597
|
const issues = audit.validate(entry);
|
|
581
598
|
const dd = issues.filter(i => i.rule === 'deprecated-domain');
|
|
582
599
|
expect(dd.length).toBe(1);
|
|
583
|
-
fs.rmSync(dir, { recursive: true });
|
|
584
600
|
});
|
|
585
601
|
|
|
586
602
|
it('flags entry with "管线" in title', () => {
|
|
587
|
-
const
|
|
588
|
-
const audit = new KnowledgeAudit({ baseDir: dir });
|
|
603
|
+
const { audit } = memoryAudit([]);
|
|
589
604
|
const entry = makeEntry({ title: 'B51 管线端到端验证常量与测试' });
|
|
590
605
|
const issues = audit.validate(entry);
|
|
591
606
|
const dd = issues.filter(i => i.rule === 'deprecated-domain');
|
|
592
607
|
expect(dd.length).toBe(1);
|
|
593
|
-
fs.rmSync(dir, { recursive: true });
|
|
594
608
|
});
|
|
595
609
|
|
|
596
610
|
it('does not flag entry without deprecated domain references', () => {
|
|
597
|
-
const
|
|
598
|
-
const audit = new KnowledgeAudit({ baseDir: dir });
|
|
611
|
+
const { audit } = memoryAudit([]);
|
|
599
612
|
const entry = makeEntry({ title: 'Agent Network 核心概念', tags: ['agent', 'architecture'] });
|
|
600
613
|
const issues = audit.validate(entry);
|
|
601
614
|
const dd = issues.filter(i => i.rule === 'deprecated-domain');
|
|
602
615
|
expect(dd.length).toBe(0);
|
|
603
|
-
fs.rmSync(dir, { recursive: true });
|
|
604
616
|
});
|
|
605
617
|
|
|
606
618
|
it('skips already archived entries', () => {
|
|
607
|
-
const
|
|
608
|
-
const audit = new KnowledgeAudit({ baseDir: dir });
|
|
619
|
+
const { audit } = memoryAudit([]);
|
|
609
620
|
const entry = makeEntry({ title: 'Pipeline legacy entry', maturity: 'archived', tags: ['pipeline'] });
|
|
610
621
|
const issues = audit.validate(entry);
|
|
611
622
|
const dd = issues.filter(i => i.rule === 'deprecated-domain');
|
|
612
623
|
expect(dd.length).toBe(0);
|
|
624
|
+
});
|
|
625
|
+
});
|
|
626
|
+
|
|
627
|
+
// ── 构造形状(harness#134 验收 3)───────────────────────
|
|
628
|
+
|
|
629
|
+
describe('构造收 store', () => {
|
|
630
|
+
it('validate() 全程不碰存储(判定不依赖 fs)', () => {
|
|
631
|
+
const forbidden = new Proxy({} as KnowledgeStore, {
|
|
632
|
+
get(_target, prop) {
|
|
633
|
+
throw new Error(`validate() 不该读存储(碰了 ${String(prop)})`);
|
|
634
|
+
},
|
|
635
|
+
});
|
|
636
|
+
const audit = new KnowledgeAudit(forbidden);
|
|
637
|
+
const issues = audit.validate(makeEntry({ title: 'Normal knowledge entry' }));
|
|
638
|
+
expect(Array.isArray(issues)).toBe(true);
|
|
639
|
+
});
|
|
640
|
+
|
|
641
|
+
it('与兄弟模块同形:lint / doctor / query / lifecycle / ingest 一样收 store', async () => {
|
|
642
|
+
const dir = makeTmpDir();
|
|
643
|
+
const store = setupStore(dir, [{ id: 'W-001', title: 'Wiring Entry' }]);
|
|
644
|
+
const { KnowledgeLifecycle } = await import('../lifecycle');
|
|
645
|
+
const { KnowledgeQuery } = await import('../query');
|
|
646
|
+
expect(() => new KnowledgeAudit(store)).not.toThrow();
|
|
647
|
+
expect(new KnowledgeAudit(store).run().totalEntries).toBe(1);
|
|
648
|
+
// 兄弟模块的构造第一形参就是 store
|
|
649
|
+
expect(new KnowledgeLifecycle(store)).toBeInstanceOf(KnowledgeLifecycle);
|
|
650
|
+
expect(new KnowledgeQuery(store)).toBeInstanceOf(KnowledgeQuery);
|
|
613
651
|
fs.rmSync(dir, { recursive: true });
|
|
614
652
|
});
|
|
615
653
|
});
|
|
@@ -618,9 +656,7 @@ describe('D2b: deprecated-domain', () => {
|
|
|
618
656
|
|
|
619
657
|
describe('run() report', () => {
|
|
620
658
|
it('returns correct report structure', () => {
|
|
621
|
-
const
|
|
622
|
-
setupStore(dir, [{ id: 'I-001' }]);
|
|
623
|
-
const audit = new KnowledgeAudit({ baseDir: dir });
|
|
659
|
+
const { audit } = memoryAudit([makeEntry({ id: 'I-001' })]);
|
|
624
660
|
const report = audit.run();
|
|
625
661
|
expect(report).toHaveProperty('timestamp');
|
|
626
662
|
expect(report).toHaveProperty('totalEntries');
|
|
@@ -637,18 +673,65 @@ describe('run() report', () => {
|
|
|
637
673
|
expect(report.dimensions).toHaveProperty('maturity');
|
|
638
674
|
expect(report.dimensions).toHaveProperty('freshness');
|
|
639
675
|
expect(report.dimensions).toHaveProperty('flywheel');
|
|
640
|
-
fs.rmSync(dir, { recursive: true });
|
|
641
676
|
});
|
|
642
677
|
|
|
643
678
|
it('summary counts match issue counts', () => {
|
|
644
|
-
const
|
|
645
|
-
|
|
646
|
-
{ id: 'I-002', title: 'Test Entry', maturity: 'proven', content: 'tiny' },
|
|
679
|
+
const { audit } = memoryAudit([
|
|
680
|
+
makeEntry({ id: 'I-002', title: 'Test Entry', maturity: 'proven', content: 'tiny' }),
|
|
647
681
|
]);
|
|
648
|
-
const audit = new KnowledgeAudit({ baseDir: dir });
|
|
649
682
|
const report = audit.run();
|
|
650
683
|
const totalFromSummary = Object.values(report.summary).reduce((a, b) => a + b, 0);
|
|
651
684
|
expect(totalFromSummary).toBe(report.issues.length);
|
|
652
|
-
|
|
685
|
+
});
|
|
686
|
+
});
|
|
687
|
+
|
|
688
|
+
// ── 构造器数值槽守卫(harness#163)─────────────────────────
|
|
689
|
+
//
|
|
690
|
+
// 三个数值槽显式传入 NaN / ±Infinity / 负数时构造期抛 TypeError(含槽名与实参),
|
|
691
|
+
// 不再放行到打分层静默关掉 short-content / stale-entry / promotion-blocked 三条判定。
|
|
692
|
+
// 未传 / 显式 undefined → 缺省;显式 0 → 合法且按 0 生效(不被兜成缺省)。
|
|
693
|
+
|
|
694
|
+
describe('构造器数值槽守卫(harness#163)', () => {
|
|
695
|
+
const slots = ['shortContentThreshold', 'staleDays', 'promotionBlockDays'] as const;
|
|
696
|
+
const dirtyValues = [NaN, Infinity, -Infinity, -1];
|
|
697
|
+
|
|
698
|
+
for (const slot of slots) {
|
|
699
|
+
for (const value of dirtyValues) {
|
|
700
|
+
it(`${slot} 显式传 ${String(value)} → 构造抛 TypeError,message 含槽名与实参`, () => {
|
|
701
|
+
expect(() => memoryAudit([], { [slot]: value })).toThrow(TypeError);
|
|
702
|
+
expect(() => memoryAudit([], { [slot]: value })).toThrow(new RegExp(`${slot}.*${String(value).replace(/[+-]/g, '\\$&')}`));
|
|
703
|
+
});
|
|
704
|
+
}
|
|
705
|
+
}
|
|
706
|
+
|
|
707
|
+
it('不传 options → 缺省生效(短内容阈值 50)', () => {
|
|
708
|
+
const { audit } = memoryAudit([]);
|
|
709
|
+
const entry = makeEntry({ content: 'a'.repeat(25), maturity: 'draft' });
|
|
710
|
+
expect(audit.validate(entry).some(i => i.rule === 'short-content')).toBe(true);
|
|
711
|
+
});
|
|
712
|
+
|
|
713
|
+
it('显式 undefined → 与未传同果,走缺省', () => {
|
|
714
|
+
const { audit } = memoryAudit([], { shortContentThreshold: undefined, staleDays: undefined, promotionBlockDays: undefined });
|
|
715
|
+
const entry = makeEntry({ content: 'a'.repeat(25), maturity: 'draft' });
|
|
716
|
+
expect(audit.validate(entry).some(i => i.rule === 'short-content')).toBe(true);
|
|
717
|
+
});
|
|
718
|
+
|
|
719
|
+
it('显式 0 → 合法且按 0 生效:short-content 不触发(不被兜回 50)', () => {
|
|
720
|
+
const { audit } = memoryAudit([], { shortContentThreshold: 0 });
|
|
721
|
+
const entry = makeEntry({ content: 'a'.repeat(25), maturity: 'draft' });
|
|
722
|
+
expect(audit.validate(entry).some(i => i.rule === 'short-content')).toBe(false);
|
|
723
|
+
});
|
|
724
|
+
|
|
725
|
+
it('staleDays 显式 0 → 按 0 生效(昨天引用的条目即判 stale,不被兜回 90)', () => {
|
|
726
|
+
const yesterday = new Date(Date.now() - 24 * 60 * 60 * 1000).toISOString();
|
|
727
|
+
const { audit } = memoryAudit([], { staleDays: 0 });
|
|
728
|
+
const entry = makeEntry({ lastReferenced: yesterday, maturity: 'proven' });
|
|
729
|
+
expect(audit.validate(entry).some(i => i.rule === 'stale-entry')).toBe(true);
|
|
730
|
+
});
|
|
731
|
+
|
|
732
|
+
it('合法正有限值(含非整数)→ 行为不变', () => {
|
|
733
|
+
const { audit } = memoryAudit([], { shortContentThreshold: 30.5 });
|
|
734
|
+
expect(audit.validate(makeEntry({ content: 'a'.repeat(30), maturity: 'draft' })).some(i => i.rule === 'short-content')).toBe(true);
|
|
735
|
+
expect(audit.validate(makeEntry({ content: 'a'.repeat(40), maturity: 'draft' })).some(i => i.rule === 'short-content')).toBe(false);
|
|
653
736
|
});
|
|
654
737
|
});
|