naive-framework 0.1.0 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +2 -1
- package/docs/decisions/decisions.json +27 -0
- package/docs/reference/ROUTES.md +36 -37
- 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 +182 -0
- package/interface/api/server.mjs +4 -2
- package/interface/boundaries.mjs +218 -0
- package/interface/brand.mjs +214 -0
- package/interface/cli/freshness.mjs +59 -0
- package/interface/cli/guard.mjs +6 -0
- package/interface/cli/mentura.mjs +78 -8
- package/interface/cli/naive.mjs +33 -8
- package/interface/context.mjs +524 -0
- package/interface/demo/board.mjs +1 -1
- package/interface/dev/setup.mjs +84 -0
- package/interface/gate-hooks.mjs +36 -0
- package/interface/harness/evals.mjs +3 -3
- package/interface/harness/run.mjs +4 -4
- package/interface/instrumentation.mjs +86 -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 +196 -0
- package/interface/provenance.mjs +24 -1
- package/interface/reads.mjs +173 -0
- package/interface/release-card.mjs +42 -0
- package/interface/router.mjs +101 -28
- package/interface/skills/naive-setup/SKILL.md +71 -0
- package/interface/skills/naive-setup/artifact.html +83 -0
- package/interface/vitals.mjs +115 -0
- package/interface/writes.mjs +190 -0
- 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/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 +7 -6
- 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/schema/README.md +1 -0
- package/schema/access.mjs +57 -0
- package/schema/adapter.schema.json +33 -0
- package/schema/assets.mjs +212 -0
- package/schema/brand.mjs +373 -0
- package/schema/build-release.mjs +61 -0
- package/schema/caching.mjs +505 -0
- package/schema/check-assets.mjs +24 -0
- package/schema/check-brand.mjs +24 -0
- package/schema/check-config.mjs +110 -0
- package/schema/check-nextjs-status.mjs +3 -1
- package/schema/check-people.mjs +70 -0
- package/schema/check-sources.mjs +9 -1
- package/schema/data-model.json +334 -8
- package/schema/deal.schema.json +80 -0
- package/schema/decisions.mjs +7 -1
- 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/founder.schema.json +72 -0
- package/schema/frame.schema.json +240 -0
- package/schema/frames.mjs +228 -0
- package/schema/freshness-log.schema.json +40 -0
- package/schema/freshness-profiles.schema.json +44 -0
- package/schema/freshness.mjs +473 -0
- package/schema/fund-terms.schema.json +107 -0
- package/schema/fund-thesis.schema.json +87 -0
- package/schema/gate-drafts.mjs +130 -0
- package/schema/gate.mjs +105 -11
- package/schema/glossary.schema.json +25 -0
- package/schema/init-venture.mjs +38 -8
- 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/migrations/kit-to-package.mjs +94 -0
- package/schema/migrations/package-layout.mjs +86 -0
- package/schema/model-steps.mjs +717 -0
- package/schema/money-summary.schema.json +138 -0
- package/schema/naive-config.mjs +165 -0
- package/schema/naive-config.schema.json +268 -0
- package/schema/need-now.json +171 -0
- package/schema/need-now.mjs +3 -2
- package/schema/packages.mjs +10 -5
- package/schema/people.schema.json +86 -0
- package/schema/public-assets.schema.json +76 -0
- package/schema/release-read.mjs +111 -0
- package/schema/releases.mjs +497 -0
- package/schema/setup.mjs +164 -0
- package/schema/stale.mjs +111 -0
- package/schema/start-release.mjs +41 -0
- package/schema/structure.mjs +186 -0
- package/schema/systems-catalog.json +126 -0
- package/schema/systems-catalog.schema.json +78 -0
- package/schema/trigger.schema.json +44 -0
- package/schema/triggers.mjs +237 -0
- package/schema/upgrade-registry.schema.json +60 -0
- package/schema/upgrade-venture.mjs +96 -118
- package/schema/upgrades.json +69 -0
- package/schema/upgrades.mjs +385 -0
- package/schema/validate.py +19 -10
- 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 +53 -0
package/schema/setup.mjs
ADDED
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
// Setup (EE-083): the questions that establish a venture's structure, in order: its people and their
|
|
2
|
+
// hats, where each function keeps its truth, the strategic foundation and the first decisions (the decision
|
|
3
|
+
// docket), and a goal for the discovery phase. It holds no view. The naive-setup skill
|
|
4
|
+
// (interface/skills/naive-setup) renders it in the model's harness, as a Claude artifact where there is one;
|
|
5
|
+
// interface/dev/setup.mjs serves the same state to a browser.
|
|
6
|
+
//
|
|
7
|
+
// node schema/setup.mjs <venture dir> state the state, as JSON
|
|
8
|
+
// node schema/setup.mjs <venture dir> artifact the state rendered into the skill's artifact template
|
|
9
|
+
// node schema/setup.mjs <venture dir> answer <decision id> <the founder's words> [--via mcp]
|
|
10
|
+
// node schema/setup.mjs <venture dir> point <function> <system> [address] [--via mcp]
|
|
11
|
+
// node schema/setup.mjs <venture dir> confirm [--yes] confirm the answers the model relayed, as the founder
|
|
12
|
+
|
|
13
|
+
import { spawnSync } from "node:child_process";
|
|
14
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
15
|
+
import { basename, dirname, join, resolve } from "node:path";
|
|
16
|
+
|
|
17
|
+
const repo = new URL("..", import.meta.url).pathname;
|
|
18
|
+
const load = (p) => JSON.parse(readFileSync(p, "utf8"));
|
|
19
|
+
export const TEMPLATE = "interface/skills/naive-setup/artifact.html";
|
|
20
|
+
|
|
21
|
+
const GOAL = "discovery-goal";
|
|
22
|
+
|
|
23
|
+
export async function state(dir) {
|
|
24
|
+
const { decisions } = await import("./decisions.mjs");
|
|
25
|
+
const v = load(join(dir, "venture.json"));
|
|
26
|
+
const people = existsSync(join(dir, "people/people.json")) ? load(join(dir, "people/people.json")) : null;
|
|
27
|
+
const sources = existsSync(join(dir, "knowledge/sources.json")) ? load(join(dir, "knowledge/sources.json")) : { sources: [], records: {} };
|
|
28
|
+
const catalog = load(join(repo, "schema/systems-catalog.json"));
|
|
29
|
+
const types = load(join(repo, "schema/venture-types.json")).types;
|
|
30
|
+
const all = decisions(dir).decisions;
|
|
31
|
+
// The roots (venture, foundation) structure the docket; they are not questions to answer here.
|
|
32
|
+
const docket = all.filter((d) => d.id !== GOAL && d.status !== "deferred" && !["venture", "foundation"].includes(d.id));
|
|
33
|
+
const now = docket.filter((d) => d.render === "now" || d.status === "decided");
|
|
34
|
+
const goal = all.find((d) => d.id === GOAL) ?? null;
|
|
35
|
+
const settled = now.filter((d) => d.status === "decided").length;
|
|
36
|
+
const pointed = Object.values(sources.functions ?? {}).filter((f) => f.systems.length).length;
|
|
37
|
+
const steps = { venture: true, people: !!people, truth: pointed > 0, docket: now.length > 0 && settled === now.length, goal: goal?.status === "decided" };
|
|
38
|
+
return {
|
|
39
|
+
venture: { id: v.id, name: v.name, phase: v.phase, type: types.find((t) => t.id === v.type) ?? null },
|
|
40
|
+
people, functions: sources.functions ?? {}, truth: sources.truth ?? "local",
|
|
41
|
+
catalog, docket: now.map((d) => ({ id: d.id, question: d.question, source: d.source, status: d.status, options: d.options ?? [], answer: d.answer ?? null, why: d.why ?? [], statement: d.statement ?? null, relayed: relayed(d) })),
|
|
42
|
+
// Answers the model wrote down from the founder's words; they wait for the founder to confirm them.
|
|
43
|
+
waiting: [...now, ...(goal ? [goal] : [])].filter(relayed).map((d) => ({ id: d.id, question: d.question, answer: d.answer })),
|
|
44
|
+
later: docket.filter((d) => d.render !== "now" && d.status !== "decided").length,
|
|
45
|
+
goal: goal ? { status: goal.status, answer: goal.answer ?? null, relayed: relayed(goal) } : null,
|
|
46
|
+
steps, done: Object.values(steps).every(Boolean), counts: { settled, of: now.length },
|
|
47
|
+
rho: existsSync(join(repo, "platform/integrations/rho.mjs")),
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// An open decision whose wording an agent wrote: in setup, the founder's words the model relayed.
|
|
52
|
+
const relayed = (d) => d.status !== "decided" && !!d.answer && d.provenance?.drafted_by?.kind === "agent";
|
|
53
|
+
|
|
54
|
+
const founderName = (dir) => existsSync(join(dir, "people/people.json")) ? load(join(dir, "people/people.json")).people[0]?.name ?? "Founder" : "Founder";
|
|
55
|
+
|
|
56
|
+
// One answer: a decision on file is approved; a foundation statement (or the goal) becomes a decision a person approves.
|
|
57
|
+
export async function decide(dir, { id, answer, via = "api", where = "/setup" }) {
|
|
58
|
+
const { approve, decisions, propose } = await import("./decisions.mjs");
|
|
59
|
+
if (!answer || !String(answer).trim()) return { ok: false, error: "write an answer first" };
|
|
60
|
+
const by = founderName(dir);
|
|
61
|
+
const d = decisions(dir).decisions.find((x) => x.id === id);
|
|
62
|
+
if (id === GOAL && !d) {
|
|
63
|
+
const p = propose(dir, { id: GOAL, question: "What must be true for the venture to leave discovery? Name the goal this phase works toward.", decides: { record: "telos", path: "foundation/telos.json" }, layer: "purpose", needed_in: "discovery", boundary: "fail-open" }, { via });
|
|
64
|
+
if (!p.ok) return p;
|
|
65
|
+
return approve(dir, GOAL, { by, answer: String(answer).trim(), via, note: `set at ${where}` });
|
|
66
|
+
}
|
|
67
|
+
if (!d) return { ok: false, error: `no decision ${id}` };
|
|
68
|
+
if (d.source === "foundation") {
|
|
69
|
+
const sid = id.replace(/^foundation-/, "");
|
|
70
|
+
const did = `statement-${sid}`;
|
|
71
|
+
const p = propose(dir, { id: did, question: d.question, statement: sid, decides: d.decides, layer: d.layer === "system" ? "purpose" : d.layer, needed_in: d.needed_in }, { via });
|
|
72
|
+
if (!p.ok) return p;
|
|
73
|
+
return approve(dir, did, { by, answer: String(answer).trim(), via, note: `the founder's words at ${where}` });
|
|
74
|
+
}
|
|
75
|
+
return approve(dir, id, { by, answer: String(answer).trim(), via, note: `decided at ${where}` });
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// One pointer: where a function keeps its truth, merged into knowledge/sources.json through the router.
|
|
79
|
+
export async function point(dir, { fn, system, address, via = "api", actor = "person", agentId }) {
|
|
80
|
+
const catalog = load(join(repo, "schema/systems-catalog.json"));
|
|
81
|
+
if (!catalog.functions[fn]?.includes(system)) return { ok: false, error: `${system} is not offered for ${fn}` };
|
|
82
|
+
const file = join(dir, "knowledge/sources.json");
|
|
83
|
+
const map = existsSync(file) ? load(file) : { sources: [], records: {}, truth: "local" };
|
|
84
|
+
const f = (map.functions ??= {})[fn] ??= { systems: [] };
|
|
85
|
+
const entry = { system, ...(address ? { address } : {}), status: "pointed" };
|
|
86
|
+
if (system === "rho" && existsSync(join(repo, "platform/integrations/rho.mjs"))) {
|
|
87
|
+
const live = Boolean(process.env.RHO_API_TOKEN);
|
|
88
|
+
const r = spawnSync("node", [join(repo, "platform/integrations/rho.mjs"), "sync", "--venture", dir, ...(live ? ["--live"] : [])], { encoding: "utf8", env: process.env });
|
|
89
|
+
if (r.status === 0) Object.assign(entry, { status: "connected", synced: new Date().toISOString().replace(/\.\d+Z$/, "Z"), address: address || (live ? "live" : "sandbox") });
|
|
90
|
+
}
|
|
91
|
+
f.systems = [...f.systems.filter((s) => s.system !== system), entry];
|
|
92
|
+
const { call } = await import("../interface/router.mjs");
|
|
93
|
+
const r = await call({ workspace: dirname(dir), venture: basename(dir), route: "knowledge/knowledge-sources", action: "write", input: map, actor, ...(agentId ? { actor_id: agentId } : {}), via, provenance: { note: `${fn}: ${system}${address ? ` at ${address}` : ""} (${entry.status})` } });
|
|
94
|
+
return r.ok ? { ok: true, result: entry } : r;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/** The model relays the founder's answer: proposed with the founder's words as its wording, by an agent,
|
|
98
|
+
* so it stays open until the founder confirms it (an agent never decides, EE-058). */
|
|
99
|
+
export async function relay(dir, { id, answer, agentId, harness, model }) {
|
|
100
|
+
const { decisions, propose } = await import("./decisions.mjs");
|
|
101
|
+
if (!answer || !String(answer).trim()) return { ok: false, error: "pass the founder's answer, in their words" };
|
|
102
|
+
const words = String(answer).trim();
|
|
103
|
+
const note = `the founder's words, relayed from ${harness ?? "the model's harness"} during setup`;
|
|
104
|
+
const opts = { agentId, via: "mcp", harness, model };
|
|
105
|
+
const d = decisions(dir).decisions.find((x) => x.id === id);
|
|
106
|
+
if (id === GOAL) return propose(dir, { ...(d ? {} : { decides: { record: "telos", path: "foundation/telos.json" }, layer: "purpose", needed_in: "discovery", boundary: "fail-open" }), id: GOAL, question: d?.question ?? "What must be true for the venture to leave discovery? Name the goal this phase works toward.", answer: words, note }, opts);
|
|
107
|
+
if (!d) return { ok: false, error: `no decision ${id}` };
|
|
108
|
+
if (d.status === "decided") return { ok: false, error: `${id} is decided; only the founder can change it` };
|
|
109
|
+
if (d.source === "foundation") {
|
|
110
|
+
const sid = id.replace(/^foundation-/, "");
|
|
111
|
+
return propose(dir, { id: `statement-${sid}`, question: d.question, statement: sid, decides: d.decides, layer: d.layer === "system" ? "purpose" : d.layer, needed_in: d.needed_in, answer: words, note }, opts);
|
|
112
|
+
}
|
|
113
|
+
return propose(dir, { id, question: d.question, answer: words, note }, opts);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/** The founder confirms the answers the model relayed, at the keyboard: each becomes a decision they made. */
|
|
117
|
+
export async function confirm(dir, { by = founderName(dir) } = {}) {
|
|
118
|
+
const { approve } = await import("./decisions.mjs");
|
|
119
|
+
const out = [];
|
|
120
|
+
for (const w of (await state(dir)).waiting) {
|
|
121
|
+
const r = approve(dir, w.id, { by, via: "cli", note: `confirmed at setup: ${by}'s words, relayed by the model` });
|
|
122
|
+
if (!r.ok) return { ...r, confirmed: out };
|
|
123
|
+
out.push(w.id);
|
|
124
|
+
}
|
|
125
|
+
return { ok: true, result: { confirmed: out, by } };
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/** The state rendered into the skill's artifact template: one self-contained page, no network. */
|
|
129
|
+
export async function artifact(dir) {
|
|
130
|
+
const s = await state(dir);
|
|
131
|
+
const data = JSON.stringify(s).replace(/</g, "\\u003c");
|
|
132
|
+
return readFileSync(join(repo, TEMPLATE), "utf8").replace("__NAIVE_SETUP_STATE__", () => data);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
if (process.argv[1] && resolve(process.argv[1]) === new URL(import.meta.url).pathname) {
|
|
136
|
+
const [dirArg, cmd, ...rest] = process.argv.slice(2);
|
|
137
|
+
const flag = (n) => { const i = rest.indexOf(`--${n}`); if (i < 0) return undefined; const v = rest[i + 1]; rest.splice(i, 2); return v; };
|
|
138
|
+
const via = flag("via") ?? "cli";
|
|
139
|
+
if (!["cli", "mcp", "api"].includes(via)) { console.error(`--via is cli, mcp or api, not ${via}`); process.exit(2); }
|
|
140
|
+
if (!dirArg || !existsSync(join(dirArg, "venture.json"))) { console.error("usage: node schema/setup.mjs <venture dir> state|artifact|answer|point|confirm ..."); process.exit(2); }
|
|
141
|
+
const dir = resolve(dirArg);
|
|
142
|
+
let r;
|
|
143
|
+
if (cmd === "state") r = { ok: true, result: await state(dir) };
|
|
144
|
+
else if (cmd === "artifact") { process.stdout.write(await artifact(dir)); process.exit(0); }
|
|
145
|
+
else if (cmd === "answer") r = await decide(dir, { id: rest[0], answer: rest.slice(1).join(" "), via, where: via === "mcp" ? "setup in the model's harness" : "setup" });
|
|
146
|
+
else if (cmd === "confirm") {
|
|
147
|
+
const waiting = (await state(dir)).waiting;
|
|
148
|
+
if (!waiting.length) { console.log("Nothing waits for you to confirm."); process.exit(0); }
|
|
149
|
+
for (const w of waiting) console.log(`\n ${w.question}\n > ${w.answer}`);
|
|
150
|
+
let yes = rest.includes("--yes");
|
|
151
|
+
if (!yes) {
|
|
152
|
+
const { createInterface } = await import("node:readline/promises");
|
|
153
|
+
const rl = createInterface({ input: process.stdin, output: process.stdout });
|
|
154
|
+
yes = (await rl.question(`\nAre these ${waiting.length} answers yours, as written? Type yes to confirm them: `)).trim().toLowerCase() === "yes";
|
|
155
|
+
rl.close();
|
|
156
|
+
}
|
|
157
|
+
if (!yes) { console.log("Nothing confirmed. Change an answer in the chat, or run this again."); process.exit(1); }
|
|
158
|
+
r = await confirm(dir);
|
|
159
|
+
}
|
|
160
|
+
else if (cmd === "point") r = await point(dir, { fn: rest[0], system: rest[1], address: rest[2], via });
|
|
161
|
+
else { console.error(`unknown command ${cmd}: state, artifact, answer, point or confirm`); process.exit(2); }
|
|
162
|
+
console.log(JSON.stringify(r.ok && cmd === "state" ? r.result : r, null, 2));
|
|
163
|
+
process.exit(r.ok ? 0 : 1);
|
|
164
|
+
}
|
package/schema/stale.mjs
ADDED
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// Stale summaries (V-9). In Next.js, content from a CMS is cached for a long time, and the CMS calls a
|
|
3
|
+
// webhook that revalidates its tag when the content changes. In Naive a system of record (a CRM, a bank
|
|
4
|
+
// such as Rho) notifies the venture that something it holds changed, and every summary that reads it is
|
|
5
|
+
// marked stale until it is written again. What reads what is already recorded:
|
|
6
|
+
//
|
|
7
|
+
// - provenance: a record whose latest write was derived from the changed item (derived_from names its
|
|
8
|
+
// path or its <source>://<path> address, or a folder that holds it) reads it;
|
|
9
|
+
// - the where-used list (knowledge/where-used.json, V-4): every child of a fact whose parent route is
|
|
10
|
+
// the changed record repeats it.
|
|
11
|
+
//
|
|
12
|
+
// Receiving the notification over the network is the platform's (platform/webhooks/server.mjs); this is
|
|
13
|
+
// the framework part, offline: notify() marks, staleOf() answers for one file (the router adds it to a
|
|
14
|
+
// read), and a summary is fresh again once it is written after the mark. Marks live in
|
|
15
|
+
// <venture>/.naive/stale.json, never in the records.
|
|
16
|
+
//
|
|
17
|
+
// node schema/stale.mjs <venture dir> [--json] what is stale now
|
|
18
|
+
// node schema/stale.mjs <venture dir> --notify --source <id> --path <p> [--path <q>] [--version <v>]
|
|
19
|
+
|
|
20
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
21
|
+
import { join } from "node:path";
|
|
22
|
+
import { entries } from "../interface/provenance.mjs";
|
|
23
|
+
import { resolve as resolveRoute } from "../interface/router.mjs";
|
|
24
|
+
import { address } from "../knowledge/connectors/contract.mjs";
|
|
25
|
+
import { LOCAL, sourcesOf } from "../knowledge/connectors/index.mjs";
|
|
26
|
+
|
|
27
|
+
export const MARKS = ".naive/stale.json";
|
|
28
|
+
const load = (p) => JSON.parse(readFileSync(p, "utf8"));
|
|
29
|
+
const now = () => new Date().toISOString();
|
|
30
|
+
const WRITES = new Set(["write", "accept", "baseline"]);
|
|
31
|
+
|
|
32
|
+
const readMarks = (dir) => (existsSync(join(dir, MARKS)) ? load(join(dir, MARKS)) : { marks: [] });
|
|
33
|
+
|
|
34
|
+
// The latest write of each file in the log.
|
|
35
|
+
function lastWrites(dir) {
|
|
36
|
+
const last = new Map();
|
|
37
|
+
for (const e of entries(dir)) if (WRITES.has(e.activity) && !e.source) last.set(e.entity, e);
|
|
38
|
+
return last;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const covers = (d, item) => d === item || (d.endsWith("/") ? item.startsWith(d) : item.startsWith(`${d}/`));
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* A system of record says items changed. Marks every summary that reads them stale and returns what it
|
|
45
|
+
* marked. source: a source id from knowledge/sources.json (venture for the venture's own folder);
|
|
46
|
+
* paths: the changed items, as paths inside that source.
|
|
47
|
+
*/
|
|
48
|
+
export function notify(dir, { source, paths, version, at = now(), via = "framework" }) {
|
|
49
|
+
const map = sourcesOf(dir);
|
|
50
|
+
if (!source || !map.sources.some((s) => s.id === source)) throw new Error(`no source ${source} in ${dir}'s knowledge/sources.json: one of ${map.sources.map((s) => s.id).join(", ")}`);
|
|
51
|
+
paths = [paths].flat().filter(Boolean);
|
|
52
|
+
if (!paths.length) throw new Error("a notification names at least one changed path");
|
|
53
|
+
// The changed items as the venture refers to them: the address always, and the bare path for its own folder
|
|
54
|
+
// or for a source whose records the venture keeps a local copy of at the same path.
|
|
55
|
+
const items = paths.flatMap((p) => [address(source, p), ...(source === LOCAL || existsSync(join(dir, p)) ? [p] : [])]);
|
|
56
|
+
const readers = new Map();
|
|
57
|
+
for (const [file, e] of lastWrites(dir)) {
|
|
58
|
+
if (items.includes(file)) continue;
|
|
59
|
+
const hit = (e.derived_from ?? []).find((d) => items.some((i) => covers(d, i)));
|
|
60
|
+
if (hit) readers.set(file, { via: "provenance", reads: hit });
|
|
61
|
+
}
|
|
62
|
+
const wu = join(dir, "knowledge/where-used.json");
|
|
63
|
+
if (existsSync(wu)) {
|
|
64
|
+
for (const fact of load(wu).facts ?? []) {
|
|
65
|
+
const { r, params } = resolveRoute(fact.parent.route);
|
|
66
|
+
if (!r || r.kind !== "record") continue;
|
|
67
|
+
const files = (r.collection === "item" ? r.files.map((f) => f.replace("*", params.id)) : r.files).filter((f) => !f.includes("*"));
|
|
68
|
+
if (!files.some((f) => items.includes(f) || items.includes(address(source, f)))) continue;
|
|
69
|
+
for (const c of fact.children) readers.set(c.source === LOCAL ? c.path : address(c.source, c.path), { via: "where-used", reads: fact.parent.route, fact: fact.id });
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
const state = readMarks(dir);
|
|
73
|
+
const because = { source, paths, ...(version ? { version } : {}), at, via };
|
|
74
|
+
for (const [file, how] of readers) state.marks.push({ file, ...how, because, marked_at: now() });
|
|
75
|
+
state.notifications = [...(state.notifications ?? []), { ...because, marked: readers.size, received_at: now() }].slice(-100);
|
|
76
|
+
mkdirSync(join(dir, ".naive"), { recursive: true });
|
|
77
|
+
writeFileSync(join(dir, MARKS), JSON.stringify(state, null, 2) + "\n");
|
|
78
|
+
return { source, paths, stale: [...readers.keys()].sort() };
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/** Whether one file is stale: the marks made after its latest write, or null when it is fresh. */
|
|
82
|
+
export function staleOf(dir, file) {
|
|
83
|
+
if (!existsSync(join(dir, MARKS))) return null;
|
|
84
|
+
const written = lastWrites(dir).get(file)?.at ?? "";
|
|
85
|
+
const live = readMarks(dir).marks.filter((m) => m.file === file && m.marked_at > written);
|
|
86
|
+
return live.length ? { since: live[0].marked_at, because: live.map((m) => ({ ...m.because, reads: m.reads, via: m.via })), fix: `write ${file} again from the changed source; it is fresh once written` } : null;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/** Every stale file in the venture now. */
|
|
90
|
+
export function stale(dir) {
|
|
91
|
+
const files = [...new Set(readMarks(dir).marks.map((m) => m.file))];
|
|
92
|
+
return files.map((f) => ({ file: f, ...staleOf(dir, f) })).filter((x) => x.since).sort((a, b) => a.file.localeCompare(b.file));
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
if (import.meta.url === `file://${process.argv[1]}`) {
|
|
96
|
+
const a = process.argv.slice(2);
|
|
97
|
+
const dir = a[0];
|
|
98
|
+
const all = (n) => a.flatMap((x, i) => (x === `--${n}` ? [a[i + 1]] : []));
|
|
99
|
+
if (!dir || !existsSync(join(dir, "venture.json"))) { console.error("usage: node schema/stale.mjs <venture dir> [--json] | --notify --source <id> --path <p> [--version <v>]"); process.exit(2); }
|
|
100
|
+
try {
|
|
101
|
+
if (a.includes("--notify")) {
|
|
102
|
+
const r = notify(dir, { source: all("source")[0], paths: all("path"), version: all("version")[0], via: "cli" });
|
|
103
|
+
if (a.includes("--json")) console.log(JSON.stringify(r, null, 2));
|
|
104
|
+
else console.log(r.stale.length ? `${r.stale.length} stale: ${r.stale.join(", ")}` : "nothing reads what changed, so nothing is stale");
|
|
105
|
+
} else {
|
|
106
|
+
const s = stale(dir);
|
|
107
|
+
if (a.includes("--json")) console.log(JSON.stringify(s, null, 2));
|
|
108
|
+
else console.log(s.length ? s.map((x) => `stale ${x.file} since ${x.since}: ${x.because.map((b) => `${b.source} changed ${b.paths.join(", ")}`).join("; ")}`).join("\n") : "nothing is stale");
|
|
109
|
+
}
|
|
110
|
+
} catch (e) { console.error(e.message); process.exit(1); }
|
|
111
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// naive start (I-17, C-12): serves a venture's release read-only over local HTTP. No router: it reads
|
|
3
|
+
// <venture>/.release/ and nothing else (schema/release-read.mjs), as a CDN serves a static shell. It
|
|
4
|
+
// reads the manifest on each request, so an item added with build-release --add shows at once (C-18).
|
|
5
|
+
// It listens on 127.0.0.1 only. The paths are listed in schema/release-read.mjs; /card.svg is the
|
|
6
|
+
// release card drawn by the interface (interface/release-card.mjs).
|
|
7
|
+
//
|
|
8
|
+
// node schema/start-release.mjs <venture dir> [--port n] serve; --port 0 picks a free port (default 4330)
|
|
9
|
+
// node schema/start-release.mjs <venture dir> --get <path> answer one path and exit, without a server
|
|
10
|
+
|
|
11
|
+
import { existsSync } from "node:fs";
|
|
12
|
+
import { createServer } from "node:http";
|
|
13
|
+
import { join } from "node:path";
|
|
14
|
+
import { svg } from "../interface/release-card.mjs";
|
|
15
|
+
import { answer, readRelease } from "./release-read.mjs";
|
|
16
|
+
|
|
17
|
+
const args = process.argv.slice(2);
|
|
18
|
+
const opt = (n) => { const i = args.indexOf(`--${n}`); return i >= 0 ? args[i + 1] : undefined; };
|
|
19
|
+
const values = new Set(["port", "get", "now"].flatMap((n) => (opt(n) !== undefined ? [opt(n)] : [])));
|
|
20
|
+
const dir = args.find((a) => !a.startsWith("--") && !values.has(a));
|
|
21
|
+
if (!dir || !existsSync(join(dir, "venture.json"))) { console.error("usage: node schema/start-release.mjs <venture dir> [--port n] | --get <path>"); process.exit(2); }
|
|
22
|
+
if (!readRelease(dir)) { console.error(`no release in ${dir}: run node schema/build-release.mjs ${dir} first`); process.exit(1); }
|
|
23
|
+
|
|
24
|
+
if (opt("get") !== undefined) {
|
|
25
|
+
const a = answer(dir, "GET", opt("get"), { draw: svg });
|
|
26
|
+
process.stdout.write(a.body);
|
|
27
|
+
process.exit(a.status < 400 ? 0 : 1);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const port = opt("port") === undefined ? 4330 : Number(opt("port"));
|
|
31
|
+
if (!Number.isInteger(port) || port < 0 || port > 65535) { console.error(`--port ${opt("port")} is not a port`); process.exit(2); }
|
|
32
|
+
const server = createServer((req, res) => {
|
|
33
|
+
const a = answer(dir, req.method, req.url ?? "/", { draw: svg });
|
|
34
|
+
res.writeHead(a.status, { "content-type": a.type, "cache-control": "no-cache", ...(a.allow ? { allow: a.allow } : {}) });
|
|
35
|
+
res.end(req.method === "HEAD" ? undefined : a.body);
|
|
36
|
+
});
|
|
37
|
+
server.listen(port, "127.0.0.1", () => {
|
|
38
|
+
const m = readRelease(dir);
|
|
39
|
+
console.log(`serving release ${m.release} of ${m.venture} read-only at http://127.0.0.1:${server.address().port}/`);
|
|
40
|
+
});
|
|
41
|
+
for (const s of ["SIGINT", "SIGTERM"]) process.on(s, () => server.close(() => process.exit(0)));
|
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// The venture folder's structure (Next.js translation P-3, P-22, P-32): what venture.json declares,
|
|
3
|
+
// which folders are private, and which names Naive reserves.
|
|
4
|
+
//
|
|
5
|
+
// node schema/structure.mjs <venture dir> [--json] check the structure; exit 1 on an error
|
|
6
|
+
// node schema/structure.mjs manifest <venture dir> print the manifest venture.json declares
|
|
7
|
+
//
|
|
8
|
+
// P-3 venture.json is Naive's package.json: identity, phase, governance, the function owners, and the
|
|
9
|
+
// packages and kits the venture uses (EE-071). packages and kits are declared the way
|
|
10
|
+
// package.json lists dependencies; when venture.json does not list packages, the manifest
|
|
11
|
+
// derives them (the core packages that fit the phase now, plus any package that holds records).
|
|
12
|
+
// P-22 A folder whose name starts with _ is private: no route reaches a file under it unless the
|
|
13
|
+
// data model names that folder in a record's path (customers/_streams/*/stream.json). _drafts/
|
|
14
|
+
// holds writes waiting for a person, _private/ never reaches a model, _notes/ holds notes,
|
|
15
|
+
// _streams/ holds a data package's append-only activity.
|
|
16
|
+
// P-32 Names reserved for Naive's own conventions, now and later. A venture that uses one for
|
|
17
|
+
// something else gets an error (the name already means something) or a warning (it will).
|
|
18
|
+
//
|
|
19
|
+
// Never calls a model. Reads the venture folder only.
|
|
20
|
+
|
|
21
|
+
import { existsSync, readFileSync, readdirSync, statSync } from "node:fs";
|
|
22
|
+
import { join } from "node:path";
|
|
23
|
+
|
|
24
|
+
const repo = new URL("..", import.meta.url).pathname;
|
|
25
|
+
const load = (f) => JSON.parse(readFileSync(f, "utf8"));
|
|
26
|
+
|
|
27
|
+
/** The private folders Naive knows, and what each holds (P-22). Any other _folder is private too. */
|
|
28
|
+
export const PRIVATE = {
|
|
29
|
+
_drafts: "writes held for a person to accept; Naive writes it, at the venture root only",
|
|
30
|
+
_private: "never sent to a model and never released",
|
|
31
|
+
_notes: "the founder's working notes beside a package; never routed",
|
|
32
|
+
_streams: "a data package's append-only activity (customers, investors, money)",
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
/** Names reserved for Naive conventions (P-32). in_use: Naive gives them a meaning today; future: kept free for a convention to come. */
|
|
36
|
+
export const RESERVED = {
|
|
37
|
+
in_use: {
|
|
38
|
+
"AGENTS.md": "a layout: instructions for agents in this folder and below",
|
|
39
|
+
"venture.json": "a venture's manifest; a folder holding one is a nested venture",
|
|
40
|
+
"naive.config.json": "the venture's overrides, at the venture root",
|
|
41
|
+
"provenance.jsonl": "the venture's provenance log, at the venture root",
|
|
42
|
+
_drafts: PRIVATE._drafts,
|
|
43
|
+
_streams: PRIVATE._streams,
|
|
44
|
+
".release": "release plans and output",
|
|
45
|
+
},
|
|
46
|
+
future: {
|
|
47
|
+
"layout.md": "a folder's own layout file (L-5)",
|
|
48
|
+
"template.md": "context recomputed on every action (P-12)",
|
|
49
|
+
"error.md": "a package's own error answer (P-15)",
|
|
50
|
+
"loading.md": "a package's own pending state (P-37)",
|
|
51
|
+
"not-found.md": "a package's own missing-record answer (P-14)",
|
|
52
|
+
"default.md": "what a slot shows for a package with nothing in it (P-17)",
|
|
53
|
+
"page.md": "a record's people-facing page (P-10)",
|
|
54
|
+
".naive": "generated framework files",
|
|
55
|
+
},
|
|
56
|
+
// Folder shapes that mean something to the router: (group) shares a layout (P-36); @slot and [param] are kept for later.
|
|
57
|
+
shapes: [
|
|
58
|
+
{ re: /^\(.+\)$/, name: "(group)", rule: "a route group: it holds only its AGENTS.md, the layout its packages share (P-36)" },
|
|
59
|
+
{ re: /^@.+/, name: "@slot", rule: "reserved for named slots (P-21)" },
|
|
60
|
+
{ re: /^\[.+\]$/, name: "[param]", rule: "reserved for dynamic segments (P-19); collections are declared by the data model, not by folder names" },
|
|
61
|
+
],
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
const DATA_PACKAGES = ["customers", "investors", "money"];
|
|
65
|
+
|
|
66
|
+
/** Whether a segment is private (P-22). */
|
|
67
|
+
export const isPrivate = (segment) => segment.startsWith("_");
|
|
68
|
+
|
|
69
|
+
/** Whether a route's file pattern may reach a venture file: every private segment of the file is named, as it is, in the pattern (P-22). */
|
|
70
|
+
export function routable(file, pattern) {
|
|
71
|
+
const ps = pattern.split("/");
|
|
72
|
+
return file.split("/").every((s, i) => !isPrivate(s) || ps[i] === s);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// Every entry under a folder, as venture-relative paths, stopping at nested ventures and dot folders other than reserved ones.
|
|
76
|
+
function walk(dir, sub = "") {
|
|
77
|
+
const d = join(dir, sub);
|
|
78
|
+
return readdirSync(d, { withFileTypes: true }).flatMap((e) => {
|
|
79
|
+
const rel = sub ? `${sub}/${e.name}` : e.name;
|
|
80
|
+
if (e.isDirectory()) {
|
|
81
|
+
if (e.name === ".git" || e.name === "node_modules") return [];
|
|
82
|
+
if (existsSync(join(d, e.name, "venture.json"))) return [{ path: rel, dir: true, venture: true }];
|
|
83
|
+
return [{ path: rel, dir: true }, ...walk(dir, rel)];
|
|
84
|
+
}
|
|
85
|
+
return [{ path: rel, dir: false }];
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/** Checks a venture folder's private folders and reserved names. { errors, warnings, private } */
|
|
90
|
+
export function structure(dir) {
|
|
91
|
+
const errors = [], warnings = [], privateFolders = [];
|
|
92
|
+
if (!existsSync(join(dir, "venture.json"))) return { errors: [`${dir} has no venture.json`], warnings, private: privateFolders };
|
|
93
|
+
for (const e of walk(dir)) {
|
|
94
|
+
const segs = e.path.split("/");
|
|
95
|
+
const name = segs.at(-1), parent = segs.slice(0, -1).join("/");
|
|
96
|
+
if (e.venture) continue;
|
|
97
|
+
if (e.dir && isPrivate(name)) privateFolders.push({ folder: `${e.path}/`, holds: PRIVATE[name] ?? "private: never routed, never released (P-22)" });
|
|
98
|
+
if (e.dir && name === "_drafts" && parent) errors.push(`${e.path}/: _drafts/ is reserved for the venture root, where Naive writes held drafts (P-32)`);
|
|
99
|
+
if (!e.dir && segs[0] === "_drafts" && segs.length === 2 && !name.endsWith(".draft") && name !== ".gitkeep") errors.push(`${e.path}: only Naive's .draft files belong in _drafts/ (P-32)`);
|
|
100
|
+
if (e.dir && name === "_streams" && !DATA_PACKAGES.includes(parent)) errors.push(`${e.path}/: _streams/ belongs directly under a data package (${DATA_PACKAGES.join(", ")}) (P-32)`);
|
|
101
|
+
if (!e.dir && ["naive.config.json", "provenance.jsonl"].includes(name) && parent) errors.push(`${e.path}: ${name} is read only at the venture root; move it or rename it (P-32)`);
|
|
102
|
+
if (e.dir && name === ".release" && parent) errors.push(`${e.path}/: .release/ is reserved for the venture root (P-32)`);
|
|
103
|
+
if (RESERVED.future[name] && !segs.some(isPrivate)) warnings.push(`${e.path}: ${name} is reserved for ${RESERVED.future[name]}; rename it so a later Naive does not read it as that (P-32)`);
|
|
104
|
+
if (e.dir) for (const s of RESERVED.shapes) {
|
|
105
|
+
if (!s.re.test(name)) continue;
|
|
106
|
+
if (s.name === "(group)") {
|
|
107
|
+
if (parent) errors.push(`${e.path}/: a route group folder sits at the venture root (P-36)`);
|
|
108
|
+
const inside = readdirSync(join(dir, e.path)).filter((f) => f !== "AGENTS.md" && f !== ".gitkeep");
|
|
109
|
+
if (inside.length) errors.push(`${e.path}/: ${s.rule}; move ${inside.join(", ")} into its package`);
|
|
110
|
+
} else warnings.push(`${e.path}/: ${s.name} is ${s.rule} (P-32)`);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
return { errors, warnings, private: privateFolders };
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
const PACKAGE_NAMES = () => existsSync(join(repo, "packages")) ? readdirSync(join(repo, "packages")).filter((n) => existsSync(join(repo, "packages", n, "package.json"))) : [];
|
|
117
|
+
const pkgOf = (n) => load(join(repo, "packages", n, "package.json"));
|
|
118
|
+
const FUNCTIONS = () => load(join(repo, "operations/departments.json")).departments.map((d) => d.id);
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* The manifest venture.json declares (P-3), with what is derived when it does not declare it:
|
|
122
|
+
* identity, phase, governance, function owners, and the packages and kits in use.
|
|
123
|
+
*/
|
|
124
|
+
export async function manifest(dir) {
|
|
125
|
+
const v = load(join(dir, "venture.json"));
|
|
126
|
+
const names = PACKAGE_NAMES();
|
|
127
|
+
let derived = null;
|
|
128
|
+
if (!v.packages) {
|
|
129
|
+
// The packages that fit the phase now, plus every package that already holds a record.
|
|
130
|
+
const { packages } = await import("./packages.mjs");
|
|
131
|
+
const p = packages(dir);
|
|
132
|
+
const holding = names.filter((n) => { const k = pkgOf(n); return (k.records ?? []).length && existsSync(join(dir, n)) && readdirSync(join(dir, n)).some((f) => /\.(json|md|mdx)$/.test(f)); });
|
|
133
|
+
derived = [...new Set([...p.core.filter((x) => x.in === "now").map((x) => x.id), ...holding])];
|
|
134
|
+
}
|
|
135
|
+
const used = v.packages ?? derived;
|
|
136
|
+
const owners = v.functions ?? {};
|
|
137
|
+
const fnOf = (n) => { const k = names.includes(n) ? pkgOf(n) : {}; return k.function ?? k.department ?? null; };
|
|
138
|
+
const solo = !(v.members?.length);
|
|
139
|
+
const functions = [...new Set(used.map(fnOf).filter(Boolean))].map((f) => ({ function: f, owner: owners[f]?.owner ?? (solo ? "the founder" : null), declared: !!owners[f], packages: used.filter((n) => fnOf(n) === f) }));
|
|
140
|
+
for (const [f, o] of Object.entries(owners)) if (!functions.some((x) => x.function === f)) functions.push({ function: f, owner: o.owner, declared: true, packages: [] });
|
|
141
|
+
return {
|
|
142
|
+
identity: { id: v.id, name: v.name, kind: v.kind ?? "venture", parent: v.parent, owner: v.owner, status: v.status },
|
|
143
|
+
phase: v.phase,
|
|
144
|
+
governance: { governed_by: v.governed_by ?? null, legal_form: v.form?.legal_form ?? "none", members: v.members ?? [], decision_rights: existsSync(join(dir, "governance/decision-rights.json")) ? "governance/decision-rights.json" : "the framework's template" },
|
|
145
|
+
functions,
|
|
146
|
+
packages: { declared: !!v.packages, list: used.map((n) => ({ name: n, kind: names.includes(n) ? pkgOf(n).kind : null, known: names.includes(n) })) },
|
|
147
|
+
kits: (v.kits ?? []).map((k) => ({ ...k, note: "a kit is Mentura's; the framework checks only that it names a package the venture uses" })),
|
|
148
|
+
};
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/** Errors in what venture.json declares (P-3): unknown packages and functions, kits extending a package not in use, an owner missing. */
|
|
152
|
+
export function checkManifest(dir) {
|
|
153
|
+
const v = load(join(dir, "venture.json"));
|
|
154
|
+
const names = PACKAGE_NAMES(), fns = FUNCTIONS();
|
|
155
|
+
const errors = [];
|
|
156
|
+
for (const n of v.packages ?? []) if (!names.includes(n)) errors.push(`venture.json packages: ${n} is not a Naive package (one of ${names.join(", ")})`);
|
|
157
|
+
for (const [f, o] of Object.entries(v.functions ?? {})) {
|
|
158
|
+
if (!fns.includes(f)) errors.push(`venture.json functions: ${f} is not a function (one of ${fns.join(", ")})`);
|
|
159
|
+
if (!o?.owner) errors.push(`venture.json functions: ${f} has no owner; every function has one accountable owner`);
|
|
160
|
+
}
|
|
161
|
+
for (const k of v.kits ?? []) {
|
|
162
|
+
if (!names.includes(k.extends)) errors.push(`venture.json kits: ${k.id} extends ${k.extends}, which is not a Naive package`);
|
|
163
|
+
else if (v.packages && !v.packages.includes(k.extends)) errors.push(`venture.json kits: ${k.id} extends ${k.extends}, which the venture does not list in packages`);
|
|
164
|
+
}
|
|
165
|
+
return errors;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
if (import.meta.url === `file://${process.argv[1]}`) {
|
|
169
|
+
const args = process.argv.slice(2);
|
|
170
|
+
if (args[0] === "manifest") {
|
|
171
|
+
if (!args[1] || !existsSync(join(args[1], "venture.json"))) { console.error("usage: node schema/structure.mjs manifest <venture dir>"); process.exit(2); }
|
|
172
|
+
console.log(JSON.stringify(await manifest(args[1]), null, 2));
|
|
173
|
+
process.exit(0);
|
|
174
|
+
}
|
|
175
|
+
const dir = args.find((a) => !a.startsWith("--"));
|
|
176
|
+
if (!dir || !existsSync(dir) || !statSync(dir).isDirectory()) { console.error("usage: node schema/structure.mjs <venture dir> [--json] | manifest <venture dir>"); process.exit(2); }
|
|
177
|
+
const s = structure(dir);
|
|
178
|
+
if (existsSync(join(dir, "venture.json"))) s.errors.push(...checkManifest(dir));
|
|
179
|
+
if (args.includes("--json")) console.log(JSON.stringify(s, null, 2));
|
|
180
|
+
else {
|
|
181
|
+
for (const w of s.warnings) console.warn(`warning: ${w}`);
|
|
182
|
+
if (s.errors.length) console.error(s.errors.join("\n"));
|
|
183
|
+
else console.log(`structure holds: ${s.private.length} private folders, no reserved name misused`);
|
|
184
|
+
}
|
|
185
|
+
process.exit(s.errors.length ? 1 : 0);
|
|
186
|
+
}
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": "0.1.0",
|
|
3
|
+
"note": "Systems a venture's functions keep their truth in (EE-083), offered at create-venture and recorded in knowledge/sources.json `functions`. connect names the code that reaches a system; a system without it is recorded as a pointer until a connector exists.",
|
|
4
|
+
"systems": {
|
|
5
|
+
"local-repo": {
|
|
6
|
+
"label": "A local code repository",
|
|
7
|
+
"kind": "code",
|
|
8
|
+
"address": "a folder path",
|
|
9
|
+
"connect": null
|
|
10
|
+
},
|
|
11
|
+
"github": {
|
|
12
|
+
"label": "GitHub",
|
|
13
|
+
"kind": "code",
|
|
14
|
+
"address": "owner/name",
|
|
15
|
+
"connect": "knowledge/connectors: the github connector reads records from it (knowledge/sources.json)"
|
|
16
|
+
},
|
|
17
|
+
"vercel": {
|
|
18
|
+
"label": "Vercel",
|
|
19
|
+
"kind": "hosting and analytics",
|
|
20
|
+
"address": "team/project",
|
|
21
|
+
"connect": null
|
|
22
|
+
},
|
|
23
|
+
"cloudflare": {
|
|
24
|
+
"label": "Cloudflare",
|
|
25
|
+
"kind": "hosting and analytics",
|
|
26
|
+
"address": "account/zone or Pages project",
|
|
27
|
+
"connect": null
|
|
28
|
+
},
|
|
29
|
+
"stripe": {
|
|
30
|
+
"label": "Stripe",
|
|
31
|
+
"kind": "payments and sales",
|
|
32
|
+
"address": "account id (acct_...)",
|
|
33
|
+
"connect": null
|
|
34
|
+
},
|
|
35
|
+
"square": {
|
|
36
|
+
"label": "Square",
|
|
37
|
+
"kind": "payments and sales",
|
|
38
|
+
"address": "location or merchant id",
|
|
39
|
+
"connect": null
|
|
40
|
+
},
|
|
41
|
+
"shopify": {
|
|
42
|
+
"label": "Shopify",
|
|
43
|
+
"kind": "store and sales",
|
|
44
|
+
"address": "store domain (name.myshopify.com)",
|
|
45
|
+
"connect": null
|
|
46
|
+
},
|
|
47
|
+
"rho": {
|
|
48
|
+
"label": "Rho",
|
|
49
|
+
"kind": "bank and spend",
|
|
50
|
+
"address": "sandbox, or live with RHO_API_TOKEN",
|
|
51
|
+
"connect": "platform/integrations/rho.mjs sync: accounts, transactions and a summary, as pointers in money/"
|
|
52
|
+
},
|
|
53
|
+
"google-drive": {
|
|
54
|
+
"label": "Google Drive",
|
|
55
|
+
"kind": "documents",
|
|
56
|
+
"address": "folder URL",
|
|
57
|
+
"connect": null
|
|
58
|
+
},
|
|
59
|
+
"docusign": {
|
|
60
|
+
"label": "DocuSign",
|
|
61
|
+
"kind": "signatures",
|
|
62
|
+
"address": "account",
|
|
63
|
+
"connect": null
|
|
64
|
+
},
|
|
65
|
+
"local-folder": {
|
|
66
|
+
"label": "A local folder",
|
|
67
|
+
"kind": "documents",
|
|
68
|
+
"address": "a folder path",
|
|
69
|
+
"connect": null
|
|
70
|
+
}
|
|
71
|
+
},
|
|
72
|
+
"functions": {
|
|
73
|
+
"products": [
|
|
74
|
+
"local-repo",
|
|
75
|
+
"github",
|
|
76
|
+
"vercel",
|
|
77
|
+
"cloudflare"
|
|
78
|
+
],
|
|
79
|
+
"marketing-and-sales": [
|
|
80
|
+
"vercel",
|
|
81
|
+
"cloudflare",
|
|
82
|
+
"stripe",
|
|
83
|
+
"square",
|
|
84
|
+
"shopify"
|
|
85
|
+
],
|
|
86
|
+
"customer-service": [
|
|
87
|
+
"stripe",
|
|
88
|
+
"shopify",
|
|
89
|
+
"square"
|
|
90
|
+
],
|
|
91
|
+
"finance": [
|
|
92
|
+
"rho",
|
|
93
|
+
"stripe",
|
|
94
|
+
"square",
|
|
95
|
+
"shopify"
|
|
96
|
+
],
|
|
97
|
+
"risk-and-compliance": [
|
|
98
|
+
"google-drive",
|
|
99
|
+
"docusign",
|
|
100
|
+
"local-folder"
|
|
101
|
+
],
|
|
102
|
+
"legal": [
|
|
103
|
+
"google-drive",
|
|
104
|
+
"docusign",
|
|
105
|
+
"local-folder"
|
|
106
|
+
],
|
|
107
|
+
"it": [
|
|
108
|
+
"github",
|
|
109
|
+
"vercel",
|
|
110
|
+
"cloudflare"
|
|
111
|
+
],
|
|
112
|
+
"external-relationships": [
|
|
113
|
+
"google-drive",
|
|
114
|
+
"local-folder"
|
|
115
|
+
],
|
|
116
|
+
"service-delivery": [
|
|
117
|
+
"local-folder",
|
|
118
|
+
"google-drive"
|
|
119
|
+
],
|
|
120
|
+
"supply-chain": [
|
|
121
|
+
"shopify",
|
|
122
|
+
"square",
|
|
123
|
+
"local-folder"
|
|
124
|
+
]
|
|
125
|
+
}
|
|
126
|
+
}
|