githolon 0.45.0 → 0.47.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/dist/cli.mjs +175 -110
- package/package.json +3 -3
package/dist/cli.mjs
CHANGED
|
@@ -351,8 +351,10 @@ var init_governance = __esm({
|
|
|
351
351
|
// src/lifecycle.ts
|
|
352
352
|
var lifecycle_exports = {};
|
|
353
353
|
__export(lifecycle_exports, {
|
|
354
|
+
DOMAIN_LIFECYCLE_DX: () => DOMAIN_LIFECYCLE_DX,
|
|
354
355
|
describeDeployPlan: () => describeDeployPlan,
|
|
355
356
|
domainKeysOf: () => domainKeysOf,
|
|
357
|
+
domainLifecycleState: () => domainLifecycleState,
|
|
356
358
|
domainsRetire: () => domainsRetire,
|
|
357
359
|
domainsStatus: () => domainsStatus,
|
|
358
360
|
fetchInstalledLaw: () => fetchInstalledLaw,
|
|
@@ -440,7 +442,7 @@ function describeDeployPlan(plan, targetHash) {
|
|
|
440
442
|
case "keep-beside":
|
|
441
443
|
return ` ${d.key}: ${t} kept beside ${short(d.currentHash)}`;
|
|
442
444
|
case "idempotent":
|
|
443
|
-
return ` ${d.key}: unchanged \u2014 ${t} is already
|
|
445
|
+
return ` ${d.key}: unchanged \u2014 ${t} is already active`;
|
|
444
446
|
}
|
|
445
447
|
});
|
|
446
448
|
}
|
|
@@ -450,7 +452,7 @@ function resolveInstallHash(arg, currentLaw) {
|
|
|
450
452
|
if (hash !== void 0) return { hash };
|
|
451
453
|
const keys = Object.keys(currentLaw);
|
|
452
454
|
return {
|
|
453
|
-
error: `'${arg}' is neither a 64-hex install hash nor
|
|
455
|
+
error: `'${arg}' is neither a 64-hex install hash nor an active domain key` + (keys.length > 0 ? ` (active keys: ${keys.join(", ")})` : " (this workspace has no active law)")
|
|
454
456
|
};
|
|
455
457
|
}
|
|
456
458
|
function keysServedBy(hash, currentLaw) {
|
|
@@ -550,14 +552,37 @@ async function domainsRetire(ws, target, opts) {
|
|
|
550
552
|
const afterLaw = after.currentLaw ?? {};
|
|
551
553
|
for (const k of retiredKeys) {
|
|
552
554
|
const now = afterLaw[k];
|
|
553
|
-
out2(` key '${k}' now serves: ${now !== void 0 ? short(now) : "NO
|
|
555
|
+
out2(` key '${k}' now serves: ${now !== void 0 ? short(now) : "NO active law"}`);
|
|
554
556
|
}
|
|
555
557
|
if (stranded.length > 0 && retiredKeys.some((k) => afterLaw[k] === void 0)) {
|
|
556
|
-
err2(`\u26A0 retiring left these keys with NO
|
|
558
|
+
err2(`\u26A0 retiring left these keys with NO active law: ${stranded.join(", ")} \u2014 nothing dispatches/reads them now`);
|
|
557
559
|
err2(` (deploy law covering them, or this is intentional \u2014 the kernel allows an unserved key)`);
|
|
558
560
|
}
|
|
559
561
|
return 0;
|
|
560
562
|
}
|
|
563
|
+
function domainLifecycleState(row, currentHashes) {
|
|
564
|
+
if (row.lifecycle === "active" || row.lifecycle === "superseded" || row.lifecycle === "retired") return row.lifecycle;
|
|
565
|
+
if (row.lifecycle === "current") return "active";
|
|
566
|
+
const hash = row.domainHash;
|
|
567
|
+
switch (row.phase) {
|
|
568
|
+
case "Active":
|
|
569
|
+
return row.current === true || hash !== void 0 && currentHashes.has(hash) ? "active" : "superseded";
|
|
570
|
+
case "Superseded":
|
|
571
|
+
return "superseded";
|
|
572
|
+
case "Retired":
|
|
573
|
+
return "retired";
|
|
574
|
+
case "Pending":
|
|
575
|
+
return "pending";
|
|
576
|
+
case "Retiring":
|
|
577
|
+
return "retiring";
|
|
578
|
+
case "Disabled":
|
|
579
|
+
return "disabled";
|
|
580
|
+
case "Failed":
|
|
581
|
+
return "failed";
|
|
582
|
+
default:
|
|
583
|
+
return "unknown";
|
|
584
|
+
}
|
|
585
|
+
}
|
|
561
586
|
async function domainsStatus(ws, opts) {
|
|
562
587
|
const cloud = cloudBase(opts.cloud);
|
|
563
588
|
const law = await fetchInstalledLaw(cloud, ws);
|
|
@@ -567,43 +592,49 @@ async function domainsStatus(ws, opts) {
|
|
|
567
592
|
}
|
|
568
593
|
const currentLaw = law.currentLaw ?? {};
|
|
569
594
|
const rows = law.domains ?? [];
|
|
570
|
-
const
|
|
595
|
+
const servedHashes = new Set(Object.values(currentLaw));
|
|
596
|
+
const lifecycleOf = (row) => domainLifecycleState(row, servedHashes);
|
|
597
|
+
const lifecycleOfHash = (hash) => {
|
|
598
|
+
const row = rows.find((r) => r.domainHash === hash);
|
|
599
|
+
return row !== void 0 ? lifecycleOf(row) : "unknown";
|
|
600
|
+
};
|
|
571
601
|
const keys = Object.keys(currentLaw).sort();
|
|
572
602
|
out2(`installed law on ${ws} (${cloud})`);
|
|
573
603
|
if (keys.length === 0) {
|
|
574
|
-
out2(` (no domain key
|
|
604
|
+
out2(` (no domain key has active law \u2014 nothing is dispatched/read here)`);
|
|
575
605
|
} else {
|
|
576
|
-
out2(`
|
|
606
|
+
out2(`active law per domain key:`);
|
|
577
607
|
for (const key of keys) {
|
|
578
608
|
const hash = currentLaw[key];
|
|
579
|
-
out2(` ${key} \u2192 ${short(hash)} [${
|
|
609
|
+
out2(` ${key} \u2192 ${short(hash)} [${lifecycleOfHash(hash)}]`);
|
|
580
610
|
}
|
|
581
611
|
}
|
|
582
|
-
const servedHashes = new Set(Object.values(currentLaw));
|
|
583
612
|
const behind = rows.filter((r) => typeof r.domainHash === "string" && !servedHashes.has(r.domainHash));
|
|
584
613
|
if (behind.length > 0) {
|
|
585
|
-
out2(`superseded
|
|
614
|
+
out2(`superseded/retired lineage (replayable, not served):`);
|
|
586
615
|
for (const r of behind) {
|
|
587
|
-
out2(` ${short(r.domainHash)} [${r
|
|
616
|
+
out2(` ${short(r.domainHash)} [${lifecycleOf(r)}]${r.generation !== void 0 ? ` gen ${r.generation}` : ""}`);
|
|
588
617
|
}
|
|
589
618
|
} else if (rows.length > 0) {
|
|
590
|
-
out2(`no superseded
|
|
619
|
+
out2(`no superseded or retired installs \u2014 every install is active`);
|
|
591
620
|
}
|
|
592
621
|
if (opts.explain === true) {
|
|
593
622
|
out2(``);
|
|
594
|
-
out2(`
|
|
595
|
-
|
|
596
|
-
out2(
|
|
597
|
-
out2(`
|
|
623
|
+
out2(`domain install lifecycle:`);
|
|
624
|
+
for (const line of DOMAIN_LIFECYCLE_DX) out2(` - ${line}`);
|
|
625
|
+
out2(``);
|
|
626
|
+
out2(`why this is the active interface:`);
|
|
627
|
+
out2(` the holon resolves active law per domain key and every live surface \u2014 interface identity, reads,`);
|
|
628
|
+
out2(` and dispatch \u2014 reads from that map, so superseded/retired installs cannot pollute it.`);
|
|
598
629
|
for (const key of keys) {
|
|
599
630
|
const hash = currentLaw[key];
|
|
600
|
-
out2(` \u2022 '${key}' serves ${short(hash)} \u2014
|
|
631
|
+
out2(` \u2022 '${key}' serves ${short(hash)} \u2014 state: active.`);
|
|
601
632
|
}
|
|
602
633
|
if (behind.length > 0) {
|
|
603
|
-
const excluded = behind.filter((r) => EXCLUDED_PHASES.has(r.phase ?? "") || !servedHashes.has(r.domainHash)).map((r) => `${short(r.domainHash)} [${r
|
|
634
|
+
const excluded = behind.filter((r) => EXCLUDED_PHASES.has(r.phase ?? "") || !servedHashes.has(r.domainHash)).map((r) => `${short(r.domainHash)} [${lifecycleOf(r)}]`);
|
|
604
635
|
out2(` \u2022 excluded from the interface (replayable, not served): ${excluded.join(", ")}`);
|
|
605
636
|
}
|
|
606
|
-
out2(` interface identity per key = the
|
|
637
|
+
out2(` interface identity per key = the active hash above; older installs are lineage, not surface.`);
|
|
607
638
|
}
|
|
608
639
|
return 0;
|
|
609
640
|
}
|
|
@@ -611,7 +642,7 @@ function signingAvailable(opts) {
|
|
|
611
642
|
const principal = opts.principal ?? activePrincipal();
|
|
612
643
|
return opts.principal !== void 0 || principal !== void 0 && getDeviceKey(principal) !== void 0;
|
|
613
644
|
}
|
|
614
|
-
var out2, err2, short,
|
|
645
|
+
var out2, err2, short, EXCLUDED_PHASES, DOMAIN_LIFECYCLE_DX;
|
|
615
646
|
var init_lifecycle = __esm({
|
|
616
647
|
"src/lifecycle.ts"() {
|
|
617
648
|
"use strict";
|
|
@@ -619,8 +650,12 @@ var init_lifecycle = __esm({
|
|
|
619
650
|
out2 = (s) => void process.stdout.write(s + "\n");
|
|
620
651
|
err2 = (s) => void process.stderr.write("error: " + s + "\n");
|
|
621
652
|
short = (h) => h.length > 16 ? h.slice(0, 16) + "\u2026" : h;
|
|
622
|
-
SERVED_PHASE = "Active";
|
|
623
653
|
EXCLUDED_PHASES = /* @__PURE__ */ new Set(["Superseded", "Retired", "Disabled", "Failed", "Pending", "Retiring"]);
|
|
654
|
+
DOMAIN_LIFECYCLE_DX = [
|
|
655
|
+
"active: served now.",
|
|
656
|
+
"superseded: replaced by a newer install for the same domain key; replayable, not served.",
|
|
657
|
+
"retired: explicitly withdrawn; replayable, not served."
|
|
658
|
+
];
|
|
624
659
|
}
|
|
625
660
|
});
|
|
626
661
|
|
|
@@ -1088,7 +1123,7 @@ async function deploy(ws, opts) {
|
|
|
1088
1123
|
return 1;
|
|
1089
1124
|
}
|
|
1090
1125
|
const phase = d.installation?.[0]?.data?.["status.phase"];
|
|
1091
|
-
phaseSuffix = typeof phase === "string" ? ` (${phase})` : "";
|
|
1126
|
+
phaseSuffix = phase === "Active" ? " (active)" : typeof phase === "string" ? ` (${phase.toLowerCase()})` : "";
|
|
1092
1127
|
deployedHash = typeof d.domainHash === "string" ? d.domainHash : void 0;
|
|
1093
1128
|
}
|
|
1094
1129
|
const after = await fetch(`${cloud}/v2/workspaces/${ws}/domains`).then((ar) => ar.json()).then((ad) => ad.ok === true ? ad : {}).catch(() => ({}));
|
|
@@ -1099,31 +1134,31 @@ async function deploy(ws, opts) {
|
|
|
1099
1134
|
if (plan === void 0 || targetHash === void 0 || plan.decisions.length === 0) return;
|
|
1100
1135
|
for (const line of describeDeployPlan2(plan, targetHash)) out3(line);
|
|
1101
1136
|
if (plan.replaceCandidates.length > 1) {
|
|
1102
|
-
out3(` \u26A0 the package's keys were
|
|
1137
|
+
out3(` \u26A0 the package's keys were active on ${plan.replaceCandidates.length} different installs \u2014 installDomain supersedes one (${plan.replaces.slice(0, 16)}\u2026); this deploy is active for every key.`);
|
|
1103
1138
|
}
|
|
1104
1139
|
if (!signed && plan.replaces !== void 0 && !opts.keepBeside) {
|
|
1105
|
-
out3(` note: the OPEN (unsigned) lane installs
|
|
1140
|
+
out3(` note: the OPEN (unsigned) lane installs beside \u2014 the new law is active, and the prior install is effectively superseded but not marked Superseded. Sign with --as <uid> to record the supersession.`);
|
|
1106
1141
|
}
|
|
1107
1142
|
};
|
|
1108
1143
|
if (deployedHash !== void 0 && isCurrent) {
|
|
1109
1144
|
out3(`\u2713 deployed ${fileName} \u2192 ${ws}${phaseSuffix}`);
|
|
1110
1145
|
out3(` law ${describeLawMove(before, deployedHash)}`);
|
|
1111
1146
|
emitPlan();
|
|
1112
|
-
out3(` \u2713
|
|
1147
|
+
out3(` \u2713 active \u2014 dispatch + reads now resolve ${deployedHash.slice(0, 16)}\u2026`);
|
|
1113
1148
|
return 0;
|
|
1114
1149
|
}
|
|
1115
1150
|
if (deployedHash === void 0 || !hasCurrencyData) {
|
|
1116
1151
|
out3(`\u2713 deployed ${fileName} \u2192 ${ws}${phaseSuffix}`);
|
|
1117
1152
|
if (deployedHash !== void 0) out3(` law ${describeLawMove(before, deployedHash)}`);
|
|
1118
1153
|
emitPlan();
|
|
1119
|
-
out3(` \u26A0 couldn't confirm it is
|
|
1154
|
+
out3(` \u26A0 couldn't confirm it is active (this workspace did not report active-law resolution).`);
|
|
1120
1155
|
return 0;
|
|
1121
1156
|
}
|
|
1122
|
-
err3(`deployed ${fileName} \u2192 ${ws}${phaseSuffix}, but it is NOT
|
|
1123
|
-
out3(` law ${describeLawMove(before, deployedHash)} (committed as an install, but the holon does not resolve it as
|
|
1157
|
+
err3(`deployed ${fileName} \u2192 ${ws}${phaseSuffix}, but it is NOT active \u2014 the upgrade did not take effect`);
|
|
1158
|
+
out3(` law ${describeLawMove(before, deployedHash)} (committed as an install, but the holon does not resolve it as active)`);
|
|
1124
1159
|
const leads = (after.domains ?? []).filter((x) => x.current === true && typeof x.domainHash === "string").map((x) => x.domainHash);
|
|
1125
|
-
if (leads.length > 0) err3(`
|
|
1126
|
-
else err3(` the workspace resolves NO
|
|
1160
|
+
if (leads.length > 0) err3(` active: ${leads.map((h) => h.slice(0, 16) + "\u2026").join(", ")}`);
|
|
1161
|
+
else err3(` the workspace resolves NO active law for this domain (materialisation wedged \u2014 the evolve gate did not advance it)`);
|
|
1127
1162
|
err3(` re-run \`githolon deploy ${ws}\`; if it persists, the deploy committed but the workspace kept the old law (report it).`);
|
|
1128
1163
|
return 2;
|
|
1129
1164
|
}
|
|
@@ -1478,8 +1513,12 @@ async function createEngine({ wasmModule, bootstrapPkg, nomosPkg, replica, insta
|
|
|
1478
1513
|
root.set("ws", new Directory(/* @__PURE__ */ new Map()));
|
|
1479
1514
|
const preopen = new PreopenDirectory("/work", root);
|
|
1480
1515
|
const STDERR = [];
|
|
1516
|
+
const pushStderr = (l) => {
|
|
1517
|
+
if (STDERR.length >= 500) STDERR.splice(0, STDERR.length - 499);
|
|
1518
|
+
STDERR.push(l);
|
|
1519
|
+
};
|
|
1481
1520
|
const fds = [new OpenFile(new File([])), ConsoleStdout.lineBuffered(() => {
|
|
1482
|
-
}), ConsoleStdout.lineBuffered(
|
|
1521
|
+
}), ConsoleStdout.lineBuffered(pushStderr), preopen];
|
|
1483
1522
|
const wasi = new WASI(["wasm_git_holon", "reactor"], [], fds, { debug: false });
|
|
1484
1523
|
const inst = await WebAssembly.instantiate(wasmModule, { wasi_snapshot_preview1: wasi.wasiImport });
|
|
1485
1524
|
const code = wasi.start(inst);
|
|
@@ -1624,24 +1663,10 @@ function parseAttestedReadNeed(error) {
|
|
|
1624
1663
|
}
|
|
1625
1664
|
return null;
|
|
1626
1665
|
}
|
|
1627
|
-
function offerIntent(eng, ws, bytes, opts) {
|
|
1628
|
-
const name = `offer-in-${eng.seq++}.json`;
|
|
1629
|
-
writeWork(eng, name, bytes);
|
|
1630
|
-
const deferProjection = !!(opts && opts.defer);
|
|
1631
|
-
const branch = opts && opts.branch || BRANCH;
|
|
1632
|
-
return JSON.parse(call(eng.ex, "offer", { repoArg: repoArgOf(ws), workspace: ws, intentFile: `/work/${name}`, branch, deferProjection }, eng.STDERR));
|
|
1633
|
-
}
|
|
1634
|
-
function offerAdmit(eng, ws, bytes, opts) {
|
|
1635
|
-
const v = offerIntent(eng, ws, bytes, opts);
|
|
1636
|
-
return v.outcome === "admitted" ? { ok: true, head: v.head, ...v.born ? { born: v.born } : {} } : { ok: false, error: v.verdict?.reason ?? v.error ?? "the offer was refused" };
|
|
1637
|
-
}
|
|
1638
|
-
function applyIntentBytes(eng, ws, bytes, opts) {
|
|
1639
|
-
return offerAdmit(eng, ws, bytes, opts);
|
|
1640
|
-
}
|
|
1641
1666
|
function verifyChainLocal(eng, ws) {
|
|
1642
1667
|
return JSON.parse(call(eng.ex, "query", { repoArg: repoArgOf(ws), workspace: ws, queryBytes: b64Json({ op: "verifyChain" }), branch: BRANCH }, eng.STDERR));
|
|
1643
1668
|
}
|
|
1644
|
-
var enc3, dec3, BRANCH, RESULT_PACK_INLINE_MAX, repoArgOf, gitdirOf, unpack, repoArgOfGitdir, bytesFromB64, kgit, installPayload, fullRef, applyPackInto, custodyLsRefs, qById, query, attestedRead, cryptoUnwrapKey, count, sum;
|
|
1669
|
+
var enc3, dec3, BRANCH, RESULT_PACK_INLINE_MAX, repoArgOf, gitdirOf, unpack, repoArgOfGitdir, bytesFromB64, kgit, installPayload, fullRef, applyPackInto, custodyLsRefs, qById, query, attestedRead, cryptoUnwrapKey, count, sum, spatialWithin;
|
|
1645
1670
|
var init_engine = __esm({
|
|
1646
1671
|
"vendor/engine/engine.mjs"() {
|
|
1647
1672
|
"use strict";
|
|
@@ -1707,6 +1732,7 @@ var init_engine = __esm({
|
|
|
1707
1732
|
cryptoUnwrapKey = (eng, { secret, hpkeEpk, ct }) => JSON.parse(call(eng.ex, "query", { queryBytes: b64Json({ op: "cryptoUnwrapKey", secret, hpkeEpk, ct }) }, eng.STDERR)).scopeKey;
|
|
1708
1733
|
count = (eng, ws, countId, groupKey, principal = "") => JSON.parse(call(eng.ex, "query", { repoArg: repoArgOf(ws), workspace: ws, queryBytes: b64Json({ op: "count", countId, groupKey }), principal, branch: BRANCH }, eng.STDERR));
|
|
1709
1734
|
sum = (eng, ws, sumId, groupKey, principal = "") => JSON.parse(call(eng.ex, "query", { repoArg: repoArgOf(ws), workspace: ws, queryBytes: b64Json({ op: "sum", sumId, groupKey }), principal, branch: BRANCH }, eng.STDERR));
|
|
1735
|
+
spatialWithin = (eng, ws, spatialId, bbox, principal = "") => JSON.parse(call(eng.ex, "query", { repoArg: repoArgOf(ws), workspace: ws, queryBytes: b64Json({ op: "spatial", spatialId, minLng: bbox.minLng, minLat: bbox.minLat, maxLng: bbox.maxLng, maxLat: bbox.maxLat }), principal, branch: BRANCH }, eng.STDERR));
|
|
1710
1736
|
}
|
|
1711
1737
|
});
|
|
1712
1738
|
|
|
@@ -1889,32 +1915,55 @@ async function runOfflineLegs(eng, ws, lawHash, legs, frameworkHash, deployUsda)
|
|
|
1889
1915
|
});
|
|
1890
1916
|
const lines = [];
|
|
1891
1917
|
const kinds = legs.legs ?? (legs.directiveId !== void 0 ? ["standalone-provable"] : []);
|
|
1918
|
+
const createLegs = [];
|
|
1892
1919
|
if (legs.directiveId !== void 0) {
|
|
1893
|
-
|
|
1920
|
+
createLegs.push({
|
|
1921
|
+
directiveId: legs.directiveId,
|
|
1922
|
+
...legs.aggregateId !== void 0 ? { aggregateId: legs.aggregateId } : {},
|
|
1923
|
+
...legs.payload !== void 0 ? { payload: legs.payload } : {},
|
|
1924
|
+
...legs.autoStamped !== void 0 ? { autoStamped: legs.autoStamped } : {},
|
|
1925
|
+
...legs.mintField !== void 0 ? { mintField: legs.mintField } : {},
|
|
1926
|
+
...legs.query !== void 0 ? { query: legs.query } : {},
|
|
1927
|
+
...legs.count !== void 0 ? { count: legs.count } : {},
|
|
1928
|
+
...legs.spatial !== void 0 ? { spatial: legs.spatial } : {}
|
|
1929
|
+
});
|
|
1930
|
+
for (const e of legs.extraCreates ?? []) createLegs.push({ ...e });
|
|
1931
|
+
}
|
|
1932
|
+
for (const leg of createLegs) {
|
|
1933
|
+
const payload = { ...leg.payload };
|
|
1894
1934
|
const stamp = (/* @__PURE__ */ new Date()).toISOString();
|
|
1895
|
-
for (const f of
|
|
1896
|
-
if (
|
|
1897
|
-
payload[
|
|
1935
|
+
for (const f of leg.autoStamped ?? []) payload[f] = stamp;
|
|
1936
|
+
if (leg.mintField !== void 0 && leg.aggregateId !== void 0) {
|
|
1937
|
+
payload[leg.mintField] = mintId(eng, leg.aggregateId);
|
|
1898
1938
|
}
|
|
1899
|
-
const res = author(eng, ws, legs.domain,
|
|
1939
|
+
const res = author(eng, ws, legs.domain, leg.directiveId, payload, lawHash);
|
|
1900
1940
|
if (res.ok === false) {
|
|
1901
|
-
return done(false, lines, `dispatch ${legs.domain}/${
|
|
1941
|
+
return done(false, lines, `dispatch ${legs.domain}/${leg.directiveId} refused: ${res.error ?? "unknown"} \u2014 the law itself rejected its own synthesized sample; check the directive's plan/payload schema`);
|
|
1902
1942
|
}
|
|
1903
1943
|
const createdId = (sealedIntentOf(eng, ws, res)?.events ?? []).find((e) => e.marker === "Create")?.aggregate;
|
|
1904
|
-
lines.push(`\u2713 dispatch ${legs.domain}/${
|
|
1905
|
-
if (
|
|
1906
|
-
const rows = query(eng, ws,
|
|
1944
|
+
lines.push(`\u2713 dispatch ${legs.domain}/${leg.directiveId} \u2014 offline write under the new law${createdId !== void 0 ? ` (${createdId.slice(0, 28)}\u2026)` : ""}`);
|
|
1945
|
+
if (leg.query !== void 0) {
|
|
1946
|
+
const rows = query(eng, ws, leg.query.id, JSON.stringify(leg.query.params));
|
|
1907
1947
|
if (rows.length !== 1 || createdId !== void 0 && rows[0].id !== createdId) {
|
|
1908
|
-
return done(false, lines, `declared query ${
|
|
1948
|
+
return done(false, lines, `declared query ${leg.query.id} expected the 1 created row, got ${JSON.stringify(rows.map((r) => r.id))} \u2014 check the query's key fields against the directive's payload`);
|
|
1909
1949
|
}
|
|
1910
|
-
lines.push(`\u2713 declared query ${
|
|
1950
|
+
lines.push(`\u2713 declared query ${leg.query.id} answers locally \u2014 1 row`);
|
|
1911
1951
|
}
|
|
1912
|
-
if (
|
|
1913
|
-
const c = count(eng, ws,
|
|
1952
|
+
if (leg.count !== void 0) {
|
|
1953
|
+
const c = count(eng, ws, leg.count.id, leg.count.group).count;
|
|
1914
1954
|
if (c !== 1) {
|
|
1915
|
-
return done(false, lines, `declared count ${
|
|
1955
|
+
return done(false, lines, `declared count ${leg.count.id}(${JSON.stringify(leg.count.group)}) expected 1, got ${c} \u2014 check the count's grouping against the created row`);
|
|
1956
|
+
}
|
|
1957
|
+
lines.push(`\u2713 declared count ${leg.count.id}(${JSON.stringify(leg.count.group)}) = 1 locally`);
|
|
1958
|
+
}
|
|
1959
|
+
if (leg.spatial !== void 0) {
|
|
1960
|
+
const world = { minLng: -180, minLat: -90, maxLng: 180, maxLat: 90 };
|
|
1961
|
+
const sr = spatialWithin(eng, ws, leg.spatial.id, world);
|
|
1962
|
+
const ids = (sr.rows ?? []).map((r) => r.id);
|
|
1963
|
+
if (sr.ok === false || createdId === void 0 || !ids.includes(createdId)) {
|
|
1964
|
+
return done(false, lines, `spatial ${leg.spatial.id} membership: expected the created row ${createdId ?? "?"} in the world-bbox probe, got ${JSON.stringify(ids)}${sr.error ? ` (${sr.error})` : ""} \u2014 check the indexed geometry path against the directive's payload`);
|
|
1916
1965
|
}
|
|
1917
|
-
lines.push(`\u2713
|
|
1966
|
+
lines.push(`\u2713 spatial ${leg.spatial.id} \u2014 the created row answers the R*Tree probe (index membership proven)`);
|
|
1918
1967
|
}
|
|
1919
1968
|
}
|
|
1920
1969
|
if (legs.childBirth !== void 0) {
|
|
@@ -1940,6 +1989,13 @@ function runChildBirthLeg(eng, ws, lawHash, cb, frameworkHash) {
|
|
|
1940
1989
|
const t0 = performance.now();
|
|
1941
1990
|
const lines = [];
|
|
1942
1991
|
const done = (ok, error) => ({ ok, ms: performance.now() - t0, legs: lines, ...error !== void 0 ? { error } : {} });
|
|
1992
|
+
if (cb.recipeIssues !== void 0 && cb.recipeIssues.length > 0) {
|
|
1993
|
+
return done(
|
|
1994
|
+
false,
|
|
1995
|
+
`child birth ${cb.parentDomain}/${cb.birthDirectiveId} \u2014 the genesis recipe has ${cb.recipeIssues.length} compile-diagnosed issue(s); fix these and recompile:
|
|
1996
|
+
- ${cb.recipeIssues.join("\n - ")}`
|
|
1997
|
+
);
|
|
1998
|
+
}
|
|
1943
1999
|
const fw = frameworkHash ?? eng.hashes?.nomos;
|
|
1944
2000
|
const payload = { ...cb.birthPayload };
|
|
1945
2001
|
if (cb.frameworkHashField !== void 0) payload[cb.frameworkHashField] = fw;
|
|
@@ -1988,7 +2044,13 @@ function runChildBirthLeg(eng, ws, lawHash, cb, frameworkHash) {
|
|
|
1988
2044
|
}
|
|
1989
2045
|
const childWs = (res.born ?? []).map((b) => b?.workspace).find((w) => typeof w === "string" && w.length > 0);
|
|
1990
2046
|
if (childWs === void 0) {
|
|
1991
|
-
|
|
2047
|
+
const kernelSays = eng.STDERR?.filter((l) => l.includes("birth-effect-failed")).at(-1);
|
|
2048
|
+
if (kernelSays !== void 0) {
|
|
2049
|
+
const hint = /not iterable/.test(kernelSays) ? "\n fix: the named step's plan must RETURN AN ARRAY of ops \u2014 fluent builders (ensure()/create().set(...)) record themselves, so end the plan with `return [];`, or wrap standalone ops in `[ ... ]`. Then recompile and rerun." : "\n fix: the named genesis step is what the child's own gate refused \u2014 correct that directive/payload in the recipe and recompile. A working template: bench/scale/fixtures/estate-birth.";
|
|
2050
|
+
return done(false, `child birth ${cb.parentDomain}/${cb.birthDirectiveId} admitted, but the child genesis FAILED inside the kernel's birth offer-effect:
|
|
2051
|
+
${kernelSays.replace(/^nomos:\s*/, "")}${hint}`);
|
|
2052
|
+
}
|
|
2053
|
+
return done(false, `child birth ${cb.parentDomain}/${cb.birthDirectiveId} admitted but the offer-effect spawned no child (born=${JSON.stringify(res.born ?? [])}) and the kernel logged no birth-effect failure \u2014 check the recipe carries a genesis chain (birth({ workspace, genesisChain })), and that the runtime wasm is current (~/.holon/runtime)`);
|
|
1992
2054
|
}
|
|
1993
2055
|
lines.push(`\u2713 child birth ${cb.parentDomain}/${cb.birthDirectiveId} \u2014 ${childWs} born (the offer-effect folded the child genesis through the child's OWN gate)`);
|
|
1994
2056
|
const installed = (hash) => {
|
|
@@ -4112,7 +4174,7 @@ var HELP = {
|
|
|
4112
4174
|
},
|
|
4113
4175
|
replay: {
|
|
4114
4176
|
usage: "githolon replay <ws|dir> [--step N] [--json] [--cloud <url>]",
|
|
4115
|
-
what: "The ledger as film: walk a chain (cloud workspace or local holon directory)
|
|
4177
|
+
what: "The ledger as film: walk a chain (cloud workspace or local holon directory), require the\nkernel's from-genesis verify_chain verdict to be green, then render the verified intent stream \u2014\nper step: directive, payload, the rows it changed, running per-type tallies. On a TTY it steps\ninteractively (enter = next, a = run to end, q = quit).",
|
|
4116
4178
|
flags: [
|
|
4117
4179
|
["<ws|dir>", "a workspace name on the cloud, or a local holon directory (githolon ledger init)"],
|
|
4118
4180
|
["--step N", "non-interactive; print the running tallies every N intents"],
|
|
@@ -4244,11 +4306,11 @@ var HELP = {
|
|
|
4244
4306
|
},
|
|
4245
4307
|
deploy: {
|
|
4246
4308
|
usage: "githolon deploy [<ws>] [--as <uid>] [--keep-beside] [--retire-replaced] [--file <deploy.json>] [--cloud <url>] [--target <name>]",
|
|
4247
|
-
what: "Deploy build/*.deploy.json \u2014 kernel-gated, no host secret. DEFAULT = REPLACE-CURRENT: for each domain\nkey in the package it resolves the workspace's
|
|
4309
|
+
what: "Deploy build/*.deploy.json \u2014 kernel-gated, no host secret. DEFAULT = REPLACE-CURRENT: for each domain\nkey in the package it resolves the workspace's active install (the holon's existing `currentLaw` map)\nand passes it as installDomain's `replaces`, so the old install becomes superseded for that key.\nRe-deploying the SAME hash stays idempotent (never self-superseded).\nTWO lanes, picked automatically:\n\u2022 SIGNED (a WARRANTED ws, or when --as / the active `githolon use` principal has a signing key on\n file): signs a `nomos/installDomain` offer (carrying `replaces`) with that key and relays it; the\n kernel re-verifies the signature + judges the installDomain relation. Required for warranted platforms.\n\u2022 OPEN (unwarranted ws / no key): the open POST /domains installs beside. The new law becomes active,\n but the prior install is only effectively superseded unless you sign with --as to record Superseded.\nAfter the POST it CONFIRMS the deployed hash is active (exit 2 if committed-but-not-active) and prints\nwhat moved, per key. With no <ws>, the project's `workspace` binding resolves it.\nLifecycle vocabulary: active = served now; superseded = replayable, not served; retired = explicitly withdrawn, replayable, not served.",
|
|
4248
4310
|
flags: [
|
|
4249
4311
|
["--as <uid>", "deploy as this principal via the SIGNED lane (needs its key \u2014 githolon key import; else githolon use)"],
|
|
4250
|
-
["--keep-beside", "install
|
|
4251
|
-
["--retire-replaced", "supersede via Retired
|
|
4312
|
+
["--keep-beside", "install beside the prior law (the old multi-version default; prior becomes effectively superseded by recency)"],
|
|
4313
|
+
["--retire-replaced", "supersede via Retired \u2014 the prior install is explicitly withdrawn"],
|
|
4252
4314
|
["--file <path>", "an explicit deploy body (default: the one build/*.deploy.json)"],
|
|
4253
4315
|
["--target <name>", "pick among named targets (workspace: { dev: \u2026, prod: \u2026 })"],
|
|
4254
4316
|
["--cloud <url>", "target cloud"]
|
|
@@ -4257,7 +4319,7 @@ var HELP = {
|
|
|
4257
4319
|
},
|
|
4258
4320
|
domains: {
|
|
4259
4321
|
usage: "githolon domains <status <ws> [--explain-current-interface] | retire <ws> <hashOrKey> [--with <hashOrKey>]>",
|
|
4260
|
-
what: "The INSTALL-LIFECYCLE lane over a workspace's law.\n\u2022 status: per domain KEY, the
|
|
4322
|
+
what: "The INSTALL-LIFECYCLE lane over a workspace's law.\n\u2022 status: per domain KEY, the active hash, then the superseded/retired lineage behind it.\n Lifecycle vocabulary: active = served now; superseded = replaced by a newer install for the same domain\n key, replayable, not served; retired = explicitly withdrawn, replayable, not served.\n --explain-current-interface spells out why active reads/dispatch use only that active map.\n\u2022 retire: mark an install Retired via a signed installDomain offer. The framework retires only as the\n consequence of installing a SUCCESSOR \u2014 by default the local compiled package (build/*.deploy.json),\n or --with <hashOrKey> names an already-installed successor. A key the successor does not cover is left\n with NO active law \u2014 WARNED loudly, never blocked. A key resolves to its active hash; a 64-hex arg is\n taken as the install hash.",
|
|
4261
4323
|
flags: [
|
|
4262
4324
|
["--explain-current-interface", "status: also explain the frontier resolution (why the interface is what it is)"],
|
|
4263
4325
|
["--with <hashOrKey>", "retire: the retiring successor package \u2014 an already-installed hash or served key"],
|
|
@@ -4372,7 +4434,6 @@ import git2 from "isomorphic-git";
|
|
|
4372
4434
|
var out8 = (s) => void process.stdout.write(s + "\n");
|
|
4373
4435
|
var err8 = (s) => void process.stderr.write("error: " + s + "\n");
|
|
4374
4436
|
var SOURCE = "source";
|
|
4375
|
-
var REPLAY = "replay";
|
|
4376
4437
|
function summarizePayload(payload, max = 100) {
|
|
4377
4438
|
if (payload === void 0) return "\u2014";
|
|
4378
4439
|
const parts = [];
|
|
@@ -4488,32 +4549,26 @@ async function replay(target, opts) {
|
|
|
4488
4549
|
err8(`the chain on '${target}' carries no intents \u2014 nothing to replay`);
|
|
4489
4550
|
return 1;
|
|
4490
4551
|
}
|
|
4491
|
-
|
|
4552
|
+
const verdict = verifyChainLocal(eng, SOURCE);
|
|
4553
|
+
if (verdict["valid"] !== true) {
|
|
4554
|
+
if (json) emit({ finale: true, verdict });
|
|
4555
|
+
else printVerdict(verdict);
|
|
4556
|
+
return 1;
|
|
4557
|
+
}
|
|
4492
4558
|
const interactive = !json && opts.step === void 0 && process.stdout.isTTY === true && process.stdin.isTTY === true;
|
|
4493
4559
|
const stepEvery = opts.step ?? (json ? 0 : 0);
|
|
4494
4560
|
const tallies = /* @__PURE__ */ new Map();
|
|
4561
|
+
const seenRows = /* @__PURE__ */ new Set();
|
|
4495
4562
|
let autoRun = !interactive;
|
|
4496
4563
|
if (!json) {
|
|
4497
|
-
out8(`replaying ${chain.length} intent(s) from ${isDir ? target : `${cloud} :: ${target}`} \u2014
|
|
4564
|
+
out8(`replaying ${chain.length} intent(s) from ${isDir ? target : `${cloud} :: ${target}`} \u2014 verify_chain green, rendering the sealed event film`);
|
|
4498
4565
|
if (interactive) out8(" keys: enter = next intent \xB7 a = run to the end \xB7 q = quit\n");
|
|
4499
4566
|
}
|
|
4500
4567
|
for (let i = 0; i < chain.length; i++) {
|
|
4501
|
-
const { oid,
|
|
4568
|
+
const { oid, doc } = chain[i];
|
|
4502
4569
|
const domain = doc.payload?.domain ?? "?";
|
|
4503
4570
|
const directive = doc.payload?.directiveId ?? "?";
|
|
4504
|
-
const
|
|
4505
|
-
const before = new Map(touched.map((id) => [id, qById(eng, REPLAY, id)[0]?.data]));
|
|
4506
|
-
const t0 = performance.now();
|
|
4507
|
-
const res = applyIntentBytes(eng, REPLAY, bytes);
|
|
4508
|
-
const ms = performance.now() - t0;
|
|
4509
|
-
if (res.ok === false) {
|
|
4510
|
-
const msg = `replay REFUSED at intent ${i} (${domain}/${directive}, ${oid.slice(0, 10)}): ${res.error ?? "unknown"}`;
|
|
4511
|
-
if (json) emit({ step: i, oid, domain, directive, refused: true, error: res.error ?? "unknown" });
|
|
4512
|
-
else err8(msg);
|
|
4513
|
-
err8("the chain's own gate rejects this entry on a fresh fold \u2014 the source it came from would refuse it too (verify below names the check)");
|
|
4514
|
-
printVerdict(verifyChainLocal(eng, SOURCE));
|
|
4515
|
-
return 1;
|
|
4516
|
-
}
|
|
4571
|
+
const seenBeforeStep = new Set(seenRows);
|
|
4517
4572
|
for (const ev of doc.events ?? []) {
|
|
4518
4573
|
if (ev.marker === "Create") tallies.set(typeOf(ev), (tallies.get(typeOf(ev)) ?? 0) + 1);
|
|
4519
4574
|
if (ev.marker === "Delete") tallies.set(typeOf(ev), (tallies.get(typeOf(ev)) ?? 1) - 1);
|
|
@@ -4527,16 +4582,16 @@ async function replay(target, opts) {
|
|
|
4527
4582
|
directive,
|
|
4528
4583
|
payload: capJsonStrings(doc.payload?.payload ?? null),
|
|
4529
4584
|
events: (doc.events ?? []).map((e) => ({ aggregate: e.aggregate, marker: e.marker, ops: summarizeOps(e) })),
|
|
4530
|
-
|
|
4585
|
+
verified: true,
|
|
4531
4586
|
tallies: Object.fromEntries(tallies)
|
|
4532
4587
|
});
|
|
4533
4588
|
} else {
|
|
4534
|
-
out8(`[${String(i + 1).padStart(String(chain.length).length)}/${chain.length}] ${domain}/${directive} (${oid.slice(0, 10)}
|
|
4589
|
+
out8(`[${String(i + 1).padStart(String(chain.length).length)}/${chain.length}] ${domain}/${directive} (${oid.slice(0, 10)})`);
|
|
4535
4590
|
out8(` payload ${summarizePayload(doc.payload?.payload)}`);
|
|
4536
4591
|
for (const ev of doc.events ?? []) {
|
|
4537
4592
|
const id = ev.aggregate ?? "?";
|
|
4538
|
-
const
|
|
4539
|
-
const verb = ev.marker === "Create" ? "+ created" : ev.marker === "Delete" ? "- deleted" :
|
|
4593
|
+
const hadRow = typeof ev.aggregate === "string" && seenBeforeStep.has(ev.aggregate);
|
|
4594
|
+
const verb = ev.marker === "Create" ? "+ created" : ev.marker === "Delete" ? "- deleted" : hadRow ? "~ updated" : "~ touched";
|
|
4540
4595
|
out8(` ${verb} ${id}`);
|
|
4541
4596
|
const ops = summarizeOps(ev);
|
|
4542
4597
|
if (ops.length > 0) out8(` ${ops}`);
|
|
@@ -4545,6 +4600,12 @@ async function replay(target, opts) {
|
|
|
4545
4600
|
const showTally = stepEvery > 0 ? (i + 1) % stepEvery === 0 || i === chain.length - 1 : true;
|
|
4546
4601
|
if (showTally && tallyLine.length > 0) out8(` rows ${tallyLine}`);
|
|
4547
4602
|
}
|
|
4603
|
+
for (const ev of doc.events ?? []) {
|
|
4604
|
+
if (typeof ev.aggregate === "string") {
|
|
4605
|
+
if (ev.marker === "Delete") seenRows.delete(ev.aggregate);
|
|
4606
|
+
else seenRows.add(ev.aggregate);
|
|
4607
|
+
}
|
|
4608
|
+
}
|
|
4548
4609
|
if (interactive && !autoRun && i < chain.length - 1) {
|
|
4549
4610
|
const key = await readKey();
|
|
4550
4611
|
if (key === "quit") {
|
|
@@ -4556,7 +4617,6 @@ stopped at intent ${i + 1}/${chain.length} \u2014 the verify verdict below cover
|
|
|
4556
4617
|
}
|
|
4557
4618
|
}
|
|
4558
4619
|
if (!json) out8("");
|
|
4559
|
-
const verdict = verifyChainLocal(eng, SOURCE);
|
|
4560
4620
|
if (json) {
|
|
4561
4621
|
emit({ finale: true, verdict });
|
|
4562
4622
|
return verdict["valid"] === true ? 0 : 1;
|
|
@@ -4631,6 +4691,7 @@ async function decrypt(ws, opts) {
|
|
|
4631
4691
|
// src/status.ts
|
|
4632
4692
|
init_engine();
|
|
4633
4693
|
init_cloud();
|
|
4694
|
+
init_lifecycle();
|
|
4634
4695
|
import { readFileSync as readFileSync10 } from "node:fs";
|
|
4635
4696
|
var out10 = (s) => void process.stdout.write(s + "\n");
|
|
4636
4697
|
var err10 = (s) => void process.stderr.write("error: " + s + "\n");
|
|
@@ -4642,39 +4703,39 @@ function lawDiffVerdict(localHash, installed, ws, hasCurrencyData) {
|
|
|
4642
4703
|
const localIsCurrent = currents.some((d) => d.domainHash === localHash);
|
|
4643
4704
|
const lines = [];
|
|
4644
4705
|
if (localIsCurrent) {
|
|
4645
|
-
lines.push(`\u2713 in sync \u2014 your local build
|
|
4646
|
-
if (priors.length > 0) lines.push(` (${priors.length} prior deploy(s)
|
|
4706
|
+
lines.push(`\u2713 in sync \u2014 your local build is active (${localHash.slice(0, 16)}\u2026, served now)`);
|
|
4707
|
+
if (priors.length > 0) lines.push(` (${priors.length} superseded prior deploy(s) are replayable, not served)`);
|
|
4647
4708
|
return { inSync: true, lines };
|
|
4648
4709
|
}
|
|
4649
4710
|
if (!hasCurrencyData && localActive !== void 0) {
|
|
4650
|
-
lines.push(`~ your local build (${localHash.slice(0, 16)}\u2026) is
|
|
4651
|
-
lines.push(` \u26A0 this workspace did not report
|
|
4711
|
+
lines.push(`~ your local build (${localHash.slice(0, 16)}\u2026) is installed on '${ws}'`);
|
|
4712
|
+
lines.push(` \u26A0 this workspace did not report active-law resolution \u2014 matched by installed presence only; can't confirm it is served now`);
|
|
4652
4713
|
return { inSync: true, lines };
|
|
4653
4714
|
}
|
|
4654
4715
|
if (active.length === 0) {
|
|
4655
4716
|
lines.push(`\u2717 workspace '${ws}' has NO active tenant law \u2014 your local build is not deployed`);
|
|
4656
4717
|
} else if (localActive !== void 0) {
|
|
4657
4718
|
if (currents.length > 0) {
|
|
4658
|
-
lines.push(`~ your build (${localHash.slice(0, 16)}\u2026) is
|
|
4659
|
-
for (const c of currents) lines.push(`
|
|
4719
|
+
lines.push(`~ your build (${localHash.slice(0, 16)}\u2026) is superseded on '${ws}' \u2014 a newer deploy is active:`);
|
|
4720
|
+
for (const c of currents) lines.push(` active ${c.domainHash.slice(0, 16)}\u2026 (${c.installedBy ?? "?"}) \u2190 served now`);
|
|
4660
4721
|
} else {
|
|
4661
|
-
lines.push(`\u2717 your build (${localHash.slice(0, 16)}\u2026) is
|
|
4662
|
-
lines.push(` the workspace resolves NO
|
|
4722
|
+
lines.push(`\u2717 your build (${localHash.slice(0, 16)}\u2026) is installed on '${ws}' but is NOT active \u2014 the deploy did not take effect:`);
|
|
4723
|
+
lines.push(` the workspace resolves NO active law for this domain (materialisation wedged \u2014 the evolve gate did not advance it)`);
|
|
4663
4724
|
}
|
|
4664
|
-
lines.push(` redeploy to make your build
|
|
4725
|
+
lines.push(` redeploy to make your build active: githolon deploy ${ws}`);
|
|
4665
4726
|
return { inSync: false, lines };
|
|
4666
4727
|
} else if (currents.length > 0) {
|
|
4667
|
-
lines.push(`\u2717 local law differs from the
|
|
4668
|
-
for (const c of currents) lines.push(`
|
|
4669
|
-
if (priors.length > 0) lines.push(` + ${priors.length} prior
|
|
4728
|
+
lines.push(`\u2717 local law differs from the active install${currents.length > 1 ? "s" : ""} on '${ws}':`);
|
|
4729
|
+
for (const c of currents) lines.push(` active ${c.domainHash.slice(0, 16)}\u2026 (${c.installedBy ?? "?"}) \u2190 served now`);
|
|
4730
|
+
if (priors.length > 0) lines.push(` + ${priors.length} superseded prior install(s) (replayable, not served)`);
|
|
4670
4731
|
} else {
|
|
4671
|
-
lines.push(`\u2717 local law differs from every
|
|
4732
|
+
lines.push(`\u2717 local law differs from every installed tenant law on '${ws}' (${active.length}):`);
|
|
4672
4733
|
for (const d of active) lines.push(` deployed ${d.domainHash.slice(0, 16)}\u2026 (${d.installedBy ?? "?"})`);
|
|
4673
4734
|
}
|
|
4674
|
-
lines.push(` remedy: githolon deploy ${ws} (installs ${localHash.slice(0, 16)}\u2026 and makes it
|
|
4735
|
+
lines.push(` remedy: githolon deploy ${ws} (installs ${localHash.slice(0, 16)}\u2026 and makes it active)`);
|
|
4675
4736
|
return { inSync: false, lines };
|
|
4676
4737
|
}
|
|
4677
|
-
var
|
|
4738
|
+
var FRAMEWORK_DOMAIN_KEYS = ["nomos", "bootstrap", "workspaces"];
|
|
4678
4739
|
async function status(wsArg, opts) {
|
|
4679
4740
|
const cloud = cloudBase(opts.cloud);
|
|
4680
4741
|
let ws;
|
|
@@ -4704,17 +4765,21 @@ async function status(wsArg, opts) {
|
|
|
4704
4765
|
}
|
|
4705
4766
|
out10(`workspace ${ws} on ${cloud}`);
|
|
4706
4767
|
const allDomains = d.domains ?? [];
|
|
4707
|
-
const
|
|
4708
|
-
const domains = allDomains.filter((dm) => !
|
|
4768
|
+
const frameworkHashes = new Set(FRAMEWORK_DOMAIN_KEYS.map((k) => d.currentLaw?.[k]).filter(Boolean));
|
|
4769
|
+
const domains = allDomains.filter((dm) => !frameworkHashes.has(dm.domainHash));
|
|
4709
4770
|
const controllerCount = allDomains.length - domains.length;
|
|
4710
4771
|
if (allDomains.length === 0) out10(`installed (none \u2014 not even the controller; the workspace may be unborn)`);
|
|
4772
|
+
const currentHashes = new Set(Object.values(d.currentLaw ?? {}));
|
|
4711
4773
|
for (const dom of domains) {
|
|
4712
|
-
const
|
|
4713
|
-
|
|
4774
|
+
const state = domainLifecycleState(dom, currentHashes);
|
|
4775
|
+
const mark = state === "active" ? " \u2190 served now" : "";
|
|
4776
|
+
out10(`law ${(dom.domainHash ?? "?").slice(0, 16)}\u2026 ${state.padEnd(10)} by ${dom.installedBy ?? "?"}${mark}`);
|
|
4714
4777
|
}
|
|
4715
|
-
if (controllerCount > 0) out10(` (+ ${controllerCount}
|
|
4778
|
+
if (controllerCount > 0) out10(` (+ ${controllerCount} framework install(s) \u2014 nomos lifecycle/governance; not tenant law)`);
|
|
4716
4779
|
const tenantActive = domains.filter((dm) => dm.phase === "Active").length;
|
|
4717
|
-
|
|
4780
|
+
const tenantServed = domains.filter((dm) => domainLifecycleState(dm, currentHashes) === "active").length;
|
|
4781
|
+
const tenantSuperseded = domains.filter((dm) => domainLifecycleState(dm, currentHashes) === "superseded").length;
|
|
4782
|
+
if (tenantActive > tenantServed || tenantSuperseded > 0) out10(` (${tenantServed} active, ${tenantSuperseded} superseded tenant install(s); superseded installs are replayable, not served)`);
|
|
4718
4783
|
if (localHash === void 0) {
|
|
4719
4784
|
out10(`local (no build/*.deploy.json here \u2014 run \`githolon compile\` to build the law this project authors)`);
|
|
4720
4785
|
return 0;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "githolon",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.47.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "githolon — the Nomos developer CLI: Rails-style generators for @githolon/dsl domains + the package compiler. Kernel-independent.",
|
|
6
6
|
"license": "SEE LICENSE IN LICENSE.md",
|
|
@@ -29,8 +29,8 @@
|
|
|
29
29
|
},
|
|
30
30
|
"dependencies": {
|
|
31
31
|
"@bjorn3/browser_wasi_shim": "0.4.2",
|
|
32
|
-
"@githolon/client": "^0.
|
|
33
|
-
"@githolon/dsl": "^0.
|
|
32
|
+
"@githolon/client": "^0.47.0",
|
|
33
|
+
"@githolon/dsl": "^0.47.0",
|
|
34
34
|
"isomorphic-git": "^1.38.4"
|
|
35
35
|
},
|
|
36
36
|
"devDependencies": {
|