@githolon/dsl 0.105.13 → 0.105.14
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/dart/CHANGELOG.md +4 -0
- package/dart/lib/src/runner/node-entry.mjs +21 -9
- package/dart/lib/src/runner/offline-install.mjs +6 -8
- package/dart/lib/src/session_bound.dart +7 -7
- package/dart/lib/src/webview_bridge.dart +4 -5
- package/dart/pubspec.yaml +1 -1
- package/package.json +1 -1
- package/runtime/generated-application-host.js +50 -17
- package/src/build_package.ts +37 -3
- package/src/check_package_main.ts +12 -10
- package/src/codegen_dart.ts +7 -4
- package/src/codegen_dart_client.ts +5 -16
- package/src/codegen_ts.ts +32 -4
- package/src/compile_package_main.ts +100 -9
- package/src/evolution.ts +313 -0
- package/src/evolution_files.ts +31 -0
- package/src/evolve_preview.ts +64 -55
- package/src/index.ts +10 -0
- package/src/law_graph.ts +58 -1
- package/src/law_graph_usda.ts +26 -0
- package/src/stable_ids_for_module.ts +1 -1
package/src/evolve_preview.ts
CHANGED
|
@@ -7,9 +7,9 @@
|
|
|
7
7
|
* THE EVOLVE PREVIEW — `githolon check --against <prior>` (architecture/author_time_gate.md,
|
|
8
8
|
* the `--against` row of check slice 3). The evolve gate is the kernel's #58 diff-by-stable-id
|
|
9
9
|
* (executor/src/law58.rs::evolve_violation): a RENAME (same sid, new label) admits silently; a
|
|
10
|
-
* true REMOVAL / merge-driver change refuses TYPED unless the
|
|
11
|
-
*
|
|
12
|
-
*
|
|
10
|
+
* true REMOVAL / merge-driver change refuses TYPED unless the candidate law itself carries the
|
|
11
|
+
* corresponding retirement or deterministic evolution. Application/deployment code never authors
|
|
12
|
+
* stable-id waivers.
|
|
13
13
|
*
|
|
14
14
|
* This module is the AUTHOR-TIME TWIN of that gate: given the PRIOR law and the CURRENT build,
|
|
15
15
|
* it diffs them BY STABLE ID and pre-fires the gate's verdicts — with the EXACT generated
|
|
@@ -18,10 +18,10 @@
|
|
|
18
18
|
* RENAME (note) — same sid, new label; admits silently, old rows read the new label.
|
|
19
19
|
* ADDITIVE (note) — a new aggregate/field/directive (incl. an additive optional VO field);
|
|
20
20
|
* the evolve gate admits it silently (vo_first_class.md slice 6).
|
|
21
|
-
* REMOVAL (refuse) — a sid disappeared;
|
|
21
|
+
* REMOVAL (refuse) — a sid disappeared; declare a typed retirement in the evolution layer.
|
|
22
22
|
* CONTRACT (refuse) — same sid, changed value language without a deterministic projection;
|
|
23
23
|
* needs `.evolvesFrom(...)`, never a disposition.
|
|
24
|
-
* DRIVER (refuse) — same sid, changed merge driver; needs
|
|
24
|
+
* DRIVER (refuse) — same sid, changed merge driver; needs a typed evolution or a new field.
|
|
25
25
|
* PAYLOAD (warn) — a directive's payload shape changed; clients compiled against the old
|
|
26
26
|
* law get typed 422s until regenerated (directives are not sid-tracked).
|
|
27
27
|
*
|
|
@@ -66,19 +66,17 @@ export interface EvolveDomain {
|
|
|
66
66
|
readonly aggregates: Record<string, EvolveAggregate>;
|
|
67
67
|
/** keyed by DIRECTIVE ID (directives are not sid-tracked — the dispatched name IS identity). */
|
|
68
68
|
readonly directives: Record<string, DirectivePayloadShape>;
|
|
69
|
+
/** Authored candidate tombstones, kept label-shaped until the compiler resolves them to sids. */
|
|
70
|
+
readonly retirements?: readonly {
|
|
71
|
+
kind: "aggregate" | "field";
|
|
72
|
+
aggregate: string;
|
|
73
|
+
field?: string;
|
|
74
|
+
}[];
|
|
69
75
|
}
|
|
70
76
|
|
|
71
77
|
/** A whole package's evolve surface, keyed by DOMAIN KEY. */
|
|
72
78
|
export type EvolveLaw = Record<string, EvolveDomain>;
|
|
73
79
|
|
|
74
|
-
/** The dispositions fragment the installDomain offer carries to admit destructive evolves (the exact
|
|
75
|
-
* shape executor/src/law58.rs::dispositions_from_payload parses). */
|
|
76
|
-
export interface EvolveDispositionsJson {
|
|
77
|
-
retired?: string[];
|
|
78
|
-
retyped?: Record<string, string>;
|
|
79
|
-
rebinds?: Record<string, string>;
|
|
80
|
-
}
|
|
81
|
-
|
|
82
80
|
// ── normalizers: build an EvolveLaw from the artifacts a dev already has ────────────
|
|
83
81
|
|
|
84
82
|
/** Build ONE domain's aggregate surface from a #58 `StableIds` block (+ optional driver
|
|
@@ -217,29 +215,39 @@ export function currentEvolveLaw(
|
|
|
217
215
|
const stableAggregate = Object.values(stableIds.aggregates).find(
|
|
218
216
|
(candidate) => candidate.sid === agg.sid,
|
|
219
217
|
);
|
|
220
|
-
const
|
|
218
|
+
const stableFields = new Map(
|
|
219
|
+
Object.values(stableAggregate?.fields ?? {})
|
|
220
|
+
.map((field) => [field.sid, field] as const),
|
|
221
|
+
);
|
|
222
|
+
const schema = locked?.schemas?.[agg.label]
|
|
223
|
+
?? Object.entries(locked?.schemas ?? {}).find(([type]) => type.endsWith(`.${agg.label}`))?.[1];
|
|
221
224
|
for (const f of Object.values(agg.fields)) {
|
|
222
|
-
const stableField =
|
|
223
|
-
(candidate) => candidate.sid === f.sid,
|
|
224
|
-
);
|
|
225
|
+
const stableField = stableFields.get(f.sid);
|
|
225
226
|
const driver = stableField?.driver ?? schema?.[f.label];
|
|
226
227
|
if (driver !== undefined) (f as { driver?: string }).driver = JSON.stringify(driver);
|
|
228
|
+
if (stableField?.contract !== undefined) f.contract = stableField.contract;
|
|
229
|
+
if (stableField?.schema !== undefined) f.schema = stableField.schema;
|
|
230
|
+
if (stableField?.evolutions !== undefined) {
|
|
231
|
+
f.evolutions = stableField.evolutions.map((rule) => ({ from: rule.from, to: rule.to }));
|
|
232
|
+
}
|
|
227
233
|
}
|
|
228
234
|
}
|
|
229
235
|
}
|
|
230
|
-
law[key] =
|
|
236
|
+
law[key] = {
|
|
237
|
+
...dom,
|
|
238
|
+
...(mod.retirements !== undefined && mod.retirements.length > 0
|
|
239
|
+
? { retirements: mod.retirements }
|
|
240
|
+
: {}),
|
|
241
|
+
};
|
|
231
242
|
}
|
|
232
243
|
return law;
|
|
233
244
|
}
|
|
234
245
|
|
|
235
246
|
// ── the diff ────────────────────────────────────────────────────────────────────────
|
|
236
247
|
|
|
237
|
-
/** The full preview result
|
|
238
|
-
* demand, keyed by DOMAIN KEY (the object carried by an installDomain offer). */
|
|
248
|
+
/** The full author-time preview result. Mechanical transition authority stays in candidate law. */
|
|
239
249
|
export interface EvolveDiffResult {
|
|
240
250
|
readonly findings: Finding[];
|
|
241
|
-
/** domain key → the dispositions fragment that makes THIS domain's destructive evolves admit. */
|
|
242
|
-
readonly dispositions: Record<string, EvolveDispositionsJson>;
|
|
243
251
|
}
|
|
244
252
|
|
|
245
253
|
/** A short, bounded render of a directive's payload field shape (for a warn's old-vs-new naming). */
|
|
@@ -250,10 +258,6 @@ function payloadShapeText(shape: DirectivePayloadShape): string {
|
|
|
250
258
|
return `{ ${parts.join(", ")} }`;
|
|
251
259
|
}
|
|
252
260
|
|
|
253
|
-
function ensureDisp(map: Record<string, EvolveDispositionsJson>, domain: string): EvolveDispositionsJson {
|
|
254
|
-
return (map[domain] ??= {});
|
|
255
|
-
}
|
|
256
|
-
|
|
257
261
|
/** Mirror executor::law58::evolution_reaches: one deterministic route per source,
|
|
258
262
|
* accumulated across supported releases, must terminate at the current schema. */
|
|
259
263
|
function evolutionReaches(
|
|
@@ -276,13 +280,12 @@ function evolutionReaches(
|
|
|
276
280
|
return true;
|
|
277
281
|
}
|
|
278
282
|
|
|
279
|
-
/** Diff one domain's aggregates/fields/directives BY STABLE ID and append findings
|
|
283
|
+
/** Diff one domain's aggregates/fields/directives BY STABLE ID and append findings. */
|
|
280
284
|
function diffDomain(
|
|
281
285
|
domain: string,
|
|
282
286
|
prior: EvolveDomain,
|
|
283
287
|
current: EvolveDomain,
|
|
284
288
|
findings: Finding[],
|
|
285
|
-
disp: Record<string, EvolveDispositionsJson>,
|
|
286
289
|
): void {
|
|
287
290
|
const curAggBySid = current.aggregates;
|
|
288
291
|
const priorAggBySid = prior.aggregates;
|
|
@@ -290,14 +293,18 @@ function diffDomain(
|
|
|
290
293
|
for (const [sid, oldAgg] of Object.entries(priorAggBySid)) {
|
|
291
294
|
const newAgg = curAggBySid[sid];
|
|
292
295
|
if (newAgg === undefined) {
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
+
const declared = current.retirements?.some(
|
|
297
|
+
(retirement) => retirement.kind === "aggregate" && retirement.aggregate === oldAgg.label,
|
|
298
|
+
) ?? false;
|
|
296
299
|
findings.push({
|
|
297
300
|
domain,
|
|
298
|
-
level: "refuse",
|
|
299
|
-
what:
|
|
300
|
-
|
|
301
|
+
level: declared ? "note" : "refuse",
|
|
302
|
+
what: declared
|
|
303
|
+
? `aggregate '${oldAgg.label}' (stable id ${sid}) is RETIRED by candidate law`
|
|
304
|
+
: `aggregate '${oldAgg.label}' (stable id ${sid}) is REMOVED without a candidate-law retirement`,
|
|
305
|
+
fix: declared
|
|
306
|
+
? "the compiler will lower this declaration to a typed USDA tombstone"
|
|
307
|
+
: `create evolutions/${domain}/<concern>.evolution.ts and export retirement(formerAggregate("${oldAgg.label}"))`,
|
|
301
308
|
});
|
|
302
309
|
continue;
|
|
303
310
|
}
|
|
@@ -313,13 +320,20 @@ function diffDomain(
|
|
|
313
320
|
for (const [fSid, oldF] of Object.entries(oldAgg.fields)) {
|
|
314
321
|
const newF = newAgg.fields[fSid];
|
|
315
322
|
if (newF === undefined) {
|
|
316
|
-
const
|
|
317
|
-
|
|
323
|
+
const declared = current.retirements?.some(
|
|
324
|
+
(retirement) => retirement.kind === "field"
|
|
325
|
+
&& retirement.aggregate === oldAgg.label
|
|
326
|
+
&& retirement.field === oldF.label,
|
|
327
|
+
) ?? false;
|
|
318
328
|
findings.push({
|
|
319
329
|
domain,
|
|
320
|
-
level: "refuse",
|
|
321
|
-
what:
|
|
322
|
-
|
|
330
|
+
level: declared ? "note" : "refuse",
|
|
331
|
+
what: declared
|
|
332
|
+
? `field '${oldAgg.label}.${oldF.label}' (stable id ${fSid}) is RETIRED by candidate law`
|
|
333
|
+
: `field '${oldAgg.label}.${oldF.label}' (stable id ${fSid}) is REMOVED without a candidate-law retirement`,
|
|
334
|
+
fix: declared
|
|
335
|
+
? "the compiler will lower this declaration to a typed USDA tombstone"
|
|
336
|
+
: `create evolutions/${domain}/<concern>.evolution.ts, name formerAggregate("${oldAgg.label}", ["${oldF.label}"]), and export its retirement`,
|
|
323
337
|
});
|
|
324
338
|
continue;
|
|
325
339
|
}
|
|
@@ -336,27 +350,26 @@ function diffDomain(
|
|
|
336
350
|
oldF.driver !== undefined && newF.driver !== undefined && oldF.driver !== newF.driver;
|
|
337
351
|
const contractChanged =
|
|
338
352
|
oldF.contract !== undefined && newF.contract !== undefined && oldF.contract !== newF.contract;
|
|
339
|
-
const
|
|
353
|
+
const evolutionCovered = evolutionReaches(newF, oldF.schema, newF.schema);
|
|
354
|
+
const contractCovered = contractChanged && evolutionCovered;
|
|
340
355
|
if (contractChanged && !contractCovered) {
|
|
341
356
|
findings.push({
|
|
342
357
|
domain,
|
|
343
358
|
level: "refuse",
|
|
344
359
|
what: `field '${oldAgg.label}.${oldF.label}' (stable id ${fSid}) changes its business value contract without deterministic evolution coverage`,
|
|
345
|
-
fix: `
|
|
360
|
+
fix: `in evolutions/${domain}/<concern>.evolution.ts export evolution(CurrentAggregate, "${newF.label}").from(oldField), optionally with an exhaustive finite business mapping`,
|
|
346
361
|
});
|
|
347
362
|
continue;
|
|
348
363
|
}
|
|
349
|
-
if ((kindChanged
|
|
350
|
-
const d = ensureDisp(disp, domain);
|
|
351
|
-
(d.retyped ??= {})[fSid] = "<why goes here>";
|
|
364
|
+
if ((kindChanged || driverChanged) && !evolutionCovered) {
|
|
352
365
|
const how = kindChanged && !contractCovered
|
|
353
366
|
? `read kind ${oldF.kind} → ${newF.kind}`
|
|
354
367
|
: `merge driver ${oldF.driver} → ${newF.driver}`;
|
|
355
368
|
findings.push({
|
|
356
369
|
domain,
|
|
357
370
|
level: "refuse",
|
|
358
|
-
what: `field '${oldAgg.label}.${oldF.label}' (stable id ${fSid})
|
|
359
|
-
fix: `
|
|
371
|
+
what: `field '${oldAgg.label}.${oldF.label}' (stable id ${fSid}) changes in place (${how}) without a candidate-law evolution`,
|
|
372
|
+
fix: `declare the old field contract and export evolution(CurrentAggregate, "${newF.label}").from(oldField) from evolutions/${domain}/<concern>.evolution.ts — or add a new field`,
|
|
360
373
|
});
|
|
361
374
|
}
|
|
362
375
|
}
|
|
@@ -433,31 +446,27 @@ function samePayloadShape(a: DirectivePayloadShape, b: DirectivePayloadShape): b
|
|
|
433
446
|
}
|
|
434
447
|
|
|
435
448
|
/**
|
|
436
|
-
* The full evolve preview: diff PRIOR vs CURRENT law by stable id
|
|
437
|
-
*
|
|
438
|
-
* one side are ADDITIVE (new domain — note) or a whole-domain REMOVAL (refuse per aggregate).
|
|
449
|
+
* The full evolve preview: diff PRIOR vs CURRENT law by stable id. Domains present in only one
|
|
450
|
+
* side are ADDITIVE (new domain — note) or a whole-domain REMOVAL (refuse per aggregate).
|
|
439
451
|
*/
|
|
440
452
|
export function evolveDiff(prior: EvolveLaw, current: EvolveLaw): EvolveDiffResult {
|
|
441
453
|
const findings: Finding[] = [];
|
|
442
|
-
const dispositions: Record<string, EvolveDispositionsJson> = {};
|
|
443
454
|
|
|
444
455
|
for (const [domain, priorDom] of Object.entries(prior)) {
|
|
445
456
|
const currentDom = current[domain];
|
|
446
457
|
if (currentDom === undefined) {
|
|
447
458
|
// The whole domain uninstalled — every aggregate's stable identity disappears.
|
|
448
|
-
const d = ensureDisp(dispositions, domain);
|
|
449
459
|
for (const [sid, agg] of Object.entries(priorDom.aggregates)) {
|
|
450
|
-
(d.retired ??= []).push(sid);
|
|
451
460
|
findings.push({
|
|
452
461
|
domain,
|
|
453
462
|
level: "refuse",
|
|
454
463
|
what: `domain '${domain}' aggregate '${agg.label}' (stable id ${sid}) disappears — the current build no longer carries domain '${domain}'`,
|
|
455
|
-
fix: `restore the domain
|
|
464
|
+
fix: `restore the domain and retire its aggregates in a typed evolution layer before removing executable law`,
|
|
456
465
|
});
|
|
457
466
|
}
|
|
458
467
|
continue;
|
|
459
468
|
}
|
|
460
|
-
diffDomain(domain, priorDom, currentDom, findings
|
|
469
|
+
diffDomain(domain, priorDom, currentDom, findings);
|
|
461
470
|
}
|
|
462
471
|
for (const [domain, currentDom] of Object.entries(current)) {
|
|
463
472
|
if (prior[domain] !== undefined) continue;
|
|
@@ -471,7 +480,7 @@ export function evolveDiff(prior: EvolveLaw, current: EvolveLaw): EvolveDiffResu
|
|
|
471
480
|
|
|
472
481
|
const rank = { refuse: 0, warn: 1, note: 2 } as const;
|
|
473
482
|
findings.sort((a, b) => rank[a.level] - rank[b.level]);
|
|
474
|
-
return { findings
|
|
483
|
+
return { findings };
|
|
475
484
|
}
|
|
476
485
|
|
|
477
486
|
/** The findings-only surface (the `evolvePreview(prior, current): Finding[]` contract). */
|
package/src/index.ts
CHANGED
|
@@ -160,6 +160,16 @@ export {
|
|
|
160
160
|
type ConflictPolicy,
|
|
161
161
|
type ConflictRule,
|
|
162
162
|
} from "./conflicts.js";
|
|
163
|
+
export {
|
|
164
|
+
evolution,
|
|
165
|
+
formerAggregate,
|
|
166
|
+
retirement,
|
|
167
|
+
type FieldEvolution,
|
|
168
|
+
type FieldEvolutionBuilder,
|
|
169
|
+
type FormerAggregate,
|
|
170
|
+
type FormerField,
|
|
171
|
+
type Retirement,
|
|
172
|
+
} from "./evolution.js";
|
|
163
173
|
export {
|
|
164
174
|
intent,
|
|
165
175
|
language,
|
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
|
-
|
|
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 };
|
package/src/law_graph_usda.ts
CHANGED
|
@@ -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(
|