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
@@ -0,0 +1,215 @@
1
+ // The brand and asset verbs of the CLI (mentura brand ..., mentura assets ...). Every write goes
2
+ // through the router, so it is checked against its schema and logged in provenance; the logic lives
3
+ // in schema/brand.mjs and schema/assets.mjs.
4
+ //
5
+ // mentura assets add <public/file> --venture <id> --source <who or url> --license <id> --credit <who>
6
+ // [--alt <text>] [--decorative] [--as person]
7
+ // register a file in public/; code fills its facts (J-2, J-4)
8
+ // mentura assets fill --venture <id> refill every entry's facts from its file, by code (J-4)
9
+ // mentura assets alt <public/file> --text <alt> --venture <id> [--as person]
10
+ // write alt text; an agent's waits for a person to confirm it
11
+ // mentura assets confirm <public/file> --venture <id> --by <name> [--confirm <public/file>]
12
+ // a person confirms the alt text as it stands (J-4)
13
+ // mentura assets remote <url> --venture <id> --license <id> --credit <who> [--id <raw id>] [--alt <text>] [--note <text>]
14
+ // record a remote image as a link with a note in raw/ (J-3)
15
+ // mentura assets hosts <host,host> --venture <id> set the hosts an agent may fetch from (J-3)
16
+ // mentura assets check|facts --venture <id>
17
+ // mentura brand tokens|css|head|check|voice --venture <id> [--json]
18
+ // mentura brand resolve <route> --venture <id> the brand at a route, nearer context winning (Y-5, T-2)
19
+ // mentura brand terms <package> --venture <id> the terms a package sees (Y-2)
20
+ // mentura brand sitemap --venture <id> --base <url> [--out <file>] sitemap.xml from the marketing sitemap (P-26)
21
+
22
+ import { existsSync, readFileSync, writeFileSync, mkdirSync } from "node:fs";
23
+ import { dirname, join, resolve as resolvePath } from "node:path";
24
+ import { call, ventureDir } from "./router.mjs";
25
+ import { agentFromEnv, record as prov } from "./provenance.mjs";
26
+ import { altStatement, checkAssets, facts as allFacts, factsOf, REGISTER, registerOf } from "../schema/assets.mjs";
27
+ import { brandOf, checkBrand, resolveBrand, sitemapOf, sitemapXml, termsFor, tokens, voice } from "../schema/brand.mjs";
28
+ import { ventureFile } from "../schema/venture-paths.mjs";
29
+
30
+ const VERSION = "0.1.0";
31
+ const today = () => new Date().toISOString();
32
+
33
+ /** The facts code reads, in the register's shape. */
34
+ export const factsFor = (dir, path) => {
35
+ const f = factsOf(readFileSync(ventureFile(dir, path)));
36
+ return Object.fromEntries(Object.entries({ type: f.type, width: f.width, height: f.height, sizes: f.sizes, bytes: f.bytes, sha256: f.sha256, filled_at: today() }).filter(([, v]) => v !== undefined));
37
+ };
38
+
39
+ const emptyRegister = () => ({ version: VERSION, assets: [] });
40
+
41
+ /** Writes the register through the router: schema first, then provenance. */
42
+ export async function writeRegister(venture, workspace, reg, { actor = "agent", note } = {}) {
43
+ return call({ venture, route: "public/assets", action: "write", input: reg, actor, workspace, via: "cli", provenance: { note } });
44
+ }
45
+
46
+ /** Registers one file in public/ with its source, license and credit; code fills its facts. */
47
+ export async function addAsset({ venture, workspace, path, source, license, credit, alt, decorative, origin_url, design_system, actor = "agent" }) {
48
+ const dir = ventureDir(venture, workspace);
49
+ if (!dir) return { ok: false, error: `no venture ${venture}` };
50
+ if (!path?.startsWith("public/")) return { ok: false, error: `${path} is not in public/` };
51
+ if (!existsSync(ventureFile(dir, path))) return { ok: false, error: `${path} is not in ${venture}` };
52
+ const reg = registerOf(dir) ?? emptyRegister();
53
+ const entry = Object.fromEntries(Object.entries({ path, source, license, credit, alt: alt ? { text: alt, by: actor === "person" ? "person" : "agent" } : undefined, decorative: decorative || undefined, origin_url, design_system, facts: factsFor(dir, path) }).filter(([, v]) => v !== undefined));
54
+ reg.assets = [...reg.assets.filter((a) => a.path !== path), entry].sort((a, b) => a.path.localeCompare(b.path));
55
+ const w = await writeRegister(venture, workspace, reg, { actor, note: `registered ${path}; facts read by code` });
56
+ if (w.ok && alt && actor === "person") prov(dir, { entity: REGISTER, route: "public/assets", activity: "confirm", statement: altStatement(path, alt), agent: agentFromEnv("person", "cli"), note: `alt text of ${path} written by a person` });
57
+ return w.ok ? { ok: true, result: { registered: path, facts: entry.facts, alt: entry.alt ?? null } } : w;
58
+ }
59
+
60
+ /** Refills every entry's facts from its file. Only code writes facts. */
61
+ export async function fill({ venture, workspace }) {
62
+ const dir = ventureDir(venture, workspace);
63
+ if (!dir) return { ok: false, error: `no venture ${venture}` };
64
+ const reg = registerOf(dir);
65
+ if (!reg) return { ok: false, error: `${venture} has no ${REGISTER}; register a file first: mentura assets add public/<file> ...` };
66
+ const changed = [], missing = [];
67
+ for (const a of reg.assets) {
68
+ if (!existsSync(ventureFile(dir, a.path))) { missing.push(a.path); continue; }
69
+ const f = factsFor(dir, a.path);
70
+ if (!a.facts || ["type", "width", "height", "bytes", "sha256"].some((k) => (a.facts[k] ?? null) !== (f[k] ?? null))) { a.facts = f; changed.push(a.path); }
71
+ }
72
+ const listed = new Set(reg.assets.map((a) => a.path));
73
+ const unregistered = allFacts(dir).map((x) => x.path).filter((p) => !listed.has(p));
74
+ if (changed.length) { const w = await writeRegister(venture, workspace, reg, { note: `facts filled by code: ${changed.join(", ")}` }); if (!w.ok) return w; }
75
+ return { ok: true, result: { filled: changed, missing, unregistered } };
76
+ }
77
+
78
+ /** Writes alt text. An agent's carries agent provenance until a person confirms it; a person's is confirmed as written. */
79
+ export async function setAlt({ venture, workspace, path, text, actor = "agent" }) {
80
+ const dir = ventureDir(venture, workspace);
81
+ if (!dir) return { ok: false, error: `no venture ${venture}` };
82
+ const reg = registerOf(dir);
83
+ const a = reg?.assets.find((x) => x.path === path);
84
+ if (!a) return { ok: false, error: `${path} is not registered; mentura assets add ${path} first` };
85
+ a.alt = { text, by: actor === "person" ? "person" : "agent" };
86
+ const w = await writeRegister(venture, workspace, reg, { actor, note: `alt text of ${path} by ${a.alt.by}` });
87
+ if (!w.ok) return w;
88
+ if (actor === "person") prov(dir, { entity: REGISTER, route: "public/assets", activity: "confirm", statement: altStatement(path, text), agent: agentFromEnv("person", "cli"), note: `alt text of ${path} written by a person` });
89
+ return { ok: true, result: { path, alt: a.alt, confirmed: actor === "person" } };
90
+ }
91
+
92
+ /** A person confirms an alt text exactly as it stands. The confirmation is bound to the text: change it and it is pending again. */
93
+ export function confirmAlt({ venture, workspace, path, by }) {
94
+ const dir = ventureDir(venture, workspace);
95
+ if (!dir) return { ok: false, error: `no venture ${venture}` };
96
+ if (!by) return { ok: false, error: "say who is confirming: --by <name>" };
97
+ const a = registerOf(dir)?.assets.find((x) => x.path === path);
98
+ if (!a?.alt?.text) return { ok: false, error: `${path} has no alt text to confirm` };
99
+ const e = prov(dir, { entity: REGISTER, route: "public/assets", activity: "confirm", statement: altStatement(path, a.alt.text), agent: agentFromEnv("person", "cli", { id: by }), note: `${by} confirmed the alt text of ${path}: ${a.alt.text}` });
100
+ return { ok: true, result: { path, alt: a.alt.text, confirmed_by: by, provenance: e.hash } };
101
+ }
102
+
103
+ const rawId = (url) => {
104
+ const u = new URL(url);
105
+ const slug = `${u.host}-${u.pathname.split("/").filter(Boolean).pop() ?? "image"}`.toLowerCase().replace(/\.[a-z0-9]+$/, "").replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "");
106
+ return slug.slice(0, 60) || "remote-image";
107
+ };
108
+
109
+ /**
110
+ * A remote image as a link with a note in raw/ (J-3): raw/sources/<id>/source.json says where it is,
111
+ * text.md is the note, and the register lists it under remote. Nothing is fetched here. The platform
112
+ * fetcher passes `fetched` (facts of the private original) and `copy` when it kept a public copy.
113
+ */
114
+ export async function addRemote({ venture, workspace, url, license, credit, id, alt, note, fetched, copy, actor = "agent" }) {
115
+ const dir = ventureDir(venture, workspace);
116
+ if (!dir) return { ok: false, error: `no venture ${venture}` };
117
+ try { new URL(url); } catch { return { ok: false, error: `${url} is not a URL` }; }
118
+ const rid = id ?? rawId(url);
119
+ const reg = registerOf(dir) ?? emptyRegister();
120
+ if (!existsSync(join(ventureFile(dir, "raw/sources"), rid, "source.json"))) {
121
+ const w = await call({ venture, route: `raw/sources/${rid}`, action: "write", input: { id: rid, origin: url, title: `Remote image at ${new URL(url).host}`, fetched: today().slice(0, 10) }, actor, workspace, via: "cli", provenance: { note: "a remote image, kept as a link (J-3)" } });
122
+ if (!w.ok) return w;
123
+ const text = [`# Remote image`, "", `- Link: ${url}`, `- Host: ${new URL(url).host}`, `- License: ${license}`, `- Credit: ${credit ?? "(missing)"}`, ...(fetched ? [`- Fetched: ${fetched.type}${fetched.width ? `, ${fetched.width}x${fetched.height}` : ""}, ${fetched.bytes} bytes, sha256 ${fetched.sha256}`] : []), "",
124
+ copy ? `A copy is kept at ${copy}: the license allows it.` : "The venture links to this image. A full copy stays private (raw/sources/<id>/original.*, out of git) unless the license allows one.", ...(note ? ["", note] : []), ""].join("\n");
125
+ writeFileSync(join(ventureFile(dir, "raw/sources"), rid, "text.md"), text);
126
+ prov(dir, { entity: `raw/sources/${rid}/text.md`, route: `raw/sources/${rid}`, activity: "write", agent: agentFromEnv(actor === "person" ? "person" : "agent", "cli"), note: "the note beside a remote image (J-3)" });
127
+ }
128
+ const entry = Object.fromEntries(Object.entries({ id: rid, url, license, credit, alt: alt ? { text: alt, by: actor === "person" ? "person" : "agent" } : undefined, fetched, copy }).filter(([, v]) => v !== undefined));
129
+ reg.remote = [...(reg.remote ?? []).filter((r) => r.url !== url), entry];
130
+ const w = await writeRegister(venture, workspace, reg, { actor, note: `remote image ${url}` });
131
+ return w.ok ? { ok: true, result: { remote: url, raw: `raw/sources/${rid}/`, copy: copy ?? null } } : w;
132
+ }
133
+
134
+ /** The hosts an agent may fetch from, a setting the platform fetcher enforces (J-3). */
135
+ export async function setHosts({ venture, workspace, hosts, actor = "agent" }) {
136
+ const dir = ventureDir(venture, workspace);
137
+ if (!dir) return { ok: false, error: `no venture ${venture}` };
138
+ const reg = registerOf(dir) ?? emptyRegister();
139
+ reg.remote_hosts = hosts;
140
+ const w = await writeRegister(venture, workspace, reg, { actor, note: `hosts an agent may fetch from: ${hosts.join(", ") || "none"}` });
141
+ return w.ok ? { ok: true, result: { remote_hosts: hosts } } : w;
142
+ }
143
+
144
+ /** mentura brand ... and mentura assets ...: returns the exit code. */
145
+ export async function main(argv, { workspace } = {}) {
146
+ const opt = (n) => { const i = argv.indexOf(`--${n}`); return i >= 0 ? argv[i + 1] : undefined; };
147
+ const flag = (n) => argv.includes(`--${n}`);
148
+ const pos = argv.filter((a, i) => !a.startsWith("--") && !(i > 0 && argv[i - 1].startsWith("--") && !["json", "decorative"].includes(argv[i - 1].slice(2))));
149
+ const [group, verb, arg] = pos;
150
+ const venture = opt("venture");
151
+ const actor = opt("as") === "person" ? "person" : "agent";
152
+ const show = (r, text) => {
153
+ if (!r.ok) { console.error(r.error ?? JSON.stringify(r, null, 2)); return 1; }
154
+ console.log(flag("json") || text === undefined ? JSON.stringify(r.result, null, 2) : text);
155
+ return 0;
156
+ };
157
+ if (!venture) { console.error(`mentura ${group} ${verb ?? ""}: --venture <id> is required`); return 2; }
158
+ const dir = ventureDir(venture, workspace);
159
+ if (!dir) { console.error(`no venture ${venture}`); return 1; }
160
+
161
+ if (group === "assets") {
162
+ if (verb === "add") return show(await addAsset({ venture, workspace, path: arg, source: opt("source"), license: opt("license"), credit: opt("credit"), alt: opt("alt"), decorative: flag("decorative"), origin_url: opt("origin-url"), design_system: opt("design-system"), actor }), `registered: ${arg}`);
163
+ if (verb === "fill") { const r = await fill({ venture, workspace }); return show(r, r.ok ? `filled: ${r.result.filled.length ? r.result.filled.join(", ") : "nothing changed"}${r.result.unregistered.length ? `\nnot registered: ${r.result.unregistered.join(", ")}` : ""}` : undefined); }
164
+ if (verb === "alt") return show(await setAlt({ venture, workspace, path: arg, text: opt("text"), actor }), `alt text of ${arg} written by ${actor}${actor === "agent" ? "; a person confirms it with mentura assets confirm" : ""}`);
165
+ if (verb === "confirm") {
166
+ // The person types the path to confirm, as with foundation statements (EE-051).
167
+ if (opt("confirm") !== arg) { console.error(`not confirmed: pass --confirm ${arg} to confirm the alt text of ${arg} as it stands`); return 1; }
168
+ return show(confirmAlt({ venture, workspace, path: arg, by: opt("by") ?? process.env.MENTURA_PERSON }), `confirmed: alt text of ${arg}`);
169
+ }
170
+ if (verb === "remote") return show(await addRemote({ venture, workspace, url: arg, license: opt("license"), credit: opt("credit"), id: opt("id"), alt: opt("alt"), note: opt("note"), actor }), `linked: ${arg}`);
171
+ if (verb === "hosts") return show(await setHosts({ venture, workspace, hosts: (arg ?? "").split(",").map((h) => h.trim()).filter(Boolean), actor }), `remote_hosts: ${arg ?? "none"}`);
172
+ if (verb === "facts") return show({ ok: true, result: allFacts(dir) }, allFacts(dir).map((x) => `${x.path} ${x.type}${x.width ? ` ${x.width}x${x.height}` : ""} ${x.bytes} bytes`).join("\n") || "public/ is empty");
173
+ if (verb === "check") {
174
+ const r = checkAssets(dir);
175
+ if (flag("json")) console.log(JSON.stringify(r, null, 2));
176
+ else { for (const e of r.errors) console.log(`error ${e}`); for (const p of r.pending) console.log(`pending ${p.path}: alt text by ${p.by}, not confirmed by a person`); console.log(r.ok ? `assets: ${r.assets} files, ${r.remote} remote links, ${r.pending.length} alt texts pending` : `assets: ${r.errors.length} errors`); }
177
+ return r.ok ? 0 : 1;
178
+ }
179
+ }
180
+
181
+ if (group === "brand") {
182
+ const brand = brandOf(dir);
183
+ const need = () => { console.error(`${venture} has no brand yet: write marketing/brand.json (mentura marketing/brand template)`); return 1; };
184
+ if (verb === "tokens" || verb === "css" || verb === "head") {
185
+ if (!brand) return need();
186
+ const t = tokens(dir, brand);
187
+ return show({ ok: true, result: verb === "tokens" ? t.tokens : verb === "head" ? t.head : t.css }, verb === "css" ? t.css.trimEnd() : undefined);
188
+ }
189
+ if (verb === "resolve") { if (!brand) return need(); return show({ ok: true, result: resolveBrand(brand, arg ?? "") }); }
190
+ if (verb === "terms") { const t = termsFor(dir, arg); return show({ ok: true, result: t }, t.terms.map((x) => `${x.term} (${x.from}): ${x.meaning}`).join("\n") || `no terms reach ${arg}`); }
191
+ if (verb === "voice") {
192
+ const v = opt("text") !== undefined ? { violations: (await import("../schema/brand.mjs")).voiceCheck(opt("text"), brand ? resolveBrand(brand, opt("for") ?? "").voice.rules : []) } : voice(dir, brand);
193
+ const lines = v.violations.map((x) => `${x.file ? `${x.file}:` : "line "}${x.line} "${x.found}" breaks ${x.rule}${x.use ? `; use "${x.use}"` : ""}`);
194
+ if (flag("json")) console.log(JSON.stringify(v, null, 2)); else console.log(lines.join("\n") || "keeps to the voice rules");
195
+ return v.violations.length ? 1 : 0;
196
+ }
197
+ if (verb === "check") {
198
+ const r = checkBrand(dir);
199
+ if (flag("json")) console.log(JSON.stringify(r, null, 2));
200
+ else { for (const e of r.errors) console.log(`error ${e}`); for (const w of r.warnings) console.log(`warn ${w}`); console.log(r.ok ? "brand: ok" : `brand: ${r.errors.length} errors`); }
201
+ return r.ok ? 0 : 1;
202
+ }
203
+ if (verb === "sitemap") {
204
+ const s = sitemapOf(dir);
205
+ if (!s) { console.error(`${venture} has no marketing sitemap yet: mentura operations/marketing/sitemap run, then write it to marketing/sitemap`); return 1; }
206
+ let xml;
207
+ try { xml = sitemapXml(s.record, opt("base") ?? ""); } catch (e) { console.error(`${e.message}; pass --base https://<your site>`); return 1; }
208
+ if (opt("out")) { const f = resolvePath(opt("out")); mkdirSync(dirname(f), { recursive: true }); writeFileSync(f, xml); console.log(`wrote ${opt("out")}: ${s.record.pages.length} pages from ${s.file}`); return 0; }
209
+ process.stdout.write(xml);
210
+ return 0;
211
+ }
212
+ }
213
+ console.error(readFileSync(new URL(import.meta.url), "utf8").split("\n").filter((l) => l.startsWith("// mentura")).map((l) => l.slice(5)).join("\n"));
214
+ return 2;
215
+ }
@@ -0,0 +1,59 @@
1
+ // The freshness commands of the mentura CLI (Revalidating, V-1 to V-8). mentura.mjs hands these
2
+ // commands here; the logic is schema/freshness.mjs.
3
+ //
4
+ // mentura revalidate <target> --venture <id> [--reason <text>] [--as person] [--json]
5
+ // target: tag:<route or file> (the record and what depends on it), path:<folder>,
6
+ // package:<name>, or a plain route or file (a tag); a plain folder is refused with the tags to use
7
+ // mentura revalidate --pivot --venture <id> [--reason <text>] every assumption in the validation package
8
+ // mentura verify <route|folder> --venture <id> [--reason <text>] [--as person]
9
+ // checked and still right, unchanged: its age starts again
10
+ // mentura phase <phase> --venture <id> --as person [--reason <text>]
11
+ // the venture changes phase; records with a per-phase profile go stale
12
+ // mentura freshness --venture <id> [--json] [--now <iso>] [--freeze <out dir>]
13
+ // what is fresh, stale, expired or a live hole; --freeze writes the static shell
14
+
15
+ import { resolve } from "node:path";
16
+ import { agentFromEnv } from "../provenance.mjs";
17
+ import { ventureDir } from "../router.mjs";
18
+ import { freeze, freshness, phase, revalidate, verify } from "../../schema/freshness.mjs";
19
+
20
+ export const COMMANDS = ["revalidate", "verify", "phase", "freshness"];
21
+
22
+ /** Runs one freshness command. Returns the exit code. */
23
+ export async function main(cmd, argv, workspace) {
24
+ const opt = (n) => { const i = argv.indexOf(`--${n}`); return i >= 0 ? argv[i + 1] : undefined; };
25
+ const flag = (n) => argv.includes(`--${n}`);
26
+ const target = argv[1] && !argv[1].startsWith("--") ? argv[1] : undefined;
27
+ const venture = opt("venture");
28
+ const dir = venture && ventureDir(venture, workspace);
29
+ const print = (x) => console.log(typeof x === "string" ? x : JSON.stringify(x, null, 2));
30
+ if (!dir) { console.error(venture ? `no venture ${venture}` : `usage: mentura ${cmd} ${cmd === "freshness" ? "" : "<target> "}--venture <id>`); return venture ? 1 : 2; }
31
+ const by = agentFromEnv(opt("as") === "person" ? "person" : "agent", "cli");
32
+ const now = opt("now");
33
+ let r;
34
+ if (cmd === "revalidate") {
35
+ if (!target && !flag("pivot")) { console.error("usage: mentura revalidate <tag:route|path:folder|package:name|route> --venture <id>, or --pivot"); return 2; }
36
+ r = await revalidate(dir, flag("pivot") ? "pivot" : target, { reason: opt("reason"), by, now });
37
+ } else if (cmd === "verify") {
38
+ if (!target) { console.error("usage: mentura verify <route|folder> --venture <id>"); return 2; }
39
+ r = verify(dir, target, { reason: opt("reason"), by, now });
40
+ } else if (cmd === "phase") {
41
+ if (!target) { console.error("usage: mentura phase <phase> --venture <id> --as person"); return 2; }
42
+ // A phase change is the founder's decision: an agent may suggest it, not make it.
43
+ if (opt("as") !== "person") { console.error(`a phase change is a person's: run mentura phase ${target} --venture ${venture} --as person`); return 1; }
44
+ r = phase(dir, target, { reason: opt("reason"), by, now });
45
+ } else if (cmd === "freshness") {
46
+ if (opt("freeze")) r = await freeze(dir, resolve(opt("freeze")), { now });
47
+ else { const f = await freshness(dir, { now }); print(flag("json") ? f : [`${f.venture} (${f.phase}): ${f.summary.fresh} fresh, ${f.summary.stale} stale, ${f.summary.expired} expired, ${f.summary.holes} live ${f.summary.holes === 1 ? "hole" : "holes"}, of ${f.summary.records} records.`,
48
+ ...f.items.filter((i) => i.state !== "fresh").map((i) => ` ${i.state.padEnd(7)} ${i.route}: ${i.reasons.map((x) => x.note).join("; ")}`),
49
+ ...f.copies.filter((c) => c.state !== "fresh").map((c) => ` ${c.state.padEnd(7)} ${c.path} (copy of ${c.parent}): ${c.note}`),
50
+ ...f.warnings.map((w) => `warning: ${w}`)].join("\n")); return 0; }
51
+ }
52
+ if (!r.ok) {
53
+ console.error(r.error);
54
+ if (r.suggest) console.error(` ${r.suggest.join("\n ")}\n or, for the whole folder: mentura revalidate ${r.or} --venture ${venture}`);
55
+ return 1;
56
+ }
57
+ print(flag("json") ? r.result : r.result.note ?? r.result);
58
+ return 0;
59
+ }
@@ -0,0 +1,6 @@
1
+ // The framework's global error for the command line (E-8, interface/boundaries.mjs). Imported first, so a
2
+ // broken data model or manifest answers with recovery steps before any module that reads it loads.
3
+ import { frameworkHealth, globalError } from "../boundaries.mjs";
4
+
5
+ const fw = frameworkHealth();
6
+ if (fw) { console.error(globalError(fw).error); process.exit(1); }
@@ -12,9 +12,12 @@
12
12
  // mentura migrate --from <project> --venture <id|dir> [--apply]
