naive-framework 0.1.0 → 0.2.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 (254) hide show
  1. package/AGENTS.md +2 -1
  2. package/docs/decisions/decisions.json +27 -0
  3. package/docs/reference/ROUTES.md +36 -37
  4. package/governance/layer.json +2 -1
  5. package/governance/venture-template/roles/venture-roles.json +62 -0
  6. package/interface/acting.mjs +83 -0
  7. package/interface/adapters/claude-code.json +16 -0
  8. package/interface/adapters/codex.json +14 -0
  9. package/interface/adapters/cursor.json +16 -0
  10. package/interface/adapters.mjs +182 -0
  11. package/interface/api/server.mjs +4 -2
  12. package/interface/boundaries.mjs +218 -0
  13. package/interface/brand.mjs +214 -0
  14. package/interface/cli/freshness.mjs +59 -0
  15. package/interface/cli/guard.mjs +6 -0
  16. package/interface/cli/mentura.mjs +78 -8
  17. package/interface/cli/naive.mjs +33 -8
  18. package/interface/context.mjs +524 -0
  19. package/interface/demo/board.mjs +1 -1
  20. package/interface/dev/setup.mjs +84 -0
  21. package/interface/gate-hooks.mjs +36 -0
  22. package/interface/harness/evals.mjs +3 -3
  23. package/interface/harness/run.mjs +4 -4
  24. package/interface/instrumentation.mjs +86 -0
  25. package/interface/kit-boundary.mjs +73 -0
  26. package/interface/layer.json +8 -2
  27. package/interface/mcp/server.mjs +47 -7
  28. package/interface/package-routes.mjs +196 -0
  29. package/interface/provenance.mjs +24 -1
  30. package/interface/reads.mjs +173 -0
  31. package/interface/release-card.mjs +42 -0
  32. package/interface/router.mjs +101 -28
  33. package/interface/skills/naive-setup/SKILL.md +71 -0
  34. package/interface/skills/naive-setup/artifact.html +83 -0
  35. package/interface/vitals.mjs +115 -0
  36. package/interface/writes.mjs +190 -0
  37. package/knowledge/freshness.json +85 -0
  38. package/knowledge/layer.json +11 -3
  39. package/knowledge/templates/glossary.md +23 -0
  40. package/operations/layer.json +10 -3
  41. package/operations/templates/marketing-brand.md +46 -0
  42. package/operations/templates/public-assets.md +39 -0
  43. package/package.json +3 -1
  44. package/packages/catalog.json +8 -0
  45. package/packages/customers/package.json +21 -1
  46. package/packages/deals/package.json +27 -0
  47. package/packages/decisions/package.json +12 -1
  48. package/packages/finance/frames/fund-without-ceding-control.frame.json +177 -0
  49. package/packages/foundation/package.json +8 -1
  50. package/packages/fund/package.json +48 -0
  51. package/packages/marketing/package.json +10 -4
  52. package/packages/money/package.json +24 -3
  53. package/packages/validation/package.json +13 -0
  54. package/platform/README.md +6 -0
  55. package/platform/adapter.json +15 -0
  56. package/platform/integrations/remote-image.mjs +79 -0
  57. package/platform/integrations/rho.mjs +7 -6
  58. package/platform/kits/README.md +8 -1
  59. package/platform/kits/agent-upgrade/kit.json +15 -0
  60. package/platform/kits/agent-upgrade/run.mjs +82 -0
  61. package/platform/kits/agent-upgrade/stand-in-agent.mjs +32 -0
  62. package/platform/kits/validation-kickoff/kit.json +29 -0
  63. package/platform/webhooks/server.mjs +70 -0
  64. package/purpose/frameworks/idea-map/ideamap.mjs +4 -2
  65. package/schema/README.md +1 -0
  66. package/schema/access.mjs +57 -0
  67. package/schema/adapter.schema.json +33 -0
  68. package/schema/assets.mjs +212 -0
  69. package/schema/brand.mjs +373 -0
  70. package/schema/build-release.mjs +61 -0
  71. package/schema/caching.mjs +505 -0
  72. package/schema/check-assets.mjs +24 -0
  73. package/schema/check-brand.mjs +24 -0
  74. package/schema/check-config.mjs +110 -0
  75. package/schema/check-nextjs-status.mjs +3 -1
  76. package/schema/check-people.mjs +70 -0
  77. package/schema/check-sources.mjs +9 -1
  78. package/schema/data-model.json +334 -8
  79. package/schema/deal.schema.json +80 -0
  80. package/schema/decisions.mjs +7 -1
  81. package/schema/examples/frames/judge.mjs +10 -0
  82. package/schema/examples/venture-type/jev.mjs +12 -0
  83. package/schema/examples/workspace/ventures/acme-labs/AGENTS.md +17 -0
  84. package/schema/examples/workspace/ventures/acme-labs/accounts/.gitkeep +0 -0
  85. package/schema/examples/workspace/ventures/acme-labs/decisions/discovery-goal.decision.json +23 -0
  86. package/schema/examples/workspace/ventures/acme-labs/decisions/statement-icp.decision.json +22 -0
  87. package/schema/examples/workspace/ventures/acme-labs/decisions/statement-problem.decision.json +22 -0
  88. package/schema/examples/workspace/ventures/acme-labs/decisions/venture.team.decision.json +37 -0
  89. package/schema/examples/workspace/ventures/acme-labs/knowledge/README.md +3 -0
  90. package/schema/examples/workspace/ventures/acme-labs/knowledge/sources.json +46 -0
  91. package/schema/examples/workspace/ventures/acme-labs/money/30000000-0000-4000-8000-000000000001.json +12 -0
  92. package/schema/examples/workspace/ventures/acme-labs/money/30000000-0000-4000-8000-000000000002.json +12 -0
  93. package/schema/examples/workspace/ventures/acme-labs/money/30000000-0000-4000-8000-000000000003.json +12 -0
  94. package/schema/examples/workspace/ventures/acme-labs/money/30000000-0000-4000-8000-000000000004.json +12 -0
  95. package/schema/examples/workspace/ventures/acme-labs/money/30000000-0000-4000-8000-000000000005.json +12 -0
  96. package/schema/examples/workspace/ventures/acme-labs/money/30000000-0000-4000-8000-000000000006.json +12 -0
  97. package/schema/examples/workspace/ventures/acme-labs/money/30000000-0000-4000-8000-000000000007.json +12 -0
  98. package/schema/examples/workspace/ventures/acme-labs/money/30000000-0000-4000-8000-000000000008.json +12 -0
  99. package/schema/examples/workspace/ventures/acme-labs/money/30000000-0000-4000-8000-000000000009.json +12 -0
  100. package/schema/examples/workspace/ventures/acme-labs/money/30000000-0000-4000-8000-000000000010.json +12 -0
  101. package/schema/examples/workspace/ventures/acme-labs/money/30000000-0000-4000-8000-000000000011.json +12 -0
  102. package/schema/examples/workspace/ventures/acme-labs/money/30000000-0000-4000-8000-000000000012.json +12 -0
  103. package/schema/examples/workspace/ventures/acme-labs/money/30000000-0000-4000-8000-000000000013.json +12 -0
  104. package/schema/examples/workspace/ventures/acme-labs/money/30000000-0000-4000-8000-000000000014.json +12 -0
  105. package/schema/examples/workspace/ventures/acme-labs/money/_streams/transactions/stream.json +5 -0
  106. package/schema/examples/workspace/ventures/acme-labs/money/_summary.json +30 -0
  107. package/schema/examples/workspace/ventures/acme-labs/operations/departments/customer-service/README.md +5 -0
  108. package/schema/examples/workspace/ventures/acme-labs/operations/departments/customer-service/department.json +5 -0
  109. package/schema/examples/workspace/ventures/acme-labs/operations/departments/finance/README.md +5 -0
  110. package/schema/examples/workspace/ventures/acme-labs/operations/departments/finance/department.json +5 -0
  111. package/schema/examples/workspace/ventures/acme-labs/operations/departments/human-capital/README.md +5 -0
  112. package/schema/examples/workspace/ventures/acme-labs/operations/departments/human-capital/department.json +5 -0
  113. package/schema/examples/workspace/ventures/acme-labs/operations/departments/it/README.md +5 -0
  114. package/schema/examples/workspace/ventures/acme-labs/operations/departments/it/department.json +5 -0
  115. package/schema/examples/workspace/ventures/acme-labs/operations/departments/marketing-and-sales/README.md +5 -0
  116. package/schema/examples/workspace/ventures/acme-labs/operations/departments/marketing-and-sales/department.json +5 -0
  117. package/schema/examples/workspace/ventures/acme-labs/operations/departments/products/README.md +5 -0
  118. package/schema/examples/workspace/ventures/acme-labs/operations/departments/products/department.json +5 -0
  119. package/schema/examples/workspace/ventures/acme-labs/operations/departments/strategy/README.md +5 -0
  120. package/schema/examples/workspace/ventures/acme-labs/operations/departments/strategy/department.json +5 -0
  121. package/schema/examples/workspace/ventures/acme-labs/people/founders/maya-chen.json +15 -0
  122. package/schema/examples/workspace/ventures/acme-labs/people/people.json +22 -0
  123. package/schema/examples/workspace/ventures/acme-labs/pillars/brand.md +5 -0
  124. package/schema/examples/workspace/ventures/acme-labs/pillars/business.md +5 -0
  125. package/schema/examples/workspace/ventures/acme-labs/pillars/product.md +5 -0
  126. package/schema/examples/workspace/ventures/acme-labs/products/.gitkeep +0 -0
  127. package/schema/examples/workspace/ventures/acme-labs/products/decisions/product.core-product.decision.json +28 -0
  128. package/schema/examples/workspace/ventures/acme-labs/products/decisions/product.exists.decision.json +38 -0
  129. package/schema/examples/workspace/ventures/acme-labs/products/decisions/product.flagship.decision.json +27 -0
  130. package/schema/examples/workspace/ventures/acme-labs/profile.md +12 -0
  131. package/schema/examples/workspace/ventures/acme-labs/provenance.jsonl +12 -0
  132. package/schema/examples/workspace/ventures/acme-labs/raw/dump.md +6 -0
  133. package/schema/examples/workspace/ventures/acme-labs/venture.json +27 -0
  134. package/schema/examples/workspace/ventures/northstar/AGENTS.md +17 -0
  135. package/schema/examples/workspace/ventures/northstar/accounts/.gitkeep +0 -0
  136. package/schema/examples/workspace/ventures/northstar/deals/ductline.json +8 -0
  137. package/schema/examples/workspace/ventures/northstar/deals/kilnworks.json +14 -0
  138. package/schema/examples/workspace/ventures/northstar/decisions/venture.team.decision.json +31 -0
  139. package/schema/examples/workspace/ventures/northstar/evidence/README.md +3 -0
  140. package/schema/examples/workspace/ventures/northstar/fund/decisions/terms.decision.json +27 -0
  141. package/schema/examples/workspace/ventures/northstar/fund/decisions/terms.first-close.decision.json +27 -0
  142. package/schema/examples/workspace/ventures/northstar/fund/decisions/thesis.decision.json +26 -0
  143. package/schema/examples/workspace/ventures/northstar/fund/terms.json +22 -0
  144. package/schema/examples/workspace/ventures/northstar/fund/thesis.json +22 -0
  145. package/schema/examples/workspace/ventures/northstar/governance/README.md +3 -0
  146. package/schema/examples/workspace/ventures/northstar/governance/departments/external-relationships/charter.md +5 -0
  147. package/schema/examples/workspace/ventures/northstar/governance/departments/external-relationships/department.json +5 -0
  148. package/schema/examples/workspace/ventures/northstar/governance/departments/finance/charter.md +5 -0
  149. package/schema/examples/workspace/ventures/northstar/governance/departments/finance/department.json +5 -0
  150. package/schema/examples/workspace/ventures/northstar/governance/departments/human-capital/charter.md +5 -0
  151. package/schema/examples/workspace/ventures/northstar/governance/departments/human-capital/department.json +5 -0
  152. package/schema/examples/workspace/ventures/northstar/governance/departments/risk-and-compliance/charter.md +5 -0
  153. package/schema/examples/workspace/ventures/northstar/governance/departments/risk-and-compliance/department.json +5 -0
  154. package/schema/examples/workspace/ventures/northstar/governance/departments/strategy/charter.md +5 -0
  155. package/schema/examples/workspace/ventures/northstar/governance/departments/strategy/department.json +5 -0
  156. package/schema/examples/workspace/ventures/northstar/knowledge/README.md +3 -0
  157. package/schema/examples/workspace/ventures/northstar/operations/README.md +3 -0
  158. package/schema/examples/workspace/ventures/northstar/operations/departments/external-relationships/README.md +5 -0
  159. package/schema/examples/workspace/ventures/northstar/operations/departments/external-relationships/department.json +5 -0
  160. package/schema/examples/workspace/ventures/northstar/operations/departments/finance/README.md +5 -0
  161. package/schema/examples/workspace/ventures/northstar/operations/departments/finance/department.json +5 -0
  162. package/schema/examples/workspace/ventures/northstar/operations/departments/human-capital/README.md +5 -0
  163. package/schema/examples/workspace/ventures/northstar/operations/departments/human-capital/department.json +5 -0
  164. package/schema/examples/workspace/ventures/northstar/operations/departments/risk-and-compliance/README.md +5 -0
  165. package/schema/examples/workspace/ventures/northstar/operations/departments/risk-and-compliance/department.json +5 -0
  166. package/schema/examples/workspace/ventures/northstar/operations/departments/strategy/README.md +5 -0
  167. package/schema/examples/workspace/ventures/northstar/operations/departments/strategy/department.json +5 -0
  168. package/schema/examples/workspace/ventures/northstar/people/founders/ana-ruiz.json +13 -0
  169. package/schema/examples/workspace/ventures/northstar/people/founders/ben-okafor.json +7 -0
  170. package/schema/examples/workspace/ventures/northstar/people/people.json +27 -0
  171. package/schema/examples/workspace/ventures/northstar/pillars/brand.md +5 -0
  172. package/schema/examples/workspace/ventures/northstar/pillars/business.md +5 -0
  173. package/schema/examples/workspace/ventures/northstar/pillars/product.md +5 -0
  174. package/schema/examples/workspace/ventures/northstar/portfolio/kilnworks/AGENTS.md +17 -0
  175. package/schema/examples/workspace/ventures/northstar/portfolio/kilnworks/accounts/.gitkeep +0 -0
  176. package/schema/examples/workspace/ventures/northstar/portfolio/kilnworks/decisions/venture.team.decision.json +31 -0
  177. package/schema/examples/workspace/ventures/northstar/portfolio/kilnworks/knowledge/README.md +3 -0
  178. package/schema/examples/workspace/ventures/northstar/portfolio/kilnworks/operations/departments/customer-service/README.md +5 -0
  179. package/schema/examples/workspace/ventures/northstar/portfolio/kilnworks/operations/departments/customer-service/department.json +5 -0
  180. package/schema/examples/workspace/ventures/northstar/portfolio/kilnworks/operations/departments/finance/README.md +5 -0
  181. package/schema/examples/workspace/ventures/northstar/portfolio/kilnworks/operations/departments/finance/department.json +5 -0
  182. package/schema/examples/workspace/ventures/northstar/portfolio/kilnworks/operations/departments/human-capital/README.md +5 -0
  183. package/schema/examples/workspace/ventures/northstar/portfolio/kilnworks/operations/departments/human-capital/department.json +5 -0
  184. package/schema/examples/workspace/ventures/northstar/portfolio/kilnworks/operations/departments/it/README.md +5 -0
  185. package/schema/examples/workspace/ventures/northstar/portfolio/kilnworks/operations/departments/it/department.json +5 -0
  186. package/schema/examples/workspace/ventures/northstar/portfolio/kilnworks/operations/departments/marketing-and-sales/README.md +5 -0
  187. package/schema/examples/workspace/ventures/northstar/portfolio/kilnworks/operations/departments/marketing-and-sales/department.json +5 -0
  188. package/schema/examples/workspace/ventures/northstar/portfolio/kilnworks/operations/departments/products/README.md +5 -0
  189. package/schema/examples/workspace/ventures/northstar/portfolio/kilnworks/operations/departments/products/department.json +5 -0
  190. package/schema/examples/workspace/ventures/northstar/portfolio/kilnworks/operations/departments/strategy/README.md +5 -0
  191. package/schema/examples/workspace/ventures/northstar/portfolio/kilnworks/operations/departments/strategy/department.json +5 -0
  192. package/schema/examples/workspace/ventures/northstar/portfolio/kilnworks/pillars/brand.md +5 -0
  193. package/schema/examples/workspace/ventures/northstar/portfolio/kilnworks/pillars/business.md +5 -0
  194. package/schema/examples/workspace/ventures/northstar/portfolio/kilnworks/pillars/product.md +5 -0
  195. package/schema/examples/workspace/ventures/northstar/portfolio/kilnworks/products/.gitkeep +0 -0
  196. package/schema/examples/workspace/ventures/northstar/portfolio/kilnworks/products/decisions/product.core-product.decision.json +28 -0
  197. package/schema/examples/workspace/ventures/northstar/portfolio/kilnworks/products/decisions/product.exists.decision.json +32 -0
  198. package/schema/examples/workspace/ventures/northstar/portfolio/kilnworks/products/decisions/product.flagship.decision.json +27 -0
  199. package/schema/examples/workspace/ventures/northstar/portfolio/kilnworks/profile.md +5 -0
  200. package/schema/examples/workspace/ventures/northstar/portfolio/kilnworks/venture.json +27 -0
  201. package/schema/examples/workspace/ventures/northstar/products/.gitkeep +0 -0
  202. package/schema/examples/workspace/ventures/northstar/profile.md +5 -0
  203. package/schema/examples/workspace/ventures/northstar/provenance.jsonl +3 -0
  204. package/schema/examples/workspace/ventures/northstar/purpose/README.md +3 -0
  205. package/schema/examples/workspace/ventures/northstar/venture.json +49 -0
  206. package/schema/founder.schema.json +72 -0
  207. package/schema/frame.schema.json +240 -0
  208. package/schema/frames.mjs +228 -0
  209. package/schema/freshness-log.schema.json +40 -0
  210. package/schema/freshness-profiles.schema.json +44 -0
  211. package/schema/freshness.mjs +473 -0
  212. package/schema/fund-terms.schema.json +107 -0
  213. package/schema/fund-thesis.schema.json +87 -0
  214. package/schema/gate-drafts.mjs +130 -0
  215. package/schema/gate.mjs +105 -11
  216. package/schema/glossary.schema.json +25 -0
  217. package/schema/init-venture.mjs +38 -8
  218. package/schema/judgment.schema.json +38 -0
  219. package/schema/kit.schema.json +47 -0
  220. package/schema/kits.mjs +24 -0
  221. package/schema/knowledge-sources.schema.json +145 -15
  222. package/schema/layer.json +16 -3
  223. package/schema/marketing-brand.schema.json +116 -0
  224. package/schema/migrations/kit-to-package.mjs +94 -0
  225. package/schema/migrations/package-layout.mjs +86 -0
  226. package/schema/model-steps.mjs +717 -0
  227. package/schema/money-summary.schema.json +138 -0
  228. package/schema/naive-config.mjs +165 -0
  229. package/schema/naive-config.schema.json +268 -0
  230. package/schema/need-now.json +171 -0
  231. package/schema/need-now.mjs +3 -2
  232. package/schema/packages.mjs +10 -5
  233. package/schema/people.schema.json +86 -0
  234. package/schema/public-assets.schema.json +76 -0
  235. package/schema/release-read.mjs +111 -0
  236. package/schema/releases.mjs +497 -0
  237. package/schema/setup.mjs +164 -0
  238. package/schema/stale.mjs +111 -0
  239. package/schema/start-release.mjs +41 -0
  240. package/schema/structure.mjs +186 -0
  241. package/schema/systems-catalog.json +126 -0
  242. package/schema/systems-catalog.schema.json +78 -0
  243. package/schema/trigger.schema.json +44 -0
  244. package/schema/triggers.mjs +237 -0
  245. package/schema/upgrade-registry.schema.json +60 -0
  246. package/schema/upgrade-venture.mjs +96 -118
  247. package/schema/upgrades.json +69 -0
  248. package/schema/upgrades.mjs +385 -0
  249. package/schema/validate.py +19 -10
  250. package/schema/venture-role.schema.json +64 -0
  251. package/schema/venture-type.mjs +62 -0
  252. package/schema/venture-types.json +346 -0
  253. package/schema/venture-types.schema.json +129 -0
  254. package/schema/venture.schema.json +53 -0
