@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 CHANGED
@@ -1,3 +1,7 @@
1
+ ## 0.64.29
2
+
3
+ - Restaged with the current kernel candidate: regenerated vendored assets (kernel release identity is baked into them). Published pub.dev archives are immutable, so regenerated bytes ride their own version.
4
+
1
5
  ## 0.64.28
2
6
 
3
7
  - Restaged with the current kernel candidate: regenerated vendored assets (kernel release identity is baked into them). Published pub.dev archives are immutable, so regenerated bytes ride their own version.
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  // <define:__NOMOS_KERNEL_RELEASE__>
4
- var define_NOMOS_KERNEL_RELEASE_default = { production: { hash: "64219cdd5441cd92cb08d5200a6e65e7814364cc6d1b7cd1b014322ea4372092", version: "kernel-64219cdd", features: ["compoundKeys", "capturedReads", "invariantGate", "surfacedMerge", "fieldProvenance", "nullSemantics", "attestedReads", "conflictSnapshots", "ownedAudience", "temporalEnum"], protocol: 1 }, testMint: { hash: "9ca691fd3524897b2edc236167b1b490701c85b41ad066cc41041829d42357f7", version: "kernel-9ca691fd", features: ["compoundKeys", "capturedReads", "invariantGate", "surfacedMerge", "fieldProvenance", "nullSemantics", "attestedReads", "conflictSnapshots", "ownedAudience", "temporalEnum"], protocol: 1 } };
4
+ var define_NOMOS_KERNEL_RELEASE_default = { production: { hash: "d37253bcefd90a180a6a1af96422a9f0724dfca9efd9ae7cff070afa2c410f09", version: "kernel-d37253bc", features: ["compoundKeys", "capturedReads", "invariantGate", "surfacedMerge", "fieldProvenance", "nullSemantics", "attestedReads", "conflictSnapshots", "ownedAudience", "temporalEnum"], protocol: 1 }, testMint: { hash: "b6bddd8d61390edbe2ee4e5f3c7f21108bdb7800af746d20f568936f63e2d517", version: "kernel-b6bddd8d", features: ["compoundKeys", "capturedReads", "invariantGate", "surfacedMerge", "fieldProvenance", "nullSemantics", "attestedReads", "conflictSnapshots", "ownedAudience", "temporalEnum"], protocol: 1 } };
5
5
 
6
6
  // runner/node-entry.ts
7
7
  import { createInterface } from "node:readline";
@@ -534,7 +534,7 @@ function createWebviewRunner({ post: post2, openRealm: openRealm2 }) {
534
534
  // FRAMEWORK-PRIVATE: generated clients say model.adopt(); the runner/client owns proof + receipt assembly.
535
535
  async adoptGeneratedModel(a) {
536
536
  const rec = pickSession(a);
537
- const r = await (await liveHolon(rec)).__nomosAdoptModel(a.domain, a.candidateHash, { because: a.because, dispositions: a.dispositions });
537
+ const r = await (await liveHolon(rec)).__nomosAdoptModel(a.domain, a.candidateHash, { because: a.because });
538
538
  if (r && r.ok && rec.autoStop && rec.autoStop.trigger) rec.autoStop.trigger("write");
539
539
  return { verdict: r };
540
540
  },
@@ -7667,7 +7667,7 @@ function prepareManagedPromotion(report, candidateHash, domain, opts = {}) {
7667
7667
  domainHash: candidateHash,
7668
7668
  ...opts.replaces ? { replaces: opts.replaces } : {},
7669
7669
  retireReplaced: opts.retireReplaced ?? false,
7670
- ...opts.dispositions ? { dispositions: opts.dispositions } : {},
7670
+ ...report.dispositions ? { dispositions: report.dispositions } : {},
7671
7671
  preflight
7672
7672
  }
7673
7673
  };
@@ -7683,7 +7683,6 @@ function prepareManagedRestoration(report, currentHash, priorHash, domain, opts
7683
7683
  }
7684
7684
  const acceptanceReason = opts.acceptanceReason?.trim();
7685
7685
  const prepared = prepareManagedPromotion(report, priorHash, domain, {
7686
- dispositions: report.dispositions,
7687
7686
  ...acceptanceReason ? { acceptanceReason } : {}
7688
7687
  });