13
13
  // alias of naive migrate: an agent rewrites a project as Naive records (EE-076)
14
14
  // mentura typegen alias of naive typegen (EE-073)
15
+ // mentura steps <payload|run|check|waterfall|provenance|confirm> --venture <id> [--answers <file>] [--speculate] [--stream]
16
+ // model steps (use model, EE-073): schema/model-steps.mjs
15
17
  // mentura accept <route> --venture <id> [--confirm <route>]
16
18
  // a person accepts an agent's draft of a gated record;
17
- // one holding a foundation statement asks you to type the route
19
+ // one holding a foundation statement asks you to type the route;
20
+ // [--pick <letter>] [--by <name>] accepts one of drafts kept side by side
18
21
  // mentura decide <decision> --venture <id> --by <name> [--answer <text>] [--confirm <decision>]
19
22
  // a person makes a decision: approves its wording or gives their own (EE-058)
20
23
  // mentura propose --venture <id> --input <file.json> [--as <role>]
@@ -29,16 +32,30 @@
29
32
  // a collection item is <route>/<id> (purpose/product/kettle); a nested venture is its
30
33
  // folder path (--venture studio/print); system/overview view shows the venture at a glance
31
34
  // [--derived-from a,b] [--decision <id>] [--note <text>] provenance for a write (EE-031)
