@mindrian_os/install 1.13.0-beta.11
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 +9 -0
- package/CHANGELOG.md +3333 -0
- package/LICENSE +123 -0
- package/README.md +673 -0
- package/agents/brain-query.md +80 -0
- package/agents/framework-runner.md +237 -0
- package/agents/grading.md +188 -0
- package/agents/investor.md +128 -0
- package/agents/larry-extended.md +135 -0
- package/agents/opportunity-scanner.md +91 -0
- package/agents/persona-analyst.md +132 -0
- package/agents/research.md +89 -0
- package/agents/reverse-salient-agent.md +27 -0
- package/bin/cli.js +142 -0
- package/bin/mindrian-mcp-server.cjs +182 -0
- package/bin/mindrian-tools.cjs +765 -0
- package/commands/act.md +439 -0
- package/commands/admin.md +404 -0
- package/commands/analyze-needs.md +42 -0
- package/commands/analyze-systems.md +39 -0
- package/commands/analyze-timing.md +42 -0
- package/commands/auto-explore.md +64 -0
- package/commands/beautiful-question.md +40 -0
- package/commands/brain-derive.md +78 -0
- package/commands/build-knowledge.md +42 -0
- package/commands/build-thesis.md +46 -0
- package/commands/causal.md +234 -0
- package/commands/challenge-assumptions.md +33 -0
- package/commands/compare-ventures.md +83 -0
- package/commands/dashboard.md +110 -0
- package/commands/deep-grade.md +82 -0
- package/commands/diagnose.md +58 -0
- package/commands/diagnostics.md +151 -0
- package/commands/doctor.md +151 -0
- package/commands/dominant-designs.md +40 -0
- package/commands/explain-decision.md +87 -0
- package/commands/explore-domains.md +42 -0
- package/commands/explore-futures.md +40 -0
- package/commands/explore-trends.md +42 -0
- package/commands/export.md +103 -0
- package/commands/file-meeting.md +724 -0
- package/commands/find-analogies.md +188 -0
- package/commands/find-bottlenecks.md +62 -0
- package/commands/find-connections.md +76 -0
- package/commands/funding.md +81 -0
- package/commands/grade.md +203 -0
- package/commands/graph.md +128 -0
- package/commands/hat-briefing.md +125 -0
- package/commands/heal.md +196 -0
- package/commands/help.md +399 -0
- package/commands/hmi-status.md +172 -0
- package/commands/jtbd.md +241 -0
- package/commands/leadership.md +73 -0
- package/commands/lean-canvas.md +40 -0
- package/commands/macro-trends.md +40 -0
- package/commands/map-unknowns.md +40 -0
- package/commands/memory.md +173 -0
- package/commands/models.md +175 -0
- package/commands/mos-reason.md +285 -0
- package/commands/mullins.md +120 -0
- package/commands/new-project.md +481 -0
- package/commands/onboard.md +434 -0
- package/commands/operator.md +149 -0
- package/commands/opportunities.md +144 -0
- package/commands/organize.md +497 -0
- package/commands/persona.md +198 -0
- package/commands/pipeline.md +112 -0
- package/commands/present.md +91 -0
- package/commands/publish.md +201 -0
- package/commands/query.md +124 -0
- package/commands/radar.md +72 -0
- package/commands/reanalyze.md +91 -0
- package/commands/research.md +196 -0
- package/commands/room.md +352 -0
- package/commands/rooms.md +598 -0
- package/commands/root-cause.md +40 -0
- package/commands/rs-experts.md +85 -0
- package/commands/rs-explain.md +100 -0
- package/commands/rs-fetch.md +94 -0
- package/commands/rs-thesis.md +85 -0
- package/commands/scenario-plan.md +40 -0
- package/commands/scheduled-tasks.md +285 -0
- package/commands/score-innovation.md +43 -0
- package/commands/scout.md +239 -0
- package/commands/setup.md +618 -0
- package/commands/snapshot.md +147 -0
- package/commands/speakers.md +84 -0
- package/commands/splash.md +28 -0
- package/commands/status.md +75 -0
- package/commands/structure-argument.md +42 -0
- package/commands/suggest-next.md +80 -0
- package/commands/systems-thinking.md +40 -0
- package/commands/think-hats.md +42 -0
- package/commands/update.md +181 -0
- package/commands/user-needs.md +40 -0
- package/commands/validate.md +40 -0
- package/commands/value-proposition.md +61 -0
- package/commands/vault.md +180 -0
- package/commands/visualize.md +52 -0
- package/commands/whitespace.md +507 -0
- package/commands/wiki.md +69 -0
- package/hooks/hooks.json +381 -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 +1043 -0
- package/lib/agents/reverse-salient-agent.cjs +679 -0
- package/lib/agents/tension-hook-agent.cjs +544 -0
- package/lib/brain/ROOM.md +44 -0
- package/lib/brain/chain-recommender.cjs +301 -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.cjs +287 -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_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/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/brain-client.cjs +865 -0
- package/lib/core/brain-derivation-prompts.cjs +326 -0
- package/lib/core/brain-derivation-queue.cjs +431 -0
- package/lib/core/brain-derivation.cjs +580 -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 +188 -0
- package/lib/core/bridge-writer.cjs +477 -0
- package/lib/core/chat-context-builder.cjs +253 -0
- package/lib/core/cross-room-aggregator.cjs +762 -0
- package/lib/core/daily-briefing.cjs +438 -0
- package/lib/core/decision-capture.cjs +618 -0
- package/lib/core/deep-links.cjs +82 -0
- package/lib/core/dispatch-optimizer.cjs +354 -0
- package/lib/core/dual-path-detector.cjs +84 -0
- package/lib/core/dual-path-detector.test.cjs +334 -0
- package/lib/core/exports-log.cjs +79 -0
- package/lib/core/feynman-minto-invariants.cjs +605 -0
- package/lib/core/folder-memory-async.cjs +338 -0
- package/lib/core/folder-memory-shared.cjs +890 -0
- package/lib/core/folder-memory.cjs +416 -0
- package/lib/core/framework-chain-composer.cjs +411 -0
- package/lib/core/frontmatter-schemas.cjs +330 -0
- package/lib/core/git-ops.cjs +141 -0
- package/lib/core/graph-ops.cjs +258 -0
- package/lib/core/hat-persistence.cjs +362 -0
- package/lib/core/index.cjs +60 -0
- package/lib/core/integration-registry.cjs +232 -0
- package/lib/core/intelligence-cascade.cjs +661 -0
- package/lib/core/lazygraph-ops.cjs +1057 -0
- package/lib/core/lru-cache.cjs +139 -0
- package/lib/core/mcp-profiles.cjs +182 -0
- package/lib/core/meeting-ops.cjs +54 -0
- package/lib/core/memory-ops.cjs +600 -0
- package/lib/core/migrations/ROOM.md +33 -0
- package/lib/core/migrations/phase-109-nodes-provenance.cjs +339 -0
- package/lib/core/migrations/phase-109-session-focus.cjs +99 -0
- package/lib/core/model-profiles.cjs +246 -0
- package/lib/core/mullins-scaffold.cjs +160 -0
- package/lib/core/nav-dial.cjs +316 -0
- package/lib/core/navigation/ROOM.md +15 -0
- package/lib/core/navigation/explanation.cjs +43 -0
- package/lib/core/navigation/focus.cjs +135 -0
- package/lib/core/navigation/ingestion.cjs +82 -0
- package/lib/core/navigation/insights.cjs +350 -0
- package/lib/core/navigation/memory-events.cjs +118 -0
- package/lib/core/navigation/neighborhood.cjs +78 -0
- package/lib/core/navigation/packet.cjs +182 -0
- package/lib/core/navigation/room-home.cjs +127 -0
- package/lib/core/navigation/transitions.cjs +82 -0
- package/lib/core/navigation-engine-shared.cjs +242 -0
- package/lib/core/navigation-engine.cjs +664 -0
- package/lib/core/navigation.cjs +60 -0
- package/lib/core/nl-graph-queries.cjs +164 -0
- package/lib/core/offer-presenter.cjs +406 -0
- package/lib/core/opportunity-extractor.cjs +183 -0
- package/lib/core/opportunity-ops.cjs +1371 -0
- package/lib/core/persona-ops.cjs +537 -0
- package/lib/core/persona-taxonomy.cjs +190 -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/reasoning-ops.cjs +639 -0
- package/lib/core/reverse-salient-persona-suffix.cjs +115 -0
- package/lib/core/room-classifier-strict-mode.cjs +229 -0
- package/lib/core/room-db.cjs +127 -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-type-detector.cjs +386 -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 +343 -0
- package/lib/core/rs-commercial-assessor.cjs +280 -0
- package/lib/core/rs-differential-scorer.cjs +376 -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-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-mind-map.cjs +656 -0
- package/lib/core/rs-neo4j-writer.cjs +388 -0
- package/lib/core/rs-nl-to-query.cjs +425 -0
- package/lib/core/rs-pinecone-bridge.cjs +303 -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 +443 -0
- package/lib/core/rs-thesis-generator.cjs +188 -0
- package/lib/core/rs_cache.py +479 -0
- package/lib/core/rs_corpus.py +468 -0
- package/lib/core/rs_hybrid.py +586 -0
- package/lib/core/rs_math.py +287 -0
- package/lib/core/rs_rooms.py +193 -0
- package/lib/core/scheduled-scanner.cjs +463 -0
- package/lib/core/scratchpad-ops.cjs +201 -0
- package/lib/core/section-8-trace-schema.cjs +138 -0
- package/lib/core/section-registry.cjs +111 -0
- package/lib/core/session-state.cjs +144 -0
- package/lib/core/shallow-doc-parser.cjs +174 -0
- package/lib/core/shallow-doc-parser.test.cjs +226 -0
- package/lib/core/skill-activation-router.cjs +284 -0
- package/lib/core/state-ops.cjs +46 -0
- package/lib/core/statusline-cache.cjs +266 -0
- package/lib/core/token-estimator.cjs +348 -0
- package/lib/core/user-archetype.cjs +239 -0
- package/lib/core/user-md-ops.cjs +524 -0
- package/lib/core/visual-ops.cjs +624 -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 +604 -0
- package/lib/hmi/cross-room-memory.cjs +575 -0
- package/lib/hmi/decoy-tier.cjs +395 -0
- package/lib/hmi/jtbd-classifier.cjs +219 -0
- package/lib/hmi/jtbd-state.cjs +199 -0
- package/lib/hmi/jtbd-taxonomy.json +392 -0
- package/lib/hmi/selector-dispatcher.cjs +546 -0
- package/lib/hmi/selector-telemetry.cjs +263 -0
- package/lib/hmi/shape-f0-renderer.cjs +139 -0
- package/lib/hmi/shape-f1-fallback.cjs +80 -0
- package/lib/hmi/shape-f1-renderer.cjs +138 -0
- package/lib/hmi/shape-f2-renderer.cjs +132 -0
- package/lib/hmi/shape-f3-renderer.cjs +66 -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 +312 -0
- package/lib/hmi/shape-f6-renderer.cjs +144 -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/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/test-fixtures/collision-vault/preexisting-room/STATE.md +8 -0
- package/lib/import/test-fixtures/collision-vault/preexisting-room/problem-definition/onboarding/onboarding.md +7 -0
- package/lib/import/test-fixtures/collision-vault/source/onboarding.md +5 -0
- package/lib/import/test-fixtures/obsidian-vault/.obsidian/workspace.json +1 -0
- package/lib/import/test-fixtures/obsidian-vault/notes/with-wikilinks.md +4 -0
- package/lib/import/test-fixtures/tiny-vault/notes/2026-01-15-team-sync.md +9 -0
- package/lib/import/test-fixtures/tiny-vault/notes/empty.md +3 -0
- package/lib/import/test-fixtures/tiny-vault/notes/onboarding.md +5 -0
- package/lib/import/test-fixtures/tiny-vault/notes/pricing.md +5 -0
- package/lib/import/test-fixtures/tiny-vault/notes/random.md +4 -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/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-router.cjs +418 -0
- package/lib/mcp/capability-registry.cjs +62 -0
- package/lib/mcp/larry-context.cjs +46 -0
- package/lib/mcp/larry-server-instructions.md +114 -0
- package/lib/mcp/pipeline-state.cjs +275 -0
- package/lib/mcp/prompts.cjs +302 -0
- package/lib/mcp/resources.cjs +227 -0
- package/lib/mcp/session-catchup.cjs +327 -0
- package/lib/mcp/surface-detect.cjs +75 -0
- package/lib/mcp/tool-router.cjs +1034 -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/brain-cache-lru.test.cjs +259 -0
- package/lib/memory/brain-client-query-shape.test.cjs +160 -0
- package/lib/memory/brain-derivation-graceful-degradation.test.cjs +1019 -0
- package/lib/memory/brain-derivation-queue.test.cjs +539 -0
- package/lib/memory/brain-derivation.test.cjs +634 -0
- package/lib/memory/brain-derive-command.test.cjs +534 -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/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/cross-room-aggregator.test.cjs +909 -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/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/feynman-minto-guardian.test.cjs +736 -0
- package/lib/memory/feynman-minto-invariants.test.cjs +511 -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/folder-memory-quadruple.test.cjs +548 -0
- package/lib/memory/folder-memory.test.cjs +503 -0
- package/lib/memory/framework-chain-composer.test.cjs +515 -0
- package/lib/memory/frontmatter-schema-validator.test.cjs +290 -0
- package/lib/memory/heal-command.test.cjs +604 -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/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-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 +722 -0
- package/lib/memory/navigation-invariants.test.cjs +483 -0
- package/lib/memory/offer-presenter.test.cjs +554 -0
- package/lib/memory/on-stop-snapshot.test.cjs +404 -0
- package/lib/memory/pending-tension-store.cjs +373 -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/problem-type-router.test.cjs +656 -0
- package/lib/memory/query-efficiency-telemetry.test.cjs +370 -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/room-classifier-strict-mode.test.cjs +417 -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 +1247 -0
- package/lib/memory/security-trifecta.test.cjs +312 -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-minto-banner.test.cjs +373 -0
- package/lib/memory/skill-activation-router.test.cjs +419 -0
- package/lib/memory/stamp-artifact-write.test.cjs +304 -0
- package/lib/memory/statusline-active-room.test.cjs +315 -0
- package/lib/memory/statusline-minto-segment.test.cjs +292 -0
- package/lib/memory/sync-async-entry-points.test.cjs +204 -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 +603 -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 +277 -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 +690 -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/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 +59 -0
- package/lib/render/render-v2.cjs +486 -0
- package/lib/render/render-v2.test.cjs +267 -0
- package/lib/render/render.cjs +65 -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/surface-detect.cjs +85 -0
- package/lib/update-bootstrap.sh.template +145 -0
- package/lib/vault/frontmatter-schema.cjs +297 -0
- package/lib/vault/room-scanner.cjs +352 -0
- package/lib/vault/wikilink-builder.cjs +231 -0
- package/lib/vault/wikilink-builder.test.cjs +182 -0
- package/lib/wiki/graph-links.cjs +281 -0
- package/lib/wiki/page-renderer.cjs +229 -0
- package/lib/wiki/wiki-chat.cjs +81 -0
- package/lib/wiki/wiki-layout.cjs +1459 -0
- package/lib/wiki/wiki-search.cjs +142 -0
- package/lib/wiki/wiki-server.cjs +678 -0
- package/lib/wiki/wiki-watcher.cjs +105 -0
- package/lib/workflow/ROOM.md +47 -0
- package/lib/workflow/command-resolver.cjs +155 -0
- package/lib/workflow/command-resolver.test.cjs +235 -0
- package/package.json +44 -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 +56 -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 +37 -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 +37 -0
- package/references/brain/causal-directives.md +91 -0
- package/references/brain/causal-enrichment.cypher +165 -0
- package/references/brain/command-triggers-schema.md +226 -0
- package/references/brain/graph-architecture.md +317 -0
- package/references/brain/query-patterns.md +460 -0
- package/references/brain/room-hierarchy-schema.md +218 -0
- package/references/brain/schema.md +76 -0
- package/references/capability-radar/capabilities-index.md +241 -0
- package/references/capability-radar/changelog-cache.md +81 -0
- package/references/causal/causal-schema.md +103 -0
- package/references/design/email-template-standard.md +155 -0
- package/references/design/graph-visualization-standard.md +178 -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/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.md +237 -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/index.md +97 -0
- package/references/methodology/leadership.md +200 -0
- package/references/methodology/lean-canvas.md +116 -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/sapphire-encoding.md +355 -0
- package/references/methodology/scenario-plan.md +178 -0
- package/references/methodology/score-innovation.md +154 -0
- package/references/methodology/structure-argument.md +158 -0
- package/references/methodology/systems-thinking.md +159 -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/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/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/symbol-system.md +151 -0
- package/skills/MOSDeckEngine/SKILL.md +325 -0
- package/skills/brain-connector/SKILL.md +114 -0
- package/skills/context-engine/SKILL.md +147 -0
- package/skills/conversation-mode/SKILL.md +102 -0
- package/skills/larry-personality/SKILL.md +219 -0
- package/skills/larry-personality/framework-chains.md +92 -0
- package/skills/larry-personality/mode-engine.md +185 -0
- package/skills/mullins-scaffold/SKILL.md +61 -0
- package/skills/mullins-scaffold/scaffold.json +146 -0
- package/skills/pws-methodology/SKILL.md +49 -0
- package/skills/room-passive/SKILL.md +165 -0
- package/skills/room-proactive/SKILL.md +250 -0
- package/skills/ui-system/SKILL.md +277 -0
|
@@ -0,0 +1,407 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/*
|
|
3
|
+
* Copyright (c) 2026 Mindrian. BSL 1.1.
|
|
4
|
+
*
|
|
5
|
+
* Phase 88-02 -- minto-debouncer tests (RED -> GREEN)
|
|
6
|
+
* ====================================================
|
|
7
|
+
* Ships the debounced queue that coalesces post-write regen intents into
|
|
8
|
+
* one regen per section per 10s window. Queue persists at
|
|
9
|
+
* .mindrian/minto-queue.json and survives session crashes. Concurrent
|
|
10
|
+
* producers (post-write hook + on-stop + intent-classifier drain) cannot
|
|
11
|
+
* corrupt the queue file because every mutation composes with the
|
|
12
|
+
* Phase 87-02 atomic write-lock (acquireLock / releaseLock).
|
|
13
|
+
*
|
|
14
|
+
* Test map (12 tests, one per PLAN <behavior> case):
|
|
15
|
+
* Test 1: first enqueue creates .mindrian/minto-queue.json with 1 entry
|
|
16
|
+
* Test 2: second enqueue on same section within 10s -> length stays 1
|
|
17
|
+
* (earliest-wins: enqueued_at preserved on FIRST entry)
|
|
18
|
+
* Test 3: two enqueues on different sections -> length 2
|
|
19
|
+
* Test 4: enqueue after 10s+ on same section -> length 2 (window expired)
|
|
20
|
+
* Test 5: drain(olderThanMs:30000) -> only entries older than 30s drained
|
|
21
|
+
* Test 6: drain crash-safe: simulated mid-drain failure leaves valid JSON
|
|
22
|
+
* Test 7: 5 concurrent forked enqueues on SAME section -> final length 1
|
|
23
|
+
* Test 8: 5 concurrent forked enqueues on 5 DISTINCT sections -> length 5
|
|
24
|
+
* Test 9: peek is read-only (peek twice -> identical result, no mutation)
|
|
25
|
+
* Test 10: malformed JSON self-heals; warning to stderr; no throw
|
|
26
|
+
* Test 11: CLI invocation: enqueue subcommand exits 0, produces queue file
|
|
27
|
+
* Test 12: drain wall-clock timeout: 100ms bound honored under load
|
|
28
|
+
*
|
|
29
|
+
* BSL 1.1. Zero npm deps. Node built-ins only. Three-surface by construction.
|
|
30
|
+
*/
|
|
31
|
+
|
|
32
|
+
'use strict';
|
|
33
|
+
|
|
34
|
+
const assert = require('node:assert/strict');
|
|
35
|
+
const fs = require('node:fs');
|
|
36
|
+
const os = require('node:os');
|
|
37
|
+
const path = require('node:path');
|
|
38
|
+
const { fork, spawnSync } = require('node:child_process');
|
|
39
|
+
|
|
40
|
+
const debouncer = require('../../scripts/minto-debouncer.cjs');
|
|
41
|
+
|
|
42
|
+
const REPO_ROOT = path.resolve(__dirname, '..', '..');
|
|
43
|
+
const DEBOUNCER_CLI = path.join(REPO_ROOT, 'scripts', 'minto-debouncer.cjs');
|
|
44
|
+
const ENQUEUE_WORKER = path.join(__dirname, 'minto-debouncer.worker.cjs');
|
|
45
|
+
|
|
46
|
+
const TMPDIRS = [];
|
|
47
|
+
function mkTmp(prefix) {
|
|
48
|
+
const d = fs.mkdtempSync(path.join(os.tmpdir(), prefix));
|
|
49
|
+
// Mark as a room so downstream resolve-room style walkers could find it.
|
|
50
|
+
fs.writeFileSync(path.join(d, '.room-root'), '');
|
|
51
|
+
TMPDIRS.push(d);
|
|
52
|
+
return d;
|
|
53
|
+
}
|
|
54
|
+
process.on('exit', () => {
|
|
55
|
+
for (const d of TMPDIRS) {
|
|
56
|
+
try { fs.rmSync(d, { recursive: true, force: true }); } catch (_) {}
|
|
57
|
+
}
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
function queuePath(roomDir) {
|
|
61
|
+
return path.join(roomDir, '.mindrian', 'minto-queue.json');
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function readQueue(roomDir) {
|
|
65
|
+
return JSON.parse(fs.readFileSync(queuePath(roomDir), 'utf-8'));
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
let passed = 0;
|
|
69
|
+
let failed = 0;
|
|
70
|
+
function run(name, fn) {
|
|
71
|
+
try {
|
|
72
|
+
fn();
|
|
73
|
+
process.stdout.write(' OK ' + name + '\n');
|
|
74
|
+
passed += 1;
|
|
75
|
+
} catch (e) {
|
|
76
|
+
process.stderr.write(' FAIL ' + name + ': ' + (e && e.message || e) + '\n');
|
|
77
|
+
if (e && e.stack) process.stderr.write(e.stack + '\n');
|
|
78
|
+
failed += 1;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
async function runAsync(name, fn) {
|
|
83
|
+
try {
|
|
84
|
+
await fn();
|
|
85
|
+
process.stdout.write(' OK ' + name + '\n');
|
|
86
|
+
passed += 1;
|
|
87
|
+
} catch (e) {
|
|
88
|
+
process.stderr.write(' FAIL ' + name + ': ' + (e && e.message || e) + '\n');
|
|
89
|
+
if (e && e.stack) process.stderr.write(e.stack + '\n');
|
|
90
|
+
failed += 1;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// ---------------------------------------------------------------------------
|
|
95
|
+
// Tests
|
|
96
|
+
// ---------------------------------------------------------------------------
|
|
97
|
+
|
|
98
|
+
async function main() {
|
|
99
|
+
process.stdout.write('minto-debouncer.test.cjs:\n');
|
|
100
|
+
|
|
101
|
+
// Sanity: worker fork helper exists.
|
|
102
|
+
assert.ok(
|
|
103
|
+
fs.existsSync(ENQUEUE_WORKER),
|
|
104
|
+
'minto-debouncer.worker.cjs missing (concurrency tests depend on it)'
|
|
105
|
+
);
|
|
106
|
+
|
|
107
|
+
// -------------------------------------------------------------------------
|
|
108
|
+
// Test 1: first enqueue creates the queue file with one entry
|
|
109
|
+
// -------------------------------------------------------------------------
|
|
110
|
+
run('Test 1: first enqueue creates queue with 1 entry', () => {
|
|
111
|
+
const room = mkTmp('mi-deb-t1-');
|
|
112
|
+
debouncer.enqueue(room, 'market-analysis', 'post-write');
|
|
113
|
+
assert.ok(fs.existsSync(queuePath(room)), 'queue file should be created');
|
|
114
|
+
const q = readQueue(room);
|
|
115
|
+
assert.strictEqual(q.version, 1, 'schema version should be 1');
|
|
116
|
+
assert.strictEqual(q.entries.length, 1, 'entries length 1');
|
|
117
|
+
assert.strictEqual(q.entries[0].section, 'market-analysis');
|
|
118
|
+
assert.strictEqual(q.entries[0].reason, 'post-write');
|
|
119
|
+
assert.strictEqual(q.entries[0].attempts, 0);
|
|
120
|
+
assert.ok(
|
|
121
|
+
typeof q.entries[0].enqueued_at === 'string' &&
|
|
122
|
+
!Number.isNaN(Date.parse(q.entries[0].enqueued_at)),
|
|
123
|
+
'enqueued_at should be a valid ISO-8601 string'
|
|
124
|
+
);
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
// -------------------------------------------------------------------------
|
|
128
|
+
// Test 2: coalesce -- second enqueue on same section within 10s stays at 1
|
|
129
|
+
// earliest-wins: enqueued_at preserved from the FIRST entry
|
|
130
|
+
// -------------------------------------------------------------------------
|
|
131
|
+
run('Test 2: coalesce within 10s (earliest-wins enqueued_at)', () => {
|
|
132
|
+
const room = mkTmp('mi-deb-t2-');
|
|
133
|
+
debouncer.enqueue(room, 'problem-definition', 'post-write');
|
|
134
|
+
const firstTs = readQueue(room).entries[0].enqueued_at;
|
|
135
|
+
// small real-time delay to ensure a second call would see a different "now"
|
|
136
|
+
const deadline = Date.now() + 5;
|
|
137
|
+
while (Date.now() < deadline) {/* burn */}
|
|
138
|
+
debouncer.enqueue(room, 'problem-definition', 'post-write');
|
|
139
|
+
const q = readQueue(room);
|
|
140
|
+
assert.strictEqual(q.entries.length, 1, 'coalesce should keep length 1');
|
|
141
|
+
assert.strictEqual(
|
|
142
|
+
q.entries[0].enqueued_at,
|
|
143
|
+
firstTs,
|
|
144
|
+
'earliest-wins: first enqueued_at must be preserved'
|
|
145
|
+
);
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
// -------------------------------------------------------------------------
|
|
149
|
+
// Test 3: two different sections -> length 2
|
|
150
|
+
// -------------------------------------------------------------------------
|
|
151
|
+
run('Test 3: distinct sections both enqueue', () => {
|
|
152
|
+
const room = mkTmp('mi-deb-t3-');
|
|
153
|
+
debouncer.enqueue(room, 'market-analysis', 'post-write');
|
|
154
|
+
debouncer.enqueue(room, 'financial-model', 'post-write');
|
|
155
|
+
const q = readQueue(room);
|
|
156
|
+
assert.strictEqual(q.entries.length, 2);
|
|
157
|
+
const sections = q.entries.map((e) => e.section).sort();
|
|
158
|
+
assert.deepStrictEqual(sections, ['financial-model', 'market-analysis']);
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
// -------------------------------------------------------------------------
|
|
162
|
+
// Test 4: after window expires, same section appends new entry
|
|
163
|
+
// We simulate time travel by rewriting the queue file with a stale
|
|
164
|
+
// enqueued_at (older than 10s). That exercises the same code path
|
|
165
|
+
// as real elapsed time without the 10s test delay.
|
|
166
|
+
// -------------------------------------------------------------------------
|
|
167
|
+
run('Test 4: window expired -> new entry appended', () => {
|
|
168
|
+
const room = mkTmp('mi-deb-t4-');
|
|
169
|
+
debouncer.enqueue(room, 'business-model', 'post-write');
|
|
170
|
+
const q1 = readQueue(room);
|
|
171
|
+
assert.strictEqual(q1.entries.length, 1);
|
|
172
|
+
// Backdate the entry 15s so the next enqueue falls OUTSIDE the 10s window.
|
|
173
|
+
q1.entries[0].enqueued_at = new Date(Date.now() - 15000).toISOString();
|
|
174
|
+
fs.writeFileSync(queuePath(room), JSON.stringify(q1));
|
|
175
|
+
debouncer.enqueue(room, 'business-model', 'on-stop');
|
|
176
|
+
const q2 = readQueue(room);
|
|
177
|
+
assert.strictEqual(q2.entries.length, 2, 'stale entry + fresh entry = 2');
|
|
178
|
+
const reasons = q2.entries.map((e) => e.reason).sort();
|
|
179
|
+
assert.deepStrictEqual(reasons, ['on-stop', 'post-write']);
|
|
180
|
+
});
|
|
181
|
+
|
|
182
|
+
// -------------------------------------------------------------------------
|
|
183
|
+
// Test 5: drain(olderThanMs:30000) -- only entries older than 30s drained
|
|
184
|
+
// -------------------------------------------------------------------------
|
|
185
|
+
run('Test 5: drain honors olderThanMs', () => {
|
|
186
|
+
const room = mkTmp('mi-deb-t5-');
|
|
187
|
+
debouncer.enqueue(room, 'old-section', 'post-write');
|
|
188
|
+
debouncer.enqueue(room, 'new-section', 'post-write');
|
|
189
|
+
// Backdate old-section to 45s ago.
|
|
190
|
+
const q = readQueue(room);
|
|
191
|
+
const old = q.entries.find((e) => e.section === 'old-section');
|
|
192
|
+
old.enqueued_at = new Date(Date.now() - 45000).toISOString();
|
|
193
|
+
fs.writeFileSync(queuePath(room), JSON.stringify(q));
|
|
194
|
+
const drained = debouncer.drain(room, { timeoutMs: 5000, olderThanMs: 30000 });
|
|
195
|
+
assert.strictEqual(drained.length, 1, 'exactly 1 entry older than 30s');
|
|
196
|
+
assert.strictEqual(drained[0].section, 'old-section');
|
|
197
|
+
const remaining = readQueue(room);
|
|
198
|
+
assert.strictEqual(remaining.entries.length, 1, 'fresh entry remains');
|
|
199
|
+
assert.strictEqual(remaining.entries[0].section, 'new-section');
|
|
200
|
+
});
|
|
201
|
+
|
|
202
|
+
// -------------------------------------------------------------------------
|
|
203
|
+
// Test 6: drain is atomic-safe -- even if the queue file is truncated
|
|
204
|
+
// mid-write, the previous committed queue remains valid JSON.
|
|
205
|
+
// We validate the temp-file-then-rename pattern by inspecting the
|
|
206
|
+
// queue file between enqueue calls (it must always parse).
|
|
207
|
+
// -------------------------------------------------------------------------
|
|
208
|
+
run('Test 6: atomic write via tmp+rename keeps queue JSON-valid', () => {
|
|
209
|
+
const room = mkTmp('mi-deb-t6-');
|
|
210
|
+
debouncer.enqueue(room, 'sec-a', 'post-write');
|
|
211
|
+
// At any point after enqueue, reading the queue must parse cleanly.
|
|
212
|
+
const raw = fs.readFileSync(queuePath(room), 'utf-8');
|
|
213
|
+
const parsed = JSON.parse(raw);
|
|
214
|
+
assert.strictEqual(parsed.version, 1);
|
|
215
|
+
// Drain and re-check.
|
|
216
|
+
debouncer.drain(room, { timeoutMs: 1000, olderThanMs: 0 });
|
|
217
|
+
const raw2 = fs.readFileSync(queuePath(room), 'utf-8');
|
|
218
|
+
const parsed2 = JSON.parse(raw2);
|
|
219
|
+
assert.strictEqual(parsed2.version, 1);
|
|
220
|
+
assert.strictEqual(parsed2.entries.length, 0, 'drain empties when olderThanMs=0');
|
|
221
|
+
// Verify no .tmp.PID leftovers exist in the .mindrian dir.
|
|
222
|
+
const mindrianDir = path.join(room, '.mindrian');
|
|
223
|
+
const leftovers = fs.readdirSync(mindrianDir).filter((f) => f.includes('.tmp.'));
|
|
224
|
+
assert.strictEqual(leftovers.length, 0, 'no tmp artifacts left behind');
|
|
225
|
+
});
|
|
226
|
+
|
|
227
|
+
// -------------------------------------------------------------------------
|
|
228
|
+
// Test 7: 5 concurrent forked enqueues on SAME section -> final length 1
|
|
229
|
+
// This proves the write-lock composition prevents duplicate entries.
|
|
230
|
+
// -------------------------------------------------------------------------
|
|
231
|
+
await runAsync('Test 7: 5 concurrent same-section -> length 1 (coalesced)', async () => {
|
|
232
|
+
const room = mkTmp('mi-deb-t7-');
|
|
233
|
+
const forks = [];
|
|
234
|
+
for (let i = 0; i < 5; i++) {
|
|
235
|
+
forks.push(new Promise((resolve) => {
|
|
236
|
+
const child = fork(ENQUEUE_WORKER, [room, 'market-analysis', 'post-write'], { silent: true });
|
|
237
|
+
let stderrBuf = '';
|
|
238
|
+
if (child.stderr) child.stderr.on('data', (c) => { stderrBuf += c.toString(); });
|
|
239
|
+
child.on('exit', (code) => resolve({ code, stderr: stderrBuf }));
|
|
240
|
+
}));
|
|
241
|
+
}
|
|
242
|
+
const results = await Promise.all(forks);
|
|
243
|
+
const failures = results.filter((r) => r.code !== 0);
|
|
244
|
+
if (failures.length) {
|
|
245
|
+
for (const r of failures) process.stderr.write('[worker stderr] ' + r.stderr);
|
|
246
|
+
}
|
|
247
|
+
assert.strictEqual(failures.length, 0, 'all 5 workers must exit 0');
|
|
248
|
+
const q = readQueue(room);
|
|
249
|
+
assert.strictEqual(
|
|
250
|
+
q.entries.length,
|
|
251
|
+
1,
|
|
252
|
+
'coalesced to 1 entry (got ' + q.entries.length + ')'
|
|
253
|
+
);
|
|
254
|
+
assert.strictEqual(q.entries[0].section, 'market-analysis');
|
|
255
|
+
});
|
|
256
|
+
|
|
257
|
+
// -------------------------------------------------------------------------
|
|
258
|
+
// Test 8: 5 concurrent forked enqueues on 5 DISTINCT sections -> length 5
|
|
259
|
+
// Zero data loss under concurrency.
|
|
260
|
+
// -------------------------------------------------------------------------
|
|
261
|
+
await runAsync('Test 8: 5 concurrent distinct sections -> length 5 (no loss)', async () => {
|
|
262
|
+
const room = mkTmp('mi-deb-t8-');
|
|
263
|
+
const sections = ['s1', 's2', 's3', 's4', 's5'];
|
|
264
|
+
const forks = sections.map((s) => new Promise((resolve) => {
|
|
265
|
+
const child = fork(ENQUEUE_WORKER, [room, s, 'post-write'], { silent: true });
|
|
266
|
+
let stderrBuf = '';
|
|
267
|
+
if (child.stderr) child.stderr.on('data', (c) => { stderrBuf += c.toString(); });
|
|
268
|
+
child.on('exit', (code) => resolve({ code, stderr: stderrBuf }));
|
|
269
|
+
}));
|
|
270
|
+
const results = await Promise.all(forks);
|
|
271
|
+
const failures = results.filter((r) => r.code !== 0);
|
|
272
|
+
if (failures.length) {
|
|
273
|
+
for (const r of failures) process.stderr.write('[worker stderr] ' + r.stderr);
|
|
274
|
+
}
|
|
275
|
+
assert.strictEqual(failures.length, 0, 'all 5 workers must exit 0');
|
|
276
|
+
const q = readQueue(room);
|
|
277
|
+
assert.strictEqual(q.entries.length, 5, 'all 5 distinct sections present');
|
|
278
|
+
const got = q.entries.map((e) => e.section).sort();
|
|
279
|
+
assert.deepStrictEqual(got, sections);
|
|
280
|
+
});
|
|
281
|
+
|
|
282
|
+
// -------------------------------------------------------------------------
|
|
283
|
+
// Test 9: peek is read-only (two peeks return identical results,
|
|
284
|
+
// queue file mtime unchanged).
|
|
285
|
+
// -------------------------------------------------------------------------
|
|
286
|
+
run('Test 9: peek is read-only (idempotent)', () => {
|
|
287
|
+
const room = mkTmp('mi-deb-t9-');
|
|
288
|
+
debouncer.enqueue(room, 'only-section', 'post-write');
|
|
289
|
+
const mtime1 = fs.statSync(queuePath(room)).mtimeMs;
|
|
290
|
+
const p1 = debouncer.peek(room);
|
|
291
|
+
const p2 = debouncer.peek(room);
|
|
292
|
+
assert.deepStrictEqual(p1, p2, 'two peeks should return deeply equal result');
|
|
293
|
+
assert.strictEqual(p1.entries.length, 1);
|
|
294
|
+
assert.strictEqual(p1.entries[0].section, 'only-section');
|
|
295
|
+
const mtime2 = fs.statSync(queuePath(room)).mtimeMs;
|
|
296
|
+
assert.strictEqual(mtime1, mtime2, 'peek must not touch the queue file');
|
|
297
|
+
});
|
|
298
|
+
|
|
299
|
+
// -------------------------------------------------------------------------
|
|
300
|
+
// Test 10: malformed JSON self-heals; warning to stderr; no throw.
|
|
301
|
+
// -------------------------------------------------------------------------
|
|
302
|
+
run('Test 10: malformed JSON self-heals with stderr warning', () => {
|
|
303
|
+
const room = mkTmp('mi-deb-t10-');
|
|
304
|
+
fs.mkdirSync(path.join(room, '.mindrian'), { recursive: true });
|
|
305
|
+
// Write garbage to the queue path.
|
|
306
|
+
fs.writeFileSync(queuePath(room), '{not-valid-json}}}');
|
|
307
|
+
// Capture stderr during the enqueue call.
|
|
308
|
+
const origWrite = process.stderr.write.bind(process.stderr);
|
|
309
|
+
let stderrBuf = '';
|
|
310
|
+
process.stderr.write = (chunk) => { stderrBuf += String(chunk); return true; };
|
|
311
|
+
try {
|
|
312
|
+
assert.doesNotThrow(
|
|
313
|
+
() => debouncer.enqueue(room, 'healed-section', 'post-write'),
|
|
314
|
+
'enqueue must not throw on corrupted queue'
|
|
315
|
+
);
|
|
316
|
+
} finally {
|
|
317
|
+
process.stderr.write = origWrite;
|
|
318
|
+
}
|
|
319
|
+
assert.ok(
|
|
320
|
+
stderrBuf.toLowerCase().includes('warn') ||
|
|
321
|
+
stderrBuf.toLowerCase().includes('corrupt') ||
|
|
322
|
+
stderrBuf.toLowerCase().includes('reset') ||
|
|
323
|
+
stderrBuf.toLowerCase().includes('heal'),
|
|
324
|
+
'should log a recovery warning to stderr (got: ' + JSON.stringify(stderrBuf) + ')'
|
|
325
|
+
);
|
|
326
|
+
const q = readQueue(room);
|
|
327
|
+
assert.strictEqual(q.version, 1);
|
|
328
|
+
assert.strictEqual(q.entries.length, 1);
|
|
329
|
+
assert.strictEqual(q.entries[0].section, 'healed-section');
|
|
330
|
+
});
|
|
331
|
+
|
|
332
|
+
// -------------------------------------------------------------------------
|
|
333
|
+
// Test 11: CLI invocation works and produces the queue file.
|
|
334
|
+
// -------------------------------------------------------------------------
|
|
335
|
+
run('Test 11: CLI enqueue subcommand produces queue file', () => {
|
|
336
|
+
const room = mkTmp('mi-deb-t11-');
|
|
337
|
+
const res = spawnSync(process.execPath, [
|
|
338
|
+
DEBOUNCER_CLI, 'enqueue', room, 'market-analysis', 'post-write',
|
|
339
|
+
], { encoding: 'utf-8' });
|
|
340
|
+
assert.strictEqual(res.status, 0, 'CLI must exit 0 (stderr: ' + res.stderr + ')');
|
|
341
|
+
assert.ok(fs.existsSync(queuePath(room)), 'queue file should exist after CLI enqueue');
|
|
342
|
+
const q = readQueue(room);
|
|
343
|
+
assert.strictEqual(q.entries.length, 1);
|
|
344
|
+
assert.strictEqual(q.entries[0].section, 'market-analysis');
|
|
345
|
+
// peek subcommand also works.
|
|
346
|
+
const peekRes = spawnSync(process.execPath, [DEBOUNCER_CLI, 'peek', room], { encoding: 'utf-8' });
|
|
347
|
+
assert.strictEqual(peekRes.status, 0, 'peek exit 0 (stderr: ' + peekRes.stderr + ')');
|
|
348
|
+
assert.ok(
|
|
349
|
+
peekRes.stdout.includes('market-analysis'),
|
|
350
|
+
'peek stdout should mention the section'
|
|
351
|
+
);
|
|
352
|
+
});
|
|
353
|
+
|
|
354
|
+
// -------------------------------------------------------------------------
|
|
355
|
+
// Test 12: drain has a wall-clock timeout bound (returns within timeoutMs
|
|
356
|
+
// even under synthetic load).
|
|
357
|
+
// -------------------------------------------------------------------------
|
|
358
|
+
run('Test 12: drain honors wall-clock timeoutMs', () => {
|
|
359
|
+
const room = mkTmp('mi-deb-t12-');
|
|
360
|
+
// Pre-populate queue with many entries (simulate a burst).
|
|
361
|
+
fs.mkdirSync(path.join(room, '.mindrian'), { recursive: true });
|
|
362
|
+
const entries = [];
|
|
363
|
+
const now = Date.now();
|
|
364
|
+
for (let i = 0; i < 500; i++) {
|
|
365
|
+
entries.push({
|
|
366
|
+
section: 'sec-' + i,
|
|
367
|
+
enqueued_at: new Date(now - 60000).toISOString(),
|
|
368
|
+
reason: 'post-write',
|
|
369
|
+
attempts: 0,
|
|
370
|
+
});
|
|
371
|
+
}
|
|
372
|
+
fs.writeFileSync(queuePath(room), JSON.stringify({ version: 1, entries }));
|
|
373
|
+
const t0 = Date.now();
|
|
374
|
+
const drained = debouncer.drain(room, { timeoutMs: 100, olderThanMs: 30000 });
|
|
375
|
+
const elapsed = Date.now() - t0;
|
|
376
|
+
// Wall-clock bound: allow generous overhead for lock acquire + rename on slow CI.
|
|
377
|
+
// The invariant we're testing is "drain returns in bounded time" -- not ms-precise.
|
|
378
|
+
assert.ok(
|
|
379
|
+
elapsed < 2000,
|
|
380
|
+
'drain should return in bounded time; took ' + elapsed + 'ms'
|
|
381
|
+
);
|
|
382
|
+
// drain under timeout pressure is allowed to return empty OR partial OR full; the
|
|
383
|
+
// invariant is "no hang, no throw, queue still valid".
|
|
384
|
+
assert.ok(Array.isArray(drained), 'drained must be an array');
|
|
385
|
+
const rem = readQueue(room);
|
|
386
|
+
assert.strictEqual(rem.version, 1);
|
|
387
|
+
assert.ok(Array.isArray(rem.entries));
|
|
388
|
+
});
|
|
389
|
+
|
|
390
|
+
// -------------------------------------------------------------------------
|
|
391
|
+
// Summary
|
|
392
|
+
// -------------------------------------------------------------------------
|
|
393
|
+
process.stdout.write('\nminto-debouncer: ' + passed + '/' + (passed + failed) + ' passed');
|
|
394
|
+
if (failed > 0) {
|
|
395
|
+
process.stdout.write(', ' + failed + ' failed\n');
|
|
396
|
+
process.exit(1);
|
|
397
|
+
} else {
|
|
398
|
+
process.stdout.write('\n');
|
|
399
|
+
process.exit(0);
|
|
400
|
+
}
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
main().catch((e) => {
|
|
404
|
+
process.stderr.write('minto-debouncer.test.cjs FATAL: ' + (e && e.message || e) + '\n');
|
|
405
|
+
if (e && e.stack) process.stderr.write(e.stack + '\n');
|
|
406
|
+
process.exit(1);
|
|
407
|
+
});
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/*
|
|
3
|
+
* Copyright (c) 2026 Mindrian. BSL 1.1.
|
|
4
|
+
*
|
|
5
|
+
* Phase 88-02 -- minto-debouncer concurrency worker
|
|
6
|
+
* ==================================================
|
|
7
|
+
* Forked by lib/memory/minto-debouncer.test.cjs Tests 7 + 8 to get true
|
|
8
|
+
* OS-level concurrency (not fake "parallel" promises in one event loop).
|
|
9
|
+
*
|
|
10
|
+
* Usage:
|
|
11
|
+
* node minto-debouncer.worker.cjs <roomDir> <section> <reason>
|
|
12
|
+
*
|
|
13
|
+
* Exit codes:
|
|
14
|
+
* 0 -- enqueue succeeded (expected happy path for all 5 workers)
|
|
15
|
+
* 1 -- unexpected error (fails the parent test)
|
|
16
|
+
*
|
|
17
|
+
* Standalone file (not inline template string in the test) to match the
|
|
18
|
+
* Phase 87-02 write-lock-atomic.worker.cjs pattern: avoids Windows/Linux
|
|
19
|
+
* path-escape ambiguity in parent test source.
|
|
20
|
+
*/
|
|
21
|
+
|
|
22
|
+
'use strict';
|
|
23
|
+
|
|
24
|
+
const path = require('node:path');
|
|
25
|
+
|
|
26
|
+
// Resolve debouncer relative to THIS worker's location.
|
|
27
|
+
// __dirname = lib/memory/, target = scripts/minto-debouncer.cjs
|
|
28
|
+
const debouncer = require(path.resolve(__dirname, '..', '..', 'scripts', 'minto-debouncer.cjs'));
|
|
29
|
+
|
|
30
|
+
const roomDir = process.argv[2];
|
|
31
|
+
const section = process.argv[3];
|
|
32
|
+
const reason = process.argv[4];
|
|
33
|
+
|
|
34
|
+
if (!roomDir || !section || !reason) {
|
|
35
|
+
process.stderr.write('minto-debouncer.worker: missing argv (roomDir, section, reason)\n');
|
|
36
|
+
process.exit(1);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
try {
|
|
40
|
+
debouncer.enqueue(roomDir, section, reason);
|
|
41
|
+
process.exit(0);
|
|
42
|
+
} catch (e) {
|
|
43
|
+
process.stderr.write('minto-debouncer.worker ERROR: ' + (e && e.message || e) + '\n');
|
|
44
|
+
if (e && e.stack) process.stderr.write(e.stack + '\n');
|
|
45
|
+
process.exit(1);
|
|
46
|
+
}
|