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
package/interface/router.mjs
CHANGED
|
@@ -24,6 +24,11 @@
|
|
|
24
24
|
// record (escalate -> an agent's write is a draft), and the role's allowed routes
|
|
25
25
|
// robots a team-plan role may list the routes it may read and write; system/robots shows them
|
|
26
26
|
// aliases interface/route-aliases.json maps a renamed route to its new one, with a note
|
|
27
|
+
// hierarchy a read resolves layout, template, error, loading, not-found, then page (P-28);
|
|
28
|
+
// resolved_by names the level that answered (interface/reads.mjs)
|
|
29
|
+
// writes a write answers with what it changed and the next route to act on; a layout lists
|
|
30
|
+
// the actions open to the actor; package, group and venture routes; global errors
|
|
31
|
+
// (interface/writes.mjs wraps call)
|
|
27
32
|
// storage a record is read and written through the connector for the source its record
|
|
28
33
|
// type lives in (knowledge/connectors, EE-037): the venture's own folder unless its
|
|
29
34
|
// knowledge/sources.json names another, such as a GitHub repository, where a write
|
|
@@ -40,7 +45,27 @@ import { agentFromEnv, entries as provEntries, record as prov } from "./provenan
|
|
|
40
45
|
import { address } from "../knowledge/connectors/contract.mjs";
|
|
41
46
|
import { holding, locked } from "../schema/decisions.mjs";
|
|
42
47
|
import { configOf, hold } from "../schema/gate.mjs";
|
|
48
|
+
// Freshness (Revalidating, V-1 to V-8): stale flags on reads, and the freshness and revalidate commands.
|
|
49
|
+
import { readFlag as freshnessFlag } from "../schema/freshness.mjs";
|
|
50
|
+
// Gate group hooks (M-18, F-5, X-2, X-4 to X-6, P-2, P-7, U-4): who is acting, access at the record,
|
|
51
|
+
// the gate's verdict, where held drafts wait, and check settings. The logic lives in these modules.
|
|
52
|
+
import { actingCall, itemId, mayReadItem, mayReadSome } from "./acting.mjs";
|
|
53
|
+
import { checkPlan, gateWrite, heldDraft } from "./gate-hooks.mjs";
|
|
43
54
|
import { LOCAL, attributeSources, connect, sourceFor } from "../knowledge/connectors/index.mjs";
|
|
55
|
+
// Caching (schema/caching.mjs): per-action memo, the record-level cache for command routes, and system/cache, system/judge, system/answer.
|
|
56
|
+
import { actionScope, cacheRoutes, command as cachedCommand, forget, internal as cacheInternal, memo } from "../schema/caching.mjs";
|
|
57
|
+
// Context hook: instrumentation of every call and judgment (interface/instrumentation.mjs, P-4, F-4).
|
|
58
|
+
import { instrumented, judgment } from "./instrumentation.mjs";
|
|
59
|
+
// Hooks for writes, reads and boundaries (wf/writes): the wrapper around call, per-record containment and private folders.
|
|
60
|
+
import { around } from "./writes.mjs";
|
|
61
|
+
import { contain } from "./boundaries.mjs";
|
|
62
|
+
import { packageSlots } from "./package-routes.mjs";
|
|
63
|
+
import { routable } from "../schema/structure.mjs";
|
|
64
|
+
// Brand hook (Y-2..Y-4, J-2..J-4, T-1, T-2, P-24, P-26): routes, commands and layout context from schema/brand.mjs.
|
|
65
|
+
import { BRAND_ROUTES, brandCommands, brandLayout } from "../schema/brand.mjs";
|
|
66
|
+
// Upgrades hook (V-9): a read says when what the record reads changed in its system of record.
|
|
67
|
+
import { staleOf } from "../schema/stale.mjs";
|
|
68
|
+
import { ventureFile } from "../schema/venture-paths.mjs";
|
|
44
69
|
|
|
45
70
|
export const repo = new URL("..", import.meta.url).pathname;
|
|
46
71
|
const load = (p) => JSON.parse(readFileSync(p, "utf8"));
|
|
@@ -70,7 +95,9 @@ export function kits() {
|
|
|
70
95
|
const needNowPriors = () => load(join(repo, "schema/need-now.json"));
|
|
71
96
|
|
|
72
97
|
/** The route table, generated from schema/data-model.json, the kit manifests and schema/need-now.json. */
|
|
73
|
-
|
|
98
|
+
// Caching hook (F-12): built once per action and shared.
|
|
99
|
+
export function routes() { return memo("routes", buildRoutes); }
|
|
100
|
+
function buildRoutes() {
|
|
74
101
|
const model = load(join(repo, "schema/data-model.json"));
|
|
75
102
|
const ks = kits();
|
|
76
103
|
const owner = new Map(ks.flatMap((k) => k.records.map((r) => [r, k])));
|
|
@@ -110,6 +137,8 @@ export function routes() {
|
|
|
110
137
|
cmd("system/package", "schema/packages.mjs", "The venture package (EE-067, EE-069): the core packages open now and the handoffs, each record confirmed, to review or to write, what moves on its own and what waits, and what completion unlocks; --write renders the view and records a version. Assumes a solo founder or small founding team in Discovery and Validation; agents are a package enhancement"),
|
|
111
138
|
cmd("system/need-now", "schema/need-now.mjs", "What the venture needs now, soon, or should hold back (EE-028)"),
|
|
112
139
|
cmd("system/time-to-ready", "schema/time-to-ready.mjs", "Time to running, and to ready per layer and MAD tier"),
|
|
140
|
+
// Model steps hook (use model, EE-073): the logic is schema/model-steps.mjs; this route only shows the payload.
|
|
141
|
+
cmd("system/steps", "schema/model-steps.mjs", "The model steps' payload (use model, EE-073): what code computed, a placeholder for each judgment a model has yet to give, and the only fields each one receives; asks no model. mentura steps run asks one", { args: ["payload"] }),
|
|
113
142
|
cmd("purpose/validation/assess", "schema/assess-problem.mjs", "What the problem's evidence is strong enough for: each assumption labelled demonstrated, inferred, assumed, unknown or contradicted, the commitment it supports and the next test (EE-050). Asks first when the record is incomplete; input as_is runs it anyway", { as_is: true }),
|
|
114
143
|
cmd("governance/analytics", "schema/decision-analytics.mjs", "Decision analytics (EE-072): how many decisions of each type each function has made, open against decided, in whose words, in which phase, and their outcomes. Data only; a view is the platform's, or a visualization skill's", { actions: ["run", "layout"] }),
|
|
115
144
|
cmd("governance/decisions", "schema/decisions.mjs", "The decision matrix: every decision the venture owes by layer and phase, with its parent and boundary, and whether it renders now, waits, comes later, is stale or deferred (EE-054, EE-057)"),
|
|
@@ -124,9 +153,17 @@ export function routes() {
|
|
|
124
153
|
cmd("governance/payouts", "schema/payouts.mjs", "Who gets what at a sale value, from the cap table (EE-022); input value, else the scenarios in exit.json", { needs: ["governance/ownership.json"], venture_arg: "id" }),
|
|
125
154
|
cmd("system/overview", null, "The venture at a glance: telos, validation, need-now, time to ready, team and recent provenance, rendered as parallel slots", { internal: "overview" }),
|
|
126
155
|
cmd("system/robots", null, "Which roles may read and write which routes, from the team plan", { internal: "robots" }),
|
|
156
|
+
// Context hook (interface/vitals.mjs, interface/context.mjs): agent vitals and the context budget (N-6, C-15, N-13).
|
|
157
|
+
cmd("system/vitals", "interface/vitals.mjs", "Agent vitals (N-6): calls per task, tokens of context per call, time to a useful answer and time from a decision opening to a person deciding it, at the 75th percentile, each checked against its threshold (C-15). Reads the events log that instrumentation writes"),
|
|
158
|
+
cmd("system/context", "interface/context.mjs", "The context budget (N-13): what every route sends an agent, in tokens, against its budget, with the largest part and where to move it (S-14, F-6)", { args: ["budget"] }),
|
|
127
159
|
cmd("knowledge/catalog", "knowledge/connectors/catalog.mjs", "Build or refresh the catalog: every item in every source the venture keeps records in, with its address and version, no content (EE-037)"),
|
|
128
160
|
cmd("knowledge/search", "knowledge/connectors/catalog.mjs", "Find items across every source through the catalog; input value is the query (EE-037)", { value_flag: "--search" }),
|
|
161
|
+
// Freshness (Revalidating, V-1 to V-8): schema/freshness.mjs.
|
|
162
|
+
cmd("system/freshness", "schema/freshness.mjs", "What is fresh, stale or expired, and which records are live holes: by time (freshness profiles, per phase too) and on demand (a dependency changed, a where-used parent drifted, a tag, path or package was revalidated)"),
|
|
163
|
+
cmd("system/revalidate", "schema/freshness.mjs", "Mark records stale on demand: input value tag:<route> (the record and what depends on it), path:<folder>, package:<name>, or pivot (the validation package); a plain folder is refused in favour of exact tags", { value_flag: "--revalidate" }),
|
|
129
164
|
);
|
|
165
|
+
out.push(...cacheRoutes(cmd)); // Caching hook: system/cache, system/judge, system/answer (schema/caching.mjs).
|
|
166
|
+
out.push(...brandCommands(cmd)); // Brand hook: brand tokens, voice, glossary terms, sitemap.xml, asset facts (schema/brand.mjs).
|
|
130
167
|
return out.sort((a, b) => a.route.localeCompare(b.route));
|
|
131
168
|
}
|
|
132
169
|
|
|
@@ -172,21 +209,22 @@ export function ventureDir(id, workspace = defaultWorkspace()) {
|
|
|
172
209
|
}
|
|
173
210
|
return null;
|
|
174
211
|
}
|
|
175
|
-
|
|
212
|
+
// A broken venture.json is found by its folder name, so its global error can answer (E-8, interface/boundaries.mjs).
|
|
213
|
+
for (const d of walkVentures(workspace)) { let vid; try { vid = load(join(d, "venture.json")).id; } catch { vid = basename(d); } if (vid === id) return d; }
|
|
176
214
|
return null;
|
|
177
215
|
}
|
|
178
216
|
|
|
179
217
|
/** The ventures under a workspace: id, name, phase, path (the [...venture] segment) and folder. */
|
|
180
218
|
export function ventures(workspace = defaultWorkspace()) {
|
|
181
219
|
const root = existsSync(join(workspace, "ventures")) ? join(workspace, "ventures") : workspace;
|
|
182
|
-
return walkVentures(workspace).map((d) => {
|
|
220
|
+
return walkVentures(workspace).map((d) => { let v; try { v = load(join(d, "venture.json")); } catch { return { id: basename(d), name: null, phase: null, path: relative(root, d), dir: d, broken: true }; } return { id: v.id, name: v.name, phase: v.phase, path: relative(root, d), dir: d }; });
|
|
183
221
|
}
|
|
184
222
|
|
|
185
223
|
// The venture's decision rights: the template, tightened by the venture's own file.
|
|
186
224
|
function decisionRights(dir) {
|
|
187
225
|
const t = load(join(repo, "governance/venture-template/decision-rights.json"));
|
|
188
226
|
const classes = new Map(t.classes.map((c) => [c.id, c]));
|
|
189
|
-
const own = join(dir, "governance/decision-rights.json");
|
|
227
|
+
const own = join(ventureFile(dir, "governance/decision-rights.json"));
|
|
190
228
|
if (existsSync(own)) for (const c of load(own).classes) classes.set(c.id, { ...classes.get(c.id), ...c, level: classes.get(c.id)?.level === "escalate" ? "escalate" : c.level });
|
|
191
229
|
return classes;
|
|
192
230
|
}
|
|
@@ -200,9 +238,9 @@ export function gateOf(r, dir) {
|
|
|
200
238
|
}
|
|
201
239
|
|
|
202
240
|
// Robots: a team-plan role may list the routes it may read and write (globs over routes).
|
|
203
|
-
function allowed(dir, actorId, action, route) {
|
|
241
|
+
export function allowed(dir, actorId, action, route) {
|
|
204
242
|
if (!actorId || !dir) return true;
|
|
205
|
-
const plan = [join(dir, "people/team-plan.json"), join(dir, "operations/team-plan.json")].find((p) => existsSync(p));
|
|
243
|
+
const plan = [join(ventureFile(dir, "people/team-plan.json")), join(ventureFile(dir, "operations/team-plan.json"))].find((p) => existsSync(p));
|
|
206
244
|
if (!plan) return true;
|
|
207
245
|
const role = load(plan).roles.find((x) => x.id === actorId);
|
|
208
246
|
if (!role?.routes) return true;
|
|
@@ -219,17 +257,19 @@ const PACKAGE_ROUTE = {
|
|
|
219
257
|
"anatomy-answers": "product/anatomy-answers", "anatomy-input": "product/anatomy", "venture-package": ".release/venture-package",
|
|
220
258
|
"decision-record": "decisions",
|
|
221
259
|
customer: "customers", investor: "investors", "money-account": "money", "raw-source": "raw/sources",
|
|
260
|
+
people: "people", founder: "people/founders", "fund-thesis": "fund/thesis", "fund-terms": "fund/terms", deal: "deals",
|
|
261
|
+
...BRAND_ROUTES, // Brand hook: marketing/brand, public/assets, glossary (schema/brand.mjs).
|
|
222
262
|
};
|
|
223
263
|
|
|
224
264
|
// Prefer a file that is already there, so an unmigrated venture keeps its old path. A new write uses the first path, which is the package layout.
|
|
225
265
|
const fileFor = (r, params, dir) => {
|
|
226
266
|
const cands = (r.collection === "item" ? r.files.map((f) => f.replace("*", params.id)) : r.files).filter((f) => !f.includes("*"));
|
|
227
267
|
if (dir) {
|
|
228
|
-
const hit = cands.find((f) => existsSync(
|
|
268
|
+
const hit = cands.find((f) => existsSync(ventureFile(dir, f)));
|
|
229
269
|
if (hit) return hit;
|
|
230
270
|
// A new collection item joins the folder the venture already keeps that collection in.
|
|
231
271
|
if (r.collection === "item") {
|
|
232
|
-
const lived = cands.find((f) => { const d =
|
|
272
|
+
const lived = cands.find((f) => { const d = ventureFile(dir, dirname(f)); return existsSync(d) && readdirSync(d).some((n) => n.endsWith(".json") || n.endsWith(".md") || n.endsWith(".mdx")); });
|
|
233
273
|
if (lived) return lived;
|
|
234
274
|
}
|
|
235
275
|
}
|
|
@@ -243,13 +283,14 @@ export function draftOf(route, venture, workspace) {
|
|
|
243
283
|
const dir = ventureDir(venture, workspace);
|
|
244
284
|
if (!r || r.kind !== "record" || r.collection === "list" || !dir) return null;
|
|
245
285
|
const file = fileFor(r, params, dir);
|
|
286
|
+
if (!file) return null; // a route with no concrete file (a pattern-only slot) has no draft
|
|
246
287
|
return { dir, file, draft: draftPath(file), gate: gateOf(r, dir), route: r.collection === "item" ? r.route.replace("[id]", params.id) : r.route };
|
|
247
288
|
}
|
|
248
289
|
|
|
249
290
|
function agentsChain(dir, route) {
|
|
250
291
|
if (!dir) return [];
|
|
251
292
|
const chain = [];
|
|
252
|
-
const add = (rel) => { const f =
|
|
293
|
+
const add = (rel) => { const f = ventureFile(dir, rel); if (existsSync(f)) chain.push({ path: rel, text: readFileSync(f, "utf8") }); };
|
|
253
294
|
add("AGENTS.md");
|
|
254
295
|
const parts = route.split("/").filter(Boolean);
|
|
255
296
|
let acc = "";
|
|
@@ -266,9 +307,11 @@ function instructionsOf(chain) {
|
|
|
266
307
|
}
|
|
267
308
|
|
|
268
309
|
/** A score below its threshold is uncertain: not an error and not an answer. It routes to the function owner. */
|
|
269
|
-
export function judge(score, threshold, owner) {
|
|
270
|
-
|
|
271
|
-
|
|
310
|
+
export function judge(score, threshold, owner, at) {
|
|
311
|
+
const out = typeof score !== "number" || score < threshold ? { outcome: "uncertain", owner, result: null } : { outcome: "answer", result: score };
|
|
312
|
+
// Context hook: a judgment made for a venture (at.dir) is an instrumentation event (F-4).
|
|
313
|
+
if (at?.dir) judgment(at.dir, { question: at.question, route: at.route, model: at.model, score, threshold, outcome: out.outcome, owner });
|
|
314
|
+
return out;
|
|
272
315
|
}
|
|
273
316
|
|
|
274
317
|
/** Runs fn. A throw is the uncaught outcome. */
|
|
@@ -292,9 +335,11 @@ export function layout(r, dir) {
|
|
|
292
335
|
const pkg = owned ? { skill: skillsOf(owned).map((s) => s.path), template: r.template ?? null, gates: owned.gates ?? [] } : null;
|
|
293
336
|
const out = { route: r.route, stack, chain, instructions: instructionsOf(chain), package: pkg, note: r.note, groups: r.groups ?? [] };
|
|
294
337
|
if (r.kind === "record") Object.assign(out, { schema: r.schema, template: r.template, checks: r.checks, gate: gateOf(r, dir) });
|
|
338
|
+
{ const brand = brandLayout(dir, r.route); if (brand) out.brand = brand; } // Brand hook: brand, voice and package terms at this route (Y-2, Y-3, T-2).
|
|
295
339
|
if (dir && r.kind === "record") {
|
|
296
340
|
try {
|
|
297
|
-
|
|
341
|
+
// Caching hook (O-5): shared with the system/need-now command in the same action.
|
|
342
|
+
const nn = JSON.parse(memo(`cmd:schema/need-now.mjs ${dir} --json`, () => spawnSync("node", [join(repo, "schema/need-now.mjs"), dir, "--json"], { encoding: "utf8" }), { dir }).stdout);
|
|
298
343
|
const row = nn.rows.find((x) => x.type === r.record);
|
|
299
344
|
if (row) out.need_now = { phase: nn.phase, status: row.status, now: row.now, next: row.next, why: row.why };
|
|
300
345
|
} catch {}
|
|
@@ -311,10 +356,18 @@ const fix = (r) => ({ skills: r.kit_path ? skillsOf(kits().find((k) => k.path ==
|
|
|
311
356
|
* routes; via: the interface (cli, mcp, api; default router); provenance: { derived_from,
|
|
312
357
|
* decision, note, agent } for the log.
|
|
313
358
|
*/
|
|
314
|
-
|
|
359
|
+
// Caching hook (F-2): one top-level call is one action; the calls it makes join it and share its memo.
|
|
360
|
+
// Context hook: every call is timed and measured for agent vitals (interface/instrumentation.mjs).
|
|
361
|
+
// Gate group hook (M-18): who is acting is checked on every call and carried on the answer.
|
|
362
|
+
// Writes hook (interface/writes.mjs): adds what reads, layouts and writes answer.
|
|
363
|
+
export const call = (args) => actionScope(() => instrumented(args, (a) => actingCall(a, (b) => around(callInAction, b), ventureDir), ventureDir));
|
|
364
|
+
async function callInAction({ venture, route, action, input, actor = "agent", actor_id = process.env.MENTURA_ACTOR_ID, workspace, via = "router", provenance = {} }) {
|
|
315
365
|
const requested = route;
|
|
316
366
|
const { r, params, alias } = resolve(route);
|
|
317
367
|
if (!r) return { ok: false, outcome: "error", error: `no route ${route}` };
|
|
368
|
+
// Context hook (interface/context.mjs): navigate reads a route header first, with its layout once per
|
|
369
|
+
// session, its template and prefetched refs; head stops after the header (N-3, L-2, O-4).
|
|
370
|
+
if (action === "navigate" || action === "head") return (await import("./context.mjs")).navigate({ ...(input ?? {}), venture, route: requested, workspace, actor, actor_id, via, header_only: action === "head" || !!input?.header_only });
|
|
318
371
|
const deprecated = alias ? { deprecated: `${alias.from} is now ${alias.to}${alias.note ? `: ${alias.note}` : ""}` } : {};
|
|
319
372
|
route = r.collection === "item" ? r.route.replace("[id]", params.id) : r.route;
|
|
320
373
|
if (!r.actions.includes(action)) return { ok: false, error: `${route} has no action ${action} (it has ${r.actions.join(", ")})` };
|
|
@@ -323,7 +376,8 @@ export async function call({ venture, route, action, input, actor = "agent", act
|
|
|
323
376
|
if (action === "layout") { if (venture && !dir) return { ok: false, error: `no venture ${venture}` }; return { ok: true, result: layout(r, dir), ...deprecated }; }
|
|
324
377
|
if (!dir) return { ok: false, error: `no venture ${venture}` };
|
|
325
378
|
const act = action === "write" ? "write" : "read";
|
|
326
|
-
|
|
379
|
+
// Gate group hook (F-5): a role that may read some items of a collection may read its list, filtered below.
|
|
380
|
+
if (!allowed(dir, actor_id, act, requested) && !allowed(dir, actor_id, act, route) && !(act === "read" && r.collection === "list" && mayReadSome(dir, actor_id, route))) return { ok: false, error: `${actor_id} may not ${act} ${route} (team plan routes)` };
|
|
327
381
|
// An activity a decision unlocks stays closed to agents until a person makes that decision (EE-058).
|
|
328
382
|
const lock = actor !== "person" && (action === "write" || action === "run") ? (locked(dir, requested, actor_id) || locked(dir, route, actor_id)) : null;
|
|
329
383
|
if (lock) return { ok: false, error: `${route} is locked until decision ${lock.id} is made: ${lock.question}`, locked_by: lock.id,
|
|
@@ -337,15 +391,19 @@ export async function call({ venture, route, action, input, actor = "agent", act
|
|
|
337
391
|
if (r.kind === "command") {
|
|
338
392
|
if (r.internal === "overview") return { ok: true, result: await overview(venture, workspace), ...deprecated };
|
|
339
393
|
if (r.internal === "robots") return { ok: true, result: robots(dir), ...deprecated };
|
|
340
|
-
|
|
394
|
+
// Caching hook: system/cache, system/judge and system/answer are handled in schema/caching.mjs.
|
|
395
|
+
if (["cache", "judge", "answer"].includes(r.internal)) return { ...(await cacheInternal(r.internal, { dir, venture, workspace, input, actor, actor_id, via, provenance }, { call, resolve, draftOf })), ...deprecated };
|
|
396
|
+
const lacking = (r.needs ?? []).filter((f) => !existsSync(ventureFile(dir, f)));
|
|
341
397
|
if (lacking.length) return { ok: false, error: `${route} needs ${lacking.join(", ")} in ${venture}`, fix: { missing: lacking } };
|
|
342
398
|
if (r.value_flag && !input?.value) return { ok: false, error: `${route} needs a value (${r.note})` };
|
|
343
399
|
const args = r.venture_arg === "id" ? [dir, ...(input?.value ? [String(input.value)] : []), "--json"] : r.value_flag ? [dir, r.value_flag, String(input.value), "--json"] : [dir, ...(r.args ?? []), ...(r.as_is && input?.as_is ? ["--as-is"] : []), "--json"];
|
|
344
|
-
|
|
400
|
+
// Caching hook (C-3, H-3): a declared command's result comes from the record-level cache; the spawn is memoized per action (O-5).
|
|
401
|
+
const p = await cachedCommand({ dir, route, script: r.script, args, input, actor, actor_id }, () => memo(`cmd:${r.script} ${args.join(" ")}`, () => spawnSync("node", [join(repo, r.script), ...args], { encoding: "utf8" }), { dir }));
|
|
402
|
+
const cache = p.cache ? { cache: p.cache } : {};
|
|
345
403
|
if (p.status !== 0) return { ok: false, error: (p.stderr || p.stdout).trim() };
|
|
346
404
|
// A command that needs input first (EE-050) answers needs_input: the caller asks the person
|
|
347
405
|
// whether to fill the record in or run as is, and runs again with input as_is to go on.
|
|
348
|
-
try { const out = JSON.parse(p.stdout); return { ok: true, result: out.needs_input ? null : out, ...(out.needs_input ? { needs_input: out.needs_input } : {}), ...deprecated }; } catch { return { ok: true, result: p.stdout, ...deprecated }; }
|
|
406
|
+
try { const out = JSON.parse(p.stdout); return { ok: true, result: out.needs_input ? null : out, ...(out.needs_input ? { needs_input: out.needs_input } : {}), ...cache, ...deprecated }; } catch { return { ok: true, result: p.stdout, ...cache, ...deprecated }; }
|
|
349
407
|
}
|
|
350
408
|
|
|
351
409
|
// Storage: the connector for the source this record type lives in.
|
|
@@ -365,11 +423,15 @@ export async function call({ venture, route, action, input, actor = "agent", act
|
|
|
365
423
|
const patterns = r.collection === "item" ? [fileFor(r, params, dir)] : r.files;
|
|
366
424
|
let items;
|
|
367
425
|
try {
|
|
368
|
-
|
|
369
|
-
|
|
426
|
+
// a file named _* is never an item; routable: private _folders (P-22)
|
|
427
|
+
const paths = patterns.some((g) => g.includes("*")) ? (await store.list()).map((x) => x.path).filter((f) => !/(^|\/)_[^/]*$/.test(f) && patterns.some((g) => glob(g).test(f) && routable(f, g))) : patterns;
|
|
428
|
+
// Caching hook (F-2): each record is read once per action and shared.
|
|
429
|
+
items = (await Promise.all(paths.map((f) => memo(`read:${src.id}:${f}`, () => store.read(f), { dir })))).filter(Boolean);
|
|
430
|
+
// Gate group hook (F-5): access is checked again at the record; a list holds only the items the role may read.
|
|
431
|
+
if (r.collection === "list" && actor_id) items = items.filter((x) => mayReadItem(dir, actor_id, route, itemId(r.files, x.path)));
|
|
370
432
|
} catch (e) { return { ok: false, error: `${src.id}: ${String(e.message ?? e)}` }; }
|
|
371
433
|
const found = items.map((x) => x.path);
|
|
372
|
-
const draft = !remote && r.collection !== "list" && existsSync(
|
|
434
|
+
const draft = !remote && r.collection !== "list" && fileFor(r, params, dir) && existsSync(ventureFile(dir, draftPath(fileFor(r, params, dir)))) ? draftPath(fileFor(r, params, dir)) : null;
|
|
373
435
|
const loading = draft ? { pending_draft: { file: draft, since: provEntries(dir).filter((e) => e.entity === draft).at(-1)?.at ?? null, accept: `mentura accept ${route} --venture ${venture}` } } : {};
|
|
374
436
|
if (remote) {
|
|
375
437
|
const prs = provEntries(dir).filter((e) => e.source?.pull_request && found.concat(patterns).some((f) => e.entity === address(src.id, f)));
|
|
@@ -394,6 +456,9 @@ export async function call({ venture, route, action, input, actor = "agent", act
|
|
|
394
456
|
}
|
|
395
457
|
if (attrs.length) x.attributes = cites;
|
|
396
458
|
}
|
|
459
|
+
// Freshness (V-5): a stale record is still read, flagged stale until it is redone (stale-while-revalidate).
|
|
460
|
+
const fresh = remote ? {} : await freshnessFlag(dir, found, r.collection === "list");
|
|
461
|
+
Object.assign(loading, fresh);
|
|
397
462
|
const parseOne = parse;
|
|
398
463
|
const parseWith = (x) => { const v = parseOne(x); return overlays.has(x.path) ? { ...v, ...overlays.get(x.path) } : v; };
|
|
399
464
|
if (r.collection === "list") {
|
|
@@ -406,23 +471,30 @@ export async function call({ venture, route, action, input, actor = "agent", act
|
|
|
406
471
|
}
|
|
407
472
|
return path;
|
|
408
473
|
};
|
|
409
|
-
|
|
474
|
+
// Per-record containment (E-6): an item that does not parse is named in errors; the rest come back.
|
|
475
|
+
const { values, errors } = contain(items, parseWith, idOf);
|
|
476
|
+
return { ok: true, result: Object.fromEntries(values.map(([x, v]) => [idOf(x.path), v])), sources: Object.fromEntries(values.map(([x]) => [idOf(x.path), cite(x)])), ...(Object.keys(errors).length ? { errors } : {}), ...fresh, ...deprecated };
|
|
410
477
|
}
|
|
411
478
|
const data = Object.fromEntries(items.map((x) => [x.path, parseWith(x)]));
|
|
412
|
-
|
|
479
|
+
// V-9 hook: a summary whose source changed since it was written is marked stale (schema/stale.mjs).
|
|
480
|
+
const stale = !remote && items.length === 1 ? staleOf(dir, found[0]) : null;
|
|
481
|
+
return { ok: true, result: items.length === 1 ? data[found[0]] : data, ...(items.length === 1 ? { source: cite(items[0]) } : { sources: Object.fromEntries(items.map((x) => [x.path, cite(x)])) }), ...loading, ...(stale ? { stale } : {}), ...deprecated };
|
|
413
482
|
}
|
|
414
483
|
|
|
415
484
|
if (action === "check") {
|
|
416
|
-
|
|
417
|
-
const
|
|
418
|
-
|
|
485
|
+
// Gate group hook (P-7): the checks settings in naive.config.json turn a check off or make it warn only.
|
|
486
|
+
const plan = checkPlan(dir, r.checks);
|
|
487
|
+
const outs = plan.filter((p) => p.mode !== "off").map((p) => ({ check: p.check, mode: p.mode, ...spawnSync("node", [join(repo, p.check), dir], { encoding: "utf8" }) }));
|
|
488
|
+
const ok = outs.every((o) => o.status === 0 || o.mode === "warn");
|
|
489
|
+
return { ok, result: [...outs.map((o) => ({ check: o.check, passed: o.status === 0, ...(o.mode === "warn" ? { warn_only: true } : {}), output: (o.stdout + o.stderr).trim() })), ...plan.filter((p) => p.mode === "off").map((p) => ({ check: p.check, passed: null, off: true }))], ...(ok ? {} : { fix: fix(r) }), ...deprecated };
|
|
419
490
|
}
|
|
420
491
|
|
|
421
492
|
if (action === "write") {
|
|
493
|
+
forget(dir); // Caching hook (H-3): a write never uses the cache, and what this action memoized for the venture is read again after it.
|
|
422
494
|
if (!input || typeof input !== "object") return { ok: false, error: "write needs the record as input" };
|
|
423
495
|
if (r.collection === "item" && !/^[a-z0-9][a-z0-9-]*$/.test(params.id)) return { ok: false, error: `${params.id} is not a valid id (lowercase letters, digits and hyphens)` };
|
|
424
496
|
const file = fileFor(r, params, dir);
|
|
425
|
-
if (["idea-dump", "raw-source", "raw-text"].includes(r.record) && existsSync(
|
|
497
|
+
if (["idea-dump", "raw-source", "raw-text"].includes(r.record) && existsSync(ventureFile(dir, file))) return { ok: false, error: `${route} is raw and immutable: add a new capture, do not edit this one` };
|
|
426
498
|
const gate = gateOf(r, dir);
|
|
427
499
|
const text = JSON.stringify(input, null, 2) + "\n";
|
|
428
500
|
// The schema first, on a scratch copy, so nothing reaches any source unless it matches.
|
|
@@ -444,7 +516,9 @@ export async function call({ venture, route, action, input, actor = "agent", act
|
|
|
444
516
|
// (EE-054): an agent's write becomes a draft, as for a gated record.
|
|
445
517
|
const held = !remote && actor !== "person" ? holding(dir, file) : null;
|
|
446
518
|
// The venture's own fail-closed routes (naive.config.json, the gate, EE-073) hold an agent's write too.
|
|
447
|
-
|
|
519
|
+
// Gate group hook (X-1 to X-6, U-4): the venture's gate decides; a refusal stops the write here.
|
|
520
|
+
const configured = !remote ? gateWrite({ dir, r, route, requested, file, actor, actor_id, provenance, gate }) : null;
|
|
521
|
+
if (configured?.then === "refuse") return { ok: false, error: configured.why, gate_rule: configured.rule, refused_by: configured.source };
|
|
448
522
|
const gatedDraft = !remote && (!!gate?.drafts || !!held || !!configured) && actor !== "person";
|
|
449
523
|
const attributeWrites = [];
|
|
450
524
|
if (split.length && !gatedDraft) {
|
|
@@ -482,13 +556,12 @@ export async function call({ venture, route, action, input, actor = "agent", act
|
|
|
482
556
|
}
|
|
483
557
|
|
|
484
558
|
const draft = gatedDraft;
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
return { ok: true, result: { written: draft ? draftPath(file) : file, draft, gate: gate?.class ?? null, provenance: entry.hash, note: draft ? (gate?.drafts ? `${r.record} is a ${gate.class} decision (${gate.level}, owner ${gate.owner}): written as a draft for a person to accept` : held ? `decision ${held.id} ${held.why}: written as a draft for a person to accept` : `${configured.rule} fails closed in naive.config.json: written as a draft for a person to accept`) : "written", ...(held ? { held_by: held.id } : {}), ...(configured ? { gate_rule: configured.rule } : {}), ...attributesOut }, ...deprecated };
|
|
559
|
+
// Gate group hook (X-2): the held draft, the owner it was sent to and any variants kept side by side.
|
|
560
|
+
let held2 = null;
|
|
561
|
+
if (draft) held2 = heldDraft(dir, { draft: draftPath(file), file, text, verdict: configured, route, agent });
|
|
562
|
+
else await store.write(file, recordText);
|
|
563
|
+
const entry = log(draft ? held2.written : file, draft ? "draft" : provenance.activity ?? "write");
|
|
564
|
+
return { ok: true, result: { written: draft ? held2.written : file, draft, gate: gate?.class ?? null, provenance: entry.hash, note: draft ? (gate?.drafts ? `${r.record} is a ${gate.class} decision (${gate.level}, owner ${gate.owner}): written as a draft for a person to accept` : held ? `decision ${held.id} ${held.why}: written as a draft for a person to accept` : configured.why) : "written", ...(held ? { held_by: held.id } : {}), ...(configured ? { gate_rule: configured.rule, ...(configured.owner ? { owner: configured.owner } : {}) } : {}), ...(held2?.variants ? { variant: held2.variant, variants: held2.variants, decision: held2.decision } : {}), ...attributesOut }, ...deprecated };
|
|
492
565
|
}
|
|
493
566
|
return { ok: false, error: `unhandled action ${action}` };
|
|
494
567
|
}
|
|
@@ -539,6 +612,7 @@ async function overview(venture, workspace) {
|
|
|
539
612
|
slot("@need-now", async () => (await run("system/need-now")).rows.filter((x) => x.status === "suggest now").map((x) => ({ record: x.type, now: x.now, why: x.why }))),
|
|
540
613
|
slot("@ready", async () => { const t = await run("system/time-to-ready"); return { running: t.running.on ?? `not yet: ${t.running.missing.join(", ")}`, layers: t.layers.map((l) => ({ layer: l.layer, ready: l.ready_on ?? `missing ${l.missing.join(", ")}` })) }; }),
|
|
541
614
|
slot("@team", async () => { const p = await read("people/team-plan"); if (typeof p === "string") return p; return p.roles.map((x) => ({ role: x.id, occupant: x.occupant, status: x.status, needed_by: x.needed_by })); }),
|
|
615
|
+
slot("@packages", () => packageSlots(dir, venture)), // P-17: a package with nothing in it shows its default
|
|
542
616
|
slot("@provenance", () => provEntries(dir).slice(-5).reverse().map((e) => ({ at: e.at, activity: e.activity, entity: e.entity, by: `${e.agent.kind}${e.agent.id ? ` ${e.agent.id}` : ""} via ${e.agent.via}` }))),
|
|
543
617
|
]),
|
|
544
618
|
};
|
|
@@ -546,7 +620,7 @@ async function overview(venture, workspace) {
|
|
|
546
620
|
|
|
547
621
|
// Robots: per role, the routes it may read and write.
|
|
548
622
|
function robots(dir) {
|
|
549
|
-
const plan = [join(dir, "people/team-plan.json"), join(dir, "operations/team-plan.json")].find((p) => existsSync(p));
|
|
623
|
+
const plan = [join(ventureFile(dir, "people/team-plan.json")), join(ventureFile(dir, "operations/team-plan.json"))].find((p) => existsSync(p));
|
|
550
624
|
if (!plan) return { note: "no team plan: every actor may reach every route; gates still apply", roles: [] };
|
|
551
625
|
return { note: "a role without routes may reach every route; gates still apply", roles: load(plan).roles.map((r) => ({ role: r.id, occupant: r.occupant, read: (r.routes?.read ?? ["**"]).join(", "), write: (r.routes?.write ?? (r.routes ? [] : ["**"])).join(", ") || "none" })) };
|
|
552
626
|
}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
---
|
|
2
|
+
type: knowledge
|
|
3
|
+
tags: [everything-ecosystem, naive, skills, interface, setup, artifacts, docket]
|
|
4
|
+
ai-model: claude-opus-5.5
|
|
5
|
+
created: 2026-10-29T06:30:00Z
|
|
6
|
+
source: hybrid
|
|
7
|
+
name: naive-setup
|
|
8
|
+
description: >
|
|
9
|
+
Walk a founder through setting up a new Naive venture inside the model's own
|
|
10
|
+
harness: their people and hats, where each function keeps its truth, the
|
|
11
|
+
strategic foundation and first decisions, and a goal for discovery. Show the
|
|
12
|
+
progress as a Claude artifact where the harness has artifacts (EE-083).
|
|
13
|
+
MANDATORY TRIGGERS: set up my venture, naive setup, finish setup, walk me
|
|
14
|
+
through setup, I just ran create-venture, establish my venture, structure my
|
|
15
|
+
business, what do I do after create-venture.
|
|
16
|
+
---
|
|
17
|
+
|
|
18
|
+
# Naive setup
|
|
19
|
+
|
|
20
|
+
Interface layer (EE-083). `create-venture` scaffolds the venture. This skill finishes the job in conversation. You ask the questions, the founder answers in their own words, and Naive writes each answer as a decision or a source-of-truth pointer, with provenance. The progress is a view the harness renders, not a server Naive runs. In Claude, it is an artifact.
|
|
21
|
+
|
|
22
|
+
The questions and their order come from Naive, not from you. Read them from `mentura_setup` (MCP) or `node schema/setup.mjs <venture dir> state`, and never invent a question.
|
|
23
|
+
|
|
24
|
+
## 1. Read the state
|
|
25
|
+
|
|
26
|
+
Call `mentura_setup` with the venture id. The result has:
|
|
27
|
+
|
|
28
|
+
- `state`: the people, sources of truth, docket and goal, plus `steps` (which of the four are done), `done`, and `counts`.
|
|
29
|
+
- `artifact`: the state, already rendered into `interface/skills/naive-setup/artifact.html`, as one self-contained HTML page.
|
|
30
|
+
|
|
31
|
+
Without MCP, run `node schema/setup.mjs <venture dir> state` and `node schema/setup.mjs <venture dir> artifact`.
|
|
32
|
+
|
|
33
|
+
## 2. Show it
|
|
34
|
+
|
|
35
|
+
- **Claude (claude.ai, the desktop app, or Claude Code with the Artifact tool):** render `artifact` as an HTML artifact. Do not edit its contents; it is generated. Render it again after every answer, so the founder sees the progress bar move.
|
|
36
|
+
- **A harness without artifacts:** show the four steps as a short checklist in Markdown, with the next open question underneath.
|
|
37
|
+
|
|
38
|
+
## 3. Ask one question at a time, in order
|
|
39
|
+
|
|
40
|
+
Work through the first step that is not done, and ask only its next open question:
|
|
41
|
+
|
|
42
|
+
1. **People and hats.** If `state.people` is missing, ask for the founder's name, role, and whether they are solo or a team. A solo founder wears every hat. These come from `create-venture`. If they are missing, tell the founder to run `npx create-venture` again with `--name`, `--role` and `--team`. Do not write `people/` by hand.
|
|
43
|
+
2. **Sources of truth.** For each function, ask where its truth lives, offering the systems in `state.catalog.functions[<function>]`. Write each answer with `mentura_setup_point` (`function`, `system`, and an `address` such as `acme/app`). If the founder has nothing for a function yet, leave it unpointed and move on. For legal, if there are no documents, say so and name the options in the catalog.
|
|
44
|
+
3. **Foundation and first decisions.** These are the `state.docket` items whose status is not `decided`, taken in order. Ask the question as written. If it has options, offer them, but take the founder's own words. Write the answer with `mentura_setup_answer` (`id` and `answer`).
|
|
45
|
+
4. **A goal for discovery.** Ask what must be true for the venture to leave discovery. Write it with `mentura_setup_answer`, using `id: "discovery-goal"`.
|
|
46
|
+
|
|
47
|
+
## 4. The founder's words, not yours
|
|
48
|
+
|
|
49
|
+
- Pass the founder's answer exactly as they wrote it. Do not polish it, summarize it, or fill in an answer they did not give.
|
|
50
|
+
- If an answer is unclear, ask a follow-up question. Do not guess.
|
|
51
|
+
- A foundation statement is the founder's to state. If they ask you to draft one, show the draft and ask them to accept it or rewrite it in their own words. Write it only after they reply, and write what they replied.
|
|
52
|
+
|
|
53
|
+
## 5. When every step is done
|
|
54
|
+
|
|
55
|
+
`state.done` is true. Render the artifact one last time: it shows "Your venture is structured." Then tell the founder, in a few lines:
|
|
56
|
+
|
|
57
|
+
- the venture now has its people, its sources of truth, its foundation and first decisions, and a discovery goal;
|
|
58
|
+
- how many decisions wait for later phases (`state.later`); the docket brings each one up when it matters;
|
|
59
|
+
- each next decision should be oriented around the discovery goal;
|
|
60
|
+
- to operate the business day to day, with a team and agents, they use Mentura. Naive is the structure; Mentura is where it runs, the way a Next.js app is deployed on Vercel.
|
|
61
|
+
|
|
62
|
+
## Without a model
|
|
63
|
+
|
|
64
|
+
The same steps run from the command line:
|
|
65
|
+
|
|
66
|
+
```sh
|
|
67
|
+
node schema/setup.mjs ~/ventures/acme state
|
|
68
|
+
node schema/setup.mjs ~/ventures/acme point finance rho
|
|
69
|
+
node schema/setup.mjs ~/ventures/acme answer discovery-goal "Ten clinic owners confirm the scheduling problem by December"
|
|
70
|
+
node schema/setup.mjs ~/ventures/acme artifact > setup.html
|
|
71
|
+
```
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
<!doctype html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="utf-8">
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
6
|
+
<title>Venture setup</title>
|
|
7
|
+
<style>
|
|
8
|
+
:root { --bg:#f6f7f4; --panel:#fff; --fg:#1d2320; --muted:#5d675f; --line:#d9ded8; --accent:#2f6b4f; --accent-soft:#e3efe8; --warn:#8a5a00; --warn-soft:#f6ecd8;
|
|
9
|
+
--body: system-ui, -apple-system, "Segoe UI", sans-serif; --mono: ui-monospace, "SF Mono", Menlo, monospace; color-scheme: light; }
|
|
10
|
+
@media (prefers-color-scheme: dark) { :root:not([data-theme="light"]) { --bg:#121614; --panel:#1a201d; --fg:#e4e9e5; --muted:#9aa59d; --line:#2b332e; --accent:#86c4a3; --accent-soft:#1d2b24; --warn:#e3b25c; --warn-soft:#2c2414; color-scheme: dark; } }
|
|
11
|
+
:root[data-theme="dark"] { --bg:#121614; --panel:#1a201d; --fg:#e4e9e5; --muted:#9aa59d; --line:#2b332e; --accent:#86c4a3; --accent-soft:#1d2b24; --warn:#e3b25c; --warn-soft:#2c2414; color-scheme: dark; }
|
|
12
|
+
* { box-sizing: border-box }
|
|
13
|
+
body { margin:0; background:var(--bg); color:var(--fg); font:16px/1.55 var(--body); }
|
|
14
|
+
.wrap { max-width: 820px; margin: 0 auto; padding: 28px 16px 64px; display: grid; gap: 22px; }
|
|
15
|
+
header { display:grid; gap:6px } h1 { margin:0; font-size:1.7rem; letter-spacing:-.01em } h2 { margin:0 0 2px; font-size:1.1rem }
|
|
16
|
+
.muted { color: var(--muted) } .mono { font-family: var(--mono); font-size: .86em; overflow-wrap: anywhere }
|
|
17
|
+
.bar { height: 8px; border-radius: 999px; background: var(--line); overflow: hidden } .bar > div { height: 100%; background: var(--accent) }
|
|
18
|
+
.steps { display:flex; gap:6px; flex-wrap:wrap } .step { font:600 .76rem/1 var(--mono); padding:6px 9px; border-radius:999px; border:1px solid var(--line); color:var(--muted) }
|
|
19
|
+
.step.ok { background: var(--accent-soft); color: var(--accent); border-color: transparent } .step.next { border-color: var(--accent); color: var(--accent) }
|
|
20
|
+
section { background: var(--panel); border:1px solid var(--line); border-radius:10px; padding:18px; display:grid; gap:12px; min-width: 0 }
|
|
21
|
+
.table { overflow-x: auto } table { width:100%; border-collapse: collapse; font-size:.93rem }
|
|
22
|
+
td.mono { white-space: nowrap }
|
|
23
|
+
td, th { text-align:left; padding:7px 6px; border-bottom:1px solid var(--line); vertical-align: top }
|
|
24
|
+
th { font: 600 .72rem/1.2 var(--mono); text-transform: uppercase; letter-spacing:.05em; color: var(--muted) }
|
|
25
|
+
.pill { display:inline-block; font:600 .72rem/1.2 var(--mono); padding:4px 7px; border-radius:999px; background: var(--accent-soft); color: var(--accent); margin: 0 4px 4px 0 }
|
|
26
|
+
.pill.pointed { background: var(--line); color: var(--fg) } .pill.none { background: var(--warn-soft); color: var(--warn) }
|
|
27
|
+
.card { border:1px solid var(--line); border-radius:8px; padding:12px; display:grid; gap:6px } .card.done { opacity:.7 } .card.next { border-color: var(--accent) }
|
|
28
|
+
.q { font-weight:600 } .hint { font-size:.88rem; color: var(--muted) }
|
|
29
|
+
.celebrate { text-align:center; padding: 28px 18px; background: var(--accent-soft); border-color: transparent }
|
|
30
|
+
.celebrate .big { font-size: 1.9rem; font-weight: 700; color: var(--accent) } ol { margin:0; padding-left: 1.2em; display:grid; gap:6px; text-align:left }
|
|
31
|
+
</style>
|
|
32
|
+
</head>
|
|
33
|
+
<body>
|
|
34
|
+
<div class="wrap" id="app"></div>
|
|
35
|
+
<script id="naive-setup-state" type="application/json">__NAIVE_SETUP_STATE__</script>
|
|
36
|
+
<script>
|
|
37
|
+
// A view of the venture's setup state, filled in by `node schema/setup.mjs <venture dir> artifact`.
|
|
38
|
+
// It reads nothing from the network and writes nothing: the founder answers in the chat, the model writes
|
|
39
|
+
// each answer through Naive, and renders this page again.
|
|
40
|
+
const S = JSON.parse(document.getElementById("naive-setup-state").textContent);
|
|
41
|
+
const esc = (s) => String(s ?? "").replace(/[&<>"]/g, (c) => ({ "&": "&", "<": "<", ">": ">", '"': """ })[c]);
|
|
42
|
+
const st = S.steps, v = S.venture;
|
|
43
|
+
const order = [["people", "People and hats"], ["truth", "Sources of truth"], ["docket", "Foundation and first decisions"], ["goal", "A goal for discovery"]];
|
|
44
|
+
const next = order.find(([k]) => !st[k])?.[0];
|
|
45
|
+
const doneCount = order.filter(([k]) => st[k]).length;
|
|
46
|
+
const label = ([k, t]) => '<span class="step ' + (st[k] ? "ok" : k === next ? "next" : "") + '">' + (st[k] ? "✓ " : "") + t + "</span>";
|
|
47
|
+
|
|
48
|
+
const people = S.people
|
|
49
|
+
? '<div class="table"><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></div>"
|
|
50
|
+
: '<p class="muted">Nobody is listed yet. Tell me your name, your role, and whether you are solo or a team.</p>';
|
|
51
|
+
|
|
52
|
+
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)])];
|
|
53
|
+
const systemsOf = (f) => { const x = S.functions[f]; return 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) : "") + " · " + esc(s.status) + (s.fixture ? " (fixture)" : "") + "</span>").join("") : '<span class="pill none">not pointed yet</span>'; };
|
|
54
|
+
const offered = (f) => (S.catalog.functions[f] ?? []).map((s) => esc(S.catalog.systems[s]?.label ?? s)).join(", ");
|
|
55
|
+
const truth = '<div class="table"><table><tr><th>Function</th><th>Source of truth</th><th>You can point it at</th></tr>' + fns.map((f) => "<tr><td>" + esc(f) + "</td><td>" + systemsOf(f) + '</td><td class="hint">' + offered(f) + "</td></tr>").join("") + "</table></div>";
|
|
56
|
+
|
|
57
|
+
const open = S.docket.filter((d) => d.status !== "decided");
|
|
58
|
+
const cards = S.docket.map((d) => '<div class="card ' + (d.status === "decided" ? "done" : d === open[0] ? "next" : "") + '"><div class="q">' + esc(d.question) + "</div>"
|
|
59
|
+
+ (d.status === "decided" ? "<div>✓ " + esc(d.answer) + "</div>"
|
|
60
|
+
: d.relayed ? "<div>" + esc(d.answer) + '</div><div class="hint">Your answer, waiting for you to confirm it.</div>'
|
|
61
|
+
|
|
62
|
+
: (d.options.length ? '<div class="hint">Options: ' + d.options.map(esc).join(" / ") + "</div>" : "") + '<div class="hint">Answer in the chat, in your own words.</div>')
|
|
63
|
+
+ "</div>").join("");
|
|
64
|
+
|
|
65
|
+
const goal = S.goal?.status === "decided" ? "<p>✓ " + esc(S.goal.answer) + "</p>" : S.goal?.relayed ? "<p>" + esc(S.goal.answer) + '</p><p class="hint">Your answer, waiting for you to confirm it.</p>' : '<p class="muted">What must be true for ' + esc(v.name) + " to leave discovery? For example: ten clinic owners confirm the scheduling problem in interviews by December.</p>";
|
|
66
|
+
|
|
67
|
+
const done = S.done
|
|
68
|
+
? '<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 each function, a settled foundation and first decisions, and a goal for discovery. Every decision from here routes through the docket.</p><ol>"
|
|
69
|
+
+ "<li>Orient each next decision around the discovery goal.</li><li>" + (S.later ? S.later + " more decisions wait for later phases; the docket brings each one up when it matters." : "The docket brings up each new decision when it matters.") + "</li><li>To operate the business day to day, with your team and your agents, use Mentura.</li></ol></section>"
|
|
70
|
+
: "";
|
|
71
|
+
|
|
72
|
+
document.getElementById("app").innerHTML =
|
|
73
|
+
'<header><span class="muted mono">' + esc(v.type?.label ?? "venture") + " · " + esc(v.phase) + "</span><h1>Set up " + esc(v.name) + '</h1><p class="muted">The structure of your venture, step by step. Answer in the chat; each answer is saved as a decision, and this page updates.</p>'
|
|
74
|
+
+ '<div class="bar" role="progressbar" aria-valuemin="0" aria-valuemax="4" aria-valuenow="' + doneCount + '"><div style="width:' + doneCount * 25 + '%"></div></div><div class="steps">' + order.map(label).join("") + "</div></header>"
|
|
75
|
+
+ (S.waiting.length ? '<section><h2>' + S.waiting.length + (S.waiting.length === 1 ? " answer waits" : " answers wait") + ' for you</h2><p class="muted">The model wrote these down from your words. They become your decisions when you confirm them yourself: run <span class="mono">npx naive setup confirm</span> in the venture.</p></section>' : "")
|
|
76
|
+
+ done
|
|
77
|
+
+ "<section><h2>1. People and hats</h2>" + people + "</section>"
|
|
78
|
+
+ '<section><h2>2. Sources of truth</h2><p class="muted">Where each function keeps its truth. Naive keeps pointers, never copies.</p>' + truth + "</section>"
|
|
79
|
+
+ '<section><h2>3. Foundation and first decisions</h2><p class="muted">The decision docket: ' + S.counts.settled + " of " + S.counts.of + " settled.</p>" + cards + "</section>"
|
|
80
|
+
+ "<section><h2>4. A goal for discovery</h2>" + goal + "</section>";
|
|
81
|
+
</script>
|
|
82
|
+
</body>
|
|
83
|
+
</html>
|