diff --git a/build/model.mjs b/build/model.mjs index 18f974e..370e427 100644 --- a/build/model.mjs +++ b/build/model.mjs @@ -21,6 +21,7 @@ import { fileURLToPath } from "node:url"; import { parseInstance, imagesOf } from "companygraph-meta-model/instance"; import { syncImages } from "@robertblust/design/images"; import { readInstance } from "./read.mjs"; +import { readSchemas } from "./schemas.mjs"; const ROOT = path.join(path.dirname(fileURLToPath(import.meta.url)), ".."); const { repo, commit } = JSON.parse(fs.readFileSync(path.join(ROOT, "source.json"), "utf8")); @@ -32,13 +33,14 @@ const SUB = "model/"; // The vendored core sits beside the container, and the parser reads the model against it: // R16 makes the declared type the only thing that decides which fields are edges, so the -// schemas travel with the pages they declare. -const CORE = "meta/core/"; - -const [files, schemas] = await Promise.all([ - readInstance({ repo, commit, sub: SUB }), - readInstance({ repo, commit, sub: CORE }), -]); +// schemas travel with the pages they declare. The packs the model's `.companygraph/manifest.json` +// lists are vendored beside core and read with it (`build/schemas.mjs`), or a pack's folders +// fail R13 as declared by no schema. A file read is the one file at that path, or undefined. +const read = async (sub, o) => { + const got = await readInstance({ repo, commit, sub }); + return o?.file ? got.get("") : got; +}; +const [files, schemas] = await Promise.all([readInstance({ repo, commit, sub: SUB }), readSchemas(read)]); // `repo` travels with the data because the stage draws more than one repository's model and the // file link has to point at the right one. `sub` is where these files sit in the model's own // repository, and the parser needs it: an entity's `path` is what a page turns into a link to diff --git a/build/read.mjs b/build/read.mjs index 305861e..5e49e30 100644 --- a/build/read.mjs +++ b/build/read.mjs @@ -18,6 +18,10 @@ function readLocal(dir, commit, sub) { } const root = path.join(dir, sub); const files = new Map(); + // An absent folder is no files, as it is on GitHub, and a path to one file is that file + // under the empty key, as the remote listing gives it. + if (!fs.existsSync(root)) return files; + if (fs.statSync(root).isFile()) return files.set("", fs.readFileSync(root, "utf8")); const walk = (d) => { for (const ent of fs.readdirSync(d, { withFileTypes: true })) { const p = path.join(d, ent.name); diff --git a/build/schemas.mjs b/build/schemas.mjs new file mode 100644 index 0000000..a3e7f3a --- /dev/null +++ b/build/schemas.mjs @@ -0,0 +1,29 @@ +// The schemas an instance is written against: its core, vendored at `/core/`, and the +// pack folders its `.companygraph/manifest.json` lists, vendored beside it at `//`. +// Core's files travel under bare keys and a pack's under `/`, which is how the +// parser tells them apart (R20) and how meta-model's own `readInstance` hands them over. +// `read(sub)` returns the files under one folder of the instance's repository with that +// prefix stripped, whether from a checkout or from GitHub. +export async function readSchemas(read) { + const where = ".companygraph/manifest.json"; + const text = await read(where, { file: true }); + let manifest = {}; + if (text !== undefined) { + try { manifest = JSON.parse(text); } + catch (e) { throw new Error(`${where} is not valid JSON: ${e.message}`); } + } + const packs = manifest.packs ?? []; + if (!Array.isArray(packs) || packs.some((p) => typeof p !== "string")) + throw new Error(`${where}: "packs" must be an array of pack names, got ${JSON.stringify(packs)}`); + const units = manifest.units ?? "meta"; + const schemas = new Map(await read(`${units}/core/`)); + for (const pack of packs) { + let files; + try { files = await read(`${units}/${pack}/`); } + catch (e) { if (e?.code !== "ENOENT") throw e; files = new Map(); } + if (files.size === 0) + throw new Error(`${where} lists the pack "${pack}", but ${units}/${pack}/ has no files in the instance`); + for (const [file, text] of files) schemas.set(`${pack}/${file}`, text); + } + return schemas; +} diff --git a/id/01a17143-220a-79fa-95e3-ab2939ad0a97/index.html b/id/01a17143-220a-79fa-95e3-ab2939ad0a97/index.html new file mode 100644 index 0000000..5250813 --- /dev/null +++ b/id/01a17143-220a-79fa-95e3-ab2939ad0a97/index.html @@ -0,0 +1,13 @@ + + + + + +SaaS + + + + +

