@githolon/dsl 0.100.4 → 0.100.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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@githolon/dsl",
3
- "version": "0.100.4",
3
+ "version": "0.100.5",
4
4
  "type": "module",
5
5
  "description": "Nomos 2 domain-authoring DSL: aggregates + directives in TS, executed and encoded to the Rust kernel's wire shapes.",
6
6
  "license": "SEE LICENSE IN LICENSE.md",
@@ -32,8 +32,13 @@ import {
32
32
  } from "./birth_meter.js";
33
33
  import {
34
34
  reclaimChild,
35
+ reclaimChildAsAdmin,
36
+ reclaimChildAsAdminRequiresRetired,
35
37
  reclaimChildRequiresRetired,
36
38
  retireChild,
39
+ retireChildAsAdmin,
40
+ retireChildAsAdminNotReclaimed,
41
+ retireChildAsAdminRequiresBirth,
37
42
  retireChildRequiresBirth,
38
43
  retireChildNotReclaimed,
39
44
  } from "./framework/birth_meter.js";
@@ -82,8 +87,19 @@ export function withBirthMeterExports<M extends Record<string, unknown>>(mod: M)
82
87
  if (!("BirthGrant" in out)) out["BirthGrant"] = presented(BirthGrant);
83
88
  if (!("retireChild" in out)) out["retireChild"] = presented(retireChild);
84
89
  if (!("reclaimChild" in out)) out["reclaimChild"] = presented(reclaimChild);
90
+ if (!("retireChildAsAdmin" in out)) out["retireChildAsAdmin"] = presented(retireChildAsAdmin);
91
+ if (!("reclaimChildAsAdmin" in out)) out["reclaimChildAsAdmin"] = presented(reclaimChildAsAdmin);
85
92
  if (!("reclaimChildRequiresRetired" in out)) out["reclaimChildRequiresRetired"] = reclaimChildRequiresRetired;
93
+ if (!("reclaimChildAsAdminRequiresRetired" in out)) {
94
+ out["reclaimChildAsAdminRequiresRetired"] = reclaimChildAsAdminRequiresRetired;
95
+ }
86
96
  if (!("retireChildRequiresBirth" in out)) out["retireChildRequiresBirth"] = retireChildRequiresBirth;
87
97
  if (!("retireChildNotReclaimed" in out)) out["retireChildNotReclaimed"] = retireChildNotReclaimed;
98
+ if (!("retireChildAsAdminRequiresBirth" in out)) {
99
+ out["retireChildAsAdminRequiresBirth"] = retireChildAsAdminRequiresBirth;
100
+ }
101
+ if (!("retireChildAsAdminNotReclaimed" in out)) {
102
+ out["retireChildAsAdminNotReclaimed"] = retireChildAsAdminNotReclaimed;
103
+ }
88
104
  return out as M;
89
105
  }
@@ -82,7 +82,10 @@ import { assertIdOfLint } from "./idof_lint.js";
82
82
  import { assertSurfacedResolvers } from "./surfaced_lint.js";
83
83
  import { expandBusinessBirthExports } from "./business_birth.js";
84
84
  import {
85
+ reclaimChildAsAdminRequiresRetired,
85
86
  reclaimChildRequiresRetired,
87
+ retireChildAsAdminNotReclaimed,
88
+ retireChildAsAdminRequiresBirth,
86
89
  retireChildNotReclaimed,
87
90
  retireChildRequiresBirth,
88
91
  } from "./framework/birth_meter.js";