35
+ // [--confidence <n>] a score computed before the write, read by the gate's confidence rules
32
36
  // knowledge/catalog run builds the catalog over the venture's sources; knowledge/search run
33
37
  // --value <query> finds items in any of them (EE-037)
34
38
  // purpose/validation/assess run lists what is missing and asks to fill it in or run as is;
35
39
  // --as-is runs it anyway (EE-050)
40
+ // [--where status=active|trial,phase=build] filters a read; --hierarchy shows how it resolved
41
+ // a package's own route (validation, packages/product) reads its state; (go-to-market) is a
42
+ // route group; venture read is the manifest, venture check runs every boundary
36
43
  // mentura baseline --venture <id> adopt existing records into the provenance log
44
+ // mentura revalidate <tag:route|path:folder|package:name|route> --venture <id> [--pivot] [--reason <text>]
45
+ // mark records stale on demand; a plain folder is refused for exact tags (Revalidating)
46
+ // mentura verify <route|folder> --venture <id> checked and still right, unchanged: its age starts again
47
+ // mentura phase <phase> --venture <id> --as person change phase; per-phase records go stale
48
+ // mentura freshness --venture <id> [--freeze <out dir>] what is fresh, stale, expired or a live hole
49
+ // mentura <route> navigate|head --venture <id> [--session <s>] [--task <text>] [--prefetch a,b | --no-prefetch] [--stream]
50
+ // read a route header first, with layout once per session, template and prefetch
51
+ // mentura batch --venture <id> --input <calls.json> [--stream]
52
+ // run a list of {route, action, input} calls, reporting each as it finishes
37
53
  //