Go on to SaaS.

+ + diff --git a/id/01a17143-31aa-7ef8-813f-4a1f19056896/index.html b/id/01a17143-31aa-7ef8-813f-4a1f19056896/index.html new file mode 100644 index 0000000..8312b83 --- /dev/null +++ b/id/01a17143-31aa-7ef8-813f-4a1f19056896/index.html @@ -0,0 +1,13 @@ + + + + + +id.blust.ch + + + + +

Go on to id.blust.ch.

+ + diff --git a/model.json b/model.json index b5d7381..40f39c8 100644 --- a/model.json +++ b/model.json @@ -1,5 +1,5 @@ { - "commit": "2a1852a7e17633fd75bde60aaa23b8ca756d8bf9", + "commit": "7829bf4e37e3ccb15cd2c18f796d46842168f7a1", "core": "0.69.0", "root": "GuestGraph", "rootId": "01a0d2e2-f788-788d-ac26-56e9ef94fdd2", @@ -172,6 +172,18 @@ "owner": null, "singular": false }, + { + "type": "system", + "folder": "systems", + "owner": null, + "singular": false + }, + { + "type": "system-kind", + "folder": "system-kinds", + "owner": null, + "singular": false + }, { "type": "track", "folder": "tracks", @@ -15809,6 +15821,81 @@ "owner": null, "path": "model/surfaces/mcp-registry-listing.md" }, + { + "id": "01a17143-220a-79fa-95e3-ab2939ad0a97", + "address": "system-kinds/saas", + "type": "system-kind", + "name": "SaaS", + "tagline": "Software another party runs and keeps, which GuestGraph's own software connects to.", + "fields": { + "id": "01a17143-220a-79fa-95e3-ab2939ad0a97", + "source": "Local", + "rank": "10", + "element": "application-component" + }, + "sections": [ + { + "heading": "What it means", + "text": "SaaS is software that another party deploys, runs and keeps, which GuestGraph reaches over that party's own interface and holds no configuration, secret or account of. Where it handles personal data, the party it runs with is a data processor, and the system's page names it. It is not software GuestGraph deploys itself, and not the platform such software runs on.", + "tables": [] + } + ], + "owner": null, + "path": "model/system-kinds/saas.md" + }, + { + "id": "01a17143-31aa-7ef8-813f-4a1f19056896", + "address": "systems/id-blust-ch", + "type": "system", + "name": "id.blust.ch", + "tagline": "Signs readers in to the chat on guestgraph.io with ZITADEL, the identity service blust.ch runs at https://id.blust.ch in its Google Cloud project blust-ch-mcp in the Zurich region for the chats on blust.ch, companygraph.io and guestgraph.io, where accounts are by invitation only, and issues the token from which the chat reads the models a reader may choose.", + "fields": { + "id": "01a17143-31aa-7ef8-813f-4a1f19056896", + "source": "Local", + "kind": "SaaS", + "owner": "Owner", + "processor": "Google Cloud", + "serves": [ + "Answering" + ] + }, + "sections": [ + { + "heading": "References", + "text": "", + "tables": [ + { + "caption": null, + "columns": [ + "What", + "URL" + ], + "rows": [ + [ + "The service's source, infrastructure and the owner's steps", + "https://github.com/robertblust/id-blust-ch" + ] + ] + } + ], + "table": { + "caption": null, + "columns": [ + "What", + "URL" + ], + "rows": [ + [ + "The service's source, infrastructure and the owner's steps", + "https://github.com/robertblust/id-blust-ch" + ] + ] + } + } + ], + "owner": null, + "path": "model/systems/id-blust-ch.md" + }, { "id": "01a0d2e3-bec0-7ddc-9a13-d048712143de", "address": "values/a-match-is-not-trusted-until-it-is-justified", @@ -21457,6 +21544,42 @@ "to": "01a0d44e-8da8-71f1-b1c7-f56d11bb8dee", "via": "surfaces", "attrs": {} + }, + { + "from": "01a17143-220a-79fa-95e3-ab2939ad0a97", + "to": "01a0d2e2-f788-7d70-903e-402e3f765e65", + "via": "source", + "attrs": {} + }, + { + "from": "01a17143-31aa-7ef8-813f-4a1f19056896", + "to": "01a17143-220a-79fa-95e3-ab2939ad0a97", + "via": "kind", + "attrs": {} + }, + { + "from": "01a17143-31aa-7ef8-813f-4a1f19056896", + "to": "01a0d2e3-bec0-7fd9-81f6-bcd405d5b168", + "via": "owner", + "attrs": {} + }, + { + "from": "01a17143-31aa-7ef8-813f-4a1f19056896", + "to": "01a108de-0195-7bc3-ad53-a16299aa585b", + "via": "processor", + "attrs": {} + }, + { + "from": "01a17143-31aa-7ef8-813f-4a1f19056896", + "to": "01a0d360-9b98-7ff9-bc00-0f521ad28bdb", + "via": "serves", + "attrs": {} + }, + { + "from": "01a17143-31aa-7ef8-813f-4a1f19056896", + "to": "01a0d2e2-f788-7d70-903e-402e3f765e65", + "via": "source", + "attrs": {} } ], "repo": "guestgraph/mental-model" diff --git a/model/og.sha b/model/og.sha index 686b751..ec062c3 100644 --- a/model/og.sha +++ b/model/og.sha @@ -1 +1 @@ -093697d65afe684aebdad91efa15908544cbbfbc34c40763f5e4b54bf52c219b +23d006dac6304298cd922a38ad5f1d25fdd33cc311a9ac69bf0f38807b4dbb94 diff --git a/principles/index.html b/principles/index.html index 5e3c659..d4a0347 100644 --- a/principles/index.html +++ b/principles/index.html @@ -320,7 +320,7 @@

