@mindrian_os/cli 1.13.0-beta.10

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.
Files changed (590) hide show
  1. package/.claude-plugin/plugin.json +21 -0
  2. package/.mcp.json +9 -0
  3. package/CHANGELOG.md +3329 -0
  4. package/LICENSE +123 -0
  5. package/README.md +673 -0
  6. package/agents/brain-query.md +80 -0
  7. package/agents/framework-runner.md +237 -0
  8. package/agents/grading.md +188 -0
  9. package/agents/investor.md +128 -0
  10. package/agents/larry-extended.md +135 -0
  11. package/agents/opportunity-scanner.md +91 -0
  12. package/agents/persona-analyst.md +132 -0
  13. package/agents/research.md +89 -0
  14. package/agents/reverse-salient-agent.md +27 -0
  15. package/bin/cli.js +93 -0
  16. package/bin/mindrian-mcp-server.cjs +182 -0
  17. package/bin/mindrian-tools.cjs +765 -0
  18. package/commands/act.md +433 -0
  19. package/commands/admin.md +404 -0
  20. package/commands/analyze-needs.md +36 -0
  21. package/commands/analyze-systems.md +33 -0
  22. package/commands/analyze-timing.md +36 -0
  23. package/commands/auto-explore.md +64 -0
  24. package/commands/beautiful-question.md +34 -0
  25. package/commands/brain-derive.md +78 -0
  26. package/commands/build-knowledge.md +36 -0
  27. package/commands/build-thesis.md +40 -0
  28. package/commands/causal.md +228 -0
  29. package/commands/challenge-assumptions.md +33 -0
  30. package/commands/compare-ventures.md +77 -0
  31. package/commands/dashboard.md +110 -0
  32. package/commands/deep-grade.md +76 -0
  33. package/commands/diagnose.md +52 -0
  34. package/commands/diagnostics.md +145 -0
  35. package/commands/doctor.md +151 -0
  36. package/commands/dominant-designs.md +34 -0
  37. package/commands/explain-decision.md +87 -0
  38. package/commands/explore-domains.md +36 -0
  39. package/commands/explore-futures.md +34 -0
  40. package/commands/explore-trends.md +36 -0
  41. package/commands/export.md +103 -0
  42. package/commands/file-meeting.md +724 -0
  43. package/commands/find-analogies.md +182 -0
  44. package/commands/find-bottlenecks.md +56 -0
  45. package/commands/find-connections.md +70 -0
  46. package/commands/funding.md +81 -0
  47. package/commands/grade.md +197 -0
  48. package/commands/graph.md +128 -0
  49. package/commands/hat-briefing.md +119 -0
  50. package/commands/heal.md +196 -0
  51. package/commands/help.md +399 -0
  52. package/commands/hmi-status.md +172 -0
  53. package/commands/jtbd.md +241 -0
  54. package/commands/leadership.md +73 -0
  55. package/commands/lean-canvas.md +34 -0
  56. package/commands/macro-trends.md +34 -0
  57. package/commands/map-unknowns.md +34 -0
  58. package/commands/memory.md +173 -0
  59. package/commands/models.md +175 -0
  60. package/commands/mos-reason.md +279 -0
  61. package/commands/mullins.md +114 -0
  62. package/commands/new-project.md +481 -0
  63. package/commands/onboard.md +434 -0
  64. package/commands/operator.md +149 -0
  65. package/commands/opportunities.md +144 -0
  66. package/commands/organize.md +497 -0
  67. package/commands/persona.md +192 -0
  68. package/commands/pipeline.md +106 -0
  69. package/commands/present.md +91 -0
  70. package/commands/publish.md +201 -0
  71. package/commands/query.md +124 -0
  72. package/commands/radar.md +72 -0
  73. package/commands/reanalyze.md +91 -0
  74. package/commands/research.md +190 -0
  75. package/commands/room.md +352 -0
  76. package/commands/rooms.md +598 -0
  77. package/commands/root-cause.md +34 -0
  78. package/commands/rs-experts.md +79 -0
  79. package/commands/rs-explain.md +94 -0
  80. package/commands/rs-fetch.md +88 -0
  81. package/commands/rs-thesis.md +79 -0
  82. package/commands/scenario-plan.md +34 -0
  83. package/commands/scheduled-tasks.md +285 -0
  84. package/commands/score-innovation.md +37 -0
  85. package/commands/scout.md +239 -0
  86. package/commands/setup.md +618 -0
  87. package/commands/snapshot.md +147 -0
  88. package/commands/speakers.md +84 -0
  89. package/commands/splash.md +28 -0
  90. package/commands/status.md +75 -0
  91. package/commands/structure-argument.md +36 -0
  92. package/commands/suggest-next.md +74 -0
  93. package/commands/systems-thinking.md +34 -0
  94. package/commands/think-hats.md +36 -0
  95. package/commands/update.md +181 -0
  96. package/commands/user-needs.md +34 -0
  97. package/commands/validate.md +34 -0
  98. package/commands/value-proposition.md +55 -0
  99. package/commands/vault.md +180 -0
  100. package/commands/visualize.md +52 -0
  101. package/commands/whitespace.md +501 -0
  102. package/commands/wiki.md +69 -0
  103. package/hooks/hooks.json +381 -0
  104. package/hooks/run-hook.cmd +64 -0
  105. package/lib/__init__.py +0 -0
  106. package/lib/__pycache__/__init__.cpython-312.pyc +0 -0
  107. package/lib/agents/auto-explore-agent.cjs +1043 -0
  108. package/lib/agents/reverse-salient-agent.cjs +679 -0
  109. package/lib/agents/tension-hook-agent.cjs +544 -0
  110. package/lib/chat/chat-context.js +185 -0
  111. package/lib/chat/chat-panel.js +721 -0
  112. package/lib/chat/fabric-chat.cjs +288 -0
  113. package/lib/chat/generative-tools.js +219 -0
  114. package/lib/conversation/ROOM.md +39 -0
  115. package/lib/conversation/classifier-rules.json +38 -0
  116. package/lib/conversation/classifier.cjs +264 -0
  117. package/lib/conversation/operator.cjs +287 -0
  118. package/lib/copy/115-spec-strings.cjs +55 -0
  119. package/lib/core/__init__.py +0 -0
  120. package/lib/core/__nav-stub.cjs +14 -0
  121. package/lib/core/__pycache__/__init__.cpython-312.pyc +0 -0
  122. package/lib/core/__pycache__/rs-math.cpython-312.pyc +0 -0
  123. package/lib/core/__pycache__/rs_cache.cpython-312.pyc +0 -0
  124. package/lib/core/__pycache__/rs_corpus.cpython-312.pyc +0 -0
  125. package/lib/core/__pycache__/rs_hybrid.cpython-312.pyc +0 -0
  126. package/lib/core/__pycache__/rs_math.cpython-312.pyc +0 -0
  127. package/lib/core/__pycache__/rs_rooms.cpython-312.pyc +0 -0
  128. package/lib/core/artifact-id.cjs +148 -0
  129. package/lib/core/asset-ops.cjs +151 -0
  130. package/lib/core/auto-commit-throttle.cjs +129 -0
  131. package/lib/core/bearer-token.cjs +199 -0
  132. package/lib/core/brain-client.cjs +865 -0
  133. package/lib/core/brain-derivation-prompts.cjs +326 -0
  134. package/lib/core/brain-derivation-queue.cjs +431 -0
  135. package/lib/core/brain-derivation.cjs +580 -0
  136. package/lib/core/brain-md-schema.cjs +528 -0
  137. package/lib/core/brain-md-staleness.cjs +357 -0
  138. package/lib/core/brain-response-sanitize.cjs +188 -0
  139. package/lib/core/bridge-writer.cjs +477 -0
  140. package/lib/core/chat-context-builder.cjs +253 -0
  141. package/lib/core/cross-room-aggregator.cjs +762 -0
  142. package/lib/core/daily-briefing.cjs +438 -0
  143. package/lib/core/decision-capture.cjs +618 -0
  144. package/lib/core/deep-links.cjs +82 -0
  145. package/lib/core/dispatch-optimizer.cjs +354 -0
  146. package/lib/core/dual-path-detector.cjs +84 -0
  147. package/lib/core/dual-path-detector.test.cjs +334 -0
  148. package/lib/core/exports-log.cjs +79 -0
  149. package/lib/core/feynman-minto-invariants.cjs +605 -0
  150. package/lib/core/folder-memory-async.cjs +338 -0
  151. package/lib/core/folder-memory-shared.cjs +890 -0
  152. package/lib/core/folder-memory.cjs +416 -0
  153. package/lib/core/framework-chain-composer.cjs +411 -0
  154. package/lib/core/frontmatter-schemas.cjs +330 -0
  155. package/lib/core/git-ops.cjs +141 -0
  156. package/lib/core/graph-ops.cjs +258 -0
  157. package/lib/core/hat-persistence.cjs +362 -0
  158. package/lib/core/index.cjs +60 -0
  159. package/lib/core/integration-registry.cjs +232 -0
  160. package/lib/core/intelligence-cascade.cjs +661 -0
  161. package/lib/core/lazygraph-ops.cjs +1057 -0
  162. package/lib/core/lru-cache.cjs +139 -0
  163. package/lib/core/mcp-profiles.cjs +182 -0
  164. package/lib/core/meeting-ops.cjs +54 -0
  165. package/lib/core/memory-ops.cjs +600 -0
  166. package/lib/core/migrations/ROOM.md +33 -0
  167. package/lib/core/migrations/phase-109-nodes-provenance.cjs +339 -0
  168. package/lib/core/migrations/phase-109-session-focus.cjs +99 -0
  169. package/lib/core/model-profiles.cjs +246 -0
  170. package/lib/core/mullins-scaffold.cjs +160 -0
  171. package/lib/core/nav-dial.cjs +316 -0
  172. package/lib/core/navigation/ROOM.md +15 -0
  173. package/lib/core/navigation/explanation.cjs +43 -0
  174. package/lib/core/navigation/focus.cjs +135 -0
  175. package/lib/core/navigation/ingestion.cjs +82 -0
  176. package/lib/core/navigation/insights.cjs +350 -0
  177. package/lib/core/navigation/memory-events.cjs +118 -0
  178. package/lib/core/navigation/neighborhood.cjs +78 -0
  179. package/lib/core/navigation/packet.cjs +182 -0
  180. package/lib/core/navigation/room-home.cjs +127 -0
  181. package/lib/core/navigation/transitions.cjs +82 -0
  182. package/lib/core/navigation-engine-shared.cjs +242 -0
  183. package/lib/core/navigation-engine.cjs +664 -0
  184. package/lib/core/navigation.cjs +60 -0
  185. package/lib/core/nl-graph-queries.cjs +164 -0
  186. package/lib/core/offer-presenter.cjs +406 -0
  187. package/lib/core/opportunity-extractor.cjs +183 -0
  188. package/lib/core/opportunity-ops.cjs +1371 -0
  189. package/lib/core/persona-ops.cjs +537 -0
  190. package/lib/core/persona-taxonomy.cjs +190 -0
  191. package/lib/core/platform-gates.cjs +120 -0
  192. package/lib/core/platform.cjs +257 -0
  193. package/lib/core/proactive-intelligence.cjs +528 -0
  194. package/lib/core/problem-type-router.cjs +315 -0
  195. package/lib/core/reasoning-ops.cjs +639 -0
  196. package/lib/core/reverse-salient-persona-suffix.cjs +115 -0
  197. package/lib/core/room-classifier-strict-mode.cjs +229 -0
  198. package/lib/core/room-db.cjs +127 -0
  199. package/lib/core/room-ops-async.cjs +92 -0
  200. package/lib/core/room-ops-shared.cjs +64 -0
  201. package/lib/core/room-ops-sync.cjs +70 -0
  202. package/lib/core/room-ops.cjs +32 -0
  203. package/lib/core/room-type-detector.cjs +386 -0
  204. package/lib/core/rs-brain-substrate-prompts.cjs +129 -0
  205. package/lib/core/rs-brain-substrate.cjs +570 -0
  206. package/lib/core/rs-breakthrough-scorer.cjs +255 -0
  207. package/lib/core/rs-canon-violations.cjs +82 -0
  208. package/lib/core/rs-chain-feeder.cjs +343 -0
  209. package/lib/core/rs-commercial-assessor.cjs +280 -0
  210. package/lib/core/rs-differential-scorer.cjs +376 -0
  211. package/lib/core/rs-domain-analyzer.cjs +385 -0
  212. package/lib/core/rs-egress-prompts.cjs +113 -0
  213. package/lib/core/rs-egress-telemetry.cjs +225 -0
  214. package/lib/core/rs-egress-violations.cjs +53 -0
  215. package/lib/core/rs-expert-mapper.cjs +467 -0
  216. package/lib/core/rs-fetcher-academic.cjs +697 -0
  217. package/lib/core/rs-fetcher-experts.cjs +314 -0
  218. package/lib/core/rs-fetcher-industry.cjs +731 -0
  219. package/lib/core/rs-fetcher-patents.cjs +564 -0
  220. package/lib/core/rs-innovation-classifier.cjs +194 -0
  221. package/lib/core/rs-mind-map.cjs +656 -0
  222. package/lib/core/rs-neo4j-writer.cjs +388 -0
  223. package/lib/core/rs-nl-to-query.cjs +425 -0
  224. package/lib/core/rs-pinecone-bridge.cjs +303 -0
  225. package/lib/core/rs-preprocessor.cjs +350 -0
  226. package/lib/core/rs-query-matrix.cjs +316 -0
  227. package/lib/core/rs-query-to-text.cjs +438 -0
  228. package/lib/core/rs-sqlite-mirror.cjs +443 -0
  229. package/lib/core/rs-thesis-generator.cjs +188 -0
  230. package/lib/core/rs_cache.py +479 -0
  231. package/lib/core/rs_corpus.py +468 -0
  232. package/lib/core/rs_hybrid.py +586 -0
  233. package/lib/core/rs_math.py +287 -0
  234. package/lib/core/rs_rooms.py +193 -0
  235. package/lib/core/scheduled-scanner.cjs +463 -0
  236. package/lib/core/scratchpad-ops.cjs +201 -0
  237. package/lib/core/section-8-trace-schema.cjs +138 -0
  238. package/lib/core/section-registry.cjs +111 -0
  239. package/lib/core/session-state.cjs +144 -0
  240. package/lib/core/shallow-doc-parser.cjs +174 -0
  241. package/lib/core/shallow-doc-parser.test.cjs +226 -0
  242. package/lib/core/skill-activation-router.cjs +284 -0
  243. package/lib/core/state-ops.cjs +46 -0
  244. package/lib/core/statusline-cache.cjs +266 -0
  245. package/lib/core/token-estimator.cjs +348 -0
  246. package/lib/core/user-archetype.cjs +239 -0
  247. package/lib/core/user-md-ops.cjs +524 -0
  248. package/lib/core/visual-ops.cjs +624 -0
  249. package/lib/core/write-lock.cjs +149 -0
  250. package/lib/graph/canvas-graph.js +467 -0
  251. package/lib/graph/constellation-config.cjs +299 -0
  252. package/lib/graph/graph-detail-panel.js +165 -0
  253. package/lib/hmi/ROOM.md +47 -0
  254. package/lib/hmi/across-session-memory.cjs +604 -0
  255. package/lib/hmi/cross-room-memory.cjs +575 -0
  256. package/lib/hmi/decoy-tier.cjs +395 -0
  257. package/lib/hmi/jtbd-classifier.cjs +219 -0
  258. package/lib/hmi/jtbd-state.cjs +199 -0
  259. package/lib/hmi/jtbd-taxonomy.json +392 -0
  260. package/lib/hmi/selector-dispatcher.cjs +546 -0
  261. package/lib/hmi/selector-telemetry.cjs +263 -0
  262. package/lib/hmi/shape-f0-renderer.cjs +139 -0
  263. package/lib/hmi/shape-f1-fallback.cjs +80 -0
  264. package/lib/hmi/shape-f1-renderer.cjs +138 -0
  265. package/lib/hmi/shape-f2-renderer.cjs +132 -0
  266. package/lib/hmi/shape-f3-renderer.cjs +66 -0
  267. package/lib/hmi/shape-f4-renderer.cjs +72 -0
  268. package/lib/hmi/shape-f5-renderer.cjs +155 -0
  269. package/lib/hmi/shape-f6-plan-review-renderer.cjs +312 -0
  270. package/lib/hmi/shape-f6-renderer.cjs +144 -0
  271. package/lib/hmi/shape-g-renderer.cjs +219 -0
  272. package/lib/hmi/shape-h-renderer.cjs +222 -0
  273. package/lib/hmi/tier-check.cjs +63 -0
  274. package/lib/import/PRECONDITIONS.md +41 -0
  275. package/lib/import/branding.cjs +210 -0
  276. package/lib/import/branding.test.cjs +235 -0
  277. package/lib/import/classifications-sync.cjs +104 -0
  278. package/lib/import/classifications-sync.test.cjs +129 -0
  279. package/lib/import/enricher.cjs +296 -0
  280. package/lib/import/enricher.test.cjs +273 -0
  281. package/lib/import/integration.test.cjs +376 -0
  282. package/lib/import/manifest.cjs +129 -0
  283. package/lib/import/manifest.schema.json +185 -0
  284. package/lib/import/manifest.test.cjs +123 -0
  285. package/lib/import/meeting-detector.cjs +92 -0
  286. package/lib/import/meeting-detector.test.cjs +100 -0
  287. package/lib/import/person-detector.cjs +229 -0
  288. package/lib/import/person-detector.test.cjs +149 -0
  289. package/lib/import/report.cjs +186 -0
  290. package/lib/import/report.test.cjs +186 -0
  291. package/lib/import/room-md-scaffolder.cjs +49 -0
  292. package/lib/import/router.cjs +224 -0
  293. package/lib/import/router.test.cjs +356 -0
  294. package/lib/import/run-all-tests.cjs +36 -0
  295. package/lib/import/smoke-test.cjs +213 -0
  296. package/lib/import/smoke-test.test.cjs +148 -0
  297. package/lib/import/test-fixtures/collision-vault/preexisting-room/STATE.md +8 -0
  298. package/lib/import/test-fixtures/collision-vault/preexisting-room/problem-definition/onboarding/onboarding.md +7 -0
  299. package/lib/import/test-fixtures/collision-vault/source/onboarding.md +5 -0
  300. package/lib/import/test-fixtures/obsidian-vault/.obsidian/workspace.json +1 -0
  301. package/lib/import/test-fixtures/obsidian-vault/notes/with-wikilinks.md +4 -0
  302. package/lib/import/test-fixtures/tiny-vault/notes/2026-01-15-team-sync.md +9 -0
  303. package/lib/import/test-fixtures/tiny-vault/notes/empty.md +3 -0
  304. package/lib/import/test-fixtures/tiny-vault/notes/onboarding.md +5 -0
  305. package/lib/import/test-fixtures/tiny-vault/notes/pricing.md +5 -0
  306. package/lib/import/test-fixtures/tiny-vault/notes/random.md +4 -0
  307. package/lib/import/undo.test.cjs +199 -0
  308. package/lib/import/vault-scanner.cjs +105 -0
  309. package/lib/import/vault-scanner.test.cjs +67 -0
  310. package/lib/mcp/app-html/dashboard.html +316 -0
  311. package/lib/mcp/app-html/graph.html +428 -0
  312. package/lib/mcp/app-html/mindrian-platform.html +1841 -0
  313. package/lib/mcp/app-html/wiki.html +383 -0
  314. package/lib/mcp/app-views.cjs +322 -0
  315. package/lib/mcp/brain-router.cjs +418 -0
  316. package/lib/mcp/capability-registry.cjs +62 -0
  317. package/lib/mcp/larry-context.cjs +46 -0
  318. package/lib/mcp/larry-server-instructions.md +114 -0
  319. package/lib/mcp/pipeline-state.cjs +275 -0
  320. package/lib/mcp/prompts.cjs +302 -0
  321. package/lib/mcp/resources.cjs +227 -0
  322. package/lib/mcp/session-catchup.cjs +327 -0
  323. package/lib/mcp/surface-detect.cjs +75 -0
  324. package/lib/mcp/tool-router.cjs +1034 -0
  325. package/lib/memory/aaak-compress.cjs +403 -0
  326. package/lib/memory/aaak-compress.test.cjs +288 -0
  327. package/lib/memory/async-artifact-auto-commit.test.cjs +223 -0
  328. package/lib/memory/bearer-token.test.cjs +315 -0
  329. package/lib/memory/brain-cache-lru.test.cjs +259 -0
  330. package/lib/memory/brain-client-query-shape.test.cjs +160 -0
  331. package/lib/memory/brain-derivation-graceful-degradation.test.cjs +1019 -0
  332. package/lib/memory/brain-derivation-queue.test.cjs +539 -0
  333. package/lib/memory/brain-derivation.test.cjs +634 -0
  334. package/lib/memory/brain-derive-command.test.cjs +534 -0
  335. package/lib/memory/brain-md-invariants-validator.test.cjs +704 -0
  336. package/lib/memory/brain-md-schema.test.cjs +467 -0
  337. package/lib/memory/brain-md-staleness.test.cjs +525 -0
  338. package/lib/memory/brain-server-resolution.test.cjs +314 -0
  339. package/lib/memory/chat-context.test.cjs +128 -0
  340. package/lib/memory/cross-room-aggregator.test.cjs +909 -0
  341. package/lib/memory/dashboard-server.test.cjs +256 -0
  342. package/lib/memory/debouncer-drain-at-prompt.test.cjs +389 -0
  343. package/lib/memory/decision-capture.test.cjs +632 -0
  344. package/lib/memory/decision-capture.worker.cjs +70 -0
  345. package/lib/memory/explain-decision-command.test.cjs +521 -0
  346. package/lib/memory/explain-decision-footer.test.cjs +316 -0
  347. package/lib/memory/explored-materials-store.cjs +392 -0
  348. package/lib/memory/feynman-minto-guardian.test.cjs +736 -0
  349. package/lib/memory/feynman-minto-invariants.test.cjs +511 -0
  350. package/lib/memory/feynman-prompts-drift.test.cjs +144 -0
  351. package/lib/memory/feynman-prompts.cjs +151 -0
  352. package/lib/memory/feynman-prompts.test.cjs +96 -0
  353. package/lib/memory/folder-memory-quadruple.test.cjs +548 -0
  354. package/lib/memory/folder-memory.test.cjs +503 -0
  355. package/lib/memory/framework-chain-composer.test.cjs +515 -0
  356. package/lib/memory/frontmatter-schema-validator.test.cjs +290 -0
  357. package/lib/memory/heal-command.test.cjs +604 -0
  358. package/lib/memory/index-artifact-transaction.test.cjs +333 -0
  359. package/lib/memory/lazygraph-rs-discoveries-view.test.cjs +122 -0
  360. package/lib/memory/mcp-input-validation.test.cjs +240 -0
  361. package/lib/memory/mcp-server-brain-deps.test.cjs +270 -0
  362. package/lib/memory/mcp-stack-fallback.test.cjs +433 -0
  363. package/lib/memory/minto-debouncer.test.cjs +407 -0
  364. package/lib/memory/minto-debouncer.worker.cjs +46 -0
  365. package/lib/memory/minto-migration-v88.test.cjs +265 -0
  366. package/lib/memory/minto-schema-v88.test.cjs +390 -0
  367. package/lib/memory/mos-status-renderer.test.cjs +631 -0
  368. package/lib/memory/narrative-schema.cjs +376 -0
  369. package/lib/memory/narrative-schema.test.cjs +209 -0
  370. package/lib/memory/nav-dial.test.cjs +414 -0
  371. package/lib/memory/navigation-engine-core.test.cjs +722 -0
  372. package/lib/memory/navigation-invariants.test.cjs +483 -0
  373. package/lib/memory/offer-presenter.test.cjs +554 -0
  374. package/lib/memory/on-stop-snapshot.test.cjs +404 -0
  375. package/lib/memory/pending-tension-store.cjs +373 -0
  376. package/lib/memory/post-compact-reinjection.test.cjs +854 -0
  377. package/lib/memory/post-write-triple.test.cjs +317 -0
  378. package/lib/memory/pre-compact-snapshot.test.cjs +495 -0
  379. package/lib/memory/problem-type-router.test.cjs +656 -0
  380. package/lib/memory/query-efficiency-telemetry.test.cjs +370 -0
  381. package/lib/memory/recompile-room-references.test.cjs +392 -0
  382. package/lib/memory/recompile-room-references.worker.cjs +42 -0
  383. package/lib/memory/record-decision-dual-write.test.cjs +454 -0
  384. package/lib/memory/room-classifier-strict-mode.test.cjs +417 -0
  385. package/lib/memory/room-minto-hook.test.cjs +398 -0
  386. package/lib/memory/rs-discovery-engine.test.cjs +323 -0
  387. package/lib/memory/run-feynman-tests.cjs +1239 -0
  388. package/lib/memory/security-trifecta.test.cjs +312 -0
  389. package/lib/memory/session-start-brain-staleness.test.cjs +363 -0
  390. package/lib/memory/session-start-triple-injection.test.cjs +514 -0
  391. package/lib/memory/sessionstart-banner-formatter.cjs +318 -0
  392. package/lib/memory/sessionstart-minto-banner.test.cjs +373 -0
  393. package/lib/memory/skill-activation-router.test.cjs +419 -0
  394. package/lib/memory/stamp-artifact-write.test.cjs +304 -0
  395. package/lib/memory/statusline-active-room.test.cjs +315 -0
  396. package/lib/memory/statusline-minto-segment.test.cjs +292 -0
  397. package/lib/memory/sync-async-entry-points.test.cjs +204 -0
  398. package/lib/memory/test-bridge-writer-enhanced.cjs +452 -0
  399. package/lib/memory/test-rs-brain-substrate-shape.cjs +529 -0
  400. package/lib/memory/test-rs-brain-substrate.cjs +636 -0
  401. package/lib/memory/test-rs-breakthrough-scorer.cjs +375 -0
  402. package/lib/memory/test-rs-canon-violations.cjs +218 -0
  403. package/lib/memory/test-rs-chain-feeder-core.cjs +344 -0
  404. package/lib/memory/test-rs-chain-feeder-skill-spawn.cjs +297 -0
  405. package/lib/memory/test-rs-commercial-assessor.cjs +385 -0
  406. package/lib/memory/test-rs-differential-scorer.cjs +480 -0
  407. package/lib/memory/test-rs-discovery-engine.cjs +603 -0
  408. package/lib/memory/test-rs-domain-analyzer.cjs +492 -0
  409. package/lib/memory/test-rs-egress-primitives.cjs +420 -0
  410. package/lib/memory/test-rs-expert-mapper.cjs +547 -0
  411. package/lib/memory/test-rs-explain-command.cjs +443 -0
  412. package/lib/memory/test-rs-fetcher-academic.cjs +848 -0
  413. package/lib/memory/test-rs-fetcher-experts.cjs +496 -0
  414. package/lib/memory/test-rs-fetcher-industry.cjs +702 -0
  415. package/lib/memory/test-rs-fetcher-patents.cjs +674 -0
  416. package/lib/memory/test-rs-innovation-classifier.cjs +301 -0
  417. package/lib/memory/test-rs-mind-map.cjs +646 -0
  418. package/lib/memory/test-rs-neo4j-writer.cjs +518 -0
  419. package/lib/memory/test-rs-nl-to-query.cjs +449 -0
  420. package/lib/memory/test-rs-pinecone-bridge.cjs +277 -0
  421. package/lib/memory/test-rs-preprocessor.cjs +433 -0
  422. package/lib/memory/test-rs-query-matrix.cjs +391 -0
  423. package/lib/memory/test-rs-query-to-text.cjs +551 -0
  424. package/lib/memory/test-rs-sqlite-mirror.cjs +649 -0
  425. package/lib/memory/test-rs-thesis-generator.cjs +360 -0
  426. package/lib/memory/triple-context-formatter.cjs +473 -0
  427. package/lib/memory/triple-context-formatter.test.cjs +442 -0
  428. package/lib/memory/user-md-persona.test.cjs +565 -0
  429. package/lib/memory/userpromptsubmit-integration.test.cjs +690 -0
  430. package/lib/memory/validators/README.md +157 -0
  431. package/lib/memory/validators/brain-md-invariants.cjs +475 -0
  432. package/lib/memory/validators/brain-substrate-invariants.cjs +285 -0
  433. package/lib/memory/validators/external-academic-invariants.cjs +249 -0
  434. package/lib/memory/validators/external-industry-invariants.cjs +271 -0
  435. package/lib/memory/validators/external-patents-invariants.cjs +266 -0
  436. package/lib/memory/validators/minto-invariants.cjs +62 -0
  437. package/lib/memory/validators/navigation-invariants.cjs +340 -0
  438. package/lib/memory/validators/queue-health.cjs +95 -0
  439. package/lib/memory/validators/snapshot-integrity.cjs +129 -0
  440. package/lib/memory/validators/stale-lifecycle.cjs +116 -0
  441. package/lib/memory/vault-section-minto-generator-atomic.test.cjs +556 -0
  442. package/lib/memory/vault-section-minto-generator-atomic.worker.cjs +73 -0
  443. package/lib/memory/write-lock-atomic.test.cjs +137 -0
  444. package/lib/memory/write-lock-atomic.worker.cjs +55 -0
  445. package/lib/parity/check-parity.cjs +83 -0
  446. package/lib/presentation/presentation-server.cjs +101 -0
  447. package/lib/presentation/presentation-watcher.cjs +123 -0
  448. package/lib/quickview/hub-server.cjs +719 -0
  449. package/lib/quickview/server.cjs +533 -0
  450. package/lib/render/JTBD-PALETTES.md +145 -0
  451. package/lib/render/ROOM.md +59 -0
  452. package/lib/render/render-v2.cjs +486 -0
  453. package/lib/render/render-v2.test.cjs +267 -0
  454. package/lib/render/render.cjs +65 -0
  455. package/lib/state/ROOM.md +46 -0
  456. package/lib/state/state-md-parser.cjs +215 -0
  457. package/lib/statusline/ROOM.md +38 -0
  458. package/lib/statusline/banner-suppression.cjs +50 -0
  459. package/lib/statusline/surface-detect.cjs +85 -0
  460. package/lib/update-bootstrap.sh.template +145 -0
  461. package/lib/vault/frontmatter-schema.cjs +297 -0
  462. package/lib/vault/room-scanner.cjs +352 -0
  463. package/lib/vault/wikilink-builder.cjs +231 -0
  464. package/lib/vault/wikilink-builder.test.cjs +182 -0
  465. package/lib/wiki/graph-links.cjs +281 -0
  466. package/lib/wiki/page-renderer.cjs +229 -0
  467. package/lib/wiki/wiki-chat.cjs +81 -0
  468. package/lib/wiki/wiki-layout.cjs +1459 -0
  469. package/lib/wiki/wiki-search.cjs +142 -0
  470. package/lib/wiki/wiki-server.cjs +678 -0
  471. package/lib/wiki/wiki-watcher.cjs +105 -0
  472. package/package.json +44 -0
  473. package/pipelines/analogy/01-decompose.md +80 -0
  474. package/pipelines/analogy/02-abstract.md +87 -0
  475. package/pipelines/analogy/03-search.md +135 -0
  476. package/pipelines/analogy/04-transfer.md +101 -0
  477. package/pipelines/analogy/05-validate.md +106 -0
  478. package/pipelines/analogy/CHAIN.md +56 -0
  479. package/pipelines/discovery/01-explore-domains.md +44 -0
  480. package/pipelines/discovery/02-think-hats.md +50 -0
  481. package/pipelines/discovery/03-analyze-needs.md +54 -0
  482. package/pipelines/discovery/CHAIN.md +37 -0
  483. package/pipelines/thesis/01-structure-argument.md +45 -0
  484. package/pipelines/thesis/02-challenge-assumptions.md +48 -0
  485. package/pipelines/thesis/03-build-thesis.md +54 -0
  486. package/pipelines/thesis/CHAIN.md +37 -0
  487. package/references/brain/causal-directives.md +91 -0
  488. package/references/brain/causal-enrichment.cypher +165 -0
  489. package/references/brain/command-triggers-schema.md +226 -0
  490. package/references/brain/graph-architecture.md +317 -0
  491. package/references/brain/query-patterns.md +460 -0
  492. package/references/brain/room-hierarchy-schema.md +218 -0
  493. package/references/brain/schema.md +76 -0
  494. package/references/capability-radar/capabilities-index.md +241 -0
  495. package/references/capability-radar/changelog-cache.md +81 -0
  496. package/references/causal/causal-schema.md +103 -0
  497. package/references/design/email-template-standard.md +155 -0
  498. package/references/design/graph-visualization-standard.md +178 -0
  499. package/references/document-generation.md +179 -0
  500. package/references/hsi/HSI-TOOLS-REFERENCE.md +222 -0
  501. package/references/import-config.md +141 -0
  502. package/references/integrations/detection-patterns.md +101 -0
  503. package/references/meeting/artifact-template.md +377 -0
  504. package/references/meeting/cross-meeting-intelligence.md +216 -0
  505. package/references/meeting/cross-relationship-patterns.md +202 -0
  506. package/references/meeting/live-join-interface.md +244 -0
  507. package/references/meeting/section-mapping.md +192 -0
  508. package/references/meeting/segment-classification.md +258 -0
  509. package/references/meeting/speaker-profile-template.md +219 -0
  510. package/references/meeting/summary-template.md +348 -0
  511. package/references/meeting/transcript-patterns.md +226 -0
  512. package/references/methodology/analyze-needs.md +135 -0
  513. package/references/methodology/analyze-systems.md +121 -0
  514. package/references/methodology/analyze-timing.md +149 -0
  515. package/references/methodology/beautiful-question.md +109 -0
  516. package/references/methodology/build-knowledge.md +161 -0
  517. package/references/methodology/build-thesis.md +237 -0
  518. package/references/methodology/challenge-assumptions.md +127 -0
  519. package/references/methodology/diagnose.md +169 -0
  520. package/references/methodology/dominant-designs.md +212 -0
  521. package/references/methodology/explore-domains.md +147 -0
  522. package/references/methodology/explore-futures.md +163 -0
  523. package/references/methodology/explore-trends.md +129 -0
  524. package/references/methodology/find-bottlenecks.md +131 -0
  525. package/references/methodology/grade.md +211 -0
  526. package/references/methodology/index.md +97 -0
  527. package/references/methodology/leadership.md +200 -0
  528. package/references/methodology/lean-canvas.md +116 -0
  529. package/references/methodology/macro-trends.md +192 -0
  530. package/references/methodology/map-unknowns.md +137 -0
  531. package/references/methodology/mullins-7-domains.md +104 -0
  532. package/references/methodology/problem-types.md +65 -0
  533. package/references/methodology/root-cause.md +178 -0
  534. package/references/methodology/sapphire-encoding.md +355 -0
  535. package/references/methodology/scenario-plan.md +178 -0
  536. package/references/methodology/score-innovation.md +154 -0
  537. package/references/methodology/structure-argument.md +158 -0
  538. package/references/methodology/systems-thinking.md +159 -0
  539. package/references/methodology/think-hats.md +147 -0
  540. package/references/methodology/triz-matrix.json +751 -0
  541. package/references/methodology/triz-principles.md +501 -0
  542. package/references/methodology/user-needs.md +199 -0
  543. package/references/methodology/validate.md +163 -0
  544. package/references/methodology/value-proposition.md +244 -0
  545. package/references/opportunities/funding-lifecycle.md +103 -0
  546. package/references/opportunities/grant-api-patterns.md +99 -0
  547. package/references/opportunities/opportunity-template.md +84 -0
  548. package/references/personality/assessment-philosophy.md +72 -0
  549. package/references/personality/lexicon.md +100 -0
  550. package/references/personality/persona-chains.md +56 -0
  551. package/references/personality/pws-lexicon-full.md +499 -0
  552. package/references/personality/voice-dna.md +156 -0
  553. package/references/personas/hat-perspectives.md +76 -0
  554. package/references/personas/persona-template.md +63 -0
  555. package/references/pipeline/act-output-contract.md +88 -0
  556. package/references/pipeline/chains-index.md +39 -0
  557. package/references/pws-profile-generation.md +79 -0
  558. package/references/reasoning/reasoning-schema.md +143 -0
  559. package/references/reasoning/reasoning-template.md +68 -0
  560. package/references/reasoning/run-template.md +38 -0
  561. package/references/research/RESEARCH_14_CLAUDE_CODE_SOURCE_ARCHITECTURE.md +209 -0
  562. package/references/research/RESEARCH_15_V1.8_OPTIMIZATION_JTBD.md +375 -0
  563. package/references/research/RESEARCH_16_NATIVE_FIRST_PLUGIN_ARCHITECTURE.md +575 -0
  564. package/references/research/RESEARCH_17_MCP_UI_FRAMEWORKS.md +272 -0
  565. package/references/taxonomy/TAXONOMY.md +192 -0
  566. package/references/templates/MINTO.md +36 -0
  567. package/references/user-research/2026-04-05-leah-lawrence-session.md +202 -0
  568. package/references/vault-kit/README.md +35 -0
  569. package/references/vault-kit/app.json +12 -0
  570. package/references/vault-kit/appearance.json +12 -0
  571. package/references/vault-kit/graph.json +35 -0
  572. package/references/vault-kit/snippets/mindrian-destijl.css +297 -0
  573. package/references/vault-kit/templates/new-artifact.md +37 -0
  574. package/references/vault-kit/templates/new-meeting-note.md +35 -0
  575. package/references/vault-kit/templates/new-team-profile.md +29 -0
  576. package/references/vault-kit/templates/new-xref.md +35 -0
  577. package/references/visual/symbol-system.md +151 -0
  578. package/skills/MOSDeckEngine/SKILL.md +325 -0
  579. package/skills/brain-connector/SKILL.md +114 -0
  580. package/skills/context-engine/SKILL.md +147 -0
  581. package/skills/conversation-mode/SKILL.md +102 -0
  582. package/skills/larry-personality/SKILL.md +219 -0
  583. package/skills/larry-personality/framework-chains.md +92 -0
  584. package/skills/larry-personality/mode-engine.md +185 -0
  585. package/skills/mullins-scaffold/SKILL.md +61 -0
  586. package/skills/mullins-scaffold/scaffold.json +146 -0
  587. package/skills/pws-methodology/SKILL.md +49 -0
  588. package/skills/room-passive/SKILL.md +165 -0
  589. package/skills/room-proactive/SKILL.md +250 -0
  590. package/skills/ui-system/SKILL.md +277 -0
