naive-framework 0.1.0 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +2 -1
- package/docs/decisions/decisions.json +42 -0
- package/docs/reference/ROUTES.md +36 -37
- package/evidence/skills/fidelity-ladder-audit/ladder-census.mjs +2 -1
- package/governance/layer.json +2 -1
- package/governance/venture-template/roles/venture-roles.json +62 -0
- package/interface/acting.mjs +83 -0
- package/interface/adapters/claude-code.json +16 -0
- package/interface/adapters/codex.json +14 -0
- package/interface/adapters/cursor.json +16 -0
- package/interface/adapters.mjs +183 -0
- package/interface/api/server.mjs +4 -2
- package/interface/boundaries.mjs +219 -0
- package/interface/brand.mjs +215 -0
- package/interface/cli/freshness.mjs +59 -0
- package/interface/cli/guard.mjs +6 -0
- package/interface/cli/mentura.mjs +82 -11
- package/interface/cli/naive.mjs +44 -14
- package/interface/context.mjs +525 -0
- package/interface/demo/board.mjs +9 -8
- package/interface/demo/decision-harness.mjs +4 -3
- package/interface/dev/setup.mjs +84 -0
- package/interface/gate-hooks.mjs +37 -0
- package/interface/harness/evals.mjs +10 -9
- package/interface/harness/run.mjs +16 -15
- package/interface/instrumentation.mjs +87 -0
- package/interface/kit-boundary.mjs +73 -0
- package/interface/layer.json +8 -2
- package/interface/mcp/server.mjs +47 -7
- package/interface/package-routes.mjs +197 -0
- package/interface/provenance.mjs +28 -4
- package/interface/reads.mjs +174 -0
- package/interface/release-card.mjs +43 -0
- package/interface/router.mjs +110 -36
- package/interface/skills/naive-setup/SKILL.md +71 -0
- package/interface/skills/naive-setup/artifact.html +83 -0
- package/interface/vitals.mjs +116 -0
- package/interface/writes.mjs +190 -0
- package/knowledge/connectors/catalog.mjs +3 -2
- package/knowledge/connectors/filesystem.mjs +10 -5
- package/knowledge/connectors/index.mjs +3 -2
- package/knowledge/freshness.json +85 -0
- package/knowledge/layer.json +11 -3
- package/knowledge/templates/glossary.md +23 -0
- package/operations/layer.json +10 -3
- package/operations/marketing/check-public.mjs +7 -6
- package/operations/marketing/site/plan.mjs +4 -3
- package/operations/marketing/site/presence.mjs +4 -3
- package/operations/templates/marketing-brand.md +46 -0
- package/operations/templates/public-assets.md +39 -0
- package/package.json +3 -1
- package/packages/catalog.json +8 -0
- package/packages/customers/package.json +21 -1
- package/packages/deals/package.json +27 -0
- package/packages/decisions/package.json +12 -1
- package/packages/finance/frames/fund-without-ceding-control.frame.json +177 -0
- package/packages/foundation/package.json +8 -1
- package/packages/fund/package.json +48 -0
- package/packages/marketing/package.json +10 -4
- package/packages/money/package.json +24 -3
- package/packages/validation/package.json +13 -0
- package/platform/README.md +6 -0
- package/platform/adapter.json +15 -0
- package/platform/integrations/remote-image.mjs +79 -0
- package/platform/integrations/rho.mjs +9 -7
- package/platform/kits/README.md +8 -1
- package/platform/kits/agent-upgrade/kit.json +15 -0
- package/platform/kits/agent-upgrade/run.mjs +82 -0
- package/platform/kits/agent-upgrade/stand-in-agent.mjs +32 -0
- package/platform/kits/validation-kickoff/kit.json +29 -0
- package/platform/webhooks/server.mjs +70 -0
- package/purpose/frameworks/idea-map/ideamap.mjs +4 -2
- package/purpose/frameworks/idea-map/route.mjs +5 -4
- package/purpose/frameworks/product-anatomy/anatomy.mjs +2 -1
- package/schema/README.md +1 -0
- package/schema/access.mjs +58 -0
- package/schema/adapter.schema.json +33 -0
- package/schema/add-department.mjs +3 -2
- package/schema/assess-problem.mjs +3 -2
- package/schema/assets.mjs +213 -0
- package/schema/brand.mjs +374 -0
- package/schema/build-release.mjs +61 -0
- package/schema/caching.mjs +506 -0
- package/schema/check-assets.mjs +24 -0
- package/schema/check-brand.mjs +24 -0
- package/schema/check-config.mjs +111 -0
- package/schema/check-data-model.mjs +2 -1
- package/schema/check-data-packages.mjs +5 -4
- package/schema/check-execution.mjs +4 -3
- package/schema/check-nextjs-status.mjs +3 -1
- package/schema/check-people.mjs +84 -0
- package/schema/check-private.mjs +2 -1
- package/schema/check-ready.mjs +6 -5
- package/schema/check-sources.mjs +11 -2
- package/schema/check-where-used.mjs +4 -3
- package/schema/data-model.json +334 -8
- package/schema/deal.schema.json +80 -0
- package/schema/decision-analytics.mjs +2 -1
- package/schema/decisions.mjs +21 -12
- package/schema/examples/frames/judge.mjs +10 -0
- package/schema/examples/venture-type/jev.mjs +12 -0
- package/schema/examples/workspace/ventures/acme-labs/AGENTS.md +17 -0
- package/schema/examples/workspace/ventures/acme-labs/accounts/.gitkeep +0 -0
- package/schema/examples/workspace/ventures/acme-labs/decisions/discovery-goal.decision.json +23 -0
- package/schema/examples/workspace/ventures/acme-labs/decisions/statement-icp.decision.json +22 -0
- package/schema/examples/workspace/ventures/acme-labs/decisions/statement-problem.decision.json +22 -0
- package/schema/examples/workspace/ventures/acme-labs/decisions/venture.team.decision.json +37 -0
- package/schema/examples/workspace/ventures/acme-labs/knowledge/README.md +3 -0
- package/schema/examples/workspace/ventures/acme-labs/knowledge/sources.json +46 -0
- package/schema/examples/workspace/ventures/acme-labs/money/30000000-0000-4000-8000-000000000001.json +12 -0
- package/schema/examples/workspace/ventures/acme-labs/money/30000000-0000-4000-8000-000000000002.json +12 -0
- package/schema/examples/workspace/ventures/acme-labs/money/30000000-0000-4000-8000-000000000003.json +12 -0
- package/schema/examples/workspace/ventures/acme-labs/money/30000000-0000-4000-8000-000000000004.json +12 -0
- package/schema/examples/workspace/ventures/acme-labs/money/30000000-0000-4000-8000-000000000005.json +12 -0
- package/schema/examples/workspace/ventures/acme-labs/money/30000000-0000-4000-8000-000000000006.json +12 -0
- package/schema/examples/workspace/ventures/acme-labs/money/30000000-0000-4000-8000-000000000007.json +12 -0
- package/schema/examples/workspace/ventures/acme-labs/money/30000000-0000-4000-8000-000000000008.json +12 -0
- package/schema/examples/workspace/ventures/acme-labs/money/30000000-0000-4000-8000-000000000009.json +12 -0
- package/schema/examples/workspace/ventures/acme-labs/money/30000000-0000-4000-8000-000000000010.json +12 -0
- package/schema/examples/workspace/ventures/acme-labs/money/30000000-0000-4000-8000-000000000011.json +12 -0
- package/schema/examples/workspace/ventures/acme-labs/money/30000000-0000-4000-8000-000000000012.json +12 -0
- package/schema/examples/workspace/ventures/acme-labs/money/30000000-0000-4000-8000-000000000013.json +12 -0
- package/schema/examples/workspace/ventures/acme-labs/money/30000000-0000-4000-8000-000000000014.json +12 -0
- package/schema/examples/workspace/ventures/acme-labs/money/_streams/transactions/stream.json +5 -0
- package/schema/examples/workspace/ventures/acme-labs/money/_summary.json +30 -0
- package/schema/examples/workspace/ventures/acme-labs/operations/departments/customer-service/README.md +5 -0
- package/schema/examples/workspace/ventures/acme-labs/operations/departments/customer-service/department.json +5 -0
- package/schema/examples/workspace/ventures/acme-labs/operations/departments/finance/README.md +5 -0
- package/schema/examples/workspace/ventures/acme-labs/operations/departments/finance/department.json +5 -0
- package/schema/examples/workspace/ventures/acme-labs/operations/departments/human-capital/README.md +5 -0
- package/schema/examples/workspace/ventures/acme-labs/operations/departments/human-capital/department.json +5 -0
- package/schema/examples/workspace/ventures/acme-labs/operations/departments/it/README.md +5 -0
- package/schema/examples/workspace/ventures/acme-labs/operations/departments/it/department.json +5 -0
- package/schema/examples/workspace/ventures/acme-labs/operations/departments/marketing-and-sales/README.md +5 -0
- package/schema/examples/workspace/ventures/acme-labs/operations/departments/marketing-and-sales/department.json +5 -0
- package/schema/examples/workspace/ventures/acme-labs/operations/departments/products/README.md +5 -0
- package/schema/examples/workspace/ventures/acme-labs/operations/departments/products/department.json +5 -0
- package/schema/examples/workspace/ventures/acme-labs/operations/departments/strategy/README.md +5 -0
- package/schema/examples/workspace/ventures/acme-labs/operations/departments/strategy/department.json +5 -0
- package/schema/examples/workspace/ventures/acme-labs/people/founders/maya-chen.json +15 -0
- package/schema/examples/workspace/ventures/acme-labs/people/people.json +22 -0
- package/schema/examples/workspace/ventures/acme-labs/pillars/brand.md +5 -0
- package/schema/examples/workspace/ventures/acme-labs/pillars/business.md +5 -0
- package/schema/examples/workspace/ventures/acme-labs/pillars/product.md +5 -0
- package/schema/examples/workspace/ventures/acme-labs/products/.gitkeep +0 -0
- package/schema/examples/workspace/ventures/acme-labs/products/decisions/product.core-product.decision.json +28 -0
- package/schema/examples/workspace/ventures/acme-labs/products/decisions/product.exists.decision.json +38 -0
- package/schema/examples/workspace/ventures/acme-labs/products/decisions/product.flagship.decision.json +27 -0
- package/schema/examples/workspace/ventures/acme-labs/profile.md +12 -0
- package/schema/examples/workspace/ventures/acme-labs/provenance.jsonl +12 -0
- package/schema/examples/workspace/ventures/acme-labs/raw/dump.md +6 -0
- package/schema/examples/workspace/ventures/acme-labs/venture.json +27 -0
- package/schema/examples/workspace/ventures/northstar/AGENTS.md +17 -0
- package/schema/examples/workspace/ventures/northstar/accounts/.gitkeep +0 -0
- package/schema/examples/workspace/ventures/northstar/deals/ductline.json +8 -0
- package/schema/examples/workspace/ventures/northstar/deals/kilnworks.json +14 -0
- package/schema/examples/workspace/ventures/northstar/decisions/venture.team.decision.json +31 -0
- package/schema/examples/workspace/ventures/northstar/evidence/README.md +3 -0
- package/schema/examples/workspace/ventures/northstar/fund/decisions/terms.decision.json +27 -0
- package/schema/examples/workspace/ventures/northstar/fund/decisions/terms.first-close.decision.json +27 -0
- package/schema/examples/workspace/ventures/northstar/fund/decisions/thesis.decision.json +26 -0
- package/schema/examples/workspace/ventures/northstar/fund/terms.json +22 -0
- package/schema/examples/workspace/ventures/northstar/fund/thesis.json +22 -0
- package/schema/examples/workspace/ventures/northstar/governance/README.md +3 -0
- package/schema/examples/workspace/ventures/northstar/governance/departments/external-relationships/charter.md +5 -0
- package/schema/examples/workspace/ventures/northstar/governance/departments/external-relationships/department.json +5 -0
- package/schema/examples/workspace/ventures/northstar/governance/departments/finance/charter.md +5 -0
- package/schema/examples/workspace/ventures/northstar/governance/departments/finance/department.json +5 -0
- package/schema/examples/workspace/ventures/northstar/governance/departments/human-capital/charter.md +5 -0
- package/schema/examples/workspace/ventures/northstar/governance/departments/human-capital/department.json +5 -0
- package/schema/examples/workspace/ventures/northstar/governance/departments/risk-and-compliance/charter.md +5 -0
- package/schema/examples/workspace/ventures/northstar/governance/departments/risk-and-compliance/department.json +5 -0
- package/schema/examples/workspace/ventures/northstar/governance/departments/strategy/charter.md +5 -0
- package/schema/examples/workspace/ventures/northstar/governance/departments/strategy/department.json +5 -0
- package/schema/examples/workspace/ventures/northstar/knowledge/README.md +3 -0
- package/schema/examples/workspace/ventures/northstar/operations/README.md +3 -0
- package/schema/examples/workspace/ventures/northstar/operations/departments/external-relationships/README.md +5 -0
- package/schema/examples/workspace/ventures/northstar/operations/departments/external-relationships/department.json +5 -0
- package/schema/examples/workspace/ventures/northstar/operations/departments/finance/README.md +5 -0
- package/schema/examples/workspace/ventures/northstar/operations/departments/finance/department.json +5 -0
- package/schema/examples/workspace/ventures/northstar/operations/departments/human-capital/README.md +5 -0
- package/schema/examples/workspace/ventures/northstar/operations/departments/human-capital/department.json +5 -0
- package/schema/examples/workspace/ventures/northstar/operations/departments/risk-and-compliance/README.md +5 -0
- package/schema/examples/workspace/ventures/northstar/operations/departments/risk-and-compliance/department.json +5 -0
- package/schema/examples/workspace/ventures/northstar/operations/departments/strategy/README.md +5 -0
- package/schema/examples/workspace/ventures/northstar/operations/departments/strategy/department.json +5 -0
- package/schema/examples/workspace/ventures/northstar/people/founders/ana-ruiz.json +13 -0
- package/schema/examples/workspace/ventures/northstar/people/founders/ben-okafor.json +7 -0
- package/schema/examples/workspace/ventures/northstar/people/people.json +27 -0
- package/schema/examples/workspace/ventures/northstar/pillars/brand.md +5 -0
- package/schema/examples/workspace/ventures/northstar/pillars/business.md +5 -0
- package/schema/examples/workspace/ventures/northstar/pillars/product.md +5 -0
- package/schema/examples/workspace/ventures/northstar/portfolio/kilnworks/AGENTS.md +17 -0
- package/schema/examples/workspace/ventures/northstar/portfolio/kilnworks/accounts/.gitkeep +0 -0
- package/schema/examples/workspace/ventures/northstar/portfolio/kilnworks/decisions/venture.team.decision.json +31 -0
- package/schema/examples/workspace/ventures/northstar/portfolio/kilnworks/knowledge/README.md +3 -0
- package/schema/examples/workspace/ventures/northstar/portfolio/kilnworks/operations/departments/customer-service/README.md +5 -0
- package/schema/examples/workspace/ventures/northstar/portfolio/kilnworks/operations/departments/customer-service/department.json +5 -0
- package/schema/examples/workspace/ventures/northstar/portfolio/kilnworks/operations/departments/finance/README.md +5 -0
- package/schema/examples/workspace/ventures/northstar/portfolio/kilnworks/operations/departments/finance/department.json +5 -0
- package/schema/examples/workspace/ventures/northstar/portfolio/kilnworks/operations/departments/human-capital/README.md +5 -0
- package/schema/examples/workspace/ventures/northstar/portfolio/kilnworks/operations/departments/human-capital/department.json +5 -0
- package/schema/examples/workspace/ventures/northstar/portfolio/kilnworks/operations/departments/it/README.md +5 -0
- package/schema/examples/workspace/ventures/northstar/portfolio/kilnworks/operations/departments/it/department.json +5 -0
- package/schema/examples/workspace/ventures/northstar/portfolio/kilnworks/operations/departments/marketing-and-sales/README.md +5 -0
- package/schema/examples/workspace/ventures/northstar/portfolio/kilnworks/operations/departments/marketing-and-sales/department.json +5 -0
- package/schema/examples/workspace/ventures/northstar/portfolio/kilnworks/operations/departments/products/README.md +5 -0
- package/schema/examples/workspace/ventures/northstar/portfolio/kilnworks/operations/departments/products/department.json +5 -0
- package/schema/examples/workspace/ventures/northstar/portfolio/kilnworks/operations/departments/strategy/README.md +5 -0
- package/schema/examples/workspace/ventures/northstar/portfolio/kilnworks/operations/departments/strategy/department.json +5 -0
- package/schema/examples/workspace/ventures/northstar/portfolio/kilnworks/pillars/brand.md +5 -0
- package/schema/examples/workspace/ventures/northstar/portfolio/kilnworks/pillars/business.md +5 -0
- package/schema/examples/workspace/ventures/northstar/portfolio/kilnworks/pillars/product.md +5 -0
- package/schema/examples/workspace/ventures/northstar/portfolio/kilnworks/products/.gitkeep +0 -0
- package/schema/examples/workspace/ventures/northstar/portfolio/kilnworks/products/decisions/product.core-product.decision.json +28 -0
- package/schema/examples/workspace/ventures/northstar/portfolio/kilnworks/products/decisions/product.exists.decision.json +32 -0
- package/schema/examples/workspace/ventures/northstar/portfolio/kilnworks/products/decisions/product.flagship.decision.json +27 -0
- package/schema/examples/workspace/ventures/northstar/portfolio/kilnworks/profile.md +5 -0
- package/schema/examples/workspace/ventures/northstar/portfolio/kilnworks/venture.json +27 -0
- package/schema/examples/workspace/ventures/northstar/products/.gitkeep +0 -0
- package/schema/examples/workspace/ventures/northstar/profile.md +5 -0
- package/schema/examples/workspace/ventures/northstar/provenance.jsonl +3 -0
- package/schema/examples/workspace/ventures/northstar/purpose/README.md +3 -0
- package/schema/examples/workspace/ventures/northstar/venture.json +49 -0
- package/schema/foundation.mjs +8 -7
- package/schema/founder.schema.json +72 -0
- package/schema/frame.schema.json +240 -0
- package/schema/frames.mjs +229 -0
- package/schema/freshness-log.schema.json +40 -0
- package/schema/freshness-profiles.schema.json +44 -0
- package/schema/freshness.mjs +474 -0
- package/schema/fund-terms.schema.json +107 -0
- package/schema/fund-thesis.schema.json +87 -0
- package/schema/gate-drafts.mjs +131 -0
- package/schema/gate.mjs +105 -11
- package/schema/glossary.schema.json +25 -0
- package/schema/init-venture.mjs +69 -21
- package/schema/judgment.schema.json +38 -0
- package/schema/kit.schema.json +47 -0
- package/schema/kits.mjs +24 -0
- package/schema/knowledge-sources.schema.json +145 -15
- package/schema/layer.json +16 -3
- package/schema/marketing-brand.schema.json +116 -0
- package/schema/migrate-venture.mjs +8 -7
- package/schema/migrations/kit-to-package.mjs +95 -0
- package/schema/migrations/package-layout.mjs +90 -0
- package/schema/model-steps.mjs +718 -0
- package/schema/money-summary.schema.json +138 -0
- package/schema/naive-config.mjs +166 -0
- package/schema/naive-config.schema.json +268 -0
- package/schema/need-now.json +171 -0
- package/schema/need-now.mjs +8 -6
- package/schema/packages.mjs +21 -15
- package/schema/payouts.mjs +3 -2
- package/schema/people.schema.json +85 -0
- package/schema/port-venture.mjs +206 -132
- package/schema/public-assets.schema.json +76 -0
- package/schema/release-read.mjs +112 -0
- package/schema/releases.mjs +498 -0
- package/schema/run-handoff.mjs +5 -4
- package/schema/setup.mjs +165 -0
- package/schema/stale.mjs +112 -0
- package/schema/start-release.mjs +41 -0
- package/schema/structure.mjs +187 -0
- package/schema/systems-catalog.json +126 -0
- package/schema/systems-catalog.schema.json +78 -0
- package/schema/time-to-ready.mjs +7 -6
- package/schema/trigger.schema.json +44 -0
- package/schema/triggers.mjs +238 -0
- package/schema/upgrade-registry.schema.json +60 -0
- package/schema/upgrade-venture.mjs +96 -118
- package/schema/upgrades.json +69 -0
- package/schema/upgrades.mjs +386 -0
- package/schema/validate.py +19 -10
- package/schema/venture-paths.mjs +85 -0
- package/schema/venture-role.schema.json +64 -0
- package/schema/venture-type.mjs +62 -0
- package/schema/venture-types.json +346 -0
- package/schema/venture-types.schema.json +129 -0
- package/schema/venture.schema.json +61 -0
- package/schema/workspace.schema.json +5 -1
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// Checks a venture's naive.config.json (P-2) and explains its gate. Rules: the file matches
|
|
3
|
+
// schema/naive-config.schema.json; rule ids are unique; every route pattern in fail_closed and the
|
|
4
|
+
// rules matches at least one route in the router's table (X-6); every check named in checks exists;
|
|
5
|
+
// every package pinned is installed at that version (U-4); every owner is a role in the team plan
|
|
6
|
+
// when the venture has one; every question setting names a framework that asks System 1 questions.
|
|
7
|
+
//
|
|
8
|
+
// node schema/check-config.mjs <venture dir> [--json] check; exit 1 on a problem
|
|
9
|
+
// node schema/check-config.mjs <venture dir> --resolved the config with the defaults filled in
|
|
10
|
+
// node schema/check-config.mjs <venture dir> --rules every rule the gate applies, and where it comes from
|
|
11
|
+
// node schema/check-config.mjs <venture dir> --explain <route> [--as person] [--role <id>] [--confidence <n>]
|
|
12
|
+
// what the gate would do with a write, without writing
|
|
13
|
+
// node schema/check-config.mjs <venture dir> --pin [<package,...>]
|
|
14
|
+
// pin packages at their installed versions: the ones
|
|
15
|
+
// named, else the ones already pinned, else every one
|
|
16
|
+
|
|
17
|
+
import { spawnSync } from "node:child_process";
|
|
18
|
+
import { existsSync, mkdtempSync, readFileSync, rmSync, writeFileSync } from "node:fs";
|
|
19
|
+
import { tmpdir } from "node:os";
|
|
20
|
+
import { join, resolve as resolvePath } from "node:path";
|
|
21
|
+
import { CONFIG, configFor, installed, pinProblems, rawConfig } from "./naive-config.mjs";
|
|
22
|
+
import { evaluate, patternRe, rulesFor } from "./gate.mjs";
|
|
23
|
+
import { teamPlan } from "./access.mjs";
|
|
24
|
+
import { gateOf, repo, resolve, routes } from "../interface/router.mjs";
|
|
25
|
+
import { agentFromEnv, record } from "../interface/provenance.mjs";
|
|
26
|
+
import { ventureFile } from "./venture-paths.mjs";
|
|
27
|
+
|
|
28
|
+
const FRAMEWORKS = ["idea-map"];
|
|
29
|
+
|
|
30
|
+
/** Problems with a venture's naive.config.json: { errors, warnings }. */
|
|
31
|
+
export function check(dir) {
|
|
32
|
+
const errors = [], warnings = [];
|
|
33
|
+
const f = ventureFile(dir, CONFIG);
|
|
34
|
+
if (!existsSync(f)) return { errors, warnings, note: `no ${CONFIG}: every default applies` };
|
|
35
|
+
let raw;
|
|
36
|
+
try { raw = rawConfig(dir); } catch (e) { return { errors: [e.message], warnings }; }
|
|
37
|
+
const scratch = mkdtempSync(join(tmpdir(), "naive-config-"));
|
|
38
|
+
writeFileSync(join(scratch, CONFIG), JSON.stringify(raw));
|
|
39
|
+
const v = spawnSync("python3", [join(repo, "schema/validate.py"), "--schema", "naive-config.schema.json", "--file", join(scratch, CONFIG)], { encoding: "utf8" });
|
|
40
|
+
rmSync(scratch, { recursive: true, force: true });
|
|
41
|
+
if (v.status !== 0) errors.push(`${CONFIG} does not match schema/naive-config.schema.json:\n${(v.stdout + v.stderr).trim()}`);
|
|
42
|
+
const config = configFor(dir);
|
|
43
|
+
const table = routes();
|
|
44
|
+
const known = (p) => !!resolve(p).r || table.some((r) => patternRe(p).test(r.route) || patternRe(p).test(r.route.replace(/\[id\]/g, "x")));
|
|
45
|
+
const ids = new Set();
|
|
46
|
+
for (const r of rulesFor(config).filter((x) => x.source === "naive.config.json" && !x.fail_closed)) {
|
|
47
|
+
if (ids.has(r.id)) errors.push(`gate rule ${r.id}: duplicate id`);
|
|
48
|
+
ids.add(r.id);
|
|
49
|
+
}
|
|
50
|
+
for (const p of config.gate.fail_closed ?? []) if (!known(p) && !known(`${p}/**`)) errors.push(`gate.fail_closed ${p}: matches no route (see node interface/router.mjs --md)`);
|
|
51
|
+
for (const r of config.gate.rules ?? []) for (const p of r.routes ?? []) if (!known(p)) errors.push(`gate rule ${r.id}: ${p} matches no route`);
|
|
52
|
+
for (const c of [...(config.checks.off ?? []), ...(config.checks.warn ?? [])]) if (!existsSync(join(repo, c))) errors.push(`checks: ${c} does not exist`);
|
|
53
|
+
for (const p of pinProblems(config)) errors.push(p.installed ? `packages: ${p.package} is pinned to ${p.pinned} but ${p.installed} is installed` : `packages: ${p.package} is not an installed package`);
|
|
54
|
+
const plan = teamPlan(dir);
|
|
55
|
+
for (const [fn, role] of Object.entries(config.owners ?? {})) {
|
|
56
|
+
if (plan && !(plan.roles ?? []).some((r) => r.id === role)) errors.push(`owners: ${fn} names ${role}, which is not a role in the team plan`);
|
|
57
|
+
if (!plan) warnings.push(`owners: ${fn} names ${role}, and there is no team plan to check it against`);
|
|
58
|
+
}
|
|
59
|
+
for (const fw of Object.keys(config.questions ?? {})) if (!FRAMEWORKS.includes(fw)) errors.push(`questions: ${fw} asks no System 1 questions (one of ${FRAMEWORKS.join(", ")})`);
|
|
60
|
+
return { errors, warnings };
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/** What the gate would do with a write to a route, without writing. */
|
|
64
|
+
export function explain(dir, route, { actor = "agent", role, confidence } = {}) {
|
|
65
|
+
const { r, params } = resolve(route);
|
|
66
|
+
if (!r || r.kind !== "record" || !r.actions.includes("write")) throw new Error(`${route} is not a writable record route`);
|
|
67
|
+
const concrete = r.collection === "item" ? r.route.replace("[id]", params.id) : r.route;
|
|
68
|
+
const cls = gateOf(r, dir);
|
|
69
|
+
const files = (r.collection === "item" ? r.files.map((x) => x.replace("*", params.id)) : r.files);
|
|
70
|
+
const exists = files.some((x) => existsSync(ventureFile(dir, x)));
|
|
71
|
+
const verdict = evaluate({ config: configFor(dir), dir, route: concrete, table: r.route, record: r.record, actor, role, confidence, exists, decisionOwner: cls?.owner ?? undefined });
|
|
72
|
+
return { route: concrete, record: r.record, actor, role: role ?? null, confidence: confidence ?? null, decision_class: cls ? { class: cls.class, level: cls.level, drafts: cls.drafts && actor !== "person" } : null, gate: verdict };
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/** Pins packages at their installed versions and logs the change. Returns the new pins. */
|
|
76
|
+
export function pin(dir, names) {
|
|
77
|
+
const raw = rawConfig(dir), have = installed();
|
|
78
|
+
const chosen = names?.length ? names : Object.keys(raw.packages ?? {}).length ? Object.keys(raw.packages) : Object.keys(have);
|
|
79
|
+
const unknown = chosen.filter((n) => !have[n]);
|
|
80
|
+
if (unknown.length) throw new Error(`not installed: ${unknown.join(", ")}`);
|
|
81
|
+
raw.packages = { ...(raw.packages ?? {}), ...Object.fromEntries(chosen.map((n) => [n, have[n]])) };
|
|
82
|
+
writeFileSync(ventureFile(dir, CONFIG), JSON.stringify(raw, null, 2) + "\n");
|
|
83
|
+
record(dir, { entity: CONFIG, route: "system/naive-config", activity: "write", agent: agentFromEnv("person", "cli"), note: `pinned ${chosen.join(", ")} at the installed versions` });
|
|
84
|
+
return raw.packages;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
if (import.meta.url === `file://${process.argv[1]}`) {
|
|
88
|
+
const argv = process.argv.slice(2);
|
|
89
|
+
const opt = (n) => { const i = argv.indexOf(`--${n}`); return i >= 0 && argv[i + 1] && !argv[i + 1].startsWith("--") ? argv[i + 1] : undefined; };
|
|
90
|
+
const dir = argv[0] && resolvePath(argv[0]);
|
|
91
|
+
if (!dir || !existsSync(join(dir, "venture.json"))) { console.error("usage: node schema/check-config.mjs <venture dir> [--json | --resolved | --rules | --explain <route> | --pin [<packages>]]"); process.exit(2); }
|
|
92
|
+
const out = (x) => console.log(JSON.stringify(x, null, 2));
|
|
93
|
+
try {
|
|
94
|
+
if (argv.includes("--resolved")) { out(configFor(dir)); process.exit(0); }
|
|
95
|
+
if (argv.includes("--rules")) { out(rulesFor(configFor(dir)).map((r) => ({ id: r.id, source: r.source, routes: r.routes, then: r.then, ...(r.when ? { when: r.when } : {}), actors: r.actors ?? ["agent"] }))); process.exit(0); }
|
|
96
|
+
if (argv.includes("--explain")) {
|
|
97
|
+
const c = opt("confidence");
|
|
98
|
+
out(explain(dir, opt("explain"), { actor: opt("as") === "person" ? "person" : "agent", role: opt("role") ?? process.env.MENTURA_ACTOR_ID, confidence: c === undefined ? undefined : Number(c) }));
|
|
99
|
+
process.exit(0);
|
|
100
|
+
}
|
|
101
|
+
if (argv.includes("--pin")) { out({ pinned: pin(dir, opt("pin")?.split(",").filter(Boolean)) }); process.exit(0); }
|
|
102
|
+
} catch (e) { console.error(String(e.message ?? e)); process.exit(1); }
|
|
103
|
+
const r = check(dir);
|
|
104
|
+
if (argv.includes("--json")) out(r);
|
|
105
|
+
else {
|
|
106
|
+
for (const w of r.warnings) console.log(`warn ${w}`);
|
|
107
|
+
for (const e of r.errors) console.log(`FAIL ${e}`);
|
|
108
|
+
if (!r.errors.length) console.log(r.note ?? `${CONFIG} is valid`);
|
|
109
|
+
}
|
|
110
|
+
process.exit(r.errors.length ? 1 : 0);
|
|
111
|
+
}
|
|
@@ -17,6 +17,7 @@
|
|
|
17
17
|
import { spawnSync } from "node:child_process";
|
|
18
18
|
import { existsSync, readFileSync, readdirSync } from "node:fs";
|
|
19
19
|
import { join, relative } from "node:path";
|
|
20
|
+
import { ventureFile } from "./venture-paths.mjs";
|
|
20
21
|
|
|
21
22
|
// EE_ROOT points the check at another copy of the repository (used by schema/test.mjs).
|
|
22
23
|
const root = process.env.EE_ROOT?.replace(/\/?$/, "/") ?? new URL("..", import.meta.url).pathname;
|
|
@@ -26,7 +27,7 @@ const errors = [];
|
|
|
26
27
|
const warnings = [];
|
|
27
28
|
|
|
28
29
|
const files = (dir) => readdirSync(join(root, dir), { withFileTypes: true }).flatMap((e) =>
|
|
29
|
-
[".git", "node_modules", ".naive"].includes(e.name) ? [] : e.isDirectory() ? files(
|
|
30
|
+
[".git", "node_modules", ".naive"].includes(e.name) ? [] : e.isDirectory() ? files(ventureFile(dir, e.name)) : [ventureFile(dir, e.name)]);
|
|
30
31
|
// The files git would track (so build output and installed packages under .gitignore are left out); a
|
|
31
32
|
// copy outside git, as the tests use, is walked instead.
|
|
32
33
|
const tracked = spawnSync("git", ["ls-files", "--cached", "--others", "--exclude-standard"], { cwd: root, encoding: "utf8" });
|
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
import { spawnSync } from "node:child_process";
|
|
11
11
|
import { existsSync, readdirSync, readFileSync, statSync } from "node:fs";
|
|
12
12
|
import { join } from "node:path";
|
|
13
|
+
import { ventureFile } from "./venture-paths.mjs";
|
|
13
14
|
|
|
14
15
|
const dir = process.argv[2];
|
|
15
16
|
if (!dir) { console.error("usage: node schema/check-data-packages.mjs <workspace or venture dir>"); process.exit(2); }
|
|
@@ -32,7 +33,7 @@ for (const v of walk(dir)) {
|
|
|
32
33
|
const id = load(join(v, "venture.json")).id;
|
|
33
34
|
const git = inGit(v);
|
|
34
35
|
for (const pack of PACKS) {
|
|
35
|
-
const base =
|
|
36
|
+
const base = ventureFile(v, pack);
|
|
36
37
|
if (!existsSync(base)) continue;
|
|
37
38
|
for (const name of readdirSync(base)) {
|
|
38
39
|
if (name.startsWith("_") || name.startsWith(".")) continue;
|
|
@@ -56,7 +57,7 @@ for (const v of walk(dir)) {
|
|
|
56
57
|
}
|
|
57
58
|
}
|
|
58
59
|
}
|
|
59
|
-
const sources = join(v, "raw/sources");
|
|
60
|
+
const sources = join(ventureFile(v, "raw/sources"));
|
|
60
61
|
if (existsSync(sources)) for (const s of readdirSync(sources)) {
|
|
61
62
|
const sd = join(sources, s);
|
|
62
63
|
if (!statSync(sd).isDirectory() || s.startsWith(".")) continue;
|
|
@@ -64,9 +65,9 @@ for (const v of walk(dir)) {
|
|
|
64
65
|
if (!existsSync(join(sd, "text.md"))) errors.push(`${id}/raw/sources/${s}: missing text.md`);
|
|
65
66
|
for (const f of readdirSync(sd)) if (f.startsWith("original.") && (!git || tracked(v, join("raw/sources", s, f)))) errors.push(`${id}/raw/sources/${s}/${f}: original.* stays out of git`);
|
|
66
67
|
}
|
|
67
|
-
const map = ["ideas/map.mdx", "purpose/ideas/map.mdx"].map((f) =>
|
|
68
|
+
const map = ["ideas/map.mdx", "purpose/ideas/map.mdx"].map((f) => ventureFile(v, f)).find((p) => existsSync(p));
|
|
68
69
|
const rawIds = new Set();
|
|
69
|
-
const dump = join(v, "raw/dump");
|
|
70
|
+
const dump = join(ventureFile(v, "raw/dump"));
|
|
70
71
|
if (existsSync(dump)) for (const f of readdirSync(dump)) if (f !== ".gitkeep") rawIds.add(f.replace(/\.[^.]+$/, ""));
|
|
71
72
|
if (existsSync(sources)) for (const s of readdirSync(sources)) if (!s.startsWith(".")) rawIds.add(s);
|
|
72
73
|
if (map && rawIds.size) {
|
|
@@ -13,6 +13,7 @@
|
|
|
13
13
|
|
|
14
14
|
import { existsSync, readFileSync, readdirSync } from "node:fs";
|
|
15
15
|
import { dirname, join, resolve } from "node:path";
|
|
16
|
+
import { ventureFile } from "./venture-paths.mjs";
|
|
16
17
|
|
|
17
18
|
const dir = process.argv[2];
|
|
18
19
|
if (!dir) { console.error("usage: node schema/check-execution.mjs <venture dir>"); process.exit(2); }
|
|
@@ -27,7 +28,7 @@ const template = load(join(root, "governance/venture-template/decision-rights.js
|
|
|
27
28
|
const errors = [];
|
|
28
29
|
|
|
29
30
|
const classes = new Map(template.classes.map((c) => [c.id, c]));
|
|
30
|
-
const own = join(dir, "governance/decision-rights.json");
|
|
31
|
+
const own = join(ventureFile(dir, "governance/decision-rights.json"));
|
|
31
32
|
if (existsSync(own)) for (const c of load(own).classes) {
|
|
32
33
|
const t = classes.get(c.id);
|
|
33
34
|
if (t && RANK[c.level] < RANK[t.level] && !c.decision) errors.push(`decision-rights/${c.id}: loosens ${t.level} to ${c.level} without naming a decision`);
|
|
@@ -36,14 +37,14 @@ if (existsSync(own)) for (const c of load(own).classes) {
|
|
|
36
37
|
for (const c of classes.values()) if (!roles.has(c.owner)) errors.push(`decision-rights/${c.id}: owner ${c.owner} is not a role`);
|
|
37
38
|
|
|
38
39
|
const results = new Map();
|
|
39
|
-
for (const f of jsonIn(join(dir, "evidence/results"))) {
|
|
40
|
+
for (const f of jsonIn(join(ventureFile(dir, "evidence/results")))) {
|
|
40
41
|
const e = load(f);
|
|
41
42
|
if (e.passed !== e.results.every((r) => r.passed)) errors.push(`evidence/${e.id}: passed says ${e.passed} but its results disagree`);
|
|
42
43
|
results.set(resolve(f), e);
|
|
43
44
|
}
|
|
44
45
|
|
|
45
46
|
let count = 0;
|
|
46
|
-
for (const f of jsonIn(join(dir, "operations/handoffs"))) {
|
|
47
|
+
for (const f of jsonIn(join(ventureFile(dir, "operations/handoffs")))) {
|
|
47
48
|
const h = load(f), at = `handoff/${h.id}`;
|
|
48
49
|
count++;
|
|
49
50
|
if (h.venture !== venture.id) errors.push(`${at}: venture ${h.venture} is not ${venture.id}`);
|
|
@@ -38,7 +38,9 @@ const counts = Object.fromEntries(STATES.map((s) => [s, count(s)]));
|
|
|
38
38
|
|
|
39
39
|
function page() {
|
|
40
40
|
const chapters = [...new Set(data.rows.map((r) => r.chapter))];
|
|
41
|
-
|
|
41
|
+
// MDX reads { and < as code outside backticks, so they are escaped there.
|
|
42
|
+
const mdx = (t) => t.split(/(`[^`]*`)/).map((x, i) => i % 2 ? x : x.replace(/[{}]/g, (c) => `\\${c}`).replace(/</g, "<").replace(/>/g, ">")).join("");
|
|
43
|
+
const cell = (s) => mdx(String(s ?? "")).replace(/\|/g, "\\|");
|
|
42
44
|
const lines = [
|
|
43
45
|
"---",
|
|
44
46
|
"title: What's built",
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// Checks venture types and people (EE-082) in a workspace: a venture's type is in schema/venture-types.json;
|
|
3
|
+
// in people/people.json every role is in the role registry and allowed for the venture's type, every hat
|
|
4
|
+
// is a function the venture imports, a solo venture has one person who wears every hat, and every
|
|
5
|
+
// founder has a file in people/founders/ that agrees with the roster (and the roster lists every file).
|
|
6
|
+
//
|
|
7
|
+
// node schema/check-people.mjs <workspace>
|
|
8
|
+
|
|
9
|
+
import { existsSync, readdirSync, readFileSync } from "node:fs";
|
|
10
|
+
import { join } from "node:path";
|
|
11
|
+
import { roles as ROLES, types as TYPES } from "./venture-type.mjs";
|
|
12
|
+
import { ventureFile } from "./venture-paths.mjs";
|
|
13
|
+
|
|
14
|
+
const ws = process.argv[2];
|
|
15
|
+
if (!ws || !existsSync(ws)) { console.error("usage: node schema/check-people.mjs <workspace>"); process.exit(2); }
|
|
16
|
+
const load = (p) => JSON.parse(readFileSync(p, "utf8"));
|
|
17
|
+
const walk = (d) => readdirSync(d, { withFileTypes: true }).flatMap((e) => e.name === "node_modules" || e.name.startsWith(".") ? []
|
|
18
|
+
: e.isDirectory() ? walk(join(d, e.name)) : e.name === "venture.json" ? [d] : []);
|
|
19
|
+
|
|
20
|
+
const types = new Map(TYPES().map((t) => [t.id, t]));
|
|
21
|
+
const roles = new Map(ROLES().map((r) => [r.id, r]));
|
|
22
|
+
const errors = [];
|
|
23
|
+
let ventures = 0, rosters = 0;
|
|
24
|
+
for (const dir of walk(ws)) {
|
|
25
|
+
ventures++;
|
|
26
|
+
const v = load(join(dir, "venture.json"));
|
|
27
|
+
const at = (m) => errors.push(`${v.id}: ${m}`);
|
|
28
|
+
const type = v.type ? types.get(v.type) : null;
|
|
29
|
+
if (v.type && !type) at(`type ${v.type} is not in schema/venture-types.json`);
|
|
30
|
+
const rosterFile = join(ventureFile(dir, "people/people.json"));
|
|
31
|
+
const fdir = join(ventureFile(dir, "people/founders"));
|
|
32
|
+
const files = existsSync(fdir) ? readdirSync(fdir).filter((f) => f.endsWith(".json")) : [];
|
|
33
|
+
if (!existsSync(rosterFile)) { if (files.length) at("people/founders/ has files but there is no people/people.json"); continue; }
|
|
34
|
+
rosters++;
|
|
35
|
+
const roster = load(rosterFile);
|
|
36
|
+
// In the structured layout (EE-086) the org chart is venture.json's functions, and a person's hats are the
|
|
37
|
+
// functions they own there; in the flat layout, the departments venture.json imports, worn as hats in people/.
|
|
38
|
+
const structured = v.layout === "structured";
|
|
39
|
+
const imported = new Set(structured ? Object.keys(v.functions ?? {}) : v.departments ?? []);
|
|
40
|
+
const ids = new Set(roster.people.map((p) => p.id));
|
|
41
|
+
if (structured) {
|
|
42
|
+
for (const [fn, f] of Object.entries(v.functions ?? {})) {
|
|
43
|
+
if (f.owner && !ids.has(f.owner)) at(`venture.json says ${f.owner} owns ${fn}, but people/people.json has no ${f.owner}`);
|
|
44
|
+
for (const c of f.contributors ?? []) if (!ids.has(c) && !roles.has(c)) at(`${fn} names contributor ${c}, who is neither a person in people/ nor a role`);
|
|
45
|
+
}
|
|
46
|
+
for (const p of roster.people) p.hats = Object.entries(v.functions ?? {}).filter(([, f]) => f.owner === p.id).map(([fn]) => fn);
|
|
47
|
+
}
|
|
48
|
+
for (const p of roster.people) {
|
|
49
|
+
const role = roles.get(p.role);
|
|
50
|
+
if (!role) { at(`${p.id} has role ${p.role}, which is not in governance/venture-template/roles/venture-roles.json`); continue; }
|
|
51
|
+
if (type && !type.roles.includes(p.role)) at(`${p.id} is a ${p.role}, which a ${type.label.toLowerCase()} does not have (${type.roles.join(", ")})`);
|
|
52
|
+
for (const h of p.hats ?? []) if (imported.size && !imported.has(h)) at(`${p.id} wears the ${h} hat, but venture.json does not import ${h}`);
|
|
53
|
+
p.hats ??= [];
|
|
54
|
+
if (!role.owns_when_solo && p.hats.length) at(`${p.id} is a ${p.role}, who owns no function, but wears ${p.hats.join(", ")}`);
|
|
55
|
+
if (role.founder) {
|
|
56
|
+
const f = `people/founders/${p.id}.json`;
|
|
57
|
+
if (p.founder_file !== f) at(`${p.id} is a founder, so founder_file is ${f}`);
|
|
58
|
+
else if (!existsSync(ventureFile(dir, f))) at(`${f} is missing`);
|
|
59
|
+
else {
|
|
60
|
+
const rec = load(ventureFile(dir, f));
|
|
61
|
+
if (rec.id !== p.id || rec.role !== p.role) at(`${f} says ${rec.id} is a ${rec.role}; the roster says ${p.id} is a ${p.role}`);
|
|
62
|
+
if (!structured && JSON.stringify([...(rec.hats ?? [])].sort()) !== JSON.stringify([...p.hats].sort())) at(`${f} and the roster disagree on ${p.id}'s hats`);
|
|
63
|
+
if (structured && rec.hats) at(`${f} stores hats; in the structured layout they are read from venture.json`);
|
|
64
|
+
if (rec.agreement && !existsSync(ventureFile(dir, rec.agreement.path))) at(`${f} links a founder agreement at ${rec.agreement.path}, which does not exist`);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
if (roster.mode === "solo") {
|
|
69
|
+
if (roster.people.length !== 1) at(`a solo venture has one person; people/people.json lists ${roster.people.length}`);
|
|
70
|
+
else if (roles.get(roster.people[0].role)?.owns_when_solo && imported.size) {
|
|
71
|
+
const missing = [...imported].filter((h) => !roster.people[0].hats.includes(h));
|
|
72
|
+
if (missing.length) at(`${roster.people[0].id} is alone, so owns every function; missing ${missing.join(", ")}`);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
if (roster.mode === "team" && imported.size) {
|
|
76
|
+
const worn = new Set(roster.people.flatMap((p) => p.hats));
|
|
77
|
+
const unowned = [...imported].filter((h) => !worn.has(h));
|
|
78
|
+
if (unowned.length) at(`no one owns ${unowned.join(", ")}`);
|
|
79
|
+
}
|
|
80
|
+
for (const f of files) if (!roster.people.some((p) => p.founder_file === `people/founders/${f}`)) at(`people/founders/${f} is not in people/people.json`);
|
|
81
|
+
}
|
|
82
|
+
for (const e of errors) console.log(`FAIL ${e}`);
|
|
83
|
+
console.log(`${ventures} ventures, ${rosters} people rosters: ${errors.length ? `${errors.length} error(s)` : "every role, hat and founder file agrees"}.`);
|
|
84
|
+
process.exit(errors.length ? 1 : 0);
|
package/schema/check-private.mjs
CHANGED
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
|
|
8
8
|
import { existsSync, readdirSync, readFileSync, statSync } from "node:fs";
|
|
9
9
|
import { join } from "node:path";
|
|
10
|
+
import { ventureFile } from "./venture-paths.mjs";
|
|
10
11
|
|
|
11
12
|
const dir = process.argv[2];
|
|
12
13
|
if (!dir) { console.error("usage: node schema/check-private.mjs <venture dir>"); process.exit(2); }
|
|
@@ -49,7 +50,7 @@ for (const { package: name, input } of inputs) {
|
|
|
49
50
|
if (privateTargets.has(input) || privateRecords.has(input.split(".")[0])) errors.push(`${name}: use_model input ${input} is private and must not reach a model`);
|
|
50
51
|
}
|
|
51
52
|
if (readsRelease) {
|
|
52
|
-
const release = join(dir, ".release");
|
|
53
|
+
const release = join(ventureFile(dir, ".release"));
|
|
53
54
|
if (existsSync(release) && statSync(release).isDirectory()) {
|
|
54
55
|
for (const f of walk(release).filter((p) => p.endsWith(".json"))) {
|
|
55
56
|
const text = readFileSync(f, "utf8");
|
package/schema/check-ready.mjs
CHANGED
|
@@ -13,19 +13,20 @@
|
|
|
13
13
|
import { execSync } from "node:child_process";
|
|
14
14
|
import { existsSync, readFileSync, readdirSync, writeFileSync } from "node:fs";
|
|
15
15
|
import { join } from "node:path";
|
|
16
|
+
import { ventureFile } from "./venture-paths.mjs";
|
|
16
17
|
|
|
17
18
|
const [dir, id] = process.argv.slice(2);
|
|
18
19
|
if (!dir || !id) { console.error("usage: node schema/check-ready.mjs <venture dir> <handoff id> [--record --by <who>]"); process.exit(2); }
|
|
19
20
|
const opt = (k) => process.argv.includes(k) ? process.argv[process.argv.indexOf(k) + 1] : undefined;
|
|
20
21
|
const root = new URL("..", import.meta.url).pathname;
|
|
21
22
|
const load = (p) => JSON.parse(readFileSync(p, "utf8"));
|
|
22
|
-
const hPath = join(dir, "operations/handoffs", `${id}.json`);
|
|
23
|
+
const hPath = join(ventureFile(dir, "operations/handoffs"), `${id}.json`);
|
|
23
24
|
const h = load(hPath);
|
|
24
25
|
const items = [];
|
|
25
26
|
const item = (name, ok, why = "") => items.push({ name, ok, why });
|
|
26
27
|
|
|
27
28
|
// 1. The brief says what done looks like and what to do, with no placeholders left.
|
|
28
|
-
const bPath = join(dir, "operations/handoffs", h.brief);
|
|
29
|
+
const bPath = join(ventureFile(dir, "operations/handoffs"), h.brief);
|
|
29
30
|
const brief = existsSync(bPath) ? readFileSync(bPath, "utf8") : "";
|
|
30
31
|
item("brief exists", !!brief, bPath);
|
|
31
32
|
const section = (name) => { const m = brief.match(new RegExp(`^##\\s+(?:\\d+\\.\\s*)?${name}\\b[^\\n]*\\n([\\s\\S]*?)(?=^##\\s|$(?![\\s\\S]))`, "mi")); return m ? m[1].trim() : null; };
|
|
@@ -34,10 +35,10 @@ const holes = brief.match(/\b(TBD|TODO|FIXME)\b|\?\?\?|<(?:title|role|entity|out
|
|
|
34
35
|
item("brief has no placeholders left", !holes.length, holes.slice(0, 3).join(", "));
|
|
35
36
|
|
|
36
37
|
// 2. Everything it names exists.
|
|
37
|
-
const products = new Set(["products", "product"].flatMap((folder) => existsSync(
|
|
38
|
+
const products = new Set(["products", "product"].flatMap((folder) => existsSync(ventureFile(dir, folder)) ? readdirSync(ventureFile(dir, folder)).filter((f) => f.endsWith(".json")).map((f) => f.slice(0, -5)) : []));
|
|
38
39
|
const missingProducts = (h.products ?? []).filter((p) => !products.has(p));
|
|
39
40
|
item("every product it serves exists", !missingProducts.length, missingProducts.join(", "));
|
|
40
|
-
const logs = [join(root, "docs/decisions/decisions.json"), join(dir, "knowledge/decisions.json")].filter(existsSync).flatMap((f) => load(f).decisions);
|
|
41
|
+
const logs = [join(root, "docs/decisions/decisions.json"), join(ventureFile(dir, "knowledge/decisions.json"))].filter(existsSync).flatMap((f) => load(f).decisions);
|
|
41
42
|
const decisions = new Map(logs.map((d) => [d.id, d]));
|
|
42
43
|
const badDecisions = (h.governed_by ?? []).filter((d) => !decisions.has(d) || ["withdrawn", "superseded"].includes(decisions.get(d).status));
|
|
43
44
|
item("every governing decision exists and is in force or proposed", !badDecisions.length, badDecisions.join(", "));
|
|
@@ -49,7 +50,7 @@ item("every input it needs exists", !missingInputs.length, missingInputs.join(",
|
|
|
49
50
|
// 3. The holder can act inside its rights.
|
|
50
51
|
const roles = new Set(load(join(root, "governance/venture-template/roles/agent-roles.json")).roles.map((r) => r.id));
|
|
51
52
|
const classes = new Map(load(join(root, "governance/venture-template/decision-rights.json")).classes.map((c) => [c.id, c]));
|
|
52
|
-
const own = join(dir, "governance/decision-rights.json");
|
|
53
|
+
const own = join(ventureFile(dir, "governance/decision-rights.json"));
|
|
53
54
|
if (existsSync(own)) for (const c of load(own).classes) classes.set(c.id, { ...classes.get(c.id), ...c });
|
|
54
55
|
item(`role ${h.role} exists`, roles.has(h.role));
|
|
55
56
|
const foreign = h.decision_classes.filter((c) => classes.get(c)?.owner !== h.role);
|
package/schema/check-sources.mjs
CHANGED
|
@@ -11,6 +11,7 @@ import { existsSync, readFileSync, readdirSync } from "node:fs";
|
|
|
11
11
|
import { join } from "node:path";
|
|
12
12
|
import { LOCAL, MAP, registry } from "../knowledge/connectors/index.mjs";
|
|
13
13
|
import { ANY, CONTEXT, specificity } from "../knowledge/connectors/resolve.mjs";
|
|
14
|
+
import { ventureFile } from "./venture-paths.mjs";
|
|
14
15
|
|
|
15
16
|
const dir = process.argv[2];
|
|
16
17
|
if (!dir) { console.error("usage: node schema/check-sources.mjs <workspace or venture dir>"); process.exit(2); }
|
|
@@ -21,11 +22,13 @@ const model = JSON.parse(readFileSync(join(root, "schema/data-model.json"), "utf
|
|
|
21
22
|
const ventureTypes = new Set(model.records.filter((r) => r.level === "venture").map((r) => r.id));
|
|
22
23
|
const PINNED = new Set(["venture", "provenance", "knowledge-sources"]);
|
|
23
24
|
const reg = registry();
|
|
25
|
+
// The systems a function's truth can live in (EE-083); a connected one must have code that reads it.
|
|
26
|
+
const catalog = JSON.parse(readFileSync(join(root, "schema/systems-catalog.json"), "utf8"));
|
|
24
27
|
const errors = [];
|
|
25
28
|
let maps = 0;
|
|
26
29
|
|
|
27
30
|
for (const v of walk(dir)) {
|
|
28
|
-
const f =
|
|
31
|
+
const f = ventureFile(v, MAP);
|
|
29
32
|
if (!existsSync(f)) continue;
|
|
30
33
|
maps++;
|
|
31
34
|
const id = JSON.parse(readFileSync(join(v, "venture.json"), "utf8")).id;
|
|
@@ -57,10 +60,16 @@ for (const v of walk(dir)) {
|
|
|
57
60
|
if (!ids.has(rule.system_of_record)) errors.push(`${where}: system of record ${rule.system_of_record} is not one of its sources`);
|
|
58
61
|
for (const s of rule.survivorship?.also_in ?? []) if (!ids.has(s) || s === rule.system_of_record) errors.push(`${where}: survivorship names ${s}, which is not another of its sources`);
|
|
59
62
|
});
|
|
63
|
+
for (const [fn, f] of Object.entries(m.functions ?? {})) for (const s of f.systems) {
|
|
64
|
+
const sys = catalog.systems[s.system];
|
|
65
|
+
if (!sys) { errors.push(`${id}: ${fn} keeps its truth in ${s.system}, which is not in schema/systems-catalog.json`); continue; }
|
|
66
|
+
if (s.status === "connected" && !sys.connect) errors.push(`${id}: ${fn} says ${s.system} is connected, but no code reads ${sys.label} yet; it is pointed`);
|
|
67
|
+
if (s.source && !ids.has(s.source)) errors.push(`${id}: ${fn} names source ${s.source}, which is not in sources`);
|
|
68
|
+
}
|
|
60
69
|
const overlap = (a, b) => a.record === b.record && ["attribute", ...CONTEXT].every((k) => a[k] === ANY || b[k] === ANY || a[k] === b[k]);
|
|
61
70
|
for (let i = 0; i < rules.length; i++) for (let j = i + 1; j < rules.length; j++)
|
|
62
71
|
if (overlap(rules[i], rules[j]) && specificity(rules[i]) === specificity(rules[j]) && rules[i].attribute === rules[j].attribute)
|
|
63
72
|
errors.push(`${id}: rules ${i + 1} and ${j + 1} (${rules[i].record}) can both hold and tie at the same specificity; narrow one`);
|
|
64
73
|
}
|
|
65
74
|
if (errors.length) { console.error(errors.join("\n")); process.exit(1); }
|
|
66
|
-
console.log(`${maps} source maps: every source has a connector, every record type a source, and no two matrix rules tie.`);
|
|
75
|
+
console.log(`${maps} source maps: every source has a connector, every record type a source, every function's systems are known, and no two matrix rules tie.`);
|
|
@@ -10,6 +10,7 @@ import { existsSync, readdirSync, readFileSync } from "node:fs";
|
|
|
10
10
|
import { join } from "node:path";
|
|
11
11
|
import { connect, sourceFor, sourcesOf } from "../knowledge/connectors/index.mjs";
|
|
12
12
|
import { resolve } from "../interface/router.mjs";
|
|
13
|
+
import { ventureFile } from "./venture-paths.mjs";
|
|
13
14
|
|
|
14
15
|
const dir = process.argv[2];
|
|
15
16
|
if (!dir) { console.error("usage: node schema/check-where-used.mjs <workspace or venture dir> [--json]"); process.exit(2); }
|
|
@@ -21,18 +22,18 @@ const at = (obj, pointer) => pointer.split("/").slice(1).map((k) => k.replace(/~
|
|
|
21
22
|
const errors = [], drift = [], unchecked = [];
|
|
22
23
|
let lists = 0, checked = 0;
|
|
23
24
|
for (const v of walk(dir)) {
|
|
24
|
-
if (!existsSync(
|
|
25
|
+
if (!existsSync(ventureFile(v, FILE))) continue;
|
|
25
26
|
lists++;
|
|
26
27
|
const id = JSON.parse(readFileSync(join(v, "venture.json"), "utf8")).id;
|
|
27
28
|
const ids = new Map(sourcesOf(v).sources.map((s) => [s.id, s]));
|
|
28
|
-
for (const fact of JSON.parse(readFileSync(
|
|
29
|
+
for (const fact of JSON.parse(readFileSync(ventureFile(v, FILE), "utf8")).facts) {
|
|
29
30
|
const where = `${id}: ${fact.id}`;
|
|
30
31
|
const { r, params } = resolve(fact.parent.route);
|
|
31
32
|
if (!r || r.kind !== "record" || r.collection === "list") { errors.push(`${where}: parent ${fact.parent.route} is not a record route`); continue; }
|
|
32
33
|
const context = r.collection === "item" && (r.record === "product" || r.record === "account") ? { [r.record]: params.id } : {};
|
|
33
34
|
const src = sourceFor(v, r.record, context);
|
|
34
35
|
const cands = (r.collection === "item" ? r.files.map((f) => f.replace("*", params.id)) : r.files).filter((f) => !f.includes("*"));
|
|
35
|
-
const path = cands.find((f) => existsSync(
|
|
36
|
+
const path = cands.find((f) => existsSync(ventureFile(v, f))) ?? cands[0];
|
|
36
37
|
if (src.connector !== "filesystem") { unchecked.push(`${where}: parent is in ${src.id}`); continue; }
|
|
37
38
|
const parent = await (await connect(src, v)).read(path);
|
|
38
39
|
const value = parent && at(JSON.parse(parent.content), fact.parent.pointer);
|