naive-framework 0.0.0-stage → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +62 -0
- package/LICENSE +21 -0
- package/README.md +37 -2
- package/docs/decisions/decisions.json +888 -0
- package/docs/ontology.json +796 -0
- package/docs/ontology.md +163 -0
- package/docs/reference/ROUTES.md +124 -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 +102 -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/governance/venture-template/roles/venture-roles.json +62 -0
- package/interface/acting.mjs +83 -0
- package/interface/adapters/claude-code.json +16 -0
- package/interface/adapters/codex.json +14 -0
- package/interface/adapters/cursor.json +16 -0
- package/interface/adapters.mjs +182 -0
- package/interface/api/server.mjs +71 -0
- package/interface/boundaries.mjs +218 -0
- package/interface/brand.mjs +214 -0
- package/interface/cli/find-venture.mjs +15 -0
- package/interface/cli/freshness.mjs +59 -0
- package/interface/cli/guard.mjs +6 -0
- package/interface/cli/mentura.mjs +341 -0
- package/interface/cli/naive.mjs +194 -0
- package/interface/context.mjs +524 -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/dev/setup.mjs +84 -0
- package/interface/gate-hooks.mjs +36 -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/instrumentation.mjs +86 -0
- package/interface/kit-boundary.mjs +73 -0
- package/interface/layer.json +37 -0
- package/interface/mcp/server.mjs +162 -0
- package/interface/operator-console/README.md +58 -0
- package/interface/package-routes.mjs +196 -0
- package/interface/provenance.mjs +65 -0
- package/interface/reads.mjs +173 -0
- package/interface/references/references.json +28 -0
- package/interface/release-card.mjs +42 -0
- package/interface/route-aliases.json +32 -0
- package/interface/router.mjs +653 -0
- package/interface/skills/install-mentura/SKILL.md +88 -0
- package/interface/skills/naive-setup/SKILL.md +71 -0
- package/interface/skills/naive-setup/artifact.html +83 -0
- package/interface/tooling/integrations.json +112 -0
- package/interface/vitals.mjs +115 -0
- package/interface/writes.mjs +190 -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/freshness.json +85 -0
- package/knowledge/layer.json +50 -0
- package/knowledge/skills/find-reference/SKILL.md +38 -0
- package/knowledge/skills/industry-awareness/SKILL.md +277 -0
- package/knowledge/templates/glossary.md +23 -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 +67 -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/marketing-brand.md +46 -0
- package/operations/templates/public-assets.md +39 -0
- package/operations/templates/recruiting-brief.md +35 -0
- package/operations/tooling/integrations.json +158 -0
- package/package.json +59 -4
- package/packages/catalog.json +280 -0
- package/packages/cofounder/package.json +42 -0
- package/packages/counsel/package.json +47 -0
- package/packages/customers/package.json +41 -0
- package/packages/deals/package.json +27 -0
- package/packages/decisions/package.json +28 -0
- package/packages/engineering/package.json +41 -0
- package/packages/evidence/package.json +17 -0
- package/packages/finance/frames/fund-without-ceding-control.frame.json +177 -0
- package/packages/finance/package.json +48 -0
- package/packages/foundation/package.json +59 -0
- package/packages/fund/package.json +48 -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 +63 -0
- package/packages/money/package.json +42 -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 +112 -0
- package/platform/README.md +51 -0
- package/platform/adapter.json +15 -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/remote-image.mjs +79 -0
- package/platform/integrations/rho.mjs +146 -0
- package/platform/integrations/site.mjs +75 -0
- package/platform/kits/README.md +14 -0
- package/platform/kits/agent-upgrade/kit.json +15 -0
- package/platform/kits/agent-upgrade/run.mjs +82 -0
- package/platform/kits/agent-upgrade/stand-in-agent.mjs +32 -0
- package/platform/kits/validation-kickoff/kit.json +29 -0
- package/platform/mcp/research.mjs +64 -0
- package/platform/webhooks/server.mjs +70 -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 +181 -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 +62 -0
- package/schema/VERSION +1 -0
- package/schema/access.mjs +57 -0
- package/schema/account.schema.json +79 -0
- package/schema/adapter.schema.json +33 -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/assets.mjs +212 -0
- package/schema/bdd.mjs +56 -0
- package/schema/board.schema.json +101 -0
- package/schema/brand.mjs +373 -0
- package/schema/build-release.mjs +61 -0
- package/schema/caching.mjs +505 -0
- package/schema/check-assets.mjs +24 -0
- package/schema/check-boundary.mjs +54 -0
- package/schema/check-brand.mjs +24 -0
- package/schema/check-config.mjs +110 -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 +79 -0
- package/schema/check-nouns.mjs +49 -0
- package/schema/check-people.mjs +70 -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 +74 -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 +1584 -0
- package/schema/data-model.schema.json +152 -0
- package/schema/deal.schema.json +80 -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 +327 -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/frames/judge.mjs +10 -0
- package/schema/examples/migrate/agent.mjs +12 -0
- package/schema/examples/venture-type/jev.mjs +12 -0
- package/schema/examples/workspace/ventures/acme-labs/AGENTS.md +17 -0
- package/schema/examples/workspace/ventures/acme-labs/accounts/.gitkeep +0 -0
- package/schema/examples/workspace/ventures/acme-labs/decisions/discovery-goal.decision.json +23 -0
- package/schema/examples/workspace/ventures/acme-labs/decisions/statement-icp.decision.json +22 -0
- package/schema/examples/workspace/ventures/acme-labs/decisions/statement-problem.decision.json +22 -0
- package/schema/examples/workspace/ventures/acme-labs/decisions/venture.team.decision.json +37 -0
- package/schema/examples/workspace/ventures/acme-labs/knowledge/README.md +3 -0
- package/schema/examples/workspace/ventures/acme-labs/knowledge/sources.json +46 -0
- package/schema/examples/workspace/ventures/acme-labs/money/30000000-0000-4000-8000-000000000001.json +12 -0
- package/schema/examples/workspace/ventures/acme-labs/money/30000000-0000-4000-8000-000000000002.json +12 -0
- package/schema/examples/workspace/ventures/acme-labs/money/30000000-0000-4000-8000-000000000003.json +12 -0
- package/schema/examples/workspace/ventures/acme-labs/money/30000000-0000-4000-8000-000000000004.json +12 -0
- package/schema/examples/workspace/ventures/acme-labs/money/30000000-0000-4000-8000-000000000005.json +12 -0
- package/schema/examples/workspace/ventures/acme-labs/money/30000000-0000-4000-8000-000000000006.json +12 -0
- package/schema/examples/workspace/ventures/acme-labs/money/30000000-0000-4000-8000-000000000007.json +12 -0
- package/schema/examples/workspace/ventures/acme-labs/money/30000000-0000-4000-8000-000000000008.json +12 -0
- package/schema/examples/workspace/ventures/acme-labs/money/30000000-0000-4000-8000-000000000009.json +12 -0
- package/schema/examples/workspace/ventures/acme-labs/money/30000000-0000-4000-8000-000000000010.json +12 -0
- package/schema/examples/workspace/ventures/acme-labs/money/30000000-0000-4000-8000-000000000011.json +12 -0
- package/schema/examples/workspace/ventures/acme-labs/money/30000000-0000-4000-8000-000000000012.json +12 -0
- package/schema/examples/workspace/ventures/acme-labs/money/30000000-0000-4000-8000-000000000013.json +12 -0
- package/schema/examples/workspace/ventures/acme-labs/money/30000000-0000-4000-8000-000000000014.json +12 -0
- package/schema/examples/workspace/ventures/acme-labs/money/_streams/transactions/stream.json +5 -0
- package/schema/examples/workspace/ventures/acme-labs/money/_summary.json +30 -0
- package/schema/examples/workspace/ventures/acme-labs/operations/departments/customer-service/README.md +5 -0
- package/schema/examples/workspace/ventures/acme-labs/operations/departments/customer-service/department.json +5 -0
- package/schema/examples/workspace/ventures/acme-labs/operations/departments/finance/README.md +5 -0
- package/schema/examples/workspace/ventures/acme-labs/operations/departments/finance/department.json +5 -0
- package/schema/examples/workspace/ventures/acme-labs/operations/departments/human-capital/README.md +5 -0
- package/schema/examples/workspace/ventures/acme-labs/operations/departments/human-capital/department.json +5 -0
- package/schema/examples/workspace/ventures/acme-labs/operations/departments/it/README.md +5 -0
- package/schema/examples/workspace/ventures/acme-labs/operations/departments/it/department.json +5 -0
- package/schema/examples/workspace/ventures/acme-labs/operations/departments/marketing-and-sales/README.md +5 -0
- package/schema/examples/workspace/ventures/acme-labs/operations/departments/marketing-and-sales/department.json +5 -0
- package/schema/examples/workspace/ventures/acme-labs/operations/departments/products/README.md +5 -0
- package/schema/examples/workspace/ventures/acme-labs/operations/departments/products/department.json +5 -0
- package/schema/examples/workspace/ventures/acme-labs/operations/departments/strategy/README.md +5 -0
- package/schema/examples/workspace/ventures/acme-labs/operations/departments/strategy/department.json +5 -0
- package/schema/examples/workspace/ventures/acme-labs/people/founders/maya-chen.json +15 -0
- package/schema/examples/workspace/ventures/acme-labs/people/people.json +22 -0
- package/schema/examples/workspace/ventures/acme-labs/pillars/brand.md +5 -0
- package/schema/examples/workspace/ventures/acme-labs/pillars/business.md +5 -0
- package/schema/examples/workspace/ventures/acme-labs/pillars/product.md +5 -0
- package/schema/examples/workspace/ventures/acme-labs/products/.gitkeep +0 -0
- package/schema/examples/workspace/ventures/acme-labs/products/decisions/product.core-product.decision.json +28 -0
- package/schema/examples/workspace/ventures/acme-labs/products/decisions/product.exists.decision.json +38 -0
- package/schema/examples/workspace/ventures/acme-labs/products/decisions/product.flagship.decision.json +27 -0
- package/schema/examples/workspace/ventures/acme-labs/profile.md +12 -0
- package/schema/examples/workspace/ventures/acme-labs/provenance.jsonl +12 -0
- package/schema/examples/workspace/ventures/acme-labs/raw/dump.md +6 -0
- package/schema/examples/workspace/ventures/acme-labs/venture.json +27 -0
- package/schema/examples/workspace/ventures/northstar/AGENTS.md +17 -0
- package/schema/examples/workspace/ventures/northstar/accounts/.gitkeep +0 -0
- package/schema/examples/workspace/ventures/northstar/deals/ductline.json +8 -0
- package/schema/examples/workspace/ventures/northstar/deals/kilnworks.json +14 -0
- package/schema/examples/workspace/ventures/northstar/decisions/venture.team.decision.json +31 -0
- package/schema/examples/workspace/ventures/northstar/evidence/README.md +3 -0
- package/schema/examples/workspace/ventures/northstar/fund/decisions/terms.decision.json +27 -0
- package/schema/examples/workspace/ventures/northstar/fund/decisions/terms.first-close.decision.json +27 -0
- package/schema/examples/workspace/ventures/northstar/fund/decisions/thesis.decision.json +26 -0
- package/schema/examples/workspace/ventures/northstar/fund/terms.json +22 -0
- package/schema/examples/workspace/ventures/northstar/fund/thesis.json +22 -0
- package/schema/examples/workspace/ventures/northstar/governance/README.md +3 -0
- package/schema/examples/workspace/ventures/northstar/governance/departments/external-relationships/charter.md +5 -0
- package/schema/examples/workspace/ventures/northstar/governance/departments/external-relationships/department.json +5 -0
- package/schema/examples/workspace/ventures/northstar/governance/departments/finance/charter.md +5 -0
- package/schema/examples/workspace/ventures/northstar/governance/departments/finance/department.json +5 -0
- package/schema/examples/workspace/ventures/northstar/governance/departments/human-capital/charter.md +5 -0
- package/schema/examples/workspace/ventures/northstar/governance/departments/human-capital/department.json +5 -0
- package/schema/examples/workspace/ventures/northstar/governance/departments/risk-and-compliance/charter.md +5 -0
- package/schema/examples/workspace/ventures/northstar/governance/departments/risk-and-compliance/department.json +5 -0
- package/schema/examples/workspace/ventures/northstar/governance/departments/strategy/charter.md +5 -0
- package/schema/examples/workspace/ventures/northstar/governance/departments/strategy/department.json +5 -0
- package/schema/examples/workspace/ventures/northstar/knowledge/README.md +3 -0
- package/schema/examples/workspace/ventures/northstar/operations/README.md +3 -0
- package/schema/examples/workspace/ventures/northstar/operations/departments/external-relationships/README.md +5 -0
- package/schema/examples/workspace/ventures/northstar/operations/departments/external-relationships/department.json +5 -0
- package/schema/examples/workspace/ventures/northstar/operations/departments/finance/README.md +5 -0
- package/schema/examples/workspace/ventures/northstar/operations/departments/finance/department.json +5 -0
- package/schema/examples/workspace/ventures/northstar/operations/departments/human-capital/README.md +5 -0
- package/schema/examples/workspace/ventures/northstar/operations/departments/human-capital/department.json +5 -0
- package/schema/examples/workspace/ventures/northstar/operations/departments/risk-and-compliance/README.md +5 -0
- package/schema/examples/workspace/ventures/northstar/operations/departments/risk-and-compliance/department.json +5 -0
- package/schema/examples/workspace/ventures/northstar/operations/departments/strategy/README.md +5 -0
- package/schema/examples/workspace/ventures/northstar/operations/departments/strategy/department.json +5 -0
- package/schema/examples/workspace/ventures/northstar/people/founders/ana-ruiz.json +13 -0
- package/schema/examples/workspace/ventures/northstar/people/founders/ben-okafor.json +7 -0
- package/schema/examples/workspace/ventures/northstar/people/people.json +27 -0
- package/schema/examples/workspace/ventures/northstar/pillars/brand.md +5 -0
- package/schema/examples/workspace/ventures/northstar/pillars/business.md +5 -0
- package/schema/examples/workspace/ventures/northstar/pillars/product.md +5 -0
- package/schema/examples/workspace/ventures/northstar/portfolio/kilnworks/AGENTS.md +17 -0
- package/schema/examples/workspace/ventures/northstar/portfolio/kilnworks/accounts/.gitkeep +0 -0
- package/schema/examples/workspace/ventures/northstar/portfolio/kilnworks/decisions/venture.team.decision.json +31 -0
- package/schema/examples/workspace/ventures/northstar/portfolio/kilnworks/knowledge/README.md +3 -0
- package/schema/examples/workspace/ventures/northstar/portfolio/kilnworks/operations/departments/customer-service/README.md +5 -0
- package/schema/examples/workspace/ventures/northstar/portfolio/kilnworks/operations/departments/customer-service/department.json +5 -0
- package/schema/examples/workspace/ventures/northstar/portfolio/kilnworks/operations/departments/finance/README.md +5 -0
- package/schema/examples/workspace/ventures/northstar/portfolio/kilnworks/operations/departments/finance/department.json +5 -0
- package/schema/examples/workspace/ventures/northstar/portfolio/kilnworks/operations/departments/human-capital/README.md +5 -0
- package/schema/examples/workspace/ventures/northstar/portfolio/kilnworks/operations/departments/human-capital/department.json +5 -0
- package/schema/examples/workspace/ventures/northstar/portfolio/kilnworks/operations/departments/it/README.md +5 -0
- package/schema/examples/workspace/ventures/northstar/portfolio/kilnworks/operations/departments/it/department.json +5 -0
- package/schema/examples/workspace/ventures/northstar/portfolio/kilnworks/operations/departments/marketing-and-sales/README.md +5 -0
- package/schema/examples/workspace/ventures/northstar/portfolio/kilnworks/operations/departments/marketing-and-sales/department.json +5 -0
- package/schema/examples/workspace/ventures/northstar/portfolio/kilnworks/operations/departments/products/README.md +5 -0
- package/schema/examples/workspace/ventures/northstar/portfolio/kilnworks/operations/departments/products/department.json +5 -0
- package/schema/examples/workspace/ventures/northstar/portfolio/kilnworks/operations/departments/strategy/README.md +5 -0
- package/schema/examples/workspace/ventures/northstar/portfolio/kilnworks/operations/departments/strategy/department.json +5 -0
- package/schema/examples/workspace/ventures/northstar/portfolio/kilnworks/pillars/brand.md +5 -0
- package/schema/examples/workspace/ventures/northstar/portfolio/kilnworks/pillars/business.md +5 -0
- package/schema/examples/workspace/ventures/northstar/portfolio/kilnworks/pillars/product.md +5 -0
- package/schema/examples/workspace/ventures/northstar/portfolio/kilnworks/products/.gitkeep +0 -0
- package/schema/examples/workspace/ventures/northstar/portfolio/kilnworks/products/decisions/product.core-product.decision.json +28 -0
- package/schema/examples/workspace/ventures/northstar/portfolio/kilnworks/products/decisions/product.exists.decision.json +32 -0
- package/schema/examples/workspace/ventures/northstar/portfolio/kilnworks/products/decisions/product.flagship.decision.json +27 -0
- package/schema/examples/workspace/ventures/northstar/portfolio/kilnworks/profile.md +5 -0
- package/schema/examples/workspace/ventures/northstar/portfolio/kilnworks/venture.json +27 -0
- package/schema/examples/workspace/ventures/northstar/products/.gitkeep +0 -0
- package/schema/examples/workspace/ventures/northstar/profile.md +5 -0
- package/schema/examples/workspace/ventures/northstar/provenance.jsonl +3 -0
- package/schema/examples/workspace/ventures/northstar/purpose/README.md +3 -0
- package/schema/examples/workspace/ventures/northstar/venture.json +49 -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/founder.schema.json +72 -0
- package/schema/frame.schema.json +240 -0
- package/schema/frames.mjs +228 -0
- package/schema/freshness-log.schema.json +40 -0
- package/schema/freshness-profiles.schema.json +44 -0
- package/schema/freshness.mjs +473 -0
- package/schema/fund-terms.schema.json +107 -0
- package/schema/fund-thesis.schema.json +87 -0
- package/schema/gate-drafts.mjs +130 -0
- package/schema/gate.mjs +133 -0
- package/schema/glossary.schema.json +25 -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 +128 -0
- package/schema/integration.schema.json +143 -0
- package/schema/judgment.schema.json +38 -0
- package/schema/kit.schema.json +47 -0
- package/schema/kits.mjs +24 -0
- package/schema/knowledge-sources.schema.json +260 -0
- package/schema/layer.json +59 -0
- package/schema/layer.schema.json +119 -0
- package/schema/load-packages.mjs +26 -0
- package/schema/marketing-brand.schema.json +116 -0
- package/schema/marketing-sitemap.schema.json +232 -0
- package/schema/migrate-venture.mjs +192 -0
- package/schema/migrations/kit-to-package.mjs +94 -0
- package/schema/migrations/package-layout.mjs +86 -0
- package/schema/model-steps.mjs +717 -0
- package/schema/money-summary.schema.json +138 -0
- package/schema/naive-config.mjs +165 -0
- package/schema/naive-config.schema.json +268 -0
- package/schema/need-now.json +772 -0
- package/schema/need-now.mjs +108 -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 +252 -0
- package/schema/payouts.mjs +91 -0
- package/schema/people.schema.json +86 -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/public-assets.schema.json +76 -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/release-read.mjs +111 -0
- package/schema/releases.mjs +497 -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/setup.mjs +164 -0
- package/schema/site-presence.schema.json +337 -0
- package/schema/stale.mjs +111 -0
- package/schema/start-release.mjs +41 -0
- package/schema/strategic-foundation.schema.json +74 -0
- package/schema/stream.schema.json +14 -0
- package/schema/structure.mjs +186 -0
- package/schema/systems-catalog.json +126 -0
- package/schema/systems-catalog.schema.json +78 -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/trigger.schema.json +44 -0
- package/schema/triggers.mjs +237 -0
- package/schema/upgrade-registry.schema.json +60 -0
- package/schema/upgrade-venture.mjs +107 -0
- package/schema/upgrades.json +69 -0
- package/schema/upgrades.mjs +385 -0
- package/schema/use-model.mjs +10 -0
- package/schema/validate.py +58 -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-role.schema.json +64 -0
- package/schema/venture-type.mjs +62 -0
- package/schema/venture-types.json +346 -0
- package/schema/venture-types.schema.json +129 -0
- package/schema/venture.schema.json +460 -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,653 @@
|
|
|
1
|
+
// The route table and its actions, shared by the CLI, the MCP server and the HTTP API (EE-030,
|
|
2
|
+
// EE-032). Routes are generated from the data model and the kit manifests, never written by hand,
|
|
3
|
+
// so they cannot drift from the files. The conventions follow the Next.js App Router:
|
|
4
|
+
//
|
|
5
|
+
// segments a route is <layer>[/<kit>]/<record>; layers stay in the path, so a route is the
|
|
6
|
+
// record's folder path (purpose/validation/problem -> purpose/validation/problem.json)
|
|
7
|
+
// [id] a collection (products, accounts, handoffs, evidence) has a list route and an
|
|
8
|
+
// item route: purpose/product and purpose/product/[id]
|
|
9
|
+
// [...venture] the venture is the dynamic segment in front of every route: an id, or its folder
|
|
10
|
+
// path for a nested venture (studio/print)
|
|
11
|
+
// route record routes: read, write, check, template; command routes: run
|
|
12
|
+
// page view: a Markdown page of a record or command result, for people
|
|
13
|
+
// layout layout: the context wrapping a route (system, layer and kit purpose, references,
|
|
14
|
+
// skills, the gate and what need-now says), given to an agent before it acts
|
|
15
|
+
// not-found read of a missing record answers with its template and need-now status
|
|
16
|
+
// loading read of a record with an agent's draft waiting says so
|
|
17
|
+
// error a failed check or write answers with the kit's skills and the template to fix it
|
|
18
|
+
// (group) route groups by phase: (discovery) ... (platform) gather the routes need-now
|
|
19
|
+
// suggests in that phase, with the phase as their layout; the path is unchanged
|
|
20
|
+
// @slot system/overview renders slots side by side (telos, validation, need-now, ready,
|
|
21
|
+
// team, provenance); a failing slot shows its error and the others still render
|
|
22
|
+
// _private _drafts/ is never routed
|
|
23
|
+
// proxy gates run before a write: the schema, the decision class the data model gives the
|
|
24
|
+
// record (escalate -> an agent's write is a draft), and the role's allowed routes
|
|
25
|
+
// robots a team-plan role may list the routes it may read and write; system/robots shows them
|
|
26
|
+
// aliases interface/route-aliases.json maps a renamed route to its new one, with a note
|
|
27
|
+
// hierarchy a read resolves layout, template, error, loading, not-found, then page (P-28);
|
|
28
|
+
// resolved_by names the level that answered (interface/reads.mjs)
|
|
29
|
+
// writes a write answers with what it changed and the next route to act on; a layout lists
|
|
30
|
+
// the actions open to the actor; package, group and venture routes; global errors
|
|
31
|
+
// (interface/writes.mjs wraps call)
|
|
32
|
+
// storage a record is read and written through the connector for the source its record
|
|
33
|
+
// type lives in (knowledge/connectors, EE-037): the venture's own folder unless its
|
|
34
|
+
// knowledge/sources.json names another, such as a GitHub repository, where a write
|
|
35
|
+
// opens a draft pull request. A read says which source and version it came from.
|
|
36
|
+
//
|
|
37
|
+
// Partial adoption: a route needs only its own record and what its kit reads.
|
|
38
|
+
|
|
39
|
+
import { execFileSync, spawnSync } from "node:child_process";
|
|
40
|
+
import { createHash } from "node:crypto";
|
|
41
|
+
import { existsSync, mkdirSync, mkdtempSync, readFileSync, readdirSync, rmSync, writeFileSync } from "node:fs";
|
|
42
|
+
import { tmpdir } from "node:os";
|
|
43
|
+
import { basename, dirname, join, relative } from "node:path";
|
|
44
|
+
import { agentFromEnv, entries as provEntries, record as prov } from "./provenance.mjs";
|
|
45
|
+
import { address } from "../knowledge/connectors/contract.mjs";
|
|
46
|
+
import { holding, locked } from "../schema/decisions.mjs";
|
|
47
|
+
import { configOf, hold } from "../schema/gate.mjs";
|
|
48
|
+
// Freshness (Revalidating, V-1 to V-8): stale flags on reads, and the freshness and revalidate commands.
|
|
49
|
+
import { readFlag as freshnessFlag } from "../schema/freshness.mjs";
|
|
50
|
+
// Gate group hooks (M-18, F-5, X-2, X-4 to X-6, P-2, P-7, U-4): who is acting, access at the record,
|
|
51
|
+
// the gate's verdict, where held drafts wait, and check settings. The logic lives in these modules.
|
|
52
|
+
import { actingCall, itemId, mayReadItem, mayReadSome } from "./acting.mjs";
|
|
53
|
+
import { checkPlan, gateWrite, heldDraft } from "./gate-hooks.mjs";
|
|
54
|
+
import { LOCAL, attributeSources, connect, sourceFor } from "../knowledge/connectors/index.mjs";
|
|
55
|
+
// Caching (schema/caching.mjs): per-action memo, the record-level cache for command routes, and system/cache, system/judge, system/answer.
|
|
56
|
+
import { actionScope, cacheRoutes, command as cachedCommand, forget, internal as cacheInternal, memo } from "../schema/caching.mjs";
|
|
57
|
+
// Context hook: instrumentation of every call and judgment (interface/instrumentation.mjs, P-4, F-4).
|
|
58
|
+
import { instrumented, judgment } from "./instrumentation.mjs";
|
|
59
|
+
// Hooks for writes, reads and boundaries (wf/writes): the wrapper around call, per-record containment and private folders.
|
|
60
|
+
import { around } from "./writes.mjs";
|
|
61
|
+
import { contain } from "./boundaries.mjs";
|
|
62
|
+
import { packageSlots } from "./package-routes.mjs";
|
|
63
|
+
import { routable } from "../schema/structure.mjs";
|
|
64
|
+
// Brand hook (Y-2..Y-4, J-2..J-4, T-1, T-2, P-24, P-26): routes, commands and layout context from schema/brand.mjs.
|
|
65
|
+
import { BRAND_ROUTES, brandCommands, brandLayout } from "../schema/brand.mjs";
|
|
66
|
+
// Upgrades hook (V-9): a read says when what the record reads changed in its system of record.
|
|
67
|
+
import { staleOf } from "../schema/stale.mjs";
|
|
68
|
+
|
|
69
|
+
export const repo = new URL("..", import.meta.url).pathname;
|
|
70
|
+
const load = (p) => JSON.parse(readFileSync(p, "utf8"));
|
|
71
|
+
const defaultWorkspace = () => process.env.MENTURA_WORKSPACE ?? join(repo, "instance");
|
|
72
|
+
export const PHASES = load(join(repo, "purpose/frameworks/product-anatomy/phases.json")).phases;
|
|
73
|
+
|
|
74
|
+
export const glob = (g) => new RegExp("^" + g.replace(/[.+^${}()|[\]\\]/g, "\\$&").replace(/\*\*\//g, "\0").replace(/\*\*/g, "\u0001").replace(/\*/g, "[^/]*").replace(/\0/g, "(?:[^/]+/)*").replace(/\u0001/g, ".*") + "$");
|
|
75
|
+
// A venture's files, stopping at a nested venture (which has its own records) and skipping dot and private folders.
|
|
76
|
+
export const files = (d, base = d) => existsSync(d) ? readdirSync(d, { withFileTypes: true }).flatMap((e) =>
|
|
77
|
+
e.name.startsWith(".") ? [] : e.isDirectory() ? (existsSync(join(d, e.name, "venture.json")) || (d === base && e.name.startsWith("_")) ? [] : files(join(d, e.name), base)) : [join(d, e.name).slice(base.length + 1)]) : [];
|
|
78
|
+
|
|
79
|
+
const skillsOf = (k) => (k.skills?.length ? k.skills : k.skill ? [k.skill] : []);
|
|
80
|
+
|
|
81
|
+
/** Layer manifests and any package that owns record types. `kit` is the name, kept so the board still groups routes. */
|
|
82
|
+
export function kits() {
|
|
83
|
+
const model = load(join(repo, "schema/data-model.json"));
|
|
84
|
+
const paths = [...model.layers.map((l) => `${l}/layer.json`), "schema/layer.json"];
|
|
85
|
+
const pkgDir = join(repo, "packages");
|
|
86
|
+
if (existsSync(pkgDir)) for (const d of readdirSync(pkgDir)) if (existsSync(join(pkgDir, d, "package.json"))) paths.push(`packages/${d}/package.json`);
|
|
87
|
+
return paths.filter((p) => existsSync(join(repo, p))).map((p) => {
|
|
88
|
+
const k = load(join(repo, p));
|
|
89
|
+
const name = k.name ?? k.kit;
|
|
90
|
+
return { path: p, ...k, name, kit: name };
|
|
91
|
+
}).filter((k) => k.path.endsWith("/layer.json") || (k.records ?? []).length);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
const needNowPriors = () => load(join(repo, "schema/need-now.json"));
|
|
95
|
+
|
|
96
|
+
/** The route table, generated from schema/data-model.json, the kit manifests and schema/need-now.json. */
|
|
97
|
+
// Caching hook (F-12): built once per action and shared.
|
|
98
|
+
export function routes() { return memo("routes", buildRoutes); }
|
|
99
|
+
function buildRoutes() {
|
|
100
|
+
const model = load(join(repo, "schema/data-model.json"));
|
|
101
|
+
const ks = kits();
|
|
102
|
+
const owner = new Map(ks.flatMap((k) => k.records.map((r) => [r, k])));
|
|
103
|
+
const N = needNowPriors();
|
|
104
|
+
const out = [];
|
|
105
|
+
for (const r of model.records.filter((x) => x.level === "venture" && x.id !== "venture")) {
|
|
106
|
+
const k = owner.get(r.id);
|
|
107
|
+
const sub = k?.path.match(/^[a-z]+\/kits\/([^/]+)\//)?.[1] ?? (k?.path.startsWith("packages/") && r.id.startsWith(`${k.name}-`) ? k.name : null);
|
|
108
|
+
const short = sub && r.id.startsWith(`${sub}-`) ? r.id.slice(sub.length + 1) : r.id;
|
|
109
|
+
const filesOf = r.paths.filter((p) => p.startsWith("instance/**/")).map((p) => p.slice("instance/**/".length));
|
|
110
|
+
// The template is the package's template dir, or <manifest folder>/templates/<record>.md, or one whose name starts <record>- (telos-based-smart.md).
|
|
111
|
+
const tdir = k?.template_dir ?? (k && !k.path.startsWith("packages/") ? join(dirname(k.path), "templates") : null);
|
|
112
|
+
const tname = tdir && existsSync(join(repo, tdir)) ? readdirSync(join(repo, tdir)).filter((f) => f === `${short}.md` || (f.startsWith(`${short}-`) && f.endsWith(".md"))).sort((a, b) => (a === `${short}.md` ? -1 : b === `${short}.md` ? 1 : a.localeCompare(b)))[0] : null;
|
|
113
|
+
const template = tname ? `${tdir}/${tname}` : null;
|
|
114
|
+
const checks = [...new Set((r.refs ?? []).map((x) => x.checked_by).filter((c) => c && c.startsWith("schema/check-") && c !== "schema/check-data-model.mjs"))];
|
|
115
|
+
const legacy = [r.layer === "system" ? "system" : r.layer, sub, short].filter(Boolean).join("/");
|
|
116
|
+
const base = PACKAGE_ROUTE[r.id] ?? legacy;
|
|
117
|
+
const form = r.form ?? "json";
|
|
118
|
+
const groups = PHASES.map((p) => p.id).filter((p) => (N.records[r.id]?.base?.[p] ?? 0) >= N.suggest).map((p) => `(${p})`);
|
|
119
|
+
const common = { kind: "record", record: r.id, layer: r.layer, kit: k?.name ?? null, package: k?.path?.startsWith("packages/") ? k.name : null, kit_path: k?.path ?? null, schema: r.schema, form, checks, template,
|
|
120
|
+
decision_class: r.decision_class ?? null, groups, note: r.note ?? "" };
|
|
121
|
+
// A collection's one wildcard is a whole id at the start of a path segment (products/*.json, accounts/*/account.json).
|
|
122
|
+
const collection = filesOf.length >= 1 && filesOf.every((p) => (p.match(/\*/g) ?? []).length === 1 && /(^|\/)\*[./]/.test(p) && !p.includes("**"));
|
|
123
|
+
const concrete = filesOf.every((f) => !f.includes("*"));
|
|
124
|
+
const writable = !!(r.schema && form === "json" && (collection || concrete));
|
|
125
|
+
if (collection) {
|
|
126
|
+
out.push({ ...common, route: base, files: filesOf, collection: "list", actions: ["read", "view", "layout", ...(checks.length ? ["check"] : []), ...(template ? ["template"] : [])] });
|
|
127
|
+
out.push({ ...common, route: `${base}/[id]`, files: filesOf, collection: "item",
|
|
128
|
+
actions: ["read", "view", "layout", ...(writable ? ["write"] : []), ...(checks.length ? ["check"] : []), ...(template ? ["template"] : [])] });
|
|
129
|
+
} else {
|
|
130
|
+
out.push({ ...common, route: base, files: filesOf,
|
|
131
|
+
actions: ["read", "view", "layout", ...(writable && !filesOf[0].includes("*") ? ["write"] : []), ...(checks.length ? ["check"] : []), ...(template ? ["template"] : [])] });
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
const cmd = (route, script, note, extra = {}) => ({ route, kind: "command", actions: ["run", "view", "layout"], script, groups: [], note, ...extra });
|
|
135
|
+
out.push(
|
|
136
|
+
cmd("system/package", "schema/packages.mjs", "The venture package (EE-067, EE-069): the core packages open now and the handoffs, each record confirmed, to review or to write, what moves on its own and what waits, and what completion unlocks; --write renders the view and records a version. Assumes a solo founder or small founding team in Discovery and Validation; agents are a package enhancement"),
|
|
137
|
+
cmd("system/need-now", "schema/need-now.mjs", "What the venture needs now, soon, or should hold back (EE-028)"),
|
|
138
|
+
cmd("system/time-to-ready", "schema/time-to-ready.mjs", "Time to running, and to ready per layer and MAD tier"),
|
|
139
|
+
// Model steps hook (use model, EE-073): the logic is schema/model-steps.mjs; this route only shows the payload.
|
|
140
|
+
cmd("system/steps", "schema/model-steps.mjs", "The model steps' payload (use model, EE-073): what code computed, a placeholder for each judgment a model has yet to give, and the only fields each one receives; asks no model. mentura steps run asks one", { args: ["payload"] }),
|
|
141
|
+
cmd("purpose/validation/assess", "schema/assess-problem.mjs", "What the problem's evidence is strong enough for: each assumption labelled demonstrated, inferred, assumed, unknown or contradicted, the commitment it supports and the next test (EE-050). Asks first when the record is incomplete; input as_is runs it anyway", { as_is: true }),
|
|
142
|
+
cmd("governance/analytics", "schema/decision-analytics.mjs", "Decision analytics (EE-072): how many decisions of each type each function has made, open against decided, in whose words, in which phase, and their outcomes. Data only; a view is the platform's, or a visualization skill's", { actions: ["run", "layout"] }),
|
|
143
|
+
cmd("governance/decisions", "schema/decisions.mjs", "The decision matrix: every decision the venture owes by layer and phase, with its parent and boundary, and whether it renders now, waits, comes later, is stale or deferred (EE-054, EE-057)"),
|
|
144
|
+
cmd("operations/marketing/sitemap", "operations/marketing/site/plan.mjs", "Plan the public site from the venture's records: the product decides whom it is offered to, that decides the audiences, and each page gets its sections in order with the MDX component, need, audience, reason, condition, source record and the public variant to write (EE-064)"),
|
|
145
|
+
cmd("operations/marketing/public", "operations/marketing/check-public.mjs", "The public gate: every marketing content file is a variant of a named internal record, says its audience and whether it is published, has a person's approval when published, and copies no run of ten words from its internal record (EE-064)"),
|
|
146
|
+
cmd("operations/marketing/site", "operations/marketing/site/presence.mjs", "How present the venture's public records are on its marketing site: a score weighed by who the site must speak to now (raising, hiring, contributors, ICPs), and each item missing, not prominent enough or present, with where it belongs (EE-062). Read the site first with interface/harness/site.mjs"),
|
|
147
|
+
cmd("purpose/ideas/routes", "purpose/frameworks/idea-map/route.mjs", "Where each idea goes in the framework: the records it feeds, each destination's stage (routed, drafted, your decision, waiting, done), whether it moves in order or on its own, and the one step that moves it; plus the decision docket traced to the ideas (EE-061)"),
|
|
148
|
+
cmd("purpose/ideas/graph", "purpose/frameworks/idea-map/graph.mjs", "The idea graph: which ideas build on which, each idea's signal, the lanes that move in order and the ones that can move on their own, and the ideas to develop next (EE-060)"),
|
|
149
|
+
cmd("purpose/ideas", "purpose/frameworks/idea-map/ideamap.mjs", "The founder's idea dump mapped onto Naive's conventions: each idea verbatim with its departments, records and whether it exists, where the ideas cluster, what the dump never mentions, and what it says about whether a product exists (EE-059)"),
|
|
150
|
+
cmd("purpose/foundation", "schema/foundation.mjs", "The venture's strategic foundation by the Five Ws and How plus whether, each confirmed, drafted by an agent, waiting as a draft, or missing (EE-051)"),
|
|
151
|
+
cmd("purpose/validation/score", "schema/check-validation.mjs", "Held (EE-050): the single validation score from graded evidence (EE-026); purpose/validation/assess replaces it for the problem", { args: ["--score"] }),
|
|
152
|
+
cmd("governance/payouts", "schema/payouts.mjs", "Who gets what at a sale value, from the cap table (EE-022); input value, else the scenarios in exit.json", { needs: ["governance/ownership.json"], venture_arg: "id" }),
|
|
153
|
+
cmd("system/overview", null, "The venture at a glance: telos, validation, need-now, time to ready, team and recent provenance, rendered as parallel slots", { internal: "overview" }),
|
|
154
|
+
cmd("system/robots", null, "Which roles may read and write which routes, from the team plan", { internal: "robots" }),
|
|
155
|
+
// Context hook (interface/vitals.mjs, interface/context.mjs): agent vitals and the context budget (N-6, C-15, N-13).
|
|
156
|
+
cmd("system/vitals", "interface/vitals.mjs", "Agent vitals (N-6): calls per task, tokens of context per call, time to a useful answer and time from a decision opening to a person deciding it, at the 75th percentile, each checked against its threshold (C-15). Reads the events log that instrumentation writes"),
|
|
157
|
+
cmd("system/context", "interface/context.mjs", "The context budget (N-13): what every route sends an agent, in tokens, against its budget, with the largest part and where to move it (S-14, F-6)", { args: ["budget"] }),
|
|
158
|
+
cmd("knowledge/catalog", "knowledge/connectors/catalog.mjs", "Build or refresh the catalog: every item in every source the venture keeps records in, with its address and version, no content (EE-037)"),
|
|
159
|
+
cmd("knowledge/search", "knowledge/connectors/catalog.mjs", "Find items across every source through the catalog; input value is the query (EE-037)", { value_flag: "--search" }),
|
|
160
|
+
// Freshness (Revalidating, V-1 to V-8): schema/freshness.mjs.
|
|
161
|
+
cmd("system/freshness", "schema/freshness.mjs", "What is fresh, stale or expired, and which records are live holes: by time (freshness profiles, per phase too) and on demand (a dependency changed, a where-used parent drifted, a tag, path or package was revalidated)"),
|
|
162
|
+
cmd("system/revalidate", "schema/freshness.mjs", "Mark records stale on demand: input value tag:<route> (the record and what depends on it), path:<folder>, package:<name>, or pivot (the validation package); a plain folder is refused in favour of exact tags", { value_flag: "--revalidate" }),
|
|
163
|
+
);
|
|
164
|
+
out.push(...cacheRoutes(cmd)); // Caching hook: system/cache, system/judge, system/answer (schema/caching.mjs).
|
|
165
|
+
out.push(...brandCommands(cmd)); // Brand hook: brand tokens, voice, glossary terms, sitemap.xml, asset facts (schema/brand.mjs).
|
|
166
|
+
return out.sort((a, b) => a.route.localeCompare(b.route));
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
/** Route groups: (phase) gathers the routes need-now suggests in that phase. The path is unchanged. */
|
|
170
|
+
export function groups() {
|
|
171
|
+
const rs = routes();
|
|
172
|
+
return PHASES.map((p) => ({ group: `(${p.id})`, phase: p.id, question: p.question, gate: p.gate, routes: rs.filter((r) => r.groups.includes(`(${p.id})`)).map((r) => r.route) }));
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
// Aliases for renamed routes (stability): interface/route-aliases.json, or MENTURA_ROUTE_ALIASES.
|
|
176
|
+
function aliases() {
|
|
177
|
+
const f = process.env.MENTURA_ROUTE_ALIASES ?? join(repo, "interface/route-aliases.json");
|
|
178
|
+
return existsSync(f) ? load(f).aliases : [];
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
/** Matches a concrete route (purpose/product/kettle) to its table entry and params. Follows aliases. */
|
|
182
|
+
export function resolve(route) {
|
|
183
|
+
const rs = routes();
|
|
184
|
+
const alias = aliases().find((a) => a.from === route || route.startsWith(`${a.from}/`));
|
|
185
|
+
const concrete = alias ? alias.to + route.slice(alias.from.length) : route;
|
|
186
|
+
const exact = rs.find((r) => r.route === concrete);
|
|
187
|
+
if (exact) return { r: exact, params: {}, alias };
|
|
188
|
+
for (const r of rs.filter((x) => x.route.endsWith("/[id]"))) {
|
|
189
|
+
const head = r.route.slice(0, -"/[id]".length);
|
|
190
|
+
if (concrete.startsWith(`${head}/`) && !concrete.slice(head.length + 1).includes("/")) return { r, params: { id: concrete.slice(head.length + 1) }, alias };
|
|
191
|
+
}
|
|
192
|
+
return { r: null, params: {}, alias };
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
const walkVentures = (d) => readdirSync(d, { withFileTypes: true }).flatMap((e) => e.name === "node_modules" || e.name.startsWith(".") || e.name.startsWith("_") ? []
|
|
196
|
+
: e.isDirectory() ? walkVentures(join(d, e.name)) : e.name === "venture.json" ? [d] : []);
|
|
197
|
+
|
|
198
|
+
/**
|
|
199
|
+
* Finds a venture folder ([...venture]): by id, or by its folder path under the workspace or its
|
|
200
|
+
* ventures/ folder, so a nested venture can be named studio/print.
|
|
201
|
+
*/
|
|
202
|
+
export function ventureDir(id, workspace = defaultWorkspace()) {
|
|
203
|
+
if (!id) return null;
|
|
204
|
+
if (id.includes("/")) {
|
|
205
|
+
for (const base of [workspace, join(workspace, "ventures")]) {
|
|
206
|
+
const d = join(base, id);
|
|
207
|
+
if (!relative(workspace, d).startsWith("..") && existsSync(join(d, "venture.json"))) return d;
|
|
208
|
+
}
|
|
209
|
+
return null;
|
|
210
|
+
}
|
|
211
|
+
// A broken venture.json is found by its folder name, so its global error can answer (E-8, interface/boundaries.mjs).
|
|
212
|
+
for (const d of walkVentures(workspace)) { let vid; try { vid = load(join(d, "venture.json")).id; } catch { vid = basename(d); } if (vid === id) return d; }
|
|
213
|
+
return null;
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
/** The ventures under a workspace: id, name, phase, path (the [...venture] segment) and folder. */
|
|
217
|
+
export function ventures(workspace = defaultWorkspace()) {
|
|
218
|
+
const root = existsSync(join(workspace, "ventures")) ? join(workspace, "ventures") : workspace;
|
|
219
|
+
return walkVentures(workspace).map((d) => { let v; try { v = load(join(d, "venture.json")); } catch { return { id: basename(d), name: null, phase: null, path: relative(root, d), dir: d, broken: true }; } return { id: v.id, name: v.name, phase: v.phase, path: relative(root, d), dir: d }; });
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
// The venture's decision rights: the template, tightened by the venture's own file.
|
|
223
|
+
function decisionRights(dir) {
|
|
224
|
+
const t = load(join(repo, "governance/venture-template/decision-rights.json"));
|
|
225
|
+
const classes = new Map(t.classes.map((c) => [c.id, c]));
|
|
226
|
+
const own = join(dir, "governance/decision-rights.json");
|
|
227
|
+
if (existsSync(own)) for (const c of load(own).classes) classes.set(c.id, { ...classes.get(c.id), ...c, level: classes.get(c.id)?.level === "escalate" ? "escalate" : c.level });
|
|
228
|
+
return classes;
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
/** The gate on a record: its decision class from the data model, and whether an agent's write is a draft. */
|
|
232
|
+
export function gateOf(r, dir) {
|
|
233
|
+
if (!r.decision_class) return null;
|
|
234
|
+
const c = dir ? decisionRights(dir).get(r.decision_class) : null;
|
|
235
|
+
const level = c?.level ?? "escalate";
|
|
236
|
+
return { class: r.decision_class, owner: c?.owner ?? null, level, drafts: level === "escalate" || level === "recommend-approve" };
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
// Robots: a team-plan role may list the routes it may read and write (globs over routes).
|
|
240
|
+
export function allowed(dir, actorId, action, route) {
|
|
241
|
+
if (!actorId || !dir) return true;
|
|
242
|
+
const plan = [join(dir, "people/team-plan.json"), join(dir, "operations/team-plan.json")].find((p) => existsSync(p));
|
|
243
|
+
if (!plan) return true;
|
|
244
|
+
const role = load(plan).roles.find((x) => x.id === actorId);
|
|
245
|
+
if (!role?.routes) return true;
|
|
246
|
+
const list = action === "write" ? role.routes.write ?? [] : role.routes.read ?? ["**"];
|
|
247
|
+
return list.some((g) => glob(g).test(route));
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
const PACKAGE_ROUTE = {
|
|
251
|
+
"validation-problem": "foundation/problem", icp: "foundation/icp", telos: "foundation/telos", alignment: "foundation/alignment",
|
|
252
|
+
"validation-solution": "validation/solution", "validation-pricing": "validation/pricing", "validation-go-to-market": "validation/go-to-market",
|
|
253
|
+
"validation-product-market-fit": "validation/product-market-fit", "validation-operations-readiness": "validation/operations-readiness",
|
|
254
|
+
"validation-team": "people/team", "team-plan": "people/team-plan", "marketing-sitemap": "marketing/sitemap", "marketing-content": "marketing/content",
|
|
255
|
+
"idea-dump": "raw/dump", "idea-map": "ideas/map", "idea-graph": "ideas/graph", product: "product", evidence: "evidence",
|
|
256
|
+
"anatomy-answers": "product/anatomy-answers", "anatomy-input": "product/anatomy", "venture-package": ".release/venture-package",
|
|
257
|
+
"decision-record": "decisions",
|
|
258
|
+
customer: "customers", investor: "investors", "money-account": "money", "raw-source": "raw/sources",
|
|
259
|
+
people: "people", founder: "people/founders", "fund-thesis": "fund/thesis", "fund-terms": "fund/terms", deal: "deals",
|
|
260
|
+
...BRAND_ROUTES, // Brand hook: marketing/brand, public/assets, glossary (schema/brand.mjs).
|
|
261
|
+
};
|
|
262
|
+
|
|
263
|
+
// Prefer a file that is already there, so an unmigrated venture keeps its old path. A new write uses the first path, which is the package layout.
|
|
264
|
+
const fileFor = (r, params, dir) => {
|
|
265
|
+
const cands = (r.collection === "item" ? r.files.map((f) => f.replace("*", params.id)) : r.files).filter((f) => !f.includes("*"));
|
|
266
|
+
if (dir) {
|
|
267
|
+
const hit = cands.find((f) => existsSync(join(dir, f)));
|
|
268
|
+
if (hit) return hit;
|
|
269
|
+
// A new collection item joins the folder the venture already keeps that collection in.
|
|
270
|
+
if (r.collection === "item") {
|
|
271
|
+
const lived = cands.find((f) => { const d = join(dir, dirname(f)); return existsSync(d) && readdirSync(d).some((n) => n.endsWith(".json") || n.endsWith(".md") || n.endsWith(".mdx")); });
|
|
272
|
+
if (lived) return lived;
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
return cands[0];
|
|
276
|
+
};
|
|
277
|
+
const draftPath = (file) => join("_drafts", `${file.replace(/\//g, "__")}.draft`);
|
|
278
|
+
|
|
279
|
+
/** Where an agent's draft of a gated record waits: { dir, file, draft, gate } or null. */
|
|
280
|
+
export function draftOf(route, venture, workspace) {
|
|
281
|
+
const { r, params } = resolve(route);
|
|
282
|
+
const dir = ventureDir(venture, workspace);
|
|
283
|
+
if (!r || r.kind !== "record" || r.collection === "list" || !dir) return null;
|
|
284
|
+
const file = fileFor(r, params, dir);
|
|
285
|
+
if (!file) return null; // a route with no concrete file (a pattern-only slot) has no draft
|
|
286
|
+
return { dir, file, draft: draftPath(file), gate: gateOf(r, dir), route: r.collection === "item" ? r.route.replace("[id]", params.id) : r.route };
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
function agentsChain(dir, route) {
|
|
290
|
+
if (!dir) return [];
|
|
291
|
+
const chain = [];
|
|
292
|
+
const add = (rel) => { const f = join(dir, rel); if (existsSync(f)) chain.push({ path: rel, text: readFileSync(f, "utf8") }); };
|
|
293
|
+
add("AGENTS.md");
|
|
294
|
+
const parts = route.split("/").filter(Boolean);
|
|
295
|
+
let acc = "";
|
|
296
|
+
for (const p of parts) { acc = acc ? `${acc}/${p}` : p; add(`${acc}/AGENTS.md`); }
|
|
297
|
+
return chain;
|
|
298
|
+
}
|
|
299
|
+
function instructionsOf(chain) {
|
|
300
|
+
const out = {};
|
|
301
|
+
for (const file of chain) for (const line of file.text.split("\n")) {
|
|
302
|
+
const m = line.match(/^([a-z][a-z0-9_-]*)\s*:\s*(.+)$/);
|
|
303
|
+
if (m) out[m[1]] = m[2].trim();
|
|
304
|
+
}
|
|
305
|
+
return out;
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
/** A score below its threshold is uncertain: not an error and not an answer. It routes to the function owner. */
|
|
309
|
+
export function judge(score, threshold, owner, at) {
|
|
310
|
+
const out = typeof score !== "number" || score < threshold ? { outcome: "uncertain", owner, result: null } : { outcome: "answer", result: score };
|
|
311
|
+
// Context hook: a judgment made for a venture (at.dir) is an instrumentation event (F-4).
|
|
312
|
+
if (at?.dir) judgment(at.dir, { question: at.question, route: at.route, model: at.model, score, threshold, outcome: out.outcome, owner });
|
|
313
|
+
return out;
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
/** Runs fn. A throw is the uncaught outcome. */
|
|
317
|
+
export function capture(fn) {
|
|
318
|
+
try { fn(); return { outcome: "answer" }; }
|
|
319
|
+
catch (e) { return { outcome: "uncaught", error: String(e.message ?? e) }; }
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
/** The layout wrapping a route: system layer, the record's layer, and its package when one owns it.
|
|
323
|
+
* chain is the AGENTS.md files from the venture root down to the route, nearest last. */
|
|
324
|
+
export function layout(r, dir) {
|
|
325
|
+
const ks = kits();
|
|
326
|
+
const owned = r.kit_path && (r.kit_path.startsWith("packages/") || r.kit_path.includes("/kits/")) ? ks.find((k) => k.path === r.kit_path) : null;
|
|
327
|
+
const stack = [ks.find((k) => k.path === "schema/layer.json"), ks.find((k) => k.path === `${r.layer ?? r.route.split("/")[0]}/layer.json`), owned]
|
|
328
|
+
.filter(Boolean).map((k) => {
|
|
329
|
+
const refs = (k.references && existsSync(join(repo, k.references)) ? k.references : null)
|
|
330
|
+
?? [join(dirname(k.path), "references/references.json"), join(dirname(k.path), "references")].find((p) => existsSync(join(repo, p)));
|
|
331
|
+
return { name: k.name, kit: k.name, path: k.path, purpose: k.purpose, skills: skillsOf(k).map((s) => s.path), references: refs ?? null, gaps: k.gaps ?? [] };
|
|
332
|
+
});
|
|
333
|
+
const chain = agentsChain(dir, r.route);
|
|
334
|
+
const pkg = owned ? { skill: skillsOf(owned).map((s) => s.path), template: r.template ?? null, gates: owned.gates ?? [] } : null;
|
|
335
|
+
const out = { route: r.route, stack, chain, instructions: instructionsOf(chain), package: pkg, note: r.note, groups: r.groups ?? [] };
|
|
336
|
+
if (r.kind === "record") Object.assign(out, { schema: r.schema, template: r.template, checks: r.checks, gate: gateOf(r, dir) });
|
|
337
|
+
{ const brand = brandLayout(dir, r.route); if (brand) out.brand = brand; } // Brand hook: brand, voice and package terms at this route (Y-2, Y-3, T-2).
|
|
338
|
+
if (dir && r.kind === "record") {
|
|
339
|
+
try {
|
|
340
|
+
// Caching hook (O-5): shared with the system/need-now command in the same action.
|
|
341
|
+
const nn = JSON.parse(memo(`cmd:schema/need-now.mjs ${dir} --json`, () => spawnSync("node", [join(repo, "schema/need-now.mjs"), dir, "--json"], { encoding: "utf8" }), { dir }).stdout);
|
|
342
|
+
const row = nn.rows.find((x) => x.type === r.record);
|
|
343
|
+
if (row) out.need_now = { phase: nn.phase, status: row.status, now: row.now, next: row.next, why: row.why };
|
|
344
|
+
} catch {}
|
|
345
|
+
}
|
|
346
|
+
return out;
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
const fix = (r) => ({ skills: r.kit_path ? skillsOf(kits().find((k) => k.path === r.kit_path) ?? {}).map((s) => s.path) : [], template: r.template ?? null, schema: r.schema ?? null });
|
|
350
|
+
|
|
351
|
+
/**
|
|
352
|
+
* Runs one action on one route for one venture. Returns { ok, result } or { ok: false, error }.
|
|
353
|
+
* input: the record to write (write), or { value } for payouts; actor: "person" or "agent" (default
|
|
354
|
+
* agent); actor_id: the role acting (default MENTURA_ACTOR_ID), checked against the team plan's
|
|
355
|
+
* routes; via: the interface (cli, mcp, api; default router); provenance: { derived_from,
|
|
356
|
+
* decision, note, agent } for the log.
|
|
357
|
+
*/
|
|
358
|
+
// Caching hook (F-2): one top-level call is one action; the calls it makes join it and share its memo.
|
|
359
|
+
// Context hook: every call is timed and measured for agent vitals (interface/instrumentation.mjs).
|
|
360
|
+
// Gate group hook (M-18): who is acting is checked on every call and carried on the answer.
|
|
361
|
+
// Writes hook (interface/writes.mjs): adds what reads, layouts and writes answer.
|
|
362
|
+
export const call = (args) => actionScope(() => instrumented(args, (a) => actingCall(a, (b) => around(callInAction, b), ventureDir), ventureDir));
|
|
363
|
+
async function callInAction({ venture, route, action, input, actor = "agent", actor_id = process.env.MENTURA_ACTOR_ID, workspace, via = "router", provenance = {} }) {
|
|
364
|
+
const requested = route;
|
|
365
|
+
const { r, params, alias } = resolve(route);
|
|
366
|
+
if (!r) return { ok: false, outcome: "error", error: `no route ${route}` };
|
|
367
|
+
// Context hook (interface/context.mjs): navigate reads a route header first, with its layout once per
|
|
368
|
+
// session, its template and prefetched refs; head stops after the header (N-3, L-2, O-4).
|
|
369
|
+
if (action === "navigate" || action === "head") return (await import("./context.mjs")).navigate({ ...(input ?? {}), venture, route: requested, workspace, actor, actor_id, via, header_only: action === "head" || !!input?.header_only });
|
|
370
|
+
const deprecated = alias ? { deprecated: `${alias.from} is now ${alias.to}${alias.note ? `: ${alias.note}` : ""}` } : {};
|
|
371
|
+
route = r.collection === "item" ? r.route.replace("[id]", params.id) : r.route;
|
|
372
|
+
if (!r.actions.includes(action)) return { ok: false, error: `${route} has no action ${action} (it has ${r.actions.join(", ")})` };
|
|
373
|
+
if (action === "template") return { ok: true, result: readFileSync(join(repo, r.template), "utf8"), ...deprecated };
|
|
374
|
+
const dir = venture ? ventureDir(venture, workspace) : null;
|
|
375
|
+
if (action === "layout") { if (venture && !dir) return { ok: false, error: `no venture ${venture}` }; return { ok: true, result: layout(r, dir), ...deprecated }; }
|
|
376
|
+
if (!dir) return { ok: false, error: `no venture ${venture}` };
|
|
377
|
+
const act = action === "write" ? "write" : "read";
|
|
378
|
+
// Gate group hook (F-5): a role that may read some items of a collection may read its list, filtered below.
|
|
379
|
+
if (!allowed(dir, actor_id, act, requested) && !allowed(dir, actor_id, act, route) && !(act === "read" && r.collection === "list" && mayReadSome(dir, actor_id, route))) return { ok: false, error: `${actor_id} may not ${act} ${route} (team plan routes)` };
|
|
380
|
+
// An activity a decision unlocks stays closed to agents until a person makes that decision (EE-058).
|
|
381
|
+
const lock = actor !== "person" && (action === "write" || action === "run") ? (locked(dir, requested, actor_id) || locked(dir, route, actor_id)) : null;
|
|
382
|
+
if (lock) return { ok: false, error: `${route} is locked until decision ${lock.id} is made: ${lock.question}`, locked_by: lock.id,
|
|
383
|
+
fix: { who: "a person, not an agent", command: `mentura decide ${lock.id} --venture ${venture} --by <their name> [--answer "<their wording>"]`, then: "retry this call" } };
|
|
384
|
+
|
|
385
|
+
if (action === "view") {
|
|
386
|
+
const inner = await call({ venture, route, action: r.kind === "command" ? "run" : "read", input, actor, actor_id, workspace, via, provenance });
|
|
387
|
+
return inner.ok ? { ok: true, result: render(route, inner), ...deprecated } : inner;
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
if (r.kind === "command") {
|
|
391
|
+
if (r.internal === "overview") return { ok: true, result: await overview(venture, workspace), ...deprecated };
|
|
392
|
+
if (r.internal === "robots") return { ok: true, result: robots(dir), ...deprecated };
|
|
393
|
+
// Caching hook: system/cache, system/judge and system/answer are handled in schema/caching.mjs.
|
|
394
|
+
if (["cache", "judge", "answer"].includes(r.internal)) return { ...(await cacheInternal(r.internal, { dir, venture, workspace, input, actor, actor_id, via, provenance }, { call, resolve, draftOf })), ...deprecated };
|
|
395
|
+
const lacking = (r.needs ?? []).filter((f) => !existsSync(join(dir, f)));
|
|
396
|
+
if (lacking.length) return { ok: false, error: `${route} needs ${lacking.join(", ")} in ${venture}`, fix: { missing: lacking } };
|
|
397
|
+
if (r.value_flag && !input?.value) return { ok: false, error: `${route} needs a value (${r.note})` };
|
|
398
|
+
const args = r.venture_arg === "id" ? [dir, ...(input?.value ? [String(input.value)] : []), "--json"] : r.value_flag ? [dir, r.value_flag, String(input.value), "--json"] : [dir, ...(r.args ?? []), ...(r.as_is && input?.as_is ? ["--as-is"] : []), "--json"];
|
|
399
|
+
// Caching hook (C-3, H-3): a declared command's result comes from the record-level cache; the spawn is memoized per action (O-5).
|
|
400
|
+
const p = await cachedCommand({ dir, route, script: r.script, args, input, actor, actor_id }, () => memo(`cmd:${r.script} ${args.join(" ")}`, () => spawnSync("node", [join(repo, r.script), ...args], { encoding: "utf8" }), { dir }));
|
|
401
|
+
const cache = p.cache ? { cache: p.cache } : {};
|
|
402
|
+
if (p.status !== 0) return { ok: false, error: (p.stderr || p.stdout).trim() };
|
|
403
|
+
// A command that needs input first (EE-050) answers needs_input: the caller asks the person
|
|
404
|
+
// whether to fill the record in or run as is, and runs again with input as_is to go on.
|
|
405
|
+
try { const out = JSON.parse(p.stdout); return { ok: true, result: out.needs_input ? null : out, ...(out.needs_input ? { needs_input: out.needs_input } : {}), ...cache, ...deprecated }; } catch { return { ok: true, result: p.stdout, ...cache, ...deprecated }; }
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
// Storage: the connector for the source this record type lives in.
|
|
409
|
+
let src, store, attrs = [];
|
|
410
|
+
if (r.kind === "record" && (action === "read" || action === "write")) {
|
|
411
|
+
const context = { actor, ...(r.collection === "item" && (r.record === "product" || r.record === "account") ? { [r.record]: params.id } : {}) };
|
|
412
|
+
try {
|
|
413
|
+
src = sourceFor(dir, r.record, context); store = await connect(src, dir);
|
|
414
|
+
// Attributes mastered elsewhere (EE-042): read from and written to their own system of record.
|
|
415
|
+
attrs = r.files.every((f) => f.endsWith(".json")) ? await Promise.all(attributeSources(dir, r.record, context).map(async (a) => ({ ...a, store: await connect(a.source, dir) }))) : [];
|
|
416
|
+
} catch (e) { return { ok: false, error: String(e.message ?? e) }; }
|
|
417
|
+
}
|
|
418
|
+
const remote = src && src.id !== LOCAL;
|
|
419
|
+
const cite = (x) => ({ source: src.id, address: address(src.id, x.path), version: x.version, ...(x.commit ? { commit: x.commit } : {}), ...(x.permalink ? { permalink: x.permalink } : {}), ...(x.attributes ? { attributes: x.attributes } : {}) });
|
|
420
|
+
|
|
421
|
+
if (action === "read") {
|
|
422
|
+
const patterns = r.collection === "item" ? [fileFor(r, params, dir)] : r.files;
|
|
423
|
+
let items;
|
|
424
|
+
try {
|
|
425
|
+
// a file named _* is never an item; routable: private _folders (P-22)
|
|
426
|
+
const paths = patterns.some((g) => g.includes("*")) ? (await store.list()).map((x) => x.path).filter((f) => !/(^|\/)_[^/]*$/.test(f) && patterns.some((g) => glob(g).test(f) && routable(f, g))) : patterns;
|
|
427
|
+
// Caching hook (F-2): each record is read once per action and shared.
|
|
428
|
+
items = (await Promise.all(paths.map((f) => memo(`read:${src.id}:${f}`, () => store.read(f), { dir })))).filter(Boolean);
|
|
429
|
+
// Gate group hook (F-5): access is checked again at the record; a list holds only the items the role may read.
|
|
430
|
+
if (r.collection === "list" && actor_id) items = items.filter((x) => mayReadItem(dir, actor_id, route, itemId(r.files, x.path)));
|
|
431
|
+
} catch (e) { return { ok: false, error: `${src.id}: ${String(e.message ?? e)}` }; }
|
|
432
|
+
const found = items.map((x) => x.path);
|
|
433
|
+
const draft = !remote && r.collection !== "list" && fileFor(r, params, dir) && existsSync(join(dir, draftPath(fileFor(r, params, dir)))) ? draftPath(fileFor(r, params, dir)) : null;
|
|
434
|
+
const loading = draft ? { pending_draft: { file: draft, since: provEntries(dir).filter((e) => e.entity === draft).at(-1)?.at ?? null, accept: `mentura accept ${route} --venture ${venture}` } } : {};
|
|
435
|
+
if (remote) {
|
|
436
|
+
const prs = provEntries(dir).filter((e) => e.source?.pull_request && found.concat(patterns).some((f) => e.entity === address(src.id, f)));
|
|
437
|
+
if (prs.length) loading.pending_pull_requests = prs.map((e) => ({ entity: e.entity, pull_request: e.source.pull_request, since: e.at }));
|
|
438
|
+
}
|
|
439
|
+
if (!found.length) {
|
|
440
|
+
const l = layout(r, dir);
|
|
441
|
+
return { ok: true, result: null, not_found: { note: r.collection === "list" ? `no ${r.record} records yet` : `no ${r.record} record yet`, ...(remote ? { source: src.id } : {}), template: r.template, schema: r.schema, need_now: l.need_now ?? null, skills: fix(r).skills }, ...loading, ...deprecated };
|
|
442
|
+
}
|
|
443
|
+
const parse = (x) => x.path.endsWith(".json") ? JSON.parse(x.content) : x.path.endsWith(".jsonl") ? x.content.split("\n").filter((l) => l.trim()).map((l) => JSON.parse(l)) : x.content;
|
|
444
|
+
// Overlay each attribute mastered elsewhere with the value its system of record holds, and
|
|
445
|
+
// cite where it came from. A source that holds nothing for it leaves the record's own value.
|
|
446
|
+
const overlays = new Map();
|
|
447
|
+
for (const x of items) {
|
|
448
|
+
const cites = {};
|
|
449
|
+
for (const a of attrs) {
|
|
450
|
+
let got;
|
|
451
|
+
try { got = await a.store.read(x.path); } catch (e) { return { ok: false, error: `${a.source.id}: ${String(e.message ?? e)}` }; }
|
|
452
|
+
const value = got ? JSON.parse(got.content)[a.attribute] : undefined;
|
|
453
|
+
cites[a.attribute] = value === undefined ? { source: a.source.id, missing: true } : { source: a.source.id, address: address(a.source.id, x.path), version: got.version };
|
|
454
|
+
if (value !== undefined) overlays.set(x.path, { ...(overlays.get(x.path) ?? {}), [a.attribute]: value });
|
|
455
|
+
}
|
|
456
|
+
if (attrs.length) x.attributes = cites;
|
|
457
|
+
}
|
|
458
|
+
// Freshness (V-5): a stale record is still read, flagged stale until it is redone (stale-while-revalidate).
|
|
459
|
+
const fresh = remote ? {} : await freshnessFlag(dir, found, r.collection === "list");
|
|
460
|
+
Object.assign(loading, fresh);
|
|
461
|
+
const parseOne = parse;
|
|
462
|
+
const parseWith = (x) => { const v = parseOne(x); return overlays.has(x.path) ? { ...v, ...overlays.get(x.path) } : v; };
|
|
463
|
+
if (r.collection === "list") {
|
|
464
|
+
const idOf = (path) => {
|
|
465
|
+
for (const g of r.files) {
|
|
466
|
+
if (!g.includes("*")) continue;
|
|
467
|
+
const re = new RegExp("^" + g.split("*").map((s) => s.replace(/[.+^${}()|[\]\\]/g, "\\$&")).join("([^/]*)") + "$");
|
|
468
|
+
const m = path.match(re);
|
|
469
|
+
if (m) return m[1];
|
|
470
|
+
}
|
|
471
|
+
return path;
|
|
472
|
+
};
|
|
473
|
+
// Per-record containment (E-6): an item that does not parse is named in errors; the rest come back.
|
|
474
|
+
const { values, errors } = contain(items, parseWith, idOf);
|
|
475
|
+
return { ok: true, result: Object.fromEntries(values.map(([x, v]) => [idOf(x.path), v])), sources: Object.fromEntries(values.map(([x]) => [idOf(x.path), cite(x)])), ...(Object.keys(errors).length ? { errors } : {}), ...fresh, ...deprecated };
|
|
476
|
+
}
|
|
477
|
+
const data = Object.fromEntries(items.map((x) => [x.path, parseWith(x)]));
|
|
478
|
+
// V-9 hook: a summary whose source changed since it was written is marked stale (schema/stale.mjs).
|
|
479
|
+
const stale = !remote && items.length === 1 ? staleOf(dir, found[0]) : null;
|
|
480
|
+
return { ok: true, result: items.length === 1 ? data[found[0]] : data, ...(items.length === 1 ? { source: cite(items[0]) } : { sources: Object.fromEntries(items.map((x) => [x.path, cite(x)])) }), ...loading, ...(stale ? { stale } : {}), ...deprecated };
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
if (action === "check") {
|
|
484
|
+
// Gate group hook (P-7): the checks settings in naive.config.json turn a check off or make it warn only.
|
|
485
|
+
const plan = checkPlan(dir, r.checks);
|
|
486
|
+
const outs = plan.filter((p) => p.mode !== "off").map((p) => ({ check: p.check, mode: p.mode, ...spawnSync("node", [join(repo, p.check), dir], { encoding: "utf8" }) }));
|
|
487
|
+
const ok = outs.every((o) => o.status === 0 || o.mode === "warn");
|
|
488
|
+
return { ok, result: [...outs.map((o) => ({ check: o.check, passed: o.status === 0, ...(o.mode === "warn" ? { warn_only: true } : {}), output: (o.stdout + o.stderr).trim() })), ...plan.filter((p) => p.mode === "off").map((p) => ({ check: p.check, passed: null, off: true }))], ...(ok ? {} : { fix: fix(r) }), ...deprecated };
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
if (action === "write") {
|
|
492
|
+
forget(dir); // Caching hook (H-3): a write never uses the cache, and what this action memoized for the venture is read again after it.
|
|
493
|
+
if (!input || typeof input !== "object") return { ok: false, error: "write needs the record as input" };
|
|
494
|
+
if (r.collection === "item" && !/^[a-z0-9][a-z0-9-]*$/.test(params.id)) return { ok: false, error: `${params.id} is not a valid id (lowercase letters, digits and hyphens)` };
|
|
495
|
+
const file = fileFor(r, params, dir);
|
|
496
|
+
if (["idea-dump", "raw-source", "raw-text"].includes(r.record) && existsSync(join(dir, file))) return { ok: false, error: `${route} is raw and immutable: add a new capture, do not edit this one` };
|
|
497
|
+
const gate = gateOf(r, dir);
|
|
498
|
+
const text = JSON.stringify(input, null, 2) + "\n";
|
|
499
|
+
// The schema first, on a scratch copy, so nothing reaches any source unless it matches.
|
|
500
|
+
const scratch = mkdtempSync(join(tmpdir(), "mentura-write-"));
|
|
501
|
+
const tmp = join(scratch, basename(file));
|
|
502
|
+
writeFileSync(tmp, text);
|
|
503
|
+
const v = spawnSync("python3", [join(repo, "schema/validate.py"), "--schema", r.schema.replace(/^schema\//, ""), "--file", tmp], { encoding: "utf8" });
|
|
504
|
+
rmSync(scratch, { recursive: true, force: true });
|
|
505
|
+
if (v.status !== 0) return { ok: false, error: `does not match ${r.schema}:\n${(v.stdout + v.stderr).trim()}`, fix: fix(r) };
|
|
506
|
+
const agent = agentFromEnv(actor === "person" ? "person" : "agent", via, { ...(actor_id && actor !== "person" ? { id: actor_id } : {}), ...provenance.agent });
|
|
507
|
+
const log = (entity, activity, extra = {}) => prov(dir, { entity, route, activity, agent, derived_from: provenance.derived_from, decision: provenance.decision ?? gate?.class ?? undefined, note: provenance.note, ...extra });
|
|
508
|
+
|
|
509
|
+
// Attributes mastered elsewhere (EE-042) go to their own system of record, each as a JSON
|
|
510
|
+
// object holding only that attribute at the record's path, and leave the record's own file, so
|
|
511
|
+
// the fact lives in one place. The whole record was checked against its schema above. A gated
|
|
512
|
+
// agent write stays whole, as a draft, until a person accepts it.
|
|
513
|
+
const split = attrs.filter((a) => a.attribute in input);
|
|
514
|
+
// A decision record that is open and fails closed, or waits on one that is, holds the record
|
|
515
|
+
// (EE-054): an agent's write becomes a draft, as for a gated record.
|
|
516
|
+
const held = !remote && actor !== "person" ? holding(dir, file) : null;
|
|
517
|
+
// The venture's own fail-closed routes (naive.config.json, the gate, EE-073) hold an agent's write too.
|
|
518
|
+
// Gate group hook (X-1 to X-6, U-4): the venture's gate decides; a refusal stops the write here.
|
|
519
|
+
const configured = !remote ? gateWrite({ dir, r, route, requested, file, actor, actor_id, provenance, gate }) : null;
|
|
520
|
+
if (configured?.then === "refuse") return { ok: false, error: configured.why, gate_rule: configured.rule, refused_by: configured.source };
|
|
521
|
+
const gatedDraft = !remote && (!!gate?.drafts || !!held || !!configured) && actor !== "person";
|
|
522
|
+
const attributeWrites = [];
|
|
523
|
+
if (split.length && !gatedDraft) {
|
|
524
|
+
for (const a of split) {
|
|
525
|
+
if (a.store.writes === "none") return { ok: false, error: `${r.record}.${a.attribute} is mastered in ${a.source.id}, which is read-only` };
|
|
526
|
+
}
|
|
527
|
+
for (const a of split) {
|
|
528
|
+
const part = JSON.stringify({ [a.attribute]: input[a.attribute] }, null, 2) + "\n";
|
|
529
|
+
let w;
|
|
530
|
+
try { w = await a.store.write(file, part, { message: `${route}.${a.attribute}: ${provenance.note ?? "written through Mentura"}`, title: `Mentura: ${route}.${a.attribute} (${venture})` }); }
|
|
531
|
+
catch (e) { return { ok: false, error: `${a.source.id}: ${String(e.message ?? e)}` }; }
|
|
532
|
+
const at = address(a.source.id, file);
|
|
533
|
+
log(`${at}#${a.attribute}`, a.store.writes === "pull-request" ? "draft" : provenance.activity ?? "write", { sha256: createHash("sha256").update(part).digest("hex"),
|
|
534
|
+
source: { id: a.source.id, connector: a.source.connector, version: w.version, ...(w.pull_request ? { pull_request: w.pull_request.url } : {}) } });
|
|
535
|
+
attributeWrites.push({ attribute: a.attribute, written: at, version: w.version, ...(w.pull_request ? { pull_request: w.pull_request.url } : {}) });
|
|
536
|
+
}
|
|
537
|
+
input = Object.fromEntries(Object.entries(input).filter(([k]) => !split.some((a) => a.attribute === k)));
|
|
538
|
+
}
|
|
539
|
+
const attributesOut = attributeWrites.length ? { attributes: attributeWrites } : {};
|
|
540
|
+
const recordText = attributeWrites.length ? JSON.stringify(input, null, 2) + "\n" : text;
|
|
541
|
+
|
|
542
|
+
if (remote) {
|
|
543
|
+
// Another source: its connector decides how a write lands. A pull-request source opens a
|
|
544
|
+
// draft pull request, which a person accepts by merging, so it is a draft for anyone.
|
|
545
|
+
if (store.writes === "none") return { ok: false, error: `${route} lives in ${src.id}, which is read-only` };
|
|
546
|
+
let w;
|
|
547
|
+
try { w = await store.write(file, recordText, { message: `${route}: ${provenance.note ?? `written by ${actor === "person" ? "a person" : "an agent"} through Mentura`}`, title: `Mentura: ${route} (${venture})` }); }
|
|
548
|
+
catch (e) { return { ok: false, error: `${src.id}: ${String(e.message ?? e)}` }; }
|
|
549
|
+
const draft = store.writes === "pull-request";
|
|
550
|
+
const at = address(src.id, file);
|
|
551
|
+
const entry = log(at, draft ? "draft" : provenance.activity ?? "write", { sha256: createHash("sha256").update(recordText).digest("hex"),
|
|
552
|
+
source: { id: src.id, connector: src.connector, version: w.version, ...(w.commit ? { commit: w.commit } : {}), ...(w.pull_request ? { pull_request: w.pull_request.url } : {}) } });
|
|
553
|
+
return { ok: true, result: { written: at, draft, gate: gate?.class ?? null, provenance: entry.hash, version: w.version, ...(w.pull_request ? { pull_request: w.pull_request.url } : {}),
|
|
554
|
+
note: draft ? `opened draft pull request ${w.pull_request.url}; merging it accepts the write` : "written", ...attributesOut }, ...deprecated };
|
|
555
|
+
}
|
|
556
|
+
|
|
557
|
+
const draft = gatedDraft;
|
|
558
|
+
// Gate group hook (X-2): the held draft, the owner it was sent to and any variants kept side by side.
|
|
559
|
+
let held2 = null;
|
|
560
|
+
if (draft) held2 = heldDraft(dir, { draft: draftPath(file), file, text, verdict: configured, route, agent });
|
|
561
|
+
else await store.write(file, recordText);
|
|
562
|
+
const entry = log(draft ? held2.written : file, draft ? "draft" : provenance.activity ?? "write");
|
|
563
|
+
return { ok: true, result: { written: draft ? held2.written : file, draft, gate: gate?.class ?? null, provenance: entry.hash, note: draft ? (gate?.drafts ? `${r.record} is a ${gate.class} decision (${gate.level}, owner ${gate.owner}): written as a draft for a person to accept` : held ? `decision ${held.id} ${held.why}: written as a draft for a person to accept` : configured.why) : "written", ...(held ? { held_by: held.id } : {}), ...(configured ? { gate_rule: configured.rule, ...(configured.owner ? { owner: configured.owner } : {}) } : {}), ...(held2?.variants ? { variant: held2.variant, variants: held2.variants, decision: held2.decision } : {}), ...attributesOut }, ...deprecated };
|
|
564
|
+
}
|
|
565
|
+
return { ok: false, error: `unhandled action ${action}` };
|
|
566
|
+
}
|
|
567
|
+
|
|
568
|
+
// Views (pages): Markdown for people. A generic renderer: objects become headings and lists,
|
|
569
|
+
// arrays of flat objects become tables.
|
|
570
|
+
function md(x, depth = 2) {
|
|
571
|
+
if (x === null || x === undefined) return "_none_";
|
|
572
|
+
if (typeof x !== "object") return String(x);
|
|
573
|
+
if (Array.isArray(x)) {
|
|
574
|
+
if (!x.length) return "_none_";
|
|
575
|
+
const flat = x.every((o) => o && typeof o === "object" && !Array.isArray(o) && Object.values(o).every((v) => v === null || typeof v !== "object"));
|
|
576
|
+
if (flat) {
|
|
577
|
+
const cols = [...new Set(x.flatMap((o) => Object.keys(o)))];
|
|
578
|
+
return [`| ${cols.join(" | ")} |`, `| ${cols.map(() => "---").join(" | ")} |`, ...x.map((o) => `| ${cols.map((c) => String(o[c] ?? "").replace(/\|/g, "/").replace(/\n/g, " ")).join(" | ")} |`)].join("\n");
|
|
579
|
+
}
|
|
580
|
+
return x.every((v) => v === null || typeof v !== "object") ? x.map((v) => `- ${v}`).join("\n") : x.map((v) => typeof v === "object" ? md(v, depth + 1) : `- ${v}`).join("\n\n");
|
|
581
|
+
}
|
|
582
|
+
const blocks = [];
|
|
583
|
+
for (const [k, v] of Object.entries(x)) {
|
|
584
|
+
if (v !== null && typeof v === "object") blocks.push(`${"#".repeat(Math.min(depth, 6))} ${k}\n\n${md(v, depth + 1)}`);
|
|
585
|
+
else if (blocks.length && blocks.at(-1).startsWith("- **")) blocks[blocks.length - 1] += `\n- **${k}:** ${v}`;
|
|
586
|
+
else blocks.push(`- **${k}:** ${v}`);
|
|
587
|
+
}
|
|
588
|
+
return blocks.join("\n\n");
|
|
589
|
+
}
|
|
590
|
+
|
|
591
|
+
function render(route, res) {
|
|
592
|
+
if (res.result?.slots) return [`# ${res.result.venture}: overview`, ...res.result.slots.map((s) => `## ${s.slot}\n\n${s.error ? `_could not render: ${s.error}_` : md(s.data, 3)}`)].join("\n\n");
|
|
593
|
+
const head = `# ${route}`;
|
|
594
|
+
if (res.result === null && res.not_found) return `${head}\n\n${res.not_found.note}.${res.not_found.template ? ` Start from \`${res.not_found.template}\`.` : ""}${res.not_found.need_now ? ` Need-now: ${res.not_found.need_now.status} (${res.not_found.need_now.now}).` : ""}`;
|
|
595
|
+
return `${head}\n\n${res.pending_draft ? `> A draft is waiting for a person to accept: \`${res.pending_draft.accept}\`\n\n` : ""}${md(res.result)}`;
|
|
596
|
+
}
|
|
597
|
+
|
|
598
|
+
// The overview's slots (@slot): each is computed on its own, so one failing does not stop the rest.
|
|
599
|
+
async function overview(venture, workspace) {
|
|
600
|
+
const dir = ventureDir(venture, workspace);
|
|
601
|
+
const v = load(join(dir, "venture.json"));
|
|
602
|
+
const slot = async (name, f) => { try { return { slot: name, data: await f() }; } catch (e) { return { slot: name, error: String(e.message ?? e).split("\n")[0] }; } };
|
|
603
|
+
const run = async (route) => { const r = await call({ venture, route, action: "run", workspace }); if (!r.ok) throw new Error(r.error); return r.result; };
|
|
604
|
+
const read = async (route) => { const r = await call({ venture, route, action: "read", workspace }); if (!r.ok) throw new Error(r.error); return r.result ?? r.not_found?.note; };
|
|
605
|
+
return {
|
|
606
|
+
venture: v.id,
|
|
607
|
+
slots: await Promise.all([
|
|
608
|
+
slot("@venture", () => ({ name: v.name, phase: v.phase, ambition: v.ambition ?? null, status: v.status, promise: v.core?.promise ?? null })),
|
|
609
|
+
slot("@telos", async () => { const t = await read("purpose/telos"); return typeof t === "string" ? t : { end: t.end ?? t.statement ?? null, phases: (t.phases ?? []).map((p) => p.phase) }; }),
|
|
610
|
+
slot("@validation", async () => { const r = await call({ venture, route: "purpose/validation/assess", action: "run", input: { as_is: true }, workspace }); if (!r.ok) throw new Error(r.error); const a = r.result; return { supports: a.supports, next: a.next, assumptions: a.assumptions.map((x) => `${x.assumption}: ${x.label}`).join(", "), missing: a.missing.length }; }),
|
|
611
|
+
slot("@need-now", async () => (await run("system/need-now")).rows.filter((x) => x.status === "suggest now").map((x) => ({ record: x.type, now: x.now, why: x.why }))),
|
|
612
|
+
slot("@ready", async () => { const t = await run("system/time-to-ready"); return { running: t.running.on ?? `not yet: ${t.running.missing.join(", ")}`, layers: t.layers.map((l) => ({ layer: l.layer, ready: l.ready_on ?? `missing ${l.missing.join(", ")}` })) }; }),
|
|
613
|
+
slot("@team", async () => { const p = await read("people/team-plan"); if (typeof p === "string") return p; return p.roles.map((x) => ({ role: x.id, occupant: x.occupant, status: x.status, needed_by: x.needed_by })); }),
|
|
614
|
+
slot("@packages", () => packageSlots(dir, venture)), // P-17: a package with nothing in it shows its default
|
|
615
|
+
slot("@provenance", () => provEntries(dir).slice(-5).reverse().map((e) => ({ at: e.at, activity: e.activity, entity: e.entity, by: `${e.agent.kind}${e.agent.id ? ` ${e.agent.id}` : ""} via ${e.agent.via}` }))),
|
|
616
|
+
]),
|
|
617
|
+
};
|
|
618
|
+
}
|
|
619
|
+
|
|
620
|
+
// Robots: per role, the routes it may read and write.
|
|
621
|
+
function robots(dir) {
|
|
622
|
+
const plan = [join(dir, "people/team-plan.json"), join(dir, "operations/team-plan.json")].find((p) => existsSync(p));
|
|
623
|
+
if (!plan) return { note: "no team plan: every actor may reach every route; gates still apply", roles: [] };
|
|
624
|
+
return { note: "a role without routes may reach every route; gates still apply", roles: load(plan).roles.map((r) => ({ role: r.id, occupant: r.occupant, read: (r.routes?.read ?? ["**"]).join(", "), write: (r.routes?.write ?? (r.routes ? [] : ["**"])).join(", ") || "none" })) };
|
|
625
|
+
}
|
|
626
|
+
|
|
627
|
+
/** The route table as Markdown, for docs/reference/ROUTES.md. */
|
|
628
|
+
export function routesMd() {
|
|
629
|
+
const rows = routes().map((r) => `| \`${r.route}\` | ${r.kind}${r.collection ? ` (${r.collection})` : ""} | ${r.actions.join(", ")} | ${r.kind === "record" ? `\`${r.files.join("`, `")}\`` : r.script ? `\`${r.script}\`` : "router"} | ${r.decision_class ?? ""} | ${r.groups.join(" ")} | ${r.note.replace(/\|/g, "/")} |`);
|
|
630
|
+
const gs = groups().map((g) => `| \`${g.group}\` | ${g.question} | ${g.routes.length ? g.routes.map((x) => `\`${x}\``).join(", ") : "none"} |`);
|
|
631
|
+
return [
|
|
632
|
+
"## Routes (generated by `node interface/router.mjs --md`)",
|
|
633
|
+
"",
|
|
634
|
+
"Every route works on one venture, named in front of it: its id, or its folder path for a nested venture (`studio/print`). Record routes read, write, check, view, lay out or show the template of one record type; a collection has a list route and an `[id]` item route; command routes run a venture-wide tool and can be viewed. A write is checked against the record's schema first; a record whose decision class escalates is written as a draft when an agent writes it, for a person to accept.",
|
|
635
|
+
"",
|
|
636
|
+
"| Route | Kind | Actions | Files or script | Decision class | Groups | What it is |",
|
|
637
|
+
"| --- | --- | --- | --- | --- | --- | --- |",
|
|
638
|
+
...rows,
|
|
639
|
+
"",
|
|
640
|
+
"### Route groups (generated)",
|
|
641
|
+
"",
|
|
642
|
+
"A group gathers the routes need-now suggests in a phase. It changes the layout an agent is given, not the path.",
|
|
643
|
+
"",
|
|
644
|
+
"| Group | The phase asks | Routes |",
|
|
645
|
+
"| --- | --- | --- |",
|
|
646
|
+
...gs,
|
|
647
|
+
].join("\n");
|
|
648
|
+
}
|
|
649
|
+
|
|
650
|
+
if (import.meta.url === `file://${process.argv[1]}`) {
|
|
651
|
+
if (process.argv.includes("--md")) console.log(routesMd());
|
|
652
|
+
else console.log(JSON.stringify(routes(), null, 2));
|
|
653
|
+
}
|