githolon 0.46.0 → 0.48.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 +137 -64
- 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);
|
|
@@ -1579,7 +1618,7 @@ function author(eng, ws, domain, directiveId, payload, controllerHash, opts = {}
|
|
|
1579
1618
|
writeWork(eng, `envelope-${seq}.json`, enc3.encode(stringifyBig(envelope)));
|
|
1580
1619
|
v = offerOnce();
|
|
1581
1620
|
}
|
|
1582
|
-
const res = v.outcome === "admitted" ? { ok: true, head: v.head, intentOut: v.intentOut, ...v.born ? { born: v.born } : {} } : v.outcome === "refused" ? { ok: false, error: v.verdict?.reason ?? v.error } : v;
|
|
1621
|
+
const res = v.outcome === "admitted" ? { ok: true, head: v.head, intentOut: v.intentOut, ...v.born ? { born: v.born } : {}, ...v.birthOutcomes ? { birthOutcomes: v.birthOutcomes } : {} } : v.outcome === "refused" ? { ok: false, error: v.verdict?.reason ?? v.error } : v;
|
|
1583
1622
|
if (defer && res.ok) (eng.pendingProjection ??= /* @__PURE__ */ new Set()).add(ws);
|
|
1584
1623
|
return res;
|
|
1585
1624
|
}
|
|
@@ -1950,6 +1989,13 @@ function runChildBirthLeg(eng, ws, lawHash, cb, frameworkHash) {
|
|
|
1950
1989
|
const t0 = performance.now();
|
|
1951
1990
|
const lines = [];
|
|
1952
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
|
+
}
|
|
1953
1999
|
const fw = frameworkHash ?? eng.hashes?.nomos;
|
|
1954
2000
|
const payload = { ...cb.birthPayload };
|
|
1955
2001
|
if (cb.frameworkHashField !== void 0) payload[cb.frameworkHashField] = fw;
|
|
@@ -1998,7 +2044,29 @@ function runChildBirthLeg(eng, ws, lawHash, cb, frameworkHash) {
|
|
|
1998
2044
|
}
|
|
1999
2045
|
const childWs = (res.born ?? []).map((b) => b?.workspace).find((w) => typeof w === "string" && w.length > 0);
|
|
2000
2046
|
if (childWs === void 0) {
|
|
2001
|
-
|
|
2047
|
+
const planReturnHint = "\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.";
|
|
2048
|
+
const recipeHint = "\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.";
|
|
2049
|
+
const failing = (res.birthOutcomes ?? []).find((o) => o?.status === "failed" || o?.status === "deferred");
|
|
2050
|
+
if (failing !== void 0) {
|
|
2051
|
+
const reason = String(failing.reason ?? "unknown");
|
|
2052
|
+
const what = failing.status === "deferred" ? `the birth of '${failing.workspace}' was DEFERRED to the edge (no local child custody):
|
|
2053
|
+
${reason}
|
|
2054
|
+
(expected on a host-less/native peer \u2014 the edge materializes the child on next sync; not a law bug)` : `the child genesis of '${failing.workspace}' FAILED inside the kernel's birth offer-effect:
|
|
2055
|
+
${reason}${/not iterable/.test(reason) ? planReturnHint : recipeHint}`;
|
|
2056
|
+
return done(false, `child birth ${cb.parentDomain}/${cb.birthDirectiveId} admitted, but ${what}`);
|
|
2057
|
+
}
|
|
2058
|
+
const kernelSays = eng.STDERR?.filter((l) => l.includes("birth-effect-failed")).at(-1);
|
|
2059
|
+
if (kernelSays !== void 0) {
|
|
2060
|
+
const hint = /not iterable/.test(kernelSays) ? planReturnHint : recipeHint;
|
|
2061
|
+
return done(false, `child birth ${cb.parentDomain}/${cb.birthDirectiveId} admitted, but the child genesis FAILED inside the kernel's birth offer-effect:
|
|
2062
|
+
${kernelSays.replace(/^nomos:\s*/, "")}${hint}`);
|
|
2063
|
+
}
|
|
2064
|
+
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)`);
|
|
2065
|
+
}
|
|
2066
|
+
const downgraded = (res.birthOutcomes ?? []).find((o) => o?.workspace === childWs && o?.status === "unwarranted-cert");
|
|
2067
|
+
if (downgraded !== void 0) {
|
|
2068
|
+
const checks = downgraded.certChecks !== void 0 ? ` certChecks=${JSON.stringify(downgraded.certChecks)}` : "";
|
|
2069
|
+
lines.push(`\u26A0 child ${childWs} was born UNWARRANTED \u2014 ${String(downgraded.reason ?? "cert not seeded")}${checks}`);
|
|
2002
2070
|
}
|
|
2003
2071
|
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)`);
|
|
2004
2072
|
const installed = (hash) => {
|
|
@@ -4254,11 +4322,11 @@ var HELP = {
|
|
|
4254
4322
|
},
|
|
4255
4323
|
deploy: {
|
|
4256
4324
|
usage: "githolon deploy [<ws>] [--as <uid>] [--keep-beside] [--retire-replaced] [--file <deploy.json>] [--cloud <url>] [--target <name>]",
|
|
4257
|
-
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
|
|
4325
|
+
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.",
|
|
4258
4326
|
flags: [
|
|
4259
4327
|
["--as <uid>", "deploy as this principal via the SIGNED lane (needs its key \u2014 githolon key import; else githolon use)"],
|
|
4260
|
-
["--keep-beside", "install
|
|
4261
|
-
["--retire-replaced", "supersede via Retired
|
|
4328
|
+
["--keep-beside", "install beside the prior law (the old multi-version default; prior becomes effectively superseded by recency)"],
|
|
4329
|
+
["--retire-replaced", "supersede via Retired \u2014 the prior install is explicitly withdrawn"],
|
|
4262
4330
|
["--file <path>", "an explicit deploy body (default: the one build/*.deploy.json)"],
|
|
4263
4331
|
["--target <name>", "pick among named targets (workspace: { dev: \u2026, prod: \u2026 })"],
|
|
4264
4332
|
["--cloud <url>", "target cloud"]
|
|
@@ -4267,7 +4335,7 @@ var HELP = {
|
|
|
4267
4335
|
},
|
|
4268
4336
|
domains: {
|
|
4269
4337
|
usage: "githolon domains <status <ws> [--explain-current-interface] | retire <ws> <hashOrKey> [--with <hashOrKey>]>",
|
|
4270
|
-
what: "The INSTALL-LIFECYCLE lane over a workspace's law.\n\u2022 status: per domain KEY, the
|
|
4338
|
+
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.",
|
|
4271
4339
|
flags: [
|
|
4272
4340
|
["--explain-current-interface", "status: also explain the frontier resolution (why the interface is what it is)"],
|
|
4273
4341
|
["--with <hashOrKey>", "retire: the retiring successor package \u2014 an already-installed hash or served key"],
|
|
@@ -4639,6 +4707,7 @@ async function decrypt(ws, opts) {
|
|
|
4639
4707
|
// src/status.ts
|
|
4640
4708
|
init_engine();
|
|
4641
4709
|
init_cloud();
|
|
4710
|
+
init_lifecycle();
|
|
4642
4711
|
import { readFileSync as readFileSync10 } from "node:fs";
|
|
4643
4712
|
var out10 = (s) => void process.stdout.write(s + "\n");
|
|
4644
4713
|
var err10 = (s) => void process.stderr.write("error: " + s + "\n");
|
|
@@ -4650,39 +4719,39 @@ function lawDiffVerdict(localHash, installed, ws, hasCurrencyData) {
|
|
|
4650
4719
|
const localIsCurrent = currents.some((d) => d.domainHash === localHash);
|
|
4651
4720
|
const lines = [];
|
|
4652
4721
|
if (localIsCurrent) {
|
|
4653
|
-
lines.push(`\u2713 in sync \u2014 your local build
|
|
4654
|
-
if (priors.length > 0) lines.push(` (${priors.length} prior deploy(s)
|
|
4722
|
+
lines.push(`\u2713 in sync \u2014 your local build is active (${localHash.slice(0, 16)}\u2026, served now)`);
|
|
4723
|
+
if (priors.length > 0) lines.push(` (${priors.length} superseded prior deploy(s) are replayable, not served)`);
|
|
4655
4724
|
return { inSync: true, lines };
|
|
4656
4725
|
}
|
|
4657
4726
|
if (!hasCurrencyData && localActive !== void 0) {
|
|
4658
|
-
lines.push(`~ your local build (${localHash.slice(0, 16)}\u2026) is
|
|
4659
|
-
lines.push(` \u26A0 this workspace did not report
|
|
4727
|
+
lines.push(`~ your local build (${localHash.slice(0, 16)}\u2026) is installed on '${ws}'`);
|
|
4728
|
+
lines.push(` \u26A0 this workspace did not report active-law resolution \u2014 matched by installed presence only; can't confirm it is served now`);
|
|
4660
4729
|
return { inSync: true, lines };
|
|
4661
4730
|
}
|
|
4662
4731
|
if (active.length === 0) {
|
|
4663
4732
|
lines.push(`\u2717 workspace '${ws}' has NO active tenant law \u2014 your local build is not deployed`);
|
|
4664
4733
|
} else if (localActive !== void 0) {
|
|
4665
4734
|
if (currents.length > 0) {
|
|
4666
|
-
lines.push(`~ your build (${localHash.slice(0, 16)}\u2026) is
|
|
4667
|
-
for (const c of currents) lines.push(`
|
|
4735
|
+
lines.push(`~ your build (${localHash.slice(0, 16)}\u2026) is superseded on '${ws}' \u2014 a newer deploy is active:`);
|
|
4736
|
+
for (const c of currents) lines.push(` active ${c.domainHash.slice(0, 16)}\u2026 (${c.installedBy ?? "?"}) \u2190 served now`);
|
|
4668
4737
|
} else {
|
|
4669
|
-
lines.push(`\u2717 your build (${localHash.slice(0, 16)}\u2026) is
|
|
4670
|
-
lines.push(` the workspace resolves NO
|
|
4738
|
+
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:`);
|
|
4739
|
+
lines.push(` the workspace resolves NO active law for this domain (materialisation wedged \u2014 the evolve gate did not advance it)`);
|
|
4671
4740
|
}
|
|
4672
|
-
lines.push(` redeploy to make your build
|
|
4741
|
+
lines.push(` redeploy to make your build active: githolon deploy ${ws}`);
|
|
4673
4742
|
return { inSync: false, lines };
|
|
4674
4743
|
} else if (currents.length > 0) {
|
|
4675
|
-
lines.push(`\u2717 local law differs from the
|
|
4676
|
-
for (const c of currents) lines.push(`
|
|
4677
|
-
if (priors.length > 0) lines.push(` + ${priors.length} prior
|
|
4744
|
+
lines.push(`\u2717 local law differs from the active install${currents.length > 1 ? "s" : ""} on '${ws}':`);
|
|
4745
|
+
for (const c of currents) lines.push(` active ${c.domainHash.slice(0, 16)}\u2026 (${c.installedBy ?? "?"}) \u2190 served now`);
|
|
4746
|
+
if (priors.length > 0) lines.push(` + ${priors.length} superseded prior install(s) (replayable, not served)`);
|
|
4678
4747
|
} else {
|
|
4679
|
-
lines.push(`\u2717 local law differs from every
|
|
4748
|
+
lines.push(`\u2717 local law differs from every installed tenant law on '${ws}' (${active.length}):`);
|
|
4680
4749
|
for (const d of active) lines.push(` deployed ${d.domainHash.slice(0, 16)}\u2026 (${d.installedBy ?? "?"})`);
|
|
4681
4750
|
}
|
|
4682
|
-
lines.push(` remedy: githolon deploy ${ws} (installs ${localHash.slice(0, 16)}\u2026 and makes it
|
|
4751
|
+
lines.push(` remedy: githolon deploy ${ws} (installs ${localHash.slice(0, 16)}\u2026 and makes it active)`);
|
|
4683
4752
|
return { inSync: false, lines };
|
|
4684
4753
|
}
|
|
4685
|
-
var
|
|
4754
|
+
var FRAMEWORK_DOMAIN_KEYS = ["nomos", "bootstrap", "workspaces"];
|
|
4686
4755
|
async function status(wsArg, opts) {
|
|
4687
4756
|
const cloud = cloudBase(opts.cloud);
|
|
4688
4757
|
let ws;
|
|
@@ -4712,17 +4781,21 @@ async function status(wsArg, opts) {
|
|
|
4712
4781
|
}
|
|
4713
4782
|
out10(`workspace ${ws} on ${cloud}`);
|
|
4714
4783
|
const allDomains = d.domains ?? [];
|
|
4715
|
-
const
|
|
4716
|
-
const domains = allDomains.filter((dm) => !
|
|
4784
|
+
const frameworkHashes = new Set(FRAMEWORK_DOMAIN_KEYS.map((k) => d.currentLaw?.[k]).filter(Boolean));
|
|
4785
|
+
const domains = allDomains.filter((dm) => !frameworkHashes.has(dm.domainHash));
|
|
4717
4786
|
const controllerCount = allDomains.length - domains.length;
|
|
4718
4787
|
if (allDomains.length === 0) out10(`installed (none \u2014 not even the controller; the workspace may be unborn)`);
|
|
4788
|
+
const currentHashes = new Set(Object.values(d.currentLaw ?? {}));
|
|
4719
4789
|
for (const dom of domains) {
|
|
4720
|
-
const
|
|
4721
|
-
|
|
4790
|
+
const state = domainLifecycleState(dom, currentHashes);
|
|
4791
|
+
const mark = state === "active" ? " \u2190 served now" : "";
|
|
4792
|
+
out10(`law ${(dom.domainHash ?? "?").slice(0, 16)}\u2026 ${state.padEnd(10)} by ${dom.installedBy ?? "?"}${mark}`);
|
|
4722
4793
|
}
|
|
4723
|
-
if (controllerCount > 0) out10(` (+ ${controllerCount}
|
|
4794
|
+
if (controllerCount > 0) out10(` (+ ${controllerCount} framework install(s) \u2014 nomos lifecycle/governance; not tenant law)`);
|
|
4724
4795
|
const tenantActive = domains.filter((dm) => dm.phase === "Active").length;
|
|
4725
|
-
|
|
4796
|
+
const tenantServed = domains.filter((dm) => domainLifecycleState(dm, currentHashes) === "active").length;
|
|
4797
|
+
const tenantSuperseded = domains.filter((dm) => domainLifecycleState(dm, currentHashes) === "superseded").length;
|
|
4798
|
+
if (tenantActive > tenantServed || tenantSuperseded > 0) out10(` (${tenantServed} active, ${tenantSuperseded} superseded tenant install(s); superseded installs are replayable, not served)`);
|
|
4726
4799
|
if (localHash === void 0) {
|
|
4727
4800
|
out10(`local (no build/*.deploy.json here \u2014 run \`githolon compile\` to build the law this project authors)`);
|
|
4728
4801
|
return 0;
|
|
@@ -4794,12 +4867,12 @@ deploy is OPEN + kernel-gated \u2014 births return no host secret, ownership is
|
|
|
4794
4867
|
githolon ws reclaim <name> [--parent <ws>] lawful custody discard via a SIGNED governance offer
|
|
4795
4868
|
(only a retired workspace; never root)
|
|
4796
4869
|
githolon deploy [<ws>] [--file <deploy.json>] deploy build/*.deploy.json \u2014 DEFAULT replace-current:
|
|
4797
|
-
[--keep-beside] [--retire-replaced]
|
|
4798
|
-
key (prints what moved). --keep-beside
|
|
4799
|
-
beside; --retire-replaced
|
|
4800
|
-
githolon domains status <ws> per domain key:
|
|
4801
|
-
[--explain-current-interface]
|
|
4802
|
-
githolon domains retire <ws> <hashOrKey> retire an install (marks it
|
|
4870
|
+
[--keep-beside] [--retire-replaced] new law becomes active; prior install is superseded
|
|
4871
|
+
per domain key (prints what moved). --keep-beside
|
|
4872
|
+
installs beside; --retire-replaced marks retired
|
|
4873
|
+
githolon domains status <ws> per domain key: active law plus superseded/retired
|
|
4874
|
+
[--explain-current-interface] lineage (active = served now)
|
|
4875
|
+
githolon domains retire <ws> <hashOrKey> retire an install (marks it retired \u2014 replayable,
|
|
4803
4876
|
[--with <hashOrKey>] no longer served) via a signed installDomain offer
|
|
4804
4877
|
githolon secret set <ws> <push-password> store a git push password (push-to-create birth lane)
|
|
4805
4878
|
githolon secret rotate <ws> RETIRED \u2014 host holds no ownership credential (410)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "githolon",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.48.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.48.0",
|
|
33
|
+
"@githolon/dsl": "^0.48.0",
|
|
34
34
|
"isomorphic-git": "^1.38.4"
|
|
35
35
|
},
|
|
36
36
|
"devDependencies": {
|