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,581 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// The decision board (EE-054, EE-058): a live page of one venture's decisions, by what renders now,
|
|
3
|
+
// with provenance as it happens, at /map the founder's idea map on one canvas (EE-059), at /graph the
|
|
4
|
+
// idea graph (EE-060), at /kanban the ideas routed into the framework and the docket as boards (EE-061), at /site
|
|
5
|
+
// site presence and the sitemap planner (EE-062 to EE-064), and at /package the venture package (EE-067). Local
|
|
6
|
+
// only: it binds to 127.0.0.1 and reads the workspace in
|
|
7
|
+
// MENTURA_WORKSPACE (the default is this repository's instance/). No dependencies: the map's React
|
|
8
|
+
// Flow is fetched once from unpkg and cached in ~/.cache/naive-board.
|
|
9
|
+
//
|
|
10
|
+
// node interface/demo/board.mjs [--venture <id>] [--port 4331]
|
|
11
|
+
|
|
12
|
+
import { createServer } from "node:http";
|
|
13
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
14
|
+
import { homedir } from "node:os";
|
|
15
|
+
import { join } from "node:path";
|
|
16
|
+
import { spawnSync } from "node:child_process";
|
|
17
|
+
import { routes, ventureDir, ventures } from "../router.mjs";
|
|
18
|
+
import { decisions } from "../../schema/decisions.mjs";
|
|
19
|
+
import { CONVENTIONS, MAP, parse, summary } from "../../purpose/frameworks/idea-map/ideamap.mjs";
|
|
20
|
+
import { GRAPH } from "../../purpose/frameworks/idea-map/graph.mjs";
|
|
21
|
+
import { route } from "../../purpose/frameworks/idea-map/route.mjs";
|
|
22
|
+
import { scan } from "../harness/site.mjs";
|
|
23
|
+
import { MATRIX } from "../../operations/marketing/site/presence.mjs";
|
|
24
|
+
import { plan } from "../../operations/marketing/site/plan.mjs";
|
|
25
|
+
import { packages, write as writePackage, OUT as PACKAGE } from "../../schema/packages.mjs";
|
|
26
|
+
|
|
27
|
+
const arg = (n, d) => { const i = process.argv.indexOf(`--${n}`); return i >= 0 ? process.argv[i + 1] : d; };
|
|
28
|
+
const port = Number(arg("port", 4331));
|
|
29
|
+
const venture = arg("venture", ventures()[0]?.id);
|
|
30
|
+
|
|
31
|
+
function state(id) {
|
|
32
|
+
const dir = ventureDir(id);
|
|
33
|
+
if (!dir) return { error: `no venture ${id}` };
|
|
34
|
+
const log = existsSync(join(dir, "provenance.jsonl")) ? readFileSync(join(dir, "provenance.jsonl"), "utf8").trim().split("\n").filter(Boolean).slice(-8).map((l) => JSON.parse(l)).reverse() : [];
|
|
35
|
+
// The kit for now (EE-028): what need-now says this venture needs in its phase, and which kit holds it.
|
|
36
|
+
const nn = spawnSync("node", [new URL("../../schema/need-now.mjs", import.meta.url).pathname, dir, "--json"], { encoding: "utf8" });
|
|
37
|
+
const kitOf = new Map(routes().filter((r) => r.record).map((r) => [r.record, { kit: r.kit, route: r.route.replace(/\/\[id\]$/, "") }]));
|
|
38
|
+
const rows = nn.status === 0 ? JSON.parse(nn.stdout).rows : [];
|
|
39
|
+
const kit = rows.filter((r) => r.now >= 0.5 || r.status === "coming soon").map((r) => ({ type: r.type, status: r.status, now: r.now, next: r.next, why: r.why, ...(kitOf.get(r.type) ?? {}) }));
|
|
40
|
+
// Money (EE-070): only the summary, never the transactions stream; the source stays the system of record.
|
|
41
|
+
const money = existsSync(join(dir, "money/_summary.json")) ? JSON.parse(readFileSync(join(dir, "money/_summary.json"), "utf8")) : null;
|
|
42
|
+
return { ...decisions(dir), kit, money, log: log.map((e) => ({ at: e.at, activity: e.activity, entity: e.entity, who: `${e.agent?.kind ?? "?"}${e.agent?.id ? ` ${e.agent.id}` : ""}`, via: e.agent?.via })) };
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// The idea map (EE-059): the founder's ideas on one canvas, by pillar and department.
|
|
46
|
+
function ideas(id) {
|
|
47
|
+
const dir = ventureDir(id);
|
|
48
|
+
if (!dir) return { error: `no venture ${id}` };
|
|
49
|
+
if (!existsSync(join(dir, MAP))) return { venture: id, waiting: "no idea dump yet" };
|
|
50
|
+
const v = JSON.parse(readFileSync(join(dir, "venture.json"), "utf8"));
|
|
51
|
+
const map = parse(readFileSync(join(dir, MAP), "utf8"));
|
|
52
|
+
return { venture: id, name: v.name, phase: v.phase, meta: map.meta, ideas: map.ideas, summary: summary(map, v.phase), pillars: CONVENTIONS.pillars, departments: CONVENTIONS.departments };
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const MAP_PAGE = `<!doctype html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1">
|
|
56
|
+
<title>Idea map</title><link rel="stylesheet" href="/vendor/reactflow@11.11.4/dist/style.css">
|
|
57
|
+
<style>
|
|
58
|
+
:root{--bg:#fbfaf7;--fg:#1d1c1a;--muted:#6b675f;--line:#e4e0d8;--card:#fff;--product:#1f6feb;--brand:#bf3989;--business:#1a7f37;--gap:#9a6700}
|
|
59
|
+
@media (prefers-color-scheme:dark){:root{--bg:#141413;--fg:#ecebe6;--muted:#a19d94;--line:#2e2d2a;--card:#1c1b19;--product:#58a6ff;--brand:#f778ba;--business:#56d364;--gap:#e3b341}}
|
|
60
|
+
*{box-sizing:border-box}html,body,#app{height:100%;margin:0}body{background:var(--bg);color:var(--fg);font:14px/1.4 system-ui,-apple-system,"Segoe UI",sans-serif}
|
|
61
|
+
#app{display:grid;grid-template-columns:1fr 320px}@media (max-width:800px){#app{grid-template-columns:1fr;grid-template-rows:60vh auto}}
|
|
62
|
+
aside{border-left:1px solid var(--line);padding:16px;overflow:auto}h1{font-size:18px;margin:0 0 4px}h2{font-size:11px;letter-spacing:.06em;text-transform:uppercase;color:var(--muted);margin:16px 0 6px}
|
|
63
|
+
.meta{color:var(--muted);font-size:12px}.bar{display:flex;height:10px;border-radius:5px;overflow:hidden;margin:6px 0}.bar span{display:block}
|
|
64
|
+
ul{margin:0;padding-left:18px}li{margin:2px 0}.n{color:var(--muted);font-variant-numeric:tabular-nums}a{color:var(--product)}
|
|
65
|
+
.node{background:var(--card);color:var(--fg);border:1px solid var(--line);border-radius:10px;padding:8px 10px;font-size:12px;max-width:230px}
|
|
66
|
+
.node.venture{font-size:16px;font-weight:700;padding:12px 16px;border-width:2px}.node.pillar{font-weight:700;text-transform:capitalize;color:#fff;border:none}
|
|
67
|
+
.node.dept{font-weight:600}.node.dept.empty{border-style:dashed;color:var(--muted);background:transparent}.node.idea .t{display:block}
|
|
68
|
+
.chip{display:inline-block;font-size:10px;border:1px solid var(--line);border-radius:99px;padding:1px 6px;margin:4px 3px 0 0;color:var(--muted)}.chip.ex{border-color:var(--business);color:var(--business);font-weight:600}
|
|
69
|
+
.react-flow__attribution{display:none}.react-flow__handle{opacity:0;pointer-events:none}
|
|
70
|
+
</style></head><body><div id="app"><div id="flow"></div><aside id="side"><h1>Idea map</h1><div class="meta">Waiting for the idea dump...</div></aside></div>
|
|
71
|
+
<script src="/vendor/react@18.3.1/umd/react.production.min.js"></script>
|
|
72
|
+
<script src="/vendor/react-dom@18.3.1/umd/react-dom.production.min.js"></script>
|
|
73
|
+
<script src="/vendor/reactflow@11.11.4/dist/umd/index.js"></script>
|
|
74
|
+
<script>
|
|
75
|
+
const h=React.createElement, RF=window.ReactFlow, Flow=RF.default||RF.ReactFlow;
|
|
76
|
+
const css=(v)=>getComputedStyle(document.documentElement).getPropertyValue(v).trim();
|
|
77
|
+
const esc=(s)=>String(s??"").replace(/[&<>"]/g,(c)=>({"&":"&","<":"<",">":">",'"':"""}[c]));
|
|
78
|
+
const ANG={product:-90,brand:30,business:150};
|
|
79
|
+
function layout(s){
|
|
80
|
+
const nodes=[],edges=[],P=(a,r)=>({x:Math.cos(a*Math.PI/180)*r,y:Math.sin(a*Math.PI/180)*r});
|
|
81
|
+
nodes.push({id:"v",position:{x:-60,y:-20},data:{label:h("div",{className:"node venture"},s.name)},type:"default",style:{border:"none",background:"transparent",padding:0,width:"auto"}});
|
|
82
|
+
const byDept={};s.ideas.forEach((i)=>{const d=(i.departments||[])[0]||"unmapped";(byDept[d]=byDept[d]||[]).push(i);});
|
|
83
|
+
for(const [p,depts] of Object.entries(s.pillars)){
|
|
84
|
+
const a=ANG[p],col=css("--"+p),pp=P(a,170);
|
|
85
|
+
nodes.push({id:"p-"+p,position:{x:pp.x-50,y:pp.y-16},data:{label:h("div",{className:"node pillar",style:{background:col}},p+" · "+s.summary.by_pillar[p])},style:{border:"none",background:"transparent",padding:0,width:"auto"}});
|
|
86
|
+
edges.push({type:"straight",id:"e-v-"+p,source:"v",target:"p-"+p,style:{stroke:col,strokeWidth:3}});
|
|
87
|
+
const span=92,step=span/Math.max(1,depts.length-1);
|
|
88
|
+
depts.forEach((d,k)=>{
|
|
89
|
+
const da=a-span/2+k*step,dp=P(da,400),n=s.summary.by_department[d];
|
|
90
|
+
nodes.push({id:"d-"+d,position:{x:dp.x-70,y:dp.y-16},data:{label:h("div",{className:"node dept"+(n?"":" empty"),style:n?{borderColor:col,borderWidth:Math.min(6,1+n)}:{},title:s.departments[d]},d.replace(/-/g," ")+(n?" · "+n:""))},style:{border:"none",background:"transparent",padding:0,width:"auto"}});
|
|
91
|
+
edges.push({type:"straight",id:"e-"+p+"-"+d,source:"p-"+p,target:"d-"+d,style:{stroke:col,strokeOpacity:n?.8:.25,strokeDasharray:n?"":"4 4"}});
|
|
92
|
+
});
|
|
93
|
+
// Ideas spread evenly across the pillar's sector, in department order, on two alternating rings,
|
|
94
|
+
// so a crowded department never stacks its cards on a neighbour's.
|
|
95
|
+
const mine=depts.flatMap((d)=>(byDept[d]||[]).map((i)=>[d,i])),n=mine.length;
|
|
96
|
+
mine.forEach(([d,i],k)=>{
|
|
97
|
+
const ia=a-60+(k+.5)*120/Math.max(1,n),ip=P(ia,760+(k%2)*230);
|
|
98
|
+
nodes.push({id:i.id,position:{x:ip.x-110,y:ip.y-30},data:{label:h("div",{className:"node idea",style:{borderColor:col},title:i.text},
|
|
99
|
+
h("span",{className:"t"},i.text.length>110?i.text.slice(0,107)+"...":i.text),
|
|
100
|
+
i.exists?h("span",{className:"chip ex"},"exists"):null,...(i.records||[]).map((r)=>h("span",{className:"chip",key:r},r.replace(/^validation-/,""))))},style:{border:"none",background:"transparent",padding:0,width:"auto"}});
|
|
101
|
+
edges.push({type:"straight",id:"e-"+i.id,source:"d-"+d,target:i.id,style:{stroke:col,strokeOpacity:.5}});
|
|
102
|
+
(i.departments||[]).slice(1).forEach((d2)=>edges.push({type:"straight",id:"e-"+i.id+"-"+d2,source:i.id,target:"d-"+d2,style:{stroke:col,strokeOpacity:.25,strokeDasharray:"2 4"}}));
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
return {nodes,edges};
|
|
106
|
+
}
|
|
107
|
+
function side(s){
|
|
108
|
+
const tot=s.summary.ideas||1,sm=s.summary;
|
|
109
|
+
return '<h1>'+esc(s.name)+'</h1><div class="meta">'+esc(s.phase)+' phase · '+sm.ideas+' ideas · tagged by '+esc(s.meta.tagged_by)+' with Naive conventions</div>'+
|
|
110
|
+
'<div class="bar">'+Object.entries(sm.by_pillar).map(([p,n])=>'<span title="'+p+' '+n+'" style="width:'+(100*n/tot)+'%;background:var(--'+p+')"></span>').join("")+'</div>'+
|
|
111
|
+
'<div class="meta">'+Object.entries(sm.by_pillar).map(([p,n])=>'<b style="color:var(--'+p+')">'+p+'</b> '+n).join(" · ")+'</div>'+
|
|
112
|
+
'<h2>Where the ideas cluster</h2><ul>'+sm.strengths.map((x)=>'<li>'+esc(x.department.replace(/-/g," "))+' <span class="n">'+x.ideas+' ideas, '+Math.round(x.share*100)+'%</span></li>').join("")+'</ul>'+
|
|
113
|
+
'<h2>Never mentioned</h2><ul>'+(sm.unasked.length?sm.unasked.map((x)=>'<li>'+esc(x.record)+': '+esc(x.about)+'</li>').join(""):'<li>Every founder record has at least one idea</li>')+'</ul>'+
|
|
114
|
+
'<h2>Departments with no ideas</h2><div class="meta">'+Object.entries(sm.by_department).filter(([,n])=>!n).map(([d])=>esc(d.replace(/-/g," "))).join(", ")+'</div>'+
|
|
115
|
+
(sm.gaps.length?'<h2>Needed this phase, not in the dump</h2><ul>'+sm.gaps.map((g)=>'<li>'+esc(g.department)+'</li>').join("")+'</ul>':'')+
|
|
116
|
+
'<h2>Does a product exist?</h2><div>'+esc(sm.product.reading)+'</div><ul>'+sm.product.exists.map((x)=>'<li>'+esc(x.id)+': '+esc(x.text)+'</li>').join("")+'</ul>'+
|
|
117
|
+
'<h2>Next</h2><div><a href="/graph'+location.search+'">The idea graph</a> orders the ideas by what builds on what. <a href="/'+location.search+'">The decision board</a> ranks what the founder decides first.</div>';
|
|
118
|
+
}
|
|
119
|
+
let last="";
|
|
120
|
+
function App(){
|
|
121
|
+
const [g,setG]=React.useState({nodes:[],edges:[]});
|
|
122
|
+
React.useEffect(()=>{const tick=async()=>{try{const s=await (await fetch("ideas.json"+location.search,{cache:"no-store"})).json();
|
|
123
|
+
if(s.waiting||s.error){document.querySelector("#side .meta").textContent=s.waiting||s.error;return;}
|
|
124
|
+
const key=JSON.stringify(s.ideas);if(key===last)return;last=key;document.getElementById("side").innerHTML=side(s);setG(layout(s));}catch(e){}};tick();const t=setInterval(tick,2000);return()=>clearInterval(t);},[]);
|
|
125
|
+
return h(Flow,{nodes:g.nodes,edges:g.edges,fitView:true,minZoom:.1,nodesDraggable:true,key:g.nodes.length},h(RF.Background,{gap:24,size:1}),h(RF.Controls,null));
|
|
126
|
+
}
|
|
127
|
+
ReactDOM.createRoot(document.getElementById("flow")).render(h(App));
|
|
128
|
+
</script></body></html>`;
|
|
129
|
+
|
|
130
|
+
// The idea graph (EE-060): the structured form of the map, left to right by dependency, lanes and
|
|
131
|
+
// ideas top down by signal.
|
|
132
|
+
function graph(id) {
|
|
133
|
+
const dir = ventureDir(id);
|
|
134
|
+
if (!dir) return { error: `no venture ${id}` };
|
|
135
|
+
if (!existsSync(join(dir, GRAPH))) return { venture: id, waiting: "no idea graph yet" };
|
|
136
|
+
return { venture: id, name: JSON.parse(readFileSync(join(dir, "venture.json"), "utf8")).name, ...JSON.parse(readFileSync(join(dir, GRAPH), "utf8")) };
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
const GRAPH_PAGE = MAP_PAGE.replace("<title>Idea map</title>", "<title>Idea graph</title>").replace(/<script>\nconst h=React[\s\S]*<\/script><\/body>/, `<script>
|
|
140
|
+
const h=React.createElement, RF=window.ReactFlow, Flow=RF.default||RF.ReactFlow;
|
|
141
|
+
const esc=(s)=>String(s??"").replace(/[&<>"]/g,(c)=>({"&":"&","<":"<",">":">",'"':"""}[c]));
|
|
142
|
+
const COLW=300,ROWH=118,GAP=46;
|
|
143
|
+
function layout(g){
|
|
144
|
+
const nodes=[],edges=[],at={};let y=0;
|
|
145
|
+
const byId=Object.fromEntries(g.nodes.map((n)=>[n.id,n]));
|
|
146
|
+
g.lanes.forEach((l)=>{
|
|
147
|
+
const cols={};l.ideas.map((i)=>byId[i]).sort((a,b)=>b.signal-a.signal).forEach((n)=>{(cols[n.depth]=cols[n.depth]||[]).push(n);});
|
|
148
|
+
const rows=Math.max(...Object.values(cols).map((c)=>c.length));
|
|
149
|
+
nodes.push({id:l.id,position:{x:-250,y:y},data:{label:h("div",{className:"node lane "+l.mode},h("b",null,l.mode==="sync"?"In order":"On its own"),h("span",{className:"t"},(l.department||"").replace(/-/g," ")+" · signal "+l.signal))},style:{border:"none",background:"transparent",padding:0,width:"auto"},draggable:false,selectable:false});
|
|
150
|
+
Object.entries(cols).forEach(([d,ns])=>ns.forEach((n,k)=>{at[n.id]=1;
|
|
151
|
+
const col=getComputedStyle(document.documentElement).getPropertyValue("--"+(n.pillar||"business")).trim();
|
|
152
|
+
nodes.push({id:n.id,position:{x:d*COLW,y:y+k*ROWH},data:{label:h("div",{className:"node idea",style:{borderColor:col},title:n.text+"\\nhash "+n.hash},
|
|
153
|
+
h("span",{className:"sig",style:{width:Math.round(n.signal*100)+"%",background:col}}),
|
|
154
|
+
h("span",{className:"t"},n.text.length>100?n.text.slice(0,97)+"...":n.text),
|
|
155
|
+
h("span",{className:"chip"},n.id+" · "+n.signal),h("span",{className:"chip mono"},n.hash.slice(0,7)),n.exists?h("span",{className:"chip ex"},"exists"):null)},
|
|
156
|
+
sourcePosition:"right",targetPosition:"left",style:{border:"none",background:"transparent",padding:0,width:"auto"}});
|
|
157
|
+
}));
|
|
158
|
+
y+=rows*ROWH+GAP;
|
|
159
|
+
});
|
|
160
|
+
g.edges.forEach((e)=>edges.push({id:e.from+">"+e.to,source:e.from,target:e.to,type:"smoothstep",animated:false,markerEnd:{type:"arrowclosed"},style:{strokeWidth:1+2*e.p}}));
|
|
161
|
+
return {nodes,edges};
|
|
162
|
+
}
|
|
163
|
+
function side(g){
|
|
164
|
+
const top=g.nodes.slice(0,5);
|
|
165
|
+
return '<h1>'+esc(g.name)+': idea graph</h1><div class="meta">'+g.nodes.length+' ideas · '+g.edges.length+' dependencies judged by '+esc(g.judged_by)+' · Merkle root <code>'+esc(g.root)+'</code></div>'+
|
|
166
|
+
'<h2>Develop next (highest signal, nothing before it)</h2><ul>'+g.next.map((id)=>{const n=g.nodes.find((x)=>x.id===id);return '<li>'+esc(id)+' <span class="n">'+n.signal+'</span>: '+esc(n.text)+'</li>';}).join("")+'</ul>'+
|
|
167
|
+
'<h2>Lanes</h2><ul>'+g.lanes.map((l)=>'<li><b>'+(l.mode==="sync"?"In order":"On its own")+'</b> '+esc((l.department||"").replace(/-/g," "))+' <span class="n">'+l.ideas.length+' ideas, signal '+l.signal+'</span></li>').join("")+'</ul>'+
|
|
168
|
+
'<h2>Highest signal</h2><ul>'+top.map((n)=>'<li>'+esc(n.id)+' <span class="n">'+n.signal+' = need '+n.parts.need+', unblocks '+n.parts.unblocks+', concrete '+n.parts.concrete+', emphasis '+n.parts.emphasis+'</span></li>').join("")+'</ul>'+
|
|
169
|
+
'<h2>How to read it</h2><div class="meta">Left to right: an idea builds on the ideas to its left. Top down: higher signal first. A lane in order moves step by step; a lane on its own can move any time, beside the others. Each idea\\'s hash covers its text and everything it builds on.</div>'+
|
|
170
|
+
'<h2>Next</h2><div><a href="/map'+location.search+'">The idea map</a> · <a href="/kanban'+location.search+'">The idea routes</a> · <a href="/'+location.search+'">The decision board</a></div>';
|
|
171
|
+
}
|
|
172
|
+
let last="";
|
|
173
|
+
function App(){
|
|
174
|
+
const [g,setG]=React.useState({nodes:[],edges:[]});
|
|
175
|
+
React.useEffect(()=>{const tick=async()=>{try{const s=await (await fetch("graph.json"+location.search,{cache:"no-store"})).json();
|
|
176
|
+
if(s.waiting||s.error){document.querySelector("#side .meta").textContent=s.waiting||s.error;return;}
|
|
177
|
+
if(s.root===last)return;last=s.root;document.getElementById("side").innerHTML=side(s);setG(layout(s));}catch(e){}};tick();const t=setInterval(tick,2000);return()=>clearInterval(t);},[]);
|
|
178
|
+
return h(Flow,{nodes:g.nodes,edges:g.edges,fitView:true,minZoom:.1,key:g.nodes.length},h(RF.Background,{gap:24,size:1}),h(RF.Controls,null));
|
|
179
|
+
}
|
|
180
|
+
ReactDOM.createRoot(document.getElementById("flow")).render(h(App));
|
|
181
|
+
</script></body>`).replace(".react-flow__attribution{display:none}", ".react-flow__attribution{display:none}.node.idea{width:240px}.sig{display:block;height:3px;border-radius:2px;margin-bottom:6px}.chip.mono{font-family:ui-monospace,monospace}.node.lane{width:200px;border-style:dashed}.node.lane b{display:block}.node.lane.sync{border-color:var(--product)}.node.lane .t{color:var(--muted);font-size:11px}");
|
|
182
|
+
|
|
183
|
+
const PAGE = `<!doctype html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1">
|
|
184
|
+
<title>Decision board</title><style>
|
|
185
|
+
:root{--bg:#fbfaf7;--fg:#1d1c1a;--muted:#6b675f;--line:#e4e0d8;--card:#fff;--now:#1f6feb;--over:#c4321f;--wait:#9a6700;--done:#1a7f37}
|
|
186
|
+
@media (prefers-color-scheme:dark){:root{--bg:#141413;--fg:#ecebe6;--muted:#a19d94;--line:#2e2d2a;--card:#1c1b19;--now:#58a6ff;--over:#ff7b6b;--wait:#e3b341;--done:#56d364}}
|
|
187
|
+
*{box-sizing:border-box}body{margin:0;background:var(--bg);color:var(--fg);font:15px/1.45 system-ui,-apple-system,"Segoe UI",sans-serif}
|
|
188
|
+
header{padding:20px 24px;border-bottom:1px solid var(--line);display:flex;gap:16px;align-items:baseline;flex-wrap:wrap}
|
|
189
|
+
h1{font-size:20px;margin:0}.meta{color:var(--muted);font-size:13px}
|
|
190
|
+
main{display:grid;grid-template-columns:repeat(auto-fit,minmax(240px,1fr));gap:16px;padding:20px 24px}
|
|
191
|
+
section h2{font-size:12px;letter-spacing:.06em;text-transform:uppercase;color:var(--muted);margin:0 0 8px}
|
|
192
|
+
.card{background:var(--card);border:1px solid var(--line);border-left:4px solid var(--line);border-radius:8px;padding:10px 12px;margin-bottom:8px}
|
|
193
|
+
.card.overdue{border-left-color:var(--over)}.card.now{border-left-color:var(--now)}.card.waiting,.card.stale{border-left-color:var(--wait)}.card.done{border-left-color:var(--done)}
|
|
194
|
+
.q{font-weight:600}.id{font:12px ui-monospace,monospace;color:var(--muted)}.tag{display:inline-block;font-size:11px;padding:2px 6px;border-radius:99px;border:1px solid var(--line);margin:4px 4px 0 0;color:var(--muted)}
|
|
195
|
+
.tag.ask{border-color:var(--now);color:var(--now)}.tag.lock{border-color:var(--wait);color:var(--wait)}
|
|
196
|
+
.why{font-size:12px;color:var(--muted);margin-top:4px}.rank{float:right;font:12px ui-monospace,monospace;color:var(--now)}
|
|
197
|
+
.kit{padding:0 24px 8px}.kit ul{list-style:none;margin:0;padding:0;display:flex;flex-wrap:wrap;gap:8px}.kit li{background:var(--card);border:1px solid var(--line);border-radius:8px;padding:8px 10px;font-size:13px;max-width:320px}
|
|
198
|
+
.kit li b{display:block;margin-bottom:2px}.tag.start{border-color:var(--now);color:var(--now);font-weight:600}.tag.soon{opacity:.6}.tag.has{color:var(--done);border-color:var(--done)}
|
|
199
|
+
.log{padding:0 24px 24px}.log table{width:100%;border-collapse:collapse;font-size:13px}.log td{padding:4px 8px;border-top:1px solid var(--line)}.log td:first-child{color:var(--muted);white-space:nowrap}
|
|
200
|
+
</style></head><body><header><h1 id="title">Decision board</h1><span class="meta" id="meta"></span><a class="meta" href="/map">Idea map</a><a class="meta" href="/graph">Idea graph</a><a class="meta" href="/kanban">Idea routes</a><a class="meta" href="/site">Site presence</a></header><div class="kit"><section><h2>The kit for this phase (from need-now)</h2><ul id="kit"></ul></section><section id="money" hidden><h2 id="money-h">Money</h2><ul id="money-l"></ul></section></div><main id="cols"></main>
|
|
201
|
+
<div class="log"><section><h2>Provenance, latest first</h2><table id="log"></table></section></div>
|
|
202
|
+
<script>
|
|
203
|
+
const COLS=[["overdue","Overdue"],["now","Now"],["waiting","Waiting"],["stale","Stale"],["later","Later"],["deferred","Deferred"],["done","Done"]];
|
|
204
|
+
const esc=(s)=>String(s??"").replace(/[&<>"]/g,(c)=>({"&":"&","<":"<",">":">",'"':"""}[c]));
|
|
205
|
+
async function tick(){
|
|
206
|
+
try{
|
|
207
|
+
const s=await (await fetch("state.json"+location.search,{cache:"no-store"})).json();
|
|
208
|
+
if(s.error){document.getElementById("meta").textContent=s.error;return;}
|
|
209
|
+
document.getElementById("title").textContent="Decisions: "+s.venture;
|
|
210
|
+
document.getElementById("meta").textContent=s.phase+" phase · "+s.kind+" · updated "+new Date().toLocaleTimeString();
|
|
211
|
+
const ds=s.decisions.filter((d)=>d.source!=="root"||d.id==="foundation");
|
|
212
|
+
const ranked=ds.filter((d)=>d.rank!=null).map((d)=>d.id);
|
|
213
|
+
const kits={};(s.kit||[]).forEach((r)=>{(kits[r.kit||"other"]=kits[r.kit||"other"]||[]).push(r);});
|
|
214
|
+
const mark={"held":["has","has"],"suggest now":["start now","start"],"coming soon":["next phase","soon"]};
|
|
215
|
+
document.getElementById("kit").innerHTML=Object.entries(kits).sort((a,b)=>b[1].filter((r)=>r.status==="suggest now").length-a[1].filter((r)=>r.status==="suggest now").length).map(([k,rs])=>'<li><b>'+esc(k)+'</b>'+rs.map((r)=>'<span class="tag '+(mark[r.status]||["",""])[1]+'" title="'+esc(r.why)+'">'+esc(r.type.replace(/^validation-/,""))+' · '+esc((mark[r.status]||[r.status])[0])+'</span>').join("")+'</li>').join("");
|
|
216
|
+
const m=s.money,box=document.getElementById("money");box.hidden=!m;
|
|
217
|
+
if(m){const $=(x)=>(x.amount/100).toLocaleString(undefined,{style:"currency",currency:x.currency,maximumFractionDigits:0});
|
|
218
|
+
document.getElementById("money-h").textContent="Money, from "+(m.source==="rho"?"Rho":"Rho's sandbox")+(m.business?" ("+m.business+")":"")+": the system of record";
|
|
219
|
+
document.getElementById("money-l").innerHTML=[["Cash",$(m.cash)],["Monthly in",$(m.monthly_inflow)],["Monthly out",$(m.monthly_outflow)],["Monthly net",$(m.monthly_net)],["Runway",m.runway_months!=null?m.runway_months+" months":"not limited by burn"],["As of",new Date(m.as_of).toLocaleDateString()+" · synced "+new Date(m.synced_at).toLocaleString()]].map(([k,v])=>'<li><b>'+esc(k)+'</b>'+esc(v)+'</li>').join("");}
|
|
220
|
+
document.getElementById("cols").innerHTML=COLS.map(([k,label])=>{const xs=ds.filter((d)=>d.render===k);return xs.length?'<section><h2>'+label+' ('+xs.length+')</h2>'+xs.map((d)=>
|
|
221
|
+
'<div class="card '+k+'">'+(d.rank!=null?'<span class="rank">#'+(ranked.indexOf(d.id)+1)+'</span>':'')+'<div class="q">'+esc(d.question)+'</div><div class="id">'+esc(d.id)+' · '+esc(d.layer)+' · '+esc(d.boundary)+'</div>'+
|
|
222
|
+
(d.awaiting?'<span class="tag ask">agent wording, awaiting approval</span>':'')+
|
|
223
|
+
(d.lane==="async"&&k!=="done"?'<span class="tag">can move on its own</span>':'')+
|
|
224
|
+
(d.held_by?'<span class="tag lock">held by '+esc(d.held_by)+'</span>':'')+
|
|
225
|
+
(d.due&&k!=="done"?'<span class="tag">due '+esc(d.due)+' ('+d.due_in_hours+'h)</span>':'')+
|
|
226
|
+
(d.provenance&&d.provenance.approved_by?'<span class="tag">approved by '+esc(d.provenance.approved_by)+' in '+esc(d.provenance.phase)+'</span>':'')+
|
|
227
|
+
(d.unlocks?'<span class="tag">unlocks: '+esc(d.unlocks.join(", "))+'</span>':'')+
|
|
228
|
+
(d.outcomes?'<span class="tag">outcomes '+(d.outcomes.total-d.outcomes.pending)+'/'+d.outcomes.total+'</span>':'')+
|
|
229
|
+
(d.why&&d.why.length?'<div class="why">Why now: '+esc(d.why.join("; "))+'</div>':'')+
|
|
230
|
+
(d.rank!=null&&d.action?'<div class="why">'+(d.action.kind==="write"?'To do: ':'Settle: <code>')+esc(d.action.command)+(d.action.kind==="write"?'':'</code>')+'</div>':'')+'</div>').join("")+'</section>':"";}).join("");
|
|
231
|
+
document.getElementById("log").innerHTML=s.log.map((e)=>'<tr><td>'+esc(new Date(e.at).toLocaleTimeString())+'</td><td>'+esc(e.activity)+'</td><td>'+esc(e.entity)+'</td><td>'+esc(e.who)+' via '+esc(e.via)+'</td></tr>').join("");
|
|
232
|
+
}catch(e){document.getElementById("meta").textContent="Lost the board's server.";}
|
|
233
|
+
}
|
|
234
|
+
tick();setInterval(tick,1500);
|
|
235
|
+
</script></body></html>`;
|
|
236
|
+
|
|
237
|
+
|
|
238
|
+
// The routes (EE-061): where each idea went in the framework, and the decision docket, as two Kanban
|
|
239
|
+
// boards. Plain columns, not React Flow: a board is a list per stage, and the canvas is for structure.
|
|
240
|
+
function routesOf(id) {
|
|
241
|
+
const dir = ventureDir(id);
|
|
242
|
+
if (!dir) return { error: `no venture ${id}` };
|
|
243
|
+
return route(dir);
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
const PACKAGE_PAGE = `<!doctype html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1">
|
|
247
|
+
<title>Venture package</title><style>
|
|
248
|
+
:root{--bg:#fbfaf7;--fg:#1d1c1a;--muted:#6b675f;--line:#e4e0d8;--card:#fff;--col:#f2efe9;--accent:#1f6feb;--ok:#1a7f37;--low:#9a6700;--miss:#cf222e}
|
|
249
|
+
@media (prefers-color-scheme:dark){:root{--bg:#141413;--fg:#ecebe6;--muted:#a19d94;--line:#2e2d2a;--card:#1c1b19;--col:#191917;--accent:#58a6ff;--ok:#56d364;--low:#e3b341;--miss:#ff7b72}}
|
|
250
|
+
*{box-sizing:border-box}body{margin:0;background:var(--bg);color:var(--fg);font:14px/1.45 system-ui,-apple-system,"Segoe UI",sans-serif}
|
|
251
|
+
header{padding:18px 24px;border-bottom:1px solid var(--line);display:flex;gap:16px;align-items:baseline;flex-wrap:wrap}h1{font-size:20px;margin:0}.meta{color:var(--muted);font-size:13px}a{color:var(--accent)}
|
|
252
|
+
.pipe{display:flex;gap:6px;padding:14px 24px;flex-wrap:wrap;align-items:stretch}.step{background:var(--col);border-radius:8px;padding:7px 10px;min-width:118px;border-top:3px solid var(--line)}.step.done{border-top-color:var(--ok)}.step.part{border-top-color:var(--low)}.step .n{font-size:11px;color:var(--muted)}.step .l{font-weight:600}.step .d{font-size:12px;color:var(--muted)}.arrow{align-self:center;color:var(--muted)}
|
|
253
|
+
.wrap{display:grid;grid-template-columns:1fr 320px;gap:16px;padding:0 24px 28px}@media (max-width:900px){.wrap{grid-template-columns:1fr}}
|
|
254
|
+
h2{font-size:15px;margin:16px 0 4px}.sub{color:var(--muted);font-size:13px;margin:0 0 8px}.cols{display:grid;grid-template-columns:repeat(auto-fill,minmax(270px,1fr));gap:12px}
|
|
255
|
+
.pkg{background:var(--card);border:1px solid var(--line);border-top:3px solid var(--line);border-radius:10px;padding:10px 12px}.pkg.complete{border-top-color:var(--ok)}.pkg.review{border-top-color:var(--accent)}.pkg.building{border-top-color:var(--low)}.pkg.held,.pkg.next{opacity:.65}
|
|
256
|
+
.t{font-weight:600}.for{font-size:12px;color:var(--muted)}.bar{height:6px;background:var(--col);border-radius:3px;margin:8px 0 4px}.bar span{display:block;height:100%;background:var(--accent);border-radius:3px}
|
|
257
|
+
.rec{font-size:12.5px;display:flex;gap:6px;align-items:baseline;margin-top:5px}.dot{width:8px;height:8px;border-radius:50%;flex:none;transform:translateY(-1px)}.dot.confirmed{background:var(--ok)}.dot.agent,.dot.draft{background:var(--accent)}.dot.missing{background:transparent;border:1.5px solid var(--miss)}
|
|
258
|
+
.opt{color:var(--muted)}.nx{font-size:11.5px;color:var(--muted);margin-left:14px}.tag{display:inline-block;font-size:11px;border:1px solid var(--line);border-radius:99px;padding:1px 7px;margin-left:6px;color:var(--muted)}.tag.now{border-color:var(--ok);color:var(--ok)}
|
|
259
|
+
.un{font-size:12px;margin-top:8px;padding-top:6px;border-top:1px dashed var(--line);color:var(--muted)}
|
|
260
|
+
aside{background:var(--col);border-radius:10px;padding:12px;align-self:start}.qq{font-size:12.5px;margin:0 0 8px}.qq b{font-weight:600}.big{font-size:34px;font-weight:700}button{font:inherit;padding:7px 12px;border-radius:8px;border:1px solid var(--accent);background:var(--accent);color:#fff;cursor:pointer}
|
|
261
|
+
</style></head><body><header><h1 id="title">Venture package</h1><span class="meta" id="meta"></span><a class="meta" href="/map">Idea map</a><a class="meta" href="/kanban">Idea routes</a><a class="meta" href="/">Decision board</a><a class="meta" href="/site">Site presence</a></header>
|
|
262
|
+
<div class="pipe" id="pipe"></div>
|
|
263
|
+
<div class="wrap"><main>
|
|
264
|
+
<h2>Core packages open now</h2><p class="sub">Where the venture's records live. A package is complete when you have confirmed its records and everything it reads; then its automation may run.</p><div class="cols" id="now"></div>
|
|
265
|
+
<h2>Handoff packages</h2><p class="sub">Snapshots of core packages for one receiver. Ready when every package they draw on is complete.</p><div class="cols" id="handoff"></div>
|
|
266
|
+
<h2>Next phase and held</h2><div class="cols" id="later"></div></main><aside id="side"></aside></div>
|
|
267
|
+
<script>
|
|
268
|
+
const esc=(s)=>String(s??"").replace(/[&<>"]/g,(c)=>({"&":"&","<":"<",">":">",'"':"""}[c]));
|
|
269
|
+
const ST={complete:"complete",review:"waiting for your review",building:"still being written"};
|
|
270
|
+
const MK={confirmed:"confirmed",agent:"agent draft, to review",draft:"draft, to accept",missing:"not written"};
|
|
271
|
+
let T={};
|
|
272
|
+
const pct=(x)=>Math.round(x*100)+"%";
|
|
273
|
+
function rec(r){
|
|
274
|
+
const move=r.role==="reads"?'<span class="tag">read from '+esc(T[r.home]||r.home)+'</span>':r.moves==="now"?'<span class="tag now">moves now</span>':r.moves==="waits"?'<span class="tag">waits on '+esc((r.waits_on_titles||r.waits_on).join(", "))+'</span>':'';
|
|
275
|
+
return '<div class="rec"><span class="dot '+r.status+'"></span><span class="'+(r.role==="optional"||r.role==="reads"?'opt':'')+'">'+esc(r.title)+(r.role==="optional"?' (optional)':'')+' <span class="opt">· '+MK[r.status]+'</span>'+move+'</span></div>'+(r.role!=="reads"&&r.status!=="confirmed"&&r.moves==="now"&&r.route&&r.route.next?'<div class="nx">Next: '+esc(r.route.next.do)+'</div>':'');
|
|
276
|
+
}
|
|
277
|
+
function core(p,written){
|
|
278
|
+
if(p.in!=="now")return '<div class="pkg '+p.in+'"><div class="t">'+esc(p.title)+'<span class="tag">'+(p.in==="next"?'next phase':'held')+' · fit '+p.fit+'</span></div><div class="for">'+esc(p.why)+'</div></div>';
|
|
279
|
+
return '<div class="pkg '+p.state+'"><div class="t">'+esc(p.title)+'<span class="tag">'+ST[p.state]+'</span></div><div class="for">'+esc(p.purpose)+'</div><div class="bar"><span style="width:'+pct(p.progress)+'"></span></div><div class="for">'+pct(p.progress)+' · fit '+p.fit+(written&&written.includes(p.id)?' · <a href="package/'+p.id+'.md" target="_blank">read the document</a>':'')+'</div>'+
|
|
280
|
+
p.records.map(rec).join("")+'<div class="un">Unlocks: '+esc(p.unlocks.join(", "))+(p.planned.length?'<br>Not in Naive yet: '+esc(p.planned.join(", ")):'')+'</div></div>';
|
|
281
|
+
}
|
|
282
|
+
function handoff(p){
|
|
283
|
+
return '<div class="pkg '+(p.in==="now"?p.state:p.in)+'"><div class="t">'+esc(p.title)+'<span class="tag">'+(p.in==="now"?(p.state==="complete"?'ready':'waits on '+esc(p.waits_on.join(", "))):(p.in==="next"?'next phase':'held')+' · fit '+p.fit)+'</span></div><div class="for">For '+esc(p.for)+'. Draws on '+esc(p.from.map((x)=>T[x]||x).join(", "))+'.</div>'+
|
|
284
|
+
(p.in==="now"?'<div class="bar"><span style="width:'+pct(p.progress)+'"></span></div>':'<div class="for" style="margin-top:4px">'+esc(p.why)+'</div>')+'<div class="un">Unlocks: '+esc(p.unlocks.join(", "))+'</div></div>';
|
|
285
|
+
}
|
|
286
|
+
async function load(){
|
|
287
|
+
const r=await (await fetch("package.json"+location.search,{cache:"no-store"})).json();
|
|
288
|
+
if(r.error){document.getElementById("meta").textContent=r.error;return;}
|
|
289
|
+
T={};r.core.forEach((p)=>T[p.id]=p.title);r.not_modeled.forEach((p)=>T[p.id]=p.title);
|
|
290
|
+
document.getElementById("title").textContent="Venture package: "+r.name;
|
|
291
|
+
document.getElementById("meta").textContent=r.phase+" phase · "+pct(r.progress)+" across the open packages"+(r.versions&&r.versions.length?" · v"+r.versions.at(-1).version:"");
|
|
292
|
+
document.getElementById("pipe").innerHTML=r.pipeline.map((s,i)=>(i?'<span class="arrow">→</span>':'')+'<div class="step '+s.state+'"><div class="n">'+(i+1)+'</div><div class="l">'+(s.href?'<a href="'+s.href+'">'+esc(s.label)+'</a>':esc(s.label))+'</div><div class="d">'+esc(s.detail)+'</div></div>').join("");
|
|
293
|
+
document.getElementById("now").innerHTML=r.core.filter((p)=>p.in==="now").map((p)=>core(p,r.written)).join("")||'<div class="meta">None.</div>';
|
|
294
|
+
document.getElementById("handoff").innerHTML=r.handoff.filter((p)=>p.in!=="held").map(handoff).join("")||'<div class="meta">None fits yet: '+esc(r.handoff.map((p)=>p.title).join(", "))+' are held.</div>';
|
|
295
|
+
document.getElementById("later").innerHTML=r.core.filter((p)=>p.in!=="now").map((p)=>core(p)).join("")+r.handoff.filter((p)=>p.in==="held").map(handoff).join("");
|
|
296
|
+
document.getElementById("side").innerHTML='<div class="big">'+Math.round(r.progress*100)+'<small style="font-size:14px">%</small></div><div class="meta">Complete, so their automation may run: '+(r.complete.length?esc(r.complete.join(", ")):"none yet")+'</div>'+
|
|
297
|
+
'<p><button id="w">'+(r.written?'Write a new view':'Write the package view')+'</button></p>'+
|
|
298
|
+
'<h2>What decided this</h2>'+r.situation.questions.map((q)=>'<div class="qq">'+esc(q.ask)+' <b>'+(q.value==null?'open, yours to answer':q.value?'yes':'no')+'</b><br><span class="meta">'+(q.by==="records"?'from the records':q.by==="jev"?'Jev, '+q.p:'the records do not say')+'</span></div>').join("")+
|
|
299
|
+
(r.versions&&r.versions.length?'<h2>Versions</h2>'+r.versions.slice().reverse().map((v)=>'<div class="qq"><b>v'+v.version+'</b> '+esc(v.phase)+': '+esc(v.why)+'<br><span class="meta">'+esc(v.at)+' · tag '+esc(v.tag)+'</span></div>').join(""):'')+
|
|
300
|
+
'<h2>Assumed</h2><div class="qq">'+esc(r.assumes.note)+'</div>'+
|
|
301
|
+
'<h2>Not included</h2><div class="qq">Agents. '+esc(r.enhancements.agents)+'</div>'+r.not_modeled.map((x)=>'<div class="qq">'+esc(x.title)+'. '+esc(x.why)+'</div>').join("");
|
|
302
|
+
document.getElementById("w").onclick=async()=>{await fetch("package/write"+location.search,{method:"POST"});load();};
|
|
303
|
+
}
|
|
304
|
+
load();
|
|
305
|
+
</script></body></html>`;
|
|
306
|
+
|
|
307
|
+
const KANBAN_PAGE = `<!doctype html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1">
|
|
308
|
+
<title>Idea routes</title><style>
|
|
309
|
+
:root{--bg:#fbfaf7;--fg:#1d1c1a;--muted:#6b675f;--line:#e4e0d8;--card:#fff;--col:#f2efe9;--routed:#6b675f;--drafted:#1f6feb;--yours:#bf3989;--waiting:#9a6700;--done:#1a7f37}
|
|
310
|
+
@media (prefers-color-scheme:dark){:root{--bg:#141413;--fg:#ecebe6;--muted:#a19d94;--line:#2e2d2a;--card:#1c1b19;--col:#191917;--routed:#a19d94;--drafted:#58a6ff;--yours:#f778ba;--waiting:#e3b341;--done:#56d364}}
|
|
311
|
+
*{box-sizing:border-box}body{margin:0;background:var(--bg);color:var(--fg);font:14px/1.45 system-ui,-apple-system,"Segoe UI",sans-serif}
|
|
312
|
+
header{padding:18px 24px;border-bottom:1px solid var(--line);display:flex;gap:16px;align-items:baseline;flex-wrap:wrap}h1{font-size:20px;margin:0}.meta{color:var(--muted);font-size:13px}a{color:var(--drafted)}
|
|
313
|
+
h2.board{font-size:15px;margin:20px 24px 4px}.sub{margin:0 24px 10px;color:var(--muted);font-size:13px;max-width:760px}
|
|
314
|
+
.cols{display:grid;grid-template-columns:repeat(5,minmax(220px,1fr));gap:12px;padding:0 24px 8px;overflow-x:auto}
|
|
315
|
+
.col{background:var(--col);border-radius:10px;padding:10px;min-height:120px}.col h3{font-size:11px;letter-spacing:.06em;text-transform:uppercase;margin:0 0 8px;display:flex;justify-content:space-between}
|
|
316
|
+
.card{background:var(--card);border:1px solid var(--line);border-top:3px solid var(--line);border-radius:8px;padding:9px 10px;margin-bottom:8px}
|
|
317
|
+
.card.routed{border-top-color:var(--routed)}.card.drafted{border-top-color:var(--drafted)}.card.yours{border-top-color:var(--yours)}.card.waiting{border-top-color:var(--waiting)}.card.done{border-top-color:var(--done)}
|
|
318
|
+
.t{font-weight:600}.r{font:11px ui-monospace,monospace;color:var(--muted)}.q{font-size:13px}
|
|
319
|
+
.chip{display:inline-block;font-size:11px;border:1px solid var(--line);border-radius:99px;padding:1px 6px;margin:4px 3px 0 0;color:var(--muted);font-family:ui-monospace,monospace}
|
|
320
|
+
.tag{display:inline-block;font-size:11px;border-radius:99px;padding:1px 6px;margin:4px 3px 0 0;border:1px solid var(--line);color:var(--muted)}.tag.async{border-color:var(--done);color:var(--done)}
|
|
321
|
+
.next{font-size:12px;margin-top:6px;padding-top:6px;border-top:1px dashed var(--line)}.next b{color:var(--muted);font-weight:600}code{font-size:11px;word-break:break-all}
|
|
322
|
+
.foot{padding:4px 24px 28px;color:var(--muted);font-size:13px}
|
|
323
|
+
.pk{display:grid;grid-template-columns:repeat(auto-fill,minmax(200px,1fr));gap:10px;padding:0 24px 6px}.pb{background:var(--card);border:1px solid var(--line);border-radius:8px;padding:8px 10px}.pb .bar{height:8px;background:var(--col);border-radius:4px;margin:6px 0 3px;overflow:hidden}.pb .bar span{display:block;height:100%;background:var(--done);border-radius:4px;transition:width .6s}.pb.next{opacity:.6}
|
|
324
|
+
</style></head><body><header><h1 id="title">Idea routes</h1><span class="meta" id="meta"></span><a class="meta" href="/map">Idea map</a><a class="meta" href="/graph">Idea graph</a><a class="meta" href="/site">Site presence</a><a class="meta" href="/package">Venture package</a><a class="meta" href="/">Decision board</a></header>
|
|
325
|
+
<h2 class="board">The packages taking shape</h2><p class="sub">Your ideas land in the venture's packages. Each bar climbs as records are written and you confirm them; at 100% the package is complete and its automation may run. <a href="/package">Open the venture package</a></p><div class="pk" id="pkgs"></div>
|
|
326
|
+
<h2 class="board">From ideas to the framework</h2><p class="sub">Each card is a record the founder's ideas feed. It moves left to right as an agent drafts it and the founder settles it; the next step comes from the framework, not from a guess.</p><div class="cols" id="dest"></div>
|
|
327
|
+
<div class="foot" id="loose"></div>
|
|
328
|
+
<h2 class="board">The decision docket</h2><p class="sub">Every open decision, by what moves it next, traced to the ideas it comes from. Ranked ones show their place in line.</p><div class="cols" id="docket"></div>
|
|
329
|
+
<script>
|
|
330
|
+
const esc=(s)=>String(s??"").replace(/[&<>"]/g,(c)=>({"&":"&","<":"<",">":">",'"':"""}[c]));
|
|
331
|
+
let text={},PK={};const chip=(id)=>'<span class="chip" title="'+esc(text[id])+'">'+esc(id)+'</span>';
|
|
332
|
+
const laneTag=(x)=>x.lane==="async"?'<span class="tag async">on its own</span>':'<span class="tag">in order'+(x.order?' #'+x.order:'')+'</span>';
|
|
333
|
+
const board=(el,stages,items,card)=>{document.getElementById(el).innerHTML=stages.map(([k,l])=>{const xs=items.filter((x)=>x.stage===k);return '<div class="col"><h3><span>'+esc(l)+'</span><span>'+xs.length+'</span></h3>'+xs.map(card).join("")+'</div>';}).join("");};
|
|
334
|
+
async function tick(){
|
|
335
|
+
try{
|
|
336
|
+
const s=await (await fetch("routes.json"+location.search,{cache:"no-store"})).json();
|
|
337
|
+
if(s.error||s.waiting){document.getElementById("meta").textContent=s.error||s.waiting;return;}
|
|
338
|
+
text={};s.destinations.forEach((d)=>d.ideas.forEach((i)=>text[i.id]=i.text));s.loose.forEach((i)=>text[i.id]=i.text);
|
|
339
|
+
document.getElementById("title").textContent="Idea routes: "+(s.name||s.venture);
|
|
340
|
+
document.getElementById("meta").textContent=s.phase+" phase · "+s.destinations.length+" destinations · updated "+new Date().toLocaleTimeString();
|
|
341
|
+
try{const k=await (await fetch("package.json"+location.search,{cache:"no-store"})).json();if(k.core){PK={};k.core.forEach((p)=>PK[p.id]=p.title);document.getElementById("pkgs").innerHTML=k.core.filter((p)=>p.in!=="held").map((p)=>'<div class="pb '+p.in+'"><div class="t">'+esc(p.title)+'</div><div class="bar"><span style="width:'+Math.round(p.progress*100)+'%"></span></div><div class="r">'+(p.in==="now"?Math.round(p.progress*100)+'% · '+(p.moving.length?p.moving.length+' moving now':'nothing can move yet'):'opens next phase')+'</div></div>').join("");}}catch(e){}
|
|
342
|
+
board("dest",s.stages,s.destinations,(x)=>'<div class="card '+x.stage+'"><div class="t">'+esc(x.title)+'</div><div class="r">'+(x.package?esc(PK[x.package]||x.package)+' package · ':'')+esc(x.route)+(x.signal!=null?' · signal '+x.signal:'')+'</div>'+laneTag(x)+'<div>'+x.ideas.map((i)=>chip(i.id)).join("")+'</div>'+
|
|
343
|
+
(x.next?'<div class="next"><b>Next, '+esc(x.next.by||"nobody yet")+':</b> '+esc(x.next.do)+(x.next.command?'<br><code>'+esc(x.next.command)+'</code>':'')+'</div>':'')+'</div>');
|
|
344
|
+
document.getElementById("loose").innerHTML=(s.loose.length?'Loose, on the map only: '+s.loose.map((i)=>chip(i.id)).join(""):'')+(s.gaps.length?' · Nothing in the dump feeds: '+esc(s.gaps.map((g)=>g.title).join(", ")):'');
|
|
345
|
+
const ranked=s.docket.filter((d)=>d.rank!=null).sort((a,b)=>b.rank-a.rank).map((d)=>d.id);
|
|
346
|
+
board("docket",s.stages,s.docket.slice().sort((a,b)=>(b.rank??-1)-(a.rank??-1)),(d)=>'<div class="card '+d.stage+'">'+(d.rank!=null?'<span class="r" style="float:right">#'+(ranked.indexOf(d.id)+1)+'</span>':'')+'<div class="q">'+esc(d.question)+'</div><div class="r">'+esc(d.id)+(d.destination?' · '+esc(d.destination):'')+'</div>'+
|
|
347
|
+
(d.lane==="async"&&d.stage!=="done"?'<span class="tag async">on its own</span>':'')+(d.held_by?'<span class="tag">held by '+esc(d.held_by)+'</span>':'')+(d.awaiting?'<span class="tag">agent wording</span>':'')+
|
|
348
|
+
(d.ideas.length?'<div>'+(d.cited?'':'<span class="tag">via its destination</span>')+d.ideas.map(chip).join("")+'</div>':'')+'</div>');
|
|
349
|
+
}catch(e){document.getElementById("meta").textContent="Lost the board's server.";}
|
|
350
|
+
}
|
|
351
|
+
tick();setInterval(tick,2000);
|
|
352
|
+
</script></body></html>`;
|
|
353
|
+
|
|
354
|
+
// Site presence (EE-062, EE-063): a founder pastes their website and sees its sitemap, each route
|
|
355
|
+
// rated on what the audiences it must speak to need from it, with the reasons. Local, like the rest of the board.
|
|
356
|
+
const SITE_PAGE = `<!doctype html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1">
|
|
357
|
+
<title>Site presence</title><link rel="stylesheet" href="/vendor/reactflow@11.11.4/dist/style.css"><style>
|
|
358
|
+
:root{--bg:#fbfaf7;--fg:#1d1c1a;--muted:#6b675f;--line:#e4e0d8;--card:#fff;--col:#f2efe9;--accent:#1f6feb;--miss:#c4321f;--low:#9a6700;--ok:#1a7f37}
|
|
359
|
+
@media (prefers-color-scheme:dark){:root{--bg:#141413;--fg:#ecebe6;--muted:#a19d94;--line:#2e2d2a;--card:#1c1b19;--col:#191917;--accent:#58a6ff;--miss:#ff7b6b;--low:#e3b341;--ok:#56d364}}
|
|
360
|
+
*{box-sizing:border-box}body{margin:0;background:var(--bg);color:var(--fg);font:14px/1.45 system-ui,-apple-system,"Segoe UI",sans-serif}
|
|
361
|
+
header{padding:16px 24px;border-bottom:1px solid var(--line);display:flex;gap:16px;align-items:baseline;flex-wrap:wrap}h1{font-size:20px;margin:0}.meta{color:var(--muted);font-size:13px}a{color:var(--accent)}
|
|
362
|
+
form{padding:14px 24px;display:grid;grid-template-columns:1fr 1fr auto;gap:10px;align-items:start}@media (max-width:860px){form{grid-template-columns:1fr}}
|
|
363
|
+
input[type=url],textarea{width:100%;font:inherit;padding:9px 12px;border:1px solid var(--line);border-radius:8px;background:var(--card);color:var(--fg)}textarea{height:40px;resize:vertical}
|
|
364
|
+
.flags{display:flex;gap:12px;flex-wrap:wrap;align-items:center}button{font:inherit;font-weight:600;padding:9px 16px;border:0;border-radius:8px;background:var(--accent);color:#fff;cursor:pointer}button:disabled{opacity:.5}label{color:var(--muted);font-size:13px}
|
|
365
|
+
#main{display:grid;grid-template-columns:1fr 380px;border-top:1px solid var(--line);height:78vh}@media (max-width:860px){#main{grid-template-columns:1fr;height:auto}#flow{height:60vh}}
|
|
366
|
+
#flow{min-height:0}aside{border-left:1px solid var(--line);padding:14px 16px;overflow:auto}aside h2{font-size:11px;letter-spacing:.06em;text-transform:uppercase;color:var(--muted);margin:14px 0 6px}
|
|
367
|
+
.score{font-size:48px;font-weight:700;line-height:1}.score small{font-size:16px;color:var(--muted)}
|
|
368
|
+
.aud{display:grid;grid-template-columns:130px 1fr 32px;gap:3px 8px;align-items:center;font-size:12px}.bar{height:7px;background:var(--col);border-radius:4px;overflow:hidden}.bar span{display:block;height:100%;background:var(--accent)}.dim{opacity:.45}
|
|
369
|
+
.node{background:var(--card);color:var(--fg);border:1px solid var(--line);border-top:4px solid var(--line);border-radius:8px;padding:7px 9px;width:170px;font-size:13px;text-align:left;cursor:pointer}
|
|
370
|
+
.node.home{width:230px;border-width:2px}.node .p{font:11px ui-monospace,monospace;color:var(--muted);word-break:break-all}.node .t{font-weight:600}.node.unread{opacity:.6}.node.ghost{border-style:dashed;background:transparent}
|
|
371
|
+
.node .rt{float:right;font-weight:700}.node.r-ok{border-top-color:var(--ok)}.node.r-low{border-top-color:var(--low)}.node.r-miss{border-top-color:var(--miss)}.node.ghost{border-color:var(--miss)}
|
|
372
|
+
.band{font-size:11px;letter-spacing:.06em;text-transform:uppercase;color:var(--muted);background:transparent;border:none}
|
|
373
|
+
.it{border:1px solid var(--line);border-radius:8px;padding:8px 10px;margin:0 0 8px;background:var(--card)}.it .t{font-weight:600}.it.miss{border-left:4px solid var(--miss)}.it.low{border-left:4px solid var(--low)}.it.ok{border-left:4px solid var(--ok)}
|
|
374
|
+
.why{font-size:12px;color:var(--muted);margin-top:4px}.why b{color:var(--fg);font-weight:600}.fix{display:inline-block;font-size:11px;border-radius:99px;padding:1px 6px;border:1px solid var(--miss);color:var(--miss);margin-left:6px}
|
|
375
|
+
.icp{font-size:12px;background:var(--col);border-radius:8px;padding:8px 10px}.chip{display:inline-block;font-size:11px;border:1px solid var(--line);border-radius:99px;padding:1px 6px;margin:3px 3px 0 0}
|
|
376
|
+
.node.planned{border-top-color:var(--accent)}
|
|
377
|
+
.react-flow__attribution{display:none}.react-flow__handle{opacity:0;pointer-events:none}
|
|
378
|
+
</style></head><body><header><h1>Site presence</h1><span class="meta">Your sitemap, each route rated on what your audiences need from it, with the reasons</span><a class="meta" href="/kanban">Idea routes</a><a class="meta" href="/">Decision board</a></header>
|
|
379
|
+
<form id="f"><div><input type="url" id="url" placeholder="https://your-site.com" required><div class="flags" style="margin-top:8px"><label><input type="checkbox" id="raising"> raising</label><label><input type="checkbox" id="hiring"> hiring</label><label><input type="checkbox" id="contributors"> inviting contributors</label></div></div>
|
|
380
|
+
<div style="display:flex;gap:8px;flex-wrap:wrap"><button id="go">Read my site</button><button type="button" id="plan" style="background:transparent;color:var(--accent);border:1px solid var(--accent)">Plan from my venture</button></div></form>
|
|
381
|
+
<div id="main"><div id="flow"></div><aside id="side"><div class="meta">Paste a link to your marketing site or your product.</div></aside></div>
|
|
382
|
+
<script src="/vendor/react@18.3.1/umd/react.production.min.js"></script>
|
|
383
|
+
<script src="/vendor/react-dom@18.3.1/umd/react-dom.production.min.js"></script>
|
|
384
|
+
<script src="/vendor/reactflow@11.11.4/dist/umd/index.js"></script>
|
|
385
|
+
<script>
|
|
386
|
+
const h=React.createElement, RF=window.ReactFlow, Flow=RF.default||RF.ReactFlow;
|
|
387
|
+
const esc=(s)=>String(s??"").replace(/[&<>"]/g,(c)=>({"&":"&","<":"<",">":">",'"':"""}[c]));
|
|
388
|
+
const LABEL=__LABELS__;let R=null,setG=null;
|
|
389
|
+
const cls=(n)=>n.ghost?"ghost":n.rating==null?"unread":n.rating>=80?"r-ok":n.rating>=50?"r-low":"r-miss";
|
|
390
|
+
const itemOf=(id)=>R.items.find((x)=>x.id===id);
|
|
391
|
+
function evidence(x){return x.evidence.map((e)=>'<div class="why"><b>'+esc(LABEL[e.audience])+'</b> need '+e.need+' (weight '+e.weight+' x importance '+e.importance+'): '+esc(e.why)+'</div>').join("");}
|
|
392
|
+
function itemCard(x){return '<div class="it '+(x.score===1?'ok':x.score?'low':'miss')+'"><div class="t">'+esc(x.label)+(R.fix_first.includes(x.id)?'<span class="fix">fix first</span>':'')+'</div><div class="why">'+esc(x.note)+'</div>'+evidence(x)+'<div class="why">From the '+esc(x.record)+' record · needs to be '+esc(x.required)+'</div></div>';}
|
|
393
|
+
function overview(){
|
|
394
|
+
const aud=Object.entries(R.audiences).sort((a,b)=>b[1].weight-a[1].weight).map(([k,v])=>'<span class="'+(v.weight<0.5?'dim':'')+'">'+esc(LABEL[k])+'</span><div class="bar '+(v.weight<0.5?'dim':'')+'"><span style="width:'+(v.score??0)+'%"></span></div><span class="'+(v.weight<0.5?'dim':'')+'">'+(v.score??"")+'</span>').join("");
|
|
395
|
+
const t=R.icp.types.map((x)=>'<span class="chip">'+esc(x.label)+(x.p!=null?' '+x.p:'')+'</span>').join("");
|
|
396
|
+
return '<div class="score">'+R.score+'<small>/100</small></div><div class="meta">'+esc(R.site)+' · '+R.pages.length+' pages read via '+esc(R.read_via)+', judged by '+esc(R.judged_by)+'</div>'+
|
|
397
|
+
'<h2>Who the site is for</h2><div class="icp">'+(R.icp.says&&R.icp.says.length?R.icp.says.slice(0,4).map((x)=>'“'+esc(x)+'”').join("<br>"):esc(R.icp.text.slice(0,240)))+'<div class="meta">Read from the site: '+esc(R.icp.cite)+'</div>'+t+'</div>'+
|
|
398
|
+
'<h2>By audience</h2><div class="aud">'+aud+'</div><div class="meta" style="margin-top:4px">Faded audiences are not a priority now, so they count little.</div>'+
|
|
399
|
+
'<h2>Fix first</h2>'+(R.fix_first.length?R.fix_first.map((id)=>itemCard(itemOf(id))).join(""):'<div class="meta">Nothing urgent.</div>')+
|
|
400
|
+
'<h2>Every item</h2>'+R.items.filter((x)=>!R.fix_first.includes(x.id)).map(itemCard).join("")+'<div class="meta">Click a route on the sitemap to see what belongs there.</div>';
|
|
401
|
+
}
|
|
402
|
+
function routePanel(n){
|
|
403
|
+
const list=(ids,none)=>ids.length?ids.map((id)=>itemCard(itemOf(id))).join(""):'<div class="meta">'+none+'</div>';
|
|
404
|
+
return '<a href="#" onclick="document.getElementById(\\'side\\').innerHTML=overview();return false">Back to the overview</a><h2>'+esc(n.ghost?'Suggested page':'Route')+'</h2><div class="t" style="font-weight:600">'+esc(n.label)+'</div><div class="meta">'+esc(n.path)+' · '+esc(n.type)+' page · '+esc(n.level)+(n.read?'':' · not read')+(n.rating!=null?' · rating '+n.rating:'')+'</div>'+
|
|
405
|
+
(n.ghost?'<div class="why">Your priorities call for this page and the site has none.</div>':'')+
|
|
406
|
+
'<h2>Belongs here, missing</h2>'+list(n.missing,n.read||n.ghost?'Nothing missing.':'Not read in this scan.')+
|
|
407
|
+
'<h2>Belongs here, present</h2>'+list(n.expected.filter((id)=>n.found.includes(id)),'None.')+
|
|
408
|
+
'<h2>Also found here</h2>'+list(n.found.filter((id)=>!n.expected.includes(id)),'None.');
|
|
409
|
+
}
|
|
410
|
+
// A sitemap, not a canvas of ideas: the home page on top, the pages in its navigation in one row,
|
|
411
|
+
// the pages linked elsewhere from it (the footer) in a row below, and each page's own routes listed
|
|
412
|
+
// under it. Unread routes of no known type fold into one "more" node per parent.
|
|
413
|
+
function layout(r){
|
|
414
|
+
const nodes=[],edges=[],W=190,GAP=20,ROW=130;
|
|
415
|
+
const kids={};r.sitemap.forEach((n)=>{if(n.parent!=null)(kids[n.parent]=kids[n.parent]||[]).push(n);});
|
|
416
|
+
const keep=(n)=>n.read||n.ghost||n.planned||n.type!=="other";
|
|
417
|
+
const card=(n)=>h("div",{className:"node "+(n.planned?"planned":cls(n))+(n.path==="/"?" home":"")},n.rating!=null?h("span",{className:"rt"},n.rating):null,h("div",{className:"t"},n.label.slice(0,40)),h("div",{className:"p"},n.path),
|
|
418
|
+
n.planned?n.sections.map((x)=>h("div",{key:x.item,className:"p"},"<"+x.component+">"+(x.when?" *":""))):n.missing.length?h("div",{className:"p"},"missing: "+n.missing.join(", ")):null);
|
|
419
|
+
const put=(n,x,y)=>{nodes.push({id:n.path,position:{x,y},data:{label:card(n),n},style:{border:"none",background:"transparent",padding:0,width:"auto"}});};
|
|
420
|
+
const home=r.sitemap.find((n)=>n.path==="/");
|
|
421
|
+
const top=(kids["/"]||[]);
|
|
422
|
+
const col=(n,x,y)=>{put(n,x,y);let yy=y+ROW*0.75;const ks=(kids[n.path]||[]);const shown=ks.filter(keep).slice(0,4),more=ks.length-shown.length;
|
|
423
|
+
shown.forEach((k)=>{put(k,x+16,yy);edges.push({id:n.path+">"+k.path,source:n.path,target:k.path,type:"smoothstep"});yy+=ROW*0.62;});
|
|
424
|
+
if(more>0){nodes.push({id:n.path+"+more",position:{x:x+16,y:yy},data:{label:h("div",{className:"node unread"},"+"+more+" more routes")},style:{border:"none",background:"transparent",padding:0,width:"auto"}});edges.push({id:n.path+">more",source:n.path,target:n.path+"+more",type:"smoothstep"});yy+=ROW*0.62;}
|
|
425
|
+
return yy;};
|
|
426
|
+
const bands=[["nav","In the navigation"],["linked","Linked from the home page"]];
|
|
427
|
+
let y=ROW*1.2;
|
|
428
|
+
bands.forEach(([lvl,title])=>{
|
|
429
|
+
let xs=top.filter((n)=>n.level===lvl||(lvl==="linked"&&n.level==="anywhere"));
|
|
430
|
+
const shown=xs.filter(keep),hidden=xs.length-shown.length;
|
|
431
|
+
if(!shown.length&&!hidden)return;
|
|
432
|
+
nodes.push({id:"band-"+lvl,position:{x:0,y:y-34},data:{label:h("div",{className:"band"},title+" ("+xs.length+")")},style:{border:"none",background:"transparent",padding:0,width:"auto"},selectable:false});
|
|
433
|
+
// Rated and suggested pages first; rows of at most seven, so a long footer wraps instead of stretching.
|
|
434
|
+
shown.sort((a,b)=>(b.ghost?2:b.read?1:0)-(a.ghost?2:a.read?1:0));
|
|
435
|
+
const cells=[...shown,...(hidden>0?[{more:hidden}]:[])];
|
|
436
|
+
for(let k=0;k<cells.length;k+=7){let x=0,maxY=y+ROW*0.7;
|
|
437
|
+
cells.slice(k,k+7).forEach((n)=>{
|
|
438
|
+
if(n.more){nodes.push({id:"more-"+lvl,position:{x,y},data:{label:h("div",{className:"node unread"},"+"+n.more+" more routes, not read")},style:{border:"none",background:"transparent",padding:0,width:"auto"}});edges.push({id:"/>more-"+lvl,source:"/",target:"more-"+lvl,type:"smoothstep"});}
|
|
439
|
+
else{maxY=Math.max(maxY,col(n,x,y));edges.push({id:"/>"+n.path,source:"/",target:n.path,type:"smoothstep",style:n.ghost?{strokeDasharray:"4 4"}:{}});}
|
|
440
|
+
x+=W+GAP;});
|
|
441
|
+
y=maxY+ROW*0.3;}
|
|
442
|
+
y+=ROW*0.4;
|
|
443
|
+
});
|
|
444
|
+
const width=Math.max(...nodes.map((n)=>n.position.x))+W;put(home,width/2-115,0);
|
|
445
|
+
return {nodes,edges};
|
|
446
|
+
}
|
|
447
|
+
function App(){const [g,sg]=React.useState({nodes:[],edges:[]});setG=sg;
|
|
448
|
+
return h(Flow,{nodes:g.nodes,edges:g.edges,fitView:true,minZoom:.1,key:g.nodes.length+":"+(R?R.site:P?"plan":""),onNodeClick:(e,n)=>{if(n.data&&n.data.n)document.getElementById("side").innerHTML=n.data.n.planned?plannedPanel(n.data.n):routePanel(n.data.n);}},h(RF.Background,{gap:24,size:1}),h(RF.Controls,null));}
|
|
449
|
+
ReactDOM.createRoot(document.getElementById("flow")).render(h(App));
|
|
450
|
+
// The planned site (EE-064): the same tree, planned from the venture's records instead of read.
|
|
451
|
+
let P=null;
|
|
452
|
+
function planOverview(){
|
|
453
|
+
const o=P.offerings.map((x)=>'<div class="it"><div class="t">'+esc(x.name)+' <span class="chip">'+esc(x.offering)+'</span></div><div class="why">Offered to '+esc(x.offered_to.join(", ")||"nobody yet")+(x.serves.length?', serving '+esc(x.serves.join(", ")):'')+' (from the '+esc(x.from)+')</div></div>').join("");
|
|
454
|
+
return '<div class="t" style="font-weight:600;font-size:16px">Planned site for '+esc(P.name)+'</div><div class="meta">Planned from the venture\\'s records by '+esc(P.planned_by)+'. The product decides the audience.</div>'+
|
|
455
|
+
'<h2>Offerings</h2>'+(o||'<div class="meta">No live or building products yet.</div>')+
|
|
456
|
+
'<h2>Who the site is for</h2><div class="icp">'+(P.audience.offered_to.map((p)=>esc(p.who)).join("<br>")||'No accepted ICP yet: planned for customers in general.')+'<div class="meta">'+esc(P.audience.note)+'</div>'+P.audience.types.map((x)=>'<span class="chip">'+esc(x.label)+'</span>').join("")+'</div>'+
|
|
457
|
+
(P.omitted.length?'<h2>Not now</h2>'+P.omitted.map((o)=>'<div class="why"><b>'+esc(o.label)+'</b>: '+esc(o.why)+(o.when?'; '+esc(o.when):'')+'</div>').join(""):'')+
|
|
458
|
+
'<h2>Handoff</h2><div class="why">'+esc(P.handoff.note)+'</div><div class="meta" style="margin-top:6px">Click a page to see its sections, components and reasons. * shows only under a condition.</div>';
|
|
459
|
+
}
|
|
460
|
+
function plannedPanel(n){
|
|
461
|
+
return '<a href="#" onclick="document.getElementById(\\'side\\').innerHTML=planOverview();return false">Back to the plan</a><h2>Planned page</h2><div class="t" style="font-weight:600">'+esc(n.label)+'</div><div class="meta">'+esc(n.path)+' · '+esc(n.type)+' page · '+esc(n.level)+'</div><h2>Sections, in order</h2>'+
|
|
462
|
+
n.sections.map((x)=>'<div class="it"><div class="t"><'+esc(x.component)+'> '+esc(x.label)+'</div><div class="why">Need '+x.need+' for '+esc(x.for.map((a)=>LABEL[a]).join(", "))+(x.when?' · shown '+esc(x.when):'')+'</div>'+x.why.map((w)=>'<div class="why">'+esc(w)+'</div>').join("")+
|
|
463
|
+
'<div class="why">Draws on '+esc(x.source.record)+(x.source.exists?'':' (not written yet)')+'; the public variant goes to <code>'+esc(x.variant)+'</code></div></div>').join("");
|
|
464
|
+
}
|
|
465
|
+
function showPlan(p){
|
|
466
|
+
if(p.error){document.getElementById("side").innerHTML='<p>'+esc(p.error)+'</p>';return;}
|
|
467
|
+
P=p;R=null;document.getElementById("side").innerHTML=planOverview();
|
|
468
|
+
const nodes=p.pages.map((x)=>({path:x.path,parent:x.parent,label:x.label,type:x.type,level:x.level,read:true,rating:null,expected:[],found:[],missing:[],planned:true,sections:x.sections}));
|
|
469
|
+
if(setG)setG(layout({sitemap:nodes,site:"plan"}));
|
|
470
|
+
}
|
|
471
|
+
document.getElementById("plan").onclick=async()=>{const q=new URLSearchParams();["raising","hiring","contributors"].forEach((k)=>{if(document.getElementById(k).checked)q.set(k,"1");});
|
|
472
|
+
try{showPlan(await (await fetch("site/plan.json?"+q,{cache:"no-store"})).json());}catch(err){showPlan({error:"The plan failed: "+err.message});}};
|
|
473
|
+
function show(r){
|
|
474
|
+
if(r.error){document.getElementById("side").innerHTML='<p>'+esc(r.error)+'</p>';return;}
|
|
475
|
+
R=r;document.getElementById("side").innerHTML=overview();if(setG)setG(layout(r));
|
|
476
|
+
}
|
|
477
|
+
document.getElementById("f").onsubmit=async(e)=>{e.preventDefault();const b=document.getElementById("go");b.disabled=true;b.textContent="Reading...";
|
|
478
|
+
const q=new URLSearchParams({url:document.getElementById("url").value});
|
|
479
|
+
["raising","hiring","contributors"].forEach((k)=>{if(document.getElementById(k).checked)q.set(k,"1");});
|
|
480
|
+
try{show(await (await fetch("site/scan?"+q,{method:"POST"})).json());}catch(err){show({error:"The scan failed: "+err.message});}b.disabled=false;b.textContent="Read my site";};
|
|
481
|
+
fetch("site.json"+location.search).then((r)=>r.json()).then((r)=>{if(r&&r.site&&r.sitemap){document.getElementById("url").value=r.site;["raising","hiring","contributors"].forEach((k)=>{document.getElementById(k).checked=!!(r.facts||{})[k];});setTimeout(()=>show(r),50);}}).catch(()=>{});
|
|
482
|
+
</script></body></html>`.replace("__LABELS__", JSON.stringify(Object.fromEntries(Object.entries(MATRIX.audiences).map(([k, v]) => [k, v.label]))));
|
|
483
|
+
|
|
484
|
+
// The map's three libraries, pinned, fetched from unpkg once and kept on disk, so the canvas works
|
|
485
|
+
// offline after its first load. Only these paths are served.
|
|
486
|
+
const VENDOR = new Set(["react@18.3.1/umd/react.production.min.js", "react-dom@18.3.1/umd/react-dom.production.min.js", "reactflow@11.11.4/dist/umd/index.js", "reactflow@11.11.4/dist/style.css"]);
|
|
487
|
+
const cache = join(homedir(), ".cache", "naive-board");
|
|
488
|
+
async function vendor(path) {
|
|
489
|
+
const f = join(cache, path.replace(/\//g, "__"));
|
|
490
|
+
if (existsSync(f)) return readFileSync(f);
|
|
491
|
+
const r = await fetch(`https://unpkg.com/${path}`);
|
|
492
|
+
if (!r.ok) throw new Error(`unpkg answered ${r.status}`);
|
|
493
|
+
const body = Buffer.from(await r.arrayBuffer());
|
|
494
|
+
mkdirSync(cache, { recursive: true });
|
|
495
|
+
writeFileSync(f, body);
|
|
496
|
+
return body;
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
// The venture package (EE-067), with the pipeline that led to it: each step's state, from the dump on.
|
|
500
|
+
function packageOf(dir, writeIt = false) {
|
|
501
|
+
const r = packages(dir);
|
|
502
|
+
if (writeIt) writePackage(dir, r);
|
|
503
|
+
const has = (f) => existsSync(join(dir, f));
|
|
504
|
+
let rt = null; try { rt = route(dir); } catch {}
|
|
505
|
+
const d = decisions(dir).decisions;
|
|
506
|
+
const settled = d.filter((x) => ["decided", "accepted"].includes(x.status)).length;
|
|
507
|
+
const open = r.core.filter((p) => p.in === "now");
|
|
508
|
+
const recs = [...new Map(open.flatMap((p) => p.records.filter((x) => x.role !== "optional")).map((x) => [x.record, x])).values()];
|
|
509
|
+
const written = has(join(PACKAGE, "venture-package.json")) ? open.filter((p) => has(join(PACKAGE, `${p.id}.md`))).map((p) => p.id) : null;
|
|
510
|
+
const manifest = has(join(PACKAGE, "venture-package.json")) ? JSON.parse(readFileSync(join(dir, PACKAGE, "venture-package.json"), "utf8")) : null;
|
|
511
|
+
const n = (f) => recs.filter(f).length;
|
|
512
|
+
const routedTo = rt?.destinations ? new Set(rt.destinations.filter((x) => x.ideas.length && x.package).map((x) => x.package)) : new Set();
|
|
513
|
+
// The founder's pipeline (EE-069): seven stages by what the founder does, not by the files they produce.
|
|
514
|
+
const pipeline = [
|
|
515
|
+
{ label: "Capture", state: has("purpose/ideas/dump.md") ? "done" : "", detail: has("purpose/ideas/dump.md") ? "ideas kept verbatim" : "write the idea dump" },
|
|
516
|
+
{ label: "Understand", href: "/map", state: rt?.understood ? "done" : has(MAP) ? "part" : "", detail: rt?.understood ? "you confirmed the reading" : has(MAP) ? "check and correct the map" : "not yet" },
|
|
517
|
+
{ label: "Route", href: "/kanban", state: rt?.destinations ? "done" : "", detail: rt?.destinations ? `ideas reach ${routedTo.size} packages` : "after Understand" },
|
|
518
|
+
{ label: "Decide", href: "/", state: settled ? "part" : "", detail: `${settled} settled, ${d.filter((x) => x.rank != null).length} waiting for you` },
|
|
519
|
+
{ label: "Write", state: recs.length && !n((x) => x.status === "missing") ? "done" : n((x) => x.status !== "missing") ? "part" : "", detail: `${n((x) => x.status !== "missing")} of ${recs.length} records written` },
|
|
520
|
+
{ label: "Confirm", state: recs.length && n((x) => x.status === "confirmed") === recs.length ? "done" : n((x) => x.status === "confirmed") ? "part" : "", detail: `${n((x) => x.status === "confirmed")} of ${recs.length} confirmed` },
|
|
521
|
+
{ label: "Package", state: open.length && r.complete.length >= open.length ? "done" : r.complete.length ? "part" : "", detail: `${r.complete.length} of ${open.length} open packages complete` },
|
|
522
|
+
];
|
|
523
|
+
return { ...r, versions: writeIt ? r.versions : manifest?.versions ?? [], pipeline, written };
|
|
524
|
+
}
|
|
525
|
+
|
|
526
|
+
export function serve(p = port, id = venture) {
|
|
527
|
+
const server = createServer(async (req, res) => {
|
|
528
|
+
const u = new URL(req.url, "http://x");
|
|
529
|
+
if (u.pathname.startsWith("/vendor/") && VENDOR.has(u.pathname.slice(8))) {
|
|
530
|
+
try { const b = await vendor(u.pathname.slice(8)); res.writeHead(200, { "content-type": u.pathname.endsWith(".css") ? "text/css" : "text/javascript", "cache-control": "max-age=86400" }); res.end(b); }
|
|
531
|
+
catch (e) { res.writeHead(502); res.end(String(e.message)); }
|
|
532
|
+
return;
|
|
533
|
+
}
|
|
534
|
+
if (u.pathname === "/state.json") { res.writeHead(200, { "content-type": "application/json", "cache-control": "no-store" }); res.end(JSON.stringify(state(u.searchParams.get("venture") ?? id))); return; }
|
|
535
|
+
if (u.pathname === "/ideas.json") { res.writeHead(200, { "content-type": "application/json", "cache-control": "no-store" }); res.end(JSON.stringify(ideas(u.searchParams.get("venture") ?? id))); return; }
|
|
536
|
+
if (u.pathname === "/graph.json") { res.writeHead(200, { "content-type": "application/json", "cache-control": "no-store" }); res.end(JSON.stringify(graph(u.searchParams.get("venture") ?? id))); return; }
|
|
537
|
+
if (u.pathname === "/routes.json") { res.writeHead(200, { "content-type": "application/json", "cache-control": "no-store" }); res.end(JSON.stringify(routesOf(u.searchParams.get("venture") ?? id))); return; }
|
|
538
|
+
if (u.pathname === "/site") { res.writeHead(200, { "content-type": "text/html; charset=utf-8" }); res.end(SITE_PAGE); return; }
|
|
539
|
+
if (u.pathname === "/site.json") {
|
|
540
|
+
const dir = ventureDir(u.searchParams.get("venture") ?? id), f = dir && join(dir, "operations/marketing/site-presence.json");
|
|
541
|
+
res.writeHead(200, { "content-type": "application/json", "cache-control": "no-store" }); res.end(f && existsSync(f) ? readFileSync(f) : "{}"); return;
|
|
542
|
+
}
|
|
543
|
+
if (u.pathname === "/site/plan.json") {
|
|
544
|
+
const dir = ventureDir(u.searchParams.get("venture") ?? id);
|
|
545
|
+
let body;
|
|
546
|
+
try { body = dir ? plan(dir, { flags: Object.fromEntries(["raising", "hiring", "contributors"].map((k) => [k, u.searchParams.get(k) === "1"])) }) : { error: `no venture ${id}` }; } catch (e) { body = { error: e.message }; }
|
|
547
|
+
res.writeHead(200, { "content-type": "application/json", "cache-control": "no-store" }); res.end(JSON.stringify(body)); return;
|
|
548
|
+
}
|
|
549
|
+
if (u.pathname === "/site/scan" && req.method === "POST") {
|
|
550
|
+
const dir = ventureDir(u.searchParams.get("venture") ?? id);
|
|
551
|
+
const flags = Object.fromEntries(["raising", "hiring", "contributors"].map((k) => [k, u.searchParams.get(k) === "1"]));
|
|
552
|
+
let body;
|
|
553
|
+
try { body = await scan(u.searchParams.get("url") ?? "", { venture: dir, flags }); } catch (e) { body = { error: e.message }; }
|
|
554
|
+
res.writeHead(200, { "content-type": "application/json", "cache-control": "no-store" }); res.end(JSON.stringify(body)); return;
|
|
555
|
+
}
|
|
556
|
+
if (u.pathname === "/package.json" || (u.pathname === "/package/write" && req.method === "POST")) {
|
|
557
|
+
const dir = ventureDir(u.searchParams.get("venture") ?? id);
|
|
558
|
+
let body;
|
|
559
|
+
try { body = dir ? packageOf(dir, u.pathname === "/package/write") : { error: `no venture ${id}` }; } catch (e) { body = { error: e.message }; }
|
|
560
|
+
res.writeHead(200, { "content-type": "application/json", "cache-control": "no-store" }); res.end(JSON.stringify(body)); return;
|
|
561
|
+
}
|
|
562
|
+
if (/^\/package\/[a-z-]+\.md$/.test(u.pathname)) {
|
|
563
|
+
const dir = ventureDir(u.searchParams.get("venture") ?? id), f = dir && join(dir, PACKAGE, u.pathname.slice("/package/".length));
|
|
564
|
+
if (f && existsSync(f)) { res.writeHead(200, { "content-type": "text/markdown; charset=utf-8" }); res.end(readFileSync(f)); } else { res.writeHead(404); res.end(); }
|
|
565
|
+
return;
|
|
566
|
+
}
|
|
567
|
+
if (u.pathname === "/package") { res.writeHead(200, { "content-type": "text/html; charset=utf-8" }); res.end(PACKAGE_PAGE); return; }
|
|
568
|
+
if (u.pathname === "/kanban") { res.writeHead(200, { "content-type": "text/html; charset=utf-8" }); res.end(KANBAN_PAGE); return; }
|
|
569
|
+
if (u.pathname === "/graph") { res.writeHead(200, { "content-type": "text/html; charset=utf-8" }); res.end(GRAPH_PAGE); return; }
|
|
570
|
+
if (u.pathname === "/map") { res.writeHead(200, { "content-type": "text/html; charset=utf-8" }); res.end(MAP_PAGE); return; }
|
|
571
|
+
if (u.pathname === "/") { res.writeHead(200, { "content-type": "text/html; charset=utf-8" }); res.end(PAGE); return; }
|
|
572
|
+
res.writeHead(404); res.end();
|
|
573
|
+
});
|
|
574
|
+
server.listen(p, "127.0.0.1");
|
|
575
|
+
return server;
|
|
576
|
+
}
|
|
577
|
+
|
|
578
|
+
if (import.meta.url === `file://${process.argv[1]}`) {
|
|
579
|
+
serve();
|
|
580
|
+
console.log(`decision board for ${venture}: http://127.0.0.1:${port}/`);
|
|
581
|
+
}
|