naive-framework 0.0.0-stage → 0.1.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 (338) hide show
  1. package/AGENTS.md +61 -0
  2. package/LICENSE +21 -0
  3. package/README.md +37 -2
  4. package/docs/decisions/decisions.json +861 -0
  5. package/docs/ontology.json +796 -0
  6. package/docs/ontology.md +163 -0
  7. package/docs/reference/ROUTES.md +125 -0
  8. package/evidence/benchmarks/2026-10-27-golden.jev.after-catalog-fix.json +558 -0
  9. package/evidence/benchmarks/2026-10-27-golden.jev.json +501 -0
  10. package/evidence/benchmarks/2026-10-27-golden.reasoning-answers.json +177 -0
  11. package/evidence/benchmarks/JEV_VS_REASONING_MODEL_2026-10-27.md +68 -0
  12. package/evidence/capabilities/agent-capabilities.json +86 -0
  13. package/evidence/design-documents.registry.json +2896 -0
  14. package/evidence/evals/evals.json +25 -0
  15. package/evidence/evals/harness-evals.json +140 -0
  16. package/evidence/layer.json +51 -0
  17. package/evidence/skills/agent-capabilities/SKILL.md +35 -0
  18. package/evidence/skills/design-coverage/SKILL.md +57 -0
  19. package/evidence/skills/design-coverage/check.mjs +192 -0
  20. package/evidence/skills/design-coverage/scan.mjs +45 -0
  21. package/evidence/skills/fidelity-ladder-audit/SKILL.md +121 -0
  22. package/evidence/skills/fidelity-ladder-audit/ladder-census.mjs +180 -0
  23. package/evidence/skills/findability/SKILL.md +32 -0
  24. package/evidence/skills/findability/inventory.rb +41 -0
  25. package/evidence/skills/foreshadowing-audit/SKILL.md +183 -0
  26. package/evidence/skills/metrics-review/SKILL.md +177 -0
  27. package/evidence/tooling/integrations.json +153 -0
  28. package/governance/layer.json +101 -0
  29. package/governance/references/references.json +26 -0
  30. package/governance/skills/board-design/SKILL.md +68 -0
  31. package/governance/skills/brand-pillar/SKILL.md +90 -0
  32. package/governance/skills/business-pillar/SKILL.md +96 -0
  33. package/governance/skills/chief-of-staff/SKILL.md +45 -0
  34. package/governance/skills/cofounder-structure/SKILL.md +64 -0
  35. package/governance/skills/cross-pillar-tradeoff/SKILL.md +168 -0
  36. package/governance/skills/equity-and-share-classes/SKILL.md +68 -0
  37. package/governance/skills/executive-roles/SKILL.md +95 -0
  38. package/governance/skills/exit-planning/SKILL.md +66 -0
  39. package/governance/skills/financing-and-investor-rights/SKILL.md +73 -0
  40. package/governance/skills/office-hours-mode/SKILL.md +113 -0
  41. package/governance/skills/product-pillar/SKILL.md +81 -0
  42. package/governance/skills/publication-policy/SKILL.md +63 -0
  43. package/governance/skills/role-and-candidate-authoring/SKILL.md +198 -0
  44. package/governance/skills/stakeholder-update/SKILL.md +177 -0
  45. package/governance/tooling/integrations.json +37 -0
  46. package/governance/venture-template/AGENT_ROLES_AND_DECISION_RIGHTS.md +58 -0
  47. package/governance/venture-template/COMPANY_VAULT_DEFINITION_2026-10-25.md +204 -0
  48. package/governance/venture-template/README.md +74 -0
  49. package/governance/venture-template/decision-rights.json +186 -0
  50. package/governance/venture-template/index.md +151 -0
  51. package/governance/venture-template/pillars/brand.md +209 -0
  52. package/governance/venture-template/pillars/business.md +255 -0
  53. package/governance/venture-template/pillars/product.md +254 -0
  54. package/governance/venture-template/render-roles.py +11 -0
  55. package/governance/venture-template/roles/README.md +85 -0
  56. package/governance/venture-template/roles/agent-roles.json +202 -0
  57. package/governance/venture-template/roles/ceo.md +163 -0
  58. package/governance/venture-template/roles/chief-of-staff.md +153 -0
  59. package/governance/venture-template/roles/cmo.md +165 -0
  60. package/governance/venture-template/roles/coo.md +189 -0
  61. package/governance/venture-template/roles/cto.md +158 -0
  62. package/governance/venture-template/roles/department-head.md +47 -0
  63. package/governance/venture-template/roles/forward-deployed-engineer.md +51 -0
  64. package/governance/venture-template/roles/founder.md +173 -0
  65. package/governance/venture-template/roles/hr-manager.md +49 -0
  66. package/governance/venture-template/roles/talent-acquisition-manager.md +50 -0
  67. package/interface/api/server.mjs +69 -0
  68. package/interface/cli/find-venture.mjs +15 -0
  69. package/interface/cli/mentura.mjs +271 -0
  70. package/interface/cli/naive.mjs +169 -0
  71. package/interface/demo/board.mjs +581 -0
  72. package/interface/demo/decision-harness.mjs +91 -0
  73. package/interface/demo/live-agent.mjs +47 -0
  74. package/interface/harness/evals.mjs +90 -0
  75. package/interface/harness/examples/pt-no-shows.dump.md +19 -0
  76. package/interface/harness/run.mjs +313 -0
  77. package/interface/harness/site.mjs +59 -0
  78. package/interface/layer.json +31 -0
  79. package/interface/mcp/server.mjs +122 -0
  80. package/interface/operator-console/README.md +58 -0
  81. package/interface/provenance.mjs +42 -0
  82. package/interface/references/references.json +28 -0
  83. package/interface/route-aliases.json +32 -0
  84. package/interface/router.mjs +580 -0
  85. package/interface/skills/install-mentura/SKILL.md +88 -0
  86. package/interface/tooling/integrations.json +112 -0
  87. package/knowledge/connectors/README.md +54 -0
  88. package/knowledge/connectors/catalog.mjs +118 -0
  89. package/knowledge/connectors/contract.mjs +56 -0
  90. package/knowledge/connectors/eval.mjs +176 -0
  91. package/knowledge/connectors/filesystem.mjs +79 -0
  92. package/knowledge/connectors/index.mjs +102 -0
  93. package/knowledge/connectors/resolve.mjs +44 -0
  94. package/knowledge/connectors/suite.mjs +80 -0
  95. package/knowledge/layer.json +42 -0
  96. package/knowledge/skills/find-reference/SKILL.md +38 -0
  97. package/knowledge/skills/industry-awareness/SKILL.md +277 -0
  98. package/knowledge/tooling/integrations.json +127 -0
  99. package/knowledge/tooling/questions.md +46 -0
  100. package/operations/FOUNDER_OPERATING_MODEL_CAMPAIGN_2026-10-14.md +16 -0
  101. package/operations/departments.json +162 -0
  102. package/operations/handoff-v2.md +93 -0
  103. package/operations/layer.json +60 -0
  104. package/operations/marketing/check-public.mjs +68 -0
  105. package/operations/marketing/site/README.md +86 -0
  106. package/operations/marketing/site/matrix.json +731 -0
  107. package/operations/marketing/site/plan.mjs +117 -0
  108. package/operations/marketing/site/presence.mjs +252 -0
  109. package/operations/references/references.json +45 -0
  110. package/operations/skills/recruiting/SKILL.md +32 -0
  111. package/operations/skills/team-design/SKILL.md +36 -0
  112. package/operations/skills/tool-selection/SKILL.md +46 -0
  113. package/operations/templates/recruiting-brief.md +35 -0
  114. package/operations/tooling/integrations.json +158 -0
  115. package/package.json +57 -4
  116. package/packages/catalog.json +272 -0
  117. package/packages/cofounder/package.json +42 -0
  118. package/packages/counsel/package.json +47 -0
  119. package/packages/customers/package.json +21 -0
  120. package/packages/decisions/package.json +17 -0
  121. package/packages/engineering/package.json +41 -0
  122. package/packages/evidence/package.json +17 -0
  123. package/packages/finance/package.json +48 -0
  124. package/packages/foundation/package.json +52 -0
  125. package/packages/investor/package.json +57 -0
  126. package/packages/investors/package.json +21 -0
  127. package/packages/legal/package.json +73 -0
  128. package/packages/marketing/package.json +57 -0
  129. package/packages/money/package.json +21 -0
  130. package/packages/operations/package.json +74 -0
  131. package/packages/partner/package.json +38 -0
  132. package/packages/people/package.json +64 -0
  133. package/packages/product/package.json +49 -0
  134. package/packages/raw/package.json +19 -0
  135. package/packages/relationships/package.json +52 -0
  136. package/packages/validation/package.json +99 -0
  137. package/platform/README.md +45 -0
  138. package/platform/connectors/eval.mjs +154 -0
  139. package/platform/connectors/fake-github.mjs +91 -0
  140. package/platform/connectors/github.mjs +105 -0
  141. package/platform/integrations/jev.mjs +30 -0
  142. package/platform/integrations/langsmith-docket.mjs +53 -0
  143. package/platform/integrations/langsmith-setup.mjs +115 -0
  144. package/platform/integrations/langsmith.mjs +47 -0
  145. package/platform/integrations/rho.mjs +145 -0
  146. package/platform/integrations/site.mjs +75 -0
  147. package/platform/kits/README.md +7 -0
  148. package/platform/mcp/research.mjs +64 -0
  149. package/purpose/checklists/vault-and-tooling-consultancy-setup.md +216 -0
  150. package/purpose/frameworks/business-discovery-framework-amendments-2026-05-20.md +181 -0
  151. package/purpose/frameworks/business-discovery-framework.html +938 -0
  152. package/purpose/frameworks/business-discovery-framework.md +326 -0
  153. package/purpose/frameworks/idea-map/README.md +74 -0
  154. package/purpose/frameworks/idea-map/conventions.json +146 -0
  155. package/purpose/frameworks/idea-map/graph.mjs +182 -0
  156. package/purpose/frameworks/idea-map/ideamap.mjs +179 -0
  157. package/purpose/frameworks/idea-map/route.mjs +140 -0
  158. package/purpose/frameworks/onboarding-handoff-framework.md +269 -0
  159. package/purpose/frameworks/post-discovery-deliverable-playbook.md +274 -0
  160. package/purpose/frameworks/product-anatomy/README.md +63 -0
  161. package/purpose/frameworks/product-anatomy/anatomy.mjs +195 -0
  162. package/purpose/frameworks/product-anatomy/examples/polity.answers.reasoned.json +11 -0
  163. package/purpose/frameworks/product-anatomy/examples/polity.json +89 -0
  164. package/purpose/frameworks/product-anatomy/phases.json +107 -0
  165. package/purpose/frameworks/product-anatomy/questions.json +74 -0
  166. package/purpose/kits/validation/README.md +14 -0
  167. package/purpose/kits/validation/references/references.json +122 -0
  168. package/purpose/kits/validation/skills/problem-validation/SKILL.md +43 -0
  169. package/purpose/kits/validation/skills/validation-score/SKILL.md +35 -0
  170. package/purpose/kits/validation/templates/go-to-market.md +19 -0
  171. package/purpose/kits/validation/templates/operations-readiness.md +20 -0
  172. package/purpose/kits/validation/templates/pricing.md +22 -0
  173. package/purpose/kits/validation/templates/problem-interview.md +36 -0
  174. package/purpose/kits/validation/templates/problem.md +45 -0
  175. package/purpose/kits/validation/templates/product-market-fit.md +23 -0
  176. package/purpose/kits/validation/templates/solution.md +18 -0
  177. package/purpose/kits/validation/templates/team.md +19 -0
  178. package/purpose/kits/validation/weights.json +22 -0
  179. package/purpose/layer.json +53 -0
  180. package/purpose/references/references.json +324 -0
  181. package/purpose/skills/ambition-tier/SKILL.md +205 -0
  182. package/purpose/skills/telos/SKILL.md +52 -0
  183. package/purpose/strategic-foundation.json +133 -0
  184. package/purpose/templates/telos-based-smart.md +39 -0
  185. package/purpose/tooling/integrations.json +48 -0
  186. package/schema/README.md +61 -0
  187. package/schema/VERSION +1 -0
  188. package/schema/account.schema.json +79 -0
  189. package/schema/add-department.mjs +41 -0
  190. package/schema/agent-capability.schema.json +94 -0
  191. package/schema/alignment.schema.json +61 -0
  192. package/schema/assess-problem.mjs +131 -0
  193. package/schema/bdd.mjs +56 -0
  194. package/schema/board.schema.json +101 -0
  195. package/schema/check-boundary.mjs +54 -0
  196. package/schema/check-data-model.mjs +170 -0
  197. package/schema/check-data-packages.mjs +83 -0
  198. package/schema/check-departments.mjs +45 -0
  199. package/schema/check-execution.mjs +85 -0
  200. package/schema/check-governance.mjs +98 -0
  201. package/schema/check-integrations.mjs +33 -0
  202. package/schema/check-nextjs-status.mjs +77 -0
  203. package/schema/check-nouns.mjs +49 -0
  204. package/schema/check-private.mjs +61 -0
  205. package/schema/check-provenance.mjs +55 -0
  206. package/schema/check-publication.mjs +96 -0
  207. package/schema/check-ready.mjs +89 -0
  208. package/schema/check-sources.mjs +66 -0
  209. package/schema/check-team.mjs +94 -0
  210. package/schema/check-telos.mjs +59 -0
  211. package/schema/check-validation.mjs +165 -0
  212. package/schema/check-ventures.mjs +81 -0
  213. package/schema/check-where-used.mjs +58 -0
  214. package/schema/data-master.schema.json +14 -0
  215. package/schema/data-model.json +1258 -0
  216. package/schema/data-model.schema.json +152 -0
  217. package/schema/decision-analytics.mjs +77 -0
  218. package/schema/decision-record.schema.json +290 -0
  219. package/schema/decision-rights.schema.json +34 -0
  220. package/schema/decision.schema.json +30 -0
  221. package/schema/decisions.mjs +321 -0
  222. package/schema/department-catalog.schema.json +55 -0
  223. package/schema/department.schema.json +15 -0
  224. package/schema/departments.mjs +32 -0
  225. package/schema/docket-eval.mjs +89 -0
  226. package/schema/docket.mjs +135 -0
  227. package/schema/eval-suite.schema.json +45 -0
  228. package/schema/evidence.schema.json +110 -0
  229. package/schema/examples/migrate/agent.mjs +12 -0
  230. package/schema/examples/workspace/ventures/studio/accounts/bakery/account.json +18 -0
  231. package/schema/examples/workspace/ventures/studio/evidence/.gitkeep +0 -0
  232. package/schema/examples/workspace/ventures/studio/governance/.gitkeep +0 -0
  233. package/schema/examples/workspace/ventures/studio/knowledge/.gitkeep +0 -0
  234. package/schema/examples/workspace/ventures/studio/operations/.gitkeep +0 -0
  235. package/schema/examples/workspace/ventures/studio/print/knowledge/.gitkeep +0 -0
  236. package/schema/examples/workspace/ventures/studio/print/purpose/.gitkeep +0 -0
  237. package/schema/examples/workspace/ventures/studio/print/venture.json +37 -0
  238. package/schema/examples/workspace/ventures/studio/products/brand-care.json +19 -0
  239. package/schema/examples/workspace/ventures/studio/products/brand-kit.json +23 -0
  240. package/schema/examples/workspace/ventures/studio/provenance.jsonl +3 -0
  241. package/schema/examples/workspace/ventures/studio/purpose/.gitkeep +0 -0
  242. package/schema/examples/workspace/ventures/studio/venture.json +43 -0
  243. package/schema/examples/workspace/ventures/tandem/accounts/.gitkeep +0 -0
  244. package/schema/examples/workspace/ventures/tandem/evidence/README.md +3 -0
  245. package/schema/examples/workspace/ventures/tandem/governance/README.md +3 -0
  246. package/schema/examples/workspace/ventures/tandem/governance/agreements/dee-fast.md +3 -0
  247. package/schema/examples/workspace/ventures/tandem/governance/board.json +51 -0
  248. package/schema/examples/workspace/ventures/tandem/governance/departments/finance/charter.md +5 -0
  249. package/schema/examples/workspace/ventures/tandem/governance/departments/finance/department.json +5 -0
  250. package/schema/examples/workspace/ventures/tandem/governance/departments/primary/marketing-and-sales/charter.md +5 -0
  251. package/schema/examples/workspace/ventures/tandem/governance/departments/primary/marketing-and-sales/department.json +5 -0
  252. package/schema/examples/workspace/ventures/tandem/governance/exit.json +33 -0
  253. package/schema/examples/workspace/ventures/tandem/governance/founder-agreement.json +24 -0
  254. package/schema/examples/workspace/ventures/tandem/governance/founder-agreement.md +3 -0
  255. package/schema/examples/workspace/ventures/tandem/governance/ownership.json +131 -0
  256. package/schema/examples/workspace/ventures/tandem/governance/publication-policy.json +171 -0
  257. package/schema/examples/workspace/ventures/tandem/knowledge/README.md +3 -0
  258. package/schema/examples/workspace/ventures/tandem/knowledge/research/friends-feedback-2026-08.md +3 -0
  259. package/schema/examples/workspace/ventures/tandem/knowledge/research/pilot-2026-10.md +3 -0
  260. package/schema/examples/workspace/ventures/tandem/knowledge/research/problem-interviews-2026-09.md +20 -0
  261. package/schema/examples/workspace/ventures/tandem/knowledge/sources.json +8 -0
  262. package/schema/examples/workspace/ventures/tandem/knowledge/where-used.json +13 -0
  263. package/schema/examples/workspace/ventures/tandem/operations/README.md +3 -0
  264. package/schema/examples/workspace/ventures/tandem/operations/departments/finance/README.md +5 -0
  265. package/schema/examples/workspace/ventures/tandem/operations/departments/finance/department.json +5 -0
  266. package/schema/examples/workspace/ventures/tandem/operations/departments/primary/marketing-and-sales/README.md +5 -0
  267. package/schema/examples/workspace/ventures/tandem/operations/departments/primary/marketing-and-sales/department.json +5 -0
  268. package/schema/examples/workspace/ventures/tandem/pillars/brand.md +5 -0
  269. package/schema/examples/workspace/ventures/tandem/pillars/business.md +5 -0
  270. package/schema/examples/workspace/ventures/tandem/pillars/product.md +5 -0
  271. package/schema/examples/workspace/ventures/tandem/products/.gitkeep +0 -0
  272. package/schema/examples/workspace/ventures/tandem/products/shop-app.json +22 -0
  273. package/schema/examples/workspace/ventures/tandem/profile.md +5 -0
  274. package/schema/examples/workspace/ventures/tandem/provenance.jsonl +13 -0
  275. package/schema/examples/workspace/ventures/tandem/purpose/README.md +3 -0
  276. package/schema/examples/workspace/ventures/tandem/purpose/telos.json +78 -0
  277. package/schema/examples/workspace/ventures/tandem/purpose/validation/go-to-market.json +35 -0
  278. package/schema/examples/workspace/ventures/tandem/purpose/validation/pricing.json +37 -0
  279. package/schema/examples/workspace/ventures/tandem/purpose/validation/problem.json +74 -0
  280. package/schema/examples/workspace/ventures/tandem/purpose/validation/product-market-fit.json +66 -0
  281. package/schema/examples/workspace/ventures/tandem/purpose/validation/solution.json +33 -0
  282. package/schema/examples/workspace/ventures/tandem/purpose/validation/team.json +52 -0
  283. package/schema/examples/workspace/ventures/tandem/venture.json +79 -0
  284. package/schema/examples/workspace/ventures/woodworking/knowledge/.gitkeep +0 -0
  285. package/schema/examples/workspace/ventures/woodworking/venture.json +21 -0
  286. package/schema/examples/workspace/workspace.json +8 -0
  287. package/schema/exit.schema.json +74 -0
  288. package/schema/foundation.mjs +118 -0
  289. package/schema/founder-agreement.schema.json +83 -0
  290. package/schema/gate.mjs +39 -0
  291. package/schema/handoff.schema.json +239 -0
  292. package/schema/icp.schema.json +136 -0
  293. package/schema/idea-graph.schema.json +61 -0
  294. package/schema/init-venture.mjs +98 -0
  295. package/schema/integration.schema.json +143 -0
  296. package/schema/knowledge-sources.schema.json +130 -0
  297. package/schema/layer.json +46 -0
  298. package/schema/layer.schema.json +119 -0
  299. package/schema/load-packages.mjs +26 -0
  300. package/schema/marketing-sitemap.schema.json +232 -0
  301. package/schema/migrate-venture.mjs +192 -0
  302. package/schema/need-now.json +601 -0
  303. package/schema/need-now.mjs +107 -0
  304. package/schema/nextjs-status.schema.json +29 -0
  305. package/schema/ownership.schema.json +282 -0
  306. package/schema/packages.json +4 -0
  307. package/schema/packages.mjs +247 -0
  308. package/schema/payouts.mjs +91 -0
  309. package/schema/port-venture.mjs +161 -0
  310. package/schema/product.schema.json +223 -0
  311. package/schema/provenance-entry.schema.json +154 -0
  312. package/schema/publication-policy.schema.json +293 -0
  313. package/schema/raw-source.schema.json +15 -0
  314. package/schema/reference.schema.json +115 -0
  315. package/schema/route-aliases.schema.json +25 -0
  316. package/schema/run-handoff.mjs +51 -0
  317. package/schema/scaffold/defaults.json +54 -0
  318. package/schema/site-presence.schema.json +337 -0
  319. package/schema/strategic-foundation.schema.json +74 -0
  320. package/schema/stream.schema.json +14 -0
  321. package/schema/team-plan.schema.json +157 -0
  322. package/schema/telos.schema.json +197 -0
  323. package/schema/time-to-ready.mjs +93 -0
  324. package/schema/upgrade-venture.mjs +129 -0
  325. package/schema/use-model.mjs +10 -0
  326. package/schema/validate.py +49 -0
  327. package/schema/validation-evidence.schema.json +123 -0
  328. package/schema/validation-go-to-market.schema.json +110 -0
  329. package/schema/validation-operations-readiness.schema.json +91 -0
  330. package/schema/validation-pricing.schema.json +114 -0
  331. package/schema/validation-problem.schema.json +118 -0
  332. package/schema/validation-product-market-fit.schema.json +135 -0
  333. package/schema/validation-solution.schema.json +79 -0
  334. package/schema/validation-team.schema.json +116 -0
  335. package/schema/venture.schema.json +407 -0
  336. package/schema/vocab/business-models.json +298 -0
  337. package/schema/where-used.schema.json +56 -0
  338. package/schema/workspace.schema.json +38 -0