@@ -316,7 +319,14 @@ export function composeDomainModule(spec: DomainModuleSpec): DomainModule {
316
319
  const workspaceInvariants = [...(spec.workspaceInvariants ?? [])];
317
320
  if (declaresBirth) {
318
321
  const haveInvariant = new Set(workspaceInvariants.map((w) => w.id));
319
- for (const lifecycle of [reclaimChildRequiresRetired, retireChildNotReclaimed, retireChildRequiresBirth]) {
322
+ for (const lifecycle of [
323
+ reclaimChildRequiresRetired,
324
+ reclaimChildAsAdminRequiresRetired,
325
+ retireChildNotReclaimed,
326
+ retireChildAsAdminNotReclaimed,
327
+ retireChildRequiresBirth,
328
+ retireChildAsAdminRequiresBirth,
329
+ ]) {
320
330
  if (!haveInvariant.has(lifecycle.id)) workspaceInvariants.push(lifecycle);
321
331
  }
322
332
  }
@@ -86,6 +86,32 @@ export const retireChild = directive("retireChild")
86
86
  ];
87
87
  });
88
88
 
89
+ /**
90
+ * The operator half of the same retirement action. This is deliberately a separate, framework-internal
91
+ * directive because the relation gate is structural: creator recovery and platform administration have
92
+ * different authority proofs even though the client presents one `retire` action and both write the same
93
+ * lifecycle fact. A broken creator key must never make a malformed child impossible to converge.
94
+ */
95
+ export const retireChildAsAdmin = directive("retireChildAsAdmin")
96
+ .ensures(NomosChildLifecycle)
97
+ .payload(z.object({
98
+ child: z.string().min(1),
99
+ principal: z.string().min(1),
100
+ retiredBy: z.string().min(1),
101
+ retiredAt: z.number().int().nonnegative(),
102
+ }))
103
+ .plan((p) => {
104
+ const rec = instance(NomosChildLifecycle, childLifecycleId(p.child));
105
+ return [
106
+ set(rec, "child", p.child),
107
+ set(rec, "principal", p.principal),
108
+ set(rec, "status", "retired"),
109
+ set(rec, "retiredBy", p.retiredBy),
110
+ set(rec, "retiredAt", p.retiredAt),
111
+ ];
112
+ })
113
+ .requires("admin");
114
+
89
115
  export const reclaimChild = directive("reclaimChild")
90
116
  .ensures(NomosChildLifecycle)
91
117
  .payload(z.object({
@@ -102,6 +128,24 @@ export const reclaimChild = directive("reclaimChild")
102
128
  ];
103
129
  });
104
130
 
131
+ /** Admin-gated recovery half of {@link reclaimChild}; hidden behind the client's one reclaim action. */
132
+ export const reclaimChildAsAdmin = directive("reclaimChildAsAdmin")
133
+ .ensures(NomosChildLifecycle)
134
+ .payload(z.object({
135
+ child: z.string().min(1),
136
+ reclaimedBy: z.string().min(1),
137
+ reclaimedAt: z.number().int().nonnegative(),
138
+ }))
139
+ .plan((p) => {
140
+ const rec = instance(NomosChildLifecycle, childLifecycleId(p.child));
141
+ return [
142
+ set(rec, "status", "reclaimed"),
143
+ set(rec, "reclaimedBy", p.reclaimedBy),
144
+ set(rec, "reclaimedAt", p.reclaimedAt),
145
+ ];
146
+ })
147
+ .requires("admin");
148
+
105
149
  /** Reclaim is two-phase; a child must first be explicitly withdrawn. */
106
150
  export const reclaimChildRequiresRetired = workspaceInvariant("ReclaimChildRequiresRetired")
107
151
  .on("reclaimChild")
@@ -115,6 +159,18 @@ export const reclaimChildRequiresRetired = workspaceInvariant("ReclaimChildRequi
115
159
  : { reject: "reclaim-child-requires-retired" },
116
160
  );
117
161
 
162
+ /** Operator recovery has the same two-phase law, without depending on the broken creator credential. */
163
+ export const reclaimChildAsAdminRequiresRetired = workspaceInvariant("ReclaimChildAsAdminRequiresRetired")
164
+ .on("reclaimChildAsAdmin")
165
+ .reads((ctx) => [
166
+ refAs("record", "NomosChildLifecycle", "pre:" + childLifecycleId(String(ctx.intent.child ?? ""))),
167
+ ])
168
+ .assert(({ record }) =>
169
+ (record as { status?: string } | undefined)?.status === "retired"
170
+ ? { accept: true }
171
+ : { reject: "reclaim-child-requires-retired" },
172
+ );
173
+
118
174
  /** Reclaimed is permanent: retirement cannot move a child back out of the terminal state. */