@@ -0,0 +1,214 @@
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
+
29
+ const VERSION = "0.1.0";
30
+ const today = () => new Date().toISOString();
31
+
32
+ /** The facts code reads, in the register's shape. */
33
+ export const factsFor = (dir, path) => {
34
+ const f = factsOf(readFileSync(join(dir, path)));
35
+ 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));
36
+ };
37
+
38
+ const emptyRegister = () => ({ version: VERSION, assets: [] });
39
+
40
+ /** Writes the register through the router: schema first, then provenance. */
41
+ export async function writeRegister(venture, workspace, reg, { actor = "agent", note } = {}) {
42
+ return call({ venture, route: "public/assets", action: "write", input: reg, actor, workspace, via: "cli", provenance: { note } });
43
+ }
44
+
45
+ /** Registers one file in public/ with its source, license and credit; code fills its facts. */
46
+ export async function addAsset({ venture, workspace, path, source, license, credit, alt, decorative, origin_url, design_system, actor = "agent" }) {
47
+ const dir = ventureDir(venture, workspace);
48
+ if (!dir) return { ok: false, error: `no venture ${venture}` };
49
+ if (!path?.startsWith("public/")) return { ok: false, error: `${path} is not in public/` };
50
+ if (!existsSync(join(dir, path))) return { ok: false, error: `${path} is not in ${venture}` };
51
+ const reg = registerOf(dir) ?? emptyRegister();
52
+ 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));
53
+ reg.assets = [...reg.assets.filter((a) => a.path !== path), entry].sort((a, b) => a.path.localeCompare(b.path));
54
+ const w = await writeRegister(venture, workspace, reg, { actor, note: `registered ${path}; facts read by code` });
55
+ 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` });
56
+ return w.ok ? { ok: true, result: { registered: path, facts: entry.facts, alt: entry.alt ?? null } } : w;
57
+ }
58
+
59
+ /** Refills every entry's facts from its file. Only code writes facts. */
60
+ export async function fill({ venture, workspace }) {
61
+ const dir = ventureDir(venture, workspace);
62
+ if (!dir) return { ok: false, error: `no venture ${venture}` };
63
+ const reg = registerOf(dir);
64
+ if (!reg) return { ok: false, error: `${venture} has no ${REGISTER}; register a file first: mentura assets add public/<file> ...` };
65
+ const changed = [], missing = [];
66
+ for (const a of reg.assets) {
67
+ if (!existsSync(join(dir, a.path))) { missing.push(a.path); continue; }
68
+ const f = factsFor(dir, a.path);
69
+ if (!a.facts || ["type", "width", "height", "bytes", "sha256"].some((k) => (a.facts[k] ?? null) !== (f[k] ?? null))) { a.facts = f; changed.push(a.path); }
70
+ }
71
+ const listed = new Set(reg.assets.map((a) => a.path));
72
+ const unregistered = allFacts(dir).map((x) => x.path).filter((p) => !listed.has(p));
73
+ if (changed.length) { const w = await writeRegister(venture, workspace, reg, { note: `facts filled by code: ${changed.join(", ")}` }); if (!w.ok) return w; }
74
+ return { ok: true, result: { filled: changed, missing, unregistered } };
75
+ }
76
+
77
+ /** Writes alt text. An agent's carries agent provenance until a person confirms it; a person's is confirmed as written. */
78
+ export async function setAlt({ venture, workspace, path, text, actor = "agent" }) {
79
+ const dir = ventureDir(venture, workspace);
80
+ if (!dir) return { ok: false, error: `no venture ${venture}` };
81
+ const reg = registerOf(dir);
82
+ const a = reg?.assets.find((x) => x.path === path);
83
+ if (!a) return { ok: false, error: `${path} is not registered; mentura assets add ${path} first` };
84
+ a.alt = { text, by: actor === "person" ? "person" : "agent" };
85
+ const w = await writeRegister(venture, workspace, reg, { actor, note: `alt text of ${path} by ${a.alt.by}` });
86
+ if (!w.ok) return w;
87
+ 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` });
88
+ return { ok: true, result: { path, alt: a.alt, confirmed: actor === "person" } };
89
+ }
90
+
91
+ /** A person confirms an alt text exactly as it stands. The confirmation is bound to the text: change it and it is pending again. */
92
+ export function confirmAlt({ venture, workspace, path, by }) {
93
+ const dir = ventureDir(venture, workspace);
94
+ if (!dir) return { ok: false, error: `no venture ${venture}` };
95
+ if (!by) return { ok: false, error: "say who is confirming: --by <name>" };
96
+ const a = registerOf(dir)?.assets.find((x) => x.path === path);
97
+ if (!a?.alt?.text) return { ok: false, error: `${path} has no alt text to confirm` };
98
+ 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}` });
99
+ return { ok: true, result: { path, alt: a.alt.text, confirmed_by: by, provenance: e.hash } };
100
+ }
101
+
102
+ const rawId = (url) => {
103
+ const u = new URL(url);
104
+ const slug = `${u.host}-${u.pathname.split("/").filter(Boolean).pop() ?? "image"}`.toLowerCase().replace(/\.[a-z0-9]+$/, "").replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "");
105
+ return slug.slice(0, 60) || "remote-image";
106
+ };
107
+
108
+ /**
109
+ * A remote image as a link with a note in raw/ (J-3): raw/sources/<id>/source.json says where it is,
110
+ * text.md is the note, and the register lists it under remote. Nothing is fetched here. The platform
111
+ * fetcher passes `fetched` (facts of the private original) and `copy` when it kept a public copy.
112
+ */
113
+ export async function addRemote({ venture, workspace, url, license, credit, id, alt, note, fetched, copy, actor = "agent" }) {
114
+ const dir = ventureDir(venture, workspace);
115
+ if (!dir) return { ok: false, error: `no venture ${venture}` };
116
+ try { new URL(url); } catch { return { ok: false, error: `${url} is not a URL` }; }
117
+ const rid = id ?? rawId(url);
118
+ const reg = registerOf(dir) ?? emptyRegister();
119
+ if (!existsSync(join(dir, "raw/sources", rid, "source.json"))) {
120
+ 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)" } });
121
+ if (!w.ok) return w;
122
+ 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}`] : []), "",
123
+ 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");
124
+ writeFileSync(join(dir, "raw/sources", rid, "text.md"), text);
125
+ 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)" });
126
+ }
127
+ 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));
128
+ reg.remote = [...(reg.remote ?? []).filter((r) => r.url !== url), entry];
129
+ const w = await writeRegister(venture, workspace, reg, { actor, note: `remote image ${url}` });
130
+ return w.ok ? { ok: true, result: { remote: url, raw: `raw/sources/${rid}/`, copy: copy ?? null } } : w;
131
+ }
132
+
133
+ /** The hosts an agent may fetch from, a setting the platform fetcher enforces (J-3). */
134
+ export async function setHosts({ venture, workspace, hosts, actor = "agent" }) {
135
+ const dir = ventureDir(venture, workspace);
136
+ if (!dir) return { ok: false, error: `no venture ${venture}` };
137
+ const reg = registerOf(dir) ?? emptyRegister();
138
+ reg.remote_hosts = hosts;
139
+ const w = await writeRegister(venture, workspace, reg, { actor, note: `hosts an agent may fetch from: ${hosts.join(", ") || "none"}` });
140
+ return w.ok ? { ok: true, result: { remote_hosts: hosts } } : w;
141
+ }
142
+
143
+ /** mentura brand ... and mentura assets ...: returns the exit code. */
144
+ export async function main(argv, { workspace } = {}) {
145
+ const opt = (n) => { const i = argv.indexOf(`--${n}`); return i >= 0 ? argv[i + 1] : undefined; };
146
+ const flag = (n) => argv.includes(`--${n}`);
147
+ const pos = argv.filter((a, i) => !a.startsWith("--") && !(i > 0 && argv[i - 1].startsWith("--") && !["json", "decorative"].includes(argv[i - 1].slice(2))));
148
+ const [group, verb, arg] = pos;
149
+ const venture = opt("venture");
150
+ const actor = opt("as") === "person" ? "person" : "agent";
151
+ const show = (r, text) => {
152
+ if (!r.ok) { console.error(r.error ?? JSON.stringify(r, null, 2)); return 1; }
153
+ console.log(flag("json") || text === undefined ? JSON.stringify(r.result, null, 2) : text);
154
+ return 0;
155
+ };
156
+ if (!venture) { console.error(`mentura ${group} ${verb ?? ""}: --venture <id> is required`); return 2; }
157
+ const dir = ventureDir(venture, workspace);
158
+ if (!dir) { console.error(`no venture ${venture}`); return 1; }
159
+
160
+ if (group === "assets") {
161
+ 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}`);
162
+ 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); }
163
+ 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" : ""}`);
164
+ if (verb === "confirm") {
165
+ // The person types the path to confirm, as with foundation statements (EE-051).
166
+ if (opt("confirm") !== arg) { console.error(`not confirmed: pass --confirm ${arg} to confirm the alt text of ${arg} as it stands`); return 1; }
167
+ return show(confirmAlt({ venture, workspace, path: arg, by: opt("by") ?? process.env.MENTURA_PERSON }), `confirmed: alt text of ${arg}`);
168
+ }
169
+ 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}`);
170
+ if (verb === "hosts") return show(await setHosts({ venture, workspace, hosts: (arg ?? "").split(",").map((h) => h.trim()).filter(Boolean), actor }), `remote_hosts: ${arg ?? "none"}`);
171
+ 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");
172
+ if (verb === "check") {
173
+ const r = checkAssets(dir);
174
+ if (flag("json")) console.log(JSON.stringify(r, null, 2));
175
+ 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`); }
176
+ return r.ok ? 0 : 1;
177
+ }
178
+ }
179
+
180
+ if (group === "brand") {
181
+ const brand = brandOf(dir);
182
+ const need = () => { console.error(`${venture} has no brand yet: write marketing/brand.json (mentura marketing/brand template)`); return 1; };
183
+ if (verb === "tokens" || verb === "css" || verb === "head") {
184
+ if (!brand) return need();
185
+ const t = tokens(dir, brand);
186
+ return show({ ok: true, result: verb === "tokens" ? t.tokens : verb === "head" ? t.head : t.css }, verb === "css" ? t.css.trimEnd() : undefined);
187
+ }
188
+ if (verb === "resolve") { if (!brand) return need(); return show({ ok: true, result: resolveBrand(brand, arg ?? "") }); }
189
+ 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}`); }
190
+ if (verb === "voice") {
191
+ const v = opt("text") !== undefined ? { violations: (await import("../schema/brand.mjs")).voiceCheck(opt("text"), brand ? resolveBrand(brand, opt("for") ?? "").voice.rules : []) } : voice(dir, brand);
192
+ const lines = v.violations.map((x) => `${x.file ? `${x.file}:` : "line "}${x.line} "${x.found}" breaks ${x.rule}${x.use ? `; use "${x.use}"` : ""}`);
193
+ if (flag("json")) console.log(JSON.stringify(v, null, 2)); else console.log(lines.join("\n") || "keeps to the voice rules");
194
+ return v.violations.length ? 1 : 0;
195
+ }
196
+ if (verb === "check") {
197
+ const r = checkBrand(dir);
198
+ if (flag("json")) console.log(JSON.stringify(r, null, 2));
199
+ 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`); }
200
+ return r.ok ? 0 : 1;
201
+ }
202
+ if (verb === "sitemap") {
203
+ const s = sitemapOf(dir);
204
+ if (!s) { console.error(`${venture} has no marketing sitemap yet: mentura operations/marketing/sitemap run, then write it to marketing/sitemap`); return 1; }
205
+ let xml;
206
+ try { xml = sitemapXml(s.record, opt("base") ?? ""); } catch (e) { console.error(`${e.message}; pass --base https://<your site>`); return 1; }
207
+ 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; }
208
+ process.stdout.write(xml);
209
+ return 0;
210
+ }
211
+ }
212
+ console.error(readFileSync(new URL(import.meta.url), "utf8").split("\n").filter((l) => l.startsWith("// mentura")).map((l) => l.slice(5)).join("\n"));
213
+ return 2;
214
+ }
@@ -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,6 +63,8 @@ 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";
50
69
 
