githolon 0.105.3 → 0.105.5

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.
@@ -587,6 +587,7 @@ __export(engine_exports, {
587
587
  cryptoUnwrapKey: () => cryptoUnwrapKey,
588
588
  cryptoWrapKey: () => cryptoWrapKey,
589
589
  currentLaw: () => currentLaw,
590
+ custodyConfirmedMain: () => custodyConfirmedMain,
590
591
  declaredCapabilities: () => declaredCapabilities,
591
592
  deepVerifyShard: () => deepVerifyShard,
592
593
  delegationCertSign: () => delegationCertSign,
@@ -613,6 +614,7 @@ __export(engine_exports, {
613
614
  ingestPackAsCustody: () => ingestPackAsCustody,
614
615
  installedLaw: () => installedLaw,
615
616
  intentOfferResidencies: () => intentOfferResidencies,
617
+ isDirty: () => isDirty,
616
618
  isWarranted: () => isWarranted,
617
619
  kernelStamp: () => kernelStamp,
618
620
  kgit: () => kgit,
@@ -1645,6 +1647,40 @@ async function admitIntentOffers(eng, ws, ledger, offers, { receiptFence = null,
1645
1647
  const s0 = Date.now();
1646
1648
  return (end = {}) => timings.spans.push({ id: `intent-offer-${t0}-${seq++}`, track: "cloud", name, kind, depth, t0: s0, t1: Date.now(), ...extra, ...end });
1647
1649
  };
1650
+ let footageRef = null;
1651
+ {
1652
+ const parked = [];
1653
+ for (const offer of Array.isArray(offers) ? offers : []) {
1654
+ let blob = offer?.blob instanceof Uint8Array ? offer.blob : null;
1655
+ try {
1656
+ if (!blob && typeof offer?.changeB64 === "string") blob = Uint8Array.from(atob(offer.changeB64), (c) => c.charCodeAt(0));
1657
+ } catch {
1658
+ }
1659
+ if (blob && blob.byteLength) parked.push(blob);
1660
+ }
1661
+ if (parked.length) {
1662
+ footageRef = `refs/heads/parked/offered/${t0.toString(36)}-${Math.floor(Math.random() * 16777215).toString(36)}`;
1663
+ const footageDone = span("park offer footage", "cseal", 1, { offers: parked.length });
1664
+ try {
1665
+ kgit.writeMetaCommit(eng, gitdirOf(ws), {
1666
+ parents: [],
1667
+ files: parked.map((bytes, i) => ({ path: `${i}.change.usda`, bytes })),
1668
+ message: `offered: ${parked.length} intent(s) parked pre-judgment (footage-first)`,
1669
+ timeSecs: 0,
1670
+ ref: footageRef
1671
+ });
1672
+ await custodyPush(eng, ws, ledger, { remoteRef: footageRef });
1673
+ } catch (e) {
1674
+ try {
1675
+ kgit.deleteRef(eng, gitdirOf(ws), footageRef);
1676
+ } catch {
1677
+ }
1678
+ throw new Error(`offer footage not durable \u2014 custody unreachable, nothing was folded; retry the batch: ${String(e).slice(0, 300)}`, { cause: e });
1679
+ } finally {
1680
+ footageDone();
1681
+ }
1682
+ }
1683
+ }
1648
1684
  for (const offer of Array.isArray(offers) ? offers : []) {
1649
1685
  const cid = typeof offer?.session === "string" && offer.session ? offer.session : "direct";
1650
1686
  const rec = sessions.get(cid) || { session: cid, admitted: [], rejected: [], skipped: [] };
@@ -1720,10 +1756,13 @@ async function admitIntentOffers(eng, ws, ledger, offers, { receiptFence = null,
1720
1756
  }
1721
1757
  timings.sessions = sessions.size;
1722
1758
  timings.intents = admittedTotal;
1759
+ let refusalsParked = true;
1723
1760
  if (deadLetters.length) {
1724
1761
  try {
1725
- await materializeRefused(eng, ws, ledger, deadLetters);
1762
+ const m = await materializeRefused(eng, ws, ledger, deadLetters);
1763
+ refusalsParked = m.unparkedSessions.size === 0;
1726
1764
  } catch (e) {
1765
+ refusalsParked = false;
1727
1766
  log("refused-materialize-failed", { ws, error: String(e).slice(0, 200) });
1728
1767
  }
1729
1768
  }
@@ -1732,6 +1771,17 @@ async function admitIntentOffers(eng, ws, ledger, offers, { receiptFence = null,
1732
1771
  await commitAndPush(eng, ws, ledger);
1733
1772
  sealDone();
1734
1773
  }
1774
+ if (footageRef && refusalsParked) {
1775
+ try {
1776
+ kgit.deleteRef(eng, gitdirOf(ws), footageRef);
1777
+ } catch {
1778
+ }
1779
+ try {
1780
+ await custodyPush(eng, ws, ledger, { remoteRef: footageRef, del: true });
1781
+ } catch (e) {
1782
+ log("footage-delete-push-failed", { ws, ref: footageRef, error: String(e).slice(0, 200) });
1783
+ }
1784
+ }
1735
1785
  const gitdir = gitdirOf(ws);
1736
1786
  const head = kgit.resolveRef(eng, gitdir, BRANCH) ?? main.head;
1737
1787
  const summary = summaryCtx ? summaryOfCapture(summaryCtx, headBefore ?? "", head ?? "") : null;
@@ -1745,7 +1795,7 @@ async function admitIntentOffers(eng, ws, ledger, offers, { receiptFence = null,
1745
1795
  }
1746
1796
  log("admit-intent-offers", { ws, sessions: sessions.size, admitted: admittedTotal, rejected: [...sessions.values()].reduce((n, s) => n + s.rejected.length, 0), resultPack: resultPackB64 ? "inline" : "none" });
1747
1797
  const bornPacked = born.length ? packBornChildren(eng, born) : born;
1748
- return { sessions: [...sessions.values()].map((s) => ({ ...s, ...s.skipped.length ? {} : { skipped: void 0 } })), main: head, deadLetters, timings, summary: summary ?? null, born: bornPacked, birthOutcomes, ...resultPackB64 ? { resultPackB64, resultBase: clientBase } : {} };
1798
+ return { sessions: [...sessions.values()].map((s) => ({ ...s, ...s.skipped.length ? {} : { skipped: void 0 } })), main: head, sealed: true, sealedMain: custodyConfirmedMain(eng, ws), deadLetters, timings, summary: summary ?? null, born: bornPacked, birthOutcomes, ...resultPackB64 ? { resultPackB64, resultBase: clientBase } : {} };
1749
1799
  }
1750
1800
  function intentOfferResidencies(eng, offers) {
1751
1801
  const unique = /* @__PURE__ */ new Map();
@@ -1877,6 +1927,7 @@ async function mountWorkspace(eng, ws, ledger) {
1877
1927
  throw new Error(`kernel refused restored main ${remoteMain}: ${JSON.stringify(mounted)}`);
1878
1928
  }
1879
1929
  restored = true;
1930
+ custodyMainOf(eng).set(ws, remoteMain);
1880
1931
  verificationVerdict = jsStruct(mounted.verdict);
1881
1932
  }
1882
1933
  } catch (e) {
@@ -2347,7 +2398,7 @@ async function custodyFetch(eng, ws, ledger, { remoteRef = "refs/heads/main", lo
2347
2398
  if (writeRef) kgit.writeRef(eng, gitdirOf(ws), fullRef(localRef || remoteRef), tip);
2348
2399
  return tip;
2349
2400
  }
2350
- async function custodyPush(eng, ws, ledger, { localRef = null, remoteRef, del = false, pointer = false, newOid = null }) {
2401
+ async function custodyPush(eng, ws, ledger, { localRef = null, remoteRef, del = false, pointer = false, newOid = null, ffOnly = false }) {
2351
2402
  const headers = ledger.headers || {};
2352
2403
  const remoteRefFull = fullRef(remoteRef);
2353
2404
  let remote;
@@ -2363,22 +2414,46 @@ async function custodyPush(eng, ws, ledger, { localRef = null, remoteRef, del =
2363
2414
  }
2364
2415
  const tgt = newOid || kgit.resolveRef(eng, gitdirOf(ws), fullRef(localRef || remoteRef));
2365
2416
  if (!tgt) throw new Error(`custodyPush: local ref ${localRef || remoteRef} unresolved`);
2366
- if (tgt === oldOid) return;
2417
+ if (tgt === oldOid) {
2418
+ if (remoteRefFull === fullRef(BRANCH)) custodyMainOf(eng).set(ws, tgt);
2419
+ return;
2420
+ }
2367
2421
  let pack = null;
2368
2422
  if (!pointer) {
2369
2423
  pack = oldOid !== ZERO_OID ? packDelta(eng, ws, oldOid, tgt) : null;
2424
+ if (!pack && ffOnly && oldOid !== ZERO_OID) {
2425
+ throw new Error(`custody ${remoteRefFull} regression refused: remote ${oldOid.slice(0, 12)} is not an ancestor of local ${tgt.slice(0, 12)} \u2014 this engine's view is stale; re-mount from custody instead of forcing`);
2426
+ }
2370
2427
  if (!pack) pack = packDelta(eng, ws, "", tgt);
2371
2428
  }
2372
2429
  const packBytes = pack?.byteLength ?? 0;
2373
2430
  try {
2374
- return await pushVia(ledger, () => pushPack(ledger.remote, headers, { updates: [{ ref: remoteRefFull, oldOid, newOid: tgt }], pack }));
2431
+ const r = await pushVia(ledger, () => pushPack(ledger.remote, headers, { updates: [{ ref: remoteRefFull, oldOid, newOid: tgt }], pack }));
2432
+ if (remoteRefFull === fullRef(BRANCH)) custodyMainOf(eng).set(ws, tgt);
2433
+ return r;
2375
2434
  } catch (error) {
2376
2435
  throw new Error(`custody receive-pack push failed for ${remoteRefFull} (${packBytes} pack bytes): ${String(error)}`, { cause: error });
2377
2436
  }
2378
2437
  }
2438
+ function custodyMainOf(eng) {
2439
+ eng._custodyMain ??= /* @__PURE__ */ new Map();
2440
+ return eng._custodyMain;
2441
+ }
2442
+ function custodyConfirmedMain(eng, ws) {
2443
+ return custodyMainOf(eng).get(ws) ?? null;
2444
+ }
2445
+ function isDirty(eng, ws) {
2446
+ try {
2447
+ const resident = kgit.resolveRef(eng, gitdirOf(ws), BRANCH);
2448
+ if (!resident) return false;
2449
+ return resident !== custodyMainOf(eng).get(ws);
2450
+ } catch {
2451
+ return false;
2452
+ }
2453
+ }
2379
2454
  async function commitAndPush(eng, ws, ledger) {
2380
2455
  try {
2381
- await custodyPush(eng, ws, ledger, { remoteRef: "refs/heads/main" });
2456
+ await custodyPush(eng, ws, ledger, { remoteRef: "refs/heads/main", ffOnly: true });
2382
2457
  } catch (e) {
2383
2458
  unmount(eng, ws);
2384
2459
  const cause = e instanceof Error ? e.cause : void 0;
@@ -2781,6 +2856,80 @@ async function admitAll(eng, ws, ledger, { receiptFence = null, shardRouting = n
2781
2856
  _sessionDone();
2782
2857
  }
2783
2858
  }
2859
+ const offeredRefPrefix = "refs/heads/parked/offered/";
2860
+ const footageConsumed = [];
2861
+ const footageBorn = [], footageBirthOutcomes = [];
2862
+ try {
2863
+ const offeredRefs = Object.entries(await custodyLsRefs(ledger, offeredRefPrefix));
2864
+ for (const [oref, ooid] of offeredRefs) {
2865
+ if (!oref.startsWith(offeredRefPrefix) || !ooid) continue;
2866
+ const fcid = `offered:${oref.slice(offeredRefPrefix.length)}`;
2867
+ const _fDone = _span(`footage ${fcid.slice(0, 24)}`, "csession", 1, { client: fcid });
2868
+ const admitted = [], rejected = [], skipped = [];
2869
+ try {
2870
+ try {
2871
+ await custodyFetch(eng, ws, ledger, { remoteRef: oref, have: kgit.resolveRef(eng, gitdir, BRANCH) });
2872
+ } catch {
2873
+ await custodyFetch(eng, ws, ledger, { remoteRef: oref, have: null });
2874
+ }
2875
+ const main = await mainIntentIds(eng, ws);
2876
+ for (let i = 0; ; i++) {
2877
+ let blob = null;
2878
+ try {
2879
+ blob = kgit.readBlob(eng, gitdir, ooid, `${i}.change.usda`);
2880
+ } catch {
2881
+ blob = null;
2882
+ }
2883
+ if (!blob || !blob.byteLength) break;
2884
+ try {
2885
+ const inspected = kgit.inspectChange(eng, blob);
2886
+ const intentId = inspected.intentId || null;
2887
+ const oid = String(intentId || `${fcid}#${i}`).slice(0, 10);
2888
+ if (!inspected.authored || !inspected.call) {
2889
+ rejected.push({ oid, error: "parked offer is not an authored nominal call" });
2890
+ continue;
2891
+ }
2892
+ const { domainPath: dom, directivePath: dirId } = inspected.call;
2893
+ if (receiptFence && dirId && Array.isArray(receiptFence[dom]) && receiptFence[dom].includes(dirId)) {
2894
+ const error = `'${dom}/${dirId}' is a capability RECEIPT \u2014 receipts are executor/owner writes, never open-session writes`;
2895
+ rejected.push({ oid, error, deadLettered: true });
2896
+ deadLetters.push({ intentId, oid, session: fcid, error, blob, domain: dom, directiveId: dirId });
2897
+ continue;
2898
+ }
2899
+ if (intentId && main.ids.has(intentId)) {
2900
+ skipped.push({ oid, intent: intentId, alreadyOnMain: true });
2901
+ continue;
2902
+ }
2903
+ const res = offerAdmit(eng, ws, blob, { defer: canDeferProjection });
2904
+ if (res.ok) {
2905
+ if (intentId) main.ids.add(intentId);
2906
+ main.oids.add(res.head);
2907
+ main.head = res.head;
2908
+ admitted.push({ oid, intent: intentId, head: res.head });
2909
+ totalAdmitted += 1;
2910
+ _intents += 1;
2911
+ if (Array.isArray(res.born) && res.born.length) footageBorn.push(...res.born);
2912
+ if (Array.isArray(res.birthOutcomes) && res.birthOutcomes.length) footageBirthOutcomes.push(...res.birthOutcomes);
2913
+ } else {
2914
+ const error = `${String(res.error || "").slice(0, 300)} \u2014 dead-lettered; fix the law (or the payload), then POST /v1/workspaces/${ws}/dead-letters/retry`;
2915
+ rejected.push({ oid, error, deadLettered: true });
2916
+ deadLetters.push({ intentId, oid, session: fcid, error, blob, domain: dom, directiveId: dirId });
2917
+ }
2918
+ } catch (e) {
2919
+ rejected.push({ oid: `${fcid}#${i}`.slice(0, 10), error: String(e).slice(0, 200) });
2920
+ }
2921
+ }
2922
+ footageConsumed.push(oref);
2923
+ results.push({ session: fcid, admitted, rejected, ...skipped.length ? { skipped } : {} });
2924
+ } catch (e) {
2925
+ results.push({ session: fcid, error: String(e).slice(0, 200) });
2926
+ } finally {
2927
+ _fDone({ admitted: admitted.length, rejected: rejected.length });
2928
+ }
2929
+ }
2930
+ } catch (e) {
2931
+ log("footage-scan-failed", { ws, error: String(e).slice(0, 200) });
2932
+ }
2784
2933
  if (canDeferProjection && totalAdmitted) {
2785
2934
  const _projDone = _span("projection flush (batch)", "cproject", 1, { admitted: totalAdmitted });
2786
2935
  try {
@@ -2804,7 +2953,8 @@ async function admitAll(eng, ws, ledger, { receiptFence = null, shardRouting = n
2804
2953
  const hasCoordination = !!(summaryCtx || placements.length || splits.length || globalsOut.length);
2805
2954
  const deferDurability = defer && !hasCoordination;
2806
2955
  let _sealed = true;
2807
- if (totalAdmitted && !deferDurability) {
2956
+ const sealNeeded = totalAdmitted > 0 || isDirty(eng, ws);
2957
+ if (sealNeeded && !deferDurability) {
2808
2958
  const _sealDone = _span(`seal (batch)`, "cseal", 1, { sessions: consumed.length, admitted: totalAdmitted });
2809
2959
  const _s = Date.now();
2810
2960
  try {
@@ -2824,17 +2974,38 @@ async function admitAll(eng, ws, ledger, { receiptFence = null, shardRouting = n
2824
2974
  } catch {
2825
2975
  }
2826
2976
  }
2977
+ for (const oref of footageConsumed) {
2978
+ const fcid = `offered:${oref.slice(offeredRefPrefix.length)}`;
2979
+ if (unparkedSessions.has(fcid)) continue;
2980
+ try {
2981
+ kgit.deleteRef(eng, gitdir, oref);
2982
+ } catch {
2983
+ }
2984
+ try {
2985
+ await custodyPush(eng, ws, ledger, { remoteRef: oref, del: true });
2986
+ } catch {
2987
+ }
2988
+ }
2827
2989
  }
2828
2990
  const newMain = kgit.resolveRef(eng, gitdir, BRANCH);
2829
- log("admit", { ws, sessions: results.length, main: newMain });
2991
+ log("admit", { ws, sessions: results.length, main: newMain, sealed: _sealed, sealedMain: custodyConfirmedMain(eng, ws) });
2830
2992
  const summary = summaryCtx ? summaryOfCapture(summaryCtx, headBefore ?? "", newMain) : null;
2831
2993
  const _total = Date.now() - _t0;
2832
2994
  const timings = { totalMs: _total, scanMs: _scanMs, fetchMs: _fetchMs, sealMs: _sealMs, gateFoldMs: Math.max(0, _total - _scanMs - _fetchMs - _sealMs), intents: _intents, sessions: results.length, spans: _spans };
2833
2995
  return {
2996
+ // SCOREBOARD TRUTH: `main` is the resident fold head (the ack the client converges to);
2997
+ // `sealedMain` is the custody-confirmed head — THE ONLY head the host may advertise/cache
2998
+ // (seated_barrier_and_seal_obligation.md Part 2). `sealed:false` ⇒ the seal obligation is
2999
+ // outstanding and the caller must re-arm the admit alarm (retry) rather than move on.
2834
3000
  sessions: results,
2835
3001
  main: newMain,
3002
+ sealed: _sealed,
3003
+ sealedMain: custodyConfirmedMain(eng, ws),
2836
3004
  deadLetters,
2837
3005
  timings,
3006
+ // Footage-replayed `.births()` offers: the born child custody + typed verdicts, for the caller
3007
+ // to persist exactly as the live POST lane does (packed here — the caller holds no engine FS).
3008
+ ...footageBorn.length ? { born: packBornChildren(eng, footageBorn), birthOutcomes: footageBirthOutcomes } : {},
2838
3009
  ...summary ? { summary } : {},
2839
3010
  ...placements.length ? { placements } : {},
2840
3011
  ...splits.length ? { splits } : {},
@@ -2848,7 +3019,7 @@ async function backupHead(eng, ws, ledger, head) {
2848
3019
  if (!eng.mounted.has(ws)) return { ok: false, error: "not mounted" };
2849
3020
  if (!head) return { ok: false, error: "no head" };
2850
3021
  try {
2851
- await custodyPush(eng, ws, ledger, { remoteRef: "refs/heads/main", newOid: head });
3022
+ await custodyPush(eng, ws, ledger, { remoteRef: "refs/heads/main", newOid: head, ffOnly: true });
2852
3023
  return { ok: true, head };
2853
3024
  } catch (e) {
2854
3025
  return { ok: false, error: String(e?.message || e).slice(0, 200), head };
@@ -2861,7 +3032,7 @@ async function backupToCustody(eng, ws, ledger, { consumed = [] } = {}) {
2861
3032
  head = kgit.resolveRef(eng, gitdir, BRANCH);
2862
3033
  if (!head) return { ok: false, error: "no local main" };
2863
3034
  try {
2864
- await custodyPush(eng, ws, ledger, { remoteRef: "refs/heads/main" });
3035
+ await custodyPush(eng, ws, ledger, { remoteRef: "refs/heads/main", ffOnly: true });
2865
3036
  } catch (e) {
2866
3037
  return { ok: false, error: String(e).slice(0, 200), head };
2867
3038
  }
@@ -936,6 +936,40 @@ async function admitIntentOffers(eng, ws, ledger, offers, { receiptFence = null,
936
936
  const s0 = Date.now();
937
937
  return (end = {}) => timings.spans.push({ id: `intent-offer-${t0}-${seq++}`, track: "cloud", name, kind, depth, t0: s0, t1: Date.now(), ...extra, ...end });
938
938
  };
939
+ let footageRef = null;
940
+ {
941
+ const parked = [];
942
+ for (const offer of Array.isArray(offers) ? offers : []) {
943
+ let blob = offer?.blob instanceof Uint8Array ? offer.blob : null;
944
+ try {
945
+ if (!blob && typeof offer?.changeB64 === "string") blob = Uint8Array.from(atob(offer.changeB64), (c) => c.charCodeAt(0));
946
+ } catch {
947
+ }
948
+ if (blob && blob.byteLength) parked.push(blob);
949
+ }
950
+ if (parked.length) {
951
+ footageRef = `refs/heads/parked/offered/${t0.toString(36)}-${Math.floor(Math.random() * 16777215).toString(36)}`;
952
+ const footageDone = span("park offer footage", "cseal", 1, { offers: parked.length });
953
+ try {
954
+ kgit.writeMetaCommit(eng, gitdirOf(ws), {
955
+ parents: [],
956
+ files: parked.map((bytes, i) => ({ path: `${i}.change.usda`, bytes })),
957
+ message: `offered: ${parked.length} intent(s) parked pre-judgment (footage-first)`,
958
+ timeSecs: 0,
959
+ ref: footageRef
960
+ });
961
+ await custodyPush(eng, ws, ledger, { remoteRef: footageRef });
962
+ } catch (e) {
963
+ try {
964
+ kgit.deleteRef(eng, gitdirOf(ws), footageRef);
965
+ } catch {
966
+ }
967
+ throw new Error(`offer footage not durable \u2014 custody unreachable, nothing was folded; retry the batch: ${String(e).slice(0, 300)}`, { cause: e });
968
+ } finally {
969
+ footageDone();
970
+ }
971
+ }
972
+ }
939
973
  for (const offer of Array.isArray(offers) ? offers : []) {
940
974
  const cid = typeof offer?.session === "string" && offer.session ? offer.session : "direct";
941
975
  const rec = sessions.get(cid) || { session: cid, admitted: [], rejected: [], skipped: [] };
@@ -1011,10 +1045,13 @@ async function admitIntentOffers(eng, ws, ledger, offers, { receiptFence = null,
1011
1045
  }
1012
1046
  timings.sessions = sessions.size;
1013
1047
  timings.intents = admittedTotal;
1048
+ let refusalsParked = true;
1014
1049
  if (deadLetters.length) {
1015
1050
  try {
1016
- await materializeRefused(eng, ws, ledger, deadLetters);
1051
+ const m = await materializeRefused(eng, ws, ledger, deadLetters);
1052
+ refusalsParked = m.unparkedSessions.size === 0;
1017
1053
  } catch (e) {
1054
+ refusalsParked = false;
1018
1055
  log("refused-materialize-failed", { ws, error: String(e).slice(0, 200) });
1019
1056
  }
1020
1057
  }
@@ -1023,6 +1060,17 @@ async function admitIntentOffers(eng, ws, ledger, offers, { receiptFence = null,
1023
1060
  await commitAndPush(eng, ws, ledger);
1024
1061
  sealDone();
1025
1062
  }
1063
+ if (footageRef && refusalsParked) {
1064
+ try {
1065
+ kgit.deleteRef(eng, gitdirOf(ws), footageRef);
1066
+ } catch {
1067
+ }
1068
+ try {
1069
+ await custodyPush(eng, ws, ledger, { remoteRef: footageRef, del: true });
1070
+ } catch (e) {
1071
+ log("footage-delete-push-failed", { ws, ref: footageRef, error: String(e).slice(0, 200) });
1072
+ }
1073
+ }
1026
1074
  const gitdir = gitdirOf(ws);
1027
1075
  const head = kgit.resolveRef(eng, gitdir, BRANCH) ?? main.head;
1028
1076
  const summary = summaryCtx ? summaryOfCapture(summaryCtx, headBefore ?? "", head ?? "") : null;
@@ -1036,7 +1084,7 @@ async function admitIntentOffers(eng, ws, ledger, offers, { receiptFence = null,
1036
1084
  }
1037
1085
  log("admit-intent-offers", { ws, sessions: sessions.size, admitted: admittedTotal, rejected: [...sessions.values()].reduce((n, s) => n + s.rejected.length, 0), resultPack: resultPackB64 ? "inline" : "none" });
1038
1086
  const bornPacked = born.length ? packBornChildren(eng, born) : born;
1039
- return { sessions: [...sessions.values()].map((s) => ({ ...s, ...s.skipped.length ? {} : { skipped: void 0 } })), main: head, deadLetters, timings, summary: summary ?? null, born: bornPacked, birthOutcomes, ...resultPackB64 ? { resultPackB64, resultBase: clientBase } : {} };
1087
+ return { sessions: [...sessions.values()].map((s) => ({ ...s, ...s.skipped.length ? {} : { skipped: void 0 } })), main: head, sealed: true, sealedMain: custodyConfirmedMain(eng, ws), deadLetters, timings, summary: summary ?? null, born: bornPacked, birthOutcomes, ...resultPackB64 ? { resultPackB64, resultBase: clientBase } : {} };
1040
1088
  }
1041
1089
  function intentOfferResidencies(eng, offers) {
1042
1090
  const unique = /* @__PURE__ */ new Map();
@@ -1093,6 +1141,7 @@ async function mountWorkspace(eng, ws, ledger) {
1093
1141
  throw new Error(`kernel refused restored main ${remoteMain}: ${JSON.stringify(mounted)}`);
1094
1142
  }
1095
1143
  restored = true;
1144
+ custodyMainOf(eng).set(ws, remoteMain);
1096
1145
  verificationVerdict = jsStruct(mounted.verdict);
1097
1146
  }
1098
1147
  } catch (e) {
@@ -1245,7 +1294,7 @@ async function custodyFetch(eng, ws, ledger, { remoteRef = "refs/heads/main", lo
1245
1294
  if (writeRef) kgit.writeRef(eng, gitdirOf(ws), fullRef(localRef || remoteRef), tip);
1246
1295
  return tip;
1247
1296
  }
1248
- async function custodyPush(eng, ws, ledger, { localRef = null, remoteRef, del = false, pointer = false, newOid = null }) {
1297
+ async function custodyPush(eng, ws, ledger, { localRef = null, remoteRef, del = false, pointer = false, newOid = null, ffOnly = false }) {
1249
1298
  const headers = ledger.headers || {};
1250
1299
  const remoteRefFull = fullRef(remoteRef);
1251
1300
  let remote;
@@ -1261,22 +1310,46 @@ async function custodyPush(eng, ws, ledger, { localRef = null, remoteRef, del =
1261
1310
  }
1262
1311
  const tgt = newOid || kgit.resolveRef(eng, gitdirOf(ws), fullRef(localRef || remoteRef));
1263
1312
  if (!tgt) throw new Error(`custodyPush: local ref ${localRef || remoteRef} unresolved`);
1264
- if (tgt === oldOid) return;
1313
+ if (tgt === oldOid) {
1314
+ if (remoteRefFull === fullRef(BRANCH)) custodyMainOf(eng).set(ws, tgt);
1315
+ return;
1316
+ }
1265
1317
  let pack = null;
1266
1318
  if (!pointer) {
1267
1319
  pack = oldOid !== ZERO_OID ? packDelta(eng, ws, oldOid, tgt) : null;
1320
+ if (!pack && ffOnly && oldOid !== ZERO_OID) {
1321
+ throw new Error(`custody ${remoteRefFull} regression refused: remote ${oldOid.slice(0, 12)} is not an ancestor of local ${tgt.slice(0, 12)} \u2014 this engine's view is stale; re-mount from custody instead of forcing`);
1322
+ }
1268
1323
  if (!pack) pack = packDelta(eng, ws, "", tgt);
1269
1324
  }
1270
1325
  const packBytes = pack?.byteLength ?? 0;
1271
1326
  try {
1272
- return await pushVia(ledger, () => pushPack(ledger.remote, headers, { updates: [{ ref: remoteRefFull, oldOid, newOid: tgt }], pack }));
1327
+ const r = await pushVia(ledger, () => pushPack(ledger.remote, headers, { updates: [{ ref: remoteRefFull, oldOid, newOid: tgt }], pack }));
1328
+ if (remoteRefFull === fullRef(BRANCH)) custodyMainOf(eng).set(ws, tgt);
1329
+ return r;
1273
1330
  } catch (error) {
1274
1331
  throw new Error(`custody receive-pack push failed for ${remoteRefFull} (${packBytes} pack bytes): ${String(error)}`, { cause: error });
1275
1332
  }
1276
1333
  }
1334
+ function custodyMainOf(eng) {
1335
+ eng._custodyMain ??= /* @__PURE__ */ new Map();
1336
+ return eng._custodyMain;
1337
+ }
1338
+ function custodyConfirmedMain(eng, ws) {
1339
+ return custodyMainOf(eng).get(ws) ?? null;
1340
+ }
1341
+ function isDirty(eng, ws) {
1342
+ try {
1343
+ const resident = kgit.resolveRef(eng, gitdirOf(ws), BRANCH);
1344
+ if (!resident) return false;
1345
+ return resident !== custodyMainOf(eng).get(ws);
1346
+ } catch {
1347
+ return false;
1348
+ }
1349
+ }
1277
1350
  async function commitAndPush(eng, ws, ledger) {
1278
1351
  try {
1279
- await custodyPush(eng, ws, ledger, { remoteRef: "refs/heads/main" });
1352
+ await custodyPush(eng, ws, ledger, { remoteRef: "refs/heads/main", ffOnly: true });
1280
1353
  } catch (e) {
1281
1354
  unmount(eng, ws);
1282
1355
  const cause = e instanceof Error ? e.cause : void 0;
@@ -1602,6 +1675,80 @@ async function admitAll(eng, ws, ledger, { receiptFence = null, shardRouting = n
1602
1675
  _sessionDone();
1603
1676
  }
1604
1677
  }
1678
+ const offeredRefPrefix = "refs/heads/parked/offered/";
1679
+ const footageConsumed = [];
1680
+ const footageBorn = [], footageBirthOutcomes = [];
1681
+ try {
1682
+ const offeredRefs = Object.entries(await custodyLsRefs(ledger, offeredRefPrefix));
1683
+ for (const [oref, ooid] of offeredRefs) {
1684
+ if (!oref.startsWith(offeredRefPrefix) || !ooid) continue;
1685
+ const fcid = `offered:${oref.slice(offeredRefPrefix.length)}`;
1686
+ const _fDone = _span(`footage ${fcid.slice(0, 24)}`, "csession", 1, { client: fcid });
1687
+ const admitted = [], rejected = [], skipped = [];
1688
+ try {
1689
+ try {
1690
+ await custodyFetch(eng, ws, ledger, { remoteRef: oref, have: kgit.resolveRef(eng, gitdir, BRANCH) });
1691
+ } catch {
1692
+ await custodyFetch(eng, ws, ledger, { remoteRef: oref, have: null });
1693
+ }
1694
+ const main = await mainIntentIds(eng, ws);
1695
+ for (let i = 0; ; i++) {
1696
+ let blob = null;
1697
+ try {
1698
+ blob = kgit.readBlob(eng, gitdir, ooid, `${i}.change.usda`);
1699
+ } catch {
1700
+ blob = null;
1701
+ }
1702
+ if (!blob || !blob.byteLength) break;
1703
+ try {
1704
+ const inspected = kgit.inspectChange(eng, blob);
1705
+ const intentId = inspected.intentId || null;
1706
+ const oid = String(intentId || `${fcid}#${i}`).slice(0, 10);
1707
+ if (!inspected.authored || !inspected.call) {
1708
+ rejected.push({ oid, error: "parked offer is not an authored nominal call" });
1709
+ continue;
1710
+ }
1711
+ const { domainPath: dom, directivePath: dirId } = inspected.call;
1712
+ if (receiptFence && dirId && Array.isArray(receiptFence[dom]) && receiptFence[dom].includes(dirId)) {
1713
+ const error = `'${dom}/${dirId}' is a capability RECEIPT \u2014 receipts are executor/owner writes, never open-session writes`;
1714
+ rejected.push({ oid, error, deadLettered: true });
1715
+ deadLetters.push({ intentId, oid, session: fcid, error, blob, domain: dom, directiveId: dirId });
1716
+ continue;
1717
+ }
1718
+ if (intentId && main.ids.has(intentId)) {
1719
+ skipped.push({ oid, intent: intentId, alreadyOnMain: true });
1720
+ continue;
1721
+ }
1722
+ const res = offerAdmit(eng, ws, blob, { defer: canDeferProjection });
1723
+ if (res.ok) {
1724
+ if (intentId) main.ids.add(intentId);
1725
+ main.oids.add(res.head);
1726
+ main.head = res.head;
1727
+ admitted.push({ oid, intent: intentId, head: res.head });
1728
+ totalAdmitted += 1;
1729
+ _intents += 1;
1730
+ if (Array.isArray(res.born) && res.born.length) footageBorn.push(...res.born);
1731
+ if (Array.isArray(res.birthOutcomes) && res.birthOutcomes.length) footageBirthOutcomes.push(...res.birthOutcomes);
1732
+ } else {
1733
+ const error = `${String(res.error || "").slice(0, 300)} \u2014 dead-lettered; fix the law (or the payload), then POST /v1/workspaces/${ws}/dead-letters/retry`;
1734
+ rejected.push({ oid, error, deadLettered: true });
1735
+ deadLetters.push({ intentId, oid, session: fcid, error, blob, domain: dom, directiveId: dirId });
1736
+ }
1737
+ } catch (e) {
1738
+ rejected.push({ oid: `${fcid}#${i}`.slice(0, 10), error: String(e).slice(0, 200) });
1739
+ }
1740
+ }
1741
+ footageConsumed.push(oref);
1742
+ results.push({ session: fcid, admitted, rejected, ...skipped.length ? { skipped } : {} });
1743
+ } catch (e) {
1744
+ results.push({ session: fcid, error: String(e).slice(0, 200) });
1745
+ } finally {
1746
+ _fDone({ admitted: admitted.length, rejected: rejected.length });
1747
+ }
1748
+ }
1749
+ } catch (e) {
1750
+ log("footage-scan-failed", { ws, error: String(e).slice(0, 200) });
1751
+ }
1605
1752
  if (canDeferProjection && totalAdmitted) {
1606
1753
  const _projDone = _span("projection flush (batch)", "cproject", 1, { admitted: totalAdmitted });
1607
1754
  try {
@@ -1625,7 +1772,8 @@ async function admitAll(eng, ws, ledger, { receiptFence = null, shardRouting = n
1625
1772
  const hasCoordination = !!(summaryCtx || placements.length || splits.length || globalsOut.length);
1626
1773
  const deferDurability = defer && !hasCoordination;
1627
1774
  let _sealed = true;
1628
- if (totalAdmitted && !deferDurability) {
1775
+ const sealNeeded = totalAdmitted > 0 || isDirty(eng, ws);
1776
+ if (sealNeeded && !deferDurability) {
1629
1777
  const _sealDone = _span(`seal (batch)`, "cseal", 1, { sessions: consumed.length, admitted: totalAdmitted });
1630
1778
  const _s = Date.now();
1631
1779
  try {
@@ -1645,17 +1793,38 @@ async function admitAll(eng, ws, ledger, { receiptFence = null, shardRouting = n
1645
1793
  } catch {
1646
1794
  }
1647
1795
  }
1796
+ for (const oref of footageConsumed) {
1797
+ const fcid = `offered:${oref.slice(offeredRefPrefix.length)}`;
1798
+ if (unparkedSessions.has(fcid)) continue;
1799
+ try {
1800
+ kgit.deleteRef(eng, gitdir, oref);
1801
+ } catch {
1802
+ }
1803
+ try {
1804
+ await custodyPush(eng, ws, ledger, { remoteRef: oref, del: true });
1805
+ } catch {
1806
+ }
1807
+ }
1648
1808
  }
1649
1809
  const newMain = kgit.resolveRef(eng, gitdir, BRANCH);
1650
- log("admit", { ws, sessions: results.length, main: newMain });
1810
+ log("admit", { ws, sessions: results.length, main: newMain, sealed: _sealed, sealedMain: custodyConfirmedMain(eng, ws) });
1651
1811
  const summary = summaryCtx ? summaryOfCapture(summaryCtx, headBefore ?? "", newMain) : null;
1652
1812
  const _total = Date.now() - _t0;
1653
1813
  const timings = { totalMs: _total, scanMs: _scanMs, fetchMs: _fetchMs, sealMs: _sealMs, gateFoldMs: Math.max(0, _total - _scanMs - _fetchMs - _sealMs), intents: _intents, sessions: results.length, spans: _spans };
1654
1814
  return {
1815
+ // SCOREBOARD TRUTH: `main` is the resident fold head (the ack the client converges to);
1816
+ // `sealedMain` is the custody-confirmed head — THE ONLY head the host may advertise/cache
1817
+ // (seated_barrier_and_seal_obligation.md Part 2). `sealed:false` ⇒ the seal obligation is
1818
+ // outstanding and the caller must re-arm the admit alarm (retry) rather than move on.
1655
1819
  sessions: results,
1656
1820
  main: newMain,
1821
+ sealed: _sealed,
1822
+ sealedMain: custodyConfirmedMain(eng, ws),
1657
1823
  deadLetters,
1658
1824
  timings,
1825
+ // Footage-replayed `.births()` offers: the born child custody + typed verdicts, for the caller
1826
+ // to persist exactly as the live POST lane does (packed here — the caller holds no engine FS).
1827
+ ...footageBorn.length ? { born: packBornChildren(eng, footageBorn), birthOutcomes: footageBirthOutcomes } : {},
1659
1828
  ...summary ? { summary } : {},
1660
1829
  ...placements.length ? { placements } : {},
1661
1830
  ...splits.length ? { splits } : {},
package/dist/full-cli.mjs CHANGED
@@ -4979,7 +4979,7 @@ function createGovernanceOffers(ctx) {
4979
4979
  * `revoked: true` means the subject's leaf was flipped `status:revoked` (remove-wins — a lawful
4980
4980
  * re-enroll of the SAME key is refused; mint + enroll a new device key instead).
4981
4981
  */
4982
- parentSignerEnrollment: async ({ parent, principal, keyHash, publicKey, authToken } = {}) => {
4982
+ parentSignerEnrollment: async ({ parent, principal, keyHash, publicKey, authToken, remote } = {}) => {
4983
4983
  if (!parent || typeof parent !== "string") throw new Error("parentSignerEnrollment requires `parent` (the workspace whose chain holds the signer tuple)");
4984
4984
  if (!principal || typeof principal !== "string") throw new Error("parentSignerEnrollment requires `principal` (the auth-provider uid)");
4985
4985
  const p = canonicalPrincipal(principal);
@@ -4989,7 +4989,7 @@ function createGovernanceOffers(ctx) {
4989
4989
  }
4990
4990
  const object = signerObjectOf(p);
4991
4991
  let rows;
4992
- if (parent === workspace) {
4992
+ if (parent === workspace && !remote) {
4993
4993
  rows = JSON.parse(call("query", { queryBytes: qBytes2({ kind: "query", queryId: "relationTuplesByObject", paramsJson: JSON.stringify({ object }) }) })) || [];
4994
4994
  } else {
4995
4995
  const headers = authToken ? { Authorization: `Bearer ${authToken}`, "x-nomos-auth": authToken } : authHeaders;
@@ -5560,7 +5560,7 @@ async function checkCompat(holon, nomosCompat) {
5560
5560
  }
5561
5561
  return verdict;
5562
5562
  }
5563
- async function connectSingle({ cloud, workspace, clientId, authToken, restoreFrom, onSpan, authorSecret, principal, allowUnborn = false, salvageOnly = false, recover = false, mountInPlace = false, __injectCheckpoint, kernelProvider, fetchImpl, historicSnapshots, onCustodyBreak, watchRebind, openWorkspaceInPlane, __knownKernelCapabilities, __testKRootPublic, __testRootSecret }) {
5563
+ async function connectSingle({ cloud, workspace, clientId, authToken, restoreFrom, onSpan, authorSecret, principal, allowUnborn = false, salvageOnly = false, recover = false, mountInPlace = false, __injectCheckpoint, kernelProvider, fetchImpl, historicSnapshots, onCustodyBreak, onLocalWorkRejected, watchRebind, openWorkspaceInPlane, __knownKernelCapabilities, __testKRootPublic, __testRootSecret }) {
5564
5564
  if (salvageOnly) allowUnborn = true;
5565
5565
  if (!cloud || !workspace) throw new Error("connect({ cloud, workspace }) required");
5566
5566
  const _fetch = typeof fetchImpl === "function" ? fetchImpl : globalThis.fetch;
@@ -5722,6 +5722,47 @@ ${String(commit).toLowerCase()}`;
5722
5722
  }
5723
5723
  };
5724
5724
  const onCustodyBreakOpt = typeof onCustodyBreak === "function" ? onCustodyBreak : null;
5725
+ let localWorkRejectedHook = typeof onLocalWorkRejected === "function" ? onLocalWorkRejected : null;
5726
+ const localWorkRejectedListeners = /* @__PURE__ */ new Set();
5727
+ const refusalIsRetryable = (error) => {
5728
+ try {
5729
+ const d = authzDiagnosisFromRefusal(String(error ?? ""));
5730
+ return d?.code === "nomos.authz.signer_enrollment_missing";
5731
+ } catch {
5732
+ return false;
5733
+ }
5734
+ };
5735
+ const emitLocalWorkRejected = (source, refused, resetTo) => {
5736
+ if (!refused.length) return;
5737
+ const items = refused.map((r) => ({
5738
+ intentId: typeof r.intentId === "string" ? r.intentId : null,
5739
+ oid: typeof r.oid === "string" ? r.oid : null,
5740
+ domain: typeof r.domain === "string" ? r.domain : null,
5741
+ directiveId: typeof r.directiveId === "string" ? r.directiveId : null,
5742
+ error: String(r.error ?? "the law refused this offer").slice(0, 600),
5743
+ retryable: refusalIsRetryable(r.error)
5744
+ }));
5745
+ const event = {
5746
+ workspace,
5747
+ refused: items,
5748
+ parked: true,
5749
+ resetTo,
5750
+ retryable: items.every((i) => i.retryable),
5751
+ source
5752
+ };
5753
+ if (localWorkRejectedHook) {
5754
+ try {
5755
+ localWorkRejectedHook(event);
5756
+ } catch {
5757
+ }
5758
+ }
5759
+ for (const l of [...localWorkRejectedListeners]) {
5760
+ try {
5761
+ l(event);
5762
+ } catch {
5763
+ }
5764
+ }
5765
+ };
5725
5766
  function captureIncarnation(j) {
5726
5767
  const inc = j && typeof j.incarnation === "string" && OID40_RE.test(j.incarnation) ? j.incarnation : null;
5727
5768
  if (inc !== null) remoteIncarnation = inc;
@@ -6308,6 +6349,7 @@ ${String(commit).toLowerCase()}`;
6308
6349
  }
6309
6350
  }
6310
6351
  }
6352
+ if (deadLettered.length) emitLocalWorkRejected("sync", deadLettered.map((d) => ({ intentId: d.id, error: d.error })), remoteHead);
6311
6353
  invalidateInstalledLawMetadata();
6312
6354
  nudgeWatches();
6313
6355
  return {
@@ -8620,6 +8662,22 @@ ${String(commit).toLowerCase()}`;
8620
8662
  if (typeof cb === "function") custodyListeners.add(cb);
8621
8663
  return () => custodyListeners.delete(cb);
8622
8664
  },
8665
+ /** THE LOCAL-WORK-REJECTED HOOK (settable; connect({onLocalWorkRejected}) is the default) — the
8666
+ * typed "the cloud refused work your device had accepted" lifecycle event. See
8667
+ * `LocalWorkRejectedEvent`: refused intents are already parked + survivors replayed when it
8668
+ * fires; the app's only job is honest UX (retryable ⇒ "queued, will retry"; terminal ⇒
8669
+ * "preserved for review — contact support"). */
8670
+ get onLocalWorkRejected() {
8671
+ return localWorkRejectedHook;
8672
+ },
8673
+ set onLocalWorkRejected(fn) {
8674
+ localWorkRejectedHook = typeof fn === "function" ? fn : null;
8675
+ },
8676
+ /** Subscribe (multi-listener) to LocalWorkRejectedEvents. Returns an unsubscribe fn. */
8677
+ onLocalWorkRejectedEvent: (cb) => {
8678
+ if (typeof cb === "function") localWorkRejectedListeners.add(cb);
8679
+ return () => localWorkRejectedListeners.delete(cb);
8680
+ },
8623
8681
  /**
8624
8682
  * INTROSPECTION — where this holon sits on the custody spectrum:
8625
8683
  * `{ incarnation, localGenesis, remoteIncarnation, syncPosture, pendingLocalIntents, salvagedIntents,
@@ -10089,6 +10147,10 @@ ${String(commit).toLowerCase()}`;
10089
10147
  });
10090
10148
  _convSpan.end();
10091
10149
  if (result && result.custodyBreak) return await resolveCustodyBreakFlow(result.custodyBreak, { operation: "sync" });
10150
+ if (admissionRefusals && admissionRefusals.size) {
10151
+ const refusedEntries = (admission?.sessions || []).flatMap((s) => (s.rejected || []).map((r) => ({ intentId: r.intent, oid: r.oid, error: r.error })));
10152
+ emitLocalWorkRejected("admission", refusedEntries, result && typeof result.remoteMain === "string" ? result.remoteMain : null);
10153
+ }
10092
10154
  try {
10093
10155
  result.stillAhead = await resolveBranchHead() !== syncedBase;
10094
10156
  } catch {
@@ -10872,7 +10934,14 @@ function routedHolon(coordinator, { cloud, workspace, clientId, authToken, fetch
10872
10934
  },
10873
10935
  set onCustodyBreak(fn) {
10874
10936
  coordinator.onCustodyBreak = fn;
10875
- }
10937
+ },
10938
+ get onLocalWorkRejected() {
10939
+ return coordinator.onLocalWorkRejected;
10940
+ },
10941
+ set onLocalWorkRejected(fn) {
10942
+ coordinator.onLocalWorkRejected = fn;
10943
+ },
10944
+ onLocalWorkRejectedEvent: (cb) => coordinator.onLocalWorkRejectedEvent(cb)
10876
10945
  };
10877
10946
  }
10878
10947
  async function inspectSnapshot(opts) {
@@ -11804,6 +11873,7 @@ __export(engine_exports, {
11804
11873
  cryptoUnwrapKey: () => cryptoUnwrapKey,
11805
11874
  cryptoWrapKey: () => cryptoWrapKey,
11806
11875
  currentLaw: () => currentLaw,
11876
+ custodyConfirmedMain: () => custodyConfirmedMain,
11807
11877
  declaredCapabilities: () => declaredCapabilities,
11808
11878
  deepVerifyShard: () => deepVerifyShard,
11809
11879
  delegationCertSign: () => delegationCertSign,
@@ -11830,6 +11900,7 @@ __export(engine_exports, {
11830
11900
  ingestPackAsCustody: () => ingestPackAsCustody,
11831
11901
  installedLaw: () => installedLaw,
11832
11902
  intentOfferResidencies: () => intentOfferResidencies,
11903
+ isDirty: () => isDirty,
11833
11904
  isWarranted: () => isWarranted,
11834
11905
  kernelStamp: () => kernelStamp,
11835
11906
  kgit: () => kgit,
@@ -12862,6 +12933,40 @@ async function admitIntentOffers(eng, ws, ledger, offers, { receiptFence = null,
12862
12933
  const s0 = Date.now();
12863
12934
  return (end = {}) => timings.spans.push({ id: `intent-offer-${t0}-${seq++}`, track: "cloud", name, kind, depth, t0: s0, t1: Date.now(), ...extra, ...end });
12864
12935
  };
12936
+ let footageRef = null;
12937
+ {
12938
+ const parked = [];
12939
+ for (const offer of Array.isArray(offers) ? offers : []) {
12940
+ let blob = offer?.blob instanceof Uint8Array ? offer.blob : null;
12941
+ try {
12942
+ if (!blob && typeof offer?.changeB64 === "string") blob = Uint8Array.from(atob(offer.changeB64), (c) => c.charCodeAt(0));
12943
+ } catch {
12944
+ }
12945
+ if (blob && blob.byteLength) parked.push(blob);
12946
+ }
12947
+ if (parked.length) {
12948
+ footageRef = `refs/heads/parked/offered/${t0.toString(36)}-${Math.floor(Math.random() * 16777215).toString(36)}`;
12949
+ const footageDone = span("park offer footage", "cseal", 1, { offers: parked.length });
12950
+ try {
12951
+ kgit.writeMetaCommit(eng, gitdirOf(ws), {
12952
+ parents: [],
12953
+ files: parked.map((bytes, i) => ({ path: `${i}.change.usda`, bytes })),
12954
+ message: `offered: ${parked.length} intent(s) parked pre-judgment (footage-first)`,
12955
+ timeSecs: 0,
12956
+ ref: footageRef
12957
+ });
12958
+ await custodyPush(eng, ws, ledger, { remoteRef: footageRef });
12959
+ } catch (e) {
12960
+ try {
12961
+ kgit.deleteRef(eng, gitdirOf(ws), footageRef);
12962
+ } catch {
12963
+ }
12964
+ throw new Error(`offer footage not durable \u2014 custody unreachable, nothing was folded; retry the batch: ${String(e).slice(0, 300)}`, { cause: e });
12965
+ } finally {
12966
+ footageDone();
12967
+ }
12968
+ }
12969
+ }
12865
12970
  for (const offer of Array.isArray(offers) ? offers : []) {
12866
12971
  const cid = typeof offer?.session === "string" && offer.session ? offer.session : "direct";
12867
12972
  const rec = sessions.get(cid) || { session: cid, admitted: [], rejected: [], skipped: [] };
@@ -12937,10 +13042,13 @@ async function admitIntentOffers(eng, ws, ledger, offers, { receiptFence = null,
12937
13042
  }
12938
13043
  timings.sessions = sessions.size;
12939
13044
  timings.intents = admittedTotal;
13045
+ let refusalsParked = true;
12940
13046
  if (deadLetters.length) {
12941
13047
  try {
12942
- await materializeRefused(eng, ws, ledger, deadLetters);
13048
+ const m = await materializeRefused(eng, ws, ledger, deadLetters);
13049
+ refusalsParked = m.unparkedSessions.size === 0;
12943
13050
  } catch (e) {
13051
+ refusalsParked = false;
12944
13052
  log("refused-materialize-failed", { ws, error: String(e).slice(0, 200) });
12945
13053
  }
12946
13054
  }
@@ -12949,6 +13057,17 @@ async function admitIntentOffers(eng, ws, ledger, offers, { receiptFence = null,
12949
13057
  await commitAndPush(eng, ws, ledger);
12950
13058
  sealDone();
12951
13059
  }
13060
+ if (footageRef && refusalsParked) {
13061
+ try {
13062
+ kgit.deleteRef(eng, gitdirOf(ws), footageRef);
13063
+ } catch {
13064
+ }
13065
+ try {
13066
+ await custodyPush(eng, ws, ledger, { remoteRef: footageRef, del: true });
13067
+ } catch (e) {
13068
+ log("footage-delete-push-failed", { ws, ref: footageRef, error: String(e).slice(0, 200) });
13069
+ }
13070
+ }
12952
13071
  const gitdir = gitdirOf(ws);
12953
13072
  const head = kgit.resolveRef(eng, gitdir, BRANCH2) ?? main2.head;
12954
13073
  const summary = summaryCtx ? summaryOfCapture(summaryCtx, headBefore ?? "", head ?? "") : null;
@@ -12962,7 +13081,7 @@ async function admitIntentOffers(eng, ws, ledger, offers, { receiptFence = null,
12962
13081
  }
12963
13082
  log("admit-intent-offers", { ws, sessions: sessions.size, admitted: admittedTotal, rejected: [...sessions.values()].reduce((n, s) => n + s.rejected.length, 0), resultPack: resultPackB64 ? "inline" : "none" });
12964
13083
  const bornPacked = born.length ? packBornChildren(eng, born) : born;
12965
- return { sessions: [...sessions.values()].map((s) => ({ ...s, ...s.skipped.length ? {} : { skipped: void 0 } })), main: head, deadLetters, timings, summary: summary ?? null, born: bornPacked, birthOutcomes, ...resultPackB64 ? { resultPackB64, resultBase: clientBase } : {} };
13084
+ return { sessions: [...sessions.values()].map((s) => ({ ...s, ...s.skipped.length ? {} : { skipped: void 0 } })), main: head, sealed: true, sealedMain: custodyConfirmedMain(eng, ws), deadLetters, timings, summary: summary ?? null, born: bornPacked, birthOutcomes, ...resultPackB64 ? { resultPackB64, resultBase: clientBase } : {} };
12966
13085
  }
12967
13086
  function intentOfferResidencies(eng, offers) {
12968
13087
  const unique = /* @__PURE__ */ new Map();
@@ -13094,6 +13213,7 @@ async function mountWorkspace(eng, ws, ledger) {
13094
13213
  throw new Error(`kernel refused restored main ${remoteMain}: ${JSON.stringify(mounted)}`);
13095
13214
  }
13096
13215
  restored = true;
13216
+ custodyMainOf(eng).set(ws, remoteMain);
13097
13217
  verificationVerdict = jsStruct2(mounted.verdict);
13098
13218
  }
13099
13219
  } catch (e) {
@@ -13564,7 +13684,7 @@ async function custodyFetch(eng, ws, ledger, { remoteRef = "refs/heads/main", lo
13564
13684
  if (writeRef) kgit.writeRef(eng, gitdirOf(ws), fullRef(localRef || remoteRef), tip);
13565
13685
  return tip;
13566
13686
  }
13567
- async function custodyPush(eng, ws, ledger, { localRef = null, remoteRef, del = false, pointer = false, newOid = null }) {
13687
+ async function custodyPush(eng, ws, ledger, { localRef = null, remoteRef, del = false, pointer = false, newOid = null, ffOnly = false }) {
13568
13688
  const headers = ledger.headers || {};
13569
13689
  const remoteRefFull = fullRef(remoteRef);
13570
13690
  let remote;
@@ -13580,22 +13700,46 @@ async function custodyPush(eng, ws, ledger, { localRef = null, remoteRef, del =
13580
13700
  }
13581
13701
  const tgt = newOid || kgit.resolveRef(eng, gitdirOf(ws), fullRef(localRef || remoteRef));
13582
13702
  if (!tgt) throw new Error(`custodyPush: local ref ${localRef || remoteRef} unresolved`);
13583
- if (tgt === oldOid) return;
13703
+ if (tgt === oldOid) {
13704
+ if (remoteRefFull === fullRef(BRANCH2)) custodyMainOf(eng).set(ws, tgt);
13705
+ return;
13706
+ }
13584
13707
  let pack = null;
13585
13708
  if (!pointer) {
13586
13709
  pack = oldOid !== ZERO_OID2 ? packDelta(eng, ws, oldOid, tgt) : null;
13710
+ if (!pack && ffOnly && oldOid !== ZERO_OID2) {
13711
+ throw new Error(`custody ${remoteRefFull} regression refused: remote ${oldOid.slice(0, 12)} is not an ancestor of local ${tgt.slice(0, 12)} \u2014 this engine's view is stale; re-mount from custody instead of forcing`);
13712
+ }
13587
13713
  if (!pack) pack = packDelta(eng, ws, "", tgt);
13588
13714
  }
13589
13715
  const packBytes = pack?.byteLength ?? 0;
13590
13716
  try {
13591
- return await pushVia(ledger, () => pushPack2(ledger.remote, headers, { updates: [{ ref: remoteRefFull, oldOid, newOid: tgt }], pack }));
13717
+ const r = await pushVia(ledger, () => pushPack2(ledger.remote, headers, { updates: [{ ref: remoteRefFull, oldOid, newOid: tgt }], pack }));
13718
+ if (remoteRefFull === fullRef(BRANCH2)) custodyMainOf(eng).set(ws, tgt);
13719
+ return r;
13592
13720
  } catch (error) {
13593
13721
  throw new Error(`custody receive-pack push failed for ${remoteRefFull} (${packBytes} pack bytes): ${String(error)}`, { cause: error });
13594
13722
  }
13595
13723
  }
13724
+ function custodyMainOf(eng) {
13725
+ eng._custodyMain ??= /* @__PURE__ */ new Map();
13726
+ return eng._custodyMain;
13727
+ }
13728
+ function custodyConfirmedMain(eng, ws) {
13729
+ return custodyMainOf(eng).get(ws) ?? null;
13730
+ }
13731
+ function isDirty(eng, ws) {
13732
+ try {
13733
+ const resident = kgit.resolveRef(eng, gitdirOf(ws), BRANCH2);
13734
+ if (!resident) return false;
13735
+ return resident !== custodyMainOf(eng).get(ws);
13736
+ } catch {
13737
+ return false;
13738
+ }
13739
+ }
13596
13740
  async function commitAndPush(eng, ws, ledger) {
13597
13741
  try {
13598
- await custodyPush(eng, ws, ledger, { remoteRef: "refs/heads/main" });
13742
+ await custodyPush(eng, ws, ledger, { remoteRef: "refs/heads/main", ffOnly: true });
13599
13743
  } catch (e) {
13600
13744
  unmount(eng, ws);
13601
13745
  const cause = e instanceof Error ? e.cause : void 0;
@@ -13998,6 +14142,80 @@ async function admitAll(eng, ws, ledger, { receiptFence = null, shardRouting = n
13998
14142
  _sessionDone();
13999
14143
  }
14000
14144
  }
14145
+ const offeredRefPrefix = "refs/heads/parked/offered/";
14146
+ const footageConsumed = [];
14147
+ const footageBorn = [], footageBirthOutcomes = [];
14148
+ try {
14149
+ const offeredRefs = Object.entries(await custodyLsRefs(ledger, offeredRefPrefix));
14150
+ for (const [oref, ooid] of offeredRefs) {
14151
+ if (!oref.startsWith(offeredRefPrefix) || !ooid) continue;
14152
+ const fcid = `offered:${oref.slice(offeredRefPrefix.length)}`;
14153
+ const _fDone = _span(`footage ${fcid.slice(0, 24)}`, "csession", 1, { client: fcid });
14154
+ const admitted = [], rejected = [], skipped = [];
14155
+ try {
14156
+ try {
14157
+ await custodyFetch(eng, ws, ledger, { remoteRef: oref, have: kgit.resolveRef(eng, gitdir, BRANCH2) });
14158
+ } catch {
14159
+ await custodyFetch(eng, ws, ledger, { remoteRef: oref, have: null });
14160
+ }
14161
+ const main2 = await mainIntentIds(eng, ws);
14162
+ for (let i = 0; ; i++) {
14163
+ let blob = null;
14164
+ try {
14165
+ blob = kgit.readBlob(eng, gitdir, ooid, `${i}.change.usda`);
14166
+ } catch {
14167
+ blob = null;
14168
+ }
14169
+ if (!blob || !blob.byteLength) break;
14170
+ try {
14171
+ const inspected = kgit.inspectChange(eng, blob);
14172
+ const intentId = inspected.intentId || null;
14173
+ const oid = String(intentId || `${fcid}#${i}`).slice(0, 10);
14174
+ if (!inspected.authored || !inspected.call) {
14175
+ rejected.push({ oid, error: "parked offer is not an authored nominal call" });
14176
+ continue;
14177
+ }
14178
+ const { domainPath: dom, directivePath: dirId } = inspected.call;
14179
+ if (receiptFence && dirId && Array.isArray(receiptFence[dom]) && receiptFence[dom].includes(dirId)) {
14180
+ const error = `'${dom}/${dirId}' is a capability RECEIPT \u2014 receipts are executor/owner writes, never open-session writes`;
14181
+ rejected.push({ oid, error, deadLettered: true });
14182
+ deadLetters.push({ intentId, oid, session: fcid, error, blob, domain: dom, directiveId: dirId });
14183
+ continue;
14184
+ }
14185
+ if (intentId && main2.ids.has(intentId)) {
14186
+ skipped.push({ oid, intent: intentId, alreadyOnMain: true });
14187
+ continue;
14188
+ }
14189
+ const res = offerAdmit(eng, ws, blob, { defer: canDeferProjection });
14190
+ if (res.ok) {
14191
+ if (intentId) main2.ids.add(intentId);
14192
+ main2.oids.add(res.head);
14193
+ main2.head = res.head;
14194
+ admitted.push({ oid, intent: intentId, head: res.head });
14195
+ totalAdmitted += 1;
14196
+ _intents += 1;
14197
+ if (Array.isArray(res.born) && res.born.length) footageBorn.push(...res.born);
14198
+ if (Array.isArray(res.birthOutcomes) && res.birthOutcomes.length) footageBirthOutcomes.push(...res.birthOutcomes);
14199
+ } else {
14200
+ const error = `${String(res.error || "").slice(0, 300)} \u2014 dead-lettered; fix the law (or the payload), then POST /v1/workspaces/${ws}/dead-letters/retry`;
14201
+ rejected.push({ oid, error, deadLettered: true });
14202
+ deadLetters.push({ intentId, oid, session: fcid, error, blob, domain: dom, directiveId: dirId });
14203
+ }
14204
+ } catch (e) {
14205
+ rejected.push({ oid: `${fcid}#${i}`.slice(0, 10), error: String(e).slice(0, 200) });
14206
+ }
14207
+ }
14208
+ footageConsumed.push(oref);
14209
+ results.push({ session: fcid, admitted, rejected, ...skipped.length ? { skipped } : {} });
14210
+ } catch (e) {
14211
+ results.push({ session: fcid, error: String(e).slice(0, 200) });
14212
+ } finally {
14213
+ _fDone({ admitted: admitted.length, rejected: rejected.length });
14214
+ }
14215
+ }
14216
+ } catch (e) {
14217
+ log("footage-scan-failed", { ws, error: String(e).slice(0, 200) });
14218
+ }
14001
14219
  if (canDeferProjection && totalAdmitted) {
14002
14220
  const _projDone = _span("projection flush (batch)", "cproject", 1, { admitted: totalAdmitted });
14003
14221
  try {
@@ -14021,7 +14239,8 @@ async function admitAll(eng, ws, ledger, { receiptFence = null, shardRouting = n
14021
14239
  const hasCoordination = !!(summaryCtx || placements.length || splits.length || globalsOut.length);
14022
14240
  const deferDurability = defer && !hasCoordination;
14023
14241
  let _sealed = true;
14024
- if (totalAdmitted && !deferDurability) {
14242
+ const sealNeeded = totalAdmitted > 0 || isDirty(eng, ws);
14243
+ if (sealNeeded && !deferDurability) {
14025
14244
  const _sealDone = _span(`seal (batch)`, "cseal", 1, { sessions: consumed.length, admitted: totalAdmitted });
14026
14245
  const _s = Date.now();
14027
14246
  try {
@@ -14041,17 +14260,38 @@ async function admitAll(eng, ws, ledger, { receiptFence = null, shardRouting = n
14041
14260
  } catch {
14042
14261
  }
14043
14262
  }
14263
+ for (const oref of footageConsumed) {
14264
+ const fcid = `offered:${oref.slice(offeredRefPrefix.length)}`;
14265
+ if (unparkedSessions.has(fcid)) continue;
14266
+ try {
14267
+ kgit.deleteRef(eng, gitdir, oref);
14268
+ } catch {
14269
+ }
14270
+ try {
14271
+ await custodyPush(eng, ws, ledger, { remoteRef: oref, del: true });
14272
+ } catch {
14273
+ }
14274
+ }
14044
14275
  }
14045
14276
  const newMain = kgit.resolveRef(eng, gitdir, BRANCH2);
14046
- log("admit", { ws, sessions: results.length, main: newMain });
14277
+ log("admit", { ws, sessions: results.length, main: newMain, sealed: _sealed, sealedMain: custodyConfirmedMain(eng, ws) });
14047
14278
  const summary = summaryCtx ? summaryOfCapture(summaryCtx, headBefore ?? "", newMain) : null;
14048
14279
  const _total = Date.now() - _t0;
14049
14280
  const timings = { totalMs: _total, scanMs: _scanMs, fetchMs: _fetchMs, sealMs: _sealMs, gateFoldMs: Math.max(0, _total - _scanMs - _fetchMs - _sealMs), intents: _intents, sessions: results.length, spans: _spans };
14050
14281
  return {
14282
+ // SCOREBOARD TRUTH: `main` is the resident fold head (the ack the client converges to);
14283
+ // `sealedMain` is the custody-confirmed head — THE ONLY head the host may advertise/cache
14284
+ // (seated_barrier_and_seal_obligation.md Part 2). `sealed:false` ⇒ the seal obligation is
14285
+ // outstanding and the caller must re-arm the admit alarm (retry) rather than move on.
14051
14286
  sessions: results,
14052
14287
  main: newMain,
14288
+ sealed: _sealed,
14289
+ sealedMain: custodyConfirmedMain(eng, ws),
14053
14290
  deadLetters,
14054
14291
  timings,
14292
+ // Footage-replayed `.births()` offers: the born child custody + typed verdicts, for the caller
14293
+ // to persist exactly as the live POST lane does (packed here — the caller holds no engine FS).
14294
+ ...footageBorn.length ? { born: packBornChildren(eng, footageBorn), birthOutcomes: footageBirthOutcomes } : {},
14055
14295
  ...summary ? { summary } : {},
14056
14296
  ...placements.length ? { placements } : {},
14057
14297
  ...splits.length ? { splits } : {},
@@ -14065,7 +14305,7 @@ async function backupHead(eng, ws, ledger, head) {
14065
14305
  if (!eng.mounted.has(ws)) return { ok: false, error: "not mounted" };
14066
14306
  if (!head) return { ok: false, error: "no head" };
14067
14307
  try {
14068
- await custodyPush(eng, ws, ledger, { remoteRef: "refs/heads/main", newOid: head });
14308
+ await custodyPush(eng, ws, ledger, { remoteRef: "refs/heads/main", newOid: head, ffOnly: true });
14069
14309
  return { ok: true, head };
14070
14310
  } catch (e) {
14071
14311
  return { ok: false, error: String(e?.message || e).slice(0, 200), head };
@@ -14078,7 +14318,7 @@ async function backupToCustody(eng, ws, ledger, { consumed = [] } = {}) {
14078
14318
  head = kgit.resolveRef(eng, gitdir, BRANCH2);
14079
14319
  if (!head) return { ok: false, error: "no local main" };
14080
14320
  try {
14081
- await custodyPush(eng, ws, ledger, { remoteRef: "refs/heads/main" });
14321
+ await custodyPush(eng, ws, ledger, { remoteRef: "refs/heads/main", ffOnly: true });
14082
14322
  } catch (e) {
14083
14323
  return { ok: false, error: String(e).slice(0, 200), head };
14084
14324
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "githolon",
3
- "version": "0.105.3",
3
+ "version": "0.105.5",
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",
@@ -30,7 +30,7 @@
30
30
  "dependencies": {
31
31
  "@bjorn3/browser_wasi_shim": "0.4.2",
32
32
  "@bufbuild/protobuf": "^2.13.0",
33
- "@githolon/dsl": "^0.105.3",
33
+ "@githolon/dsl": "^0.105.5",
34
34
  "isomorphic-git": "^1.38.4",
35
35
  "tsx": "^4.19.2"
36
36
  },