119
175
  export const retireChildNotReclaimed = workspaceInvariant("RetireChildNotReclaimed")
120
176
  .on("retireChild")
@@ -127,6 +183,18 @@ export const retireChildNotReclaimed = workspaceInvariant("RetireChildNotReclaim
127
183
  : { accept: true },
128
184
  );
129
185
 
186
+ /** Reclaimed is terminal for operator recovery too. */
187
+ export const retireChildAsAdminNotReclaimed = workspaceInvariant("RetireChildAsAdminNotReclaimed")
188
+ .on("retireChildAsAdmin")
189
+ .reads((ctx) => [
190
+ refAs("record", "NomosChildLifecycle", "pre:" + childLifecycleId(String(ctx.intent.child ?? ""))),
191
+ ])
192
+ .assert(({ record }) =>
193
+ (record as { status?: string } | undefined)?.status === "reclaimed"
194
+ ? { reject: "reclaimed-child-is-terminal" }
195
+ : { accept: true },
196
+ );
197
+
130
198
  /** A lifecycle cannot mint authority for arbitrary custody: retirement must name its exact birth witness. */
131
199
  export const retireChildRequiresBirth = workspaceInvariant("RetireChildRequiresBirth")
132
200
  .on("retireChild")
@@ -143,6 +211,20 @@ export const retireChildRequiresBirth = workspaceInvariant("RetireChildRequiresB
143
211
  : { reject: "retire-child-requires-exact-birth-record" };
144
212
  });
145
213
 
214
+ /** Admin recovery may act for the recorded creator, but still cannot invent or retarget custody. */
215
+ export const retireChildAsAdminRequiresBirth = workspaceInvariant("RetireChildAsAdminRequiresBirth")
216
+ .on("retireChildAsAdmin")
217
+ .reads((ctx) => [
218
+ refAs("birth", "NomosBirthRecord", "pre:" + birthRecordId(String(ctx.intent.child ?? ""))),
219
+ ])
220
+ .assert(({ birth }, ctx) => {
221
+ const record = birth as { child?: string; principal?: string } | undefined;
222
+ const intent = ctx?.intent as { child?: string; principal?: string } | undefined;
223
+ return record?.child === intent?.child && record?.principal === intent?.principal
224
+ ? { accept: true }
225
+ : { reject: "retire-child-requires-exact-birth-record" };
226
+ });
227
+
146
228
  function sameLifecyclePrincipal(recorded: string | undefined, author: string | undefined): boolean {
147
229
  const normalize = (s: string | undefined): string => s?.startsWith("user:") ? s.slice(5) : (s ?? "");
148
230
  return normalize(recorded) !== "" && normalize(recorded) === normalize(author);
package/src/index.ts CHANGED
@@ -477,10 +477,15 @@ export {
477
477
  seedBirthPolicy,
478
478
  grantBirthAllowance,
479
479
  retireChild,
480
+ retireChildAsAdmin,
480
481
  reclaimChild,
482
+ reclaimChildAsAdmin,
481
483
  reclaimChildRequiresRetired,
484
+ reclaimChildAsAdminRequiresRetired,
482
485
  retireChildRequiresBirth,
486
+ retireChildAsAdminRequiresBirth,
483
487
  retireChildNotReclaimed,
488
+ retireChildAsAdminNotReclaimed,
484
489
  } from "./framework/birth_meter.js";
485
490
  // THE PLATFORM FOUNDING-ADMIN BOOTSTRAP (the platform analog of seedHomeCreator) — a tenant-importable
486
491
  // factory that seats a tenant-named governance relation for a write-once, author-bound founder at genesis.