@githolon/dsl 0.105.13 → 0.105.15

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/src/law_graph.ts CHANGED
@@ -745,10 +745,20 @@ export type DeclaredLawNode =
745
745
  export interface LawGraph {
746
746
  readonly v: 1;
747
747
  readonly domain: LawDomainNode;
748
+ /** Explicit tombstones authored in evolution files. The compiler registry remembers
749
+ * every disappearance, but only these candidate-carried facts authorize retirement. */
750
+ readonly retirements: readonly LawRetirement[];
748
751
  /** Canonically SID-ordered, so module/export discovery order cannot affect traversal. */
749
752
  readonly nodes: readonly DeclaredLawNode[];
750
753
  }
751
754
 
755
+ export interface LawRetirement {
756
+ readonly kind: "aggregate" | "field";
757
+ readonly sid: string;
758
+ readonly label: string;
759
+ readonly parentSid?: string;
760
+ }
761
+
752
762
  export interface DiscoverLawGraphOptions {
753
763
  readonly intentCarries?: readonly LawIdentityCarry[];
754
764
  readonly readCarries?: readonly LawIdentityCarry[];
@@ -2273,7 +2283,54 @@ export function discoverLawGraph(
2273
2283
  ...predicateNodes.values(),
2274
2284
  ].sort((a, b) => compareText(a.entity.id, b.entity.id));
2275
2285
  registry = identities.finish();
2276
- return { registry, graph: { v: 1, domain: domainNode, nodes } };
2286
+ const identityEntity = (
2287
+ kind: "aggregate" | "field",
2288
+ parent: string,
2289
+ label: string,
2290
+ status?: "active" | "retired",
2291
+ ) => {
2292
+ const matches = Object.entries(registry.entities).filter(([, candidate]) =>
2293
+ candidate.kind === kind &&
2294
+ candidate.parent === parent &&
2295
+ (status === undefined || candidate.status === status) &&
2296
+ (candidate.label === label || candidate.priorLabels?.includes(label) === true)
2297
+ );
2298
+ if (matches.length !== 1) {
2299
+ fail(
2300
+ `${kind} retirement '${label}' resolves to ${matches.length} prior identities under '${parent}'; ` +
2301
+ "a retirement must name exactly one declaration from the committed identity layer",
2302
+ );
2303
+ }
2304
+ return matches[0]!;
2305
+ };
2306
+ const retirements = (module.retirements ?? []).map((declaration): LawRetirement => {
2307
+ const [aggregateKey, aggregate] = identityEntity(
2308
+ "aggregate",
2309
+ domainKey,
2310
+ declaration.aggregate,
2311
+ declaration.kind === "aggregate" ? "retired" : undefined,
2312
+ );
2313
+ if (declaration.kind === "aggregate") {
2314
+ return {
2315
+ kind: "aggregate",
2316
+ sid: aggregate.sid,
2317
+ label: aggregate.label,
2318
+ };
2319
+ }
2320
+ const [, field] = identityEntity(
2321
+ "field",
2322
+ aggregateKey,
2323
+ declaration.field ?? "",
2324
+ "retired",
2325
+ );
2326
+ return {
2327
+ kind: "field",
2328
+ sid: field.sid,
2329
+ label: field.label,
2330
+ parentSid: aggregate.sid,
2331
+ };
2332
+ }).sort((left, right) => compareText(`${left.kind}:${left.sid}`, `${right.kind}:${right.sid}`));
2333
+ return { registry, graph: { v: 1, domain: domainNode, retirements, nodes } };
2277
2334
  }
2278
2335
 
2279
2336
  type ReadInputBase = { id: string; public: boolean };
@@ -17,6 +17,7 @@ import type {
17
17
  LawPredicateNode,
18
18
  LawReadContractNode,
19
19
  LawReadNode,
20
+ LawRetirement,
20
21
  LawRelationNode,
21
22
  LawRelationObjectTypeNode,
22
23
  LawRelationPredicateNode,
@@ -586,6 +587,19 @@ export function emitSparseLawGraphUsda(
586
587
  ` custom uniform string nomos:parentCertField = ${quote(domain.parentAttestedBirth.certField)}`,
587
588
  );
588
589
  }
590
+ if (graph.retirements.length > 0) {
591
+ lines.push("", ' def Scope "Retirements"', " {");
592
+ for (const retirement of graph.retirements) {
593
+ const name = `${retirement.kind === "aggregate" ? "a" : "f"}_${retirement.sid}`;
594
+ lines.push(
595
+ ` def NomosRetirement ${quote(name)}`,
596
+ " {",
597
+ ...emitRetirement(retirement, 20),
598
+ " }",
599
+ );
600
+ }
601
+ lines.push(" }");
602
+ }
589
603
  lines.push("");
590
604
  emitContainer(lines, "Aggregates", aggregates, 12, (aggregate, indent) =>
591
605
  emitAggregate(aggregate, fields, pathById, resolveDependency, indent));
@@ -664,6 +678,18 @@ export function emitSparseLawGraphUsda(
664
678
  return lines.join("\n");
665
679
  }
666
680
 
681
+ function emitRetirement(retirement: LawRetirement, indent: number): string[] {
682
+ const pad = " ".repeat(indent);
683
+ return [
684
+ `${pad}custom uniform token nomos:entityKind = ${quote(retirement.kind)}`,
685
+ `${pad}custom uniform string nomos:label = ${quote(retirement.label)}`,
686
+ `${pad}custom uniform string nomos:sid = ${quote(retirement.sid)}`,
687
+ ...(retirement.parentSid === undefined
688
+ ? []
689
+ : [`${pad}custom uniform string nomos:parentSid = ${quote(retirement.parentSid)}`]),
690
+ ];
691
+ }
692
+
667
693
  function emitAggregate(
668
694
  aggregate: LawAggregateNode,
669
695
  fields: readonly LawFieldNode[],
@@ -170,7 +170,7 @@ function fieldEvolutionRules(
170
170
  fieldSid,
171
171
  declaration.source.kind,
172
172
  source.contract,
173
- driver,
173
+ encodeDriver(declaration.source.driver),
174
174
  );
175
175
  if (declaration.mode === "identity" && !safeIdentityProjection(source.shape, target.shape)) {
176
176
  throw new Error(