7689
7688
  if (!prepared.ok) {
@@ -8173,7 +8172,14 @@ function createGovernanceOffers(ctx) {
8173
8172
  rows = JSON.parse(call("query", { queryBytes: qBytes2({ kind: "query", queryId: "relationTuplesByObject", paramsJson: JSON.stringify({ object }) }) })) || [];
8174
8173
  } else {
8175
8174
  const headers = authToken ? { Authorization: `Bearer ${authToken}`, "x-nomos-auth": authToken } : authHeaders;
8176
- const r = await _fetch(`${base}/v2/workspaces/${parent}/relationTuplesByObject?object=${encodeURIComponent(object)}`, { headers });
8175
+ const route = await resolveFrameworkRoute({
8176
+ parent,
8177
+ operationId: "relationTuplesByObject",
8178
+ method: "get",
8179
+ token: authToken,
8180
+ principal: p
8181
+ });
8182
+ const r = await _fetch(`${base}/v2/workspaces/${parent}${route.path}?object=${encodeURIComponent(object)}`, { headers });
8177
8183
  const text = await r.text();
8178
8184
  let body;
8179
8185
  try {
@@ -8227,7 +8233,14 @@ function createGovernanceOffers(ctx) {
8227
8233
  rows = JSON.parse(call("query", { queryBytes: qBytes2({ kind: "query", queryId: "relationTuplesByObject", paramsJson: JSON.stringify({ object }) }) })) || [];
8228
8234
  } else {
8229
8235
  const headers = authToken ? { Authorization: `Bearer ${authToken}`, "x-nomos-auth": authToken } : authHeaders;
8230
- const r = await _fetch(`${base}/v2/workspaces/${ws}/relationTuplesByObject?object=${encodeURIComponent(object)}`, { headers });
8236
+ const route = await resolveFrameworkRoute({
8237
+ parent: ws,
8238
+ operationId: "relationTuplesByObject",
8239
+ method: "get",
8240
+ token: authToken,
8241
+ principal: subj
8242
+ });
8243
+ const r = await _fetch(`${base}/v2/workspaces/${ws}${route.path}?object=${encodeURIComponent(object)}`, { headers });
8231
8244
  const text = await r.text();
8232
8245
  let body;
8233
8246
  try {
@@ -10234,7 +10247,7 @@ ${String(commit).toLowerCase()}`;
10234
10247
  domain,
10235
10248
  candidateHash,
10236
10249
  atHash,
10237
- ...opts.dispositions ? { dispositions: opts.dispositions } : {}
10250
+ ackBy: connectionPrincipal
10238
10251
  })
10239
10252
  }));
10240
10253
  });
@@ -10247,8 +10260,7 @@ ${String(commit).toLowerCase()}`;
10247
10260
  }
10248
10261
  const prepared = prepareManagedPromotion(report, candidateHash, domain, {
10249
10262
  ...currentHash && currentHash !== candidateHash ? { replaces: currentHash } : {},
10250
- ...opts.because !== void 0 ? { acceptanceReason: opts.because } : {},
10251
- ...opts.dispositions ? { dispositions: opts.dispositions } : {}
10263
+ ...opts.because !== void 0 ? { acceptanceReason: opts.because } : {}
10252
10264
  });
10253
10265
  if (!prepared.ok) return prepared;
10254
10266
  try {
@@ -6,7 +6,7 @@ var __export = (target, all) => {
6
6
  };
7
7
 
8
8
  // <define:__NOMOS_KERNEL_RELEASE__>
9
- var define_NOMOS_KERNEL_RELEASE_default = { production: { hash: "64219cdd5441cd92cb08d5200a6e65e7814364cc6d1b7cd1b014322ea4372092", version: "kernel-64219cdd", features: ["compoundKeys", "capturedReads", "invariantGate", "surfacedMerge", "fieldProvenance", "nullSemantics", "attestedReads", "conflictSnapshots", "ownedAudience", "temporalEnum"], protocol: 1 }, testMint: { hash: "9ca691fd3524897b2edc236167b1b490701c85b41ad066cc41041829d42357f7", version: "kernel-9ca691fd", features: ["compoundKeys", "capturedReads", "invariantGate", "surfacedMerge", "fieldProvenance", "nullSemantics", "attestedReads", "conflictSnapshots", "ownedAudience", "temporalEnum"], protocol: 1 } };
9
+ var define_NOMOS_KERNEL_RELEASE_default = { production: { hash: "d37253bcefd90a180a6a1af96422a9f0724dfca9efd9ae7cff070afa2c410f09", version: "kernel-d37253bc", features: ["compoundKeys", "capturedReads", "invariantGate", "surfacedMerge", "fieldProvenance", "nullSemantics", "attestedReads", "conflictSnapshots", "ownedAudience", "temporalEnum"], protocol: 1 }, testMint: { hash: "b6bddd8d61390edbe2ee4e5f3c7f21108bdb7800af746d20f568936f63e2d517", version: "kernel-b6bddd8d", features: ["compoundKeys", "capturedReads", "invariantGate", "surfacedMerge", "fieldProvenance", "nullSemantics", "attestedReads", "conflictSnapshots", "ownedAudience", "temporalEnum"], protocol: 1 } };
10
10
 
11
11
  // runner/offline-install.ts
12
12
  import { readFileSync as readFileSync2, writeFileSync as writeFileSync2, existsSync } from "node:fs";
@@ -2009,7 +2009,7 @@ function prepareManagedPromotion(report, candidateHash, domain, opts = {}) {
2009
2009
  domainHash: candidateHash,
2010
2010
  ...opts.replaces ? { replaces: opts.replaces } : {},
2011
2011
  retireReplaced: opts.retireReplaced ?? false,
2012
- ...opts.dispositions ? { dispositions: opts.dispositions } : {},
2012
+ ...report.dispositions ? { dispositions: report.dispositions } : {},
2013
2013
  preflight
2014
2014
  }
2015
2015
  };
@@ -2025,7 +2025,6 @@ function prepareManagedRestoration(report, currentHash, priorHash, domain, opts
2025
2025
  }
2026
2026
  const acceptanceReason = opts.acceptanceReason?.trim();
2027
2027
  const prepared = prepareManagedPromotion(report, priorHash, domain, {
2028
- dispositions: report.dispositions,
2029
2028
  ...acceptanceReason ? { acceptanceReason } : {}
2030
2029
  });
2031
2030
  if (!prepared.ok) {
@@ -7115,7 +7114,7 @@ async function mountWorkspace(eng2, ws, ledger) {
7115
7114
  throw new Error(`kernel refused restored main ${remoteMain}: ${JSON.stringify(mounted)}`);
7116
7115
  }
7117
7116
  restored = true;
7118
- if (!seeded?.seeded) {
7117
+ if (!seeded?.seeded && ledger.readOnly !== true) {
7119
7118
  void pushCheckpoint(eng2, ws, ledger).catch(() => {
7120
7119
  });
7121
7120
  }
@@ -8580,12 +8579,12 @@ async function verifyChain(eng2, ws, ledger) {
8580
8579
  function verifyChainLocal(eng2, ws) {
8581
8580
  return namedQueryProto(eng2, ws, QueryOperation.VERIFY_CHAIN);
8582
8581
  }
8583
- function historyPreflight(eng2, ws, domain, candidateHash, atHash, dispositions) {
8582
+ function historyPreflight(eng2, ws, domain, candidateHash, atHash, actor = "") {
8584
8583
  return namedQueryProto(eng2, ws, QueryOperation.HISTORY_PREFLIGHT, {
8585
8584
  domain,
8586
8585
  candidateHash,
8587
8586
  atHash,
8588
- ...dispositions ? { dispositions } : {}
8587
+ ...actor.trim() ? { ackBy: actor.trim() } : {}
8589
8588
  });
8590
8589
  }
8591
8590
  function historyRestorePreflight(eng2, ws, domain, currentHash, priorHash, atHash, actor) {
@@ -8612,7 +8611,7 @@ function promotePreflighted(eng2, ws, domain, candidateHash, opts = {}) {
8612
8611
  }
8613
8612
  let report;
8614
8613
  try {
8615
- report = historyPreflight(eng2, ws, domain, candidateHash, atHash, opts.dispositions);
8614
+ report = historyPreflight(eng2, ws, domain, candidateHash, atHash, actingPrincipal);
8616
8615
  } catch (error) {
8617
8616
  return { ok: false, stage: "preflight", error: String(error?.message || error) };
8618
8617
  }
@@ -8621,7 +8620,6 @@ function promotePreflighted(eng2, ws, domain, candidateHash, opts = {}) {
8621
8620
  const {
8622
8621
  replaces: _replaces,
8623
8622
  retireReplaced: _retireReplaced,
8624
- dispositions: _dispositions,
8625
8623
  acceptanceReason: _acceptance,
8626
8624
  ...authorOpts
8627
8625
  } = opts;
@@ -191,7 +191,7 @@ class DomainClientModelAdoptionRequired implements Exception {
191
191
  final String summary;
192
192
  final DomainModelReview review;
193
193
  final Future<void> Function()? _stage;
194
- final Future<void> Function(String reason, {Map<String, Object?>? dispositions})? _accept;
194
+ final Future<void> Function(String reason)? _accept;
195
195
 
196
196
  const DomainClientModelAdoptionRequired({
197
197
  required this.expectedDomainKey,
@@ -200,7 +200,7 @@ class DomainClientModelAdoptionRequired implements Exception {
200
200
  required this.summary,
201
201
  required this.review,
202
202
  Future<void> Function()? stage,
203
- Future<void> Function(String reason, {Map<String, Object?>? dispositions})? accept,
203
+ Future<void> Function(String reason)? accept,
204
204
  }) : _stage = stage,
205
205
  _accept = accept;
206
206
 
@@ -216,10 +216,10 @@ class DomainClientModelAdoptionRequired implements Exception {
216
216
 
217
217
  bool get canAccept => _accept != null;
218
218
 
219
- /// [dispositions] carries only sids the kernel's OWN exact-history proof
220
- /// named as truly removed (`{"retired": [sid, ...]}`) — never authored from
221
- /// memory; a wrong disposition is a silent data-model error, not a waiver.
222
- Future<void> accept(String reason, {Map<String, Object?>? dispositions}) {
219
+ /// Accept the candidate after reviewing the compiler-derived consequences.
220
+ /// Stable identities, retirements and value evolutions come from candidate
221
+ /// law; application code supplies accountability, never mechanical waivers.
222
+ Future<void> accept(String reason) {
223
223
  final value = reason.trim();
224
224
  if (value.isEmpty) {
225
225
  throw ArgumentError.value(
@@ -232,7 +232,7 @@ class DomainClientModelAdoptionRequired implements Exception {
232
232
  if (accept == null) {
233
233
  throw StateError('This generated model change cannot be accepted.');
234
234
  }
235
- return accept(value, dispositions: dispositions);
235
+ return accept(value);
236
236
  }
237
237
 
238
238
  @override
@@ -2761,13 +2761,11 @@ class NomosBridge {
2761
2761
  required String domain,
2762
2762
  required String candidateHash,
2763
2763
  String? because,
2764
- Map<String, Object?>? dispositions,
2765
2764
  }) async {
2766
2765
  final r = _asMap(await _call('adoptGeneratedModel', {
2767
2766
  'domain': domain,
2768
2767
  'candidateHash': candidateHash,
2769
2768
  if (because != null) 'because': because,
2770
- if (dispositions != null) 'dispositions': dispositions,
2771
2769
  }));
2772
2770
  return ((r['verdict'] as Map?) ?? const <String, Object?>{})
2773
2771
  .cast<String, Object?>();
@@ -4057,7 +4055,8 @@ class NomosBridge {
4057
4055
  if (!_syncEvents.isClosed) await _syncEvents.close();
4058
4056
  if (!_signalEvents.isClosed) await _signalEvents.close();
4059
4057
  if (!_custodyEvents.isClosed) await _custodyEvents.close();
4060
- if (!_localWorkRejectedEvents.isClosed) await _localWorkRejectedEvents.close();
4058
+ if (!_localWorkRejectedEvents.isClosed)
4059
+ await _localWorkRejectedEvents.close();
4061
4060
  if (!_flameEvents.isClosed) await _flameEvents.close();
4062
4061
  if (!_spanEvents.isClosed) await _spanEvents.close();
4063
4062
  if (!_transferEvents.isClosed) await _transferEvents.close();
@@ -4382,8 +4381,8 @@ class _BridgeForeignWorkspaceReader implements NomosForeignWorkspaceReader {
4382
4381
  String? nomosInternalResidencyRequiredWorkspace(Object error) {
4383
4382
  final text = error.toString();
4384
4383
  if (!text.contains('foreign-read-not-ready')) return null;
4385
- final match = RegExp(r"source workspace '([^']+)' is not resident")
4386
- .firstMatch(text);
4384
+ final match =
4385
+ RegExp(r"source workspace '([^']+)' is not resident").firstMatch(text);
4387
4386
  final workspace = match?.group(1)?.trim();
4388
4387
  return (workspace == null || workspace.isEmpty) ? null : workspace;
4389
4388
  }
package/dart/pubspec.yaml CHANGED
@@ -7,7 +7,7 @@ repository: https://github.com/Captain-App/nomos2
7
7
  description: >-
8
8
  Nomos 2 Dart client — generated domain values, intent payloads and application
9
9
  lifecycle types over the typed Nomos kernel protocol and USDA custody model.
10
- version: 0.64.28
10
+ version: 0.64.29
11
11
 
12
12
  environment:
13
13
  sdk: ^3.5.0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@githolon/dsl",
3
- "version": "0.105.13",
3
+ "version": "0.105.14",
4
4
  "type": "module",
5
5
  "sideEffects": false,
6
6
  "description": "Nomos 2 domain-authoring DSL: aggregates + directives in TS, executed and encoded to the Rust kernel's wire shapes.",
@@ -6698,7 +6698,7 @@ function prepareManagedPromotion(report, candidateHash, domain, opts = {}) {
6698
6698
  domainHash: candidateHash,
6699
6699
  ...opts.replaces ? { replaces: opts.replaces } : {},
6700
6700
  retireReplaced: opts.retireReplaced ?? false,
6701
- ...opts.dispositions ? { dispositions: opts.dispositions } : {},
6701
+ ...report.dispositions ? { dispositions: report.dispositions } : {},
6702
6702
  preflight
6703
6703
  }
6704
6704
  };
@@ -6714,7 +6714,6 @@ function prepareManagedRestoration(report, currentHash, priorHash, domain, opts
6714
6714
  }
6715
6715
  const acceptanceReason = opts.acceptanceReason?.trim();
6716
6716
  const prepared = prepareManagedPromotion(report, priorHash, domain, {
6717
- dispositions: report.dispositions,
6718
6717
  ...acceptanceReason ? { acceptanceReason } : {}
6719
6718
  });
6720
6719
  if (!prepared.ok) {
@@ -7204,7 +7203,14 @@ function createGovernanceOffers(ctx) {
7204
7203
  rows = JSON.parse(call("query", { queryBytes: qBytes2({ kind: "query", queryId: "relationTuplesByObject", paramsJson: JSON.stringify({ object }) }) })) || [];
7205
7204
  } else {
7206
7205
  const headers = authToken ? { Authorization: `Bearer ${authToken}`, "x-nomos-auth": authToken } : authHeaders;
7207
- const r = await _fetch(`${base}/v2/workspaces/${parent}/relationTuplesByObject?object=${encodeURIComponent(object)}`, { headers });
7206
+ const route = await resolveFrameworkRoute({
7207
+ parent,
7208
+ operationId: "relationTuplesByObject",
7209
+ method: "get",
7210
+ token: authToken,
7211
+ principal: p
7212
+ });
7213
+ const r = await _fetch(`${base}/v2/workspaces/${parent}${route.path}?object=${encodeURIComponent(object)}`, { headers });
7208
7214
  const text2 = await r.text();
7209
7215
  let body;
7210
7216
  try {
@@ -7258,7 +7264,14 @@ function createGovernanceOffers(ctx) {
7258
7264
  rows = JSON.parse(call("query", { queryBytes: qBytes2({ kind: "query", queryId: "relationTuplesByObject", paramsJson: JSON.stringify({ object }) }) })) || [];
7259
7265
  } else {
7260
7266
  const headers = authToken ? { Authorization: `Bearer ${authToken}`, "x-nomos-auth": authToken } : authHeaders;
7261
- const r = await _fetch(`${base}/v2/workspaces/${ws}/relationTuplesByObject?object=${encodeURIComponent(object)}`, { headers });
7267
+ const route = await resolveFrameworkRoute({
7268
+ parent: ws,
7269
+ operationId: "relationTuplesByObject",
7270
+ method: "get",
7271
+ token: authToken,
7272
+ principal: subj
7273
+ });
7274
+ const r = await _fetch(`${base}/v2/workspaces/${ws}${route.path}?object=${encodeURIComponent(object)}`, { headers });
7262
7275
  const text2 = await r.text();
7263
7276
  let body;
7264
7277
  try {
@@ -9265,7 +9278,7 @@ ${String(commit).toLowerCase()}`;
9265
9278
  domain,
9266
9279
  candidateHash,
9267
9280
  atHash,
9268
- ...opts.dispositions ? { dispositions: opts.dispositions } : {}
9281
+ ackBy: connectionPrincipal
9269
9282
  })
9270
9283
  }));
9271
9284
  });
@@ -9278,8 +9291,7 @@ ${String(commit).toLowerCase()}`;
9278
9291
  }
9279
9292
  const prepared = prepareManagedPromotion(report, candidateHash, domain, {
9280
9293
  ...currentHash && currentHash !== candidateHash ? { replaces: currentHash } : {},
9281
- ...opts.because !== void 0 ? { acceptanceReason: opts.because } : {},
9282
- ...opts.dispositions ? { dispositions: opts.dispositions } : {}
9294
+ ...opts.because !== void 0 ? { acceptanceReason: opts.because } : {}
9283
9295
  });
9284
9296
  if (!prepared.ok) return prepared;
9285
9297
  try {
@@ -13492,16 +13504,34 @@ function applicationError(message, code, cause) {
13492
13504
  if (cause !== void 0) error.cause = cause;
13493
13505
  return error;
13494
13506
  }
13495
- async function ensureGeneratedModelCurrent(application) {
13507
+ async function ensureGeneratedModelCurrent(application, approveModel) {
13496
13508
  if (application.model?.adopt === void 0) {
13497
13509
  throw applicationError(
13498
13510
  "This generated application has no model lifecycle surface and cannot prove its plans are current.",
13499
13511
  "nomos.application.model_lifecycle_missing"
13500
13512
  );
13501
13513
  }
13502
- const adoption = await application.model.adopt();
13514
+ let adoption = await application.model.adopt();
13515
+ if (adoption.status === "needs-staging" && adoption.stage !== void 0) {
13516
+ adoption = await adoption.stage();
13517
+ }
13503
13518
  if (adoption.status === "adopted") return;
13504
13519
  if (adoption.status === "needs-decision") {
13520
+ if (approveModel !== void 0 && adoption.accept !== void 0) {
13521
+ const approval = await approveModel({
13522
+ summary: adoption.summary,
13523
+ review: adoption.review
13524
+ });
13525
+ const because = approval.because.trim();
13526
+ if (!because) {
13527
+ throw applicationError(
13528
+ "Model approval must state the business reason for the change.",
13529
+ "nomos.application.model_decision_reason_missing"
13530
+ );
13531
+ }
13532
+ adoption = await adoption.accept(because);
13533
+ if (adoption.status === "adopted") return;
13534
+ }
13505
13535
  throw applicationError(
13506
13536
  "This application model changes prior business outcomes and needs an attributed migration decision before the app can write.",
13507
13537
  "nomos.application.model_decision_required",
@@ -13624,7 +13654,7 @@ async function nomosInternalOpenGeneratedApplication(options, bind, internals =
13624
13654
  ...environment?.NOMOS_TEST_K_ROOT_PUBLIC ? { __testKRootPublic: environment.NOMOS_TEST_K_ROOT_PUBLIC } : {}
13625
13655
  });
13626
13656
  const applicationObject = measureOpenSync("bind generated business API", () => bind(holon2, user));
13627
- await ensureGeneratedModelCurrent(applicationObject);
13657
+ await ensureGeneratedModelCurrent(applicationObject, options.approveModel);
13628
13658
  await measureOpen("prepare generated entry commands", () => prepareGeneratedEntryCommands(holon2, options.readyCommands, options.onPerformance));
13629
13659
  const stop = holon2.autoSync({
13630
13660
  onResult: (result) => {
@@ -13871,7 +13901,6 @@ async function nomosInternalOpenGeneratedApplication(options, bind, internals =
13871
13901
  parent: application,
13872
13902
  principal,
13873
13903
  subject,
13874
- lawHash: options.lawHash,
13875
13904
  device: { secret, public: publicKey },
13876
13905
  authToken,
13877
13906
  idToken: authToken,
@@ -13882,9 +13911,13 @@ async function nomosInternalOpenGeneratedApplication(options, bind, internals =
13882
13911
  const verdict = birth.verify;
13883
13912
  if (verdict !== void 0 && verdict.valid !== true) throw new Error(`the born application failed replay verification: ${String(verdict.error ?? "unknown")}`);
13884
13913
  } else if (!hadDurableSecret) {
13885
- await measureOpen("enrol application device with its parent", () => authorityHolon.ensureParentSignerEnrollment({
13886
- parent: application,
13887
- principal,
13914
+ await measureOpen("enrol application device in its private custody", () => authorityHolon.ensureParentSignerEnrollment({
13915
+ // The write gate that this device will use is the receiving-group workspace itself.
13916
+ // The application root authorises/births that home; it is not a substitute signer index.
13917
+ parent: receivingGroup,
13918
+ // The author gate speaks canonical Nomos principals. A provider subject is deliberately
13919
+ // opaque and may contain its own namespace separators; never enrol that raw value as the actor.
13920
+ principal: subject,
13888
13921
  publicKey,
13889
13922
  authorSecret: secret,
13890
13923
  authToken,
@@ -13904,8 +13937,8 @@ async function nomosInternalOpenGeneratedApplication(options, bind, internals =
13904
13937
  if (verified.valid !== true) throw new Error(`the private authority context failed replay verification: ${String(verified.error ?? "unknown")}`);
13905
13938
  await measureOpen("save application bootstrap progress", () => store.saveText(journalState, "verified"));
13906
13939
  try {
13907
- await measureOpen("confirm parent signer enrolment", () => authorityHolon.ensureParentSignerEnrollment({
13908
- parent: application,
13940
+ await measureOpen("confirm private-custody signer enrolment", () => authorityHolon.ensureParentSignerEnrollment({
13941
+ parent: receivingGroup,
13909
13942
  principal: subject,
13910
13943
  authorSecret: secret,
13911
13944
  authToken,
@@ -13930,7 +13963,7 @@ async function nomosInternalOpenGeneratedApplication(options, bind, internals =
13930
13963
  } catch {
13931
13964
  }
13932
13965
  const applicationObject = measureOpenSync("bind generated business API", () => bind(holon, user));
13933
- await measureOpen("confirm generated model is current", () => ensureGeneratedModelCurrent(applicationObject));
13966
+ await measureOpen("confirm generated model is current", () => ensureGeneratedModelCurrent(applicationObject, options.approveModel));
13934
13967
  await measureOpen("prepare generated entry commands", () => prepareGeneratedEntryCommands(holon, options.readyCommands, options.onPerformance));
13935
13968
  phase = "current";
13936
13969
  autoSync = holon.autoSync({
@@ -27,6 +27,7 @@ import { createHash } from "node:crypto";
27
27
  import { z } from "zod";
28
28
 
29
29
  import type { AggregateHandle } from "./aggregate.js";
30
+ import type { Field } from "./fields.js";
30
31
  import type { Directive } from "./directive.js";
31
32
  import type { DomainModule, PermissionVocabulary, DartImport } from "./codegen_dart.js";
32
33
  import type { AnyCount } from "./count.js";
@@ -83,6 +84,11 @@ import {
83
84
  applyConflictPolicyResolvers,
84
85
  conflictPoliciesOf,
85
86
  } from "./conflicts.js";
87
+ import {
88
+ applyFieldEvolutionsToAggregates,
89
+ fieldEvolutionsOf,
90
+ retirementsOf,
91
+ } from "./evolution.js";
86
92
  import {
87
93
  expandBusinessBirthExports,
88
94
  isBusinessBirthDirective,
@@ -200,6 +206,8 @@ export interface DomainModuleSpec {
200
206
  export function composeDomainModule(spec: DomainModuleSpec): DomainModule {
201
207
  const domain = spec.domain ?? spec.name;
202
208
  const conflictPolicies = conflictPoliciesOf(spec.modules, domain);
209
+ const fieldEvolutions = fieldEvolutionsOf(spec.modules);
210
+ const retirements = retirementsOf(spec.modules, domain);
203
211
  const applicationLanguage = applicationLanguageOf(spec.modules, domain);
204
212
  let merged: Mod = {};
205
213
  for (const m of spec.modules) merged = { ...merged, ...m };
@@ -520,11 +528,34 @@ export function composeDomainModule(spec: DomainModuleSpec): DomainModule {
520
528
  directives.push(dir);
521
529
  autoDeclaredEnsures.push(ty);
522
530
  }
523
- const aggregates = applyConflictPoliciesToAggregates(
524
- [...byId.values()],
525
- conflictPolicies,
531
+ const aggregates = applyFieldEvolutionsToAggregates(
532
+ applyConflictPoliciesToAggregates(
533
+ [...byId.values()],
534
+ conflictPolicies,
535
+ domain,
536
+ ),
537
+ fieldEvolutions,
526
538
  domain,
527
539
  );
540
+ const currentAggregates = new Map(aggregates.map((aggregate) => [aggregate.id, aggregate]));
541
+ for (const retired of retirements) {
542
+ const current = currentAggregates.get(retired.aggregate);
543
+ if (retired.kind === "aggregate" && current !== undefined) {
544
+ throw new Error(
545
+ `domain '${domain}' declares '${retired.aggregate}' both current and retired`,
546
+ );
547
+ }
548
+ if (
549
+ retired.kind === "field" &&
550
+ current !== undefined &&
551
+ retired.field !== undefined &&
552
+ (current.fields as Record<string, Field>)[retired.field] !== undefined
553
+ ) {
554
+ throw new Error(
555
+ `domain '${domain}' declares '${retired.aggregate}.${retired.field}' both current and retired`,
556
+ );
557
+ }
558
+ }
528
559
  const directiveIds = new Set(directives.map((d) => d.id));
529
560
  for (const task of declaredCapabilities) {
530
561
  for (const directive of [task.obligation, task.complete, task.fail, task.block, task.deadLetter]) {
@@ -855,6 +886,9 @@ export function composeDomainModule(spec: DomainModuleSpec): DomainModule {
855
886
  // hash-bearing manifest; payload-field tracing also drives generated typed references.
856
887
  // Omit-when-empty because ordinary domains need no resolver metadata.
857
888
  ...(surfacedResolvers.length > 0 ? { surfacedResolvers } : {}),
889
+ // Explicit authored tombstones are law. The compiler identity registry may remember
890
+ // every disappearance, but only these declarations permit an activation to retire it.
891
+ ...(retirements.length > 0 ? { retirements } : {}),
858
892
  ...(queries.length > 0 ? { queries } : {}),
859
893
  ...(counts.length > 0 ? { counts } : {}),
860
894
  ...(spatials.length > 0 ? { spatials } : {}),
@@ -17,6 +17,8 @@ import { pathToFileURL } from "node:url";
17
17
  import { composeDomainModule, type Mod } from "./build_package.js";
18
18
  import type { DomainModule } from "./codegen_dart.js";
19
19
  import { bindEnumTypeExports } from "./enum_type.js";
20
+ import { discoverEvolutionModulePaths, isEvolutionModulePath } from "./evolution_files.js";
21
+ import { assertEvolutionModuleExports } from "./evolution.js";
20
22
  import { checkDomainModules, formatFindings, hasRefusal, type Finding } from "./law_check.js";
21
23
  import { LawSourceMap } from "./law_source_map.js";
22
24
  import {
@@ -105,9 +107,18 @@ export async function loadComposedModulesWithSources(
105
107
  const files: string[] = [];
106
108
  for (const rel of d.modules) {
107
109
  const abs = path.resolve(cfgDir, rel);
110
+ if (isEvolutionModulePath(abs)) {
111
+ fail(
112
+ `domain '${d.key}': ${rel} is an evolution concern; move it under ` +
113
+ `evolutions/${d.key}/ and remove it from feature modules so it cannot enter QuickJS`,
114
+ );
115
+ }
108
116
  if (!existsSync(abs)) fail(`domain '${d.key}': module not found: ${abs}`);
109
117
  files.push(abs);
110
118
  }
119
+ for (const abs of discoverEvolutionModulePaths(cfgDir, d.key)) {
120
+ if (!files.includes(abs)) files.push(abs);
121
+ }
111
122
  try {
112
123
  assertPublicPackageImports(files);
113
124
  } catch (error) {
@@ -116,6 +127,7 @@ export async function loadComposedModulesWithSources(
116
127
  for (const abs of files) {
117
128
  // Cache-bust so a re-import (the LSP re-check after an edit) re-evaluates the edited module.
118
129
  const mod = (await import(`${pathToFileURL(abs).href}?t=${Date.now()}`)) as Mod;
130
+ if (isEvolutionModulePath(abs)) assertEvolutionModuleExports(mod, abs);
119
131
  bindEnumTypeExports(mod, abs);
120
132
  mods.push(mod);
121
133
  }
@@ -201,7 +213,6 @@ async function main(): Promise<void> {
201
213
  const findings: Finding[] = [...dependencyFindings, ...checkDomainModules(modules)];
202
214
 
203
215
  // ── EVOLVE PREVIEW (--against): diff the current build against a prior law + pre-fire the gate. ──
204
- let evolveDispositions: Record<string, unknown> | undefined;
205
216
  if (againstIdx >= 0 || againstArg !== undefined) {
206
217
  if (againstArg === undefined || againstArg.startsWith("--")) {
207
218
  fail(`--against needs a path: --against <${NOMOS_IDENTITY_FILENAME} | package dir>`);
@@ -218,7 +229,6 @@ async function main(): Promise<void> {
218
229
  const current = currentEvolveLaw(modules, lock);
219
230
  const diff = evolveDiff(prior.law, current);
220
231
  findings.push(...diff.findings);
221
- if (Object.keys(diff.dispositions).length > 0) evolveDispositions = diff.dispositions;
222
232
  if (!asJson) console.log(`githolon check: evolve preview against ${prior.source}`);
223
233
  }
224
234
 
@@ -255,14 +265,6 @@ async function main(): Promise<void> {
255
265
  }
256
266
  if (findings.length === 0) console.log(" all clear — no static twin flagged anything");
257
267
 
258
- // The consolidated, pasteable dispositions the destructive evolves demand (per domain).
259
- if (evolveDispositions !== undefined) {
260
- console.log(
261
- "\ngitholon check: to admit the destructive evolve, include `dispositions` in the installation offer:",
262
- );
263
- console.log(JSON.stringify(evolveDispositions, null, 2));
264
- }
265
-
266
268
  process.exit(hasRefusal(findings) ? 1 : 0);
267
269
  }
268
270
 
@@ -2742,14 +2742,14 @@ export function generateDartActivityRegistry(entries: readonly PackageActivityEn
2742
2742
  ` final String summary;`,
2743
2743
  ` final NomosModelReview review;`,
2744
2744
  ` final Future<NomosModelAdoption> Function()? _stage;`,
2745
- ` final Future<NomosModelAdoption> Function(String, {Map<String, Object?>? dispositions})? _accept;`,
2745
+ ` final Future<NomosModelAdoption> Function(String)? _accept;`,
2746
2746
  ` const NomosModelAdoption.adopted({required this.summary, required this.review})`,
2747
2747
  ` : status = NomosModelAdoptionStatus.adopted, _stage = null, _accept = null;`,
2748
2748
  ` const NomosModelAdoption.blocked({required this.summary, required this.review})`,
2749
2749
  ` : status = NomosModelAdoptionStatus.blocked, _stage = null, _accept = null;`,
2750
2750
  ` NomosModelAdoption.needsStaging({required this.summary, required this.review, required Future<NomosModelAdoption> Function() stage})`,
2751
2751
  ` : status = NomosModelAdoptionStatus.needsStaging, _stage = stage, _accept = null;`,
2752
- ` NomosModelAdoption.needsDecision({required this.summary, required this.review, required Future<NomosModelAdoption> Function(String, {Map<String, Object?>? dispositions}) accept})`,
2752
+ ` NomosModelAdoption.needsDecision({required this.summary, required this.review, required Future<NomosModelAdoption> Function(String) accept})`,
2753
2753
  ` : status = NomosModelAdoptionStatus.needsDecision, _stage = null, _accept = accept;`,
2754
2754
  ` Future<NomosModelAdoption> stage() {`,
2755
2755
  ` final stageCandidate = _stage;`,
@@ -2758,14 +2758,14 @@ export function generateDartActivityRegistry(entries: readonly PackageActivityEn
2758
2758
  ` }`,
2759
2759
  ` return stageCandidate();`,
2760
2760
  ` }`,
2761
- ` Future<NomosModelAdoption> accept(String because, {Map<String, Object?>? dispositions}) {`,
2761
+ ` Future<NomosModelAdoption> accept(String because) {`,
2762
2762
  ` final reason = because.trim();`,
2763
2763
  ` final acceptDecision = _accept;`,
2764
2764
  ` if (status != NomosModelAdoptionStatus.needsDecision || acceptDecision == null) {`,
2765
2765
  ` throw StateError('This model change is not waiting for a business decision.');`,
2766
2766
  ` }`,
2767
2767
  ` if (reason.isEmpty) throw ArgumentError('accept(reason) needs the business reason for changing prior outcomes');`,
2768
- ` return acceptDecision(reason, dispositions: dispositions);`,
2768
+ ` return acceptDecision(reason);`,
2769
2769
  ` }`,
2770
2770
  `}`,
2771
2771
  ``,
@@ -4788,6 +4788,9 @@ export interface DomainModule {
4788
4788
  * Absent when the domain declares no Surfaced field (byte-identical — the era rule).
4789
4789
  */
4790
4790
  surfacedResolvers?: import("./surfaced_lint.js").SurfacedResolverBinding[];
4791
+ /** Explicit tombstones authored in separate evolution files. These compile into
4792
+ * candidate USDA and are never exposed through generated tenant clients. */
4793
+ retirements?: import("./evolution.js").Retirement[];
4791
4794
  /**
4792
4795
  * STABLE-ID CONTINUITY (#58 — names are labels, identity is minted). When present
4793
4796
  * (attached by `nomos-compile` from the PRIOR build's identity manifests), the
@@ -1673,11 +1673,11 @@ export function generateDartBridgeClient(
1673
1673
  ` sameOutcome: projection?['equal'] as bool?, differences: differences);`,
1674
1674
  ` }`,
1675
1675
  ``,
1676
- ` Future<NomosModelAdoption> _adoptGeneratedModel([String? because, Map<String, Object?>? dispositions]) async {`,
1676
+ ` Future<NomosModelAdoption> _adoptGeneratedModel([String? because]) async {`,
1677
1677
  ` final raw = await bridge.nomosFrameworkAdoptGeneratedModel(`,
1678
1678
  ` domain: ${dartStr(
1679
1679
  domain
1680
- )}, candidateHash: domainHash, because: because, dispositions: dispositions);`,
1680
+ )}, candidateHash: domainHash, because: because);`,
1681
1681
  ` final reportRaw = raw['report'];`,
1682
1682
  ` final report = reportRaw is Map ? reportRaw.cast<String, Object?>() : null;`,
1683
1683
  ` final review = _nomosModelReview(report);`,
@@ -1703,20 +1703,9 @@ export function generateDartBridgeClient(
1703
1703
  ` if (stage == 'acceptance') {`,
1704
1704
  ` return NomosModelAdoption.needsDecision(`,
1705
1705
  ` summary: 'This model would change prior business outcomes. Review the differences and record why that is intended.',`,
1706
- ` review: review, accept: (reason, {dispositions}) => _adoptGeneratedModel(reason, dispositions));`,
1706
+ ` review: review, accept: (reason) => _adoptGeneratedModel(reason));`,
1707
1707
  ` }`,
1708
1708
  ` final evolve = (report?['evolve'] as Map?)?.cast<String, Object?>();`,
1709
- ` // A field/aggregate the candidate genuinely removed is a #58 disposition decision, not a`,
1710
- ` // dead end: retry with the SAME accept/dispositions seam so the tenant's own policy can`,
1711
- ` // supply dispositions.retired. Every prior stage (promoted/staging-required/acceptance) is`,
1712
- ` // already handled above; 'identity' (needs sign-in) is the one remaining stage this must NOT`,
1713
- ` // paper over, so it is the sole exclusion rather than requiring an exact 'preflight' match or`,
1714
- ` // exact boolean report fields, which may not round-trip identically across every bridge.`,
1715
- ` if (stage != 'identity') {`,
1716
- ` return NomosModelAdoption.needsDecision(`,
1717
- ` summary: 'This model cannot yet interpret every existing value without a declared disposition. Review the differences and record why that is intended.',`,
1718
- ` review: review, accept: (reason, {dispositions}) => _adoptGeneratedModel(reason, dispositions));`,
1719
- ` }`,
1720
1709
  ` final rawError = raw['error']?.toString().trim();`,
1721
1710
  ` final summary = rawError != null && rawError.isNotEmpty`,
1722
1711
  ` ? rawError`,
@@ -2436,8 +2425,8 @@ export function generateDartBridgeClient(
2436
2425
  ` : null,`,
2437
2426
  ]),
2438
2427
  ` accept: adoption.status == NomosModelAdoptionStatus.needsDecision`,
2439
- ` ? (reason, {dispositions}) async {`,
2440
- ` final accepted = await adoption.accept(reason, dispositions: dispositions);`,
2428
+ ` ? (reason) async {`,
2429
+ ` final accepted = await adoption.accept(reason);`,
2441
2430
  ` // A retry that still isn't adopted (a transient business-outcome/history race,`,
2442
2431
  ` // e.g. concurrent writes landing between the bare preflight and this accept) must`,
2443
2432
  ` // NOT be swallowed as a terminal StateError: re-throw the SAME typed exception so`,