38
54
  // The workspace is MENTURA_WORKSPACE, else --workspace <dir>, else this repository's instance/.
39
55
  // Writes default to an agent actor; --as person is for a person at the keyboard, and a gated
40
56
  // record written by an agent lands in <venture>/_drafts/ for a person to accept.
41
57
 
58
+ import "./guard.mjs"; // wf/writes hook: a broken framework file answers with recovery steps (E-8)
42
59
  import { existsSync, readFileSync, rmSync } from "node:fs";
43
60
  import { join, relative, resolve } from "node:path";
44
61
  import { spawnSync } from "node:child_process";
@@ -46,7 +63,10 @@ import { call, draftOf, files, glob, groups, repo, routes, routesMd, ventureDir,
46
63
  import { agentFromEnv, entries, record } from "../provenance.mjs";
47
64
  import { FRAME, filesOf } from "../../schema/foundation.mjs";
48
65
  import { approve, decisions, propose } from "../../schema/decisions.mjs";
66
+ // Gate group hook (X-2): held drafts kept side by side.
67
+ import { pick as pickDraft, settle as settleDraft } from "../../schema/gate-drafts.mjs";
49
68
  import { asRouted, findVenture } from "./find-venture.mjs";
69
+ import { ventureFile } from "../../schema/venture-paths.mjs";
50
70
 
51
71
  // The danger zone (EE-051): a foundation statement is accepted or confirmed only when the person types
52
72
  // its name, at the prompt or with --confirm <name>.
@@ -68,7 +88,7 @@ const here = !argv.includes("--venture") && !argv.includes("--workspace") ? find
68
88
  if (here) { argv.push("--venture", asRouted(here).venture, "--workspace", asRouted(here).workspace); process.env.MENTURA_WORKSPACE = asRouted(here).workspace; }
69
89
  const opt = (name) => { const i = argv.indexOf(`--${name}`); return i >= 0 ? argv[i + 1] : undefined; };
70
90
  const flag = (name) => argv.includes(`--${name}`);
71
- const positional = argv.filter((a, i) => !a.startsWith("--") && !(i > 0 && argv[i - 1].startsWith("--") && !["json"].includes(argv[i - 1].slice(2))));
91
+ const positional = argv.filter((a, i) => !a.startsWith("--") && !(i > 0 && argv[i - 1].startsWith("--") && !["json", "hierarchy"].includes(argv[i - 1].slice(2))));
72
92
  const workspace = opt("workspace") ? resolve(opt("workspace")) : undefined;
73
93
  const out = (x) => console.log(typeof x === "string" ? x : JSON.stringify(x, null, 2));
74
94
 
@@ -95,6 +115,9 @@ if (cmd === "ventures") {
95
115
  process.exit(0);
96
116
  }
97
117
 
118
+ // Brand hook: mentura brand ... and mentura assets ... (Y-2..Y-4, J-2..J-4, T-1, T-2, P-24, P-26) live in interface/brand.mjs.
119
+ if (cmd === "brand" || cmd === "assets") process.exit(await (await import("../brand.mjs")).main(argv, { workspace }));
120
+
98
121
  if (cmd === "add") {
99
122
  // Imports one function into a venture (EE-047, EE-048, EE-071). department is the alias for function for one release.
100
123
  const v = opt("venture");
@@ -108,6 +131,27 @@ if (cmd === "typegen") {
108
131
  process.exit(p.status ?? 1);
109
132
  }
110
133
 
134
+ // Upgrades hook (M-12, E-7, V-9, Z-4): these commands live in their own modules; this only dispatches.
135
+ // mentura triggers --venture <id> [--fire] [--json]
136
+ // the venture's triggers, their runs and open failures; --fire runs what new events start
137
+ // mentura stale --venture <id> [--json] summaries whose system of record changed since they were written
138
+ // mentura notify --venture <id> --source <id> --path <p> [--version <v>]
139
+ // a system of record says <p> changed: mark what reads it stale (a webhook does this on Mentura)
140
+ // mentura adapt <adapter> --venture <id> write a harness's entry files into the venture (claude-code, codex, cursor)
141
+ // mentura adapters [verify [<adapter>]] [--json] the adapters, or run the framework's tests an adapter must pass
142
+ if (["triggers", "stale", "notify", "adapt", "adapters"].includes(cmd)) {
143
+ const d = ["triggers", "stale", "notify", "adapt"].includes(cmd) ? (opt("venture") ? ventureDir(opt("venture"), workspace) : null) : null;
144
+ if (cmd !== "adapters" && !d) { console.error(opt("venture") ? `no venture ${opt("venture")}` : `mentura ${cmd} needs --venture <id>`); process.exit(2); }
145
+ const pass = (...names) => names.flatMap((n) => argv.flatMap((a, i) => (a === `--${n}` ? [a, ...(argv[i + 1] && !argv[i + 1].startsWith("--") ? [argv[i + 1]] : [])] : [])));
146
+ const args = cmd === "triggers" ? ["schema/triggers.mjs", d, ...pass("fire", "json")]
147
+ : cmd === "stale" ? ["schema/stale.mjs", d, ...pass("json")]
148
+ : cmd === "notify" ? ["schema/stale.mjs", d, "--notify", ...pass("source", "path", "version", "json")]
149
+ : cmd === "adapt" ? ["interface/adapters.mjs", "adapt", action ?? "", d, ...pass("json")]
150
+ : ["interface/adapters.mjs", ...positional.slice(1), ...pass("json")];
151
+ const p = spawnSync("node", [join(repo, args[0]), ...args.slice(1)], { stdio: "inherit" });
152
+ process.exit(p.status ?? 1);
153
+ }
154
+
111
155
  if (cmd === "migrate" && (opt("from") || flag("apply"))) {
112
156
  // migrate means rewriting an outside project into a new venture (EE-076): the same as naive migrate.
113
157
  const v = opt("venture");
@@ -127,6 +171,16 @@ if (cmd === "upgrade" || cmd === "migrate") {
127
171
  process.exit(p.status ?? 1);
128
172
  }
129
173
 
174
+ // Model steps hook (use model, EE-073): mentura steps <payload|run|check|waterfall|provenance|confirm> --venture <id> [...].
175
+ // The logic is schema/model-steps.mjs; this passes the venture folder and the other arguments through.
176
+ if (cmd === "steps") {
177
+ const v = opt("venture"), d = v && ventureDir(v, workspace);
178
+ if (!d || !action) usage();
179
+ const pass = argv.slice(argv.indexOf("steps") + 1).filter((a, i, all) => !["--venture", "--workspace"].includes(a) && !["--venture", "--workspace"].includes(all[i - 1]));
180
+ const p = spawnSync("node", [join(repo, "schema/model-steps.mjs"), d, ...pass], { stdio: "inherit" });
181
+ process.exit(p.status ?? 1);
182
+ }
183
+
130
184
  if (cmd === "init") {
131
185
  // The venture scaffold lives in schema/init-venture.mjs; this passes the options through.
132
186
  const id = opt("id");
@@ -148,22 +202,27 @@ if (cmd === "accept") {
148
202
  if (!d) { console.error(`${rt} is not a record route, or no venture ${venture}`); process.exit(1); }
149
203
  const draft = join(d.dir, d.draft);
150
204
  if (!existsSync(draft)) { console.error(`no draft of ${d.route} in ${venture}`); process.exit(1); }
205
+ // Gate group hook (X-2): drafts kept side by side are accepted one at a time with --pick <letter>.
206
+ let chosen;
207
+ try { chosen = pickDraft(d.dir, d.draft, opt("pick")); } catch (e) { console.error(e.message); process.exit(1); }
208
+ const chosenFile = join(d.dir, chosen);
151
209
  const recType = routes().find((x) => x.route === d.route || x.route === d.route.replace(/[^/]+$/, "[id]"))?.record;
152
210
  const touched = foundationOf(recType);
153
211
  if (touched.length) {
154
212
  const get = (o, p) => p.split(".").reduce((x, k) => (x == null ? undefined : x[k]), o);
155
213
  const now = existsSync(join(d.dir, d.file)) ? JSON.parse(readFileSync(join(d.dir, d.file), "utf8")) : {};
156
- const next = JSON.parse(readFileSync(draft, "utf8"));
214
+ const next = JSON.parse(readFileSync(chosenFile, "utf8"));
157
215
  const diff = touched.map((s) => ` ${s.id}:\n now: ${JSON.stringify(get(now, s.attribute) ?? null)}\n draft: ${JSON.stringify(get(next, s.attribute) ?? null)}`).join("\n");
158
216
  const names = new Set([d.route, rt]);
159
217
  const confirmed = opt("confirm") !== undefined ? names.has(opt("confirm")) : await typed(d.route, `Accepting ${d.route} changes these foundation statements:\n${diff}`);
160
218
  if (!confirmed) { console.error("not accepted: the confirmation did not match"); process.exit(1); }
161
219
  }
162
- const w = await call({ venture, route: d.route, action: "write", input: JSON.parse(readFileSync(draft, "utf8")), actor: "person", workspace, via: "cli",
163
- provenance: { activity: "accept", derived_from: [d.draft], note: opt("note") } });
220
+ const w = await call({ venture, route: d.route, action: "write", input: JSON.parse(readFileSync(chosenFile, "utf8")), actor: "person", workspace, via: "cli",
221
+ provenance: { activity: "accept", derived_from: [chosen], note: opt("note") } });
164
222
  if (!w.ok) { console.error(w.error); process.exit(1); }
165
- rmSync(draft);
166
- out(`accepted: ${w.result.written}`);
223
+ // Gate group hook (X-2): removes the draft, its variants and sidecar, and decides the variants decision.
224
+ const settled = settleDraft(d.dir, d.draft, { picked: opt("pick") ?? "a", by: opt("by") });
225
+ out(`accepted: ${w.result.written}${settled.decision ? ` (draft ${settled.picked}; decision ${settled.decision} ${settled.decided ? "decided" : `not decided: ${settled.error}`})` : ""}`);
167
226
  process.exit(0);
168
227
  }
169
228
 
@@ -197,7 +256,7 @@ if (cmd === "understand") {
197
256
  // Mentura got wrong, and confirms the understanding before the ideas are routed.
198
257
  const venture = opt("venture"), dir = venture && ventureDir(venture, workspace);
199
258
  if (!dir) usage();
200
- const hits = ["purpose/ideas/map.mdx", "purpose/ideas/graph.json"].filter((f) => existsSync(join(dir, f)));
259
+ const hits = ["purpose/ideas/map.mdx", "purpose/ideas/graph.json"].filter((f) => existsSync(ventureFile(dir, f)));
201
260
  if (!hits.length) { console.error(`${venture} has no idea map yet: map the idea dump first (purpose/ideas)`); process.exit(1); }
202
261
  if (!(await typed("ideas", `Confirming Mentura's understanding of your ideas: ${hits.join(", ")}`, "Your ideas are routed to their records from this understanding."))) { console.error("not confirmed: the confirmation did not match"); process.exit(1); }
203
262
  for (const f of hits) record(dir, { entity: f, route: f.endsWith(".mdx") ? "purpose/ideas" : "purpose/ideas/graph", activity: "confirm", statement: "ideas", agent: agentFromEnv("person", "cli"), note: opt("note") });
@@ -213,10 +272,10 @@ if (cmd === "confirm") {
213
272
  const s = FRAME.questions.flatMap((q) => q.statements).find((x) => x.id === id);
214
273
  if (!s?.record) { console.error(`${id} is not a foundation statement with a record (see mentura purpose/foundation run)`); process.exit(1); }
215
274
  const rt = routes().find((x) => x.record === s.record && x.collection !== "list")?.route ?? s.record;
216
- const hits = filesOf(dir, s.record).filter((f) => existsSync(join(dir, f)));
275
+ const hits = filesOf(dir, s.record).filter((f) => existsSync(ventureFile(dir, f)));
217
276
  if (!hits.length) { console.error(`${venture} has no ${s.record} record to confirm`); process.exit(1); }
218
277
  const get = (o, p) => p.split(".").reduce((x, k) => (x == null ? undefined : x[k]), o);
219
- const shown = hits.map((f) => ` ${f}: ${JSON.stringify(get(JSON.parse(readFileSync(join(dir, f), "utf8")), s.attribute) ?? null)}`).join("\n");
278
+ const shown = hits.map((f) => ` ${f}: ${JSON.stringify(get(JSON.parse(readFileSync(ventureFile(dir, f), "utf8")), s.attribute) ?? null)}`).join("\n");
220
279
  if (!(await typed(id, `Confirming ${id} as yours:\n${shown}`))) { console.error("not confirmed: the confirmation did not match"); process.exit(1); }
221
280
  for (const f of hits) record(dir, { entity: f, route: rt, activity: "confirm", statement: id, agent: agentFromEnv("person", "cli"), note: opt("note") });
222
281
  out(`confirmed: ${id}`);
@@ -243,6 +302,13 @@ if (cmd === "baseline") {
243
302
  process.exit(0);
244
303
  }
245
304
 
305
+ // Freshness (Revalidating, V-1 to V-8): revalidate, verify, phase and freshness live in ./freshness.mjs.
306
+ if (["revalidate", "verify", "phase", "freshness"].includes(cmd)) process.exit(await (await import("./freshness.mjs")).main(cmd, argv, workspace));
307
+
308
+ // Context hook (interface/context.mjs): navigate and head read a route header first, with its layout once per
309
+ // session (--session), its template and prefetched refs (--task, --prefetch, --no-prefetch, --stream); batch runs
310
+ // a list of calls with progress.
311
+ if (["navigate", "head"].includes(action) || cmd === "batch") { const { cli } = await import("../context.mjs"); process.exit(await cli(argv, { workspace })); }
246
312
  if (!action) usage();
247
313
  const venture = opt("venture");
248
314
  if (!venture && !["template", "layout"].includes(action)) usage();
@@ -251,7 +317,12 @@ const src = opt("input");
251
317
  if (src) input = JSON.parse(src === "-" ? readFileSync(0, "utf8") : readFileSync(src, "utf8"));
252
318
  if (opt("value")) input = { ...(input ?? {}), value: Number.isNaN(Number(opt("value"))) ? opt("value") : Number(opt("value")) };
253
319
  if (flag("as-is")) input = { ...(input ?? {}), as_is: true };
320
+ // wf/writes hook: --where k=v[,k=v] filters a read (L-10); --hierarchy returns its resolution levels (P-28).
321
+ if (["read", "view"].includes(action) && opt("where")) input = { ...(input ?? {}), where: Object.fromEntries(opt("where").split(",").map((kv) => kv.split("=")).map(([k, ...v]) => [k, v.join("=").includes("|") ? v.join("=").split("|") : v.join("=")])) };
322
+ if (["read", "view"].includes(action) && flag("hierarchy")) input = { ...(input ?? {}), hierarchy: true };
254
323
  const provenance = { derived_from: opt("derived-from")?.split(",").filter(Boolean), decision: opt("decision"), note: opt("note") };
324
+ // Gate group hook (X-4): a confidence score computed before the write, for the gate's confidence rules.
325
+ if (opt("confidence") !== undefined) provenance.confidence = Number(opt("confidence"));
255
326
  let r = await call({ venture, route: cmd, action, input, actor: opt("as") === "person" ? "person" : "agent", workspace, via: "cli", provenance });
256
327
  // Asks before running (EE-050): at a terminal, the person chooses; otherwise it prints both choices.
257
328
  if (r.ok && r.needs_input && !flag("json")) {
@@ -267,5 +338,5 @@ if (r.ok && r.needs_input && !flag("json")) {
267
338
  }
268
339
  if (!r.ok) { console.error(r.error ?? JSON.stringify(r.result, null, 2)); process.exit(1); }
269
340
  if (r.deprecated) console.error(`note: ${r.deprecated}`);
270
- const extra = ["not_found", "pending_draft", "fix", "needs_input"].some((k) => r[k]);
341
+ const extra = ["not_found", "pending_draft", "fix", "needs_input", "errors", "filtered_out", "hierarchy", "stale"].some((k) => r[k]); // stale: V-9 (schema/stale.mjs)
271
342
  out(flag("json") || extra ? r : r.result);