The core stays open

-

Generated from guestgraph/mental-model@2a1852a — model/vision.md and model/values/. That repository is an instance of CompanyGraph, a meta-model for describing a company as a graph of Markdown.

+

Generated from guestgraph/mental-model@7829bf4 — model/vision.md and model/values/. That repository is an instance of CompanyGraph, a meta-model for describing a company as a graph of Markdown.

diff --git a/principles/og.sha b/principles/og.sha index 120c41e..7a69bad 100644 --- a/principles/og.sha +++ b/principles/og.sha @@ -1 +1 @@ -2be0977685d73bfdc01b89b4b30cfaedfc89f3ae24ca004fa4de538520eceedf +f047a22fea63d40826443819656293714f6f9f6b9243d3c8f59cc5cf876c0f46 diff --git a/privacy/index.html b/privacy/index.html index d756390..f47aaff 100644 --- a/privacy/index.html +++ b/privacy/index.html @@ -331,7 +331,7 @@

Where your data goes

-
On guestgraph.io
Your visit
@2a1852a
+
On guestgraph.io
Your visit
@7829bf4
  • Kept in your browseruntil you clear it · never sent on its own
    diff --git a/privacy/og.sha b/privacy/og.sha index ff979ea..b16fc35 100644 --- a/privacy/og.sha +++ b/privacy/og.sha @@ -1 +1 @@ -3afabb4c8e2772ea7017efa3bbda40f798f05fc9ad54808bc64e48e2230368f4 +fbe13e54136c15a701ade47a146256615c7e15dcf613ee470d8b84b2f2d67b1f diff --git a/processes/og.sha b/processes/og.sha index 822b89a..868629e 100644 --- a/processes/og.sha +++ b/processes/og.sha @@ -1 +1 @@ -fed94390de43680124dec962ed31709a69224841544382629ad6f9425647301e +0ab7aaac8bf73218915b81b67c44c80704adf887d93c9e73c2b28ab79e47e86e diff --git a/source.json b/source.json index 7433cd5..468b652 100644 --- a/source.json +++ b/source.json @@ -1 +1 @@ -{"repo":"guestgraph/mental-model","commit":"2a1852a7e17633fd75bde60aaa23b8ca756d8bf9"} \ No newline at end of file +{"repo":"guestgraph/mental-model","commit":"7829bf4e37e3ccb15cd2c18f796d46842168f7a1"} \ No newline at end of file diff --git a/surfaces/index.html b/surfaces/index.html index 3e21e77..daabd8b 100644 --- a/surfaces/index.html +++ b/surfaces/index.html @@ -341,7 +341,7 @@

    -
    The model
    mental-model
    @2a1852a
    +
    The model
    mental-model
    @7829bf4
    • guestgraph.github.iobuild
      diff --git a/surfaces/og.sha b/surfaces/og.sha index 76036a0..b66f51c 100644 --- a/surfaces/og.sha +++ b/surfaces/og.sha @@ -1 +1 @@ -020e3477208a9995d4f8d1326193fe27e6717e1be12b8f79b80f24bc12941bb9 +b46c4a97398000b9e140d99772de7c7e9d1f109839ed71dabf7fd2e8b59f37ef