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.
- package/AGENTS.md +61 -0
- package/LICENSE +21 -0
- package/README.md +37 -2
- package/docs/decisions/decisions.json +861 -0
- package/docs/ontology.json +796 -0
- package/docs/ontology.md +163 -0
- package/docs/reference/ROUTES.md +125 -0
- package/evidence/benchmarks/2026-10-27-golden.jev.after-catalog-fix.json +558 -0
- package/evidence/benchmarks/2026-10-27-golden.jev.json +501 -0
- package/evidence/benchmarks/2026-10-27-golden.reasoning-answers.json +177 -0
- package/evidence/benchmarks/JEV_VS_REASONING_MODEL_2026-10-27.md +68 -0
- package/evidence/capabilities/agent-capabilities.json +86 -0
- package/evidence/design-documents.registry.json +2896 -0
- package/evidence/evals/evals.json +25 -0
- package/evidence/evals/harness-evals.json +140 -0
- package/evidence/layer.json +51 -0
- package/evidence/skills/agent-capabilities/SKILL.md +35 -0
- package/evidence/skills/design-coverage/SKILL.md +57 -0
- package/evidence/skills/design-coverage/check.mjs +192 -0
- package/evidence/skills/design-coverage/scan.mjs +45 -0
- package/evidence/skills/fidelity-ladder-audit/SKILL.md +121 -0
- package/evidence/skills/fidelity-ladder-audit/ladder-census.mjs +180 -0
- package/evidence/skills/findability/SKILL.md +32 -0
- package/evidence/skills/findability/inventory.rb +41 -0
- package/evidence/skills/foreshadowing-audit/SKILL.md +183 -0
- package/evidence/skills/metrics-review/SKILL.md +177 -0
- package/evidence/tooling/integrations.json +153 -0
- package/governance/layer.json +101 -0
- package/governance/references/references.json +26 -0
- package/governance/skills/board-design/SKILL.md +68 -0
- package/governance/skills/brand-pillar/SKILL.md +90 -0
- package/governance/skills/business-pillar/SKILL.md +96 -0
- package/governance/skills/chief-of-staff/SKILL.md +45 -0
- package/governance/skills/cofounder-structure/SKILL.md +64 -0
- package/governance/skills/cross-pillar-tradeoff/SKILL.md +168 -0
- package/governance/skills/equity-and-share-classes/SKILL.md +68 -0
- package/governance/skills/executive-roles/SKILL.md +95 -0
- package/governance/skills/exit-planning/SKILL.md +66 -0
- package/governance/skills/financing-and-investor-rights/SKILL.md +73 -0
- package/governance/skills/office-hours-mode/SKILL.md +113 -0
- package/governance/skills/product-pillar/SKILL.md +81 -0
- package/governance/skills/publication-policy/SKILL.md +63 -0
- package/governance/skills/role-and-candidate-authoring/SKILL.md +198 -0
- package/governance/skills/stakeholder-update/SKILL.md +177 -0
- package/governance/tooling/integrations.json +37 -0
- package/governance/venture-template/AGENT_ROLES_AND_DECISION_RIGHTS.md +58 -0
- package/governance/venture-template/COMPANY_VAULT_DEFINITION_2026-10-25.md +204 -0
- package/governance/venture-template/README.md +74 -0
- package/governance/venture-template/decision-rights.json +186 -0
- package/governance/venture-template/index.md +151 -0
- package/governance/venture-template/pillars/brand.md +209 -0
- package/governance/venture-template/pillars/business.md +255 -0
- package/governance/venture-template/pillars/product.md +254 -0
- package/governance/venture-template/render-roles.py +11 -0
- package/governance/venture-template/roles/README.md +85 -0
- package/governance/venture-template/roles/agent-roles.json +202 -0
- package/governance/venture-template/roles/ceo.md +163 -0
- package/governance/venture-template/roles/chief-of-staff.md +153 -0
- package/governance/venture-template/roles/cmo.md +165 -0
- package/governance/venture-template/roles/coo.md +189 -0
- package/governance/venture-template/roles/cto.md +158 -0
- package/governance/venture-template/roles/department-head.md +47 -0
- package/governance/venture-template/roles/forward-deployed-engineer.md +51 -0
- package/governance/venture-template/roles/founder.md +173 -0
- package/governance/venture-template/roles/hr-manager.md +49 -0
- package/governance/venture-template/roles/talent-acquisition-manager.md +50 -0
- package/interface/api/server.mjs +69 -0
- package/interface/cli/find-venture.mjs +15 -0
- package/interface/cli/mentura.mjs +271 -0
- package/interface/cli/naive.mjs +169 -0
- package/interface/demo/board.mjs +581 -0
- package/interface/demo/decision-harness.mjs +91 -0
- package/interface/demo/live-agent.mjs +47 -0
- package/interface/harness/evals.mjs +90 -0
- package/interface/harness/examples/pt-no-shows.dump.md +19 -0
- package/interface/harness/run.mjs +313 -0
- package/interface/harness/site.mjs +59 -0
- package/interface/layer.json +31 -0
- package/interface/mcp/server.mjs +122 -0
- package/interface/operator-console/README.md +58 -0
- package/interface/provenance.mjs +42 -0
- package/interface/references/references.json +28 -0
- package/interface/route-aliases.json +32 -0
- package/interface/router.mjs +580 -0
- package/interface/skills/install-mentura/SKILL.md +88 -0
- package/interface/tooling/integrations.json +112 -0
- package/knowledge/connectors/README.md +54 -0
- package/knowledge/connectors/catalog.mjs +118 -0
- package/knowledge/connectors/contract.mjs +56 -0
- package/knowledge/connectors/eval.mjs +176 -0
- package/knowledge/connectors/filesystem.mjs +79 -0
- package/knowledge/connectors/index.mjs +102 -0
- package/knowledge/connectors/resolve.mjs +44 -0
- package/knowledge/connectors/suite.mjs +80 -0
- package/knowledge/layer.json +42 -0
- package/knowledge/skills/find-reference/SKILL.md +38 -0
- package/knowledge/skills/industry-awareness/SKILL.md +277 -0
- package/knowledge/tooling/integrations.json +127 -0
- package/knowledge/tooling/questions.md +46 -0
- package/operations/FOUNDER_OPERATING_MODEL_CAMPAIGN_2026-10-14.md +16 -0
- package/operations/departments.json +162 -0
- package/operations/handoff-v2.md +93 -0
- package/operations/layer.json +60 -0
- package/operations/marketing/check-public.mjs +68 -0
- package/operations/marketing/site/README.md +86 -0
- package/operations/marketing/site/matrix.json +731 -0
- package/operations/marketing/site/plan.mjs +117 -0
- package/operations/marketing/site/presence.mjs +252 -0
- package/operations/references/references.json +45 -0
- package/operations/skills/recruiting/SKILL.md +32 -0
- package/operations/skills/team-design/SKILL.md +36 -0
- package/operations/skills/tool-selection/SKILL.md +46 -0
- package/operations/templates/recruiting-brief.md +35 -0
- package/operations/tooling/integrations.json +158 -0
- package/package.json +57 -4
- package/packages/catalog.json +272 -0
- package/packages/cofounder/package.json +42 -0
- package/packages/counsel/package.json +47 -0
- package/packages/customers/package.json +21 -0
- package/packages/decisions/package.json +17 -0
- package/packages/engineering/package.json +41 -0
- package/packages/evidence/package.json +17 -0
- package/packages/finance/package.json +48 -0
- package/packages/foundation/package.json +52 -0
- package/packages/investor/package.json +57 -0
- package/packages/investors/package.json +21 -0
- package/packages/legal/package.json +73 -0
- package/packages/marketing/package.json +57 -0
- package/packages/money/package.json +21 -0
- package/packages/operations/package.json +74 -0
- package/packages/partner/package.json +38 -0
- package/packages/people/package.json +64 -0
- package/packages/product/package.json +49 -0
- package/packages/raw/package.json +19 -0
- package/packages/relationships/package.json +52 -0
- package/packages/validation/package.json +99 -0
- package/platform/README.md +45 -0
- package/platform/connectors/eval.mjs +154 -0
- package/platform/connectors/fake-github.mjs +91 -0
- package/platform/connectors/github.mjs +105 -0
- package/platform/integrations/jev.mjs +30 -0
- package/platform/integrations/langsmith-docket.mjs +53 -0
- package/platform/integrations/langsmith-setup.mjs +115 -0
- package/platform/integrations/langsmith.mjs +47 -0
- package/platform/integrations/rho.mjs +145 -0
- package/platform/integrations/site.mjs +75 -0
- package/platform/kits/README.md +7 -0
- package/platform/mcp/research.mjs +64 -0
- package/purpose/checklists/vault-and-tooling-consultancy-setup.md +216 -0
- package/purpose/frameworks/business-discovery-framework-amendments-2026-05-20.md +181 -0
- package/purpose/frameworks/business-discovery-framework.html +938 -0
- package/purpose/frameworks/business-discovery-framework.md +326 -0
- package/purpose/frameworks/idea-map/README.md +74 -0
- package/purpose/frameworks/idea-map/conventions.json +146 -0
- package/purpose/frameworks/idea-map/graph.mjs +182 -0
- package/purpose/frameworks/idea-map/ideamap.mjs +179 -0
- package/purpose/frameworks/idea-map/route.mjs +140 -0
- package/purpose/frameworks/onboarding-handoff-framework.md +269 -0
- package/purpose/frameworks/post-discovery-deliverable-playbook.md +274 -0
- package/purpose/frameworks/product-anatomy/README.md +63 -0
- package/purpose/frameworks/product-anatomy/anatomy.mjs +195 -0
- package/purpose/frameworks/product-anatomy/examples/polity.answers.reasoned.json +11 -0
- package/purpose/frameworks/product-anatomy/examples/polity.json +89 -0
- package/purpose/frameworks/product-anatomy/phases.json +107 -0
- package/purpose/frameworks/product-anatomy/questions.json +74 -0
- package/purpose/kits/validation/README.md +14 -0
- package/purpose/kits/validation/references/references.json +122 -0
- package/purpose/kits/validation/skills/problem-validation/SKILL.md +43 -0
- package/purpose/kits/validation/skills/validation-score/SKILL.md +35 -0
- package/purpose/kits/validation/templates/go-to-market.md +19 -0
- package/purpose/kits/validation/templates/operations-readiness.md +20 -0
- package/purpose/kits/validation/templates/pricing.md +22 -0
- package/purpose/kits/validation/templates/problem-interview.md +36 -0
- package/purpose/kits/validation/templates/problem.md +45 -0
- package/purpose/kits/validation/templates/product-market-fit.md +23 -0
- package/purpose/kits/validation/templates/solution.md +18 -0
- package/purpose/kits/validation/templates/team.md +19 -0
- package/purpose/kits/validation/weights.json +22 -0
- package/purpose/layer.json +53 -0
- package/purpose/references/references.json +324 -0
- package/purpose/skills/ambition-tier/SKILL.md +205 -0
- package/purpose/skills/telos/SKILL.md +52 -0
- package/purpose/strategic-foundation.json +133 -0
- package/purpose/templates/telos-based-smart.md +39 -0
- package/purpose/tooling/integrations.json +48 -0
- package/schema/README.md +61 -0
- package/schema/VERSION +1 -0
- package/schema/account.schema.json +79 -0
- package/schema/add-department.mjs +41 -0
- package/schema/agent-capability.schema.json +94 -0
- package/schema/alignment.schema.json +61 -0
- package/schema/assess-problem.mjs +131 -0
- package/schema/bdd.mjs +56 -0
- package/schema/board.schema.json +101 -0
- package/schema/check-boundary.mjs +54 -0
- package/schema/check-data-model.mjs +170 -0
- package/schema/check-data-packages.mjs +83 -0
- package/schema/check-departments.mjs +45 -0
- package/schema/check-execution.mjs +85 -0
- package/schema/check-governance.mjs +98 -0
- package/schema/check-integrations.mjs +33 -0
- package/schema/check-nextjs-status.mjs +77 -0
- package/schema/check-nouns.mjs +49 -0
- package/schema/check-private.mjs +61 -0
- package/schema/check-provenance.mjs +55 -0
- package/schema/check-publication.mjs +96 -0
- package/schema/check-ready.mjs +89 -0
- package/schema/check-sources.mjs +66 -0
- package/schema/check-team.mjs +94 -0
- package/schema/check-telos.mjs +59 -0
- package/schema/check-validation.mjs +165 -0
- package/schema/check-ventures.mjs +81 -0
- package/schema/check-where-used.mjs +58 -0
- package/schema/data-master.schema.json +14 -0
- package/schema/data-model.json +1258 -0
- package/schema/data-model.schema.json +152 -0
- package/schema/decision-analytics.mjs +77 -0
- package/schema/decision-record.schema.json +290 -0
- package/schema/decision-rights.schema.json +34 -0
- package/schema/decision.schema.json +30 -0
- package/schema/decisions.mjs +321 -0
- package/schema/department-catalog.schema.json +55 -0
- package/schema/department.schema.json +15 -0
- package/schema/departments.mjs +32 -0
- package/schema/docket-eval.mjs +89 -0
- package/schema/docket.mjs +135 -0
- package/schema/eval-suite.schema.json +45 -0
- package/schema/evidence.schema.json +110 -0
- package/schema/examples/migrate/agent.mjs +12 -0
- package/schema/examples/workspace/ventures/studio/accounts/bakery/account.json +18 -0
- package/schema/examples/workspace/ventures/studio/evidence/.gitkeep +0 -0
- package/schema/examples/workspace/ventures/studio/governance/.gitkeep +0 -0
- package/schema/examples/workspace/ventures/studio/knowledge/.gitkeep +0 -0
- package/schema/examples/workspace/ventures/studio/operations/.gitkeep +0 -0
- package/schema/examples/workspace/ventures/studio/print/knowledge/.gitkeep +0 -0
- package/schema/examples/workspace/ventures/studio/print/purpose/.gitkeep +0 -0
- package/schema/examples/workspace/ventures/studio/print/venture.json +37 -0
- package/schema/examples/workspace/ventures/studio/products/brand-care.json +19 -0
- package/schema/examples/workspace/ventures/studio/products/brand-kit.json +23 -0
- package/schema/examples/workspace/ventures/studio/provenance.jsonl +3 -0
- package/schema/examples/workspace/ventures/studio/purpose/.gitkeep +0 -0
- package/schema/examples/workspace/ventures/studio/venture.json +43 -0
- package/schema/examples/workspace/ventures/tandem/accounts/.gitkeep +0 -0
- package/schema/examples/workspace/ventures/tandem/evidence/README.md +3 -0
- package/schema/examples/workspace/ventures/tandem/governance/README.md +3 -0
- package/schema/examples/workspace/ventures/tandem/governance/agreements/dee-fast.md +3 -0
- package/schema/examples/workspace/ventures/tandem/governance/board.json +51 -0
- package/schema/examples/workspace/ventures/tandem/governance/departments/finance/charter.md +5 -0
- package/schema/examples/workspace/ventures/tandem/governance/departments/finance/department.json +5 -0
- package/schema/examples/workspace/ventures/tandem/governance/departments/primary/marketing-and-sales/charter.md +5 -0
- package/schema/examples/workspace/ventures/tandem/governance/departments/primary/marketing-and-sales/department.json +5 -0
- package/schema/examples/workspace/ventures/tandem/governance/exit.json +33 -0
- package/schema/examples/workspace/ventures/tandem/governance/founder-agreement.json +24 -0
- package/schema/examples/workspace/ventures/tandem/governance/founder-agreement.md +3 -0
- package/schema/examples/workspace/ventures/tandem/governance/ownership.json +131 -0
- package/schema/examples/workspace/ventures/tandem/governance/publication-policy.json +171 -0
- package/schema/examples/workspace/ventures/tandem/knowledge/README.md +3 -0
- package/schema/examples/workspace/ventures/tandem/knowledge/research/friends-feedback-2026-08.md +3 -0
- package/schema/examples/workspace/ventures/tandem/knowledge/research/pilot-2026-10.md +3 -0
- package/schema/examples/workspace/ventures/tandem/knowledge/research/problem-interviews-2026-09.md +20 -0
- package/schema/examples/workspace/ventures/tandem/knowledge/sources.json +8 -0
- package/schema/examples/workspace/ventures/tandem/knowledge/where-used.json +13 -0
- package/schema/examples/workspace/ventures/tandem/operations/README.md +3 -0
- package/schema/examples/workspace/ventures/tandem/operations/departments/finance/README.md +5 -0
- package/schema/examples/workspace/ventures/tandem/operations/departments/finance/department.json +5 -0
- package/schema/examples/workspace/ventures/tandem/operations/departments/primary/marketing-and-sales/README.md +5 -0
- package/schema/examples/workspace/ventures/tandem/operations/departments/primary/marketing-and-sales/department.json +5 -0
- package/schema/examples/workspace/ventures/tandem/pillars/brand.md +5 -0
- package/schema/examples/workspace/ventures/tandem/pillars/business.md +5 -0
- package/schema/examples/workspace/ventures/tandem/pillars/product.md +5 -0
- package/schema/examples/workspace/ventures/tandem/products/.gitkeep +0 -0
- package/schema/examples/workspace/ventures/tandem/products/shop-app.json +22 -0
- package/schema/examples/workspace/ventures/tandem/profile.md +5 -0
- package/schema/examples/workspace/ventures/tandem/provenance.jsonl +13 -0
- package/schema/examples/workspace/ventures/tandem/purpose/README.md +3 -0
- package/schema/examples/workspace/ventures/tandem/purpose/telos.json +78 -0
- package/schema/examples/workspace/ventures/tandem/purpose/validation/go-to-market.json +35 -0
- package/schema/examples/workspace/ventures/tandem/purpose/validation/pricing.json +37 -0
- package/schema/examples/workspace/ventures/tandem/purpose/validation/problem.json +74 -0
- package/schema/examples/workspace/ventures/tandem/purpose/validation/product-market-fit.json +66 -0
- package/schema/examples/workspace/ventures/tandem/purpose/validation/solution.json +33 -0
- package/schema/examples/workspace/ventures/tandem/purpose/validation/team.json +52 -0
- package/schema/examples/workspace/ventures/tandem/venture.json +79 -0
- package/schema/examples/workspace/ventures/woodworking/knowledge/.gitkeep +0 -0
- package/schema/examples/workspace/ventures/woodworking/venture.json +21 -0
- package/schema/examples/workspace/workspace.json +8 -0
- package/schema/exit.schema.json +74 -0
- package/schema/foundation.mjs +118 -0
- package/schema/founder-agreement.schema.json +83 -0
- package/schema/gate.mjs +39 -0
- package/schema/handoff.schema.json +239 -0
- package/schema/icp.schema.json +136 -0
- package/schema/idea-graph.schema.json +61 -0
- package/schema/init-venture.mjs +98 -0
- package/schema/integration.schema.json +143 -0
- package/schema/knowledge-sources.schema.json +130 -0
- package/schema/layer.json +46 -0
- package/schema/layer.schema.json +119 -0
- package/schema/load-packages.mjs +26 -0
- package/schema/marketing-sitemap.schema.json +232 -0
- package/schema/migrate-venture.mjs +192 -0
- package/schema/need-now.json +601 -0
- package/schema/need-now.mjs +107 -0
- package/schema/nextjs-status.schema.json +29 -0
- package/schema/ownership.schema.json +282 -0
- package/schema/packages.json +4 -0
- package/schema/packages.mjs +247 -0
- package/schema/payouts.mjs +91 -0
- package/schema/port-venture.mjs +161 -0
- package/schema/product.schema.json +223 -0
- package/schema/provenance-entry.schema.json +154 -0
- package/schema/publication-policy.schema.json +293 -0
- package/schema/raw-source.schema.json +15 -0
- package/schema/reference.schema.json +115 -0
- package/schema/route-aliases.schema.json +25 -0
- package/schema/run-handoff.mjs +51 -0
- package/schema/scaffold/defaults.json +54 -0
- package/schema/site-presence.schema.json +337 -0
- package/schema/strategic-foundation.schema.json +74 -0
- package/schema/stream.schema.json +14 -0
- package/schema/team-plan.schema.json +157 -0
- package/schema/telos.schema.json +197 -0
- package/schema/time-to-ready.mjs +93 -0
- package/schema/upgrade-venture.mjs +129 -0
- package/schema/use-model.mjs +10 -0
- package/schema/validate.py +49 -0
- package/schema/validation-evidence.schema.json +123 -0
- package/schema/validation-go-to-market.schema.json +110 -0
- package/schema/validation-operations-readiness.schema.json +91 -0
- package/schema/validation-pricing.schema.json +114 -0
- package/schema/validation-problem.schema.json +118 -0
- package/schema/validation-product-market-fit.schema.json +135 -0
- package/schema/validation-solution.schema.json +79 -0
- package/schema/validation-team.schema.json +116 -0
- package/schema/venture.schema.json +407 -0
- package/schema/vocab/business-models.json +298 -0
- package/schema/where-used.schema.json +56 -0
- package/schema/workspace.schema.json +38 -0
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
+
"$id": "agent-capability.schema.json",
|
|
4
|
+
"title": "Agent capability register",
|
|
5
|
+
"description": "What agents can and cannot do, task by task, with sources and dates (EE-029), at evidence/capabilities/agent-capabilities.json. Team plans cite these entries: a task an agent cannot do, may not do, or is not known to do needs a named human loop. Kept current by reading new research and by our own evals.",
|
|
6
|
+
"type": "object",
|
|
7
|
+
"required": [
|
|
8
|
+
"updated",
|
|
9
|
+
"capabilities"
|
|
10
|
+
],
|
|
11
|
+
"additionalProperties": false,
|
|
12
|
+
"properties": {
|
|
13
|
+
"updated": {
|
|
14
|
+
"type": "string",
|
|
15
|
+
"format": "date"
|
|
16
|
+
},
|
|
17
|
+
"note": {
|
|
18
|
+
"type": "string"
|
|
19
|
+
},
|
|
20
|
+
"capabilities": {
|
|
21
|
+
"type": "array",
|
|
22
|
+
"items": {
|
|
23
|
+
"type": "object",
|
|
24
|
+
"required": [
|
|
25
|
+
"id",
|
|
26
|
+
"task",
|
|
27
|
+
"status",
|
|
28
|
+
"basis",
|
|
29
|
+
"checked"
|
|
30
|
+
],
|
|
31
|
+
"additionalProperties": false,
|
|
32
|
+
"properties": {
|
|
33
|
+
"id": {
|
|
34
|
+
"type": "string",
|
|
35
|
+
"pattern": "^[a-z0-9-]+$"
|
|
36
|
+
},
|
|
37
|
+
"task": {
|
|
38
|
+
"type": "string"
|
|
39
|
+
},
|
|
40
|
+
"area": {
|
|
41
|
+
"type": "string",
|
|
42
|
+
"description": "The layer or MAD tier the task belongs to"
|
|
43
|
+
},
|
|
44
|
+
"status": {
|
|
45
|
+
"enum": [
|
|
46
|
+
"can",
|
|
47
|
+
"can-with-review",
|
|
48
|
+
"cannot",
|
|
49
|
+
"not-allowed",
|
|
50
|
+
"unknown"
|
|
51
|
+
],
|
|
52
|
+
"description": "can: no human loop needed; can-with-review: an agent does it and a person or a check reviews it; cannot: beyond agents today; not-allowed: agents may be able, but decision rights or law reserve it to a person; unknown: no evidence yet, so a human loop by default and an eval to find out"
|
|
53
|
+
},
|
|
54
|
+
"basis": {
|
|
55
|
+
"type": "array",
|
|
56
|
+
"items": {
|
|
57
|
+
"type": "object",
|
|
58
|
+
"additionalProperties": false,
|
|
59
|
+
"minProperties": 1,
|
|
60
|
+
"properties": {
|
|
61
|
+
"title": {
|
|
62
|
+
"type": "string"
|
|
63
|
+
},
|
|
64
|
+
"url": {
|
|
65
|
+
"type": "string",
|
|
66
|
+
"format": "uri"
|
|
67
|
+
},
|
|
68
|
+
"checked": {
|
|
69
|
+
"type": "string",
|
|
70
|
+
"format": "date"
|
|
71
|
+
},
|
|
72
|
+
"eval": {
|
|
73
|
+
"type": "string",
|
|
74
|
+
"description": "A result file of our own eval, relative to the repository"
|
|
75
|
+
},
|
|
76
|
+
"decision_class": {
|
|
77
|
+
"type": "string",
|
|
78
|
+
"description": "For not-allowed: the decision class that reserves it"
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
},
|
|
83
|
+
"checked": {
|
|
84
|
+
"type": "string",
|
|
85
|
+
"format": "date"
|
|
86
|
+
},
|
|
87
|
+
"notes": {
|
|
88
|
+
"type": "string"
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
+
"$id": "alignment.schema.json",
|
|
4
|
+
"title": "Alignment",
|
|
5
|
+
"description": "How the venture's products and agents must behave (EE-053): a list of principles, each with its source and the check that holds it, at <venture>/purpose/alignment.json. A foundation statement, gated: an agent's change is a draft the founder accepts.",
|
|
6
|
+
"type": "object",
|
|
7
|
+
"required": [
|
|
8
|
+
"venture",
|
|
9
|
+
"principles"
|
|
10
|
+
],
|
|
11
|
+
"additionalProperties": false,
|
|
12
|
+
"properties": {
|
|
13
|
+
"venture": {
|
|
14
|
+
"type": "string"
|
|
15
|
+
},
|
|
16
|
+
"updated": {
|
|
17
|
+
"type": "string",
|
|
18
|
+
"format": "date"
|
|
19
|
+
},
|
|
20
|
+
"principles": {
|
|
21
|
+
"type": "array",
|
|
22
|
+
"items": {
|
|
23
|
+
"type": "object",
|
|
24
|
+
"required": [
|
|
25
|
+
"id",
|
|
26
|
+
"statement",
|
|
27
|
+
"source"
|
|
28
|
+
],
|
|
29
|
+
"additionalProperties": false,
|
|
30
|
+
"properties": {
|
|
31
|
+
"id": {
|
|
32
|
+
"type": "string",
|
|
33
|
+
"pattern": "^[a-z0-9-]+$"
|
|
34
|
+
},
|
|
35
|
+
"statement": {
|
|
36
|
+
"type": "string",
|
|
37
|
+
"description": "What must always or never happen, in the founder's words"
|
|
38
|
+
},
|
|
39
|
+
"applies_to": {
|
|
40
|
+
"type": "array",
|
|
41
|
+
"items": {
|
|
42
|
+
"type": "string"
|
|
43
|
+
},
|
|
44
|
+
"description": "Product ids, or agents or team, that the principle binds"
|
|
45
|
+
},
|
|
46
|
+
"source": {
|
|
47
|
+
"type": "string",
|
|
48
|
+
"description": "Who or what it comes from: the founder's words with a date, a decision id or a reference"
|
|
49
|
+
},
|
|
50
|
+
"check": {
|
|
51
|
+
"type": [
|
|
52
|
+
"string",
|
|
53
|
+
"null"
|
|
54
|
+
],
|
|
55
|
+
"description": "The test or script that holds it, or null when nothing checks it yet"
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
}
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// Assesses a venture's problem from its evidence (EE-050), the lean validation suite. No single
|
|
3
|
+
// score: the problem splits into four assumptions, each labelled from its evidence, and the
|
|
4
|
+
// labels say which commitment the evidence is strong enough for. Behaviors it must keep are in
|
|
5
|
+
// purpose/kits/validation/features/problem-assessment.feature.
|
|
6
|
+
//
|
|
7
|
+
// node schema/assess-problem.mjs <venture dir> [--json] [--preflight] [--as-is]
|
|
8
|
+
//
|
|
9
|
+
// Assumptions: exists (the problem is real), segment (our segment has it), hurts (it costs
|
|
10
|
+
// enough that people already spend time or money on it), why-now (something has changed).
|
|
11
|
+
// Labels: demonstrated, inferred, assumed, unknown, contradicted.
|
|
12
|
+
// An item demonstrates when it is behavior or commitment from the target segment, first-hand
|
|
13
|
+
// (a file in the venture folder, or origin first-hand), five or more people, under 18 months
|
|
14
|
+
// old. Any other item from a target, adjacent or unknown audience infers; the wrong audience
|
|
15
|
+
// counts for nothing. Competitors with paying customers are behavior from an adjacent audience:
|
|
16
|
+
// they infer the problem, never count against it.
|
|
17
|
+
// Contradicted needs evidence against from the target segment at least as strong as the best
|
|
18
|
+
// evidence for. Missing evidence is unknown, never a verdict.
|
|
19
|
+
// Ladder: explore, prototype, charge, raise. The result names the rung the evidence supports and
|
|
20
|
+
// the cheapest test for each assumption that blocks the next rung. It never says "not viable".
|
|
21
|
+
// Forms: a root problem may take several forms (forms[]), each with its own evidence (evidence
|
|
22
|
+
// form); each open form is labelled from its own items, which also count for the root.
|
|
23
|
+
// Preflight: before assessing, it lists what is missing from the record and what running as is
|
|
24
|
+
// would mean, and stops with needs_input unless --as-is is given. The router, CLI, MCP server
|
|
25
|
+
// and API pass the same choice on (input as_is).
|
|
26
|
+
|
|
27
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
28
|
+
import { join } from "node:path";
|
|
29
|
+
|
|
30
|
+
export const ASSUMPTIONS = [
|
|
31
|
+
{ id: "exists", says: "the problem is real", field: "statement", test: "Find people already paying for or building a workaround (search, forums, competitors' customers)" },
|
|
32
|
+
{ id: "segment", says: "our segment has it", field: "segment", test: "Five problem interviews with people in the segment, asking about the last time it happened" },
|
|
33
|
+
{ id: "hurts", says: "it costs enough that people already spend time or money on it", field: "cost or workarounds", test: "Ask in those interviews what they spent on it last month, in hours or money, and look for a workaround they pay for" },
|
|
34
|
+
{ id: "why-now", says: "something has changed that makes it solvable or urgent now", field: "why_now", test: "Cite the change: a trend, a new technology, a rule, with a source" },
|
|
35
|
+
];
|
|
36
|
+
export const LADDER = [
|
|
37
|
+
{ rung: "explore", unlocks: "keep researching the problem" },
|
|
38
|
+
{ rung: "prototype", unlocks: "build something cheap to test a solution", needs: { exists: 1, hurts: 1 } },
|
|
39
|
+
{ rung: "charge", unlocks: "ask people in the segment to pay", needs: { exists: 1, segment: 2, hurts: 2 } },
|
|
40
|
+
{ rung: "raise", unlocks: "ask investors for money on the problem's strength (the problem alone is never enough)", needs: { exists: 2, segment: 2, hurts: 2, "why-now": 1 } },
|
|
41
|
+
];
|
|
42
|
+
const LABEL = { 2: "demonstrated", 1: "inferred" };
|
|
43
|
+
const MIN_SAMPLE = 5, MAX_AGE_MONTHS = 18;
|
|
44
|
+
|
|
45
|
+
const months = (from, to) => (to - new Date(from)) / (1000 * 60 * 60 * 24 * 30.44);
|
|
46
|
+
export const originOf = (e) => e.origin ?? (e.source?.path && !e.source?.url ? "first-hand" : "reported");
|
|
47
|
+
|
|
48
|
+
/** How strongly one item bears on its assumption: 2 demonstrates, 1 infers, 0 counts for nothing. */
|
|
49
|
+
export function strength(e, today = new Date()) {
|
|
50
|
+
if (e.audience === "wrong") return 0;
|
|
51
|
+
const shows = ["behavior", "commitment"].includes(e.kind) && e.audience === "target" && originOf(e) === "first-hand"
|
|
52
|
+
&& (e.sample?.n ?? 0) >= MIN_SAMPLE && months(e.collected, today) <= MAX_AGE_MONTHS;
|
|
53
|
+
return shows ? 2 : 1;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
const stated = (rec, a) => a.id === "hurts" ? !!(rec.cost || rec.workarounds?.length) : a.id === "why-now" ? !!rec.why_now : !!rec[a.field];
|
|
57
|
+
|
|
58
|
+
/** The label for each assumption, with the items behind it. */
|
|
59
|
+
export function label(rec, today = new Date()) {
|
|
60
|
+
const ev = rec?.evidence ?? [];
|
|
61
|
+
return ASSUMPTIONS.map((a) => {
|
|
62
|
+
const on = ev.filter((e) => (e.assumption ?? "exists") === a.id).map((e) => ({ id: e.id, supports: e.supports !== false, s: strength(e, today), target: e.audience === "target" }));
|
|
63
|
+
const best = Math.max(0, ...on.filter((x) => x.supports).map((x) => x.s));
|
|
64
|
+
const against = Math.max(0, ...on.filter((x) => !x.supports && x.target).map((x) => x.s));
|
|
65
|
+
const level = against > 0 && against >= best ? 0 : best;
|
|
66
|
+
const name = against > 0 && against >= best ? "contradicted" : LABEL[best] ?? (rec && stated(rec, a) ? "assumed" : "unknown");
|
|
67
|
+
return { assumption: a.id, says: a.says, label: name, level, for: on.filter((x) => x.supports && x.s > 0).map((x) => x.id), against: on.filter((x) => !x.supports).map((x) => x.id), ignored: on.filter((x) => x.s === 0).map((x) => x.id) };
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/** The highest rung the labels support, and what blocks the next one. */
|
|
72
|
+
export function ladder(labels) {
|
|
73
|
+
const lv = Object.fromEntries(labels.map((l) => [l.assumption, l.level]));
|
|
74
|
+
const contradicted = labels.filter((l) => l.label === "contradicted").map((l) => l.assumption);
|
|
75
|
+
let i = 0;
|
|
76
|
+
while (i + 1 < LADDER.length && !contradicted.length && Object.entries(LADDER[i + 1].needs).every(([a, n]) => lv[a] >= n)) i++;
|
|
77
|
+
const next = LADDER[i + 1];
|
|
78
|
+
const blocking = next ? Object.entries(next.needs).filter(([a, n]) => lv[a] < n || contradicted.includes(a)).map(([a, n]) => ({ assumption: a, needs: LABEL[n], has: labels.find((l) => l.assumption === a).label, test: ASSUMPTIONS.find((x) => x.id === a).test })) : [];
|
|
79
|
+
return { supports: LADDER[i].rung, unlocks: LADDER[i].unlocks, next: next?.rung ?? null, blocking, contradicted };
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/** What is missing from the record before assessing it, and what running as is would mean. */
|
|
83
|
+
export function preflight(rec) {
|
|
84
|
+
if (!rec) return [{ field: "purpose/validation/problem.json", why: "there is no problem record", as_is: "every assumption is unknown; the evidence supports exploring only" }];
|
|
85
|
+
const m = [];
|
|
86
|
+
for (const a of ASSUMPTIONS) if (!stated(rec, a)) m.push({ field: a.field, why: `says whether ${a.says}`, as_is: `${a.id} is unknown unless evidence bears on it` });
|
|
87
|
+
const untagged = (rec.evidence ?? []).filter((e) => !e.assumption).map((e) => e.id);
|
|
88
|
+
if (untagged.length) m.push({ field: "evidence[].assumption", items: untagged, why: "which assumption each item bears on (exists, segment, hurts, why-now)", as_is: "these items count toward exists only" });
|
|
89
|
+
const thin = (rec.evidence ?? []).filter((e) => ["behavior", "commitment", "stated"].includes(e.kind) && !e.sample).map((e) => e.id);
|
|
90
|
+
if (thin.length) m.push({ field: "evidence[].sample", items: thin, why: "how many people the item comes from", as_is: "these items can infer but not demonstrate" });
|
|
91
|
+
const bare = ASSUMPTIONS.filter((a) => !(rec.evidence ?? []).some((e) => (e.assumption ?? "exists") === a.id)).map((a) => a.id);
|
|
92
|
+
if (bare.length) m.push({ field: "evidence", items: bare, why: "no evidence bears on these assumptions yet", as_is: "they stay assumed or unknown" });
|
|
93
|
+
return m;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
export const ASK = "Some things the assessment reads are missing. Fill them in first, or run it as is?";
|
|
97
|
+
|
|
98
|
+
/** The whole run: preflight first, then the assessment unless input is missing and as_is is not set. */
|
|
99
|
+
export function assess(dir, { as_is = false, today = new Date() } = {}) {
|
|
100
|
+
const f = join(dir, "purpose/validation/problem.json");
|
|
101
|
+
const rec = existsSync(f) ? JSON.parse(readFileSync(f, "utf8")) : null;
|
|
102
|
+
const missing = preflight(rec);
|
|
103
|
+
if (missing.length && !as_is) return { needs_input: { ask: ASK, missing, options: [
|
|
104
|
+
{ choice: "fill-in", how: "write the missing fields to purpose/validation/problem.json (template: purpose/kits/validation/templates/problem.md), then run again" },
|
|
105
|
+
{ choice: "run-as-is", how: "run again with as_is (CLI --as-is; MCP and API as_is: true)" }] } };
|
|
106
|
+
const labels = label(rec, today);
|
|
107
|
+
// Each open form is assessed from its own evidence; its evidence also counts for the root.
|
|
108
|
+
const forms = (rec?.forms ?? []).filter((f) => f.status !== "dropped").map((f) => {
|
|
109
|
+
const own = label({ ...rec, statement: f.statement, segment: f.segment ?? rec.segment, evidence: (rec.evidence ?? []).filter((e) => e.form === f.id) }, today);
|
|
110
|
+
const { supports, next, blocking } = ladder(own);
|
|
111
|
+
return { form: f.id, statement: f.statement, assumptions: own, supports, next, blocking };
|
|
112
|
+
});
|
|
113
|
+
return { venture: rec?.venture ?? null, ran_as_is: missing.length > 0, missing, assumptions: labels, ...ladder(labels), forms };
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
if (import.meta.url === `file://${process.argv[1]}`) {
|
|
117
|
+
const dir = process.argv[2];
|
|
118
|
+
if (!dir) { console.error("usage: node schema/assess-problem.mjs <venture dir> [--json] [--preflight] [--as-is]"); process.exit(2); }
|
|
119
|
+
const flag = (f) => process.argv.includes(f);
|
|
120
|
+
const out = flag("--preflight") ? { missing: preflight(existsSync(join(dir, "purpose/validation/problem.json")) ? JSON.parse(readFileSync(join(dir, "purpose/validation/problem.json"), "utf8")) : null) } : assess(dir, { as_is: flag("--as-is") });
|
|
121
|
+
if (flag("--json")) { console.log(JSON.stringify(out, null, 2)); process.exit(0); }
|
|
122
|
+
const list = (m) => m.map((x) => ` - ${x.field}${x.items ? ` (${x.items.join(", ")})` : ""}: ${x.why}. As is: ${x.as_is}`).join("\n");
|
|
123
|
+
if (out.needs_input) { console.log(`${out.needs_input.ask}\n${list(out.needs_input.missing)}\nFill in: ${out.needs_input.options[0].how}\nRun as is: ${out.needs_input.options[1].how}`); process.exit(0); }
|
|
124
|
+
if (!out.assumptions) { console.log(out.missing.length ? list(out.missing) : "nothing missing"); process.exit(0); }
|
|
125
|
+
for (const a of out.assumptions) console.log(`${a.assumption.padEnd(8)} ${a.label.padEnd(13)} ${a.says}${a.for.length ? ` [for: ${a.for.join(", ")}]` : ""}${a.against.length ? ` [against: ${a.against.join(", ")}]` : ""}`);
|
|
126
|
+
console.log(`\nThe evidence supports: ${out.supports} (${out.unlocks}).`);
|
|
127
|
+
if (out.contradicted.length) console.log(`Evidence from the segment contradicts ${out.contradicted.join(", ")}: reframe the problem or the segment and test again.`);
|
|
128
|
+
if (out.next) console.log(`To reach ${out.next}:\n${out.blocking.map((b) => ` - ${b.assumption}: ${b.has}, needs ${b.needs}. ${b.test}`).join("\n")}`);
|
|
129
|
+
for (const f of out.forms) console.log(`\nForm ${f.form}: supports ${f.supports}. ${f.assumptions.map((a) => `${a.assumption} ${a.label}`).join(", ")}`);
|
|
130
|
+
if (out.ran_as_is) console.log(`\nRan as is; missing:\n${list(out.missing)}`);
|
|
131
|
+
}
|
package/schema/bdd.mjs
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// Runs Gherkin feature files as tests (EE-050): behavior-driven development without dependencies.
|
|
3
|
+
// Each scenario's Given, When, Then, And and But steps are matched against the step definitions
|
|
4
|
+
// in <name>.steps.mjs beside <name>.feature (an exported array of [RegExp, async (world, ...groups)]).
|
|
5
|
+
// A step with no definition fails its scenario, so a behavior cannot be written down and skipped.
|
|
6
|
+
//
|
|
7
|
+
// node schema/bdd.mjs <file.feature>...
|
|
8
|
+
//
|
|
9
|
+
// Supports Feature, Rule, Background, Scenario, the five step keywords and # comments; not
|
|
10
|
+
// Scenario Outline, tables or doc strings yet.
|
|
11
|
+
|
|
12
|
+
import { readFileSync } from "node:fs";
|
|
13
|
+
|
|
14
|
+
export function parse(text) {
|
|
15
|
+
const scenarios = [];
|
|
16
|
+
let rule = null, background = [], cur = null;
|
|
17
|
+
for (const raw of text.split("\n")) {
|
|
18
|
+
const line = raw.trim();
|
|
19
|
+
if (!line || line.startsWith("#")) continue;
|
|
20
|
+
let m;
|
|
21
|
+
if ((m = line.match(/^Rule:\s*(.*)$/))) { rule = m[1]; background = []; cur = null; }
|
|
22
|
+
else if (/^Background:/.test(line)) cur = { background: true, steps: background };
|
|
23
|
+
else if ((m = line.match(/^Scenario:\s*(.*)$/))) { cur = { name: m[1], rule, steps: [...background] }; scenarios.push(cur); }
|
|
24
|
+
else if ((m = line.match(/^(Given|When|Then|And|But)\s+(.*)$/)) && cur) cur.steps.push(m[2]);
|
|
25
|
+
}
|
|
26
|
+
return scenarios;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export async function run(file) {
|
|
30
|
+
const { steps } = await import(file.replace(/\.feature$/, ".steps.mjs").replace(/^(?!\/)/, `${process.cwd()}/`));
|
|
31
|
+
const results = [];
|
|
32
|
+
for (const sc of parse(readFileSync(file, "utf8"))) {
|
|
33
|
+
const world = {};
|
|
34
|
+
let error = null;
|
|
35
|
+
for (const text of sc.steps) {
|
|
36
|
+
const hit = steps.map(([re, fn]) => [text.match(re), fn]).find(([m]) => m);
|
|
37
|
+
if (!hit) { error = `no step definition: ${text}`; break; }
|
|
38
|
+
try { await hit[1](world, ...hit[0].slice(1)); } catch (e) { error = `${text}: ${e.message}`; break; }
|
|
39
|
+
}
|
|
40
|
+
await world.cleanup?.();
|
|
41
|
+
results.push({ scenario: sc.name, rule: sc.rule, ok: !error, error });
|
|
42
|
+
}
|
|
43
|
+
return results;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
if (import.meta.url === `file://${process.argv[1]}`) {
|
|
47
|
+
const files = process.argv.slice(2);
|
|
48
|
+
if (!files.length) { console.error("usage: node schema/bdd.mjs <file.feature>..."); process.exit(2); }
|
|
49
|
+
let failed = 0;
|
|
50
|
+
for (const f of files) for (const r of await run(f)) {
|
|
51
|
+
console.log(`${r.ok ? "ok " : "FAIL"} ${r.scenario}${r.ok ? "" : `\n ${r.error}`}`);
|
|
52
|
+
if (!r.ok) failed++;
|
|
53
|
+
}
|
|
54
|
+
console.log(failed ? `\n${failed} scenarios failing` : "\nall scenarios pass");
|
|
55
|
+
process.exit(failed ? 1 : 0);
|
|
56
|
+
}
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
+
"$id": "board.schema.json",
|
|
4
|
+
"title": "Boards",
|
|
5
|
+
"description": "The venture's boards, at <venture>/governance/board.json (EE-022): at most one formal board of directors (or managers, or trustees), which holds legal authority, and any advisory boards, which advise and decide nothing. A seat says who fills it and who appoints them; reserved matters are what only the formal board may decide. schema/check-governance.mjs checks seats against ownership.json.",
|
|
6
|
+
"type": "object",
|
|
7
|
+
"required": [
|
|
8
|
+
"updated",
|
|
9
|
+
"boards"
|
|
10
|
+
],
|
|
11
|
+
"additionalProperties": false,
|
|
12
|
+
"properties": {
|
|
13
|
+
"updated": {
|
|
14
|
+
"type": "string",
|
|
15
|
+
"format": "date"
|
|
16
|
+
},
|
|
17
|
+
"boards": {
|
|
18
|
+
"type": "array",
|
|
19
|
+
"items": {
|
|
20
|
+
"type": "object",
|
|
21
|
+
"required": [
|
|
22
|
+
"id",
|
|
23
|
+
"kind",
|
|
24
|
+
"seats"
|
|
25
|
+
],
|
|
26
|
+
"additionalProperties": false,
|
|
27
|
+
"properties": {
|
|
28
|
+
"id": {
|
|
29
|
+
"type": "string",
|
|
30
|
+
"pattern": "^[a-z0-9-]+$"
|
|
31
|
+
},
|
|
32
|
+
"kind": {
|
|
33
|
+
"enum": [
|
|
34
|
+
"formal",
|
|
35
|
+
"advisory"
|
|
36
|
+
]
|
|
37
|
+
},
|
|
38
|
+
"formed": {
|
|
39
|
+
"type": "string",
|
|
40
|
+
"format": "date"
|
|
41
|
+
},
|
|
42
|
+
"meets": {
|
|
43
|
+
"enum": [
|
|
44
|
+
"monthly",
|
|
45
|
+
"every-6-weeks",
|
|
46
|
+
"quarterly",
|
|
47
|
+
"twice-a-year",
|
|
48
|
+
"yearly",
|
|
49
|
+
"as-needed"
|
|
50
|
+
]
|
|
51
|
+
},
|
|
52
|
+
"seats": {
|
|
53
|
+
"type": "array",
|
|
54
|
+
"items": {
|
|
55
|
+
"type": "object",
|
|
56
|
+
"required": [
|
|
57
|
+
"id",
|
|
58
|
+
"type",
|
|
59
|
+
"appointed_by"
|
|
60
|
+
],
|
|
61
|
+
"additionalProperties": false,
|
|
62
|
+
"properties": {
|
|
63
|
+
"id": {
|
|
64
|
+
"type": "string",
|
|
65
|
+
"pattern": "^[a-z0-9-]+$"
|
|
66
|
+
},
|
|
67
|
+
"person": {
|
|
68
|
+
"type": [
|
|
69
|
+
"string",
|
|
70
|
+
"null"
|
|
71
|
+
],
|
|
72
|
+
"description": "null while the seat is empty"
|
|
73
|
+
},
|
|
74
|
+
"type": {
|
|
75
|
+
"enum": [
|
|
76
|
+
"founder",
|
|
77
|
+
"investor",
|
|
78
|
+
"independent",
|
|
79
|
+
"advisor",
|
|
80
|
+
"observer"
|
|
81
|
+
]
|
|
82
|
+
},
|
|
83
|
+
"appointed_by": {
|
|
84
|
+
"type": "string",
|
|
85
|
+
"description": "A holder id, a class id from ownership.json, or 'board' (chosen by the board itself)"
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
},
|
|
90
|
+
"reserved_matters": {
|
|
91
|
+
"type": "array",
|
|
92
|
+
"items": {
|
|
93
|
+
"type": "string"
|
|
94
|
+
},
|
|
95
|
+
"description": "Formal boards only: decisions only this board may make, such as issuing shares, approving the budget, hiring or removing the CEO, selling the company"
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
// The framework/platform boundary (EE-041). Mentura is a framework (conventions that work offline,
|
|
2
|
+
// with no account and no named vendor, like Next.js) and a platform that hosts, connects and
|
|
3
|
+
// integrates (like Vercel). Both live in this repository; platform code lives in platform/.
|
|
4
|
+
//
|
|
5
|
+
// 1. Nothing outside platform/ imports from it. The platform builds on the framework, never the
|
|
6
|
+
// other way round. The framework may still load a platform connector by the module path the
|
|
7
|
+
// registry names (knowledge/tooling/integrations.json): that is inversion of control, like a
|
|
8
|
+
// Next.js adapter, and it never fails when the platform is absent from a copy.
|
|
9
|
+
// 2. A connector the framework ships (its module outside platform/) works offline and names no
|
|
10
|
+
// vendor: no fetch, no node:http, node:https or node:net, and no credentials.
|
|
11
|
+
// 3. Every registered connector module exists.
|
|
12
|
+
//
|
|
13
|
+
// node schema/check-boundary.mjs (EE_ROOT=<dir> checks another copy)
|
|
14
|
+
|
|
15
|
+
import { existsSync, readdirSync, readFileSync } from "node:fs";
|
|
16
|
+
import { dirname, join, relative, resolve } from "node:path";
|
|
17
|
+
|
|
18
|
+
const root = process.env.EE_ROOT ?? new URL("..", import.meta.url).pathname;
|
|
19
|
+
const PLATFORM = "platform";
|
|
20
|
+
const SKIP = /^(\.git|node_modules|\.next|_pagefind|out|generated)$/;
|
|
21
|
+
const CODE = /\.(mjs|cjs|js|jsx|ts|tsx)$/;
|
|
22
|
+
const errors = [];
|
|
23
|
+
|
|
24
|
+
const walk = (d) => readdirSync(d, { withFileTypes: true }).flatMap((e) => SKIP.test(e.name) ? []
|
|
25
|
+
: e.isDirectory() ? walk(join(d, e.name)) : CODE.test(e.name) ? [join(d, e.name)] : []);
|
|
26
|
+
const inPlatform = (abs) => { const r = relative(root, abs); return r === PLATFORM || r.startsWith(PLATFORM + "/"); };
|
|
27
|
+
|
|
28
|
+
// 1. Imports: static, dynamic with a literal, and require.
|
|
29
|
+
const SPEC = /(?:\bfrom\s*|\bimport\s*\(\s*|\brequire\s*\(\s*|^\s*import\s+)(["'`])([^"'`]+)\1/gm;
|
|
30
|
+
let files = 0;
|
|
31
|
+
for (const f of walk(root).filter((f) => !inPlatform(f))) {
|
|
32
|
+
files++;
|
|
33
|
+
const src = readFileSync(f, "utf8");
|
|
34
|
+
for (const m of src.matchAll(SPEC)) {
|
|
35
|
+
const spec = m[2];
|
|
36
|
+
const target = spec.startsWith(".") ? resolve(dirname(f), spec) : spec.startsWith("/") ? spec : null;
|
|
37
|
+
if (target && inPlatform(target)) errors.push(`${relative(root, f)} imports ${spec}: framework code never imports platform code`);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// 2 and 3. Connectors in the registry.
|
|
42
|
+
const regFile = join(root, "knowledge/tooling/integrations.json");
|
|
43
|
+
const reg = existsSync(regFile) ? JSON.parse(readFileSync(regFile, "utf8")).filter((t) => t.connector) : [];
|
|
44
|
+
const NET = /\bfetch\s*\(|["']node:(https?|net|tls|dgram)["']/;
|
|
45
|
+
for (const t of reg) {
|
|
46
|
+
const abs = join(root, t.connector.module);
|
|
47
|
+
if (!existsSync(abs)) { errors.push(`integration ${t.id}: connector module ${t.connector.module} does not exist`); continue; }
|
|
48
|
+
if (inPlatform(abs)) continue;
|
|
49
|
+
if (NET.test(readFileSync(abs, "utf8"))) errors.push(`integration ${t.id}: ${t.connector.module} reaches the network, so it belongs in ${PLATFORM}/`);
|
|
50
|
+
if (t.credentials?.length) errors.push(`integration ${t.id}: a framework connector needs no credentials (it names ${t.credentials.join(", ")}), so it belongs in ${PLATFORM}/`);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
if (errors.length) { for (const e of errors) console.error(e); process.exit(1); }
|
|
54
|
+
console.log(`${files} framework code files import nothing from ${PLATFORM}/; ${reg.filter((t) => !inPlatform(join(root, t.connector.module))).length} framework connector(s) work offline.`);
|