githolon 0.33.0 → 0.34.1

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.
Files changed (2) hide show
  1. package/dist/cli.mjs +1351 -62
  2. package/package.json +4 -3
package/dist/cli.mjs CHANGED
@@ -924,20 +924,45 @@ async function wsRetire(ws, opts) {
924
924
  const cloud = cloudBase(opts.cloud);
925
925
  const { signAndPostGovernance: signAndPostGovernance2, resolveGovernancePrincipal: resolveGovernancePrincipal2 } = await Promise.resolve().then(() => (init_governance(), governance_exports));
926
926
  const parent = opts.parent ?? "root";
927
+ let record;
928
+ try {
929
+ const q = await fetch(`${cloud}/v2/workspaces/${parent}/workspaceByName?name=${encodeURIComponent(ws)}`);
930
+ const qd = await q.json().catch(() => void 0);
931
+ const rows = qd?.ok === true ? qd.rows ?? [] : [];
932
+ record = rows.find((r) => r.data?.status !== "retired" && r.data?.status !== "reclaimed") ?? rows[0];
933
+ } catch {
934
+ }
935
+ const recordId = record?.id;
936
+ if (recordId === void 0) {
937
+ err3(
938
+ `retire '${ws}': no CloudWorkspace record named '${ws}' in ${parent}'s registry \u2014 nothing lawful to retire.
939
+ (a workspace born via the OPEN lane \u2014 bare POST /v2/workspaces/${ws} \u2014 has no registry row;
940
+ registered births ride \`githolon ws create ${ws} --via ${parent}\`. An unregistered leak needs a custodian sweep.)`
941
+ );
942
+ return 1;
943
+ }
927
944
  let v;
928
945
  try {
929
946
  const principal = resolveGovernancePrincipal2(opts);
930
- const payload = { workspaceId: ws, retiredBy: principal, retiredAt: Date.now() };
931
- v = await signAndPostGovernance2(parent, "retireOwnWorkspace", payload, opts);
932
- if (!v.ok && v.status === 422) {
933
- v = await signAndPostGovernance2(parent, "retireWorkspace", payload, opts);
934
- }
947
+ const payload = { workspaceId: recordId, retiredBy: principal, retiredAt: Date.now() };
948
+ const bare = (s) => s.startsWith("user:") ? s.slice(5) : s;
949
+ const creator = record?.data?.createdBy ?? record?.data?.principal ?? "";
950
+ const own = creator !== "" && bare(creator) === bare(principal);
951
+ v = await signAndPostGovernance2(parent, own ? "retireOwnWorkspace" : "retireWorkspace", payload, opts);
935
952
  } catch (e) {
936
953
  err3(e.message);
937
954
  return 1;
938
955
  }
939
956
  if (!v.ok) {
940
957
  err3(`retire '${ws}' refused (${v.status}): ${typeof v.body === "string" ? v.body : JSON.stringify(v.body)}`);
958
+ if (JSON.stringify(v.body ?? "").includes("retire-own-only")) {
959
+ err3(
960
+ `note: you appear to BE '${ws}'s creator \u2014 the parent's deployed governance law may predate the
961
+ 0.34.1 retireOwnWorkspace creator-normalization fix (the 'user:' subject prefix vs the bare uid).
962
+ A governance re-deploy to ${parent} fixes the self-service lane; meanwhile an ADMIN can retire it:
963
+ githolon ws retire ${ws} --as <admin-uid>`
964
+ );
965
+ }
941
966
  return 1;
942
967
  }
943
968
  out3(`\u2713 workspace ${ws} retired via signed governance offer to ${parent} on ${cloud}`);
@@ -1143,7 +1168,7 @@ function makeGitFs(preopen, mount, { File: File3, Directory: Directory3 }) {
1143
1168
  const rel = p.slice(mount.length + 1).replace(/\/+$/, "");
1144
1169
  return rel === "" ? [] : rel.split("/");
1145
1170
  }
1146
- function resolve3(ps) {
1171
+ function resolve4(ps) {
1147
1172
  let cur = root;
1148
1173
  for (const part of ps) {
1149
1174
  if (!isDir(cur)) return null;
@@ -1153,11 +1178,11 @@ function makeGitFs(preopen, mount, { File: File3, Directory: Directory3 }) {
1153
1178
  }
1154
1179
  return cur;
1155
1180
  }
1156
- const parent = (ps) => resolve3(ps.slice(0, -1));
1181
+ const parent = (ps) => resolve4(ps.slice(0, -1));
1157
1182
  const leaf = (ps) => ps[ps.length - 1];
1158
1183
  const promises = {
1159
1184
  async readFile(p, opts) {
1160
- const n = resolve3(parts(p));
1185
+ const n = resolve4(parts(p));
1161
1186
  if (n === null) throw fsErr("ENOENT", p);
1162
1187
  if (isDir(n)) throw fsErr("EISDIR", p);
1163
1188
  const data = n.data ?? new Uint8Array(0);
@@ -1176,7 +1201,7 @@ function makeGitFs(preopen, mount, { File: File3, Directory: Directory3 }) {
1176
1201
  if (!isDir(par) || !par.contents.delete(leaf(ps))) throw fsErr("ENOENT", p);
1177
1202
  },
1178
1203
  async readdir(p) {
1179
- const n = resolve3(parts(p));
1204
+ const n = resolve4(parts(p));
1180
1205
  if (n === null) throw fsErr("ENOENT", p);
1181
1206
  if (!isDir(n)) throw fsErr("ENOTDIR", p);
1182
1207
  return [...n.contents.keys()];
@@ -1196,7 +1221,7 @@ function makeGitFs(preopen, mount, { File: File3, Directory: Directory3 }) {
1196
1221
  return promises.lstat(p);
1197
1222
  },
1198
1223
  async lstat(p) {
1199
- const n = resolve3(parts(p));
1224
+ const n = resolve4(parts(p));
1200
1225
  if (n === null) throw fsErr("ENOENT", p);
1201
1226
  const dir = isDir(n);
1202
1227
  return {
@@ -1245,13 +1270,13 @@ var init_tree = __esm({
1245
1270
  function concatBytes(chunks) {
1246
1271
  let n = 0;
1247
1272
  for (const c of chunks) n += c.length;
1248
- const out11 = new Uint8Array(n);
1273
+ const out12 = new Uint8Array(n);
1249
1274
  let o = 0;
1250
1275
  for (const c of chunks) {
1251
- out11.set(c, o);
1276
+ out12.set(c, o);
1252
1277
  o += c.length;
1253
1278
  }
1254
- return out11;
1279
+ return out12;
1255
1280
  }
1256
1281
  function pkt(payload) {
1257
1282
  const body = typeof payload === "string" ? enc2.encode(payload) : payload;
@@ -1404,13 +1429,13 @@ async function sha256hex(t) {
1404
1429
  function osEntropyBuffer(n) {
1405
1430
  const bytes = new Uint8Array(n * 8);
1406
1431
  for (let o = 0; o < bytes.length; o += 65536) crypto.getRandomValues(bytes.subarray(o, Math.min(o + 65536, bytes.length)));
1407
- const out11 = new Array(n), T = 2 ** 53;
1432
+ const out12 = new Array(n), T = 2 ** 53;
1408
1433
  for (let i = 0; i < n; i++) {
1409
1434
  let z = 0n;
1410
1435
  for (let b = 7; b >= 0; b--) z = z << 8n | BigInt(bytes[i * 8 + b]);
1411
- out11[i] = Number(z >> 11n) / T;
1436
+ out12[i] = Number(z >> 11n) / T;
1412
1437
  }
1413
- return out11;
1438
+ return out12;
1414
1439
  }
1415
1440
  function stringifyBig(o) {
1416
1441
  return JSON.stringify(o, (_k, v) => typeof v === "bigint" ? `@@B:${v}@@` : v).replace(/"@@B:(\d+)@@"/g, "$1");
@@ -2112,11 +2137,1198 @@ var init_proof_offline = __esm({
2112
2137
  }
2113
2138
  });
2114
2139
 
2140
+ // src/scene_projector.ts
2141
+ import { createHash as createHash4 } from "node:crypto";
2142
+ function fnv1a32(s) {
2143
+ let h = 2166136261;
2144
+ for (let i = 0; i < s.length; i++) {
2145
+ h ^= s.charCodeAt(i);
2146
+ h = Math.imul(h, 16777619);
2147
+ }
2148
+ return h >>> 0;
2149
+ }
2150
+ function hslToRgb(h, s, l) {
2151
+ const hue = (h % 1 + 1) % 1;
2152
+ const q = l < 0.5 ? l * (1 + s) : l + s - l * s;
2153
+ const p = 2 * l - q;
2154
+ const chan = (t0) => {
2155
+ let t = (t0 % 1 + 1) % 1;
2156
+ if (t < 1 / 6) return p + (q - p) * 6 * t;
2157
+ if (t < 1 / 2) return q;
2158
+ if (t < 2 / 3) return p + (q - p) * (2 / 3 - t) * 6;
2159
+ return p;
2160
+ };
2161
+ const r32 = (v) => Math.round(v * 1e3) / 1e3;
2162
+ return [r32(chan(hue + 1 / 3)), r32(chan(hue)), r32(chan(hue - 1 / 3))];
2163
+ }
2164
+ function icosphere(subdiv) {
2165
+ const t = (1 + Math.sqrt(5)) / 2;
2166
+ let points = [
2167
+ [-1, t, 0],
2168
+ [1, t, 0],
2169
+ [-1, -t, 0],
2170
+ [1, -t, 0],
2171
+ [0, -1, t],
2172
+ [0, 1, t],
2173
+ [0, -1, -t],
2174
+ [0, 1, -t],
2175
+ [t, 0, -1],
2176
+ [t, 0, 1],
2177
+ [-t, 0, -1],
2178
+ [-t, 0, 1]
2179
+ ].map(norm);
2180
+ let faces = [
2181
+ [0, 11, 5],
2182
+ [0, 5, 1],
2183
+ [0, 1, 7],
2184
+ [0, 7, 10],
2185
+ [0, 10, 11],
2186
+ [1, 5, 9],
2187
+ [5, 11, 4],
2188
+ [11, 10, 2],
2189
+ [10, 7, 6],
2190
+ [7, 1, 8],
2191
+ [3, 9, 4],
2192
+ [3, 4, 2],
2193
+ [3, 2, 6],
2194
+ [3, 6, 8],
2195
+ [3, 8, 9],
2196
+ [4, 9, 5],
2197
+ [2, 4, 11],
2198
+ [6, 2, 10],
2199
+ [8, 6, 7],
2200
+ [9, 8, 1]
2201
+ ];
2202
+ for (let s = 0; s < subdiv; s++) {
2203
+ const midCache = /* @__PURE__ */ new Map();
2204
+ const mid = (a, b) => {
2205
+ const key = a < b ? `${a}_${b}` : `${b}_${a}`;
2206
+ const hit = midCache.get(key);
2207
+ if (hit !== void 0) return hit;
2208
+ const idx = points.length;
2209
+ points = [...points, norm(mul(add(points[a], points[b]), 0.5))];
2210
+ midCache.set(key, idx);
2211
+ return idx;
2212
+ };
2213
+ const next = [];
2214
+ for (const [a, b, c] of faces) {
2215
+ const ab = mid(a, b), bc = mid(b, c), ca = mid(c, a);
2216
+ next.push([a, ab, ca], [b, bc, ab], [c, ca, bc], [ab, bc, ca]);
2217
+ }
2218
+ faces = next;
2219
+ }
2220
+ return { points, indices: faces.flat() };
2221
+ }
2222
+ function boxMesh() {
2223
+ const p = [];
2224
+ for (const z of [-0.5, 0.5]) for (const y of [-0.5, 0.5]) for (const x of [-0.5, 0.5]) p.push([x, y, z]);
2225
+ const quads = [
2226
+ [0, 2, 3, 1],
2227
+ // -z
2228
+ [4, 5, 7, 6],
2229
+ // +z
2230
+ [0, 1, 5, 4],
2231
+ // -y
2232
+ [2, 6, 7, 3],
2233
+ // +y
2234
+ [0, 4, 6, 2],
2235
+ // -x
2236
+ [1, 3, 7, 5]
2237
+ // +x
2238
+ ];
2239
+ const indices = [];
2240
+ for (const [a, b, c, d] of quads) indices.push(a, b, c, a, c, d);
2241
+ return { points: p, indices };
2242
+ }
2243
+ function prism(n) {
2244
+ const points = [];
2245
+ for (const y of [-0.5, 0.5]) {
2246
+ for (let i = 0; i < n; i++) {
2247
+ const a = 2 * Math.PI * i / n;
2248
+ points.push([Math.cos(a), y, Math.sin(a)]);
2249
+ }
2250
+ }
2251
+ const indices = [];
2252
+ for (let i = 0; i < n; i++) {
2253
+ const j = (i + 1) % n;
2254
+ indices.push(i, j, n + j, i, n + j, n + i);
2255
+ }
2256
+ const bottomCenter = points.length;
2257
+ points.push([0, -0.5, 0]);
2258
+ const topCenter = points.length;
2259
+ points.push([0, 0.5, 0]);
2260
+ for (let i = 0; i < n; i++) {
2261
+ const j = (i + 1) % n;
2262
+ indices.push(bottomCenter, j, i);
2263
+ indices.push(topCenter, n + i, n + j);
2264
+ }
2265
+ return { points, indices };
2266
+ }
2267
+ function facetedPolyhedron(sides, elongation = 1) {
2268
+ const n = Math.max(3, Math.floor(sides));
2269
+ const points = [[0, elongation, 0], [0, -elongation, 0]];
2270
+ for (let i = 0; i < n; i++) {
2271
+ const a = 2 * Math.PI * i / n;
2272
+ points.push([Math.cos(a), 0, Math.sin(a)]);
2273
+ }
2274
+ const indices = [];
2275
+ for (let i = 0; i < n; i++) {
2276
+ const a = 2 + i, b = 2 + (i + 1) % n;
2277
+ indices.push(0, b, a);
2278
+ indices.push(1, a, b);
2279
+ }
2280
+ return { points, indices };
2281
+ }
2282
+ function pinLeaf() {
2283
+ const n = 10;
2284
+ const ringY = 1;
2285
+ const ringR = 0.34;
2286
+ const points = [[0, 0, 0]];
2287
+ for (let i = 0; i < n; i++) {
2288
+ const a = 2 * Math.PI * i / n;
2289
+ points.push([Math.cos(a) * ringR, ringY, Math.sin(a) * ringR]);
2290
+ }
2291
+ const ringCenter = points.length;
2292
+ points.push([0, ringY, 0]);
2293
+ const indices = [];
2294
+ for (let i = 0; i < n; i++) {
2295
+ const a = 1 + i, b = 1 + (i + 1) % n;
2296
+ indices.push(0, b, a);
2297
+ indices.push(ringCenter, a, b);
2298
+ }
2299
+ const head = icosphere(1);
2300
+ const headR = 0.42, headY = 1.28;
2301
+ const base = points.length;
2302
+ for (const p of head.points) points.push([p[0] * headR, p[1] * headR + headY, p[2] * headR]);
2303
+ for (const i of head.indices) indices.push(base + i);
2304
+ return { points, indices };
2305
+ }
2306
+ function segmentStrip(a, b, width) {
2307
+ const d = sub(b, a);
2308
+ const dn = norm(d);
2309
+ const ref = Math.abs(dn[1]) < 0.9 ? [0, 1, 0] : [1, 0, 0];
2310
+ const u = norm(cross(dn, ref));
2311
+ const v = norm(cross(dn, u));
2312
+ const hu = mul(u, width / 2);
2313
+ const hv = mul(v, width / 2);
2314
+ const corners = (p) => [
2315
+ add(add(p, hu), hv),
2316
+ add(sub(p, hu), hv),
2317
+ sub(sub(p, hu), hv),
2318
+ sub(add(p, hu), hv)
2319
+ ];
2320
+ const points = [...corners(a), ...corners(b)];
2321
+ const quads = [
2322
+ [0, 1, 2, 3],
2323
+ // a cap
2324
+ [4, 7, 6, 5],
2325
+ // b cap
2326
+ [0, 4, 5, 1],
2327
+ [1, 5, 6, 2],
2328
+ [2, 6, 7, 3],
2329
+ [3, 7, 4, 0]
2330
+ ];
2331
+ const indices = [];
2332
+ for (const [x, y, z, w] of quads) indices.push(x, y, z, x, z, w);
2333
+ return { points, indices };
2334
+ }
2335
+ function sortDeep(v) {
2336
+ if (Array.isArray(v)) return v.map(sortDeep);
2337
+ if (v !== null && typeof v === "object") {
2338
+ return Object.fromEntries(
2339
+ Object.keys(v).sort().map((k) => [k, sortDeep(v[k])])
2340
+ );
2341
+ }
2342
+ return v;
2343
+ }
2344
+ function canonicalStructure(level, def, voIndex) {
2345
+ if (level === "aggregate") {
2346
+ const schema = def ?? {};
2347
+ const kinds = Object.values(schema).map((driver) => JSON.stringify(sortDeep(driver))).sort();
2348
+ return JSON.stringify({ level: "aggregate", fields: kinds });
2349
+ }
2350
+ if (level === "directive") {
2351
+ const d = def ?? {};
2352
+ return JSON.stringify({
2353
+ level: "directive",
2354
+ marker: d.marker ?? "",
2355
+ reads: (d.reads ?? []).length,
2356
+ requires: (d.requires ?? []).length
2357
+ });
2358
+ }
2359
+ const vo = def;
2360
+ if (vo.geometry !== void 0 && vo.fields === void 0) {
2361
+ return JSON.stringify({ level: "valueObject", geometry: vo.geometry });
2362
+ }
2363
+ const fields = Object.values(vo.fields ?? {}).map((f) => {
2364
+ const child = f.ref !== void 0 ? voIndex?.get(f.ref) : void 0;
2365
+ const childHash = child !== void 0 ? sha256Hex(canonicalStructure("valueObject", child, voIndex)) : void 0;
2366
+ return JSON.stringify(
2367
+ sortDeep({
2368
+ type: f.type ?? "",
2369
+ ...f.optional === true ? { optional: true } : {},
2370
+ ...f.array === true ? { array: true } : {},
2371
+ ...f.geometry !== void 0 ? { geometry: f.geometry } : {},
2372
+ ...childHash !== void 0 ? { child: childHash } : {}
2373
+ })
2374
+ );
2375
+ }).sort();
2376
+ return JSON.stringify({ level: "valueObject", fields });
2377
+ }
2378
+ function glyphSignature(structJson, name, partCount) {
2379
+ const h = sha256Hex(structJson);
2380
+ const hueBase = parseInt(h.slice(0, 8), 16) / 4294967296;
2381
+ const tint = fnv1a32(name) % 1e3 / 1e3 * 0.08;
2382
+ const hue = (hueBase + tint) % 1;
2383
+ const facetCount = 4 + parseInt(h.slice(8, 10), 16) % 5;
2384
+ const elongation = Math.round((0.8 + parseInt(h.slice(10, 12), 16) / 255 * 0.5) * 1e3) / 1e3;
2385
+ const size = Math.round((1 + Math.log2(1 + Math.max(0, partCount)) * 0.18) * 1e3) / 1e3;
2386
+ return { structureHash: h, facetCount, hue, hueBase, elongation, size, color: hslToRgb(hue, 0.62, 0.6) };
2387
+ }
2388
+ function packageLayers(packageUsda) {
2389
+ const m = /nomos:usdJsonHex = "([0-9a-f]+)"/.exec(packageUsda);
2390
+ if (!m) throw new Error("a chain-installed package carries no nomos:usdJsonHex \u2014 not an openusd-ir package");
2391
+ return JSON.parse(Buffer.from(m[1], "hex").toString("utf8")).layers;
2392
+ }
2393
+ function lawInstallsFromChain(entries) {
2394
+ const installs = [];
2395
+ for (let t = 0; t < entries.length; t++) {
2396
+ const p = JSON.parse(entries[t].intent).payload;
2397
+ if (p?.directiveId !== "installDomain" || typeof p.payload?.packageUsda !== "string") continue;
2398
+ installs.push({
2399
+ frame: t,
2400
+ domainHash: typeof p.payload.domainHash === "string" ? p.payload.domainHash : "",
2401
+ layers: packageLayers(p.payload.packageUsda)
2402
+ });
2403
+ }
2404
+ return installs;
2405
+ }
2406
+ function intentKind(directiveId) {
2407
+ if (directiveId === "installDomain") return "install";
2408
+ if (/order/i.test(directiveId)) return "order";
2409
+ if (/receipt/i.test(directiveId)) return "receipt";
2410
+ return "offer";
2411
+ }
2412
+ function nodePosition(domain, kind, name) {
2413
+ const angle = seededAngle(`domain:${domain}`) - Math.PI / 2;
2414
+ const boughRadius = 2;
2415
+ const center = [Math.cos(angle) * boughRadius, 0.35 + Math.sin(angle) * boughRadius * 0.55, 2.2];
2416
+ const outward0 = [center[0], center[1] - 0.35, 0];
2417
+ const outward = norm(outward0[0] * outward0[0] + outward0[1] * outward0[1] < 0.01 ? [1, 0.2, 0] : outward0);
2418
+ const lateral = [-outward[1], outward[0], 0];
2419
+ const place = (id2, forward, radius, lift) => {
2420
+ const spreadAngle = seededAngle(id2, Math.PI * 1.5) - Math.PI * 0.75;
2421
+ const off = add(
2422
+ add(mul(outward, radius * (0.55 + 0.45 * Math.abs(Math.cos(spreadAngle)))), mul(lateral, Math.sin(spreadAngle) * radius)),
2423
+ [0, lift, forward]
2424
+ );
2425
+ return round3(add(center, off));
2426
+ };
2427
+ const id = `${kind}:${domain}:${name}`;
2428
+ if (kind === "aggregate") return place(id, 1.15, 0.95, 0.15);
2429
+ if (kind === "directive") return place(id, 2.3, 1.35, 0.35);
2430
+ if (kind === "relationType") return place(id, 0.7, 1.15, -0.75);
2431
+ if (kind === "geoField") return place(id, 1.6, 1.05, -0.35);
2432
+ return place(id, 0.7, 0.8, -1.15);
2433
+ }
2434
+ function buildSceneGraph(installs) {
2435
+ const nodes = [];
2436
+ const edges = [];
2437
+ const layersByPath = /* @__PURE__ */ new Map();
2438
+ for (const inst of installs) {
2439
+ for (const layer of inst.layers) {
2440
+ const list = layersByPath.get(layer.path) ?? [];
2441
+ list.push({ frame: inst.frame, layer });
2442
+ layersByPath.set(layer.path, list);
2443
+ }
2444
+ }
2445
+ for (const path of [...layersByPath.keys()].sort()) {
2446
+ const gens = layersByPath.get(path);
2447
+ const domain = path.split("/").pop() ?? path;
2448
+ for (let g = 0; g < gens.length; g++) {
2449
+ const { frame, layer } = gens[g];
2450
+ const wither = g + 1 < gens.length ? gens[g + 1].frame : void 0;
2451
+ if (wither === frame) continue;
2452
+ const suffix = `f${frame}`;
2453
+ const voIndex = new Map(
2454
+ (layer.nomosValueObjects?.valueObjects ?? []).map((vo) => [vo.name, vo])
2455
+ );
2456
+ const nodeIds = /* @__PURE__ */ new Map();
2457
+ const pushNode = (kind, name, glyph, scale) => {
2458
+ const id = `${kind}:${domain}:${name}`;
2459
+ nodeIds.set(`${kind}:${name}`, id);
2460
+ nodes.push({
2461
+ id,
2462
+ primName: `${kind}_${sanitize(domain)}_${sanitize(name)}_${suffix}`,
2463
+ domain,
2464
+ name,
2465
+ kind,
2466
+ glyph,
2467
+ pos: nodePosition(domain, kind, name),
2468
+ scale,
2469
+ born: frame,
2470
+ wither
2471
+ });
2472
+ };
2473
+ const aggs = layer.prims.filter((p) => p.kind === "aggregate").sort((a, b) => (a.type ?? "") < (b.type ?? "") ? -1 : 1);
2474
+ for (const p of aggs) {
2475
+ const name = bareTypeLabel(p.type ?? p.path.split("/").pop() ?? "?");
2476
+ const struct = canonicalStructure("aggregate", p.schema ?? {});
2477
+ const glyph = glyphSignature(struct, name, Object.keys(p.schema ?? {}).length);
2478
+ pushNode("aggregate", name, glyph, 0.24 * glyph.size);
2479
+ }
2480
+ const dirs = layer.prims.filter((p) => p.kind === "directive").sort((a, b) => a.path < b.path ? -1 : 1);
2481
+ for (const p of dirs) {
2482
+ const name = p.path.split("/").pop() ?? "?";
2483
+ const struct = canonicalStructure("directive", p);
2484
+ const glyph = glyphSignature(struct, name, (p.requires ?? []).length + (p.reads ?? []).length + 1);
2485
+ pushNode("directive", name, glyph, 0.16 * glyph.size);
2486
+ }
2487
+ for (const rt of Object.keys(layer.nomosRelations?.types ?? {}).sort()) {
2488
+ const struct = JSON.stringify({ level: "relationType" });
2489
+ pushNode("relationType", rt, glyphSignature(struct, rt, 1), 0.1);
2490
+ }
2491
+ for (const vo of (layer.nomosValueObjects?.valueObjects ?? []).slice().sort((a, b) => a.name < b.name ? -1 : 1)) {
2492
+ const struct = canonicalStructure("valueObject", vo, voIndex);
2493
+ const glyph = glyphSignature(struct, vo.name, Object.keys(vo.fields ?? {}).length || 1);
2494
+ pushNode("valueObject", vo.name, glyph, 0.12 * glyph.size);
2495
+ }
2496
+ for (const u of (layer.nomosValueObjects?.usages ?? []).filter((u2) => voIndex.get(u2.vo)?.geometry !== void 0).slice().sort((a, b) => a.aggregate !== b.aggregate ? a.aggregate < b.aggregate ? -1 : 1 : a.field < b.field ? -1 : 1)) {
2497
+ const struct = JSON.stringify({ level: "geoField", geometry: voIndex.get(u.vo).geometry });
2498
+ const glyph = glyphSignature(struct, `${u.aggregate}.${u.field}`, 1);
2499
+ pushNode("geoField", `${u.aggregate}.${u.field}`, glyph, 0.1);
2500
+ }
2501
+ const aggNames = aggs.map((p) => bareTypeLabel(p.type ?? ""));
2502
+ const pushEdge = (kind, from, to, label) => {
2503
+ const fromId = nodeIds.get(from);
2504
+ const toId = nodeIds.get(to);
2505
+ if (fromId === void 0 || toId === void 0) return;
2506
+ edges.push({ id: `${kind}:${domain}:${label}:${suffix}`, kind, from: fromId, to: toId, label, born: frame, wither });
2507
+ };
2508
+ for (const p of aggs) {
2509
+ const pName = bareTypeLabel(p.type ?? "");
2510
+ const fieldKinds = layer.nomosReadProjection?.aggregateFieldKinds?.[p.type ?? ""] ?? layer.nomosReadProjection?.aggregateFieldKinds?.[pName] ?? p.schema ?? {};
2511
+ for (const f of Object.keys(fieldKinds).sort()) {
2512
+ if (!/Id$/.test(f) || f.length < 3) continue;
2513
+ const stem = f[0].toUpperCase() + f.slice(1, -2);
2514
+ const targets = aggNames.filter((t2) => t2 !== pName && (t2 === stem || t2.endsWith(stem))).sort((x, y) => x.length - y.length || (x < y ? -1 : 1));
2515
+ if (targets.length === 0) continue;
2516
+ pushEdge("nameRef", `aggregate:${pName}`, `aggregate:${targets[0]}`, `${pName}.${f}`);
2517
+ }
2518
+ }
2519
+ for (const p of dirs) {
2520
+ const name = p.path.split("/").pop() ?? "?";
2521
+ if (typeof p.target === "string" && p.target.length > 0) {
2522
+ const target = bareTypeLabel(p.target);
2523
+ pushEdge("targets", `directive:${name}`, `aggregate:${target}`, `${name}\u2192${target}`);
2524
+ }
2525
+ }
2526
+ const gate = layer.nomosRelationGate?.requires ?? {};
2527
+ for (const dirId of Object.keys(gate).sort()) {
2528
+ const req = gate[dirId];
2529
+ const relType = String(req.object ?? "").split(":")[0] ?? "";
2530
+ pushEdge("relationGate", `relationType:${relType}`, `directive:${dirId}`, `${dirId}\xB7${String(req.relation ?? "")}`);
2531
+ }
2532
+ }
2533
+ }
2534
+ const typeIndex = /* @__PURE__ */ new Map();
2535
+ for (const n of nodes) {
2536
+ if (n.kind !== "aggregate") continue;
2537
+ const list = typeIndex.get(n.name) ?? [];
2538
+ if (!list.includes(n.id)) list.push(n.id);
2539
+ typeIndex.set(n.name, list);
2540
+ }
2541
+ for (const [k, v] of typeIndex) typeIndex.set(k, v.sort());
2542
+ return { nodes, edges, typeIndex };
2543
+ }
2544
+ function bareTypeLabel(t) {
2545
+ const i = t.lastIndexOf(".");
2546
+ return i > 0 && i < t.length - 1 ? t.slice(i + 1) : t;
2547
+ }
2548
+ function geoFieldsOfInstalls(installs) {
2549
+ const out12 = /* @__PURE__ */ new Map();
2550
+ const add2 = (type, field) => {
2551
+ const set = out12.get(type) ?? /* @__PURE__ */ new Set();
2552
+ set.add(field);
2553
+ out12.set(type, set);
2554
+ };
2555
+ for (const inst of installs) {
2556
+ for (const layer of inst.layers) {
2557
+ const voIndex = new Map((layer.nomosValueObjects?.valueObjects ?? []).map((vo) => [vo.name, vo]));
2558
+ const qualified = layer.nomosReadProjectionNamespace?.types ?? [];
2559
+ const delim = layer.nomosReadProjectionNamespace?.delimiter ?? ".";
2560
+ for (const u of layer.nomosValueObjects?.usages ?? []) {
2561
+ if (voIndex.get(u.vo)?.geometry !== "Point") continue;
2562
+ add2(u.aggregate, u.field);
2563
+ for (const q of qualified) {
2564
+ if (q.endsWith(`${delim}${u.aggregate}`)) add2(q, u.field);
2565
+ }
2566
+ }
2567
+ }
2568
+ }
2569
+ return out12;
2570
+ }
2571
+ function pointOfOpValue(setOp) {
2572
+ let v = setOp["Json"];
2573
+ if (typeof v === "string") {
2574
+ try {
2575
+ v = JSON.parse(v);
2576
+ } catch {
2577
+ return void 0;
2578
+ }
2579
+ }
2580
+ if (v === void 0 && typeof setOp["Str"] === "string") {
2581
+ try {
2582
+ v = JSON.parse(setOp["Str"]);
2583
+ } catch {
2584
+ return void 0;
2585
+ }
2586
+ }
2587
+ const g = v;
2588
+ if (g === void 0 || g === null || g.type !== "Point" || !Array.isArray(g.coordinates)) return void 0;
2589
+ const [lng, lat] = g.coordinates;
2590
+ if (typeof lng !== "number" || typeof lat !== "number" || !Number.isFinite(lng) || !Number.isFinite(lat)) return void 0;
2591
+ return [lng, lat];
2592
+ }
2593
+ function framesFromChain(entries, upTo, geoFields) {
2594
+ const typeOf2 = /* @__PURE__ */ new Map();
2595
+ const frames = [];
2596
+ const firstPoint = /* @__PURE__ */ new Map();
2597
+ const allBorn = [];
2598
+ for (let t = 0; t < Math.min(entries.length, upTo); t++) {
2599
+ const doc = JSON.parse(entries[t].intent);
2600
+ const touched = [];
2601
+ const born = [];
2602
+ for (const ev of doc.events ?? []) {
2603
+ const aggId = ev.aggregate;
2604
+ if (typeof aggId !== "string") continue;
2605
+ const declaredRaw = (ev.ops ?? []).find((o) => o.field === "__type")?.op?.Set?.Str;
2606
+ const declared = declaredRaw === void 0 ? void 0 : bareTypeLabel(declaredRaw);
2607
+ if (declared !== void 0 && !typeOf2.has(aggId)) {
2608
+ typeOf2.set(aggId, declared);
2609
+ const b = { type: declared, id: aggId };
2610
+ born.push(b);
2611
+ allBorn.push(b);
2612
+ }
2613
+ const type = declared ?? typeOf2.get(aggId);
2614
+ if (type !== void 0) touched.push(type);
2615
+ const fields = type !== void 0 ? geoFields?.get(type) : void 0;
2616
+ if (fields !== void 0 && !firstPoint.has(aggId)) {
2617
+ for (const o of ev.ops ?? []) {
2618
+ if (typeof o.field !== "string" || !fields.has(o.field) || o.op?.Set === void 0) continue;
2619
+ const pt = pointOfOpValue(o.op.Set);
2620
+ if (pt !== void 0) {
2621
+ firstPoint.set(aggId, pt);
2622
+ break;
2623
+ }
2624
+ }
2625
+ }
2626
+ }
2627
+ frames.push({
2628
+ t,
2629
+ intentId: doc.id ?? "",
2630
+ directive: `${doc.payload?.domain ?? "?"}/${doc.payload?.directiveId ?? "?"}`,
2631
+ kind: intentKind(doc.payload?.directiveId ?? ""),
2632
+ touched: [...new Set(touched)].sort(),
2633
+ born
2634
+ });
2635
+ }
2636
+ for (const b of allBorn) {
2637
+ const pt = firstPoint.get(b.id);
2638
+ if (pt !== void 0) b.point = pt;
2639
+ }
2640
+ return frames;
2641
+ }
2642
+ function trunkPosition(k) {
2643
+ return round3([Math.sin(k * 0.45) * 0.38, -0.012 * k, -0.55 - 1.15 * k]);
2644
+ }
2645
+ function materialFor(mats, color, opacity, emissiveK) {
2646
+ const key = `${vec(color)}|${num(opacity)}|${num(emissiveK)}`;
2647
+ const hit = mats.names.get(key);
2648
+ if (hit !== void 0) return hit;
2649
+ const name = `mat_${String(mats.names.size).padStart(3, "0")}`;
2650
+ mats.names.set(key, name);
2651
+ const emissive = [r3(color[0] * emissiveK), r3(color[1] * emissiveK), r3(color[2] * emissiveK)];
2652
+ mats.defs.push(
2653
+ ` def Material "${name}"`,
2654
+ ` {`,
2655
+ ` token outputs:surface.connect = </Materials/${name}/PreviewSurface.outputs:surface>`,
2656
+ ` def Shader "PreviewSurface"`,
2657
+ ` {`,
2658
+ ` uniform token info:id = "UsdPreviewSurface"`,
2659
+ ` color3f inputs:diffuseColor = ${vec(color)}`,
2660
+ ` color3f inputs:emissiveColor = ${vec(emissive)}`,
2661
+ ` float inputs:opacity = ${num(opacity)}`,
2662
+ ` float inputs:roughness = 0.4`,
2663
+ ` token outputs:surface`,
2664
+ ` }`,
2665
+ ` }`
2666
+ );
2667
+ return name;
2668
+ }
2669
+ function meshPrim(o, indent) {
2670
+ const I = indent, I2 = indent + " ", I3 = indent + " ";
2671
+ const pts = o.mesh.points.map((p) => vec(mul(p, o.meshScale)));
2672
+ const counts = new Array(o.mesh.indices.length / 3).fill(3).join(", ");
2673
+ const lines = [
2674
+ `${I}def Xform ${usdaString(o.primName)} (`,
2675
+ `${I2}prepend apiSchemas = ["MaterialBindingAPI"]`,
2676
+ `${I})`,
2677
+ `${I}{`,
2678
+ `${I2}rel material:binding = </Materials/${o.material}>`
2679
+ ];
2680
+ for (const [k, v] of o.attrs ?? []) lines.push(`${I2}custom string ${k} = ${usdaString(v)}`);
2681
+ const ops = [];
2682
+ if (o.translate !== void 0) {
2683
+ const t = o.translate;
2684
+ lines.push(
2685
+ `${I2}matrix4d xformOp:transform = ( (1, 0, 0, 0), (0, 1, 0, 0), (0, 0, 1, 0), (${num(t[0])}, ${num(t[1])}, ${num(t[2])}, 1) )`
2686
+ );
2687
+ ops.push('"xformOp:transform"');
2688
+ }
2689
+ if (o.translateSamples !== void 0 && o.translateSamples.length > 0) {
2690
+ lines.push(`${I2}double3 xformOp:translate = ${vec(o.translateSamples[0][1])}`);
2691
+ lines.push(`${I2}double3 xformOp:translate.timeSamples = {`);
2692
+ for (const [t, p] of o.translateSamples) lines.push(`${I3}${t}: ${vec(p)},`);
2693
+ lines.push(`${I2}}`);
2694
+ ops.push('"xformOp:translate"');
2695
+ }
2696
+ if (o.scaleSamples !== void 0 && o.scaleSamples.length > 0) {
2697
+ lines.push(`${I2}float3 xformOp:scale = (1, 1, 1)`);
2698
+ lines.push(`${I2}float3 xformOp:scale.timeSamples = {`);
2699
+ for (const [t, s] of o.scaleSamples) lines.push(`${I3}${t}: (${num(s)}, ${num(s)}, ${num(s)}),`);
2700
+ lines.push(`${I2}}`);
2701
+ ops.push('"xformOp:scale"');
2702
+ }
2703
+ if (ops.length > 0) lines.push(`${I2}uniform token[] xformOpOrder = [${ops.join(", ")}]`);
2704
+ if (o.visibilitySamples !== void 0 && o.visibilitySamples.length > 0) {
2705
+ lines.push(`${I2}token visibility.timeSamples = {`);
2706
+ for (const [t, v] of o.visibilitySamples) lines.push(`${I3}${t}: ${usdaString(v)},`);
2707
+ lines.push(`${I2}}`);
2708
+ }
2709
+ lines.push(
2710
+ `${I2}def Mesh "geo"`,
2711
+ `${I2}{`,
2712
+ `${I3}float3[] extent = ${extentOf(o.mesh.points, o.meshScale)}`,
2713
+ `${I3}int[] faceVertexCounts = [${counts}]`,
2714
+ `${I3}int[] faceVertexIndices = [${o.mesh.indices.join(", ")}]`,
2715
+ `${I3}point3f[] points = [${pts.join(", ")}]`,
2716
+ `${I3}uniform token subdivisionScheme = "none"`,
2717
+ `${I2}}`,
2718
+ `${I}}`
2719
+ );
2720
+ return lines;
2721
+ }
2722
+ function insetOffset(lng, lat) {
2723
+ return round3([lng / 360 * INSET_W, 0.05, -(lat / 180) * INSET_D]);
2724
+ }
2725
+ function emitSceneUsda(o) {
2726
+ const endT = o.frames.length - 1 + (o.refusal !== void 0 ? 1 : 0);
2727
+ const mats = { names: /* @__PURE__ */ new Map(), defs: [] };
2728
+ const body = [];
2729
+ const nodeById = new Map(o.graph.nodes.map((n) => [n.id, n]));
2730
+ const pulses = /* @__PURE__ */ new Map();
2731
+ for (const f of o.frames) {
2732
+ const domainHint = f.directive.split("/")[0] ?? "";
2733
+ for (const type of f.touched) {
2734
+ const candidates = (o.graph.typeIndex.get(type) ?? []).filter((id2) => {
2735
+ const n = nodeById.get(id2);
2736
+ return n.born <= f.t && (n.wither === void 0 || n.wither > f.t);
2737
+ });
2738
+ const id = candidates.find((c) => nodeById.get(c).domain === domainHint) ?? candidates[0];
2739
+ if (id === void 0) continue;
2740
+ const track = pulses.get(id) ?? /* @__PURE__ */ new Map();
2741
+ if (track.size === 0 && f.t > 0) track.set(0, 1);
2742
+ track.set(f.t, 1.45);
2743
+ if (!track.has(f.t + 1) || track.get(f.t + 1) === 1) track.set(f.t + 1, 1);
2744
+ pulses.set(id, track);
2745
+ }
2746
+ }
2747
+ body.push(` def Scope "Law"`, ` {`);
2748
+ const sortedNodes = [...o.graph.nodes].sort((a, b) => a.primName < b.primName ? -1 : 1);
2749
+ for (const n of sortedNodes) {
2750
+ const mesh = n.kind === "aggregate" ? GEO_ICOSPHERE_FINE : n.kind === "directive" ? GEO_HEX : n.kind === "valueObject" ? facetedPolyhedron(n.glyph.facetCount, n.glyph.elongation) : n.kind === "geoField" ? GEO_PIN : GEO_ICOSPHERE;
2751
+ const vis = [];
2752
+ if (n.born > 0) vis.push([0, "invisible"], [n.born, "inherited"]);
2753
+ if (n.wither !== void 0) {
2754
+ if (vis.length === 0) vis.push([0, "inherited"]);
2755
+ vis.push([Math.min(endT, n.wither + 1), "invisible"]);
2756
+ }
2757
+ const scaleSamples = [];
2758
+ const track = pulses.get(n.id);
2759
+ if (track !== void 0) for (const t of [...track.keys()].sort((a, b) => a - b)) scaleSamples.push([t, track.get(t)]);
2760
+ if (n.wither !== void 0) {
2761
+ if (scaleSamples.length === 0 && n.wither > 0) scaleSamples.push([0, 1]);
2762
+ scaleSamples.push([n.wither, 0.05]);
2763
+ }
2764
+ body.push(
2765
+ ...meshPrim(
2766
+ {
2767
+ primName: n.primName,
2768
+ mesh,
2769
+ meshScale: n.scale,
2770
+ translate: n.pos,
2771
+ material: materialFor(mats, n.glyph.color, 1, 0.25),
2772
+ ...vis.length > 0 ? { visibilitySamples: vis } : {},
2773
+ ...scaleSamples.length > 0 ? { scaleSamples } : {},
2774
+ attrs: [
2775
+ ["nomos:law:domain", n.domain],
2776
+ ["nomos:law:kind", n.kind],
2777
+ ["nomos:law:name", n.name],
2778
+ ["nomos:law:structureHash", n.glyph.structureHash]
2779
+ ]
2780
+ },
2781
+ " "
2782
+ )
2783
+ );
2784
+ }
2785
+ body.push(` }`);
2786
+ body.push(` def Scope "Edges"`, ` {`);
2787
+ const sortedEdges = [...o.graph.edges].sort((a, b) => a.id < b.id ? -1 : 1);
2788
+ sortedEdges.forEach((e, i) => {
2789
+ const a = nodeById.get(e.from);
2790
+ const b = nodeById.get(e.to);
2791
+ const vis = [];
2792
+ if (e.born > 0) vis.push([0, "invisible"], [e.born, "inherited"]);
2793
+ if (e.wither !== void 0) {
2794
+ if (vis.length === 0) vis.push([0, "inherited"]);
2795
+ vis.push([e.wither, "invisible"]);
2796
+ }
2797
+ body.push(
2798
+ ...meshPrim(
2799
+ {
2800
+ primName: `e${String(i).padStart(3, "0")}_${sanitize(e.label).slice(0, 40)}`,
2801
+ mesh: segmentStrip(a.pos, b.pos, 0.02),
2802
+ meshScale: 1,
2803
+ material: materialFor(mats, EDGE_COLORS[e.kind], 0.8, 0.1),
2804
+ ...vis.length > 0 ? { visibilitySamples: vis } : {},
2805
+ attrs: [
2806
+ ["nomos:edge:kind", e.kind],
2807
+ ["nomos:edge:label", e.label]
2808
+ ]
2809
+ },
2810
+ " "
2811
+ )
2812
+ );
2813
+ });
2814
+ body.push(` }`);
2815
+ body.push(` def Scope "Chain"`, ` {`);
2816
+ const N = o.frames.length + (o.refusal !== void 0 ? 1 : 0);
2817
+ for (const f of o.frames) {
2818
+ const stepsBehindHead = N - 1 - f.t;
2819
+ body.push(
2820
+ ...meshPrim(
2821
+ {
2822
+ primName: `link_${String(f.t).padStart(4, "0")}`,
2823
+ mesh: GEO_ICOSPHERE,
2824
+ meshScale: 0.12,
2825
+ translate: trunkPosition(stepsBehindHead),
2826
+ material: materialFor(mats, KIND_COLORS[f.kind], 1, 0.5),
2827
+ ...f.t > 0 ? { visibilitySamples: [[0, "invisible"], [f.t, "inherited"]] } : {},
2828
+ attrs: [
2829
+ ["nomos:chain:intent", f.intentId],
2830
+ ["nomos:chain:directive", f.directive],
2831
+ ["nomos:chain:kind", f.kind],
2832
+ ["nomos:chain:verdict", "admitted"]
2833
+ ]
2834
+ },
2835
+ " "
2836
+ )
2837
+ );
2838
+ }
2839
+ if (o.refusal !== void 0) {
2840
+ body.push(
2841
+ ...meshPrim(
2842
+ {
2843
+ primName: `link_refused`,
2844
+ mesh: GEO_ICOSPHERE,
2845
+ meshScale: 0.16,
2846
+ translate: trunkPosition(0),
2847
+ material: materialFor(mats, REFUSED_COLOR, 1, 0.8),
2848
+ visibilitySamples: [[0, "invisible"], [endT, "inherited"]],
2849
+ attrs: [
2850
+ ["nomos:chain:intent", o.refusal.intentId],
2851
+ ["nomos:chain:directive", o.refusal.directive],
2852
+ ["nomos:chain:verdict", `refused: ${o.refusal.reason}`]
2853
+ ]
2854
+ },
2855
+ " "
2856
+ )
2857
+ );
2858
+ }
2859
+ if (N > 1) {
2860
+ body.push(
2861
+ ...meshPrim(
2862
+ {
2863
+ primName: "rail",
2864
+ mesh: segmentStrip(add(trunkPosition(0), [0, -0.1, 0]), add(trunkPosition(N - 1), [0, -0.1, 0]), 0.03),
2865
+ meshScale: 1,
2866
+ material: materialFor(mats, [0.267, 0.325, 0.416], 0.7, 0),
2867
+ attrs: [["nomos:chain:role", "rail"]]
2868
+ },
2869
+ " "
2870
+ )
2871
+ );
2872
+ }
2873
+ const cursorSamples = [];
2874
+ for (let t = 0; t <= endT; t++) cursorSamples.push([t, add(trunkPosition(N - 1 - Math.min(t, N - 1)), [0, 0.3, 0])]);
2875
+ body.push(
2876
+ ...meshPrim(
2877
+ {
2878
+ primName: "cursor",
2879
+ mesh: GEO_ICOSPHERE,
2880
+ meshScale: 0.09,
2881
+ material: materialFor(mats, o.refusal !== void 0 ? REFUSED_COLOR : CURSOR_COLOR, 1, 1),
2882
+ translateSamples: cursorSamples,
2883
+ attrs: [["nomos:chain:role", "cursor"]]
2884
+ },
2885
+ " "
2886
+ )
2887
+ );
2888
+ body.push(` }`);
2889
+ body.push(` def Scope "Instances"`, ` {`);
2890
+ const perNodeCount = /* @__PURE__ */ new Map();
2891
+ const insetBorn = /* @__PURE__ */ new Map();
2892
+ const budLines = [];
2893
+ for (const f of o.frames) {
2894
+ const domainHint = f.directive.split("/")[0] ?? "";
2895
+ for (const inst of f.born) {
2896
+ const candidates = (o.graph.typeIndex.get(inst.type) ?? []).filter((id) => {
2897
+ const n = nodeById.get(id);
2898
+ return n.born <= f.t && (n.wither === void 0 || n.wither > f.t);
2899
+ });
2900
+ const anchorId = candidates.find((c) => nodeById.get(c).domain === domainHint) ?? candidates[0];
2901
+ if (anchorId === void 0) continue;
2902
+ const anchor = nodeById.get(anchorId);
2903
+ const k = perNodeCount.get(anchorId) ?? 0;
2904
+ perNodeCount.set(anchorId, k + 1);
2905
+ let translate;
2906
+ if (inst.point !== void 0) {
2907
+ const [lng, lat] = inst.point;
2908
+ translate = round3(add(add(anchor.pos, INSET_DROP), insetOffset(lng, lat)));
2909
+ if (!insetBorn.has(anchorId) || insetBorn.get(anchorId) > f.t) insetBorn.set(anchorId, f.t);
2910
+ } else {
2911
+ const ang = k * 2.399963229728653;
2912
+ const r = 0.45 + 0.06 * Math.floor(k / 9);
2913
+ const jz = 0.25 + (fnv1a32(inst.id) & 255) / 255 * 0.1;
2914
+ translate = round3(add(anchor.pos, [Math.cos(ang) * r, Math.sin(ang) * r * 0.7, jz]));
2915
+ }
2916
+ budLines.push(
2917
+ ...meshPrim(
2918
+ {
2919
+ primName: `inst_${String(f.t).padStart(4, "0")}_${String(k).padStart(2, "0")}_${sanitize(inst.id).slice(0, 32)}`,
2920
+ mesh: GEO_ICOSPHERE,
2921
+ meshScale: 0.05,
2922
+ translate,
2923
+ material: materialFor(mats, INSTANCE_COLOR, 1, 0.6),
2924
+ ...f.t > 0 ? { visibilitySamples: [[0, "invisible"], [f.t, "inherited"]] } : {},
2925
+ attrs: [
2926
+ ["nomos:id", inst.id],
2927
+ ["nomos:type", inst.type],
2928
+ ...inst.point !== void 0 ? [["nomos:geo:point", `${num(inst.point[0])},${num(inst.point[1])}`]] : []
2929
+ ]
2930
+ },
2931
+ " "
2932
+ )
2933
+ );
2934
+ }
2935
+ }
2936
+ for (const anchorId of [...insetBorn.keys()].sort()) {
2937
+ const anchor = nodeById.get(anchorId);
2938
+ const bornAt = insetBorn.get(anchorId);
2939
+ budLines.push(
2940
+ ...meshPrim(
2941
+ {
2942
+ primName: `inset_${anchor.primName}`,
2943
+ mesh: INSET_FRAME,
2944
+ meshScale: 1,
2945
+ translate: round3(add(anchor.pos, INSET_DROP)),
2946
+ material: materialFor(mats, [0.353, 0.42, 0.494], 0.55, 0),
2947
+ ...bornAt > 0 ? { visibilitySamples: [[0, "invisible"], [bornAt, "inherited"]] } : {},
2948
+ attrs: [
2949
+ ["nomos:inset:aggregate", anchor.name],
2950
+ ["nomos:inset:projection", "equirectangular lng/lat"]
2951
+ ]
2952
+ },
2953
+ " "
2954
+ )
2955
+ );
2956
+ }
2957
+ body.push(...budLines, ` }`);
2958
+ const header = [
2959
+ `#usda 1.0`,
2960
+ `(`,
2961
+ ` doc = "Nomos ledger scene (portable profile): the chain-carried law canopy, the validated replay as timeSamples. timeCode is the chain index."`,
2962
+ ` customLayerData = {`,
2963
+ ` string "nomos:format" = "nomos.usd-portable-scene.v1"`,
2964
+ ` string "nomos:scene" = ${usdaString(o.name)}`,
2965
+ ` string "nomos:packages" = ${usdaString(o.packageHashes.join(","))}`,
2966
+ ` string "nomos:verifyChain" = ${usdaString(o.verdictLine)}`,
2967
+ ` string "nomos:timeCode" = "chain index (intent position on main)"`,
2968
+ ` }`,
2969
+ ` defaultPrim = "Scene"`,
2970
+ ` startTimeCode = 0`,
2971
+ ` endTimeCode = ${endT}`,
2972
+ ` timeCodesPerSecond = 1`,
2973
+ ` framesPerSecond = 1`,
2974
+ ` metersPerUnit = 1`,
2975
+ ` upAxis = "Y"`,
2976
+ `)`,
2977
+ ``,
2978
+ `def Scope "Materials"`,
2979
+ `{`,
2980
+ ...mats.defs,
2981
+ `}`,
2982
+ ``,
2983
+ `def Xform "Scene"`,
2984
+ `{`
2985
+ ];
2986
+ return [...header, ...body, `}`, ``].join("\n");
2987
+ }
2988
+ function lintPortableProfile(usda) {
2989
+ const violations = [];
2990
+ const defRe = /^\s*def\s+(\w+)\s+"/gm;
2991
+ let m;
2992
+ const ALLOWED_DEFS = /* @__PURE__ */ new Set(["Xform", "Scope", "Mesh", "Material", "Shader"]);
2993
+ while ((m = defRe.exec(usda)) !== null) {
2994
+ if (!ALLOWED_DEFS.has(m[1])) violations.push(`forbidden prim schema: def ${m[1]}`);
2995
+ }
2996
+ if (/\bsubLayers\b/.test(usda)) violations.push("subLayers in a flattened stage");
2997
+ if (/\bprepend references\b|\bpayload\b/.test(usda)) violations.push("references/payloads in a flattened stage");
2998
+ const tsRe = /([\w:]+)\.timeSamples/g;
2999
+ const ALLOWED_TS = /* @__PURE__ */ new Set(["xformOp:translate", "xformOp:scale", "visibility"]);
3000
+ while ((m = tsRe.exec(usda)) !== null) {
3001
+ const attr = m[1].split(" ").pop();
3002
+ if (!ALLOWED_TS.has(attr)) violations.push(`forbidden timeSamples on ${attr}`);
3003
+ }
3004
+ const opRe = /xformOp:(\w+)/g;
3005
+ const ALLOWED_OPS = /* @__PURE__ */ new Set(["transform", "translate", "scale"]);
3006
+ while ((m = opRe.exec(usda)) !== null) {
3007
+ if (!ALLOWED_OPS.has(m[1])) violations.push(`forbidden xformOp:${m[1]}`);
3008
+ }
3009
+ return [...new Set(violations)];
3010
+ }
3011
+ function crc32(bytes) {
3012
+ let c = 4294967295;
3013
+ for (let i = 0; i < bytes.length; i++) c = CRC_TABLE[(c ^ bytes[i]) & 255] ^ c >>> 8;
3014
+ return (c ^ 4294967295) >>> 0;
3015
+ }
3016
+ function usdzPack(entries) {
3017
+ const chunks = [];
3018
+ const central = [];
3019
+ let offset = 0;
3020
+ const w16 = (v) => [v & 255, v >>> 8 & 255];
3021
+ const w32 = (v) => [v & 255, v >>> 8 & 255, v >>> 16 & 255, v >>> 24 & 255];
3022
+ for (const [name, data] of entries) {
3023
+ const nameBytes = new TextEncoder().encode(name);
3024
+ const crc = crc32(data);
3025
+ const headerLen = 30 + nameBytes.length;
3026
+ const mis = (offset + headerLen) % 64;
3027
+ let extraLen = 0;
3028
+ if (mis !== 0) {
3029
+ extraLen = 64 - mis;
3030
+ if (extraLen < 4) extraLen += 64;
3031
+ }
3032
+ const pad = extraLen === 0 ? 0 : extraLen - 4;
3033
+ const local = new Uint8Array([
3034
+ ...w32(67324752),
3035
+ ...w16(20),
3036
+ // version needed
3037
+ ...w16(0),
3038
+ // flags
3039
+ ...w16(0),
3040
+ // method: stored
3041
+ ...w16(0),
3042
+ // dos time (fixed)
3043
+ ...w16(33),
3044
+ // dos date: 1980-01-01
3045
+ ...w32(crc),
3046
+ ...w32(data.length),
3047
+ ...w32(data.length),
3048
+ ...w16(nameBytes.length),
3049
+ ...w16(extraLen),
3050
+ ...nameBytes,
3051
+ ...extraLen > 0 ? [...w16(6534), ...w16(pad), ...new Array(pad).fill(0)] : []
3052
+ ]);
3053
+ chunks.push(local, data);
3054
+ central.push(
3055
+ new Uint8Array([
3056
+ ...w32(33639248),
3057
+ ...w16(20),
3058
+ ...w16(20),
3059
+ ...w16(0),
3060
+ ...w16(0),
3061
+ ...w16(0),
3062
+ ...w16(33),
3063
+ ...w32(crc),
3064
+ ...w32(data.length),
3065
+ ...w32(data.length),
3066
+ ...w16(nameBytes.length),
3067
+ ...w16(0),
3068
+ // no extra in central
3069
+ ...w16(0),
3070
+ // comment
3071
+ ...w16(0),
3072
+ // disk
3073
+ ...w16(0),
3074
+ // internal attrs
3075
+ ...w32(0),
3076
+ // external attrs
3077
+ ...w32(offset),
3078
+ ...nameBytes
3079
+ ])
3080
+ );
3081
+ offset += local.length + data.length;
3082
+ }
3083
+ const centralStart = offset;
3084
+ let centralLen = 0;
3085
+ for (const c of central) centralLen += c.length;
3086
+ const eocd = new Uint8Array([
3087
+ ...w32(101010256),
3088
+ ...w16(0),
3089
+ ...w16(0),
3090
+ ...w16(entries.length),
3091
+ ...w16(entries.length),
3092
+ ...w32(centralLen),
3093
+ ...w32(centralStart),
3094
+ ...w16(0)
3095
+ ]);
3096
+ const total = offset + centralLen + eocd.length;
3097
+ const outBytes = new Uint8Array(total);
3098
+ let at = 0;
3099
+ for (const c of [...chunks, ...central, eocd]) {
3100
+ outBytes.set(c, at);
3101
+ at += c.length;
3102
+ }
3103
+ return outBytes;
3104
+ }
3105
+ var sha256Hex, add, sub, mul, norm, cross, seededAngle, r3, round3, sanitize, KIND_COLORS, REFUSED_COLOR, EDGE_COLORS, INSTANCE_COLOR, CURSOR_COLOR, num, vec, usdaString, extentOf, GEO_ICOSPHERE, GEO_ICOSPHERE_FINE, GEO_BOX, GEO_HEX, GEO_PIN, INSET_W, INSET_D, INSET_DROP, INSET_FRAME, CRC_TABLE;
3106
+ var init_scene_projector = __esm({
3107
+ "src/scene_projector.ts"() {
3108
+ "use strict";
3109
+ sha256Hex = (s) => createHash4("sha256").update(s).digest("hex");
3110
+ add = (a, b) => [a[0] + b[0], a[1] + b[1], a[2] + b[2]];
3111
+ sub = (a, b) => [a[0] - b[0], a[1] - b[1], a[2] - b[2]];
3112
+ mul = (a, k) => [a[0] * k, a[1] * k, a[2] * k];
3113
+ norm = (a) => {
3114
+ const l = Math.sqrt(a[0] * a[0] + a[1] * a[1] + a[2] * a[2]) || 1;
3115
+ return [a[0] / l, a[1] / l, a[2] / l];
3116
+ };
3117
+ cross = (a, b) => [
3118
+ a[1] * b[2] - a[2] * b[1],
3119
+ a[2] * b[0] - a[0] * b[2],
3120
+ a[0] * b[1] - a[1] * b[0]
3121
+ ];
3122
+ seededAngle = (id, spread = Math.PI * 2) => fnv1a32(id) % 3600 / 3600 * spread;
3123
+ r3 = (v) => {
3124
+ const x = Math.round(v * 1e3) / 1e3;
3125
+ return Object.is(x, -0) ? 0 : x;
3126
+ };
3127
+ round3 = (p) => [r3(p[0]), r3(p[1]), r3(p[2])];
3128
+ sanitize = (s) => s.replace(/[^A-Za-z0-9_]/g, "_");
3129
+ KIND_COLORS = {
3130
+ install: [0.953, 0.714, 0.302],
3131
+ // amber
3132
+ order: [0.431, 0.659, 1],
3133
+ // blue
3134
+ receipt: [0.773, 0.549, 1],
3135
+ // violet
3136
+ offer: [0.604, 0.655, 0.71]
3137
+ // neutral
3138
+ };
3139
+ REFUSED_COLOR = [1, 0.373, 0.337];
3140
+ EDGE_COLORS = {
3141
+ nameRef: [0.45, 0.47, 0.5],
3142
+ relationGate: [0.63, 0.26, 0.96],
3143
+ targets: [1, 0.42, 0.42]
3144
+ };
3145
+ INSTANCE_COLOR = [0.22, 0.85, 0.51];
3146
+ CURSOR_COLOR = [0.24, 0.72, 0.35];
3147
+ num = (n) => {
3148
+ if (!Number.isFinite(n)) throw new Error("non-finite number in a usda literal");
3149
+ const v = Math.round(n * 1e3) / 1e3;
3150
+ return String(Object.is(v, -0) ? 0 : v);
3151
+ };
3152
+ vec = (p) => `(${p.map(num).join(", ")})`;
3153
+ usdaString = (s) => JSON.stringify(s);
3154
+ extentOf = (points, scale) => {
3155
+ let min = [Infinity, Infinity, Infinity];
3156
+ let max = [-Infinity, -Infinity, -Infinity];
3157
+ for (const p of points) {
3158
+ for (let i = 0; i < 3; i++) {
3159
+ if (p[i] * scale < min[i]) min[i] = p[i] * scale;
3160
+ if (p[i] * scale > max[i]) max[i] = p[i] * scale;
3161
+ }
3162
+ }
3163
+ return `[${vec(round3(min))}, ${vec(round3(max))}]`;
3164
+ };
3165
+ GEO_ICOSPHERE = icosphere(1);
3166
+ GEO_ICOSPHERE_FINE = icosphere(2);
3167
+ GEO_BOX = boxMesh();
3168
+ GEO_HEX = prism(6);
3169
+ GEO_PIN = pinLeaf();
3170
+ INSET_W = 1.6;
3171
+ INSET_D = 0.8;
3172
+ INSET_DROP = [0, -0.85, 0];
3173
+ INSET_FRAME = {
3174
+ points: GEO_BOX.points.map((p) => [p[0] * INSET_W, p[1] * 0.02, p[2] * INSET_D]),
3175
+ indices: GEO_BOX.indices
3176
+ };
3177
+ CRC_TABLE = (() => {
3178
+ const t = new Uint32Array(256);
3179
+ for (let n = 0; n < 256; n++) {
3180
+ let c = n;
3181
+ for (let k = 0; k < 8; k++) c = c & 1 ? 3988292384 ^ c >>> 1 : c >>> 1;
3182
+ t[n] = c >>> 0;
3183
+ }
3184
+ return t;
3185
+ })();
3186
+ }
3187
+ });
3188
+
3189
+ // src/scene.ts
3190
+ var scene_exports = {};
3191
+ __export(scene_exports, {
3192
+ scene: () => scene
3193
+ });
3194
+ import { existsSync as existsSync9, mkdirSync as mkdirSync4, writeFileSync as writeFileSync6 } from "node:fs";
3195
+ import { join as join10, resolve as resolve2 } from "node:path";
3196
+ import { createHash as createHash5 } from "node:crypto";
3197
+ import git3 from "isomorphic-git";
3198
+ async function walkChain2(eng, ws) {
3199
+ const gitdir = gitdirOf(ws);
3200
+ const dec4 = new TextDecoder();
3201
+ const entries = [];
3202
+ for (const c of await git3.log({ fs: eng.fs, gitdir, ref: "main" })) {
3203
+ try {
3204
+ const { blob } = await git3.readBlob({ fs: eng.fs, gitdir, oid: c.oid, filepath: "intent.json" });
3205
+ entries.push({ oid: c.oid, intent: dec4.decode(blob) });
3206
+ } catch {
3207
+ }
3208
+ }
3209
+ entries.reverse();
3210
+ return entries;
3211
+ }
3212
+ async function projectOnce(cloud, target, isDir, name) {
3213
+ const { eng } = await holonEngine(cloud, void 0, "githolon-scene");
3214
+ const SOURCE3 = "source";
3215
+ if (isDir) {
3216
+ const gitDir = existsSync9(join10(target, ".git")) ? join10(target, ".git") : target;
3217
+ if (!existsSync9(join10(gitDir, "HEAD"))) throw new Error(`${target} is not a git repo (no HEAD)`);
3218
+ await mountFresh(eng, SOURCE3);
3219
+ eng.preopen.dir.contents.get("ws").contents.get(SOURCE3).contents.set("nomos.git", readTreeFromDisk(gitDir));
3220
+ } else {
3221
+ const m = await mountWorkspace(eng, SOURCE3, { remote: `${cloud}/v2/workspaces/${target}/git`, headers: {} });
3222
+ if (m.restored !== true) {
3223
+ throw new Error(
3224
+ `workspace '${target}' has no refs/heads/main on ${cloud} \u2014 nothing to project` + (m.restoreError ? ` (${String(m.restoreError).split(" | ")[0]})` : "")
3225
+ );
3226
+ }
3227
+ }
3228
+ const entries = await walkChain2(eng, SOURCE3);
3229
+ if (entries.length === 0) throw new Error(`the chain on '${target}' carries no intents \u2014 nothing to project`);
3230
+ const verdict = verifyChainLocal(eng, SOURCE3);
3231
+ let verdictLine;
3232
+ let upTo;
3233
+ let refusal;
3234
+ if (verdict["valid"] === true) {
3235
+ verdictLine = `GREEN (${verdict["plansRerun"]} plans re-run)`;
3236
+ upTo = entries.length;
3237
+ } else {
3238
+ const at = Number.isInteger(verdict["atIndex"]) ? verdict["atIndex"] : 0;
3239
+ upTo = Math.max(0, at);
3240
+ const bad = JSON.parse(entries[Math.min(at, entries.length - 1)].intent);
3241
+ refusal = {
3242
+ index: at,
3243
+ reason: `${String(verdict["check"] ?? "replay")}: ${String(verdict["error"] ?? "invalid").slice(0, 200)}`,
3244
+ directive: `${bad.payload?.domain ?? "?"}/${bad.payload?.directiveId ?? "?"}`,
3245
+ intentId: bad.id ?? ""
3246
+ };
3247
+ verdictLine = `REFUSED at index ${at} \u2014 ${refusal.reason}`;
3248
+ }
3249
+ if (upTo === 0 && refusal === void 0) throw new Error(`${name}: nothing to emit`);
3250
+ const consideredEntries = entries.slice(0, Math.max(upTo, 1));
3251
+ const installs = lawInstallsFromChain(consideredEntries);
3252
+ if (installs.length === 0) throw new Error("the chain installs no openusd-ir packages \u2014 nothing to draw");
3253
+ const graph = buildSceneGraph(installs);
3254
+ const frames = framesFromChain(entries, upTo, geoFieldsOfInstalls(installs));
3255
+ const usda = emitSceneUsda({
3256
+ name,
3257
+ graph,
3258
+ frames,
3259
+ verdictLine,
3260
+ packageHashes: installs.map((i) => i.domainHash),
3261
+ refusal
3262
+ });
3263
+ return {
3264
+ usda,
3265
+ stats: {
3266
+ frames: frames.length,
3267
+ nodes: graph.nodes.length,
3268
+ edges: graph.edges.length,
3269
+ endT: frames.length - 1 + (refusal !== void 0 ? 1 : 0),
3270
+ verdictLine
3271
+ }
3272
+ };
3273
+ }
3274
+ async function scene(target, opts) {
3275
+ const cloud = cloudBase(opts.cloud);
3276
+ const isDir = existsSync9(target) || target.includes("/") || target.startsWith(".");
3277
+ const name = isDir ? (resolve2(target).split("/").filter(Boolean).pop() ?? "scene").replace(/\.git$/, "") : target;
3278
+ out11(`scene \u2014 ${name} (${isDir ? resolve2(target) : `${cloud} :: ${target}`})`);
3279
+ let first, second;
3280
+ try {
3281
+ first = await projectOnce(cloud, target, isDir, name);
3282
+ second = await projectOnce(cloud, target, isDir, name);
3283
+ } catch (e) {
3284
+ err11(String(e.message ?? e));
3285
+ return 1;
3286
+ }
3287
+ if (first.usda !== second.usda) {
3288
+ err11(`two replays of the SAME chain emitted DIFFERENT bytes \u2014 the projector is nondeterministic (refusing to write)`);
3289
+ return 1;
3290
+ }
3291
+ const violations = lintPortableProfile(first.usda);
3292
+ if (violations.length > 0) {
3293
+ err11(`the emitted stage violates the portable profile: ${violations.join("; ")}`);
3294
+ return 1;
3295
+ }
3296
+ const outDir = resolve2(opts.out ?? join10("scene", name));
3297
+ mkdirSync4(outDir, { recursive: true });
3298
+ const usdaPath = join10(outDir, `${name}.usda`);
3299
+ const usdzPath = join10(outDir, `${name}.usdz`);
3300
+ const usdaBytes = new TextEncoder().encode(first.usda);
3301
+ writeFileSync6(usdaPath, usdaBytes);
3302
+ writeFileSync6(usdzPath, usdzPack([[`${name}.usda`, usdaBytes]]));
3303
+ const s = first.stats;
3304
+ out11(` verify_chain ${s.verdictLine}`);
3305
+ out11(` law canopy ${s.nodes} node(s), ${s.edges} edge(s) \u2014 from the chain's own installed packages`);
3306
+ out11(` chain tier ${s.frames} frame(s), timeCodes 0..${s.endT} (timeCode = chain index)`);
3307
+ out11(` determinism two fresh replays \u2192 BYTE-IDENTICAL sha256 ${sha256(first.usda).slice(0, 16)}\u2026`);
3308
+ out11(` profile portable (Mesh-only, UsdPreviewSurface, translate/scale/visibility timeSamples)`);
3309
+ out11(` \u2192 ${usdaPath} (${usdaBytes.length} bytes)`);
3310
+ out11(` \u2192 ${usdzPath}`);
3311
+ return s.verdictLine.startsWith("GREEN") ? 0 : 2;
3312
+ }
3313
+ var out11, err11, sha256;
3314
+ var init_scene = __esm({
3315
+ "src/scene.ts"() {
3316
+ "use strict";
3317
+ init_engine();
3318
+ init_cloud();
3319
+ init_local_holon();
3320
+ init_scene_projector();
3321
+ out11 = (s) => void process.stdout.write(s + "\n");
3322
+ err11 = (s) => void process.stderr.write("error: " + s + "\n");
3323
+ sha256 = (s) => createHash5("sha256").update(s).digest("hex");
3324
+ }
3325
+ });
3326
+
2115
3327
  // src/cli.ts
2116
- import { existsSync as existsSync9, readdirSync as readdirSync4, readFileSync as readFileSync11 } from "node:fs";
3328
+ import { existsSync as existsSync10, readdirSync as readdirSync4, readFileSync as readFileSync11 } from "node:fs";
2117
3329
  import { spawnSync as spawnSync4 } from "node:child_process";
2118
3330
  import { createRequire as createRequire2 } from "node:module";
2119
- import { dirname as dirname5, join as join10, resolve as resolve2 } from "node:path";
3331
+ import { dirname as dirname5, join as join11, resolve as resolve3 } from "node:path";
2120
3332
  import { pathToFileURL as pathToFileURL4 } from "node:url";
2121
3333
 
2122
3334
  // src/generate.ts
@@ -2827,7 +4039,7 @@ var HELP = {
2827
4039
  },
2828
4040
  proof: {
2829
4041
  usage: "githolon proof [build/<name>.proof.mts] [--domain <key>] [--live] [--keep]",
2830
- what: "Run the proof GENERATED from your law. DEFAULT: the OFFLINE legs on a LOCAL holon \u2014 the\nsame engine plane the cloud edge runs; no cloud workspace is touched (develop/play/understand\nhere, deploy with confidence after). --domain <key> proves a SPECIFIC domain of a multi-domain\npackage (offline; the default is the first). --live runs the full cloud loop (throwaway\nworkspace \u2192 deploy \u2192 offline write \u2192 admission \u2192 cloud reads \u2192 convergence), RETIRED on every\nexit; --keep keeps it and prints the secret once. ALL GREEN or it names the jam. The offline\nlegs also rerun on every save in `githolon dev`.",
4042
+ what: "Run the proof GENERATED from your law. DEFAULT: the OFFLINE legs on a LOCAL holon \u2014 the\nsame engine plane the cloud edge runs; no cloud workspace is touched (develop/play/understand\nhere, deploy with confidence after). --domain <key> proves a SPECIFIC domain of a multi-domain\npackage (offline; the default is the first). --live runs the full cloud loop (throwaway\nworkspace \u2192 deploy \u2192 offline write \u2192 admission \u2192 cloud reads \u2192 convergence). The throwaway is\ncreated REGISTERED (a signed createWorkspace at root \u2014 your principal needs creation authority\nthere) and RETIRED on every exit via the lawful retireOwnWorkspace; --keep keeps it. ALL GREEN or it names the jam. The offline\nlegs also rerun on every save in `githolon dev`.",
2831
4043
  examples: ["githolon proof", "githolon proof --domain co2_platform", "githolon proof --live", "githolon proof --live --keep"]
2832
4044
  },
2833
4045
  dev: {
@@ -2856,6 +4068,16 @@ var HELP = {
2856
4068
  ],
2857
4069
  examples: ["githolon replay my-guestbook", "githolon replay ./my-holon --step 10", "githolon replay my-guestbook --json | jq .directive"]
2858
4070
  },
4071
+ scene: {
4072
+ usage: "githolon scene <ws|dir> [--out <dir>] [--cloud <url>]",
4073
+ what: "The ledger as a PORTABLE USD SCENE \u2014 the thin, canonical chain\u2192USD projector. Clones the chain\n(cloud workspace or local holon directory), GATES it on the wasm verify_chain (full emission only\non GREEN; a refusal emits up to the failing index with the red cursor verdict), and projects the\nCHAIN-CARRIED law (the packages' USD-IR \u2014 never TS source) + the validated replay into ONE\nflattened stage: UsdGeomMesh-only glyphs (icosphere aggregates, hex-prism directives, faceted\nvalue-object molecules, the kind-colored commit-link trunk), UsdPreviewSurface materials, and\ntranslate/scale/visibility timeSamples \u2014 timeCode = chain index. Deterministic: the scene is\nemitted twice through fresh engines and byte-compared. Writes <name>.usda + <name>.usdz (a stored\n64-byte-aligned zip \u2014 loads in three.js, usdview, Blender, QuickLook).\nExit codes: 0 green \xB7 2 emitted-with-refusal \xB7 1 error.",
4074
+ flags: [
4075
+ ["<ws|dir>", "a workspace name on the cloud, or a local ledger clone / holon directory"],
4076
+ ["--out <dir>", "the output directory (default: ./scene/<name>)"],
4077
+ ["--cloud <url>", "target cloud (default: $NOMOS_CLOUD or the public cloud)"]
4078
+ ],
4079
+ examples: ["githolon scene my-guestbook", "githolon scene ./co2-clone --out build/scene", "githolon scene co2-platform"]
4080
+ },
2859
4081
  status: {
2860
4082
  usage: "githolon status [<ws>] [--target <name>] [--file <deploy.json>]",
2861
4083
  what: "IS WHAT I BUILT WHAT IS DEPLOYED? Compares the local law hash (build/*.deploy.json) against\nthe workspace's installed law and names the remedy when they differ.\nExit codes: 0 in sync \xB7 2 drift \xB7 1 error.",
@@ -3131,9 +4353,9 @@ function capJsonStrings(v, max = 2048) {
3131
4353
  if (typeof v === "string") return v.length > max ? `${v.slice(0, max)}\u2026(${v.length} chars)` : v;
3132
4354
  if (Array.isArray(v)) return v.map((x) => capJsonStrings(x, max));
3133
4355
  if (typeof v === "object" && v !== null) {
3134
- const out11 = {};
3135
- for (const [k, x] of Object.entries(v)) out11[k] = capJsonStrings(x, max);
3136
- return out11;
4356
+ const out12 = {};
4357
+ for (const [k, x] of Object.entries(v)) out12[k] = capJsonStrings(x, max);
4358
+ return out12;
3137
4359
  }
3138
4360
  return v;
3139
4361
  }
@@ -3463,6 +4685,9 @@ The inner loop:
3463
4685
  githolon status [<ws>] local law hash vs the DEPLOYED law, remedy named
3464
4686
  githolon replay <ws|dir> the ledger as film: step the chain intent-by-
3465
4687
  intent; finale = the verify_chain verdict
4688
+ githolon scene <ws|dir> [--out <dir>] the ledger as a PORTABLE USD scene: verify_chain-
4689
+ gated chain\u2192USD projection (.usda + .usdz, loads
4690
+ in three.js/usdview/QuickLook; timeCode = index)
3466
4691
  githolon decrypt <ws> --as <p> --secret <b64> clone + decrypt as a principal: unwrap their scope
3467
4692
  --query <id> [--param k=v] keys from the chain, read private fields in cleartext
3468
4693
 
@@ -3539,7 +4764,7 @@ Options:
3539
4764
  function cloudArgs(rest) {
3540
4765
  const pos = [];
3541
4766
  const opts = {};
3542
- const VALUE_FLAGS = ["--cloud", "--principal", "--as", "--file", "--target", "--parent", "--via", "--max", "--label", "--secret", "--payload", "--payload-file", "--save", "--format", "--with"];
4767
+ const VALUE_FLAGS = ["--cloud", "--principal", "--as", "--file", "--target", "--parent", "--via", "--max", "--label", "--secret", "--payload", "--payload-file", "--save", "--format", "--with", "--out"];
3543
4768
  const BOOL_FLAGS = ["--sha256", "--stamp", "--yes", "--clear", "--keep-beside", "--retire-replaced", "--explain-current-interface"];
3544
4769
  for (let i = 0; i < rest.length; i++) {
3545
4770
  const a = rest[i];
@@ -3566,6 +4791,7 @@ function cloudArgs(rest) {
3566
4791
  else if (a === "--save") opts.save = v;
3567
4792
  else if (a === "--format") opts.format = v;
3568
4793
  else if (a === "--with") opts.with = v;
4794
+ else if (a === "--out") opts.out = v;
3569
4795
  else if (a === "--max") {
3570
4796
  const n = Number(v);
3571
4797
  if (!Number.isInteger(n) || n <= 0) return { pos, opts, bad: `--max requires a positive integer` };
@@ -3591,9 +4817,9 @@ ${USAGE}`);
3591
4817
  if (bad !== void 0) return usageFail(bad);
3592
4818
  const boundWs = async (explicit, hint) => resolveWorkspace(explicit, process.cwd(), opts.target, hint);
3593
4819
  if (command === "ws") {
3594
- const [sub2, name] = pos;
3595
- if (sub2 === "create" && name !== void 0) return wsCreate(name, opts);
3596
- if (sub2 === "status") {
4820
+ const [sub3, name] = pos;
4821
+ if (sub3 === "create" && name !== void 0) return wsCreate(name, opts);
4822
+ if (sub3 === "status") {
3597
4823
  try {
3598
4824
  return await wsStatus(await boundWs(name, `githolon ws status <name>`), opts);
3599
4825
  } catch (e) {
@@ -3602,8 +4828,8 @@ ${USAGE}`);
3602
4828
  return 1;
3603
4829
  }
3604
4830
  }
3605
- if (sub2 === "retire" && name !== void 0) return wsRetire(name, opts);
3606
- if (sub2 === "reclaim" && name !== void 0) return wsReclaim(name, opts);
4831
+ if (sub3 === "retire" && name !== void 0) return wsRetire(name, opts);
4832
+ if (sub3 === "reclaim" && name !== void 0) return wsReclaim(name, opts);
3607
4833
  return usageFail("expected: githolon ws <create|status|retire|reclaim> <name>");
3608
4834
  }
3609
4835
  if (command === "deploy") {
@@ -3615,9 +4841,9 @@ ${USAGE}`);
3615
4841
  return 1;
3616
4842
  }
3617
4843
  }
3618
- const [sub, ws, secret] = pos;
3619
- if (sub === "set" && ws !== void 0 && secret !== void 0) return secretSet(ws, secret, opts);
3620
- if (sub === "rotate" && ws !== void 0) return secretRotate(ws, opts);
4844
+ const [sub2, ws, secret] = pos;
4845
+ if (sub2 === "set" && ws !== void 0 && secret !== void 0) return secretSet(ws, secret, opts);
4846
+ if (sub2 === "rotate" && ws !== void 0) return secretRotate(ws, opts);
3621
4847
  return usageFail("expected: githolon secret <set <ws> <secret> | rotate <ws>>");
3622
4848
  }
3623
4849
  function isKind(s) {
@@ -3648,11 +4874,11 @@ async function runProof(args) {
3648
4874
  const explicit = args.find((a, i) => !a.startsWith("--") && i !== domainValueIdx);
3649
4875
  let proofPath;
3650
4876
  if (explicit !== void 0) {
3651
- proofPath = resolve2(process.cwd(), explicit);
3652
- if (!existsSync9(proofPath)) return refuse(`proof not found: ${proofPath} \u2014 run \`githolon compile\` to (re)generate it`);
4877
+ proofPath = resolve3(process.cwd(), explicit);
4878
+ if (!existsSync10(proofPath)) return refuse(`proof not found: ${proofPath} \u2014 run \`githolon compile\` to (re)generate it`);
3653
4879
  } else {
3654
- const buildDir = join10(process.cwd(), "build");
3655
- if (!existsSync9(buildDir)) {
4880
+ const buildDir = join11(process.cwd(), "build");
4881
+ if (!existsSync10(buildDir)) {
3656
4882
  return refuse("no build/ directory here \u2014 run `githolon compile` first (every compile generates build/<name>.proof.mts from your law)");
3657
4883
  }
3658
4884
  const proofs = readdirSync4(buildDir).filter((f) => f.endsWith(".proof.mts"));
@@ -3662,7 +4888,7 @@ async function runProof(args) {
3662
4888
  if (proofs.length > 1) {
3663
4889
  return refuse(`found ${proofs.length} proofs (${proofs.join(", ")}) \u2014 name one: githolon proof build/<name>.proof.mts`);
3664
4890
  }
3665
- proofPath = join10(buildDir, proofs[0]);
4891
+ proofPath = join11(buildDir, proofs[0]);
3666
4892
  }
3667
4893
  if (!live) {
3668
4894
  try {
@@ -3675,14 +4901,14 @@ async function runProof(args) {
3675
4901
  }
3676
4902
  const resolvePkgDir = (name, fromDir) => {
3677
4903
  try {
3678
- return dirname5(createRequire2(pathToFileURL4(join10(fromDir, "noop.js"))).resolve(`${name}/package.json`));
4904
+ return dirname5(createRequire2(pathToFileURL4(join11(fromDir, "noop.js"))).resolve(`${name}/package.json`));
3679
4905
  } catch {
3680
4906
  return void 0;
3681
4907
  }
3682
4908
  };
3683
4909
  const dslDir = (() => {
3684
4910
  try {
3685
- return dirname5(createRequire2(pathToFileURL4(join10(process.cwd(), "noop.js"))).resolve("@githolon/dsl/package.json"));
4911
+ return dirname5(createRequire2(pathToFileURL4(join11(process.cwd(), "noop.js"))).resolve("@githolon/dsl/package.json"));
3686
4912
  } catch {
3687
4913
  try {
3688
4914
  return dirname5(createRequire2(import.meta.url).resolve("@githolon/dsl/package.json"));
@@ -3695,22 +4921,67 @@ async function runProof(args) {
3695
4921
  if (tsxDir === void 0) {
3696
4922
  return refuse("tsx not found \u2014 add @githolon/dsl to your project's dependencies (tsx rides along) and npm install");
3697
4923
  }
3698
- const tsxPkg = JSON.parse(readFileSync11(join10(tsxDir, "package.json"), "utf8"));
4924
+ const tsxPkg = JSON.parse(readFileSync11(join11(tsxDir, "package.json"), "utf8"));
3699
4925
  const tsxBinRel = typeof tsxPkg.bin === "string" ? tsxPkg.bin : tsxPkg.bin?.["tsx"];
3700
- const tsxCli = join10(tsxDir, tsxBinRel ?? "dist/cli.mjs");
3701
- console.log("githolon proof --live \u2014 the full cloud loop (throwaway workspace, retired on exit" + (keep ? "; --keep: kept, secret printed once" : "") + ")");
4926
+ const tsxCli = join11(tsxDir, tsxBinRel ?? "dist/cli.mjs");
4927
+ console.log("githolon proof --live \u2014 the full cloud loop (throwaway workspace, retired on exit" + (keep ? "; --keep: kept" : "") + ")");
4928
+ const { wsCreate: wsCreate2, wsRetire: wsRetire2 } = await Promise.resolve().then(() => (init_cloud(), cloud_exports));
4929
+ const { resolveGovernancePrincipal: resolveGovernancePrincipal2 } = await Promise.resolve().then(() => (init_governance(), governance_exports));
4930
+ const { login: login2, activePrincipal: activePrincipal2 } = await Promise.resolve().then(() => (init_cloud(), cloud_exports));
4931
+ let principal = process.env.NOMOS_PRINCIPAL;
4932
+ try {
4933
+ principal ??= resolveGovernancePrincipal2({});
4934
+ } catch {
4935
+ const lr = await login2({ agent: true });
4936
+ principal = lr === 0 ? activePrincipal2() : void 0;
4937
+ }
4938
+ if (principal === void 0) {
4939
+ return refuse("proof --live needs a principal \u2014 githolon login --agent (self-onboarding) failed; try it directly or pass NOMOS_PRINCIPAL");
4940
+ }
4941
+ const proofSlug = proofPath.split("/").pop().replace(/\.proof\.mts$/, "").toLowerCase().replace(/[^a-z0-9-]+/g, "-");
4942
+ const ws = process.env.NOMOS_WS ?? `${proofSlug}-proof-${Math.random().toString(36).slice(2, 8)}`;
4943
+ if (process.env.NOMOS_WS === void 0) {
4944
+ const cr = await wsCreate2(ws, { via: "root", principal });
4945
+ if (cr !== 0) {
4946
+ return refuse(
4947
+ `proof --live: registered create refused at root \u2014 principal ${principal} lacks creation authority, and the open birth lane would leak an unretirable workspace.
4948
+ remedies:
4949
+ \u2022 ask an operator: githolon grant creation ${principal} (then rerun)
4950
+ \u2022 or pre-create a REGISTERED workspace yourself and hand it over: NOMOS_WS=<ws> githolon proof --live
4951
+ \u2022 or develop offline (no cloud workspace at all): githolon proof`
4952
+ );
4953
+ }
4954
+ }
3702
4955
  const r = spawnSync4(process.execPath, [tsxCli, proofPath], {
3703
4956
  stdio: "inherit",
3704
4957
  cwd: process.cwd(),
3705
- env: { ...process.env, ...keep ? { PROOF_KEEP: "1" } : {} }
4958
+ env: {
4959
+ ...process.env,
4960
+ NOMOS_WS: ws,
4961
+ NOMOS_PRINCIPAL: principal,
4962
+ NOMOS_PROOF_MANAGED: "1",
4963
+ // the proof skips its own (dead) cleanup lane; the CLI retires below
4964
+ ...keep ? { PROOF_KEEP: "1" } : {}
4965
+ }
3706
4966
  });
3707
- return r.status ?? 1;
4967
+ const proofStatus = r.status ?? 1;
4968
+ if (keep) {
4969
+ console.log(`workspace ${ws} KEPT (--keep) \u2014 retire it when done: githolon ws retire ${ws}`);
4970
+ return proofStatus;
4971
+ }
4972
+ const rr = await wsRetire2(ws, { principal }).catch(() => 1);
4973
+ if (rr !== 0) {
4974
+ console.error(`\u26A0 THROWAWAY WORKSPACE NOT RETIRED: ${ws}`);
4975
+ console.error(`\u26A0 retire it manually: githolon ws retire ${ws}`);
4976
+ console.error(`\u26A0 (if it has no root registry row it was born unregistered and needs a custodian sweep)`);
4977
+ }
4978
+ return proofStatus;
3708
4979
  }
3709
4980
  function parseArgs(argv) {
3710
4981
  const [command, ...rest] = argv;
3711
4982
  if (command !== "generate" && command !== "g") {
3712
4983
  throw new Error(
3713
- `Unknown command '${command ?? "(none)"}'. Expected dev | compile | proof | replay | status | generate | ws | deploy | domains | secret.`
4984
+ `Unknown command '${command ?? "(none)"}'. Expected dev | compile | proof | replay | scene | status | generate | ws | deploy | domains | secret.`
3714
4985
  );
3715
4986
  }
3716
4987
  let outDir = DEFAULT_OUT;
@@ -3825,6 +5096,24 @@ ${commandHelp("replay")}`);
3825
5096
  }
3826
5097
  return replay(target, { ...opts, ...step !== void 0 ? { step } : {}, ...json ? { json: true } : {} });
3827
5098
  }
5099
+ if (argv[0] === "scene") {
5100
+ const { scene: scene2 } = await Promise.resolve().then(() => (init_scene(), scene_exports));
5101
+ const { pos, opts, bad } = cloudArgs(argv.slice(1));
5102
+ if (bad !== void 0) {
5103
+ process.stderr.write(`error: ${bad}
5104
+
5105
+ ${commandHelp("scene")}`);
5106
+ return 1;
5107
+ }
5108
+ const target = pos[0];
5109
+ if (target === void 0) {
5110
+ process.stderr.write(`error: expected: githolon scene <ws|dir>
5111
+
5112
+ ${commandHelp("scene")}`);
5113
+ return 1;
5114
+ }
5115
+ return scene2(target, opts);
5116
+ }
3828
5117
  if (argv[0] === "decrypt") {
3829
5118
  const rest = argv.slice(1);
3830
5119
  const pull = (flag) => {
@@ -3885,10 +5174,10 @@ ${commandHelp("status")}`);
3885
5174
  ${commandHelp("domains") ?? USAGE}`);
3886
5175
  return 1;
3887
5176
  }
3888
- const [sub, ...tail] = pos;
5177
+ const [sub2, ...tail] = pos;
3889
5178
  const boundWs = async (explicit, hint) => resolveWorkspace(explicit, process.cwd(), opts.target, hint);
3890
5179
  try {
3891
- if (sub === "retire") {
5180
+ if (sub2 === "retire") {
3892
5181
  const [ws, target] = tail;
3893
5182
  if (ws === void 0 || target === void 0) {
3894
5183
  process.stderr.write(`error: expected: githolon domains retire <ws> <domainHashOrKey> [--with <hashOrKey>]
@@ -3898,7 +5187,7 @@ ${commandHelp("domains") ?? USAGE}`);
3898
5187
  }
3899
5188
  return await domainsRetire2(await boundWs(ws, "githolon domains retire <ws> <hashOrKey>"), target, opts);
3900
5189
  }
3901
- if (sub === "status") {
5190
+ if (sub2 === "status") {
3902
5191
  return await domainsStatus2(await boundWs(tail[0], "githolon domains status <ws>"), opts);
3903
5192
  }
3904
5193
  } catch (e) {
@@ -3920,11 +5209,11 @@ ${commandHelp("domains") ?? USAGE}`);
3920
5209
  ${USAGE}`);
3921
5210
  return 1;
3922
5211
  }
3923
- const [sub] = pos;
3924
- if (sub === "enroll") return keyEnroll2(opts);
3925
- if (sub === "show") return keyShow2(opts);
3926
- if (sub === "import") return keyImport2(opts);
3927
- if (sub === "export") return keyExport2(opts);
5212
+ const [sub2] = pos;
5213
+ if (sub2 === "enroll") return keyEnroll2(opts);
5214
+ if (sub2 === "show") return keyShow2(opts);
5215
+ if (sub2 === "import") return keyImport2(opts);
5216
+ if (sub2 === "export") return keyExport2(opts);
3928
5217
  process.stderr.write(`error: expected: githolon key <enroll|show|import|export> [--principal <uid>]
3929
5218
 
3930
5219
  ${USAGE}`);
@@ -4007,9 +5296,9 @@ ${USAGE}`);
4007
5296
  ${USAGE}`);
4008
5297
  return 1;
4009
5298
  }
4010
- const [sub, dir] = pos;
4011
- if (sub === "init" && dir !== void 0) return ledgerInit(dir, opts);
4012
- if (sub === "verify" && dir !== void 0) return ledgerVerify(dir, opts);
5299
+ const [sub2, dir] = pos;
5300
+ if (sub2 === "init" && dir !== void 0) return ledgerInit(dir, opts);
5301
+ if (sub2 === "verify" && dir !== void 0) return ledgerVerify(dir, opts);
4013
5302
  process.stderr.write(`error: expected: githolon ledger <init|verify> <dir>
4014
5303
 
4015
5304
  ${USAGE}`);
@@ -4024,9 +5313,9 @@ ${USAGE}`);
4024
5313
  ${USAGE}`);
4025
5314
  return 1;
4026
5315
  }
4027
- const [sub, ws, remoteName] = pos;
4028
- if (sub === "setup") return gitSetup(opts);
4029
- if (sub === "remote") {
5316
+ const [sub2, ws, remoteName] = pos;
5317
+ if (sub2 === "setup") return gitSetup(opts);
5318
+ if (sub2 === "remote") {
4030
5319
  try {
4031
5320
  return await gitRemote(await resolveWorkspace(ws, process.cwd(), opts.target, "githolon git remote <ws>"), remoteName ?? "nomos", opts);
4032
5321
  } catch (e) {
@@ -4043,8 +5332,8 @@ ${USAGE}`);
4043
5332
  let parsed;
4044
5333
  try {
4045
5334
  parsed = parseArgs(argv);
4046
- } catch (err11) {
4047
- process.stderr.write(`error: ${err11.message}
5335
+ } catch (err12) {
5336
+ process.stderr.write(`error: ${err12.message}
4048
5337
 
4049
5338
  ${USAGE}`);
4050
5339
  return 1;
@@ -4063,8 +5352,8 @@ ${USAGE}`);
4063
5352
  process.stdout.write(`created ${path}
4064
5353
  `);
4065
5354
  return 0;
4066
- } catch (err11) {
4067
- process.stderr.write(`error: ${err11.message}
5355
+ } catch (err12) {
5356
+ process.stderr.write(`error: ${err12.message}
4068
5357
  `);
4069
5358
  return 1;
4070
5359
  }