naive-framework 0.1.0 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (281) hide show
  1. package/AGENTS.md +2 -1
  2. package/docs/decisions/decisions.json +42 -0
  3. package/docs/reference/ROUTES.md +36 -37
  4. package/evidence/skills/fidelity-ladder-audit/ladder-census.mjs +2 -1
  5. package/governance/layer.json +2 -1
  6. package/governance/venture-template/roles/venture-roles.json +62 -0
  7. package/interface/acting.mjs +83 -0
  8. package/interface/adapters/claude-code.json +16 -0
  9. package/interface/adapters/codex.json +14 -0
  10. package/interface/adapters/cursor.json +16 -0
  11. package/interface/adapters.mjs +183 -0
  12. package/interface/api/server.mjs +4 -2
  13. package/interface/boundaries.mjs +219 -0
  14. package/interface/brand.mjs +215 -0
  15. package/interface/cli/freshness.mjs +59 -0
  16. package/interface/cli/guard.mjs +6 -0
  17. package/interface/cli/mentura.mjs +82 -11
  18. package/interface/cli/naive.mjs +44 -14
  19. package/interface/context.mjs +525 -0
  20. package/interface/demo/board.mjs +9 -8
  21. package/interface/demo/decision-harness.mjs +4 -3
  22. package/interface/dev/setup.mjs +84 -0
  23. package/interface/gate-hooks.mjs +37 -0
  24. package/interface/harness/evals.mjs +10 -9
  25. package/interface/harness/run.mjs +16 -15
  26. package/interface/instrumentation.mjs +87 -0
  27. package/interface/kit-boundary.mjs +73 -0
  28. package/interface/layer.json +8 -2
  29. package/interface/mcp/server.mjs +47 -7
  30. package/interface/package-routes.mjs +197 -0
  31. package/interface/provenance.mjs +28 -4
  32. package/interface/reads.mjs +174 -0
  33. package/interface/release-card.mjs +43 -0
  34. package/interface/router.mjs +110 -36
  35. package/interface/skills/naive-setup/SKILL.md +71 -0
  36. package/interface/skills/naive-setup/artifact.html +83 -0
  37. package/interface/vitals.mjs +116 -0
  38. package/interface/writes.mjs +190 -0
  39. package/knowledge/connectors/catalog.mjs +3 -2
  40. package/knowledge/connectors/filesystem.mjs +10 -5
  41. package/knowledge/connectors/index.mjs +3 -2
  42. package/knowledge/freshness.json +85 -0
  43. package/knowledge/layer.json +11 -3
  44. package/knowledge/templates/glossary.md +23 -0
  45. package/operations/layer.json +10 -3
  46. package/operations/marketing/check-public.mjs +7 -6
  47. package/operations/marketing/site/plan.mjs +4 -3
  48. package/operations/marketing/site/presence.mjs +4 -3
  49. package/operations/templates/marketing-brand.md +46 -0
  50. package/operations/templates/public-assets.md +39 -0
  51. package/package.json +3 -1
  52. package/packages/catalog.json +8 -0
  53. package/packages/customers/package.json +21 -1
  54. package/packages/deals/package.json +27 -0
  55. package/packages/decisions/package.json +12 -1
  56. package/packages/finance/frames/fund-without-ceding-control.frame.json +177 -0
  57. package/packages/foundation/package.json +8 -1
  58. package/packages/fund/package.json +48 -0
  59. package/packages/marketing/package.json +10 -4
  60. package/packages/money/package.json +24 -3
  61. package/packages/validation/package.json +13 -0
  62. package/platform/README.md +6 -0
  63. package/platform/adapter.json +15 -0
  64. package/platform/integrations/remote-image.mjs +79 -0
  65. package/platform/integrations/rho.mjs +9 -7
  66. package/platform/kits/README.md +8 -1
  67. package/platform/kits/agent-upgrade/kit.json +15 -0
  68. package/platform/kits/agent-upgrade/run.mjs +82 -0
  69. package/platform/kits/agent-upgrade/stand-in-agent.mjs +32 -0
  70. package/platform/kits/validation-kickoff/kit.json +29 -0
  71. package/platform/webhooks/server.mjs +70 -0
  72. package/purpose/frameworks/idea-map/ideamap.mjs +4 -2
  73. package/purpose/frameworks/idea-map/route.mjs +5 -4
  74. package/purpose/frameworks/product-anatomy/anatomy.mjs +2 -1
  75. package/schema/README.md +1 -0
  76. package/schema/access.mjs +58 -0
  77. package/schema/adapter.schema.json +33 -0
  78. package/schema/add-department.mjs +3 -2
  79. package/schema/assess-problem.mjs +3 -2
  80. package/schema/assets.mjs +213 -0
  81. package/schema/brand.mjs +374 -0
  82. package/schema/build-release.mjs +61 -0
  83. package/schema/caching.mjs +506 -0
  84. package/schema/check-assets.mjs +24 -0
  85. package/schema/check-brand.mjs +24 -0
  86. package/schema/check-config.mjs +111 -0
  87. package/schema/check-data-model.mjs +2 -1
  88. package/schema/check-data-packages.mjs +5 -4
  89. package/schema/check-execution.mjs +4 -3
  90. package/schema/check-nextjs-status.mjs +3 -1
  91. package/schema/check-people.mjs +84 -0
  92. package/schema/check-private.mjs +2 -1
  93. package/schema/check-ready.mjs +6 -5
  94. package/schema/check-sources.mjs +11 -2
  95. package/schema/check-where-used.mjs +4 -3
  96. package/schema/data-model.json +334 -8
  97. package/schema/deal.schema.json +80 -0
  98. package/schema/decision-analytics.mjs +2 -1
  99. package/schema/decisions.mjs +21 -12
  100. package/schema/examples/frames/judge.mjs +10 -0
  101. package/schema/examples/venture-type/jev.mjs +12 -0
  102. package/schema/examples/workspace/ventures/acme-labs/AGENTS.md +17 -0
  103. package/schema/examples/workspace/ventures/acme-labs/accounts/.gitkeep +0 -0
  104. package/schema/examples/workspace/ventures/acme-labs/decisions/discovery-goal.decision.json +23 -0
  105. package/schema/examples/workspace/ventures/acme-labs/decisions/statement-icp.decision.json +22 -0
  106. package/schema/examples/workspace/ventures/acme-labs/decisions/statement-problem.decision.json +22 -0
  107. package/schema/examples/workspace/ventures/acme-labs/decisions/venture.team.decision.json +37 -0
  108. package/schema/examples/workspace/ventures/acme-labs/knowledge/README.md +3 -0
  109. package/schema/examples/workspace/ventures/acme-labs/knowledge/sources.json +46 -0
  110. package/schema/examples/workspace/ventures/acme-labs/money/30000000-0000-4000-8000-000000000001.json +12 -0
  111. package/schema/examples/workspace/ventures/acme-labs/money/30000000-0000-4000-8000-000000000002.json +12 -0
  112. package/schema/examples/workspace/ventures/acme-labs/money/30000000-0000-4000-8000-000000000003.json +12 -0
  113. package/schema/examples/workspace/ventures/acme-labs/money/30000000-0000-4000-8000-000000000004.json +12 -0
  114. package/schema/examples/workspace/ventures/acme-labs/money/30000000-0000-4000-8000-000000000005.json +12 -0
  115. package/schema/examples/workspace/ventures/acme-labs/money/30000000-0000-4000-8000-000000000006.json +12 -0
  116. package/schema/examples/workspace/ventures/acme-labs/money/30000000-0000-4000-8000-000000000007.json +12 -0
  117. package/schema/examples/workspace/ventures/acme-labs/money/30000000-0000-4000-8000-000000000008.json +12 -0
  118. package/schema/examples/workspace/ventures/acme-labs/money/30000000-0000-4000-8000-000000000009.json +12 -0
  119. package/schema/examples/workspace/ventures/acme-labs/money/30000000-0000-4000-8000-000000000010.json +12 -0
  120. package/schema/examples/workspace/ventures/acme-labs/money/30000000-0000-4000-8000-000000000011.json +12 -0
  121. package/schema/examples/workspace/ventures/acme-labs/money/30000000-0000-4000-8000-000000000012.json +12 -0
  122. package/schema/examples/workspace/ventures/acme-labs/money/30000000-0000-4000-8000-000000000013.json +12 -0
  123. package/schema/examples/workspace/ventures/acme-labs/money/30000000-0000-4000-8000-000000000014.json +12 -0
  124. package/schema/examples/workspace/ventures/acme-labs/money/_streams/transactions/stream.json +5 -0
  125. package/schema/examples/workspace/ventures/acme-labs/money/_summary.json +30 -0
  126. package/schema/examples/workspace/ventures/acme-labs/operations/departments/customer-service/README.md +5 -0
  127. package/schema/examples/workspace/ventures/acme-labs/operations/departments/customer-service/department.json +5 -0
  128. package/schema/examples/workspace/ventures/acme-labs/operations/departments/finance/README.md +5 -0
  129. package/schema/examples/workspace/ventures/acme-labs/operations/departments/finance/department.json +5 -0
  130. package/schema/examples/workspace/ventures/acme-labs/operations/departments/human-capital/README.md +5 -0
  131. package/schema/examples/workspace/ventures/acme-labs/operations/departments/human-capital/department.json +5 -0
  132. package/schema/examples/workspace/ventures/acme-labs/operations/departments/it/README.md +5 -0
  133. package/schema/examples/workspace/ventures/acme-labs/operations/departments/it/department.json +5 -0
  134. package/schema/examples/workspace/ventures/acme-labs/operations/departments/marketing-and-sales/README.md +5 -0
  135. package/schema/examples/workspace/ventures/acme-labs/operations/departments/marketing-and-sales/department.json +5 -0
  136. package/schema/examples/workspace/ventures/acme-labs/operations/departments/products/README.md +5 -0
  137. package/schema/examples/workspace/ventures/acme-labs/operations/departments/products/department.json +5 -0
  138. package/schema/examples/workspace/ventures/acme-labs/operations/departments/strategy/README.md +5 -0
  139. package/schema/examples/workspace/ventures/acme-labs/operations/departments/strategy/department.json +5 -0
  140. package/schema/examples/workspace/ventures/acme-labs/people/founders/maya-chen.json +15 -0
  141. package/schema/examples/workspace/ventures/acme-labs/people/people.json +22 -0
  142. package/schema/examples/workspace/ventures/acme-labs/pillars/brand.md +5 -0
  143. package/schema/examples/workspace/ventures/acme-labs/pillars/business.md +5 -0
  144. package/schema/examples/workspace/ventures/acme-labs/pillars/product.md +5 -0
  145. package/schema/examples/workspace/ventures/acme-labs/products/.gitkeep +0 -0
  146. package/schema/examples/workspace/ventures/acme-labs/products/decisions/product.core-product.decision.json +28 -0
  147. package/schema/examples/workspace/ventures/acme-labs/products/decisions/product.exists.decision.json +38 -0
  148. package/schema/examples/workspace/ventures/acme-labs/products/decisions/product.flagship.decision.json +27 -0
  149. package/schema/examples/workspace/ventures/acme-labs/profile.md +12 -0
  150. package/schema/examples/workspace/ventures/acme-labs/provenance.jsonl +12 -0
  151. package/schema/examples/workspace/ventures/acme-labs/raw/dump.md +6 -0
  152. package/schema/examples/workspace/ventures/acme-labs/venture.json +27 -0
  153. package/schema/examples/workspace/ventures/northstar/AGENTS.md +17 -0
  154. package/schema/examples/workspace/ventures/northstar/accounts/.gitkeep +0 -0
  155. package/schema/examples/workspace/ventures/northstar/deals/ductline.json +8 -0
  156. package/schema/examples/workspace/ventures/northstar/deals/kilnworks.json +14 -0
  157. package/schema/examples/workspace/ventures/northstar/decisions/venture.team.decision.json +31 -0
  158. package/schema/examples/workspace/ventures/northstar/evidence/README.md +3 -0
  159. package/schema/examples/workspace/ventures/northstar/fund/decisions/terms.decision.json +27 -0
  160. package/schema/examples/workspace/ventures/northstar/fund/decisions/terms.first-close.decision.json +27 -0
  161. package/schema/examples/workspace/ventures/northstar/fund/decisions/thesis.decision.json +26 -0
  162. package/schema/examples/workspace/ventures/northstar/fund/terms.json +22 -0
  163. package/schema/examples/workspace/ventures/northstar/fund/thesis.json +22 -0
  164. package/schema/examples/workspace/ventures/northstar/governance/README.md +3 -0
  165. package/schema/examples/workspace/ventures/northstar/governance/departments/external-relationships/charter.md +5 -0
  166. package/schema/examples/workspace/ventures/northstar/governance/departments/external-relationships/department.json +5 -0
  167. package/schema/examples/workspace/ventures/northstar/governance/departments/finance/charter.md +5 -0
  168. package/schema/examples/workspace/ventures/northstar/governance/departments/finance/department.json +5 -0
  169. package/schema/examples/workspace/ventures/northstar/governance/departments/human-capital/charter.md +5 -0
  170. package/schema/examples/workspace/ventures/northstar/governance/departments/human-capital/department.json +5 -0
  171. package/schema/examples/workspace/ventures/northstar/governance/departments/risk-and-compliance/charter.md +5 -0
  172. package/schema/examples/workspace/ventures/northstar/governance/departments/risk-and-compliance/department.json +5 -0
  173. package/schema/examples/workspace/ventures/northstar/governance/departments/strategy/charter.md +5 -0
  174. package/schema/examples/workspace/ventures/northstar/governance/departments/strategy/department.json +5 -0
  175. package/schema/examples/workspace/ventures/northstar/knowledge/README.md +3 -0
  176. package/schema/examples/workspace/ventures/northstar/operations/README.md +3 -0
  177. package/schema/examples/workspace/ventures/northstar/operations/departments/external-relationships/README.md +5 -0
  178. package/schema/examples/workspace/ventures/northstar/operations/departments/external-relationships/department.json +5 -0
  179. package/schema/examples/workspace/ventures/northstar/operations/departments/finance/README.md +5 -0
  180. package/schema/examples/workspace/ventures/northstar/operations/departments/finance/department.json +5 -0
  181. package/schema/examples/workspace/ventures/northstar/operations/departments/human-capital/README.md +5 -0
  182. package/schema/examples/workspace/ventures/northstar/operations/departments/human-capital/department.json +5 -0
  183. package/schema/examples/workspace/ventures/northstar/operations/departments/risk-and-compliance/README.md +5 -0
  184. package/schema/examples/workspace/ventures/northstar/operations/departments/risk-and-compliance/department.json +5 -0
  185. package/schema/examples/workspace/ventures/northstar/operations/departments/strategy/README.md +5 -0
  186. package/schema/examples/workspace/ventures/northstar/operations/departments/strategy/department.json +5 -0
  187. package/schema/examples/workspace/ventures/northstar/people/founders/ana-ruiz.json +13 -0
  188. package/schema/examples/workspace/ventures/northstar/people/founders/ben-okafor.json +7 -0
  189. package/schema/examples/workspace/ventures/northstar/people/people.json +27 -0
  190. package/schema/examples/workspace/ventures/northstar/pillars/brand.md +5 -0
  191. package/schema/examples/workspace/ventures/northstar/pillars/business.md +5 -0
  192. package/schema/examples/workspace/ventures/northstar/pillars/product.md +5 -0
  193. package/schema/examples/workspace/ventures/northstar/portfolio/kilnworks/AGENTS.md +17 -0
  194. package/schema/examples/workspace/ventures/northstar/portfolio/kilnworks/accounts/.gitkeep +0 -0
  195. package/schema/examples/workspace/ventures/northstar/portfolio/kilnworks/decisions/venture.team.decision.json +31 -0
  196. package/schema/examples/workspace/ventures/northstar/portfolio/kilnworks/knowledge/README.md +3 -0
  197. package/schema/examples/workspace/ventures/northstar/portfolio/kilnworks/operations/departments/customer-service/README.md +5 -0
  198. package/schema/examples/workspace/ventures/northstar/portfolio/kilnworks/operations/departments/customer-service/department.json +5 -0
  199. package/schema/examples/workspace/ventures/northstar/portfolio/kilnworks/operations/departments/finance/README.md +5 -0
  200. package/schema/examples/workspace/ventures/northstar/portfolio/kilnworks/operations/departments/finance/department.json +5 -0
  201. package/schema/examples/workspace/ventures/northstar/portfolio/kilnworks/operations/departments/human-capital/README.md +5 -0
  202. package/schema/examples/workspace/ventures/northstar/portfolio/kilnworks/operations/departments/human-capital/department.json +5 -0
  203. package/schema/examples/workspace/ventures/northstar/portfolio/kilnworks/operations/departments/it/README.md +5 -0
  204. package/schema/examples/workspace/ventures/northstar/portfolio/kilnworks/operations/departments/it/department.json +5 -0
  205. package/schema/examples/workspace/ventures/northstar/portfolio/kilnworks/operations/departments/marketing-and-sales/README.md +5 -0
  206. package/schema/examples/workspace/ventures/northstar/portfolio/kilnworks/operations/departments/marketing-and-sales/department.json +5 -0
  207. package/schema/examples/workspace/ventures/northstar/portfolio/kilnworks/operations/departments/products/README.md +5 -0
  208. package/schema/examples/workspace/ventures/northstar/portfolio/kilnworks/operations/departments/products/department.json +5 -0
  209. package/schema/examples/workspace/ventures/northstar/portfolio/kilnworks/operations/departments/strategy/README.md +5 -0
  210. package/schema/examples/workspace/ventures/northstar/portfolio/kilnworks/operations/departments/strategy/department.json +5 -0
  211. package/schema/examples/workspace/ventures/northstar/portfolio/kilnworks/pillars/brand.md +5 -0
  212. package/schema/examples/workspace/ventures/northstar/portfolio/kilnworks/pillars/business.md +5 -0
  213. package/schema/examples/workspace/ventures/northstar/portfolio/kilnworks/pillars/product.md +5 -0
  214. package/schema/examples/workspace/ventures/northstar/portfolio/kilnworks/products/.gitkeep +0 -0
  215. package/schema/examples/workspace/ventures/northstar/portfolio/kilnworks/products/decisions/product.core-product.decision.json +28 -0
  216. package/schema/examples/workspace/ventures/northstar/portfolio/kilnworks/products/decisions/product.exists.decision.json +32 -0
  217. package/schema/examples/workspace/ventures/northstar/portfolio/kilnworks/products/decisions/product.flagship.decision.json +27 -0
  218. package/schema/examples/workspace/ventures/northstar/portfolio/kilnworks/profile.md +5 -0
  219. package/schema/examples/workspace/ventures/northstar/portfolio/kilnworks/venture.json +27 -0
  220. package/schema/examples/workspace/ventures/northstar/products/.gitkeep +0 -0
  221. package/schema/examples/workspace/ventures/northstar/profile.md +5 -0
  222. package/schema/examples/workspace/ventures/northstar/provenance.jsonl +3 -0
  223. package/schema/examples/workspace/ventures/northstar/purpose/README.md +3 -0
  224. package/schema/examples/workspace/ventures/northstar/venture.json +49 -0
  225. package/schema/foundation.mjs +8 -7
  226. package/schema/founder.schema.json +72 -0
  227. package/schema/frame.schema.json +240 -0
  228. package/schema/frames.mjs +229 -0
  229. package/schema/freshness-log.schema.json +40 -0
  230. package/schema/freshness-profiles.schema.json +44 -0
  231. package/schema/freshness.mjs +474 -0
  232. package/schema/fund-terms.schema.json +107 -0
  233. package/schema/fund-thesis.schema.json +87 -0
  234. package/schema/gate-drafts.mjs +131 -0
  235. package/schema/gate.mjs +105 -11
  236. package/schema/glossary.schema.json +25 -0
  237. package/schema/init-venture.mjs +69 -21
  238. package/schema/judgment.schema.json +38 -0
  239. package/schema/kit.schema.json +47 -0
  240. package/schema/kits.mjs +24 -0
  241. package/schema/knowledge-sources.schema.json +145 -15
  242. package/schema/layer.json +16 -3
  243. package/schema/marketing-brand.schema.json +116 -0
  244. package/schema/migrate-venture.mjs +8 -7
  245. package/schema/migrations/kit-to-package.mjs +95 -0
  246. package/schema/migrations/package-layout.mjs +90 -0
  247. package/schema/model-steps.mjs +718 -0
  248. package/schema/money-summary.schema.json +138 -0
  249. package/schema/naive-config.mjs +166 -0
  250. package/schema/naive-config.schema.json +268 -0
  251. package/schema/need-now.json +171 -0
  252. package/schema/need-now.mjs +8 -6
  253. package/schema/packages.mjs +21 -15
  254. package/schema/payouts.mjs +3 -2
  255. package/schema/people.schema.json +85 -0
  256. package/schema/port-venture.mjs +206 -132
  257. package/schema/public-assets.schema.json +76 -0
  258. package/schema/release-read.mjs +112 -0
  259. package/schema/releases.mjs +498 -0
  260. package/schema/run-handoff.mjs +5 -4
  261. package/schema/setup.mjs +165 -0
  262. package/schema/stale.mjs +112 -0
  263. package/schema/start-release.mjs +41 -0
  264. package/schema/structure.mjs +187 -0
  265. package/schema/systems-catalog.json +126 -0
  266. package/schema/systems-catalog.schema.json +78 -0
  267. package/schema/time-to-ready.mjs +7 -6
  268. package/schema/trigger.schema.json +44 -0
  269. package/schema/triggers.mjs +238 -0
  270. package/schema/upgrade-registry.schema.json +60 -0
  271. package/schema/upgrade-venture.mjs +96 -118
  272. package/schema/upgrades.json +69 -0
  273. package/schema/upgrades.mjs +386 -0
  274. package/schema/validate.py +19 -10
  275. package/schema/venture-paths.mjs +85 -0
  276. package/schema/venture-role.schema.json +64 -0
  277. package/schema/venture-type.mjs +62 -0
  278. package/schema/venture-types.json +346 -0
  279. package/schema/venture-types.schema.json +129 -0
  280. package/schema/venture.schema.json +61 -0
  281. package/schema/workspace.schema.json +5 -1
