githolon 0.100.1 → 0.100.3

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 +102 -15
  2. package/package.json +3 -3
package/dist/cli.mjs CHANGED
@@ -3120,6 +3120,7 @@ __export(governance_exports, {
3120
3120
  renderRebirthImpact: () => renderRebirthImpact,
3121
3121
  resolveGovernanceHash: () => resolveGovernanceHash,
3122
3122
  resolveGovernancePrincipal: () => resolveGovernancePrincipal,
3123
+ signAndPostChildLifecycle: () => signAndPostChildLifecycle,
3123
3124
  signAndPostGovernance: () => signAndPostGovernance,
3124
3125
  signAndPostOffer: () => signAndPostOffer,
3125
3126
  signAndPostWorkspaceBirth: () => signAndPostWorkspaceBirth,
@@ -3433,6 +3434,22 @@ async function signAndPostWorkspaceRetirement(parent, workspaceId, creator, opts
3433
3434
  creator
3434
3435
  );
3435
3436
  }
3437
+ async function signAndPostChildLifecycle(parent, child, creator, action, opts) {
3438
+ return signAndPostPreparedOffer(
3439
+ parent,
3440
+ void 0,
3441
+ (actor) => action === "retire" ? {
3442
+ directiveId: "retireChild",
3443
+ payload: { child, principal: creator, retiredBy: lineageIdentity(actor), retiredAt: Date.now() }
3444
+ } : {
3445
+ directiveId: "reclaimChild",
3446
+ payload: { child, reclaimedBy: lineageIdentity(actor), reclaimedAt: Date.now() }
3447
+ },
3448
+ opts,
3449
+ "self-if-unenrolled",
3450
+ creator
3451
+ );
3452
+ }
3436
3453
  async function relaySealedOfferViaStage(cloud, ws, directiveId, intentBytesB64, fetchImpl) {
3437
3454
  const raw = Buffer.from(intentBytesB64, "base64");
3438
3455
  const sha2562 = createHash("sha256").update(raw).digest("hex");
@@ -5166,6 +5183,7 @@ var init_lifecycle = __esm({
5166
5183
  var cloud_exports = {};
5167
5184
  __export(cloud_exports, {
5168
5185
  activePrincipal: () => activePrincipal,
5186
+ advertisedMainHead: () => advertisedMainHead,
5169
5187
  authUse: () => authUse,
5170
5188
  clearActAs: () => clearActAs,
5171
5189
  cloudBase: () => cloudBase,
@@ -5187,6 +5205,7 @@ __export(cloud_exports, {
5187
5205
  setDeviceKey: () => setDeviceKey,
5188
5206
  setPrincipal: () => setPrincipal,
5189
5207
  setSecret: () => setSecret,
5208
+ waitForDurableMainAdvance: () => waitForDurableMainAdvance,
5190
5209
  whoami: () => whoami,
5191
5210
  wsCreate: () => wsCreate,
5192
5211
  wsReclaim: () => wsReclaim,
@@ -5469,9 +5488,19 @@ async function wsStatus(name, opts) {
5469
5488
  out4(text);
5470
5489
  return 0;
5471
5490
  }
5491
+ async function childBirthRecord(cloud, parent, child) {
5492
+ try {
5493
+ const id = `nomos-birth-record:${child}`;
5494
+ const r = await fetch(`${cloud}/v2/workspaces/${encodeURIComponent(parent)}/aggregates/${encodeURIComponent(id)}`);
5495
+ const body = await r.json().catch(() => void 0);
5496
+ return body?.ok === true ? body.rows?.find((row) => row.data?.child === child) : void 0;
5497
+ } catch {
5498
+ return void 0;
5499
+ }
5500
+ }
5472
5501
  async function wsRetire(ws, opts) {
5473
5502
  const cloud = cloudBase(opts.cloud);
5474
- const { signAndPostWorkspaceRetirement: signAndPostWorkspaceRetirement2 } = await Promise.resolve().then(() => (init_governance(), governance_exports));
5503
+ const { signAndPostChildLifecycle: signAndPostChildLifecycle2, signAndPostWorkspaceRetirement: signAndPostWorkspaceRetirement2 } = await Promise.resolve().then(() => (init_governance(), governance_exports));
5475
5504
  const parent = opts.parent ?? "root";
5476
5505
  let record;
5477
5506
  try {
@@ -5483,12 +5512,29 @@ async function wsRetire(ws, opts) {
5483
5512
  }
5484
5513
  const recordId = record?.id;
5485
5514
  if (recordId === void 0) {
5486
- err4(
5487
- `retire '${ws}': no CloudWorkspace record named '${ws}' in ${parent}'s registry \u2014 nothing lawful to retire.
5488
- (a workspace born via the OPEN lane \u2014 bare POST /v2/workspaces/${ws} \u2014 has no registry row;
5489
- registered births ride \`githolon ws create ${ws} --via ${parent}\`. An unregistered leak needs a custodian sweep.)`
5490
- );
5491
- return 1;
5515
+ const childRecord = await childBirthRecord(cloud, parent, ws);
5516
+ if (childRecord === void 0) {
5517
+ err4(
5518
+ `retire '${ws}': no CloudWorkspace or NomosBirthRecord named '${ws}' in ${parent}'s registry \u2014 nothing lawful to retire.
5519
+ An out-of-band custody repo has no parent lifecycle fact and needs a custodian investigation.`
5520
+ );
5521
+ return 1;
5522
+ }
5523
+ const creator = childRecord.data?.principal ?? "";
5524
+ let childVerdict;
5525
+ try {
5526
+ childVerdict = await signAndPostChildLifecycle2(parent, ws, creator, "retire", opts);
5527
+ } catch (e) {
5528
+ err4(e.message);
5529
+ return 1;
5530
+ }
5531
+ if (!childVerdict.ok) {
5532
+ err4(`retire '${ws}' refused (${childVerdict.status}): ${typeof childVerdict.body === "string" ? childVerdict.body : JSON.stringify(childVerdict.body)}`);
5533
+ return 1;
5534
+ }
5535
+ out4(`\u2713 child ${ws} retired via signed lifecycle offer to ${parent} on ${cloud}`);
5536
+ out4(" custody is retained; reclaim is a separate, law-gated terminal step");
5537
+ return 0;
5492
5538
  }
5493
5539
  let v;
5494
5540
  try {
@@ -5517,17 +5563,30 @@ async function wsRetire(ws, opts) {
5517
5563
  }
5518
5564
  async function wsReclaim(ws, opts) {
5519
5565
  const cloud = cloudBase(opts.cloud);
5520
- const { signAndPostGovernance: signAndPostGovernance2, resolveGovernancePrincipal: resolveGovernancePrincipal2 } = await Promise.resolve().then(() => (init_governance(), governance_exports));
5566
+ const { signAndPostChildLifecycle: signAndPostChildLifecycle2, signAndPostGovernance: signAndPostGovernance2, resolveGovernancePrincipal: resolveGovernancePrincipal2 } = await Promise.resolve().then(() => (init_governance(), governance_exports));
5521
5567
  const parent = opts.parent ?? "root";
5522
5568
  let v;
5523
5569
  try {
5524
5570
  const principal = resolveGovernancePrincipal2(opts);
5525
- v = await signAndPostGovernance2(
5526
- parent,
5527
- "reclaimWorkspace",
5528
- { workspaceId: ws, reclaimedBy: principal, reclaimedAt: Date.now() },
5529
- opts
5530
- );
5571
+ const q = await fetch(`${cloud}/v2/workspaces/${parent}/workspaceByName?name=${encodeURIComponent(ws)}`);
5572
+ const qd = await q.json().catch(() => void 0);
5573
+ const rows = qd?.ok === true ? qd.rows ?? [] : [];
5574
+ const workspace = rows.find((r) => r.data?.status === "retired") ?? rows[0];
5575
+ if (workspace?.id !== void 0) {
5576
+ v = await signAndPostGovernance2(
5577
+ parent,
5578
+ "reclaimWorkspace",
5579
+ { workspaceId: workspace.id, reclaimedBy: principal, reclaimedAt: Date.now() },
5580
+ opts
5581
+ );
5582
+ } else {
5583
+ const child = await childBirthRecord(cloud, parent, ws);
5584
+ if (child === void 0) {
5585
+ err4(`reclaim '${ws}': no CloudWorkspace or NomosBirthRecord named '${ws}' in ${parent}'s registry`);
5586
+ return 1;
5587
+ }
5588
+ v = await signAndPostChildLifecycle2(parent, ws, child.data?.principal ?? "", "reclaim", opts);
5589
+ }
5531
5590
  } catch (e) {
5532
5591
  err4(e.message);
5533
5592
  return 1;
@@ -5536,10 +5595,28 @@ async function wsReclaim(ws, opts) {
5536
5595
  err4(`reclaim '${ws}' refused (${v.status}): ${typeof v.body === "string" ? v.body : JSON.stringify(v.body)}`);
5537
5596
  return 1;
5538
5597
  }
5539
- out4(`\u2713 workspace ${ws} reclaimed via signed governance offer to ${parent} on ${cloud}`);
5598
+ out4(`\u2713 workspace ${ws} reclaimed via signed lifecycle offer to ${parent} on ${cloud}`);
5540
5599
  out4(" the custodian drops the ledger bytes on seeing this fact folded; the reclaimed FACT is append-only");
5541
5600
  return 0;
5542
5601
  }
5602
+ function advertisedMainHead(text) {
5603
+ return /([0-9a-f]{40}) refs\/heads\/main\b/.exec(text)?.[1];
5604
+ }
5605
+ async function durableMainHead(cloud, ws, fetchImpl = fetch) {
5606
+ return fetchImpl(`${cloud}/v2/workspaces/${encodeURIComponent(ws)}/git/info/refs?service=git-upload-pack`).then(async (r) => r.ok ? advertisedMainHead(await r.text()) : void 0).catch(() => void 0);
5607
+ }
5608
+ async function waitForDurableMainAdvance(cloud, ws, previousHead, opts = {}) {
5609
+ const timeoutMs = Math.max(100, Number(opts.timeoutMs) || 3e4);
5610
+ const pollMs = Math.max(1, Number(opts.pollMs) || 250);
5611
+ const fetchImpl = opts.fetchImpl ?? fetch;
5612
+ const deadline = Date.now() + timeoutMs;
5613
+ while (Date.now() < deadline) {
5614
+ const head = await durableMainHead(cloud, ws, fetchImpl);
5615
+ if (head !== void 0 && head !== previousHead) return head;
5616
+ await new Promise((resolve6) => setTimeout(resolve6, pollMs));
5617
+ }
5618
+ return void 0;
5619
+ }
5543
5620
  async function deploy2(ws, opts) {
5544
5621
  const cloud = cloudBase(opts.cloud);
5545
5622
  let file;
@@ -5553,6 +5630,7 @@ async function deploy2(ws, opts) {
5553
5630
  const beforeLaw = await fetch(`${cloud}/v2/workspaces/${ws}/domains`).then((br) => br.json()).then((bd) => bd.ok === true ? bd : {}).catch(() => ({}));
5554
5631
  const before = beforeLaw.domains ?? [];
5555
5632
  const currentLaw2 = beforeLaw.currentLaw ?? {};
5633
+ const beforeDurableHead = await durableMainHead(cloud, ws);
5556
5634
  try {
5557
5635
  const compat = JSON.parse(readFileSync5(file, "utf8")).nomosCompat;
5558
5636
  const requires = compat?.requiresKernel ?? [];
@@ -5677,6 +5755,15 @@ async function deploy2(ws, opts) {
5677
5755
  }
5678
5756
  };
5679
5757
  if (deployedHash !== void 0 && isCurrent) {
5758
+ const wasCurrentBefore = Object.values(currentLaw2).includes(deployedHash);
5759
+ if (!wasCurrentBefore && beforeDurableHead !== void 0) {
5760
+ const durableHead = await waitForDurableMainAdvance(cloud, ws, beforeDurableHead);
5761
+ if (durableHead === void 0) {
5762
+ err4(`deploy '${ws}' was admitted, but canonical Git main did not advance within 30000ms`);
5763
+ err4(` Nomos will not start application code against a law that fresh peers cannot clone yet; retry the deploy.`);
5764
+ return 2;
5765
+ }
5766
+ }
5680
5767
  out4(`\u2713 deployed ${fileName} \u2192 ${ws}${phaseSuffix}`);
5681
5768
  out4(` law ${describeLawMove(before, deployedHash)}`);
5682
5769
  emitPlan();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "githolon",
3
- "version": "0.100.1",
3
+ "version": "0.100.3",
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.100.1",
33
- "@githolon/dsl": "^0.100.1",
32
+ "@githolon/client": "^0.100.3",
33
+ "@githolon/dsl": "^0.100.3",
34
34
  "isomorphic-git": "^1.38.4"
35
35
  },
36
36
  "devDependencies": {