@@ -0,0 +1,722 @@
1
+ #!/usr/bin/env node
2
+ 'use strict';
3
+
4
+ /*
5
+ * Copyright (c) 2026 Mindrian. BSL 1.1.
6
+ *
7
+ * Phase 91-00 -- Navigation Engine Core fixture tests
8
+ * ====================================================
9
+ * The L5 Decision layer is the composition point that turns the shipped
10
+ * L1-L4 substrate into a routing decision surface. This file exercises
11
+ * lib/core/navigation-engine.cjs (decide) + lib/core/navigation-engine-shared.cjs
12
+ * (frozen tables + pure helpers) per the Phase 90-09 frozen interface
13
+ * contract v1 (.planning/research/navigation-engine-brain-interface.md).
14
+ *
15
+ * Test map (tests 1-33, one-to-one with the PLAN <behavior> blocks):
16
+ *
17
+ * Task 1 (shared helpers + frozen tables, pure functions, zero I/O):
18
+ * 1. STALENESS_MULTIPLIERS table matches Section 4 byte-for-byte
19
+ * 2. CANONICAL_VERBS frozen array length 10, Canon Part 3 order
20
+ * 3. SECTION_WEIGHTS required-section sum equals 1.0; optional excluded
21
+ * 4. applyStalenessMultiplier core rows (null, fresh, brain_offline 0.9)
22
+ * 5. applyStalenessMultiplier clamps + parse_failed override + unknown
23
+ * stale_reason fallback
24
+ * 6. emptyDecision shell shape
25
+ * 7. emptyDecisionTrace 8 Section 8 fields + 5 structural fields
26
+ * 8. resolveTierMode -> mode_a (brain non-null + reachable + parseable)
27
+ * 9. resolveTierMode -> mode_b (brain non-null + unreachable +
28
+ * brain_offline)
29
+ * 10. resolveTierMode -> tier_0 (brain null OR parse_failed OR
30
+ * unavailable)
31
+ *
32
+ * Task 2 (decide() orchestration + gates + triangulation):
33
+ * 11. decide on brain:null -> tier_0, weight 0.0, staleness 'absent'
34
+ * 12. decide on fresh+strong-pattern -> mode_a + RECOMMENDED rendered
35
+ * 13. decide on fresh+weak-pattern (0.65) -> RECOMMENDED suppressed,
36
+ * confidence still recorded
37
+ * 14. RECOMMENDED never renders in mode_b
38
+ * 15. RECOMMENDED never renders in tier_0
39
+ * 16. Attribution guard: brain.author !== 'brain' -> weight 0.0 + trace
40
+ * note
41
+ * 17. All 8 Section 8 trace fields present on every decision
42
+ * 18. brain_md_sections_consumed reflects non-null sections
43
+ * 19. Five-signal triangulation: weak-MINTO + strong-Brain
44
+ * 20. Five-signal triangulation: fresh-everything
45
+ * 21. Five-signal triangulation: wicked-detected -> soft-systems route
46
+ * 22. Brain-offline exemption: weight 0.9 + mode_b + RECOMMENDED off
47
+ * 23. parse_failed -> tier_0 + weight 0.0
48
+ * 24. governing_thought_changed -> weight 0.3
49
+ * 25. derivation_timeout -> weight 0.2
50
+ * 26. No cross-turn caching: tier mode re-resolves per turn
51
+ * 27. Per-turn cache boundary: within decide(), readQuadruple touched
52
+ * at most once per sectionPath
53
+ * 28. FORBIDDEN direct fs reads on BRAIN.md (grep)
54
+ * 29. FORBIDDEN Brain network calls (grep)
55
+ * 30. Performance budget: 5-section fixture under 800ms cold, 300ms warm
56
+ * 31. suppress_skills output array shape
57
+ * 32. persona_updates output struct shape
58
+ * 33. offer_next_step noise gate (max 1 per turn)
59
+ *
60
+ * Registered in lib/memory/run-feynman-tests.cjs.
61
+ */
62
+
63
+ const assert = require('node:assert/strict');
64
+ const fs = require('node:fs');
65
+ const path = require('node:path');
66
+
67
+ const REPO = path.resolve(__dirname, '..', '..');
68
+ const SHARED_PATH = path.join(REPO, 'lib/core/navigation-engine-shared.cjs');
69
+ const ENGINE_PATH = path.join(REPO, 'lib/core/navigation-engine.cjs');
70
+
71
+ // Lazy require so the shared-helper-only tests can run before the engine
72
+ // module exists (during Task 1 RED). Engine tests require the engine file.
73
+ function requireShared() {
74
+ return require(SHARED_PATH);
75
+ }
76
+ function requireEngine() {
77
+ return require(ENGINE_PATH);
78
+ }
79
+
80
+ // ---------- Test scaffolding ----------
81
+
82
+ let passed = 0;
83
+ let failed = 0;
84
+ function run(name, fn) {
85
+ try {
86
+ fn();
87
+ process.stdout.write('ok ' + name + '\n');
88
+ passed += 1;
89
+ } catch (err) {
90
+ process.stderr.write('FAIL ' + name + '\n');
91
+ process.stderr.write(String(err && err.stack ? err.stack : err) + '\n');
92
+ failed += 1;
93
+ }
94
+ }
95
+
96
+ // ---------- Fixture builders ----------
97
+
98
+ function makeBrain(overrides) {
99
+ const base = {
100
+ exists: true,
101
+ section: 'market-analysis',
102
+ brain_generated_at: '2026-04-20T12:00:00Z',
103
+ brain_graph_version: 1,
104
+ governing_thought_hash: 'sha256:abc123',
105
+ staleness: 'fresh',
106
+ stale_reason: null,
107
+ author: 'brain',
108
+ confidence_baseline: 0.5,
109
+ parse_failed: false,
110
+ sections: {
111
+ pattern_matches: null,
112
+ framework_chain_predictions: null,
113
+ cross_domain_analogies: null,
114
+ wicked_indicators: null,
115
+ unfilled_opportunity_matches: null,
116
+ assessment_thinking_chain_position: null,
117
+ problemtype_classification: null,
118
+ flagged_contradictions_xroom: null,
119
+ hsi_signals: null,
120
+ },
121
+ flagged_weaknesses: [],
122
+ };
123
+ return Object.assign({}, base, overrides || {});
124
+ }
125
+
126
+ function makeQuadruple(overrides) {
127
+ const base = {
128
+ room: { exists: true, identity_text: 'market analysis section', references: [] },
129
+ state: { exists: true, artifact_count: 3, completeness_score: 0.6 },
130
+ reasoning: {
131
+ exists: true,
132
+ governing_thought: 'Customers will pay a premium for X',
133
+ reasoning_health_score: 0.7,
134
+ is_stale: false,
135
+ arguments: [],
136
+ },
137
+ brain: makeBrain(),
138
+ };
139
+ return Object.assign({}, base, overrides || {});
140
+ }
141
+
142
+ function makeTurn(overrides) {
143
+ return Object.assign(
144
+ {
145
+ userText: 'help me think about market sizing',
146
+ sectionPath: '/tmp/fixture-room/market-analysis',
147
+ sessionId: 'test-session-1',
148
+ },
149
+ overrides || {}
150
+ );
151
+ }
152
+
153
+ function makeContext(overrides) {
154
+ return Object.assign(
155
+ {
156
+ quadruple: makeQuadruple(),
157
+ brainAvailable: true,
158
+ userPersona: { archetype: 'Founder', problem_type: 'IDP', venture_stage: 'discovery' },
159
+ intentSignal: { intent: 'analyze', confidence: 0.6 },
160
+ },
161
+ overrides || {}
162
+ );
163
+ }
164
+
165
+ // =========================================================
166
+ // Task 1 -- shared helpers + frozen tables (Tests 1..10)
167
+ // =========================================================
168
+
169
+ run('Test 1: STALENESS_MULTIPLIERS table matches Section 4 byte-for-byte', () => {
170
+ const { STALENESS_MULTIPLIERS } = requireShared();
171
+ // Each row checked against the Section 4.1 multiplier table.
172
+ // Key shape: stale_reason string OR sentinel keys for null/fresh/unavailable.
173
+ assert.equal(STALENESS_MULTIPLIERS.null, 0.0);
174
+ assert.equal(STALENESS_MULTIPLIERS.fresh, 1.0);
175
+ assert.equal(STALENESS_MULTIPLIERS.age_exceeded, 0.7);
176
+ assert.equal(STALENESS_MULTIPLIERS.governing_thought_changed, 0.3);
177
+ assert.equal(STALENESS_MULTIPLIERS.brain_graph_version_mismatch, 0.5);
178
+ assert.equal(STALENESS_MULTIPLIERS.brain_offline, 0.9);
179
+ assert.equal(STALENESS_MULTIPLIERS.derivation_timeout, 0.2);
180
+ assert.equal(STALENESS_MULTIPLIERS.parse_failed, 0.0);
181
+ assert.equal(STALENESS_MULTIPLIERS.unavailable, 0.0);
182
+ assert.equal(Object.isFrozen(STALENESS_MULTIPLIERS), true);
183
+ });
184
+
185
+ run('Test 2: CANONICAL_VERBS frozen array length 10 in Canon Part 3 order', () => {
186
+ const { CANONICAL_VERBS } = requireShared();
187
+ assert.equal(Array.isArray(CANONICAL_VERBS), true);
188
+ assert.equal(CANONICAL_VERBS.length, 10);
189
+ assert.deepEqual(CANONICAL_VERBS, [
190
+ 'Run Methodology',
191
+ 'Reformulate',
192
+ 'Spawn Sub-Agent',
193
+ 'Navigate Graph',
194
+ "Devil's Advocate",
195
+ 'Scenario Plan',
196
+ 'Synthesize',
197
+ 'Bank Opportunity',
198
+ 'Defer',
199
+ 'Free-Text',
200
+ ]);
201
+ assert.equal(Object.isFrozen(CANONICAL_VERBS), true);
202
+ });
203
+
204
+ run('Test 3: SECTION_WEIGHTS required sum to 1.0; optional excluded', () => {
205
+ const { SECTION_WEIGHTS } = requireShared();
206
+ // Required sections (Section 3.2 weights):
207
+ const requiredSum =
208
+ SECTION_WEIGHTS.pattern_matches +
209
+ SECTION_WEIGHTS.framework_chain_predictions +
210
+ SECTION_WEIGHTS.cross_domain_analogies +
211
+ SECTION_WEIGHTS.wicked_indicators +
212
+ SECTION_WEIGHTS.unfilled_opportunity_matches +
213
+ SECTION_WEIGHTS.assessment_thinking_chain_position +
214
+ SECTION_WEIGHTS.problemtype_classification;
215
+ // Floating-point sum is exact: 0.35 + 0.20 + 0.15 + 0.10 + 0.10 + 0.05 + 0.05 = 1.00.
216
+ assert.equal(Math.abs(requiredSum - 1.0) < 1e-9, true,
217
+ 'required sections must sum to 1.0, got ' + requiredSum);
218
+ // Optional sections are explicitly recorded as 0 (caveat surface only).
219
+ assert.equal(SECTION_WEIGHTS.flagged_contradictions_xroom, 0);
220
+ assert.equal(SECTION_WEIGHTS.hsi_signals, 0);
221
+ assert.equal(Object.isFrozen(SECTION_WEIGHTS), true);
222
+ });
223
+
224
+ run('Test 4: applyStalenessMultiplier core rows', () => {
225
+ const { applyStalenessMultiplier } = requireShared();
226
+ assert.equal(applyStalenessMultiplier(null), 0.0);
227
+ assert.equal(applyStalenessMultiplier({ staleness: 'fresh' }), 1.0);
228
+ assert.equal(
229
+ applyStalenessMultiplier({ staleness: 'stale', stale_reason: 'brain_offline' }),
230
+ 0.9
231
+ );
232
+ });
233
+
234
+ run('Test 5: applyStalenessMultiplier clamps + parse_failed + unknown reason', () => {
235
+ const { applyStalenessMultiplier } = requireShared();
236
+ // parse_failed override regardless of staleness label
237
+ assert.equal(applyStalenessMultiplier({ parse_failed: true, staleness: 'fresh' }), 0.0);
238
+ // unknown stale_reason falls back to 0.0 conservatively
239
+ assert.equal(
240
+ applyStalenessMultiplier({ staleness: 'stale', stale_reason: 'invented_reason' }),
241
+ 0.0
242
+ );
243
+ // unavailable -> 0.0 regardless of stale_reason
244
+ assert.equal(
245
+ applyStalenessMultiplier({ staleness: 'unavailable', stale_reason: null }),
246
+ 0.0
247
+ );
248
+ // Result always within [0, 1]
249
+ const out = applyStalenessMultiplier({ staleness: 'fresh' });
250
+ assert.equal(out >= 0.0 && out <= 1.0, true);
251
+ });
252
+
253
+ run('Test 6: emptyDecision returns expected shell', () => {
254
+ const { emptyDecision } = requireShared();
255
+ const d = emptyDecision();
256
+ assert.equal(d.fire_skill, null);
257
+ assert.equal(d.offer_next_step, null);
258
+ assert.deepEqual(d.suppress_skills, []);
259
+ assert.equal(d.persona_updates, null);
260
+ assert.equal(typeof d.decision_trace, 'object');
261
+ assert.notEqual(d.decision_trace, null);
262
+ });
263
+
264
+ run('Test 7: emptyDecisionTrace has 8 Section 8 fields + 5 structural fields', () => {
265
+ const { emptyDecisionTrace } = requireShared();
266
+ const t = emptyDecisionTrace();
267
+ // 8 Section 8 required fields:
268
+ assert.equal('brain_md_version' in t, true);
269
+ assert.equal('brain_md_staleness' in t, true);
270
+ assert.equal('brain_md_stale_reason' in t, true);
271
+ assert.equal('brain_md_weight_applied' in t, true);
272
+ assert.equal('brain_md_recommended_confidence' in t, true);
273
+ assert.equal('brain_md_recommended_marker_rendered' in t, true);
274
+ assert.equal('brain_md_tier_mode' in t, true);
275
+ assert.equal('brain_md_sections_consumed' in t, true);
276
+ // 5 structural fields:
277
+ assert.equal('icm_scope' in t, true);
278
+ assert.equal('sql_signals' in t, true);
279
+ assert.equal('minto_reasoning' in t, true);
280
+ assert.equal('intent_persona' in t, true);
281
+ assert.equal('chosen_rationale' in t, true);
282
+ // Sentinels:
283
+ assert.equal(t.brain_md_tier_mode, 'tier_0');
284
+ assert.equal(t.brain_md_recommended_marker_rendered, false);
285
+ assert.equal(Array.isArray(t.brain_md_sections_consumed), true);
286
+ });
287
+
288
+ run('Test 8: resolveTierMode -> mode_a happy path', () => {
289
+ const { resolveTierMode } = requireShared();
290
+ const q = makeQuadruple();
291
+ assert.equal(resolveTierMode(q, true), 'mode_a');
292
+ });
293
+
294
+ run('Test 9: resolveTierMode -> mode_b on brain_offline + unreachable', () => {
295
+ const { resolveTierMode } = requireShared();
296
+ const q = makeQuadruple({
297
+ brain: makeBrain({ staleness: 'stale', stale_reason: 'brain_offline' }),
298
+ });
299
+ assert.equal(resolveTierMode(q, false), 'mode_b');
300
+ });
301
+
302
+ run('Test 10: resolveTierMode -> tier_0 on null / parse_failed / unavailable', () => {
303
+ const { resolveTierMode } = requireShared();
304
+ // brain === null
305
+ assert.equal(resolveTierMode(makeQuadruple({ brain: null }), true), 'tier_0');
306
+ // parse_failed === true
307
+ assert.equal(
308
+ resolveTierMode(makeQuadruple({ brain: makeBrain({ parse_failed: true }) }), true),
309
+ 'tier_0'
310
+ );
311
+ // staleness === 'unavailable'
312
+ assert.equal(
313
+ resolveTierMode(makeQuadruple({ brain: makeBrain({ staleness: 'unavailable' }) }), true),
314
+ 'tier_0'
315
+ );
316
+ });
317
+
318
+ // =========================================================
319
+ // Task 2 -- decide() orchestration + gates (Tests 11..33)
320
+ // =========================================================
321
+ // These tests REQUIRE lib/core/navigation-engine.cjs to exist. During
322
+ // Task 1 RED they are skipped (file does not exist yet); Task 2 RED
323
+ // re-runs them and they must fail; Task 2 GREEN they pass.
324
+
325
+ function engineLoadable() {
326
+ return fs.existsSync(ENGINE_PATH);
327
+ }
328
+
329
+ if (engineLoadable()) {
330
+ run('Test 11: decide on brain:null -> tier_0, weight 0.0, absent', () => {
331
+ const { decide } = requireEngine();
332
+ const ctx = makeContext({ quadruple: makeQuadruple({ brain: null }) });
333
+ const d = decide(makeTurn(), ctx);
334
+ assert.equal(d.decision_trace.brain_md_tier_mode, 'tier_0');
335
+ assert.equal(d.decision_trace.brain_md_weight_applied, 0.0);
336
+ assert.equal(d.decision_trace.brain_md_staleness, 'absent');
337
+ assert.equal(/Tier 0 fallback/i.test(d.decision_trace.chosen_rationale || ''), true);
338
+ assert.equal(d.fire_skill, null);
339
+ });
340
+
341
+ run('Test 12: fresh + strong pattern_matches (0.85) -> RECOMMENDED rendered', () => {
342
+ const { decide } = requireEngine();
343
+ const ctx = makeContext({
344
+ quadruple: makeQuadruple({
345
+ brain: makeBrain({
346
+ sections: Object.assign({}, makeBrain().sections, {
347
+ pattern_matches: {
348
+ body: '- Run Methodology (confidence: 0.85, source: SWOT)',
349
+ tokens_estimate: 12,
350
+ },
351
+ }),
352
+ }),
353
+ }),
354
+ });
355
+ const d = decide(makeTurn(), ctx);
356
+ assert.equal(d.decision_trace.brain_md_tier_mode, 'mode_a');
357
+ assert.equal(d.decision_trace.brain_md_recommended_marker_rendered, true);
358
+ assert.equal(d.decision_trace.brain_md_recommended_confidence, 0.85);
359
+ });
360
+
361
+ run('Test 13: weak pattern (0.65) -> RECOMMENDED suppressed, confidence recorded', () => {
362
+ const { decide } = requireEngine();
363
+ const ctx = makeContext({
364
+ quadruple: makeQuadruple({
365
+ brain: makeBrain({
366
+ sections: Object.assign({}, makeBrain().sections, {
367
+ pattern_matches: {
368
+ body: '- Run Methodology (confidence: 0.65, source: PESTLE)',
369
+ tokens_estimate: 12,
370
+ },
371
+ }),
372
+ }),
373
+ }),
374
+ });
375
+ const d = decide(makeTurn(), ctx);
376
+ assert.equal(d.decision_trace.brain_md_recommended_marker_rendered, false);
377
+ assert.equal(d.decision_trace.brain_md_recommended_confidence, 0.65);
378
+ });
379
+
380
+ run('Test 14: RECOMMENDED never renders in mode_b', () => {
381
+ const { decide } = requireEngine();
382
+ const ctx = makeContext({
383
+ brainAvailable: false,
384
+ quadruple: makeQuadruple({
385
+ brain: makeBrain({
386
+ staleness: 'stale',
387
+ stale_reason: 'brain_offline',
388
+ sections: Object.assign({}, makeBrain().sections, {
389
+ pattern_matches: {
390
+ body: '- Run Methodology (confidence: 0.95, source: BMC)',
391
+ tokens_estimate: 12,
392
+ },
393
+ }),
394
+ }),
395
+ }),
396
+ });
397
+ const d = decide(makeTurn(), ctx);
398
+ assert.equal(d.decision_trace.brain_md_tier_mode, 'mode_b');
399
+ assert.equal(d.decision_trace.brain_md_recommended_marker_rendered, false);
400
+ assert.equal(/mode_b suppresses RECOMMENDED/i.test(d.decision_trace.chosen_rationale || ''), true);
401
+ });
402
+
403
+ run('Test 15: RECOMMENDED never renders in tier_0', () => {
404
+ const { decide } = requireEngine();
405
+ const d = decide(
406
+ makeTurn(),
407
+ makeContext({ quadruple: makeQuadruple({ brain: null }) })
408
+ );
409
+ assert.equal(d.decision_trace.brain_md_tier_mode, 'tier_0');
410
+ assert.equal(d.decision_trace.brain_md_recommended_marker_rendered, false);
411
+ });
412
+
413
+ run('Test 16: attribution guard demotes non-brain author to weight 0.0', () => {
414
+ const { decide } = requireEngine();
415
+ const ctx = makeContext({
416
+ quadruple: makeQuadruple({
417
+ brain: makeBrain({
418
+ author: 'human',
419
+ sections: Object.assign({}, makeBrain().sections, {
420
+ pattern_matches: {
421
+ body: '- Run Methodology (confidence: 0.9, source: forged)',
422
+ tokens_estimate: 12,
423
+ },
424
+ }),
425
+ }),
426
+ }),
427
+ });
428
+ const d = decide(makeTurn(), ctx);
429
+ assert.equal(d.decision_trace.brain_md_weight_applied, 0.0);
430
+ assert.equal(d.decision_trace.brain_md_recommended_marker_rendered, false);
431
+ assert.equal(/canon_part_2_attribution_breach/i.test(d.decision_trace.chosen_rationale || ''), true);
432
+ });
433
+
434
+ run('Test 17: all 8 Section 8 trace fields present on every decision', () => {
435
+ const { decide } = requireEngine();
436
+ const fixtures = [
437
+ makeContext({ quadruple: makeQuadruple({ brain: null }) }),
438
+ makeContext(),
439
+ makeContext({
440
+ brainAvailable: false,
441
+ quadruple: makeQuadruple({
442
+ brain: makeBrain({ staleness: 'stale', stale_reason: 'brain_offline' }),
443
+ }),
444
+ }),
445
+ ];
446
+ const required = [
447
+ 'brain_md_version',
448
+ 'brain_md_staleness',
449
+ 'brain_md_stale_reason',
450
+ 'brain_md_weight_applied',
451
+ 'brain_md_recommended_confidence',
452
+ 'brain_md_recommended_marker_rendered',
453
+ 'brain_md_tier_mode',
454
+ 'brain_md_sections_consumed',
455
+ ];
456
+ for (const ctx of fixtures) {
457
+ const d = decide(makeTurn(), ctx);
458
+ for (const k of required) {
459
+ assert.equal(k in d.decision_trace, true, 'missing trace field: ' + k);
460
+ }
461
+ }
462
+ });
463
+
464
+ run('Test 18: brain_md_sections_consumed reflects non-null sections only', () => {
465
+ const { decide } = requireEngine();
466
+ const ctx = makeContext({
467
+ quadruple: makeQuadruple({
468
+ brain: makeBrain({
469
+ sections: Object.assign({}, makeBrain().sections, {
470
+ pattern_matches: { body: '- ok (confidence: 0.8)', tokens_estimate: 4 },
471
+ }),
472
+ }),
473
+ }),
474
+ });
475
+ const d = decide(makeTurn(), ctx);
476
+ assert.deepEqual(d.decision_trace.brain_md_sections_consumed, ['pattern_matches']);
477
+ });
478
+
479
+ run('Test 19: weak-MINTO + strong-Brain -> trace captures both', () => {
480
+ const { decide } = requireEngine();
481
+ const ctx = makeContext({
482
+ quadruple: makeQuadruple({
483
+ reasoning: {
484
+ exists: true,
485
+ governing_thought: 'shaky claim',
486
+ reasoning_health_score: 0.3,
487
+ is_stale: false,
488
+ arguments: [],
489
+ },
490
+ brain: makeBrain({
491
+ sections: Object.assign({}, makeBrain().sections, {
492
+ pattern_matches: { body: '- Run Methodology (confidence: 0.9)', tokens_estimate: 8 },
493
+ }),
494
+ }),
495
+ }),
496
+ });
497
+ const d = decide(makeTurn(), ctx);
498
+ assert.equal(d.decision_trace.minto_reasoning.reasoning_health_score, 0.3);
499
+ assert.equal(d.decision_trace.brain_md_weight_applied, 1.0);
500
+ });
501
+
502
+ run('Test 20: fresh-everything -> all five signals captured + non-null fire_skill', () => {
503
+ const { decide } = requireEngine();
504
+ const ctx = makeContext({
505
+ quadruple: makeQuadruple({
506
+ reasoning: {
507
+ exists: true,
508
+ governing_thought: 'firm claim',
509
+ reasoning_health_score: 0.9,
510
+ is_stale: false,
511
+ arguments: [],
512
+ },
513
+ brain: makeBrain({
514
+ sections: Object.assign({}, makeBrain().sections, {
515
+ pattern_matches: { body: '- Run Methodology (confidence: 0.9, source: SWOT)', tokens_estimate: 10 },
516
+ framework_chain_predictions: { body: '- SWOT FEEDS_INTO Porter Five Forces', tokens_estimate: 8 },
517
+ }),
518
+ }),
519
+ }),
520
+ });
521
+ const d = decide(makeTurn(), ctx);
522
+ assert.notEqual(d.decision_trace.icm_scope, null);
523
+ assert.notEqual(d.decision_trace.sql_signals, null);
524
+ assert.notEqual(d.decision_trace.minto_reasoning, null);
525
+ assert.notEqual(d.decision_trace.intent_persona, null);
526
+ assert.equal(d.decision_trace.brain_md_tier_mode, 'mode_a');
527
+ assert.notEqual(d.fire_skill, null);
528
+ });
529
+
530
+ run('Test 21: wicked_indicators (score 9) -> soft-systems route', () => {
531
+ const { decide } = requireEngine();
532
+ const ctx = makeContext({
533
+ quadruple: makeQuadruple({
534
+ brain: makeBrain({
535
+ sections: Object.assign({}, makeBrain().sections, {
536
+ wicked_indicators: { body: '- wicked_score: 9 stakeholders divergent', tokens_estimate: 8 },
537
+ }),
538
+ }),
539
+ }),
540
+ });
541
+ const d = decide(makeTurn(), ctx);
542
+ assert.equal(/soft.?systems/i.test(d.fire_skill || ''), true,
543
+ 'expected soft-systems route, got ' + d.fire_skill);
544
+ assert.equal(/wicked_escalation/i.test(d.decision_trace.chosen_rationale || ''), true);
545
+ });
546
+
547
+ run('Test 22: brain-offline exemption -> weight 0.9, mode_b, RECOMMENDED off', () => {
548
+ const { decide } = requireEngine();
549
+ const ctx = makeContext({
550
+ brainAvailable: false,
551
+ quadruple: makeQuadruple({
552
+ brain: makeBrain({
553
+ staleness: 'stale',
554
+ stale_reason: 'brain_offline',
555
+ sections: Object.assign({}, makeBrain().sections, {
556
+ pattern_matches: { body: '- Run Methodology (confidence: 0.9)', tokens_estimate: 8 },
557
+ }),
558
+ }),
559
+ }),
560
+ });
561
+ const d = decide(makeTurn(), ctx);
562
+ assert.equal(d.decision_trace.brain_md_weight_applied, 0.9);
563
+ assert.equal(d.decision_trace.brain_md_tier_mode, 'mode_b');
564
+ assert.equal(d.decision_trace.brain_md_recommended_marker_rendered, false);
565
+ });
566
+
567
+ run('Test 23: parse_failed -> tier_0, weight 0.0', () => {
568
+ const { decide } = requireEngine();
569
+ const ctx = makeContext({
570
+ quadruple: makeQuadruple({ brain: makeBrain({ parse_failed: true }) }),
571
+ });
572
+ const d = decide(makeTurn(), ctx);
573
+ assert.equal(d.decision_trace.brain_md_tier_mode, 'tier_0');
574
+ assert.equal(d.decision_trace.brain_md_weight_applied, 0.0);
575
+ assert.equal(/brain_parse_failed/i.test(d.decision_trace.chosen_rationale || ''), true);
576
+ });
577
+
578
+ run('Test 24: governing_thought_changed -> weight 0.3', () => {
579
+ const { decide } = requireEngine();
580
+ const ctx = makeContext({
581
+ quadruple: makeQuadruple({
582
+ brain: makeBrain({ staleness: 'stale', stale_reason: 'governing_thought_changed' }),
583
+ }),
584
+ });
585
+ const d = decide(makeTurn(), ctx);
586
+ assert.equal(d.decision_trace.brain_md_weight_applied, 0.3);
587
+ assert.equal(d.decision_trace.brain_md_tier_mode, 'mode_a');
588
+ });
589
+
590
+ run('Test 25: derivation_timeout -> weight 0.2', () => {
591
+ const { decide } = requireEngine();
592
+ const ctx = makeContext({
593
+ quadruple: makeQuadruple({
594
+ brain: makeBrain({ staleness: 'stale', stale_reason: 'derivation_timeout' }),
595
+ }),
596
+ });
597
+ const d = decide(makeTurn(), ctx);
598
+ assert.equal(d.decision_trace.brain_md_weight_applied, 0.2);
599
+ });
600
+
601
+ run('Test 26: no cross-turn caching: tier mode re-resolves per turn', () => {
602
+ const { decide } = requireEngine();
603
+ const turn = makeTurn();
604
+ const ctx1 = makeContext({ brainAvailable: true });
605
+ const d1 = decide(turn, ctx1);
606
+ assert.equal(d1.decision_trace.brain_md_tier_mode, 'mode_a');
607
+ const ctx2 = makeContext({
608
+ brainAvailable: false,
609
+ quadruple: makeQuadruple({
610
+ brain: makeBrain({ staleness: 'stale', stale_reason: 'brain_offline' }),
611
+ }),
612
+ });
613
+ const d2 = decide(turn, ctx2);
614
+ assert.equal(d2.decision_trace.brain_md_tier_mode, 'mode_b');
615
+ });
616
+
617
+ run('Test 27: per-turn cache bound (no cross-turn leakage of quadruple)', () => {
618
+ // The cache check is structural: decide() must not retain quadruple
619
+ // across calls. We test by mutating quadruple between calls and
620
+ // verifying the second call sees the new shape, not a cached one.
621
+ const { decide } = requireEngine();
622
+ const turn = makeTurn();
623
+ const q1 = makeQuadruple();
624
+ const d1 = decide(turn, makeContext({ quadruple: q1 }));
625
+ assert.equal(d1.decision_trace.brain_md_tier_mode, 'mode_a');
626
+ const q2 = makeQuadruple({ brain: null });
627
+ const d2 = decide(turn, makeContext({ quadruple: q2 }));
628
+ assert.equal(d2.decision_trace.brain_md_tier_mode, 'tier_0');
629
+ });
630
+
631
+ run('Test 28: FORBIDDEN direct fs reads on BRAIN.md (grep)', () => {
632
+ const src = fs.readFileSync(ENGINE_PATH, 'utf8');
633
+ // Forbidden: any fs.readFileSync or fs.readFile referencing BRAIN.md
634
+ const re = /fs\s*\.\s*(readFileSync|readFile|promises\.readFile)\s*\([^)]*BRAIN\.md/;
635
+ assert.equal(re.test(src), false,
636
+ 'navigation-engine.cjs MUST NOT read BRAIN.md directly; route through readQuadruple');
637
+ });
638
+
639
+ run('Test 29: FORBIDDEN Brain network calls (grep)', () => {
640
+ const src = fs.readFileSync(ENGINE_PATH, 'utf8');
641
+ // Forbidden: brain-client.query / search / smartSearch and any direct
642
+ // network egress to brain.mindrian.ai or fetch/curl for Brain.
643
+ const reBad = /brain[-_]?client\s*\.\s*(query|search|smartSearch)\b|brain\.mindrian\.ai|\bfetch\s*\(|\bcurl\b/;
644
+ // Allow brain-client.isAvailable and brain-client.schema (Section 9.3).
645
+ // Strip those allowed calls before scanning.
646
+ const filtered = src
647
+ .replace(/brain[-_]?client\s*\.\s*isAvailable\s*\([^)]*\)/g, '')
648
+ .replace(/brain[-_]?client\s*\.\s*schema\s*\([^)]*\)/g, '');
649
+ assert.equal(reBad.test(filtered), false,
650
+ 'navigation-engine.cjs MUST NOT call Brain network APIs from decision logic');
651
+ });
652
+
653
+ run('Test 30: performance budget on 5-section fixture', () => {
654
+ const { decide } = requireEngine();
655
+ // Build five distinct contexts (simulating five-section turn budget).
656
+ const sections = ['intro', 'market', 'tech', 'ops', 'finance'];
657
+ const contexts = sections.map((slug) =>
658
+ makeContext({
659
+ quadruple: makeQuadruple({
660
+ brain: makeBrain({
661
+ section: slug,
662
+ sections: Object.assign({}, makeBrain().sections, {
663
+ pattern_matches: { body: '- Run Methodology (confidence: 0.8)', tokens_estimate: 8 },
664
+ framework_chain_predictions: { body: '- A FEEDS_INTO B', tokens_estimate: 6 },
665
+ }),
666
+ }),
667
+ }),
668
+ })
669
+ );
670
+ const cold = process.hrtime.bigint();
671
+ for (const c of contexts) decide(makeTurn(), c);
672
+ const coldDur = Number(process.hrtime.bigint() - cold) / 1e6;
673
+ assert.equal(coldDur < 800, true, 'cold 5-section decide() exceeded 800ms: ' + coldDur + 'ms');
674
+ const warm = process.hrtime.bigint();
675
+ for (const c of contexts) decide(makeTurn(), c);
676
+ const warmDur = Number(process.hrtime.bigint() - warm) / 1e6;
677
+ assert.equal(warmDur < 300, true, 'warm 5-section decide() exceeded 300ms: ' + warmDur + 'ms');
678
+ });
679
+
680
+ run('Test 31: suppress_skills array shape', () => {
681
+ const { decide } = requireEngine();
682
+ const d = decide(makeTurn(), makeContext());
683
+ assert.equal(Array.isArray(d.suppress_skills), true);
684
+ });
685
+
686
+ run('Test 32: persona_updates struct shape (null when no update signal)', () => {
687
+ const { decide } = requireEngine();
688
+ const d = decide(makeTurn(), makeContext());
689
+ // null when no threshold crossed; struct when present.
690
+ if (d.persona_updates !== null) {
691
+ assert.equal(typeof d.persona_updates, 'object');
692
+ // Allowed keys per Phase 91 CONTEXT decide() signature.
693
+ const allowed = new Set(['archetype', 'problem_type', 'venture_stage']);
694
+ for (const k of Object.keys(d.persona_updates)) {
695
+ assert.equal(allowed.has(k), true, 'persona_updates has unexpected key: ' + k);
696
+ }
697
+ }
698
+ });
699
+
700
+ run('Test 33: offer_next_step noise gate (max 1 per turn)', () => {
701
+ const { decide } = requireEngine();
702
+ const d = decide(makeTurn(), makeContext());
703
+ // Either null OR a single object with command + reason.
704
+ if (d.offer_next_step !== null) {
705
+ assert.equal(typeof d.offer_next_step, 'object');
706
+ assert.equal('command' in d.offer_next_step, true);
707
+ assert.equal('reason' in d.offer_next_step, true);
708
+ }
709
+ });
710
+ } else {
711
+ // Engine module absent (Task 1 RED phase). Skip Task 2 tests with a
712
+ // single notice; do NOT count as failure (matches RED contract).
713
+ process.stdout.write('skip Task 2 tests: ' + path.relative(REPO, ENGINE_PATH) + ' not yet implemented\n');
714
+ }
715
+
716
+ // ---------- Final summary ----------
717
+
718
+ const total = passed + failed;
719
+ process.stdout.write(
720
+ '\nnavigation-engine-core: ' + passed + '/' + total + ' passed, ' + failed + ' failed\n'
721
+ );
722
+ process.exit(failed === 0 ? 0 : 1);