@@ -24,6 +24,11 @@
24
24
  // record (escalate -> an agent's write is a draft), and the role's allowed routes
25
25
  // robots a team-plan role may list the routes it may read and write; system/robots shows them
26
26
  // aliases interface/route-aliases.json maps a renamed route to its new one, with a note
27
+ // hierarchy a read resolves layout, template, error, loading, not-found, then page (P-28);
28
+ // resolved_by names the level that answered (interface/reads.mjs)
29
+ // writes a write answers with what it changed and the next route to act on; a layout lists
30
+ // the actions open to the actor; package, group and venture routes; global errors
31
+ // (interface/writes.mjs wraps call)
27
32
  // storage a record is read and written through the connector for the source its record
28
33
  // type lives in (knowledge/connectors, EE-037): the venture's own folder unless its
29
34
  // knowledge/sources.json names another, such as a GitHub repository, where a write
@@ -40,7 +45,27 @@ import { agentFromEnv, entries as provEntries, record as prov } from "./provenan
40
45
  import { address } from "../knowledge/connectors/contract.mjs";
41
46
  import { holding, locked } from "../schema/decisions.mjs";
42
47
  import { configOf, hold } from "../schema/gate.mjs";
48
+ // Freshness (Revalidating, V-1 to V-8): stale flags on reads, and the freshness and revalidate commands.
49
+ import { readFlag as freshnessFlag } from "../schema/freshness.mjs";
50
+ // Gate group hooks (M-18, F-5, X-2, X-4 to X-6, P-2, P-7, U-4): who is acting, access at the record,
51
+ // the gate's verdict, where held drafts wait, and check settings. The logic lives in these modules.
52
+ import { actingCall, itemId, mayReadItem, mayReadSome } from "./acting.mjs";
53
+ import { checkPlan, gateWrite, heldDraft } from "./gate-hooks.mjs";
43
54
  import { LOCAL, attributeSources, connect, sourceFor } from "../knowledge/connectors/index.mjs";
