@soleri/core 9.0.4 → 9.3.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/data/flows/build.flow.yaml +8 -9
- package/data/flows/deliver.flow.yaml +9 -10
- package/data/flows/design.flow.yaml +3 -4
- package/data/flows/enhance.flow.yaml +5 -6
- package/data/flows/explore.flow.yaml +3 -4
- package/data/flows/fix.flow.yaml +5 -6
- package/data/flows/plan.flow.yaml +4 -5
- package/data/flows/review.flow.yaml +3 -4
- package/dist/brain/intelligence.d.ts +27 -0
- package/dist/brain/intelligence.d.ts.map +1 -1
- package/dist/brain/intelligence.js +160 -14
- package/dist/brain/intelligence.js.map +1 -1
- package/dist/brain/learning-radar.d.ts +4 -0
- package/dist/brain/learning-radar.d.ts.map +1 -1
- package/dist/brain/learning-radar.js +20 -1
- package/dist/brain/learning-radar.js.map +1 -1
- package/dist/brain/strength-scorer.d.ts +31 -0
- package/dist/brain/strength-scorer.d.ts.map +1 -0
- package/dist/brain/strength-scorer.js +264 -0
- package/dist/brain/strength-scorer.js.map +1 -0
- package/dist/chat/agent-loop.d.ts.map +1 -1
- package/dist/chat/agent-loop.js +2 -0
- package/dist/chat/agent-loop.js.map +1 -1
- package/dist/chat/notifications.d.ts.map +1 -1
- package/dist/chat/notifications.js +2 -0
- package/dist/chat/notifications.js.map +1 -1
- package/dist/claudemd/compose.js +1 -1
- package/dist/claudemd/compose.js.map +1 -1
- package/dist/control/intent-router.d.ts.map +1 -1
- package/dist/control/intent-router.js +12 -4
- package/dist/control/intent-router.js.map +1 -1
- package/dist/curator/contradiction-detector.d.ts +27 -0
- package/dist/curator/contradiction-detector.d.ts.map +1 -0
- package/dist/curator/contradiction-detector.js +62 -0
- package/dist/curator/contradiction-detector.js.map +1 -0
- package/dist/curator/curator.d.ts +3 -4
- package/dist/curator/curator.d.ts.map +1 -1
- package/dist/curator/curator.js +94 -453
- package/dist/curator/curator.js.map +1 -1
- package/dist/curator/duplicate-detector.d.ts +14 -0
- package/dist/curator/duplicate-detector.d.ts.map +1 -0
- package/dist/curator/duplicate-detector.js +77 -0
- package/dist/curator/duplicate-detector.js.map +1 -0
- package/dist/curator/health-audit.d.ts +15 -0
- package/dist/curator/health-audit.d.ts.map +1 -0
- package/dist/curator/health-audit.js +97 -0
- package/dist/curator/health-audit.js.map +1 -0
- package/dist/curator/metadata-enricher.d.ts +17 -0
- package/dist/curator/metadata-enricher.d.ts.map +1 -0
- package/dist/curator/metadata-enricher.js +60 -0
- package/dist/curator/metadata-enricher.js.map +1 -0
- package/dist/curator/schema.d.ts +7 -0
- package/dist/curator/schema.d.ts.map +1 -0
- package/dist/curator/schema.js +62 -0
- package/dist/curator/schema.js.map +1 -0
- package/dist/curator/tag-manager.d.ts +36 -0
- package/dist/curator/tag-manager.d.ts.map +1 -0
- package/dist/curator/tag-manager.js +78 -0
- package/dist/curator/tag-manager.js.map +1 -0
- package/dist/engine/bin/soleri-engine.js +24 -3
- package/dist/engine/bin/soleri-engine.js.map +1 -1
- package/dist/engine/core-ops.d.ts.map +1 -1
- package/dist/engine/core-ops.js +23 -8
- package/dist/engine/core-ops.js.map +1 -1
- package/dist/engine/module-manifest.d.ts.map +1 -1
- package/dist/engine/module-manifest.js +42 -2
- package/dist/engine/module-manifest.js.map +1 -1
- package/dist/engine/register-engine.d.ts.map +1 -1
- package/dist/engine/register-engine.js +50 -2
- package/dist/engine/register-engine.js.map +1 -1
- package/dist/errors/retry.d.ts.map +1 -1
- package/dist/errors/retry.js +2 -0
- package/dist/errors/retry.js.map +1 -1
- package/dist/facades/types.d.ts +1 -1
- package/dist/flows/chain-types.d.ts +18 -18
- package/dist/flows/gate-evaluator.d.ts.map +1 -1
- package/dist/flows/gate-evaluator.js +22 -0
- package/dist/flows/gate-evaluator.js.map +1 -1
- package/dist/flows/types.d.ts +157 -28
- package/dist/flows/types.d.ts.map +1 -1
- package/dist/flows/types.js +4 -0
- package/dist/flows/types.js.map +1 -1
- package/dist/index.d.ts +10 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +9 -1
- package/dist/index.js.map +1 -1
- package/dist/intake/intake-pipeline.d.ts.map +1 -1
- package/dist/intake/intake-pipeline.js +1 -0
- package/dist/intake/intake-pipeline.js.map +1 -1
- package/dist/intake/text-ingester.d.ts.map +1 -1
- package/dist/intake/text-ingester.js +2 -0
- package/dist/intake/text-ingester.js.map +1 -1
- package/dist/llm/key-pool.d.ts +1 -1
- package/dist/llm/key-pool.d.ts.map +1 -1
- package/dist/llm/key-pool.js +3 -4
- package/dist/llm/key-pool.js.map +1 -1
- package/dist/llm/utils.d.ts.map +1 -1
- package/dist/llm/utils.js +2 -0
- package/dist/llm/utils.js.map +1 -1
- package/dist/migrations/migration-runner.test-helpers.d.ts +13 -0
- package/dist/migrations/migration-runner.test-helpers.d.ts.map +1 -0
- package/dist/migrations/migration-runner.test-helpers.js +47 -0
- package/dist/migrations/migration-runner.test-helpers.js.map +1 -0
- package/dist/operator/operator-profile.d.ts +44 -0
- package/dist/operator/operator-profile.d.ts.map +1 -0
- package/dist/operator/operator-profile.js +383 -0
- package/dist/operator/operator-profile.js.map +1 -0
- package/dist/operator/operator-signals.d.ts +45 -0
- package/dist/operator/operator-signals.d.ts.map +1 -0
- package/dist/operator/operator-signals.js +228 -0
- package/dist/operator/operator-signals.js.map +1 -0
- package/dist/operator/operator-types.d.ts +360 -0
- package/dist/operator/operator-types.d.ts.map +1 -0
- package/dist/operator/operator-types.js +24 -0
- package/dist/operator/operator-types.js.map +1 -0
- package/dist/packs/types.d.ts +27 -27
- package/dist/paths.d.ts +40 -0
- package/dist/paths.d.ts.map +1 -0
- package/dist/paths.js +98 -0
- package/dist/paths.js.map +1 -0
- package/dist/persistence/index.d.ts +1 -1
- package/dist/persistence/index.d.ts.map +1 -1
- package/dist/persistence/index.js +1 -1
- package/dist/persistence/index.js.map +1 -1
- package/dist/persistence/sqlite-provider.d.ts +2 -0
- package/dist/persistence/sqlite-provider.d.ts.map +1 -1
- package/dist/persistence/sqlite-provider.js +8 -5
- package/dist/persistence/sqlite-provider.js.map +1 -1
- package/dist/planning/evidence-collector.d.ts +13 -1
- package/dist/planning/evidence-collector.d.ts.map +1 -1
- package/dist/planning/evidence-collector.js +33 -0
- package/dist/planning/evidence-collector.js.map +1 -1
- package/dist/planning/gap-analysis.d.ts +5 -4
- package/dist/planning/gap-analysis.d.ts.map +1 -1
- package/dist/planning/gap-analysis.js +7 -341
- package/dist/planning/gap-analysis.js.map +1 -1
- package/dist/planning/gap-passes.d.ts +19 -0
- package/dist/planning/gap-passes.d.ts.map +1 -0
- package/dist/planning/gap-passes.js +174 -0
- package/dist/planning/gap-passes.js.map +1 -0
- package/dist/planning/gap-patterns.d.ts +29 -0
- package/dist/planning/gap-patterns.d.ts.map +1 -0
- package/dist/planning/gap-patterns.js +175 -0
- package/dist/planning/gap-patterns.js.map +1 -0
- package/dist/planning/gap-types.d.ts +1 -1
- package/dist/planning/gap-types.d.ts.map +1 -1
- package/dist/planning/gap-types.js +1 -0
- package/dist/planning/gap-types.js.map +1 -1
- package/dist/planning/github-projection.d.ts +122 -0
- package/dist/planning/github-projection.d.ts.map +1 -0
- package/dist/planning/github-projection.js +313 -0
- package/dist/planning/github-projection.js.map +1 -0
- package/dist/planning/impact-analyzer.d.ts +26 -0
- package/dist/planning/impact-analyzer.d.ts.map +1 -0
- package/dist/planning/impact-analyzer.js +201 -0
- package/dist/planning/impact-analyzer.js.map +1 -0
- package/dist/planning/plan-lifecycle.d.ts +136 -0
- package/dist/planning/plan-lifecycle.d.ts.map +1 -0
- package/dist/planning/plan-lifecycle.js +309 -0
- package/dist/planning/plan-lifecycle.js.map +1 -0
- package/dist/planning/planner-types.d.ts +202 -0
- package/dist/planning/planner-types.d.ts.map +1 -0
- package/dist/planning/planner-types.js +6 -0
- package/dist/planning/planner-types.js.map +1 -0
- package/dist/planning/planner.d.ts +31 -383
- package/dist/planning/planner.d.ts.map +1 -1
- package/dist/planning/planner.js +151 -832
- package/dist/planning/planner.js.map +1 -1
- package/dist/planning/rationalization-detector.d.ts +32 -0
- package/dist/planning/rationalization-detector.d.ts.map +1 -0
- package/dist/planning/rationalization-detector.js +89 -0
- package/dist/planning/rationalization-detector.js.map +1 -0
- package/dist/planning/reconciliation-engine.d.ts +47 -0
- package/dist/planning/reconciliation-engine.d.ts.map +1 -0
- package/dist/planning/reconciliation-engine.js +128 -0
- package/dist/planning/reconciliation-engine.js.map +1 -0
- package/dist/planning/task-verifier.d.ts +85 -0
- package/dist/planning/task-verifier.d.ts.map +1 -0
- package/dist/planning/task-verifier.js +235 -0
- package/dist/planning/task-verifier.js.map +1 -0
- package/dist/plugins/types.d.ts +4 -4
- package/dist/runtime/admin-ops.d.ts +2 -2
- package/dist/runtime/admin-ops.d.ts.map +1 -1
- package/dist/runtime/admin-ops.js +44 -17
- package/dist/runtime/admin-ops.js.map +1 -1
- package/dist/runtime/admin-setup-ops.d.ts.map +1 -1
- package/dist/runtime/admin-setup-ops.js +22 -46
- package/dist/runtime/admin-setup-ops.js.map +1 -1
- package/dist/runtime/archive-ops.d.ts +10 -0
- package/dist/runtime/archive-ops.d.ts.map +1 -0
- package/dist/runtime/archive-ops.js +310 -0
- package/dist/runtime/archive-ops.js.map +1 -0
- package/dist/runtime/branching-ops.d.ts +12 -0
- package/dist/runtime/branching-ops.d.ts.map +1 -0
- package/dist/runtime/branching-ops.js +100 -0
- package/dist/runtime/branching-ops.js.map +1 -0
- package/dist/runtime/capture-ops.d.ts.map +1 -1
- package/dist/runtime/capture-ops.js +42 -7
- package/dist/runtime/capture-ops.js.map +1 -1
- package/dist/runtime/claude-md-helpers.js +1 -1
- package/dist/runtime/claude-md-helpers.js.map +1 -1
- package/dist/runtime/context-health.d.ts +31 -0
- package/dist/runtime/context-health.d.ts.map +1 -0
- package/dist/runtime/context-health.js +57 -0
- package/dist/runtime/context-health.js.map +1 -0
- package/dist/runtime/facades/archive-facade.d.ts +10 -0
- package/dist/runtime/facades/archive-facade.d.ts.map +1 -0
- package/dist/runtime/facades/archive-facade.js +11 -0
- package/dist/runtime/facades/archive-facade.js.map +1 -0
- package/dist/runtime/facades/brain-facade.d.ts.map +1 -1
- package/dist/runtime/facades/brain-facade.js +2 -0
- package/dist/runtime/facades/brain-facade.js.map +1 -1
- package/dist/runtime/facades/branching-facade.d.ts +7 -0
- package/dist/runtime/facades/branching-facade.d.ts.map +1 -0
- package/dist/runtime/facades/branching-facade.js +8 -0
- package/dist/runtime/facades/branching-facade.js.map +1 -0
- package/dist/runtime/facades/chat-facade.d.ts +7 -0
- package/dist/runtime/facades/chat-facade.d.ts.map +1 -1
- package/dist/runtime/facades/chat-facade.js +15 -800
- package/dist/runtime/facades/chat-facade.js.map +1 -1
- package/dist/runtime/facades/chat-service-ops.d.ts +9 -0
- package/dist/runtime/facades/chat-service-ops.d.ts.map +1 -0
- package/dist/runtime/facades/chat-service-ops.js +332 -0
- package/dist/runtime/facades/chat-service-ops.js.map +1 -0
- package/dist/runtime/facades/chat-session-ops.d.ts +8 -0
- package/dist/runtime/facades/chat-session-ops.d.ts.map +1 -0
- package/dist/runtime/facades/chat-session-ops.js +136 -0
- package/dist/runtime/facades/chat-session-ops.js.map +1 -0
- package/dist/runtime/facades/chat-state.d.ts +31 -0
- package/dist/runtime/facades/chat-state.d.ts.map +1 -0
- package/dist/runtime/facades/chat-state.js +32 -0
- package/dist/runtime/facades/chat-state.js.map +1 -0
- package/dist/runtime/facades/chat-transport-ops.d.ts +9 -0
- package/dist/runtime/facades/chat-transport-ops.d.ts.map +1 -0
- package/dist/runtime/facades/chat-transport-ops.js +337 -0
- package/dist/runtime/facades/chat-transport-ops.js.map +1 -0
- package/dist/runtime/facades/control-facade.d.ts.map +1 -1
- package/dist/runtime/facades/control-facade.js +4 -1
- package/dist/runtime/facades/control-facade.js.map +1 -1
- package/dist/runtime/facades/index.d.ts.map +1 -1
- package/dist/runtime/facades/index.js +48 -0
- package/dist/runtime/facades/index.js.map +1 -1
- package/dist/runtime/facades/intake-facade.d.ts +9 -0
- package/dist/runtime/facades/intake-facade.d.ts.map +1 -0
- package/dist/runtime/facades/intake-facade.js +11 -0
- package/dist/runtime/facades/intake-facade.js.map +1 -0
- package/dist/runtime/facades/links-facade.d.ts +9 -0
- package/dist/runtime/facades/links-facade.d.ts.map +1 -0
- package/dist/runtime/facades/links-facade.js +10 -0
- package/dist/runtime/facades/links-facade.js.map +1 -0
- package/dist/runtime/facades/memory-facade.d.ts.map +1 -1
- package/dist/runtime/facades/memory-facade.js +75 -6
- package/dist/runtime/facades/memory-facade.js.map +1 -1
- package/dist/runtime/facades/operator-facade.d.ts +8 -0
- package/dist/runtime/facades/operator-facade.d.ts.map +1 -0
- package/dist/runtime/facades/operator-facade.js +220 -0
- package/dist/runtime/facades/operator-facade.js.map +1 -0
- package/dist/runtime/facades/orchestrate-facade.js +3 -3
- package/dist/runtime/facades/orchestrate-facade.js.map +1 -1
- package/dist/runtime/facades/plan-facade.d.ts.map +1 -1
- package/dist/runtime/facades/plan-facade.js +42 -6
- package/dist/runtime/facades/plan-facade.js.map +1 -1
- package/dist/runtime/facades/review-facade.d.ts +7 -0
- package/dist/runtime/facades/review-facade.d.ts.map +1 -0
- package/dist/runtime/facades/review-facade.js +8 -0
- package/dist/runtime/facades/review-facade.js.map +1 -0
- package/dist/runtime/facades/sync-facade.d.ts +7 -0
- package/dist/runtime/facades/sync-facade.d.ts.map +1 -0
- package/dist/runtime/facades/sync-facade.js +8 -0
- package/dist/runtime/facades/sync-facade.js.map +1 -0
- package/dist/runtime/facades/tier-facade.d.ts +7 -0
- package/dist/runtime/facades/tier-facade.d.ts.map +1 -0
- package/dist/runtime/facades/tier-facade.js +8 -0
- package/dist/runtime/facades/tier-facade.js.map +1 -0
- package/dist/runtime/facades/vault-facade.d.ts +12 -1
- package/dist/runtime/facades/vault-facade.d.ts.map +1 -1
- package/dist/runtime/facades/vault-facade.js +55 -251
- package/dist/runtime/facades/vault-facade.js.map +1 -1
- package/dist/runtime/github-integration.d.ts +49 -0
- package/dist/runtime/github-integration.d.ts.map +1 -0
- package/dist/runtime/github-integration.js +120 -0
- package/dist/runtime/github-integration.js.map +1 -0
- package/dist/runtime/grading-ops.js +1 -1
- package/dist/runtime/grading-ops.js.map +1 -1
- package/dist/runtime/memory-extra-ops.d.ts.map +1 -1
- package/dist/runtime/memory-extra-ops.js +6 -2
- package/dist/runtime/memory-extra-ops.js.map +1 -1
- package/dist/runtime/orchestrate-ops.d.ts.map +1 -1
- package/dist/runtime/orchestrate-ops.js +386 -37
- package/dist/runtime/orchestrate-ops.js.map +1 -1
- package/dist/runtime/planning-extra-ops.d.ts.map +1 -1
- package/dist/runtime/planning-extra-ops.js +69 -4
- package/dist/runtime/planning-extra-ops.js.map +1 -1
- package/dist/runtime/review-ops.d.ts +10 -0
- package/dist/runtime/review-ops.d.ts.map +1 -0
- package/dist/runtime/review-ops.js +97 -0
- package/dist/runtime/review-ops.js.map +1 -0
- package/dist/runtime/runtime.d.ts.map +1 -1
- package/dist/runtime/runtime.js +29 -12
- package/dist/runtime/runtime.js.map +1 -1
- package/dist/runtime/session-briefing.d.ts +3 -0
- package/dist/runtime/session-briefing.d.ts.map +1 -1
- package/dist/runtime/session-briefing.js +72 -1
- package/dist/runtime/session-briefing.js.map +1 -1
- package/dist/runtime/sync-ops.d.ts +12 -0
- package/dist/runtime/sync-ops.d.ts.map +1 -0
- package/dist/runtime/sync-ops.js +288 -0
- package/dist/runtime/sync-ops.js.map +1 -0
- package/dist/runtime/tier-ops.d.ts +13 -0
- package/dist/runtime/tier-ops.d.ts.map +1 -0
- package/dist/runtime/tier-ops.js +110 -0
- package/dist/runtime/tier-ops.js.map +1 -0
- package/dist/runtime/types.d.ts +10 -4
- package/dist/runtime/types.d.ts.map +1 -1
- package/dist/runtime/vault-extra-ops.d.ts +5 -4
- package/dist/runtime/vault-extra-ops.d.ts.map +1 -1
- package/dist/runtime/vault-extra-ops.js +5 -300
- package/dist/runtime/vault-extra-ops.js.map +1 -1
- package/dist/runtime/vault-sharing-ops.d.ts +4 -4
- package/dist/runtime/vault-sharing-ops.d.ts.map +1 -1
- package/dist/runtime/vault-sharing-ops.js +5 -300
- package/dist/runtime/vault-sharing-ops.js.map +1 -1
- package/dist/skills/sync-skills.d.ts +27 -0
- package/dist/skills/sync-skills.d.ts.map +1 -0
- package/dist/skills/sync-skills.js +81 -0
- package/dist/skills/sync-skills.js.map +1 -0
- package/dist/update-check.d.ts +14 -0
- package/dist/update-check.d.ts.map +1 -0
- package/dist/update-check.js +96 -0
- package/dist/update-check.js.map +1 -0
- package/dist/vault/linking.d.ts +10 -12
- package/dist/vault/linking.d.ts.map +1 -1
- package/dist/vault/linking.js +140 -161
- package/dist/vault/linking.js.map +1 -1
- package/dist/vault/vault-entries.d.ts +69 -0
- package/dist/vault/vault-entries.d.ts.map +1 -0
- package/dist/vault/vault-entries.js +299 -0
- package/dist/vault/vault-entries.js.map +1 -0
- package/dist/vault/vault-interfaces.d.ts +153 -0
- package/dist/vault/vault-interfaces.d.ts.map +1 -0
- package/dist/vault/vault-interfaces.js +2 -0
- package/dist/vault/vault-interfaces.js.map +1 -0
- package/dist/vault/vault-maintenance.d.ts +40 -0
- package/dist/vault/vault-maintenance.d.ts.map +1 -0
- package/dist/vault/vault-maintenance.js +146 -0
- package/dist/vault/vault-maintenance.js.map +1 -0
- package/dist/vault/vault-markdown-sync.d.ts +22 -0
- package/dist/vault/vault-markdown-sync.d.ts.map +1 -0
- package/dist/vault/vault-markdown-sync.js +143 -0
- package/dist/vault/vault-markdown-sync.js.map +1 -0
- package/dist/vault/vault-memories.d.ts +61 -0
- package/dist/vault/vault-memories.d.ts.map +1 -0
- package/dist/vault/vault-memories.js +242 -0
- package/dist/vault/vault-memories.js.map +1 -0
- package/dist/vault/vault-schema.d.ts +9 -0
- package/dist/vault/vault-schema.d.ts.map +1 -0
- package/dist/vault/vault-schema.js +205 -0
- package/dist/vault/vault-schema.js.map +1 -0
- package/dist/vault/vault.d.ts +29 -81
- package/dist/vault/vault.d.ts.map +1 -1
- package/dist/vault/vault.js +82 -931
- package/dist/vault/vault.js.map +1 -1
- package/package.json +7 -7
- package/src/agency/agency-manager.test.ts +620 -0
- package/src/agency/default-rules.test.ts +236 -0
- package/src/{__tests__ → brain}/brain-intelligence.test.ts +37 -14
- package/src/{__tests__ → brain}/brain.test.ts +1 -1
- package/src/brain/intelligence.ts +196 -15
- package/src/brain/learning-radar.ts +22 -1
- package/src/{__tests__ → brain}/second-brain-features.test.ts +4 -4
- package/src/{__tests__ → brain}/session-lifecycle.test.ts +2 -2
- package/src/capabilities/chain-mapping.test.ts +66 -0
- package/src/capabilities/registry.test.ts +359 -0
- package/src/chat/agent-loop.test.ts +384 -0
- package/src/chat/agent-loop.ts +2 -0
- package/src/{__tests__ → chat}/chat-differentiators.test.ts +3 -3
- package/src/{__tests__ → chat}/chat-enhanced.test.ts +4 -4
- package/src/{__tests__ → chat}/chat-transport.test.ts +6 -6
- package/src/chat/mcp-bridge.test.ts +178 -0
- package/src/chat/notifications.ts +2 -0
- package/src/chat/output-compressor.test.ts +164 -0
- package/src/claudemd/compose.test.ts +178 -0
- package/src/claudemd/compose.ts +1 -1
- package/src/claudemd/inject.test.ts +201 -0
- package/src/context/context-engine.test.ts +506 -0
- package/src/control/identity-manager.test.ts +305 -0
- package/src/control/intent-router.test.ts +360 -0
- package/src/control/intent-router.ts +13 -4
- package/src/curator/classifier.test.ts +110 -0
- package/src/curator/contradiction-detector.test.ts +205 -0
- package/src/curator/contradiction-detector.ts +87 -0
- package/src/{__tests__ → curator}/curator-pipeline-e2e.test.ts +10 -10
- package/src/{__tests__ → curator}/curator.test.ts +77 -1
- package/src/curator/curator.ts +160 -600
- package/src/curator/duplicate-detector.test.ts +245 -0
- package/src/curator/duplicate-detector.ts +103 -0
- package/src/curator/health-audit.ts +126 -0
- package/src/curator/metadata-enricher.ts +84 -0
- package/src/curator/quality-gate.test.ts +175 -0
- package/src/curator/schema.ts +65 -0
- package/src/curator/tag-manager.test.ts +173 -0
- package/src/curator/tag-manager.ts +109 -0
- package/src/domain-packs/inject-rules.test.ts +117 -0
- package/src/domain-packs/knowledge-installer.test.ts +163 -0
- package/src/domain-packs/loader.test.ts +86 -0
- package/src/domain-packs/pack-runtime.test.ts +140 -0
- package/src/domain-packs/skills-installer.test.ts +135 -0
- package/src/domain-packs/token-resolver.test.ts +148 -0
- package/src/domain-packs/types.test.ts +144 -0
- package/src/enforcement/adapters/claude-code.test.ts +216 -0
- package/src/enforcement/registry.test.ts +258 -0
- package/src/engine/bin/soleri-engine.ts +30 -4
- package/src/engine/core-ops.test.ts +254 -0
- package/src/engine/core-ops.ts +25 -8
- package/src/engine/module-manifest.test.ts +125 -0
- package/src/engine/module-manifest.ts +42 -2
- package/src/engine/register-engine.test.ts +235 -0
- package/src/engine/register-engine.ts +50 -3
- package/src/errors/classify.test.ts +203 -0
- package/src/errors/retry.test.ts +153 -0
- package/src/errors/retry.ts +2 -0
- package/src/errors/types.test.ts +108 -0
- package/src/events/event-bus.test.ts +149 -0
- package/src/extensions/middleware.test.ts +234 -0
- package/src/facades/facade-factory.test.ts +470 -0
- package/src/flows/chain-runner.test.ts +273 -0
- package/src/flows/context-router.test.ts +52 -0
- package/src/flows/dispatch-registry.test.ts +128 -0
- package/src/flows/epilogue.test.ts +113 -0
- package/src/flows/executor.test.ts +263 -0
- package/src/flows/gate-evaluator.test.ts +200 -0
- package/src/flows/gate-evaluator.ts +23 -0
- package/src/flows/types.ts +4 -0
- package/src/governance/governance.test.ts +842 -0
- package/src/{__tests__ → health}/health-registry.test.ts +75 -55
- package/src/health/vault-integrity.test.ts +110 -0
- package/src/index.ts +92 -0
- package/src/intake/content-classifier.test.ts +279 -0
- package/src/intake/dedup-gate.test.ts +147 -0
- package/src/intake/intake-pipeline.test.ts +508 -0
- package/src/intake/intake-pipeline.ts +1 -0
- package/src/intake/text-ingester.test.ts +200 -0
- package/src/intake/text-ingester.ts +2 -0
- package/src/llm/key-pool.test.ts +234 -0
- package/src/llm/key-pool.ts +3 -4
- package/src/llm/llm-client.test.ts +342 -0
- package/src/llm/oauth-discovery.test.ts +180 -0
- package/src/llm/utils.test.ts +371 -0
- package/src/llm/utils.ts +2 -0
- package/src/{__tests__ → logging}/logger.test.ts +44 -62
- package/src/loop/loop-manager.test.ts +515 -0
- package/src/migrations/migration-runner.edge-cases.test.ts +314 -0
- package/src/migrations/migration-runner.test-helpers.ts +64 -0
- package/src/migrations/migration-runner.test.ts +385 -0
- package/src/operator/auto-signal-pipeline.test.ts +207 -0
- package/src/operator/operator-profile-extended.test.ts +330 -0
- package/src/operator/operator-profile.test.ts +332 -0
- package/src/operator/operator-profile.ts +485 -0
- package/src/operator/operator-signals-extended.test.ts +257 -0
- package/src/operator/operator-signals.test.ts +277 -0
- package/src/operator/operator-signals.ts +262 -0
- package/src/operator/operator-types.ts +444 -0
- package/src/operator/prompts/hook-precompact-operator-dispatch.md +98 -0
- package/src/operator/prompts/subagent-soft-signal-extractor.md +130 -0
- package/src/operator/prompts/subagent-synthesis-cognition.md +190 -0
- package/src/operator/prompts/subagent-synthesis-communication.md +146 -0
- package/src/operator/prompts/subagent-synthesis-technical.md +170 -0
- package/src/operator/prompts/subagent-synthesis-trust.md +149 -0
- package/src/{__tests__ → packs}/pack-lockfile.test.ts +3 -3
- package/src/{__tests__ → packs}/pack-system.test.ts +2 -2
- package/src/paths.ts +115 -0
- package/src/persistence/index.ts +1 -1
- package/src/persistence/sqlite-provider.test.ts +540 -0
- package/src/persistence/sqlite-provider.ts +8 -5
- package/src/persona/defaults.test.ts +55 -0
- package/src/persona/loader.test.ts +67 -0
- package/src/persona/prompt-generator.test.ts +127 -0
- package/src/planning/evidence-collector.test.ts +515 -0
- package/src/planning/evidence-collector.ts +47 -0
- package/src/planning/gap-analysis-alternatives.test.ts +199 -0
- package/src/planning/gap-analysis.ts +21 -636
- package/src/planning/gap-passes.test.ts +554 -0
- package/src/planning/gap-passes.ts +367 -0
- package/src/planning/gap-patterns.test.ts +394 -0
- package/src/planning/gap-patterns.ts +317 -0
- package/src/planning/gap-types.ts +4 -1
- package/src/planning/github-projection.test.ts +182 -0
- package/src/planning/github-projection.ts +446 -0
- package/src/planning/impact-analyzer.test.ts +167 -0
- package/src/planning/impact-analyzer.ts +251 -0
- package/src/planning/plan-lifecycle.test.ts +379 -0
- package/src/planning/plan-lifecycle.ts +377 -0
- package/src/planning/planner-types.ts +215 -0
- package/src/{__tests__ → planning}/planner.test.ts +179 -15
- package/src/planning/planner.ts +221 -1112
- package/src/planning/rationalization-detector.test.ts +156 -0
- package/src/planning/rationalization-detector.ts +136 -0
- package/src/planning/reconciliation-engine.test.ts +158 -0
- package/src/planning/reconciliation-engine.ts +161 -0
- package/src/planning/task-verifier.test.ts +267 -0
- package/src/planning/task-verifier.ts +309 -0
- package/src/planning/verification-protocol.test.ts +201 -0
- package/src/playbooks/generic/generic-playbooks.test.ts +438 -0
- package/src/playbooks/index.test.ts +77 -0
- package/src/playbooks/playbook-executor.test.ts +253 -0
- package/src/playbooks/playbook-registry.test.ts +232 -0
- package/src/playbooks/playbook-seeder.test.ts +153 -0
- package/src/plugins/plugin-loader.test.ts +217 -0
- package/src/plugins/plugin-registry.test.ts +284 -0
- package/src/project/project-registry.test.ts +439 -0
- package/src/prompts/parser.test.ts +100 -0
- package/src/prompts/template-manager.test.ts +112 -0
- package/src/{__tests__ → queue}/async-infrastructure.test.ts +3 -3
- package/src/queue/job-queue.test.ts +327 -0
- package/src/queue/pipeline-runner.test.ts +209 -0
- package/src/runtime/admin-extra-ops.test.ts +513 -0
- package/src/runtime/admin-ops.test.ts +255 -0
- package/src/runtime/admin-ops.ts +45 -17
- package/src/runtime/admin-setup-ops.test.ts +327 -0
- package/src/runtime/admin-setup-ops.ts +26 -42
- package/src/runtime/archive-ops.test.ts +272 -0
- package/src/runtime/archive-ops.ts +347 -0
- package/src/runtime/branching-ops.test.ts +144 -0
- package/src/runtime/branching-ops.ts +107 -0
- package/src/runtime/capture-ops.test.ts +419 -0
- package/src/runtime/capture-ops.ts +50 -8
- package/src/runtime/chain-ops.test.ts +159 -0
- package/src/runtime/claude-md-helpers.test.ts +189 -0
- package/src/runtime/claude-md-helpers.ts +1 -1
- package/src/runtime/context-health.test.ts +76 -0
- package/src/runtime/context-health.ts +83 -0
- package/src/runtime/curator-extra-ops.test.ts +204 -0
- package/src/runtime/deprecation.test.ts +98 -0
- package/src/runtime/domain-ops.test.ts +278 -0
- package/src/runtime/facades/admin-facade.test.ts +330 -0
- package/src/runtime/facades/agency-facade.test.ts +278 -0
- package/src/runtime/facades/archive-facade.test.ts +308 -0
- package/src/runtime/facades/archive-facade.ts +14 -0
- package/src/runtime/facades/brain-facade.test.ts +818 -0
- package/src/runtime/facades/brain-facade.ts +2 -0
- package/src/runtime/facades/branching-facade.test.ts +43 -0
- package/src/runtime/facades/branching-facade.ts +11 -0
- package/src/runtime/facades/chat-facade.test.ts +219 -0
- package/src/runtime/facades/chat-facade.ts +15 -906
- package/src/runtime/facades/chat-service-ops.test.ts +381 -0
- package/src/runtime/facades/chat-service-ops.ts +376 -0
- package/src/runtime/facades/chat-session-ops.test.ts +212 -0
- package/src/runtime/facades/chat-session-ops.ts +146 -0
- package/src/runtime/facades/chat-state.ts +60 -0
- package/src/runtime/facades/chat-transport-ops.test.ts +336 -0
- package/src/runtime/facades/chat-transport-ops.ts +379 -0
- package/src/runtime/facades/context-facade.test.ts +123 -0
- package/src/runtime/facades/control-facade.test.ts +436 -0
- package/src/runtime/facades/control-facade.ts +6 -1
- package/src/runtime/facades/curator-facade.test.ts +303 -0
- package/src/runtime/facades/index.ts +48 -0
- package/src/runtime/facades/intake-facade.test.ts +215 -0
- package/src/runtime/facades/intake-facade.ts +14 -0
- package/src/runtime/facades/links-facade.test.ts +203 -0
- package/src/runtime/facades/links-facade.ts +13 -0
- package/src/runtime/facades/loop-facade.test.ts +262 -0
- package/src/runtime/facades/memory-facade.test.ts +283 -0
- package/src/runtime/facades/memory-facade.ts +78 -6
- package/src/runtime/facades/operator-facade.test.ts +221 -0
- package/src/runtime/facades/operator-facade.ts +244 -0
- package/src/runtime/facades/orchestrate-facade.test.ts +191 -0
- package/src/runtime/facades/orchestrate-facade.ts +3 -3
- package/src/runtime/facades/plan-facade.test.ts +283 -0
- package/src/runtime/facades/plan-facade.ts +47 -6
- package/src/runtime/facades/review-facade.test.ts +82 -0
- package/src/runtime/facades/review-facade.ts +11 -0
- package/src/runtime/facades/sync-facade.test.ts +113 -0
- package/src/runtime/facades/sync-facade.ts +11 -0
- package/src/runtime/facades/tier-facade.test.ts +47 -0
- package/src/runtime/facades/tier-facade.ts +11 -0
- package/src/runtime/facades/vault-facade.test.ts +563 -0
- package/src/runtime/facades/vault-facade.ts +66 -265
- package/src/runtime/feature-flags.test.ts +140 -0
- package/src/runtime/github-integration.test.ts +89 -0
- package/src/runtime/github-integration.ts +162 -0
- package/src/runtime/grading-ops.test.ts +172 -0
- package/src/runtime/grading-ops.ts +1 -1
- package/src/runtime/intake-ops.test.ts +261 -0
- package/src/runtime/loop-ops.test.ts +248 -0
- package/src/runtime/memory-cross-project-ops.test.ts +188 -0
- package/src/runtime/memory-extra-ops.test.ts +453 -0
- package/src/runtime/memory-extra-ops.ts +6 -2
- package/src/runtime/orchestrate-ops.test.ts +302 -0
- package/src/runtime/orchestrate-ops.ts +461 -45
- package/src/runtime/pack-ops.test.ts +175 -0
- package/src/runtime/planning-extra-ops.test.ts +593 -0
- package/src/runtime/planning-extra-ops.ts +74 -4
- package/src/{__tests__ → runtime}/playbook-ops-execution.test.ts +3 -3
- package/src/runtime/playbook-ops.test.ts +285 -0
- package/src/runtime/plugin-ops.test.ts +259 -0
- package/src/runtime/project-ops.test.ts +255 -0
- package/src/runtime/review-ops.test.ts +142 -0
- package/src/runtime/review-ops.ts +99 -0
- package/src/runtime/runtime.test.ts +363 -0
- package/src/runtime/runtime.ts +41 -12
- package/src/runtime/session-briefing.test.ts +431 -0
- package/src/runtime/session-briefing.ts +86 -1
- package/src/runtime/sync-ops.test.ts +212 -0
- package/src/runtime/sync-ops.ts +325 -0
- package/src/runtime/telemetry-ops.test.ts +157 -0
- package/src/runtime/tier-ops.test.ts +159 -0
- package/src/runtime/tier-ops.ts +119 -0
- package/src/runtime/types.ts +10 -4
- package/src/runtime/vault-extra-ops.test.ts +270 -0
- package/src/runtime/vault-extra-ops.ts +5 -332
- package/src/runtime/vault-linking-ops.test.ts +237 -0
- package/src/runtime/vault-sharing-ops.test.ts +127 -0
- package/src/runtime/vault-sharing-ops.ts +5 -329
- package/src/skills/sync-skills.ts +98 -0
- package/src/streams/normalize.test.ts +95 -0
- package/src/streams/replayable-stream.test.ts +166 -0
- package/src/telemetry/telemetry.test.ts +143 -0
- package/src/transport/http-server.test.ts +394 -0
- package/src/transport/lsp-server.test.ts +458 -0
- package/src/transport/rate-limiter.test.ts +126 -0
- package/src/transport/session-manager.test.ts +133 -0
- package/src/transport/token-auth.test.ts +136 -0
- package/src/transport/ws-server.test.ts +297 -0
- package/src/update-check.ts +111 -0
- package/src/vault/__tests__/vault-characterization.test.ts +579 -0
- package/src/vault/content-hash.test.ts +78 -0
- package/src/vault/git-vault-sync.test.ts +234 -0
- package/src/vault/knowledge-review.test.ts +269 -0
- package/src/vault/linking.test.ts +391 -0
- package/src/vault/linking.ts +188 -181
- package/src/vault/obsidian-sync.test.ts +345 -0
- package/src/vault/playbook.test.ts +152 -0
- package/src/vault/scope-detector.test.ts +185 -0
- package/src/vault/vault-branching.test.ts +252 -0
- package/src/{__tests__ → vault}/vault-connect.test.ts +1 -1
- package/src/vault/vault-entries.ts +426 -0
- package/src/vault/vault-maintenance.ts +200 -0
- package/src/vault/vault-manager.test.ts +206 -0
- package/src/vault/vault-markdown-sync.test.ts +203 -0
- package/src/vault/vault-markdown-sync.ts +163 -0
- package/src/vault/vault-memories.ts +339 -0
- package/src/{__tests__ → vault}/vault-scaling.test.ts +1 -1
- package/src/vault/vault-schema.ts +238 -0
- package/src/{__tests__ → vault}/vault-sharing.test.ts +4 -4
- package/src/{__tests__ → vault}/vault.test.ts +2 -2
- package/src/vault/vault.ts +87 -1123
- package/dist/cognee/client.d.ts +0 -43
- package/dist/cognee/client.d.ts.map +0 -1
- package/dist/cognee/client.js +0 -375
- package/dist/cognee/client.js.map +0 -1
- package/dist/cognee/sync-manager.d.ts +0 -153
- package/dist/cognee/sync-manager.d.ts.map +0 -1
- package/dist/cognee/sync-manager.js +0 -390
- package/dist/cognee/sync-manager.js.map +0 -1
- package/dist/cognee/types.d.ts +0 -62
- package/dist/cognee/types.d.ts.map +0 -1
- package/dist/cognee/types.js +0 -3
- package/dist/cognee/types.js.map +0 -1
- package/dist/governance/index.d.ts +0 -3
- package/dist/governance/index.d.ts.map +0 -1
- package/dist/governance/index.js +0 -2
- package/dist/governance/index.js.map +0 -1
- package/dist/health/doctor-checks.d.ts +0 -15
- package/dist/health/doctor-checks.d.ts.map +0 -1
- package/dist/health/doctor-checks.js +0 -98
- package/dist/health/doctor-checks.js.map +0 -1
- package/dist/persistence/postgres-provider.d.ts +0 -81
- package/dist/persistence/postgres-provider.d.ts.map +0 -1
- package/dist/persistence/postgres-provider.js +0 -256
- package/dist/persistence/postgres-provider.js.map +0 -1
- package/dist/runtime/cognee-sync-ops.d.ts +0 -12
- package/dist/runtime/cognee-sync-ops.d.ts.map +0 -1
- package/dist/runtime/cognee-sync-ops.js +0 -93
- package/dist/runtime/cognee-sync-ops.js.map +0 -1
- package/dist/runtime/core-ops.d.ts +0 -23
- package/dist/runtime/core-ops.d.ts.map +0 -1
- package/dist/runtime/core-ops.js +0 -1296
- package/dist/runtime/core-ops.js.map +0 -1
- package/dist/runtime/facades/cognee-facade.d.ts +0 -8
- package/dist/runtime/facades/cognee-facade.d.ts.map +0 -1
- package/dist/runtime/facades/cognee-facade.js +0 -156
- package/dist/runtime/facades/cognee-facade.js.map +0 -1
- package/src/__tests__/admin-extra-ops.test.ts +0 -484
- package/src/__tests__/admin-ops.test.ts +0 -268
- package/src/__tests__/admin-setup-ops.test.ts +0 -355
- package/src/__tests__/agency-manager.test.ts +0 -374
- package/src/__tests__/agent-loop.test.ts +0 -256
- package/src/__tests__/capture-ops.test.ts +0 -784
- package/src/__tests__/claudemd.test.ts +0 -282
- package/src/__tests__/content-hash.test.ts +0 -60
- package/src/__tests__/context-engine.test.ts +0 -251
- package/src/__tests__/core-ops.test.ts +0 -550
- package/src/__tests__/curator-extra-ops.test.ts +0 -383
- package/src/__tests__/deprecation.test.ts +0 -78
- package/src/__tests__/domain-ops.test.ts +0 -226
- package/src/__tests__/domain-packs.test.ts +0 -421
- package/src/__tests__/enforcement.test.ts +0 -153
- package/src/__tests__/errors.test.ts +0 -388
- package/src/__tests__/extensions.test.ts +0 -233
- package/src/__tests__/facade-factory.test.ts +0 -271
- package/src/__tests__/feature-flags.test.ts +0 -137
- package/src/__tests__/flows.test.ts +0 -604
- package/src/__tests__/git-vault-sync.test.ts +0 -230
- package/src/__tests__/governance.test.ts +0 -522
- package/src/__tests__/grading-ops.test.ts +0 -361
- package/src/__tests__/identity-manager.test.ts +0 -243
- package/src/__tests__/intake-pipeline.test.ts +0 -162
- package/src/__tests__/intent-router.test.ts +0 -222
- package/src/__tests__/knowledge-review.test.ts +0 -104
- package/src/__tests__/llm-client.test.ts +0 -69
- package/src/__tests__/llm.test.ts +0 -556
- package/src/__tests__/loader.test.ts +0 -176
- package/src/__tests__/loop-ops.test.ts +0 -469
- package/src/__tests__/lsp-transport.test.ts +0 -442
- package/src/__tests__/memory-cross-project-ops.test.ts +0 -248
- package/src/__tests__/memory-extra-ops.test.ts +0 -352
- package/src/__tests__/migration-runner.test.ts +0 -170
- package/src/__tests__/module-manifest-drift.test.ts +0 -59
- package/src/__tests__/normalize.test.ts +0 -85
- package/src/__tests__/obsidian-sync.test.ts +0 -354
- package/src/__tests__/orchestrate-ops.test.ts +0 -289
- package/src/__tests__/pack-ops.test.ts +0 -146
- package/src/__tests__/persistence.test.ts +0 -291
- package/src/__tests__/planning-extra-ops.test.ts +0 -706
- package/src/__tests__/playbook-executor.test.ts +0 -249
- package/src/__tests__/playbook-registry.test.ts +0 -326
- package/src/__tests__/playbook-seeder.test.ts +0 -163
- package/src/__tests__/playbook.test.ts +0 -389
- package/src/__tests__/plugin-ops.test.ts +0 -411
- package/src/__tests__/plugin-system.test.ts +0 -509
- package/src/__tests__/project-ops.test.ts +0 -381
- package/src/__tests__/replayable-stream.test.ts +0 -177
- package/src/__tests__/runtime.test.ts +0 -95
- package/src/__tests__/scope-detector.test.ts +0 -121
- package/src/__tests__/template-manager.test.ts +0 -222
- package/src/__tests__/token-resolver.test.ts +0 -79
- package/src/__tests__/transport.test.ts +0 -758
- package/src/__tests__/vault-branching.test.ts +0 -274
- package/src/__tests__/vault-extra-ops.test.ts +0 -482
- package/src/__tests__/vault-integrity.test.ts +0 -71
- package/src/__tests__/vault-manager.test.ts +0 -238
- package/src/__tests__/ws-transport.test.ts +0 -479
- package/src/engine/index.ts +0 -21
- package/src/persona/index.ts +0 -9
package/src/curator/curator.ts
CHANGED
|
@@ -1,17 +1,10 @@
|
|
|
1
1
|
import type { Vault } from '../vault/vault.js';
|
|
2
2
|
import type { IntelligenceEntry } from '../intelligence/types.js';
|
|
3
3
|
import type { PersistenceProvider } from '../persistence/types.js';
|
|
4
|
-
import {
|
|
5
|
-
tokenize,
|
|
6
|
-
calculateTfIdf,
|
|
7
|
-
cosineSimilarity,
|
|
8
|
-
type SparseVector,
|
|
9
|
-
} from '../text/similarity.js';
|
|
10
4
|
import type {
|
|
11
5
|
CuratorStatus,
|
|
12
6
|
TagNormalizationResult,
|
|
13
7
|
CanonicalTag,
|
|
14
|
-
DuplicateCandidate,
|
|
15
8
|
DuplicateDetectionResult,
|
|
16
9
|
Contradiction,
|
|
17
10
|
ContradictionStatus,
|
|
@@ -23,115 +16,78 @@ import type {
|
|
|
23
16
|
HealthAuditResult,
|
|
24
17
|
} from './types.js';
|
|
25
18
|
|
|
19
|
+
import {
|
|
20
|
+
detectDuplicates as detectDuplicatesPure,
|
|
21
|
+
DEFAULT_DUPLICATE_THRESHOLD,
|
|
22
|
+
} from './duplicate-detector.js';
|
|
23
|
+
import {
|
|
24
|
+
findContradictions,
|
|
25
|
+
DEFAULT_CONTRADICTION_THRESHOLD,
|
|
26
|
+
type ContradictionCandidate,
|
|
27
|
+
} from './contradiction-detector.js';
|
|
28
|
+
import {
|
|
29
|
+
normalizeTag as normalizeTagPure,
|
|
30
|
+
normalizeAndDedup,
|
|
31
|
+
addTagAlias as addTagAliasPure,
|
|
32
|
+
getCanonicalTags as getCanonicalTagsPure,
|
|
33
|
+
seedDefaultAliases,
|
|
34
|
+
type TagStore,
|
|
35
|
+
} from './tag-manager.js';
|
|
36
|
+
import { initializeTables } from './schema.js';
|
|
37
|
+
import { computeHealthAudit, type HealthDataProvider } from './health-audit.js';
|
|
38
|
+
import { enrichEntryMetadata } from './metadata-enricher.js';
|
|
39
|
+
|
|
26
40
|
// ─── Constants ──────────────────────────────────────────────────────
|
|
27
41
|
|
|
28
|
-
const DEFAULT_DUPLICATE_THRESHOLD = 0.45;
|
|
29
|
-
const MERGE_SUGGESTION_THRESHOLD = 0.65;
|
|
30
|
-
const DEFAULT_CONTRADICTION_THRESHOLD = 0.4;
|
|
31
42
|
const DEFAULT_STALE_DAYS = 90;
|
|
32
43
|
|
|
33
|
-
const DEFAULT_TAG_ALIASES: Array<[string, string]> = [
|
|
34
|
-
['a11y', 'accessibility'],
|
|
35
|
-
['ts', 'typescript'],
|
|
36
|
-
['js', 'javascript'],
|
|
37
|
-
['css', 'styling'],
|
|
38
|
-
['tailwind', 'styling'],
|
|
39
|
-
['tw', 'styling'],
|
|
40
|
-
['vitest', 'testing'],
|
|
41
|
-
['jest', 'testing'],
|
|
42
|
-
['perf', 'performance'],
|
|
43
|
-
['sec', 'security'],
|
|
44
|
-
['auth', 'authentication'],
|
|
45
|
-
['i18n', 'internationalization'],
|
|
46
|
-
['l10n', 'localization'],
|
|
47
|
-
];
|
|
48
|
-
|
|
49
44
|
// ─── Curator Class ──────────────────────────────────────────────────
|
|
50
45
|
|
|
51
46
|
export class Curator {
|
|
52
47
|
private vault: Vault;
|
|
53
48
|
private provider: PersistenceProvider;
|
|
49
|
+
private tagStore: TagStore;
|
|
54
50
|
|
|
55
51
|
constructor(vault: Vault) {
|
|
56
52
|
this.vault = vault;
|
|
57
53
|
this.provider = vault.getProvider();
|
|
58
|
-
this.
|
|
59
|
-
this.
|
|
54
|
+
this.tagStore = this.createTagStore();
|
|
55
|
+
initializeTables(this.provider);
|
|
56
|
+
this.provider.transaction(() => seedDefaultAliases(this.tagStore));
|
|
60
57
|
}
|
|
61
58
|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
source TEXT NOT NULL DEFAULT 'unknown' CHECK(source IN ('manual', 'capture', 'seed', 'unknown')),
|
|
71
|
-
last_groomed_at INTEGER,
|
|
72
|
-
created_at INTEGER NOT NULL DEFAULT (unixepoch())
|
|
73
|
-
);
|
|
74
|
-
|
|
75
|
-
CREATE TABLE IF NOT EXISTS curator_tag_canonical (
|
|
76
|
-
tag TEXT PRIMARY KEY,
|
|
77
|
-
description TEXT,
|
|
78
|
-
created_at INTEGER NOT NULL DEFAULT (unixepoch())
|
|
79
|
-
);
|
|
80
|
-
|
|
81
|
-
CREATE TABLE IF NOT EXISTS curator_tag_alias (
|
|
82
|
-
alias TEXT PRIMARY KEY,
|
|
83
|
-
canonical TEXT NOT NULL,
|
|
84
|
-
created_at INTEGER NOT NULL DEFAULT (unixepoch()),
|
|
85
|
-
FOREIGN KEY (canonical) REFERENCES curator_tag_canonical(tag)
|
|
86
|
-
);
|
|
87
|
-
|
|
88
|
-
CREATE TABLE IF NOT EXISTS curator_changelog (
|
|
89
|
-
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
90
|
-
action TEXT NOT NULL,
|
|
91
|
-
entry_id TEXT NOT NULL,
|
|
92
|
-
before_value TEXT,
|
|
93
|
-
after_value TEXT,
|
|
94
|
-
reason TEXT NOT NULL,
|
|
95
|
-
created_at INTEGER NOT NULL DEFAULT (unixepoch())
|
|
96
|
-
);
|
|
97
|
-
|
|
98
|
-
CREATE TABLE IF NOT EXISTS curator_entry_history (
|
|
99
|
-
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
100
|
-
entry_id TEXT NOT NULL,
|
|
101
|
-
snapshot TEXT NOT NULL,
|
|
102
|
-
changed_by TEXT DEFAULT 'system',
|
|
103
|
-
change_reason TEXT,
|
|
104
|
-
created_at INTEGER NOT NULL DEFAULT (unixepoch())
|
|
105
|
-
);
|
|
106
|
-
|
|
107
|
-
CREATE TABLE IF NOT EXISTS curator_contradictions (
|
|
108
|
-
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
109
|
-
pattern_id TEXT NOT NULL,
|
|
110
|
-
antipattern_id TEXT NOT NULL,
|
|
111
|
-
similarity REAL NOT NULL,
|
|
112
|
-
status TEXT NOT NULL DEFAULT 'open' CHECK(status IN ('open', 'resolved', 'dismissed')),
|
|
113
|
-
created_at INTEGER NOT NULL DEFAULT (unixepoch()),
|
|
114
|
-
resolved_at INTEGER,
|
|
115
|
-
UNIQUE(pattern_id, antipattern_id)
|
|
116
|
-
);
|
|
117
|
-
CREATE INDEX IF NOT EXISTS idx_curator_state_status ON curator_entry_state(status);
|
|
118
|
-
CREATE INDEX IF NOT EXISTS idx_curator_changelog_entry ON curator_changelog(entry_id);
|
|
119
|
-
`);
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
private seedDefaultAliases(): void {
|
|
123
|
-
this.provider.transaction(() => {
|
|
124
|
-
const canonicals = new Set(DEFAULT_TAG_ALIASES.map(([, c]) => c));
|
|
125
|
-
for (const tag of canonicals) {
|
|
126
|
-
this.provider.run('INSERT OR IGNORE INTO curator_tag_canonical (tag) VALUES (?)', [tag]);
|
|
127
|
-
}
|
|
128
|
-
for (const [alias, canonical] of DEFAULT_TAG_ALIASES) {
|
|
129
|
-
this.provider.run(
|
|
130
|
-
'INSERT OR IGNORE INTO curator_tag_alias (alias, canonical) VALUES (?, ?)',
|
|
131
|
-
[alias, canonical],
|
|
59
|
+
private createTagStore(): TagStore {
|
|
60
|
+
const p = this.provider;
|
|
61
|
+
return {
|
|
62
|
+
getAlias(lower: string) {
|
|
63
|
+
return (
|
|
64
|
+
p.get<{ canonical: string }>('SELECT canonical FROM curator_tag_alias WHERE alias = ?', [
|
|
65
|
+
lower,
|
|
66
|
+
])?.canonical ?? null
|
|
132
67
|
);
|
|
133
|
-
}
|
|
134
|
-
|
|
68
|
+
},
|
|
69
|
+
insertCanonical(tag: string) {
|
|
70
|
+
p.run('INSERT OR IGNORE INTO curator_tag_canonical (tag) VALUES (?)', [tag]);
|
|
71
|
+
},
|
|
72
|
+
upsertAlias(alias: string, canonical: string) {
|
|
73
|
+
p.run('INSERT OR REPLACE INTO curator_tag_alias (alias, canonical) VALUES (?, ?)', [
|
|
74
|
+
alias,
|
|
75
|
+
canonical,
|
|
76
|
+
]);
|
|
77
|
+
},
|
|
78
|
+
getCanonicalRows() {
|
|
79
|
+
return p.all<{ tag: string; description: string | null; alias_count: number }>(
|
|
80
|
+
`SELECT c.tag, c.description, (SELECT COUNT(*) FROM curator_tag_alias a WHERE a.canonical = c.tag) as alias_count FROM curator_tag_canonical c ORDER BY c.tag`,
|
|
81
|
+
);
|
|
82
|
+
},
|
|
83
|
+
countTagUsage(tag: string) {
|
|
84
|
+
return (
|
|
85
|
+
p.get<{ count: number }>('SELECT COUNT(*) as count FROM entries WHERE tags LIKE ?', [
|
|
86
|
+
`%"${tag}"%`,
|
|
87
|
+
])?.count ?? 0
|
|
88
|
+
);
|
|
89
|
+
},
|
|
90
|
+
};
|
|
135
91
|
}
|
|
136
92
|
|
|
137
93
|
// ─── Status ─────────────────────────────────────────────────────
|
|
@@ -143,11 +99,9 @@ export class Curator {
|
|
|
143
99
|
count: 0,
|
|
144
100
|
}
|
|
145
101
|
).count;
|
|
146
|
-
|
|
147
102
|
const lastGroomed = this.provider.get<{ ts: number | null }>(
|
|
148
103
|
'SELECT MAX(last_groomed_at) as ts FROM curator_entry_state WHERE last_groomed_at IS NOT NULL',
|
|
149
104
|
) ?? { ts: null };
|
|
150
|
-
|
|
151
105
|
return {
|
|
152
106
|
initialized: true,
|
|
153
107
|
tables: {
|
|
@@ -161,37 +115,17 @@ export class Curator {
|
|
|
161
115
|
};
|
|
162
116
|
}
|
|
163
117
|
|
|
164
|
-
// ───
|
|
118
|
+
// ─── Tags (delegates to tag-manager) ──────────────────────────
|
|
165
119
|
|
|
166
120
|
normalizeTag(tag: string): TagNormalizationResult {
|
|
167
|
-
|
|
168
|
-
const row = this.provider.get<{ canonical: string }>(
|
|
169
|
-
'SELECT canonical FROM curator_tag_alias WHERE alias = ?',
|
|
170
|
-
[lower],
|
|
171
|
-
);
|
|
172
|
-
if (row) {
|
|
173
|
-
return { original: tag, normalized: row.canonical, wasAliased: true };
|
|
174
|
-
}
|
|
175
|
-
return { original: tag, normalized: lower, wasAliased: false };
|
|
121
|
+
return normalizeTagPure(tag, this.tagStore);
|
|
176
122
|
}
|
|
177
123
|
|
|
178
124
|
normalizeTags(entryId: string): TagNormalizationResult[] {
|
|
179
125
|
const entry = this.vault.get(entryId);
|
|
180
126
|
if (!entry) return [];
|
|
181
|
-
|
|
182
|
-
const results: TagNormalizationResult[] = [];
|
|
183
|
-
const normalizedTags: string[] = [];
|
|
184
|
-
let changed = false;
|
|
185
|
-
|
|
186
|
-
for (const tag of entry.tags) {
|
|
187
|
-
const result = this.normalizeTag(tag);
|
|
188
|
-
results.push(result);
|
|
189
|
-
normalizedTags.push(result.normalized);
|
|
190
|
-
if (result.normalized !== tag) changed = true;
|
|
191
|
-
}
|
|
192
|
-
|
|
127
|
+
const { results, dedupedTags, changed } = normalizeAndDedup(entry.tags, this.tagStore);
|
|
193
128
|
if (changed) {
|
|
194
|
-
const dedupedTags = [...new Set(normalizedTags)];
|
|
195
129
|
this.provider.run('UPDATE entries SET tags = ?, updated_at = unixepoch() WHERE id = ?', [
|
|
196
130
|
JSON.stringify(dedupedTags),
|
|
197
131
|
entryId,
|
|
@@ -204,161 +138,39 @@ export class Curator {
|
|
|
204
138
|
'Tag normalization',
|
|
205
139
|
);
|
|
206
140
|
}
|
|
207
|
-
|
|
208
141
|
return results;
|
|
209
142
|
}
|
|
210
143
|
|
|
211
144
|
addTagAlias(alias: string, canonical: string): void {
|
|
212
|
-
|
|
213
|
-
const canonicalLower = canonical.toLowerCase().trim();
|
|
214
|
-
this.provider.run('INSERT OR IGNORE INTO curator_tag_canonical (tag) VALUES (?)', [
|
|
215
|
-
canonicalLower,
|
|
216
|
-
]);
|
|
217
|
-
this.provider.run('INSERT OR REPLACE INTO curator_tag_alias (alias, canonical) VALUES (?, ?)', [
|
|
218
|
-
lower,
|
|
219
|
-
canonicalLower,
|
|
220
|
-
]);
|
|
145
|
+
addTagAliasPure(alias, canonical, this.tagStore);
|
|
221
146
|
}
|
|
222
|
-
|
|
223
147
|
getCanonicalTags(): CanonicalTag[] {
|
|
224
|
-
|
|
225
|
-
tag: string;
|
|
226
|
-
description: string | null;
|
|
227
|
-
alias_count: number;
|
|
228
|
-
}>(
|
|
229
|
-
`SELECT c.tag, c.description,
|
|
230
|
-
(SELECT COUNT(*) FROM curator_tag_alias a WHERE a.canonical = c.tag) as alias_count
|
|
231
|
-
FROM curator_tag_canonical c
|
|
232
|
-
ORDER BY c.tag`,
|
|
233
|
-
);
|
|
234
|
-
|
|
235
|
-
return rows.map((row) => ({
|
|
236
|
-
tag: row.tag,
|
|
237
|
-
description: row.description,
|
|
238
|
-
usageCount: this.countTagUsage(row.tag),
|
|
239
|
-
aliasCount: row.alias_count,
|
|
240
|
-
}));
|
|
148
|
+
return getCanonicalTagsPure(this.tagStore);
|
|
241
149
|
}
|
|
242
150
|
|
|
243
|
-
|
|
244
|
-
const row = this.provider.get<{ count: number }>(
|
|
245
|
-
'SELECT COUNT(*) as count FROM entries WHERE tags LIKE ?',
|
|
246
|
-
[`%"${tag}"%`],
|
|
247
|
-
);
|
|
248
|
-
return row?.count ?? 0;
|
|
249
|
-
}
|
|
250
|
-
|
|
251
|
-
// ─── Duplicate Detection ────────────────────────────────────────
|
|
151
|
+
// ─── Duplicates (delegates to duplicate-detector) ─────────────
|
|
252
152
|
|
|
253
153
|
detectDuplicates(entryId?: string, threshold?: number): DuplicateDetectionResult[] {
|
|
254
|
-
|
|
255
|
-
const entries = this.vault.list({ limit: 100000 });
|
|
256
|
-
if (entries.length === 0) return [];
|
|
257
|
-
|
|
258
|
-
// Build transient vocabulary
|
|
259
|
-
const vocabulary = this.buildVocabulary(entries);
|
|
260
|
-
|
|
261
|
-
// Build vectors for all entries
|
|
262
|
-
const vectors = new Map<string, SparseVector>();
|
|
263
|
-
for (const entry of entries) {
|
|
264
|
-
const text = [entry.title, entry.description, entry.context ?? '', entry.tags.join(' ')].join(
|
|
265
|
-
' ',
|
|
266
|
-
);
|
|
267
|
-
vectors.set(entry.id, calculateTfIdf(tokenize(text), vocabulary));
|
|
268
|
-
}
|
|
269
|
-
|
|
270
|
-
const targetEntries = entryId ? entries.filter((e) => e.id === entryId) : entries;
|
|
271
|
-
|
|
272
|
-
const results: DuplicateDetectionResult[] = [];
|
|
273
|
-
|
|
274
|
-
for (const entry of targetEntries) {
|
|
275
|
-
const entryVec = vectors.get(entry.id)!;
|
|
276
|
-
const matches: DuplicateCandidate[] = [];
|
|
277
|
-
|
|
278
|
-
for (const other of entries) {
|
|
279
|
-
if (other.id === entry.id) continue;
|
|
280
|
-
const otherVec = vectors.get(other.id)!;
|
|
281
|
-
const similarity = cosineSimilarity(entryVec, otherVec);
|
|
282
|
-
if (similarity >= effectiveThreshold) {
|
|
283
|
-
matches.push({
|
|
284
|
-
entryId: other.id,
|
|
285
|
-
title: other.title,
|
|
286
|
-
similarity,
|
|
287
|
-
suggestMerge: similarity >= MERGE_SUGGESTION_THRESHOLD,
|
|
288
|
-
});
|
|
289
|
-
}
|
|
290
|
-
}
|
|
291
|
-
|
|
292
|
-
if (matches.length > 0) {
|
|
293
|
-
matches.sort((a, b) => b.similarity - a.similarity);
|
|
294
|
-
results.push({
|
|
295
|
-
entryId: entry.id,
|
|
296
|
-
matches,
|
|
297
|
-
scannedCount: entries.length - 1,
|
|
298
|
-
});
|
|
299
|
-
}
|
|
300
|
-
}
|
|
301
|
-
|
|
302
|
-
return results;
|
|
154
|
+
return detectDuplicatesPure(this.vault.list({ limit: 100000 }), entryId, threshold);
|
|
303
155
|
}
|
|
304
156
|
|
|
305
|
-
// ─── Contradictions
|
|
157
|
+
// ─── Contradictions (delegates to contradiction-detector) ─────
|
|
306
158
|
|
|
307
159
|
detectContradictions(threshold?: number): Contradiction[] {
|
|
308
|
-
const
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
if (antipatterns.length === 0 || patterns.length === 0) return [];
|
|
314
|
-
|
|
315
|
-
const vocabulary = this.buildVocabulary(entries);
|
|
316
|
-
const detected: Contradiction[] = [];
|
|
317
|
-
|
|
318
|
-
for (const ap of antipatterns) {
|
|
319
|
-
// Stage 1: FTS5 candidate retrieval (fall back to all patterns if FTS returns empty)
|
|
320
|
-
let candidates: IntelligenceEntry[];
|
|
321
|
-
try {
|
|
322
|
-
const searchResults = this.vault.search(ap.title, { type: 'pattern', limit: 20 });
|
|
323
|
-
candidates = searchResults.length > 0 ? searchResults.map((r) => r.entry) : patterns;
|
|
324
|
-
} catch {
|
|
325
|
-
candidates = patterns;
|
|
326
|
-
}
|
|
327
|
-
|
|
328
|
-
// Stage 2: TF-IDF cosine similarity
|
|
329
|
-
const apText = [ap.title, ap.description, ap.context ?? ''].join(' ');
|
|
330
|
-
const apVec = calculateTfIdf(tokenize(apText), vocabulary);
|
|
331
|
-
|
|
332
|
-
for (const pattern of candidates) {
|
|
333
|
-
const pText = [pattern.title, pattern.description, pattern.context ?? ''].join(' ');
|
|
334
|
-
const pVec = calculateTfIdf(tokenize(pText), vocabulary);
|
|
335
|
-
const similarity = cosineSimilarity(apVec, pVec);
|
|
336
|
-
|
|
337
|
-
if (similarity >= effectiveThreshold) {
|
|
338
|
-
const result = this.provider.run(
|
|
339
|
-
'INSERT OR IGNORE INTO curator_contradictions (pattern_id, antipattern_id, similarity) VALUES (?, ?, ?)',
|
|
340
|
-
[pattern.id, ap.id, similarity],
|
|
341
|
-
);
|
|
342
|
-
if (result.changes > 0) {
|
|
343
|
-
const row = this.provider.get<Record<string, unknown>>(
|
|
344
|
-
'SELECT * FROM curator_contradictions WHERE pattern_id = ? AND antipattern_id = ?',
|
|
345
|
-
[pattern.id, ap.id],
|
|
346
|
-
);
|
|
347
|
-
if (row) detected.push(this.rowToContradiction(row));
|
|
348
|
-
}
|
|
349
|
-
}
|
|
350
|
-
}
|
|
351
|
-
}
|
|
352
|
-
|
|
353
|
-
return detected;
|
|
160
|
+
const searchFn = (title: string) =>
|
|
161
|
+
this.vault.search(title, { type: 'pattern', limit: 20 }).map((r) => r.entry);
|
|
162
|
+
return this.persistContradictions(
|
|
163
|
+
findContradictions(this.vault.list({ limit: 100000 }), threshold, searchFn),
|
|
164
|
+
);
|
|
354
165
|
}
|
|
355
166
|
|
|
356
167
|
getContradictions(status?: ContradictionStatus): Contradiction[] {
|
|
357
168
|
const query = status
|
|
358
169
|
? 'SELECT * FROM curator_contradictions WHERE status = ? ORDER BY similarity DESC'
|
|
359
170
|
: 'SELECT * FROM curator_contradictions ORDER BY similarity DESC';
|
|
360
|
-
|
|
361
|
-
|
|
171
|
+
return this.provider
|
|
172
|
+
.all<Record<string, unknown>>(query, status ? [status] : undefined)
|
|
173
|
+
.map((r) => this.rowToContradiction(r));
|
|
362
174
|
}
|
|
363
175
|
|
|
364
176
|
resolveContradiction(id: number, resolution: 'resolved' | 'dismissed'): Contradiction | null {
|
|
@@ -373,103 +185,45 @@ export class Curator {
|
|
|
373
185
|
return row ? this.rowToContradiction(row) : null;
|
|
374
186
|
}
|
|
375
187
|
|
|
376
|
-
async detectContradictionsHybrid(
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
const entries = this.vault.list({ limit: 100000 });
|
|
382
|
-
const antipatterns = entries.filter((e) => e.type === 'anti-pattern');
|
|
383
|
-
const patterns = entries.filter((e) => e.type === 'pattern');
|
|
384
|
-
|
|
385
|
-
if (antipatterns.length === 0 || patterns.length === 0) {
|
|
386
|
-
return { contradictions: [], method: 'tfidf-only' };
|
|
387
|
-
}
|
|
388
|
-
|
|
389
|
-
const vocabulary = this.buildVocabulary(entries);
|
|
390
|
-
const detected: Contradiction[] = [];
|
|
391
|
-
|
|
392
|
-
for (const ap of antipatterns) {
|
|
393
|
-
let candidates: IntelligenceEntry[];
|
|
394
|
-
try {
|
|
395
|
-
const searchResults = this.vault.search(ap.title, { type: 'pattern', limit: 20 });
|
|
396
|
-
candidates = searchResults.length > 0 ? searchResults.map((r) => r.entry) : patterns;
|
|
397
|
-
} catch {
|
|
398
|
-
candidates = patterns;
|
|
399
|
-
}
|
|
400
|
-
|
|
401
|
-
const apText = [ap.title, ap.description, ap.context ?? ''].join(' ');
|
|
402
|
-
const apVec = calculateTfIdf(tokenize(apText), vocabulary);
|
|
403
|
-
|
|
404
|
-
for (const pattern of candidates) {
|
|
405
|
-
const pText = [pattern.title, pattern.description, pattern.context ?? ''].join(' ');
|
|
406
|
-
const pVec = calculateTfIdf(tokenize(pText), vocabulary);
|
|
407
|
-
const finalScore = cosineSimilarity(apVec, pVec);
|
|
408
|
-
|
|
409
|
-
if (finalScore >= effectiveThreshold) {
|
|
410
|
-
const result = this.provider.run(
|
|
411
|
-
'INSERT OR IGNORE INTO curator_contradictions (pattern_id, antipattern_id, similarity) VALUES (?, ?, ?)',
|
|
412
|
-
[pattern.id, ap.id, finalScore],
|
|
413
|
-
);
|
|
414
|
-
if (result.changes > 0) {
|
|
415
|
-
const row = this.provider.get<Record<string, unknown>>(
|
|
416
|
-
'SELECT * FROM curator_contradictions WHERE pattern_id = ? AND antipattern_id = ?',
|
|
417
|
-
[pattern.id, ap.id],
|
|
418
|
-
);
|
|
419
|
-
if (row) detected.push(this.rowToContradiction(row));
|
|
420
|
-
}
|
|
421
|
-
}
|
|
422
|
-
}
|
|
423
|
-
}
|
|
424
|
-
|
|
188
|
+
async detectContradictionsHybrid(
|
|
189
|
+
threshold?: number,
|
|
190
|
+
): Promise<{ contradictions: Contradiction[]; method: 'tfidf-only' }> {
|
|
191
|
+
const searchFn = (title: string) =>
|
|
192
|
+
this.vault.search(title, { type: 'pattern', limit: 20 }).map((r) => r.entry);
|
|
425
193
|
return {
|
|
426
|
-
contradictions:
|
|
194
|
+
contradictions: this.persistContradictions(
|
|
195
|
+
findContradictions(this.vault.list({ limit: 100000 }), threshold, searchFn),
|
|
196
|
+
),
|
|
427
197
|
method: 'tfidf-only',
|
|
428
198
|
};
|
|
429
199
|
}
|
|
430
200
|
|
|
431
|
-
// ─── Grooming
|
|
201
|
+
// ─── Grooming ─────────────────────────────────────────────────
|
|
432
202
|
|
|
433
203
|
groomEntry(entryId: string): GroomResult | null {
|
|
434
204
|
const entry = this.vault.get(entryId);
|
|
435
205
|
if (!entry) return null;
|
|
436
|
-
|
|
437
206
|
const tagsNormalized = this.normalizeTags(entryId);
|
|
438
|
-
|
|
439
|
-
// Check staleness based on entry's updated_at timestamp
|
|
440
207
|
const row = this.provider.get<{ updated_at: number }>(
|
|
441
208
|
'SELECT updated_at FROM entries WHERE id = ?',
|
|
442
209
|
[entryId],
|
|
443
210
|
);
|
|
444
211
|
const now = Math.floor(Date.now() / 1000);
|
|
445
212
|
const stale = row ? now - row.updated_at > DEFAULT_STALE_DAYS * 86400 : false;
|
|
446
|
-
|
|
447
213
|
const status = stale ? 'stale' : 'active';
|
|
448
|
-
|
|
449
|
-
// Upsert entry state
|
|
450
214
|
this.provider.run(
|
|
451
|
-
`INSERT INTO curator_entry_state (entry_id, status, last_groomed_at)
|
|
452
|
-
VALUES (?, ?, unixepoch())
|
|
453
|
-
ON CONFLICT(entry_id) DO UPDATE SET status = excluded.status, last_groomed_at = unixepoch()`,
|
|
215
|
+
`INSERT INTO curator_entry_state (entry_id, status, last_groomed_at) VALUES (?, ?, unixepoch()) ON CONFLICT(entry_id) DO UPDATE SET status = excluded.status, last_groomed_at = unixepoch()`,
|
|
454
216
|
[entryId, status],
|
|
455
217
|
);
|
|
456
|
-
|
|
457
218
|
this.logChange('groom', entryId, null, `status=${status}`, 'Routine grooming');
|
|
458
|
-
|
|
459
|
-
return {
|
|
460
|
-
entryId,
|
|
461
|
-
tagsNormalized,
|
|
462
|
-
stale,
|
|
463
|
-
lastGroomedAt: now,
|
|
464
|
-
};
|
|
219
|
+
return { entryId, tagsNormalized, stale, lastGroomedAt: now };
|
|
465
220
|
}
|
|
466
221
|
|
|
467
222
|
groomAll(): GroomAllResult {
|
|
468
223
|
const start = Date.now();
|
|
469
224
|
const entries = this.vault.list({ limit: 100000 });
|
|
470
|
-
let tagsNormalized = 0
|
|
471
|
-
|
|
472
|
-
|
|
225
|
+
let tagsNormalized = 0,
|
|
226
|
+
staleCount = 0;
|
|
473
227
|
for (const entry of entries) {
|
|
474
228
|
const result = this.groomEntry(entry.id);
|
|
475
229
|
if (result) {
|
|
@@ -477,7 +231,6 @@ export class Curator {
|
|
|
477
231
|
if (result.stale) staleCount++;
|
|
478
232
|
}
|
|
479
233
|
}
|
|
480
|
-
|
|
481
234
|
return {
|
|
482
235
|
totalEntries: entries.length,
|
|
483
236
|
groomedCount: entries.length,
|
|
@@ -487,7 +240,7 @@ export class Curator {
|
|
|
487
240
|
};
|
|
488
241
|
}
|
|
489
242
|
|
|
490
|
-
// ─── Consolidation
|
|
243
|
+
// ─── Consolidation ───────────────────────────────────────────
|
|
491
244
|
|
|
492
245
|
consolidate(options?: ConsolidationOptions): ConsolidationResult {
|
|
493
246
|
const start = Date.now();
|
|
@@ -496,31 +249,19 @@ export class Curator {
|
|
|
496
249
|
const duplicateThreshold = options?.duplicateThreshold ?? DEFAULT_DUPLICATE_THRESHOLD;
|
|
497
250
|
const contradictionThreshold =
|
|
498
251
|
options?.contradictionThreshold ?? DEFAULT_CONTRADICTION_THRESHOLD;
|
|
499
|
-
|
|
500
|
-
// Detect duplicates
|
|
501
252
|
const duplicates = this.detectDuplicates(undefined, duplicateThreshold);
|
|
502
|
-
|
|
503
|
-
// Detect stale entries
|
|
504
253
|
const now = Math.floor(Date.now() / 1000);
|
|
505
|
-
const staleThreshold = now - staleDaysThreshold * 86400;
|
|
506
254
|
const staleRows = this.provider.all<{ id: string }>(
|
|
507
255
|
'SELECT id FROM entries WHERE updated_at < ?',
|
|
508
|
-
[
|
|
256
|
+
[now - staleDaysThreshold * 86400],
|
|
509
257
|
);
|
|
510
258
|
const staleEntries = staleRows.map((r) => r.id);
|
|
511
|
-
|
|
512
|
-
// Detect contradictions
|
|
513
259
|
const contradictions = this.detectContradictions(contradictionThreshold);
|
|
514
|
-
|
|
515
260
|
let mutations = 0;
|
|
516
|
-
|
|
517
261
|
if (!dryRun) {
|
|
518
|
-
// Archive stale entries
|
|
519
262
|
for (const entryId of staleEntries) {
|
|
520
263
|
this.provider.run(
|
|
521
|
-
`INSERT INTO curator_entry_state (entry_id, status, last_groomed_at)
|
|
522
|
-
VALUES (?, 'archived', unixepoch())
|
|
523
|
-
ON CONFLICT(entry_id) DO UPDATE SET status = 'archived', last_groomed_at = unixepoch()`,
|
|
264
|
+
`INSERT INTO curator_entry_state (entry_id, status, last_groomed_at) VALUES (?, 'archived', unixepoch()) ON CONFLICT(entry_id) DO UPDATE SET status = 'archived', last_groomed_at = unixepoch()`,
|
|
524
265
|
[entryId],
|
|
525
266
|
);
|
|
526
267
|
this.logChange(
|
|
@@ -532,8 +273,6 @@ export class Curator {
|
|
|
532
273
|
);
|
|
533
274
|
mutations++;
|
|
534
275
|
}
|
|
535
|
-
|
|
536
|
-
// Remove lower-similarity duplicates (keep the first entry, remove matches)
|
|
537
276
|
const removed = new Set<string>();
|
|
538
277
|
for (const result of duplicates) {
|
|
539
278
|
for (const match of result.matches) {
|
|
@@ -552,7 +291,6 @@ export class Curator {
|
|
|
552
291
|
}
|
|
553
292
|
}
|
|
554
293
|
}
|
|
555
|
-
|
|
556
294
|
return {
|
|
557
295
|
dryRun,
|
|
558
296
|
duplicates,
|
|
@@ -563,139 +301,42 @@ export class Curator {
|
|
|
563
301
|
};
|
|
564
302
|
}
|
|
565
303
|
|
|
566
|
-
// ─── Changelog
|
|
304
|
+
// ─── Changelog ────────────────────────────────────────────────
|
|
567
305
|
|
|
568
306
|
getEntryHistory(entryId: string, limit?: number): ChangelogEntry[] {
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
307
|
+
return this.provider
|
|
308
|
+
.all<Record<string, unknown>>(
|
|
309
|
+
'SELECT * FROM curator_changelog WHERE entry_id = ? ORDER BY created_at DESC, id DESC LIMIT ?',
|
|
310
|
+
[entryId, limit ?? 50],
|
|
311
|
+
)
|
|
312
|
+
.map((r) => this.rowToChangelog(r));
|
|
574
313
|
}
|
|
575
314
|
|
|
576
|
-
// ─── Health Audit
|
|
315
|
+
// ─── Health Audit (delegates to health-audit) ─────────────────
|
|
577
316
|
|
|
578
317
|
healthAudit(): HealthAuditResult {
|
|
579
318
|
const entries = this.vault.list({ limit: 100000 });
|
|
580
|
-
const
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
}
|
|
597
|
-
const hasPatterns = typeCount.pattern > 0;
|
|
598
|
-
const hasAntiPatterns = typeCount['anti-pattern'] > 0;
|
|
599
|
-
const hasRules = typeCount.rule > 0;
|
|
600
|
-
let coverageScore = 1;
|
|
601
|
-
if (!hasPatterns) {
|
|
602
|
-
score -= 10;
|
|
603
|
-
coverageScore -= 0.33;
|
|
604
|
-
recommendations.push('No patterns found — add patterns to improve coverage.');
|
|
605
|
-
}
|
|
606
|
-
if (!hasAntiPatterns) {
|
|
607
|
-
score -= 5;
|
|
608
|
-
coverageScore -= 0.17;
|
|
609
|
-
recommendations.push('No anti-patterns found — add anti-patterns to detect contradictions.');
|
|
610
|
-
}
|
|
611
|
-
if (!hasRules) {
|
|
612
|
-
score -= 5;
|
|
613
|
-
coverageScore -= 0.17;
|
|
614
|
-
recommendations.push('No rules found — add rules for completeness.');
|
|
615
|
-
}
|
|
616
|
-
coverageScore = Math.max(0, coverageScore);
|
|
617
|
-
|
|
618
|
-
// Freshness: penalize stale entries
|
|
619
|
-
const now = Math.floor(Date.now() / 1000);
|
|
620
|
-
const staleThreshold = now - DEFAULT_STALE_DAYS * 86400;
|
|
621
|
-
const staleCount = (
|
|
622
|
-
this.provider.get<{ count: number }>(
|
|
623
|
-
'SELECT COUNT(*) as count FROM entries WHERE updated_at < ?',
|
|
624
|
-
[staleThreshold],
|
|
625
|
-
) ?? { count: 0 }
|
|
626
|
-
).count;
|
|
627
|
-
const staleRatio = staleCount / entries.length;
|
|
628
|
-
const freshnessScore = 1 - staleRatio;
|
|
629
|
-
if (staleRatio > 0.3) {
|
|
630
|
-
const penalty = Math.min(20, Math.round(staleRatio * 30));
|
|
631
|
-
score -= penalty;
|
|
632
|
-
recommendations.push(
|
|
633
|
-
`${staleCount} stale entries (${Math.round(staleRatio * 100)}%) — run grooming to update.`,
|
|
634
|
-
);
|
|
635
|
-
}
|
|
636
|
-
|
|
637
|
-
// Quality: penalize duplicates and contradictions
|
|
638
|
-
const duplicates = this.detectDuplicates();
|
|
639
|
-
const contradictions = this.getContradictions('open');
|
|
640
|
-
let qualityScore = 1;
|
|
641
|
-
if (duplicates.length > 0) {
|
|
642
|
-
const penalty = Math.min(15, duplicates.length * 3);
|
|
643
|
-
score -= penalty;
|
|
644
|
-
qualityScore -= penalty / 30;
|
|
645
|
-
recommendations.push(`${duplicates.length} entries have duplicates — run consolidation.`);
|
|
646
|
-
}
|
|
647
|
-
if (contradictions.length > 0) {
|
|
648
|
-
const penalty = Math.min(15, contradictions.length * 5);
|
|
649
|
-
score -= penalty;
|
|
650
|
-
qualityScore -= penalty / 30;
|
|
651
|
-
recommendations.push(`${contradictions.length} open contradictions — resolve or dismiss.`);
|
|
652
|
-
}
|
|
653
|
-
qualityScore = Math.max(0, qualityScore);
|
|
654
|
-
|
|
655
|
-
// Tag health: penalize entries with few or no tags
|
|
656
|
-
const lowTagEntries = entries.filter((e) => e.tags.length < 2);
|
|
657
|
-
const lowTagRatio = lowTagEntries.length / entries.length;
|
|
658
|
-
const tagHealthScore = 1 - lowTagRatio;
|
|
659
|
-
if (lowTagRatio > 0.3) {
|
|
660
|
-
const penalty = Math.min(10, Math.round(lowTagRatio * 15));
|
|
661
|
-
score -= penalty;
|
|
662
|
-
recommendations.push(
|
|
663
|
-
`${lowTagEntries.length} entries have fewer than 2 tags — improve tagging.`,
|
|
664
|
-
);
|
|
665
|
-
}
|
|
666
|
-
|
|
667
|
-
// Penalize ungroomed entries
|
|
668
|
-
const groomedCount = (
|
|
669
|
-
this.provider.get<{ count: number }>(
|
|
670
|
-
'SELECT COUNT(*) as count FROM curator_entry_state WHERE last_groomed_at IS NOT NULL',
|
|
671
|
-
) ?? { count: 0 }
|
|
672
|
-
).count;
|
|
673
|
-
if (groomedCount < entries.length) {
|
|
674
|
-
const ungroomed = entries.length - groomedCount;
|
|
675
|
-
const penalty = Math.min(10, Math.round((ungroomed / entries.length) * 10));
|
|
676
|
-
score -= penalty;
|
|
677
|
-
recommendations.push(`${ungroomed} entries never groomed — run groomAll().`);
|
|
678
|
-
}
|
|
679
|
-
|
|
680
|
-
score = Math.max(0, score);
|
|
681
|
-
|
|
682
|
-
if (recommendations.length === 0) {
|
|
683
|
-
recommendations.push('Vault is healthy — no issues detected.');
|
|
684
|
-
}
|
|
685
|
-
|
|
686
|
-
return {
|
|
687
|
-
score,
|
|
688
|
-
metrics: {
|
|
689
|
-
coverage: coverageScore,
|
|
690
|
-
freshness: freshnessScore,
|
|
691
|
-
quality: qualityScore,
|
|
692
|
-
tagHealth: tagHealthScore,
|
|
693
|
-
},
|
|
694
|
-
recommendations,
|
|
319
|
+
const dataProvider: HealthDataProvider = {
|
|
320
|
+
getStaleCount: (threshold) =>
|
|
321
|
+
(
|
|
322
|
+
this.provider.get<{ count: number }>(
|
|
323
|
+
'SELECT COUNT(*) as count FROM entries WHERE updated_at < ?',
|
|
324
|
+
[threshold],
|
|
325
|
+
) ?? { count: 0 }
|
|
326
|
+
).count,
|
|
327
|
+
getGroomedCount: () =>
|
|
328
|
+
(
|
|
329
|
+
this.provider.get<{ count: number }>(
|
|
330
|
+
'SELECT COUNT(*) as count FROM curator_entry_state WHERE last_groomed_at IS NOT NULL',
|
|
331
|
+
) ?? { count: 0 }
|
|
332
|
+
).count,
|
|
333
|
+
getDuplicates: () => this.detectDuplicates(),
|
|
334
|
+
getOpenContradictions: () => this.getContradictions('open'),
|
|
695
335
|
};
|
|
336
|
+
return computeHealthAudit(entries, dataProvider, DEFAULT_STALE_DAYS);
|
|
696
337
|
}
|
|
697
338
|
|
|
698
|
-
// ─── Entry History (Version Snapshots)
|
|
339
|
+
// ─── Entry History (Version Snapshots) ────────────────────────
|
|
699
340
|
|
|
700
341
|
recordSnapshot(
|
|
701
342
|
entryId: string,
|
|
@@ -704,12 +345,10 @@ export class Curator {
|
|
|
704
345
|
): { recorded: boolean; historyId: number } {
|
|
705
346
|
const entry = this.vault.get(entryId);
|
|
706
347
|
if (!entry) return { recorded: false, historyId: -1 };
|
|
707
|
-
|
|
708
348
|
const result = this.provider.run(
|
|
709
349
|
'INSERT INTO curator_entry_history (entry_id, snapshot, changed_by, change_reason, created_at) VALUES (?, ?, ?, ?, unixepoch())',
|
|
710
350
|
[entryId, JSON.stringify(entry), changedBy ?? 'system', changeReason ?? null],
|
|
711
351
|
);
|
|
712
|
-
|
|
713
352
|
return { recorded: true, historyId: Number(result.lastInsertRowid) };
|
|
714
353
|
}
|
|
715
354
|
|
|
@@ -721,22 +360,22 @@ export class Curator {
|
|
|
721
360
|
changeReason: string | null;
|
|
722
361
|
createdAt: number;
|
|
723
362
|
}> {
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
363
|
+
return this.provider
|
|
364
|
+
.all<Record<string, unknown>>(
|
|
365
|
+
'SELECT * FROM curator_entry_history WHERE entry_id = ? ORDER BY created_at ASC, id ASC',
|
|
366
|
+
[entryId],
|
|
367
|
+
)
|
|
368
|
+
.map((row) => ({
|
|
369
|
+
historyId: row.id as number,
|
|
370
|
+
entryId: row.entry_id as string,
|
|
371
|
+
snapshot: JSON.parse(row.snapshot as string) as IntelligenceEntry,
|
|
372
|
+
changedBy: row.changed_by as string,
|
|
373
|
+
changeReason: (row.change_reason as string) ?? null,
|
|
374
|
+
createdAt: row.created_at as number,
|
|
375
|
+
}));
|
|
737
376
|
}
|
|
738
377
|
|
|
739
|
-
// ─── Queue Stats
|
|
378
|
+
// ─── Queue Stats ─────────────────────────────────────────────
|
|
740
379
|
|
|
741
380
|
getQueueStats(): {
|
|
742
381
|
totalEntries: number;
|
|
@@ -746,57 +385,50 @@ export class Curator {
|
|
|
746
385
|
freshEntries: number;
|
|
747
386
|
avgDaysSinceGroom: number;
|
|
748
387
|
} {
|
|
388
|
+
const p = this.provider;
|
|
749
389
|
const totalEntries = (
|
|
750
|
-
|
|
390
|
+
p.get<{ count: number }>('SELECT COUNT(*) as count FROM entries') ?? { count: 0 }
|
|
751
391
|
).count;
|
|
752
|
-
|
|
753
392
|
const groomedEntries = (
|
|
754
|
-
|
|
393
|
+
p.get<{ count: number }>(
|
|
755
394
|
'SELECT COUNT(*) as count FROM curator_entry_state WHERE last_groomed_at IS NOT NULL',
|
|
756
395
|
) ?? { count: 0 }
|
|
757
396
|
).count;
|
|
758
|
-
|
|
759
|
-
const ungroomedEntries = totalEntries - groomedEntries;
|
|
760
|
-
|
|
761
397
|
const now = Math.floor(Date.now() / 1000);
|
|
762
|
-
const staleThreshold = now - 30 * 86400;
|
|
763
|
-
const freshThreshold = now - 7 * 86400;
|
|
764
|
-
|
|
765
398
|
const staleEntries = (
|
|
766
|
-
|
|
399
|
+
p.get<{ count: number }>(
|
|
767
400
|
'SELECT COUNT(*) as count FROM curator_entry_state WHERE last_groomed_at IS NOT NULL AND last_groomed_at < ?',
|
|
768
|
-
[
|
|
401
|
+
[now - 30 * 86400],
|
|
769
402
|
) ?? { count: 0 }
|
|
770
403
|
).count;
|
|
771
|
-
|
|
772
404
|
const freshEntries = (
|
|
773
|
-
|
|
405
|
+
p.get<{ count: number }>(
|
|
774
406
|
'SELECT COUNT(*) as count FROM curator_entry_state WHERE last_groomed_at IS NOT NULL AND last_groomed_at >= ?',
|
|
775
|
-
[
|
|
407
|
+
[now - 7 * 86400],
|
|
776
408
|
) ?? { count: 0 }
|
|
777
409
|
).count;
|
|
778
|
-
|
|
779
410
|
let avgDaysSinceGroom = 0;
|
|
780
411
|
if (groomedEntries > 0) {
|
|
781
|
-
const
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
412
|
+
const totalSeconds =
|
|
413
|
+
(
|
|
414
|
+
p.get<{ total: number | null }>(
|
|
415
|
+
'SELECT SUM(? - last_groomed_at) as total FROM curator_entry_state WHERE last_groomed_at IS NOT NULL',
|
|
416
|
+
[now],
|
|
417
|
+
) ?? { total: 0 }
|
|
418
|
+
).total ?? 0;
|
|
786
419
|
avgDaysSinceGroom = Math.round((totalSeconds / groomedEntries / 86400) * 100) / 100;
|
|
787
420
|
}
|
|
788
|
-
|
|
789
421
|
return {
|
|
790
422
|
totalEntries,
|
|
791
423
|
groomedEntries,
|
|
792
|
-
ungroomedEntries,
|
|
424
|
+
ungroomedEntries: totalEntries - groomedEntries,
|
|
793
425
|
staleEntries,
|
|
794
426
|
freshEntries,
|
|
795
427
|
avgDaysSinceGroom,
|
|
796
428
|
};
|
|
797
429
|
}
|
|
798
430
|
|
|
799
|
-
// ─── Metadata Enrichment
|
|
431
|
+
// ─── Metadata Enrichment (delegates to metadata-enricher) ────
|
|
800
432
|
|
|
801
433
|
enrichMetadata(entryId: string): {
|
|
802
434
|
enriched: boolean;
|
|
@@ -804,79 +436,10 @@ export class Curator {
|
|
|
804
436
|
} {
|
|
805
437
|
const entry = this.vault.get(entryId);
|
|
806
438
|
if (!entry) return { enriched: false, changes: [] };
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
const updates: Partial<
|
|
810
|
-
Pick<IntelligenceEntry, 'title' | 'description' | 'tags' | 'severity' | 'type'>
|
|
811
|
-
> = {};
|
|
812
|
-
|
|
813
|
-
// Auto-capitalize title
|
|
814
|
-
if (entry.title.length > 0 && entry.title[0] !== entry.title[0].toUpperCase()) {
|
|
815
|
-
const capitalized = entry.title[0].toUpperCase() + entry.title.slice(1);
|
|
816
|
-
changes.push({ field: 'title', before: entry.title, after: capitalized });
|
|
817
|
-
updates.title = capitalized;
|
|
818
|
-
}
|
|
819
|
-
|
|
820
|
-
// Normalize tags: lowercase, trim, dedup
|
|
821
|
-
const normalizedTags = [...new Set(entry.tags.map((t) => t.toLowerCase().trim()))];
|
|
822
|
-
const tagsChanged =
|
|
823
|
-
normalizedTags.length !== entry.tags.length ||
|
|
824
|
-
normalizedTags.some((t, i) => t !== entry.tags[i]);
|
|
825
|
-
if (tagsChanged) {
|
|
826
|
-
changes.push({
|
|
827
|
-
field: 'tags',
|
|
828
|
-
before: JSON.stringify(entry.tags),
|
|
829
|
-
after: JSON.stringify(normalizedTags),
|
|
830
|
-
});
|
|
831
|
-
updates.tags = normalizedTags;
|
|
832
|
-
}
|
|
833
|
-
|
|
834
|
-
// Infer severity from keywords if currently 'suggestion'
|
|
835
|
-
if (entry.severity === 'suggestion') {
|
|
836
|
-
const text = (entry.title + ' ' + entry.description).toLowerCase();
|
|
837
|
-
const criticalKeywords = ['never', 'must not', 'critical', 'security', 'vulnerability'];
|
|
838
|
-
const warningKeywords = ['avoid', 'should not', 'deprecated', 'careful', 'warning'];
|
|
839
|
-
if (criticalKeywords.some((k) => text.includes(k))) {
|
|
840
|
-
changes.push({ field: 'severity', before: entry.severity, after: 'critical' });
|
|
841
|
-
updates.severity = 'critical';
|
|
842
|
-
} else if (warningKeywords.some((k) => text.includes(k))) {
|
|
843
|
-
changes.push({ field: 'severity', before: entry.severity, after: 'warning' });
|
|
844
|
-
updates.severity = 'warning';
|
|
845
|
-
}
|
|
846
|
-
}
|
|
847
|
-
|
|
848
|
-
// Infer type from title patterns
|
|
849
|
-
if (entry.type === 'pattern') {
|
|
850
|
-
const titleLower = entry.title.toLowerCase();
|
|
851
|
-
if (
|
|
852
|
-
titleLower.startsWith('avoid') ||
|
|
853
|
-
titleLower.startsWith('never') ||
|
|
854
|
-
titleLower.startsWith("don't") ||
|
|
855
|
-
titleLower.startsWith('do not')
|
|
856
|
-
) {
|
|
857
|
-
changes.push({ field: 'type', before: entry.type, after: 'anti-pattern' });
|
|
858
|
-
updates.type = 'anti-pattern';
|
|
859
|
-
}
|
|
860
|
-
}
|
|
861
|
-
|
|
862
|
-
// Trim whitespace from description
|
|
863
|
-
const trimmed = entry.description.trim();
|
|
864
|
-
if (trimmed !== entry.description) {
|
|
865
|
-
changes.push({ field: 'description', before: entry.description, after: trimmed });
|
|
866
|
-
updates.description = trimmed;
|
|
867
|
-
}
|
|
868
|
-
|
|
869
|
-
if (changes.length === 0) {
|
|
870
|
-
return { enriched: false, changes: [] };
|
|
871
|
-
}
|
|
872
|
-
|
|
873
|
-
// Apply updates
|
|
439
|
+
const { changes, updates } = enrichEntryMetadata(entry);
|
|
440
|
+
if (changes.length === 0) return { enriched: false, changes: [] };
|
|
874
441
|
this.vault.update(entryId, updates);
|
|
875
|
-
|
|
876
|
-
// Record snapshot
|
|
877
442
|
this.recordSnapshot(entryId, 'curator', 'Metadata enrichment');
|
|
878
|
-
|
|
879
|
-
// Log change
|
|
880
443
|
this.logChange(
|
|
881
444
|
'enrich_metadata',
|
|
882
445
|
entryId,
|
|
@@ -884,30 +447,27 @@ export class Curator {
|
|
|
884
447
|
JSON.stringify(changes.map((c) => c.after)),
|
|
885
448
|
'Rule-based metadata enrichment',
|
|
886
449
|
);
|
|
887
|
-
|
|
888
450
|
return { enriched: true, changes };
|
|
889
451
|
}
|
|
890
452
|
|
|
891
|
-
// ─── Private Helpers
|
|
453
|
+
// ─── Private Helpers ──────────────────────────────────────────
|
|
892
454
|
|
|
893
|
-
private
|
|
894
|
-
const
|
|
895
|
-
const
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
455
|
+
private persistContradictions(candidates: ContradictionCandidate[]): Contradiction[] {
|
|
456
|
+
const detected: Contradiction[] = [];
|
|
457
|
+
for (const c of candidates) {
|
|
458
|
+
const result = this.provider.run(
|
|
459
|
+
'INSERT OR IGNORE INTO curator_contradictions (pattern_id, antipattern_id, similarity) VALUES (?, ?, ?)',
|
|
460
|
+
[c.patternId, c.antipatternId, c.similarity],
|
|
899
461
|
);
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
462
|
+
if (result.changes > 0) {
|
|
463
|
+
const row = this.provider.get<Record<string, unknown>>(
|
|
464
|
+
'SELECT * FROM curator_contradictions WHERE pattern_id = ? AND antipattern_id = ?',
|
|
465
|
+
[c.patternId, c.antipatternId],
|
|
466
|
+
);
|
|
467
|
+
if (row) detected.push(this.rowToContradiction(row));
|
|
903
468
|
}
|
|
904
469
|
}
|
|
905
|
-
|
|
906
|
-
for (const [term, df] of termDocFreq) {
|
|
907
|
-
const idf = Math.log((docCount + 1) / (df + 1)) + 1;
|
|
908
|
-
vocabulary.set(term, idf);
|
|
909
|
-
}
|
|
910
|
-
return vocabulary;
|
|
470
|
+
return detected;
|
|
911
471
|
}
|
|
912
472
|
|
|
913
473
|
private logChange(
|