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,717 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// Model steps ("use model", EE-073): the client side of Naive's server and client split. Code is the
|
|
3
|
+
// server: it reads records, computes values and decides. A model is the client: it answers one atomic
|
|
4
|
+
// judgment over unstructured input, or drafts prose. A package (or a kit) declares its model steps in
|
|
5
|
+
// use_model; this module checks them, assembles the state each one is given, asks a model in batches,
|
|
6
|
+
// checks the typed answers, and writes each answer as a judgment record through the router.
|
|
7
|
+
//
|
|
8
|
+
// kinds classify, noul (judge fit: yes, no or unknown), choice, score, draft. Each call is
|
|
9
|
+
// one question about one item (S-2). Only a draft is model-only (S-17)
|
|
10
|
+
// decompose a step names the fields its question needs (item.notes, foundation/problem#statement,
|
|
11
|
+
// ada.note) and the model gets those fields and nothing else (S-4); a field path, never a
|
|
12
|
+
// whole record or package, inside a context budget (S-11)
|
|
13
|
+
// batch every question of a package in one wave goes in one request, and packages are asked in
|
|
14
|
+
// parallel (S-5, F-11)
|
|
15
|
+
// payload code's results, a placeholder for each pending judgment, and the state each judgment
|
|
16
|
+
// receives (S-6); a run emits computed values first and judgments as they arrive (S-7)
|
|
17
|
+
// provenance a judgment is the agent's until a person confirms it, and so is an agent's record
|
|
18
|
+
// derived from it; the records the step read stay their authors' (S-9, S-10)
|
|
19
|
+
// JSON state is plain JSON and every question points at paths in it (S-12)
|
|
20
|
+
// keep a draft can carry computed values it must not change ({{seats}}); code fills them in
|
|
21
|
+
// and checks they came through unchanged (S-13)
|
|
22
|
+
// after a step waits on the steps it reads first; independent steps move at once (F-10, F-11)
|
|
23
|
+
// when a step asked only when a computed condition holds; --speculate asks it while code is
|
|
24
|
+
// still computing and discards the answers not used (F-13)
|
|
25
|
+
// outcomes answer, expected (an invalid write, a missing record, a gate that holds), uncaught (a
|
|
26
|
+
// bug, such as a cycle in the step order) and uncertain (below threshold, malformed or
|
|
27
|
+
// unanswered), which goes to the founder as an open decision (E-1, E-9). When a judgment
|
|
28
|
+
// and a code check disagree, the code wins (overruled)
|
|
29
|
+
//
|
|
30
|
+
// The model is passed in: a function (tests), an answers file (--answers, written by the founder's agent
|
|
31
|
+
// or by platform/integrations/jev.mjs), or MENTURA_MODEL_CMD, a command that reads one request on stdin
|
|
32
|
+
// and prints {model, answers} (the same protocol as Jev). With none, run refuses and payload shows what
|
|
33
|
+
// would be asked. This module never names a vendor or a credential.
|
|
34
|
+
//
|
|
35
|
+
// node schema/model-steps.mjs <venture dir> payload [--package <name>] [--kit <kit.json>] [--json]
|
|
36
|
+
// node schema/model-steps.mjs <venture dir> run [--package <name>] [--kit <kit.json>] [--answers <file>] [--speculate] [--stream] [--fresh] [--json]
|
|
37
|
+
// node schema/model-steps.mjs <venture dir> check [--package <name>] [--kit <kit.json>]
|
|
38
|
+
// node schema/model-steps.mjs <venture dir> waterfall [--json]
|
|
39
|
+
// node schema/model-steps.mjs <venture dir> provenance <file> [--checks] [--json]
|
|
40
|
+
// node schema/model-steps.mjs <venture dir> confirm <judgment id> --by <name>
|
|
41
|
+
|
|
42
|
+
import { spawn } from "node:child_process";
|
|
43
|
+
import { createHash } from "node:crypto";
|
|
44
|
+
import { existsSync, readFileSync, readdirSync } from "node:fs";
|
|
45
|
+
import { dirname, join, resolve as resolvePath } from "node:path";
|
|
46
|
+
import { call as routerCall, files as ventureFiles, glob, routes } from "../interface/router.mjs";
|
|
47
|
+
import { agentFromEnv, entries as provEntries, record as prov } from "../interface/provenance.mjs";
|
|
48
|
+
import { decisions as decisionsOf, propose } from "./decisions.mjs";
|
|
49
|
+
import { loadPackages } from "./load-packages.mjs";
|
|
50
|
+
|
|
51
|
+
const repo = new URL("..", import.meta.url).pathname;
|
|
52
|
+
const load = (p) => JSON.parse(readFileSync(p, "utf8"));
|
|
53
|
+
|
|
54
|
+
export const KINDS = {
|
|
55
|
+
classify: { options: true, judgment: true },
|
|
56
|
+
noul: { judgment: true },
|
|
57
|
+
choice: { options: true, judgment: true },
|
|
58
|
+
score: { scale: true, judgment: true },
|
|
59
|
+
draft: { judgment: false },
|
|
60
|
+
};
|
|
61
|
+
export const THRESHOLD = 0.7;
|
|
62
|
+
export const BUDGET = 2000;
|
|
63
|
+
export const ACTOR = "model-step";
|
|
64
|
+
export const JUDGMENTS = "knowledge/judgments";
|
|
65
|
+
const ID = /^[a-z0-9][a-z0-9-]*$/;
|
|
66
|
+
|
|
67
|
+
/** A bug: a broken step definition, a cycle in the step order, an assembler that built a bad request. */
|
|
68
|
+
export class Uncaught extends Error { constructor(m) { super(m); this.name = "Uncaught"; this.uncaught = true; } }
|
|
69
|
+
/** An expected failure: a missing record, a failed model request, a field over budget. */
|
|
70
|
+
export class Expected extends Error { constructor(m, kind = "expected") { super(m); this.name = "Expected"; this.expected = true; this.kind = kind; } }
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* The three kinds of error, plus the answer (E-1). x is a router result, a judgment, or a thrown error.
|
|
74
|
+
* expected: an invalid write, a missing record, a gate that holds. uncaught: a bug. uncertain: a judgment
|
|
75
|
+
* below threshold, routed to a person.
|
|
76
|
+
*/
|
|
77
|
+
export function outcomeOf(x) {
|
|
78
|
+
if (x instanceof Error) return x.expected ? { outcome: "expected", kind: x.kind, error: x.message } : { outcome: "uncaught", kind: "bug", error: x.message };
|
|
79
|
+
if (x?.outcome === "uncertain") return { outcome: "uncertain", kind: "below threshold", why: x.why };
|
|
80
|
+
if (x?.outcome === "overruled") return { outcome: "answer", kind: "code wins", why: x.why };
|
|
81
|
+
if (x && x.ok === false) return { outcome: "expected", kind: x.locked_by ? "locked" : /does not match/.test(x.error ?? "") ? "invalid write" : "refused", error: x.error };
|
|
82
|
+
if (x?.not_found) return { outcome: "expected", kind: "missing record", error: x.not_found.note };
|
|
83
|
+
if (x?.result?.draft) return { outcome: "expected", kind: "gate holds", error: x.result.note };
|
|
84
|
+
return { outcome: "answer", kind: "answer" };
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/** A draft is model-only: code never expects a deterministic answer from it (S-17). */
|
|
88
|
+
export function expectDeterministic(step) {
|
|
89
|
+
if (!KINDS[step.kind]?.judgment) throw new Uncaught(`${step.package}/${step.step} is a ${step.kind}: only a model can do it, so code cannot expect a deterministic answer from it`);
|
|
90
|
+
return true;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// ---- Steps and their checks ----------------------------------------------------------------------
|
|
94
|
+
|
|
95
|
+
/** The model steps declared by the repository's packages (all, or the named ones), or by a kit.json. */
|
|
96
|
+
export function stepsOf({ packages: names, kit } = {}) {
|
|
97
|
+
if (kit) {
|
|
98
|
+
const k = typeof kit === "string" ? load(kit) : kit;
|
|
99
|
+
return (k.boundary?.use_model ?? []).map((s) => ({ ...s, package: k.name }));
|
|
100
|
+
}
|
|
101
|
+
const dir = join(repo, "packages");
|
|
102
|
+
return readdirSync(dir).filter((n) => existsSync(join(dir, n, "package.json")) && (!names?.length || names.includes(n)))
|
|
103
|
+
.flatMap((n) => (load(join(dir, n, "package.json")).use_model ?? []).map((s) => (typeof s === "string" ? { step: s, package: n } : { ...s, package: n })));
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
const keyOf = (s, item) => `${s.package}/${s.step}/${item ?? "-"}`;
|
|
107
|
+
export const judgmentId = (s, item) => `${s.package}-${s.step}${item ? `-${item}` : ""}`.toLowerCase().replace(/[^a-z0-9-]+/g, "-");
|
|
108
|
+
const refsOf = (q) => [...String(q ?? "").replace(/\{\{[^}]*\}\}/g, "").matchAll(/\{([^{}\s]+)\}/g)].map((m) => m[1]);
|
|
109
|
+
const placeholdersOf = (t) => [...String(t ?? "").matchAll(/\{\{([a-z0-9_-]+)\}\}/gi)].map((m) => m[1]);
|
|
110
|
+
|
|
111
|
+
/** What an input names: a field of the item, a field of a routed record, or a field of a record by id. */
|
|
112
|
+
export function inputOf(input) {
|
|
113
|
+
if (typeof input !== "string") return { error: "an input is a string" };
|
|
114
|
+
if (input === "item" || input.startsWith("item.")) return input.length > 5 ? { form: "item", path: input.slice(5) } : { error: "names the whole item" };
|
|
115
|
+
if (input.includes("#")) { const [route, path] = input.split("#"); return path ? { form: "route", route, path } : { error: "names a whole record" }; }
|
|
116
|
+
const dot = input.indexOf(".");
|
|
117
|
+
if (dot < 0) return { error: "names a whole record or package" };
|
|
118
|
+
return { form: "id", id: input.slice(0, dot), path: input.slice(dot + 1) };
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/** Errors in one step definition. */
|
|
122
|
+
export function checkStep(s, { known } = {}) {
|
|
123
|
+
const at = `${s.package}/${s.step ?? "(no step id)"}`;
|
|
124
|
+
const e = [];
|
|
125
|
+
if (typeof s.step !== "string" || !ID.test(s.step)) e.push(`${at}: a step has an id of lowercase letters, digits and hyphens`);
|
|
126
|
+
const k = KINDS[s.kind];
|
|
127
|
+
if (!k) { e.push(`${at}: kind is one of ${Object.keys(KINDS).join(", ")} (one atomic judgment, or a draft)`); return e; }
|
|
128
|
+
if (typeof s.question !== "string" || !s.question.trim()) e.push(`${at}: a step asks one question`);
|
|
129
|
+
const computed = s.computed ?? {};
|
|
130
|
+
const inputs = s.inputs ?? [];
|
|
131
|
+
if (!Array.isArray(inputs)) e.push(`${at}: inputs is an array of field paths`);
|
|
132
|
+
if (!inputs.length && !Object.keys(computed).length) e.push(`${at}: a step names the fields its question needs`);
|
|
133
|
+
for (const i of inputs) {
|
|
134
|
+
const p = inputOf(i);
|
|
135
|
+
if (p.error) e.push(`${at}: input ${i} ${p.error}; name only the fields its question needs (keep model calls at the leaves)`);
|
|
136
|
+
else if (p.form === "item" && !s.for_each) e.push(`${at}: input ${i} names the item, but the step has no for_each`);
|
|
137
|
+
}
|
|
138
|
+
if (s.for_each !== undefined && (typeof s.for_each !== "string" || (known && !known.lists.has(s.for_each)))) e.push(`${at}: for_each ${s.for_each} is not a collection route`);
|
|
139
|
+
for (const [name, spec] of Object.entries(computed)) {
|
|
140
|
+
const forms = ["count", "read", "item"].filter((f) => spec && spec[f] !== undefined);
|
|
141
|
+
if (forms.length !== 1) e.push(`${at}: computed ${name} is one of {count: <route>}, {read: <route>#<path>}, {item: <path>}`);
|
|
142
|
+
else if (forms[0] === "item" && !s.for_each) e.push(`${at}: computed ${name} reads the item, but the step has no for_each`);
|
|
143
|
+
else if (forms[0] === "read" && !String(spec.read).includes("#")) e.push(`${at}: computed ${name} reads <route>#<path>`);
|
|
144
|
+
}
|
|
145
|
+
const after = s.after ?? [];
|
|
146
|
+
const refs = refsOf(s.question);
|
|
147
|
+
for (const r of refs) {
|
|
148
|
+
if (r.startsWith("steps.")) { if (!after.includes(r.slice(6))) e.push(`${at}: the question reads {${r}}, so the step lists ${r.slice(6)} in after`); }
|
|
149
|
+
else if (!(r in computed) && !inputs.includes(r)) e.push(`${at}: the question points at {${r}}, which is not one of its inputs or computed values`);
|
|
150
|
+
}
|
|
151
|
+
for (const i of inputs) if (!refs.includes(i)) e.push(`${at}: input ${i} is given to the model but the question never points at it`);
|
|
152
|
+
if (k.options && !(Array.isArray(s.options) && s.options.length >= 2 && s.options.every((o) => typeof o === "string"))) e.push(`${at}: a ${s.kind} step lists at least two options`);
|
|
153
|
+
if (k.scale && !(Array.isArray(s.scale) && s.scale.length === 2 && s.scale.every(Number.isFinite) && s.scale[0] < s.scale[1])) e.push(`${at}: a score step gives its scale as [min, max]`);
|
|
154
|
+
if (s.threshold !== undefined && !(typeof s.threshold === "number" && s.threshold >= 0 && s.threshold <= 1)) e.push(`${at}: threshold is between 0 and 1`);
|
|
155
|
+
if (!k.judgment) {
|
|
156
|
+
if (s.threshold !== undefined) e.push(`${at}: a draft is model-only and has no confidence, so it has no threshold`);
|
|
157
|
+
if (s.check) e.push(`${at}: a draft is model-only: code cannot check it for a deterministic answer`);
|
|
158
|
+
}
|
|
159
|
+
if (s.check && !(s.check.equals in computed)) e.push(`${at}: check.equals names one of the step's computed values`);
|
|
160
|
+
for (const n of s.keep ?? []) {
|
|
161
|
+
if (!(n in computed)) e.push(`${at}: keep ${n} is not one of the step's computed values`);
|
|
162
|
+
if (!s.question?.includes(`{{${n}}}`)) e.push(`${at}: keep ${n}: the question asks the model to write {{${n}}} where it goes`);
|
|
163
|
+
}
|
|
164
|
+
if (s.keep?.length && k.judgment) e.push(`${at}: keep is for drafts; a judgment's code check is check.equals`);
|
|
165
|
+
if (s.when) {
|
|
166
|
+
const ops = ["equals", "in", "gte", "lte"].filter((o) => s.when[o] !== undefined);
|
|
167
|
+
if (!(s.when.computed in computed) || ops.length !== 1) e.push(`${at}: when is {computed: <name>, equals|in|gte|lte: <value>}`);
|
|
168
|
+
}
|
|
169
|
+
if (s.budget !== undefined && !(Number.isInteger(s.budget) && s.budget > 0)) e.push(`${at}: budget is a positive number of characters`);
|
|
170
|
+
return e;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
/** Steps in waves: each step after the steps it reads first. A cycle is a bug (uncaught). */
|
|
174
|
+
export function waves(steps) {
|
|
175
|
+
const id = (s) => `${s.package}/${s.step}`;
|
|
176
|
+
const byId = new Map(steps.map((s) => [id(s), s]));
|
|
177
|
+
const deps = new Map(steps.map((s) => [id(s), (s.after ?? []).map((a) => `${s.package}/${a}`)]));
|
|
178
|
+
for (const [s, ds] of deps) for (const d of ds) if (!byId.has(d)) throw new Uncaught(`${s} waits on ${d}, which no package declares`);
|
|
179
|
+
const out = [], done = new Set();
|
|
180
|
+
while (done.size < byId.size) {
|
|
181
|
+
const wave = [...byId.keys()].filter((s) => !done.has(s) && deps.get(s).every((d) => done.has(d)));
|
|
182
|
+
if (!wave.length) {
|
|
183
|
+
const left = [...byId.keys()].filter((s) => !done.has(s));
|
|
184
|
+
const cycle = [left[0]]; let at = left[0];
|
|
185
|
+
for (;;) { at = deps.get(at).find((d) => !done.has(d)); if (cycle.includes(at)) { cycle.push(at); break; } cycle.push(at); }
|
|
186
|
+
throw new Uncaught(`a cycle in the step order: ${cycle.slice(cycle.indexOf(at)).join(" -> ")}`);
|
|
187
|
+
}
|
|
188
|
+
wave.forEach((s) => done.add(s));
|
|
189
|
+
out.push(wave.map((s) => byId.get(s)));
|
|
190
|
+
}
|
|
191
|
+
return out;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
const knownRoutes = () => { const rs = routes(); return { lists: new Set(rs.filter((r) => r.collection === "list").map((r) => r.route)), all: new Set(rs.map((r) => r.route)) }; };
|
|
195
|
+
|
|
196
|
+
/** Errors across steps: each definition, unknown routes, the order, and (with a venture) private fields named. */
|
|
197
|
+
export async function checkSteps(steps, { dir, call = routerCall, workspace } = {}) {
|
|
198
|
+
const known = knownRoutes();
|
|
199
|
+
const errors = steps.flatMap((s) => checkStep(s, { known }));
|
|
200
|
+
for (const s of steps) for (const i of s.inputs ?? []) { const p = inputOf(i); if (p.form === "route" && !known.all.has(p.route)) errors.push(`${s.package}/${s.step}: input ${i} names no route`); }
|
|
201
|
+
try { waves(steps); } catch (e) { errors.push(e.message); }
|
|
202
|
+
if (dir && !errors.length) {
|
|
203
|
+
const ctx = await context(dir, { call, workspace });
|
|
204
|
+
for (const s of steps) {
|
|
205
|
+
let items;
|
|
206
|
+
try { items = await itemsOf(ctx, s); } catch (e) { errors.push(`${s.package}/${s.step}: ${e.message}`); continue; }
|
|
207
|
+
for (const it of items) for (const i of s.inputs ?? []) {
|
|
208
|
+
const p = inputOf(i);
|
|
209
|
+
if (p.form === "item" && privacy(it.record, p.path)) errors.push(`${s.package}/${s.step}: input ${i} of ${it.id} is ${privacy(it.record, p.path)} and must not reach a model`);
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
return errors;
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
// ---- Code: reading state and computing values ----------------------------------------------------
|
|
217
|
+
|
|
218
|
+
const get = (o, path) => path.split(".").reduce((x, k) => (x == null ? undefined : x[k]), o);
|
|
219
|
+
const set = (o, path, v) => { const ks = path.split("."); let x = o; for (const k of ks.slice(0, -1)) x = x[k] ??= {}; x[ks.at(-1)] = v; return o; };
|
|
220
|
+
const fileOf = (src) => src?.address?.replace(/^venture:\/\//, "") ?? null;
|
|
221
|
+
|
|
222
|
+
/** Why a field of a record must not reach a model, or null (S-16, EE-073). */
|
|
223
|
+
function privacy(rec, path) {
|
|
224
|
+
if (!rec || typeof rec !== "object") return null;
|
|
225
|
+
if (rec.private === true) return "in a private record";
|
|
226
|
+
if (rec.sensitive === true) return "in a sensitive record";
|
|
227
|
+
if ((rec.private_fields ?? []).some((f) => path === f || path.startsWith(`${f}.`))) return "a private field";
|
|
228
|
+
return null;
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
/** Plain JSON only: what a model is given must survive serialization unchanged (S-12). */
|
|
232
|
+
export function assertJson(v, at = "state") {
|
|
233
|
+
if (v === null || typeof v === "string" || typeof v === "boolean") return;
|
|
234
|
+
if (typeof v === "number") { if (!Number.isFinite(v)) throw new Expected(`${at} is not a finite number`, "not JSON"); return; }
|
|
235
|
+
if (Array.isArray(v)) { v.forEach((x, i) => assertJson(x, `${at}[${i}]`)); return; }
|
|
236
|
+
if (typeof v === "object" && Object.getPrototypeOf(v) === Object.prototype) { for (const [k, x] of Object.entries(v)) assertJson(x, `${at}.${k}`); return; }
|
|
237
|
+
throw new Expected(`${at} is not plain JSON (${v === undefined ? "undefined" : typeof v === "object" ? v.constructor?.name ?? "object" : typeof v})`, "not JSON");
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
/** Every path a request's questions point at resolves in its state, and every question has state (S-12). */
|
|
241
|
+
export function checkRequest(req) {
|
|
242
|
+
assertJson(req.state);
|
|
243
|
+
for (const [key, q] of Object.entries(req.questions)) {
|
|
244
|
+
if (!(key in req.state)) throw new Uncaught(`question ${key} has no state`);
|
|
245
|
+
for (const m of q.instructions.matchAll(/`state\["([^"]+)"\]((?:\.[A-Za-z0-9_-]+|\["[^"]+"\])*)`/g)) {
|
|
246
|
+
const parts = [m[1], ...[...m[2].matchAll(/\.([A-Za-z0-9_-]+)|\["([^"]+)"\]/g)].map((x) => x[1] ?? x[2])];
|
|
247
|
+
if (parts.reduce((x, k) => (x == null ? undefined : x[k]), req.state) === undefined) throw new Uncaught(`question ${key} points at ${m[0]}, which is not in its state`);
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
return true;
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
async function context(dir, { call = routerCall, workspace } = {}) {
|
|
254
|
+
const venture = load(join(dir, "venture.json")).id;
|
|
255
|
+
const ws = workspace ?? dirname(dir);
|
|
256
|
+
const memo = new Map();
|
|
257
|
+
const read = (route) => {
|
|
258
|
+
if (!memo.has(route)) memo.set(route, call({ venture, workspace: ws, route, action: "read", actor: "agent", actor_id: ACTOR }).then((r) => {
|
|
259
|
+
if (!r.ok) throw new Expected(`${route}: ${r.error}`, "refused");
|
|
260
|
+
return r;
|
|
261
|
+
}));
|
|
262
|
+
return memo.get(route);
|
|
263
|
+
};
|
|
264
|
+
let byId = null;
|
|
265
|
+
const recordById = (id) => {
|
|
266
|
+
byId ??= new Map(ventureFiles(dir).filter((f) => f.endsWith(".json") && !f.startsWith("knowledge/judgments/")).flatMap((f) => {
|
|
267
|
+
try { const r = load(join(dir, f)); return r && typeof r.id === "string" ? [[r.id, { file: f, record: r }]] : []; } catch { return []; }
|
|
268
|
+
}));
|
|
269
|
+
return byId.get(id) ?? null;
|
|
270
|
+
};
|
|
271
|
+
return { dir, venture, workspace: ws, call, read, recordById };
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
async function itemsOf(ctx, s) {
|
|
275
|
+
if (!s.for_each) return [{ id: null, record: null, file: null }];
|
|
276
|
+
const r = await ctx.read(s.for_each);
|
|
277
|
+
if (r.result === null) return [];
|
|
278
|
+
return Object.entries(r.result).map(([id, record]) => ({ id, record, file: fileOf(r.sources?.[id]) }));
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
// The fields one question is given: { state, reads, files, withheld }. Nothing else from the record.
|
|
282
|
+
async function inputsFor(ctx, s, it) {
|
|
283
|
+
const state = {}, reads = [], files = new Set();
|
|
284
|
+
for (const i of s.inputs ?? []) {
|
|
285
|
+
const p = inputOf(i);
|
|
286
|
+
let rec, file;
|
|
287
|
+
if (p.form === "item") { rec = it.record; file = it.file; }
|
|
288
|
+
else if (p.form === "route") { const r = await ctx.read(p.route); if (r.result === null) return { missing: `${p.route} has no record yet` }; rec = r.result; file = fileOf(r.source); }
|
|
289
|
+
else { const hit = ctx.recordById(p.id); if (!hit) return { missing: `no record ${p.id}` }; rec = hit.record; file = hit.file; }
|
|
290
|
+
const why = privacy(rec, p.path);
|
|
291
|
+
if (why) return { withheld: `${i} is ${why}` };
|
|
292
|
+
const v = get(rec, p.path);
|
|
293
|
+
if (v === undefined) return { missing: `${it.id ?? file ?? i} has no ${p.path}` };
|
|
294
|
+
if (p.form === "item") set(state, `item.${p.path}`, v); else (state.records ??= {})[i] = v;
|
|
295
|
+
reads.push(`${file}#${p.path}`); if (file) files.add(file);
|
|
296
|
+
}
|
|
297
|
+
return { state, reads, files: [...files] };
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
async function computeFor(ctx, s, it) {
|
|
301
|
+
const out = {};
|
|
302
|
+
for (const [name, spec] of Object.entries(s.computed ?? {})) {
|
|
303
|
+
if (spec.item !== undefined) out[name] = get(it.record, spec.item);
|
|
304
|
+
else if (spec.count !== undefined) { const r = await ctx.read(spec.count); out[name] = r.result ? Object.keys(r.result).length : 0; }
|
|
305
|
+
else { const [route, path] = spec.read.split("#"); const r = await ctx.read(route); out[name] = r.result ? get(r.result, path) : undefined; }
|
|
306
|
+
}
|
|
307
|
+
return out;
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
const holds = (w, c) => {
|
|
311
|
+
const v = c?.[w.computed];
|
|
312
|
+
if (w.equals !== undefined) return v === w.equals;
|
|
313
|
+
if (w.in !== undefined) return w.in.includes(v);
|
|
314
|
+
if (w.gte !== undefined) return typeof v === "number" && v >= w.gte;
|
|
315
|
+
return typeof v === "number" && v <= w.lte;
|
|
316
|
+
};
|
|
317
|
+
const usesComputed = (s) => !!(s.keep?.length || refsOf(s.question).some((r) => r in (s.computed ?? {})));
|
|
318
|
+
|
|
319
|
+
/** The question as the model reads it: each {ref} becomes the path to it in the state. */
|
|
320
|
+
function instructionsFor(s, key) {
|
|
321
|
+
const p = (r) => r.startsWith("item.") ? `\`state["${key}"].item.${r.slice(5)}\`` : r.startsWith("steps.") ? `\`state["${key}"].steps.${r.slice(6)}\``
|
|
322
|
+
: r in (s.computed ?? {}) ? `\`state["${key}"].computed.${r}\`` : `\`state["${key}"].records["${r}"]\``;
|
|
323
|
+
const q = s.question.replace(/\{\{([^}]*)\}\}/g, "\u0000$1\u0001").replace(/\{([^{}\s]+)\}/g, (_, r) => p(r)).replace(/\u0000/g, "{{").replace(/\u0001/g, "}}");
|
|
324
|
+
return q;
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
function questionFor(s, key) {
|
|
328
|
+
return { type: s.kind, instructions: instructionsFor(s, key), deterministic: !!KINDS[s.kind].judgment,
|
|
329
|
+
...(s.options ? { options: s.options } : {}), ...(s.scale ? { scale: s.scale } : {}), ...(s.kind === "noul" ? { criteria: { true: "Yes", false: "No", unknown: "The state does not say" } } : {}) };
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
// ---- The model's answers: typed, thresholded, checked by code ------------------------------------
|
|
333
|
+
|
|
334
|
+
/** One answer checked against its step's type (E-9): a malformed answer never comes back. */
|
|
335
|
+
export function typed(s, raw) {
|
|
336
|
+
if (raw === undefined || raw === null) return { ok: false, why: "no answer came back" };
|
|
337
|
+
let value = raw, confidence = null;
|
|
338
|
+
if (typeof raw === "object" && !Array.isArray(raw)) {
|
|
339
|
+
value = raw.value ?? raw.answer;
|
|
340
|
+
confidence = raw.confidence ?? raw.probability ?? null;
|
|
341
|
+
} else if (s.kind === "noul" && typeof raw === "number") { value = raw >= 0.5; confidence = Math.max(raw, 1 - raw); }
|
|
342
|
+
if (confidence !== null && !(typeof confidence === "number" && confidence >= 0 && confidence <= 1)) return { ok: false, why: `malformed: confidence ${JSON.stringify(confidence)} is not between 0 and 1` };
|
|
343
|
+
const bad = (w) => ({ ok: false, why: `malformed: ${w}` });
|
|
344
|
+
switch (s.kind) {
|
|
345
|
+
case "noul": {
|
|
346
|
+
const v = value === true || value === "true" || value === "yes" ? true : value === false || value === "false" || value === "no" ? false : value === "unknown" ? "unknown" : undefined;
|
|
347
|
+
if (v === undefined) return bad(`${JSON.stringify(value)} is not yes, no or unknown`);
|
|
348
|
+
if (v === "unknown") return { ok: false, why: "the model said the state does not settle it", confidence };
|
|
349
|
+
return { ok: true, value: v, confidence };
|
|
350
|
+
}
|
|
351
|
+
case "choice": case "classify":
|
|
352
|
+
return s.options.includes(value) ? { ok: true, value, confidence } : bad(`${JSON.stringify(value)} is not one of ${s.options.join(", ")}`);
|
|
353
|
+
case "score": {
|
|
354
|
+
const v = typeof value === "string" && value.trim() !== "" ? Number(value) : value;
|
|
355
|
+
return typeof v === "number" && Number.isFinite(v) && v >= s.scale[0] && v <= s.scale[1] ? { ok: true, value: v, confidence } : bad(`${JSON.stringify(value)} is not a number from ${s.scale[0]} to ${s.scale[1]}`);
|
|
356
|
+
}
|
|
357
|
+
case "draft":
|
|
358
|
+
return typeof value === "string" && value.trim() ? { ok: true, value, confidence: null } : bad("a draft is non-empty text");
|
|
359
|
+
}
|
|
360
|
+
return bad(`unknown kind ${s.kind}`);
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
/** Fills {{name}} with the computed value and confirms every kept value came through unchanged (S-13). */
|
|
364
|
+
export function keepComputed(text, kept) {
|
|
365
|
+
let out = text;
|
|
366
|
+
for (const [n, v] of Object.entries(kept)) out = out.split(`{{${n}}}`).join(String(v));
|
|
367
|
+
const stray = placeholdersOf(out);
|
|
368
|
+
if (stray.length) return { ok: false, why: `the draft left {{${stray[0]}}}, which code does not compute` };
|
|
369
|
+
for (const [n, v] of Object.entries(kept)) if (!out.includes(String(v))) return { ok: false, why: `the computed ${n} (${v}) did not come through unchanged; the code's value stands, so the draft goes to a person` };
|
|
370
|
+
return { ok: true, value: out };
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
/** A judgment from one answer: outcome answer, overruled (code wins) or uncertain. */
|
|
374
|
+
export function settle(s, raw, computed = {}) {
|
|
375
|
+
const t = typed(s, raw);
|
|
376
|
+
const threshold = KINDS[s.kind].judgment ? s.threshold ?? THRESHOLD : null;
|
|
377
|
+
if (!t.ok) return { outcome: "uncertain", answer: null, confidence: t.confidence ?? null, threshold, why: t.why };
|
|
378
|
+
if (s.kind === "draft") {
|
|
379
|
+
if (!s.keep?.length) return { outcome: "answer", answer: t.value, confidence: null, threshold, why: "drafted; a person confirms it" };
|
|
380
|
+
const kept = Object.fromEntries(s.keep.map((n) => [n, computed[n]]));
|
|
381
|
+
const k = keepComputed(t.value, kept);
|
|
382
|
+
return k.ok ? { outcome: "answer", answer: k.value, confidence: null, threshold, kept, why: "drafted around the computed values, which came through unchanged" } : { outcome: "uncertain", answer: null, confidence: null, threshold, why: k.why };
|
|
383
|
+
}
|
|
384
|
+
if (t.confidence === null) return { outcome: "uncertain", answer: null, confidence: null, threshold, why: "the answer gave no confidence" };
|
|
385
|
+
if (t.confidence < threshold) return { outcome: "uncertain", answer: null, confidence: t.confidence, threshold, why: `confidence ${t.confidence} is below the threshold ${threshold}` };
|
|
386
|
+
if (s.check && computed[s.check.equals] !== undefined && computed[s.check.equals] !== t.value)
|
|
387
|
+
return { outcome: "overruled", answer: computed[s.check.equals], confidence: t.confidence, threshold, overruled: { judgment: t.value, code: computed[s.check.equals], check: `equals ${s.check.equals}` },
|
|
388
|
+
why: `the model said ${JSON.stringify(t.value)} and code computed ${JSON.stringify(computed[s.check.equals])}: the code wins` };
|
|
389
|
+
return { outcome: "answer", answer: t.value, confidence: t.confidence, threshold, why: `confidence ${t.confidence} is at or above ${threshold}` };
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
// ---- Models: passed in ----------------------------------------------------------------------------
|
|
393
|
+
|
|
394
|
+
/** A model from an answers file or object: {model, answers: {<key>: {value, confidence}}}. */
|
|
395
|
+
export function answersModel(src) {
|
|
396
|
+
const a = typeof src === "string" ? load(src) : src;
|
|
397
|
+
const all = a.answers ?? a;
|
|
398
|
+
return async (req) => ({ model: a.model ?? "answers-file", answers: Object.fromEntries(Object.keys(req.questions).filter((k) => k in all).map((k) => [k, all[k]])) });
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
/** A model behind a command that reads one request on stdin and prints {model, answers} (the Jev protocol). */
|
|
402
|
+
export function commandModel(cmdline) {
|
|
403
|
+
const argv = cmdline.split(" ").filter(Boolean);
|
|
404
|
+
return (req) => new Promise((ok, fail) => {
|
|
405
|
+
const p = spawn(argv[0], argv.slice(1), { stdio: ["pipe", "pipe", "pipe"] });
|
|
406
|
+
let out = "", err = "";
|
|
407
|
+
p.stdout.on("data", (d) => (out += d)); p.stderr.on("data", (d) => (err += d));
|
|
408
|
+
p.on("error", (e) => fail(new Expected(`could not run the model command ${argv[0]}: ${e.message}`, "failed request")));
|
|
409
|
+
p.on("close", (code) => {
|
|
410
|
+
if (code !== 0) return fail(new Expected(`the model command stopped with status ${code}: ${err.trim().slice(0, 300)}`, "failed request"));
|
|
411
|
+
try { ok(JSON.parse(out)); } catch { fail(new Expected(`the model command did not answer with JSON: ${out.trim().slice(0, 200)}`, "failed request")); }
|
|
412
|
+
});
|
|
413
|
+
p.stdin.end(JSON.stringify(req));
|
|
414
|
+
});
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
// ---- The payload and the run ---------------------------------------------------------------------
|
|
418
|
+
|
|
419
|
+
async function plan(dir, steps, opts) {
|
|
420
|
+
const bad = await checkSteps(steps);
|
|
421
|
+
if (bad.length) throw new Uncaught(`the model steps are broken:\n${bad.join("\n")}`);
|
|
422
|
+
const order = waves(steps);
|
|
423
|
+
const ctx = await context(dir, opts);
|
|
424
|
+
const items = new Map();
|
|
425
|
+
for (const s of steps) items.set(`${s.package}/${s.step}`, await itemsOf(ctx, s));
|
|
426
|
+
const founder = new Map(decisionsOf(dir).decisions.filter((d) => d.source === "file" && d.id.startsWith("judge-")).map((d) => [d.id, d]));
|
|
427
|
+
return { ctx, order, items, founder };
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
/**
|
|
431
|
+
* The router's answer before any model is asked (S-6): what code computed, a placeholder for each
|
|
432
|
+
* pending judgment with the state it receives, and what is withheld or missing. Makes no model call.
|
|
433
|
+
*/
|
|
434
|
+
export async function payload(dir, steps, opts = {}) {
|
|
435
|
+
const { ctx, order, items } = await plan(dir, steps, opts);
|
|
436
|
+
const computed = {}, pending = [], skipped = [], withheld = [];
|
|
437
|
+
for (const wave of order) for (const s of wave) for (const it of items.get(`${s.package}/${s.step}`)) {
|
|
438
|
+
const key = keyOf(s, it.id);
|
|
439
|
+
const c = await computeFor(ctx, s, it);
|
|
440
|
+
if (Object.keys(c).length) computed[key] = c;
|
|
441
|
+
if (s.when && !holds(s.when, c)) { skipped.push({ key, why: `when ${s.when.computed} does not hold` }); continue; }
|
|
442
|
+
const inp = await inputsFor(ctx, s, it);
|
|
443
|
+
if (inp.withheld) { withheld.push({ key, why: inp.withheld }); continue; }
|
|
444
|
+
if (inp.missing) { skipped.push({ key, why: inp.missing }); continue; }
|
|
445
|
+
const state = { ...inp.state };
|
|
446
|
+
for (const a of s.after ?? []) set(state, `steps.${a}`, { pending: keyOf({ ...s, step: a }, it.id) });
|
|
447
|
+
const used = Object.fromEntries(Object.entries(c).filter(([n]) => s.keep?.includes(n) || refsOf(s.question).includes(n)));
|
|
448
|
+
if (Object.keys(used).length) state.computed = used;
|
|
449
|
+
pending.push({ placeholder: key, package: s.package, step: s.step, kind: s.kind, item: it.id, deterministic: !!KINDS[s.kind].judgment,
|
|
450
|
+
waits_on: (s.after ?? []).map((a) => keyOf({ ...s, step: a }, it.id)), question: instructionsFor(s, key), reads: inp.reads, state });
|
|
451
|
+
}
|
|
452
|
+
return { venture: ctx.venture, computed, pending, skipped, withheld };
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
/**
|
|
456
|
+
* Runs the steps: asks the model in one batch per package per wave, packages in parallel, and writes
|
|
457
|
+
* each judgment through the router. Options: ask (a model function), speculate, write (default true),
|
|
458
|
+
* onEvent (computed first, then each judgment as it arrives), hooks.compute (wraps the code's compute),
|
|
459
|
+
* call (the router's call, or a kit's wrapped one), model (the model name to request), fresh (ask again even
|
|
460
|
+
* when a judgment on the same state exists; otherwise it is reused, and a person's answer always is).
|
|
461
|
+
*/
|
|
462
|
+
export async function run(dir, steps, { ask, speculate = false, write = true, fresh = false, onEvent = () => {}, hooks = {}, call = routerCall, workspace, model } = {}) {
|
|
463
|
+
if (typeof ask !== "function") throw new Expected("no model: pass --answers <file> or set MENTURA_MODEL_CMD (payload shows what would be asked)", "no model");
|
|
464
|
+
const t0 = Date.now();
|
|
465
|
+
const { ctx, order, items, founder } = await plan(dir, steps, { call, workspace });
|
|
466
|
+
const out = { venture: ctx.venture, speculate, judgments: [], reused: [], uncertain: [], discarded: [], skipped: [], waiting: [], withheld: [], errors: [], uncaught: [] };
|
|
467
|
+
const answers = new Map(); // key -> judgment, for steps that read an earlier step
|
|
468
|
+
|
|
469
|
+
// Code computes every step's values; judgments that need them wait, the rest may be asked meanwhile.
|
|
470
|
+
const all = steps.flatMap((s) => items.get(`${s.package}/${s.step}`).map((it) => [s, it]));
|
|
471
|
+
const computeAll = async () => Object.fromEntries(await Promise.all(all.map(async ([s, it]) => [keyOf(s, it.id), await computeFor(ctx, s, it)])));
|
|
472
|
+
let computed = null;
|
|
473
|
+
const computeP = Promise.resolve(hooks.compute ? hooks.compute(computeAll) : computeAll());
|
|
474
|
+
computeP.then((c) => { computed = c; onEvent({ event: "computed", computed: Object.fromEntries(Object.entries(c).filter(([, v]) => Object.keys(v).length)) }); }, () => {});
|
|
475
|
+
|
|
476
|
+
let queue = Promise.resolve();
|
|
477
|
+
const serial = (f) => (queue = queue.then(f, f));
|
|
478
|
+
const writeJudgment = (s, it, j, reads, files, by) => serial(async () => {
|
|
479
|
+
const id = judgmentId(s, it.id);
|
|
480
|
+
const rec = { id, package: s.package, step: s.step, kind: s.kind, item: it.id, question: s.question, reads, outcome: j.outcome, answer: j.answer, confidence: j.confidence ?? null,
|
|
481
|
+
threshold: j.threshold ?? null, why: j.why, ...(j.kept ? { kept: j.kept } : {}), ...(j.overruled ? { overruled: j.overruled } : {}), ...(j.decision ? { decision: j.decision } : {}),
|
|
482
|
+
by, ...(j.state_sha256 ? { state_sha256: j.state_sha256 } : {}), asked_at: new Date().toISOString().replace(/\.\d+Z$/, "Z") };
|
|
483
|
+
const person = by.kind === "person";
|
|
484
|
+
const w = await call({ venture: ctx.venture, workspace: ctx.workspace, route: `knowledge/judgment/${id}`, action: "write", input: rec, actor: person ? "person" : "agent", actor_id: person ? undefined : ACTOR,
|
|
485
|
+
provenance: { derived_from: files, ...(j.decision ? { decision: j.decision } : {}), note: `use model: ${s.package}/${s.step}${person ? `, answered by ${by.name}` : ""}`, ...(person ? {} : { agent: by.model ? { model: by.model } : {} }) } });
|
|
486
|
+
const o = outcomeOf(w);
|
|
487
|
+
if (o.outcome !== "answer") throw new Expected(`${id}: ${o.error}`, o.kind);
|
|
488
|
+
return w.result.written;
|
|
489
|
+
});
|
|
490
|
+
|
|
491
|
+
for (const wave of order) {
|
|
492
|
+
const needNow = wave.some((s) => usesComputed(s) || (s.when && !speculate));
|
|
493
|
+
if (needNow) await computeP;
|
|
494
|
+
const batches = new Map(); // package -> { req, qs }
|
|
495
|
+
for (const s of wave) for (const it of items.get(`${s.package}/${s.step}`)) {
|
|
496
|
+
const key = keyOf(s, it.id), jid = judgmentId(s, it.id);
|
|
497
|
+
const d = founder.get(`judge-${jid}`);
|
|
498
|
+
const prior = existsSync(join(dir, JUDGMENTS, `${jid}.json`)) ? load(join(dir, JUDGMENTS, `${jid}.json`)) : null;
|
|
499
|
+
const reuse = (why) => { answers.set(key, { outcome: prior.outcome, answer: prior.answer, file: `${JUDGMENTS}/${jid}.json` }); out.reused.push({ key, id: jid, answer: prior.answer, why }); onEvent({ event: "judgment", key, outcome: prior.outcome, answer: prior.answer, reused: true }); };
|
|
500
|
+
if (prior?.by?.kind === "person" && prior.outcome !== "uncertain") { reuse(`answered by ${prior.by.name ?? "a person"}`); continue; }
|
|
501
|
+
const own = prior && prior.outcome !== "uncertain" ? confirmedBy(dir, `${JUDGMENTS}/${jid}.json`) : null;
|
|
502
|
+
if (own) { reuse(`confirmed by ${own}`); continue; }
|
|
503
|
+
if (d?.status === "decided") {
|
|
504
|
+
// The founder answered an uncertain judgment: their answer is the judgment, written as theirs.
|
|
505
|
+
const value = s.kind === "score" ? Number(d.answer) : s.kind === "noul" ? d.answer === "true" || d.answer === "yes" : d.answer;
|
|
506
|
+
const j = { outcome: "answer", answer: value, confidence: null, threshold: null, why: `answered by ${d.provenance?.approved_by ?? "a person"} in decision ${d.id}`, decision: d.id };
|
|
507
|
+
try { const inp = await inputsFor(ctx, s, it); const file = await writeJudgment(s, it, j, inp.reads ?? [], [...(inp.files ?? []), d.file].filter(Boolean), { kind: "person", name: d.provenance?.approved_by ?? "person" });
|
|
508
|
+
answers.set(key, { ...j, file }); out.judgments.push({ key, id: jid, ...j, by: "person", written: file }); onEvent({ event: "judgment", key, ...j, by: "person" }); }
|
|
509
|
+
catch (e) { (e.expected ? out.errors : out.uncaught).push({ key, error: e.message }); }
|
|
510
|
+
continue;
|
|
511
|
+
}
|
|
512
|
+
if (d && d.status !== "withdrawn") { out.waiting.push({ key, decision: d.id, why: "an earlier answer was uncertain and waits on the founder" }); continue; }
|
|
513
|
+
if (computed && s.when && !holds(s.when, computed[key])) { out.skipped.push({ key, why: `when ${s.when.computed} does not hold` }); continue; }
|
|
514
|
+
const blocked = (s.after ?? []).map((a) => keyOf({ ...s, step: a }, it.id)).find((k) => !answers.has(k));
|
|
515
|
+
if (blocked) { out.skipped.push({ key, why: `waits on ${blocked}, which has no answer yet` }); continue; }
|
|
516
|
+
let inp;
|
|
517
|
+
try { inp = await inputsFor(ctx, s, it); } catch (e) { (e.expected ? out.errors : out.uncaught).push({ key, error: e.message }); continue; }
|
|
518
|
+
if (inp.withheld) { out.withheld.push({ key, why: inp.withheld }); continue; }
|
|
519
|
+
if (inp.missing) { out.skipped.push({ key, why: inp.missing }); continue; }
|
|
520
|
+
const state = { ...inp.state };
|
|
521
|
+
const reads = [...inp.reads], files = [...inp.files];
|
|
522
|
+
for (const a of s.after ?? []) { const prior = answers.get(keyOf({ ...s, step: a }, it.id)); set(state, `steps.${a}`, prior.answer); reads.push(`${prior.file}#answer`); files.push(prior.file); }
|
|
523
|
+
if (usesComputed(s)) {
|
|
524
|
+
const c = computed[key];
|
|
525
|
+
const used = Object.fromEntries(Object.entries(c).filter(([n]) => s.keep?.includes(n) || refsOf(s.question).includes(n)));
|
|
526
|
+
const missing = Object.entries(used).find(([, v]) => v === undefined);
|
|
527
|
+
if (missing) { out.skipped.push({ key, why: `code has no value for ${missing[0]}` }); continue; }
|
|
528
|
+
state.computed = used;
|
|
529
|
+
}
|
|
530
|
+
const sha = createHash("sha256").update(JSON.stringify({ question: s.question, state })).digest("hex");
|
|
531
|
+
if (!fresh && prior?.by?.kind === "model" && prior.outcome !== "uncertain" && prior.state_sha256 === sha) { reuse("asked before on the same state"); continue; }
|
|
532
|
+
if (JSON.stringify(state).length > (s.budget ?? BUDGET)) { out.errors.push({ key, error: `the state is over the step's context budget of ${s.budget ?? BUDGET} characters: ask a narrower question` }); continue; }
|
|
533
|
+
const b = batches.get(s.package) ?? batches.set(s.package, { req: { model: model ?? "default", state: {}, questions: {} }, qs: [] }).get(s.package);
|
|
534
|
+
b.req.state[key] = state;
|
|
535
|
+
b.req.questions[key] = questionFor(s, key);
|
|
536
|
+
b.qs.push({ key, s, it, reads, files: [...new Set(files)], sha, speculative: !!(s.when && !computed) });
|
|
537
|
+
}
|
|
538
|
+
// One request per package, all at once; one failing or uncertain answer never sinks the rest.
|
|
539
|
+
const settled = await Promise.allSettled([...batches.values()].map(async ({ req, qs }) => {
|
|
540
|
+
try { assertJson(req.state); checkRequest(req); } catch (e) { throw e.expected ? new Uncaught(`the assembled request is not plain JSON: ${e.message}`) : e; }
|
|
541
|
+
let res;
|
|
542
|
+
try { res = await ask(req); }
|
|
543
|
+
catch (e) { for (const q of qs) out.errors.push({ key: q.key, error: e.message ?? String(e) }); return; }
|
|
544
|
+
const c = await computeP;
|
|
545
|
+
await Promise.all(qs.map(async (q) => {
|
|
546
|
+
const cq = c[q.key] ?? {};
|
|
547
|
+
if (q.s.when && !holds(q.s.when, cq)) { out.discarded.push({ key: q.key, why: `asked while code computed; when ${q.s.when.computed} does not hold, so the answer is not used` }); onEvent({ event: "discarded", key: q.key }); return; }
|
|
548
|
+
const j = { ...settle(q.s, res?.answers?.[q.key], cq), state_sha256: q.sha };
|
|
549
|
+
const jid = judgmentId(q.s, q.it.id);
|
|
550
|
+
if (j.outcome === "uncertain") j.decision = `judge-${jid}`;
|
|
551
|
+
try {
|
|
552
|
+
const file = await (write ? writeJudgment(q.s, q.it, j, q.reads, q.files, { kind: "model", ...(res?.model ? { model: String(res.model) } : {}) }) : null);
|
|
553
|
+
if (j.outcome === "uncertain" && write) await serial(async () => {
|
|
554
|
+
const p = propose(dir, { id: j.decision, at: "knowledge", question: `${q.s.package}/${q.s.step}${q.it.id ? ` for ${q.it.id}` : ""}: ${q.s.question} (the model was not sure: ${j.why})`, layer: "knowledge", boundary: "fail-open",
|
|
555
|
+
decides: { record: "judgment", path: `${JUDGMENTS}/${jid}.json` }, ...(q.s.options ? { options: q.s.options } : {}), note: `Answer with mentura decide ${j.decision} --by <name> --answer <answer>; the next run writes it as the judgment. The model read ${q.reads.join(", ")}.` },
|
|
556
|
+
{ agentId: ACTOR });
|
|
557
|
+
if (!p.ok) throw new Expected(`could not route ${jid} to the founder: ${p.error}`, "refused");
|
|
558
|
+
});
|
|
559
|
+
if (j.outcome !== "uncertain") answers.set(q.key, { ...j, file });
|
|
560
|
+
out.judgments.push({ key: q.key, id: jid, outcome: j.outcome, answer: j.answer, confidence: j.confidence, why: j.why, by: "model", ...(file ? { written: file } : {}) });
|
|
561
|
+
if (j.outcome === "uncertain") out.uncertain.push({ key: q.key, decision: j.decision, why: j.why });
|
|
562
|
+
onEvent({ event: "judgment", key: q.key, outcome: j.outcome, answer: j.answer, confidence: j.confidence, why: j.why });
|
|
563
|
+
} catch (e) { (e.expected ? out.errors : out.uncaught).push({ key: q.key, error: e.message }); }
|
|
564
|
+
}));
|
|
565
|
+
}));
|
|
566
|
+
for (const r of settled) if (r.status === "rejected") out.uncaught.push({ error: r.reason?.message ?? String(r.reason) });
|
|
567
|
+
}
|
|
568
|
+
out.computed = Object.fromEntries(Object.entries(await computeP).filter(([, v]) => Object.keys(v).length));
|
|
569
|
+
await queue;
|
|
570
|
+
out.ms = Date.now() - t0;
|
|
571
|
+
onEvent({ event: "done", judgments: out.judgments.length, uncertain: out.uncertain.length, discarded: out.discarded.length, uncaught: out.uncaught.length });
|
|
572
|
+
return out;
|
|
573
|
+
}
|
|
574
|
+
|
|
575
|
+
// ---- Provenance downstream of a model (S-9, S-10) -------------------------------------------------
|
|
576
|
+
|
|
577
|
+
const WRITES = new Set(["write", "draft", "accept", "baseline", "move"]);
|
|
578
|
+
/** The person who confirmed a file's current content, or null. */
|
|
579
|
+
function confirmedBy(dir, file) {
|
|
580
|
+
const es = provEntries(dir).filter((e) => e.entity === file);
|
|
581
|
+
const last = es.filter((e) => WRITES.has(e.activity)).at(-1);
|
|
582
|
+
const c = last && es.slice(es.indexOf(last) + 1).find((e) => e.activity === "confirm" && e.agent.kind === "person");
|
|
583
|
+
return c ? c.agent.id ?? "a person" : null;
|
|
584
|
+
}
|
|
585
|
+
const isModelJudgment = (dir, f) => {
|
|
586
|
+
if (!glob(`${JUDGMENTS}/*.json`).test(f) || !existsSync(join(dir, f))) return false;
|
|
587
|
+
try { return load(join(dir, f)).by?.kind === "model"; } catch { return false; }
|
|
588
|
+
};
|
|
589
|
+
|
|
590
|
+
/**
|
|
591
|
+
* Whose a file is: "person" (a person wrote, accepted or confirmed it), "agent", or "clear" (an agent's,
|
|
592
|
+
* and its checks pass). model is true for a model's judgment; downstream_of_model lists the model
|
|
593
|
+
* outputs an agent's record was derived from. A person's record a step read stays the person's.
|
|
594
|
+
*/
|
|
595
|
+
export async function provenanceOf(dir, file, { checks = false, call = routerCall, workspace, seen = new Set() } = {}) {
|
|
596
|
+
seen.add(file);
|
|
597
|
+
const es = provEntries(dir).filter((e) => e.entity === file);
|
|
598
|
+
const writes = es.filter((e) => WRITES.has(e.activity));
|
|
599
|
+
const last = writes.at(-1);
|
|
600
|
+
if (!last) return { entity: file, status: "unlogged", model: false, downstream_of_model: [], why: "not in the provenance log" };
|
|
601
|
+
const confirmed = es.slice(es.indexOf(last) + 1).find((e) => e.activity === "confirm" && e.agent.kind === "person");
|
|
602
|
+
const model = isModelJudgment(dir, file) && last.agent.kind === "agent";
|
|
603
|
+
if (last.agent.kind === "person" || confirmed) return { entity: file, status: "person", model: false, downstream_of_model: [],
|
|
604
|
+
why: confirmed ? `confirmed by ${confirmed.agent.id ?? "a person"}` : `${last.activity === "accept" ? "accepted" : "written"} by ${last.agent.id ?? "a person"}` };
|
|
605
|
+
const upstream = [];
|
|
606
|
+
for (const d of (last.derived_from ?? []).map((x) => x.split("#")[0]).filter((x) => !seen.has(x) && existsSync(join(dir, x)))) {
|
|
607
|
+
const u = await provenanceOf(dir, d, { seen });
|
|
608
|
+
if (u.model) upstream.push(d); else upstream.push(...u.downstream_of_model);
|
|
609
|
+
}
|
|
610
|
+
const res = { entity: file, status: "agent", model, downstream_of_model: [...new Set(upstream)], why: model ? `a model's judgment (${last.note ?? "use model"}); a person confirms it` : upstream.length ? "an agent wrote it from a model's output" : "an agent wrote it" };
|
|
611
|
+
if (checks && !(model && load(join(dir, file)).kind === "draft") && last.route) {
|
|
612
|
+
const venture = load(join(dir, "venture.json")).id;
|
|
613
|
+
const r = await call({ venture, workspace: workspace ?? dirname(dir), route: last.route, action: "check" });
|
|
614
|
+
if (r.ok && r.result?.length) return { ...res, status: "clear", why: `${res.why}; its checks pass (${r.result.map((x) => x.check).join(", ")})` };
|
|
615
|
+
}
|
|
616
|
+
return res;
|
|
617
|
+
}
|
|
618
|
+
|
|
619
|
+
/** A person confirms a judgment as theirs: the content is unchanged and the log records the confirmation. */
|
|
620
|
+
export function confirm(dir, id, { by, note } = {}) {
|
|
621
|
+
if (!by) throw new Expected("confirming needs the person's name (--by)", "refused");
|
|
622
|
+
const file = `${JUDGMENTS}/${id}.json`;
|
|
623
|
+
if (!existsSync(join(dir, file))) throw new Expected(`no judgment ${id}`, "missing record");
|
|
624
|
+
const j = load(join(dir, file));
|
|
625
|
+
if (j.outcome === "uncertain") throw new Expected(`${id} is uncertain: answer its decision ${j.decision} instead`, "refused");
|
|
626
|
+
const e = prov(dir, { entity: file, route: `knowledge/judgment/${id}`, activity: "confirm", agent: agentFromEnv("person", "cli", { id: by }), note: note ?? `confirmed ${j.package}/${j.step}${j.item ? ` for ${j.item}` : ""}` });
|
|
627
|
+
return { confirmed: id, by, provenance: e.hash };
|
|
628
|
+
}
|
|
629
|
+
|
|
630
|
+
// ---- The waterfall: the venture's real order (F-10) ------------------------------------------------
|
|
631
|
+
|
|
632
|
+
/** Records in order: each after the records it reads first (packages/catalog.json records.after). A cycle is uncaught. */
|
|
633
|
+
export async function waterfall(dir, { graph } = {}) {
|
|
634
|
+
const after = graph ?? Object.fromEntries(Object.entries(loadPackages(repo).records).map(([k, v]) => [k, v.after ?? []]));
|
|
635
|
+
const types = [...new Set([...Object.keys(after), ...Object.values(after).flat()])].sort();
|
|
636
|
+
const deps = (t) => after[t] ?? [];
|
|
637
|
+
const order = [], done = new Set();
|
|
638
|
+
while (done.size < types.length) {
|
|
639
|
+
const wave = types.filter((t) => !done.has(t) && deps(t).every((d) => done.has(d)));
|
|
640
|
+
if (!wave.length) {
|
|
641
|
+
const left = types.filter((t) => !done.has(t));
|
|
642
|
+
const cycle = [left[0]]; let at = left[0];
|
|
643
|
+
for (;;) { at = deps(at).find((d) => !done.has(d)); if (cycle.includes(at)) { cycle.push(at); break; } cycle.push(at); }
|
|
644
|
+
throw new Uncaught(`a cycle in the record order: ${cycle.slice(cycle.indexOf(at)).join(" -> ")}`);
|
|
645
|
+
}
|
|
646
|
+
wave.forEach((t) => done.add(t));
|
|
647
|
+
order.push(wave);
|
|
648
|
+
}
|
|
649
|
+
let status = new Map();
|
|
650
|
+
if (dir) {
|
|
651
|
+
const { packages } = await import("./packages.mjs");
|
|
652
|
+
const p = packages(dir);
|
|
653
|
+
status = new Map(p.core.flatMap((c) => c.records.map((r) => [r.record, r.status])));
|
|
654
|
+
}
|
|
655
|
+
const st = (t) => status.get(t) ?? "missing";
|
|
656
|
+
const records = order.flatMap((w, i) => w.map((t) => {
|
|
657
|
+
const waits = deps(t).filter((d) => st(d) !== "confirmed");
|
|
658
|
+
return { record: t, wave: i, status: st(t), after: deps(t), moves: st(t) === "confirmed" ? "done" : waits.length ? "waits" : "now", ...(waits.length && st(t) !== "confirmed" ? { waits_on: waits } : {}) };
|
|
659
|
+
}));
|
|
660
|
+
return { order, records, now: records.filter((r) => r.moves === "now").map((r) => r.record) };
|
|
661
|
+
}
|
|
662
|
+
|
|
663
|
+
// ---- Command line ---------------------------------------------------------------------------------
|
|
664
|
+
|
|
665
|
+
if (import.meta.url === `file://${process.argv[1]}`) {
|
|
666
|
+
const argv = process.argv.slice(2);
|
|
667
|
+
const opt = (n) => { const i = argv.indexOf(`--${n}`); return i >= 0 ? argv[i + 1] : undefined; };
|
|
668
|
+
const flag = (n) => argv.includes(`--${n}`);
|
|
669
|
+
const [dirArg, sub, arg] = argv.filter((a, i) => !a.startsWith("--") && !(i > 0 && argv[i - 1].startsWith("--") && !["json", "speculate", "stream", "checks", "fresh"].includes(argv[i - 1].slice(2))));
|
|
670
|
+
const usage = () => { console.error(readFileSync(new URL(import.meta.url), "utf8").split("\n").filter((l) => l.startsWith("// node ")).map((l) => l.slice(5)).join("\n")); process.exit(2); };
|
|
671
|
+
if (!dirArg || !sub || !existsSync(join(resolvePath(dirArg), "venture.json"))) usage();
|
|
672
|
+
const dir = resolvePath(dirArg);
|
|
673
|
+
const json = flag("json");
|
|
674
|
+
const print = (x) => console.log(typeof x === "string" ? x : JSON.stringify(x, null, 2));
|
|
675
|
+
const pick = () => stepsOf({ packages: opt("package") ? [opt("package")] : undefined, kit: opt("kit") ? resolvePath(opt("kit")) : undefined });
|
|
676
|
+
const kitCall = async () => {
|
|
677
|
+
if (!opt("kit")) return routerCall;
|
|
678
|
+
const { wrap } = await import("../interface/kit-boundary.mjs");
|
|
679
|
+
return wrap(load(resolvePath(opt("kit"))));
|
|
680
|
+
};
|
|
681
|
+
try {
|
|
682
|
+
if (sub === "check") {
|
|
683
|
+
const errors = await checkSteps(pick(), { dir, call: await kitCall() });
|
|
684
|
+
if (errors.length) { console.error(errors.join("\n")); process.exit(1); }
|
|
685
|
+
print(`${pick().length} model steps check: each asks one typed question about named fields, in order, and no private field reaches a model.`);
|
|
686
|
+
} else if (sub === "payload") {
|
|
687
|
+
const p = await payload(dir, pick(), { call: await kitCall() });
|
|
688
|
+
print(json ? p : [`${p.pending.length} judgments pending, ${Object.keys(p.computed).length} computed by code, ${p.withheld.length} withheld, ${p.skipped.length} skipped.`,
|
|
689
|
+
...p.pending.map((x) => ` ${x.placeholder} ${x.kind}${x.waits_on.length ? ` waits on ${x.waits_on.join(", ")}` : ""}`), ...p.withheld.map((x) => ` withheld ${x.key}: ${x.why}`)].join("\n"));
|
|
690
|
+
} else if (sub === "run") {
|
|
691
|
+
const ask = opt("answers") ? answersModel(resolvePath(opt("answers"))) : process.env.MENTURA_MODEL_CMD ? commandModel(process.env.MENTURA_MODEL_CMD) : null;
|
|
692
|
+
const stream = flag("stream");
|
|
693
|
+
const r = await run(dir, pick(), { ask, speculate: flag("speculate"), fresh: flag("fresh"), call: await kitCall(), onEvent: stream ? (e) => console.log(JSON.stringify(e)) : undefined });
|
|
694
|
+
if (!stream) print(json ? r : [
|
|
695
|
+
...r.reused.map((j) => `${j.key} reused ${JSON.stringify(j.answer)} (${j.why})`),
|
|
696
|
+
...r.judgments.map((j) => `${j.key} ${j.outcome}${j.answer !== null ? ` ${JSON.stringify(j.answer)}` : ""}${j.confidence !== null && j.confidence !== undefined ? ` (${j.confidence})` : ""}`),
|
|
697
|
+
...r.uncertain.map((u) => `uncertain ${u.key}: ${u.why}; the founder answers decision ${u.decision}`),
|
|
698
|
+
...r.waiting.map((w) => `waiting ${w.key}: decision ${w.decision}`), ...r.discarded.map((d) => `discarded ${d.key}`), ...r.skipped.map((s) => `skipped ${s.key}: ${s.why}`),
|
|
699
|
+
...r.withheld.map((w) => `withheld ${w.key}: ${w.why}`), ...r.errors.map((e) => `error ${e.key}: ${e.error}`), ...r.uncaught.map((e) => `UNCAUGHT ${e.error}`)].join("\n") || "nothing to ask");
|
|
700
|
+
if (r.uncaught.length) process.exit(1);
|
|
701
|
+
} else if (sub === "waterfall") {
|
|
702
|
+
const w = await waterfall(dir);
|
|
703
|
+
print(json ? w : w.order.map((wave, i) => `${i}: ${wave.map((t) => { const r = w.records.find((x) => x.record === t); return `${t} (${r.moves}${r.waits_on ? ` on ${r.waits_on.join(", ")}` : ""})`; }).join(", ")}`).join("\n"));
|
|
704
|
+
} else if (sub === "provenance") {
|
|
705
|
+
if (!arg) usage();
|
|
706
|
+
const p = await provenanceOf(dir, arg, { checks: flag("checks") });
|
|
707
|
+
print(json ? p : `${p.entity}: ${p.status}${p.model ? " (a model's judgment)" : ""}${p.downstream_of_model.length ? ` (downstream of ${p.downstream_of_model.join(", ")})` : ""}: ${p.why}`);
|
|
708
|
+
} else if (sub === "confirm") {
|
|
709
|
+
if (!arg) usage();
|
|
710
|
+
print(confirm(dir, arg, { by: opt("by"), note: opt("note") }));
|
|
711
|
+
} else usage();
|
|
712
|
+
} catch (e) {
|
|
713
|
+
const o = outcomeOf(e);
|
|
714
|
+
console.error(o.outcome === "uncaught" ? `uncaught: ${e.message}` : e.message);
|
|
715
|
+
process.exit(1);
|
|
716
|
+
}
|
|
717
|
+
}
|