@soleri/core 9.0.4 → 9.2.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/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 +90 -525
- 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 +22 -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 +26 -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 +377 -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 +157 -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 +129 -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 +294 -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 +199 -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 +296 -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 +154 -878
- 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 +227 -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 +21 -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/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/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 +330 -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 +6 -0
- package/dist/runtime/facades/index.js.map +1 -1
- 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 +39 -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/vault-facade.d.ts +4 -1
- package/dist/runtime/facades/vault-facade.d.ts.map +1 -1
- package/dist/runtime/facades/vault-facade.js +13 -66
- 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 +113 -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 +367 -40
- 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 +27 -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 +68 -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/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 +104 -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 +257 -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 +142 -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 +240 -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 +179 -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 +78 -931
- package/dist/vault/vault.js.map +1 -1
- package/package.json +1 -1
- package/src/agency/agency-manager.test.ts +600 -0
- package/src/agency/default-rules.test.ts +228 -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/brain/strength-scorer.ts +404 -0
- package/src/capabilities/chain-mapping.test.ts +66 -0
- package/src/capabilities/registry.test.ts +369 -0
- package/src/chat/agent-loop.test.ts +394 -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 +173 -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 +211 -0
- package/src/context/context-engine.test.ts +461 -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 +104 -0
- package/src/curator/contradiction-detector.test.ts +180 -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 +115 -777
- package/src/curator/duplicate-detector.test.ts +183 -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 +135 -0
- package/src/curator/schema.ts +65 -0
- package/src/curator/tag-manager.test.ts +165 -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 +171 -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 +150 -0
- package/src/domain-packs/types.test.ts +130 -0
- package/src/enforcement/adapters/claude-code.test.ts +216 -0
- package/src/enforcement/registry.test.ts +264 -0
- package/src/engine/bin/soleri-engine.ts +28 -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 +124 -0
- package/src/engine/module-manifest.ts +22 -2
- package/src/engine/register-engine.test.ts +230 -0
- package/src/engine/register-engine.ts +26 -2
- package/src/errors/classify.test.ts +199 -0
- package/src/errors/retry.test.ts +156 -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 +424 -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 +107 -0
- package/src/flows/executor.test.ts +263 -0
- package/src/flows/gate-evaluator.test.ts +194 -0
- package/src/flows/gate-evaluator.ts +25 -0
- package/src/flows/types.ts +4 -0
- package/src/governance/governance.test.ts +726 -0
- package/src/health/health-registry.test.ts +186 -0
- package/src/health/vault-integrity.test.ts +110 -0
- package/src/index.ts +92 -0
- package/src/intake/content-classifier.test.ts +209 -0
- package/src/intake/dedup-gate.test.ts +131 -0
- package/src/intake/intake-pipeline.test.ts +506 -0
- package/src/intake/intake-pipeline.ts +1 -0
- package/src/intake/text-ingester.test.ts +194 -0
- package/src/intake/text-ingester.ts +2 -0
- package/src/llm/key-pool.test.ts +236 -0
- package/src/llm/key-pool.ts +3 -4
- package/src/llm/llm-client.test.ts +345 -0
- package/src/llm/oauth-discovery.test.ts +180 -0
- package/src/llm/utils.test.ts +327 -0
- package/src/llm/utils.ts +2 -0
- package/src/{__tests__ → logging}/logger.test.ts +41 -62
- package/src/loop/loop-manager.test.ts +519 -0
- package/src/migrations/migration-runner.edge-cases.test.ts +319 -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 +320 -0
- package/src/operator/operator-profile.test.ts +314 -0
- package/src/operator/operator-profile.ts +469 -0
- package/src/operator/operator-signals-extended.test.ts +245 -0
- package/src/operator/operator-signals.test.ts +281 -0
- package/src/operator/operator-signals.ts +261 -0
- package/src/operator/operator-types.ts +444 -0
- package/src/operator/prompts/hook-precompact-operator-dispatch.md +94 -0
- package/src/operator/prompts/subagent-soft-signal-extractor.md +125 -0
- package/src/operator/prompts/subagent-synthesis-cognition.md +181 -0
- package/src/operator/prompts/subagent-synthesis-communication.md +140 -0
- package/src/operator/prompts/subagent-synthesis-technical.md +160 -0
- package/src/operator/prompts/subagent-synthesis-trust.md +143 -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 +59 -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 +406 -0
- package/src/planning/evidence-collector.ts +50 -0
- package/src/planning/gap-analysis-alternatives.test.ts +169 -0
- package/src/planning/gap-analysis.ts +21 -636
- package/src/planning/gap-passes.test.ts +372 -0
- package/src/planning/gap-passes.ts +298 -0
- package/src/planning/gap-patterns.test.ts +320 -0
- package/src/planning/gap-patterns.ts +234 -0
- package/src/planning/gap-types.ts +4 -1
- package/src/planning/github-projection.test.ts +177 -0
- package/src/planning/github-projection.ts +425 -0
- package/src/planning/impact-analyzer.test.ts +180 -0
- package/src/planning/impact-analyzer.ts +264 -0
- package/src/planning/plan-lifecycle.test.ts +312 -0
- package/src/planning/plan-lifecycle.ts +346 -0
- package/src/planning/planner-types.ts +215 -0
- package/src/{__tests__ → planning}/planner.test.ts +169 -15
- package/src/planning/planner.ts +197 -1228
- package/src/planning/rationalization-detector.test.ts +171 -0
- package/src/planning/rationalization-detector.ts +138 -0
- package/src/planning/reconciliation-engine.test.ts +141 -0
- package/src/planning/reconciliation-engine.ts +162 -0
- package/src/planning/task-verifier.test.ts +235 -0
- package/src/planning/task-verifier.ts +303 -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 +255 -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 +212 -0
- package/src/plugins/plugin-registry.test.ts +272 -0
- package/src/project/project-registry.test.ts +428 -0
- package/src/prompts/parser.test.ts +100 -0
- package/src/prompts/template-manager.test.ts +109 -0
- package/src/{__tests__ → queue}/async-infrastructure.test.ts +3 -3
- package/src/queue/job-queue.test.ts +331 -0
- package/src/queue/pipeline-runner.test.ts +209 -0
- package/src/runtime/admin-extra-ops.test.ts +527 -0
- package/src/runtime/admin-ops.test.ts +257 -0
- package/src/runtime/admin-ops.ts +45 -17
- package/src/runtime/admin-setup-ops.test.ts +328 -0
- package/src/runtime/admin-setup-ops.ts +20 -43
- package/src/runtime/archive-ops.test.ts +269 -0
- package/src/runtime/archive-ops.ts +347 -0
- package/src/runtime/capture-ops.test.ts +433 -0
- package/src/runtime/capture-ops.ts +50 -8
- package/src/runtime/chain-ops.test.ts +149 -0
- package/src/runtime/claude-md-helpers.test.ts +191 -0
- package/src/runtime/claude-md-helpers.ts +1 -1
- package/src/runtime/context-health.test.ts +78 -0
- package/src/runtime/context-health.ts +85 -0
- package/src/runtime/curator-extra-ops.test.ts +202 -0
- package/src/runtime/deprecation.test.ts +98 -0
- package/src/runtime/domain-ops.test.ts +268 -0
- package/src/runtime/facades/admin-facade.test.ts +333 -0
- package/src/runtime/facades/agency-facade.test.ts +278 -0
- package/src/runtime/facades/archive-facade.test.ts +294 -0
- package/src/runtime/facades/archive-facade.ts +14 -0
- package/src/runtime/facades/brain-facade.test.ts +714 -0
- package/src/runtime/facades/brain-facade.ts +2 -0
- package/src/runtime/facades/chat-facade.test.ts +166 -0
- package/src/runtime/facades/chat-facade.ts +15 -906
- package/src/runtime/facades/chat-service-ops.test.ts +276 -0
- package/src/runtime/facades/chat-service-ops.ts +374 -0
- package/src/runtime/facades/chat-session-ops.test.ts +197 -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 +269 -0
- package/src/runtime/facades/chat-transport-ops.ts +380 -0
- package/src/runtime/facades/context-facade.test.ts +108 -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 +6 -0
- package/src/runtime/facades/loop-facade.test.ts +245 -0
- package/src/runtime/facades/memory-facade.test.ts +269 -0
- package/src/runtime/facades/memory-facade.ts +78 -6
- package/src/runtime/facades/operator-facade.test.ts +208 -0
- package/src/runtime/facades/operator-facade.ts +236 -0
- package/src/runtime/facades/orchestrate-facade.test.ts +185 -0
- package/src/runtime/facades/orchestrate-facade.ts +3 -3
- package/src/runtime/facades/plan-facade.test.ts +266 -0
- package/src/runtime/facades/plan-facade.ts +42 -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/vault-facade.test.ts +631 -0
- package/src/runtime/facades/vault-facade.ts +15 -70
- package/src/runtime/feature-flags.test.ts +140 -0
- package/src/runtime/github-integration.test.ts +89 -0
- package/src/runtime/github-integration.ts +159 -0
- package/src/runtime/grading-ops.test.ts +141 -0
- package/src/runtime/grading-ops.ts +1 -1
- package/src/runtime/intake-ops.test.ts +208 -0
- package/src/runtime/loop-ops.test.ts +238 -0
- package/src/runtime/memory-cross-project-ops.test.ts +177 -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 +435 -46
- package/src/runtime/pack-ops.test.ts +158 -0
- package/src/runtime/planning-extra-ops.test.ts +583 -0
- package/src/runtime/planning-extra-ops.ts +72 -4
- package/src/{__tests__ → runtime}/playbook-ops-execution.test.ts +3 -3
- package/src/runtime/playbook-ops.test.ts +262 -0
- package/src/runtime/plugin-ops.test.ts +201 -0
- package/src/runtime/project-ops.test.ts +235 -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 +39 -12
- package/src/runtime/session-briefing.test.ts +302 -0
- package/src/runtime/session-briefing.ts +80 -1
- package/src/runtime/sync-ops.test.ts +221 -0
- package/src/runtime/sync-ops.ts +325 -0
- package/src/runtime/telemetry-ops.test.ts +132 -0
- package/src/runtime/types.ts +10 -4
- package/src/runtime/vault-extra-ops.test.ts +246 -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 +130 -0
- package/src/runtime/vault-sharing-ops.ts +5 -329
- package/src/skills/sync-skills.ts +108 -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 +294 -0
- package/src/update-check.ts +111 -0
- package/src/vault/__tests__/vault-characterization.test.ts +168 -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 +358 -0
- package/src/vault/linking.ts +149 -183
- package/src/vault/obsidian-sync.test.ts +342 -0
- package/src/vault/playbook.test.ts +152 -0
- package/src/vault/scope-detector.test.ts +187 -0
- package/src/vault/vault-branching.test.ts +250 -0
- package/src/{__tests__ → vault}/vault-connect.test.ts +1 -1
- package/src/vault/vault-entries.ts +282 -0
- package/src/vault/vault-interfaces.ts +56 -0
- package/src/vault/vault-maintenance.ts +205 -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 +160 -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 +181 -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 +89 -1171
- 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__/health-registry.test.ts +0 -173
- 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/dist/planning/planner.js
CHANGED
|
@@ -1,137 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Planner facade — delegates to extracted modules, owns persistence.
|
|
3
|
+
* Re-exports all public types for backward compatibility.
|
|
4
|
+
*/
|
|
1
5
|
import { readFileSync, writeFileSync, existsSync, mkdirSync } from 'node:fs';
|
|
2
|
-
import { createHash } from 'node:crypto';
|
|
3
6
|
import { dirname } from 'node:path';
|
|
4
|
-
import { SEVERITY_WEIGHTS, CATEGORY_PENALTY_CAPS, CATEGORY_BONUS_CAPS } from './gap-types.js';
|
|
5
7
|
import { runGapAnalysis } from './gap-analysis.js';
|
|
6
|
-
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
draft: ['approved'],
|
|
14
|
-
approved: ['executing'],
|
|
15
|
-
executing: ['validating', 'reconciling'],
|
|
16
|
-
validating: ['reconciling', 'executing'],
|
|
17
|
-
reconciling: ['completed'],
|
|
18
|
-
completed: ['archived'],
|
|
19
|
-
archived: [],
|
|
20
|
-
};
|
|
21
|
-
/**
|
|
22
|
-
* Statuses where the 30-minute TTL should NOT apply.
|
|
23
|
-
* Plans in these states may span multiple sessions.
|
|
24
|
-
*/
|
|
25
|
-
export const NON_EXPIRING_STATUSES = [
|
|
26
|
-
'brainstorming',
|
|
27
|
-
'executing',
|
|
28
|
-
'validating',
|
|
29
|
-
'reconciling',
|
|
30
|
-
];
|
|
31
|
-
/**
|
|
32
|
-
* Validate a lifecycle status transition.
|
|
33
|
-
* Returns true if the transition is valid, false otherwise.
|
|
34
|
-
*/
|
|
35
|
-
export function isValidTransition(from, to) {
|
|
36
|
-
return LIFECYCLE_TRANSITIONS[from].includes(to);
|
|
37
|
-
}
|
|
38
|
-
/**
|
|
39
|
-
* Get the valid next statuses for a given status.
|
|
40
|
-
*/
|
|
41
|
-
export function getValidNextStatuses(status) {
|
|
42
|
-
return LIFECYCLE_TRANSITIONS[status];
|
|
43
|
-
}
|
|
44
|
-
/**
|
|
45
|
-
* Check if a status should have TTL expiration.
|
|
46
|
-
* Plans in executing/reconciling states persist indefinitely.
|
|
47
|
-
*/
|
|
48
|
-
export function shouldExpire(status) {
|
|
49
|
-
return !NON_EXPIRING_STATUSES.includes(status);
|
|
50
|
-
}
|
|
51
|
-
/**
|
|
52
|
-
* Severity weights for drift accuracy score calculation.
|
|
53
|
-
* Score = 100 - sum(drift_items * weight_per_impact)
|
|
54
|
-
* Ported from Salvador's plan-lifecycle-types.ts.
|
|
55
|
-
*/
|
|
56
|
-
export const DRIFT_WEIGHTS = {
|
|
57
|
-
high: 20,
|
|
58
|
-
medium: 10,
|
|
59
|
-
low: 5,
|
|
60
|
-
};
|
|
61
|
-
/**
|
|
62
|
-
* Calculate drift accuracy score from drift items.
|
|
63
|
-
* Score = max(0, 100 - sum(weight_per_impact))
|
|
64
|
-
* Ported from Salvador's calculateDriftScore.
|
|
65
|
-
*/
|
|
66
|
-
export function calculateDriftScore(items) {
|
|
67
|
-
let deductions = 0;
|
|
68
|
-
for (const item of items) {
|
|
69
|
-
deductions += DRIFT_WEIGHTS[item.impact];
|
|
70
|
-
}
|
|
71
|
-
return Math.max(0, 100 - deductions);
|
|
72
|
-
}
|
|
73
|
-
/**
|
|
74
|
-
* Calculate score from gaps with severity-weighted deductions and iteration leniency.
|
|
75
|
-
* Ported from Salvador MCP's plan-grading.ts.
|
|
76
|
-
*
|
|
77
|
-
* - Minor gaps: weight=0 on iteration 1 (free sketching), weight=1 on iteration 2, full weight on 3+
|
|
78
|
-
* - Per-category deductions are capped before summing (prevents one category from tanking the score)
|
|
79
|
-
* - Score = max(0, 100 - totalDeductions)
|
|
80
|
-
*/
|
|
81
|
-
export function calculateScore(gaps, iteration = 1) {
|
|
82
|
-
const categoryDeductions = new Map();
|
|
83
|
-
const categoryBonuses = new Map();
|
|
84
|
-
for (const gap of gaps) {
|
|
85
|
-
let weight = SEVERITY_WEIGHTS[gap.severity];
|
|
86
|
-
// Iteration leniency for minor gaps
|
|
87
|
-
if (gap.severity === 'minor') {
|
|
88
|
-
if (iteration === 1)
|
|
89
|
-
weight = 0;
|
|
90
|
-
else if (iteration === 2)
|
|
91
|
-
weight = 1;
|
|
92
|
-
// iteration 3+: full weight (2)
|
|
93
|
-
}
|
|
94
|
-
const category = gap.category;
|
|
95
|
-
if (weight < 0) {
|
|
96
|
-
// Bonus — accumulate as positive value for capping, apply as negative deduction
|
|
97
|
-
categoryBonuses.set(category, (categoryBonuses.get(category) ?? 0) + Math.abs(weight));
|
|
98
|
-
}
|
|
99
|
-
else {
|
|
100
|
-
categoryDeductions.set(category, (categoryDeductions.get(category) ?? 0) + weight);
|
|
101
|
-
}
|
|
102
|
-
}
|
|
103
|
-
let deductions = 0;
|
|
104
|
-
for (const [category, total] of categoryDeductions) {
|
|
105
|
-
const cap = CATEGORY_PENALTY_CAPS[category];
|
|
106
|
-
deductions += cap !== undefined ? Math.min(total, cap) : total;
|
|
107
|
-
}
|
|
108
|
-
let bonuses = 0;
|
|
109
|
-
for (const [category, total] of categoryBonuses) {
|
|
110
|
-
const cap = CATEGORY_BONUS_CAPS[category];
|
|
111
|
-
bonuses += cap !== undefined ? Math.min(total, cap) : total;
|
|
112
|
-
}
|
|
113
|
-
return Math.max(0, Math.min(100, 100 - deductions + bonuses));
|
|
114
|
-
}
|
|
8
|
+
export * from './plan-lifecycle.js';
|
|
9
|
+
export * from './reconciliation-engine.js';
|
|
10
|
+
export * from './task-verifier.js';
|
|
11
|
+
export * from './planner-types.js';
|
|
12
|
+
import { applyTransition, scoreToGrade, gradeToMinScore, PlanGradeRejectionError, hasCircularDependencies, applyIteration, applySplitTasks, calculateScore, applyTaskStatusUpdate, createPlanObject, } from './plan-lifecycle.js';
|
|
13
|
+
import { buildReconciliationReport, buildAutoReconcileInput, computeExecutionSummary } from './reconciliation-engine.js';
|
|
14
|
+
import { createEvidence, verifyTaskLogic, verifyPlanLogic, verifyDeliverablesLogic, createDeliverable, buildSpecReviewPrompt, buildQualityReviewPrompt, } from './task-verifier.js';
|
|
115
15
|
export class Planner {
|
|
116
16
|
filePath;
|
|
117
17
|
store;
|
|
118
18
|
gapOptions;
|
|
119
|
-
|
|
19
|
+
minGradeForApproval;
|
|
20
|
+
constructor(filePath, options) {
|
|
120
21
|
this.filePath = filePath;
|
|
121
|
-
|
|
22
|
+
if (options && 'minGradeForApproval' in options) {
|
|
23
|
+
this.gapOptions = options.gapOptions;
|
|
24
|
+
this.minGradeForApproval = options.minGradeForApproval ?? 'A';
|
|
25
|
+
}
|
|
26
|
+
else {
|
|
27
|
+
this.gapOptions = options;
|
|
28
|
+
this.minGradeForApproval = 'A';
|
|
29
|
+
}
|
|
122
30
|
this.store = this.load();
|
|
123
31
|
}
|
|
124
32
|
load() {
|
|
125
|
-
if (!existsSync(this.filePath))
|
|
33
|
+
if (!existsSync(this.filePath))
|
|
126
34
|
return { version: '1.0', plans: [] };
|
|
127
|
-
}
|
|
128
35
|
try {
|
|
129
36
|
const data = readFileSync(this.filePath, 'utf-8');
|
|
130
37
|
const store = JSON.parse(data);
|
|
131
|
-
|
|
132
|
-
for (const plan of store.plans) {
|
|
38
|
+
for (const plan of store.plans)
|
|
133
39
|
plan.checks = plan.checks ?? [];
|
|
134
|
-
}
|
|
135
40
|
return store;
|
|
136
41
|
}
|
|
137
42
|
catch {
|
|
@@ -142,31 +47,25 @@ export class Planner {
|
|
|
142
47
|
mkdirSync(dirname(this.filePath), { recursive: true });
|
|
143
48
|
writeFileSync(this.filePath, JSON.stringify(this.store, null, 2), 'utf-8');
|
|
144
49
|
}
|
|
50
|
+
transition(plan, to) {
|
|
51
|
+
const r = applyTransition(plan.status, to);
|
|
52
|
+
plan.status = r.status;
|
|
53
|
+
plan.updatedAt = r.updatedAt;
|
|
54
|
+
}
|
|
55
|
+
requirePlan(planId) {
|
|
56
|
+
const plan = this.get(planId);
|
|
57
|
+
if (!plan)
|
|
58
|
+
throw new Error(`Plan not found: ${planId}`);
|
|
59
|
+
return plan;
|
|
60
|
+
}
|
|
61
|
+
requireTask(plan, taskId) {
|
|
62
|
+
const task = plan.tasks.find((t) => t.id === taskId);
|
|
63
|
+
if (!task)
|
|
64
|
+
throw new Error(`Task not found: ${taskId}`);
|
|
65
|
+
return task;
|
|
66
|
+
}
|
|
145
67
|
create(params) {
|
|
146
|
-
const
|
|
147
|
-
const plan = {
|
|
148
|
-
id: `plan-${now}-${Math.random().toString(36).slice(2, 8)}`,
|
|
149
|
-
objective: params.objective,
|
|
150
|
-
scope: params.scope,
|
|
151
|
-
status: params.initialStatus ?? 'draft',
|
|
152
|
-
decisions: params.decisions ?? [],
|
|
153
|
-
tasks: (params.tasks ?? []).map((t, i) => ({
|
|
154
|
-
id: `task-${i + 1}`,
|
|
155
|
-
title: t.title,
|
|
156
|
-
description: t.description,
|
|
157
|
-
status: 'pending',
|
|
158
|
-
updatedAt: now,
|
|
159
|
-
})),
|
|
160
|
-
...(params.approach !== undefined && { approach: params.approach }),
|
|
161
|
-
...(params.context !== undefined && { context: params.context }),
|
|
162
|
-
...(params.success_criteria !== undefined && { success_criteria: params.success_criteria }),
|
|
163
|
-
...(params.tool_chain !== undefined && { tool_chain: params.tool_chain }),
|
|
164
|
-
...(params.flow !== undefined && { flow: params.flow }),
|
|
165
|
-
...(params.target_mode !== undefined && { target_mode: params.target_mode }),
|
|
166
|
-
checks: [],
|
|
167
|
-
createdAt: now,
|
|
168
|
-
updatedAt: now,
|
|
169
|
-
};
|
|
68
|
+
const plan = createPlanObject(params);
|
|
170
69
|
this.store.plans.push(plan);
|
|
171
70
|
this.save();
|
|
172
71
|
return plan;
|
|
@@ -174,12 +73,7 @@ export class Planner {
|
|
|
174
73
|
get(planId) {
|
|
175
74
|
return this.store.plans.find((p) => p.id === planId) ?? null;
|
|
176
75
|
}
|
|
177
|
-
list() {
|
|
178
|
-
return [...this.store.plans];
|
|
179
|
-
}
|
|
180
|
-
/**
|
|
181
|
-
* Permanently remove a plan by ID. Returns true if found and removed.
|
|
182
|
-
*/
|
|
76
|
+
list() { return [...this.store.plans]; }
|
|
183
77
|
remove(planId) {
|
|
184
78
|
const idx = this.store.plans.findIndex((p) => p.id === planId);
|
|
185
79
|
if (idx < 0)
|
|
@@ -188,784 +82,239 @@ export class Planner {
|
|
|
188
82
|
this.save();
|
|
189
83
|
return true;
|
|
190
84
|
}
|
|
191
|
-
/**
|
|
192
|
-
* Transition a plan to a new status using the typed FSM.
|
|
193
|
-
* Validates that the transition is allowed before applying it.
|
|
194
|
-
*/
|
|
195
|
-
transition(plan, to) {
|
|
196
|
-
if (!isValidTransition(plan.status, to)) {
|
|
197
|
-
const valid = getValidNextStatuses(plan.status);
|
|
198
|
-
throw new Error(`Invalid transition: '${plan.status}' → '${to}'. ` +
|
|
199
|
-
`Valid transitions from '${plan.status}': ${valid.length > 0 ? valid.join(', ') : 'none'}`);
|
|
200
|
-
}
|
|
201
|
-
plan.status = to;
|
|
202
|
-
plan.updatedAt = Date.now();
|
|
203
|
-
}
|
|
204
|
-
/**
|
|
205
|
-
* Promote a brainstorming plan to draft status.
|
|
206
|
-
* Only allowed from 'brainstorming'.
|
|
207
|
-
*/
|
|
208
85
|
promoteToDraft(planId) {
|
|
209
|
-
const plan = this.
|
|
210
|
-
if (!plan)
|
|
211
|
-
throw new Error(`Plan not found: ${planId}`);
|
|
86
|
+
const plan = this.requirePlan(planId);
|
|
212
87
|
this.transition(plan, 'draft');
|
|
213
88
|
this.save();
|
|
214
89
|
return plan;
|
|
215
90
|
}
|
|
216
91
|
approve(planId) {
|
|
217
|
-
const plan = this.
|
|
218
|
-
|
|
219
|
-
|
|
92
|
+
const plan = this.requirePlan(planId);
|
|
93
|
+
const check = plan.latestCheck;
|
|
94
|
+
if (check && check.score < gradeToMinScore(this.minGradeForApproval)) {
|
|
95
|
+
throw new PlanGradeRejectionError(check.grade, check.score, this.minGradeForApproval, check.gaps);
|
|
96
|
+
}
|
|
220
97
|
this.transition(plan, 'approved');
|
|
221
98
|
this.save();
|
|
222
99
|
return plan;
|
|
223
100
|
}
|
|
224
101
|
startExecution(planId) {
|
|
225
|
-
const plan = this.
|
|
226
|
-
if (!plan)
|
|
227
|
-
throw new Error(`Plan not found: ${planId}`);
|
|
102
|
+
const plan = this.requirePlan(planId);
|
|
228
103
|
this.transition(plan, 'executing');
|
|
229
104
|
this.save();
|
|
230
105
|
return plan;
|
|
231
106
|
}
|
|
232
|
-
updateTask(planId, taskId, status) {
|
|
233
|
-
const plan = this.get(planId);
|
|
234
|
-
if (!plan)
|
|
235
|
-
throw new Error(`Plan not found: ${planId}`);
|
|
236
|
-
if (plan.status !== 'executing' && plan.status !== 'validating')
|
|
237
|
-
throw new Error(`Cannot update tasks on plan in '${plan.status}' status — must be 'executing' or 'validating'`);
|
|
238
|
-
const task = plan.tasks.find((t) => t.id === taskId);
|
|
239
|
-
if (!task)
|
|
240
|
-
throw new Error(`Task not found: ${taskId}`);
|
|
241
|
-
const now = Date.now();
|
|
242
|
-
// Auto-set startedAt on first in_progress transition
|
|
243
|
-
if (status === 'in_progress' && !task.startedAt) {
|
|
244
|
-
task.startedAt = now;
|
|
245
|
-
}
|
|
246
|
-
// Auto-set completedAt and compute durationMs on terminal transitions
|
|
247
|
-
if (status === 'completed' || status === 'skipped' || status === 'failed') {
|
|
248
|
-
task.completedAt = now;
|
|
249
|
-
if (task.startedAt) {
|
|
250
|
-
if (!task.metrics)
|
|
251
|
-
task.metrics = {};
|
|
252
|
-
task.metrics.durationMs = now - task.startedAt;
|
|
253
|
-
}
|
|
254
|
-
}
|
|
255
|
-
task.status = status;
|
|
256
|
-
task.updatedAt = now;
|
|
257
|
-
plan.updatedAt = now;
|
|
258
|
-
this.save();
|
|
259
|
-
return plan;
|
|
260
|
-
}
|
|
261
|
-
/**
|
|
262
|
-
* Transition plan to 'validating' state (post-execution verification).
|
|
263
|
-
* Only allowed from 'executing'.
|
|
264
|
-
*/
|
|
265
107
|
startValidation(planId) {
|
|
266
|
-
const plan = this.
|
|
267
|
-
if (!plan)
|
|
268
|
-
throw new Error(`Plan not found: ${planId}`);
|
|
108
|
+
const plan = this.requirePlan(planId);
|
|
269
109
|
this.transition(plan, 'validating');
|
|
270
110
|
this.save();
|
|
271
111
|
return plan;
|
|
272
112
|
}
|
|
273
|
-
/**
|
|
274
|
-
* Transition plan to 'reconciling' state.
|
|
275
|
-
* Allowed from 'executing' or 'validating'.
|
|
276
|
-
*/
|
|
277
113
|
startReconciliation(planId) {
|
|
278
|
-
const plan = this.
|
|
279
|
-
if (!plan)
|
|
280
|
-
throw new Error(`Plan not found: ${planId}`);
|
|
114
|
+
const plan = this.requirePlan(planId);
|
|
281
115
|
this.transition(plan, 'reconciling');
|
|
282
116
|
this.save();
|
|
283
117
|
return plan;
|
|
284
118
|
}
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
119
|
+
updateTask(planId, taskId, status) {
|
|
120
|
+
const plan = this.requirePlan(planId);
|
|
121
|
+
if (plan.status !== 'executing' && plan.status !== 'validating')
|
|
122
|
+
throw new Error(`Cannot update tasks on plan in '${plan.status}' status — must be 'executing' or 'validating'`);
|
|
123
|
+
applyTaskStatusUpdate(this.requireTask(plan, taskId), status);
|
|
124
|
+
plan.updatedAt = Date.now();
|
|
125
|
+
this.save();
|
|
126
|
+
return plan;
|
|
127
|
+
}
|
|
128
|
+
reconcile(planId, report) {
|
|
129
|
+
const plan = this.requirePlan(planId);
|
|
130
|
+
if (plan.status !== 'executing' && plan.status !== 'validating' && plan.status !== 'reconciling')
|
|
131
|
+
throw new Error(`Cannot reconcile plan in '${plan.status}' status — must be 'executing', 'validating', or 'reconciling'`);
|
|
132
|
+
plan.reconciliation = buildReconciliationReport(planId, report);
|
|
133
|
+
plan.executionSummary = computeExecutionSummary(plan.tasks);
|
|
134
|
+
if (plan.status === 'executing' || plan.status === 'validating')
|
|
135
|
+
plan.status = 'reconciling';
|
|
136
|
+
plan.status = 'completed';
|
|
137
|
+
plan.updatedAt = Date.now();
|
|
138
|
+
this.save();
|
|
139
|
+
return plan;
|
|
140
|
+
}
|
|
290
141
|
complete(planId) {
|
|
291
|
-
const plan = this.
|
|
292
|
-
if (
|
|
293
|
-
|
|
294
|
-
plan.executionSummary =
|
|
142
|
+
const plan = this.requirePlan(planId);
|
|
143
|
+
if (plan.status === 'executing' || plan.status === 'validating')
|
|
144
|
+
return this.reconcile(planId, { actualOutcome: 'All tasks completed', reconciledBy: 'auto' });
|
|
145
|
+
plan.executionSummary = computeExecutionSummary(plan.tasks);
|
|
295
146
|
this.transition(plan, 'completed');
|
|
296
147
|
this.save();
|
|
297
148
|
return plan;
|
|
298
149
|
}
|
|
150
|
+
autoReconcile(planId) {
|
|
151
|
+
const plan = this.requirePlan(planId);
|
|
152
|
+
if (plan.status !== 'executing' && plan.status !== 'validating')
|
|
153
|
+
throw new Error(`Cannot auto-reconcile plan in '${plan.status}' status — must be 'executing' or 'validating'`);
|
|
154
|
+
const result = buildAutoReconcileInput(plan.tasks);
|
|
155
|
+
if (!result.canAutoReconcile || !result.input)
|
|
156
|
+
return null;
|
|
157
|
+
return this.reconcile(planId, result.input);
|
|
158
|
+
}
|
|
299
159
|
getExecuting() {
|
|
300
160
|
return this.store.plans.filter((p) => p.status === 'executing' || p.status === 'validating');
|
|
301
161
|
}
|
|
302
162
|
getActive() {
|
|
303
|
-
return this.store.plans.filter((p) => p.status === 'brainstorming' ||
|
|
304
|
-
p.status === '
|
|
305
|
-
p.status === 'approved' ||
|
|
306
|
-
p.status === 'executing' ||
|
|
307
|
-
p.status === 'validating' ||
|
|
308
|
-
p.status === 'reconciling');
|
|
163
|
+
return this.store.plans.filter((p) => p.status === 'brainstorming' || p.status === 'draft' || p.status === 'approved' ||
|
|
164
|
+
p.status === 'executing' || p.status === 'validating' || p.status === 'reconciling');
|
|
309
165
|
}
|
|
310
|
-
/**
|
|
311
|
-
* Iterate on a draft plan — modify objective, scope, decisions, or tasks.
|
|
312
|
-
* Only allowed on plans in 'draft' status.
|
|
313
|
-
*/
|
|
314
166
|
iterate(planId, changes) {
|
|
315
|
-
const plan = this.
|
|
316
|
-
if (!plan)
|
|
317
|
-
throw new Error(`Plan not found: ${planId}`);
|
|
167
|
+
const plan = this.requirePlan(planId);
|
|
318
168
|
if (plan.status !== 'draft' && plan.status !== 'brainstorming')
|
|
319
169
|
throw new Error(`Cannot iterate plan in '${plan.status}' status — must be 'draft' or 'brainstorming'`);
|
|
320
|
-
|
|
321
|
-
if (changes.objective !== undefined)
|
|
322
|
-
plan.objective = changes.objective;
|
|
323
|
-
if (changes.scope !== undefined)
|
|
324
|
-
plan.scope = changes.scope;
|
|
325
|
-
if (changes.decisions !== undefined)
|
|
326
|
-
plan.decisions = changes.decisions;
|
|
327
|
-
if (changes.approach !== undefined)
|
|
328
|
-
plan.approach = changes.approach;
|
|
329
|
-
if (changes.context !== undefined)
|
|
330
|
-
plan.context = changes.context;
|
|
331
|
-
if (changes.success_criteria !== undefined)
|
|
332
|
-
plan.success_criteria = changes.success_criteria;
|
|
333
|
-
if (changes.tool_chain !== undefined)
|
|
334
|
-
plan.tool_chain = changes.tool_chain;
|
|
335
|
-
if (changes.flow !== undefined)
|
|
336
|
-
plan.flow = changes.flow;
|
|
337
|
-
if (changes.target_mode !== undefined)
|
|
338
|
-
plan.target_mode = changes.target_mode;
|
|
339
|
-
// Remove tasks by ID
|
|
340
|
-
if (changes.removeTasks && changes.removeTasks.length > 0) {
|
|
341
|
-
const removeSet = new Set(changes.removeTasks);
|
|
342
|
-
plan.tasks = plan.tasks.filter((t) => !removeSet.has(t.id));
|
|
343
|
-
}
|
|
344
|
-
// Add new tasks
|
|
345
|
-
if (changes.addTasks && changes.addTasks.length > 0) {
|
|
346
|
-
const maxIndex = plan.tasks.reduce((max, t) => {
|
|
347
|
-
const num = parseInt(t.id.replace('task-', ''), 10);
|
|
348
|
-
return isNaN(num) ? max : Math.max(max, num);
|
|
349
|
-
}, 0);
|
|
350
|
-
for (let i = 0; i < changes.addTasks.length; i++) {
|
|
351
|
-
plan.tasks.push({
|
|
352
|
-
id: `task-${maxIndex + i + 1}`,
|
|
353
|
-
title: changes.addTasks[i].title,
|
|
354
|
-
description: changes.addTasks[i].description,
|
|
355
|
-
status: 'pending',
|
|
356
|
-
updatedAt: now,
|
|
357
|
-
});
|
|
358
|
-
}
|
|
359
|
-
}
|
|
360
|
-
plan.updatedAt = now;
|
|
170
|
+
applyIteration(plan, changes);
|
|
361
171
|
this.save();
|
|
362
172
|
return plan;
|
|
363
173
|
}
|
|
364
|
-
/**
|
|
365
|
-
* Split a plan's tasks into sub-tasks with dependency tracking.
|
|
366
|
-
* Replaces existing tasks with a new set that includes dependency references.
|
|
367
|
-
* Only allowed on 'draft' or 'approved' plans.
|
|
368
|
-
*/
|
|
369
174
|
splitTasks(planId, tasks) {
|
|
370
|
-
const plan = this.
|
|
371
|
-
if (!plan)
|
|
372
|
-
throw new Error(`Plan not found: ${planId}`);
|
|
175
|
+
const plan = this.requirePlan(planId);
|
|
373
176
|
if (plan.status !== 'brainstorming' && plan.status !== 'draft' && plan.status !== 'approved')
|
|
374
177
|
throw new Error(`Cannot split tasks on plan in '${plan.status}' status — must be 'brainstorming', 'draft', or 'approved'`);
|
|
375
|
-
|
|
376
|
-
plan.tasks = tasks.map((t, i) => ({
|
|
377
|
-
id: `task-${i + 1}`,
|
|
378
|
-
title: t.title,
|
|
379
|
-
description: t.description,
|
|
380
|
-
status: 'pending',
|
|
381
|
-
dependsOn: t.dependsOn,
|
|
382
|
-
...(t.acceptanceCriteria && { acceptanceCriteria: t.acceptanceCriteria }),
|
|
383
|
-
updatedAt: now,
|
|
384
|
-
}));
|
|
385
|
-
// Validate dependency references
|
|
386
|
-
const taskIds = new Set(plan.tasks.map((t) => t.id));
|
|
387
|
-
for (const task of plan.tasks) {
|
|
388
|
-
if (task.dependsOn) {
|
|
389
|
-
for (const dep of task.dependsOn) {
|
|
390
|
-
if (!taskIds.has(dep)) {
|
|
391
|
-
throw new Error(`Task '${task.id}' depends on unknown task '${dep}'`);
|
|
392
|
-
}
|
|
393
|
-
}
|
|
394
|
-
}
|
|
395
|
-
}
|
|
396
|
-
plan.updatedAt = now;
|
|
397
|
-
this.save();
|
|
398
|
-
return plan;
|
|
399
|
-
}
|
|
400
|
-
/**
|
|
401
|
-
* Reconcile a plan — compare what was planned vs what actually happened.
|
|
402
|
-
* Uses impact-weighted drift scoring (ported from Salvador's calculateDriftScore).
|
|
403
|
-
*
|
|
404
|
-
* Transitions: executing → reconciling → completed (automatic).
|
|
405
|
-
* Also allowed from 'validating' and 'reconciling' states.
|
|
406
|
-
*/
|
|
407
|
-
reconcile(planId, report) {
|
|
408
|
-
const plan = this.get(planId);
|
|
409
|
-
if (!plan)
|
|
410
|
-
throw new Error(`Plan not found: ${planId}`);
|
|
411
|
-
if (plan.status !== 'executing' &&
|
|
412
|
-
plan.status !== 'validating' &&
|
|
413
|
-
plan.status !== 'reconciling')
|
|
414
|
-
throw new Error(`Cannot reconcile plan in '${plan.status}' status — must be 'executing', 'validating', or 'reconciling'`);
|
|
415
|
-
const driftItems = report.driftItems ?? [];
|
|
416
|
-
// Impact-weighted drift scoring (ported from Salvador)
|
|
417
|
-
const accuracy = calculateDriftScore(driftItems);
|
|
418
|
-
plan.reconciliation = {
|
|
419
|
-
planId,
|
|
420
|
-
accuracy,
|
|
421
|
-
driftItems,
|
|
422
|
-
summary: report.actualOutcome,
|
|
423
|
-
reconciledAt: Date.now(),
|
|
424
|
-
};
|
|
425
|
-
// Compute execution summary from per-task metrics
|
|
426
|
-
plan.executionSummary = this.computeExecutionSummary(plan);
|
|
427
|
-
// Transition through reconciling → completed via FSM
|
|
428
|
-
if (plan.status === 'executing' || plan.status === 'validating') {
|
|
429
|
-
plan.status = 'reconciling';
|
|
430
|
-
}
|
|
431
|
-
// Auto-complete after reconciliation
|
|
432
|
-
plan.status = 'completed';
|
|
433
|
-
plan.updatedAt = Date.now();
|
|
178
|
+
applySplitTasks(plan, tasks);
|
|
434
179
|
this.save();
|
|
435
180
|
return plan;
|
|
436
181
|
}
|
|
437
|
-
/**
|
|
438
|
-
* Add review evidence to a plan or specific task.
|
|
439
|
-
*/
|
|
440
182
|
addReview(planId, review) {
|
|
441
|
-
const plan = this.
|
|
442
|
-
if (
|
|
443
|
-
|
|
444
|
-
if (review.taskId) {
|
|
445
|
-
const task = plan.tasks.find((t) => t.id === review.taskId);
|
|
446
|
-
if (!task)
|
|
447
|
-
throw new Error(`Task not found: ${review.taskId}`);
|
|
448
|
-
}
|
|
183
|
+
const plan = this.requirePlan(planId);
|
|
184
|
+
if (review.taskId)
|
|
185
|
+
this.requireTask(plan, review.taskId);
|
|
449
186
|
if (!plan.reviews)
|
|
450
187
|
plan.reviews = [];
|
|
451
188
|
plan.reviews.push({
|
|
452
|
-
planId,
|
|
453
|
-
|
|
454
|
-
reviewer: review.reviewer,
|
|
455
|
-
outcome: review.outcome,
|
|
456
|
-
comments: review.comments,
|
|
457
|
-
reviewedAt: Date.now(),
|
|
189
|
+
planId, taskId: review.taskId, reviewer: review.reviewer,
|
|
190
|
+
outcome: review.outcome, comments: review.comments, reviewedAt: Date.now(),
|
|
458
191
|
});
|
|
459
192
|
plan.updatedAt = Date.now();
|
|
460
193
|
this.save();
|
|
461
194
|
return plan;
|
|
462
195
|
}
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
if (!plan)
|
|
470
|
-
throw new Error(`Plan not found: ${planId}`);
|
|
471
|
-
const task = plan.tasks.find((t) => t.id === taskId);
|
|
472
|
-
if (!task)
|
|
473
|
-
throw new Error(`Task not found: ${taskId}`);
|
|
474
|
-
const unmetDependencies = [];
|
|
475
|
-
if (task.dependsOn) {
|
|
476
|
-
for (const depId of task.dependsOn) {
|
|
477
|
-
const dep = plan.tasks.find((t) => t.id === depId);
|
|
478
|
-
if (dep && dep.status !== 'completed') {
|
|
479
|
-
unmetDependencies.push(dep);
|
|
480
|
-
}
|
|
481
|
-
}
|
|
482
|
-
}
|
|
483
|
-
const result = {
|
|
484
|
-
task,
|
|
485
|
-
unmetDependencies,
|
|
486
|
-
ready: unmetDependencies.length === 0,
|
|
487
|
-
};
|
|
488
|
-
// Include deliverable status if deliverables exist
|
|
489
|
-
if (task.deliverables && task.deliverables.length > 0) {
|
|
490
|
-
result.deliverableStatus = {
|
|
491
|
-
count: task.deliverables.length,
|
|
492
|
-
staleCount: task.deliverables.filter((d) => d.stale).length,
|
|
493
|
-
};
|
|
494
|
-
}
|
|
495
|
-
return result;
|
|
196
|
+
setGitHubProjection(planId, projection) {
|
|
197
|
+
const plan = this.requirePlan(planId);
|
|
198
|
+
plan.githubProjection = projection;
|
|
199
|
+
plan.updatedAt = Date.now();
|
|
200
|
+
this.save();
|
|
201
|
+
return plan;
|
|
496
202
|
}
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
let tasksSkipped = 0;
|
|
506
|
-
let tasksFailed = 0;
|
|
507
|
-
let tasksWithDuration = 0;
|
|
508
|
-
for (const task of plan.tasks) {
|
|
509
|
-
if (task.status === 'completed')
|
|
510
|
-
tasksCompleted++;
|
|
511
|
-
else if (task.status === 'skipped')
|
|
512
|
-
tasksSkipped++;
|
|
513
|
-
else if (task.status === 'failed')
|
|
514
|
-
tasksFailed++;
|
|
515
|
-
if (task.metrics?.durationMs) {
|
|
516
|
-
totalDurationMs += task.metrics.durationMs;
|
|
517
|
-
tasksWithDuration++;
|
|
518
|
-
}
|
|
203
|
+
getDispatch(planId, taskId) {
|
|
204
|
+
const plan = this.requirePlan(planId);
|
|
205
|
+
const task = this.requireTask(plan, taskId);
|
|
206
|
+
const unmetDeps = [];
|
|
207
|
+
for (const depId of task.dependsOn ?? []) {
|
|
208
|
+
const dep = plan.tasks.find((t) => t.id === depId);
|
|
209
|
+
if (dep && dep.status !== 'completed')
|
|
210
|
+
unmetDeps.push(dep);
|
|
519
211
|
}
|
|
520
212
|
return {
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
avgTaskDurationMs: tasksWithDuration > 0 ? Math.round(totalDurationMs / tasksWithDuration) : 0,
|
|
213
|
+
task, unmetDependencies: unmetDeps, ready: unmetDeps.length === 0,
|
|
214
|
+
...(task.deliverables?.length && {
|
|
215
|
+
deliverableStatus: { count: task.deliverables.length, staleCount: task.deliverables.filter((d) => d.stale).length },
|
|
216
|
+
}),
|
|
526
217
|
};
|
|
527
218
|
}
|
|
528
|
-
/**
|
|
529
|
-
* Submit a deliverable for a task. Auto-computes SHA-256 hash for file deliverables.
|
|
530
|
-
*/
|
|
531
219
|
submitDeliverable(planId, taskId, deliverable) {
|
|
532
|
-
const plan = this.
|
|
533
|
-
|
|
534
|
-
throw new Error(`Plan not found: ${planId}`);
|
|
535
|
-
const task = plan.tasks.find((t) => t.id === taskId);
|
|
536
|
-
if (!task)
|
|
537
|
-
throw new Error(`Task not found: ${taskId}`);
|
|
538
|
-
const entry = {
|
|
539
|
-
type: deliverable.type,
|
|
540
|
-
path: deliverable.path,
|
|
541
|
-
};
|
|
542
|
-
// Auto-compute hash for file deliverables
|
|
543
|
-
if (deliverable.type === 'file' && !deliverable.hash) {
|
|
544
|
-
try {
|
|
545
|
-
if (existsSync(deliverable.path)) {
|
|
546
|
-
const content = readFileSync(deliverable.path);
|
|
547
|
-
entry.hash = createHash('sha256').update(content).digest('hex');
|
|
548
|
-
}
|
|
549
|
-
}
|
|
550
|
-
catch {
|
|
551
|
-
// Graceful degradation — skip hash if file can't be read
|
|
552
|
-
}
|
|
553
|
-
}
|
|
554
|
-
else if (deliverable.hash) {
|
|
555
|
-
entry.hash = deliverable.hash;
|
|
556
|
-
}
|
|
220
|
+
const plan = this.requirePlan(planId);
|
|
221
|
+
const task = this.requireTask(plan, taskId);
|
|
557
222
|
if (!task.deliverables)
|
|
558
223
|
task.deliverables = [];
|
|
559
|
-
task.deliverables.push(
|
|
224
|
+
task.deliverables.push(createDeliverable(deliverable));
|
|
560
225
|
task.updatedAt = Date.now();
|
|
561
226
|
plan.updatedAt = Date.now();
|
|
562
227
|
this.save();
|
|
563
228
|
return task;
|
|
564
229
|
}
|
|
565
|
-
/**
|
|
566
|
-
* Verify all deliverables for a task.
|
|
567
|
-
* - file: checks existsSync + SHA-256 hash match
|
|
568
|
-
* - vault_entry: checks vault.get(path) non-null (requires vault instance)
|
|
569
|
-
* - url: skips (just records, no fetch)
|
|
570
|
-
*/
|
|
571
230
|
verifyDeliverables(planId, taskId, vault) {
|
|
572
|
-
const plan = this.
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
if (!task)
|
|
577
|
-
throw new Error(`Task not found: ${taskId}`);
|
|
578
|
-
const deliverables = task.deliverables ?? [];
|
|
579
|
-
let staleCount = 0;
|
|
580
|
-
const now = Date.now();
|
|
581
|
-
for (const d of deliverables) {
|
|
582
|
-
d.stale = false;
|
|
583
|
-
if (d.type === 'file') {
|
|
584
|
-
if (!existsSync(d.path)) {
|
|
585
|
-
d.stale = true;
|
|
586
|
-
staleCount++;
|
|
587
|
-
}
|
|
588
|
-
else if (d.hash) {
|
|
589
|
-
try {
|
|
590
|
-
const content = readFileSync(d.path);
|
|
591
|
-
const currentHash = createHash('sha256').update(content).digest('hex');
|
|
592
|
-
if (currentHash !== d.hash) {
|
|
593
|
-
d.stale = true;
|
|
594
|
-
staleCount++;
|
|
595
|
-
}
|
|
596
|
-
}
|
|
597
|
-
catch {
|
|
598
|
-
d.stale = true;
|
|
599
|
-
staleCount++;
|
|
600
|
-
}
|
|
601
|
-
}
|
|
602
|
-
d.verifiedAt = now;
|
|
603
|
-
}
|
|
604
|
-
else if (d.type === 'vault_entry') {
|
|
605
|
-
if (vault) {
|
|
606
|
-
const entry = vault.get(d.path);
|
|
607
|
-
if (!entry) {
|
|
608
|
-
d.stale = true;
|
|
609
|
-
staleCount++;
|
|
610
|
-
}
|
|
611
|
-
}
|
|
612
|
-
d.verifiedAt = now;
|
|
613
|
-
}
|
|
614
|
-
// url: skip — just record
|
|
615
|
-
}
|
|
231
|
+
const plan = this.requirePlan(planId);
|
|
232
|
+
const task = this.requireTask(plan, taskId);
|
|
233
|
+
const result = verifyDeliverablesLogic(task.deliverables ?? [], vault);
|
|
234
|
+
task.deliverables = result.deliverables;
|
|
616
235
|
plan.updatedAt = Date.now();
|
|
617
236
|
this.save();
|
|
618
|
-
return
|
|
237
|
+
return result;
|
|
619
238
|
}
|
|
620
|
-
// ─── Evidence & Verification ────────────────────────────────────
|
|
621
|
-
/**
|
|
622
|
-
* Submit evidence for a task acceptance criterion.
|
|
623
|
-
* Evidence is stored on the task and used by verifyTask() to check completeness.
|
|
624
|
-
*/
|
|
625
239
|
submitEvidence(planId, taskId, evidence) {
|
|
626
|
-
const plan = this.
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
const task = plan.tasks.find((t) => t.id === taskId);
|
|
630
|
-
if (!task)
|
|
631
|
-
throw new Error(`Task not found: ${taskId}`);
|
|
632
|
-
if (!task.evidence)
|
|
633
|
-
task.evidence = [];
|
|
634
|
-
task.evidence.push({
|
|
635
|
-
criterion: evidence.criterion,
|
|
636
|
-
content: evidence.content,
|
|
637
|
-
type: evidence.type,
|
|
638
|
-
submittedAt: Date.now(),
|
|
639
|
-
});
|
|
240
|
+
const plan = this.requirePlan(planId);
|
|
241
|
+
const task = this.requireTask(plan, taskId);
|
|
242
|
+
task.evidence = createEvidence(task.evidence ?? [], evidence);
|
|
640
243
|
task.updatedAt = Date.now();
|
|
641
244
|
plan.updatedAt = Date.now();
|
|
642
245
|
this.save();
|
|
643
246
|
return task;
|
|
644
247
|
}
|
|
645
|
-
/**
|
|
646
|
-
* Verify a task — check that evidence exists for all acceptance criteria
|
|
647
|
-
* and any reviews have passed.
|
|
648
|
-
* Returns verification status with details.
|
|
649
|
-
*/
|
|
650
248
|
verifyTask(planId, taskId) {
|
|
651
|
-
const plan = this.
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
throw new Error(`Task not found: ${taskId}`);
|
|
657
|
-
// Check evidence coverage
|
|
658
|
-
const criteria = task.acceptanceCriteria ?? [];
|
|
659
|
-
const evidencedCriteria = new Set((task.evidence ?? []).map((e) => e.criterion));
|
|
660
|
-
const missingCriteria = criteria.filter((c) => !evidencedCriteria.has(c));
|
|
661
|
-
// Check task-level reviews
|
|
662
|
-
const taskReviews = (plan.reviews ?? []).filter((r) => r.taskId === taskId);
|
|
663
|
-
let reviewStatus = 'no_reviews';
|
|
664
|
-
if (taskReviews.length > 0) {
|
|
665
|
-
const latest = taskReviews[taskReviews.length - 1];
|
|
666
|
-
reviewStatus = latest.outcome;
|
|
667
|
-
}
|
|
668
|
-
const verified = task.status === 'completed' &&
|
|
669
|
-
missingCriteria.length === 0 &&
|
|
670
|
-
(reviewStatus === 'approved' || reviewStatus === 'no_reviews');
|
|
671
|
-
if (verified !== task.verified) {
|
|
672
|
-
task.verified = verified;
|
|
249
|
+
const plan = this.requirePlan(planId);
|
|
250
|
+
const task = this.requireTask(plan, taskId);
|
|
251
|
+
const result = verifyTaskLogic(task, plan.reviews ?? []);
|
|
252
|
+
if (result.verified !== task.verified) {
|
|
253
|
+
task.verified = result.verified;
|
|
673
254
|
task.updatedAt = Date.now();
|
|
674
255
|
plan.updatedAt = Date.now();
|
|
675
256
|
this.save();
|
|
676
257
|
}
|
|
677
|
-
return {
|
|
258
|
+
return { ...result, task };
|
|
678
259
|
}
|
|
679
|
-
/**
|
|
680
|
-
* Verify an entire plan — check all tasks are in a final state,
|
|
681
|
-
* all verification-required tasks have evidence, no tasks stuck in_progress.
|
|
682
|
-
* Returns a validation report.
|
|
683
|
-
*/
|
|
684
260
|
verifyPlan(planId) {
|
|
685
|
-
|
|
686
|
-
if (!plan)
|
|
687
|
-
throw new Error(`Plan not found: ${planId}`);
|
|
688
|
-
const issues = [];
|
|
689
|
-
let verified = 0;
|
|
690
|
-
let completed = 0;
|
|
691
|
-
let skipped = 0;
|
|
692
|
-
let failed = 0;
|
|
693
|
-
let pending = 0;
|
|
694
|
-
let inProgress = 0;
|
|
695
|
-
for (const task of plan.tasks) {
|
|
696
|
-
switch (task.status) {
|
|
697
|
-
case 'completed':
|
|
698
|
-
completed++;
|
|
699
|
-
break;
|
|
700
|
-
case 'skipped':
|
|
701
|
-
skipped++;
|
|
702
|
-
break;
|
|
703
|
-
case 'failed':
|
|
704
|
-
failed++;
|
|
705
|
-
break;
|
|
706
|
-
case 'pending':
|
|
707
|
-
pending++;
|
|
708
|
-
break;
|
|
709
|
-
case 'in_progress':
|
|
710
|
-
inProgress++;
|
|
711
|
-
break;
|
|
712
|
-
}
|
|
713
|
-
if (task.verified)
|
|
714
|
-
verified++;
|
|
715
|
-
// Check for stuck tasks
|
|
716
|
-
if (task.status === 'in_progress') {
|
|
717
|
-
issues.push({ taskId: task.id, issue: 'Task stuck in in_progress state' });
|
|
718
|
-
}
|
|
719
|
-
if (task.status === 'pending') {
|
|
720
|
-
issues.push({ taskId: task.id, issue: 'Task still pending — not started' });
|
|
721
|
-
}
|
|
722
|
-
// Check evidence for completed tasks with acceptance criteria
|
|
723
|
-
if (task.status === 'completed' &&
|
|
724
|
-
task.acceptanceCriteria &&
|
|
725
|
-
task.acceptanceCriteria.length > 0) {
|
|
726
|
-
const evidencedCriteria = new Set((task.evidence ?? []).map((e) => e.criterion));
|
|
727
|
-
const missing = task.acceptanceCriteria.filter((c) => !evidencedCriteria.has(c));
|
|
728
|
-
if (missing.length > 0) {
|
|
729
|
-
issues.push({
|
|
730
|
-
taskId: task.id,
|
|
731
|
-
issue: `Missing evidence for ${missing.length} criteria: ${missing.join(', ')}`,
|
|
732
|
-
});
|
|
733
|
-
}
|
|
734
|
-
}
|
|
735
|
-
}
|
|
736
|
-
const valid = issues.length === 0 && pending === 0 && inProgress === 0;
|
|
737
|
-
return {
|
|
738
|
-
valid,
|
|
739
|
-
planId,
|
|
740
|
-
issues,
|
|
741
|
-
summary: {
|
|
742
|
-
total: plan.tasks.length,
|
|
743
|
-
completed,
|
|
744
|
-
skipped,
|
|
745
|
-
failed,
|
|
746
|
-
pending,
|
|
747
|
-
inProgress,
|
|
748
|
-
verified,
|
|
749
|
-
},
|
|
750
|
-
};
|
|
261
|
+
return verifyPlanLogic(planId, this.requirePlan(planId).tasks);
|
|
751
262
|
}
|
|
752
|
-
/**
|
|
753
|
-
* Auto-reconcile a plan — fast path for plans with minimal drift.
|
|
754
|
-
* Checks all tasks are in final state, generates reconciliation report automatically.
|
|
755
|
-
* Returns null if drift is too significant for auto-reconciliation (>2 non-completed tasks).
|
|
756
|
-
*/
|
|
757
|
-
autoReconcile(planId) {
|
|
758
|
-
const plan = this.get(planId);
|
|
759
|
-
if (!plan)
|
|
760
|
-
throw new Error(`Plan not found: ${planId}`);
|
|
761
|
-
if (plan.status !== 'executing' && plan.status !== 'validating')
|
|
762
|
-
throw new Error(`Cannot auto-reconcile plan in '${plan.status}' status — must be 'executing' or 'validating'`);
|
|
763
|
-
const completed = plan.tasks.filter((t) => t.status === 'completed').length;
|
|
764
|
-
const skipped = plan.tasks.filter((t) => t.status === 'skipped').length;
|
|
765
|
-
const failed = plan.tasks.filter((t) => t.status === 'failed').length;
|
|
766
|
-
const pending = plan.tasks.filter((t) => t.status === 'pending').length;
|
|
767
|
-
const inProgress = plan.tasks.filter((t) => t.status === 'in_progress').length;
|
|
768
|
-
// Can't auto-reconcile if tasks are still in progress
|
|
769
|
-
if (inProgress > 0)
|
|
770
|
-
return null;
|
|
771
|
-
// Can't auto-reconcile if too many non-completed tasks
|
|
772
|
-
if (pending + failed > 2)
|
|
773
|
-
return null;
|
|
774
|
-
const driftItems = [];
|
|
775
|
-
for (const task of plan.tasks) {
|
|
776
|
-
if (task.status === 'skipped') {
|
|
777
|
-
driftItems.push({
|
|
778
|
-
type: 'skipped',
|
|
779
|
-
description: `Task '${task.title}' was skipped`,
|
|
780
|
-
impact: 'medium',
|
|
781
|
-
rationale: 'Task not executed during plan implementation',
|
|
782
|
-
});
|
|
783
|
-
}
|
|
784
|
-
else if (task.status === 'failed') {
|
|
785
|
-
driftItems.push({
|
|
786
|
-
type: 'modified',
|
|
787
|
-
description: `Task '${task.title}' failed`,
|
|
788
|
-
impact: 'high',
|
|
789
|
-
rationale: 'Task execution failed',
|
|
790
|
-
});
|
|
791
|
-
}
|
|
792
|
-
else if (task.status === 'pending') {
|
|
793
|
-
driftItems.push({
|
|
794
|
-
type: 'skipped',
|
|
795
|
-
description: `Task '${task.title}' was never started`,
|
|
796
|
-
impact: 'low',
|
|
797
|
-
rationale: 'Task left in pending state',
|
|
798
|
-
});
|
|
799
|
-
}
|
|
800
|
-
}
|
|
801
|
-
return this.reconcile(planId, {
|
|
802
|
-
actualOutcome: `Auto-reconciled: ${completed}/${plan.tasks.length} tasks completed, ${skipped} skipped, ${failed} failed`,
|
|
803
|
-
driftItems,
|
|
804
|
-
reconciledBy: 'auto',
|
|
805
|
-
});
|
|
806
|
-
}
|
|
807
|
-
/**
|
|
808
|
-
* Generate a review prompt for spec compliance checking.
|
|
809
|
-
* Used by subagent dispatch — the controller generates the prompt, a subagent executes it.
|
|
810
|
-
*/
|
|
811
263
|
generateReviewSpec(planId, taskId) {
|
|
812
|
-
const plan = this.
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
const task = plan.tasks.find((t) => t.id === taskId);
|
|
816
|
-
if (!task)
|
|
817
|
-
throw new Error(`Task not found: ${taskId}`);
|
|
818
|
-
const criteria = task.acceptanceCriteria?.length
|
|
819
|
-
? `\n\nAcceptance Criteria:\n${task.acceptanceCriteria.map((c, i) => `${i + 1}. ${c}`).join('\n')}`
|
|
820
|
-
: '';
|
|
821
|
-
const prompt = [
|
|
822
|
-
`# Spec Compliance Review`,
|
|
823
|
-
``,
|
|
824
|
-
`## Task: ${task.title}`,
|
|
825
|
-
`**Description:** ${task.description}`,
|
|
826
|
-
`**Plan Objective:** ${plan.objective}${criteria}`,
|
|
827
|
-
``,
|
|
828
|
-
`## Review Checklist`,
|
|
829
|
-
`1. Does the implementation match the task description?`,
|
|
830
|
-
`2. Are all acceptance criteria satisfied?`,
|
|
831
|
-
`3. Does it align with the plan's overall objective?`,
|
|
832
|
-
`4. Are there any spec deviations?`,
|
|
833
|
-
``,
|
|
834
|
-
`Provide: outcome (approved/rejected/needs_changes) and detailed comments.`,
|
|
835
|
-
].join('\n');
|
|
836
|
-
return { prompt, task, plan };
|
|
264
|
+
const plan = this.requirePlan(planId);
|
|
265
|
+
const task = this.requireTask(plan, taskId);
|
|
266
|
+
return { prompt: buildSpecReviewPrompt(task, plan.objective), task, plan };
|
|
837
267
|
}
|
|
838
|
-
/**
|
|
839
|
-
* Generate a review prompt for code quality checking.
|
|
840
|
-
*/
|
|
841
268
|
generateReviewQuality(planId, taskId) {
|
|
842
|
-
const plan = this.
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
const task = plan.tasks.find((t) => t.id === taskId);
|
|
846
|
-
if (!task)
|
|
847
|
-
throw new Error(`Task not found: ${taskId}`);
|
|
848
|
-
const prompt = [
|
|
849
|
-
`# Code Quality Review`,
|
|
850
|
-
``,
|
|
851
|
-
`## Task: ${task.title}`,
|
|
852
|
-
`**Description:** ${task.description}`,
|
|
853
|
-
``,
|
|
854
|
-
`## Quality Checklist`,
|
|
855
|
-
`1. **Correctness** — Does it work as intended?`,
|
|
856
|
-
`2. **Security** — No injection, XSS, or OWASP top 10 vulnerabilities?`,
|
|
857
|
-
`3. **Performance** — No unnecessary allocations, N+1 queries, or blocking calls?`,
|
|
858
|
-
`4. **Maintainability** — Clear naming, appropriate abstractions, documented intent?`,
|
|
859
|
-
`5. **Testing** — Adequate test coverage for the changes?`,
|
|
860
|
-
`6. **Error Handling** — Graceful degradation, no swallowed errors?`,
|
|
861
|
-
`7. **Conventions** — Follows project coding standards?`,
|
|
862
|
-
``,
|
|
863
|
-
`Provide: outcome (approved/rejected/needs_changes) and detailed comments.`,
|
|
864
|
-
].join('\n');
|
|
865
|
-
return { prompt, task, plan };
|
|
269
|
+
const plan = this.requirePlan(planId);
|
|
270
|
+
const task = this.requireTask(plan, taskId);
|
|
271
|
+
return { prompt: buildQualityReviewPrompt(task), task, plan };
|
|
866
272
|
}
|
|
867
|
-
/**
|
|
868
|
-
* Archive completed plans — transitions them to 'archived' status.
|
|
869
|
-
* If olderThanDays is provided, only archives plans older than that.
|
|
870
|
-
* Returns the archived plans.
|
|
871
|
-
*/
|
|
872
273
|
archive(olderThanDays) {
|
|
873
274
|
const cutoff = olderThanDays !== undefined
|
|
874
|
-
? Date.now() - olderThanDays * 24 * 60 * 60 * 1000
|
|
875
|
-
: Date.now() + 1; // +1ms so archive() with no args archives all completed plans
|
|
275
|
+
? Date.now() - olderThanDays * 24 * 60 * 60 * 1000 : Date.now() + 1;
|
|
876
276
|
const toArchive = this.store.plans.filter((p) => p.status === 'completed' && p.updatedAt < cutoff);
|
|
877
277
|
for (const plan of toArchive) {
|
|
878
278
|
plan.status = 'archived';
|
|
879
279
|
plan.updatedAt = Date.now();
|
|
880
280
|
}
|
|
881
|
-
if (toArchive.length > 0)
|
|
281
|
+
if (toArchive.length > 0)
|
|
882
282
|
this.save();
|
|
883
|
-
}
|
|
884
283
|
return toArchive;
|
|
885
284
|
}
|
|
886
|
-
/**
|
|
887
|
-
* Get statistics about all plans.
|
|
888
|
-
*/
|
|
889
285
|
stats() {
|
|
890
286
|
const plans = this.store.plans;
|
|
891
|
-
const byStatus = {
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
approved: 0,
|
|
895
|
-
executing: 0,
|
|
896
|
-
validating: 0,
|
|
897
|
-
reconciling: 0,
|
|
898
|
-
completed: 0,
|
|
899
|
-
archived: 0,
|
|
900
|
-
};
|
|
901
|
-
const tasksByStatus = {
|
|
902
|
-
pending: 0,
|
|
903
|
-
in_progress: 0,
|
|
904
|
-
completed: 0,
|
|
905
|
-
skipped: 0,
|
|
906
|
-
failed: 0,
|
|
907
|
-
};
|
|
287
|
+
const byStatus = { brainstorming: 0, draft: 0, approved: 0, executing: 0,
|
|
288
|
+
validating: 0, reconciling: 0, completed: 0, archived: 0 };
|
|
289
|
+
const tasksByStatus = { pending: 0, in_progress: 0, completed: 0, skipped: 0, failed: 0 };
|
|
908
290
|
let totalTasks = 0;
|
|
909
291
|
for (const p of plans) {
|
|
910
292
|
byStatus[p.status]++;
|
|
911
293
|
totalTasks += p.tasks.length;
|
|
912
|
-
for (const t of p.tasks)
|
|
294
|
+
for (const t of p.tasks)
|
|
913
295
|
tasksByStatus[t.status]++;
|
|
914
|
-
}
|
|
915
296
|
}
|
|
916
297
|
return {
|
|
917
|
-
total: plans.length,
|
|
918
|
-
byStatus,
|
|
298
|
+
total: plans.length, byStatus, totalTasks, tasksByStatus,
|
|
919
299
|
avgTasksPerPlan: plans.length > 0 ? Math.round((totalTasks / plans.length) * 100) / 100 : 0,
|
|
920
|
-
totalTasks,
|
|
921
|
-
tasksByStatus,
|
|
922
300
|
};
|
|
923
301
|
}
|
|
924
|
-
// ─── Grading ──────────────────────────────────────────────────────
|
|
925
|
-
/**
|
|
926
|
-
* Grade a plan using gap analysis with severity-weighted scoring.
|
|
927
|
-
* Ported from Salvador MCP's multi-pass grading engine.
|
|
928
|
-
*
|
|
929
|
-
* 6 built-in passes + optional custom passes (domain-specific checks).
|
|
930
|
-
*
|
|
931
|
-
* Scoring:
|
|
932
|
-
* - Each gap has a severity (critical=30, major=15, minor=2, info=0)
|
|
933
|
-
* - Deductions are per-category with optional caps
|
|
934
|
-
* - Iteration leniency: minor gaps free on iter 1, half on iter 2, full on 3+
|
|
935
|
-
* - Score = max(0, 100 - deductions)
|
|
936
|
-
*
|
|
937
|
-
* Grade thresholds: A+=95, A=90, B=80, C=70, D=60, F=<60
|
|
938
|
-
*/
|
|
939
302
|
grade(planId) {
|
|
940
|
-
const plan = this.
|
|
941
|
-
if (!plan)
|
|
942
|
-
throw new Error(`Plan not found: ${planId}`);
|
|
943
|
-
// Run 6-pass gap analysis
|
|
303
|
+
const plan = this.requirePlan(planId);
|
|
944
304
|
const gaps = runGapAnalysis(plan, this.gapOptions);
|
|
945
|
-
|
|
946
|
-
if (this.hasCircularDependencies(plan)) {
|
|
305
|
+
if (hasCircularDependencies(plan.tasks)) {
|
|
947
306
|
gaps.push({
|
|
948
|
-
id: `gap_${Date.now()}_circ`,
|
|
949
|
-
severity: 'critical',
|
|
950
|
-
category: 'structure',
|
|
307
|
+
id: `gap_${Date.now()}_circ`, severity: 'critical', category: 'structure',
|
|
951
308
|
description: 'Circular dependencies detected among tasks.',
|
|
952
309
|
recommendation: 'Remove circular dependency chains so tasks can be executed in order.',
|
|
953
|
-
location: 'tasks',
|
|
954
|
-
_trigger: 'circular_dependencies',
|
|
310
|
+
location: 'tasks', _trigger: 'circular_dependencies',
|
|
955
311
|
});
|
|
956
312
|
}
|
|
957
|
-
// Iteration = number of previous checks + 1
|
|
958
313
|
const iteration = plan.checks.length + 1;
|
|
959
314
|
const score = calculateScore(gaps, iteration);
|
|
960
|
-
const grade = this.scoreToGrade(score);
|
|
961
315
|
const check = {
|
|
962
316
|
checkId: `chk-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`,
|
|
963
|
-
planId,
|
|
964
|
-
grade,
|
|
965
|
-
score,
|
|
966
|
-
gaps,
|
|
967
|
-
iteration,
|
|
968
|
-
checkedAt: Date.now(),
|
|
317
|
+
planId, grade: scoreToGrade(score), score, gaps, iteration, checkedAt: Date.now(),
|
|
969
318
|
};
|
|
970
319
|
plan.checks.push(check);
|
|
971
320
|
plan.latestCheck = check;
|
|
@@ -973,88 +322,15 @@ export class Planner {
|
|
|
973
322
|
this.save();
|
|
974
323
|
return check;
|
|
975
324
|
}
|
|
976
|
-
/**
|
|
977
|
-
* Get the latest check for a plan.
|
|
978
|
-
*/
|
|
979
325
|
getLatestCheck(planId) {
|
|
980
|
-
|
|
981
|
-
if (!plan)
|
|
982
|
-
throw new Error(`Plan not found: ${planId}`);
|
|
983
|
-
return plan.latestCheck ?? null;
|
|
326
|
+
return this.requirePlan(planId).latestCheck ?? null;
|
|
984
327
|
}
|
|
985
|
-
/**
|
|
986
|
-
* Get all checks for a plan (history).
|
|
987
|
-
*/
|
|
988
328
|
getCheckHistory(planId) {
|
|
989
|
-
|
|
990
|
-
if (!plan)
|
|
991
|
-
throw new Error(`Plan not found: ${planId}`);
|
|
992
|
-
return [...plan.checks];
|
|
329
|
+
return [...this.requirePlan(planId).checks];
|
|
993
330
|
}
|
|
994
|
-
/**
|
|
995
|
-
* Auto-grade: grade the plan and return whether it meets a target grade.
|
|
996
|
-
*/
|
|
997
331
|
meetsGrade(planId, targetGrade) {
|
|
998
332
|
const check = this.grade(planId);
|
|
999
|
-
|
|
1000
|
-
return { meets: check.score >= targetScore, check };
|
|
1001
|
-
}
|
|
1002
|
-
// ─── Grading Helpers ──────────────────────────────────────────────
|
|
1003
|
-
scoreToGrade(score) {
|
|
1004
|
-
if (score >= 95)
|
|
1005
|
-
return 'A+';
|
|
1006
|
-
if (score >= 90)
|
|
1007
|
-
return 'A';
|
|
1008
|
-
if (score >= 80)
|
|
1009
|
-
return 'B';
|
|
1010
|
-
if (score >= 70)
|
|
1011
|
-
return 'C';
|
|
1012
|
-
if (score >= 60)
|
|
1013
|
-
return 'D';
|
|
1014
|
-
return 'F';
|
|
1015
|
-
}
|
|
1016
|
-
gradeToMinScore(grade) {
|
|
1017
|
-
switch (grade) {
|
|
1018
|
-
case 'A+':
|
|
1019
|
-
return 95;
|
|
1020
|
-
case 'A':
|
|
1021
|
-
return 90;
|
|
1022
|
-
case 'B':
|
|
1023
|
-
return 80;
|
|
1024
|
-
case 'C':
|
|
1025
|
-
return 70;
|
|
1026
|
-
case 'D':
|
|
1027
|
-
return 60;
|
|
1028
|
-
case 'F':
|
|
1029
|
-
return 0;
|
|
1030
|
-
}
|
|
1031
|
-
}
|
|
1032
|
-
hasCircularDependencies(plan) {
|
|
1033
|
-
const visited = new Set();
|
|
1034
|
-
const inStack = new Set();
|
|
1035
|
-
const taskMap = new Map(plan.tasks.map((t) => [t.id, t]));
|
|
1036
|
-
const dfs = (taskId) => {
|
|
1037
|
-
if (inStack.has(taskId))
|
|
1038
|
-
return true;
|
|
1039
|
-
if (visited.has(taskId))
|
|
1040
|
-
return false;
|
|
1041
|
-
visited.add(taskId);
|
|
1042
|
-
inStack.add(taskId);
|
|
1043
|
-
const task = taskMap.get(taskId);
|
|
1044
|
-
if (task?.dependsOn) {
|
|
1045
|
-
for (const dep of task.dependsOn) {
|
|
1046
|
-
if (dfs(dep))
|
|
1047
|
-
return true;
|
|
1048
|
-
}
|
|
1049
|
-
}
|
|
1050
|
-
inStack.delete(taskId);
|
|
1051
|
-
return false;
|
|
1052
|
-
};
|
|
1053
|
-
for (const task of plan.tasks) {
|
|
1054
|
-
if (dfs(task.id))
|
|
1055
|
-
return true;
|
|
1056
|
-
}
|
|
1057
|
-
return false;
|
|
333
|
+
return { meets: check.score >= gradeToMinScore(targetGrade), check };
|
|
1058
334
|
}
|
|
1059
335
|
}
|
|
1060
336
|
//# sourceMappingURL=planner.js.map
|