55
+ // Caching (schema/caching.mjs): per-action memo, the record-level cache for command routes, and system/cache, system/judge, system/answer.
56
+ import { actionScope, cacheRoutes, command as cachedCommand, forget, internal as cacheInternal, memo } from "../schema/caching.mjs";
57
+ // Context hook: instrumentation of every call and judgment (interface/instrumentation.mjs, P-4, F-4).
58
+ import { instrumented, judgment } from "./instrumentation.mjs";
59
+ // Hooks for writes, reads and boundaries (wf/writes): the wrapper around call, per-record containment and private folders.
60
+ import { around } from "./writes.mjs";
61
+ import { contain } from "./boundaries.mjs";
62
+ import { packageSlots } from "./package-routes.mjs";
63
+ import { routable } from "../schema/structure.mjs";
64
+ // Brand hook (Y-2..Y-4, J-2..J-4, T-1, T-2, P-24, P-26): routes, commands and layout context from schema/brand.mjs.
65
+ import { BRAND_ROUTES, brandCommands, brandLayout } from "../schema/brand.mjs";
66
+ // Upgrades hook (V-9): a read says when what the record reads changed in its system of record.
67
+ import { staleOf } from "../schema/stale.mjs";
68
+ import { ventureFile } from "../schema/venture-paths.mjs";
44
69
 
45
70
  export const repo = new URL("..", import.meta.url).pathname;
46
71
  const load = (p) => JSON.parse(readFileSync(p, "utf8"));