@@ -0,0 +1,49 @@
1
+ // Checks the system's nouns (EE-014). Fails when a retired noun from docs/ontology.json
2
+ // reappears in the checked files, or when a noun cites a reference id that no layer's
3
+ // references/references.json holds. With --md, prints the glossary as Markdown instead.
4
+ //
5
+ // node schema/check-nouns.mjs check
6
+ // node schema/check-nouns.mjs --md print docs/ontology.md's glossary tables
7
+ //
8
+ // A line that names a retired noun on purpose (to say what replaced it) carries "(was"
9
+ // or "retired" and is skipped.
10
+
11
+ import { existsSync, readFileSync, readdirSync, statSync } from "node:fs";
12
+ import { join } from "node:path";
13
+
14
+ // EE_ROOT points the check at another copy of the repository (used by schema/test.mjs).
15
+ const root = process.env.EE_ROOT?.replace(/\/?$/, "/") ?? new URL("..", import.meta.url).pathname;
16
+ const { check, nouns } = JSON.parse(readFileSync(join(root, "docs/ontology.json"), "utf8"));
17
+
18
+ if (process.argv.includes("--md")) {
19
+ const row = (n) => `| ${n.noun} | ${n.meaning} | ${n.standard} | ${(n.references ?? []).join(", ")} |`;
20
+ for (const level of [...new Set(nouns.filter((n) => n.status !== "retired").map((n) => n.level))]) {
21
+ console.log(`\n### ${level[0].toUpperCase() + level.slice(1)}\n\n| Noun | Meaning | Standard | References |\n| --- | --- | --- | --- |`);
22
+ nouns.filter((n) => n.level === level && n.status !== "retired").forEach((n) => console.log(row(n)));
23
+ }
24
+ console.log(`\n### Retired\n\n| Noun | Status |\n| --- | --- |`);
25
+ nouns.filter((n) => n.status === "retired").forEach((n) => console.log(`| ${n.noun} | retired; use ${n.replaced_by} |`));
26
+ process.exit(0);
27
+ }
28
+
29
+ const walk = (p) => statSync(p).isDirectory()
30
+ ? readdirSync(p).flatMap((e) => walk(join(p, e)))
31
+ : /\.(mdx?|json|mjs)$/.test(p) ? [p] : [];
32
+ const files = check.map((c) => join(root, c)).filter(existsSync).flatMap(walk)
33
+ .filter((f) => !f.endsWith("docs/ontology.json") && !f.endsWith("check-nouns.mjs"));
34
+ const errors = [];
35
+ for (const n of nouns.filter((x) => x.retired_pattern)) {
36
+ const re = new RegExp(n.retired_pattern, "i");
37
+ for (const f of files) readFileSync(f, "utf8").split("\n").forEach((line, i) => {
38
+ if (re.test(line) && !/\(was|retired/i.test(line)) errors.push(`${f.slice(root.length)}:${i + 1}: retired noun "${n.noun}" (use ${n.replaced_by})`);
39
+ });
40
+ }
41
+ // Layer references, and those of any kit inside a layer (<layer>/kits/<kit>/references).
42
+ const refFiles = readdirSync(root).flatMap((d) => [join(d, "references/references.json"),
43
+ ...(existsSync(join(root, d, "kits")) ? readdirSync(join(root, d, "kits")).map((k) => join(d, "kits", k, "references/references.json")) : [])]);
44
+ const known = new Set(refFiles.filter((f) => existsSync(join(root, f)))
45
+ .flatMap((f) => JSON.parse(readFileSync(join(root, f), "utf8")).references.map((r) => r.id)));
46
+ for (const n of nouns) for (const r of n.references ?? []) if (!known.has(r)) errors.push(`docs/ontology.json: ${n.noun} cites unknown reference ${r}`);
47
+
48
+ if (errors.length) { console.error(errors.join("\n")); process.exit(1); }
49
+ console.log(`${nouns.length} nouns (${nouns.filter((n) => n.status === "retired").length} retired), ${files.length} files checked: no retired noun in use, every reference resolves.`);
@@ -0,0 +1,61 @@
1
+ #!/usr/bin/env node
2
+ // Private records, fields, and sensitive streams never reach a model (EE-073).
3
+ // Fails when a private target is named in a package use_model step's inputs, or when
4
+ // a private record sits in a release that a package with use_model would read.
5
+ //
6
+ // node schema/check-private.mjs <venture dir>
7
+
8
+ import { existsSync, readdirSync, readFileSync, statSync } from "node:fs";
9
+ import { join } from "node:path";
10
+
11
+ const dir = process.argv[2];
12
+ if (!dir) { console.error("usage: node schema/check-private.mjs <venture dir>"); process.exit(2); }
13
+ const repo = process.env.EE_ROOT?.replace(/\/?$/, "/") ?? new URL("..", import.meta.url).pathname;
14
+ const load = (p) => JSON.parse(readFileSync(p, "utf8"));
15
+
16
+ const packages = readdirSync(join(repo, "packages")).flatMap((name) => {
17
+ const p = join(repo, "packages", name, "package.json");
18
+ return existsSync(p) ? [load(p)] : [];
19
+ });
20
+ const inputs = packages.flatMap((pkg) => (pkg.use_model ?? []).flatMap((step) => {
21
+ if (typeof step === "string") return [{ package: pkg.name, input: step }];
22
+ return (step.inputs ?? []).map((input) => ({ package: pkg.name, input }));
23
+ }));
24
+ const readsRelease = packages.some((pkg) => (pkg.use_model ?? []).length > 0);
25
+
26
+ function walk(d) {
27
+ if (!existsSync(d)) return [];
28
+ return readdirSync(d, { withFileTypes: true }).flatMap((e) => {
29
+ if (e.name === "node_modules" || e.name === ".git") return [];
30
+ const p = join(d, e.name);
31
+ return e.isDirectory() ? walk(p) : [p];
32
+ });
33
+ }
34
+
35
+ const privateTargets = new Set();
36
+ const privateRecords = new Set();
37
+ for (const f of walk(dir).filter((p) => p.endsWith(".json"))) {
38
+ let rec;
39
+ try { rec = load(f); } catch { continue; }
40
+ if (!rec || typeof rec !== "object" || Array.isArray(rec)) continue;
41
+ const id = typeof rec.id === "string" ? rec.id : null;
42
+ if (rec.private === true && id) { privateTargets.add(id); privateRecords.add(id); }
43
+ for (const field of rec.private_fields ?? []) if (id) privateTargets.add(`${id}.${field}`);
44
+ if (rec.sensitive === true && id) privateTargets.add(id);
45
+ }
46
+
47
+ const errors = [];
48
+ for (const { package: name, input } of inputs) {
49
+ if (privateTargets.has(input) || privateRecords.has(input.split(".")[0])) errors.push(`${name}: use_model input ${input} is private and must not reach a model`);
50
+ }
51
+ if (readsRelease) {
52
+ const release = join(dir, ".release");
53
+ if (existsSync(release) && statSync(release).isDirectory()) {
54
+ for (const f of walk(release).filter((p) => p.endsWith(".json"))) {
55
+ const text = readFileSync(f, "utf8");
56
+ for (const id of privateRecords) if (text.includes(`"${id}"`)) errors.push(`.release mentions private record ${id}, and a package with use_model would read it`);
57
+ }
58
+ }
59
+ }
60
+ if (errors.length) { console.error(errors.join("\n")); process.exit(1); }
61
+ console.log(`${dir}: private records stay off the model.`);
@@ -0,0 +1,55 @@
1
+ // Checks each venture's provenance log (<venture>/provenance.jsonl, EE-031).
2
+ // errors: a line that is not JSON; a broken chain (a line's prev is not the hash of the line
3
+ // before, or its hash does not match its content), which means a line was edited, removed or
4
+ // reordered; an entity outside the venture folder (a record in another source is logged by
5
+ // its address, <source>://<path>, and carries the source's version; EE-037).
6
+ // warnings: a logged file that has changed since its last entry, or is gone, so its later
7
+ // history is only in git; a derived_from path (not a URL or a decision id) that is not in the
8
+ // venture folder; time going backwards.
9
+ // The schema of each line is checked by schema/validate.py.
10
+ //
11
+ // node schema/check-provenance.mjs <workspace or venture dir>
12
+
13
+ import { existsSync, readFileSync, readdirSync } from "node:fs";
14
+ import { join } from "node:path";
15
+ import { contentHash, lineHash, LOG } from "../interface/provenance.mjs";
16
+
17
+ const dir = process.argv[2];
18
+ if (!dir) { console.error("usage: node schema/check-provenance.mjs <workspace or venture dir>"); process.exit(2); }
19
+ const walk = (d) => readdirSync(d, { withFileTypes: true }).flatMap((e) => e.name === "node_modules" || e.name.startsWith(".") ? []
20
+ : e.isDirectory() ? walk(join(d, e.name)) : e.name === "venture.json" ? [d] : []);
21
+ const errors = [], warnings = [];
22
+ let logs = 0, lines = 0;
23
+
24
+ for (const root of walk(dir)) {
25
+ const f = join(root, LOG);
26
+ if (!existsSync(f)) continue;
27
+ logs++;
28
+ const id = JSON.parse(readFileSync(join(root, "venture.json"), "utf8")).id;
29
+ let prev = "genesis", at = "";
30
+ const last = new Map();
31
+ readFileSync(f, "utf8").split("\n").forEach((l, i) => {
32
+ if (!l.trim()) return;
33
+ const where = `${id}/${LOG}:${i + 1}`;
34
+ let e;
35
+ try { e = JSON.parse(l); } catch { errors.push(`${where}: not JSON`); prev = null; return; }
36
+ lines++;
37
+ if (prev !== null && e.prev !== prev) errors.push(`${where}: chain broken: prev does not match the line before (a line was edited, removed or reordered)`);
38
+ if (e.hash !== lineHash(e.prev, e)) errors.push(`${where}: hash does not match the line's content (the line was edited)`);
39
+ if (/^\/|(^|\/)\.\.(\/|$)/.test(e.entity ?? "") || (/^[a-z][a-z0-9+.-]*:\/\//.test(e.entity ?? "") && !e.source)) errors.push(`${where}: entity ${e.entity} is outside the venture folder`);
40
+ if (e.at < at) warnings.push(`${where}: time goes backwards`);
41
+ at = e.at; prev = e.hash;
42
+ if (typeof e.entity !== "string") { errors.push(`${where}: no entity`); return; }
43
+ if (!e.source) last.set(e.entity, e);
44
+ for (const d of e.derived_from ?? []) if (!/^[a-z][a-z0-9+.-]*:\/\//.test(d) && !/^[A-Z]+-\d+$/.test(d) && !d.startsWith("_drafts/") && !existsSync(join(root, d))) warnings.push(`${where}: derived from ${d}, which is not in the venture folder`);
45
+ });
46
+ for (const [entity, e] of last) {
47
+ const p = join(root, entity);
48
+ if (!existsSync(p)) { if (e.activity !== "draft") warnings.push(`${id}: ${entity} is in the log but gone; git holds what happened`); continue; }
49
+ if (contentHash(p) !== e.sha256) warnings.push(`${id}: ${entity} changed outside the router since ${e.at}; git holds that history`);
50
+ }
51
+ }
52
+
53
+ for (const w of warnings) console.warn(`warning: ${w}`);
54
+ if (errors.length) { console.error(errors.join("\n")); process.exit(1); }
55
+ console.log(`${logs} provenance logs, ${lines} entries: every chain holds.`);
@@ -0,0 +1,96 @@
1
+ // Checks each venture's publication policy (governance/publication-policy.json, EE-025) and the
2
+ // handoffs that publish. Every publication passes the gate. Rules:
3
+ // policy: channel, guideline and team ids unique; a team's members are known roles; a channel's
4
+ // guidelines are known; a guideline or channel config named by path exists (paths are
5
+ // relative to the venture folder); an outside guideline with a URL states when it was
6
+ // checked (warning); a channel following no brand or editorial guideline is a warning.
7
+ // platforms: a channel on a platform cites a platform guideline; a social, newsletter or
8
+ // app-store channel names its platform.
9
+ // replies: a social, newsletter or blog channel says how replies and comments are handled;
10
+ // delegated replies go to known roles or teams that include a person, never to agents alone.
11
+ // rights: drafters and approvers are known roles or teams; no agent approves, and a team
12
+ // approves only if a person is among its members; every rule has a person approving.
13
+ // handoffs: a handoff that publishes (public-commitment in its decision classes or its
14
+ // escalations) names a channel and a content rule the policy holds; a venture with such a
15
+ // handoff and no policy fails.
16
+ //
17
+ // node schema/check-publication.mjs <workspace or venture dir>
18
+
19
+ import { existsSync, readFileSync, readdirSync } from "node:fs";
20
+ import { join } from "node:path";
21
+
22
+ const dir = process.argv[2];
23
+ if (!dir) { console.error("usage: node schema/check-publication.mjs <workspace or venture dir>"); process.exit(2); }
24
+ const load = (p) => JSON.parse(readFileSync(p, "utf8"));
25
+ const walk = (d) => readdirSync(d, { withFileTypes: true }).flatMap((e) => e.name === "node_modules" || e.name.startsWith(".") ? []
26
+ : e.isDirectory() ? walk(join(d, e.name)) : e.name === "venture.json" ? [d] : []);
27
+ const roles = new Map(load(new URL("../governance/venture-template/roles/agent-roles.json", import.meta.url)).roles.map((r) => [r.id, r]));
28
+ const errors = [], warnings = [];
29
+ let checked = 0;
30
+
31
+ for (const root of walk(dir)) {
32
+ const v = load(join(root, "venture.json"));
33
+ const pPath = join(root, "governance/publication-policy.json");
34
+ const policy = existsSync(pPath) ? load(pPath) : null;
35
+ const at = `${v.id}/governance/publication-policy.json`;
36
+ const hDir = join(root, "operations/handoffs");
37
+ const handoffs = existsSync(hDir) ? readdirSync(hDir).filter((f) => f.endsWith(".json")).map((f) => [f, load(join(hDir, f))]) : [];
38
+
39
+ for (const [f, h] of handoffs) {
40
+ const publishes = (h.decision_classes ?? []).includes("public-commitment") || (h.escalations ?? []).some((e) => e.class === "public-commitment");
41
+ const where = `${v.id}/operations/handoffs/${f}`;
42
+ if (!publishes) { if (h.publication) warnings.push(`${where}: names a publication but has no public-commitment class`); continue; }
43
+ if (!policy) { errors.push(`${where}: publishes, but the venture has no governance/publication-policy.json`); continue; }
44
+ if (!h.publication) { errors.push(`${where}: publishes, so it names its channel and content (publication)`); continue; }
45
+ const ch = policy.channels.find((c) => c.id === h.publication.channel);
46
+ if (!ch) errors.push(`${where}: unknown channel ${h.publication.channel}`);
47
+ else if (!ch.rules.some((r) => r.content === h.publication.content)) errors.push(`${where}: channel ${ch.id} has no rule for "${h.publication.content}"`);
48
+ }
49
+ if (!policy) continue;
50
+ checked++;
51
+
52
+ const dup = (xs, what) => { const ids = xs.map((x) => x.id); if (new Set(ids).size !== ids.length) errors.push(`${at}: duplicate ${what} ids`); };
53
+ dup(policy.channels, "channel"); dup(policy.guidelines ?? [], "guideline"); dup(policy.teams ?? [], "team");
54
+ const teams = new Map((policy.teams ?? []).map((t) => [t.id, t]));
55
+ for (const t of teams.values()) {
56
+ if (roles.has(t.id)) errors.push(`${at}: team ${t.id} has the same id as a role`);
57
+ for (const m of t.members) if (!roles.has(m)) errors.push(`${at}: team ${t.id} has unknown role ${m}`);
58
+ }
59
+ const guidelines = new Map((policy.guidelines ?? []).map((g) => [g.id, g]));
60
+ for (const g of guidelines.values()) {
61
+ if (g.path && !existsSync(join(root, g.path))) errors.push(`${at}: guideline ${g.id} not found at ${g.path} (paths are relative to the venture folder)`);
62
+ if (!g.path && !g.url) errors.push(`${at}: guideline ${g.id} needs a path or a url`);
63
+ if (g.url && !g.checked) warnings.push(`${at}: guideline ${g.id} has a url but no checked date`);
64
+ }
65
+
66
+ const person = (id) => roles.get(id)?.occupant === "person";
67
+ for (const c of policy.channels) {
68
+ for (const g of c.guidelines ?? []) if (!guidelines.has(g)) errors.push(`${at}: channel ${c.id} follows unknown guideline ${g}`);
69
+ if (!(c.guidelines ?? []).some((g) => ["brand", "editorial"].includes(guidelines.get(g)?.kind))) warnings.push(`${at}: channel ${c.id} follows no brand or editorial guideline`);
70
+ const onPlatform = ["social", "newsletter", "app-store"].includes(c.kind);
71
+ if (onPlatform && !c.platform) errors.push(`${at}: channel ${c.id} is ${c.kind}, so it names its platform`);
72
+ if (c.platform && !(c.guidelines ?? []).some((g) => guidelines.get(g)?.kind === "platform")) errors.push(`${at}: channel ${c.id} runs on ${c.platform} but cites none of its rules (a guideline of kind platform)`);
73
+ if (["social", "newsletter", "blog"].includes(c.kind) && !c.replies) errors.push(`${at}: channel ${c.id} says how replies and comments are handled (replies: gated, delegated or none)`);
74
+ if (c.replies?.mode === "delegated") {
75
+ const ds = c.replies.delegates ?? [];
76
+ if (!ds.length) errors.push(`${at}: channel ${c.id} delegates replies to no one`);
77
+ for (const id of ds) if (!roles.has(id) && !teams.has(id)) errors.push(`${at}: channel ${c.id} delegates replies to unknown role or team ${id}`);
78
+ if (ds.length && !ds.some((id) => person(id) || teams.get(id)?.members.some(person))) errors.push(`${at}: channel ${c.id} delegates replies to agents only; a person answers for the venture`);
79
+ }
80
+ if (c.config && !existsSync(join(root, c.config))) errors.push(`${at}: channel ${c.id} config not found at ${c.config}`);
81
+ for (const r of c.rules) {
82
+ const rule = `${at}: ${c.id} "${r.content}"`;
83
+ for (const id of [...(r.drafters ?? []), ...r.approvers]) if (!roles.has(id) && !teams.has(id)) errors.push(`${rule}: unknown role or team ${id}`);
84
+ for (const id of r.approvers) {
85
+ if (roles.has(id) && !person(id)) errors.push(`${rule}: ${id} is an agent role; agents draft and check, a person approves`);
86
+ if (teams.has(id) && !teams.get(id).members.some(person)) errors.push(`${rule}: team ${id} has no person, so it cannot approve`);
87
+ }
88
+ if (!r.approvers.some((id) => person(id) || teams.get(id)?.members.some(person))) errors.push(`${rule}: no person approves`);
89
+ for (const l of r.from_layers ?? []) if (v.layers && v.layers[l] === false) warnings.push(`${rule}: publishes from ${l}, which this venture has turned off`);
90
+ }
91
+ }
92
+ }
93
+
94
+ for (const w of warnings) console.warn(`warning: ${w}`);
95
+ if (errors.length) { console.error(errors.join("\n")); process.exit(1); }
96
+ console.log(`${checked} ventures with a publication policy: every publication passes the gate.`);
@@ -0,0 +1,89 @@
1
+ // Checks that a handoff is ready to execute before anyone starts on it (EE-019): its
2
+ // definition of ready. An agent that claims a handoff runs this first; a gap it finds is a
3
+ // question for the handoff's author, asked before work starts, not an ambiguity discovered
4
+ // halfway through. Every item is objective; judging whether the brief says enough is the
5
+ // holder's first act, recorded with --record.
6
+ //
7
+ // node schema/check-ready.mjs <venture dir> <handoff id>
8
+ // node schema/check-ready.mjs <venture dir> <handoff id> --record --by <role or session>
9
+ //
10
+ // --record writes `ready_check` into the handoff (and moves a draft to ready) only when
11
+ // every item passes. check-execution.mjs refuses an in-progress handoff without one.
12
+
13
+ import { execSync } from "node:child_process";
14
+ import { existsSync, readFileSync, readdirSync, writeFileSync } from "node:fs";
15
+ import { join } from "node:path";
16
+
17
+ const [dir, id] = process.argv.slice(2);
18
+ if (!dir || !id) { console.error("usage: node schema/check-ready.mjs <venture dir> <handoff id> [--record --by <who>]"); process.exit(2); }
19
+ const opt = (k) => process.argv.includes(k) ? process.argv[process.argv.indexOf(k) + 1] : undefined;
20
+ const root = new URL("..", import.meta.url).pathname;
21
+ const load = (p) => JSON.parse(readFileSync(p, "utf8"));
22
+ const hPath = join(dir, "operations/handoffs", `${id}.json`);
23
+ const h = load(hPath);
24
+ const items = [];
25
+ const item = (name, ok, why = "") => items.push({ name, ok, why });
26
+
27
+ // 1. The brief says what done looks like and what to do, with no placeholders left.
28
+ const bPath = join(dir, "operations/handoffs", h.brief);
29
+ const brief = existsSync(bPath) ? readFileSync(bPath, "utf8") : "";
30
+ item("brief exists", !!brief, bPath);
31
+ const section = (name) => { const m = brief.match(new RegExp(`^##\\s+(?:\\d+\\.\\s*)?${name}\\b[^\\n]*\\n([\\s\\S]*?)(?=^##\\s|$(?![\\s\\S]))`, "mi")); return m ? m[1].trim() : null; };
32
+ for (const s of ["Outcome", "Work"]) { const t = section(s); item(`brief has its ${s} section, with text`, !!t, t === null ? "missing" : "empty"); }
33
+ const holes = brief.match(/\b(TBD|TODO|FIXME)\b|\?\?\?|<(?:title|role|entity|outcome|id|date)[^>]*>/gi) ?? [];
34
+ item("brief has no placeholders left", !holes.length, holes.slice(0, 3).join(", "));
35
+
36
+ // 2. Everything it names exists.
37
+ const products = new Set(["products", "product"].flatMap((folder) => existsSync(join(dir, folder)) ? readdirSync(join(dir, folder)).filter((f) => f.endsWith(".json")).map((f) => f.slice(0, -5)) : []));
38
+ const missingProducts = (h.products ?? []).filter((p) => !products.has(p));
39
+ item("every product it serves exists", !missingProducts.length, missingProducts.join(", "));
40
+ const logs = [join(root, "docs/decisions/decisions.json"), join(dir, "knowledge/decisions.json")].filter(existsSync).flatMap((f) => load(f).decisions);
41
+ const decisions = new Map(logs.map((d) => [d.id, d]));
42
+ const badDecisions = (h.governed_by ?? []).filter((d) => !decisions.has(d) || ["withdrawn", "superseded"].includes(decisions.get(d).status));
43
+ item("every governing decision exists and is in force or proposed", !badDecisions.length, badDecisions.join(", "));
44
+ // A method assignment (such as a MAD A-NNN) belongs to the build method a venture uses, which is
45
+ // not part of Mentura (EE-038); a handoff may cite one, and this does not check it.
46
+ const missingInputs = (h.inputs ?? []).filter((p) => !existsSync(join(root, p)));
47
+ item("every input it needs exists", !missingInputs.length, missingInputs.join(", "));
48
+
49
+ // 3. The holder can act inside its rights.
50
+ const roles = new Set(load(join(root, "governance/venture-template/roles/agent-roles.json")).roles.map((r) => r.id));
51
+ const classes = new Map(load(join(root, "governance/venture-template/decision-rights.json")).classes.map((c) => [c.id, c]));
52
+ const own = join(dir, "governance/decision-rights.json");
53
+ if (existsSync(own)) for (const c of load(own).classes) classes.set(c.id, { ...classes.get(c.id), ...c });
54
+ item(`role ${h.role} exists`, roles.has(h.role));
55
+ const foreign = h.decision_classes.filter((c) => classes.get(c)?.owner !== h.role);
56
+ item("every decision class it carries belongs to its role", !foreign.length, foreign.join(", "));
57
+ const open = (h.escalations ?? []).filter((e) => !e.answer);
58
+ item("no escalation is waiting for an answer", !open.length, open.map((e) => `${e.class} to ${e.to}`).join(", "));
59
+ item("attempts remain", h.attempts < h.max_attempts, `${h.attempts} of ${h.max_attempts}`);
60
+
61
+ // 4. Done can be proved: the criteria exist and the scripts they call are there.
62
+ const kinds = new Set(h.done_criteria.map((d) => d.kind));
63
+ item("has a conformance and a self-proof criterion", kinds.has("conformance") && kinds.has("self-proof"));
64
+ const scripts = h.done_criteria.filter((d) => d.kind !== "review").flatMap((d) => {
65
+ let cwd = root;
66
+ return d.check.split(/&&|\|/).flatMap((part) => {
67
+ const words = part.trim().split(/\s+/);
68
+ if (words[0] === "cd") { cwd = join(cwd, words[1]); return []; }
69
+ return words.filter((w) => /\.(mjs|js|py|sh)$/.test(w)).map((w) => ({ d: d.id, path: join(cwd, w) }));
70
+ });
71
+ });
72
+ const lost = scripts.filter((s) => !existsSync(s.path));
73
+ item("every script a criterion runs exists", !lost.length, lost.map((s) => `${s.d}: ${s.path.slice(root.length)}`).join(", "));
74
+ const badReview = h.done_criteria.filter((d) => d.kind === "review" && (!roles.has(d.reviewer) || d.reviewer === h.role));
75
+ item("every review names a reviewer other than the holder", !badReview.length, badReview.map((d) => d.id).join(", "));
76
+
77
+ for (const i of items) console.log(`${i.ok ? "pass" : "FAIL"} ${i.name}${!i.ok && i.why ? `: ${i.why}` : ""}`);
78
+ const ready = items.every((i) => i.ok);
79
+ console.log(ready ? `${h.id} is ready to execute.` : `${h.id} is not ready: ask its author before starting.`);
80
+ if (process.argv.includes("--record")) {
81
+ if (!ready) process.exit(1);
82
+ if (!opt("--by")) { console.error("--record needs --by <role or session>"); process.exit(2); }
83
+ const version = execSync("git rev-parse --short HEAD", { cwd: root, encoding: "utf8" }).trim();
84
+ h.ready_check = { at: new Date().toISOString().replace(/\.\d+Z$/, "Z"), by: opt("--by"), version, items: items.length };
85
+ if (h.status === "draft") h.status = "ready";
86
+ writeFileSync(hPath, JSON.stringify(h, null, 2) + "\n");
87
+ console.log(`recorded in ${hPath}`);
88
+ }
89
+ process.exit(ready ? 0 : 1);
@@ -0,0 +1,66 @@
1
+ // Checks each venture's source map (<venture>/knowledge/sources.json, EE-037) for what its schema
2
+ // cannot state: source ids are unique; each names a connector in the registry
3
+ // (knowledge/tooling/integrations.json) and a write mode that connector supports; a GitHub source
4
+ // names its repository; every record type it maps is a venture record type and goes to a source
5
+ // it lists. The venture, its provenance log and the map itself stay in the venture folder, so they
6
+ // cannot be mapped away. It does not connect to any source: the connector evals do that.
7
+ //
8
+ // node schema/check-sources.mjs <workspace or venture dir>
9
+
10
+ import { existsSync, readFileSync, readdirSync } from "node:fs";
11
+ import { join } from "node:path";
12
+ import { LOCAL, MAP, registry } from "../knowledge/connectors/index.mjs";
13
+ import { ANY, CONTEXT, specificity } from "../knowledge/connectors/resolve.mjs";
14
+
15
+ const dir = process.argv[2];
16
+ if (!dir) { console.error("usage: node schema/check-sources.mjs <workspace or venture dir>"); process.exit(2); }
17
+ const root = new URL("..", import.meta.url).pathname;
18
+ const walk = (d) => readdirSync(d, { withFileTypes: true }).flatMap((e) => e.name === "node_modules" || e.name.startsWith(".") ? []
19
+ : e.isDirectory() ? walk(join(d, e.name)) : e.name === "venture.json" ? [d] : []);
20
+ const model = JSON.parse(readFileSync(join(root, "schema/data-model.json"), "utf8"));
21
+ const ventureTypes = new Set(model.records.filter((r) => r.level === "venture").map((r) => r.id));
22
+ const PINNED = new Set(["venture", "provenance", "knowledge-sources"]);
23
+ const reg = registry();
24
+ const errors = [];
25
+ let maps = 0;
26
+
27
+ for (const v of walk(dir)) {
28
+ const f = join(v, MAP);
29
+ if (!existsSync(f)) continue;
30
+ maps++;
31
+ const id = JSON.parse(readFileSync(join(v, "venture.json"), "utf8")).id;
32
+ const m = JSON.parse(readFileSync(f, "utf8"));
33
+ const ids = new Set([LOCAL]);
34
+ for (const s of m.sources ?? []) {
35
+ const where = `${id}: source ${s.id}`;
36
+ if (ids.has(s.id)) errors.push(`${where}: duplicate id${s.id === LOCAL ? " (venture is the venture's own folder)" : ""}`);
37
+ ids.add(s.id);
38
+ const c = reg.get(s.connector);
39
+ if (!c) { errors.push(`${where}: no connector ${s.connector} (the registry has ${[...reg.keys()].join(", ")})`); continue; }
40
+ if (s.writes && !c.writes.includes(s.writes)) errors.push(`${where}: ${s.connector} writes ${c.writes.join(" or ")}, not ${s.writes}`);
41
+ if (s.connector === "github" && !s.repo) errors.push(`${where}: a github source names its repo`);
42
+ for (const k of Object.keys(s).filter((k) => !["id", "connector", "writes", "note"].includes(k))) if (!(c.options ?? []).includes(k)) errors.push(`${where}: ${s.connector} takes no option ${k}`);
43
+ }
44
+ for (const [type, src] of Object.entries(m.records ?? {})) {
45
+ if (!ventureTypes.has(type)) errors.push(`${id}: ${type} is not a venture record type`);
46
+ if (PINNED.has(type)) errors.push(`${id}: ${type} stays in the venture folder`);
47
+ if (!ids.has(src)) errors.push(`${id}: ${type} goes to ${src}, which is not one of its sources`);
48
+ }
49
+ // The matrix (EE-042): every rule names a known record type and a known source, and two rules
50
+ // that can hold at once never tie. Product and account ids are not checked here: those records
51
+ // may themselves live in another source.
52
+ const rules = m.rules ?? [];
53
+ rules.forEach((rule, i) => {
54
+ const where = `${id}: rule ${i + 1} (${rule.record}${rule.attribute !== ANY ? `.${rule.attribute}` : ""})`;
55
+ if (!ventureTypes.has(rule.record)) errors.push(`${where}: ${rule.record} is not a venture record type`);
56
+ if (PINNED.has(rule.record)) errors.push(`${where}: ${rule.record} stays in the venture folder`);
57
+ if (!ids.has(rule.system_of_record)) errors.push(`${where}: system of record ${rule.system_of_record} is not one of its sources`);
58
+ for (const s of rule.survivorship?.also_in ?? []) if (!ids.has(s) || s === rule.system_of_record) errors.push(`${where}: survivorship names ${s}, which is not another of its sources`);
59
+ });
60
+ const overlap = (a, b) => a.record === b.record && ["attribute", ...CONTEXT].every((k) => a[k] === ANY || b[k] === ANY || a[k] === b[k]);
61
+ for (let i = 0; i < rules.length; i++) for (let j = i + 1; j < rules.length; j++)
62
+ if (overlap(rules[i], rules[j]) && specificity(rules[i]) === specificity(rules[j]) && rules[i].attribute === rules[j].attribute)
63
+ errors.push(`${id}: rules ${i + 1} and ${j + 1} (${rules[i].record}) can both hold and tie at the same specificity; narrow one`);
64
+ }
65
+ if (errors.length) { console.error(errors.join("\n")); process.exit(1); }
66
+ console.log(`${maps} source maps: every source has a connector, every record type a source, and no two matrix rules tie.`);
@@ -0,0 +1,94 @@
1
+ // Checks each venture's team plan (operations/team-plan.json) against the agent capability register
2
+ // (evidence/capabilities/agent-capabilities.json) and decision rights (EE-029). Rules:
3
+ // roles: ids unique; a role not in the registry has a name; an agent occupies only a role the
4
+ // registry gives to agents, or its own.
5
+ // authority: each delegated class exists in the venture's decision rights; its owner is this
6
+ // role; no agent holds an escalate class (those stay with a person).
7
+ // human loops: every task cites a known capability. An agent's task that is cannot, not-allowed
8
+ // or unknown names a human loop, a person role in the plan; can-with-review names one too.
9
+ // recruiting: an open person role needed by the current phase is a role to recruit now; it needs
10
+ // a recruiting entry, and the entry's brief exists (paths relative to the venture folder).
11
+ // routes (robots, EE-032): each glob a role lists matches at least one route.
12
+ // Also checks the register: ids unique; an entry with a url has a checked date; not-allowed cites
13
+ // a decision class that exists; an eval file exists.
14
+ //
15
+ // node schema/check-team.mjs <workspace or venture dir>
16
+
17
+ import { existsSync, readFileSync, readdirSync } from "node:fs";
18
+ import { join } from "node:path";
19
+
20
+ const dir = process.argv[2];
21
+ if (!dir) { console.error("usage: node schema/check-team.mjs <workspace or venture dir>"); process.exit(2); }
22
+ const repo = new URL("..", import.meta.url).pathname;
23
+ const load = (p) => JSON.parse(readFileSync(p, "utf8"));
24
+ const walk = (d) => readdirSync(d, { withFileTypes: true }).flatMap((e) => e.name === "node_modules" || e.name.startsWith(".") ? []
25
+ : e.isDirectory() ? walk(join(d, e.name)) : e.name === "venture.json" ? [d] : []);
26
+ const PHASES = ["discovery", "validation", "beachhead", "scale", "platform"];
27
+ const registry = new Map(load(join(repo, "governance/venture-template/roles/agent-roles.json")).roles.map((r) => [r.id, r]));
28
+ const template = load(join(repo, "governance/venture-template/decision-rights.json"));
29
+ const caps = load(join(repo, "evidence/capabilities/agent-capabilities.json"));
30
+ const errors = [], warnings = [], recruitNow = [];
31
+ const { routes, glob: routeGlob } = await import("../interface/router.mjs");
32
+ const routeList = routes().map((r) => r.route.replace("[id]", "x"));
33
+
34
+ const capIds = caps.capabilities.map((c) => c.id);
35
+ if (new Set(capIds).size !== capIds.length) errors.push("agent-capabilities.json: duplicate ids");
36
+ const templateClasses = new Set(template.classes.map((c) => c.id));
37
+ for (const c of caps.capabilities) for (const b of c.basis) {
38
+ if (b.url && !b.checked) errors.push(`agent-capabilities.json: ${c.id} cites a url with no checked date`);
39
+ if (b.eval && !existsSync(join(repo, b.eval))) errors.push(`agent-capabilities.json: ${c.id} eval ${b.eval} not found`);
40
+ if (b.decision_class && !templateClasses.has(b.decision_class)) errors.push(`agent-capabilities.json: ${c.id} cites unknown decision class ${b.decision_class}`);
41
+ }
42
+ for (const c of caps.capabilities) if (c.status === "not-allowed" && !c.basis.some((b) => b.decision_class)) errors.push(`agent-capabilities.json: ${c.id} is not-allowed but cites no decision class`);
43
+ const cap = new Map(caps.capabilities.map((c) => [c.id, c]));
44
+
45
+ let checked = 0;
46
+ for (const root of walk(dir)) {
47
+ const v = load(join(root, "venture.json"));
48
+ const pPath = [join(root, "people/team-plan.json"), join(root, "operations/team-plan.json")].find((p) => existsSync(p)) ?? join(root, "operations/team-plan.json");
49
+ if (!existsSync(pPath)) continue;
50
+ checked++;
51
+ const plan = load(pPath), at = `${v.id}/operations/team-plan.json`;
52
+ // The venture's decision rights: the template, tightened by the venture's own file.
53
+ const classes = new Map(template.classes.map((c) => [c.id, c]));
54
+ const own = join(root, "governance/decision-rights.json");
55
+ if (existsSync(own)) for (const c of load(own).classes) classes.set(c.id, { ...classes.get(c.id), ...c });
56
+ const ids = plan.roles.map((r) => r.id);
57
+ if (new Set(ids).size !== ids.length) errors.push(`${at}: duplicate role ids`);
58
+ const persons = new Set(plan.roles.filter((r) => r.occupant === "person").map((r) => r.id));
59
+ for (const r of plan.roles) {
60
+ const reg = registry.get(r.id);
61
+ if (!reg && !r.name) errors.push(`${at}: ${r.id} is not in the role registry, so it needs a name`);
62
+ for (const g of [...(r.routes?.read ?? []), ...(r.routes?.write ?? [])]) if (!routeList.some((x) => routeGlob(g).test(x))) errors.push(`${at}: ${r.id} lists route ${g}, which matches no route`);
63
+ if (reg && reg.occupant === "person" && r.occupant === "agent") errors.push(`${at}: ${r.id} is a person's role in the registry; an agent cannot occupy it`);
64
+ for (const a of r.authority ?? []) {
65
+ const c = classes.get(a);
66
+ if (!c) { errors.push(`${at}: ${r.id} holds unknown decision class ${a}`); continue; }
67
+ if (c.owner !== r.id) errors.push(`${at}: ${r.id} holds ${a}, which decision rights give to ${c.owner}`);
68
+ if (r.occupant === "agent" && c.level === "escalate") errors.push(`${at}: ${r.id} is an agent and holds ${a}, an escalate class; a person decides it`);
69
+ }
70
+ for (const t of r.tasks ?? []) {
71
+ const c = cap.get(t.capability);
72
+ if (!c) { errors.push(`${at}: ${r.id} task cites unknown capability ${t.capability}`); continue; }
73
+ if (r.occupant === "agent" && c.status !== "can") {
74
+ if (!t.human_loop) errors.push(`${at}: ${r.id} does ${t.capability} (${c.status}), so it names a human loop`);
75
+ else if (!persons.has(t.human_loop)) errors.push(`${at}: ${r.id}'s human loop ${t.human_loop} is not a person role in the plan`);
76
+ }
77
+ if (r.occupant === "agent" && c.status === "not-allowed") warnings.push(`${at}: ${r.id} does ${t.capability}, which agents may not do; the agent prepares and ${t.human_loop ?? "a person"} acts`);
78
+ }
79
+ if (r.occupant === "person" && r.status === "open" && PHASES.indexOf(r.needed_by) <= PHASES.indexOf(v.phase)) {
80
+ recruitNow.push(`${v.id}: ${r.id}`);
81
+ const rec = (plan.recruiting ?? []).find((x) => x.role === r.id);
82
+ if (!rec) errors.push(`${at}: ${r.id} is needed now and open, but has no recruiting entry`);
83
+ }
84
+ }
85
+ for (const rec of plan.recruiting ?? []) {
86
+ if (!ids.includes(rec.role)) errors.push(`${at}: recruiting for unknown role ${rec.role}`);
87
+ if (!existsSync(join(root, rec.brief))) errors.push(`${at}: recruiting brief ${rec.brief} not found (paths are relative to the venture folder)`);
88
+ if (rec.assisted_by && plan.roles.find((r) => r.id === rec.assisted_by)?.occupant !== "agent") warnings.push(`${at}: recruiting for ${rec.role} is assisted by ${rec.assisted_by}, which is not an agent role in the plan`);
89
+ }
90
+ }
91
+
92
+ for (const w of warnings) console.warn(`warning: ${w}`);
93
+ if (errors.length) { console.error(errors.join("\n")); process.exit(1); }
94
+ console.log(`${checked} ventures with a team plan: every agent task has its human loop${recruitNow.length ? `; recruit now: ${recruitNow.join(", ")}` : ""}.`);
@@ -0,0 +1,59 @@
1
+ // Checks each venture's telos (purpose/telos.json, EE-023): start with the end. Rules:
2
+ // measures: ids unique, exactly one primary.
3
+ // phases: each phase listed once; every exit criterion serves at least one known measure,
4
+ // so each phase's exit traces back to the end; the venture's current phase has exit
5
+ // criteria (warning when it has none).
6
+ // owner outcome: the path exit.json prefers is the telos path, and the telos path is not
7
+ // ruled out there; an ambition that cannot fit the path is a warning (aggressive growth
8
+ // that intends to hold, or a hobby that intends an IPO).
9
+ // presence: a venture past Discovery, with an ambition beyond hobby, and no telos is a
10
+ // warning: the method starts with the end, but only the owner can write it.
11
+ //
12
+ // node schema/check-telos.mjs <workspace or venture dir>
13
+
14
+ import { existsSync, readFileSync, readdirSync } from "node:fs";
15
+ import { join } from "node:path";
16
+
17
+ const dir = process.argv[2];
18
+ if (!dir) { console.error("usage: node schema/check-telos.mjs <workspace or venture dir>"); process.exit(2); }
19
+ const load = (p) => JSON.parse(readFileSync(p, "utf8"));
20
+ const walk = (d) => readdirSync(d, { withFileTypes: true }).flatMap((e) => e.name === "node_modules" || e.name.startsWith(".") ? []
21
+ : e.isDirectory() ? walk(join(d, e.name)) : e.name === "venture.json" ? [d] : []);
22
+ const errors = [], warnings = [];
23
+ let checked = 0;
24
+
25
+ for (const root of walk(dir)) {
26
+ const v = load(join(root, "venture.json"));
27
+ const tPath = join(root, "purpose/telos.json");
28
+ if (!existsSync(tPath)) {
29
+ if (v.phase !== "discovery" && v.ambition && v.ambition !== "hobby") warnings.push(`${v.id}: past Discovery with no purpose/telos.json; the owner should state the end first`);
30
+ continue;
31
+ }
32
+ checked++;
33
+ const t = load(tPath), at = `${v.id}/purpose/telos.json`;
34
+ const ids = t.measures.map((m) => m.id);
35
+ if (new Set(ids).size !== ids.length) errors.push(`${at}: duplicate measure ids`);
36
+ if (t.measures.filter((m) => m.primary).length !== 1) errors.push(`${at}: exactly one measure is primary`);
37
+ const seen = new Set();
38
+ for (const p of t.phases ?? []) {
39
+ if (seen.has(p.phase)) errors.push(`${at}: phase ${p.phase} listed twice`);
40
+ seen.add(p.phase);
41
+ for (const c of p.exit_criteria) for (const s of c.serves) if (!ids.includes(s)) errors.push(`${at}: ${p.phase}/${c.id} serves unknown measure ${s}`);
42
+ }
43
+ if (!seen.has(v.phase)) warnings.push(`${at}: no exit criteria for the current phase, ${v.phase}`);
44
+
45
+ const path = t.owner_outcome.path;
46
+ if (v.ambition === "aggressive-growth" && path === "hold") warnings.push(`${at}: aggressive growth usually means investors expecting an exit, but the owners intend to hold`);
47
+ if (v.ambition === "hobby" && ["ipo", "acquisition"].includes(path)) warnings.push(`${at}: a hobby that intends ${path}; is the ambition still hobby?`);
48
+ const ePath = join(root, "governance/exit.json");
49
+ if (existsSync(ePath)) {
50
+ const paths = load(ePath).paths;
51
+ const preferred = paths.filter((p) => p.stance === "preferred").map((p) => p.kind);
52
+ if (paths.some((p) => p.kind === path && p.stance === "ruled-out")) errors.push(`${v.id}/governance/exit.json: rules out ${path}, the path the telos intends`);
53
+ if (preferred.length && !preferred.includes(path)) errors.push(`${v.id}/governance/exit.json: prefers ${preferred.join(", ")}, but the telos intends ${path}`);
54
+ }
55
+ }
56
+
57
+ for (const w of warnings) console.warn(`warning: ${w}`);
58
+ if (errors.length) { console.error(errors.join("\n")); process.exit(1); }
59
+ console.log(`${checked} ventures with a telos: every exit criterion traces to the end.`);