@mindrian_os/cli 2.0.0-beta.3 → 2.0.0-beta.33
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/.claude-plugin/plugin.json +21 -0
- package/.mcp.json +14 -0
- package/CHANGELOG.md +497 -0
- package/agents/analogy-query-fetcher.md +119 -0
- package/agents/brain-query.md +91 -0
- package/agents/competitor-watch-fetcher.md +94 -0
- package/agents/framework-runner.md +258 -0
- package/agents/grading.md +216 -0
- package/agents/grant-reviewer.md +191 -0
- package/agents/investor.md +141 -0
- package/agents/larry-extended.md +215 -0
- package/agents/meeting-perspective-extractor.md +94 -0
- package/agents/opportunity-scanner.md +105 -0
- package/agents/persona-analyst.md +201 -0
- package/agents/research.md +103 -0
- package/agents/reverse-salient-agent.md +42 -0
- package/agents/vault-section-reviewer.md +107 -0
- package/assets/banner-showcase.html +537 -0
- package/assets/fonts/BebasNeue-Regular.ttf +0 -0
- package/assets/fonts/Inter-Bold.ttf +0 -0
- package/assets/fonts/Inter-Regular.ttf +0 -0
- package/assets/logo.svg +10 -0
- package/bin/local-chain-recommender.cjs +256 -0
- package/bin/mindrian-brain-mcp-client.cjs +319 -0
- package/bin/mindrian-mcp-server.cjs +445 -0
- package/bin/mindrian-mcp-shim.cjs +100 -0
- package/bin/mindrian-tools.cjs +784 -0
- package/commands/act.md +529 -0
- package/commands/admin.md +439 -0
- package/commands/agentshield.md +49 -0
- package/commands/analyze-needs.md +78 -0
- package/commands/analyze-systems.md +73 -0
- package/commands/analyze-timing.md +98 -0
- package/commands/auto-explore.md +85 -0
- package/commands/beautiful-question.md +76 -0
- package/commands/bono.md +152 -0
- package/commands/brain-derive.md +137 -0
- package/commands/build-knowledge.md +121 -0
- package/commands/build-thesis.md +94 -0
- package/commands/causal.md +269 -0
- package/commands/challenge-assumptions.md +78 -0
- package/commands/compare-ventures.md +117 -0
- package/commands/correct-reference-now.md +88 -0
- package/commands/dashboard.md +144 -0
- package/commands/deck.md +117 -0
- package/commands/deep-grade.md +214 -0
- package/commands/diagnose.md +131 -0
- package/commands/diagnostics.md +195 -0
- package/commands/dial-memory-refresh.md +113 -0
- package/commands/diffusion.md +129 -0
- package/commands/discover.md +221 -0
- package/commands/doctor.md +271 -0
- package/commands/dogfood-flush.md +130 -0
- package/commands/dominant-designs.md +76 -0
- package/commands/eureka.md +315 -0
- package/commands/explain-decision.md +135 -0
- package/commands/explore-domains.md +78 -0
- package/commands/explore-futures.md +67 -0
- package/commands/explore-opportunity.md +172 -0
- package/commands/explore-trends.md +78 -0
- package/commands/export.md +128 -0
- package/commands/feynman-timeline-refresh.md +102 -0
- package/commands/file-meeting.md +1195 -0
- package/commands/find-analogies.md +369 -0
- package/commands/find-bottlenecks.md +108 -0
- package/commands/find-connections.md +111 -0
- package/commands/funding.md +114 -0
- package/commands/futures.md +128 -0
- package/commands/grade-grant.md +297 -0
- package/commands/grade.md +257 -0
- package/commands/graph.md +218 -0
- package/commands/hat-briefing.md +163 -0
- package/commands/heal.md +72 -0
- package/commands/help.md +176 -0
- package/commands/hmi-status.md +75 -0
- package/commands/ignite.md +229 -0
- package/commands/ingest-methodology.md +128 -0
- package/commands/intel-pipeline.md +133 -0
- package/commands/jtbd.md +262 -0
- package/commands/leadership.md +113 -0
- package/commands/lean-canvas.md +74 -0
- package/commands/macro-trends.md +88 -0
- package/commands/map-unknowns.md +76 -0
- package/commands/memory-cortex-reach.md +79 -0
- package/commands/memory.md +213 -0
- package/commands/models.md +200 -0
- package/commands/mos-reason.md +489 -0
- package/commands/mos.md +162 -0
- package/commands/mullins.md +155 -0
- package/commands/mva-brief.md +86 -0
- package/commands/mva-option.md +119 -0
- package/commands/mva-report.md +76 -0
- package/commands/new-project.md +633 -0
- package/commands/new-surface.md +116 -0
- package/commands/onboard.md +489 -0
- package/commands/operator.md +168 -0
- package/commands/opportunities.md +177 -0
- package/commands/organize.md +74 -0
- package/commands/persona.md +276 -0
- package/commands/pipeline.md +167 -0
- package/commands/present.md +125 -0
- package/commands/publish.md +227 -0
- package/commands/pws-brain.md +154 -0
- package/commands/qualify-opportunity.md +113 -0
- package/commands/query.md +70 -0
- package/commands/radar.md +110 -0
- package/commands/reanalyze.md +124 -0
- package/commands/research.md +467 -0
- package/commands/room.md +385 -0
- package/commands/rooms.md +682 -0
- package/commands/root-cause.md +77 -0
- package/commands/rs-experts.md +153 -0
- package/commands/rs-explain.md +134 -0
- package/commands/rs-fetch.md +119 -0
- package/commands/rs-thesis.md +131 -0
- package/commands/scenario-plan.md +80 -0
- package/commands/scheduled-tasks.md +308 -0
- package/commands/score-innovation.md +78 -0
- package/commands/scout.md +379 -0
- package/commands/setup.md +658 -0
- package/commands/show.md +71 -0
- package/commands/skill.md +108 -0
- package/commands/snapshot.md +179 -0
- package/commands/speakers.md +118 -0
- package/commands/splash.md +55 -0
- package/commands/stance.md +91 -0
- package/commands/status.md +109 -0
- package/commands/structure-argument.md +82 -0
- package/commands/suggest-next.md +141 -0
- package/commands/systems-thinking.md +224 -0
- package/commands/think-hats.md +86 -0
- package/commands/trending-to-absurd.md +151 -0
- package/commands/update.md +256 -0
- package/commands/user-needs.md +77 -0
- package/commands/validate.md +77 -0
- package/commands/value-proposition.md +116 -0
- package/commands/vault.md +330 -0
- package/commands/visualize.md +71 -0
- package/commands/whitespace.md +541 -0
- package/commands/wiki.md +96 -0
- package/data/ROOM.md +47 -0
- package/data/brain-census.generated.json +2617 -0
- package/data/brain-orchestration-projection.json +4973 -0
- package/data/brain-packet-schema.json +545 -0
- package/data/brain-surface-contract.json +32 -0
- package/data/capability-ledger.json +391 -0
- package/data/capability-map-registry.json +23 -0
- package/data/command-registry.json +2433 -0
- package/data/connector-coverage-ledger.json +1618 -0
- package/data/connector-out-of-spine-allowlist.json +71 -0
- package/data/connector-registry.json +3637 -0
- package/data/corpus-stats-source.json +11 -0
- package/data/corpus-stats.generated.json +10 -0
- package/data/cross-domain-analogues.json +15 -0
- package/data/deck-aliases.json +6 -0
- package/data/deck-styles.json +85 -0
- package/data/deployment-surfaces.json +69 -0
- package/data/dispatch-framework-map.json +18 -0
- package/data/doctor-modules.json +206 -0
- package/data/dual-graph-health-baseline.json +12 -0
- package/data/eureka-critic-tags.json +32 -0
- package/data/first-reward-surfaces.json +66 -0
- package/data/first-touch-surfaces.json +60 -0
- package/data/flagship-floor-set.json +37 -0
- package/data/framework-names.json +121 -0
- package/data/grant-rubric-fixtures/early-stage-pre-seed.json +18 -0
- package/data/grant-rubric-fixtures/early-stage-round-a.json +18 -0
- package/data/grant-rubric-fixtures/early-stage-seed.json +18 -0
- package/data/grant-rubric-fixtures/hybrid-seed-incentive.json +18 -0
- package/data/grant-rubric-fixtures/rd-fund.json +18 -0
- package/data/grant-rubric-fixtures/regional-innovation-incubators.json +18 -0
- package/data/grant-rubric-fixtures/technology-incubators.json +18 -0
- package/data/grant-rubric-fixtures/tnufa.json +167 -0
- package/data/grant-rubric-schema.json +33 -0
- package/data/harness-fixtures/converged-room/.context/ROOM.md +13 -0
- package/data/harness-fixtures/converged-room/.intelligence/ROOM.md +13 -0
- package/data/harness-fixtures/converged-room/.snapshots/ROOM.md +13 -0
- package/data/harness-fixtures/converged-room/MINTO.md +19 -0
- package/data/harness-fixtures/converged-room/STATE.md +73 -0
- package/data/harness-fixtures/converged-room/USER.md +23 -0
- package/data/harness-fixtures/converged-room/assets/ROOM.md +13 -0
- package/data/harness-fixtures/converged-room/business-model/CONTEXT.md +32 -0
- package/data/harness-fixtures/converged-room/business-model/ROOM.md +36 -0
- package/data/harness-fixtures/converged-room/competitive-analysis/CONTEXT.md +33 -0
- package/data/harness-fixtures/converged-room/competitive-analysis/ROOM.md +35 -0
- package/data/harness-fixtures/converged-room/financial-model/CONTEXT.md +30 -0
- package/data/harness-fixtures/converged-room/financial-model/ROOM.md +35 -0
- package/data/harness-fixtures/converged-room/funding/CONTEXT.md +41 -0
- package/data/harness-fixtures/converged-room/funding/ROOM.md +35 -0
- package/data/harness-fixtures/converged-room/legal-ip/CONTEXT.md +30 -0
- package/data/harness-fixtures/converged-room/legal-ip/ROOM.md +35 -0
- package/data/harness-fixtures/converged-room/market-analysis/CONTEXT.md +32 -0
- package/data/harness-fixtures/converged-room/market-analysis/ROOM.md +35 -0
- package/data/harness-fixtures/converged-room/opportunity-bank/CONTEXT.md +39 -0
- package/data/harness-fixtures/converged-room/opportunity-bank/ROOM.md +36 -0
- package/data/harness-fixtures/converged-room/problem-definition/CONTEXT.md +32 -0
- package/data/harness-fixtures/converged-room/problem-definition/ROOM.md +36 -0
- package/data/harness-fixtures/converged-room/references/ROOM.md +13 -0
- package/data/harness-fixtures/converged-room/references/SECTION-SCHEMA.md +213 -0
- package/data/harness-fixtures/converged-room/references/SUB-SCHEMAS.md +91 -0
- package/data/harness-fixtures/converged-room/solution-design/CONTEXT.md +36 -0
- package/data/harness-fixtures/converged-room/solution-design/ROOM.md +36 -0
- package/data/harness-fixtures/converged-room/strategy/CONTEXT.md +35 -0
- package/data/harness-fixtures/converged-room/strategy/ROOM.md +35 -0
- package/data/harness-fixtures/converged-room/team/ROOM.md +13 -0
- package/data/harness-fixtures/converged-room/team-execution/CONTEXT.md +36 -0
- package/data/harness-fixtures/converged-room/team-execution/ROOM.md +35 -0
- package/data/harness-manifest.json +74 -0
- package/data/harness-policies/CONTEXT.md +55 -0
- package/data/harness-policies/_schema.json +42 -0
- package/data/harness-policies/contract-parity-larry.json +63 -0
- package/data/harness-policies/gate-card-fire.json +13 -0
- package/data/harness-policies/gate-graph-derive-health.json +13 -0
- package/data/harness-policies/gate-shape-declaration.json +13 -0
- package/data/harness-policies/gate-tool-honesty.json +13 -0
- package/data/harness-policies/gate-worktree-hygiene.json +13 -0
- package/data/harness-policies/memory-write-policy.json +28 -0
- package/data/harness-policies/registry-drift.json +13 -0
- package/data/harness-policies/release-payload-ceiling.json +13 -0
- package/data/harness-policies/voice-backend-noun-free.json +13 -0
- package/data/harness-policies/voice-fork-as-card.json +13 -0
- package/data/harness-policies/voice-glyph-present.json +13 -0
- package/data/harness-policies/voice-hyphens-only.json +13 -0
- package/data/help-groups.json +236 -0
- package/data/hitl-shape-backfill.json +777 -0
- package/data/hitl-shape-declaration-fixtures/invalid-bad-shape.json +5 -0
- package/data/hitl-shape-declaration-fixtures/invalid-missing-why.json +4 -0
- package/data/hitl-shape-declaration-fixtures/invalid-ranker-contradiction.json +6 -0
- package/data/hitl-shape-declaration-fixtures/invalid-skill-gap.json +3 -0
- package/data/hitl-shape-declaration-fixtures/valid-multi-stage.json +7 -0
- package/data/hitl-shape-declaration-fixtures/valid-single-shape.json +5 -0
- package/data/hitl-shape-declaration-fixtures/valid-skill-exempt.json +5 -0
- package/data/hitl-shape-declaration-schema.json +34 -0
- package/data/hitl-stages-fixtures/bono.json +10 -0
- package/data/hitl-stages-fixtures/pipeline-1.json +9 -0
- package/data/hitl-stages-fixtures/pipeline-2.json +9 -0
- package/data/hitl-stages-fixtures/pipeline-3.json +10 -0
- package/data/hitl-stages-fixtures/research.json +10 -0
- package/data/hitl-stages-fixtures/rs-convergence.json +10 -0
- package/data/hitl-stages-fixtures/rs-family.json +11 -0
- package/data/hitl-stages-fixtures/rs-reverse-salient.json +10 -0
- package/data/hitl-stages-fixtures/trending-to-absurd.json +11 -0
- package/data/hitl-stages-schema.json +26 -0
- package/data/mcp-tool-connectors.json +464 -0
- package/data/mva-agent-prompts.json +31 -0
- package/data/mva-deck-template.html +15 -0
- package/data/mva-heuristic-keywords.json +35 -0
- package/data/orchestration-command-ledger.json +576 -0
- package/data/orchestration-unwired-allowlist.json +6 -0
- package/data/portfolio-ahp-matrix.json +16 -0
- package/data/publish-needs.json +76 -0
- package/data/render-coverage-registry.json +1152 -0
- package/data/research-sources.json +58 -0
- package/data/roadmap-type-chains.json +28 -0
- package/data/room-blueprints.json +156 -0
- package/data/subagent-dispatch-grants.json +150 -0
- package/hooks/hooks.json +493 -0
- package/hooks/run-hook.cmd +64 -0
- package/lib/__init__.py +0 -0
- package/lib/__pycache__/__init__.cpython-312.pyc +0 -0
- package/lib/agents/auto-explore-agent.cjs +1163 -0
- package/lib/agents/mva/brain-classic-traps.cjs +55 -0
- package/lib/agents/mva/brain-cross-domain.cjs +79 -0
- package/lib/agents/mva/brain-similar-ventures.cjs +93 -0
- package/lib/agents/mva/dashboard-graph-neighborhood.cjs +72 -0
- package/lib/agents/mva/index.cjs +42 -0
- package/lib/agents/mva/six-hats-red-black.cjs +137 -0
- package/lib/agents/mva/tavily-funding-scan.cjs +147 -0
- package/lib/agents/mva/test-all-six-agents.cjs +467 -0
- package/lib/agents/reverse-salient-agent.cjs +757 -0
- package/lib/agents/tension-hook-agent.cjs +586 -0
- package/lib/brain/ROOM.md +44 -0
- package/lib/brain/chain-recommender.cjs +784 -0
- package/lib/brain/curation-batch.cjs +519 -0
- package/lib/brain/framework-chain-slice.cjs +252 -0
- package/lib/brain/hypergraph-event-schema.cjs +184 -0
- package/lib/chat/chat-context.js +185 -0
- package/lib/chat/chat-panel.js +721 -0
- package/lib/chat/fabric-chat.cjs +288 -0
- package/lib/chat/generative-tools.js +219 -0
- package/lib/conversation/ROOM.md +39 -0
- package/lib/conversation/classifier-rules.json +38 -0
- package/lib/conversation/classifier.cjs +264 -0
- package/lib/conversation/operator-epistemic-cap.test.cjs +156 -0
- package/lib/conversation/operator.cjs +492 -0
- package/lib/conversation/operator.test.cjs +160 -0
- package/lib/copy/115-spec-strings.cjs +55 -0
- package/lib/core/__init__.py +0 -0
- package/lib/core/__nav-stub.cjs +14 -0
- package/lib/core/__pycache__/__init__.cpython-312.pyc +0 -0
- package/lib/core/__pycache__/rs-math.cpython-312.pyc +0 -0
- package/lib/core/__pycache__/rs_cache.cpython-312.pyc +0 -0
- package/lib/core/__pycache__/rs_corpus.cpython-312.pyc +0 -0
- package/lib/core/__pycache__/rs_corpus_exclude.cpython-312.pyc +0 -0
- package/lib/core/__pycache__/rs_hybrid.cpython-312.pyc +0 -0
- package/lib/core/__pycache__/rs_math.cpython-312.pyc +0 -0
- package/lib/core/__pycache__/rs_rooms.cpython-312.pyc +0 -0
- package/lib/core/abstraction-gate.cjs +133 -0
- package/lib/core/act-jtbd-blurb.cjs +231 -0
- package/lib/core/artifact-id.cjs +148 -0
- package/lib/core/asset-ops.cjs +151 -0
- package/lib/core/auto-commit-throttle.cjs +129 -0
- package/lib/core/bearer-token.cjs +199 -0
- package/lib/core/behavioral/observation-schema.cjs +86 -0
- package/lib/core/bono/cell-fanout.cjs +305 -0
- package/lib/core/bono/debate-composition.cjs +495 -0
- package/lib/core/bono/hat-governance.cjs +367 -0
- package/lib/core/bono/persona-research.cjs +288 -0
- package/lib/core/bono/reviewer-governance.cjs +331 -0
- package/lib/core/brain-client.cjs +2621 -0
- package/lib/core/brain-derivation-prompts.cjs +331 -0
- package/lib/core/brain-derivation-queue.cjs +535 -0
- package/lib/core/brain-derivation.cjs +740 -0
- package/lib/core/brain-md-schema.cjs +528 -0
- package/lib/core/brain-md-staleness.cjs +357 -0
- package/lib/core/brain-response-sanitize.cjs +396 -0
- package/lib/core/breakthrough/canary.cjs +134 -0
- package/lib/core/breakthrough/canary.test.cjs +136 -0
- package/lib/core/breakthrough/detectors.cjs +359 -0
- package/lib/core/breakthrough/detectors.test.cjs +333 -0
- package/lib/core/breakthrough/ethics-fence.cjs +129 -0
- package/lib/core/breakthrough/ethics-fence.test.cjs +178 -0
- package/lib/core/breakthrough/resurfacing.cjs +150 -0
- package/lib/core/breakthrough/resurfacing.test.cjs +233 -0
- package/lib/core/breakthrough/review-queue.cjs +168 -0
- package/lib/core/breakthrough/review-queue.test.cjs +160 -0
- package/lib/core/breakthrough/scanner-d17-d18.test.cjs +229 -0
- package/lib/core/breakthrough/scanner.cjs +435 -0
- package/lib/core/breakthrough/scanner.test.cjs +267 -0
- package/lib/core/breakthrough/schema.cjs +180 -0
- package/lib/core/breakthrough/schema.test.cjs +256 -0
- package/lib/core/breakthrough/scoring.cjs +293 -0
- package/lib/core/breakthrough/scoring.test.cjs +423 -0
- package/lib/core/breakthrough/verb-dispatch.cjs +230 -0
- package/lib/core/breakthrough/verb-dispatch.test.cjs +185 -0
- package/lib/core/breakthrough/voice-scaffold.cjs +249 -0
- package/lib/core/breakthrough/voice-scaffold.test.cjs +251 -0
- package/lib/core/bridge-writer.cjs +477 -0
- package/lib/core/cache-prune.cjs +314 -0
- package/lib/core/card-fire-gate.cjs +107 -0
- package/lib/core/card-fire-sidechannel.cjs +518 -0
- package/lib/core/chain-executor.cjs +939 -0
- package/lib/core/chain-retry.cjs +214 -0
- package/lib/core/chain-step-dispatcher.cjs +397 -0
- package/lib/core/chat-context-builder.cjs +253 -0
- package/lib/core/check-plugin-enabled.cjs +145 -0
- package/lib/core/close-loop-writer.cjs +492 -0
- package/lib/core/command-registration-check.cjs +246 -0
- package/lib/core/correlation-label-index.cjs +230 -0
- package/lib/core/correlation.cjs +112 -0
- package/lib/core/coverage-monitor.cjs +183 -0
- package/lib/core/coverage-rollup.cjs +232 -0
- package/lib/core/cross-room-aggregator.cjs +1127 -0
- package/lib/core/cross-room-store.cjs +292 -0
- package/lib/core/cross-room-triggers.cjs +222 -0
- package/lib/core/daily-briefing.cjs +438 -0
- package/lib/core/decision-axes.cjs +178 -0
- package/lib/core/decision-capture.cjs +618 -0
- package/lib/core/deck-design-rules.cjs +211 -0
- package/lib/core/deep-links.cjs +82 -0
- package/lib/core/directive-envelope.cjs +230 -0
- package/lib/core/directive-envelope.test.cjs +225 -0
- package/lib/core/dispatch-optimizer.cjs +354 -0
- package/lib/core/doc-text-extractor.cjs +133 -0
- package/lib/core/doctor/capability-ledger-module.cjs +170 -0
- package/lib/core/doctor/card-fire-health-module.cjs +159 -0
- package/lib/core/doctor/cascade-rooms-active-module.cjs +127 -0
- package/lib/core/doctor/cascade-rooms-module.cjs +184 -0
- package/lib/core/doctor/class-m-brain-smoke.cjs +479 -0
- package/lib/core/doctor/class-m-brain-smoke.test.cjs +448 -0
- package/lib/core/doctor/class-s-eureka-smoke.cjs +380 -0
- package/lib/core/doctor/deployment-surfaces-module.cjs +408 -0
- package/lib/core/doctor/deprecated-usage-module.cjs +155 -0
- package/lib/core/doctor/eureka-fts-health-module.cjs +236 -0
- package/lib/core/doctor/graph-derive-heal-retrofit-module.cjs +148 -0
- package/lib/core/doctor/graph-derive-health-module.cjs +486 -0
- package/lib/core/doctor/install-incomplete-module.cjs +320 -0
- package/lib/core/doctor/install-state-module.cjs +503 -0
- package/lib/core/doctor/mcp-surface-module.cjs +369 -0
- package/lib/core/doctor/mode-select-checkpoint-module.cjs +113 -0
- package/lib/core/doctor/plugin-enabled-state-module.cjs +86 -0
- package/lib/core/doctor/room-graph-density-module.cjs +181 -0
- package/lib/core/doctor/room-md-module.cjs +198 -0
- package/lib/core/doctor/shared.cjs +555 -0
- package/lib/core/doctor/stale-first-touch-copy-module.cjs +83 -0
- package/lib/core/doctor/statusline-visibility-module.cjs +295 -0
- package/lib/core/doctor/ui-compliance-module.cjs +223 -0
- package/lib/core/doctor/umbilical-module.cjs +486 -0
- package/lib/core/doctor/verify-surface-module.cjs +82 -0
- package/lib/core/doctor/voice-style-log-module.cjs +146 -0
- package/lib/core/domain-insight-sweep.cjs +224 -0
- package/lib/core/drift-baseline.cjs +362 -0
- package/lib/core/drift-runtime-reachability.cjs +283 -0
- package/lib/core/dual-path-detector.cjs +84 -0
- package/lib/core/dual-path-detector.test.cjs +334 -0
- package/lib/core/enrichment-queue.cjs +636 -0
- package/lib/core/eureka/ahp-weights.cjs +232 -0
- package/lib/core/eureka/analogy-fitness.cjs +326 -0
- package/lib/core/eureka/compression-meter.cjs +165 -0
- package/lib/core/eureka/embedding-classifier.cjs +226 -0
- package/lib/core/eureka/embedding-spine.cjs +563 -0
- package/lib/core/eureka/entity-classifier.cjs +232 -0
- package/lib/core/eureka/entity-extractor.cjs +356 -0
- package/lib/core/eureka/eureka-enable.cjs +228 -0
- package/lib/core/eureka/eureka-offer.cjs +225 -0
- package/lib/core/eureka/eureka-reach-runner.cjs +430 -0
- package/lib/core/eureka/explore-chain.cjs +834 -0
- package/lib/core/eureka/explored-artifact.cjs +456 -0
- package/lib/core/eureka/fts-index-lifecycle.cjs +265 -0
- package/lib/core/eureka/grade-grant-examine.cjs +544 -0
- package/lib/core/eureka/grade-grant.cjs +596 -0
- package/lib/core/eureka/hybrid-retrieve.cjs +256 -0
- package/lib/core/eureka/lateral-engine-adapter.cjs +118 -0
- package/lib/core/eureka/lexical-overlap.cjs +98 -0
- package/lib/core/eureka/online-pattern-query.cjs +142 -0
- package/lib/core/eureka/opportunity-harvest.cjs +752 -0
- package/lib/core/eureka/opportunity-statement.cjs +519 -0
- package/lib/core/eureka/portfolio-dimensions.cjs +255 -0
- package/lib/core/eureka/qualify-opportunity.cjs +607 -0
- package/lib/core/eureka/reasoning-mode.cjs +481 -0
- package/lib/core/eureka/report-html.cjs +368 -0
- package/lib/core/eureka/research-filing.cjs +547 -0
- package/lib/core/eureka/room-native-substrate.cjs +343 -0
- package/lib/core/eureka/tail-quadrant.cjs +177 -0
- package/lib/core/eureka/tri-modal-index.cjs +564 -0
- package/lib/core/eureka/vector-store.cjs +414 -0
- package/lib/core/eureka-critic.cjs +733 -0
- package/lib/core/eureka-deps-resolver.cjs +179 -0
- package/lib/core/expert-genesis.cjs +437 -0
- package/lib/core/expert-library.cjs +303 -0
- package/lib/core/exports-log.cjs +79 -0
- package/lib/core/feynman/ROOM.md +25 -0
- package/lib/core/feynman/dial-memory-renderer.cjs +279 -0
- package/lib/core/feynman/dial-memory-runner.cjs +173 -0
- package/lib/core/feynman/feynman-seed-writer.cjs +187 -0
- package/lib/core/feynman/timeline-renderer.cjs +197 -0
- package/lib/core/feynman/timeline-runner.cjs +281 -0
- package/lib/core/feynman-minto-invariants.cjs +609 -0
- package/lib/core/findings-wirer.cjs +360 -0
- package/lib/core/first-touch-version-stamper.cjs +113 -0
- package/lib/core/folder-memory-async.cjs +424 -0
- package/lib/core/folder-memory-shared.cjs +890 -0
- package/lib/core/folder-memory.cjs +546 -0
- package/lib/core/framework-chain-composer.cjs +583 -0
- package/lib/core/frontmatter-schemas.cjs +468 -0
- package/lib/core/fusion-router.cjs +540 -0
- package/lib/core/futures/causal-cue.cjs +91 -0
- package/lib/core/futures/orchestrator.cjs +1391 -0
- package/lib/core/futures/subsystem-render.cjs +164 -0
- package/lib/core/gate-relevance.cjs +278 -0
- package/lib/core/git-ops.cjs +141 -0
- package/lib/core/graph-backfill.cjs +562 -0
- package/lib/core/graph-candidate-producer.cjs +235 -0
- package/lib/core/graph-derivation.cjs +546 -0
- package/lib/core/graph-derive-classifier.cjs +352 -0
- package/lib/core/graph-ops.cjs +274 -0
- package/lib/core/graph-refine-loop.cjs +194 -0
- package/lib/core/graph-self-heal.cjs +289 -0
- package/lib/core/greeting-intent-detector.cjs +201 -0
- package/lib/core/grill-engine.cjs +459 -0
- package/lib/core/hat-persistence.cjs +274 -0
- package/lib/core/hsi-engine.cjs +439 -0
- package/lib/core/hsi-lsa.cjs +246 -0
- package/lib/core/hsi-spectral.cjs +554 -0
- package/lib/core/hsi-to-graph.test.cjs +168 -0
- package/lib/core/icm-forest.cjs +703 -0
- package/lib/core/ignite-branch-gate.cjs +66 -0
- package/lib/core/index.cjs +60 -0
- package/lib/core/insight-sensors.cjs +1000 -0
- package/lib/core/install-state.cjs +242 -0
- package/lib/core/integration-registry.cjs +232 -0
- package/lib/core/intel-pipeline.cjs +566 -0
- package/lib/core/intelligence-cascade.cjs +852 -0
- package/lib/core/issue-tree.cjs +269 -0
- package/lib/core/larry-thinness-acknowledgment.cjs +64 -0
- package/lib/core/larry-thinness-acknowledgment.test.cjs +97 -0
- package/lib/core/lazygraph-ops.cjs +1453 -0
- package/lib/core/lens-engine.cjs +434 -0
- package/lib/core/leverage-scan.cjs +218 -0
- package/lib/core/llm-name-suggester.cjs +194 -0
- package/lib/core/llm-name-suggester.test.cjs +132 -0
- package/lib/core/lru-cache.cjs +139 -0
- package/lib/core/mcp-dep-heal.cjs +475 -0
- package/lib/core/mcp-dep-heal.test.cjs +253 -0
- package/lib/core/mcp-profiles.cjs +185 -0
- package/lib/core/meeting-ops.cjs +54 -0
- package/lib/core/memory/governance-candidate-raiser.cjs +205 -0
- package/lib/core/memory/governance-candidate.cjs +95 -0
- package/lib/core/memory/reconcile-memory-runner.cjs +653 -0
- package/lib/core/memory-ops.cjs +641 -0
- package/lib/core/meter/gate-density-reader.cjs +112 -0
- package/lib/core/meter/transfer-reader.cjs +276 -0
- package/lib/core/meter/two-gauge.cjs +245 -0
- package/lib/core/methodology-ingest.cjs +346 -0
- package/lib/core/metrics/larryreach-loop-health.cjs +142 -0
- package/lib/core/migration-snapshot.cjs +287 -0
- package/lib/core/migration-snapshot.test.cjs +174 -0
- package/lib/core/migrations/ROOM.md +33 -0
- package/lib/core/migrations/phase-109-nodes-provenance.cjs +400 -0
- package/lib/core/migrations/phase-109-session-focus.cjs +99 -0
- package/lib/core/migrations/phase-160-nodes-bitemporal.cjs +168 -0
- package/lib/core/migrations/phase-160-nodes-bitemporal.test.cjs +140 -0
- package/lib/core/migrations/phase-162-section-nodes.cjs +156 -0
- package/lib/core/migrations/phase-222-ranker-weights.cjs +62 -0
- package/lib/core/migrations/phase-224-edge-review-status.cjs +87 -0
- package/lib/core/mindrian-brain-shim.test.cjs +221 -0
- package/lib/core/mode-select-sidechannel.cjs +259 -0
- package/lib/core/model-profiles.cjs +246 -0
- package/lib/core/mullins-scaffold.cjs +258 -0
- package/lib/core/mullins-scaffold.test.cjs +128 -0
- package/lib/core/mva-agent-contract.cjs +170 -0
- package/lib/core/mva-agent-contract.test.cjs +169 -0
- package/lib/core/mva-budget.cjs +75 -0
- package/lib/core/mva-budget.test.cjs +68 -0
- package/lib/core/mva-classifier.cjs +392 -0
- package/lib/core/mva-classifier.test.cjs +248 -0
- package/lib/core/mva-deck-builder.cjs +452 -0
- package/lib/core/mva-deck-builder.test.cjs +287 -0
- package/lib/core/mva-detect.smoke.test.cjs +209 -0
- package/lib/core/mva-dispatcher.cjs +110 -0
- package/lib/core/mva-dispatcher.test.cjs +216 -0
- package/lib/core/mva-option-router.cjs +414 -0
- package/lib/core/mva-option-router.test.cjs +483 -0
- package/lib/core/mva-orchestrator.cjs +365 -0
- package/lib/core/mva-orchestrator.test.cjs +916 -0
- package/lib/core/mva-progressive-renderer.cjs +194 -0
- package/lib/core/mva-progressive-renderer.test.cjs +157 -0
- package/lib/core/mva-rule-linter.cjs +458 -0
- package/lib/core/mva-rule-linter.test.cjs +484 -0
- package/lib/core/mva-state.cjs +159 -0
- package/lib/core/mva-telemetry.cjs +58 -0
- package/lib/core/mva-telemetry.test.cjs +196 -0
- package/lib/core/mva-vercel-deploy.cjs +168 -0
- package/lib/core/mva-vercel-deploy.test.cjs +239 -0
- package/lib/core/nav-dial.cjs +440 -0
- package/lib/core/navigation/ROOM.md +15 -0
- package/lib/core/navigation/abstraction-claim.cjs +185 -0
- package/lib/core/navigation/calibration-gate.cjs +284 -0
- package/lib/core/navigation/calibration-log.cjs +141 -0
- package/lib/core/navigation/confirm-node.cjs +73 -0
- package/lib/core/navigation/dashboard-helpers.cjs +145 -0
- package/lib/core/navigation/edges.cjs +1150 -0
- package/lib/core/navigation/evidence-claim.cjs +186 -0
- package/lib/core/navigation/explanation.cjs +48 -0
- package/lib/core/navigation/file-evidence-readback.cjs +242 -0
- package/lib/core/navigation/focus.cjs +155 -0
- package/lib/core/navigation/get-domains-for-trends.cjs +277 -0
- package/lib/core/navigation/governance.cjs +101 -0
- package/lib/core/navigation/grant-rubric.cjs +218 -0
- package/lib/core/navigation/graph-export.cjs +325 -0
- package/lib/core/navigation/ingestion.cjs +109 -0
- package/lib/core/navigation/insights-stale.test.cjs +127 -0
- package/lib/core/navigation/insights.cjs +574 -0
- package/lib/core/navigation/lens-nodes.cjs +315 -0
- package/lib/core/navigation/memory-artifacts.cjs +437 -0
- package/lib/core/navigation/memory-cortex-packet.cjs +274 -0
- package/lib/core/navigation/memory-events.cjs +811 -0
- package/lib/core/navigation/neighborhood.cjs +79 -0
- package/lib/core/navigation/packet.cjs +420 -0
- package/lib/core/navigation/planning-artifacts.cjs +219 -0
- package/lib/core/navigation/projections.cjs +295 -0
- package/lib/core/navigation/ranker-weights.cjs +133 -0
- package/lib/core/navigation/reasoning-write.cjs +211 -0
- package/lib/core/navigation/reconcile-guard.cjs +98 -0
- package/lib/core/navigation/reified-claim.cjs +287 -0
- package/lib/core/navigation/research-preflight.cjs +156 -0
- package/lib/core/navigation/room-birth.cjs +1213 -0
- package/lib/core/navigation/room-context.cjs +380 -0
- package/lib/core/navigation/room-home.cjs +142 -0
- package/lib/core/navigation/spine-events.cjs +548 -0
- package/lib/core/navigation/synthetic-expert.cjs +177 -0
- package/lib/core/navigation/transitions.cjs +253 -0
- package/lib/core/navigation/typed-claim.cjs +208 -0
- package/lib/core/navigation/typed-domain.cjs +249 -0
- package/lib/core/navigation/typed-entity.cjs +236 -0
- package/lib/core/navigation/typed-frame.cjs +257 -0
- package/lib/core/navigation/typed-open-question.cjs +130 -0
- package/lib/core/navigation/typed-opportunity.cjs +418 -0
- package/lib/core/navigation-engine-offer.cjs +776 -0
- package/lib/core/navigation-engine-shared.cjs +256 -0
- package/lib/core/navigation-engine.cjs +1696 -0
- package/lib/core/navigation.cjs +686 -0
- package/lib/core/nl-graph-queries.cjs +164 -0
- package/lib/core/node-insert-epistemic.test.cjs +212 -0
- package/lib/core/node-insert-overrides.test.cjs +273 -0
- package/lib/core/node-insert.cjs +274 -0
- package/lib/core/npm-cli-resolve.cjs +151 -0
- package/lib/core/npm-cli-resolve.test.cjs +153 -0
- package/lib/core/npm-install-lock.cjs +326 -0
- package/lib/core/npm-install-lock.test.cjs +379 -0
- package/lib/core/numeric/sklearn-stopwords-v1.json +325 -0
- package/lib/core/numeric/svd.cjs +377 -0
- package/lib/core/numeric/tfidf.cjs +241 -0
- package/lib/core/offer-presenter.cjs +406 -0
- package/lib/core/opportunity-extractor.cjs +183 -0
- package/lib/core/opportunity-ops.cjs +1423 -0
- package/lib/core/orchestration-candidate-lift.cjs +284 -0
- package/lib/core/part8-egress-guard.cjs +563 -0
- package/lib/core/part8-egress-guard.test.cjs +194 -0
- package/lib/core/part8-egress-ontology.cjs +174 -0
- package/lib/core/part8-egress-ontology.test.cjs +133 -0
- package/lib/core/persona-ops.cjs +692 -0
- package/lib/core/persona-override.cjs +327 -0
- package/lib/core/persona-taxonomy.cjs +328 -0
- package/lib/core/pinecone-inference.cjs +176 -0
- package/lib/core/pitch-feedback-schemas.cjs +303 -0
- package/lib/core/planning/artifact-brain-packet.cjs +212 -0
- package/lib/core/planning/reconcile-runner.cjs +347 -0
- package/lib/core/platform-gates.cjs +120 -0
- package/lib/core/platform.cjs +257 -0
- package/lib/core/proactive-intelligence.cjs +528 -0
- package/lib/core/problem-type-router.cjs +315 -0
- package/lib/core/publish-needs-default-lane.cjs +110 -0
- package/lib/core/ralph-loop-gate.cjs +48 -0
- package/lib/core/reader/ab-harness.cjs +177 -0
- package/lib/core/reader/decide-projection-reader.cjs +358 -0
- package/lib/core/reasoning-ops.cjs +639 -0
- package/lib/core/recipe-maps.cjs +442 -0
- package/lib/core/recovery/case-file.cjs +358 -0
- package/lib/core/recovery/controller.cjs +1008 -0
- package/lib/core/recovery/dispatcher.cjs +533 -0
- package/lib/core/recovery/result-semantics.cjs +488 -0
- package/lib/core/recovery/stage-envelope.cjs +338 -0
- package/lib/core/refusal-messaging.cjs +569 -0
- package/lib/core/refusal-messaging.test.cjs +220 -0
- package/lib/core/repo-version.cjs +134 -0
- package/lib/core/research-cache.cjs +165 -0
- package/lib/core/research-cache.test.cjs +137 -0
- package/lib/core/research-context-extractor.cjs +283 -0
- package/lib/core/research-corpus.cjs +814 -0
- package/lib/core/research-corpus.test.cjs +280 -0
- package/lib/core/research-filing-selector.cjs +189 -0
- package/lib/core/resolve-active-room.cjs +602 -0
- package/lib/core/resolve-brain-key.cjs +273 -0
- package/lib/core/resolve-umbilical-target.cjs +288 -0
- package/lib/core/resolve-vercel-key.cjs +107 -0
- package/lib/core/resolve-vercel-key.test.cjs +137 -0
- package/lib/core/reverse-salient-persona-suffix.cjs +115 -0
- package/lib/core/room-auto-create.cjs +318 -0
- package/lib/core/room-auto-create.test.cjs +198 -0
- package/lib/core/room-chooser.cjs +244 -0
- package/lib/core/room-classifier-strict-mode.cjs +235 -0
- package/lib/core/room-db.cjs +348 -0
- package/lib/core/room-discard-cascade.cjs +238 -0
- package/lib/core/room-discard-cascade.test.cjs +135 -0
- package/lib/core/room-name-validator.cjs +132 -0
- package/lib/core/room-name-validator.test.cjs +156 -0
- package/lib/core/room-naming-selector.cjs +357 -0
- package/lib/core/room-naming-selector.test.cjs +277 -0
- package/lib/core/room-open.cjs +259 -0
- package/lib/core/room-ops-async.cjs +92 -0
- package/lib/core/room-ops-shared.cjs +64 -0
- package/lib/core/room-ops-sync.cjs +70 -0
- package/lib/core/room-ops.cjs +32 -0
- package/lib/core/room-receipt-emit.cjs +63 -0
- package/lib/core/room-root.cjs +93 -0
- package/lib/core/room-skeleton-scaffold.cjs +669 -0
- package/lib/core/room-skeleton-scaffold.test.cjs +296 -0
- package/lib/core/room-type-detector.cjs +386 -0
- package/lib/core/rs-backend-dispatch.cjs +64 -0
- package/lib/core/rs-brain-substrate-prompts.cjs +129 -0
- package/lib/core/rs-brain-substrate.cjs +570 -0
- package/lib/core/rs-breakthrough-scorer.cjs +255 -0
- package/lib/core/rs-canon-violations.cjs +82 -0
- package/lib/core/rs-chain-feeder.cjs +381 -0
- package/lib/core/rs-commercial-assessor.cjs +280 -0
- package/lib/core/rs-corpus-quality-gate.cjs +62 -0
- package/lib/core/rs-differential-scorer.cjs +629 -0
- package/lib/core/rs-domain-analyzer.cjs +385 -0
- package/lib/core/rs-egress-prompts.cjs +113 -0
- package/lib/core/rs-egress-telemetry.cjs +225 -0
- package/lib/core/rs-egress-violations.cjs +53 -0
- package/lib/core/rs-engine.cjs +597 -0
- package/lib/core/rs-expert-brain-projection.cjs +280 -0
- package/lib/core/rs-expert-mapper.cjs +467 -0
- package/lib/core/rs-fetcher-academic.cjs +697 -0
- package/lib/core/rs-fetcher-experts.cjs +314 -0
- package/lib/core/rs-fetcher-industry.cjs +731 -0
- package/lib/core/rs-fetcher-patents.cjs +564 -0
- package/lib/core/rs-innovation-classifier.cjs +194 -0
- package/lib/core/rs-math.cjs +458 -0
- package/lib/core/rs-mind-map.cjs +656 -0
- package/lib/core/rs-neo4j-writer.cjs +388 -0
- package/lib/core/rs-nl-to-query.cjs +435 -0
- package/lib/core/rs-pinecone-bridge.cjs +344 -0
- package/lib/core/rs-preprocessor.cjs +350 -0
- package/lib/core/rs-query-matrix.cjs +316 -0
- package/lib/core/rs-query-to-text.cjs +438 -0
- package/lib/core/rs-sqlite-mirror.cjs +495 -0
- package/lib/core/rs-thesis-generator.cjs +188 -0
- package/lib/core/rs_cache.py +635 -0
- package/lib/core/rs_corpus.py +562 -0
- package/lib/core/rs_corpus_exclude.py +62 -0
- package/lib/core/rs_hybrid.py +589 -0
- package/lib/core/rs_math.py +287 -0
- package/lib/core/rs_rooms.py +196 -0
- package/lib/core/salient-governance.cjs +315 -0
- package/lib/core/scheduled-scanner.cjs +658 -0
- package/lib/core/scratchpad-ops.cjs +316 -0
- package/lib/core/seam-liveness.cjs +191 -0
- package/lib/core/seam-liveness.test.cjs +177 -0
- package/lib/core/section-8-trace-schema.cjs +138 -0
- package/lib/core/section-registry.cjs +167 -0
- package/lib/core/security/agentshield-adapters.test.cjs +104 -0
- package/lib/core/security/agentshield-run.cjs +343 -0
- package/lib/core/security/agentshield-scanner.cjs +314 -0
- package/lib/core/security/agentshield-scanner.test.cjs +259 -0
- package/lib/core/sensors/hat-scoping-table.cjs +103 -0
- package/lib/core/sensors/sensor-circularity.cjs +278 -0
- package/lib/core/sensors/sensor-content-relevance.cjs +407 -0
- package/lib/core/sensors/sensor-diffusion-adoption.cjs +230 -0
- package/lib/core/sensors/sensor-eureka.cjs +208 -0
- package/lib/core/sensors/sensor-expert-skill.cjs +293 -0
- package/lib/core/sensors/sensor-external-fact.cjs +184 -0
- package/lib/core/sensors/sensor-gate-approach.cjs +104 -0
- package/lib/core/sensors/sensor-jtbd-reweight.cjs +141 -0
- package/lib/core/sensors/sensor-lagging-component.cjs +115 -0
- package/lib/core/sensors/sensor-memory-cortex.cjs +111 -0
- package/lib/core/sensors/sensor-methodology-decision.cjs +131 -0
- package/lib/core/sensors/sensor-opportunity-harvest.cjs +195 -0
- package/lib/core/sensors/sensor-perspective-lock.cjs +174 -0
- package/lib/core/sensors/sensor-priority.cjs +183 -0
- package/lib/core/sensors/sensor-recency.cjs +151 -0
- package/lib/core/sensors/sensor-roadmap-type.cjs +421 -0
- package/lib/core/sensors/sensor-room-pick.cjs +238 -0
- package/lib/core/sensors/sensor-show-share.cjs +215 -0
- package/lib/core/sensors/sensor-temporal-blindness.cjs +123 -0
- package/lib/core/sensors/sensor-types.cjs +294 -0
- package/lib/core/sensors/sensor-url-ingest.cjs +234 -0
- package/lib/core/session-binding.cjs +208 -0
- package/lib/core/session-presence.cjs +404 -0
- package/lib/core/session-register.cjs +138 -0
- package/lib/core/session-state.cjs +144 -0
- package/lib/core/shallow-doc-parser.cjs +353 -0
- package/lib/core/shallow-doc-parser.test.cjs +226 -0
- package/lib/core/skill-activation-router.cjs +284 -0
- package/lib/core/skillopt-schemas.cjs +509 -0
- package/lib/core/stale-copy-scanner.cjs +190 -0
- package/lib/core/stance-state.cjs +153 -0
- package/lib/core/state-aware-router.cjs +78 -0
- package/lib/core/state-ops.cjs +80 -0
- package/lib/core/state-version.cjs +350 -0
- package/lib/core/statusline-cache.cjs +266 -0
- package/lib/core/statusline-liveness-gate.cjs +105 -0
- package/lib/core/surface-fence.cjs +127 -0
- package/lib/core/synthesizers/comparison-matrix.cjs +82 -0
- package/lib/core/synthesizers/convergence-map.cjs +107 -0
- package/lib/core/synthesizers/domain-hierarchy.cjs +138 -0
- package/lib/core/synthesizers/source-comparison.cjs +56 -0
- package/lib/core/synthesizers/tension-map.cjs +119 -0
- package/lib/core/synthetic-expert-behavior-gate.cjs +99 -0
- package/lib/core/synthetic-expert-construction-gate.cjs +170 -0
- package/lib/core/telemetry/schema.cjs +168 -0
- package/lib/core/telemetry/schema.test.cjs +124 -0
- package/lib/core/telemetry/validator.cjs +200 -0
- package/lib/core/telemetry/validator.test.cjs +188 -0
- package/lib/core/telemetry/writer.cjs +141 -0
- package/lib/core/telemetry/writer.test.cjs +331 -0
- package/lib/core/temporal/date-sync-gate.cjs +256 -0
- package/lib/core/temporal/date-sync-gate.test.cjs +191 -0
- package/lib/core/temporal/dual-stamp.cjs +172 -0
- package/lib/core/temporal/dual-stamp.test.cjs +172 -0
- package/lib/core/temporal/point-in-time.cjs +76 -0
- package/lib/core/temporal/point-in-time.test.cjs +169 -0
- package/lib/core/temporal/recency-decay.cjs +119 -0
- package/lib/core/temporal/recency-decay.test.cjs +140 -0
- package/lib/core/temporal/reference-now.cjs +281 -0
- package/lib/core/temporal/reference-now.test.cjs +225 -0
- package/lib/core/temporal/resolve-relative-time.cjs +116 -0
- package/lib/core/temporal/resolve-relative-time.test.cjs +138 -0
- package/lib/core/temporal/supersession.cjs +250 -0
- package/lib/core/temporal/supersession.test.cjs +103 -0
- package/lib/core/terminal-capability.cjs +88 -0
- package/lib/core/token-estimator.cjs +348 -0
- package/lib/core/tone-filter.cjs +147 -0
- package/lib/core/trending-to-absurd/orchestrator.cjs +341 -0
- package/lib/core/trending-to-absurd/stage7-roadmap.cjs +317 -0
- package/lib/core/trending-to-absurd/variance.cjs +169 -0
- package/lib/core/unknowns/bandit.cjs +332 -0
- package/lib/core/unknowns/corpus-adapter.cjs +327 -0
- package/lib/core/unknowns/dsp.cjs +356 -0
- package/lib/core/unknowns/edge-writer.cjs +211 -0
- package/lib/core/unknowns/iface.cjs +212 -0
- package/lib/core/unknowns/orchestrator.cjs +375 -0
- package/lib/core/unknowns/pattern-miner.cjs +214 -0
- package/lib/core/unknowns/rumsfeld-matrix.cjs +155 -0
- package/lib/core/unknowns/verdict.cjs +254 -0
- package/lib/core/update-path.cjs +57 -0
- package/lib/core/url-ingest.cjs +568 -0
- package/lib/core/user-archetype.cjs +284 -0
- package/lib/core/user-md-ops.cjs +578 -0
- package/lib/core/venture-shape-nudge.cjs +178 -0
- package/lib/core/venture-shape-nudge.test.cjs +266 -0
- package/lib/core/verb-reach-affinity.cjs +299 -0
- package/lib/core/visual-ops.cjs +692 -0
- package/lib/core/voice-transition-detector.cjs +125 -0
- package/lib/core/watched-sources.cjs +237 -0
- package/lib/core/write-lock.cjs +149 -0
- package/lib/graph/canvas-graph.js +467 -0
- package/lib/graph/constellation-config.cjs +299 -0
- package/lib/graph/graph-detail-panel.js +165 -0
- package/lib/hmi/ROOM.md +47 -0
- package/lib/hmi/across-session-memory.cjs +669 -0
- package/lib/hmi/agentshield-drift-gate.cjs +142 -0
- package/lib/hmi/brain-review-packet.cjs +221 -0
- package/lib/hmi/cortex-reach-adapter.cjs +271 -0
- package/lib/hmi/cross-room-memory.cjs +618 -0
- package/lib/hmi/decoy-tier.cjs +395 -0
- package/lib/hmi/depth-state.cjs +164 -0
- package/lib/hmi/dial-label-composer.cjs +410 -0
- package/lib/hmi/dial-presenter.cjs +508 -0
- package/lib/hmi/dial-reach-orchestrator.cjs +377 -0
- package/lib/hmi/dial-selector.cjs +239 -0
- package/lib/hmi/f1-pick-capture-cli.cjs +131 -0
- package/lib/hmi/f3-depth-capture-cli.cjs +77 -0
- package/lib/hmi/f4-scope-capture-cli.cjs +86 -0
- package/lib/hmi/f8-action-capture-cli.cjs +145 -0
- package/lib/hmi/f8-capture.test.cjs +77 -0
- package/lib/hmi/f9-ordered-capture-cli.cjs +138 -0
- package/lib/hmi/harvest-scope-state.cjs +202 -0
- package/lib/hmi/hats-persona-cache.cjs +142 -0
- package/lib/hmi/jtbd-classifier.cjs +219 -0
- package/lib/hmi/jtbd-state.cjs +263 -0
- package/lib/hmi/jtbd-taxonomy.json +402 -0
- package/lib/hmi/mindrian-ascii-logo.cjs +96 -0
- package/lib/hmi/part8-egress-gate.cjs +149 -0
- package/lib/hmi/reach-component-map.json +47 -0
- package/lib/hmi/reach-relevance-gate.cjs +147 -0
- package/lib/hmi/selector-dispatcher.cjs +1201 -0
- package/lib/hmi/selector-dispatcher.test.cjs +141 -0
- package/lib/hmi/selector-telemetry.cjs +269 -0
- package/lib/hmi/shape-f0-renderer.cjs +145 -0
- package/lib/hmi/shape-f1-fallback.cjs +80 -0
- package/lib/hmi/shape-f1-renderer.cjs +219 -0
- package/lib/hmi/shape-f2-renderer.cjs +132 -0
- package/lib/hmi/shape-f3-parity.test.cjs +64 -0
- package/lib/hmi/shape-f3-renderer.cjs +66 -0
- package/lib/hmi/shape-f4-parity.test.cjs +72 -0
- package/lib/hmi/shape-f4-renderer.cjs +72 -0
- package/lib/hmi/shape-f5-renderer.cjs +155 -0
- package/lib/hmi/shape-f6-plan-review-renderer.cjs +324 -0
- package/lib/hmi/shape-f6-renderer.cjs +144 -0
- package/lib/hmi/shape-f7-breakthrough-renderer.cjs +234 -0
- package/lib/hmi/shape-f7-breakthrough-renderer.test.cjs +233 -0
- package/lib/hmi/shape-f8-renderer.cjs +150 -0
- package/lib/hmi/shape-f8-renderer.test.cjs +70 -0
- package/lib/hmi/shape-f9-renderer.cjs +187 -0
- package/lib/hmi/shape-f9-renderer.test.cjs +80 -0
- package/lib/hmi/shape-g-renderer.cjs +219 -0
- package/lib/hmi/shape-h-renderer.cjs +222 -0
- package/lib/hmi/tier-check.cjs +63 -0
- package/lib/hmi/turn-text.cjs +268 -0
- package/lib/hmi/voice-color-mark.cjs +318 -0
- package/lib/hmi/voice-style-log.cjs +276 -0
- package/lib/import/PRECONDITIONS.md +41 -0
- package/lib/import/branding.cjs +210 -0
- package/lib/import/branding.test.cjs +235 -0
- package/lib/import/classifications-sync.cjs +104 -0
- package/lib/import/classifications-sync.test.cjs +129 -0
- package/lib/import/enricher.cjs +296 -0
- package/lib/import/enricher.test.cjs +273 -0
- package/lib/import/integration.test.cjs +376 -0
- package/lib/import/manifest.cjs +129 -0
- package/lib/import/manifest.schema.json +185 -0
- package/lib/import/manifest.test.cjs +123 -0
- package/lib/import/meeting-detector.cjs +92 -0
- package/lib/import/meeting-detector.test.cjs +100 -0
- package/lib/import/person-detector.cjs +229 -0
- package/lib/import/person-detector.test.cjs +149 -0
- package/lib/import/report.cjs +186 -0
- package/lib/import/report.test.cjs +186 -0
- package/lib/import/room-md-scaffolder.cjs +49 -0
- package/lib/import/router.cjs +224 -0
- package/lib/import/router.test.cjs +356 -0
- package/lib/import/run-all-tests.cjs +36 -0
- package/lib/import/smoke-test.cjs +213 -0
- package/lib/import/smoke-test.test.cjs +148 -0
- package/lib/import/undo.test.cjs +199 -0
- package/lib/import/vault-scanner.cjs +105 -0
- package/lib/import/vault-scanner.test.cjs +67 -0
- package/lib/lens-engine/correlation-resolver.cjs +174 -0
- package/lib/lens-engine/source-lens-driver.cjs +736 -0
- package/lib/mcp/adapter-client.cjs +103 -0
- package/lib/mcp/app-html/dashboard.html +316 -0
- package/lib/mcp/app-html/graph.html +428 -0
- package/lib/mcp/app-html/mindrian-platform.html +1841 -0
- package/lib/mcp/app-html/wiki.html +383 -0
- package/lib/mcp/app-views.cjs +322 -0
- package/lib/mcp/brain-composition-census.cjs +184 -0
- package/lib/mcp/brain-router.cjs +551 -0
- package/lib/mcp/capability-registry.cjs +62 -0
- package/lib/mcp/contract-version.cjs +59 -0
- package/lib/mcp/daemon-lifecycle.cjs +324 -0
- package/lib/mcp/gate-dedup.cjs +120 -0
- package/lib/mcp/gate-ledger.cjs +145 -0
- package/lib/mcp/gate-render.cjs +483 -0
- package/lib/mcp/hook-adapter-audit.cjs +199 -0
- package/lib/mcp/larry-context.cjs +42 -0
- package/lib/mcp/mcp-first-flag.cjs +147 -0
- package/lib/mcp/no-instructions.test.cjs +270 -0
- package/lib/mcp/pipeline-state.cjs +424 -0
- package/lib/mcp/prompts.cjs +421 -0
- package/lib/mcp/register-core-tools.cjs +78 -0
- package/lib/mcp/resources.cjs +359 -0
- package/lib/mcp/runtime-instructions.cjs +38 -0
- package/lib/mcp/session-catchup.cjs +349 -0
- package/lib/mcp/session-registry.cjs +113 -0
- package/lib/mcp/session-room.cjs +124 -0
- package/lib/mcp/sse-event-bus.cjs +98 -0
- package/lib/mcp/stop-gate-handler.cjs +589 -0
- package/lib/mcp/surface-detect.cjs +186 -0
- package/lib/mcp/tool-router.cjs +2115 -0
- package/lib/mcp/tools/chain.cjs +674 -0
- package/lib/mcp/tools/claim.cjs +180 -0
- package/lib/mcp/tools/context.cjs +92 -0
- package/lib/mcp/tools/dual-path.cjs +79 -0
- package/lib/mcp/tools/gate.cjs +392 -0
- package/lib/mcp/tools/graph-reason.cjs +130 -0
- package/lib/mcp/tools/graph.cjs +321 -0
- package/lib/mcp/tools/identity.cjs +174 -0
- package/lib/mcp/tools/room.cjs +449 -0
- package/lib/mcp/tools/sensors.cjs +450 -0
- package/lib/mcp/tools/status.cjs +183 -0
- package/lib/mcp/tools/stop-gate.cjs +103 -0
- package/lib/mcp/tools/views.cjs +382 -0
- package/lib/mcp/tree-watcher.cjs +199 -0
- package/lib/memory/.mindrian/auto-explore-mat-test-001.json +11 -0
- package/lib/memory/aaak-compress.cjs +403 -0
- package/lib/memory/aaak-compress.test.cjs +288 -0
- package/lib/memory/async-artifact-auto-commit.test.cjs +223 -0
- package/lib/memory/bearer-token.test.cjs +315 -0
- package/lib/memory/body-shape-coverage.test.cjs +268 -0
- package/lib/memory/brain-cache-lru.test.cjs +259 -0
- package/lib/memory/brain-client-query-shape.test.cjs +160 -0
- package/lib/memory/brain-cypher-chain-slice.test.cjs +368 -0
- package/lib/memory/brain-derivation-graceful-degradation.test.cjs +1019 -0
- package/lib/memory/brain-derivation-queue.test.cjs +553 -0
- package/lib/memory/brain-derivation.test.cjs +639 -0
- package/lib/memory/brain-derive-command.test.cjs +534 -0
- package/lib/memory/brain-derive-curation.test.cjs +243 -0
- package/lib/memory/brain-md-invariants-validator.test.cjs +704 -0
- package/lib/memory/brain-md-schema.test.cjs +467 -0
- package/lib/memory/brain-md-staleness.test.cjs +525 -0
- package/lib/memory/brain-server-resolution.test.cjs +314 -0
- package/lib/memory/brain-suggestion-template.test.cjs +200 -0
- package/lib/memory/chain-recommender-canonical.test.cjs +208 -0
- package/lib/memory/chain-recommender.test.cjs +233 -0
- package/lib/memory/chat-context.test.cjs +128 -0
- package/lib/memory/command-registry.test.cjs +220 -0
- package/lib/memory/connector-registry.test.cjs +94 -0
- package/lib/memory/correlation-label-index.test.cjs +119 -0
- package/lib/memory/correlation.test.cjs +253 -0
- package/lib/memory/cross-room-aggregator.test.cjs +909 -0
- package/lib/memory/curation-132-03-dedup-held-rename.test.cjs +251 -0
- package/lib/memory/curation-132-05-pseudonymize.test.cjs +205 -0
- package/lib/memory/curation-batch.test.cjs +213 -0
- package/lib/memory/dashboard-server.test.cjs +256 -0
- package/lib/memory/debouncer-drain-at-prompt.test.cjs +389 -0
- package/lib/memory/decision-capture.test.cjs +632 -0
- package/lib/memory/decision-capture.worker.cjs +70 -0
- package/lib/memory/doctor-deprecation-surface.test.cjs +188 -0
- package/lib/memory/dual-graph-health.test.cjs +485 -0
- package/lib/memory/explain-decision-command.test.cjs +521 -0
- package/lib/memory/explain-decision-footer.test.cjs +316 -0
- package/lib/memory/explored-materials-store.cjs +392 -0
- package/lib/memory/f-selector-ranker.test.cjs +593 -0
- package/lib/memory/feynman-minto-guardian.test.cjs +863 -0
- package/lib/memory/feynman-minto-invariants.test.cjs +554 -0
- package/lib/memory/feynman-prompts-drift.test.cjs +144 -0
- package/lib/memory/feynman-prompts.cjs +151 -0
- package/lib/memory/feynman-prompts.test.cjs +96 -0
- package/lib/memory/first-touch-version.test.cjs +198 -0
- package/lib/memory/folder-memory-quadruple.test.cjs +548 -0
- package/lib/memory/folder-memory.test.cjs +503 -0
- package/lib/memory/framework-chain-composer.test.cjs +549 -0
- package/lib/memory/frontmatter-schema-validator.test.cjs +344 -0
- package/lib/memory/guardian-onstop-reaches-user.test.cjs +396 -0
- package/lib/memory/heal-command.test.cjs +604 -0
- package/lib/memory/help-coverage.test.cjs +108 -0
- package/lib/memory/help-renderer.test.cjs +145 -0
- package/lib/memory/huji-run-one-async-parity.test.cjs +242 -0
- package/lib/memory/hypergraph-event-schema.test.cjs +116 -0
- package/lib/memory/index-artifact-transaction.test.cjs +333 -0
- package/lib/memory/lazygraph-rs-discoveries-view.test.cjs +122 -0
- package/lib/memory/local-chain-recommender.test.cjs +172 -0
- package/lib/memory/mcp-input-validation.test.cjs +240 -0
- package/lib/memory/mcp-server-brain-deps.test.cjs +270 -0
- package/lib/memory/mcp-stack-fallback.test.cjs +433 -0
- package/lib/memory/minto-debounce-consumer-census.test.cjs +567 -0
- package/lib/memory/minto-debouncer.test.cjs +407 -0
- package/lib/memory/minto-debouncer.worker.cjs +46 -0
- package/lib/memory/minto-migration-v88.test.cjs +265 -0
- package/lib/memory/minto-schema-v88.test.cjs +390 -0
- package/lib/memory/mos-status-renderer.test.cjs +631 -0
- package/lib/memory/narrative-schema.cjs +376 -0
- package/lib/memory/narrative-schema.test.cjs +209 -0
- package/lib/memory/nav-dial.test.cjs +414 -0
- package/lib/memory/navigation-engine-core.test.cjs +712 -0
- package/lib/memory/navigation-engine-offer.test.cjs +404 -0
- package/lib/memory/navigation-hook-resolver.test.cjs +177 -0
- package/lib/memory/navigation-invariants.test.cjs +483 -0
- package/lib/memory/navigation-projections.test.cjs +241 -0
- package/lib/memory/navigation-write-edge.test.cjs +206 -0
- package/lib/memory/offer-closer.test.cjs +258 -0
- package/lib/memory/offer-presenter.test.cjs +554 -0
- package/lib/memory/on-stop-snapshot.test.cjs +404 -0
- package/lib/memory/orchestration-projection.test.cjs +706 -0
- package/lib/memory/packet-chain-hint.test.cjs +407 -0
- package/lib/memory/packet-schema-validation.test.cjs +317 -0
- package/lib/memory/palette-consistency.test.cjs +127 -0
- package/lib/memory/pending-tension-store.cjs +453 -0
- package/lib/memory/per-command-jtbd-derivation.test.cjs +130 -0
- package/lib/memory/per-command-teaching.test.cjs +110 -0
- package/lib/memory/post-compact-reinjection.test.cjs +854 -0
- package/lib/memory/post-write-triple.test.cjs +317 -0
- package/lib/memory/pre-compact-snapshot.test.cjs +495 -0
- package/lib/memory/precommit-real-commit.test.cjs +301 -0
- package/lib/memory/problem-type-router.test.cjs +656 -0
- package/lib/memory/query-efficiency-telemetry.test.cjs +612 -0
- package/lib/memory/recompile-room-references.test.cjs +392 -0
- package/lib/memory/recompile-room-references.worker.cjs +42 -0
- package/lib/memory/record-decision-dual-write.test.cjs +454 -0
- package/lib/memory/render-v2-disposition.test.cjs +199 -0
- package/lib/memory/room-classifier-strict-mode.test.cjs +430 -0
- package/lib/memory/room-minto-hook.test.cjs +398 -0
- package/lib/memory/rs-discovery-engine.test.cjs +323 -0
- package/lib/memory/run-feynman-tests.cjs +2101 -0
- package/lib/memory/security-trifecta.test.cjs +329 -0
- package/lib/memory/selector-alias-map.test.cjs +172 -0
- package/lib/memory/selector-decisions.test.cjs +417 -0
- package/lib/memory/selector-miss.test.cjs +290 -0
- package/lib/memory/session-start-brain-staleness.test.cjs +363 -0
- package/lib/memory/session-start-triple-injection.test.cjs +514 -0
- package/lib/memory/sessionstart-banner-formatter.cjs +318 -0
- package/lib/memory/sessionstart-coordinator.test.cjs +446 -0
- package/lib/memory/sessionstart-minto-banner.test.cjs +373 -0
- package/lib/memory/skill-activation-router.test.cjs +465 -0
- package/lib/memory/skill-vs-code-drift.test.cjs +257 -0
- package/lib/memory/soft-alias.test.cjs +144 -0
- package/lib/memory/spine-events-correlation.test.cjs +198 -0
- package/lib/memory/stale-copy-scanner.test.cjs +294 -0
- package/lib/memory/stamp-artifact-write.test.cjs +304 -0
- package/lib/memory/state-aware-router.test.cjs +90 -0
- package/lib/memory/statusline-active-room-write.test.cjs +245 -0
- package/lib/memory/statusline-active-room.test.cjs +315 -0
- package/lib/memory/statusline-minto-segment.test.cjs +292 -0
- package/lib/memory/statusline-two-row.test.cjs +338 -0
- package/lib/memory/suggest-next-workflow.test.cjs +176 -0
- package/lib/memory/sync-async-entry-points.test.cjs +204 -0
- package/lib/memory/terminal-capability.test.cjs +155 -0
- package/lib/memory/test-bridge-writer-enhanced.cjs +452 -0
- package/lib/memory/test-rs-brain-substrate-shape.cjs +529 -0
- package/lib/memory/test-rs-brain-substrate.cjs +636 -0
- package/lib/memory/test-rs-breakthrough-scorer.cjs +375 -0
- package/lib/memory/test-rs-canon-violations.cjs +218 -0
- package/lib/memory/test-rs-chain-feeder-core.cjs +344 -0
- package/lib/memory/test-rs-chain-feeder-skill-spawn.cjs +297 -0
- package/lib/memory/test-rs-commercial-assessor.cjs +385 -0
- package/lib/memory/test-rs-differential-scorer.cjs +480 -0
- package/lib/memory/test-rs-discovery-engine.cjs +648 -0
- package/lib/memory/test-rs-domain-analyzer.cjs +492 -0
- package/lib/memory/test-rs-egress-primitives.cjs +420 -0
- package/lib/memory/test-rs-expert-mapper.cjs +547 -0
- package/lib/memory/test-rs-explain-command.cjs +443 -0
- package/lib/memory/test-rs-fetcher-academic.cjs +848 -0
- package/lib/memory/test-rs-fetcher-experts.cjs +496 -0
- package/lib/memory/test-rs-fetcher-industry.cjs +702 -0
- package/lib/memory/test-rs-fetcher-patents.cjs +674 -0
- package/lib/memory/test-rs-innovation-classifier.cjs +301 -0
- package/lib/memory/test-rs-mind-map.cjs +646 -0
- package/lib/memory/test-rs-neo4j-writer.cjs +518 -0
- package/lib/memory/test-rs-nl-to-query.cjs +449 -0
- package/lib/memory/test-rs-pinecone-bridge.cjs +282 -0
- package/lib/memory/test-rs-preprocessor.cjs +433 -0
- package/lib/memory/test-rs-query-matrix.cjs +391 -0
- package/lib/memory/test-rs-query-to-text.cjs +551 -0
- package/lib/memory/test-rs-sqlite-mirror.cjs +649 -0
- package/lib/memory/test-rs-thesis-generator.cjs +360 -0
- package/lib/memory/triple-context-formatter.cjs +473 -0
- package/lib/memory/triple-context-formatter.test.cjs +442 -0
- package/lib/memory/user-md-persona.test.cjs +565 -0
- package/lib/memory/userpromptsubmit-integration.test.cjs +787 -0
- package/lib/memory/validators/README.md +157 -0
- package/lib/memory/validators/brain-md-invariants.cjs +475 -0
- package/lib/memory/validators/brain-substrate-invariants.cjs +285 -0
- package/lib/memory/validators/external-academic-invariants.cjs +249 -0
- package/lib/memory/validators/external-industry-invariants.cjs +271 -0
- package/lib/memory/validators/external-patents-invariants.cjs +266 -0
- package/lib/memory/validators/minto-invariants.cjs +62 -0
- package/lib/memory/validators/navigation-invariants.cjs +340 -0
- package/lib/memory/validators/queue-health.cjs +95 -0
- package/lib/memory/validators/snapshot-integrity.cjs +129 -0
- package/lib/memory/validators/stale-lifecycle.cjs +116 -0
- package/lib/memory/vault-section-minto-generator-atomic.test.cjs +556 -0
- package/lib/memory/vault-section-minto-generator-atomic.worker.cjs +73 -0
- package/lib/memory/workflow-layer-e2e.test.cjs +262 -0
- package/lib/memory/write-lock-atomic.test.cjs +137 -0
- package/lib/memory/write-lock-atomic.worker.cjs +55 -0
- package/lib/parity/check-parity.cjs +83 -0
- package/lib/presentation/presentation-server.cjs +101 -0
- package/lib/presentation/presentation-watcher.cjs +123 -0
- package/lib/quickview/hub-server.cjs +719 -0
- package/lib/quickview/server.cjs +533 -0
- package/lib/render/JTBD-PALETTES.md +145 -0
- package/lib/render/ROOM.md +111 -0
- package/lib/render/render-v2.cjs +532 -0
- package/lib/render/render-v2.test.cjs +267 -0
- package/lib/render/render.cjs +65 -0
- package/lib/sessionstart/budget-compressor.cjs +130 -0
- package/lib/sessionstart/contributor-interface.cjs +134 -0
- package/lib/sessionstart/contributor-isolator.cjs +128 -0
- package/lib/sessionstart/precedence-ladder.cjs +47 -0
- package/lib/state/ROOM.md +46 -0
- package/lib/state/state-md-parser.cjs +215 -0
- package/lib/statusline/ROOM.md +38 -0
- package/lib/statusline/banner-suppression.cjs +50 -0
- package/lib/statusline/cockpit-renderer.cjs +443 -0
- package/lib/statusline/cockpit-signals.cjs +312 -0
- package/lib/statusline/cockpit-telemetry.cjs +152 -0
- package/lib/statusline/ctx-window.cjs +135 -0
- package/lib/statusline/governing-thought-truncator.cjs +45 -0
- package/lib/statusline/next-move-cache.cjs +235 -0
- package/lib/statusline/ratification-next.cjs +164 -0
- package/lib/statusline/room-health-cache.cjs +200 -0
- package/lib/statusline/surface-detect.cjs +85 -0
- package/lib/statusline/two-row-renderer.cjs +207 -0
- package/lib/statusline/version-resolver.cjs +81 -0
- package/lib/ui/design-system.cjs +19 -0
- package/lib/update-bootstrap.sh.template +145 -0
- package/lib/vault/frontmatter-schema.cjs +297 -0
- package/lib/vault/room-scanner.cjs +365 -0
- package/lib/vault/wikilink-builder.cjs +231 -0
- package/lib/vault/wikilink-builder.test.cjs +182 -0
- package/lib/wiki/briefing.cjs +193 -0
- package/lib/wiki/editor-dist/wiki-editor.css +1 -0
- package/lib/wiki/editor-dist/wiki-editor.js +1327 -0
- package/lib/wiki/graph-links.cjs +281 -0
- package/lib/wiki/page-renderer.cjs +229 -0
- package/lib/wiki/room-home.cjs +353 -0
- package/lib/wiki/wiki-chat.cjs +81 -0
- package/lib/wiki/wiki-export.cjs +364 -0
- package/lib/wiki/wiki-layout.cjs +1143 -0
- package/lib/wiki/wiki-search.cjs +142 -0
- package/lib/wiki/wiki-server.cjs +748 -0
- package/lib/wiki/wiki-watcher.cjs +105 -0
- package/lib/workflow/ROOM.md +47 -0
- package/lib/workflow/chain-source.cjs +179 -0
- package/lib/workflow/command-resolver.cjs +155 -0
- package/lib/workflow/command-resolver.test.cjs +235 -0
- package/lib/workflow/cross-room-umbilical-closer.cjs +364 -0
- package/lib/workflow/dial-close-reach.cjs +405 -0
- package/lib/workflow/f-selector-ranker.cjs +1148 -0
- package/lib/workflow/f1-pick-consumer.cjs +232 -0
- package/lib/workflow/f3-depth-consumer.cjs +98 -0
- package/lib/workflow/f4-scope-consumer.cjs +112 -0
- package/lib/workflow/f8-consumer.test.cjs +82 -0
- package/lib/workflow/f8-fanout-consumer.cjs +208 -0
- package/lib/workflow/f9-consumer.test.cjs +71 -0
- package/lib/workflow/f9-ordered-consumer.cjs +199 -0
- package/lib/workflow/local-chain-recommender.cjs +330 -0
- package/lib/workflow/memory-cascade-f9-adapter.cjs +273 -0
- package/lib/workflow/memory-governance-closer.cjs +384 -0
- package/lib/workflow/offer-closer.cjs +407 -0
- package/lib/workflow/reach-hedge-ranker.cjs +809 -0
- package/lib/workflow/reach-reject-reader.cjs +372 -0
- package/lib/workflow/reconcile-f9-adapter.cjs +299 -0
- package/lib/workflow/selector-decisions.cjs +478 -0
- package/lib/workflow/session-binding-consumer.cjs +249 -0
- package/npm-shrinkwrap.json +1544 -0
- package/output-styles/destijl.md +67 -0
- package/package.json +22 -4
- package/pipelines/PWS_grading/01-deep-grade.md +48 -0
- package/pipelines/PWS_grading/02-mullins.md +50 -0
- package/pipelines/PWS_grading/03-build-thesis.md +60 -0
- package/pipelines/PWS_grading/04-structure-argument.md +77 -0
- package/pipelines/PWS_grading/CHAIN.md +79 -0
- package/pipelines/analogy/01-decompose.md +80 -0
- package/pipelines/analogy/02-abstract.md +87 -0
- package/pipelines/analogy/03-search.md +135 -0
- package/pipelines/analogy/04-transfer.md +101 -0
- package/pipelines/analogy/05-validate.md +106 -0
- package/pipelines/analogy/CHAIN.md +73 -0
- package/pipelines/discovery/01-explore-domains.md +44 -0
- package/pipelines/discovery/02-think-hats.md +50 -0
- package/pipelines/discovery/03-analyze-needs.md +54 -0
- package/pipelines/discovery/CHAIN.md +48 -0
- package/pipelines/thesis/01-structure-argument.md +45 -0
- package/pipelines/thesis/02-challenge-assumptions.md +48 -0
- package/pipelines/thesis/03-build-thesis.md +54 -0
- package/pipelines/thesis/CHAIN.md +48 -0
- package/references/brain/causal-directives.md +91 -0
- package/references/brain/causal-enrichment.cypher +165 -0
- package/references/brain/command-triggers-schema.md +15 -0
- package/references/brain/graph-architecture.md +317 -0
- package/references/brain/query-patterns.md +504 -0
- package/references/brain/room-hierarchy-schema.md +224 -0
- package/references/brain/schema.md +86 -0
- package/references/capability-radar/capabilities-index.md +269 -0
- package/references/capability-radar/changelog-cache.md +95 -0
- package/references/causal/causal-schema.md +103 -0
- package/references/design/email-template-standard.md +169 -0
- package/references/design/graph-visualization-standard.md +178 -0
- package/references/design/newsletter-email-template.html +307 -0
- package/references/document-generation.md +179 -0
- package/references/hsi/HSI-TOOLS-REFERENCE.md +222 -0
- package/references/import-config.md +141 -0
- package/references/integrations/detection-patterns.md +101 -0
- package/references/meeting/artifact-template.md +377 -0
- package/references/meeting/cross-meeting-intelligence.md +216 -0
- package/references/meeting/cross-relationship-patterns.md +202 -0
- package/references/meeting/extraction-perspectives.md +225 -0
- package/references/meeting/filing-protocol.md +222 -0
- package/references/meeting/knowledge-typing.md +242 -0
- package/references/meeting/live-join-interface.md +244 -0
- package/references/meeting/section-mapping.md +192 -0
- package/references/meeting/segment-classification.md +258 -0
- package/references/meeting/speaker-profile-template.md +219 -0
- package/references/meeting/summary-template.md +348 -0
- package/references/meeting/transcript-patterns.md +226 -0
- package/references/methodology/analyze-needs.md +135 -0
- package/references/methodology/analyze-systems.md +121 -0
- package/references/methodology/analyze-timing.md +149 -0
- package/references/methodology/beautiful-question.md +109 -0
- package/references/methodology/build-knowledge.md +161 -0
- package/references/methodology/build-thesis-scored.md +60 -0
- package/references/methodology/build-thesis.md +237 -0
- package/references/methodology/causal-loop-diagrams.md +65 -0
- package/references/methodology/challenge-assumptions.md +127 -0
- package/references/methodology/diagnose.md +169 -0
- package/references/methodology/dominant-designs.md +212 -0
- package/references/methodology/explore-domains.md +147 -0
- package/references/methodology/explore-futures.md +163 -0
- package/references/methodology/explore-trends.md +129 -0
- package/references/methodology/find-bottlenecks.md +131 -0
- package/references/methodology/grade.md +211 -0
- package/references/methodology/huji-stage-a-intake.md +246 -0
- package/references/methodology/index.md +34 -0
- package/references/methodology/leadership.md +200 -0
- package/references/methodology/lean-canvas.md +116 -0
- package/references/methodology/leverage-scan-signatures.md +83 -0
- package/references/methodology/macro-trends.md +192 -0
- package/references/methodology/map-unknowns.md +137 -0
- package/references/methodology/mullins-7-domains.md +104 -0
- package/references/methodology/problem-types.md +65 -0
- package/references/methodology/root-cause.md +178 -0
- package/references/methodology/rubric-huji.md +160 -0
- package/references/methodology/sapphire-encoding.md +355 -0
- package/references/methodology/scenario-plan.md +190 -0
- package/references/methodology/score-innovation.md +154 -0
- package/references/methodology/skillopt-judge-rubric.md +48 -0
- package/references/methodology/skillopt-queries-rubric.md +60 -0
- package/references/methodology/skillopt-refute-rubric.md +59 -0
- package/references/methodology/skillopt-review-rubric.md +72 -0
- package/references/methodology/skillopt-revise-rubric.md +48 -0
- package/references/methodology/structure-argument.md +158 -0
- package/references/methodology/systems-thinking.md +231 -0
- package/references/methodology/think-hats.md +147 -0
- package/references/methodology/triz-matrix.json +751 -0
- package/references/methodology/triz-principles.md +501 -0
- package/references/methodology/user-needs.md +199 -0
- package/references/methodology/validate.md +163 -0
- package/references/methodology/value-proposition.md +244 -0
- package/references/opportunities/funding-lifecycle.md +103 -0
- package/references/opportunities/grant-api-patterns.md +99 -0
- package/references/opportunities/opportunity-template.md +84 -0
- package/references/opportunities/tnufa-rubric.md +81 -0
- package/references/personality/assessment-philosophy.md +72 -0
- package/references/personality/lexicon.md +100 -0
- package/references/personality/persona-chains.md +56 -0
- package/references/personality/pws-lexicon-full.md +499 -0
- package/references/personality/voice-dna.md +156 -0
- package/references/personas/hat-perspectives.md +76 -0
- package/references/personas/persona-template.md +63 -0
- package/references/pipeline/act-output-contract.md +88 -0
- package/references/pipeline/chains-index.md +39 -0
- package/references/pws-profile-generation.md +79 -0
- package/references/reasoning/reasoning-schema.md +143 -0
- package/references/reasoning/reasoning-template.md +68 -0
- package/references/reasoning/run-template.md +38 -0
- package/references/research/RESEARCH_14_CLAUDE_CODE_SOURCE_ARCHITECTURE.md +209 -0
- package/references/research/RESEARCH_15_V1.8_OPTIMIZATION_JTBD.md +375 -0
- package/references/research/RESEARCH_16_NATIVE_FIRST_PLUGIN_ARCHITECTURE.md +575 -0
- package/references/research/RESEARCH_17_MCP_UI_FRAMEWORKS.md +272 -0
- package/references/security/agentshield-baseline.json +8 -0
- package/references/security/cve-db.json +220 -0
- package/references/taxonomy/TAXONOMY.md +192 -0
- package/references/templates/MINTO.md +36 -0
- package/references/user-research/2026-04-05-leah-lawrence-session.md +202 -0
- package/references/vault-kit/README.md +35 -0
- package/references/vault-kit/app.json +12 -0
- package/references/vault-kit/appearance.json +12 -0
- package/references/vault-kit/graph.json +35 -0
- package/references/vault-kit/snippets/mindrian-destijl.css +297 -0
- package/references/vault-kit/templates/new-artifact.md +37 -0
- package/references/vault-kit/templates/new-meeting-note.md +35 -0
- package/references/vault-kit/templates/new-team-profile.md +29 -0
- package/references/vault-kit/templates/new-xref.md +35 -0
- package/references/visual/ROOM.md +55 -0
- package/references/visual/palette.json +54 -0
- package/references/visual/symbol-system.md +151 -0
- package/scripts/189-plurai-gate-check.cjs +88 -0
- package/scripts/198-plurai-gate-check.cjs +118 -0
- package/scripts/198-rollback-rehearsal.cjs +233 -0
- package/scripts/83-scope-injection.test.cjs +648 -0
- package/scripts/PYTHON_GATES.md +78 -0
- package/scripts/__pycache__/compute-hsi.cpython-312.pyc +0 -0
- package/scripts/__pycache__/detect-reverse-salients.cpython-312.pyc +0 -0
- package/scripts/__pycache__/rs-engine.cpython-312.pyc +0 -0
- package/scripts/act-command.cjs +518 -0
- package/scripts/admin-brain-write.cjs +442 -0
- package/scripts/admin-command-gate.cjs +178 -0
- package/scripts/agentshield-scan-cli.cjs +226 -0
- package/scripts/agentshield-sessionstart-scan.cjs +282 -0
- package/scripts/analogy-fitness-report.cjs +266 -0
- package/scripts/analyze-room +1079 -0
- package/scripts/apply-teaching-content.cjs +298 -0
- package/scripts/async-artifact-auto-commit.cjs +485 -0
- package/scripts/audit-active-phase-resolver.cjs +160 -0
- package/scripts/audit-body-shape-coverage.cjs +213 -0
- package/scripts/auto-explore-drain.cjs +274 -0
- package/scripts/auto-explore-fingerprint.cjs +354 -0
- package/scripts/auto-explore-fire.cjs +359 -0
- package/scripts/backfill-correlation-id.cjs +425 -0
- package/scripts/backfill-hitl-shape.cjs +276 -0
- package/scripts/backfill-hitl-shape.test.cjs +307 -0
- package/scripts/backup-modifications +68 -0
- package/scripts/banner +182 -0
- package/scripts/brain-derivation-drain.cjs +291 -0
- package/scripts/brain-derive-command.cjs +846 -0
- package/scripts/brain-normalize-books.cypher +505 -0
- package/scripts/brain-normalize-final.cypher +1355 -0
- package/scripts/brain-normalize-problemtype.cypher +475 -0
- package/scripts/brain-normalize-supplement.cypher +449 -0
- package/scripts/brain-normalize-targeted.cypher +344 -0
- package/scripts/brain-normalize-v194.cypher +61 -0
- package/scripts/brain-normalize.cypher +530 -0
- package/scripts/brain-response-sanitize-hook.cjs +165 -0
- package/scripts/build-brain-census.cjs +957 -0
- package/scripts/build-brain-packet-schema.cjs +225 -0
- package/scripts/build-command-registry.cjs +587 -0
- package/scripts/build-connector-registry.cjs +1331 -0
- package/scripts/build-corpus-stats.cjs +408 -0
- package/scripts/build-dist-bundles.cjs +495 -0
- package/scripts/build-ecosystem-graph.cjs +490 -0
- package/scripts/build-graph +1462 -0
- package/scripts/build-graph-from-sqlite.cjs +97 -0
- package/scripts/build-harness-manifest.cjs +1090 -0
- package/scripts/build-jtbd-nudges +205 -0
- package/scripts/build-new-surface.cjs +805 -0
- package/scripts/build-orchestration-projection.cjs +1221 -0
- package/scripts/build-render-coverage.cjs +637 -0
- package/scripts/build-skill-mirrors.cjs +681 -0
- package/scripts/cache-hitrate-report.cjs +216 -0
- package/scripts/causal-to-graph.cjs +163 -0
- package/scripts/check-abstraction-fixture-neutral.cjs +160 -0
- package/scripts/check-admin-identity.cjs +212 -0
- package/scripts/check-brain-tool-liveness.cjs +502 -0
- package/scripts/check-card-fire.cjs +1809 -0
- package/scripts/check-cirs-declaration.cjs +279 -0
- package/scripts/check-deck-design.cjs +104 -0
- package/scripts/check-dual-graph-health.cjs +645 -0
- package/scripts/check-first-touch-drift.cjs +220 -0
- package/scripts/check-flagship-floor.cjs +391 -0
- package/scripts/check-framework-vocabulary-drift.cjs +563 -0
- package/scripts/check-gate-seam.cjs +178 -0
- package/scripts/check-graph-derive-health.cjs +208 -0
- package/scripts/check-graph-export-typemap.cjs +118 -0
- package/scripts/check-help-coverage.cjs +290 -0
- package/scripts/check-hitl-stages.cjs +219 -0
- package/scripts/check-hook-schema-compatibility.cjs +231 -0
- package/scripts/check-hsi-deps +42 -0
- package/scripts/check-kuzu-reintroduction.cjs +251 -0
- package/scripts/check-onboard +43 -0
- package/scripts/check-onboard-statusline.cjs +363 -0
- package/scripts/check-palette-consistency.cjs +106 -0
- package/scripts/check-pending-ambiguous.cjs +244 -0
- package/scripts/check-pending-breakthrough.cjs +189 -0
- package/scripts/check-pending-breakthrough.test.cjs +166 -0
- package/scripts/check-pending-naming-decision.cjs +97 -0
- package/scripts/check-plugin-path-anchoring.cjs +830 -0
- package/scripts/check-publish-needs.cjs +126 -0
- package/scripts/check-registry-drift.cjs +206 -0
- package/scripts/check-release-payload-ceiling.cjs +269 -0
- package/scripts/check-render-coverage.cjs +558 -0
- package/scripts/check-research-isomorphism.cjs +446 -0
- package/scripts/check-reward-before-investment.cjs +261 -0
- package/scripts/check-room-blueprints.cjs +213 -0
- package/scripts/check-schema-aliases.cjs +532 -0
- package/scripts/check-shape-declaration.cjs +958 -0
- package/scripts/check-shape-declaration.test.cjs +185 -0
- package/scripts/check-skill-spec.cjs +312 -0
- package/scripts/check-skill-vs-code-drift.cjs +178 -0
- package/scripts/check-substrate.cjs +513 -0
- package/scripts/check-tool-honesty.cjs +1601 -0
- package/scripts/check-update +60 -0
- package/scripts/check-version-and-sha.cjs +366 -0
- package/scripts/check-voice-style.cjs +184 -0
- package/scripts/check-worktree-hygiene.cjs +530 -0
- package/scripts/classify-insight +59 -0
- package/scripts/coherence-smoke-test.cjs +489 -0
- package/scripts/collect-cold-install-evidence.cjs +411 -0
- package/scripts/compute-bayesian-surprise.py +175 -0
- package/scripts/compute-blindspot-mass.py +209 -0
- package/scripts/compute-disruption-index.py +211 -0
- package/scripts/compute-element-novelty.py +254 -0
- package/scripts/compute-external-whitespace.py +563 -0
- package/scripts/compute-hsi.py +947 -0
- package/scripts/compute-meetings-intelligence +716 -0
- package/scripts/compute-opportunity-state +17 -0
- package/scripts/compute-state +393 -0
- package/scripts/compute-team +1112 -0
- package/scripts/compute-whitespace-embeddings.py +464 -0
- package/scripts/compute-whitespace-gaps.py +562 -0
- package/scripts/compute_topic_forest.py +684 -0
- package/scripts/consolidate-pinecone.py +265 -0
- package/scripts/context-monitor +943 -0
- package/scripts/create-speaker-profile +311 -0
- package/scripts/cross-room-detect.cjs +195 -0
- package/scripts/csv-to-idea-graph.cjs +755 -0
- package/scripts/ctxl-eval.cjs +846 -0
- package/scripts/curation-132-03-dedup-held-rename.cjs +395 -0
- package/scripts/curation-132-05-pseudonymize.cjs +189 -0
- package/scripts/derive-verb-reach-affinity.cjs +507 -0
- package/scripts/detect-reverse-salients.py +242 -0
- package/scripts/diagnostics-command.cjs +345 -0
- package/scripts/dial-memory-refresh-command.cjs +147 -0
- package/scripts/discover-analogy-whitespace.py +433 -0
- package/scripts/discover-hsi-whitespace.py +352 -0
- package/scripts/discover-rs-whitespace.py +327 -0
- package/scripts/discovery-cycle.cjs +625 -0
- package/scripts/disposition-render-v2.cjs +229 -0
- package/scripts/doctor-preflight-format.cjs +101 -0
- package/scripts/doctor.cjs +4327 -0
- package/scripts/dogfood-bridge/append-to-queue.sh +59 -0
- package/scripts/dogfood-bridge/path-filter.cjs +86 -0
- package/scripts/dogfood-derive.cjs +158 -0
- package/scripts/dogfood-emit.cjs +126 -0
- package/scripts/empathy-observation-emit.cjs +108 -0
- package/scripts/enrichment-queue-append.cjs +157 -0
- package/scripts/ensure-brain-baseline.cjs +117 -0
- package/scripts/entity-extract.cjs +1135 -0
- package/scripts/eureka-command.cjs +687 -0
- package/scripts/eureka-critic-run.cjs +662 -0
- package/scripts/eureka-portfolio-report.cjs +2038 -0
- package/scripts/eureka-room-report.cjs +419 -0
- package/scripts/explain-decision-command.cjs +499 -0
- package/scripts/export-pinecone-embeddings.cjs +516 -0
- package/scripts/extract-room-intelligence.cjs +714 -0
- package/scripts/fetch-brain-baseline.cjs +190 -0
- package/scripts/fetch-brain-baseline.py +360 -0
- package/scripts/feynman-minto-guardian.cjs +672 -0
- package/scripts/feynman-timeline-refresh-command.cjs +270 -0
- package/scripts/file-asset +253 -0
- package/scripts/fire-sensor-event.cjs +83 -0
- package/scripts/first-install-router.cjs +968 -0
- package/scripts/frontmatter-schema-validator.cjs +339 -0
- package/scripts/fts-index-drain.cjs +221 -0
- package/scripts/generate-chat-embed.cjs +179 -0
- package/scripts/generate-deck.cjs +322 -0
- package/scripts/generate-export.cjs +474 -0
- package/scripts/generate-hub.cjs +2553 -0
- package/scripts/generate-lobby.cjs +718 -0
- package/scripts/generate-presentation.cjs +988 -0
- package/scripts/generate-presentation.test.cjs +405 -0
- package/scripts/generate-section-intelligence.cjs +330 -0
- package/scripts/generate-snapshot.cjs +1410 -0
- package/scripts/generate-standalone +159 -0
- package/scripts/git-ops +211 -0
- package/scripts/graph-heal-pipeline.cjs +321 -0
- package/scripts/gsd-artifact-graph-hook.cjs +219 -0
- package/scripts/gsd-graph-derive-drain.cjs +571 -0
- package/scripts/gsd-graph-derive-sweep.cjs +195 -0
- package/scripts/heal-command.cjs +1365 -0
- package/scripts/hedge-refit-pipeline.cjs +207 -0
- package/scripts/help-renderer.cjs +303 -0
- package/scripts/hmi-compliance-poll.cjs +451 -0
- package/scripts/hmi-status-command.cjs +510 -0
- package/scripts/hooked-rescore-117.cjs +224 -0
- package/scripts/hooks/pre-commit +533 -0
- package/scripts/hooks/pre-commit-room-minto-guard.cmd +43 -0
- package/scripts/hooks/pre-commit-room-minto-guard.sh +533 -0
- package/scripts/hsi-to-graph.cjs +194 -0
- package/scripts/huji-batch.cjs +821 -0
- package/scripts/huji-eval.cjs +1158 -0
- package/scripts/huji-intake.cjs +563 -0
- package/scripts/huji-pin-smoketest.cjs +454 -0
- package/scripts/huji-run-one-async.cjs +305 -0
- package/scripts/huji-run-one.cjs +671 -0
- package/scripts/install-pre-commit.sh +113 -0
- package/scripts/intent-classifier +106 -0
- package/scripts/intent-classifier.cjs +3620 -0
- package/scripts/interpret-whitespace.cjs +691 -0
- package/scripts/jtbd-command.cjs +810 -0
- package/scripts/jtbd-update.cjs +299 -0
- package/scripts/label-topic-forest.cjs +667 -0
- package/scripts/learn-from-usage +168 -0
- package/scripts/lib/__pycache__/ensure_ml_deps.cpython-312.pyc +0 -0
- package/scripts/lib/ensure_ml_deps.py +97 -0
- package/scripts/load-embeddings-into-neo4j.cjs +569 -0
- package/scripts/memory-artifact-graph-hook.cjs +191 -0
- package/scripts/memory-command.cjs +687 -0
- package/scripts/memory-completion-detector.cjs +217 -0
- package/scripts/memory-lifecycle.cjs +620 -0
- package/scripts/memory-resume-nudge.cjs +233 -0
- package/scripts/methodology-ingest-local.sh +36 -0
- package/scripts/migrate-brain-mcp-from-http-to-stdio.cjs +190 -0
- package/scripts/migrate-brain-mcp-from-http-to-stdio.test.cjs +242 -0
- package/scripts/migrate-hats-to-roomdb.cjs +217 -0
- package/scripts/migrate-lazygraph.cjs +250 -0
- package/scripts/migrate-minto-schema-v88.cjs +296 -0
- package/scripts/migrate-plugin-root-refs.cjs +168 -0
- package/scripts/migrate-room-sections-v275.cjs +556 -0
- package/scripts/migrate-rooms +417 -0
- package/scripts/migrate-skill-frontmatter.cjs +472 -0
- package/scripts/migrate-stale-user-settings.cjs +296 -0
- package/scripts/migrate-telemetry-v1.cjs +384 -0
- package/scripts/migrate-telemetry-v1.test.cjs +356 -0
- package/scripts/minto-debouncer.cjs +428 -0
- package/scripts/mos-status.cjs +622 -0
- package/scripts/mva-detect.cjs +229 -0
- package/scripts/mva-run.cjs +36 -0
- package/scripts/on-agent-complete +163 -0
- package/scripts/on-cwd-changed +149 -0
- package/scripts/on-file-changed +68 -0
- package/scripts/on-stop +617 -0
- package/scripts/on-task-complete +194 -0
- package/scripts/operator-command.cjs +858 -0
- package/scripts/operator-update.cjs +308 -0
- package/scripts/part8-egress-guard-hook.cjs +229 -0
- package/scripts/pipeline-command.cjs +271 -0
- package/scripts/post-compact +383 -0
- package/scripts/post-update-activation.cjs +339 -0
- package/scripts/post-write +283 -0
- package/scripts/power-demo-prompt.md +621 -0
- package/scripts/pre-compact +273 -0
- package/scripts/preflight-auto-explore.cjs +314 -0
- package/scripts/preflight-doctor.cjs +225 -0
- package/scripts/preflight-release-drift.cjs +168 -0
- package/scripts/preflight-tension-surface.cjs +414 -0
- package/scripts/probe-brain-contract.cjs +502 -0
- package/scripts/publish-ops +346 -0
- package/scripts/query-efficiency-telemetry.cjs +364 -0
- package/scripts/query-semantic-scholar.cjs +545 -0
- package/scripts/reapply-modifications +49 -0
- package/scripts/reassign-primary.cjs +125 -0
- package/scripts/recompile-room-references.cjs +601 -0
- package/scripts/release-beta-preflight.sh +69 -0
- package/scripts/render-pdf +588 -0
- package/scripts/render-viz +190 -0
- package/scripts/research-speaker +239 -0
- package/scripts/resolve-room +204 -0
- package/scripts/restore-post-compact-context.cjs +320 -0
- package/scripts/room-auto-create-nudge.cjs +136 -0
- package/scripts/room-naming-selector.cjs +201 -0
- package/scripts/room-registry +767 -0
- package/scripts/rs-discovery-engine.cjs +725 -0
- package/scripts/rs-engine.py +2073 -0
- package/scripts/rs-experts-command.cjs +300 -0
- package/scripts/rs-explain-command.cjs +323 -0
- package/scripts/rs-fetch-command.cjs +170 -0
- package/scripts/rs-thesis-command.cjs +203 -0
- package/scripts/rs-vector-bridge.cjs +410 -0
- package/scripts/run-harness.cjs +890 -0
- package/scripts/scout-cadence-guard.cjs +323 -0
- package/scripts/scout-cadence-runner.cjs +509 -0
- package/scripts/scout-telemetry-aggregator.cjs +268 -0
- package/scripts/sealed-walker.py +96 -0
- package/scripts/seed-brain-commands.cjs +324 -0
- package/scripts/self-update +105 -0
- package/scripts/sentinel-deadline-monitor +241 -0
- package/scripts/sentinel-health-check +198 -0
- package/scripts/sentinel-snapshot +42 -0
- package/scripts/serve-dashboard +103 -0
- package/scripts/serve-dashboard-live +932 -0
- package/scripts/serve-presentation +97 -0
- package/scripts/serve-wiki +90 -0
- package/scripts/session-end-presence.cjs +79 -0
- package/scripts/session-start +1943 -0
- package/scripts/sessionstart-coordinator.cjs +249 -0
- package/scripts/sessionstart-npm-reconcile.cjs +111 -0
- package/scripts/sessionstart-post-update-preflight.cjs +303 -0
- package/scripts/sessionstart-reference-now-seed.cjs +137 -0
- package/scripts/setup-hooks.sh +112 -0
- package/scripts/skillopt-codereview.cjs +830 -0
- package/scripts/skillopt-eval.cjs +519 -0
- package/scripts/skillopt-funnel.cjs +1076 -0
- package/scripts/skillopt-genqueries.cjs +571 -0
- package/scripts/skillopt-inventory.cjs +319 -0
- package/scripts/skillopt-merge.cjs +656 -0
- package/scripts/skillopt-triggerloop.cjs +1126 -0
- package/scripts/soft-alias-runner.cjs +116 -0
- package/scripts/stamp-artifact-write.cjs +276 -0
- package/scripts/stamp-firing-block.cjs +325 -0
- package/scripts/state-write.cjs +97 -0
- package/scripts/statusline-fallback-echo.cjs +297 -0
- package/scripts/statusline-mos +116 -0
- package/scripts/statusline-mos-dispatch +127 -0
- package/scripts/suggest-next-command.cjs +416 -0
- package/scripts/sweep-skill-descriptions.cjs +263 -0
- package/scripts/sync-rooms-graph +264 -0
- package/scripts/telemetry-command-invocation.cjs +120 -0
- package/scripts/test-doctor-recovery.cjs +242 -0
- package/scripts/test-fresh-install.md +140 -0
- package/scripts/track-analytics +156 -0
- package/scripts/transcribe-audio +148 -0
- package/scripts/transcript-ingest.cjs +225 -0
- package/scripts/update-icm-index +153 -0
- package/scripts/v182-brain-optimize.cypher +1191 -0
- package/scripts/validate-model-profiles +93 -0
- package/scripts/vault-content-reformatter.cjs +581 -0
- package/scripts/vault-export-orchestrator.cjs +400 -0
- package/scripts/vault-footer-injector.cjs +315 -0
- package/scripts/vault-import.cjs +839 -0
- package/scripts/vault-integration-test.cjs +318 -0
- package/scripts/vault-regenerate-all.cjs +216 -0
- package/scripts/vault-regenerate-all.test.cjs +138 -0
- package/scripts/vault-rules-generator.cjs +220 -0
- package/scripts/vault-section-minto-generator.cjs +1642 -0
- package/scripts/vault-section-minto-generator.integration.test.cjs +576 -0
- package/scripts/vault-section-minto-generator.test.cjs +291 -0
- package/scripts/vault-section-state-generator.cjs +385 -0
- package/scripts/vault-welcome-generator.cjs +403 -0
- package/scripts/vault-wikilink-injector.cjs +449 -0
- package/scripts/verify-phase-132.cjs +338 -0
- package/scripts/verify-release +744 -0
- package/scripts/whitespace-command.cjs +831 -0
- package/scripts/whitespace-to-brain.cjs +384 -0
- package/scripts/whitespace-to-graph.cjs +220 -0
- package/scripts/wikilink-batch.cjs +102 -0
- package/scripts/wikilink-file.cjs +139 -0
- package/scripts/write-bridge-artifacts.cjs +140 -0
- package/scripts/write-scope-check +12 -0
- package/scripts/write-scope-check.cjs +461 -0
- package/scripts/write-whitespace-sections.cjs +483 -0
- package/settings.json +7 -0
- package/skills/act/SKILL.md +523 -0
- package/skills/admin/SKILL.md +437 -0
- package/skills/agentshield/SKILL.md +50 -0
- package/skills/analyze-needs/SKILL.md +74 -0
- package/skills/analyze-systems/SKILL.md +69 -0
- package/skills/analyze-timing/SKILL.md +94 -0
- package/skills/auto-explore/SKILL.md +84 -0
- package/skills/beautiful-question/SKILL.md +72 -0
- package/skills/bono/SKILL.md +146 -0
- package/skills/brain-connector/SKILL.md +206 -0
- package/skills/brain-derive/SKILL.md +140 -0
- package/skills/build-knowledge/SKILL.md +118 -0
- package/skills/build-thesis/SKILL.md +90 -0
- package/skills/causal/SKILL.md +265 -0
- package/skills/challenge-assumptions/SKILL.md +74 -0
- package/skills/client-discovery-interview/SKILL.md +94 -0
- package/skills/client-discovery-interview/discovery-scaffold.json +41 -0
- package/skills/client-discovery-interview/question-bank.md +149 -0
- package/skills/compare-ventures/SKILL.md +113 -0
- package/skills/context-engine/SKILL.md +169 -0
- package/skills/conversation-mode/SKILL.md +189 -0
- package/skills/correct-reference-now/SKILL.md +86 -0
- package/skills/dashboard/SKILL.md +142 -0
- package/skills/deck/SKILL.md +113 -0
- package/skills/deep-grade/SKILL.md +210 -0
- package/skills/diagnose/SKILL.md +128 -0
- package/skills/diagnostics/SKILL.md +193 -0
- package/skills/dial-memory-refresh/SKILL.md +116 -0
- package/skills/diffusion/SKILL.md +125 -0
- package/skills/discover/SKILL.md +216 -0
- package/skills/doctor/SKILL.md +269 -0
- package/skills/dogfood-flush/SKILL.md +133 -0
- package/skills/dominant-designs/SKILL.md +72 -0
- package/skills/eureka/SKILL.md +313 -0
- package/skills/explain-decision/SKILL.md +138 -0
- package/skills/explore-domains/SKILL.md +74 -0
- package/skills/explore-futures/SKILL.md +64 -0
- package/skills/explore-opportunity/SKILL.md +168 -0
- package/skills/explore-trends/SKILL.md +74 -0
- package/skills/export/SKILL.md +126 -0
- package/skills/feynman-timeline-refresh/SKILL.md +105 -0
- package/skills/file-meeting/SKILL.md +1190 -0
- package/skills/find-analogies/SKILL.md +360 -0
- package/skills/find-bottlenecks/SKILL.md +104 -0
- package/skills/find-connections/SKILL.md +108 -0
- package/skills/funding/SKILL.md +110 -0
- package/skills/futures/SKILL.md +124 -0
- package/skills/grade/SKILL.md +252 -0
- package/skills/grade-grant/SKILL.md +293 -0
- package/skills/graph/SKILL.md +216 -0
- package/skills/hat-briefing/SKILL.md +160 -0
- package/skills/heal/SKILL.md +69 -0
- package/skills/help/SKILL.md +172 -0
- package/skills/hmi-status/SKILL.md +73 -0
- package/skills/ignite/SKILL.md +225 -0
- package/skills/ingest-methodology/SKILL.md +125 -0
- package/skills/intel-pipeline/SKILL.md +128 -0
- package/skills/intelligence-orchestrator/SKILL.md +262 -0
- package/skills/jtbd/SKILL.md +261 -0
- package/skills/larry-personality/SKILL.md +571 -0
- package/skills/larry-personality/framework-chains.md +92 -0
- package/skills/larry-personality/mode-engine.md +185 -0
- package/skills/leadership/SKILL.md +109 -0
- package/skills/lean-canvas/SKILL.md +70 -0
- package/skills/macro-trends/SKILL.md +84 -0
- package/skills/map-unknowns/SKILL.md +72 -0
- package/skills/memory/SKILL.md +212 -0
- package/skills/memory-cortex-reach/SKILL.md +80 -0
- package/skills/models/SKILL.md +197 -0
- package/skills/mos/SKILL.md +160 -0
- package/skills/mos-deck-engine/SKILL.md +329 -0
- package/skills/mos-reason/SKILL.md +484 -0
- package/skills/mullins/SKILL.md +151 -0
- package/skills/mullins-scaffold/SKILL.md +132 -0
- package/skills/mullins-scaffold/scaffold.json +223 -0
- package/skills/mva-brief/SKILL.md +87 -0
- package/skills/mva-option/SKILL.md +120 -0
- package/skills/mva-pipeline/SKILL.md +158 -0
- package/skills/mva-report/SKILL.md +79 -0
- package/skills/new-project/SKILL.md +629 -0
- package/skills/new-surface/SKILL.md +112 -0
- package/skills/onboard/SKILL.md +485 -0
- package/skills/operator/SKILL.md +167 -0
- package/skills/opportunities/SKILL.md +171 -0
- package/skills/organize/SKILL.md +69 -0
- package/skills/persona/SKILL.md +272 -0
- package/skills/pipeline/SKILL.md +164 -0
- package/skills/present/SKILL.md +123 -0
- package/skills/publish/SKILL.md +224 -0
- package/skills/pws-brain/SKILL.md +152 -0
- package/skills/pws-methodology/SKILL.md +92 -0
- package/skills/qualify-opportunity/SKILL.md +111 -0
- package/skills/query/SKILL.md +68 -0
- package/skills/radar/SKILL.md +106 -0
- package/skills/reanalyze/SKILL.md +122 -0
- package/skills/research/SKILL.md +462 -0
- package/skills/room/SKILL.md +381 -0
- package/skills/room-passive/SKILL.md +174 -0
- package/skills/room-proactive/SKILL.md +344 -0
- package/skills/rooms/SKILL.md +678 -0
- package/skills/root-cause/SKILL.md +73 -0
- package/skills/rs-experts/SKILL.md +151 -0
- package/skills/rs-explain/SKILL.md +130 -0
- package/skills/rs-fetch/SKILL.md +115 -0
- package/skills/rs-thesis/SKILL.md +129 -0
- package/skills/scenario-plan/SKILL.md +76 -0
- package/skills/scheduled-tasks/SKILL.md +301 -0
- package/skills/score-innovation/SKILL.md +74 -0
- package/skills/scout/SKILL.md +370 -0
- package/skills/setup/SKILL.md +654 -0
- package/skills/show/SKILL.md +68 -0
- package/skills/skill/SKILL.md +104 -0
- package/skills/snapshot/SKILL.md +178 -0
- package/skills/speakers/SKILL.md +116 -0
- package/skills/splash/SKILL.md +54 -0
- package/skills/stance/SKILL.md +89 -0
- package/skills/status/SKILL.md +107 -0
- package/skills/structure-argument/SKILL.md +78 -0
- package/skills/suggest-next/SKILL.md +137 -0
- package/skills/systems-thinking/SKILL.md +220 -0
- package/skills/think-hats/SKILL.md +82 -0
- package/skills/trending-to-absurd/SKILL.md +100 -0
- package/skills/ui-system/SKILL.md +474 -0
- package/skills/ui-system/design-system/M-OS-DESIGN-SYSTEM.md +273 -0
- package/skills/ui-system/design-system/SPEC.md +72 -0
- package/skills/ui-system/design-system/image-prompt-style.md +29 -0
- package/skills/ui-system/design-system/mos-design-system.css +240 -0
- package/skills/ui-system/rules/design-system.md +60 -0
- package/skills/ui-system/rules/dual-palette.md +156 -0
- package/skills/ui-system/rules/glyph-disambiguation.md +171 -0
- package/skills/ui-system/rules/shape-f-zero-and-six.md +169 -0
- package/skills/update/SKILL.md +254 -0
- package/skills/user-needs/SKILL.md +73 -0
- package/skills/validate/SKILL.md +73 -0
- package/skills/value-proposition/SKILL.md +141 -0
- package/skills/vault/SKILL.md +326 -0
- package/skills/visualize/SKILL.md +69 -0
- package/skills/whitespace/SKILL.md +539 -0
- package/skills/wiki/SKILL.md +94 -0
- package/templates/_base.html +20 -0
- package/templates/chat-panel.html +205 -0
- package/templates/constellation-chat-hooks.js +89 -0
- package/templates/constellation.html +1272 -0
- package/templates/destijl-base.css +268 -0
- package/templates/discovery/discovery-brief-template.md +97 -0
- package/templates/due-diligence.html +49 -0
- package/templates/executive-summary.html +40 -0
- package/templates/icm/CLAUDE.md +31 -0
- package/templates/icm/GROUP-CLAUDE.md +33 -0
- package/templates/icm/INDEX.md +26 -0
- package/templates/import/stage-contracts/01-ingest.md +34 -0
- package/templates/import/stage-contracts/02-classify.md +48 -0
- package/templates/import/stage-contracts/03-route.md +42 -0
- package/templates/import/stage-contracts/04-enrich.md +40 -0
- package/templates/investment-thesis.html +25 -0
- package/templates/meeting-report.html +361 -0
- package/templates/presentation/dashboard.html +1150 -0
- package/templates/presentation/deck.html +540 -0
- package/templates/presentation/diagrams.html +437 -0
- package/templates/presentation/graph.html +391 -0
- package/templates/presentation/insights.html +548 -0
- package/templates/presentation/wiki.html +533 -0
- package/templates/profile.html +136 -0
- package/templates/room-context/last-session.md +56 -0
- package/templates/room-context/methodology-history.md +28 -0
- package/templates/room-context/rejection-log.md +25 -0
- package/templates/room-context/weekly-digest.md +54 -0
- package/templates/room-skeleton/MINTO.md.tmpl +19 -0
- package/templates/room-skeleton/ROOM.md.identity.tmpl +13 -0
- package/templates/room-skeleton/ROOM.md.section.tmpl +33 -0
- package/templates/room-skeleton/STATE.md.tmpl +33 -0
- package/templates/room-skeleton/USER.md.tmpl +23 -0
- package/templates/room-skeleton/references/.gitkeep +0 -0
- package/templates/room-skeleton/references/SECTION-SCHEMA.md +213 -0
- package/templates/room-skeleton/references/SUB-SCHEMAS.md +91 -0
- package/templates/room-skeleton/section-contracts/.gitkeep +0 -0
- package/templates/room-skeleton/section-contracts/business-model.md +32 -0
- package/templates/room-skeleton/section-contracts/competitive-analysis.md +33 -0
- package/templates/room-skeleton/section-contracts/financial-model.md +30 -0
- package/templates/room-skeleton/section-contracts/funding.md +41 -0
- package/templates/room-skeleton/section-contracts/legal-ip.md +30 -0
- package/templates/room-skeleton/section-contracts/market-analysis.md +32 -0
- package/templates/room-skeleton/section-contracts/opportunity-bank.md +39 -0
- package/templates/room-skeleton/section-contracts/problem-definition.md +32 -0
- package/templates/room-skeleton/section-contracts/solution-design.md +36 -0
- package/templates/room-skeleton/section-contracts/strategy.md +35 -0
- package/templates/room-skeleton/section-contracts/team-execution.md +36 -0
- package/templates/shared.css +427 -0
- package/templates/showcase/blueprint.html +432 -0
- package/templates/showcase/library.html +571 -0
- package/templates/showcase/narrative.html +361 -0
- package/templates/showcase/overview.html +468 -0
- package/templates/showcase/synthesis.html +473 -0
- package/templates/signature-footer.html +34 -0
|
@@ -0,0 +1,2073 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
rs-engine.py -- Reverse Salient Engine (Phase 89)
|
|
4
|
+
==================================================
|
|
5
|
+
|
|
6
|
+
Authoritative port of the Kwan 2023 reverse-salient algorithm. Runs on a
|
|
7
|
+
single room (Mode A) and produces .rs-engine-results.json with pairs scored
|
|
8
|
+
by abs(semantic - lsa) and classified by signed directionality.
|
|
9
|
+
|
|
10
|
+
Algorithmic signature (RESEARCH.md Q1, ALGORITHM-SOURCE.md):
|
|
11
|
+
- Structural similarity: TF-IDF + TruncatedSVD(80) + topic-keyword
|
|
12
|
+
membership counting + L1 pairwise distance + invert-and-rescale.
|
|
13
|
+
This is NOT cosine-on-SVD. See lib/core/rs_math.py docstrings.
|
|
14
|
+
- Semantic similarity: sentence-transformer embeddings (MiniLM local by
|
|
15
|
+
default; multilingual-e5-large via Pinecone inference when available).
|
|
16
|
+
- Detection: iterative argmax on |semantic - lsa| with symmetric cleanup.
|
|
17
|
+
- Classification: signed (semantic - lsa) -> structural_transfer or
|
|
18
|
+
semantic_implementation.
|
|
19
|
+
|
|
20
|
+
Embedding model is configurable per RESEARCH.md Q4:
|
|
21
|
+
- RS_EMBEDDING_MODEL unset (default) -> multilingual-e5-large via Pinecone
|
|
22
|
+
inference (Plan 89-03 wires the cold path).
|
|
23
|
+
- RS_EMBEDDING_MODEL=minilm -> local all-MiniLM-L6-v2.
|
|
24
|
+
- RS_EMBEDDING_MODEL=bert-large-cased -> explicit repro mode (heavy).
|
|
25
|
+
|
|
26
|
+
Mode A (this plan, 89-01): internal single-room scan.
|
|
27
|
+
Mode B/C extend this script in Plans 89-04 / 89-05 (multi-room + external
|
|
28
|
+
corpus + hybrid). The mode dispatch is already wired here so those plans
|
|
29
|
+
only add helpers.
|
|
30
|
+
|
|
31
|
+
Three-surface usage:
|
|
32
|
+
CLI: python scripts/rs-engine.py --mode internal --room ./room
|
|
33
|
+
Desktop: invoked by ReverseSalientAgent conversational trigger (Plan 89-07).
|
|
34
|
+
Cowork: writes symlink into 00_Context/rs-engine-results.json when
|
|
35
|
+
COWORK=1 is set.
|
|
36
|
+
|
|
37
|
+
License: BSL-1.1 (see LICENSE at repo root).
|
|
38
|
+
"""
|
|
39
|
+
|
|
40
|
+
from __future__ import annotations
|
|
41
|
+
|
|
42
|
+
import argparse
|
|
43
|
+
import hashlib
|
|
44
|
+
import json
|
|
45
|
+
import os
|
|
46
|
+
import re
|
|
47
|
+
import sqlite3
|
|
48
|
+
import sys
|
|
49
|
+
from datetime import datetime, timezone
|
|
50
|
+
from pathlib import Path
|
|
51
|
+
from typing import Any, Dict, List, Optional, Sequence, Tuple
|
|
52
|
+
|
|
53
|
+
# Auto-install Python ML deps if missing (v1.10.9, plan 85-10, LAWRENCE-001).
|
|
54
|
+
# rs-engine.py is the direct entry point for /mos:find-bottlenecks (invoked by
|
|
55
|
+
# lib/agents/reverse-salient-agent.cjs); it never had this wiring even though
|
|
56
|
+
# sibling whitespace scripts did (RCA phase-134-python-elimination-false-
|
|
57
|
+
# complete -- Change 1). `requests` is included here (not just numpy) because
|
|
58
|
+
# it is a transitive import in lib/core/rs_corpus.py, loaded a few lines below
|
|
59
|
+
# this block -- and `requests` was the actual root cause of the original
|
|
60
|
+
# Windows tester 2026-05-23 silent-failure class this file's own comments
|
|
61
|
+
# reference (see runRsEngine's diagnostic-forwarding logic in
|
|
62
|
+
# reverse-salient-agent.cjs).
|
|
63
|
+
sys.path.insert(0, str(Path(__file__).resolve().parent / "lib"))
|
|
64
|
+
from ensure_ml_deps import ensure
|
|
65
|
+
ensure(["numpy", "requests"])
|
|
66
|
+
|
|
67
|
+
# --- Guarded imports ---------------------------------------------------------
|
|
68
|
+
|
|
69
|
+
try:
|
|
70
|
+
import numpy as np
|
|
71
|
+
except ImportError:
|
|
72
|
+
print(
|
|
73
|
+
"rs-engine requires numpy. Run: pip install -r requirements-hsi.txt",
|
|
74
|
+
file=sys.stderr,
|
|
75
|
+
)
|
|
76
|
+
sys.exit(1)
|
|
77
|
+
|
|
78
|
+
# Import shared math helpers. rs-engine.py is invoked from the repo root so
|
|
79
|
+
# the "lib.core.rs_math" package path is canonical (plan verify block uses
|
|
80
|
+
# this exact import).
|
|
81
|
+
_THIS_FILE = Path(__file__).resolve()
|
|
82
|
+
_REPO_ROOT = _THIS_FILE.parent.parent
|
|
83
|
+
if str(_REPO_ROOT) not in sys.path:
|
|
84
|
+
sys.path.insert(0, str(_REPO_ROOT))
|
|
85
|
+
|
|
86
|
+
from lib.core.rs_math import ( # noqa: E402
|
|
87
|
+
abs_diff_topk,
|
|
88
|
+
build_lsa_matrix,
|
|
89
|
+
classify_direction,
|
|
90
|
+
)
|
|
91
|
+
from lib.core.rs_corpus import ( # noqa: E402
|
|
92
|
+
fetch_corpus,
|
|
93
|
+
topic_slug,
|
|
94
|
+
semantic_gate,
|
|
95
|
+
SEMANTIC_FLOOR,
|
|
96
|
+
)
|
|
97
|
+
from lib.core.rs_rooms import ( # noqa: E402
|
|
98
|
+
load_multi_room_corpus,
|
|
99
|
+
summarize_corpus,
|
|
100
|
+
)
|
|
101
|
+
# Plan 89-05 wires Mode C (hybrid) via rs_hybrid. Import is optional at the
|
|
102
|
+
# top so Mode A / Mode B remain importable when rs_hybrid is missing; the
|
|
103
|
+
# hybrid dispatch in main() raises a clear error if the import failed.
|
|
104
|
+
try:
|
|
105
|
+
from lib.core.rs_hybrid import ( # noqa: E402
|
|
106
|
+
build_unified_corpus as _rs_hybrid_build,
|
|
107
|
+
filter_cross_corpus_pairs as _rs_hybrid_filter,
|
|
108
|
+
)
|
|
109
|
+
_RS_HYBRID_AVAILABLE = True
|
|
110
|
+
except Exception as _rs_hybrid_import_err: # pragma: no cover -- defensive
|
|
111
|
+
_RS_HYBRID_AVAILABLE = False
|
|
112
|
+
_rs_hybrid_import_err_msg = str(_rs_hybrid_import_err)
|
|
113
|
+
# Plan 89-03 wires the Pinecone rs-external cache for Mode B. The import is
|
|
114
|
+
# optional so Mode A (internal) remains usable without Pinecone installed --
|
|
115
|
+
# rs_cache has a guarded import of the pinecone SDK.
|
|
116
|
+
try:
|
|
117
|
+
from lib.core.rs_cache import ( # noqa: E402
|
|
118
|
+
namespace_slug as _rs_cache_namespace_slug,
|
|
119
|
+
get_namespace_freshness as _rs_cache_freshness,
|
|
120
|
+
upsert_corpus as _rs_cache_upsert,
|
|
121
|
+
fetch_all_from_namespace as _rs_cache_fetch_all,
|
|
122
|
+
is_fresh as _rs_cache_is_fresh,
|
|
123
|
+
TTL_DAYS as _RS_CACHE_TTL_DAYS,
|
|
124
|
+
)
|
|
125
|
+
# Module reference (not just the named re-exports above) so the SEED-018
|
|
126
|
+
# semantic gate can call rs_cache._embed_via_bridge directly and build its
|
|
127
|
+
# topic vector from the SAME local encoder that produced the record
|
|
128
|
+
# vectors it gates -- see _gate_records_cached below (Plan 296-05).
|
|
129
|
+
from lib.core import rs_cache
|
|
130
|
+
_RS_CACHE_AVAILABLE = True
|
|
131
|
+
except Exception as _rs_cache_import_err: # pragma: no cover -- defensive
|
|
132
|
+
_RS_CACHE_AVAILABLE = False
|
|
133
|
+
_rs_cache_import_err_msg = str(_rs_cache_import_err)
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
# --- Constants ---------------------------------------------------------------
|
|
137
|
+
|
|
138
|
+
DEFAULT_EMBEDDING_MODEL = "multilingual-e5-large"
|
|
139
|
+
# SKIP_FILES / SKIP_DIRS from the ONE shared source (Phase 200-01, SEED-018).
|
|
140
|
+
# Was a local copy that the other walkers drifted from; now single-sourced.
|
|
141
|
+
from lib.core.rs_corpus_exclude import SKIP_DIRS, SKIP_FILES # noqa: E402
|
|
142
|
+
CACHE_FILENAME = ".rs-engine-cache.json"
|
|
143
|
+
RESULTS_FILENAME = ".rs-engine-results.json"
|
|
144
|
+
DEFAULT_TOPK = 100
|
|
145
|
+
DEFAULT_THRESHOLD = 0.30
|
|
146
|
+
ENGINE_VERSION = "1"
|
|
147
|
+
|
|
148
|
+
# Mode C (hybrid) post-filter overshoot. After abs-diff top-k over the
|
|
149
|
+
# unified corpus, we discard any intra-corpus pair (room-room or
|
|
150
|
+
# external-external) and keep only cross-corpus pairs. An overshoot of
|
|
151
|
+
# 10x the requested topk gives enough headroom to reach topk on realistic
|
|
152
|
+
# corpora where room artifacts (O(100)) are dwarfed by external docs
|
|
153
|
+
# (O(2000)) and most of the strongest abs-diff pairs would be
|
|
154
|
+
# external-external by volume alone. Smaller overshoots silently shrink
|
|
155
|
+
# delivered pair count below topk; larger overshoots waste O(k log k)
|
|
156
|
+
# sort work. Plan 89-05 Detailed Steps specify 10x.
|
|
157
|
+
HYBRID_OVERSHOOT = 10
|
|
158
|
+
|
|
159
|
+
|
|
160
|
+
# --- Artifact discovery ------------------------------------------------------
|
|
161
|
+
|
|
162
|
+
def parse_frontmatter(content: str) -> Dict[str, str]:
|
|
163
|
+
fm_match = re.match(r"^---\n([\s\S]*?)\n---", content)
|
|
164
|
+
if not fm_match:
|
|
165
|
+
return {}
|
|
166
|
+
fields = {}
|
|
167
|
+
for line in fm_match.group(1).split("\n"):
|
|
168
|
+
if ":" in line:
|
|
169
|
+
key, _, val = line.partition(":")
|
|
170
|
+
fields[key.strip()] = val.strip().strip('"').strip("'")
|
|
171
|
+
return fields
|
|
172
|
+
|
|
173
|
+
|
|
174
|
+
def extract_title(content: str, filepath: Path) -> str:
|
|
175
|
+
match = re.search(r"^# (.+)$", content, re.MULTILINE)
|
|
176
|
+
if match:
|
|
177
|
+
return match.group(1).strip()
|
|
178
|
+
return filepath.stem.replace("-", " ").title()
|
|
179
|
+
|
|
180
|
+
|
|
181
|
+
def extract_body(content: str) -> str:
|
|
182
|
+
fm_match = re.match(r"^---\n[\s\S]*?\n---\n?", content)
|
|
183
|
+
if fm_match:
|
|
184
|
+
return content[fm_match.end():]
|
|
185
|
+
return content
|
|
186
|
+
|
|
187
|
+
|
|
188
|
+
def discover_artifacts(room_dir: Path) -> List[Dict]:
|
|
189
|
+
"""Walk room_dir for .md artifacts. Mirrors compute-hsi.py pattern.
|
|
190
|
+
|
|
191
|
+
The authoritative source's artifact loader assumed a flat SQLite
|
|
192
|
+
"artifacts" table. The real MindrianOS schema (lib/core/lazygraph-ops.cjs)
|
|
193
|
+
keeps artifacts on disk as .md files and tracks them in the nodes table
|
|
194
|
+
by reference. Plan 89-01 said to read room.db artifacts directly; that
|
|
195
|
+
table does not exist. We walk the filesystem instead (same pattern as
|
|
196
|
+
compute-hsi.py) so the port works against real rooms, and cache
|
|
197
|
+
embeddings in a JSON sidecar below. This is a documented Rule 3 fix.
|
|
198
|
+
"""
|
|
199
|
+
artifacts: List[Dict] = []
|
|
200
|
+
room_path = room_dir.resolve()
|
|
201
|
+
|
|
202
|
+
for root, dirs, files in os.walk(room_path):
|
|
203
|
+
dirs[:] = [d for d in dirs if d not in SKIP_DIRS]
|
|
204
|
+
rel_root = Path(root).relative_to(room_path)
|
|
205
|
+
if str(rel_root) == ".":
|
|
206
|
+
# Skip room-root files -- not part of any section.
|
|
207
|
+
continue
|
|
208
|
+
section = str(rel_root).split(os.sep)[0]
|
|
209
|
+
for fname in sorted(files):
|
|
210
|
+
if not fname.endswith(".md"):
|
|
211
|
+
continue
|
|
212
|
+
if fname in SKIP_FILES:
|
|
213
|
+
continue
|
|
214
|
+
fpath = Path(root) / fname
|
|
215
|
+
try:
|
|
216
|
+
content = fpath.read_text(encoding="utf-8")
|
|
217
|
+
except (OSError, UnicodeDecodeError):
|
|
218
|
+
continue
|
|
219
|
+
body = extract_body(content).strip()
|
|
220
|
+
if len(body) < 50:
|
|
221
|
+
continue
|
|
222
|
+
artifact_id = str(rel_root / Path(fname).stem).replace(os.sep, "/")
|
|
223
|
+
artifacts.append({
|
|
224
|
+
"id": artifact_id,
|
|
225
|
+
"section": section,
|
|
226
|
+
"title": extract_title(content, fpath),
|
|
227
|
+
"path": str(fpath.relative_to(room_path)),
|
|
228
|
+
"text": body,
|
|
229
|
+
})
|
|
230
|
+
return artifacts
|
|
231
|
+
|
|
232
|
+
|
|
233
|
+
# --- Embedding path ---------------------------------------------------------
|
|
234
|
+
|
|
235
|
+
def _content_hash(text: str) -> str:
|
|
236
|
+
return hashlib.sha256(text.encode("utf-8")).hexdigest()[:16]
|
|
237
|
+
|
|
238
|
+
|
|
239
|
+
def _load_embedding_cache(room_dir: Path) -> Dict[str, Dict]:
|
|
240
|
+
cache_path = room_dir / CACHE_FILENAME
|
|
241
|
+
if not cache_path.exists():
|
|
242
|
+
return {}
|
|
243
|
+
try:
|
|
244
|
+
raw = json.loads(cache_path.read_text(encoding="utf-8"))
|
|
245
|
+
if not isinstance(raw, dict) or raw.get("version") != ENGINE_VERSION:
|
|
246
|
+
return {}
|
|
247
|
+
embeddings = raw.get("embeddings", {})
|
|
248
|
+
return embeddings if isinstance(embeddings, dict) else {}
|
|
249
|
+
except (json.JSONDecodeError, OSError):
|
|
250
|
+
return {}
|
|
251
|
+
|
|
252
|
+
|
|
253
|
+
def _save_embedding_cache(
|
|
254
|
+
room_dir: Path,
|
|
255
|
+
cache: Dict[str, Dict],
|
|
256
|
+
model_name: str,
|
|
257
|
+
) -> None:
|
|
258
|
+
cache_path = room_dir / CACHE_FILENAME
|
|
259
|
+
payload = {
|
|
260
|
+
"version": ENGINE_VERSION,
|
|
261
|
+
"updated_at": datetime.now(timezone.utc).isoformat(),
|
|
262
|
+
"model": model_name,
|
|
263
|
+
"embeddings": cache,
|
|
264
|
+
}
|
|
265
|
+
try:
|
|
266
|
+
tmp_path = cache_path.with_suffix(cache_path.suffix + ".tmp")
|
|
267
|
+
tmp_path.write_text(json.dumps(payload, indent=2), encoding="utf-8")
|
|
268
|
+
tmp_path.replace(cache_path)
|
|
269
|
+
except OSError as e:
|
|
270
|
+
print(f"rs-engine: cache write failed: {e}", file=sys.stderr)
|
|
271
|
+
|
|
272
|
+
|
|
273
|
+
def _embed_local_minilm(texts: Sequence[str]) -> Optional[np.ndarray]:
|
|
274
|
+
try:
|
|
275
|
+
from sentence_transformers import SentenceTransformer
|
|
276
|
+
except ImportError:
|
|
277
|
+
print(
|
|
278
|
+
"rs-engine: sentence-transformers not installed; "
|
|
279
|
+
"run `pip install -r requirements-hsi.txt`",
|
|
280
|
+
file=sys.stderr,
|
|
281
|
+
)
|
|
282
|
+
return None
|
|
283
|
+
model = SentenceTransformer("all-MiniLM-L6-v2")
|
|
284
|
+
vectors = model.encode(list(texts), show_progress_bar=False)
|
|
285
|
+
return np.asarray(vectors, dtype=np.float32)
|
|
286
|
+
|
|
287
|
+
|
|
288
|
+
def _embed_via_pinecone_inference(texts: Sequence[str]) -> Optional[np.ndarray]:
|
|
289
|
+
"""Cold path: call Pinecone inference API for multilingual-e5-large.
|
|
290
|
+
|
|
291
|
+
Plan-check Gap 1 (PLAN-CHECK.md) explicitly documents that this entry is
|
|
292
|
+
stubbed in Plan 89-01 and completed in Plans 89-03 / 89-05. Raising
|
|
293
|
+
NotImplementedError keeps the failure loud and localizable if 89-03 is
|
|
294
|
+
skipped, rather than silently falling back to MiniLM.
|
|
295
|
+
|
|
296
|
+
The plan authorizes this stub: PLAN-CHECK Gap 1 resolution says "89-01
|
|
297
|
+
must stub with NotImplementedError for the cold path, documented as
|
|
298
|
+
'completed in 89-03 / 89-05.'"
|
|
299
|
+
"""
|
|
300
|
+
raise NotImplementedError(
|
|
301
|
+
"Pinecone inference embedding is wired in Plan 89-03 / 89-05. "
|
|
302
|
+
"For Plan 89-01 (Mode A internal), set RS_EMBEDDING_MODEL=minilm "
|
|
303
|
+
"or rely on cached embeddings in .rs-engine-cache.json."
|
|
304
|
+
)
|
|
305
|
+
|
|
306
|
+
|
|
307
|
+
def compute_embeddings(
|
|
308
|
+
artifacts: Sequence[Dict],
|
|
309
|
+
room_dir: Path,
|
|
310
|
+
) -> Tuple[Optional[np.ndarray], str]:
|
|
311
|
+
"""Return (embedding_matrix, model_name). Uses cache where possible.
|
|
312
|
+
|
|
313
|
+
Precedence:
|
|
314
|
+
1. RS_EMBEDDING_MODEL=minilm OR default-but-Pinecone-unavailable
|
|
315
|
+
-> local MiniLM.
|
|
316
|
+
2. RS_EMBEDDING_MODEL=bert-large-cased -> repro path (not implemented
|
|
317
|
+
in 89-01; deferred to a future plan).
|
|
318
|
+
3. Default (no env var) AND Pinecone inference wired
|
|
319
|
+
-> multilingual-e5-large (stub raises; see docstring).
|
|
320
|
+
"""
|
|
321
|
+
model_env = os.environ.get("RS_EMBEDDING_MODEL", "").strip().lower()
|
|
322
|
+
|
|
323
|
+
# Fall back to MiniLM when Pinecone stub cannot serve the cold path.
|
|
324
|
+
cold_model = "all-MiniLM-L6-v2" if model_env in ("", "minilm") else model_env
|
|
325
|
+
cache = _load_embedding_cache(room_dir)
|
|
326
|
+
|
|
327
|
+
# Resolve cache hits vs misses.
|
|
328
|
+
missing_indices: List[int] = []
|
|
329
|
+
dim: Optional[int] = None
|
|
330
|
+
vectors: List[Optional[np.ndarray]] = [None] * len(artifacts)
|
|
331
|
+
for idx, art in enumerate(artifacts):
|
|
332
|
+
digest = _content_hash(art["text"])
|
|
333
|
+
cache_entry = cache.get(art["id"])
|
|
334
|
+
if (
|
|
335
|
+
isinstance(cache_entry, dict)
|
|
336
|
+
and cache_entry.get("hash") == digest
|
|
337
|
+
and cache_entry.get("model") == cold_model
|
|
338
|
+
and isinstance(cache_entry.get("vector"), list)
|
|
339
|
+
):
|
|
340
|
+
vec = np.asarray(cache_entry["vector"], dtype=np.float32)
|
|
341
|
+
vectors[idx] = vec
|
|
342
|
+
if dim is None:
|
|
343
|
+
dim = vec.shape[0]
|
|
344
|
+
else:
|
|
345
|
+
missing_indices.append(idx)
|
|
346
|
+
|
|
347
|
+
if missing_indices:
|
|
348
|
+
missing_texts = [artifacts[i]["text"] for i in missing_indices]
|
|
349
|
+
new_vectors: Optional[np.ndarray] = None
|
|
350
|
+
model_used = cold_model
|
|
351
|
+
|
|
352
|
+
if model_env == "minilm" or model_env == "" and cold_model == "all-MiniLM-L6-v2":
|
|
353
|
+
# MiniLM is the honest default until 89-03 wires Pinecone.
|
|
354
|
+
new_vectors = _embed_local_minilm(missing_texts)
|
|
355
|
+
model_used = "all-MiniLM-L6-v2"
|
|
356
|
+
elif model_env == DEFAULT_EMBEDDING_MODEL or model_env == "e5-large":
|
|
357
|
+
# Pinecone inference path. Plan 89-01 stub -> NotImplementedError.
|
|
358
|
+
new_vectors = _embed_via_pinecone_inference(missing_texts)
|
|
359
|
+
model_used = DEFAULT_EMBEDDING_MODEL
|
|
360
|
+
elif model_env == "bert-large-cased":
|
|
361
|
+
# Repro path -- not wired in 89-01; raise clear error.
|
|
362
|
+
raise NotImplementedError(
|
|
363
|
+
"RS_EMBEDDING_MODEL=bert-large-cased is not wired in Plan 89-01. "
|
|
364
|
+
"Use RS_EMBEDDING_MODEL=minilm for this plan."
|
|
365
|
+
)
|
|
366
|
+
else:
|
|
367
|
+
# Unknown model name -- default to MiniLM with a warning.
|
|
368
|
+
print(
|
|
369
|
+
f"rs-engine: unknown RS_EMBEDDING_MODEL={model_env!r}; "
|
|
370
|
+
"falling back to local MiniLM",
|
|
371
|
+
file=sys.stderr,
|
|
372
|
+
)
|
|
373
|
+
new_vectors = _embed_local_minilm(missing_texts)
|
|
374
|
+
model_used = "all-MiniLM-L6-v2"
|
|
375
|
+
|
|
376
|
+
if new_vectors is None:
|
|
377
|
+
return None, model_used
|
|
378
|
+
|
|
379
|
+
for offset, idx in enumerate(missing_indices):
|
|
380
|
+
vec = new_vectors[offset]
|
|
381
|
+
vectors[idx] = vec
|
|
382
|
+
if dim is None:
|
|
383
|
+
dim = vec.shape[0]
|
|
384
|
+
cache[artifacts[idx]["id"]] = {
|
|
385
|
+
"hash": _content_hash(artifacts[idx]["text"]),
|
|
386
|
+
"model": model_used,
|
|
387
|
+
"vector": vec.astype(np.float32).tolist(),
|
|
388
|
+
}
|
|
389
|
+
_save_embedding_cache(room_dir, cache, model_used)
|
|
390
|
+
cold_model = model_used
|
|
391
|
+
|
|
392
|
+
matrix = np.vstack([v for v in vectors if v is not None])
|
|
393
|
+
return matrix, cold_model
|
|
394
|
+
|
|
395
|
+
|
|
396
|
+
def semantic_similarity_matrix(embeddings: np.ndarray) -> np.ndarray:
|
|
397
|
+
"""Cosine similarity on row-normalized embedding vectors."""
|
|
398
|
+
from sklearn.metrics.pairwise import cosine_similarity
|
|
399
|
+
sim = cosine_similarity(embeddings)
|
|
400
|
+
return np.clip(sim, 0.0, 1.0).astype(np.float32)
|
|
401
|
+
|
|
402
|
+
|
|
403
|
+
# --- SQLite edge writer ------------------------------------------------------
|
|
404
|
+
|
|
405
|
+
# System provenance defaults for rs-engine's own node writes. Mirrors
|
|
406
|
+
# lib/core/node-insert.cjs's SYSTEM_SOURCE_PATH / SYSTEM_CREATED_BY, using a
|
|
407
|
+
# distinct source_path handle ('system:rs-engine' vs the JS module's
|
|
408
|
+
# 'system:hsi-to-graph') so RS-written nodes stay distinguishable from
|
|
409
|
+
# HSI-written ones per the RCA's Change 1 requirement.
|
|
410
|
+
RS_ENGINE_SOURCE_PATH = "system:rs-engine"
|
|
411
|
+
RS_ENGINE_CREATED_BY = "system"
|
|
412
|
+
|
|
413
|
+
|
|
414
|
+
def _now_ms() -> int:
|
|
415
|
+
"""Epoch milliseconds, matching the INTEGER created_at/last_seen_at
|
|
416
|
+
columns the Phase-109 migration writes (see phase-109-nodes-provenance.cjs,
|
|
417
|
+
which stores JS Date.now()-shaped epoch-ms integers)."""
|
|
418
|
+
return int(datetime.now(timezone.utc).timestamp() * 1000)
|
|
419
|
+
|
|
420
|
+
|
|
421
|
+
def _nodes_table_is_migrated(conn: sqlite3.Connection) -> bool:
|
|
422
|
+
"""Detect the Phase-109 provenance schema on the nodes table.
|
|
423
|
+
|
|
424
|
+
Mirrors lib/core/node-insert.cjs's isMigratedSchema: PRAGMA table_info(nodes)
|
|
425
|
+
and check for the source_path column. Defensive: any PRAGMA failure falls
|
|
426
|
+
back to the legacy 3-column shape (the wide insert would throw on a real
|
|
427
|
+
legacy db, so defaulting to legacy is the safe choice -- same rationale as
|
|
428
|
+
the JS helper).
|
|
429
|
+
"""
|
|
430
|
+
try:
|
|
431
|
+
cols = conn.execute("PRAGMA table_info(nodes)").fetchall()
|
|
432
|
+
# PRAGMA table_info row shape: (cid, name, type, notnull, dflt_value, pk)
|
|
433
|
+
return any(row[1] == "source_path" for row in cols)
|
|
434
|
+
except sqlite3.Error:
|
|
435
|
+
return False
|
|
436
|
+
|
|
437
|
+
|
|
438
|
+
def _upsert_node(
|
|
439
|
+
conn: sqlite3.Connection,
|
|
440
|
+
migrated: bool,
|
|
441
|
+
node_id: str,
|
|
442
|
+
node_type: str,
|
|
443
|
+
properties_json: str,
|
|
444
|
+
) -> None:
|
|
445
|
+
"""NOT-NULL-safe node upsert (RCA rs-engine-python-insert-not-null-and-detail-drop-regression).
|
|
446
|
+
|
|
447
|
+
Mirrors lib/core/node-insert.cjs's insertNode contract in Python. The bare
|
|
448
|
+
3-column insert this replaces crashed with
|
|
449
|
+
`NOT NULL constraint failed: nodes.source_path` on any room.db carrying
|
|
450
|
+
the Phase-109 wide nodes schema, since node-insert.cjs's Phase 140-01 fix
|
|
451
|
+
only covers its 4 JS call sites and this script is Python.
|
|
452
|
+
|
|
453
|
+
On the migrated (wide) schema, supplies the four NOT NULL provenance
|
|
454
|
+
columns with rs-engine's own system-write defaults. On the legacy
|
|
455
|
+
3-column schema, keeps the original bare insert unchanged.
|
|
456
|
+
"""
|
|
457
|
+
if migrated:
|
|
458
|
+
now_ms = _now_ms()
|
|
459
|
+
conn.execute(
|
|
460
|
+
"INSERT INTO nodes "
|
|
461
|
+
"(id, type, properties, source_path, created_by, created_at, last_seen_at) "
|
|
462
|
+
"VALUES (?, ?, ?, ?, ?, ?, ?) "
|
|
463
|
+
"ON CONFLICT(id) DO UPDATE SET "
|
|
464
|
+
"properties = excluded.properties, last_seen_at = excluded.last_seen_at",
|
|
465
|
+
(
|
|
466
|
+
node_id,
|
|
467
|
+
node_type,
|
|
468
|
+
properties_json,
|
|
469
|
+
RS_ENGINE_SOURCE_PATH,
|
|
470
|
+
RS_ENGINE_CREATED_BY,
|
|
471
|
+
now_ms,
|
|
472
|
+
now_ms,
|
|
473
|
+
),
|
|
474
|
+
)
|
|
475
|
+
return
|
|
476
|
+
conn.execute(
|
|
477
|
+
"INSERT INTO nodes (id, type, properties) VALUES (?, ?, ?) "
|
|
478
|
+
"ON CONFLICT(id) DO UPDATE SET properties = excluded.properties",
|
|
479
|
+
(node_id, node_type, properties_json),
|
|
480
|
+
)
|
|
481
|
+
|
|
482
|
+
|
|
483
|
+
def write_reverse_salient_edges(
|
|
484
|
+
room_dir: Path,
|
|
485
|
+
pairs: Sequence[Dict],
|
|
486
|
+
) -> int:
|
|
487
|
+
"""Insert REVERSE_SALIENT edges into room.db carrying source='rs-engine'.
|
|
488
|
+
|
|
489
|
+
Plan 89-01 said to ALTER the REVERSE_SALIENT table to add a `source`
|
|
490
|
+
column; REVERSE_SALIENT is an edge TYPE not a table. The real schema
|
|
491
|
+
(lib/core/lazygraph-ops.cjs) uses a single `edges` table with a
|
|
492
|
+
flexible `properties` JSON column. Rule 3 adaptation: we namespace our
|
|
493
|
+
edges by writing `source: 'rs-engine'` inside properties, and we DO NOT
|
|
494
|
+
delete edges written by hsi-to-graph.cjs (which carry source='hsi'
|
|
495
|
+
implicitly or via future backfill). Coexistence is the RESEARCH.md Q1
|
|
496
|
+
decision (b) invariant.
|
|
497
|
+
|
|
498
|
+
Returns the number of edges actually inserted.
|
|
499
|
+
"""
|
|
500
|
+
db_path = room_dir / ".mindrian" / "room.db"
|
|
501
|
+
if not db_path.exists():
|
|
502
|
+
# Room has no graph yet -- skip silently; JSON output is still the
|
|
503
|
+
# authoritative artifact for Plan 89-01.
|
|
504
|
+
return 0
|
|
505
|
+
|
|
506
|
+
conn = sqlite3.connect(str(db_path))
|
|
507
|
+
try:
|
|
508
|
+
conn.execute("PRAGMA journal_mode=WAL")
|
|
509
|
+
# Ensure the edges table exists (rooms initialized outside lazygraph).
|
|
510
|
+
conn.execute(
|
|
511
|
+
"""
|
|
512
|
+
CREATE TABLE IF NOT EXISTS edges (
|
|
513
|
+
source TEXT NOT NULL,
|
|
514
|
+
target TEXT NOT NULL,
|
|
515
|
+
type TEXT NOT NULL,
|
|
516
|
+
properties TEXT DEFAULT '{}',
|
|
517
|
+
PRIMARY KEY (source, target, type)
|
|
518
|
+
)
|
|
519
|
+
"""
|
|
520
|
+
)
|
|
521
|
+
conn.execute(
|
|
522
|
+
"""
|
|
523
|
+
CREATE TABLE IF NOT EXISTS nodes (
|
|
524
|
+
id TEXT PRIMARY KEY,
|
|
525
|
+
type TEXT NOT NULL,
|
|
526
|
+
properties TEXT DEFAULT '{}'
|
|
527
|
+
)
|
|
528
|
+
"""
|
|
529
|
+
)
|
|
530
|
+
|
|
531
|
+
# Cleanup: remove only rs-engine-sourced edges (json_extract safely
|
|
532
|
+
# returns NULL on missing key, so non-rs-engine edges are preserved).
|
|
533
|
+
conn.execute(
|
|
534
|
+
"DELETE FROM edges WHERE type = 'REVERSE_SALIENT' "
|
|
535
|
+
"AND json_extract(properties, '$.source') = 'rs-engine'"
|
|
536
|
+
)
|
|
537
|
+
|
|
538
|
+
upsert_edge = (
|
|
539
|
+
"INSERT INTO edges (source, target, type, properties) "
|
|
540
|
+
"VALUES (?, ?, ?, ?) ON CONFLICT(source, target, type) "
|
|
541
|
+
"DO UPDATE SET properties = excluded.properties"
|
|
542
|
+
)
|
|
543
|
+
# Detect schema ONCE per invocation (D-02a both-schema safety, mirrors
|
|
544
|
+
# node-insert.cjs's per-call PRAGMA check): a migrated db requires the
|
|
545
|
+
# wide insert, a legacy 3-col db (created by the CREATE TABLE IF NOT
|
|
546
|
+
# EXISTS above, on a room with no prior migration) rejects it.
|
|
547
|
+
migrated = _nodes_table_is_migrated(conn)
|
|
548
|
+
|
|
549
|
+
written = 0
|
|
550
|
+
for pair in pairs:
|
|
551
|
+
src = pair.get("source_artifact_id")
|
|
552
|
+
tgt = pair.get("target_artifact_id")
|
|
553
|
+
if not src or not tgt:
|
|
554
|
+
continue
|
|
555
|
+
# Upsert artifact nodes as a courtesy so downstream readers can
|
|
556
|
+
# resolve them without relying on compute-hsi having run first.
|
|
557
|
+
_upsert_node(
|
|
558
|
+
conn,
|
|
559
|
+
migrated,
|
|
560
|
+
src,
|
|
561
|
+
"Artifact",
|
|
562
|
+
json.dumps({
|
|
563
|
+
"title": pair.get("source_title", ""),
|
|
564
|
+
"section": pair.get("source_section", ""),
|
|
565
|
+
}),
|
|
566
|
+
)
|
|
567
|
+
_upsert_node(
|
|
568
|
+
conn,
|
|
569
|
+
migrated,
|
|
570
|
+
tgt,
|
|
571
|
+
"Artifact",
|
|
572
|
+
json.dumps({
|
|
573
|
+
"title": pair.get("target_title", ""),
|
|
574
|
+
"section": pair.get("target_section", ""),
|
|
575
|
+
}),
|
|
576
|
+
)
|
|
577
|
+
props = json.dumps({
|
|
578
|
+
"source": "rs-engine",
|
|
579
|
+
"lsa_score": pair["lsa_score"],
|
|
580
|
+
"semantic_score": pair["semantic_score"],
|
|
581
|
+
"signed_diff": pair["signed_diff"],
|
|
582
|
+
"abs_diff": pair["abs_diff"],
|
|
583
|
+
"direction": pair["direction"],
|
|
584
|
+
"innovation_type": pair["direction"],
|
|
585
|
+
})
|
|
586
|
+
conn.execute(upsert_edge, (src, tgt, "REVERSE_SALIENT", props))
|
|
587
|
+
written += 1
|
|
588
|
+
conn.commit()
|
|
589
|
+
return written
|
|
590
|
+
finally:
|
|
591
|
+
conn.close()
|
|
592
|
+
|
|
593
|
+
|
|
594
|
+
# --- Mode A runner -----------------------------------------------------------
|
|
595
|
+
|
|
596
|
+
def run_mode_internal(
|
|
597
|
+
room_dir: Path,
|
|
598
|
+
topk: int,
|
|
599
|
+
threshold: float,
|
|
600
|
+
no_thesis: bool,
|
|
601
|
+
) -> Dict:
|
|
602
|
+
artifacts = discover_artifacts(room_dir)
|
|
603
|
+
if len(artifacts) < 2:
|
|
604
|
+
return {
|
|
605
|
+
"metadata": {
|
|
606
|
+
"mode": "internal",
|
|
607
|
+
"room_dir": str(room_dir),
|
|
608
|
+
"artifact_count": len(artifacts),
|
|
609
|
+
"timestamp": datetime.now(timezone.utc).isoformat(),
|
|
610
|
+
"engine_version": ENGINE_VERSION,
|
|
611
|
+
},
|
|
612
|
+
"pairs": [],
|
|
613
|
+
}
|
|
614
|
+
|
|
615
|
+
texts = [f"{a['title']}\n{a['text']}" for a in artifacts]
|
|
616
|
+
|
|
617
|
+
# Structural signal: authoritative topic-keyword-membership LSA.
|
|
618
|
+
lsa_matrix = build_lsa_matrix(texts)
|
|
619
|
+
|
|
620
|
+
# Semantic signal: embedding cosine.
|
|
621
|
+
embeddings, model_used = compute_embeddings(artifacts, room_dir)
|
|
622
|
+
if embeddings is None:
|
|
623
|
+
# Fallback: treat semantic as identity so abs_diff collapses to 1-lsa.
|
|
624
|
+
# This keeps the engine honest when no embedder is available.
|
|
625
|
+
print(
|
|
626
|
+
"rs-engine: no embedder available; semantic matrix set to identity",
|
|
627
|
+
file=sys.stderr,
|
|
628
|
+
)
|
|
629
|
+
n = len(artifacts)
|
|
630
|
+
sem_matrix = np.eye(n, dtype=np.float32)
|
|
631
|
+
else:
|
|
632
|
+
sem_matrix = semantic_similarity_matrix(embeddings)
|
|
633
|
+
|
|
634
|
+
top_pairs = abs_diff_topk(lsa_matrix, sem_matrix, k=topk)
|
|
635
|
+
|
|
636
|
+
pair_dicts: List[Dict] = []
|
|
637
|
+
for i, j, signed, absv in top_pairs:
|
|
638
|
+
if absv < threshold:
|
|
639
|
+
continue
|
|
640
|
+
a_i, a_j = artifacts[i], artifacts[j]
|
|
641
|
+
pair_dicts.append({
|
|
642
|
+
"source_artifact_id": a_i["id"],
|
|
643
|
+
"source_title": a_i["title"],
|
|
644
|
+
"source_section": a_i["section"],
|
|
645
|
+
"target_artifact_id": a_j["id"],
|
|
646
|
+
"target_title": a_j["title"],
|
|
647
|
+
"target_section": a_j["section"],
|
|
648
|
+
"lsa_score": round(float(lsa_matrix[i, j]), 4),
|
|
649
|
+
"semantic_score": round(float(sem_matrix[i, j]), 4),
|
|
650
|
+
"signed_diff": round(float(signed), 4),
|
|
651
|
+
"abs_diff": round(float(absv), 4),
|
|
652
|
+
"direction": classify_direction(signed),
|
|
653
|
+
})
|
|
654
|
+
|
|
655
|
+
edges_written = write_reverse_salient_edges(room_dir, pair_dicts)
|
|
656
|
+
|
|
657
|
+
result = {
|
|
658
|
+
"metadata": {
|
|
659
|
+
"mode": "internal",
|
|
660
|
+
"room_dir": str(room_dir),
|
|
661
|
+
"artifact_count": len(artifacts),
|
|
662
|
+
"topk_requested": topk,
|
|
663
|
+
"threshold": threshold,
|
|
664
|
+
"embedding_model": model_used,
|
|
665
|
+
"thesis_generated": False, # Plan 89-01 always cost-free.
|
|
666
|
+
"no_thesis": bool(no_thesis),
|
|
667
|
+
"edges_written": edges_written,
|
|
668
|
+
"timestamp": datetime.now(timezone.utc).isoformat(),
|
|
669
|
+
"engine_version": ENGINE_VERSION,
|
|
670
|
+
},
|
|
671
|
+
"pairs": pair_dicts,
|
|
672
|
+
}
|
|
673
|
+
return result
|
|
674
|
+
|
|
675
|
+
|
|
676
|
+
# --- Cross-room (Mode A multi-room) runner -----------------------------------
|
|
677
|
+
|
|
678
|
+
# Overshoot multiplier for cross-room filtering. After abs-diff top-k, we
|
|
679
|
+
# discard any pair whose two artifacts live in the same room. A modest 3x
|
|
680
|
+
# overshoot keeps the post-filter yield close to the requested topk even when
|
|
681
|
+
# a few of the strongest pairs happen to be intra-room. Larger overshoots
|
|
682
|
+
# pay an O(k log k) cost but are wasted if yield is already >= topk.
|
|
683
|
+
CROSS_ROOM_OVERSHOOT = 3
|
|
684
|
+
CROSS_ROOM_WARN_SHARE = 0.05 # warn when any room contributes < 5% of corpus
|
|
685
|
+
|
|
686
|
+
|
|
687
|
+
def run_mode_cross_room(
|
|
688
|
+
room_paths: Sequence[str],
|
|
689
|
+
topk: int,
|
|
690
|
+
threshold: float,
|
|
691
|
+
no_thesis: bool,
|
|
692
|
+
) -> Dict:
|
|
693
|
+
"""Mode A multi-room: scan N rooms and keep only cross-room pairs.
|
|
694
|
+
|
|
695
|
+
Output contract (per plan):
|
|
696
|
+
- metadata.mode == "cross-room"
|
|
697
|
+
- metadata.rooms == [room_id, ...] in input order
|
|
698
|
+
- every pair has source_room != target_room
|
|
699
|
+
- pairs carry source_room / target_room / source_artifact / target_artifact
|
|
700
|
+
|
|
701
|
+
The cross-room summary table is printed to stderr by the caller; this
|
|
702
|
+
function only computes the payload.
|
|
703
|
+
"""
|
|
704
|
+
corpus = load_multi_room_corpus(room_paths)
|
|
705
|
+
room_ids = []
|
|
706
|
+
seen = set()
|
|
707
|
+
for art in corpus:
|
|
708
|
+
rid = art["room_id"]
|
|
709
|
+
if rid not in seen:
|
|
710
|
+
room_ids.append(rid)
|
|
711
|
+
seen.add(rid)
|
|
712
|
+
|
|
713
|
+
if len(corpus) < 10:
|
|
714
|
+
# The authoritative algorithm's LSA fit needs enough documents to
|
|
715
|
+
# produce stable topic keywords. Below 10, cross-room signal is
|
|
716
|
+
# unreliable. Plan's canonical threshold.
|
|
717
|
+
print(
|
|
718
|
+
f"[rs-engine] Only {len(corpus)} artifacts across "
|
|
719
|
+
f"{len(room_paths)} rooms. Need >= 10 for cross-room mode.",
|
|
720
|
+
file=sys.stderr,
|
|
721
|
+
)
|
|
722
|
+
return {
|
|
723
|
+
"metadata": {
|
|
724
|
+
"mode": "cross-room",
|
|
725
|
+
"rooms": room_ids,
|
|
726
|
+
"room_paths": [str(Path(rp).resolve()) for rp in room_paths],
|
|
727
|
+
"artifact_count": len(corpus),
|
|
728
|
+
"room_counts": summarize_corpus(corpus),
|
|
729
|
+
"topk_requested": topk,
|
|
730
|
+
"threshold": threshold,
|
|
731
|
+
"no_thesis": bool(no_thesis),
|
|
732
|
+
"thesis_generated": False,
|
|
733
|
+
"pair_matrix": {},
|
|
734
|
+
"timestamp": datetime.now(timezone.utc).isoformat(),
|
|
735
|
+
"engine_version": ENGINE_VERSION,
|
|
736
|
+
},
|
|
737
|
+
"pairs": [],
|
|
738
|
+
}
|
|
739
|
+
|
|
740
|
+
if len({a["room_id"] for a in corpus}) < 2:
|
|
741
|
+
# All usable artifacts came from a single room; no cross-room pair
|
|
742
|
+
# is possible by construction. Emit an empty-pairs result with a
|
|
743
|
+
# clear message rather than silently running the algorithm.
|
|
744
|
+
print(
|
|
745
|
+
"[rs-engine] All artifacts come from a single room; "
|
|
746
|
+
"cross-room filter yields zero pairs by construction.",
|
|
747
|
+
file=sys.stderr,
|
|
748
|
+
)
|
|
749
|
+
return {
|
|
750
|
+
"metadata": {
|
|
751
|
+
"mode": "cross-room",
|
|
752
|
+
"rooms": room_ids,
|
|
753
|
+
"room_paths": [str(Path(rp).resolve()) for rp in room_paths],
|
|
754
|
+
"artifact_count": len(corpus),
|
|
755
|
+
"room_counts": summarize_corpus(corpus),
|
|
756
|
+
"topk_requested": topk,
|
|
757
|
+
"threshold": threshold,
|
|
758
|
+
"no_thesis": bool(no_thesis),
|
|
759
|
+
"thesis_generated": False,
|
|
760
|
+
"pair_matrix": {},
|
|
761
|
+
"timestamp": datetime.now(timezone.utc).isoformat(),
|
|
762
|
+
"engine_version": ENGINE_VERSION,
|
|
763
|
+
},
|
|
764
|
+
"pairs": [],
|
|
765
|
+
}
|
|
766
|
+
|
|
767
|
+
# Share-skew warning (plan Risk 1). Any room contributing < 5% of the
|
|
768
|
+
# corpus can skew LSA toward the dominant rooms' vocabulary.
|
|
769
|
+
room_counts = summarize_corpus(corpus)
|
|
770
|
+
total = len(corpus)
|
|
771
|
+
for rid, count in room_counts.items():
|
|
772
|
+
share = count / total if total else 0.0
|
|
773
|
+
if share < CROSS_ROOM_WARN_SHARE:
|
|
774
|
+
print(
|
|
775
|
+
f"[rs-engine] Warning: room {rid!r} contributes "
|
|
776
|
+
f"{count}/{total} artifacts ({share:.1%}); LSA may skew "
|
|
777
|
+
f"away from it.",
|
|
778
|
+
file=sys.stderr,
|
|
779
|
+
)
|
|
780
|
+
|
|
781
|
+
# Build the multi-room corpus as if it were one flat artifact list. The
|
|
782
|
+
# engine's LSA fit + abs-diff top-k run byte-identical to Mode A; only
|
|
783
|
+
# the post-filter step is new.
|
|
784
|
+
texts = [f"{a['title']}\n{a['text']}" for a in corpus]
|
|
785
|
+
|
|
786
|
+
lsa_matrix = build_lsa_matrix(texts)
|
|
787
|
+
|
|
788
|
+
# Semantic signal: use the same path as Mode A single-room. We embed
|
|
789
|
+
# into a transient cache in the FIRST room's .mindrian-less root. The
|
|
790
|
+
# cache lives alongside the cross-room results file so multi-room
|
|
791
|
+
# embeddings cannot collide with a single-room Mode A cache on the same
|
|
792
|
+
# first-room path (different artifact-id shapes).
|
|
793
|
+
first_room_dir = Path(room_paths[0]).resolve()
|
|
794
|
+
cache_dir = first_room_dir / ".rs-engine-cross-room-cache"
|
|
795
|
+
cache_dir.mkdir(parents=True, exist_ok=True)
|
|
796
|
+
# Remap to the compute_embeddings contract (it expects artifact["id"]).
|
|
797
|
+
# Use global_id so cache keys stay unique across rooms.
|
|
798
|
+
cache_artifacts = [
|
|
799
|
+
{"id": a["global_id"], "text": a["text"]}
|
|
800
|
+
for a in corpus
|
|
801
|
+
]
|
|
802
|
+
embeddings, model_used = compute_embeddings(cache_artifacts, cache_dir)
|
|
803
|
+
if embeddings is None:
|
|
804
|
+
print(
|
|
805
|
+
"rs-engine: no embedder available; semantic matrix set to identity",
|
|
806
|
+
file=sys.stderr,
|
|
807
|
+
)
|
|
808
|
+
n = len(corpus)
|
|
809
|
+
sem_matrix = np.eye(n, dtype=np.float32)
|
|
810
|
+
else:
|
|
811
|
+
sem_matrix = semantic_similarity_matrix(embeddings)
|
|
812
|
+
|
|
813
|
+
# Overshoot so post-filter still yields topk when some pairs are intra-room.
|
|
814
|
+
overshoot_k = max(topk * CROSS_ROOM_OVERSHOOT, topk + 10)
|
|
815
|
+
top_pairs = abs_diff_topk(lsa_matrix, sem_matrix, k=overshoot_k)
|
|
816
|
+
|
|
817
|
+
# Cross-room filter + pair matrix accumulator.
|
|
818
|
+
pair_dicts: List[Dict] = []
|
|
819
|
+
pair_matrix: Dict[str, int] = {}
|
|
820
|
+
for i, j, signed, absv in top_pairs:
|
|
821
|
+
if absv < threshold:
|
|
822
|
+
continue
|
|
823
|
+
a_i, a_j = corpus[i], corpus[j]
|
|
824
|
+
if a_i["room_id"] == a_j["room_id"]:
|
|
825
|
+
continue # intra-room pair -- discard.
|
|
826
|
+
src_room = a_i["room_id"]
|
|
827
|
+
tgt_room = a_j["room_id"]
|
|
828
|
+
# Canonical pair-matrix key: sort room_ids so (A, B) == (B, A) counts.
|
|
829
|
+
pm_key = " x ".join(sorted([src_room, tgt_room]))
|
|
830
|
+
pair_matrix[pm_key] = pair_matrix.get(pm_key, 0) + 1
|
|
831
|
+
pair_dicts.append({
|
|
832
|
+
"source_artifact_id": a_i["global_id"],
|
|
833
|
+
"source_artifact": a_i["artifact_id"],
|
|
834
|
+
"source_room": src_room,
|
|
835
|
+
"source_title": a_i["title"],
|
|
836
|
+
"source_section": a_i["section"],
|
|
837
|
+
"source_path": a_i["path"],
|
|
838
|
+
"target_artifact_id": a_j["global_id"],
|
|
839
|
+
"target_artifact": a_j["artifact_id"],
|
|
840
|
+
"target_room": tgt_room,
|
|
841
|
+
"target_title": a_j["title"],
|
|
842
|
+
"target_section": a_j["section"],
|
|
843
|
+
"target_path": a_j["path"],
|
|
844
|
+
"lsa_score": round(float(lsa_matrix[i, j]), 4),
|
|
845
|
+
"semantic_score": round(float(sem_matrix[i, j]), 4),
|
|
846
|
+
"signed_diff": round(float(signed), 4),
|
|
847
|
+
"abs_diff": round(float(absv), 4),
|
|
848
|
+
"direction": classify_direction(signed),
|
|
849
|
+
})
|
|
850
|
+
if len(pair_dicts) >= topk:
|
|
851
|
+
break
|
|
852
|
+
|
|
853
|
+
result = {
|
|
854
|
+
"metadata": {
|
|
855
|
+
"mode": "cross-room",
|
|
856
|
+
"rooms": room_ids,
|
|
857
|
+
"room_paths": [str(Path(rp).resolve()) for rp in room_paths],
|
|
858
|
+
"artifact_count": len(corpus),
|
|
859
|
+
"room_counts": room_counts,
|
|
860
|
+
"topk_requested": topk,
|
|
861
|
+
"threshold": threshold,
|
|
862
|
+
"embedding_model": model_used,
|
|
863
|
+
"thesis_generated": False,
|
|
864
|
+
"no_thesis": bool(no_thesis),
|
|
865
|
+
"edges_written": 0, # Mode A multi-room does NOT write room.db edges.
|
|
866
|
+
"pair_matrix": pair_matrix,
|
|
867
|
+
"timestamp": datetime.now(timezone.utc).isoformat(),
|
|
868
|
+
"engine_version": ENGINE_VERSION,
|
|
869
|
+
},
|
|
870
|
+
"pairs": pair_dicts,
|
|
871
|
+
}
|
|
872
|
+
return result
|
|
873
|
+
|
|
874
|
+
|
|
875
|
+
def _print_cross_room_summary(result: Dict) -> None:
|
|
876
|
+
"""Print the plan-canonical Room-A x Room-B | Bridges table to stderr."""
|
|
877
|
+
matrix = result.get("metadata", {}).get("pair_matrix") or {}
|
|
878
|
+
if not matrix:
|
|
879
|
+
print("[rs-engine] No cross-room bridges found.", file=sys.stderr)
|
|
880
|
+
return
|
|
881
|
+
# Sort pair keys by bridge count desc, then alphabetically for stability.
|
|
882
|
+
rows = sorted(matrix.items(), key=lambda kv: (-kv[1], kv[0]))
|
|
883
|
+
max_key_len = max(len(k) for k, _ in rows)
|
|
884
|
+
header_key = "Room pair".ljust(max_key_len)
|
|
885
|
+
print(f"\n{header_key} | Bridges", file=sys.stderr)
|
|
886
|
+
print(f"{'-' * max_key_len}-+--------", file=sys.stderr)
|
|
887
|
+
for key, count in rows:
|
|
888
|
+
print(f"{key.ljust(max_key_len)} | {count}", file=sys.stderr)
|
|
889
|
+
print("", file=sys.stderr)
|
|
890
|
+
|
|
891
|
+
|
|
892
|
+
# --- Mode B runner -----------------------------------------------------------
|
|
893
|
+
|
|
894
|
+
def _write_corpus_jsonl(corpus_dir: Path, docs: Sequence[Dict]) -> Path:
|
|
895
|
+
"""Persist the raw fetched corpus for provenance.
|
|
896
|
+
|
|
897
|
+
One JSON document per line keeps the file streaming-friendly and easy to
|
|
898
|
+
diff. Plan 89-03 will key the Pinecone upsert on (source, external_id)
|
|
899
|
+
read directly from this file.
|
|
900
|
+
"""
|
|
901
|
+
corpus_dir.mkdir(parents=True, exist_ok=True)
|
|
902
|
+
path = corpus_dir / "_corpus.jsonl"
|
|
903
|
+
with path.open("w", encoding="utf-8") as fh:
|
|
904
|
+
for doc in docs:
|
|
905
|
+
fh.write(json.dumps(doc, ensure_ascii=False) + "\n")
|
|
906
|
+
return path
|
|
907
|
+
|
|
908
|
+
|
|
909
|
+
def _corpus_docs_to_artifacts(docs: Sequence[Dict]) -> List[Dict]:
|
|
910
|
+
"""Shape fetched docs into the artifact dict the rest of the engine
|
|
911
|
+
expects (id, section, title, path, text)."""
|
|
912
|
+
artifacts: List[Dict] = []
|
|
913
|
+
for doc in docs:
|
|
914
|
+
external_id = doc.get("external_id") or ""
|
|
915
|
+
source = doc.get("source") or "external"
|
|
916
|
+
# Build an artifact id that is stable across runs and unique within
|
|
917
|
+
# the corpus. External ids can contain URL characters; stripping to
|
|
918
|
+
# an id-friendly slug keeps downstream SQLite/Pinecone keys predictable.
|
|
919
|
+
eid_slug = re.sub(r"[^a-zA-Z0-9]+", "-", external_id).strip("-")
|
|
920
|
+
if not eid_slug:
|
|
921
|
+
eid_slug = re.sub(r"[^a-zA-Z0-9]+", "-", doc.get("title") or "untitled").strip("-")
|
|
922
|
+
artifact_id = f"{source}/{eid_slug}"
|
|
923
|
+
text = (doc.get("abstract") or "").strip()
|
|
924
|
+
if not text:
|
|
925
|
+
continue
|
|
926
|
+
artifacts.append({
|
|
927
|
+
"id": artifact_id,
|
|
928
|
+
"section": source,
|
|
929
|
+
"title": (doc.get("title") or "").strip() or external_id,
|
|
930
|
+
"path": doc.get("url") or external_id,
|
|
931
|
+
"text": text,
|
|
932
|
+
"year": doc.get("year"),
|
|
933
|
+
"authors": doc.get("authors", []),
|
|
934
|
+
"doi": doc.get("doi"),
|
|
935
|
+
})
|
|
936
|
+
return artifacts
|
|
937
|
+
|
|
938
|
+
|
|
939
|
+
def _records_to_artifacts(records: Sequence[Dict]) -> List[Dict]:
|
|
940
|
+
"""Shape rs_cache records (id + values + metadata) into artifact dicts.
|
|
941
|
+
|
|
942
|
+
Plan 296-05 warm path: records come straight from the per-room local
|
|
943
|
+
signal cache (lib.core.rs_cache) and carry the abstract in
|
|
944
|
+
metadata['abstract'], the public DOI in metadata['doi'], and the human
|
|
945
|
+
title in metadata['title']. The local encoder's vectors live in
|
|
946
|
+
record['values']; we attach them so the caller can build the semantic
|
|
947
|
+
matrix directly without re-embedding.
|
|
948
|
+
"""
|
|
949
|
+
artifacts: List[Dict] = []
|
|
950
|
+
for rec in records:
|
|
951
|
+
rid = rec.get("id") or ""
|
|
952
|
+
meta = rec.get("metadata", {}) or {}
|
|
953
|
+
abstract = (meta.get("abstract") or "").strip()
|
|
954
|
+
if not abstract:
|
|
955
|
+
# A namespace record without a usable abstract cannot feed LSA;
|
|
956
|
+
# silently drop, matching the 89-02 empty-abstract filter.
|
|
957
|
+
continue
|
|
958
|
+
source = (meta.get("source") or "external").strip() or "external"
|
|
959
|
+
title = (meta.get("title") or "").strip() or rid
|
|
960
|
+
# Reconstruct a stable artifact id matching Mode B's cold path shape
|
|
961
|
+
# (source/slug). This keeps pair ids comparable between warm and
|
|
962
|
+
# cold runs over the same topic.
|
|
963
|
+
eid_slug = re.sub(r"[^a-zA-Z0-9]+", "-", rid).strip("-")
|
|
964
|
+
if not eid_slug:
|
|
965
|
+
eid_slug = re.sub(r"[^a-zA-Z0-9]+", "-", title or "untitled").strip("-")
|
|
966
|
+
artifact_id = f"{source}/{eid_slug}"
|
|
967
|
+
artifacts.append({
|
|
968
|
+
"id": artifact_id,
|
|
969
|
+
"section": source,
|
|
970
|
+
"title": title,
|
|
971
|
+
"path": f"https://doi.org/{meta['doi']}" if meta.get("doi") else rid,
|
|
972
|
+
"text": abstract,
|
|
973
|
+
"year": meta.get("year") or None,
|
|
974
|
+
"authors": [],
|
|
975
|
+
"doi": meta.get("doi") or None,
|
|
976
|
+
"_cached_values": rec.get("values") or [],
|
|
977
|
+
})
|
|
978
|
+
return artifacts
|
|
979
|
+
|
|
980
|
+
|
|
981
|
+
def _signal_cache_available(room_dir: Optional[Any] = None) -> Tuple[bool, str]:
|
|
982
|
+
"""Return (ok, reason). Reason is empty on success, explanatory on skip.
|
|
983
|
+
|
|
984
|
+
Renamed from the prior remote-key gate (Plan 296-05). The former hosted
|
|
985
|
+
API-key presence check and the RS_EMBEDDING_MODEL=minilm opt-out are both
|
|
986
|
+
DROPPED here: neither means anything against a per-room local sidecar
|
|
987
|
+
cache, and leaving the key check in place would make the local path
|
|
988
|
+
unreachable for the overwhelming majority of users, who have no key. In
|
|
989
|
+
their place: the room must resolve, since the local cache is scoped to a
|
|
990
|
+
room by construction (lib.core.rs_cache, closes SEED-029 finding F8).
|
|
991
|
+
"""
|
|
992
|
+
if not _RS_CACHE_AVAILABLE:
|
|
993
|
+
return False, f"rs_cache import failed: {globals().get('_rs_cache_import_err_msg', 'unknown')}"
|
|
994
|
+
if not room_dir:
|
|
995
|
+
return False, "no room scope for the signal cache"
|
|
996
|
+
return True, ""
|
|
997
|
+
|
|
998
|
+
|
|
999
|
+
def _build_sem_matrix_from_records(artifacts: Sequence[Dict]) -> Optional[np.ndarray]:
|
|
1000
|
+
"""Build the pairwise cosine similarity matrix from cached embeddings.
|
|
1001
|
+
|
|
1002
|
+
Returns None if any artifact is missing a vector (triggers fallback to
|
|
1003
|
+
local embedding path). All vectors must share dimensionality.
|
|
1004
|
+
"""
|
|
1005
|
+
vectors: List[List[float]] = []
|
|
1006
|
+
for a in artifacts:
|
|
1007
|
+
v = a.get("_cached_values") or []
|
|
1008
|
+
if not v:
|
|
1009
|
+
return None
|
|
1010
|
+
vectors.append(list(v))
|
|
1011
|
+
if not vectors:
|
|
1012
|
+
return None
|
|
1013
|
+
dim = len(vectors[0])
|
|
1014
|
+
if any(len(v) != dim for v in vectors):
|
|
1015
|
+
return None
|
|
1016
|
+
matrix = np.asarray(vectors, dtype=np.float32)
|
|
1017
|
+
return semantic_similarity_matrix(matrix)
|
|
1018
|
+
|
|
1019
|
+
|
|
1020
|
+
# --- SEED-018 H2 semantic-floor gate: LIVE production wiring ----------------
|
|
1021
|
+
#
|
|
1022
|
+
# The gate itself lives in lib/core/rs_corpus.semantic_gate (implemented + unit
|
|
1023
|
+
# tested in Plan 200-01). Until this wiring it had NO production caller -- it was
|
|
1024
|
+
# dormant. The helpers below call it on the two live Mode B corpus paths:
|
|
1025
|
+
#
|
|
1026
|
+
# 1. Local signal-cache (warm/cold) path: gate the raw `records` (each carrying
|
|
1027
|
+
# its local-encoder vector in record['values']) against a topic vector from
|
|
1028
|
+
# the SAME encoder (rs_cache._embed_via_bridge), BEFORE _records_to_artifacts.
|
|
1029
|
+
# Plan 296-05: rerouted off Pinecone inference -- after the repoint, records
|
|
1030
|
+
# carry local 384-dim vectors, so a topic vector from a different encoder
|
|
1031
|
+
# would either produce a dimension mismatch or a meaningless cosine, and in
|
|
1032
|
+
# both cases the gate would stop filtering without raising.
|
|
1033
|
+
# 2. Local fallback path (Plan 89-02): gate the fetched `docs` using the reused
|
|
1034
|
+
# local MiniLM encoder (_embed_local_minilm), batched so the model loads once.
|
|
1035
|
+
#
|
|
1036
|
+
# Safe-by-default (this is the core scoring input):
|
|
1037
|
+
# - No-op when the topic vector cannot be obtained (encode / inference absent).
|
|
1038
|
+
# - Reuses SEMANTIC_FLOOR (default 0.15, tunable via RS_SEMANTIC_FLOOR).
|
|
1039
|
+
# - STARVATION GUARD: never drops the usable count below the differential
|
|
1040
|
+
# minimum (the engine early-outs under 2 artifacts). Worst case a no-op,
|
|
1041
|
+
# never a regression.
|
|
1042
|
+
# - Reuses the EXISTING encoders only (Pinecone inference + MiniLM). No new
|
|
1043
|
+
# encoder, no new npm/pip package, no new egress class.
|
|
1044
|
+
|
|
1045
|
+
# The engine early-outs when fewer than this many artifacts survive (see the
|
|
1046
|
+
# `len(artifacts) < 2` guard in run_mode_external). The gate must never push the
|
|
1047
|
+
# corpus below it.
|
|
1048
|
+
_DIFFERENTIAL_MIN_ARTIFACTS = 2
|
|
1049
|
+
|
|
1050
|
+
|
|
1051
|
+
def _semantic_cosine(a: Sequence[float], b: Sequence[float]) -> float:
|
|
1052
|
+
"""Minimal cosine similarity for the starvation-guard ranking. Returns 0.0
|
|
1053
|
+
on shape mismatch or a zero-norm vector."""
|
|
1054
|
+
if not a or not b or len(a) != len(b):
|
|
1055
|
+
return 0.0
|
|
1056
|
+
dot = 0.0
|
|
1057
|
+
na = 0.0
|
|
1058
|
+
nb = 0.0
|
|
1059
|
+
for x, y in zip(a, b):
|
|
1060
|
+
dot += x * y
|
|
1061
|
+
na += x * x
|
|
1062
|
+
nb += y * y
|
|
1063
|
+
if na == 0.0 or nb == 0.0:
|
|
1064
|
+
return 0.0
|
|
1065
|
+
return dot / ((na ** 0.5) * (nb ** 0.5))
|
|
1066
|
+
|
|
1067
|
+
|
|
1068
|
+
def _gate_with_starvation_guard(
|
|
1069
|
+
topic_vec: Optional[Sequence[float]],
|
|
1070
|
+
candidates: List[Dict],
|
|
1071
|
+
encode_fn,
|
|
1072
|
+
floor: Optional[float] = None,
|
|
1073
|
+
min_keep: int = _DIFFERENTIAL_MIN_ARTIFACTS,
|
|
1074
|
+
label: str = "corpus",
|
|
1075
|
+
) -> List[Dict]:
|
|
1076
|
+
"""Apply the H2 semantic-floor gate with a mandatory starvation guard.
|
|
1077
|
+
|
|
1078
|
+
Delegates the actual floor filtering to rs_corpus.semantic_gate (the single
|
|
1079
|
+
gate implementation) so both live paths share one tested code path. When the
|
|
1080
|
+
floor would push the survivor count below the differential minimum on an
|
|
1081
|
+
otherwise-sufficient corpus, keep the top-N candidates by similarity instead
|
|
1082
|
+
of starving score(), and log the skip to stderr.
|
|
1083
|
+
"""
|
|
1084
|
+
if not topic_vec or not callable(encode_fn):
|
|
1085
|
+
# No usable topic vector / encoder -> safe no-op pass-through.
|
|
1086
|
+
return list(candidates)
|
|
1087
|
+
|
|
1088
|
+
original_n = len(candidates)
|
|
1089
|
+
# Production caller of the SEED-018 H2 gate (rs_corpus.semantic_gate).
|
|
1090
|
+
kept = semantic_gate(topic_vec, candidates, encode_fn, floor)
|
|
1091
|
+
|
|
1092
|
+
if len(kept) >= min_keep or original_n < min_keep:
|
|
1093
|
+
return kept
|
|
1094
|
+
|
|
1095
|
+
# Starvation guard: the floor would collapse the corpus below the
|
|
1096
|
+
# differential minimum. Preserve the top-N most-similar candidates so the
|
|
1097
|
+
# differential still runs. Worst case the gate is a no-op, never a
|
|
1098
|
+
# regression that starves the scorer.
|
|
1099
|
+
ranked = sorted(
|
|
1100
|
+
candidates,
|
|
1101
|
+
key=lambda c: _semantic_cosine(topic_vec, encode_fn(c)),
|
|
1102
|
+
reverse=True,
|
|
1103
|
+
)
|
|
1104
|
+
keep_n = max(min_keep, len(kept))
|
|
1105
|
+
guarded = ranked[:keep_n]
|
|
1106
|
+
print(
|
|
1107
|
+
f"[rs-engine] semantic gate skipped: would starve corpus "
|
|
1108
|
+
f"(kept {len(guarded)})",
|
|
1109
|
+
file=sys.stderr,
|
|
1110
|
+
)
|
|
1111
|
+
return guarded
|
|
1112
|
+
|
|
1113
|
+
|
|
1114
|
+
def _embed_topic_via_signal_cache(topic: str) -> Optional[List[float]]:
|
|
1115
|
+
"""Embed `topic` through rs_cache's local encoder (Plan 296-05).
|
|
1116
|
+
|
|
1117
|
+
Reuses the SAME local encoder (rs_cache._embed_via_bridge, which wraps
|
|
1118
|
+
scripts/rs-vector-bridge.cjs -> lib/core/eureka/embedding-spine.cjs) that
|
|
1119
|
+
produced the record vectors this topic vector is compared against. This
|
|
1120
|
+
is the load-bearing part of the reroute: after the repoint, records carry
|
|
1121
|
+
local 384-dim vectors, so a topic vector from ANY other encoder either
|
|
1122
|
+
produces a dimension mismatch or a meaningless cosine -- and in both
|
|
1123
|
+
cases the gate stops filtering off-topic documents without raising.
|
|
1124
|
+
|
|
1125
|
+
Returns a plain float list, or None on ANY failure (rs_cache unavailable,
|
|
1126
|
+
bridge unreachable, unexpected shape) so the caller degrades to a safe
|
|
1127
|
+
no-op. Adds no new encoder and no new egress class -- the bridge is the
|
|
1128
|
+
same local encoder the rest of this repoint already uses.
|
|
1129
|
+
"""
|
|
1130
|
+
if not _RS_CACHE_AVAILABLE:
|
|
1131
|
+
return None
|
|
1132
|
+
try:
|
|
1133
|
+
vectors, provenance = rs_cache._embed_via_bridge([topic])
|
|
1134
|
+
except Exception as e: # pragma: no cover -- defensive
|
|
1135
|
+
print(
|
|
1136
|
+
f"[rs-engine] semantic gate: topic embed unavailable ({e}); "
|
|
1137
|
+
f"gate no-op for this run.",
|
|
1138
|
+
file=sys.stderr,
|
|
1139
|
+
)
|
|
1140
|
+
return None
|
|
1141
|
+
if not vectors or provenance is None:
|
|
1142
|
+
return None
|
|
1143
|
+
return [float(x) for x in vectors[0]]
|
|
1144
|
+
|
|
1145
|
+
|
|
1146
|
+
def _gate_records_cached(topic: str, records: List[Dict]) -> List[Dict]:
|
|
1147
|
+
"""Gate local signal-cache `records` by the semantic floor before
|
|
1148
|
+
_records_to_artifacts.
|
|
1149
|
+
|
|
1150
|
+
Renamed from the prior remote-cache gate (Plan 296-05). Each record carries
|
|
1151
|
+
its local-encoder vector in record['values']; the topic vector now comes
|
|
1152
|
+
from the SAME encoder via _embed_topic_via_signal_cache, not a different
|
|
1153
|
+
one. No-op (returns records unchanged) when the topic vector is
|
|
1154
|
+
unavailable.
|
|
1155
|
+
"""
|
|
1156
|
+
if not records:
|
|
1157
|
+
return records
|
|
1158
|
+
topic_vec = _embed_topic_via_signal_cache(topic)
|
|
1159
|
+
if not topic_vec:
|
|
1160
|
+
return records
|
|
1161
|
+
return _gate_with_starvation_guard(
|
|
1162
|
+
topic_vec,
|
|
1163
|
+
records,
|
|
1164
|
+
lambda rec: rec.get("values") or [],
|
|
1165
|
+
label="signal-cache-records",
|
|
1166
|
+
)
|
|
1167
|
+
|
|
1168
|
+
|
|
1169
|
+
def _gate_docs_local(topic: str, docs: List[Dict]) -> List[Dict]:
|
|
1170
|
+
"""Gate local-fallback `docs` by the semantic floor using the reused local
|
|
1171
|
+
MiniLM encoder. Batched so SentenceTransformer loads once. Clean no-op when
|
|
1172
|
+
sentence-transformers is absent (encode returns None)."""
|
|
1173
|
+
if not docs:
|
|
1174
|
+
return docs
|
|
1175
|
+
topic_arr = _embed_local_minilm([topic])
|
|
1176
|
+
if topic_arr is None:
|
|
1177
|
+
# sentence-transformers not installed -> safe no-op (Tri-Polar degrade).
|
|
1178
|
+
return docs
|
|
1179
|
+
texts = [
|
|
1180
|
+
f"{d.get('title', '')}\n{d.get('abstract', '')}".strip() for d in docs
|
|
1181
|
+
]
|
|
1182
|
+
cand_arr = _embed_local_minilm(texts)
|
|
1183
|
+
if cand_arr is None or len(cand_arr) != len(docs):
|
|
1184
|
+
return docs
|
|
1185
|
+
topic_vec = [float(x) for x in topic_arr[0]]
|
|
1186
|
+
vec_by_id = {id(d): [float(x) for x in cand_arr[i]] for i, d in enumerate(docs)}
|
|
1187
|
+
return _gate_with_starvation_guard(
|
|
1188
|
+
topic_vec,
|
|
1189
|
+
docs,
|
|
1190
|
+
lambda d: vec_by_id.get(id(d), []),
|
|
1191
|
+
label="local-fallback",
|
|
1192
|
+
)
|
|
1193
|
+
|
|
1194
|
+
|
|
1195
|
+
def run_mode_external(
|
|
1196
|
+
room_dir: Path,
|
|
1197
|
+
topic: str,
|
|
1198
|
+
topk: int,
|
|
1199
|
+
threshold: float,
|
|
1200
|
+
no_thesis: bool,
|
|
1201
|
+
) -> Tuple[Dict, Path]:
|
|
1202
|
+
"""Mode B: fetch external corpus (cached in the per-room local signal
|
|
1203
|
+
cache), run LSA + abs_diff.
|
|
1204
|
+
|
|
1205
|
+
Plan 296-05 wiring: lib.core.rs_cache's per-room local sidecar caches
|
|
1206
|
+
embedded corpora per topic, scoped to room_dir. On each call:
|
|
1207
|
+
1. Compute namespace for topic (rs_cache.namespace_slug).
|
|
1208
|
+
2. Check freshness, scoped to this room. If < 30 days, skip fetch and
|
|
1209
|
+
use the cached vectors directly (warm path).
|
|
1210
|
+
3. Else, fetch_corpus -> upsert_corpus -> fetch_all_from_namespace
|
|
1211
|
+
(cold path; embeds through the local encoder bridge, then re-reads
|
|
1212
|
+
so the vectors used for scoring came from the cache, not a
|
|
1213
|
+
from-memory list).
|
|
1214
|
+
4. Fall back to the Plan 89-02 local path (fetch_corpus + local
|
|
1215
|
+
MiniLM embedding) when the signal cache is unavailable (rs_cache
|
|
1216
|
+
import failed, or the room does not resolve).
|
|
1217
|
+
|
|
1218
|
+
Returns (result dict, corpus_dir) -- CLI writes results JSON inside
|
|
1219
|
+
corpus_dir at {room}/research/{topic-slug}/.rs-engine-results.json.
|
|
1220
|
+
"""
|
|
1221
|
+
slug = topic_slug(topic)
|
|
1222
|
+
if not slug:
|
|
1223
|
+
raise ValueError("topic slug resolved to empty string")
|
|
1224
|
+
corpus_dir = room_dir / "research" / slug
|
|
1225
|
+
corpus_dir.mkdir(parents=True, exist_ok=True)
|
|
1226
|
+
|
|
1227
|
+
# Overshoot target_n so dedup + threshold filtering can still hit topk.
|
|
1228
|
+
target_n = max(topk * 20, topk * 2)
|
|
1229
|
+
|
|
1230
|
+
# Freshness + cache decision.
|
|
1231
|
+
signal_ok, signal_skip_reason = _signal_cache_available(room_dir=room_dir)
|
|
1232
|
+
cache_mode = "bypass"
|
|
1233
|
+
cache_age_days: Optional[float] = None
|
|
1234
|
+
cache_namespace: Optional[str] = None
|
|
1235
|
+
records: List[Dict] = []
|
|
1236
|
+
docs: List[Dict] = []
|
|
1237
|
+
|
|
1238
|
+
if signal_ok:
|
|
1239
|
+
ns = _rs_cache_namespace_slug(topic)
|
|
1240
|
+
cache_namespace = ns
|
|
1241
|
+
try:
|
|
1242
|
+
cache_age_days = _rs_cache_freshness(ns, room_dir=room_dir)
|
|
1243
|
+
except Exception as e:
|
|
1244
|
+
print(f"rs-engine: signal cache freshness check failed ({e})", file=sys.stderr)
|
|
1245
|
+
cache_age_days = None
|
|
1246
|
+
|
|
1247
|
+
if _rs_cache_is_fresh(cache_age_days, ttl_days=_RS_CACHE_TTL_DAYS):
|
|
1248
|
+
# WARM path: namespace is within TTL. Skip fetch.
|
|
1249
|
+
cache_mode = "warm"
|
|
1250
|
+
print(
|
|
1251
|
+
f"[rs-engine] Cache hit (age={cache_age_days:.1f} days, "
|
|
1252
|
+
f"ttl={_RS_CACHE_TTL_DAYS}). Using local signal cache {ns}.",
|
|
1253
|
+
file=sys.stderr,
|
|
1254
|
+
)
|
|
1255
|
+
try:
|
|
1256
|
+
records = _rs_cache_fetch_all(ns, limit=target_n, room_dir=room_dir)
|
|
1257
|
+
except Exception as e:
|
|
1258
|
+
print(
|
|
1259
|
+
f"rs-engine: warm-path fetch failed ({e}); "
|
|
1260
|
+
f"falling back to cold path.",
|
|
1261
|
+
file=sys.stderr,
|
|
1262
|
+
)
|
|
1263
|
+
records = []
|
|
1264
|
+
cache_mode = "bypass"
|
|
1265
|
+
|
|
1266
|
+
if cache_mode != "warm":
|
|
1267
|
+
# COLD path: fetch from external APIs, upsert to the local
|
|
1268
|
+
# signal cache, then re-fetch so the vectors used for scoring
|
|
1269
|
+
# come from the cache's own round-trip (not a from-memory list).
|
|
1270
|
+
age_label = "unset" if cache_age_days is None else f"{cache_age_days:.1f} days"
|
|
1271
|
+
print(
|
|
1272
|
+
f"[rs-engine] Cache miss (age={age_label}). "
|
|
1273
|
+
f"Fetching corpus for topic {topic!r}...",
|
|
1274
|
+
file=sys.stderr,
|
|
1275
|
+
)
|
|
1276
|
+
docs = fetch_corpus(topic, target_n=target_n)
|
|
1277
|
+
if len(docs) >= 2:
|
|
1278
|
+
try:
|
|
1279
|
+
_rs_cache_upsert(topic, docs, room_dir=room_dir)
|
|
1280
|
+
cache_mode = "cold"
|
|
1281
|
+
# Re-read from the local signal cache so the semantic
|
|
1282
|
+
# matrix uses the cached (round-tripped) local vectors.
|
|
1283
|
+
records = _rs_cache_fetch_all(ns, limit=target_n, room_dir=room_dir)
|
|
1284
|
+
except Exception as e:
|
|
1285
|
+
print(
|
|
1286
|
+
f"rs-engine: local signal cache upsert/fetch failed ({e}); "
|
|
1287
|
+
f"falling back to local embedding path.",
|
|
1288
|
+
file=sys.stderr,
|
|
1289
|
+
)
|
|
1290
|
+
records = []
|
|
1291
|
+
cache_mode = "bypass"
|
|
1292
|
+
else:
|
|
1293
|
+
# Signal cache path not available -- preserve Plan 89-02 behavior.
|
|
1294
|
+
print(
|
|
1295
|
+
f"[rs-engine] Signal cache skipped ({signal_skip_reason}); "
|
|
1296
|
+
f"using Plan 89-02 local embedding path.",
|
|
1297
|
+
file=sys.stderr,
|
|
1298
|
+
)
|
|
1299
|
+
|
|
1300
|
+
# Provenance sidecar: keep _corpus.jsonl in both paths.
|
|
1301
|
+
# Warm path has no fresh fetch; docs stays empty but we still want the
|
|
1302
|
+
# sidecar to exist (stale from prior cold run). If absent, skip quietly
|
|
1303
|
+
# so the warm path does not accidentally wipe prior provenance.
|
|
1304
|
+
if docs:
|
|
1305
|
+
corpus_path = _write_corpus_jsonl(corpus_dir, docs)
|
|
1306
|
+
else:
|
|
1307
|
+
corpus_path = corpus_dir / "_corpus.jsonl"
|
|
1308
|
+
if not corpus_path.exists():
|
|
1309
|
+
# Warm path with no prior sidecar (first time the namespace is
|
|
1310
|
+
# warm on this machine). Create an empty file so downstream
|
|
1311
|
+
# readers do not trip on a missing path.
|
|
1312
|
+
corpus_path.write_text("", encoding="utf-8")
|
|
1313
|
+
|
|
1314
|
+
# Build artifacts + texts depending on path taken.
|
|
1315
|
+
if records:
|
|
1316
|
+
# SEED-018 H2 LIVE-WIRE (local signal-cache path): drop off-topic records
|
|
1317
|
+
# using the local-encoder vectors they already carry vs a topic vector
|
|
1318
|
+
# from the SAME encoder, BEFORE they become artifacts and reach the
|
|
1319
|
+
# differential. Starvation-guarded + no-op when the topic vector is
|
|
1320
|
+
# unavailable.
|
|
1321
|
+
records = _gate_records_cached(topic, records)
|
|
1322
|
+
artifacts = _records_to_artifacts(records)
|
|
1323
|
+
else:
|
|
1324
|
+
# Fallback to Plan 89-02 behavior.
|
|
1325
|
+
if not docs:
|
|
1326
|
+
docs = fetch_corpus(topic, target_n=target_n)
|
|
1327
|
+
corpus_path = _write_corpus_jsonl(corpus_dir, docs)
|
|
1328
|
+
# SEED-018 H2 LIVE-WIRE (local fallback): gate off-topic docs with the
|
|
1329
|
+
# reused local MiniLM encoder before they reach the differential.
|
|
1330
|
+
docs = _gate_docs_local(topic, docs)
|
|
1331
|
+
artifacts = _corpus_docs_to_artifacts(docs)
|
|
1332
|
+
|
|
1333
|
+
# Early out if the usable-artifact count collapses.
|
|
1334
|
+
if len(artifacts) < 2:
|
|
1335
|
+
return (
|
|
1336
|
+
{
|
|
1337
|
+
"metadata": {
|
|
1338
|
+
"mode": "external",
|
|
1339
|
+
"topic": topic,
|
|
1340
|
+
"topic_slug": slug,
|
|
1341
|
+
"room_dir": str(room_dir),
|
|
1342
|
+
"corpus_dir": str(corpus_dir),
|
|
1343
|
+
"corpus_path": str(corpus_path),
|
|
1344
|
+
"corpus_size": len(docs) if docs else len(records),
|
|
1345
|
+
"artifact_count": len(artifacts),
|
|
1346
|
+
"tier_counts": _count_by_source(docs) if docs else {},
|
|
1347
|
+
"cache_mode": cache_mode,
|
|
1348
|
+
"cache_age_days": round(cache_age_days, 2) if cache_age_days is not None else None,
|
|
1349
|
+
"cache_namespace": cache_namespace,
|
|
1350
|
+
"cache_ttl_days": _RS_CACHE_TTL_DAYS if signal_ok else None,
|
|
1351
|
+
"topk_requested": topk,
|
|
1352
|
+
"threshold": threshold,
|
|
1353
|
+
"no_thesis": bool(no_thesis),
|
|
1354
|
+
"thesis_generated": False,
|
|
1355
|
+
"timestamp": datetime.now(timezone.utc).isoformat(),
|
|
1356
|
+
"engine_version": ENGINE_VERSION,
|
|
1357
|
+
},
|
|
1358
|
+
"pairs": [],
|
|
1359
|
+
},
|
|
1360
|
+
corpus_dir,
|
|
1361
|
+
)
|
|
1362
|
+
|
|
1363
|
+
texts = [f"{a['title']}\n{a['text']}" for a in artifacts]
|
|
1364
|
+
|
|
1365
|
+
# Structural signal (authoritative topic-keyword-membership LSA).
|
|
1366
|
+
lsa_matrix = build_lsa_matrix(texts)
|
|
1367
|
+
|
|
1368
|
+
# Semantic signal. Warm/cold path: vectors come straight from Pinecone.
|
|
1369
|
+
# Bypass path: fall back to Plan 89-02's local embedder.
|
|
1370
|
+
sem_matrix: Optional[np.ndarray] = None
|
|
1371
|
+
model_used: str
|
|
1372
|
+
if cache_mode in ("warm", "cold") and records:
|
|
1373
|
+
sem_matrix = _build_sem_matrix_from_records(artifacts)
|
|
1374
|
+
if sem_matrix is not None:
|
|
1375
|
+
model_used = "multilingual-e5-large"
|
|
1376
|
+
else:
|
|
1377
|
+
# Vectors missing -- rare but defensible. Drop to local path.
|
|
1378
|
+
print(
|
|
1379
|
+
"rs-engine: cached records missing values; "
|
|
1380
|
+
"falling back to local embedding for semantic matrix.",
|
|
1381
|
+
file=sys.stderr,
|
|
1382
|
+
)
|
|
1383
|
+
embeddings, model_used = compute_embeddings(artifacts, corpus_dir)
|
|
1384
|
+
sem_matrix = (
|
|
1385
|
+
semantic_similarity_matrix(embeddings)
|
|
1386
|
+
if embeddings is not None
|
|
1387
|
+
else np.eye(len(artifacts), dtype=np.float32)
|
|
1388
|
+
)
|
|
1389
|
+
else:
|
|
1390
|
+
embeddings, model_used = compute_embeddings(artifacts, corpus_dir)
|
|
1391
|
+
if embeddings is None:
|
|
1392
|
+
print(
|
|
1393
|
+
"rs-engine: no embedder available for Mode B; "
|
|
1394
|
+
"semantic matrix set to identity.",
|
|
1395
|
+
file=sys.stderr,
|
|
1396
|
+
)
|
|
1397
|
+
sem_matrix = np.eye(len(artifacts), dtype=np.float32)
|
|
1398
|
+
else:
|
|
1399
|
+
sem_matrix = semantic_similarity_matrix(embeddings)
|
|
1400
|
+
|
|
1401
|
+
top_pairs = abs_diff_topk(lsa_matrix, sem_matrix, k=topk)
|
|
1402
|
+
|
|
1403
|
+
pair_dicts: List[Dict] = []
|
|
1404
|
+
for i, j, signed, absv in top_pairs:
|
|
1405
|
+
if absv < threshold:
|
|
1406
|
+
continue
|
|
1407
|
+
a_i, a_j = artifacts[i], artifacts[j]
|
|
1408
|
+
pair_dicts.append({
|
|
1409
|
+
"source_artifact_id": a_i["id"],
|
|
1410
|
+
"source_title": a_i["title"],
|
|
1411
|
+
"source_section": a_i["section"],
|
|
1412
|
+
"source_url": a_i.get("path", ""),
|
|
1413
|
+
"source_doi": a_i.get("doi"),
|
|
1414
|
+
"target_artifact_id": a_j["id"],
|
|
1415
|
+
"target_title": a_j["title"],
|
|
1416
|
+
"target_section": a_j["section"],
|
|
1417
|
+
"target_url": a_j.get("path", ""),
|
|
1418
|
+
"target_doi": a_j.get("doi"),
|
|
1419
|
+
"lsa_score": round(float(lsa_matrix[i, j]), 4),
|
|
1420
|
+
"semantic_score": round(float(sem_matrix[i, j]), 4),
|
|
1421
|
+
"signed_diff": round(float(signed), 4),
|
|
1422
|
+
"abs_diff": round(float(absv), 4),
|
|
1423
|
+
"direction": classify_direction(signed),
|
|
1424
|
+
})
|
|
1425
|
+
|
|
1426
|
+
result = {
|
|
1427
|
+
"metadata": {
|
|
1428
|
+
"mode": "external",
|
|
1429
|
+
"topic": topic,
|
|
1430
|
+
"topic_slug": slug,
|
|
1431
|
+
"room_dir": str(room_dir),
|
|
1432
|
+
"corpus_dir": str(corpus_dir),
|
|
1433
|
+
"corpus_path": str(corpus_path),
|
|
1434
|
+
"corpus_size": len(docs) if docs else len(records),
|
|
1435
|
+
"artifact_count": len(artifacts),
|
|
1436
|
+
"tier_counts": _count_by_source(docs) if docs else {},
|
|
1437
|
+
"cache_mode": cache_mode,
|
|
1438
|
+
"cache_age_days": round(cache_age_days, 2) if cache_age_days is not None else None,
|
|
1439
|
+
"cache_namespace": cache_namespace,
|
|
1440
|
+
"cache_ttl_days": _RS_CACHE_TTL_DAYS if signal_ok else None,
|
|
1441
|
+
"topk_requested": topk,
|
|
1442
|
+
"threshold": threshold,
|
|
1443
|
+
"embedding_model": model_used,
|
|
1444
|
+
"thesis_generated": False,
|
|
1445
|
+
"no_thesis": bool(no_thesis),
|
|
1446
|
+
"edges_written": 0, # Mode B does NOT write room.db edges.
|
|
1447
|
+
"timestamp": datetime.now(timezone.utc).isoformat(),
|
|
1448
|
+
"engine_version": ENGINE_VERSION,
|
|
1449
|
+
},
|
|
1450
|
+
"pairs": pair_dicts,
|
|
1451
|
+
}
|
|
1452
|
+
return result, corpus_dir
|
|
1453
|
+
|
|
1454
|
+
|
|
1455
|
+
def _count_by_source(docs: Sequence[Dict]) -> Dict[str, int]:
|
|
1456
|
+
counts: Dict[str, int] = {}
|
|
1457
|
+
for d in docs:
|
|
1458
|
+
src = d.get("source") or "unknown"
|
|
1459
|
+
counts[src] = counts.get(src, 0) + 1
|
|
1460
|
+
return counts
|
|
1461
|
+
|
|
1462
|
+
|
|
1463
|
+
# --- Mode C (hybrid) runner --------------------------------------------------
|
|
1464
|
+
|
|
1465
|
+
# Design note on semantic similarity for the unified corpus (Plan 89-05,
|
|
1466
|
+
# updated Plan 296-05): the external side used to carry cached 1024-dim
|
|
1467
|
+
# multilingual-e5-large vectors from Pinecone, which would have been a
|
|
1468
|
+
# dimensional bug (plan Risk 1) if mixed directly with the room side's
|
|
1469
|
+
# 384-dim local MiniLM vectors. Plan 296-05's repoint of lib.core.rs_cache
|
|
1470
|
+
# onto the local signal cache CLOSES that hazard by construction: the
|
|
1471
|
+
# external side now also carries local 384-dim vectors from the same
|
|
1472
|
+
# encoder as the room side. The safe path below is UNCHANGED regardless --
|
|
1473
|
+
# embed the entire unified corpus in a single model space (local MiniLM via
|
|
1474
|
+
# compute_embeddings()) so the pairwise cosine is well-defined -- because
|
|
1475
|
+
# doing so also lets a run with a cold, empty, or unavailable signal cache
|
|
1476
|
+
# degrade the same way it always has, rather than depending on the cache
|
|
1477
|
+
# having already run.
|
|
1478
|
+
|
|
1479
|
+
|
|
1480
|
+
def run_mode_hybrid(
|
|
1481
|
+
room_dir: Path,
|
|
1482
|
+
topic: str,
|
|
1483
|
+
topk: int,
|
|
1484
|
+
threshold: float,
|
|
1485
|
+
no_thesis: bool,
|
|
1486
|
+
external_target: int = 2000,
|
|
1487
|
+
) -> Tuple[Dict, Path]:
|
|
1488
|
+
"""Mode C: unified room + external corpus with cross-corpus filter.
|
|
1489
|
+
|
|
1490
|
+
The killer feature of Phase 89: rediscovers external-domain concepts
|
|
1491
|
+
that have a structural match in the user's room but never entered the
|
|
1492
|
+
room's vocabulary, and vice versa. Consumed downstream by the Plan
|
|
1493
|
+
89-06 bridge-writer for Obsidian rendering.
|
|
1494
|
+
|
|
1495
|
+
Returns (result dict, corpus_dir) -- CLI writes results JSON inside
|
|
1496
|
+
corpus_dir at {room}/research/{topic-slug}/.rs-engine-results.json.
|
|
1497
|
+
|
|
1498
|
+
Cross-corpus contract:
|
|
1499
|
+
- metadata.mode == "hybrid"
|
|
1500
|
+
- metadata.hybrid == True (explicit flag per plan must_haves[2])
|
|
1501
|
+
- every pair has origin mismatch: one room side, one external side
|
|
1502
|
+
- pair_dicts carry both room_artifact_id and external_doc fields
|
|
1503
|
+
- direction is the standard Mode A / Mode B signed classification
|
|
1504
|
+
|
|
1505
|
+
Edge cases:
|
|
1506
|
+
- rs_hybrid import failed: raise NotImplementedError early so the
|
|
1507
|
+
CLI surfaces a clear "cannot run Mode C" message.
|
|
1508
|
+
- Empty room corpus: return empty pairs with metadata preserved so
|
|
1509
|
+
downstream readers do not trip on missing keys.
|
|
1510
|
+
- Empty external corpus (rare -- cold fetch returned nothing):
|
|
1511
|
+
same empty-pairs result. Caller sees metadata.external_count=0.
|
|
1512
|
+
- Unified corpus < 10 artifacts: skip LSA (same threshold the
|
|
1513
|
+
cross-room path uses). LSA topic keywords are unstable below
|
|
1514
|
+
this size.
|
|
1515
|
+
"""
|
|
1516
|
+
if not _RS_HYBRID_AVAILABLE:
|
|
1517
|
+
raise NotImplementedError(
|
|
1518
|
+
"rs-engine: --mode hybrid requires lib.core.rs_hybrid. "
|
|
1519
|
+
f"Import failed: {globals().get('_rs_hybrid_import_err_msg', 'unknown')}"
|
|
1520
|
+
)
|
|
1521
|
+
|
|
1522
|
+
# Signal-cache availability check (mirrors run_mode_external's gate,
|
|
1523
|
+
# Plan 296-05). Not blocking: build_unified_corpus's own
|
|
1524
|
+
# _load_external_records degrades to an empty external side
|
|
1525
|
+
# (cache_mode="bypass") on its own when the signal cache cannot be
|
|
1526
|
+
# reached, via the room_dir it now threads through. This call exists so
|
|
1527
|
+
# the reason is narrated up front instead of only discoverable later in
|
|
1528
|
+
# hybrid_meta.
|
|
1529
|
+
signal_ok, signal_skip_reason = _signal_cache_available(room_dir=str(room_dir))
|
|
1530
|
+
if not signal_ok:
|
|
1531
|
+
print(
|
|
1532
|
+
f"[rs-engine hybrid] Signal cache skipped ({signal_skip_reason}); "
|
|
1533
|
+
f"external side of the unified corpus will be empty this run.",
|
|
1534
|
+
file=sys.stderr,
|
|
1535
|
+
)
|
|
1536
|
+
|
|
1537
|
+
# Build the unified corpus + origin mask via rs_hybrid.
|
|
1538
|
+
corpus, origin_mask, hybrid_meta = _rs_hybrid_build(
|
|
1539
|
+
str(room_dir),
|
|
1540
|
+
topic,
|
|
1541
|
+
external_target=external_target,
|
|
1542
|
+
)
|
|
1543
|
+
slug = hybrid_meta.get("topic_slug") or topic_slug(topic)
|
|
1544
|
+
if not slug:
|
|
1545
|
+
raise ValueError("topic slug resolved to empty string")
|
|
1546
|
+
corpus_dir = room_dir / "research" / slug
|
|
1547
|
+
corpus_dir.mkdir(parents=True, exist_ok=True)
|
|
1548
|
+
|
|
1549
|
+
room_count = int(hybrid_meta.get("room_count", 0))
|
|
1550
|
+
external_count = int(hybrid_meta.get("external_count", 0))
|
|
1551
|
+
|
|
1552
|
+
def _empty_result(reason: str) -> Tuple[Dict, Path]:
|
|
1553
|
+
print(f"[rs-engine hybrid] {reason}", file=sys.stderr)
|
|
1554
|
+
return (
|
|
1555
|
+
{
|
|
1556
|
+
"metadata": {
|
|
1557
|
+
"mode": "hybrid",
|
|
1558
|
+
"hybrid": True,
|
|
1559
|
+
"topic": topic,
|
|
1560
|
+
"topic_slug": slug,
|
|
1561
|
+
"room_dir": str(room_dir),
|
|
1562
|
+
"corpus_dir": str(corpus_dir),
|
|
1563
|
+
"room_id": hybrid_meta.get("room_id"),
|
|
1564
|
+
"room_count": room_count,
|
|
1565
|
+
"external_count": external_count,
|
|
1566
|
+
"artifact_count": room_count + external_count,
|
|
1567
|
+
"cache_mode": hybrid_meta.get("cache_mode"),
|
|
1568
|
+
"cache_age_days": hybrid_meta.get("cache_age_days"),
|
|
1569
|
+
"cache_namespace": hybrid_meta.get("cache_namespace"),
|
|
1570
|
+
"cache_ttl_days": hybrid_meta.get("cache_ttl_days"),
|
|
1571
|
+
"topk_requested": topk,
|
|
1572
|
+
"threshold": threshold,
|
|
1573
|
+
"no_thesis": bool(no_thesis),
|
|
1574
|
+
"thesis_generated": False,
|
|
1575
|
+
"edges_written": 0,
|
|
1576
|
+
"timestamp": datetime.now(timezone.utc).isoformat(),
|
|
1577
|
+
"engine_version": ENGINE_VERSION,
|
|
1578
|
+
"skip_reason": reason,
|
|
1579
|
+
},
|
|
1580
|
+
"pairs": [],
|
|
1581
|
+
},
|
|
1582
|
+
corpus_dir,
|
|
1583
|
+
)
|
|
1584
|
+
|
|
1585
|
+
# Edge: no room artifacts -- cross-corpus filter yields zero pairs by
|
|
1586
|
+
# construction; save the LSA compute.
|
|
1587
|
+
if room_count == 0:
|
|
1588
|
+
return _empty_result(
|
|
1589
|
+
"Room has no indexable .md artifacts; cross-corpus filter "
|
|
1590
|
+
"yields zero pairs by construction."
|
|
1591
|
+
)
|
|
1592
|
+
# Edge: no external corpus.
|
|
1593
|
+
if external_count == 0:
|
|
1594
|
+
return _empty_result(
|
|
1595
|
+
f"External corpus for topic {topic!r} is empty (cache_mode="
|
|
1596
|
+
f"{hybrid_meta.get('cache_mode')}); cross-corpus filter "
|
|
1597
|
+
f"yields zero pairs by construction."
|
|
1598
|
+
)
|
|
1599
|
+
# Edge: combined corpus too small for stable LSA topic-keyword fit.
|
|
1600
|
+
if len(corpus) < 10:
|
|
1601
|
+
return _empty_result(
|
|
1602
|
+
f"Unified corpus has only {len(corpus)} artifacts; need >= 10 "
|
|
1603
|
+
f"for stable LSA fit."
|
|
1604
|
+
)
|
|
1605
|
+
|
|
1606
|
+
# Structural signal over the unified vocabulary.
|
|
1607
|
+
texts = [f"{a.get('title','')}\n{a['text']}" for a in corpus]
|
|
1608
|
+
lsa_matrix = build_lsa_matrix(texts)
|
|
1609
|
+
|
|
1610
|
+
# Semantic signal: embed the full unified corpus in one model space so
|
|
1611
|
+
# the sem_matrix is dimensionally homogeneous. Room and external share
|
|
1612
|
+
# the SAME model by construction (Plan Risk 1 mitigation). We reuse
|
|
1613
|
+
# compute_embeddings (MiniLM-or-cache) and scope the cache to the
|
|
1614
|
+
# research/{slug}/ directory to avoid polluting Mode A's room-root
|
|
1615
|
+
# .rs-engine-cache.json -- different corpus shape, different keys.
|
|
1616
|
+
cache_artifacts = [
|
|
1617
|
+
{"id": c["global_id"], "text": t}
|
|
1618
|
+
for c, t in zip(corpus, texts)
|
|
1619
|
+
]
|
|
1620
|
+
embeddings, model_used = compute_embeddings(cache_artifacts, corpus_dir)
|
|
1621
|
+
if embeddings is None:
|
|
1622
|
+
print(
|
|
1623
|
+
"rs-engine hybrid: no embedder available; "
|
|
1624
|
+
"semantic matrix set to identity.",
|
|
1625
|
+
file=sys.stderr,
|
|
1626
|
+
)
|
|
1627
|
+
n = len(corpus)
|
|
1628
|
+
sem_matrix = np.eye(n, dtype=np.float32)
|
|
1629
|
+
else:
|
|
1630
|
+
sem_matrix = semantic_similarity_matrix(embeddings)
|
|
1631
|
+
|
|
1632
|
+
# Overshoot top-k, then post-filter to cross-corpus only.
|
|
1633
|
+
overshoot_k = max(topk * HYBRID_OVERSHOOT, topk + 10)
|
|
1634
|
+
raw_pairs = abs_diff_topk(lsa_matrix, sem_matrix, k=overshoot_k)
|
|
1635
|
+
|
|
1636
|
+
cross_pairs = _rs_hybrid_filter(raw_pairs, origin_mask, topk)
|
|
1637
|
+
|
|
1638
|
+
# Apply threshold + build full pair dicts with room_artifact / external_doc.
|
|
1639
|
+
pair_dicts: List[Dict] = []
|
|
1640
|
+
for cp in cross_pairs:
|
|
1641
|
+
absv = cp["abs_diff"]
|
|
1642
|
+
if absv < threshold:
|
|
1643
|
+
continue
|
|
1644
|
+
i, j = cp["i"], cp["j"]
|
|
1645
|
+
room_idx = cp["room_side"]
|
|
1646
|
+
ext_idx = cp["external_side"]
|
|
1647
|
+
room_art = corpus[room_idx]
|
|
1648
|
+
ext_doc = corpus[ext_idx]
|
|
1649
|
+
signed = cp["signed_diff"]
|
|
1650
|
+
|
|
1651
|
+
# Preserve the standard Mode A / Mode B fields (source_*/target_*)
|
|
1652
|
+
# so the bridge-writer resolvePairIdentity picks them up without
|
|
1653
|
+
# per-mode branches. Canonical orientation: source = room side,
|
|
1654
|
+
# target = external side. This maps the signed-direction
|
|
1655
|
+
# interpretation from plan must_haves[3]:
|
|
1656
|
+
# structural_transfer (signed > 0):
|
|
1657
|
+
# "your work has the vocabulary, world has the concept"
|
|
1658
|
+
# semantic_implementation (signed < 0):
|
|
1659
|
+
# "your concept exists in the world's vocabulary"
|
|
1660
|
+
pair_dicts.append({
|
|
1661
|
+
"source_artifact_id": room_art["global_id"],
|
|
1662
|
+
"source_artifact": room_art.get("artifact_id") or room_art["global_id"],
|
|
1663
|
+
"source_room": room_art.get("room_id"),
|
|
1664
|
+
"source_section": room_art.get("section"),
|
|
1665
|
+
"source_title": room_art.get("title", ""),
|
|
1666
|
+
"source_path": room_art.get("path", ""),
|
|
1667
|
+
"target_artifact_id": ext_doc["global_id"],
|
|
1668
|
+
"target_external_id": ext_doc.get("external_id") or ext_doc["global_id"],
|
|
1669
|
+
"target_doi": ext_doc.get("doi"),
|
|
1670
|
+
"target_source": ext_doc.get("source"),
|
|
1671
|
+
"target_title": ext_doc.get("title", ""),
|
|
1672
|
+
"target_url": ext_doc.get("path", ""),
|
|
1673
|
+
"target_year": ext_doc.get("year"),
|
|
1674
|
+
# Rich cross-corpus annotations consumed by Plan 89-06 bridge
|
|
1675
|
+
# writer and Plan 89-07 release dashboard.
|
|
1676
|
+
"room_artifact": {
|
|
1677
|
+
"global_id": room_art["global_id"],
|
|
1678
|
+
"artifact_id": room_art.get("artifact_id"),
|
|
1679
|
+
"room_id": room_art.get("room_id"),
|
|
1680
|
+
"section": room_art.get("section"),
|
|
1681
|
+
"title": room_art.get("title", ""),
|
|
1682
|
+
"path": room_art.get("path", ""),
|
|
1683
|
+
},
|
|
1684
|
+
"external_doc": {
|
|
1685
|
+
"global_id": ext_doc["global_id"],
|
|
1686
|
+
"external_id": ext_doc.get("external_id"),
|
|
1687
|
+
"source": ext_doc.get("source"),
|
|
1688
|
+
"title": ext_doc.get("title", ""),
|
|
1689
|
+
"doi": ext_doc.get("doi"),
|
|
1690
|
+
"year": ext_doc.get("year"),
|
|
1691
|
+
"url": ext_doc.get("path", ""),
|
|
1692
|
+
},
|
|
1693
|
+
"lsa_score": round(float(lsa_matrix[i, j]), 4),
|
|
1694
|
+
"semantic_score": round(float(sem_matrix[i, j]), 4),
|
|
1695
|
+
"signed_diff": round(float(signed), 4),
|
|
1696
|
+
"abs_diff": round(float(absv), 4),
|
|
1697
|
+
"direction": classify_direction(signed),
|
|
1698
|
+
})
|
|
1699
|
+
|
|
1700
|
+
result = {
|
|
1701
|
+
"metadata": {
|
|
1702
|
+
"mode": "hybrid",
|
|
1703
|
+
"hybrid": True,
|
|
1704
|
+
"topic": topic,
|
|
1705
|
+
"topic_slug": slug,
|
|
1706
|
+
"room_dir": str(room_dir),
|
|
1707
|
+
"corpus_dir": str(corpus_dir),
|
|
1708
|
+
"room_id": hybrid_meta.get("room_id"),
|
|
1709
|
+
"room_count": room_count,
|
|
1710
|
+
"external_count": external_count,
|
|
1711
|
+
"artifact_count": len(corpus),
|
|
1712
|
+
"cache_mode": hybrid_meta.get("cache_mode"),
|
|
1713
|
+
"cache_age_days": hybrid_meta.get("cache_age_days"),
|
|
1714
|
+
"cache_namespace": hybrid_meta.get("cache_namespace"),
|
|
1715
|
+
"cache_ttl_days": hybrid_meta.get("cache_ttl_days"),
|
|
1716
|
+
"topk_requested": topk,
|
|
1717
|
+
"threshold": threshold,
|
|
1718
|
+
"embedding_model": model_used,
|
|
1719
|
+
"no_thesis": bool(no_thesis),
|
|
1720
|
+
"thesis_generated": False,
|
|
1721
|
+
# Mode C does NOT write REVERSE_SALIENT edges into room.db.
|
|
1722
|
+
# Plan 89-06 bridge-writer produces the user-visible artifact
|
|
1723
|
+
# surface; Plan 89-07 wires the optional room.db edge with
|
|
1724
|
+
# properties.source='rs-engine-hybrid' alongside the agent.
|
|
1725
|
+
"edges_written": 0,
|
|
1726
|
+
"hybrid_overshoot": HYBRID_OVERSHOOT,
|
|
1727
|
+
"external_target": external_target,
|
|
1728
|
+
"timestamp": datetime.now(timezone.utc).isoformat(),
|
|
1729
|
+
"engine_version": ENGINE_VERSION,
|
|
1730
|
+
},
|
|
1731
|
+
"pairs": pair_dicts,
|
|
1732
|
+
}
|
|
1733
|
+
return result, corpus_dir
|
|
1734
|
+
|
|
1735
|
+
|
|
1736
|
+
# --- Cowork tri-polar surface ------------------------------------------------
|
|
1737
|
+
|
|
1738
|
+
def _write_cowork_symlink(room_dir: Path, results_path: Path) -> None:
|
|
1739
|
+
"""When COWORK=1, expose results under 00_Context/ for team visibility."""
|
|
1740
|
+
if os.environ.get("COWORK") != "1":
|
|
1741
|
+
return
|
|
1742
|
+
cowork_dir = room_dir / "00_Context"
|
|
1743
|
+
if not cowork_dir.is_dir():
|
|
1744
|
+
return
|
|
1745
|
+
symlink_path = cowork_dir / "rs-engine-results.json"
|
|
1746
|
+
try:
|
|
1747
|
+
if symlink_path.is_symlink() or symlink_path.exists():
|
|
1748
|
+
symlink_path.unlink()
|
|
1749
|
+
os.symlink(results_path, symlink_path)
|
|
1750
|
+
except OSError as e:
|
|
1751
|
+
print(f"rs-engine: cowork symlink skipped: {e}", file=sys.stderr)
|
|
1752
|
+
|
|
1753
|
+
|
|
1754
|
+
# --- CLI ---------------------------------------------------------------------
|
|
1755
|
+
|
|
1756
|
+
def _write_results(room_dir: Path, result: Dict, output: Optional[str]) -> Path:
|
|
1757
|
+
path = Path(output).resolve() if output else (room_dir / RESULTS_FILENAME)
|
|
1758
|
+
path.write_text(json.dumps(result, indent=2), encoding="utf-8")
|
|
1759
|
+
return path
|
|
1760
|
+
|
|
1761
|
+
|
|
1762
|
+
def build_parser() -> argparse.ArgumentParser:
|
|
1763
|
+
parser = argparse.ArgumentParser(
|
|
1764
|
+
prog="rs-engine",
|
|
1765
|
+
description=(
|
|
1766
|
+
"Reverse Salient Engine (Phase 89). Faithfully ports the Kwan 2023 "
|
|
1767
|
+
"topic-keyword-membership LSA + signed abs-diff detection "
|
|
1768
|
+
"algorithm. Mode A runs on a single room; Mode B fetches an "
|
|
1769
|
+
"external research corpus (OpenAlex, arXiv, Tavily); Mode C "
|
|
1770
|
+
"(hybrid) unifies the room with an external corpus and keeps only "
|
|
1771
|
+
"cross-corpus pairs -- the killer feature of Phase 89."
|
|
1772
|
+
),
|
|
1773
|
+
epilog=(
|
|
1774
|
+
"Three-surface usage:\n"
|
|
1775
|
+
" CLI python scripts/rs-engine.py --mode internal --room ./room\n"
|
|
1776
|
+
" python scripts/rs-engine.py --rooms ~/rooms/a ~/rooms/b ~/rooms/c --topk 50\n"
|
|
1777
|
+
" python scripts/rs-engine.py --mode external --topic 'quantum biology' --room ./room\n"
|
|
1778
|
+
" python scripts/rs-engine.py --mode hybrid --topic 'quantum biology' --room ./room --topk 30\n"
|
|
1779
|
+
" Desktop invoked by ReverseSalientAgent conversational trigger\n"
|
|
1780
|
+
" (Plan 89-07).\n"
|
|
1781
|
+
" Cowork set COWORK=1 to symlink results under 00_Context/.\n"
|
|
1782
|
+
),
|
|
1783
|
+
formatter_class=argparse.RawDescriptionHelpFormatter,
|
|
1784
|
+
)
|
|
1785
|
+
parser.add_argument(
|
|
1786
|
+
"--mode",
|
|
1787
|
+
choices=["internal", "external", "hybrid"],
|
|
1788
|
+
default="internal",
|
|
1789
|
+
help=(
|
|
1790
|
+
"internal=Mode A (single room, Plan 89-01); "
|
|
1791
|
+
"external=Mode B (research corpus, Plan 89-02); "
|
|
1792
|
+
"hybrid=Mode C (room x external unified matrix, Plan 89-05)."
|
|
1793
|
+
),
|
|
1794
|
+
)
|
|
1795
|
+
parser.add_argument("--room", required=False, help="Path to room directory (Mode A / B / C).")
|
|
1796
|
+
parser.add_argument(
|
|
1797
|
+
"--rooms",
|
|
1798
|
+
nargs="+",
|
|
1799
|
+
required=False,
|
|
1800
|
+
help=(
|
|
1801
|
+
"Two or more room paths for cross-room Mode A (Plan 89-04). "
|
|
1802
|
+
"Rediscovers bridges between user projects. Mutually exclusive "
|
|
1803
|
+
"with --room. Results land in the FIRST room's .rs-engine-results.json "
|
|
1804
|
+
"with metadata.mode='cross-room'."
|
|
1805
|
+
),
|
|
1806
|
+
)
|
|
1807
|
+
parser.add_argument(
|
|
1808
|
+
"--topic",
|
|
1809
|
+
required=False,
|
|
1810
|
+
help=(
|
|
1811
|
+
"Free-text research topic (required for --mode external). "
|
|
1812
|
+
"Normalized to a kebab-case slug via lib.core.rs_corpus.topic_slug "
|
|
1813
|
+
"for consistent {room}/research/{topic-slug}/ paths."
|
|
1814
|
+
),
|
|
1815
|
+
)
|
|
1816
|
+
parser.add_argument(
|
|
1817
|
+
"--topk",
|
|
1818
|
+
type=int,
|
|
1819
|
+
default=DEFAULT_TOPK,
|
|
1820
|
+
help=f"Maximum number of pairs to keep (default {DEFAULT_TOPK}).",
|
|
1821
|
+
)
|
|
1822
|
+
parser.add_argument(
|
|
1823
|
+
"--threshold",
|
|
1824
|
+
type=float,
|
|
1825
|
+
default=DEFAULT_THRESHOLD,
|
|
1826
|
+
help=(
|
|
1827
|
+
f"Minimum abs(semantic - lsa) to keep a pair (default {DEFAULT_THRESHOLD}). "
|
|
1828
|
+
"0.0 disables filtering."
|
|
1829
|
+
),
|
|
1830
|
+
)
|
|
1831
|
+
parser.add_argument(
|
|
1832
|
+
"--no-thesis",
|
|
1833
|
+
action="store_true",
|
|
1834
|
+
help=(
|
|
1835
|
+
"Skip LLM innovation-thesis generation (RESEARCH Q6). Plan 89-01 always "
|
|
1836
|
+
"runs cost-free; this flag is accepted for compatibility with later plans."
|
|
1837
|
+
),
|
|
1838
|
+
)
|
|
1839
|
+
parser.add_argument(
|
|
1840
|
+
"--external-target",
|
|
1841
|
+
type=int,
|
|
1842
|
+
default=2000,
|
|
1843
|
+
help=(
|
|
1844
|
+
"Max external docs to include in --mode hybrid's unified corpus "
|
|
1845
|
+
"(default 2000; clamped to rs_hybrid.MAX_EXTERNAL_TARGET). "
|
|
1846
|
+
"Ignored for Mode A / Mode B."
|
|
1847
|
+
),
|
|
1848
|
+
)
|
|
1849
|
+
parser.add_argument(
|
|
1850
|
+
"--output",
|
|
1851
|
+
default=None,
|
|
1852
|
+
help=f"Output JSON path (default: {{room}}/{RESULTS_FILENAME}).",
|
|
1853
|
+
)
|
|
1854
|
+
return parser
|
|
1855
|
+
|
|
1856
|
+
|
|
1857
|
+
def main(argv: Optional[Sequence[str]] = None) -> int:
|
|
1858
|
+
parser = build_parser()
|
|
1859
|
+
args = parser.parse_args(argv)
|
|
1860
|
+
|
|
1861
|
+
# Cross-room Mode A multi-room (Plan 89-04). --rooms is mutually
|
|
1862
|
+
# exclusive with --room. When --rooms is set, --mode is ignored
|
|
1863
|
+
# (cross-room is implicitly "internal multi-room").
|
|
1864
|
+
if args.rooms:
|
|
1865
|
+
if args.room:
|
|
1866
|
+
print(
|
|
1867
|
+
"rs-engine: --rooms and --room are mutually exclusive. "
|
|
1868
|
+
"Pick one.",
|
|
1869
|
+
file=sys.stderr,
|
|
1870
|
+
)
|
|
1871
|
+
return 2
|
|
1872
|
+
if len(args.rooms) < 2:
|
|
1873
|
+
print(
|
|
1874
|
+
"rs-engine: --rooms requires at least two room paths for "
|
|
1875
|
+
"cross-room mode.",
|
|
1876
|
+
file=sys.stderr,
|
|
1877
|
+
)
|
|
1878
|
+
return 2
|
|
1879
|
+
if args.mode == "external":
|
|
1880
|
+
print(
|
|
1881
|
+
"rs-engine: --mode external is incompatible with --rooms. "
|
|
1882
|
+
"Use --room for external mode.",
|
|
1883
|
+
file=sys.stderr,
|
|
1884
|
+
)
|
|
1885
|
+
return 2
|
|
1886
|
+
if args.mode == "hybrid":
|
|
1887
|
+
print(
|
|
1888
|
+
"rs-engine: --mode hybrid is incompatible with --rooms. "
|
|
1889
|
+
"Hybrid unifies ONE room with an external corpus; use --room "
|
|
1890
|
+
"for hybrid mode.",
|
|
1891
|
+
file=sys.stderr,
|
|
1892
|
+
)
|
|
1893
|
+
return 2
|
|
1894
|
+
|
|
1895
|
+
topk = max(1, args.topk)
|
|
1896
|
+
threshold = max(0.0, args.threshold)
|
|
1897
|
+
|
|
1898
|
+
try:
|
|
1899
|
+
result = run_mode_cross_room(
|
|
1900
|
+
args.rooms,
|
|
1901
|
+
topk=topk,
|
|
1902
|
+
threshold=threshold,
|
|
1903
|
+
no_thesis=args.no_thesis,
|
|
1904
|
+
)
|
|
1905
|
+
except NotImplementedError as e:
|
|
1906
|
+
print(f"rs-engine: {e}", file=sys.stderr)
|
|
1907
|
+
return 3
|
|
1908
|
+
except ValueError as e:
|
|
1909
|
+
print(f"rs-engine: {e}", file=sys.stderr)
|
|
1910
|
+
return 2
|
|
1911
|
+
except Exception as e: # pragma: no cover -- defensive envelope
|
|
1912
|
+
print(f"rs-engine: error: {e}", file=sys.stderr)
|
|
1913
|
+
return 1
|
|
1914
|
+
|
|
1915
|
+
# Output lands in the FIRST room's .rs-engine-results.json by
|
|
1916
|
+
# default (per plan). --output overrides if supplied.
|
|
1917
|
+
first_room_dir = Path(args.rooms[0]).resolve()
|
|
1918
|
+
if not first_room_dir.is_dir():
|
|
1919
|
+
print(
|
|
1920
|
+
f"rs-engine: first room {first_room_dir} is not a directory; "
|
|
1921
|
+
f"cannot write results.",
|
|
1922
|
+
file=sys.stderr,
|
|
1923
|
+
)
|
|
1924
|
+
return 1
|
|
1925
|
+
output_path = _write_results(first_room_dir, result, args.output)
|
|
1926
|
+
|
|
1927
|
+
_write_cowork_symlink(first_room_dir, output_path)
|
|
1928
|
+
_print_cross_room_summary(result)
|
|
1929
|
+
|
|
1930
|
+
meta = result["metadata"]
|
|
1931
|
+
print(
|
|
1932
|
+
f"rs-engine: {len(result['pairs'])} cross-room pairs from "
|
|
1933
|
+
f"{meta['artifact_count']} artifacts across "
|
|
1934
|
+
f"{len(meta['rooms'])} rooms written to {output_path} "
|
|
1935
|
+
f"(model={meta.get('embedding_model', 'n/a')})",
|
|
1936
|
+
file=sys.stderr,
|
|
1937
|
+
)
|
|
1938
|
+
return 0
|
|
1939
|
+
|
|
1940
|
+
if not args.room:
|
|
1941
|
+
print(
|
|
1942
|
+
f"rs-engine: --room is required for --mode {args.mode} "
|
|
1943
|
+
f"(or pass --rooms for cross-room mode)",
|
|
1944
|
+
file=sys.stderr,
|
|
1945
|
+
)
|
|
1946
|
+
return 2
|
|
1947
|
+
|
|
1948
|
+
room_dir = Path(args.room).resolve()
|
|
1949
|
+
# Allow the room directory to be auto-created for Mode B when it does
|
|
1950
|
+
# not exist yet. Mode A still requires an existing room. This matches
|
|
1951
|
+
# the plan's end-to-end smoke which targets /tmp/test-room. Hybrid
|
|
1952
|
+
# Mode C requires an existing room (its whole point is room x external).
|
|
1953
|
+
if args.mode == "external" and not room_dir.exists():
|
|
1954
|
+
try:
|
|
1955
|
+
room_dir.mkdir(parents=True, exist_ok=True)
|
|
1956
|
+
except OSError as e:
|
|
1957
|
+
print(f"rs-engine: could not create {room_dir}: {e}", file=sys.stderr)
|
|
1958
|
+
return 1
|
|
1959
|
+
if not room_dir.is_dir():
|
|
1960
|
+
print(f"rs-engine: {room_dir} is not a directory", file=sys.stderr)
|
|
1961
|
+
return 1
|
|
1962
|
+
|
|
1963
|
+
topk = max(1, args.topk)
|
|
1964
|
+
threshold = max(0.0, args.threshold)
|
|
1965
|
+
|
|
1966
|
+
try:
|
|
1967
|
+
if args.mode == "internal":
|
|
1968
|
+
result = run_mode_internal(
|
|
1969
|
+
room_dir,
|
|
1970
|
+
topk=topk,
|
|
1971
|
+
threshold=threshold,
|
|
1972
|
+
no_thesis=args.no_thesis,
|
|
1973
|
+
)
|
|
1974
|
+
output_path = _write_results(room_dir, result, args.output)
|
|
1975
|
+
elif args.mode == "hybrid":
|
|
1976
|
+
if not args.topic:
|
|
1977
|
+
print(
|
|
1978
|
+
"rs-engine: --topic is required for --mode hybrid",
|
|
1979
|
+
file=sys.stderr,
|
|
1980
|
+
)
|
|
1981
|
+
return 2
|
|
1982
|
+
result, corpus_dir = run_mode_hybrid(
|
|
1983
|
+
room_dir,
|
|
1984
|
+
topic=args.topic,
|
|
1985
|
+
topk=topk,
|
|
1986
|
+
threshold=threshold,
|
|
1987
|
+
no_thesis=args.no_thesis,
|
|
1988
|
+
external_target=args.external_target,
|
|
1989
|
+
)
|
|
1990
|
+
default_path = corpus_dir / RESULTS_FILENAME
|
|
1991
|
+
if args.output:
|
|
1992
|
+
output_path = Path(args.output).resolve()
|
|
1993
|
+
output_path.parent.mkdir(parents=True, exist_ok=True)
|
|
1994
|
+
output_path.write_text(
|
|
1995
|
+
json.dumps(result, indent=2), encoding="utf-8",
|
|
1996
|
+
)
|
|
1997
|
+
else:
|
|
1998
|
+
default_path.parent.mkdir(parents=True, exist_ok=True)
|
|
1999
|
+
default_path.write_text(
|
|
2000
|
+
json.dumps(result, indent=2), encoding="utf-8",
|
|
2001
|
+
)
|
|
2002
|
+
output_path = default_path
|
|
2003
|
+
else: # args.mode == "external"
|
|
2004
|
+
if not args.topic:
|
|
2005
|
+
print(
|
|
2006
|
+
"rs-engine: --topic is required for --mode external",
|
|
2007
|
+
file=sys.stderr,
|
|
2008
|
+
)
|
|
2009
|
+
return 2
|
|
2010
|
+
result, corpus_dir = run_mode_external(
|
|
2011
|
+
room_dir,
|
|
2012
|
+
topic=args.topic,
|
|
2013
|
+
topk=topk,
|
|
2014
|
+
threshold=threshold,
|
|
2015
|
+
no_thesis=args.no_thesis,
|
|
2016
|
+
)
|
|
2017
|
+
# Default external output: {room}/research/{topic-slug}/.rs-engine-results.json
|
|
2018
|
+
default_path = corpus_dir / RESULTS_FILENAME
|
|
2019
|
+
if args.output:
|
|
2020
|
+
output_path = Path(args.output).resolve()
|
|
2021
|
+
output_path.parent.mkdir(parents=True, exist_ok=True)
|
|
2022
|
+
output_path.write_text(
|
|
2023
|
+
json.dumps(result, indent=2), encoding="utf-8",
|
|
2024
|
+
)
|
|
2025
|
+
else:
|
|
2026
|
+
default_path.parent.mkdir(parents=True, exist_ok=True)
|
|
2027
|
+
default_path.write_text(
|
|
2028
|
+
json.dumps(result, indent=2), encoding="utf-8",
|
|
2029
|
+
)
|
|
2030
|
+
output_path = default_path
|
|
2031
|
+
except NotImplementedError as e:
|
|
2032
|
+
print(f"rs-engine: {e}", file=sys.stderr)
|
|
2033
|
+
return 3
|
|
2034
|
+
except ValueError as e:
|
|
2035
|
+
print(f"rs-engine: {e}", file=sys.stderr)
|
|
2036
|
+
return 2
|
|
2037
|
+
except Exception as e: # pragma: no cover - defensive envelope
|
|
2038
|
+
print(f"rs-engine: error: {e}", file=sys.stderr)
|
|
2039
|
+
return 1
|
|
2040
|
+
|
|
2041
|
+
_write_cowork_symlink(room_dir, output_path)
|
|
2042
|
+
|
|
2043
|
+
meta = result["metadata"]
|
|
2044
|
+
if args.mode == "internal":
|
|
2045
|
+
print(
|
|
2046
|
+
f"rs-engine: {len(result['pairs'])}/{meta['artifact_count']} pairs written to "
|
|
2047
|
+
f"{output_path} (model={meta['embedding_model']}, edges={meta['edges_written']})",
|
|
2048
|
+
file=sys.stderr,
|
|
2049
|
+
)
|
|
2050
|
+
elif args.mode == "hybrid":
|
|
2051
|
+
print(
|
|
2052
|
+
f"rs-engine: {len(result['pairs'])} cross-corpus pairs from "
|
|
2053
|
+
f"{meta.get('room_count', 0)} room + {meta.get('external_count', 0)} "
|
|
2054
|
+
f"external = {meta.get('artifact_count', 0)} artifacts "
|
|
2055
|
+
f"(cache={meta.get('cache_mode', 'n/a')}) written to {output_path} "
|
|
2056
|
+
f"(model={meta.get('embedding_model', 'n/a')})",
|
|
2057
|
+
file=sys.stderr,
|
|
2058
|
+
)
|
|
2059
|
+
else:
|
|
2060
|
+
tiers = meta.get("tier_counts", {})
|
|
2061
|
+
tier_str = ", ".join(f"{k}={v}" for k, v in tiers.items()) or "empty"
|
|
2062
|
+
print(
|
|
2063
|
+
f"rs-engine: {len(result['pairs'])} pairs from "
|
|
2064
|
+
f"{meta.get('artifact_count', 0)} artifacts (corpus={meta.get('corpus_size', 0)}, "
|
|
2065
|
+
f"{tier_str}) written to {output_path} "
|
|
2066
|
+
f"(model={meta.get('embedding_model', 'n/a')})",
|
|
2067
|
+
file=sys.stderr,
|
|
2068
|
+
)
|
|
2069
|
+
return 0
|
|
2070
|
+
|
|
2071
|
+
|
|
2072
|
+
if __name__ == "__main__":
|
|
2073
|
+
sys.exit(main())
|