51
70
  // The danger zone (EE-051): a foundation statement is accepted or confirmed only when the person types
@@ -68,7 +87,7 @@ const here = !argv.includes("--venture") && !argv.includes("--workspace") ? find
68
87
  if (here) { argv.push("--venture", asRouted(here).venture, "--workspace", asRouted(here).workspace); process.env.MENTURA_WORKSPACE = asRouted(here).workspace; }
69
88
  const opt = (name) => { const i = argv.indexOf(`--${name}`); return i >= 0 ? argv[i + 1] : undefined; };
70
89
  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))));
90
+ const positional = argv.filter((a, i) => !a.startsWith("--") && !(i > 0 && argv[i - 1].startsWith("--") && !["json", "hierarchy"].includes(argv[i - 1].slice(2))));
72
91
  const workspace = opt("workspace") ? resolve(opt("workspace")) : undefined;
73
92
  const out = (x) => console.log(typeof x === "string" ? x : JSON.stringify(x, null, 2));
74
93
 
@@ -95,6 +114,9 @@ if (cmd === "ventures") {
95
114
  process.exit(0);
96
115
  }
97
116
 
117
+ // 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.
118
+ if (cmd === "brand" || cmd === "assets") process.exit(await (await import("../brand.mjs")).main(argv, { workspace }));
119
+
98
120
  if (cmd === "add") {
99
121
  // Imports one function into a venture (EE-047, EE-048, EE-071). department is the alias for function for one release.
100
122
  const v = opt("venture");
@@ -108,6 +130,27 @@ if (cmd === "typegen") {
108
130
  process.exit(p.status ?? 1);
109
131
  }
110
132
 
133
+ // Upgrades hook (M-12, E-7, V-9, Z-4): these commands live in their own modules; this only dispatches.
134
+ // mentura triggers --venture <id> [--fire] [--json]
135
+ // the venture's triggers, their runs and open failures; --fire runs what new events start
136
+ // mentura stale --venture <id> [--json] summaries whose system of record changed since they were written
137
+ // mentura notify --venture <id> --source <id> --path <p> [--version <v>]
138
+ // a system of record says <p> changed: mark what reads it stale (a webhook does this on Mentura)
139
+ // mentura adapt <adapter> --venture <id> write a harness's entry files into the venture (claude-code, codex, cursor)
140
+ // mentura adapters [verify [<adapter>]] [--json] the adapters, or run the framework's tests an adapter must pass
141
+ if (["triggers", "stale", "notify", "adapt", "adapters"].includes(cmd)) {
142
+ const d = ["triggers", "stale", "notify", "adapt"].includes(cmd) ? (opt("venture") ? ventureDir(opt("venture"), workspace) : null) : null;
143
+ if (cmd !== "adapters" && !d) { console.error(opt("venture") ? `no venture ${opt("venture")}` : `mentura ${cmd} needs --venture <id>`); process.exit(2); }
144
+ const pass = (...names) => names.flatMap((n) => argv.flatMap((a, i) => (a === `--${n}` ? [a, ...(argv[i + 1] && !argv[i + 1].startsWith("--") ? [argv[i + 1]] : [])] : [])));
145
+ const args = cmd === "triggers" ? ["schema/triggers.mjs", d, ...pass("fire", "json")]
146
+ : cmd === "stale" ? ["schema/stale.mjs", d, ...pass("json")]
147
+ : cmd === "notify" ? ["schema/stale.mjs", d, "--notify", ...pass("source", "path", "version", "json")]
148
+ : cmd === "adapt" ? ["interface/adapters.mjs", "adapt", action ?? "", d, ...pass("json")]
149
+ : ["interface/adapters.mjs", ...positional.slice(1), ...pass("json")];
150
+ const p = spawnSync("node", [join(repo, args[0]), ...args.slice(1)], { stdio: "inherit" });
151
+ process.exit(p.status ?? 1);
152
+ }
153
+
111
154
  if (cmd === "migrate" && (opt("from") || flag("apply"))) {
112
155
  // migrate means rewriting an outside project into a new venture (EE-076): the same as naive migrate.
113
156
  const v = opt("venture");
@@ -127,6 +170,16 @@ if (cmd === "upgrade" || cmd === "migrate") {
127
170
  process.exit(p.status ?? 1);
128
171
  }
129
172
 
173
+ // Model steps hook (use model, EE-073): mentura steps <payload|run|check|waterfall|provenance|confirm> --venture <id> [...].
174
+ // The logic is schema/model-steps.mjs; this passes the venture folder and the other arguments through.
175
+ if (cmd === "steps") {
176
+ const v = opt("venture"), d = v && ventureDir(v, workspace);
177
+ if (!d || !action) usage();
178
+ const pass = argv.slice(argv.indexOf("steps") + 1).filter((a, i, all) => !["--venture", "--workspace"].includes(a) && !["--venture", "--workspace"].includes(all[i - 1]));
179
+ const p = spawnSync("node", [join(repo, "schema/model-steps.mjs"), d, ...pass], { stdio: "inherit" });
180
+ process.exit(p.status ?? 1);
181
+ }
182
+
130
183
  if (cmd === "init") {
131
184
  // The venture scaffold lives in schema/init-venture.mjs; this passes the options through.
132
185
  const id = opt("id");
@@ -148,22 +201,27 @@ if (cmd === "accept") {
148
201
  if (!d) { console.error(`${rt} is not a record route, or no venture ${venture}`); process.exit(1); }
149
202
  const draft = join(d.dir, d.draft);
150
203
  if (!existsSync(draft)) { console.error(`no draft of ${d.route} in ${venture}`); process.exit(1); }
204
+ // Gate group hook (X-2): drafts kept side by side are accepted one at a time with --pick <letter>.
205
+ let chosen;
206
+ try { chosen = pickDraft(d.dir, d.draft, opt("pick")); } catch (e) { console.error(e.message); process.exit(1); }
207
+ const chosenFile = join(d.dir, chosen);
151
208
  const recType = routes().find((x) => x.route === d.route || x.route === d.route.replace(/[^/]+$/, "[id]"))?.record;
152
209
  const touched = foundationOf(recType);
153
210
  if (touched.length) {
154
211
  const get = (o, p) => p.split(".").reduce((x, k) => (x == null ? undefined : x[k]), o);
155
212
  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"));
213
+ const next = JSON.parse(readFileSync(chosenFile, "utf8"));
157
214
  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
215
  const names = new Set([d.route, rt]);
159
216
  const confirmed = opt("confirm") !== undefined ? names.has(opt("confirm")) : await typed(d.route, `Accepting ${d.route} changes these foundation statements:\n${diff}`);
160
217
  if (!confirmed) { console.error("not accepted: the confirmation did not match"); process.exit(1); }
161
218
  }
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") } });
219
+ const w = await call({ venture, route: d.route, action: "write", input: JSON.parse(readFileSync(chosenFile, "utf8")), actor: "person", workspace, via: "cli",
220
+ provenance: { activity: "accept", derived_from: [chosen], note: opt("note") } });
164
221
  if (!w.ok) { console.error(w.error); process.exit(1); }
165
- rmSync(draft);
166
- out(`accepted: ${w.result.written}`);
222
+ // Gate group hook (X-2): removes the draft, its variants and sidecar, and decides the variants decision.
223
+ const settled = settleDraft(d.dir, d.draft, { picked: opt("pick") ?? "a", by: opt("by") });
224
+ out(`accepted: ${w.result.written}${settled.decision ? ` (draft ${settled.picked}; decision ${settled.decision} ${settled.decided ? "decided" : `not decided: ${settled.error}`})` : ""}`);
167
225
  process.exit(0);
168
226
  }
169
227
 
@@ -243,6 +301,13 @@ if (cmd === "baseline") {
243
301
  process.exit(0);
244
302
  }
245
303
 
304
+ // Freshness (Revalidating, V-1 to V-8): revalidate, verify, phase and freshness live in ./freshness.mjs.
305
+ if (["revalidate", "verify", "phase", "freshness"].includes(cmd)) process.exit(await (await import("./freshness.mjs")).main(cmd, argv, workspace));
306
+
307
+ // Context hook (interface/context.mjs): navigate and head read a route header first, with its layout once per
308
+ // session (--session), its template and prefetched refs (--task, --prefetch, --no-prefetch, --stream); batch runs
309
+ // a list of calls with progress.
310
+ if (["navigate", "head"].includes(action) || cmd === "batch") { const { cli } = await import("../context.mjs"); process.exit(await cli(argv, { workspace })); }
246
311
  if (!action) usage();
247
312
  const venture = opt("venture");
248
313
  if (!venture && !["template", "layout"].includes(action)) usage();
@@ -251,7 +316,12 @@ const src = opt("input");
251
316
  if (src) input = JSON.parse(src === "-" ? readFileSync(0, "utf8") : readFileSync(src, "utf8"));
252
317
  if (opt("value")) input = { ...(input ?? {}), value: Number.isNaN(Number(opt("value"))) ? opt("value") : Number(opt("value")) };
253
318
  if (flag("as-is")) input = { ...(input ?? {}), as_is: true };
319
+ // wf/writes hook: --where k=v[,k=v] filters a read (L-10); --hierarchy returns its resolution levels (P-28).
320
+ 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("=")])) };
321
+ if (["read", "view"].includes(action) && flag("hierarchy")) input = { ...(input ?? {}), hierarchy: true };
254
322
  const provenance = { derived_from: opt("derived-from")?.split(",").filter(Boolean), decision: opt("decision"), note: opt("note") };
