naive-framework 0.1.0 → 0.3.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 +2 -1
- package/docs/decisions/decisions.json +42 -0
- package/docs/reference/ROUTES.md +36 -37
- package/evidence/skills/fidelity-ladder-audit/ladder-census.mjs +2 -1
- package/governance/layer.json +2 -1
- 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 +183 -0
- package/interface/api/server.mjs +4 -2
- package/interface/boundaries.mjs +219 -0
- package/interface/brand.mjs +215 -0
- package/interface/cli/freshness.mjs +59 -0
- package/interface/cli/guard.mjs +6 -0
- package/interface/cli/mentura.mjs +82 -11
- package/interface/cli/naive.mjs +44 -14
- package/interface/context.mjs +525 -0
- package/interface/demo/board.mjs +9 -8
- package/interface/demo/decision-harness.mjs +4 -3
- package/interface/dev/setup.mjs +84 -0
- package/interface/gate-hooks.mjs +37 -0
- package/interface/harness/evals.mjs +10 -9
- package/interface/harness/run.mjs +16 -15
- package/interface/instrumentation.mjs +87 -0
- package/interface/kit-boundary.mjs +73 -0
- package/interface/layer.json +8 -2
- package/interface/mcp/server.mjs +47 -7
- package/interface/package-routes.mjs +197 -0
- package/interface/provenance.mjs +28 -4
- package/interface/reads.mjs +174 -0
- package/interface/release-card.mjs +43 -0
- package/interface/router.mjs +110 -36
- package/interface/skills/naive-setup/SKILL.md +71 -0
- package/interface/skills/naive-setup/artifact.html +83 -0
- package/interface/vitals.mjs +116 -0
- package/interface/writes.mjs +190 -0
- package/knowledge/connectors/catalog.mjs +3 -2
- package/knowledge/connectors/filesystem.mjs +10 -5
- package/knowledge/connectors/index.mjs +3 -2
- package/knowledge/freshness.json +85 -0
- package/knowledge/layer.json +11 -3
- package/knowledge/templates/glossary.md +23 -0
- package/operations/layer.json +10 -3
- package/operations/marketing/check-public.mjs +7 -6
- package/operations/marketing/site/plan.mjs +4 -3
- package/operations/marketing/site/presence.mjs +4 -3
- package/operations/templates/marketing-brand.md +46 -0
- package/operations/templates/public-assets.md +39 -0
- package/package.json +3 -1
- package/packages/catalog.json +8 -0
- package/packages/customers/package.json +21 -1
- package/packages/deals/package.json +27 -0
- package/packages/decisions/package.json +12 -1
- package/packages/finance/frames/fund-without-ceding-control.frame.json +177 -0
- package/packages/foundation/package.json +8 -1
- package/packages/fund/package.json +48 -0
- package/packages/marketing/package.json +10 -4
- package/packages/money/package.json +24 -3
- package/packages/validation/package.json +13 -0
- package/platform/README.md +6 -0
- package/platform/adapter.json +15 -0
- package/platform/integrations/remote-image.mjs +79 -0
- package/platform/integrations/rho.mjs +9 -7
- package/platform/kits/README.md +8 -1
- 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/webhooks/server.mjs +70 -0
- package/purpose/frameworks/idea-map/ideamap.mjs +4 -2
- package/purpose/frameworks/idea-map/route.mjs +5 -4
- package/purpose/frameworks/product-anatomy/anatomy.mjs +2 -1
- package/schema/README.md +1 -0
- package/schema/access.mjs +58 -0
- package/schema/adapter.schema.json +33 -0
- package/schema/add-department.mjs +3 -2
- package/schema/assess-problem.mjs +3 -2
- package/schema/assets.mjs +213 -0
- package/schema/brand.mjs +374 -0
- package/schema/build-release.mjs +61 -0
- package/schema/caching.mjs +506 -0
- package/schema/check-assets.mjs +24 -0
- package/schema/check-brand.mjs +24 -0
- package/schema/check-config.mjs +111 -0
- package/schema/check-data-model.mjs +2 -1
- package/schema/check-data-packages.mjs +5 -4
- package/schema/check-execution.mjs +4 -3
- package/schema/check-nextjs-status.mjs +3 -1
- package/schema/check-people.mjs +84 -0
- package/schema/check-private.mjs +2 -1
- package/schema/check-ready.mjs +6 -5
- package/schema/check-sources.mjs +11 -2
- package/schema/check-where-used.mjs +4 -3
- package/schema/data-model.json +334 -8
- package/schema/deal.schema.json +80 -0
- package/schema/decision-analytics.mjs +2 -1
- package/schema/decisions.mjs +21 -12
- package/schema/examples/frames/judge.mjs +10 -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/foundation.mjs +8 -7
- package/schema/founder.schema.json +72 -0
- package/schema/frame.schema.json +240 -0
- package/schema/frames.mjs +229 -0
- package/schema/freshness-log.schema.json +40 -0
- package/schema/freshness-profiles.schema.json +44 -0
- package/schema/freshness.mjs +474 -0
- package/schema/fund-terms.schema.json +107 -0
- package/schema/fund-thesis.schema.json +87 -0
- package/schema/gate-drafts.mjs +131 -0
- package/schema/gate.mjs +105 -11
- package/schema/glossary.schema.json +25 -0
- package/schema/init-venture.mjs +69 -21
- 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 +145 -15
- package/schema/layer.json +16 -3
- package/schema/marketing-brand.schema.json +116 -0
- package/schema/migrate-venture.mjs +8 -7
- package/schema/migrations/kit-to-package.mjs +95 -0
- package/schema/migrations/package-layout.mjs +90 -0
- package/schema/model-steps.mjs +718 -0
- package/schema/money-summary.schema.json +138 -0
- package/schema/naive-config.mjs +166 -0
- package/schema/naive-config.schema.json +268 -0
- package/schema/need-now.json +171 -0
- package/schema/need-now.mjs +8 -6
- package/schema/packages.mjs +21 -15
- package/schema/payouts.mjs +3 -2
- package/schema/people.schema.json +85 -0
- package/schema/port-venture.mjs +206 -132
- package/schema/public-assets.schema.json +76 -0
- package/schema/release-read.mjs +112 -0
- package/schema/releases.mjs +498 -0
- package/schema/run-handoff.mjs +5 -4
- package/schema/setup.mjs +165 -0
- package/schema/stale.mjs +112 -0
- package/schema/start-release.mjs +41 -0
- package/schema/structure.mjs +187 -0
- package/schema/systems-catalog.json +126 -0
- package/schema/systems-catalog.schema.json +78 -0
- package/schema/time-to-ready.mjs +7 -6
- package/schema/trigger.schema.json +44 -0
- package/schema/triggers.mjs +238 -0
- package/schema/upgrade-registry.schema.json +60 -0
- package/schema/upgrade-venture.mjs +96 -118
- package/schema/upgrades.json +69 -0
- package/schema/upgrades.mjs +386 -0
- package/schema/validate.py +19 -10
- package/schema/venture-paths.mjs +85 -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 +61 -0
- package/schema/workspace.schema.json +5 -1
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
// The setup page (EE-083): what `naive dev` serves at /setup, the way next dev serves a first page. It
|
|
2
|
+
// walks a founder through establishing the venture's structure, in order: the venture and its people,
|
|
3
|
+
// where each function keeps its truth, the strategic foundation and the first decisions (the decision
|
|
4
|
+
// docket), and a goal for the discovery phase. Every answer is written as the code would write it:
|
|
5
|
+
// a decision a person approves (schema/decisions.mjs), or knowledge/sources.json through the router, with
|
|
6
|
+
// provenance. When the docket's first decisions are settled, the page says the structure is established
|
|
7
|
+
// and what comes next.
|
|
8
|
+
//
|
|
9
|
+
// handle(req, res, dir) -> true when it answered a /setup request
|
|
10
|
+
|
|
11
|
+
import { decide, point, state } from "../../schema/setup.mjs";
|
|
12
|
+
|
|
13
|
+
const json = (res, code, body) => { res.writeHead(code, { "content-type": "application/json" }); res.end(JSON.stringify(body)); };
|
|
14
|
+
const body = (req) => new Promise((ok) => { let b = ""; req.on("data", (c) => (b += c)); req.on("end", () => { try { ok(JSON.parse(b || "{}")); } catch { ok({}); } }); });
|
|
15
|
+
const GOAL = "discovery-goal";
|
|
16
|
+
|
|
17
|
+
export async function handle(req, res, dir) {
|
|
18
|
+
const u = new URL(req.url, "http://x");
|
|
19
|
+
if (!u.pathname.startsWith("/setup")) return false;
|
|
20
|
+
if (u.pathname === "/setup" || u.pathname === "/setup/") { res.writeHead(200, { "content-type": "text/html; charset=utf-8" }); res.end(PAGE); return true; }
|
|
21
|
+
if (u.pathname === "/setup/state") { json(res, 200, await state(dir)); return true; }
|
|
22
|
+
if (req.method === "POST" && u.pathname === "/setup/decide") { const r = await decide(dir, await body(req)); json(res, r.ok ? 200 : 400, r); return true; }
|
|
23
|
+
if (req.method === "POST" && u.pathname === "/setup/point") { const r = await point(dir, await body(req)); json(res, r.ok ? 200 : 400, r); return true; }
|
|
24
|
+
json(res, 404, { error: "no such setup route" });
|
|
25
|
+
return true;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const PAGE = `<!doctype html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1">
|
|
29
|
+
<title>Set up your venture</title>
|
|
30
|
+
<style>
|
|
31
|
+
:root { --bg:#f6f7f4; --panel:#fff; --fg:#1d2320; --muted:#5d675f; --line:#d9ded8; --accent:#2f6b4f; --accent-soft:#e3efe8; --warn:#8a5a00; --warn-soft:#f6ecd8;
|
|
32
|
+
--body: "Source Sans 3", system-ui, -apple-system, "Segoe UI", sans-serif; --mono: ui-monospace, "SF Mono", Menlo, monospace; color-scheme: light; }
|
|
33
|
+
@media (prefers-color-scheme: dark) { :root { --bg:#121614; --panel:#1a201d; --fg:#e4e9e5; --muted:#9aa59d; --line:#2b332e; --accent:#86c4a3; --accent-soft:#1d2b24; --warn:#e3b25c; --warn-soft:#2c2414; color-scheme: dark; } }
|
|
34
|
+
* { box-sizing: border-box } body { margin:0; background:var(--bg); color:var(--fg); font:16px/1.55 var(--body); }
|
|
35
|
+
.wrap { max-width: 860px; margin: 0 auto; padding: 32px max(16px, env(safe-area-inset-left)) 80px; display: grid; gap: 28px; }
|
|
36
|
+
header { display:grid; gap:6px } h1 { margin:0; font-size:1.9rem; letter-spacing:-.01em } h2 { margin:0 0 4px; font-size:1.2rem }
|
|
37
|
+
.muted { color: var(--muted) } .mono { font-family: var(--mono); font-size: .88em }
|
|
38
|
+
.progress { display:flex; gap:6px; flex-wrap:wrap } .step { font:600 .78rem/1 var(--mono); padding:7px 10px; border-radius:999px; border:1px solid var(--line); color:var(--muted) }
|
|
39
|
+
.step.ok { background: var(--accent-soft); color: var(--accent); border-color: transparent }
|
|
40
|
+
section { background: var(--panel); border:1px solid var(--line); border-radius:10px; padding:20px; display:grid; gap:14px }
|
|
41
|
+
table { width:100%; border-collapse: collapse; font-size:.95rem } td, th { text-align:left; padding:8px 6px; border-bottom:1px solid var(--line); vertical-align: top } th { font: 600 .74rem/1.2 var(--mono); text-transform: uppercase; letter-spacing:.05em; color: var(--muted) }
|
|
42
|
+
.pill { display:inline-block; font:600 .72rem/1 var(--mono); padding:4px 7px; border-radius:999px; background: var(--accent-soft); color: var(--accent); margin: 0 4px 4px 0 } .pill.pointed { background: var(--line); color: var(--fg) } .pill.none { background: var(--warn-soft); color: var(--warn) }
|
|
43
|
+
.card { border:1px solid var(--line); border-radius:8px; padding:14px; display:grid; gap:10px } .card.done { opacity:.75 }
|
|
44
|
+
.card .q { font-weight:600 } textarea, input, select { font: inherit; color: inherit; background: var(--bg); border:1px solid var(--line); border-radius:6px; padding:8px; width:100% }
|
|
45
|
+
.row { display:flex; gap:8px; flex-wrap:wrap; align-items:center } .row > * { flex: 1 1 140px }
|
|
46
|
+
button { font: 600 .92rem var(--body); background: var(--accent); color: var(--bg); border: 0; border-radius: 6px; padding: 9px 14px; cursor: pointer; flex: 0 0 auto }
|
|
47
|
+
button.ghost { background: transparent; color: var(--accent); border:1px solid var(--accent) } button:focus-visible, textarea:focus-visible, input:focus-visible, select:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px }
|
|
48
|
+
.err { color: var(--warn); font-size: .9rem } .celebrate { text-align:center; padding: 32px 20px; display:grid; gap:10px; background: var(--accent-soft); border-color: transparent }
|
|
49
|
+
.celebrate .big { font-size: 2.2rem; font-weight: 700; color: var(--accent) } ol { margin:0; padding-left: 1.2em; display:grid; gap:6px; text-align:left }
|
|
50
|
+
@media (prefers-reduced-motion: no-preference) { .celebrate { animation: pop .5s ease-out } @keyframes pop { from { transform: scale(.97); opacity:.4 } to { transform: none; opacity:1 } } }
|
|
51
|
+
</style></head><body><div class="wrap" id="app"><p class="muted">Loading your venture…</p></div>
|
|
52
|
+
<script>
|
|
53
|
+
const $ = (s) => document.querySelector(s);
|
|
54
|
+
const esc = (s) => String(s ?? "").replace(/[&<>"]/g, (c) => ({ "&":"&","<":"<",">":">",'"':""" })[c]);
|
|
55
|
+
const post = (u, b) => fetch(u, { method: "POST", headers: { "content-type": "application/json" }, body: JSON.stringify(b) }).then((r) => r.json());
|
|
56
|
+
let S = null;
|
|
57
|
+
async function load() { S = await fetch("/setup/state").then((r) => r.json()); render(); }
|
|
58
|
+
function render() {
|
|
59
|
+
const v = S.venture, st = S.steps;
|
|
60
|
+
const label = (k, t) => '<span class="step ' + (st[k] ? "ok" : "") + '">' + (st[k] ? "✓ " : "") + t + "</span>";
|
|
61
|
+
const people = S.people ? '<table><tr><th>Person</th><th>Role</th><th>Hats</th></tr>' + S.people.people.map((p) => "<tr><td>" + esc(p.name) + "</td><td class=mono>" + esc(p.role) + "</td><td>" + (p.hats.map((h) => '<span class="pill pointed">' + esc(h) + "</span>").join("") || '<span class="muted">none</span>') + "</td></tr>").join("") + "</table>" : '<p class="err">No people/people.json yet: create the venture with create-venture.</p>';
|
|
62
|
+
const fns = [...new Set([...Object.keys(S.catalog.functions).filter((f) => f === "legal" || (S.people?.people ?? []).some((p) => p.hats.includes(f))), ...Object.keys(S.functions)])];
|
|
63
|
+
const truth = '<table><tr><th>Function</th><th>Source of truth</th></tr>' + fns.map((f) => { const x = S.functions[f]; const sys = x?.systems?.length ? x.systems.map((s) => '<span class="pill ' + (s.status === "connected" ? "" : "pointed") + '">' + esc(S.catalog.systems[s.system]?.label ?? s.system) + (s.address ? " · " + esc(s.address) : "") + " · " + s.status + (s.fixture ? " (fixture)" : "") + "</span>").join("") : '<span class="pill none">none yet</span>' + (x?.note ? ' <span class="muted">' + esc(x.note) + "</span>" : ""); return "<tr><td>" + esc(f === "legal" ? "legal documents" : f) + "</td><td>" + sys + "</td></tr>"; }).join("") + "</table>"
|
|
64
|
+
+ '<div class="row"><select id="fn">' + fns.map((f) => "<option>" + f + "</option>").join("") + '</select><select id="sys"></select><input id="addr" placeholder="address, such as acme/web"><button id="point">Point</button></div><div class="err" id="perr"></div>';
|
|
65
|
+
const cards = S.docket.map((d) => '<div class="card ' + (d.status === "decided" ? "done" : "") + '" data-id="' + esc(d.id) + '"><div class="q">' + esc(d.question) + "</div>" + (d.status === "decided" ? '<div>✓ <span class="muted">' + esc(d.answer) + "</span></div>" : (d.options.length ? '<div class="row">' + d.options.map((o) => '<button class="ghost opt">' + esc(o) + "</button>").join("") + "</div>" : "") + '<textarea rows="2" placeholder="In your own words"></textarea><div class="row"><button class="ans">Save</button><span class="err"></span></div>') + "</div>").join("");
|
|
66
|
+
const goal = S.goal?.status === "decided" ? '<p>✓ ' + esc(S.goal.answer) + "</p>" : '<textarea id="goal" rows="2" placeholder="For example: ten clinic owners confirm the scheduling problem in interviews by December"></textarea><div class="row"><button id="setgoal">Set the goal</button><span class="err" id="gerr"></span></div>';
|
|
67
|
+
const done = S.done ? '<section class="celebrate"><div class="big">Your venture is structured.</div><p>' + esc(v.name) + " has its people and their hats, a source of truth for its functions, a settled foundation, and a goal for discovery. Every decision from here routes through the docket.</p><ol><li>Run <span class=mono>npm run dev</span> and keep the board open: decisions waiting on you show there first.</li><li>Orient each next decision around the discovery goal; a sprint or campaign is a set of decisions toward it.</li><li>To operate the venture with your team and agents (tasks, documents, money and people in one workspace), connect it to Mentura.</li></ol>" + (S.later ? '<p class="muted">' + S.later + " more decisions come later, as the venture needs them.</p>" : "") + "</section>" : "";
|
|
68
|
+
$("#app").innerHTML = '<header><span class="muted mono">' + esc(v.type?.label ?? "venture") + " · " + esc(v.phase) + '</span><h1>Set up ' + esc(v.name) + '</h1><p class="muted">Establish the structure of your venture. Each answer is saved as you go.</p><div class="progress">' + label("people", "People and hats") + label("truth", "Sources of truth") + label("docket", "Foundation and first decisions " + S.counts.settled + "/" + S.counts.of) + label("goal", "Discovery goal") + "</div></header>" + done
|
|
69
|
+
+ "<section><h2>1. People and hats</h2>" + people + "</section>"
|
|
70
|
+
+ '<section><h2>2. Sources of truth</h2><p class="muted">Point Naive at where each function keeps its truth. Naive keeps pointers, never copies; Rho is read through its API.</p>' + truth + "</section>"
|
|
71
|
+
+ '<section><h2>3. Foundation and first decisions</h2><p class="muted">The decision docket: what the venture must settle first, in order.</p>' + cards + "</section>"
|
|
72
|
+
+ '<section><h2>4. A goal for discovery</h2><p class="muted">What must be true to leave discovery? Every decision after this one works toward it.</p>' + goal + "</section>";
|
|
73
|
+
const sysFor = () => { $("#sys").innerHTML = (S.catalog.functions[$("#fn").value] ?? []).map((s) => '<option value="' + s + '">' + esc(S.catalog.systems[s].label) + "</option>").join(""); $("#addr").placeholder = S.catalog.systems[$("#sys").value]?.address ?? "address"; };
|
|
74
|
+
$("#fn").onchange = sysFor; sysFor(); $("#sys").onchange = () => ($("#addr").placeholder = S.catalog.systems[$("#sys").value]?.address ?? "address");
|
|
75
|
+
$("#point").onclick = async () => { const r = await post("/setup/point", { fn: $("#fn").value, system: $("#sys").value, address: $("#addr").value.trim() || undefined }); if (!r.ok) $("#perr").textContent = r.error; else load(); };
|
|
76
|
+
document.querySelectorAll(".card").forEach((c) => {
|
|
77
|
+
const id = c.dataset.id, ta = c.querySelector("textarea");
|
|
78
|
+
c.querySelectorAll(".opt").forEach((b) => (b.onclick = () => { ta.value = b.textContent; }));
|
|
79
|
+
const save = c.querySelector(".ans"); if (save) save.onclick = async () => { const r = await post("/setup/decide", { id, answer: ta.value }); if (!r.ok) c.querySelector(".err").textContent = r.error; else load(); };
|
|
80
|
+
});
|
|
81
|
+
const g = $("#setgoal"); if (g) g.onclick = async () => { const r = await post("/setup/decide", { id: "${GOAL}", answer: $("#goal").value }); if (!r.ok) $("#gerr").textContent = r.error; else load(); };
|
|
82
|
+
}
|
|
83
|
+
load();
|
|
84
|
+
</script></body></html>`;
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
// The router's hooks into the gate group (X-1 to X-6, P-2, P-7, U-4). Each is called from one
|
|
2
|
+
// marked line in interface/router.mjs, so the router stays the route table and the logic stays in
|
|
3
|
+
// schema/gate.mjs (the verdict), schema/gate-drafts.mjs (held drafts) and schema/naive-config.mjs
|
|
4
|
+
// (the venture's overrides).
|
|
5
|
+
|
|
6
|
+
import { existsSync } from "node:fs";
|
|
7
|
+
import { join } from "node:path";
|
|
8
|
+
import { evaluate } from "../schema/gate.mjs";
|
|
9
|
+
import { writeHeld } from "../schema/gate-drafts.mjs";
|
|
10
|
+
import { checksFor, configFor } from "../schema/naive-config.mjs";
|
|
11
|
+
import { ventureFile } from "../schema/venture-paths.mjs";
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* The gate's verdict on a local write, or null when it passes. A person is never held, only
|
|
15
|
+
* refused. A naive.config.json that is not valid JSON fails closed: an agent's write is held.
|
|
16
|
+
*/
|
|
17
|
+
export function gateWrite({ dir, r, route, requested, file, actor, actor_id, provenance = {}, gate }) {
|
|
18
|
+
let config;
|
|
19
|
+
try { config = configFor(dir); }
|
|
20
|
+
catch (e) { return actor === "person" ? null : { then: "draft", rule: "naive.config.json", source: "naive.config.json", why: `${e.message}: the gate fails closed, written as a draft for a person to accept` }; }
|
|
21
|
+
const confidence = provenance.confidence === undefined || provenance.confidence === null || provenance.confidence === "" ? undefined : Number(provenance.confidence);
|
|
22
|
+
const v = evaluate({ config, dir, route, table: r.route, requested, record: r.record, actor, role: actor_id, confidence: Number.isNaN(confidence) ? undefined : confidence,
|
|
23
|
+
exists: existsSync(ventureFile(dir, file)), decisionOwner: gate?.owner ?? undefined });
|
|
24
|
+
if (v.then === "pass") return null;
|
|
25
|
+
if (actor === "person" && v.then !== "refuse") return null;
|
|
26
|
+
return v;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/** Stores an agent's held draft: its sidecar, the owner it was sent to, and variants side by side. */
|
|
30
|
+
export function heldDraft(dir, { draft, file, text, verdict, route, agent }) {
|
|
31
|
+
return writeHeld(dir, { draft, file, text, verdict, route, agent });
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/** A route's checks with their mode from naive.config.json: run, warn or off. A broken config runs them all. */
|
|
35
|
+
export function checkPlan(dir, checks) {
|
|
36
|
+
try { return checksFor(configFor(dir), checks); } catch { return checks.map((check) => ({ check, mode: "run" })); }
|
|
37
|
+
}
|
|
@@ -11,6 +11,7 @@ import { DUMP, MAP, parse } from "../../purpose/frameworks/idea-map/ideamap.mjs"
|
|
|
11
11
|
import { GRAPH } from "../../purpose/frameworks/idea-map/graph.mjs";
|
|
12
12
|
import { ideasOf } from "../../purpose/frameworks/idea-map/route.mjs";
|
|
13
13
|
import { createHash } from "node:crypto";
|
|
14
|
+
import { ventureFile } from "../../schema/venture-paths.mjs";
|
|
14
15
|
|
|
15
16
|
const load = (f) => JSON.parse(readFileSync(f, "utf8"));
|
|
16
17
|
const norm = (s) => s.replace(/\s+/g, " ").trim();
|
|
@@ -22,29 +23,29 @@ export function evaluate(dir, { researched = [], settled = [] } = {}) {
|
|
|
22
23
|
const add = (key, score, comment) => out.push({ key, score, comment });
|
|
23
24
|
|
|
24
25
|
// The founder's ideas stay theirs: every idea on the map is their text, unchanged.
|
|
25
|
-
const dump = norm(readFileSync(
|
|
26
|
-
const ideas = existsSync(
|
|
26
|
+
const dump = norm(readFileSync(ventureFile(dir, DUMP), "utf8"));
|
|
27
|
+
const ideas = existsSync(ventureFile(dir, MAP)) ? parse(readFileSync(ventureFile(dir, MAP), "utf8")).ideas : [];
|
|
27
28
|
const changed = ideas.filter((i) => !dump.includes(norm(i.text)));
|
|
28
29
|
add("ideas_verbatim", ideas.length ? frac(ideas.length - changed.length, ideas.length) : 0, changed.length ? `rewritten: ${changed.map((i) => i.id).join(", ")}` : `${ideas.length} ideas, all verbatim`);
|
|
29
30
|
add("ideas_tagged", frac(ideas.filter((i) => i.departments?.length).length, ideas.length), `${ideas.filter((i) => !i.departments?.length).length} ideas without a department`);
|
|
30
31
|
|
|
31
32
|
// The idea graph is a Merkle DAG: every hash recomputes from the idea's text and what it builds on.
|
|
32
|
-
if (existsSync(
|
|
33
|
-
const g = load(
|
|
33
|
+
if (existsSync(ventureFile(dir, GRAPH))) {
|
|
34
|
+
const g = load(ventureFile(dir, GRAPH));
|
|
34
35
|
const byId = Object.fromEntries(g.nodes.map((n) => [n.id, n]));
|
|
35
36
|
const bad = g.nodes.filter((n) => createHash("sha256").update(JSON.stringify({ text: n.text, parents: n.builds_on.map((p) => byId[p]?.hash).sort() })).digest("hex").slice(0, 16) !== n.hash);
|
|
36
37
|
add("graph_merkle", bad.length ? 0 : 1, bad.length ? `hashes that do not recompute: ${bad.map((n) => n.id).join(", ")}` : `${g.nodes.length} hashes recompute; root ${g.root}`);
|
|
37
38
|
}
|
|
38
39
|
|
|
39
40
|
// Agents propose; people decide. No accept, confirm or decision is an agent's.
|
|
40
|
-
const log = existsSync(join(dir, "provenance.jsonl")) ? readFileSync(join(dir, "provenance.jsonl"), "utf8").trim().split("\n").filter(Boolean).map((l) => JSON.parse(l)) : [];
|
|
41
|
+
const log = existsSync(join(ventureFile(dir, "provenance.jsonl"))) ? readFileSync(join(ventureFile(dir, "provenance.jsonl")), "utf8").trim().split("\n").filter(Boolean).map((l) => JSON.parse(l)) : [];
|
|
41
42
|
const byAgent = log.filter((e) => ["accept", "confirm"].includes(e.activity) && e.agent?.kind !== "person");
|
|
42
43
|
const r = decisions(dir);
|
|
43
44
|
const decidedByAgent = r.decisions.filter((d) => d.status === "decided" && d.provenance && d.provenance.approved_by && log.some((e) => e.activity === "accept" && e.entity === d.file && e.agent?.kind !== "person"));
|
|
44
45
|
add("people_decide", byAgent.length || decidedByAgent.length ? 0 : 1, byAgent.length ? `${byAgent.length} accept or confirm entries by an agent` : "every accept, confirm and decision is a person's");
|
|
45
46
|
|
|
46
47
|
// Evidence is cited: each item names a URL, and the URL came from a research result in this run.
|
|
47
|
-
const problem = [join(dir, "purpose/validation/problem.json"), join(dir, "_drafts/purpose__validation__problem.json.draft")].find(existsSync);
|
|
48
|
+
const problem = [join(ventureFile(dir, "purpose/validation/problem.json")), join(ventureFile(dir, "_drafts/purpose__validation__problem.json.draft"))].find(existsSync);
|
|
48
49
|
const items = problem ? load(problem).evidence ?? [] : [];
|
|
49
50
|
const urlOf = (e) => e.url ?? e.source?.url ?? e.source ?? null;
|
|
50
51
|
const cited = items.filter((e) => typeof urlOf(e) === "string" && /^https?:\/\//.test(urlOf(e)));
|
|
@@ -72,7 +73,7 @@ export function evaluate(dir, { researched = [], settled = [] } = {}) {
|
|
|
72
73
|
add("founder_settled", settled.length ? 1 : 0, settled.length ? `settled: ${settled.join(", ")}` : "the founder settled nothing");
|
|
73
74
|
// Money (EE-070): when the venture has a money summary, it is fresh, the agent cites it, and no raw
|
|
74
75
|
// transaction leaves its sensitive stream for another record.
|
|
75
|
-
const summaryFile = join(dir, "money/
|
|
76
|
+
const summaryFile = join(ventureFile(dir, "money/_summary.json"));
|
|
76
77
|
if (existsSync(summaryFile)) {
|
|
77
78
|
const m = load(summaryFile);
|
|
78
79
|
const age = (Date.now() - Date.parse(m.synced_at)) / 864e5;
|
|
@@ -80,8 +81,8 @@ export function evaluate(dir, { researched = [], settled = [] } = {}) {
|
|
|
80
81
|
// Only what touches money has to cite it: pricing, spend, hiring, runway, fundraising, cash.
|
|
81
82
|
const records = [...agentDrafted, ...(problem ? [{ problem: load(problem) }] : [])].map((d) => JSON.stringify(d));
|
|
82
83
|
const touching = records.filter((x) => /money\/summary|\b(pric(e|ing)|spend|budget|hir(e|ing)|runway|burn|fundrais|cash|revenue)\b/i.test(x));
|
|
83
|
-
const citing = touching.filter((x) => x.includes("money/
|
|
84
|
-
add("money_cited", frac(citing.length, touching.length), touching.length ? `${citing.length} of ${touching.length} agent records that touch money cite money/
|
|
84
|
+
const citing = touching.filter((x) => x.includes("money/_summary"));
|
|
85
|
+
add("money_cited", frac(citing.length, touching.length), touching.length ? `${citing.length} of ${touching.length} agent records that touch money cite money/_summary.json` : "no agent record touches money, so none needs to cite it");
|
|
85
86
|
const leaked = walk(dir).filter((f) => !f.includes("/money/_streams/") && f.endsWith(".json") && /:transaction:/.test(readFileSync(f, "utf8")));
|
|
86
87
|
add("money_private", leaked.length ? 0 : 1, leaked.length ? `transaction pointers outside the money stream: ${leaked.map((f) => f.slice(dir.length + 1)).join(", ")}` : "transactions stay in their sensitive stream");
|
|
87
88
|
}
|
|
@@ -42,6 +42,7 @@ import { understood } from "../../purpose/frameworks/idea-map/route.mjs";
|
|
|
42
42
|
import { createHash } from "node:crypto";
|
|
43
43
|
import { agentFromEnv, record as prov } from "../provenance.mjs";
|
|
44
44
|
import { evaluate } from "./evals.mjs";
|
|
45
|
+
import { ventureFile } from "../../schema/venture-paths.mjs";
|
|
45
46
|
|
|
46
47
|
const repo = new URL("../../", import.meta.url).pathname;
|
|
47
48
|
const argv = process.argv.slice(2);
|
|
@@ -71,8 +72,8 @@ if (!existsSync(join(dir, "venture.json"))) {
|
|
|
71
72
|
if (p.status !== 0) { console.error(p.stderr || p.stdout); process.exit(1); }
|
|
72
73
|
if (opt("name")) { const v = JSON.parse(readFileSync(join(dir, "venture.json"), "utf8")); v.name = opt("name"); writeFileSync(join(dir, "venture.json"), JSON.stringify(v, null, 2) + "\n"); }
|
|
73
74
|
}
|
|
74
|
-
mkdirSync(join(dir, "purpose/ideas"), { recursive: true });
|
|
75
|
-
copyFileSync(dumpFile,
|
|
75
|
+
mkdirSync(join(ventureFile(dir, "purpose/ideas")), { recursive: true });
|
|
76
|
+
copyFileSync(dumpFile, ventureFile(dir, DUMP));
|
|
76
77
|
prov(dir, { entity: DUMP, route: "purpose/idea-dump", activity: "create", agent: agentFromEnv("person", "harness", { id: founder }), note: "the founder's idea dump, verbatim" });
|
|
77
78
|
lap(`venture ${id} scaffolded in ${ws}; idea dump saved (${readFileSync(dumpFile, "utf8").length} characters)`);
|
|
78
79
|
|
|
@@ -83,9 +84,9 @@ if (opt("money")) {
|
|
|
83
84
|
const m = opt("money");
|
|
84
85
|
const src = m === "live" ? ["--live"] : m === "sandbox" ? [] : ["--fixture", resolve(m)];
|
|
85
86
|
const p = spawnSync("node", [join(repo, "platform/integrations/rho.mjs"), "sync", "--venture", dir, ...src], { encoding: "utf8" });
|
|
86
|
-
if (p.status === 0 && existsSync(join(dir, "money/
|
|
87
|
-
money = JSON.parse(readFileSync(join(dir, "money/
|
|
88
|
-
prov(dir, { entity: "money/
|
|
87
|
+
if (p.status === 0 && existsSync(join(ventureFile(dir, "money/_summary.json")))) {
|
|
88
|
+
money = JSON.parse(readFileSync(join(ventureFile(dir, "money/_summary.json")), "utf8"));
|
|
89
|
+
prov(dir, { entity: "money/_summary.json", route: "money/_summary", activity: "create", agent: agentFromEnv("agent", "harness", { id: "naive-money" }), note: `synced from ${money.source}; ${money.transactions} transactions kept as pointers in a sensitive stream` });
|
|
89
90
|
lap(`money synced from ${money.source}${money.business ? ` (${money.business})` : ""}: cash ${(money.cash.amount / 100).toFixed(2)} ${money.cash.currency}, monthly net ${(money.monthly_net.amount / 100).toFixed(2)}, runway ${money.runway_months ?? "not limited by burn"}, as of ${money.as_of}`);
|
|
90
91
|
} else console.log(` money sync failed, so the run goes on without it: ${(p.stderr || p.stdout).trim().slice(0, 300)}`);
|
|
91
92
|
}
|
|
@@ -100,7 +101,7 @@ if (flag("board")) { process.env.MENTURA_WORKSPACE = ws; board = (await import("
|
|
|
100
101
|
|
|
101
102
|
// The trace (EE-058): every step and every tool call the agent made, with timings, for LangSmith.
|
|
102
103
|
const iso = () => new Date().toISOString();
|
|
103
|
-
const trace = { name: `naive discovery: ${id}`, run_type: "chain", start: iso(), inputs: { dump: DUMP, ideas: readFileSync(
|
|
104
|
+
const trace = { name: `naive discovery: ${id}`, run_type: "chain", start: iso(), inputs: { dump: DUMP, ideas: readFileSync(ventureFile(dir, DUMP), "utf8"), founder, phase }, children: [], metadata: {
|
|
104
105
|
venture: id, venture_id: id, pipeline_step: phase, money: money?.source ?? null,
|
|
105
106
|
// LangSmith groups a founder's runs into one thread by thread_id: one session per venture per day.
|
|
106
107
|
thread_id: process.env.NAIVE_SESSION ?? `${id}-${new Date().toISOString().slice(0, 10)}`,
|
|
@@ -155,11 +156,11 @@ async function agent(prompt, label, { tools = "mcp__naive__*,mcp__research__*",
|
|
|
155
156
|
// The run's evals and trace: written beside the workspace, and sent to LangSmith when a key is set.
|
|
156
157
|
function finish(code = 0) {
|
|
157
158
|
trace.end = iso();
|
|
158
|
-
const evals = existsSync(
|
|
159
|
+
const evals = existsSync(ventureFile(dir, DUMP)) ? evaluate(dir, { researched, settled }) : [];
|
|
159
160
|
trace.feedback = evals;
|
|
160
161
|
// What the run drafted, so a judge reading only the root run (LangSmith's online evaluators, Jev)
|
|
161
162
|
// sees the problem record and the agent's reports beside the founder's dump in the inputs.
|
|
162
|
-
const problem = join(dir, "purpose/validation/problem.json");
|
|
163
|
+
const problem = join(ventureFile(dir, "purpose/validation/problem.json"));
|
|
163
164
|
const drafted = { problem: existsSync(problem) ? JSON.parse(readFileSync(problem, "utf8")) : null, reports: trace.children.filter((c) => c.outputs?.report).map((c) => ({ pass: c.name, report: c.outputs.report })) };
|
|
164
165
|
trace.outputs = { settled, docket: { steps: docketSteps, now: compact(docket(dir)) }, drafted, evals: Object.fromEntries(evals.map((e) => [e.key, e.score])), laps };
|
|
165
166
|
writeFileSync(join(ws, "trace.json"), JSON.stringify(trace, null, 2) + "\n");
|
|
@@ -180,7 +181,7 @@ const compact = (k) => ({ leading: k.functions[0]?.function ?? null, functions:
|
|
|
180
181
|
|
|
181
182
|
// 1. The idea map: Jev tags each idea with Naive's conventions; without Jev, the agent does, with the
|
|
182
183
|
// same vocabulary. Either way the ideas stay verbatim and unknown tags are dropped.
|
|
183
|
-
const ideas = split(readFileSync(
|
|
184
|
+
const ideas = split(readFileSync(ventureFile(dir, DUMP), "utf8"));
|
|
184
185
|
const venture = JSON.parse(readFileSync(join(dir, "venture.json"), "utf8"));
|
|
185
186
|
let tags, tagger;
|
|
186
187
|
const mapNode = { name: "idea map", run_type: "tool", start: iso(), inputs: { ideas: ideas.length }, children: [] };
|
|
@@ -200,24 +201,24 @@ Ideas: ${JSON.stringify(Object.fromEntries(ideas.map((i) => [i.id, i.section ? `
|
|
|
200
201
|
tags = JSON.parse(text.slice(text.indexOf("{"), text.lastIndexOf("}") + 1));
|
|
201
202
|
tagger = "claude-code";
|
|
202
203
|
}
|
|
203
|
-
writeFileSync(
|
|
204
|
+
writeFileSync(ventureFile(dir, MAP), render(ideas, clean(tags), { venture: id, tagger }));
|
|
204
205
|
prov(dir, { entity: MAP, route: "purpose/idea-map", activity: "create", derived_from: [DUMP], agent: agentFromEnv("agent", "harness", { id: tagger }), note: `${ideas.length} ideas tagged with Naive's conventions` });
|
|
205
|
-
const sum = summary(parse(readFileSync(
|
|
206
|
+
const sum = summary(parse(readFileSync(ventureFile(dir, MAP), "utf8")), phase);
|
|
206
207
|
trace.children.push(Object.assign(mapNode, { end: iso(), outputs: { tagger, summary: sum }, metadata: { tagger } }));
|
|
207
208
|
lap(`idea map: ${sum.ideas} ideas; ${Object.entries(sum.by_pillar).map(([k, v]) => `${k} ${v}`).join(", ")}; clusters ${sum.strengths.map((s) => s.department).join(", ")}; ${sum.product.reading}`);
|
|
208
209
|
|
|
209
210
|
// 1b. The idea graph: the map's structured form. Jev judges which ideas build on which; without Jev,
|
|
210
211
|
// Naive's record order does. Signal, lanes and layout follow in code.
|
|
211
212
|
const graphNode = { name: "idea graph", run_type: "tool", start: iso(), inputs: { ideas: ideas.length }, children: [] };
|
|
212
|
-
const mapIdeas = parse(readFileSync(
|
|
213
|
+
const mapIdeas = parse(readFileSync(ventureFile(dir, MAP), "utf8")).ideas;
|
|
213
214
|
let graphAnswers = null;
|
|
214
215
|
if (jev) {
|
|
215
216
|
const p = spawnSync("node", [join(repo, "platform/integrations/jev.mjs")], { input: JSON.stringify(graphRequest(mapIdeas, venture)), encoding: "utf8", maxBuffer: 1 << 26 });
|
|
216
217
|
if (p.status === 0) { const out = JSON.parse(p.stdout); graphAnswers = out.answers; console.log(` Jev answered ${Object.keys(out.answers).length} dependency and concreteness questions in ${out.ms} ms`); }
|
|
217
218
|
else console.log(` Jev could not judge the dependencies (${p.stderr.trim().slice(0, 200)}); using Naive's record order`);
|
|
218
219
|
}
|
|
219
|
-
const g = buildGraph(mapIdeas, { answers: graphAnswers, phase, venture: id, source: `${MAP}@${createHash("sha256").update(readFileSync(
|
|
220
|
-
writeFileSync(
|
|
220
|
+
const g = buildGraph(mapIdeas, { answers: graphAnswers, phase, venture: id, source: `${MAP}@${createHash("sha256").update(readFileSync(ventureFile(dir, MAP))).digest("hex").slice(0, 16)}` });
|
|
221
|
+
writeFileSync(ventureFile(dir, GRAPH), JSON.stringify(g, null, 2) + "\n");
|
|
221
222
|
prov(dir, { entity: GRAPH, route: "purpose/idea-graph", activity: "create", derived_from: [MAP], agent: agentFromEnv("agent", "harness", { id: g.judged_by }), note: `Merkle root ${g.root}` });
|
|
222
223
|
trace.children.push(Object.assign(graphNode, { end: iso(), outputs: { root: g.root, edges: g.edges.length, lanes: g.lanes.map((l) => ({ mode: l.mode, ideas: l.ideas.length })), next: g.next } }));
|
|
223
224
|
lap(`idea graph: ${g.edges.length} dependencies (${g.judged_by}); ${g.lanes.filter((l) => l.mode === "sync").length} lane(s) in order, ${g.lanes.filter((l) => l.mode === "async").length} on their own; develop next: ${g.next.join(", ")}; root ${g.root}`);
|
|
@@ -236,7 +237,7 @@ lap("the founder confirmed Mentura's understanding of the ideas; routing them");
|
|
|
236
237
|
|
|
237
238
|
const rules = `You are the builder agent (agent_id builder) for the venture ${id}. Work only through the naive MCP tools${research ? " and the research tools" : ""}; pass agent_id "builder" on every naive call.
|
|
238
239
|
Rules: the founder's idea dump is theirs. Quote it, cite ideas by id (i-01...), never put words in their mouth. Never invent evidence: every evidence item cites a URL a research result gave you${research ? "" : " (you have no research tool in this run, so leave evidence empty and say so)"}. Records Naive treats as the founder's are written as drafts for the founder to accept; that is expected. You never decide; you propose. knowledge/venture-profile cannot be written over MCP; skip it.${money ? `
|
|
239
|
-
Money: Naive synced the venture's money summary from ${money.source}, the system of record (money/
|
|
240
|
+
Money: Naive synced the venture's money summary from ${money.source}, the system of record (money/_summary.json): ${JSON.stringify(money)}. Amounts are in minor units (cents). Any proposal or record that touches money (pricing, spend, hiring, runway, fundraising) cites "money/_summary.json as of ${money.as_of}" and uses only the figures the summary gives; never estimate the founder's finances.` : ""}`;
|
|
240
241
|
|
|
241
242
|
await agent(`${rules}
|
|
242
243
|
The founder has just submitted an idea dump. Naive mapped it onto its conventions.
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
// Instrumentation (Naive's instrumentation.ts, P-4; logging fetches in development, F-4). One hook
|
|
2
|
+
// where every router call, every judgment and every provenance entry (write, draft, accept,
|
|
3
|
+
// confirm, decision) emits an event. Listeners registered in-process get every event. When a venture
|
|
4
|
+
// turns instrumentation on, events are also appended to <venture>/.naive/events.jsonl, which agent
|
|
5
|
+
// vitals read (interface/vitals.mjs, N-6). Provenance stays the record of what changed; events are
|
|
6
|
+
// what it cost. A platform exporter (platform/integrations/langsmith.mjs) can ship a trace made from
|
|
7
|
+
// the events; the framework names no vendor.
|
|
8
|
+
//
|
|
9
|
+
// Turn it on in the venture's naive.config.json, like adding instrumentation.ts to a Next.js app:
|
|
10
|
+
//
|
|
11
|
+
// { "instrumentation": { "events": true } }
|
|
12
|
+
//
|
|
13
|
+
// NAIVE_INSTRUMENT=1 turns it on for every venture in this process, and NAIVE_INSTRUMENT=0 off.
|
|
14
|
+
// No dependencies and no network. An event holds routes, sizes and times, never record content.
|
|
15
|
+
|
|
16
|
+
import { AsyncLocalStorage } from "node:async_hooks";
|
|
17
|
+
import { randomUUID } from "node:crypto";
|
|
18
|
+
import { appendFileSync, existsSync, mkdirSync, readFileSync } from "node:fs";
|
|
19
|
+
import { join } from "node:path";
|
|
20
|
+
import { ventureFile } from "../schema/venture-paths.mjs";
|
|
21
|
+
|
|
22
|
+
export const EVENTS = ".naive/events.jsonl";
|
|
23
|
+
const listeners = new Set();
|
|
24
|
+
const scope = new AsyncLocalStorage();
|
|
25
|
+
|
|
26
|
+
/** Tokens a payload costs an agent, estimated as characters / 4 (the common rule of thumb). */
|
|
27
|
+
export const tokens = (x) => Math.ceil((x === undefined ? 0 : typeof x === "string" ? x.length : (JSON.stringify(x) ?? "").length) / 4);
|
|
28
|
+
|
|
29
|
+
/** Registers a listener for every event, like register() in instrumentation.ts. Returns the unregister. */
|
|
30
|
+
export function register(fn) { listeners.add(fn); return () => listeners.delete(fn); }
|
|
31
|
+
|
|
32
|
+
const configOf = (dir) => {
|
|
33
|
+
const f = dir && join(ventureFile(dir, "naive.config.json"));
|
|
34
|
+
if (!f || !existsSync(f)) return {};
|
|
35
|
+
try { return JSON.parse(readFileSync(f, "utf8")); } catch { return {}; }
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
/** Whether a venture writes its events log. */
|
|
39
|
+
export function enabled(dir) {
|
|
40
|
+
if (process.env.NAIVE_INSTRUMENT === "0") return false;
|
|
41
|
+
if (process.env.NAIVE_INSTRUMENT === "1") return !!dir;
|
|
42
|
+
const i = configOf(dir).instrumentation;
|
|
43
|
+
return i === true || i?.events === true;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/** Emits one event: to every listener, and to the venture's events log when it is on. */
|
|
47
|
+
export function emit(dir, event) {
|
|
48
|
+
const e = { at: new Date().toISOString(), id: randomUUID(), session: process.env.MENTURA_SESSION ?? null, ...event };
|
|
49
|
+
for (const k of Object.keys(e)) if (e[k] === undefined) delete e[k];
|
|
50
|
+
for (const fn of listeners) { try { fn(e, dir); } catch {} }
|
|
51
|
+
if (dir && enabled(dir)) {
|
|
52
|
+
try { mkdirSync(join(dir, ".naive"), { recursive: true }); appendFileSync(ventureFile(dir, EVENTS), JSON.stringify(e) + "\n"); } catch {}
|
|
53
|
+
}
|
|
54
|
+
return e;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/** The venture's events, oldest first. */
|
|
58
|
+
export function events(dir) {
|
|
59
|
+
const f = ventureFile(dir, EVENTS);
|
|
60
|
+
if (!existsSync(f)) return [];
|
|
61
|
+
return readFileSync(f, "utf8").split("\n").filter((l) => l.trim()).flatMap((l) => { try { return [JSON.parse(l)]; } catch { return []; } });
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Wraps one router call: times it, measures what it sends back, and emits a call event. A call made
|
|
66
|
+
* inside another (a view reads its record, the overview runs its slots) carries its parent's id, so
|
|
67
|
+
* vitals count only what the agent asked for. dirOf resolves the venture folder.
|
|
68
|
+
*/
|
|
69
|
+
export async function instrumented(args, fn, dirOf) {
|
|
70
|
+
const parent = scope.getStore() ?? null;
|
|
71
|
+
const id = randomUUID();
|
|
72
|
+
const t0 = performance.now();
|
|
73
|
+
const r = await scope.run(id, () => fn(args));
|
|
74
|
+
if (process.env.NAIVE_INSTRUMENT === "0" && !listeners.size) return r;
|
|
75
|
+
let dir = null;
|
|
76
|
+
try { dir = args.venture ? dirOf(args.venture, args.workspace) : null; } catch {}
|
|
77
|
+
if (!dir && !listeners.size) return r;
|
|
78
|
+
const useful = !!r?.ok && r.result !== null && r.result !== undefined && !r.needs_input;
|
|
79
|
+
emit(dir, { kind: "call", id, parent, session: args.session ?? args.input?.session ?? process.env.MENTURA_SESSION ?? null, venture: args.venture ?? null, route: args.route, action: args.action,
|
|
80
|
+
ok: !!r?.ok, useful, ms: Math.round(performance.now() - t0), tokens: tokens(r?.result ?? r?.error), actor: args.actor ?? "agent", via: args.via ?? "router" });
|
|
81
|
+
return r;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/** Records one judgment (a System 1 call or a scored check): what was asked, the score and the outcome. */
|
|
85
|
+
export function judgment(dir, { question, score, threshold, outcome, owner, model, route, ms }) {
|
|
86
|
+
return emit(dir, { kind: "judgment", question, score, threshold, outcome, owner, model, route, ms });
|
|
87
|
+
}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
// The boundary around a kit's agent workflow (S-15): the Next.js advice to wrap a third-party component
|
|
2
|
+
// that uses client features, with the library author declaring "use client". A kit (Mentura's enhanced
|
|
3
|
+
// package, platform/kits/<name>/kit.json) declares, under boundary, what its workflow reads, what it
|
|
4
|
+
// writes and which of its steps use a model:
|
|
5
|
+
//
|
|
6
|
+
// { "name": "...", "package": "<the package it extends>",
|
|
7
|
+
// "boundary": { "reads": [<route globs>], "writes": [<route globs>], "use_model": [<model steps>] } }
|
|
8
|
+
//
|
|
9
|
+
// wrap(kit) returns the router's call with that boundary around it: a read outside reads or writes, a
|
|
10
|
+
// write outside writes, or a command it does not read is refused as an expected error, and every call
|
|
11
|
+
// acts as the kit's agent, never as a person. A kit that uses a model writes judgments, so it declares
|
|
12
|
+
// knowledge/judgment/** in writes. This module reads kit.json as data and never imports from platform/.
|
|
13
|
+
//
|
|
14
|
+
// node interface/kit-boundary.mjs <kit.json> check one kit's boundary
|
|
15
|
+
|
|
16
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
17
|
+
import { join } from "node:path";
|
|
18
|
+
import { call as routerCall, glob, repo, routes } from "./router.mjs";
|
|
19
|
+
// schema/model-steps.mjs is imported inside checkKit, not here, because it imports wrap from this module.
|
|
20
|
+
|
|
21
|
+
const ID = /^[a-z0-9][a-z0-9-]*$/;
|
|
22
|
+
export const actorOf = (kit) => `kit-${kit.name}`;
|
|
23
|
+
const any = (globs, route) => (globs ?? []).some((g) => glob(g).test(route));
|
|
24
|
+
const reachable = (b, route) => any(b.reads, route) || any(b.writes, route);
|
|
25
|
+
|
|
26
|
+
/** Errors in a kit's declared boundary, its model steps included. */
|
|
27
|
+
export async function checkKit(kit) {
|
|
28
|
+
const { checkSteps, inputOf } = await import("../schema/model-steps.mjs");
|
|
29
|
+
const e = [];
|
|
30
|
+
if (!kit || typeof kit !== "object") return ["a kit.json is an object"];
|
|
31
|
+
if (typeof kit.name !== "string" || !ID.test(kit.name)) e.push("a kit has a name of lowercase letters, digits and hyphens");
|
|
32
|
+
if (typeof kit.package !== "string" || !existsSync(join(repo, "packages", kit.package, "package.json"))) e.push(`kit ${kit.name}: package ${kit.package} is not a package in packages/ (a kit extends one package and never replaces it)`);
|
|
33
|
+
const b = kit.boundary;
|
|
34
|
+
if (!b || typeof b !== "object") { e.push(`kit ${kit.name}: declares a boundary: what it reads, what it writes, which steps use a model`); return e; }
|
|
35
|
+
for (const k of ["reads", "writes", "use_model"]) if (!Array.isArray(b[k])) e.push(`kit ${kit.name}: boundary.${k} is an array${k === "use_model" ? ", empty when it calls no model" : ""}`);
|
|
36
|
+
if (e.length) return e;
|
|
37
|
+
const all = routes().map((r) => r.route);
|
|
38
|
+
for (const g of [...b.reads, ...b.writes]) if (!all.some((r) => glob(g).test(r) || glob(g).test(r.replace("[id]", "x")))) e.push(`kit ${kit.name}: ${g} matches no route`);
|
|
39
|
+
const steps = b.use_model.map((s) => ({ ...s, package: kit.name }));
|
|
40
|
+
e.push(...(await checkSteps(steps)).map((x) => `kit ${x}`));
|
|
41
|
+
for (const s of steps) {
|
|
42
|
+
if (s.for_each && !reachable(b, s.for_each)) e.push(`kit ${kit.name}/${s.step}: reads ${s.for_each}, outside the kit's reads`);
|
|
43
|
+
for (const i of s.inputs ?? []) { const p = inputOf(i); if (p.form === "route" && !reachable(b, p.route)) e.push(`kit ${kit.name}/${s.step}: input ${i} is outside the kit's reads`); }
|
|
44
|
+
for (const spec of Object.values(s.computed ?? {})) {
|
|
45
|
+
const route = spec?.count ?? (spec?.read ? String(spec.read).split("#")[0] : null);
|
|
46
|
+
if (route && !reachable(b, route)) e.push(`kit ${kit.name}/${s.step}: computes from ${route}, outside the kit's reads`);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
if (steps.length && !any(b.writes, "knowledge/judgment/x")) e.push(`kit ${kit.name}: uses a model, so it writes judgments: declare knowledge/judgment/** in boundary.writes`);
|
|
50
|
+
return e;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/** The router's call, wrapped in the kit's boundary. */
|
|
54
|
+
export function wrap(kit, call = routerCall) {
|
|
55
|
+
const b = kit.boundary ?? { reads: [], writes: [], use_model: [] };
|
|
56
|
+
return async (args) => {
|
|
57
|
+
const route = args.route;
|
|
58
|
+
const act = args.action === "write" ? "write" : "read";
|
|
59
|
+
const ok = act === "write" ? any(b.writes, route) : reachable(b, route);
|
|
60
|
+
if (!ok) return { ok: false, outcome: "expected", error: `kit ${kit.name} declares no ${act} of ${route} (boundary.${act === "write" ? "writes" : "reads"} in its kit.json)` };
|
|
61
|
+
// The kit's workflow is an agent, whatever the caller passed.
|
|
62
|
+
return call({ ...args, actor: "agent", actor_id: actorOf(kit), provenance: { ...(args.provenance ?? {}), note: `kit ${kit.name}${args.provenance?.note ? `: ${args.provenance.note}` : ""}` } });
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
if (import.meta.url === `file://${process.argv[1]}`) {
|
|
67
|
+
const f = process.argv[2];
|
|
68
|
+
if (!f) { console.error("usage: node interface/kit-boundary.mjs <kit.json>"); process.exit(2); }
|
|
69
|
+
const kit = JSON.parse(readFileSync(f, "utf8"));
|
|
70
|
+
const e = await checkKit(kit);
|
|
71
|
+
if (e.length) { console.error(e.join("\n")); process.exit(1); }
|
|
72
|
+
console.log(`kit ${kit.name}: reads ${kit.boundary.reads.length} route patterns, writes ${kit.boundary.writes.length}, ${kit.boundary.use_model.length} model steps, inside its boundary.`);
|
|
73
|
+
}
|
package/interface/layer.json
CHANGED
|
@@ -6,10 +6,15 @@
|
|
|
6
6
|
{
|
|
7
7
|
"id": "install-mentura",
|
|
8
8
|
"path": "interface/skills/install-mentura/SKILL.md"
|
|
9
|
+
},
|
|
10
|
+
{
|
|
11
|
+
"id": "naive-setup",
|
|
12
|
+
"path": "interface/skills/naive-setup/SKILL.md"
|
|
9
13
|
}
|
|
10
14
|
],
|
|
11
15
|
"records": [
|
|
12
|
-
"route-aliases"
|
|
16
|
+
"route-aliases",
|
|
17
|
+
"adapter"
|
|
13
18
|
],
|
|
14
19
|
"reads": [
|
|
15
20
|
"integration"
|
|
@@ -17,7 +22,8 @@
|
|
|
17
22
|
"writes": [],
|
|
18
23
|
"checks": [
|
|
19
24
|
"schema/check-integrations.mjs",
|
|
20
|
-
"interface/test.mjs"
|
|
25
|
+
"interface/test.mjs",
|
|
26
|
+
"interface/adapters.mjs"
|
|
21
27
|
],
|
|
22
28
|
"gaps": [
|
|
23
29
|
"The operator console is a folder, not part of the layer yet",
|