Spaces:
Running
Running
| // core/semantic.js — the SEMANTIC SKIN (gap C2): every κ-object the substrate produces carries an | |
| // open-semantic-web type, so humans, agents, and W3C validators all read it the same way. Closes the | |
| // weakest audit axis (κ names objects, but few declared a W3C @type). Two W3C standards, both native: | |
| // • JSON-LD / schema.org — WHAT a thing is (a skill is a schema:HowTo; a file is a schema:DigitalDocument) | |
| // • PROV-O — HOW it came to be (the κ-chain becomes prov:wasRevisionOf links) | |
| // Pure, dependency-free, browser+node. The result round-trips through any JSON-LD/RDF tool. | |
| export const HOLO_CONTEXT = { | |
| schema: "https://schema.org/", | |
| prov: "http://www.w3.org/ns/prov#", | |
| holo: "https://hologram.foundation/ns/", | |
| "@vocab": "https://schema.org/", | |
| }; | |
| // schema.org type for a substrate object kind (the canonical mapping, L2 — fixed at the ingest boundary) | |
| const TYPE_FOR = { | |
| skill: "HowTo", // a reusable procedure = schema:HowTo (steps + when-to-use) | |
| file: "DigitalDocument", | |
| app: "SoftwareApplication", | |
| model: "SoftwareSourceCode", | |
| conversation: "Conversation", | |
| receipt: "CreativeWork", // a sealed work record: schema:CreativeWork that is also a prov:Entity | |
| }; | |
| // wrap any object as a typed, content-addressed JSON-LD node. `kappa` is its did:holo (the @id — | |
| // content IS the identity, Law L1). `prov` is the κ-chain → prov:wasRevisionOf links (PROV-O). | |
| export function asLinkedData({ kind, kappa, props = {}, prov = [] }) { | |
| const t = TYPE_FOR[kind] || "Thing"; | |
| const node = { | |
| "@context": HOLO_CONTEXT, | |
| "@id": kappa, // content-derived identity (no location — Law L1) | |
| "@type": Array.isArray(t) ? t : ["schema:" + t, "prov:Entity"], // schema kind + PROV entity | |
| ...props, | |
| }; | |
| if (prov.length >= 2) { // the version chain → PROV-O revision links | |
| const cur = prov[prov.length - 1], parent = prov[prov.length - 2]; | |
| node["prov:wasRevisionOf"] = { "@id": parent.kappa }; | |
| node["holo:version"] = cur.v; | |
| } | |
| node["prov:wasDerivedFrom"] = prov.length ? prov.map((p) => ({ "@id": p.kappa })) : undefined; | |
| return node; | |
| } | |
| // a skill → schema:HowTo (steps become schema:HowToStep), with its PROV-O revision chain. | |
| export function skillAsHowTo({ name, description, instructions, kappa, prov = [] }) { | |
| const steps = String(instructions || "").split("\n").map((s) => s.trim()).filter(Boolean) | |
| .map((text, i) => ({ "@type": "schema:HowToStep", "schema:position": i + 1, "schema:text": text.replace(/^\d+[.)]\s*/, "") })); | |
| return asLinkedData({ kind: "skill", kappa, prov, props: { "schema:name": name, "schema:description": description, "schema:step": steps } }); | |
| } | |
| // extension → IANA media type (schema:encodingFormat). Small, common set; default text/plain. | |
| const MEDIA = { html: "text/html", htm: "text/html", js: "text/javascript", mjs: "text/javascript", json: "application/json", jsonld: "application/ld+json", css: "text/css", md: "text/markdown", txt: "text/plain", py: "text/x-python", svg: "image/svg+xml", wasm: "application/wasm", gz: "application/gzip" }; | |
| const mediaOf = (name) => MEDIA[String(name).toLowerCase().split(".").pop()] || "text/plain"; | |
| // a workspace file → schema:DigitalDocument (name + media type + byte size), content-addressed (L1). | |
| export function fileAsDocument({ path, kappa, bytes = 0, mediaType, prov = [] }) { | |
| const name = String(path).split("/").filter(Boolean).pop() || String(path); | |
| return asLinkedData({ kind: "file", kappa, prov, props: { | |
| "schema:name": name, "schema:identifier": kappa, | |
| "schema:encodingFormat": mediaType || mediaOf(name), "schema:contentSize": bytes } }); | |
| } | |
| // a built app → schema:SoftwareApplication (a self-contained WebGPU/Web app object). | |
| export function appAsSoftware({ name, kappa, bytes = 0, prov = [] }) { | |
| return asLinkedData({ kind: "app", kappa, prov, props: { | |
| "schema:name": String(name).replace(/\.html$/i, ""), "schema:identifier": kappa, | |
| "schema:applicationCategory": "WebApplication", "schema:operatingSystem": "Any (WebGPU/Web)", | |
| "schema:fileSize": bytes } }); | |
| } | |
| // a loaded model → schema:SoftwareSourceCode (the κ-object that runs on the GPU). | |
| export function modelAsSource({ name, kappa, family, params, format, prov = [] }) { | |
| return asLinkedData({ kind: "model", kappa, prov, props: { | |
| "schema:name": name, "schema:identifier": kappa, "schema:programmingLanguage": "WGSL/WebGPU", | |
| "holo:family": family, "holo:parameters": params, "holo:format": format } }); | |
| } | |
| // one dispatcher so any producer can type any κ-object: linkedDataFor("app", {...}) etc. | |
| export function linkedDataFor(kind, props = {}) { | |
| switch (kind) { | |
| case "skill": return skillAsHowTo(props); | |
| case "file": return fileAsDocument(props); | |
| case "app": return appAsSoftware(props); | |
| case "model": return modelAsSource(props); | |
| default: return asLinkedData({ kind, kappa: props.kappa, prov: props.prov || [], props: props.props || props }); | |
| } | |
| } | |
| // VERIFY (the gate): an object is semantically valid iff it has @context, an @id (κ), and a @type | |
| // carrying both a schema.org kind and PROV-O lineage. Returns { ok, types, hasId, hasContext }. | |
| export function verifySemantic(node) { | |
| const types = [].concat(node && node["@type"] || []); | |
| const hasSchema = types.some((t) => /^schema:|^https:\/\/schema\.org\//.test(t)); | |
| const hasProv = types.some((t) => /^prov:/.test(t)) || !!node["prov:wasDerivedFrom"]; | |
| const hasId = typeof node?.["@id"] === "string" && node["@id"].startsWith("did:holo:"); | |
| const hasContext = !!node?.["@context"]; | |
| return { ok: hasSchema && hasId && hasContext, hasSchema, hasProv, hasId, hasContext, types }; | |
| } | |