@@ -70,7 +95,9 @@ export function kits() {
70
95
  const needNowPriors = () => load(join(repo, "schema/need-now.json"));
71
96
 
72
97
  /** The route table, generated from schema/data-model.json, the kit manifests and schema/need-now.json. */
73
- export function routes() {
98
+ // Caching hook (F-12): built once per action and shared.
99
+ export function routes() { return memo("routes", buildRoutes); }
100
+ function buildRoutes() {
74
101
  const model = load(join(repo, "schema/data-model.json"));
75
102
  const ks = kits();
76
103
  const owner = new Map(ks.flatMap((k) => k.records.map((r) => [r, k])));
@@ -110,6 +137,8 @@ export function routes() {
110
137
  cmd("system/package", "schema/packages.mjs", "The venture package (EE-067, EE-069): the core packages open now and the handoffs, each record confirmed, to review or to write, what moves on its own and what waits, and what completion unlocks; --write renders the view and records a version. Assumes a solo founder or small founding team in Discovery and Validation; agents are a package enhancement"),
111
138
  cmd("system/need-now", "schema/need-now.mjs", "What the venture needs now, soon, or should hold back (EE-028)"),
112
139
  cmd("system/time-to-ready", "schema/time-to-ready.mjs", "Time to running, and to ready per layer and MAD tier"),
140
+ // Model steps hook (use model, EE-073): the logic is schema/model-steps.mjs; this route only shows the payload.
141
+ cmd("system/steps", "schema/model-steps.mjs", "The model steps' payload (use model, EE-073): what code computed, a placeholder for each judgment a model has yet to give, and the only fields each one receives; asks no model. mentura steps run asks one", { args: ["payload"] }),
113
142
  cmd("purpose/validation/assess", "schema/assess-problem.mjs", "What the problem's evidence is strong enough for: each assumption labelled demonstrated, inferred, assumed, unknown or contradicted, the commitment it supports and the next test (EE-050). Asks first when the record is incomplete; input as_is runs it anyway", { as_is: true }),
114
143
  cmd("governance/analytics", "schema/decision-analytics.mjs", "Decision analytics (EE-072): how many decisions of each type each function has made, open against decided, in whose words, in which phase, and their outcomes. Data only; a view is the platform's, or a visualization skill's", { actions: ["run", "layout"] }),
115
144
  cmd("governance/decisions", "schema/decisions.mjs", "The decision matrix: every decision the venture owes by layer and phase, with its parent and boundary, and whether it renders now, waits, comes later, is stale or deferred (EE-054, EE-057)"),
@@ -124,9 +153,17 @@ export function routes() {
124
153
  cmd("governance/payouts", "schema/payouts.mjs", "Who gets what at a sale value, from the cap table (EE-022); input value, else the scenarios in exit.json", { needs: ["governance/ownership.json"], venture_arg: "id" }),
125
154
  cmd("system/overview", null, "The venture at a glance: telos, validation, need-now, time to ready, team and recent provenance, rendered as parallel slots", { internal: "overview" }),
126
155
  cmd("system/robots", null, "Which roles may read and write which routes, from the team plan", { internal: "robots" }),
156
+ // Context hook (interface/vitals.mjs, interface/context.mjs): agent vitals and the context budget (N-6, C-15, N-13).
157
+ cmd("system/vitals", "interface/vitals.mjs", "Agent vitals (N-6): calls per task, tokens of context per call, time to a useful answer and time from a decision opening to a person deciding it, at the 75th percentile, each checked against its threshold (C-15). Reads the events log that instrumentation writes"),
158
+ cmd("system/context", "interface/context.mjs", "The context budget (N-13): what every route sends an agent, in tokens, against its budget, with the largest part and where to move it (S-14, F-6)", { args: ["budget"] }),
127
159
  cmd("knowledge/catalog", "knowledge/connectors/catalog.mjs", "Build or refresh the catalog: every item in every source the venture keeps records in, with its address and version, no content (EE-037)"),
128
160
  cmd("knowledge/search", "knowledge/connectors/catalog.mjs", "Find items across every source through the catalog; input value is the query (EE-037)", { value_flag: "--search" }),
161
+ // Freshness (Revalidating, V-1 to V-8): schema/freshness.mjs.
162
+ cmd("system/freshness", "schema/freshness.mjs", "What is fresh, stale or expired, and which records are live holes: by time (freshness profiles, per phase too) and on demand (a dependency changed, a where-used parent drifted, a tag, path or package was revalidated)"),
163
+ cmd("system/revalidate", "schema/freshness.mjs", "Mark records stale on demand: input value tag:<route> (the record and what depends on it), path:<folder>, package:<name>, or pivot (the validation package); a plain folder is refused in favour of exact tags", { value_flag: "--revalidate" }),
129
164
  );
165
+ out.push(...cacheRoutes(cmd)); // Caching hook: system/cache, system/judge, system/answer (schema/caching.mjs).
166
+ out.push(...brandCommands(cmd)); // Brand hook: brand tokens, voice, glossary terms, sitemap.xml, asset facts (schema/brand.mjs).
130
167
  return out.sort((a, b) => a.route.localeCompare(b.route));
131
168
  }
132
169
 
@@ -172,21 +209,22 @@ export function ventureDir(id, workspace = defaultWorkspace()) {
172
209
  }
173
210
  return null;
174
211
  }
175
- for (const d of walkVentures(workspace)) if (load(join(d, "venture.json")).id === id) return d;
212
+ // A broken venture.json is found by its folder name, so its global error can answer (E-8, interface/boundaries.mjs).
213
+ for (const d of walkVentures(workspace)) { let vid; try { vid = load(join(d, "venture.json")).id; } catch { vid = basename(d); } if (vid === id) return d; }
176
214
  return null;
177
215
  }
178
216
 
179
217
  /** The ventures under a workspace: id, name, phase, path (the [...venture] segment) and folder. */
180
218
  export function ventures(workspace = defaultWorkspace()) {
181
219
  const root = existsSync(join(workspace, "ventures")) ? join(workspace, "ventures") : workspace;
182
- return walkVentures(workspace).map((d) => { const v = load(join(d, "venture.json")); return { id: v.id, name: v.name, phase: v.phase, path: relative(root, d), dir: d }; });
220
+ return walkVentures(workspace).map((d) => { let v; try { v = load(join(d, "venture.json")); } catch { return { id: basename(d), name: null, phase: null, path: relative(root, d), dir: d, broken: true }; } return { id: v.id, name: v.name, phase: v.phase, path: relative(root, d), dir: d }; });
183
221
  }
184
222
 
185
223
  // The venture's decision rights: the template, tightened by the venture's own file.
186
224
  function decisionRights(dir) {
187
225
  const t = load(join(repo, "governance/venture-template/decision-rights.json"));
188
226
  const classes = new Map(t.classes.map((c) => [c.id, c]));
189
- const own = join(dir, "governance/decision-rights.json");
227
+ const own = join(ventureFile(dir, "governance/decision-rights.json"));
190
228
  if (existsSync(own)) for (const c of load(own).classes) classes.set(c.id, { ...classes.get(c.id), ...c, level: classes.get(c.id)?.level === "escalate" ? "escalate" : c.level });
191
229
  return classes;
192
230
  }
@@ -200,9 +238,9 @@ export function gateOf(r, dir) {
200
238
  }
201
239
 
202
240
  // Robots: a team-plan role may list the routes it may read and write (globs over routes).
203
- function allowed(dir, actorId, action, route) {
241
+ export function allowed(dir, actorId, action, route) {
204
242
  if (!actorId || !dir) return true;
205
- const plan = [join(dir, "people/team-plan.json"), join(dir, "operations/team-plan.json")].find((p) => existsSync(p));
243
+ const plan = [join(ventureFile(dir, "people/team-plan.json")), join(ventureFile(dir, "operations/team-plan.json"))].find((p) => existsSync(p));
206
244
  if (!plan) return true;
207
245
  const role = load(plan).roles.find((x) => x.id === actorId);
208
246
  if (!role?.routes) return true;
@@ -219,17 +257,19 @@ const PACKAGE_ROUTE = {
219
257
  "anatomy-answers": "product/anatomy-answers", "anatomy-input": "product/anatomy", "venture-package": ".release/venture-package",
220
258
  "decision-record": "decisions",
221
259
  customer: "customers", investor: "investors", "money-account": "money", "raw-source": "raw/sources",
260
+ people: "people", founder: "people/founders", "fund-thesis": "fund/thesis", "fund-terms": "fund/terms", deal: "deals",
261
+ ...BRAND_ROUTES, // Brand hook: marketing/brand, public/assets, glossary (schema/brand.mjs).
222
262
  };
223
263
 
224
264
  // Prefer a file that is already there, so an unmigrated venture keeps its old path. A new write uses the first path, which is the package layout.
225
265
  const fileFor = (r, params, dir) => {
226
266
  const cands = (r.collection === "item" ? r.files.map((f) => f.replace("*", params.id)) : r.files).filter((f) => !f.includes("*"));
227
267
  if (dir) {
228
- const hit = cands.find((f) => existsSync(join(dir, f)));
268
+ const hit = cands.find((f) => existsSync(ventureFile(dir, f)));
229
269
  if (hit) return hit;
230
270
  // A new collection item joins the folder the venture already keeps that collection in.
231
271
  if (r.collection === "item") {
232
- const lived = cands.find((f) => { const d = join(dir, dirname(f)); return existsSync(d) && readdirSync(d).some((n) => n.endsWith(".json") || n.endsWith(".md") || n.endsWith(".mdx")); });
272
+ const lived = cands.find((f) => { const d = ventureFile(dir, dirname(f)); return existsSync(d) && readdirSync(d).some((n) => n.endsWith(".json") || n.endsWith(".md") || n.endsWith(".mdx")); });
233
273
  if (lived) return lived;
234
274
  }
235
275
  }
@@ -243,13 +283,14 @@ export function draftOf(route, venture, workspace) {
243
283
  const dir = ventureDir(venture, workspace);
244
284
  if (!r || r.kind !== "record" || r.collection === "list" || !dir) return null;
245
285
  const file = fileFor(r, params, dir);
286
+ if (!file) return null; // a route with no concrete file (a pattern-only slot) has no draft
246
287
  return { dir, file, draft: draftPath(file), gate: gateOf(r, dir), route: r.collection === "item" ? r.route.replace("[id]", params.id) : r.route };
247
288
  }
248
289
 
249
290
  function agentsChain(dir, route) {
250
291
  if (!dir) return [];
251
292
  const chain = [];
252
- const add = (rel) => { const f = join(dir, rel); if (existsSync(f)) chain.push({ path: rel, text: readFileSync(f, "utf8") }); };
293
+ const add = (rel) => { const f = ventureFile(dir, rel); if (existsSync(f)) chain.push({ path: rel, text: readFileSync(f, "utf8") }); };
253
294
  add("AGENTS.md");
254
295
  const parts = route.split("/").filter(Boolean);
255
296
  let acc = "";
@@ -266,9 +307,11 @@ function instructionsOf(chain) {
266
307
  }
267
308
 
268
309
  /** A score below its threshold is uncertain: not an error and not an answer. It routes to the function owner. */
269
- export function judge(score, threshold, owner) {
270
- if (typeof score !== "number" || score < threshold) return { outcome: "uncertain", owner, result: null };
271
- return { outcome: "answer", result: score };
310
+ export function judge(score, threshold, owner, at) {
311
+ const out = typeof score !== "number" || score < threshold ? { outcome: "uncertain", owner, result: null } : { outcome: "answer", result: score };
312
+ // Context hook: a judgment made for a venture (at.dir) is an instrumentation event (F-4).
313
+ if (at?.dir) judgment(at.dir, { question: at.question, route: at.route, model: at.model, score, threshold, outcome: out.outcome, owner });
314
+ return out;
272
315
  }
273
316
 
274
317
  /** Runs fn. A throw is the uncaught outcome. */
@@ -292,9 +335,11 @@ export function layout(r, dir) {
292
335
  const pkg = owned ? { skill: skillsOf(owned).map((s) => s.path), template: r.template ?? null, gates: owned.gates ?? [] } : null;
293
336
  const out = { route: r.route, stack, chain, instructions: instructionsOf(chain), package: pkg, note: r.note, groups: r.groups ?? [] };
294
337
  if (r.kind === "record") Object.assign(out, { schema: r.schema, template: r.template, checks: r.checks, gate: gateOf(r, dir) });
338
+ { const brand = brandLayout(dir, r.route); if (brand) out.brand = brand; } // Brand hook: brand, voice and package terms at this route (Y-2, Y-3, T-2).
295
339
  if (dir && r.kind === "record") {
296
340
  try {
297
- const nn = JSON.parse(spawnSync("node", [join(repo, "schema/need-now.mjs"), dir, "--json"], { encoding: "utf8" }).stdout);
341
+ // Caching hook (O-5): shared with the system/need-now command in the same action.
342
+ const nn = JSON.parse(memo(`cmd:schema/need-now.mjs ${dir} --json`, () => spawnSync("node", [join(repo, "schema/need-now.mjs"), dir, "--json"], { encoding: "utf8" }), { dir }).stdout);
298
343
  const row = nn.rows.find((x) => x.type === r.record);
299
344
  if (row) out.need_now = { phase: nn.phase, status: row.status, now: row.now, next: row.next, why: row.why };
300
345
  } catch {}
@@ -311,10 +356,18 @@ const fix = (r) => ({ skills: r.kit_path ? skillsOf(kits().find((k) => k.path ==
311
356
  * routes; via: the interface (cli, mcp, api; default router); provenance: { derived_from,
312
357
  * decision, note, agent } for the log.
313
358
  */
314
- export async function call({ venture, route, action, input, actor = "agent", actor_id = process.env.MENTURA_ACTOR_ID, workspace, via = "router", provenance = {} }) {
359
+ // Caching hook (F-2): one top-level call is one action; the calls it makes join it and share its memo.
360
+ // Context hook: every call is timed and measured for agent vitals (interface/instrumentation.mjs).
361
+ // Gate group hook (M-18): who is acting is checked on every call and carried on the answer.
362
+ // Writes hook (interface/writes.mjs): adds what reads, layouts and writes answer.
363
+ export const call = (args) => actionScope(() => instrumented(args, (a) => actingCall(a, (b) => around(callInAction, b), ventureDir), ventureDir));
364
+ async function callInAction({ venture, route, action, input, actor = "agent", actor_id = process.env.MENTURA_ACTOR_ID, workspace, via = "router", provenance = {} }) {
315
365
  const requested = route;
316
366
  const { r, params, alias } = resolve(route);
317
367
  if (!r) return { ok: false, outcome: "error", error: `no route ${route}` };
368
+ // Context hook (interface/context.mjs): navigate reads a route header first, with its layout once per
369
+ // session, its template and prefetched refs; head stops after the header (N-3, L-2, O-4).
370
+ if (action === "navigate" || action === "head") return (await import("./context.mjs")).navigate({ ...(input ?? {}), venture, route: requested, workspace, actor, actor_id, via, header_only: action === "head" || !!input?.header_only });
318
371
  const deprecated = alias ? { deprecated: `${alias.from} is now ${alias.to}${alias.note ? `: ${alias.note}` : ""}` } : {};
319
372
  route = r.collection === "item" ? r.route.replace("[id]", params.id) : r.route;
320
373
  if (!r.actions.includes(action)) return { ok: false, error: `${route} has no action ${action} (it has ${r.actions.join(", ")})` };
@@ -323,7 +376,8 @@ export async function call({ venture, route, action, input, actor = "agent", act
323
376
  if (action === "layout") { if (venture && !dir) return { ok: false, error: `no venture ${venture}` }; return { ok: true, result: layout(r, dir), ...deprecated }; }
324
377
  if (!dir) return { ok: false, error: `no venture ${venture}` };
325
378
  const act = action === "write" ? "write" : "read";
326
- if (!allowed(dir, actor_id, act, requested) && !allowed(dir, actor_id, act, route)) return { ok: false, error: `${actor_id} may not ${act} ${route} (team plan routes)` };
379
+ // Gate group hook (F-5): a role that may read some items of a collection may read its list, filtered below.
380
+ if (!allowed(dir, actor_id, act, requested) && !allowed(dir, actor_id, act, route) && !(act === "read" && r.collection === "list" && mayReadSome(dir, actor_id, route))) return { ok: false, error: `${actor_id} may not ${act} ${route} (team plan routes)` };
327
381
  // An activity a decision unlocks stays closed to agents until a person makes that decision (EE-058).
328
382
  const lock = actor !== "person" && (action === "write" || action === "run") ? (locked(dir, requested, actor_id) || locked(dir, route, actor_id)) : null;
329
383
  if (lock) return { ok: false, error: `${route} is locked until decision ${lock.id} is made: ${lock.question}`, locked_by: lock.id,
@@ -337,15 +391,19 @@ export async function call({ venture, route, action, input, actor = "agent", act
337
391
  if (r.kind === "command") {
338
392
  if (r.internal === "overview") return { ok: true, result: await overview(venture, workspace), ...deprecated };
339
393
  if (r.internal === "robots") return { ok: true, result: robots(dir), ...deprecated };
340
- const lacking = (r.needs ?? []).filter((f) => !existsSync(join(dir, f)));
394
+ // Caching hook: system/cache, system/judge and system/answer are handled in schema/caching.mjs.
395
+ if (["cache", "judge", "answer"].includes(r.internal)) return { ...(await cacheInternal(r.internal, { dir, venture, workspace, input, actor, actor_id, via, provenance }, { call, resolve, draftOf })), ...deprecated };
396
+ const lacking = (r.needs ?? []).filter((f) => !existsSync(ventureFile(dir, f)));
341
397
  if (lacking.length) return { ok: false, error: `${route} needs ${lacking.join(", ")} in ${venture}`, fix: { missing: lacking } };
342
398
  if (r.value_flag && !input?.value) return { ok: false, error: `${route} needs a value (${r.note})` };
343
399
  const args = r.venture_arg === "id" ? [dir, ...(input?.value ? [String(input.value)] : []), "--json"] : r.value_flag ? [dir, r.value_flag, String(input.value), "--json"] : [dir, ...(r.args ?? []), ...(r.as_is && input?.as_is ? ["--as-is"] : []), "--json"];
344
- const p = spawnSync("node", [join(repo, r.script), ...args], { encoding: "utf8" });
400
+ // Caching hook (C-3, H-3): a declared command's result comes from the record-level cache; the spawn is memoized per action (O-5).
401
+ const p = await cachedCommand({ dir, route, script: r.script, args, input, actor, actor_id }, () => memo(`cmd:${r.script} ${args.join(" ")}`, () => spawnSync("node", [join(repo, r.script), ...args], { encoding: "utf8" }), { dir }));
402
+ const cache = p.cache ? { cache: p.cache } : {};
345
403
  if (p.status !== 0) return { ok: false, error: (p.stderr || p.stdout).trim() };
346
404
  // A command that needs input first (EE-050) answers needs_input: the caller asks the person
347
405
  // whether to fill the record in or run as is, and runs again with input as_is to go on.
348
- try { const out = JSON.parse(p.stdout); return { ok: true, result: out.needs_input ? null : out, ...(out.needs_input ? { needs_input: out.needs_input } : {}), ...deprecated }; } catch { return { ok: true, result: p.stdout, ...deprecated }; }
406
+ try { const out = JSON.parse(p.stdout); return { ok: true, result: out.needs_input ? null : out, ...(out.needs_input ? { needs_input: out.needs_input } : {}), ...cache, ...deprecated }; } catch { return { ok: true, result: p.stdout, ...cache, ...deprecated }; }
349
407
  }
350
408
 
351
409
  // Storage: the connector for the source this record type lives in.
@@ -365,11 +423,15 @@ export async function call({ venture, route, action, input, actor = "agent", act
365
423
  const patterns = r.collection === "item" ? [fileFor(r, params, dir)] : r.files;
366
424
  let items;
367
425
  try {
368
- const paths = patterns.some((g) => g.includes("*")) ? (await store.list()).map((x) => x.path).filter((f) => patterns.some((g) => glob(g).test(f))) : patterns;
369
- items = (await Promise.all(paths.map((f) => store.read(f)))).filter(Boolean);
426
+ // a file named _* is never an item; routable: private _folders (P-22)
427
+ const paths = patterns.some((g) => g.includes("*")) ? (await store.list()).map((x) => x.path).filter((f) => !/(^|\/)_[^/]*$/.test(f) && patterns.some((g) => glob(g).test(f) && routable(f, g))) : patterns;
428
+ // Caching hook (F-2): each record is read once per action and shared.
429
+ items = (await Promise.all(paths.map((f) => memo(`read:${src.id}:${f}`, () => store.read(f), { dir })))).filter(Boolean);
430
+ // Gate group hook (F-5): access is checked again at the record; a list holds only the items the role may read.
431
+ if (r.collection === "list" && actor_id) items = items.filter((x) => mayReadItem(dir, actor_id, route, itemId(r.files, x.path)));
370
432
  } catch (e) { return { ok: false, error: `${src.id}: ${String(e.message ?? e)}` }; }
371
433
  const found = items.map((x) => x.path);
372
- const draft = !remote && r.collection !== "list" && existsSync(join(dir, draftPath(fileFor(r, params, dir)))) ? draftPath(fileFor(r, params, dir)) : null;
434
+ const draft = !remote && r.collection !== "list" && fileFor(r, params, dir) && existsSync(ventureFile(dir, draftPath(fileFor(r, params, dir)))) ? draftPath(fileFor(r, params, dir)) : null;
373
435
  const loading = draft ? { pending_draft: { file: draft, since: provEntries(dir).filter((e) => e.entity === draft).at(-1)?.at ?? null, accept: `mentura accept ${route} --venture ${venture}` } } : {};
374
436
  if (remote) {
375
437
  const prs = provEntries(dir).filter((e) => e.source?.pull_request && found.concat(patterns).some((f) => e.entity === address(src.id, f)));
@@ -394,6 +456,9 @@ export async function call({ venture, route, action, input, actor = "agent", act
394
456
  }
395
457
  if (attrs.length) x.attributes = cites;
396
458
  }
459
+ // Freshness (V-5): a stale record is still read, flagged stale until it is redone (stale-while-revalidate).
460
+ const fresh = remote ? {} : await freshnessFlag(dir, found, r.collection === "list");
461
+ Object.assign(loading, fresh);
397
462
  const parseOne = parse;
398
463
  const parseWith = (x) => { const v = parseOne(x); return overlays.has(x.path) ? { ...v, ...overlays.get(x.path) } : v; };
399
464
  if (r.collection === "list") {
@@ -406,23 +471,30 @@ export async function call({ venture, route, action, input, actor = "agent", act
406
471
  }
407
472
  return path;
408
473
  };
409
- return { ok: true, result: Object.fromEntries(items.map((x) => [idOf(x.path), parseWith(x)])), sources: Object.fromEntries(items.map((x) => [idOf(x.path), cite(x)])), ...deprecated };
474
+ // Per-record containment (E-6): an item that does not parse is named in errors; the rest come back.
475
+ const { values, errors } = contain(items, parseWith, idOf);
476
+ return { ok: true, result: Object.fromEntries(values.map(([x, v]) => [idOf(x.path), v])), sources: Object.fromEntries(values.map(([x]) => [idOf(x.path), cite(x)])), ...(Object.keys(errors).length ? { errors } : {}), ...fresh, ...deprecated };
410
477
  }
411
478
  const data = Object.fromEntries(items.map((x) => [x.path, parseWith(x)]));
412
- return { ok: true, result: items.length === 1 ? data[found[0]] : data, ...(items.length === 1 ? { source: cite(items[0]) } : { sources: Object.fromEntries(items.map((x) => [x.path, cite(x)])) }), ...loading, ...deprecated };
479
+ // V-9 hook: a summary whose source changed since it was written is marked stale (schema/stale.mjs).
480
+ const stale = !remote && items.length === 1 ? staleOf(dir, found[0]) : null;
481
+ return { ok: true, result: items.length === 1 ? data[found[0]] : data, ...(items.length === 1 ? { source: cite(items[0]) } : { sources: Object.fromEntries(items.map((x) => [x.path, cite(x)])) }), ...loading, ...(stale ? { stale } : {}), ...deprecated };
413
482
  }
414
483
 
415
484
  if (action === "check") {
416
- const outs = r.checks.map((c) => ({ check: c, ...spawnSync("node", [join(repo, c), dir], { encoding: "utf8" }) }));
417
- const ok = outs.every((o) => o.status === 0);
418
- return { ok, result: outs.map((o) => ({ check: o.check, passed: o.status === 0, output: (o.stdout + o.stderr).trim() })), ...(ok ? {} : { fix: fix(r) }), ...deprecated };
485
+ // Gate group hook (P-7): the checks settings in naive.config.json turn a check off or make it warn only.
486
+ const plan = checkPlan(dir, r.checks);
487
+ const outs = plan.filter((p) => p.mode !== "off").map((p) => ({ check: p.check, mode: p.mode, ...spawnSync("node", [join(repo, p.check), dir], { encoding: "utf8" }) }));
488
+ const ok = outs.every((o) => o.status === 0 || o.mode === "warn");
489
+ return { ok, result: [...outs.map((o) => ({ check: o.check, passed: o.status === 0, ...(o.mode === "warn" ? { warn_only: true } : {}), output: (o.stdout + o.stderr).trim() })), ...plan.filter((p) => p.mode === "off").map((p) => ({ check: p.check, passed: null, off: true }))], ...(ok ? {} : { fix: fix(r) }), ...deprecated };
419
490
  }
420
491
 
421
492
  if (action === "write") {
493
+ forget(dir); // Caching hook (H-3): a write never uses the cache, and what this action memoized for the venture is read again after it.
422
494
  if (!input || typeof input !== "object") return { ok: false, error: "write needs the record as input" };
423
495
  if (r.collection === "item" && !/^[a-z0-9][a-z0-9-]*$/.test(params.id)) return { ok: false, error: `${params.id} is not a valid id (lowercase letters, digits and hyphens)` };
424
496
  const file = fileFor(r, params, dir);
425
- if (["idea-dump", "raw-source", "raw-text"].includes(r.record) && existsSync(join(dir, file))) return { ok: false, error: `${route} is raw and immutable: add a new capture, do not edit this one` };
497
+ if (["idea-dump", "raw-source", "raw-text"].includes(r.record) && existsSync(ventureFile(dir, file))) return { ok: false, error: `${route} is raw and immutable: add a new capture, do not edit this one` };
426
498
  const gate = gateOf(r, dir);
427
499
  const text = JSON.stringify(input, null, 2) + "\n";
428
500
  // The schema first, on a scratch copy, so nothing reaches any source unless it matches.
@@ -444,7 +516,9 @@ export async function call({ venture, route, action, input, actor = "agent", act
444
516
  // (EE-054): an agent's write becomes a draft, as for a gated record.
445
517
  const held = !remote && actor !== "person" ? holding(dir, file) : null;
446
518
  // The venture's own fail-closed routes (naive.config.json, the gate, EE-073) hold an agent's write too.
447
- const configured = !remote ? hold(configOf(dir), r.route, actor) : null;
519
+ // Gate group hook (X-1 to X-6, U-4): the venture's gate decides; a refusal stops the write here.
520
+ const configured = !remote ? gateWrite({ dir, r, route, requested, file, actor, actor_id, provenance, gate }) : null;
521
+ if (configured?.then === "refuse") return { ok: false, error: configured.why, gate_rule: configured.rule, refused_by: configured.source };
448
522
  const gatedDraft = !remote && (!!gate?.drafts || !!held || !!configured) && actor !== "person";
449
523
  const attributeWrites = [];
450
524
  if (split.length && !gatedDraft) {
@@ -482,13 +556,12 @@ export async function call({ venture, route, action, input, actor = "agent", act
482
556
  }
483
557
 
484
558
  const draft = gatedDraft;
485
- if (draft) {
486
- const dest = join(dir, draftPath(file));
487
- mkdirSync(dirname(dest), { recursive: true });
488
- writeFileSync(dest, text);
489
- } else await store.write(file, recordText);
490
- const entry = log(draft ? draftPath(file) : file, draft ? "draft" : provenance.activity ?? "write");
491
- return { ok: true, result: { written: draft ? draftPath(file) : file, draft, gate: gate?.class ?? null, provenance: entry.hash, note: draft ? (gate?.drafts ? `${r.record} is a ${gate.class} decision (${gate.level}, owner ${gate.owner}): written as a draft for a person to accept` : held ? `decision ${held.id} ${held.why}: written as a draft for a person to accept` : `${configured.rule} fails closed in naive.config.json: written as a draft for a person to accept`) : "written", ...(held ? { held_by: held.id } : {}), ...(configured ? { gate_rule: configured.rule } : {}), ...attributesOut }, ...deprecated };
559
+ // Gate group hook (X-2): the held draft, the owner it was sent to and any variants kept side by side.
560
+ let held2 = null;
561
+ if (draft) held2 = heldDraft(dir, { draft: draftPath(file), file, text, verdict: configured, route, agent });
562
+ else await store.write(file, recordText);
563
+ const entry = log(draft ? held2.written : file, draft ? "draft" : provenance.activity ?? "write");
564
+ return { ok: true, result: { written: draft ? held2.written : file, draft, gate: gate?.class ?? null, provenance: entry.hash, note: draft ? (gate?.drafts ? `${r.record} is a ${gate.class} decision (${gate.level}, owner ${gate.owner}): written as a draft for a person to accept` : held ? `decision ${held.id} ${held.why}: written as a draft for a person to accept` : configured.why) : "written", ...(held ? { held_by: held.id } : {}), ...(configured ? { gate_rule: configured.rule, ...(configured.owner ? { owner: configured.owner } : {}) } : {}), ...(held2?.variants ? { variant: held2.variant, variants: held2.variants, decision: held2.decision } : {}), ...attributesOut }, ...deprecated };
492
565
  }
493
566
  return { ok: false, error: `unhandled action ${action}` };
494
567
  }
@@ -539,6 +612,7 @@ async function overview(venture, workspace) {
539
612
  slot("@need-now", async () => (await run("system/need-now")).rows.filter((x) => x.status === "suggest now").map((x) => ({ record: x.type, now: x.now, why: x.why }))),
540
613
  slot("@ready", async () => { const t = await run("system/time-to-ready"); return { running: t.running.on ?? `not yet: ${t.running.missing.join(", ")}`, layers: t.layers.map((l) => ({ layer: l.layer, ready: l.ready_on ?? `missing ${l.missing.join(", ")}` })) }; }),
541
614
  slot("@team", async () => { const p = await read("people/team-plan"); if (typeof p === "string") return p; return p.roles.map((x) => ({ role: x.id, occupant: x.occupant, status: x.status, needed_by: x.needed_by })); }),
615
+ slot("@packages", () => packageSlots(dir, venture)), // P-17: a package with nothing in it shows its default
542
616
  slot("@provenance", () => provEntries(dir).slice(-5).reverse().map((e) => ({ at: e.at, activity: e.activity, entity: e.entity, by: `${e.agent.kind}${e.agent.id ? ` ${e.agent.id}` : ""} via ${e.agent.via}` }))),
543
617
  ]),
544
618
  };
@@ -546,7 +620,7 @@ async function overview(venture, workspace) {
546
620
 
547
621
  // Robots: per role, the routes it may read and write.
548
622
  function robots(dir) {
549
- const plan = [join(dir, "people/team-plan.json"), join(dir, "operations/team-plan.json")].find((p) => existsSync(p));
623
+ const plan = [join(ventureFile(dir, "people/team-plan.json")), join(ventureFile(dir, "operations/team-plan.json"))].find((p) => existsSync(p));
550
624
  if (!plan) return { note: "no team plan: every actor may reach every route; gates still apply", roles: [] };
551
625
  return { note: "a role without routes may reach every route; gates still apply", roles: load(plan).roles.map((r) => ({ role: r.id, occupant: r.occupant, read: (r.routes?.read ?? ["**"]).join(", "), write: (r.routes?.write ?? (r.routes ? [] : ["**"])).join(", ") || "none" })) };
552
626
  }
@@ -0,0 +1,71 @@
1
+ ---
2
+ type: knowledge
3
+ tags: [everything-ecosystem, naive, skills, interface, setup, artifacts, docket]
4
+ ai-model: claude-opus-5.5
5
+ created: 2026-10-29T06:30:00Z
6
+ source: hybrid
7
+ name: naive-setup
8
+ description: >
9
+ Walk a founder through setting up a new Naive venture inside the model's own
10
+ harness: their people and hats, where each function keeps its truth, the
11
+ strategic foundation and first decisions, and a goal for discovery. Show the
12
+ progress as a Claude artifact where the harness has artifacts (EE-083).
13
+ MANDATORY TRIGGERS: set up my venture, naive setup, finish setup, walk me
14
+ through setup, I just ran create-venture, establish my venture, structure my
15
+ business, what do I do after create-venture.
16
+ ---
17
+
18
+ # Naive setup
19
+
20
+ Interface layer (EE-083). `create-venture` scaffolds the venture. This skill finishes the job in conversation. You ask the questions, the founder answers in their own words, and Naive writes each answer as a decision or a source-of-truth pointer, with provenance. The progress is a view the harness renders, not a server Naive runs. In Claude, it is an artifact.
21
+
22
+ The questions and their order come from Naive, not from you. Read them from `mentura_setup` (MCP) or `node schema/setup.mjs <venture dir> state`, and never invent a question.
23
+
24
+ ## 1. Read the state
25
+
26
+ Call `mentura_setup` with the venture id. The result has:
27
+
28
+ - `state`: the people, sources of truth, docket and goal, plus `steps` (which of the four are done), `done`, and `counts`.
29
+ - `artifact`: the state, already rendered into `interface/skills/naive-setup/artifact.html`, as one self-contained HTML page.
30
+
31
+ Without MCP, run `node schema/setup.mjs <venture dir> state` and `node schema/setup.mjs <venture dir> artifact`.
32
+
33
+ ## 2. Show it
34
+
35
+ - **Claude (claude.ai, the desktop app, or Claude Code with the Artifact tool):** render `artifact` as an HTML artifact. Do not edit its contents; it is generated. Render it again after every answer, so the founder sees the progress bar move.
36
+ - **A harness without artifacts:** show the four steps as a short checklist in Markdown, with the next open question underneath.
37
+
38
+ ## 3. Ask one question at a time, in order
39
+
40
+ Work through the first step that is not done, and ask only its next open question:
41
+
42
+ 1. **People and hats.** If `state.people` is missing, ask for the founder's name, role, and whether they are solo or a team. A solo founder wears every hat. These come from `create-venture`. If they are missing, tell the founder to run `npx create-venture` again with `--name`, `--role` and `--team`. Do not write `people/` by hand.
43
+ 2. **Sources of truth.** For each function, ask where its truth lives, offering the systems in `state.catalog.functions[<function>]`. Write each answer with `mentura_setup_point` (`function`, `system`, and an `address` such as `acme/app`). If the founder has nothing for a function yet, leave it unpointed and move on. For legal, if there are no documents, say so and name the options in the catalog.
44
+ 3. **Foundation and first decisions.** These are the `state.docket` items whose status is not `decided`, taken in order. Ask the question as written. If it has options, offer them, but take the founder's own words. Write the answer with `mentura_setup_answer` (`id` and `answer`).
45
+ 4. **A goal for discovery.** Ask what must be true for the venture to leave discovery. Write it with `mentura_setup_answer`, using `id: "discovery-goal"`.
46
+
47
+ ## 4. The founder's words, not yours
48
+
49
+ - Pass the founder's answer exactly as they wrote it. Do not polish it, summarize it, or fill in an answer they did not give.
50
+ - If an answer is unclear, ask a follow-up question. Do not guess.
51
+ - A foundation statement is the founder's to state. If they ask you to draft one, show the draft and ask them to accept it or rewrite it in their own words. Write it only after they reply, and write what they replied.
52
+
53
+ ## 5. When every step is done
54
+
55
+ `state.done` is true. Render the artifact one last time: it shows "Your venture is structured." Then tell the founder, in a few lines:
56
+
57
+ - the venture now has its people, its sources of truth, its foundation and first decisions, and a discovery goal;
58
+ - how many decisions wait for later phases (`state.later`); the docket brings each one up when it matters;
59
+ - each next decision should be oriented around the discovery goal;
60
+ - to operate the business day to day, with a team and agents, they use Mentura. Naive is the structure; Mentura is where it runs, the way a Next.js app is deployed on Vercel.
61
+
62
+ ## Without a model
63
+
64
+ The same steps run from the command line:
65
+
66
+ ```sh
67
+ node schema/setup.mjs ~/ventures/acme state
68
+ node schema/setup.mjs ~/ventures/acme point finance rho
69
+ node schema/setup.mjs ~/ventures/acme answer discovery-goal "Ten clinic owners confirm the scheduling problem by December"
70
+ node schema/setup.mjs ~/ventures/acme artifact > setup.html
71
+ ```
@@ -0,0 +1,83 @@
1
+ <!doctype html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="utf-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1">
6
+ <title>Venture setup</title>
7
+ <style>
8
+ :root { --bg:#f6f7f4; --panel:#fff; --fg:#1d2320; --muted:#5d675f; --line:#d9ded8; --accent:#2f6b4f; --accent-soft:#e3efe8; --warn:#8a5a00; --warn-soft:#f6ecd8;
9
+ --body: system-ui, -apple-system, "Segoe UI", sans-serif; --mono: ui-monospace, "SF Mono", Menlo, monospace; color-scheme: light; }
10
+ @media (prefers-color-scheme: dark) { :root:not([data-theme="light"]) { --bg:#121614; --panel:#1a201d; --fg:#e4e9e5; --muted:#9aa59d; --line:#2b332e; --accent:#86c4a3; --accent-soft:#1d2b24; --warn:#e3b25c; --warn-soft:#2c2414; color-scheme: dark; } }
11
+ :root[data-theme="dark"] { --bg:#121614; --panel:#1a201d; --fg:#e4e9e5; --muted:#9aa59d; --line:#2b332e; --accent:#86c4a3; --accent-soft:#1d2b24; --warn:#e3b25c; --warn-soft:#2c2414; color-scheme: dark; }
12
+ * { box-sizing: border-box }
13
+ body { margin:0; background:var(--bg); color:var(--fg); font:16px/1.55 var(--body); }
14
+ .wrap { max-width: 820px; margin: 0 auto; padding: 28px 16px 64px; display: grid; gap: 22px; }
15
+ header { display:grid; gap:6px } h1 { margin:0; font-size:1.7rem; letter-spacing:-.01em } h2 { margin:0 0 2px; font-size:1.1rem }
16
+ .muted { color: var(--muted) } .mono { font-family: var(--mono); font-size: .86em; overflow-wrap: anywhere }
17
+ .bar { height: 8px; border-radius: 999px; background: var(--line); overflow: hidden } .bar > div { height: 100%; background: var(--accent) }
18
+ .steps { display:flex; gap:6px; flex-wrap:wrap } .step { font:600 .76rem/1 var(--mono); padding:6px 9px; border-radius:999px; border:1px solid var(--line); color:var(--muted) }
19
+ .step.ok { background: var(--accent-soft); color: var(--accent); border-color: transparent } .step.next { border-color: var(--accent); color: var(--accent) }
20
+ section { background: var(--panel); border:1px solid var(--line); border-radius:10px; padding:18px; display:grid; gap:12px; min-width: 0 }
21
+ .table { overflow-x: auto } table { width:100%; border-collapse: collapse; font-size:.93rem }
22
+ td.mono { white-space: nowrap }
23
+ td, th { text-align:left; padding:7px 6px; border-bottom:1px solid var(--line); vertical-align: top }
24
+ th { font: 600 .72rem/1.2 var(--mono); text-transform: uppercase; letter-spacing:.05em; color: var(--muted) }
25
+ .pill { display:inline-block; font:600 .72rem/1.2 var(--mono); padding:4px 7px; border-radius:999px; background: var(--accent-soft); color: var(--accent); margin: 0 4px 4px 0 }
26
+ .pill.pointed { background: var(--line); color: var(--fg) } .pill.none { background: var(--warn-soft); color: var(--warn) }
27
+ .card { border:1px solid var(--line); border-radius:8px; padding:12px; display:grid; gap:6px } .card.done { opacity:.7 } .card.next { border-color: var(--accent) }
28
+ .q { font-weight:600 } .hint { font-size:.88rem; color: var(--muted) }
29
+ .celebrate { text-align:center; padding: 28px 18px; background: var(--accent-soft); border-color: transparent }
30
+ .celebrate .big { font-size: 1.9rem; font-weight: 700; color: var(--accent) } ol { margin:0; padding-left: 1.2em; display:grid; gap:6px; text-align:left }
31
+ </style>
32
+ </head>
33
+ <body>
34
+ <div class="wrap" id="app"></div>
35
+ <script id="naive-setup-state" type="application/json">__NAIVE_SETUP_STATE__</script>
36
+ <script>
37
+ // A view of the venture's setup state, filled in by `node schema/setup.mjs <venture dir> artifact`.
38
+ // It reads nothing from the network and writes nothing: the founder answers in the chat, the model writes
39
+ // each answer through Naive, and renders this page again.
40
+ const S = JSON.parse(document.getElementById("naive-setup-state").textContent);
41
+ const esc = (s) => String(s ?? "").replace(/[&<>"]/g, (c) => ({ "&": "&amp;", "<": "&lt;", ">": "&gt;", '"': "&quot;" })[c]);
42
+ const st = S.steps, v = S.venture;
43
+ const order = [["people", "People and hats"], ["truth", "Sources of truth"], ["docket", "Foundation and first decisions"], ["goal", "A goal for discovery"]];
44
+ const next = order.find(([k]) => !st[k])?.[0];
45
+ const doneCount = order.filter(([k]) => st[k]).length;
46
+ const label = ([k, t]) => '<span class="step ' + (st[k] ? "ok" : k === next ? "next" : "") + '">' + (st[k] ? "&#10003; " : "") + t + "</span>";
47
+
48
+ const people = S.people
49
+ ? '<div class="table"><table><tr><th>Person</th><th>Role</th><th>Hats</th></tr>' + S.people.people.map((p) => "<tr><td>" + esc(p.name) + '</td><td class="mono">' + esc(p.role) + "</td><td>" + (p.hats.map((h) => '<span class="pill pointed">' + esc(h) + "</span>").join("") || '<span class="muted">none</span>') + "</td></tr>").join("") + "</table></div>"
50
+ : '<p class="muted">Nobody is listed yet. Tell me your name, your role, and whether you are solo or a team.</p>';
51
+
52
+ const fns = [...new Set([...Object.keys(S.catalog.functions).filter((f) => f === "legal" || (S.people?.people ?? []).some((p) => p.hats.includes(f))), ...Object.keys(S.functions)])];
53
+ const systemsOf = (f) => { const x = S.functions[f]; return x?.systems?.length ? x.systems.map((s) => '<span class="pill ' + (s.status === "connected" ? "" : "pointed") + '">' + esc(S.catalog.systems[s.system]?.label ?? s.system) + (s.address ? " &middot; " + esc(s.address) : "") + " &middot; " + esc(s.status) + (s.fixture ? " (fixture)" : "") + "</span>").join("") : '<span class="pill none">not pointed yet</span>'; };
54
+ const offered = (f) => (S.catalog.functions[f] ?? []).map((s) => esc(S.catalog.systems[s]?.label ?? s)).join(", ");
55
+ const truth = '<div class="table"><table><tr><th>Function</th><th>Source of truth</th><th>You can point it at</th></tr>' + fns.map((f) => "<tr><td>" + esc(f) + "</td><td>" + systemsOf(f) + '</td><td class="hint">' + offered(f) + "</td></tr>").join("") + "</table></div>";
56
+
57
+ const open = S.docket.filter((d) => d.status !== "decided");
58
+ const cards = S.docket.map((d) => '<div class="card ' + (d.status === "decided" ? "done" : d === open[0] ? "next" : "") + '"><div class="q">' + esc(d.question) + "</div>"
59
+ + (d.status === "decided" ? "<div>&#10003; " + esc(d.answer) + "</div>"
60
+ : d.relayed ? "<div>" + esc(d.answer) + '</div><div class="hint">Your answer, waiting for you to confirm it.</div>'
61
+
62
+ : (d.options.length ? '<div class="hint">Options: ' + d.options.map(esc).join(" / ") + "</div>" : "") + '<div class="hint">Answer in the chat, in your own words.</div>')
63
+ + "</div>").join("");
64
+
65
+ const goal = S.goal?.status === "decided" ? "<p>&#10003; " + esc(S.goal.answer) + "</p>" : S.goal?.relayed ? "<p>" + esc(S.goal.answer) + '</p><p class="hint">Your answer, waiting for you to confirm it.</p>' : '<p class="muted">What must be true for ' + esc(v.name) + " to leave discovery? For example: ten clinic owners confirm the scheduling problem in interviews by December.</p>";
66
+
67
+ const done = S.done
68
+ ? '<section class="celebrate"><div class="big">Your venture is structured.</div><p>' + esc(v.name) + " has its people and their hats, a source of truth for each function, a settled foundation and first decisions, and a goal for discovery. Every decision from here routes through the docket.</p><ol>"
69
+ + "<li>Orient each next decision around the discovery goal.</li><li>" + (S.later ? S.later + " more decisions wait for later phases; the docket brings each one up when it matters." : "The docket brings up each new decision when it matters.") + "</li><li>To operate the business day to day, with your team and your agents, use Mentura.</li></ol></section>"
70
+ : "";
71
+
72
+ document.getElementById("app").innerHTML =
73
+ '<header><span class="muted mono">' + esc(v.type?.label ?? "venture") + " &middot; " + esc(v.phase) + "</span><h1>Set up " + esc(v.name) + '</h1><p class="muted">The structure of your venture, step by step. Answer in the chat; each answer is saved as a decision, and this page updates.</p>'
74
+ + '<div class="bar" role="progressbar" aria-valuemin="0" aria-valuemax="4" aria-valuenow="' + doneCount + '"><div style="width:' + doneCount * 25 + '%"></div></div><div class="steps">' + order.map(label).join("") + "</div></header>"
75
+ + (S.waiting.length ? '<section><h2>' + S.waiting.length + (S.waiting.length === 1 ? " answer waits" : " answers wait") + ' for you</h2><p class="muted">The model wrote these down from your words. They become your decisions when you confirm them yourself: run <span class="mono">npx naive setup confirm</span> in the venture.</p></section>' : "")
76
+ + done
77
+ + "<section><h2>1. People and hats</h2>" + people + "</section>"
78
+ + '<section><h2>2. Sources of truth</h2><p class="muted">Where each function keeps its truth. Naive keeps pointers, never copies.</p>' + truth + "</section>"
79
+ + '<section><h2>3. Foundation and first decisions</h2><p class="muted">The decision docket: ' + S.counts.settled + " of " + S.counts.of + " settled.</p>" + cards + "</section>"
80
+ + "<section><h2>4. A goal for discovery</h2>" + goal + "</section>";
81
+ </script>
82
+ </body>
83
+ </html>