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,117 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// The sitemap planner (EE-064): site presence in reverse. Site presence reads a site and rates it;
|
|
3
|
+
// the planner reads the venture's records and plans the site: which information is shown where,
|
|
4
|
+
// how (the MDX component), when (while raising, hiring...) and for whom, with the reason for each.
|
|
5
|
+
// The product decides the audience: the people a product is offered_to are the site's customers,
|
|
6
|
+
// even when they are partners serving customers of their own (B2B2C). Their customer types set what
|
|
7
|
+
// they look for (matrix.json customer_types); the venture's facts weigh the other audiences.
|
|
8
|
+
//
|
|
9
|
+
// Pages follow topics, not audiences (audience-based navigation tends to hurt usability, NN/g), with
|
|
10
|
+
// one exception: an audience with content of its own (candidates, investors, contributors) gets its
|
|
11
|
+
// own page, labelled for it. The plan is a marketing record (operations/marketing/sitemap.json). It
|
|
12
|
+
// names, for each section, the internal record it draws on and the public variant a marketing agent
|
|
13
|
+
// writes (operations/marketing/content/<page>/<item>.mdx), so nothing internal is published as is
|
|
14
|
+
// (check-public.mjs). Planning the sitemap and designing the site are separate agents: this plan is
|
|
15
|
+
// part of the marketing package handed to engineering, which builds the site (EE-065).
|
|
16
|
+
//
|
|
17
|
+
// node plan.mjs <venture> [--raising] [--hiring] [--contributors] [--write] [--json]
|
|
18
|
+
|
|
19
|
+
import { existsSync, readdirSync, readFileSync, writeFileSync, mkdirSync } from "node:fs";
|
|
20
|
+
import { dirname, join } from "node:path";
|
|
21
|
+
import { customerNeeds, factsOf, MATRIX, needs, typesOf, weights } from "./presence.mjs";
|
|
22
|
+
|
|
23
|
+
const load = (p) => JSON.parse(readFileSync(p, "utf8"));
|
|
24
|
+
export const SITEMAP = "operations/marketing/sitemap.json";
|
|
25
|
+
export const CONTENT = "operations/marketing/content";
|
|
26
|
+
const LABELS = MATRIX.page_labels;
|
|
27
|
+
const RANK = Object.fromEntries(Object.entries(MATRIX.prominence).map(([k, v]) => [k, v.rank]));
|
|
28
|
+
|
|
29
|
+
// Which internal record an item draws on, as a venture-relative file where one exists.
|
|
30
|
+
const RECORD_FILE = { venture: "venture.json", icp: "purpose/icp.json", "validation-problem": "purpose/validation/problem.json", "validation-solution": "purpose/validation/solution.json",
|
|
31
|
+
"validation-pricing": "purpose/validation/pricing.json", "validation-go-to-market": "purpose/validation/go-to-market.json", "validation-team": "purpose/validation/team.json",
|
|
32
|
+
telos: "purpose/telos.json", alignment: "purpose/alignment.json", "team-plan": "operations/team-plan.json", "business-models": "purpose/business-models.json", "publication-policy": "governance/publication-policy.json", evidence: "evidence/" };
|
|
33
|
+
|
|
34
|
+
/** The offerings: each product, what kind of offering it is, whom it is offered to and whom it serves through them. */
|
|
35
|
+
export function offeringsOf(dir) {
|
|
36
|
+
const icp = existsSync(join(dir, "purpose/icp.json")) ? load(join(dir, "purpose/icp.json")) : { profiles: [] };
|
|
37
|
+
const profiles = new Map(icp.profiles.map((p) => [p.id, p]));
|
|
38
|
+
const primary = icp.profiles.find((p) => p.primary) ?? icp.profiles[0];
|
|
39
|
+
const pdir = join(dir, "products");
|
|
40
|
+
const products = existsSync(pdir) ? readdirSync(pdir).filter((f) => f.endsWith(".json")).map((f) => load(join(pdir, f))).filter((p) => !["retired", "archived", "idea"].includes(p.status)) : [];
|
|
41
|
+
return { profiles, products: products.map((p) => {
|
|
42
|
+
// Who it is offered to: the product says so; otherwise the profiles others are reached through
|
|
43
|
+
// (partners, in a B2B2C venture); otherwise the primary profile.
|
|
44
|
+
const channel = icp.profiles.filter((x) => icp.profiles.some((y) => y.reached_through?.includes(x.id)));
|
|
45
|
+
const to = p.offered_to ?? (channel.length ? channel.map((x) => x.id) : primary ? [primary.id] : []);
|
|
46
|
+
const serves = p.serves ?? icp.profiles.filter((y) => y.reached_through?.some((r) => to.includes(r))).map((y) => y.id);
|
|
47
|
+
return { id: p.id, name: p.name, offering: p.offering ?? "product", status: p.status ?? null, offered_to: to, serves, from: p.offered_to ? "product record" : channel.length ? "icp reached_through" : primary ? "primary ICP profile" : "nothing: no ICP yet" };
|
|
48
|
+
}) };
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export function plan(dir, { flags = {}, answers = null } = {}) {
|
|
52
|
+
const venture = load(join(dir, "venture.json"));
|
|
53
|
+
const facts = factsOf(dir, flags);
|
|
54
|
+
const { profiles, products } = offeringsOf(dir);
|
|
55
|
+
// The site's customers are the people the products are offered to. Their customer types come from
|
|
56
|
+
// the profile when the founder set one, else from Jev's answers, else from the matrix's keywords.
|
|
57
|
+
const toIds = [...new Set(products.flatMap((p) => p.offered_to))];
|
|
58
|
+
const to = toIds.map((id) => profiles.get(id)).filter(Boolean);
|
|
59
|
+
const text = to.map((p) => [p.who, p.short, ...(p.traits ?? [])].filter(Boolean).join(". ")).join(" | ");
|
|
60
|
+
const set = to.map((p) => p.customer_type).filter((t) => MATRIX.customer_types.types[t]);
|
|
61
|
+
const types = set.length ? set.map((id) => ({ id, label: MATRIX.customer_types.types[id].label, p: 1 })) : typesOf({ text }, answers);
|
|
62
|
+
const partnerLed = to.some((p) => profiles.size && [...profiles.values()].some((y) => y.reached_through?.includes(p.id)));
|
|
63
|
+
const w = weights(facts), need = needs(w, customerNeeds(types));
|
|
64
|
+
|
|
65
|
+
const pages = new Map();
|
|
66
|
+
const page = (type, level, extra = {}) => {
|
|
67
|
+
const path = type === "home" ? "/" : `/${type}`;
|
|
68
|
+
if (!pages.has(path)) pages.set(path, { path, parent: type === "home" ? null : "/", label: LABELS[type] ?? type, type, level, sections: [], ...extra });
|
|
69
|
+
const pg = pages.get(path);
|
|
70
|
+
if (RANK[level] > RANK[pg.level]) pg.level = level;
|
|
71
|
+
return pg;
|
|
72
|
+
};
|
|
73
|
+
page("home", "home");
|
|
74
|
+
const omitted = [];
|
|
75
|
+
for (const [id, it] of Object.entries(MATRIX.items)) {
|
|
76
|
+
const n = need[id];
|
|
77
|
+
const top = n.evidence[0];
|
|
78
|
+
const when = Object.entries(MATRIX.when).find(([k]) => (MATRIX.audiences[top.audience].weight?.[k] != null))?.[1] ?? null;
|
|
79
|
+
if (n.need < MATRIX.prominence.linked.at_least) { omitted.push({ item: id, label: it.label, why: `${MATRIX.audiences[top.audience].label} are not a priority now (need ${n.need})`, ...(when ? { when: `show it ${when}` } : {}) }); continue; }
|
|
80
|
+
// Where: the home page when the need says so and the item belongs there; else its own page,
|
|
81
|
+
// in the navigation or the footer as the need says.
|
|
82
|
+
const own = it.belongs.find((b) => b !== "home");
|
|
83
|
+
const target = n.required === "home" && it.belongs.includes("home") ? page("home", "home") : page(own ?? "about", n.required === "home" ? "nav" : n.required === "anywhere" ? "linked" : n.required);
|
|
84
|
+
const record = it.record;
|
|
85
|
+
target.sections.push({ item: id, label: it.label, component: it.component, need: n.need, required: n.required, for: n.for.length ? n.for : [top.audience],
|
|
86
|
+
why: n.evidence.filter((e, k) => k === 0 || e.need >= 0.2).map((e) => `${MATRIX.audiences[e.audience].label}: ${e.why}`),
|
|
87
|
+
source: { record, file: RECORD_FILE[record] ?? null, exists: RECORD_FILE[record] ? existsSync(join(dir, RECORD_FILE[record])) : false },
|
|
88
|
+
variant: `${CONTENT}/${target.type}/${id}.mdx`, ...(when && !n.evidence.some((e) => e.audience !== top.audience && e.need >= MATRIX.prominence.linked.at_least) ? { when } : {}) });
|
|
89
|
+
}
|
|
90
|
+
// A product page for each product beyond the first, under /product, when there are several.
|
|
91
|
+
if (products.length > 1) for (const p of products) pages.set(`/product/${p.id}`, { path: `/product/${p.id}`, parent: pages.has("/product") ? "/product" : "/", label: p.name, type: "product", level: "anywhere", sections: [
|
|
92
|
+
{ item: "how", label: `How ${p.name} works`, component: "HowItWorks", need: need.how.need, required: "anywhere", for: ["customers"], why: [`${p.offering} offered to ${p.offered_to.join(", ")}`], source: { record: "product", file: `products/${p.id}.json`, exists: true }, variant: `${CONTENT}/product/${p.id}.mdx` }] });
|
|
93
|
+
for (const pg of pages.values()) pg.sections.sort((a, b) => b.need - a.need);
|
|
94
|
+
const list = [...pages.values()].sort((a, b) => RANK[b.level] - RANK[a.level] || b.sections.reduce((s, x) => s + x.need, 0) - a.sections.reduce((s, x) => s + x.need, 0));
|
|
95
|
+
return { version: MATRIX.version, venture: venture.id, name: venture.name, phase: venture.phase, planned_at: new Date().toISOString().replace(/\.\d+Z$/, "Z"), planned_by: answers ? "jev" : "conventions", facts,
|
|
96
|
+
offerings: products, audience: { offered_to: to.map((p) => ({ id: p.id, who: p.who })), partner_led: partnerLed, types, weights: w,
|
|
97
|
+
note: partnerLed ? "The products are offered to partners who serve customers of their own: the partners are this site's customers, and their customers appear as who it ultimately serves." : "The site's customers are the profiles the products are offered to." },
|
|
98
|
+
pages: list, omitted,
|
|
99
|
+
handoff: { to: "engineering", note: "Part of the marketing package: engineering builds the site from this plan, the content variants and the brand; it does not decide what goes where (EE-065).", variants: list.flatMap((p) => p.sections.map((s) => s.variant)) } };
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
if (import.meta.url === `file://${process.argv[1]}`) {
|
|
103
|
+
const dir = process.argv[2];
|
|
104
|
+
if (!dir || !existsSync(join(dir, "venture.json"))) { console.error("usage: node plan.mjs <venture> [--raising] [--hiring] [--contributors] [--write] [--json]"); process.exit(2); }
|
|
105
|
+
const has = (n) => process.argv.includes(`--${n}`);
|
|
106
|
+
const r = plan(dir, { flags: Object.fromEntries(["raising", "hiring", "contributors"].filter(has).map((k) => [k, true])) });
|
|
107
|
+
if (has("write")) { mkdirSync(join(dir, dirname(SITEMAP)), { recursive: true }); writeFileSync(join(dir, SITEMAP), JSON.stringify(r, null, 2) + "\n"); }
|
|
108
|
+
if (has("json")) console.log(JSON.stringify(r, null, 2));
|
|
109
|
+
else {
|
|
110
|
+
console.log(`${r.name}: site planned for ${r.audience.offered_to.map((p) => p.id).join(", ") || "no ICP yet"} (${r.audience.types.map((t) => t.label).join(", ") || "customers in general"})${r.audience.partner_led ? "; partner-led" : ""}`);
|
|
111
|
+
for (const p of r.pages) {
|
|
112
|
+
console.log(`${p.path.padEnd(14)} ${p.level.padEnd(8)} ${p.label}`);
|
|
113
|
+
for (const s of p.sections) console.log(` <${s.component}> ${s.label} (need ${s.need}${s.when ? `, ${s.when}` : ""}) from ${s.source.record}${s.source.exists ? "" : " (not written yet)"}`);
|
|
114
|
+
}
|
|
115
|
+
if (r.omitted.length) console.log(`Not now: ${r.omitted.map((o) => `${o.label}${o.when ? ` (${o.when})` : ""}`).join("; ")}`);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
@@ -0,0 +1,252 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// Site presence (EE-062, EE-063): how present a venture's public records are on its marketing site,
|
|
3
|
+
// weighed by who the site has to speak to now, with the evidence for every weight. The marketing
|
|
4
|
+
// matrix (matrix.json) names the audiences, what each needs to learn and why, and the record each
|
|
5
|
+
// piece of information comes from. What customers look for depends on who they are: Jev places the
|
|
6
|
+
// venture's ICP into customer types (consumers, small-business owners, enterprise buyers, developers,
|
|
7
|
+
// regulated professionals...), and each type says how much each item matters to it and why. The ICP
|
|
8
|
+
// comes from the venture's ICP record, from the founder's own sentence, or, failing both, is inferred
|
|
9
|
+
// from the home page and marked so. The venture's facts (raising, hiring, inviting contributors) weigh
|
|
10
|
+
// the other audiences. Each item's need sets how prominent it must be: on the home page, in the
|
|
11
|
+
// navigation, linked from the home page, or anywhere. The site is shown as its sitemap, each route
|
|
12
|
+
// rated on the items that belong there, with the pages the venture's priorities call for and the site
|
|
13
|
+
// lacks. Ours, version 0.x and unevaluated: the reasons are claims to test with real customers.
|
|
14
|
+
//
|
|
15
|
+
// Whether a page says something, and which customer types the ICP is, are judged by Jev when the
|
|
16
|
+
// platform hands over its answers; otherwise by the matrix's keywords, which is rougher.
|
|
17
|
+
//
|
|
18
|
+
// node presence.mjs request <site.json> the Jev request
|
|
19
|
+
// node presence.mjs score <site.json> [--answers <jev.json>] [--venture <dir>] [--raising] [--hiring] [--contributors]
|
|
20
|
+
// node presence.mjs <venture> [--json] the saved result
|
|
21
|
+
|
|
22
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
23
|
+
import { dirname, join } from "node:path";
|
|
24
|
+
import { fileURLToPath } from "node:url";
|
|
25
|
+
|
|
26
|
+
const here = dirname(fileURLToPath(import.meta.url));
|
|
27
|
+
const load = (p) => JSON.parse(readFileSync(p, "utf8"));
|
|
28
|
+
export const MATRIX = load(join(here, "matrix.json"));
|
|
29
|
+
export const PRESENCE = "operations/marketing/site-presence.json";
|
|
30
|
+
const LEVELS = Object.entries(MATRIX.prominence).sort(([, a], [, b]) => b.at_least - a.at_least);
|
|
31
|
+
const TYPES = MATRIX.customer_types;
|
|
32
|
+
const r2 = (x) => Math.round(x * 100) / 100;
|
|
33
|
+
|
|
34
|
+
/** The venture's facts: flags the founder gave, else what its ICP record says about who it courts. */
|
|
35
|
+
export function factsOf(dir, flags = {}) {
|
|
36
|
+
const f = { raising: false, hiring: false, contributors: false };
|
|
37
|
+
const icpFile = dir && join(dir, "purpose/icp.json");
|
|
38
|
+
if (icpFile && existsSync(icpFile)) {
|
|
39
|
+
const said = JSON.stringify(load(icpFile)).toLowerCase();
|
|
40
|
+
if (/investor|angel|\bvc\b/.test(said)) f.raising = true;
|
|
41
|
+
if (/candidate|hire|recruit/.test(said)) f.hiring = true;
|
|
42
|
+
if (/contributor|open source|volunteer/.test(said)) f.contributors = true;
|
|
43
|
+
}
|
|
44
|
+
for (const k of ["raising", "hiring", "contributors"]) if (flags[k] != null) f[k] = !!flags[k];
|
|
45
|
+
return f;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/** Who the customers are, and where that comes from: the founder's words, the ICP record, or the home page. */
|
|
49
|
+
// Who the site is for, read from the site itself (2026-10-28): what it says outright ("for clinics",
|
|
50
|
+
// "built for teams", "trusted by") and, failing that, what its opening implies. The founder does not
|
|
51
|
+
// type it. The ICP the venture's records state is kept beside it, unscored, for a later clarity check
|
|
52
|
+
// (stated versus seen).
|
|
53
|
+
const SAYS = /\b(?:built for|made for|designed for|for (?:teams|companies|businesses|developers|founders|clinics|owners|people|creators|agencies|startups|enterprises|schools|families)|trusted by|helps?|serving|used by|loved by)\b[^.\n]{0,140}/gi;
|
|
54
|
+
export function icpOf(dir, site) {
|
|
55
|
+
const ok = (site?.pages ?? []).filter((p) => !p.error);
|
|
56
|
+
const home = ok.find((p) => p.depth === 0);
|
|
57
|
+
const who = ok.filter((p) => /customer|solution|who|industr|use-case|about|for-/i.test(p.path));
|
|
58
|
+
const says = [...new Set([home, ...who].filter(Boolean).flatMap((p) => (p.text ?? "").match(SAYS) ?? []).map((x) => x.trim()))].slice(0, 8);
|
|
59
|
+
const opening = (home?.text ?? "").slice(0, 500);
|
|
60
|
+
const icpFile = dir && join(dir, "purpose/icp.json");
|
|
61
|
+
const stated = icpFile && existsSync(icpFile) ? (load(icpFile).profiles ?? []).map((p) => p.who ?? p.description ?? p.name).filter(Boolean) : null;
|
|
62
|
+
return { text: [says.join(" | "), opening].filter(Boolean).join(" || "), source: says.length ? "site" : "inferred", says,
|
|
63
|
+
cite: says.length ? `what the site says about who it is for (${says.length} phrase${says.length > 1 ? "s" : ""}), with the home page's opening` : "the home page's opening text (inferred: the site does not say outright who it is for)",
|
|
64
|
+
...(stated?.length ? { stated } : {}) };
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/** Which customer types the ICP is: Jev's answers when given, else the matrix's keywords. */
|
|
68
|
+
export function typesOf(icp, answers) {
|
|
69
|
+
const out = Object.entries(TYPES.types).map(([id, t]) => ({ id, label: t.label,
|
|
70
|
+
p: answers ? r2(answers[`type:${id}`]?.noul ?? 0) : new RegExp(`\\b(?:${t.match})`, "i").test(icp.text) ? 1 : 0 }));
|
|
71
|
+
// Jev answers "mainly" conservatively: a type counts at the floor or above and within 0.6 of the strongest.
|
|
72
|
+
const top = Math.max(0, ...out.map((t) => t.p));
|
|
73
|
+
return out.filter((t) => t.p >= TYPES.floor && t.p >= 0.6 * top).sort((a, b) => b.p - a.p);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/** How much each item matters to the venture's customers, and why: the strongest of its customer types. */
|
|
77
|
+
export function customerNeeds(types) {
|
|
78
|
+
const rows = types.length ? types : [{ id: "default", label: TYPES.default.label, p: 1 }];
|
|
79
|
+
return Object.fromEntries(Object.keys(TYPES.default.why).map((item) => {
|
|
80
|
+
const per = rows.map((t) => { const [imp, why] = (t.id !== "default" && TYPES.types[t.id].why[item]) || TYPES.default.why[item]; return { type: t.label, p: t.p, importance: imp, why }; });
|
|
81
|
+
// A customer who is several types at once (a clinic owner is a small-business owner and a regulated
|
|
82
|
+
// professional) needs what the most demanding of them needs.
|
|
83
|
+
return [item, { importance: Math.max(...per.map((x) => x.importance)), per }];
|
|
84
|
+
}));
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// One reason per distinct claim: types that share the general reason are named together.
|
|
88
|
+
const whyOf = (per) => [...per.reduce((m, x) => m.set(x.why, [...(m.get(x.why) ?? []), `${x.type} (${x.importance})`]), new Map())].map(([why, ts]) => `${ts.join(", ")}: ${why}`).join("; ");
|
|
89
|
+
|
|
90
|
+
/** Each audience's weight from the facts: the ICP priority matrix in its simplest form. */
|
|
91
|
+
export function weights(facts) {
|
|
92
|
+
return Object.fromEntries(Object.entries(MATRIX.audiences).map(([a, x]) => {
|
|
93
|
+
const w = typeof x.weight === "number" ? x.weight : (Object.entries(x.weight).find(([k]) => k !== "default" && facts[k])?.[1] ?? x.weight.default);
|
|
94
|
+
return [a, w];
|
|
95
|
+
}));
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/** How much the site needs each item now, how prominent it must be, and the evidence: who needs it and why. */
|
|
99
|
+
export function needs(w, cust = customerNeeds([])) {
|
|
100
|
+
return Object.fromEntries(Object.entries(MATRIX.items).map(([id, it]) => {
|
|
101
|
+
const evidence = Object.entries(it.audiences).map(([a, imp]) => {
|
|
102
|
+
const c = a === "customers" && cust[id];
|
|
103
|
+
const importance = c ? c.importance : imp;
|
|
104
|
+
return { audience: a, weight: w[a], importance, need: r2(w[a] * importance),
|
|
105
|
+
why: c ? whyOf(c.per) : it.why?.[a] ?? MATRIX.audiences[a].why, source: c ? "customer types" : "matrix" };
|
|
106
|
+
}).sort((x, y) => y.need - x.need);
|
|
107
|
+
const need = evidence[0]?.need ?? 0;
|
|
108
|
+
// An item whose place is its own page (pricing, careers) needs that page in the navigation at most.
|
|
109
|
+
let required = LEVELS.find(([, l]) => need >= l.at_least)[0];
|
|
110
|
+
if (required === "home" && !it.belongs.includes("home")) required = "nav";
|
|
111
|
+
return [id, { need, required, for: evidence.filter((e) => e.need >= 0.3).map((e) => e.audience), evidence }];
|
|
112
|
+
}));
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
export function pageType(p) {
|
|
116
|
+
if (p.depth === 0 || p.path === "/") return "home";
|
|
117
|
+
const s = `${p.path} ${p.title ?? ""} ${p.label ?? ""}`.toLowerCase();
|
|
118
|
+
return Object.entries(MATRIX.pages).find(([k, re]) => k !== "home" && new RegExp(re, "i").test(s))?.[0] ?? "other";
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
const pageText = (p) => `[${p.path}] ${p.title}\n${(p.text ?? "").slice(0, 3000)}`;
|
|
122
|
+
|
|
123
|
+
/** The Jev request: does each page tell a visitor each item, and which customer types the ICP is. */
|
|
124
|
+
export function request(site, icp = icpOf(null, site)) {
|
|
125
|
+
const pages = site.pages.filter((p) => !p.error);
|
|
126
|
+
const questions = {};
|
|
127
|
+
pages.forEach((p, i) => { for (const [id, it] of Object.entries(MATRIX.items))
|
|
128
|
+
questions[`has:${id}:p${i}`] = { type: "noul", instructions: `Does the web page in \`pages.p${i}\` tell a visitor ${it.question}? Judge what the page itself states, not what the venture might do.`,
|
|
129
|
+
criteria: { true: "Yes: the page states it", false: "No: the page does not state it" } }; });
|
|
130
|
+
for (const [id, t] of Object.entries(TYPES.types))
|
|
131
|
+
questions[`type:${id}`] = { type: "noul", instructions: `Are the customers described in \`icp\` mainly ${t.label.toLowerCase()} (${t.about})?`, criteria: { true: `Yes: they are ${t.label.toLowerCase()}`, false: "No" } };
|
|
132
|
+
return { state: { site: site.site, icp: icp.text, guidelines: "Each page is a venture's public marketing page. Answer from the page's own words only.", pages: Object.fromEntries(pages.map((p, i) => [`p${i}`, pageText(p)])) }, model: "jev-latest", questions };
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
// Without a judge: the matrix's keywords, matched in the page's text.
|
|
136
|
+
function byKeywords(p, id, it) {
|
|
137
|
+
if (id === "what") return p.depth === 0 && (p.text ?? "").length > 80;
|
|
138
|
+
const t = `${p.path} ${p.title} ${p.text ?? ""}`.toLowerCase();
|
|
139
|
+
const hits = it.keywords.filter((k) => t.includes(k.toLowerCase())).length;
|
|
140
|
+
return hits >= (it.keywords.length >= 5 ? 2 : 1);
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
const norm = (path) => path.replace(/\/$/, "") || "/";
|
|
144
|
+
|
|
145
|
+
/** The sitemap as a tree of routes, each rated on the items that belong there, with the missing pages. */
|
|
146
|
+
export function sitemap(site, pages, items) {
|
|
147
|
+
const byPath = new Map();
|
|
148
|
+
const add = (path, x) => {
|
|
149
|
+
const k = norm(path);
|
|
150
|
+
if (!byPath.has(k)) byPath.set(k, { path: k, label: "", in_nav: false, read: false, ...x });
|
|
151
|
+
else Object.assign(byPath.get(k), Object.fromEntries(Object.entries(x).filter(([, v]) => v)));
|
|
152
|
+
};
|
|
153
|
+
add("/", { label: "Home", in_nav: true });
|
|
154
|
+
for (const l of site.links ?? []) add(l.path, { label: l.label, in_nav: l.in_nav });
|
|
155
|
+
for (const p of pages) add(p.path, { title: p.title, read: true, label: p.label || p.title });
|
|
156
|
+
const need = Object.fromEntries(items.map((x) => [x.id, x.need]));
|
|
157
|
+
const nodes = [...byPath.values()].map((n) => {
|
|
158
|
+
const segs = n.path.split("/").filter(Boolean);
|
|
159
|
+
let parent = n.path === "/" ? null : "/";
|
|
160
|
+
for (let k = segs.length - 1; k >= 1; k--) { const up = "/" + segs.slice(0, k).join("/"); if (byPath.has(up)) { parent = up; break; } }
|
|
161
|
+
const type = pageType({ depth: n.path === "/" ? 0 : 1, path: n.path, title: n.title, label: n.label });
|
|
162
|
+
const level = n.path === "/" ? "home" : segs.length === 1 && n.in_nav ? "nav" : segs.length === 1 ? "linked" : "anywhere";
|
|
163
|
+
const found = n.read ? items.filter((x) => x.found.some((f) => norm(f.path) === n.path)).map((x) => x.id) : [];
|
|
164
|
+
// What belongs on this route: on the home page, what must be there; elsewhere, the items whose place is this page type.
|
|
165
|
+
const expected = items.filter((x) => x.need >= 0.2 && (type === "home" ? x.required === "home" : MATRIX.items[x.id].belongs.includes(type))).map((x) => x.id);
|
|
166
|
+
const total = expected.reduce((a, id) => a + need[id], 0);
|
|
167
|
+
const rating = n.read && total ? Math.round((100 * expected.filter((id) => found.includes(id)).reduce((a, id) => a + need[id], 0)) / total) : null;
|
|
168
|
+
// A short name: the page's title before its site name, a short link text, or the path's last segment.
|
|
169
|
+
const seg = segs.at(-1)?.replace(/[-_]+/g, " ") ?? "Home";
|
|
170
|
+
const titled = n.title?.split(/\s[–|·-]\s/)[0]?.trim();
|
|
171
|
+
const name = n.path === "/" ? "Home" : titled && titled.length <= 32 ? titled : n.label && n.label.length <= 24 ? n.label : seg[0].toUpperCase() + seg.slice(1);
|
|
172
|
+
return { path: n.path, parent, label: name, type, level, read: n.read, rating, expected, found, missing: n.read ? expected.filter((id) => !found.includes(id)) : [] };
|
|
173
|
+
});
|
|
174
|
+
// Pages the venture's priorities call for that the site lacks: an item needed one click away or
|
|
175
|
+
// closer, missing everywhere, whose place is a page type the site has no route for.
|
|
176
|
+
const types = new Set(nodes.map((n) => n.type));
|
|
177
|
+
const ghosts = new Map();
|
|
178
|
+
for (const x of items) {
|
|
179
|
+
if (x.score > 0 || x.need < 0.4) continue;
|
|
180
|
+
const t = MATRIX.items[x.id].belongs.find((b) => b !== "home");
|
|
181
|
+
if (!t || types.has(t)) continue;
|
|
182
|
+
const g = ghosts.get(t) ?? { path: `/${t}`, parent: "/", label: `${t[0].toUpperCase()}${t.slice(1)} (suggested)`, type: t, level: x.required === "nav" ? "nav" : "linked", read: false, rating: 0, expected: [], found: [], missing: [], ghost: true };
|
|
183
|
+
g.expected.push(x.id); g.missing.push(x.id); ghosts.set(t, g);
|
|
184
|
+
}
|
|
185
|
+
return [...nodes, ...ghosts.values()];
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
export function score(site, { answers = null, facts = {}, venture = null, icp = null } = {}) {
|
|
189
|
+
icp ??= icpOf(null, site);
|
|
190
|
+
const types = typesOf(icp, answers);
|
|
191
|
+
const w = weights(facts), need = needs(w, customerNeeds(types));
|
|
192
|
+
const pages = site.pages.filter((p) => !p.error).map((p, i) => ({ ...p, i, type: pageType(p), level: p.depth === 0 ? "home" : p.in_nav ? "nav" : p.depth === 1 ? "linked" : "anywhere" }));
|
|
193
|
+
const rankOf = (l) => MATRIX.prominence[l]?.rank ?? 0;
|
|
194
|
+
const siteTypes = new Set(pages.map((p) => p.type));
|
|
195
|
+
const label = (a) => MATRIX.audiences[a].label.toLowerCase();
|
|
196
|
+
const list = (xs) => xs.length > 1 ? `${xs.slice(0, -1).join(", ")} and ${xs.at(-1)}` : xs[0];
|
|
197
|
+
const items = Object.entries(MATRIX.items).map(([id, it]) => {
|
|
198
|
+
const n = need[id];
|
|
199
|
+
const found = pages.filter((p) => answers ? (answers[`has:${id}:p${p.i}`]?.noul ?? 0) >= MATRIX.threshold : byKeywords(p, id, it));
|
|
200
|
+
const at = found.map((p) => p.level).sort((a, b) => rankOf(b) - rankOf(a))[0] ?? null;
|
|
201
|
+
const s = !at ? 0 : rankOf(at) >= rankOf(n.required) ? 1 : 0.5;
|
|
202
|
+
const who = list(n.for.map(label));
|
|
203
|
+
const req = MATRIX.prominence[n.required].means;
|
|
204
|
+
const page = it.belongs.find((b) => b !== "home");
|
|
205
|
+
const note = !at && !n.for.length ? `Not on the pages read, which is fine while ${list(Object.keys(it.audiences).map(label))} are not a priority.`
|
|
206
|
+
: !at ? `Not on the pages read. ${who[0].toUpperCase() + who.slice(1)} need it ${req}${page && !siteTypes.has(page) ? `; there is no ${page} page` : ""}.`
|
|
207
|
+
: s < 1 ? `Found on ${found.map((p) => p.path).slice(0, 2).join(", ")}, but ${who} need it ${req}.`
|
|
208
|
+
: n.need < 0.4 && at === "home" ? `On the home page, though ${list(Object.keys(it.audiences).map(label))} are not a priority now.`
|
|
209
|
+
: `Present ${MATRIX.prominence[at].means}.`;
|
|
210
|
+
return { id, label: it.label, record: it.record, need: n.need, required: n.required, for: n.for, at, score: s, found: found.map((p) => ({ path: p.path, type: p.type })), note,
|
|
211
|
+
evidence: n.evidence.filter((e, k) => k === 0 || e.need >= 0.1) };
|
|
212
|
+
});
|
|
213
|
+
const total = items.reduce((a, x) => a + x.need, 0);
|
|
214
|
+
const audiences = Object.fromEntries(Object.keys(MATRIX.audiences).map((a) => {
|
|
215
|
+
const xs = items.map((x) => [x, MATRIX.items[x.id].audiences[a] == null ? 0 : (need[x.id].evidence.find((e) => e.audience === a)?.importance ?? 0)]).filter(([, imp]) => imp);
|
|
216
|
+
const t = xs.reduce((s, [, imp]) => s + imp, 0);
|
|
217
|
+
return [a, { weight: w[a], score: t ? Math.round((100 * xs.reduce((s, [x, imp]) => s + imp * x.score, 0)) / t) : null }];
|
|
218
|
+
}));
|
|
219
|
+
const fix = items.filter((x) => x.score < 1 && x.need >= 0.4).sort((a, b) => b.need * (1 - b.score) - a.need * (1 - a.score)).slice(0, 3).map((x) => x.id);
|
|
220
|
+
return { version: MATRIX.version, site: site.site, venture, read_via: site.via, read_at: site.read_at, judged_by: answers ? "jev" : "keywords", facts,
|
|
221
|
+
icp: { ...icp, types: types.length ? types : [{ id: "default", label: TYPES.default.label, p: null }] },
|
|
222
|
+
score: Math.round((100 * items.reduce((a, x) => a + x.need * x.score, 0)) / total), audiences,
|
|
223
|
+
pages: pages.map((p) => ({ path: p.path, title: p.title, type: p.type, level: p.level })),
|
|
224
|
+
sitemap: sitemap(site, pages, items),
|
|
225
|
+
items: items.sort((a, b) => b.need * (1 - b.score) - a.need * (1 - a.score) || b.need - a.need),
|
|
226
|
+
fix_first: fix };
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
if (import.meta.url === `file://${process.argv[1]}`) {
|
|
230
|
+
const [a, b] = process.argv.slice(2);
|
|
231
|
+
const opt = (n) => { const i = process.argv.indexOf(`--${n}`); return i > 0 ? process.argv[i + 1] : null; };
|
|
232
|
+
const has = (n) => process.argv.includes(`--${n}`);
|
|
233
|
+
const siteOf = (p) => load(p === "-" ? 0 : p);
|
|
234
|
+
if (a === "request") { const site = siteOf(b); console.log(JSON.stringify(request(site, icpOf(opt("venture"), site)))); }
|
|
235
|
+
else if (a === "score") {
|
|
236
|
+
const dir = opt("venture"), site = siteOf(b);
|
|
237
|
+
const answers = opt("answers") ? (load(opt("answers")).answers ?? load(opt("answers"))) : null;
|
|
238
|
+
const facts = factsOf(dir, Object.fromEntries(["raising", "hiring", "contributors"].filter(has).map((k) => [k, true])));
|
|
239
|
+
const r = score(site, { answers, facts, icp: icpOf(dir, site), venture: dir ? load(join(dir, "venture.json")).id : null });
|
|
240
|
+
if (dir) { mkdirSync(join(dir, dirname(PRESENCE)), { recursive: true }); writeFileSync(join(dir, PRESENCE), JSON.stringify(r, null, 2) + "\n"); }
|
|
241
|
+
console.log(JSON.stringify(r, null, 2));
|
|
242
|
+
} else if (a && existsSync(join(a, "venture.json"))) {
|
|
243
|
+
if (!existsSync(join(a, PRESENCE))) { console.error(`${PRESENCE} does not exist yet: read the site first (node interface/harness/site.mjs <url> --venture <dir>)`); process.exit(1); }
|
|
244
|
+
const r = load(join(a, PRESENCE));
|
|
245
|
+
if (has("json")) console.log(JSON.stringify(r, null, 2));
|
|
246
|
+
else {
|
|
247
|
+
console.log(`${r.site}: site presence ${r.score}/100 (${r.judged_by}); customers: ${r.icp.types.map((t) => t.label).join(", ")} (ICP ${r.icp.source})`);
|
|
248
|
+
for (const n of r.sitemap) console.log(` ${n.ghost ? "+" : " "} ${n.path.padEnd(28)} ${n.type.padEnd(9)} ${n.rating == null ? (n.read ? "-" : "not read") : n.rating}${n.missing.length ? ` missing: ${n.missing.join(", ")}` : ""}`);
|
|
249
|
+
for (const x of r.items) console.log(`${x.score === 1 ? "ok " : x.score ? "low " : "miss"} ${x.label.padEnd(28)} ${x.note}`);
|
|
250
|
+
}
|
|
251
|
+
} else { console.error("usage: node presence.mjs request <site.json> | score <site.json> [--answers f] [--venture d] [--raising] [--hiring] [--contributors] | <venture> [--json]"); process.exit(2); }
|
|
252
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
{
|
|
2
|
+
"layer": "operations",
|
|
3
|
+
"references": [
|
|
4
|
+
{
|
|
5
|
+
"id": "porter-1985-competitive-advantage",
|
|
6
|
+
"title": "Competitive Advantage: Creating and Sustaining Superior Performance",
|
|
7
|
+
"authors": [
|
|
8
|
+
"Michael E. Porter"
|
|
9
|
+
],
|
|
10
|
+
"year": 1985,
|
|
11
|
+
"kind": "book",
|
|
12
|
+
"summary": "The value chain: primary activities (inbound logistics, operations, outbound logistics, marketing and sales, service) and support activities (firm infrastructure, human resource management, technology development, procurement).",
|
|
13
|
+
"url": "https://umbrex.com/resources/frameworks/organization-frameworks/porter-value-chain/",
|
|
14
|
+
"checked": "2026-10-28",
|
|
15
|
+
"informs": [
|
|
16
|
+
"field:department.value_chain",
|
|
17
|
+
"rule:department-grouping"
|
|
18
|
+
],
|
|
19
|
+
"stance": {
|
|
20
|
+
"is": "adopted",
|
|
21
|
+
"why": "Each department is tagged primary or support by Porter's activities, and the optional grouping folders take those names; product development is filed as primary for a venture whose product is its work."
|
|
22
|
+
}
|
|
23
|
+
},
|
|
24
|
+
{
|
|
25
|
+
"id": "apqc-pcf-cross-industry",
|
|
26
|
+
"title": "APQC Process Classification Framework (PCF), cross-industry and industry-specific versions",
|
|
27
|
+
"authors": [
|
|
28
|
+
"APQC"
|
|
29
|
+
],
|
|
30
|
+
"year": 2025,
|
|
31
|
+
"kind": "standard",
|
|
32
|
+
"summary": "A hierarchical taxonomy of business processes with 13 top-level categories (from Develop Vision and Strategy to Develop and Manage Business Capabilities), published cross-industry and in industry-specific versions.",
|
|
33
|
+
"url": "https://www.apqc.org/process-frameworks/industry-specific-process-frameworks",
|
|
34
|
+
"checked": "2026-10-28",
|
|
35
|
+
"informs": [
|
|
36
|
+
"field:venture.departments",
|
|
37
|
+
"field:department.apqc"
|
|
38
|
+
],
|
|
39
|
+
"stance": {
|
|
40
|
+
"is": "adopted",
|
|
41
|
+
"why": "A venture's departments are the PCF's top-level categories under short ids; the catalog maps to them and cites them rather than copying the framework's text, which carries licence terms."
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
]
|
|
45
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
---
|
|
2
|
+
type: knowledge
|
|
3
|
+
tags: [everything-ecosystem, skills, operations, recruiting, cofounder, hiring, investors]
|
|
4
|
+
ai-model: claude-opus-5.5
|
|
5
|
+
created: 2026-10-27T20:45:13Z
|
|
6
|
+
source: hybrid
|
|
7
|
+
name: recruiting
|
|
8
|
+
description: >
|
|
9
|
+
Recruit the people the team plan says the venture needs now (cofounders,
|
|
10
|
+
hires, contractors, advisors, investors): write a brief that says what the
|
|
11
|
+
role is for and why a person, find and screen candidates with an agent's help,
|
|
12
|
+
and leave contact and the decision to a person (EE-029).
|
|
13
|
+
MANDATORY TRIGGERS: recruit, hire, hiring, find a cofounder, cofounder search,
|
|
14
|
+
job description, recruiting brief, contractor, find investors, advisor search.
|
|
15
|
+
---
|
|
16
|
+
|
|
17
|
+
# Recruiting
|
|
18
|
+
|
|
19
|
+
Operations layer, governance tier (EE-029). Reads `operations/team-plan.json`; writes a brief per open role from `operations/templates/recruiting-brief.md` and the plan's `recruiting` entry.
|
|
20
|
+
|
|
21
|
+
## Steps
|
|
22
|
+
|
|
23
|
+
1. **Only roles needed now.** `check-team.mjs` lists them ("recruit now"). A role for a later phase waits.
|
|
24
|
+
2. **Write the brief.** It says why a person is needed (the capability register), what they bring (the validation team record's gaps), and what they get (ownership and the option pool).
|
|
25
|
+
3. **Agent's part:** search public profiles and communities, draft outreach, schedule, screen against the must-haves, and keep notes. The register lists sourcing candidates as `unknown`, so a person reviews every shortlist.
|
|
26
|
+
4. **Person's part:** contact, interviews, the decision, and any offer. Offers of equity are `ownership-or-control`; offers of pay are `spend-money`. Both escalate.
|
|
27
|
+
5. **Cofounders** go on to the governance kit's `cofounder-structure` skill before anyone commits. Investors go to `financing-and-investor-rights`.
|
|
28
|
+
|
|
29
|
+
## Guardrails
|
|
30
|
+
|
|
31
|
+
- Candidates' personal data stays out of public repositories.
|
|
32
|
+
- No automated outreach in a person's name without their approval of the message (`public-commitment`).
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
---
|
|
2
|
+
type: knowledge
|
|
3
|
+
tags: [everything-ecosystem, skills, operations, team, agents, human-in-the-loop]
|
|
4
|
+
ai-model: claude-opus-5.5
|
|
5
|
+
created: 2026-10-27T20:45:13Z
|
|
6
|
+
source: hybrid
|
|
7
|
+
name: team-design
|
|
8
|
+
description: >
|
|
9
|
+
Decide who the venture needs, agent or person, and when: which roles agents
|
|
10
|
+
fill, what authority each gets, where a human stays in the loop, and which
|
|
11
|
+
person roles to recruit. Records operations/team-plan.json against the agent
|
|
12
|
+
capability register and decision rights; schema/check-team.mjs checks it (EE-029).
|
|
13
|
+
MANDATORY TRIGGERS: team design, who do we need, hire or agent, can an agent
|
|
14
|
+
do this, human in the loop, delegate to agents, agent roles, org design,
|
|
15
|
+
what authority should the agent have, when do we need a person.
|
|
16
|
+
---
|
|
17
|
+
|
|
18
|
+
# Team design
|
|
19
|
+
|
|
20
|
+
Operations layer, governance tier (EE-029). Writes `operations/team-plan.json`; `node schema/check-team.mjs <dir>` checks it.
|
|
21
|
+
|
|
22
|
+
## Push agents to their limit, and find the loops that need a person
|
|
23
|
+
|
|
24
|
+
1. **List the work** the current and next phase need, from the need-now score (`node schema/need-now.mjs <venture>`) and the validation team record's skills.
|
|
25
|
+
2. **Look up each task** in the capability register (`evidence/capabilities/agent-capabilities.json`):
|
|
26
|
+
- `can`: an agent role does it.
|
|
27
|
+
- `can-with-review`: an agent does it; name the person who reviews (`human_loop`).
|
|
28
|
+
- `cannot` or `not-allowed`: an agent prepares; a person does or decides it.
|
|
29
|
+
- `unknown`: a person stays in the loop, and the task becomes an eval. Record the result in the register.
|
|
30
|
+
- Not in the register: add it as `unknown` with what you know.
|
|
31
|
+
3. **Delegate authority** by decision class. An agent role may hold only classes that decision rights give to it, never an escalate class.
|
|
32
|
+
4. **Open person roles** needed by the current phase are recruited now (`recruiting` skill). Roles for later phases stay `planned`, not recruited early.
|
|
33
|
+
|
|
34
|
+
## Don't overbuild the team
|
|
35
|
+
|
|
36
|
+
A role is added when the work exists, not when an org chart would have one. Re-run at each phase gate.
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: tool-selection
|
|
3
|
+
description: "Help someone choose the tools for a layer (knowledge, operations, execution, evidence, governance, interface, purpose) by offering situation questions, only when they want help. Triggers: which tool should I use, Obsidian or Notion, what should we use for, compare tools, I'm not sure what to use, switching tools. Do not trigger when the person already names their tools."
|
|
4
|
+
type: skill
|
|
5
|
+
source: hybrid
|
|
6
|
+
created: 2026-10-26T21:57:48Z
|
|
7
|
+
ai-model: claude-opus-5.5
|
|
8
|
+
tags: [skill, everything-ecosystem, tooling, tool-selection, typesafe]
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
# tool-selection
|
|
12
|
+
|
|
13
|
+
Every layer has a tooling record (`<layer>/tooling/integrations.json`). This skill helps fill it when someone wants help deciding. It is opt-in: most people arrive knowing their tools, and for them the job is only to record what they use.
|
|
14
|
+
|
|
15
|
+
## When to run
|
|
16
|
+
|
|
17
|
+
- **Run** when the person asks for help choosing, compares tools, says they are unsure, or is about to switch.
|
|
18
|
+
- **Offer once** (one line, easy to decline) when a choice looks hard to reverse: data would move into a proprietary format, many people or agents would depend on it, or it holds sensitive material.
|
|
19
|
+
- **Do not run** when the person names their tools. Record them as `in-use` and move on. If they name a tool and ask for nothing else, do not second-guess it.
|
|
20
|
+
|
|
21
|
+
## Principles for the questions
|
|
22
|
+
|
|
23
|
+
The questions are for the person, to help them decide. They are not a form. Each layer's `tooling/questions.md` gives the dimensions that matter for that layer and example questions. The examples show the kind of question to ask; they are not a script, and the agent should ask others when the situation calls for it.
|
|
24
|
+
|
|
25
|
+
1. **Ask about the situation, not the tools.** "Who edits this?" beats "Do you want real-time collaboration?". Tool features come in only when the presets are compared.
|
|
26
|
+
2. **Ask only what can change the answer.** If every remaining candidate answers a question the same way, skip it. Stop when the ranking is clear.
|
|
27
|
+
3. **Start from what exists.** Ask what they use today and what it would cost to move. Current tools are candidates like any other, and switching cost counts.
|
|
28
|
+
4. **One narrow judgment per question.** Each question is a choice from a set (with "none of these"), a score on described levels, or a yes/no likelihood. That keeps answers scoreable and comparable.
|
|
29
|
+
5. **Separate must-haves from preferences.** A must-have ("must stay on my machine", "agents must be able to write to it") rules a tool out. Preferences are weighted. Never let strong preferences buy back a failed must-have.
|
|
30
|
+
6. **Weigh risk separately from fit.** For each leading candidate, ask how costly it is to be wrong and how hard to undo: lock-in, export quality, who else depends on it. A close fit on a hard-to-reverse choice is worth a pause; a close fit on an easy one is not.
|
|
31
|
+
7. **More than one tool per layer is normal.** A vault for working notes and a repo for docs as code is a combination, not a failure to choose. Say which job each tool does.
|
|
32
|
+
8. **Say what the person gave up.** The result names the tool, the reasons, the runner-up and what would make the runner-up the better pick.
|
|
33
|
+
|
|
34
|
+
## How to score
|
|
35
|
+
|
|
36
|
+
Recommended: **Jev plus a reasoning model**, each doing what it is good at.
|
|
37
|
+
|
|
38
|
+
- The **reasoning model** reads the situation, picks which questions to ask (using the principles and the layer's dimensions), turns the person's answers into the typed judgments, checks must-haves, judges risk, and writes the explanation.
|
|
39
|
+
- **Jev** (TypeSafe) scores the typed judgments for each candidate against the layer's presets and returns probabilities. It is fast, cheap and consistent, but gives no reasons and jitters by a few hundredths, so near-equal scores are reported as a tie band, not an order.
|
|
40
|
+
- Without `TYPESAFE_API_KEY`, the reasoning model scores alone and says so. Sending project details to TypeSafe is something the person turns on.
|
|
41
|
+
|
|
42
|
+
This follows MAD's scoring contract (MAD requirements 13 and 14: typed judgments, weights kept separate, must-holds checked apart from weighted sums).
|
|
43
|
+
|
|
44
|
+
## Recording the result
|
|
45
|
+
|
|
46
|
+
Update `<layer>/tooling/integrations.json`: chosen tools `in-use` with a `reason`, serious alternatives `candidate`, and ruled-out tools `rejected` with the must-have they failed. Credentials are named by environment variable only.
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
---
|
|
2
|
+
type: template
|
|
3
|
+
tags: [everything-ecosystem, operations, marketing, brand, template]
|
|
4
|
+
ai-model: claude-opus-5.5
|
|
5
|
+
created: 2026-10-29T06:04:07Z
|
|
6
|
+
source: hybrid
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# Template: brand
|
|
10
|
+
|
|
11
|
+
Fills `<venture>/marketing/brand.json` (`schema/marketing-brand.schema.json`). The brand is the venture's global stylesheet: every agent's layout carries its voice and fonts, and `marketing/brand/tokens` renders it as design tokens and CSS for any interface. Check it with `mentura marketing/brand check`.
|
|
12
|
+
|
|
13
|
+
```json
|
|
14
|
+
{
|
|
15
|
+
"version": "0.1.0",
|
|
16
|
+
"venture": "<venture id>",
|
|
17
|
+
"name": "<the name as the brand writes it>",
|
|
18
|
+
"colors": { "accent": "#1d4e89", "ink": "#111111", "paper": "#ffffff" },
|
|
19
|
+
"typefaces": [
|
|
20
|
+
{ "id": "inter", "family": "Inter", "source": "self-hosted", "license": "OFL-1.1", "files": ["public/fonts/inter.woff2"], "fallback": ["system-ui", "sans-serif"] }
|
|
21
|
+
],
|
|
22
|
+
"fonts": { "body": "inter", "heading": "inter" },
|
|
23
|
+
"voice": {
|
|
24
|
+
"tone": ["plain", "warm"],
|
|
25
|
+
"rules": [{ "id": "no-jargon", "avoid": ["synergy", "leverage"], "use": "work with", "why": "our customers do not talk like this" }]
|
|
26
|
+
},
|
|
27
|
+
"marks": [
|
|
28
|
+
{ "kind": "logo", "asset": "public/logo.svg" },
|
|
29
|
+
{ "kind": "icon", "asset": "public/icon.svg" },
|
|
30
|
+
{ "kind": "apple-icon", "asset": "public/apple-icon.png" },
|
|
31
|
+
{ "kind": "favicon", "asset": "public/favicon.ico" }
|
|
32
|
+
],
|
|
33
|
+
"design_systems": [{ "id": "radix", "name": "Radix Colors", "url": "https://www.radix-ui.com/colors", "version": "3.0.0", "license": "MIT", "uses": ["the blue scale"] }],
|
|
34
|
+
"scopes": [{ "for": "investors", "colors": { "accent": "#000000" }, "voice": { "tone": ["direct"] } }]
|
|
35
|
+
}
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
## What each part means
|
|
39
|
+
|
|
40
|
+
- **Colors** are hex values by token name.
|
|
41
|
+
- **Typefaces** are defined once, each with its license. A self-hosted face names its files in `public/`, each registered in `public/assets.json` under the same license. A remote face fails the check: every reader's browser would call another host.
|
|
42
|
+
- **Fonts** assign a typeface to a role everywhere. A scope can assign a different one under a route prefix.
|
|
43
|
+
- **Voice** is the stylesheet for words: the tone, and phrases the venture never uses, with what to use instead.
|
|
44
|
+
- **Marks** are the logo and icons. An apple-icon is a 180x180 PNG, an icon is square, a favicon is an ICO.
|
|
45
|
+
- **Design systems** are linked, never copied in.
|
|
46
|
+
- **Scopes** override the brand for a route prefix. The nearer scope wins.
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
---
|
|
2
|
+
type: template
|
|
3
|
+
tags: [everything-ecosystem, operations, marketing, assets, template]
|
|
4
|
+
ai-model: claude-opus-5.5
|
|
5
|
+
created: 2026-10-29T06:04:07Z
|
|
6
|
+
source: hybrid
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# Template: public assets
|
|
10
|
+
|
|
11
|
+
Fills `<venture>/public/assets.json` (`schema/public-assets.schema.json`), the register of what the venture ships as it is. Do not write it by hand: `mentura assets add` registers a file and code fills its facts; `mentura assets fill` refreshes them. Check it with `mentura public/assets check`.
|
|
12
|
+
|
|
13
|
+
```json
|
|
14
|
+
{
|
|
15
|
+
"version": "0.1.0",
|
|
16
|
+
"remote_hosts": ["images.example.com"],
|
|
17
|
+
"assets": [
|
|
18
|
+
{
|
|
19
|
+
"path": "public/logo.svg",
|
|
20
|
+
"source": "Acme design",
|
|
21
|
+
"license": "owned",
|
|
22
|
+
"credit": "Acme",
|
|
23
|
+
"alt": { "text": "Acme wordmark", "by": "agent" },
|
|
24
|
+
"facts": { "type": "svg", "width": 200, "height": 60, "bytes": 812, "sha256": "<filled by code>" }
|
|
25
|
+
}
|
|
26
|
+
],
|
|
27
|
+
"remote": [
|
|
28
|
+
{ "id": "images-example-com-clinic", "url": "https://images.example.com/clinic.jpg", "license": "all-rights-reserved", "credit": "Ana Photo" }
|
|
29
|
+
]
|
|
30
|
+
}
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
## The rules the check holds
|
|
34
|
+
|
|
35
|
+
- Every file in `public/` has an entry with a source, a license and a credit.
|
|
36
|
+
- Facts are read from the file by code. An entry whose facts differ from the file fails.
|
|
37
|
+
- An image needs alt text unless it is marked decorative. Alt text an agent wrote is pending until a person confirms it with `mentura assets confirm`.
|
|
38
|
+
- A remote image is a link with a note in `raw/sources/<id>/`. A full copy in `public/` needs a license that allows one.
|
|
39
|
+
- `remote_hosts` lists the hosts an agent may fetch from.
|