323
+ // Gate group hook (X-4): a confidence score computed before the write, for the gate's confidence rules.
324
+ if (opt("confidence") !== undefined) provenance.confidence = Number(opt("confidence"));
255
325
  let r = await call({ venture, route: cmd, action, input, actor: opt("as") === "person" ? "person" : "agent", workspace, via: "cli", provenance });
256
326
  // Asks before running (EE-050): at a terminal, the person chooses; otherwise it prints both choices.
257
327
  if (r.ok && r.needs_input && !flag("json")) {
@@ -267,5 +337,5 @@ if (r.ok && r.needs_input && !flag("json")) {
267
337
  }
268
338
  if (!r.ok) { console.error(r.error ?? JSON.stringify(r.result, null, 2)); process.exit(1); }
269
339
  if (r.deprecated) console.error(`note: ${r.deprecated}`);
270
- const extra = ["not_found", "pending_draft", "fix", "needs_input"].some((k) => r[k]);
340
+ 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
341
  out(flag("json") || extra ? r : r.result);
@@ -3,13 +3,17 @@
3
3
  // venture.json in the current directory or its parents. mentura stays for platform commands and as an
4
4
  // alias, so `mentura typegen` and `mentura upgrade` run these same commands.
5
5
  //
6
- // naive dev [--port 4331] the board, live: edit a record and it updates; each saved
6
+ // naive dev [--port 4331] the setup page at /setup and the board, live: edit a record and it updates; each saved
7
7
  // record is checked against its schema as you go (I-17, I-24)
8
8
  // naive build check every package and write a release (I-17)
9
9
  // naive start serve the latest release read-only (I-17)
10
10
  // naive check run every check that applies to the venture (I-9)
11
11
  // naive info versions, the venture, and which agents are connected (I-6)
12
12
  // naive mcp serve the venture to an agent over MCP (stdio)
13
+ // naive setup [state|artifact|answer|point|confirm] set the venture up: the state, an artifact of it, and
14
+ // confirming the answers the model relayed (EE-083)
15
+ // naive frame "<the founder's words>" match the words to a frame in the decision library, judge its
16
+ // premises against the venture and propose its decisions, open
13
17
  // naive typegen write .naive/types/ from the route table and the MCP tools
14
18
  // naive upgrade [<venture dir>] [--framework] move a venture onto the next Naive layout; --framework first
15
19
  // updates the installed framework with the venture's package manager (I-29)
@@ -62,10 +66,21 @@ async function dev(dir) {
62
66
  const { workspace, venture } = asRouted(dir);
63
67
  process.env.MENTURA_WORKSPACE = workspace;
64
68
  const port = await free(Number(opt("port") ?? 4331));
69
+ // One origin: /setup is the setup page (interface/dev/setup.mjs); everything else is the board, served
70
+ // on a port of its own and passed through.
65
71
  const { serve } = await import("../demo/board.mjs");
66
- serve(port, venture);
72
+ const { handle } = await import("../dev/setup.mjs");
73
+ const boardPort = await free(port + 1000);
74
+ serve(boardPort, venture);
75
+ const { createServer: http, request } = await import("node:http");
76
+ http(async (req, res) => {
77
+ if (await handle(req, res, dir)) return;
78
+ const up = request({ host: "127.0.0.1", port: boardPort, path: req.url, method: req.method, headers: req.headers }, (r) => { res.writeHead(r.statusCode, r.headers); r.pipe(res); });
79
+ up.on("error", (e) => { res.writeHead(502); res.end(String(e.message)); });
80
+ req.pipe(up);
81
+ }).listen(port, "127.0.0.1");
67
82
  const name = JSON.parse(readFileSync(join(dir, "venture.json"), "utf8")).name ?? venture;
68
- console.log(`\n ▲ Naive ${version()}\n - Venture: ${name} (${relative(process.cwd(), dir) || "."})\n - Board: http://127.0.0.1:${port}/\n - Map: http://127.0.0.1:${port}/map\n\n Watching for changes. Each saved record is checked against its schema. Ctrl-C to stop.\n`);
83
+ console.log(`\n ▲ Naive ${version()}\n - Venture: ${name} (${relative(process.cwd(), dir) || "."})\n - Setup: http://127.0.0.1:${port}/setup\n - Board: http://127.0.0.1:${port}/\n - Map: http://127.0.0.1:${port}/map\n\n Watching for changes. Each saved record is checked against its schema. Ctrl-C to stop.\n`);
69
84
  // Live feedback on save (I-24): a record that no longer matches its schema says so at once.
70
85
  const pending = new Map();
71
86
  watch(dir, { recursive: true }, (_, f) => {
@@ -85,13 +100,12 @@ async function dev(dir) {
85
100
  });
86
101
  }
87
102
 
88
- // The checks that read a workspace, run on the folder that holds this venture.
89
- const CHECKS = ["check-ventures", "check-provenance", "check-sources", "check-data-packages", "check-governance", "check-telos", "check-publication", "check-private", "check-where-used"];
103
+ // The checks that read a workspace, run on this venture: it and the ventures nested in it, not its neighbours.
104
+ const CHECKS = ["check-ventures", "check-people", "check-provenance", "check-sources", "check-data-packages", "check-governance", "check-telos", "check-publication", "check-private", "check-where-used"];
90
105
  function check(dir) {
91
- const { workspace } = asRouted(dir);
92
106
  let failed = 0;
93
107
  for (const c of CHECKS) {
94
- const r = spawnSync("node", [join(repo, `schema/${c}.mjs`), workspace], { encoding: "utf8" });
108
+ const r = spawnSync("node", [join(repo, `schema/${c}.mjs`), dir], { encoding: "utf8" });
95
109
  const last = (r.stdout + r.stderr).trim().split("\n").filter(Boolean).at(-1) ?? "";
96
110
  if (r.status !== 0) failed++;
97
111
  console.log(`${r.status === 0 ? "pass" : "FAIL"} ${c.replace("check-", "")}: ${last}`);
@@ -150,7 +164,18 @@ else if (cmd === "build" || cmd === "start") {
150
164
  // Outside a venture with none named, upgrade-venture.mjs prints its usage.
151
165
  const dir = rest[0] && !rest[0].startsWith("--") ? resolve(rest[0]) : (opt("venture") ? here() : findVenture());
152
166
  if (dir && rest.includes("--framework") && upgradeFramework(dir) !== 0) process.exit(1);
153
- process.exit(run("schema/upgrade-venture.mjs", dir ? [dir] : []));
167
+ // Flags (--check, --guide, --adopt, --canary, --agent, --accept, --confirm) pass through to upgrade-venture.mjs; --framework and --venture are handled here.
168
+ const flags = rest.filter((a, i) => !(i === 0 && !a.startsWith("--")) && a !== "--framework" && a !== "--venture" && rest[i - 1] !== "--venture");
169
+ process.exit(run("schema/upgrade-venture.mjs", dir ? [dir, ...flags] : flags));
170
+ } else if (cmd === "frame") {
171
+ // The decision library (schema/frames.mjs): naive frame "<the founder's words>" runs the frame those words fit.
172
+ const sub = rest[0];
173
+ if (["list", "match", "check", "eval"].includes(sub)) process.exit(run("schema/frames.mjs", rest));
174
+ const words = rest.filter((a, i) => a !== "--venture" && rest[i - 1] !== "--venture" && a !== "--frame" && rest[i - 1] !== "--frame" && a !== "--lookup" && rest[i - 1] !== "--lookup").join(" ");
175
+ process.exit(run("schema/frames.mjs", ["run", here(), ...(opt("frame") ? [opt("frame")] : []), ...(words ? ["--text", words] : []), ...(opt("lookup") ? ["--lookup", opt("lookup")] : [])]));
176
+ } else if (cmd === "setup") {
177
+ // Setup in the model's harness (EE-083): the state, the artifact, and the founder confirming relayed answers.
178
+ process.exit(run("schema/setup.mjs", [here(), ...(rest.length ? rest.filter((a, i) => a !== "--venture" && rest[i - 1] !== "--venture") : ["state"])]));
154
179
  } else if (cmd === "port" || cmd === "migrate") {
155
180
  // Inside a venture, --venture defaults to it.
156
181
  const args = rest.includes("--venture") ? rest : [...rest, "--venture", here()];