@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,632 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/*
|
|
3
|
+
* Copyright (c) 2026 Mindrian. BSL 1.1.
|
|
4
|
+
*
|
|
5
|
+
* Phase 88-10 -- decision-capture acceptance tests
|
|
6
|
+
* =================================================
|
|
7
|
+
* Verifies the per-section decision persistence module
|
|
8
|
+
* (lib/core/decision-capture.cjs) which writes APPROVE/REJECT/DEFER
|
|
9
|
+
* decisions to MINTO.md.frontmatter.decision_log and archives older
|
|
10
|
+
* entries to .mindrian/decision-archive/YYYY-MM/<section>.jsonl when the
|
|
11
|
+
* per-section cap (20) is exceeded.
|
|
12
|
+
*
|
|
13
|
+
* Test map (12 cases, one-to-one with the PLAN <behavior> block):
|
|
14
|
+
* 1. First recordDecision on section with no decision_log ->
|
|
15
|
+
* MINTO.md frontmatter gets decision_log: [<entry>].
|
|
16
|
+
* 2. Second recordDecision appends, length 2, original preserved.
|
|
17
|
+
* 3. 21st recordDecision -> oldest entry removed from decision_log
|
|
18
|
+
* (now has entries 2-21) and written to YYYY-MM/<section>.jsonl.
|
|
19
|
+
* 4. Archive files are JSONL; appending does NOT rewrite prior lines.
|
|
20
|
+
* 5. Archive partitioned by YYYY-MM of the ARCHIVED entry's
|
|
21
|
+
* timestamp, not the current time.
|
|
22
|
+
* 6. Invalid entry -> {success:false, violations}; MINTO unchanged.
|
|
23
|
+
* 7. readDecisionLog on section with no MINTO.md -> [].
|
|
24
|
+
* 8. readDecisionLog on section with MINTO but no decision_log field
|
|
25
|
+
* -> [] (no throw).
|
|
26
|
+
* 9. Concurrent recordDecision: 3 forked workers -> final MINTO has
|
|
27
|
+
* exactly 3 entries (all writes applied, no data loss).
|
|
28
|
+
* 10. MINTO post-write still validates via feynman-minto-invariants
|
|
29
|
+
* (no critical severity triggered by our mutation).
|
|
30
|
+
* 11. Preserve-on-regen: after recordDecision, vault-section-minto-
|
|
31
|
+
* generator --write with new narrative preserves the decision_log.
|
|
32
|
+
* 12. Special-char section name (spaces/unicode) handled correctly
|
|
33
|
+
* and archive filename safely escaped.
|
|
34
|
+
*
|
|
35
|
+
* Registered in lib/memory/run-feynman-tests.cjs.
|
|
36
|
+
*/
|
|
37
|
+
|
|
38
|
+
'use strict';
|
|
39
|
+
|
|
40
|
+
const assert = require('node:assert/strict');
|
|
41
|
+
const fs = require('node:fs');
|
|
42
|
+
const os = require('node:os');
|
|
43
|
+
const path = require('node:path');
|
|
44
|
+
const { spawnSync } = require('node:child_process');
|
|
45
|
+
|
|
46
|
+
const REPO = path.resolve(__dirname, '..', '..');
|
|
47
|
+
const MODULE_PATH = path.join(REPO, 'lib', 'core', 'decision-capture.cjs');
|
|
48
|
+
const INVARIANTS_PATH = path.join(
|
|
49
|
+
REPO,
|
|
50
|
+
'lib',
|
|
51
|
+
'core',
|
|
52
|
+
'feynman-minto-invariants.cjs'
|
|
53
|
+
);
|
|
54
|
+
const GENERATOR = path.join(
|
|
55
|
+
REPO,
|
|
56
|
+
'scripts',
|
|
57
|
+
'vault-section-minto-generator.cjs'
|
|
58
|
+
);
|
|
59
|
+
|
|
60
|
+
// ---------- Fixture helpers ----------
|
|
61
|
+
|
|
62
|
+
const TMP_ROOTS = [];
|
|
63
|
+
function mkTmp(prefix) {
|
|
64
|
+
const d = fs.mkdtempSync(path.join(os.tmpdir(), prefix));
|
|
65
|
+
TMP_ROOTS.push(d);
|
|
66
|
+
return d;
|
|
67
|
+
}
|
|
68
|
+
process.on('exit', function () {
|
|
69
|
+
for (const d of TMP_ROOTS) {
|
|
70
|
+
try {
|
|
71
|
+
fs.rmSync(d, { recursive: true, force: true });
|
|
72
|
+
} catch (_e) { /* best effort */ }
|
|
73
|
+
}
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
function buildRoomRoot(prefix) {
|
|
77
|
+
const d = mkTmp(prefix);
|
|
78
|
+
fs.writeFileSync(path.join(d, '.room-root'), 'mindrian-room\n');
|
|
79
|
+
fs.mkdirSync(path.join(d, '.mindrian'), { recursive: true });
|
|
80
|
+
return d;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
// Write a minimal valid v88 MINTO.md fixture into <roomDir>/<section>/.
|
|
84
|
+
function writeMintoMd(roomDir, section, opts) {
|
|
85
|
+
const sectionDir = path.join(roomDir, section);
|
|
86
|
+
fs.mkdirSync(sectionDir, { recursive: true });
|
|
87
|
+
// Minimal ROOM.md to satisfy any section walker.
|
|
88
|
+
fs.writeFileSync(
|
|
89
|
+
path.join(sectionDir, 'ROOM.md'),
|
|
90
|
+
'---\ntype: section\nsection: ' + section + '\n---\n\n# ' + section + '\n'
|
|
91
|
+
);
|
|
92
|
+
const preserved = (opts && opts.preserved) || {};
|
|
93
|
+
const fmLines = [
|
|
94
|
+
'---',
|
|
95
|
+
'type: section-minto',
|
|
96
|
+
'schema_version: "1.0"',
|
|
97
|
+
'section: ' + section,
|
|
98
|
+
'created: 2026-04-20',
|
|
99
|
+
'room: ' + path.basename(roomDir),
|
|
100
|
+
'parent-moc: ROOM.md',
|
|
101
|
+
'methodology: minto-pyramid',
|
|
102
|
+
'sources: []',
|
|
103
|
+
'related: []',
|
|
104
|
+
'status: active',
|
|
105
|
+
'governing_thought: "test fixture governing thought for ' + section + '"',
|
|
106
|
+
'last_generated_at: "2026-04-20T10:00:00Z"',
|
|
107
|
+
'last_artifact_write_seen_at: null',
|
|
108
|
+
'reasoning_health_score: null',
|
|
109
|
+
'flagged_weaknesses: []',
|
|
110
|
+
];
|
|
111
|
+
const dl = preserved.decision_log || [];
|
|
112
|
+
if (dl.length === 0) {
|
|
113
|
+
fmLines.push('decision_log: []');
|
|
114
|
+
} else {
|
|
115
|
+
fmLines.push('decision_log:');
|
|
116
|
+
for (const entry of dl) {
|
|
117
|
+
fmLines.push(' - session_id: "' + entry.session_id + '"');
|
|
118
|
+
fmLines.push(' timestamp: "' + entry.timestamp + '"');
|
|
119
|
+
fmLines.push(' action: "' + entry.action + '"');
|
|
120
|
+
fmLines.push(' user_response: "' + entry.user_response + '"');
|
|
121
|
+
fmLines.push(' reason: "' + entry.reason + '"');
|
|
122
|
+
if (entry.outcome !== undefined && entry.outcome !== null) {
|
|
123
|
+
fmLines.push(' outcome: "' + entry.outcome + '"');
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
fmLines.push('---');
|
|
128
|
+
const body = [
|
|
129
|
+
'',
|
|
130
|
+
'> [!abstract] Governing Thought',
|
|
131
|
+
'> test fixture governing thought for ' + section,
|
|
132
|
+
'',
|
|
133
|
+
'## Key Claims',
|
|
134
|
+
'',
|
|
135
|
+
'### Claim 1: evidence is filed',
|
|
136
|
+
'',
|
|
137
|
+
'Source: fixture',
|
|
138
|
+
'',
|
|
139
|
+
'### Claim 2: another claim',
|
|
140
|
+
'',
|
|
141
|
+
'Source: fixture',
|
|
142
|
+
'',
|
|
143
|
+
'### Claim 3: a third claim',
|
|
144
|
+
'',
|
|
145
|
+
'Source: fixture',
|
|
146
|
+
'',
|
|
147
|
+
'## MECE Issue Tree',
|
|
148
|
+
'',
|
|
149
|
+
'- Primary evidence',
|
|
150
|
+
' - claim 1',
|
|
151
|
+
'- Supporting evidence',
|
|
152
|
+
' - claim 2',
|
|
153
|
+
'- Open dimension',
|
|
154
|
+
' - gap',
|
|
155
|
+
'',
|
|
156
|
+
].join('\n');
|
|
157
|
+
fs.writeFileSync(
|
|
158
|
+
path.join(sectionDir, 'MINTO.md'),
|
|
159
|
+
fmLines.join('\n') + '\n' + body
|
|
160
|
+
);
|
|
161
|
+
return sectionDir;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
function mkEntry(overrides) {
|
|
165
|
+
return Object.assign(
|
|
166
|
+
{
|
|
167
|
+
session_id: 'sess-' + Math.random().toString(36).slice(2, 10),
|
|
168
|
+
timestamp: new Date().toISOString().replace(/\.\d{3}Z$/, 'Z'),
|
|
169
|
+
action: 'flag_tam_staleness',
|
|
170
|
+
user_response: 'defer',
|
|
171
|
+
reason: 'Q3 refresh required',
|
|
172
|
+
},
|
|
173
|
+
overrides || {}
|
|
174
|
+
);
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
function readDecisionLogFromFile(mintoPath) {
|
|
178
|
+
// Small parser: pull every ` - session_id: "..."` entry from frontmatter.
|
|
179
|
+
const raw = fs.readFileSync(mintoPath, 'utf8');
|
|
180
|
+
const fm = raw.match(/^---\r?\n([\s\S]*?)\r?\n---\r?\n/);
|
|
181
|
+
if (!fm) return [];
|
|
182
|
+
const lines = fm[1].split(/\r?\n/);
|
|
183
|
+
const out = [];
|
|
184
|
+
let current = null;
|
|
185
|
+
let inDl = false;
|
|
186
|
+
for (let i = 0; i < lines.length; i++) {
|
|
187
|
+
const line = lines[i];
|
|
188
|
+
if (/^decision_log:\s*\[\]/.test(line)) return [];
|
|
189
|
+
if (/^decision_log:\s*$/.test(line)) { inDl = true; continue; }
|
|
190
|
+
if (!inDl) continue;
|
|
191
|
+
if (/^[A-Za-z_]/.test(line)) break; // next top-level
|
|
192
|
+
const head = line.match(/^\s+-\s+([A-Za-z_][A-Za-z0-9_]*):\s*(.*)$/);
|
|
193
|
+
if (head) {
|
|
194
|
+
if (current) out.push(current);
|
|
195
|
+
current = {};
|
|
196
|
+
current[head[1]] = stripQuote(head[2]);
|
|
197
|
+
continue;
|
|
198
|
+
}
|
|
199
|
+
const cont = line.match(/^\s+([A-Za-z_][A-Za-z0-9_]*):\s*(.*)$/);
|
|
200
|
+
if (cont && current) {
|
|
201
|
+
current[cont[1]] = stripQuote(cont[2]);
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
if (current) out.push(current);
|
|
205
|
+
return out;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
function stripQuote(s) {
|
|
209
|
+
const t = (s || '').trim();
|
|
210
|
+
if (
|
|
211
|
+
(t.startsWith('"') && t.endsWith('"')) ||
|
|
212
|
+
(t.startsWith("'") && t.endsWith("'"))
|
|
213
|
+
) return t.slice(1, -1);
|
|
214
|
+
return t;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
// ---------- Test runner ----------
|
|
218
|
+
|
|
219
|
+
let passed = 0;
|
|
220
|
+
let failed = 0;
|
|
221
|
+
function run(name, fn) {
|
|
222
|
+
try {
|
|
223
|
+
fn();
|
|
224
|
+
process.stdout.write('PASS ' + name + '\n');
|
|
225
|
+
passed += 1;
|
|
226
|
+
} catch (e) {
|
|
227
|
+
process.stderr.write('FAIL ' + name + ': ' + (e && e.message || e) + '\n');
|
|
228
|
+
if (e && e.stack) process.stderr.write(e.stack + '\n');
|
|
229
|
+
failed += 1;
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
// ---------- Module require guard ----------
|
|
234
|
+
|
|
235
|
+
run('Module file exists on disk', function () {
|
|
236
|
+
assert.ok(fs.existsSync(MODULE_PATH), 'decision-capture.cjs missing');
|
|
237
|
+
});
|
|
238
|
+
|
|
239
|
+
const dc = require(MODULE_PATH);
|
|
240
|
+
|
|
241
|
+
run('Module exports recordDecision + readDecisionLog as functions', function () {
|
|
242
|
+
assert.strictEqual(typeof dc.recordDecision, 'function');
|
|
243
|
+
assert.strictEqual(typeof dc.readDecisionLog, 'function');
|
|
244
|
+
});
|
|
245
|
+
|
|
246
|
+
// ---------- Test 1: first recordDecision ----------
|
|
247
|
+
|
|
248
|
+
run('Test 1: first recordDecision writes decision_log with one entry', function () {
|
|
249
|
+
const room = buildRoomRoot('dc-t1-');
|
|
250
|
+
writeMintoMd(room, 'market-analysis');
|
|
251
|
+
const entry = mkEntry();
|
|
252
|
+
const r = dc.recordDecision(room, 'market-analysis', entry);
|
|
253
|
+
assert.strictEqual(r.success, true, 'success should be true, got ' + JSON.stringify(r));
|
|
254
|
+
const log = dc.readDecisionLog(room, 'market-analysis');
|
|
255
|
+
assert.strictEqual(log.length, 1, 'expected 1 entry, got ' + log.length);
|
|
256
|
+
assert.strictEqual(log[0].session_id, entry.session_id);
|
|
257
|
+
assert.strictEqual(log[0].action, entry.action);
|
|
258
|
+
assert.strictEqual(log[0].user_response, entry.user_response);
|
|
259
|
+
});
|
|
260
|
+
|
|
261
|
+
// ---------- Test 2: append preserves earlier entry ----------
|
|
262
|
+
|
|
263
|
+
run('Test 2: second recordDecision appends, length 2', function () {
|
|
264
|
+
const room = buildRoomRoot('dc-t2-');
|
|
265
|
+
writeMintoMd(room, 'market-analysis');
|
|
266
|
+
const e1 = mkEntry({ session_id: 'sess-1', action: 'first' });
|
|
267
|
+
const e2 = mkEntry({ session_id: 'sess-2', action: 'second' });
|
|
268
|
+
assert.strictEqual(dc.recordDecision(room, 'market-analysis', e1).success, true);
|
|
269
|
+
assert.strictEqual(dc.recordDecision(room, 'market-analysis', e2).success, true);
|
|
270
|
+
const log = dc.readDecisionLog(room, 'market-analysis');
|
|
271
|
+
assert.strictEqual(log.length, 2, 'expected 2 entries, got ' + log.length);
|
|
272
|
+
assert.strictEqual(log[0].session_id, 'sess-1');
|
|
273
|
+
assert.strictEqual(log[1].session_id, 'sess-2');
|
|
274
|
+
});
|
|
275
|
+
|
|
276
|
+
// ---------- Test 3: cap at 20 + archive oldest ----------
|
|
277
|
+
|
|
278
|
+
run('Test 3: 21st entry archives oldest, log length stays 20', function () {
|
|
279
|
+
const room = buildRoomRoot('dc-t3-');
|
|
280
|
+
writeMintoMd(room, 'problem-definition');
|
|
281
|
+
// Write 20 entries first (all inside the cap).
|
|
282
|
+
for (let i = 0; i < 20; i++) {
|
|
283
|
+
const ts = new Date(Date.UTC(2026, 3, i + 1, 0, 0, 0))
|
|
284
|
+
.toISOString()
|
|
285
|
+
.replace(/\.\d{3}Z$/, 'Z');
|
|
286
|
+
const r = dc.recordDecision(
|
|
287
|
+
room,
|
|
288
|
+
'problem-definition',
|
|
289
|
+
mkEntry({
|
|
290
|
+
session_id: 'sess-' + i,
|
|
291
|
+
action: 'a' + i,
|
|
292
|
+
timestamp: ts,
|
|
293
|
+
})
|
|
294
|
+
);
|
|
295
|
+
assert.strictEqual(r.success, true, 'entry ' + i + ' should succeed');
|
|
296
|
+
}
|
|
297
|
+
const logAt20 = dc.readDecisionLog(room, 'problem-definition');
|
|
298
|
+
assert.strictEqual(logAt20.length, 20, 'should have 20 entries pre-overflow');
|
|
299
|
+
// Now write the 21st -- triggers archive of the oldest.
|
|
300
|
+
const twentyFirst = mkEntry({
|
|
301
|
+
session_id: 'sess-20',
|
|
302
|
+
action: 'a20',
|
|
303
|
+
timestamp: '2026-04-21T00:00:00Z',
|
|
304
|
+
});
|
|
305
|
+
const r = dc.recordDecision(room, 'problem-definition', twentyFirst);
|
|
306
|
+
assert.strictEqual(r.success, true);
|
|
307
|
+
assert.ok(r.archived, 'archive metadata should be non-null');
|
|
308
|
+
const logAfter = dc.readDecisionLog(room, 'problem-definition');
|
|
309
|
+
assert.strictEqual(logAfter.length, 20, 'cap holds at 20, got ' + logAfter.length);
|
|
310
|
+
// The oldest (sess-0) should be gone from decision_log.
|
|
311
|
+
const ids = logAfter.map(function (e) { return e.session_id; });
|
|
312
|
+
assert.ok(ids.indexOf('sess-0') === -1, 'sess-0 should be archived, not in log');
|
|
313
|
+
assert.ok(ids.indexOf('sess-20') !== -1, 'sess-20 should be in log');
|
|
314
|
+
// Archive file lives under .mindrian/decision-archive/YYYY-MM/ and is JSONL.
|
|
315
|
+
const archiveDir = path.join(room, '.mindrian', 'decision-archive');
|
|
316
|
+
assert.ok(fs.existsSync(archiveDir), 'archive dir should exist: ' + archiveDir);
|
|
317
|
+
// Archive month comes from ARCHIVED entry (sess-0 at 2026-04-01).
|
|
318
|
+
const apr = path.join(archiveDir, '2026-04');
|
|
319
|
+
assert.ok(fs.existsSync(apr), '2026-04 partition should exist');
|
|
320
|
+
const fname = path.join(apr, 'problem-definition.jsonl');
|
|
321
|
+
assert.ok(fs.existsSync(fname), 'archive file should exist: ' + fname);
|
|
322
|
+
const lines = fs.readFileSync(fname, 'utf8').split(/\n/).filter(function (s) { return s.length > 0; });
|
|
323
|
+
assert.strictEqual(lines.length, 1, 'should have exactly 1 archived line');
|
|
324
|
+
const parsed = JSON.parse(lines[0]);
|
|
325
|
+
assert.strictEqual(parsed.session_id, 'sess-0');
|
|
326
|
+
});
|
|
327
|
+
|
|
328
|
+
// ---------- Test 4: JSONL append -- prior lines preserved byte-for-byte ----------
|
|
329
|
+
|
|
330
|
+
run('Test 4: JSONL archive is append-only, prior lines not rewritten', function () {
|
|
331
|
+
const room = buildRoomRoot('dc-t4-');
|
|
332
|
+
writeMintoMd(room, 'market-analysis');
|
|
333
|
+
// Fill to 20 + two overflow entries.
|
|
334
|
+
for (let i = 0; i < 22; i++) {
|
|
335
|
+
const ts = new Date(Date.UTC(2026, 3, i + 1, 0, 0, 0))
|
|
336
|
+
.toISOString()
|
|
337
|
+
.replace(/\.\d{3}Z$/, 'Z');
|
|
338
|
+
dc.recordDecision(
|
|
339
|
+
room,
|
|
340
|
+
'market-analysis',
|
|
341
|
+
mkEntry({
|
|
342
|
+
session_id: 'sess-' + i,
|
|
343
|
+
action: 'a' + i,
|
|
344
|
+
timestamp: ts,
|
|
345
|
+
// First batch fall in April, second batch one more in April.
|
|
346
|
+
})
|
|
347
|
+
);
|
|
348
|
+
}
|
|
349
|
+
const fname = path.join(
|
|
350
|
+
room,
|
|
351
|
+
'.mindrian',
|
|
352
|
+
'decision-archive',
|
|
353
|
+
'2026-04',
|
|
354
|
+
'market-analysis.jsonl'
|
|
355
|
+
);
|
|
356
|
+
assert.ok(fs.existsSync(fname), 'archive should exist');
|
|
357
|
+
const firstBytes = fs.readFileSync(fname, 'utf8');
|
|
358
|
+
const lines = firstBytes.split(/\n/).filter(function (s) { return s.length > 0; });
|
|
359
|
+
assert.strictEqual(lines.length, 2, 'should have 2 archived lines, got ' + lines.length);
|
|
360
|
+
// Each line is parseable JSON with a session_id.
|
|
361
|
+
for (const line of lines) {
|
|
362
|
+
const obj = JSON.parse(line);
|
|
363
|
+
assert.ok(typeof obj.session_id === 'string');
|
|
364
|
+
}
|
|
365
|
+
// Prior line (sess-0) must remain byte-identical after the second archive
|
|
366
|
+
// appended -- verify by re-reading and comparing.
|
|
367
|
+
const sess0Line = lines[0];
|
|
368
|
+
assert.ok(sess0Line.indexOf('"sess-0"') !== -1, 'sess-0 should be first');
|
|
369
|
+
const sess1Line = lines[1];
|
|
370
|
+
assert.ok(sess1Line.indexOf('"sess-1"') !== -1, 'sess-1 should be second');
|
|
371
|
+
});
|
|
372
|
+
|
|
373
|
+
// ---------- Test 5: archive partition by ARCHIVED entry month ----------
|
|
374
|
+
|
|
375
|
+
run('Test 5: archive month reflects archived entry timestamp, not today', function () {
|
|
376
|
+
const room = buildRoomRoot('dc-t5-');
|
|
377
|
+
writeMintoMd(room, 'market-analysis');
|
|
378
|
+
// 20 entries in Dec 2025.
|
|
379
|
+
for (let i = 0; i < 20; i++) {
|
|
380
|
+
const ts = new Date(Date.UTC(2025, 11, i + 1, 0, 0, 0))
|
|
381
|
+
.toISOString()
|
|
382
|
+
.replace(/\.\d{3}Z$/, 'Z');
|
|
383
|
+
dc.recordDecision(
|
|
384
|
+
room,
|
|
385
|
+
'market-analysis',
|
|
386
|
+
mkEntry({
|
|
387
|
+
session_id: 'old-' + i,
|
|
388
|
+
action: 'old' + i,
|
|
389
|
+
timestamp: ts,
|
|
390
|
+
})
|
|
391
|
+
);
|
|
392
|
+
}
|
|
393
|
+
// 21st entry dated today (Apr 2026) -- archives oldest (2025-12-01).
|
|
394
|
+
const now = '2026-04-21T12:00:00Z';
|
|
395
|
+
dc.recordDecision(
|
|
396
|
+
room,
|
|
397
|
+
'market-analysis',
|
|
398
|
+
mkEntry({ session_id: 'fresh', action: 'a_fresh', timestamp: now })
|
|
399
|
+
);
|
|
400
|
+
// Archive should live under 2025-12/, NOT 2026-04/.
|
|
401
|
+
const dec = path.join(
|
|
402
|
+
room,
|
|
403
|
+
'.mindrian',
|
|
404
|
+
'decision-archive',
|
|
405
|
+
'2025-12',
|
|
406
|
+
'market-analysis.jsonl'
|
|
407
|
+
);
|
|
408
|
+
assert.ok(fs.existsSync(dec), 'should partition by archived entry month (2025-12), not today; file: ' + dec);
|
|
409
|
+
const cur = path.join(
|
|
410
|
+
room,
|
|
411
|
+
'.mindrian',
|
|
412
|
+
'decision-archive',
|
|
413
|
+
'2026-04',
|
|
414
|
+
'market-analysis.jsonl'
|
|
415
|
+
);
|
|
416
|
+
// The 2026-04 partition should NOT exist for this archive operation.
|
|
417
|
+
assert.strictEqual(fs.existsSync(cur), false, '2026-04 partition should not be created');
|
|
418
|
+
});
|
|
419
|
+
|
|
420
|
+
// ---------- Test 6: invalid entry rejected + MINTO unchanged ----------
|
|
421
|
+
|
|
422
|
+
run('Test 6: invalid entry returns {success:false}; MINTO unchanged', function () {
|
|
423
|
+
const room = buildRoomRoot('dc-t6-');
|
|
424
|
+
const sectionDir = writeMintoMd(room, 'market-analysis');
|
|
425
|
+
const before = fs.readFileSync(path.join(sectionDir, 'MINTO.md'), 'utf8');
|
|
426
|
+
const bad = { session_id: '', timestamp: 'not-an-iso', action: '', user_response: 'maybe', reason: '' };
|
|
427
|
+
const r = dc.recordDecision(room, 'market-analysis', bad);
|
|
428
|
+
assert.strictEqual(r.success, false);
|
|
429
|
+
assert.ok(Array.isArray(r.violations) && r.violations.length > 0, 'violations must be non-empty');
|
|
430
|
+
const after = fs.readFileSync(path.join(sectionDir, 'MINTO.md'), 'utf8');
|
|
431
|
+
assert.strictEqual(after, before, 'MINTO must be unchanged on validation failure');
|
|
432
|
+
});
|
|
433
|
+
|
|
434
|
+
// ---------- Test 7: readDecisionLog on no MINTO returns [] ----------
|
|
435
|
+
|
|
436
|
+
run('Test 7: readDecisionLog on section with no MINTO.md returns []', function () {
|
|
437
|
+
const room = buildRoomRoot('dc-t7-');
|
|
438
|
+
// Do NOT write MINTO.md.
|
|
439
|
+
const log = dc.readDecisionLog(room, 'nonexistent-section');
|
|
440
|
+
assert.ok(Array.isArray(log) && log.length === 0);
|
|
441
|
+
});
|
|
442
|
+
|
|
443
|
+
// ---------- Test 8: readDecisionLog with no decision_log field returns [] ----------
|
|
444
|
+
|
|
445
|
+
run('Test 8: readDecisionLog on MINTO lacking decision_log field returns []', function () {
|
|
446
|
+
const room = buildRoomRoot('dc-t8-');
|
|
447
|
+
const sectionDir = path.join(room, 'old-section');
|
|
448
|
+
fs.mkdirSync(sectionDir, { recursive: true });
|
|
449
|
+
// Pre-88-00 MINTO.md shape (no decision_log).
|
|
450
|
+
fs.writeFileSync(
|
|
451
|
+
path.join(sectionDir, 'MINTO.md'),
|
|
452
|
+
[
|
|
453
|
+
'---',
|
|
454
|
+
'type: section-minto',
|
|
455
|
+
'section: old-section',
|
|
456
|
+
'created: 2026-04-01',
|
|
457
|
+
'---',
|
|
458
|
+
'',
|
|
459
|
+
'# old-section',
|
|
460
|
+
].join('\n') + '\n'
|
|
461
|
+
);
|
|
462
|
+
const log = dc.readDecisionLog(room, 'old-section');
|
|
463
|
+
assert.ok(Array.isArray(log) && log.length === 0);
|
|
464
|
+
});
|
|
465
|
+
|
|
466
|
+
// ---------- Test 9: 3 concurrent forked workers ----------
|
|
467
|
+
|
|
468
|
+
run('Test 9: 3 concurrent workers -> final MINTO has 3 entries, no data loss', function () {
|
|
469
|
+
const room = buildRoomRoot('dc-t9-');
|
|
470
|
+
writeMintoMd(room, 'market-analysis');
|
|
471
|
+
const workerPath = path.join(__dirname, 'decision-capture.worker.cjs');
|
|
472
|
+
assert.ok(fs.existsSync(workerPath), 'worker file must exist');
|
|
473
|
+
const kids = [];
|
|
474
|
+
for (let i = 0; i < 3; i++) {
|
|
475
|
+
const res = spawnSync(
|
|
476
|
+
process.execPath,
|
|
477
|
+
[workerPath, room, 'market-analysis', 'sess-w' + i, 'action-w' + i],
|
|
478
|
+
{ encoding: 'utf-8' }
|
|
479
|
+
);
|
|
480
|
+
kids.push(res);
|
|
481
|
+
}
|
|
482
|
+
for (const res of kids) {
|
|
483
|
+
assert.strictEqual(res.status, 0, 'worker should exit 0: stderr=' + (res.stderr || ''));
|
|
484
|
+
}
|
|
485
|
+
const log = dc.readDecisionLog(room, 'market-analysis');
|
|
486
|
+
assert.strictEqual(log.length, 3, 'should have 3 entries (one per worker), got ' + log.length);
|
|
487
|
+
const ids = log.map(function (e) { return e.session_id; }).sort();
|
|
488
|
+
assert.deepStrictEqual(ids, ['sess-w0', 'sess-w1', 'sess-w2']);
|
|
489
|
+
});
|
|
490
|
+
|
|
491
|
+
// ---------- Test 10: post-write MINTO still validates via invariants ----------
|
|
492
|
+
|
|
493
|
+
run('Test 10: MINTO post-write validates via feynman-minto-invariants (no critical)', function () {
|
|
494
|
+
const room = buildRoomRoot('dc-t10-');
|
|
495
|
+
const sectionDir = writeMintoMd(room, 'market-analysis');
|
|
496
|
+
for (let i = 0; i < 5; i++) {
|
|
497
|
+
dc.recordDecision(
|
|
498
|
+
room,
|
|
499
|
+
'market-analysis',
|
|
500
|
+
mkEntry({ session_id: 'sess-' + i, action: 'a' + i })
|
|
501
|
+
);
|
|
502
|
+
}
|
|
503
|
+
const invariants = require(INVARIANTS_PATH);
|
|
504
|
+
const validation = invariants.validate(path.join(sectionDir, 'MINTO.md'));
|
|
505
|
+
// Severity may be warning or info (coherence drift); must NOT be critical.
|
|
506
|
+
assert.notStrictEqual(
|
|
507
|
+
validation.severity,
|
|
508
|
+
invariants.SEVERITY.CRITICAL,
|
|
509
|
+
'post-write MINTO must not be invariant-critical: ' + JSON.stringify(validation.violations)
|
|
510
|
+
);
|
|
511
|
+
});
|
|
512
|
+
|
|
513
|
+
// ---------- Test 11: preserve-on-regen (composition with 88-00 Task 1) ----------
|
|
514
|
+
|
|
515
|
+
run('Test 11: decision_log preserved byte-identical across generator --write', function () {
|
|
516
|
+
// Use the frozen fixture-small room. We inject a MINTO.md with
|
|
517
|
+
// decision_log, then run the generator --write with the known-good
|
|
518
|
+
// fixture-small.json narrative, and expect the decision_log to be
|
|
519
|
+
// preserved byte-for-byte across the regen.
|
|
520
|
+
const srcRoom = path.join(
|
|
521
|
+
REPO,
|
|
522
|
+
'test-fixtures',
|
|
523
|
+
'feynman',
|
|
524
|
+
'sections',
|
|
525
|
+
'fixture-small'
|
|
526
|
+
);
|
|
527
|
+
const narrativePath = path.join(
|
|
528
|
+
REPO,
|
|
529
|
+
'test-fixtures',
|
|
530
|
+
'feynman',
|
|
531
|
+
'narratives',
|
|
532
|
+
'fixture-small.json'
|
|
533
|
+
);
|
|
534
|
+
// Copy the fixture room to a tmpdir so we can mutate it freely.
|
|
535
|
+
const dst = mkTmp('dc-t11-');
|
|
536
|
+
fs.cpSync(srcRoom, dst, { recursive: true });
|
|
537
|
+
fs.writeFileSync(path.join(dst, '.room-root'), 'mindrian-room\n');
|
|
538
|
+
fs.mkdirSync(path.join(dst, '.mindrian'), { recursive: true });
|
|
539
|
+
|
|
540
|
+
// Seed a first generator run so MINTO.md exists (preserve-on-regen
|
|
541
|
+
// requires a pre-existing file; the fixture may not ship with one).
|
|
542
|
+
const seedRes = spawnSync(
|
|
543
|
+
process.execPath,
|
|
544
|
+
[GENERATOR, '--write', dst, '--section', 'problem-definition', '--narrative', narrativePath],
|
|
545
|
+
{
|
|
546
|
+
env: Object.assign({}, process.env, {
|
|
547
|
+
MINTO_FROZEN_DATE: '2026-04-14',
|
|
548
|
+
MINTO_FROZEN_TIMESTAMP: '2026-04-14T00:00:00Z',
|
|
549
|
+
}),
|
|
550
|
+
encoding: 'utf-8',
|
|
551
|
+
}
|
|
552
|
+
);
|
|
553
|
+
assert.strictEqual(seedRes.status, 0, 'seed generator run must succeed, stderr=' + (seedRes.stderr || ''));
|
|
554
|
+
|
|
555
|
+
// Record 3 decisions.
|
|
556
|
+
const entries = [];
|
|
557
|
+
for (let i = 0; i < 3; i++) {
|
|
558
|
+
const e = mkEntry({ session_id: 'regen-' + i, action: 'a' + i });
|
|
559
|
+
entries.push(e);
|
|
560
|
+
const r = dc.recordDecision(dst, 'problem-definition', e);
|
|
561
|
+
assert.strictEqual(r.success, true, 'recordDecision should succeed');
|
|
562
|
+
}
|
|
563
|
+
const before = dc.readDecisionLog(dst, 'problem-definition');
|
|
564
|
+
assert.strictEqual(before.length, 3);
|
|
565
|
+
|
|
566
|
+
// Re-run generator --write with the same narrative; the preserve-on-regen
|
|
567
|
+
// contract (88-00) keeps the decision_log across the write.
|
|
568
|
+
const regenRes = spawnSync(
|
|
569
|
+
process.execPath,
|
|
570
|
+
[GENERATOR, '--write', dst, '--section', 'problem-definition', '--narrative', narrativePath],
|
|
571
|
+
{
|
|
572
|
+
env: Object.assign({}, process.env, {
|
|
573
|
+
MINTO_FROZEN_DATE: '2026-04-14',
|
|
574
|
+
MINTO_FROZEN_TIMESTAMP: '2026-04-14T00:00:00Z',
|
|
575
|
+
}),
|
|
576
|
+
encoding: 'utf-8',
|
|
577
|
+
}
|
|
578
|
+
);
|
|
579
|
+
assert.strictEqual(
|
|
580
|
+
regenRes.status,
|
|
581
|
+
0,
|
|
582
|
+
'generator --write must succeed, stderr=' + (regenRes.stderr || '')
|
|
583
|
+
);
|
|
584
|
+
|
|
585
|
+
const after = dc.readDecisionLog(dst, 'problem-definition');
|
|
586
|
+
assert.strictEqual(after.length, 3, 'decision_log length preserved across regen');
|
|
587
|
+
for (let i = 0; i < 3; i++) {
|
|
588
|
+
assert.strictEqual(after[i].session_id, before[i].session_id);
|
|
589
|
+
assert.strictEqual(after[i].action, before[i].action);
|
|
590
|
+
assert.strictEqual(after[i].user_response, before[i].user_response);
|
|
591
|
+
assert.strictEqual(after[i].reason, before[i].reason);
|
|
592
|
+
}
|
|
593
|
+
});
|
|
594
|
+
|
|
595
|
+
// ---------- Test 12: special-char section name ----------
|
|
596
|
+
|
|
597
|
+
run('Test 12: special-char section name handled via path.join + safe escape', function () {
|
|
598
|
+
// We test two separate special cases because filesystems differ.
|
|
599
|
+
const room = buildRoomRoot('dc-t12-');
|
|
600
|
+
const section = 'section with spaces';
|
|
601
|
+
writeMintoMd(room, section);
|
|
602
|
+
const r = dc.recordDecision(room, section, mkEntry({ session_id: 'sp-1', action: 'a' }));
|
|
603
|
+
assert.strictEqual(r.success, true, 'should succeed on spaced section name');
|
|
604
|
+
const log = dc.readDecisionLog(room, section);
|
|
605
|
+
assert.strictEqual(log.length, 1);
|
|
606
|
+
assert.strictEqual(log[0].session_id, 'sp-1');
|
|
607
|
+
// Trigger archive (fill + overflow) to verify archive filename is safe.
|
|
608
|
+
for (let i = 0; i < 20; i++) {
|
|
609
|
+
const ts = new Date(Date.UTC(2026, 3, i + 1, 0, 0, 0))
|
|
610
|
+
.toISOString()
|
|
611
|
+
.replace(/\.\d{3}Z$/, 'Z');
|
|
612
|
+
dc.recordDecision(room, section, mkEntry({
|
|
613
|
+
session_id: 'f-' + i, action: 'a' + i, timestamp: ts,
|
|
614
|
+
}));
|
|
615
|
+
}
|
|
616
|
+
// At least one archive file should exist under 2026-04/.
|
|
617
|
+
const apr = path.join(room, '.mindrian', 'decision-archive', '2026-04');
|
|
618
|
+
assert.ok(fs.existsSync(apr), 'archive partition should exist');
|
|
619
|
+
const files = fs.readdirSync(apr);
|
|
620
|
+
assert.ok(files.length >= 1, 'at least one archive file');
|
|
621
|
+
// Filename should contain the section segment (with its special chars)
|
|
622
|
+
// in a form that does NOT break the filesystem. We accept either raw
|
|
623
|
+
// spaces or escaped variants; the contract is "file exists and is
|
|
624
|
+
// writable" not a specific filename convention.
|
|
625
|
+
const allJsonl = files.every(function (f) { return f.endsWith('.jsonl'); });
|
|
626
|
+
assert.ok(allJsonl, 'all archive files should end with .jsonl: ' + files.join(', '));
|
|
627
|
+
});
|
|
628
|
+
|
|
629
|
+
// ---------- Summary ----------
|
|
630
|
+
|
|
631
|
+
process.stdout.write('\ndecision-capture.test.cjs: ' + passed + '/' + (passed + failed) + ' passed\n');
|
|
632
|
+
process.exit(failed === 0 ? 0 : 1);
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/*
|
|
3
|
+
* Copyright (c) 2026 Mindrian. BSL 1.1.
|
|
4
|
+
*
|
|
5
|
+
* Phase 88-10 -- decision-capture concurrency worker
|
|
6
|
+
* ===================================================
|
|
7
|
+
* Forked by lib/memory/decision-capture.test.cjs Test 9 (3 concurrent
|
|
8
|
+
* recordDecision calls against the same section) so the test exercises
|
|
9
|
+
* OS-level concurrency rather than fake "parallel" promises in one event
|
|
10
|
+
* loop. Matches the Phase 87-02 write-lock-atomic.worker.cjs and Phase
|
|
11
|
+
* 88-04-B vault-section-minto-generator-atomic.worker.cjs conventions
|
|
12
|
+
* (standalone file, not inline template string).
|
|
13
|
+
*
|
|
14
|
+
* Usage:
|
|
15
|
+
* node decision-capture.worker.cjs <roomPath> <section> <session_id> <action>
|
|
16
|
+
*
|
|
17
|
+
* The worker fabricates a valid decision log entry inline and calls
|
|
18
|
+
* recordDecision() through lib/core/decision-capture.cjs. Each worker
|
|
19
|
+
* gets a unique session_id + action so the final MINTO decision_log
|
|
20
|
+
* carries exactly one entry per worker after the race.
|
|
21
|
+
*
|
|
22
|
+
* Exit codes:
|
|
23
|
+
* 0 -- recordDecision returned success:true
|
|
24
|
+
* 1 -- unexpected error or success:false (fails the parent test)
|
|
25
|
+
*/
|
|
26
|
+
|
|
27
|
+
'use strict';
|
|
28
|
+
|
|
29
|
+
const path = require('node:path');
|
|
30
|
+
|
|
31
|
+
const dc = require(path.resolve(__dirname, '..', 'core', 'decision-capture.cjs'));
|
|
32
|
+
|
|
33
|
+
const roomPath = process.argv[2];
|
|
34
|
+
const section = process.argv[3];
|
|
35
|
+
const sessionId = process.argv[4];
|
|
36
|
+
const action = process.argv[5];
|
|
37
|
+
|
|
38
|
+
if (!roomPath || !section || !sessionId || !action) {
|
|
39
|
+
process.stderr.write(
|
|
40
|
+
'decision-capture.worker: missing argv (roomPath, section, session_id, action)\n'
|
|
41
|
+
);
|
|
42
|
+
process.exit(1);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const entry = {
|
|
46
|
+
session_id: sessionId,
|
|
47
|
+
timestamp: new Date().toISOString().replace(/\.\d{3}Z$/, 'Z'),
|
|
48
|
+
action: action,
|
|
49
|
+
user_response: 'approve',
|
|
50
|
+
reason: 'concurrent worker ' + sessionId,
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
try {
|
|
54
|
+
const r = dc.recordDecision(roomPath, section, entry);
|
|
55
|
+
if (!r || r.success !== true) {
|
|
56
|
+
process.stderr.write(
|
|
57
|
+
'decision-capture.worker: success=false, violations=' +
|
|
58
|
+
JSON.stringify((r && r.violations) || []) +
|
|
59
|
+
'\n'
|
|
60
|
+
);
|
|
61
|
+
process.exit(1);
|
|
62
|
+
}
|
|
63
|
+
process.exit(0);
|
|
64
|
+
} catch (e) {
|
|
65
|
+
process.stderr.write(
|
|
66
|
+
'decision-capture.worker ERROR: ' + (e && e.message || e) + '\n'
|
|
67
|
+
);
|
|
68
|
+
if (e && e.stack) process.stderr.write(e.stack + '\n');
|
|
69
|
+
process.exit(1);
|
|
70
|
+
}
|