@origintrail-official/dkg-agent 10.0.21 → 10.0.23

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.
Files changed (207) hide show
  1. package/dist/approved-private-replica-proof-runs.d.ts +40 -0
  2. package/dist/approved-private-replica-proof-runs.d.ts.map +1 -0
  3. package/dist/approved-private-replica-proof-runs.js +49 -0
  4. package/dist/approved-private-replica-proof-runs.js.map +1 -0
  5. package/dist/approved-private-replica.d.ts +18 -1
  6. package/dist/approved-private-replica.d.ts.map +1 -1
  7. package/dist/approved-private-replica.js +9 -3
  8. package/dist/approved-private-replica.js.map +1 -1
  9. package/dist/context-graph-chain-discovery-binding.d.ts +2 -0
  10. package/dist/context-graph-chain-discovery-binding.d.ts.map +1 -1
  11. package/dist/context-graph-chain-discovery-binding.js +7 -0
  12. package/dist/context-graph-chain-discovery-binding.js.map +1 -1
  13. package/dist/context-graph-dormant-identity.d.ts +8 -0
  14. package/dist/context-graph-dormant-identity.d.ts.map +1 -0
  15. package/dist/context-graph-dormant-identity.js +48 -0
  16. package/dist/context-graph-dormant-identity.js.map +1 -0
  17. package/dist/context-graph-meta-projection.d.ts +19 -55
  18. package/dist/context-graph-meta-projection.d.ts.map +1 -1
  19. package/dist/context-graph-meta-projection.js +94 -46
  20. package/dist/context-graph-meta-projection.js.map +1 -1
  21. package/dist/context-graph-name-binding-target.d.ts +21 -0
  22. package/dist/context-graph-name-binding-target.d.ts.map +1 -0
  23. package/dist/context-graph-name-binding-target.js +68 -0
  24. package/dist/context-graph-name-binding-target.js.map +1 -0
  25. package/dist/context-graph-persisted-name-aliases.d.ts +47 -0
  26. package/dist/context-graph-persisted-name-aliases.d.ts.map +1 -0
  27. package/dist/context-graph-persisted-name-aliases.js +173 -0
  28. package/dist/context-graph-persisted-name-aliases.js.map +1 -0
  29. package/dist/context-graph-read-authority.d.ts +26 -11
  30. package/dist/context-graph-read-authority.d.ts.map +1 -1
  31. package/dist/context-graph-read-authority.js +26 -2
  32. package/dist/context-graph-read-authority.js.map +1 -1
  33. package/dist/context-graph-subscription-authority-recovery.d.ts +4 -0
  34. package/dist/context-graph-subscription-authority-recovery.d.ts.map +1 -1
  35. package/dist/context-graph-subscription-authority-recovery.js +8 -2
  36. package/dist/context-graph-subscription-authority-recovery.js.map +1 -1
  37. package/dist/context-graph-subscription-policy.d.ts +34 -1
  38. package/dist/context-graph-subscription-policy.d.ts.map +1 -1
  39. package/dist/context-graph-subscription-policy.js +143 -1
  40. package/dist/context-graph-subscription-policy.js.map +1 -1
  41. package/dist/curator-meta-refresh.d.ts.map +1 -1
  42. package/dist/curator-meta-refresh.js +31 -9
  43. package/dist/curator-meta-refresh.js.map +1 -1
  44. package/dist/dkg-agent-base.d.ts.map +1 -1
  45. package/dist/dkg-agent-base.js +0 -1
  46. package/dist/dkg-agent-base.js.map +1 -1
  47. package/dist/dkg-agent-cg-name-resolution.d.ts +6 -24
  48. package/dist/dkg-agent-cg-name-resolution.d.ts.map +1 -1
  49. package/dist/dkg-agent-cg-name-resolution.js +42 -58
  50. package/dist/dkg-agent-cg-name-resolution.js.map +1 -1
  51. package/dist/dkg-agent-cg-on-chain-id.d.ts +3 -3
  52. package/dist/dkg-agent-cg-on-chain-id.d.ts.map +1 -1
  53. package/dist/dkg-agent-cg-on-chain-id.js +7 -23
  54. package/dist/dkg-agent-cg-on-chain-id.js.map +1 -1
  55. package/dist/dkg-agent-cg-registry.d.ts +7 -3
  56. package/dist/dkg-agent-cg-registry.d.ts.map +1 -1
  57. package/dist/dkg-agent-cg-registry.js +30 -16
  58. package/dist/dkg-agent-cg-registry.js.map +1 -1
  59. package/dist/dkg-agent-cg-resolve.d.ts +10 -6
  60. package/dist/dkg-agent-cg-resolve.d.ts.map +1 -1
  61. package/dist/dkg-agent-cg-resolve.js +16 -19
  62. package/dist/dkg-agent-cg-resolve.js.map +1 -1
  63. package/dist/dkg-agent-context-graph.d.ts.map +1 -1
  64. package/dist/dkg-agent-context-graph.js +0 -4
  65. package/dist/dkg-agent-context-graph.js.map +1 -1
  66. package/dist/dkg-agent-crypto.d.ts.map +1 -1
  67. package/dist/dkg-agent-crypto.js +53 -57
  68. package/dist/dkg-agent-crypto.js.map +1 -1
  69. package/dist/dkg-agent-lifecycle.d.ts +5 -0
  70. package/dist/dkg-agent-lifecycle.d.ts.map +1 -1
  71. package/dist/dkg-agent-lifecycle.js +156 -133
  72. package/dist/dkg-agent-lifecycle.js.map +1 -1
  73. package/dist/dkg-agent-publish.d.ts.map +1 -1
  74. package/dist/dkg-agent-publish.js +2 -3
  75. package/dist/dkg-agent-publish.js.map +1 -1
  76. package/dist/dkg-agent-query.d.ts.map +1 -1
  77. package/dist/dkg-agent-query.js +12 -9
  78. package/dist/dkg-agent-query.js.map +1 -1
  79. package/dist/dkg-agent-registered-private-empty-vm.d.ts +8 -0
  80. package/dist/dkg-agent-registered-private-empty-vm.d.ts.map +1 -1
  81. package/dist/dkg-agent-registered-private-empty-vm.js +15 -0
  82. package/dist/dkg-agent-registered-private-empty-vm.js.map +1 -1
  83. package/dist/dkg-agent-rfc64-catalog-auto-publish.d.ts.map +1 -1
  84. package/dist/dkg-agent-rfc64-catalog-auto-publish.js +4 -0
  85. package/dist/dkg-agent-rfc64-catalog-auto-publish.js.map +1 -1
  86. package/dist/dkg-agent-rfc64-catalog-upsert.d.ts.map +1 -1
  87. package/dist/dkg-agent-rfc64-catalog-upsert.js +7 -0
  88. package/dist/dkg-agent-rfc64-catalog-upsert.js.map +1 -1
  89. package/dist/dkg-agent-rfc64-catalog.d.ts +4 -0
  90. package/dist/dkg-agent-rfc64-catalog.d.ts.map +1 -1
  91. package/dist/dkg-agent-rfc64-catalog.js +12 -2
  92. package/dist/dkg-agent-rfc64-catalog.js.map +1 -1
  93. package/dist/dkg-agent-rfc64-swm-catalog-projection-supervisor.d.ts +2 -1
  94. package/dist/dkg-agent-rfc64-swm-catalog-projection-supervisor.d.ts.map +1 -1
  95. package/dist/dkg-agent-rfc64-swm-catalog-projection-supervisor.js +23 -15
  96. package/dist/dkg-agent-rfc64-swm-catalog-projection-supervisor.js.map +1 -1
  97. package/dist/dkg-agent-rfc64-swm-catalog-projection.d.ts.map +1 -1
  98. package/dist/dkg-agent-rfc64-swm-catalog-projection.js +3 -0
  99. package/dist/dkg-agent-rfc64-swm-catalog-projection.js.map +1 -1
  100. package/dist/dkg-agent-swm-host.d.ts +13 -22
  101. package/dist/dkg-agent-swm-host.d.ts.map +1 -1
  102. package/dist/dkg-agent-swm-host.js +179 -81
  103. package/dist/dkg-agent-swm-host.js.map +1 -1
  104. package/dist/dkg-agent-swm-substrate.d.ts +2 -0
  105. package/dist/dkg-agent-swm-substrate.d.ts.map +1 -1
  106. package/dist/dkg-agent-swm-substrate.js +5 -9
  107. package/dist/dkg-agent-swm-substrate.js.map +1 -1
  108. package/dist/dkg-agent-types.d.ts +3 -2
  109. package/dist/dkg-agent-types.d.ts.map +1 -1
  110. package/dist/dkg-agent.d.ts +4 -10
  111. package/dist/dkg-agent.d.ts.map +1 -1
  112. package/dist/dkg-agent.js +43 -58
  113. package/dist/dkg-agent.js.map +1 -1
  114. package/dist/index.d.ts +1 -1
  115. package/dist/index.d.ts.map +1 -1
  116. package/dist/index.js.map +1 -1
  117. package/dist/internal/context-graph-authority/context-graph-agent-gate-authority.d.ts +6 -1
  118. package/dist/internal/context-graph-authority/context-graph-agent-gate-authority.d.ts.map +1 -1
  119. package/dist/internal/context-graph-authority/context-graph-agent-gate-authority.js +5 -5
  120. package/dist/internal/context-graph-authority/context-graph-agent-gate-authority.js.map +1 -1
  121. package/dist/internal/context-graph-authority/context-graph-authority.d.ts +14 -1
  122. package/dist/internal/context-graph-authority/context-graph-authority.d.ts.map +1 -1
  123. package/dist/internal/context-graph-authority/context-graph-authority.js +19 -0
  124. package/dist/internal/context-graph-authority/context-graph-authority.js.map +1 -1
  125. package/dist/internal/context-graph-cache-invalidating-store.d.ts +10 -2
  126. package/dist/internal/context-graph-cache-invalidating-store.d.ts.map +1 -1
  127. package/dist/internal/context-graph-cache-invalidating-store.js +67 -67
  128. package/dist/internal/context-graph-cache-invalidating-store.js.map +1 -1
  129. package/dist/internal/context-graph-meta-record-copy.d.ts +1 -1
  130. package/dist/internal/context-graph-meta-record-copy.d.ts.map +1 -1
  131. package/dist/internal/context-graph-meta-record.d.ts +40 -0
  132. package/dist/internal/context-graph-meta-record.d.ts.map +1 -0
  133. package/dist/internal/context-graph-meta-record.js +3 -0
  134. package/dist/internal/context-graph-meta-record.js.map +1 -0
  135. package/dist/internal/peer-gate-revision.d.ts +28 -0
  136. package/dist/internal/peer-gate-revision.d.ts.map +1 -0
  137. package/dist/internal/peer-gate-revision.js +46 -0
  138. package/dist/internal/peer-gate-revision.js.map +1 -0
  139. package/dist/internal/projection-mutation-observer.d.ts +5 -0
  140. package/dist/internal/projection-mutation-observer.d.ts.map +1 -0
  141. package/dist/internal/projection-mutation-observer.js +24 -0
  142. package/dist/internal/projection-mutation-observer.js.map +1 -0
  143. package/dist/internal/promote/assertion-promote-precommit.d.ts +8 -6
  144. package/dist/internal/promote/assertion-promote-precommit.d.ts.map +1 -1
  145. package/dist/internal/promote/assertion-promote-precommit.js +8 -6
  146. package/dist/internal/promote/assertion-promote-precommit.js.map +1 -1
  147. package/dist/internal/recipient-key-route-fence.d.ts +57 -0
  148. package/dist/internal/recipient-key-route-fence.d.ts.map +1 -0
  149. package/dist/internal/recipient-key-route-fence.js +169 -0
  150. package/dist/internal/recipient-key-route-fence.js.map +1 -0
  151. package/dist/internal/store-mutation.d.ts +28 -0
  152. package/dist/internal/store-mutation.d.ts.map +1 -0
  153. package/dist/internal/store-mutation.js +3 -0
  154. package/dist/internal/store-mutation.js.map +1 -0
  155. package/dist/main-thread-time-slice.d.ts +19 -0
  156. package/dist/main-thread-time-slice.d.ts.map +1 -0
  157. package/dist/main-thread-time-slice.js +32 -0
  158. package/dist/main-thread-time-slice.js.map +1 -0
  159. package/dist/p2p/storage-ack-registrar.d.ts.map +1 -1
  160. package/dist/p2p/storage-ack-registrar.js +3 -5
  161. package/dist/p2p/storage-ack-registrar.js.map +1 -1
  162. package/dist/registered-context-graph-authority.d.ts +17 -0
  163. package/dist/registered-context-graph-authority.d.ts.map +1 -1
  164. package/dist/registered-private-empty-vm-attempt-v1.d.ts.map +1 -1
  165. package/dist/registered-private-empty-vm-attempt-v1.js +8 -4
  166. package/dist/registered-private-empty-vm-attempt-v1.js.map +1 -1
  167. package/dist/rfc64/catalog-repair-retry-v1.d.ts +9 -3
  168. package/dist/rfc64/catalog-repair-retry-v1.d.ts.map +1 -1
  169. package/dist/rfc64/catalog-repair-retry-v1.js +22 -6
  170. package/dist/rfc64/catalog-repair-retry-v1.js.map +1 -1
  171. package/dist/rfc64/control-envelope-signer-v1.d.ts +8 -0
  172. package/dist/rfc64/control-envelope-signer-v1.d.ts.map +1 -1
  173. package/dist/rfc64/control-envelope-signer-v1.js +20 -0
  174. package/dist/rfc64/control-envelope-signer-v1.js.map +1 -1
  175. package/dist/rfc64/legacy-swm-boundary-fence-v1.d.ts +109 -0
  176. package/dist/rfc64/legacy-swm-boundary-fence-v1.d.ts.map +1 -0
  177. package/dist/rfc64/legacy-swm-boundary-fence-v1.js +236 -0
  178. package/dist/rfc64/legacy-swm-boundary-fence-v1.js.map +1 -0
  179. package/dist/rfc64/legacy-swm-boundary-v1.d.ts +28 -1
  180. package/dist/rfc64/legacy-swm-boundary-v1.d.ts.map +1 -1
  181. package/dist/rfc64/legacy-swm-boundary-v1.js +74 -145
  182. package/dist/rfc64/legacy-swm-boundary-v1.js.map +1 -1
  183. package/dist/rfc64/public-catalog-successor-producer-v1.d.ts +2 -0
  184. package/dist/rfc64/public-catalog-successor-producer-v1.d.ts.map +1 -1
  185. package/dist/rfc64/public-catalog-successor-producer-v1.js +77 -33
  186. package/dist/rfc64/public-catalog-successor-producer-v1.js.map +1 -1
  187. package/dist/rfc64/public-catalog-successor-row-binding-v1.d.ts +16 -0
  188. package/dist/rfc64/public-catalog-successor-row-binding-v1.d.ts.map +1 -0
  189. package/dist/rfc64/public-catalog-successor-row-binding-v1.js +28 -0
  190. package/dist/rfc64/public-catalog-successor-row-binding-v1.js.map +1 -0
  191. package/dist/rfc64/swm-inventory-catalog-reconciler-v1.d.ts +6 -0
  192. package/dist/rfc64/swm-inventory-catalog-reconciler-v1.d.ts.map +1 -1
  193. package/dist/rfc64/swm-inventory-catalog-reconciler-v1.js +9 -3
  194. package/dist/rfc64/swm-inventory-catalog-reconciler-v1.js.map +1 -1
  195. package/dist/rfc64/swm-inventory-settlement-outlook-v1.d.ts +8 -0
  196. package/dist/rfc64/swm-inventory-settlement-outlook-v1.d.ts.map +1 -0
  197. package/dist/rfc64/swm-inventory-settlement-outlook-v1.js +32 -0
  198. package/dist/rfc64/swm-inventory-settlement-outlook-v1.js.map +1 -0
  199. package/dist/sync/requester/swm-recovery-apply.d.ts +1 -1
  200. package/dist/sync/requester/swm-recovery-apply.d.ts.map +1 -1
  201. package/dist/sync/requester/swm-recovery-apply.js +0 -1
  202. package/dist/sync/requester/swm-recovery-apply.js.map +1 -1
  203. package/dist/sync/requester/swm-target-executor.d.ts +2 -2
  204. package/dist/sync/requester/swm-target-executor.d.ts.map +1 -1
  205. package/dist/sync/requester/swm-target-executor.js +1 -2
  206. package/dist/sync/requester/swm-target-executor.js.map +1 -1
  207. package/package.json +11 -8
@@ -188,11 +188,12 @@ import { chainAuthorityReadBudgetsOf } from './chain-authority-read-budgets.js';
188
188
  import { peekFinalizedAuthorityColdResolution } from './finalized-authority-cold-resolution.js';
189
189
  import { AGENTS_PHONEBOOK_PRIME_MAX_DIALS, peekOnDemandAgentsPhonebook, } from './sync/on-demand-agents-phonebook.js';
190
190
  import { raceWithBootTimeout, isTransientBootChainError } from './dkg-agent-boot.js';
191
- import { normalizeContextGraphSubscriptionTransition, projectContextGraphSubscriptionPersistence, } from './context-graph-subscription-policy.js';
191
+ import { mayAdoptContextGraphWireSubscription, normalizeContextGraphSubscriptionTransition, projectContextGraphSubscriptionPersistence, projectContextGraphSubscriptionPersistenceWithSavedHosting, readPreservedContextGraphCoreHosting, isContextGraphSubscriptionPersistenceTargetCurrent, projectContextGraphJoinSubscriptionRecord, contextGraphSubscriptionClearTargets, retainsInactiveContextGraphBinding, } from './context-graph-subscription-policy.js';
192
192
  import { partitionSupersededContextGraphNamePlaceholders } from './dkg-agent-cg-name-resolution.js';
193
193
  import { authoritativeSyncPeerId, resolveBoundedCuratorSyncPeer, resolveCuratorSyncPeer, } from './dkg-agent-cg-resolve.js';
194
194
  import { runCuratorMetaRefreshFromPeer } from './curator-meta-refresh.js';
195
195
  import { isCanonicalAuthoritativeContextGraphId } from './context-graph-binding-state.js';
196
+ import { projectDormantContextGraphIdentities } from './context-graph-dormant-identity.js';
196
197
  import { normalizeAgentDid, inferAdapterPublisherAddress, } from './dkg-agent-helpers.js';
197
198
  import { DKGAgentBase } from './dkg-agent-base.js';
198
199
  import { VmReconcileShutdownTimeoutError } from './vm-reconcile-service.js';
@@ -208,7 +209,7 @@ import { isRfc64PrivateRecoveryOwnerV1, resolveRfc64PrivateRecoveryContextGraphI
208
209
  import { CONTEXT_GRAPH_AUTHORITY_RPC_SITES as CG_AUTH_RPC_SITES, withRpcUsageSite, } from '@origintrail-official/dkg-chain';
209
210
  import { rfc64ExecutionPlanAllowsLegacySyncV1, resolveRfc64RuntimeCatalogBootstrapConfigV1, } from './rfc64/public-catalog-activation-config-v1.js';
210
211
  import { reconcileRfc64CatalogAuthorityPlanV1 } from './rfc64/catalog-rollout-authority-reconciliation-v1.js';
211
- import { initializeRfc64LegacySwmBoundaryV1, prepareRfc64LateLegacySwmBoundaryV1, retireRfc64LegacySwmAfterFinalizedVmV1, } from './rfc64/legacy-swm-boundary-v1.js';
212
+ import { describeRfc64LegacySwmFenceEventV1, initializeRfc64LegacySwmBoundaryV1, rfc64LateLegacySwmCompanionResolverV1, retireRfc64LegacySwmAfterFinalizedVmV1, } from './rfc64/legacy-swm-boundary-v1.js';
212
213
  import { projectPersistedJoinApprovals, } from './join-approval-restart-projection.js';
213
214
  import { verifiedCuratorDialAddress } from './curator-dial-address.js';
214
215
  const DEFAULT_HOST_MODE_RECONCILE_JITTER_RATIO = 0.15;
@@ -1045,7 +1046,7 @@ export class LifecycleSyncMethods extends DKGAgentBase {
1045
1046
  try {
1046
1047
  if (this.config.dataDir !== undefined
1047
1048
  && this.rfc64PersistenceV1 !== undefined) {
1048
- await initializeRfc64LegacySwmBoundaryV1(this, this.rfc64PersistenceV1.rootPath, this.store);
1049
+ await initializeRfc64LegacySwmBoundaryV1(this, this.rfc64PersistenceV1.rootPath, this.store, { onFenceEvent: (event) => this.log.warn(ctx, describeRfc64LegacySwmFenceEventV1(event)) });
1049
1050
  await reconcileRfc64CatalogAuthorityPlanV1(this.rfc64PersistenceV1, this.store, this.config.rfc64CatalogExecutionPlan);
1050
1051
  }
1051
1052
  await this.prepareFinalizationRecoveryStore();
@@ -1348,6 +1349,7 @@ export class LifecycleSyncMethods extends DKGAgentBase {
1348
1349
  await this.loadSwmSenderKeyState();
1349
1350
  await this.initializeSwmHostModeStore();
1350
1351
  await this.rehydrateContextGraphsFromDurableState();
1352
+ await this.restoreSwmHostModeSubscriptions();
1351
1353
  this.networkAdmissionCoordinator.registerIdentityProtocol(this.router);
1352
1354
  // Register protocol handlers. PROTOCOL_ACCESS migrated onto the
1353
1355
  // Universal Messenger substrate in rc.9 PR-8 — handler is
@@ -1628,7 +1630,7 @@ export class LifecycleSyncMethods extends DKGAgentBase {
1628
1630
  recordStorageAckDecline: (code) => this.recordStorageAckDecline(code),
1629
1631
  gossipWireIdFor: (id) => this.gossipWireIdFor(id),
1630
1632
  getSwmSubscriptionSource: (...ids) => this.getSwmSubscriptionSource(...ids),
1631
- prepareDurableRootAtomicCompanion: (input) => prepareRfc64LateLegacySwmBoundaryV1(this, input.contextGraphId, input.kaUal, input.shareOperationId, input.assertionVersion),
1633
+ prepareDurableRootAtomicCompanion: rfc64LateLegacySwmCompanionResolverV1(this),
1632
1634
  chain: this.chain,
1633
1635
  config: this.config,
1634
1636
  log: this.log,
@@ -2132,6 +2134,7 @@ export class LifecycleSyncMethods extends DKGAgentBase {
2132
2134
  // once the allowlist is locally visible — clean self-heal,
2133
2135
  // no spurious denial. Other gossip topics (publish/app/
2134
2136
  // update/finalization) wire up immediately as before.
2137
+ const wirePredecessor = this.resolveWireOnlyContextGraphSubscription(contextGraphId);
2135
2138
  this.subscribeToContextGraph(contextGraphId, {
2136
2139
  deferSharedMemoryGossipSubscribe: true,
2137
2140
  syncMode: 'always-on',
@@ -2158,6 +2161,9 @@ export class LifecycleSyncMethods extends DKGAgentBase {
2158
2161
  // as a public CG locally, which is the exact corruption these
2159
2162
  // guards exist to prevent. Lex review on PR #517 round 2 + Codex.
2160
2163
  this.setContextGraphSubscription(contextGraphId, approvedSubscription, { persist: false });
2164
+ if (wirePredecessor && !this.subscribedContextGraphs.has(wirePredecessor.localId)) {
2165
+ void this.persistContextGraphSubscription(contextGraphId, { retiredNamePredecessor: { ...wirePredecessor.subscription, id: wirePredecessor.localId } });
2166
+ }
2161
2167
  this.joinRequestAcceptedBy.delete(this.joinRequestTrackingKey(contextGraphId, approvedAddr, requestGeneration));
2162
2168
  // Sync immediately by targeting the curator peer we just received
2163
2169
  // this notification from, instead of relying on the periodic
@@ -4328,7 +4334,6 @@ export class LifecycleSyncMethods extends DKGAgentBase {
4328
4334
  });
4329
4335
  if (outcome === 'applied') {
4330
4336
  this.invalidateListContextGraphsCache();
4331
- this.contextGraphMetaProjection.markDirtyFromQuads(authentication.asset.metadataQuads);
4332
4337
  try {
4333
4338
  let retiredTwin;
4334
4339
  const retirement = await reconcileFinalizedSwmTwin({
@@ -6346,17 +6351,13 @@ export class LifecycleSyncMethods extends DKGAgentBase {
6346
6351
  async refreshMetaSyncedFlags(contextGraphIds) {
6347
6352
  for (const contextGraphId of contextGraphIds) {
6348
6353
  const sub = this.subscribedContextGraphs.get(contextGraphId);
6349
- if (!sub)
6354
+ if (!sub || (!sub.subscribed && sub.coreHosted !== true))
6350
6355
  continue;
6351
6356
  if (await this.hasConfirmedMetaState(contextGraphId)) {
6352
- // A late private-CG member may never have observed the one-shot public
6353
- // registry announcement that normally supplies the numeric chain id.
6354
- // The authenticated curator snapshot carries that immutable binding in
6355
- // the CG's exact top-level `_meta` graph. Once the full private
6356
- // definition has passed `hasConfirmedMetaState`, bind and persist it as
6357
- // part of the same readiness transition. This also makes a completed
6358
- // catch-up restart-safe instead of leaving `on_chain_id = NULL` in the
6359
- // durable subscription row.
6357
+ if (this.subscribedContextGraphs.get(contextGraphId) !== sub)
6358
+ continue;
6359
+ // Confirmed private metadata can fill an unbound late member's numeric id.
6360
+ // Persist this readiness binding so completed catch-up survives restart.
6360
6361
  const contextGraphUri = contextGraphDataGraphUri(contextGraphId);
6361
6362
  const metaGraph = contextGraphMetaGraphUri(contextGraphId);
6362
6363
  const onChainIdPredicate = `${DKG_ONTOLOGY.DKG_CONTEXT_GRAPH}OnChainId`;
@@ -6372,6 +6373,8 @@ export class LifecycleSyncMethods extends DKGAgentBase {
6372
6373
  }
6373
6374
  }
6374
6375
  `, { source: 'agent.durableSync.registrationBinding' });
6376
+ if (this.subscribedContextGraphs.get(contextGraphId) !== sub)
6377
+ continue;
6375
6378
  let confirmedOnChainId;
6376
6379
  let confirmedOnChainHash;
6377
6380
  let invalidOnChainId = false;
@@ -6398,6 +6401,10 @@ export class LifecycleSyncMethods extends DKGAgentBase {
6398
6401
  confirmedOnChainHash = undefined;
6399
6402
  }
6400
6403
  let current = this.subscribedContextGraphs.get(contextGraphId) ?? sub;
6404
+ if (confirmedOnChainId && isCanonicalAuthoritativeContextGraphId(current.onChainId) && current.onChainId !== confirmedOnChainId) {
6405
+ this.log.warn(createOperationContext('sync'), `Ignored conflicting confirmed-meta binding for ${contextGraphId}: retained ${current.onChainId}, observed ${confirmedOnChainId}`);
6406
+ continue;
6407
+ }
6401
6408
  let registrationChanged = false;
6402
6409
  let nextOnChainHash = current.onChainHash;
6403
6410
  if (confirmedOnChainId && current.onChainId !== confirmedOnChainId) {
@@ -6416,6 +6423,7 @@ export class LifecycleSyncMethods extends DKGAgentBase {
6416
6423
  });
6417
6424
  current = this.subscribedContextGraphs.get(contextGraphId) ?? current;
6418
6425
  }
6426
+ const metadataWasPending = current.metaSynced !== true || current.pendingMeta === true;
6419
6427
  if (current.metaSynced !== true) {
6420
6428
  this.setContextGraphSubscription(contextGraphId, { ...current, metaSynced: true });
6421
6429
  }
@@ -6425,12 +6433,10 @@ export class LifecycleSyncMethods extends DKGAgentBase {
6425
6433
  // (set by the join-approved handler) no longer applies — the
6426
6434
  // CG will now surface via the normal `_meta` branch in
6427
6435
  // `listContextGraphs`.
6428
- this.setContextGraphSubscription(contextGraphId, {
6429
- ...current,
6430
- metaSynced: true,
6431
- pendingMeta: false,
6432
- });
6436
+ this.setContextGraphSubscription(contextGraphId, { ...current, metaSynced: true, pendingMeta: false });
6433
6437
  }
6438
+ if (metadataWasPending)
6439
+ this.announceJoinMetadataConfirmedV1(contextGraphId);
6434
6440
  // Private responsibility depends on the live ACL projection, not only
6435
6441
  // on subscription fields. A curator refresh can add/remove the local
6436
6442
  // agent while `metaSynced`, `subscribed`, and the chain binding all stay
@@ -6454,7 +6460,8 @@ export class LifecycleSyncMethods extends DKGAgentBase {
6454
6460
  const explicitNextWireId = next.onChainHash === undefined
6455
6461
  ? undefined
6456
6462
  : this.contextGraphWireId(next.onChainHash);
6457
- const adoptsWireOnlySubscription = wireOnlySubscription !== null
6463
+ const adoptsWireOnlySubscription = options?.deferWireAdoption !== true && wireOnlySubscription !== null
6464
+ && mayAdoptContextGraphWireSubscription(next, wireOnlySubscription.subscription)
6458
6465
  && (explicitNextWireId === undefined || explicitNextWireId === localWireId)
6459
6466
  && (next.onChainId === undefined
6460
6467
  || wireOnlySubscription.subscription.onChainId === undefined
@@ -6471,28 +6478,16 @@ export class LifecycleSyncMethods extends DKGAgentBase {
6471
6478
  // was first recorded under.
6472
6479
  ...(adoptsWireOnlySubscription
6473
6480
  && wireOnlySubscription.subscription.coreHosted === true
6474
- && next.coreHosted === undefined
6475
6481
  ? { coreHosted: true }
6476
6482
  : {}),
6477
6483
  });
6478
6484
  if (adoptsWireOnlySubscription) {
6479
- // A private chain event reaches an Edge before its join approval and can
6480
- // only identify the graph by the committed name hash. Once a trusted
6481
- // local path supplies the matching cleartext id, that hash-only row is
6482
- // an identity placeholder rather than a second graph. Retire it before
6483
- // publishing the canonical row so its RFC-64 responsibility, binding
6484
- // fence, and receiver lifecycle cannot remain active under the wire id.
6485
- // Host-mode bookkeeping is already wire-keyed and therefore survives
6486
- // this local-identity promotion without rewiring its ciphertext topic.
6485
+ // Retire the authenticated placeholder before publishing its canonical
6486
+ // row so no RFC-64 responsibility, binding or receiver stays wire-keyed.
6487
6487
  this.deleteContextGraphSubscription(wireOnlySubscription.localId);
6488
6488
  if (this.wireIdToLocalCgId.get(localWireId) === wireOnlySubscription.localId) {
6489
6489
  this.wireIdToLocalCgId.delete(localWireId);
6490
6490
  }
6491
- // A placeholder can be durable (a `dkg subscribe <hash> --save` row, or
6492
- // a Core's hosted row). Delete that record too, whatever this caller's
6493
- // persist flag: otherwise the next rehydration resurrects the hash row
6494
- // and re-points the reverse index away from the cleartext id.
6495
- this.retirePersistedContextGraphNamePlaceholder(wireOnlySubscription.localId, wireOnlySubscription.subscription);
6496
6491
  this.log.info(createOperationContext('system'), `Promoted wire-only Context Graph ${localWireId.slice(0, 18)}… to local identity "${contextGraphId}"`);
6497
6492
  }
6498
6493
  const previousWireId = previous?.onChainHash
@@ -6506,13 +6501,21 @@ export class LifecycleSyncMethods extends DKGAgentBase {
6506
6501
  ...normalizedNext,
6507
6502
  ...(normalizedNext.onChainHash === nextOnChainHash ? {} : { onChainHash: nextOnChainHash }),
6508
6503
  };
6504
+ const retainsInactiveBinding = retainsInactiveContextGraphBinding(previous, canonicalNext);
6509
6505
  const bindingFacts = this.contextGraphBindingState.applySubscriptionTransition(contextGraphId, previous, canonicalNext, nextWireId);
6510
6506
  if (previousWireId !== nextWireId
6511
6507
  && this.wireIdToLocalCgId.get(previousWireId) === contextGraphId) {
6512
6508
  this.wireIdToLocalCgId.delete(previousWireId);
6513
6509
  }
6514
6510
  this.subscribedContextGraphs.set(contextGraphId, canonicalNext);
6515
- this.wireIdToLocalCgId.set(nextWireId, contextGraphId);
6511
+ const wireOwnerId = this.wireIdToLocalCgId.get(nextWireId);
6512
+ const wireOwner = wireOwnerId === undefined ? undefined : this.subscribedContextGraphs.get(wireOwnerId);
6513
+ const preserveWireOwner = options?.deferWireAdoption === true || ((options?.preserveAdmittedWireBinding === true || retainsInactiveBinding)
6514
+ && wireOwnerId !== contextGraphId
6515
+ && (wireOwner?.subscribed === true || wireOwner?.coreHosted === true)
6516
+ && (retainsInactiveBinding || wireOwner.onChainId !== canonicalNext.onChainId));
6517
+ if (!preserveWireOwner)
6518
+ this.wireIdToLocalCgId.set(nextWireId, contextGraphId);
6516
6519
  if ((canonicalNext.subscribed === true || canonicalNext.coreHosted === true)
6517
6520
  && previous?.subscribed !== true
6518
6521
  && previous?.coreHosted !== true) {
@@ -6573,20 +6576,20 @@ export class LifecycleSyncMethods extends DKGAgentBase {
6573
6576
  else if (!bindingFacts.admitted) {
6574
6577
  this.clearVmReconcileStateForContextGraph(contextGraphId);
6575
6578
  }
6576
- // On-demand member subscriptions deliberately keep their live state and
6577
- // readiness process-local. A Core's independent hosting obligation is
6578
- // still durable, though: persistContextGraphSubscription writes a
6579
- // host-only snapshot without converting the member intent to always-on.
6580
6579
  const persistence = projectContextGraphSubscriptionPersistence({
6581
6580
  contextGraphId,
6582
6581
  subscription: canonicalNext,
6583
6582
  syncScoped: (this.config.syncContextGraphs ?? []).includes(contextGraphId),
6583
+ preserveInactiveIntent: options?.persist !== true && retainsInactiveBinding
6584
+ && previous?.subscribed !== true && previous?.coreHosted !== true,
6584
6585
  });
6585
6586
  if (options?.persist !== false && persistence.action !== 'skip') {
6586
6587
  if (this.config.contextGraphSubscriptionStore) {
6587
6588
  const revision = this.nextContextGraphSubscriptionPersistRevision(contextGraphId);
6588
6589
  this.persistContextGraphSubscription(contextGraphId, {
6589
6590
  revision,
6591
+ preserveDormantHosting: previous?.coreHosted !== true || canonicalNext.coreHosted === true,
6592
+ retiredNamePredecessor: adoptsWireOnlySubscription ? { ...wireOnlySubscription.subscription, id: wireOnlySubscription.localId } : undefined,
6590
6593
  updateRehydrationStatus: options?.updateRehydrationStatus !== false,
6591
6594
  });
6592
6595
  }
@@ -6713,21 +6716,31 @@ export class LifecycleSyncMethods extends DKGAgentBase {
6713
6716
  const hostedActivatedIds = status.hostedActivatedIds ?? [];
6714
6717
  const wasAccounted = this.contextGraphSubscriptionRehydrationAccountedIds.has(contextGraphId);
6715
6718
  const isPersisted = next?.subscribed === true || next?.coreHosted === true;
6719
+ const live = this.subscribedContextGraphs.get(contextGraphId);
6720
+ const isActive = live?.subscribed === true || live?.coreHosted === true;
6716
6721
  let persistedTotal = status.persistedTotal;
6717
6722
  let activated = status.activated;
6718
6723
  const dormancyById = this.contextGraphSubscriptionDormancyById;
6719
- dormancyById.delete(contextGraphId);
6724
+ if (!isPersisted || isActive)
6725
+ dormancyById.delete(contextGraphId);
6726
+ else
6727
+ dormancyById.set(contextGraphId, dormancyById.get(contextGraphId)
6728
+ ?? (status.rehydrationEnabled ? 'authorityUnavailable' : 'rehydrationDisabled'));
6720
6729
  let nextHostedActivatedIds = hostedActivatedIds.filter((id) => id !== contextGraphId);
6721
- if (next?.coreHosted === true) {
6730
+ if (live?.coreHosted === true) {
6722
6731
  nextHostedActivatedIds = sortIds([...nextHostedActivatedIds, contextGraphId]);
6723
6732
  }
6724
6733
  if (isPersisted) {
6725
- if (wasDormant) {
6734
+ if (wasDormant && isActive) {
6726
6735
  activated += 1;
6727
6736
  }
6728
6737
  else if (!wasAccounted) {
6729
6738
  persistedTotal += 1;
6730
- activated += 1;
6739
+ if (isActive)
6740
+ activated += 1;
6741
+ }
6742
+ else if (!wasDormant && !isActive) {
6743
+ activated = Math.max(0, activated - 1);
6731
6744
  }
6732
6745
  this.contextGraphSubscriptionRehydrationAccountedIds.add(contextGraphId);
6733
6746
  }
@@ -6933,6 +6946,7 @@ export class LifecycleSyncMethods extends DKGAgentBase {
6933
6946
  return this.persistContextGraphSubscription(contextGraphId, {
6934
6947
  revision: this.nextContextGraphSubscriptionPersistRevision(contextGraphId),
6935
6948
  updateRehydrationStatus: false,
6949
+ preserveInactiveIntent: true,
6936
6950
  });
6937
6951
  }
6938
6952
  persistContextGraphSubscription(contextGraphId, options) {
@@ -6943,44 +6957,45 @@ export class LifecycleSyncMethods extends DKGAgentBase {
6943
6957
  return Promise.resolve();
6944
6958
  }
6945
6959
  const sub = this.subscribedContextGraphs.get(contextGraphId);
6946
- const persistence = projectContextGraphSubscriptionPersistence({
6947
- contextGraphId,
6948
- subscription: sub,
6960
+ const generation = this.contextGraphBindingState.capture(contextGraphId);
6961
+ const revision = options?.revision ?? this.contextGraphSubscriptionPersistRevisions.get(contextGraphId);
6962
+ const input = {
6963
+ contextGraphId, subscription: sub && { ...sub },
6949
6964
  syncScoped: (this.config.syncContextGraphs ?? []).includes(contextGraphId),
6950
- });
6951
- if (persistence.action === 'skip') {
6952
- // Some lifecycle paths persist reconciliation watermarks directly
6953
- // instead of going through setContextGraphSubscription. Preserve the
6954
- // process-local lifetime at this lowest shared write boundary too.
6965
+ preserveInactiveIntent: options?.preserveInactiveIntent,
6966
+ };
6967
+ if (projectContextGraphSubscriptionPersistence(input).action === 'skip') {
6955
6968
  this.clearContextGraphSubscriptionPersistRevisionStateIfIdle(contextGraphId);
6956
6969
  return Promise.resolve();
6957
6970
  }
6958
- // Persist member subscriptions AND (Phase D) public CGs this Core hosts —
6959
- // the host-only record MUST survive restart so a Core that was offline
6960
- // during a publish remembers it hosts the CG and fills its gap. Drop the
6961
- // row only when the node neither subscribes to nor hosts the CG.
6962
6971
  this.beginContextGraphSubscriptionPersistRevision(contextGraphId, options?.revision);
6963
- if (persistence.action === 'delete') {
6964
- return this.enqueueContextGraphSubscriptionPersistWrite(contextGraphId, () => store.delete(contextGraphId))
6965
- .then(() => {
6966
- if (options?.updateRehydrationStatus === true &&
6967
- this.claimContextGraphSubscriptionPersistRevision(contextGraphId, options.revision)) {
6968
- this.updateContextGraphSubscriptionRehydrationStatusAfterPersist(contextGraphId, undefined);
6969
- }
6970
- })
6971
- .catch((err) => {
6972
- this.log.warn(createOperationContext('system'), `Failed to delete persisted context-graph subscription for "${contextGraphId}": ${err instanceof Error ? err.message : String(err)}`);
6973
- })
6974
- .finally(() => {
6975
- this.finishContextGraphSubscriptionPersistRevision(contextGraphId, options?.revision);
6976
- });
6977
- }
6978
- const record = persistence.record;
6979
- return this.enqueueContextGraphSubscriptionPersistWrite(contextGraphId, () => store.save(record))
6980
- .then(() => {
6981
- if (options?.updateRehydrationStatus === true &&
6982
- this.claimContextGraphSubscriptionPersistRevision(contextGraphId, options.revision)) {
6983
- this.updateContextGraphSubscriptionRehydrationStatusAfterPersist(contextGraphId, record);
6972
+ let written = false;
6973
+ let persisted;
6974
+ return this.enqueueContextGraphSubscriptionPersistWrite(contextGraphId, async () => {
6975
+ const persistence = input.subscription?.coreHosted === true
6976
+ ? projectContextGraphSubscriptionPersistence(input)
6977
+ : await projectContextGraphSubscriptionPersistenceWithSavedHosting(input, store, options?.preserveDormantHosting);
6978
+ const current = this.subscribedContextGraphs.get(contextGraphId);
6979
+ const retired = persistence.action === 'delete' && current === undefined;
6980
+ if (persistence.action === 'skip' || (!retired && (!this.contextGraphBindingState.isGenerationCurrent(contextGraphId, generation)
6981
+ || !isContextGraphSubscriptionPersistenceTargetCurrent(sub, input, current, {
6982
+ revision,
6983
+ pendingRevisions: this.contextGraphSubscriptionPersistPendingRevisions.get(contextGraphId),
6984
+ syncScoped: (this.config.syncContextGraphs ?? []).includes(contextGraphId),
6985
+ }))))
6986
+ return;
6987
+ if (persistence.action === 'delete')
6988
+ await store.delete(contextGraphId);
6989
+ else {
6990
+ persisted = persistence.record;
6991
+ await store.save(persisted);
6992
+ }
6993
+ await this.retirePersistedContextGraphNamePredecessors(input, sub, generation, revision, persistence, options?.retiredNamePredecessor);
6994
+ written = true;
6995
+ }).then(() => {
6996
+ if (written && options?.updateRehydrationStatus === true
6997
+ && this.claimContextGraphSubscriptionPersistRevision(contextGraphId, options.revision)) {
6998
+ this.updateContextGraphSubscriptionRehydrationStatusAfterPersist(contextGraphId, persisted);
6984
6999
  }
6985
7000
  }).catch((err) => {
6986
7001
  this.log.warn(createOperationContext('system'), `Failed to persist context-graph subscription for "${contextGraphId}": ${err instanceof Error ? err.message : String(err)}`);
@@ -7042,6 +7057,7 @@ export class LifecycleSyncMethods extends DKGAgentBase {
7042
7057
  // Queue behind any fire-and-forget writes scheduled by subscribe/mark so
7043
7058
  // this final authoritative snapshot is the last write before the ACK.
7044
7059
  await this.enqueueContextGraphSubscriptionPersistWrite(input.contextGraphId, async () => {
7060
+ const hostedRecord = await readPreservedContextGraphCoreHosting(store, record);
7045
7061
  const current = this.subscribedContextGraphs.get(input.contextGraphId);
7046
7062
  if (!(input.isCurrent?.() ?? true)
7047
7063
  || current !== expectedLiveSub
@@ -7049,7 +7065,7 @@ export class LifecycleSyncMethods extends DKGAgentBase {
7049
7065
  || !this.contextGraphBindingState.isGenerationCurrent(input.contextGraphId, expectedBindingGeneration)) {
7050
7066
  throw asSyncFetchAbortError(new Error(`Context graph "${input.contextGraphId}" changed before its strict subscription snapshot (${input.operation}) was persisted`));
7051
7067
  }
7052
- await store.save(record);
7068
+ await store.save(hostedRecord);
7053
7069
  });
7054
7070
  }
7055
7071
  /**
@@ -7116,22 +7132,19 @@ export class LifecycleSyncMethods extends DKGAgentBase {
7116
7132
  ? await subscriptionStore.load(contextGraphId)
7117
7133
  : (await subscriptionStore.loadAll()).find((row) => row.id === contextGraphId) ?? null;
7118
7134
  }
7119
- const subscriptionRecord = {
7120
- id: contextGraphId,
7121
- name: subscription.name,
7122
- subscribed: subscription.subscribed,
7123
- synced: subscription.synced,
7124
- sharedMemorySynced: subscription.sharedMemorySynced,
7125
- metaSynced: subscription.metaSynced,
7126
- onChainId: subscription.onChainId,
7127
- onChainHash: subscription.onChainHash,
7128
- lastReconciledOrdinal: subscription.lastReconciledOrdinal,
7129
- coreHosted: subscription.coreHosted,
7130
- syncScoped: true,
7131
- };
7135
+ const subscriptionRecord = projectContextGraphJoinSubscriptionRecord(contextGraphId, subscription);
7132
7136
  let membershipAttempted = false;
7133
7137
  let subscriptionAttempted = false;
7134
7138
  try {
7139
+ const expectedLiveSub = this.subscribedContextGraphs.get(contextGraphId);
7140
+ const generation = this.contextGraphBindingState.capture(contextGraphId);
7141
+ const hostedRecord = subscriptionStore
7142
+ ? await readPreservedContextGraphCoreHosting(subscriptionStore, subscriptionRecord, previousSubscription)
7143
+ : subscriptionRecord;
7144
+ if (subscriptionStore && (this.subscribedContextGraphs.get(contextGraphId) !== expectedLiveSub
7145
+ || !this.contextGraphBindingState.isGenerationCurrent(contextGraphId, generation))) {
7146
+ throw asSyncFetchAbortError(new Error(`Context graph "${contextGraphId}" changed before join persistence`));
7147
+ }
7135
7148
  if (membershipStore) {
7136
7149
  membershipAttempted = true;
7137
7150
  await membershipStore.upsert({
@@ -7142,7 +7155,7 @@ export class LifecycleSyncMethods extends DKGAgentBase {
7142
7155
  }
7143
7156
  if (subscriptionStore) {
7144
7157
  subscriptionAttempted = true;
7145
- await subscriptionStore.save(subscriptionRecord);
7158
+ await subscriptionStore.save(hostedRecord);
7146
7159
  }
7147
7160
  }
7148
7161
  catch (error) {
@@ -7310,20 +7323,27 @@ export class LifecycleSyncMethods extends DKGAgentBase {
7310
7323
  lastReconciledOrdinal: record.lastReconciledOrdinal,
7311
7324
  coreHosted: record.coreHosted,
7312
7325
  }, {
7313
- persist: false,
7326
+ persist: false, deferWireAdoption: true,
7314
7327
  updateRehydrationStatus: input.updateRehydrationStatus,
7315
7328
  }),
7316
7329
  current: (contextGraphId) => this.subscribedContextGraphs.get(contextGraphId),
7317
7330
  remove: (contextGraphId) => this.deleteContextGraphSubscription(contextGraphId),
7331
+ restoreInactive: (id, previous) => void this.setContextGraphSubscription(id, previous, { persist: false, preserveAdmittedWireBinding: true }),
7318
7332
  rollbackNetworkEffects: (contextGraphId) => this.unsubscribeFromContextGraph(contextGraphId, { persist: false, updateRehydrationStatus: false }),
7319
7333
  trackSync: (contextGraphId) => this.trackSyncContextGraph(contextGraphId),
7320
7334
  subscribe: (contextGraphId) => this.subscribeToContextGraph(contextGraphId, {
7321
7335
  trackSyncScope: false,
7322
- persist: false,
7336
+ persist: false, deferWireAdoption: true,
7323
7337
  syncMode: 'always-on',
7324
7338
  }),
7325
- persistMembership: (contextGraphId) => {
7326
- this.persistLocalNodeMembership(contextGraphId, 'rehydrated-subscription');
7339
+ persistMembership: (id) => this.persistLocalNodeMembership(id, 'rehydrated-subscription'),
7340
+ commit: (id) => {
7341
+ const wire = this.resolveWireOnlyContextGraphSubscription(id);
7342
+ this.retireLiveContextGraphNamePlaceholderFor(id);
7343
+ const committed = this.setContextGraphSubscription(id, this.subscribedContextGraphs.get(id), { persist: false, preserveAdmittedWireBinding: true });
7344
+ if (wire && !this.subscribedContextGraphs.has(wire.localId))
7345
+ void this.persistContextGraphSubscription(id, { retiredNamePredecessor: { ...wire.subscription, id: wire.localId } });
7346
+ return committed;
7327
7347
  },
7328
7348
  }, options);
7329
7349
  }
@@ -7473,7 +7493,8 @@ export class LifecycleSyncMethods extends DKGAgentBase {
7473
7493
  this.updateContextGraphSubscriptionRehydrationStatusAfterClear([], [contextGraphId]);
7474
7494
  continue;
7475
7495
  }
7476
- if (this.subscribedContextGraphs.has(contextGraphId)) {
7496
+ const currentSubscription = this.subscribedContextGraphs.get(contextGraphId);
7497
+ if (currentSubscription?.subscribed || currentSubscription?.coreHosted) {
7477
7498
  this.contextGraphSubscriptionRehydrationPendingIds.delete(contextGraphId);
7478
7499
  this.contextGraphSubscriptionDormancyById.delete(contextGraphId);
7479
7500
  continue;
@@ -7527,7 +7548,8 @@ export class LifecycleSyncMethods extends DKGAgentBase {
7527
7548
  || this.contextGraphSubscriptionDormancyById.get(contextGraphId) !== 'activationCap') {
7528
7549
  continue;
7529
7550
  }
7530
- if (this.subscribedContextGraphs.has(contextGraphId)) {
7551
+ const freshSubscription = this.subscribedContextGraphs.get(contextGraphId);
7552
+ if (freshSubscription?.subscribed || freshSubscription?.coreHosted) {
7531
7553
  this.contextGraphSubscriptionRehydrationPendingIds.delete(contextGraphId);
7532
7554
  this.contextGraphSubscriptionDormancyById.delete(contextGraphId);
7533
7555
  continue;
@@ -7675,9 +7697,10 @@ export class LifecycleSyncMethods extends DKGAgentBase {
7675
7697
  this.recordPersistedContextGraphIdAliases(persistedRows);
7676
7698
  this.rewriteContextGraphSyncScopeAliases();
7677
7699
  let rows = persistedRows.filter((r) => !systemContextGraphs.has(r.id));
7700
+ const namePlaceholders = partitionSupersededContextGraphNamePlaceholders(rows);
7701
+ rows = namePlaceholders.active;
7678
7702
  // The operator kill-switch below promises not to touch durable state.
7679
7703
  if (this.config.contextGraphSubscriptionRehydrationEnabled) {
7680
- const namePlaceholders = partitionSupersededContextGraphNamePlaceholders(rows);
7681
7704
  for (const superseded of namePlaceholders.superseded) {
7682
7705
  try {
7683
7706
  await store.delete(superseded.id);
@@ -7687,7 +7710,6 @@ export class LifecycleSyncMethods extends DKGAgentBase {
7687
7710
  this.log.warn(ctx, `Failed to drop superseded name-hash subscription row ${superseded.id.slice(0, 18)}…: ${error instanceof Error ? error.message : String(error)}`);
7688
7711
  }
7689
7712
  }
7690
- rows = namePlaceholders.active;
7691
7713
  }
7692
7714
  // Validate the cap before either branch below so diagnostics retain the
7693
7715
  // operator's configured cap even when the independent rehydration gate
@@ -7703,12 +7725,19 @@ export class LifecycleSyncMethods extends DKGAgentBase {
7703
7725
  `(must be a non-negative integer); using default ${DEFAULT_MAX_REHYDRATED_SUBSCRIPTIONS}.`);
7704
7726
  }
7705
7727
  }
7706
- // Operator kill-switch: inventory the durable rows for diagnostics but
7707
- // deliberately do not copy any of them into runtime subscription state.
7708
- // In particular, do not install gossip handlers, add automatic sync
7709
- // scope, persist node-membership side effects, or touch the RDF store.
7710
- // A later explicit subscribe remains a normal live activation and updates
7711
- // the status through updateContextGraphSubscriptionRehydrationStatusAfterPersist.
7728
+ // Restore exact saved identity before discovery, independently of every
7729
+ // activation gate (disabled, capped or authority-denied). Inactive rows
7730
+ // cannot admit gossip, sync or RFC-64 authority; installation owns no
7731
+ // subscription, membership or RDF writes. Unbound/invalid rows stay
7732
+ // absent, and the fresh authority gates below still own activation.
7733
+ for (const [id, identity] of projectDormantContextGraphIdentities(rows)) {
7734
+ const current = this.subscribedContextGraphs.get(id);
7735
+ if (current?.subscribed || current?.coreHosted)
7736
+ continue;
7737
+ this.setContextGraphSubscription(id, identity, {
7738
+ persist: false, preserveAdmittedWireBinding: true,
7739
+ });
7740
+ }
7712
7741
  if (!this.config.contextGraphSubscriptionRehydrationEnabled) {
7713
7742
  const dormancyById = this.contextGraphSubscriptionDormancyById;
7714
7743
  dormancyById.clear();
@@ -7722,7 +7751,7 @@ export class LifecycleSyncMethods extends DKGAgentBase {
7722
7751
  this.contextGraphSubscriptionRehydrationStatus = {
7723
7752
  rehydrationEnabled: false,
7724
7753
  persistedTotal: rows.length,
7725
- systemExcluded: persistedRows.length - rows.length,
7754
+ systemExcluded: persistedRows.length - rows.length - namePlaceholders.superseded.length,
7726
7755
  hostedActivated: 0,
7727
7756
  hostedActivatedIds: [],
7728
7757
  activated: 0,
@@ -7762,10 +7791,9 @@ export class LifecycleSyncMethods extends DKGAgentBase {
7762
7791
  // the cap stay PERSISTED but dormant and are promoted when an earlier
7763
7792
  // rehydrated subscription reaches a safe synced state. Prioritise
7764
7793
  // core-hosted, then subscribed, so the first set is most relevant.
7765
- // A dormant (capped-out) row has no in-memory entry, so individual
7766
- // `POST /api/context-graph/unsubscribe` cannot target it. The row remains
7767
- // eligible for rolling activation or bulk pruning through the existing
7768
- // DELETE endpoint.
7794
+ // Canonically bound dormant rows retain inactive identity only; no data
7795
+ // lane is active. Every dormant row remains eligible for rolling
7796
+ // activation or explicit pruning through the existing DELETE endpoint.
7769
7797
  // coreHosted graphs MUST always be restored — their chain-driven
7770
7798
  // reconcile / host-mode path depends on it — so EXEMPT them from the cap.
7771
7799
  // The cap (a #997 anti-wedge measure) applies only to the non-hosted
@@ -7976,7 +8004,7 @@ export class LifecycleSyncMethods extends DKGAgentBase {
7976
8004
  this.contextGraphSubscriptionRehydrationStatus = {
7977
8005
  rehydrationEnabled: true,
7978
8006
  persistedTotal: rows.length,
7979
- systemExcluded: persistedRows.length - rows.length,
8007
+ systemExcluded: persistedRows.length - rows.length - namePlaceholders.superseded.length,
7980
8008
  hostedActivated: activatedRows.filter((r) => r.coreHosted).length,
7981
8009
  hostedActivatedIds: activatedRows.filter((r) => r.coreHosted).map((r) => r.id),
7982
8010
  activated: activatedRows.length,
@@ -8040,29 +8068,13 @@ export class LifecycleSyncMethods extends DKGAgentBase {
8040
8068
  // USER context-graph subscriptions, live and persisted alike. (The store's
8041
8069
  // system-unaware bulk delete is deliberately NOT used here.)
8042
8070
  const systemContextGraphs = new Set(Object.values(SYSTEM_CONTEXT_GRAPHS));
8043
- // Clearable = a NON-system, NON-coreHosted user subscription. System CGs are
8044
- // the control plane (above). coreHosted graphs are legitimate hosted graphs
8045
- // that `unsubscribeFromContextGraph` deliberately keeps wired for host-mode /
8046
- // chain reconcile (so a teardown can't fully remove them anyway) and that the
8047
- // rehydration cap already exempts — clearing/counting them here would report a
8048
- // removal that didn't happen. The clear targets only the stale non-hosted
8049
- // backlog, the actual #997 wedge.
8050
- const isClearable = (id, coreHosted) => !systemContextGraphs.has(id) && coreHosted !== true;
8051
- const activeUserIds = [...this.subscribedContextGraphs.entries()]
8052
- .filter(([id, s]) => isClearable(id, s?.coreHosted))
8053
- .map(([id]) => id);
8054
- // The full persisted clearable backlog (active + dormant rows left behind by
8055
- // the rehydration cap). Counted up front: `unsubscribeFromContextGraph`
8056
- // deletes each active row as a side effect, so a post-teardown count would
8057
- // miss them. Starts empty so a node with NO store reports 0 persisted rows
8058
- // removed (the active teardown is logged separately) rather than a phantom
8059
- // count of the in-memory subs.
8060
- let persistedUserIds = [];
8071
+ // Snapshot saved hosting before teardown: dormant Core intent is not
8072
+ // reflected by the inactive runtime projection. Pending writes are checked
8073
+ // again inside their serial lane before a selective durable deletion.
8074
+ let persistedRows = [];
8061
8075
  if (store) {
8062
8076
  try {
8063
- persistedUserIds = (await store.loadAll())
8064
- .filter((r) => isClearable(r.id, r.coreHosted))
8065
- .map((r) => r.id);
8077
+ persistedRows = await store.loadAll();
8066
8078
  }
8067
8079
  catch (err) {
8068
8080
  // Can't enumerate the persisted backlog → the dormant (capped-out) rows
@@ -8075,6 +8087,7 @@ export class LifecycleSyncMethods extends DKGAgentBase {
8075
8087
  `Resolve the store error and retry.`);
8076
8088
  }
8077
8089
  }
8090
+ const { activeUserIds, persistedUserIds } = contextGraphSubscriptionClearTargets(this.subscribedContextGraphs, persistedRows, systemContextGraphs);
8078
8091
  const activeUserIdsWithPendingStoreWrite = store
8079
8092
  ? activeUserIds.filter((id) => this.contextGraphSubscriptionPersistence.hasLane(id))
8080
8093
  : [];
@@ -8116,7 +8129,17 @@ export class LifecycleSyncMethods extends DKGAgentBase {
8116
8129
  cleared = 0;
8117
8130
  for (const id of storeDeleteIds) {
8118
8131
  try {
8119
- await this.enqueueContextGraphSubscriptionPersistWrite(id, () => store.delete(id));
8132
+ let deleted = false;
8133
+ await this.enqueueContextGraphSubscriptionPersistWrite(id, async () => {
8134
+ const hosted = store.load ? (await store.load(id))?.coreHosted === true
8135
+ : (await store.loadAll()).some(row => row.id === id && row.coreHosted === true);
8136
+ if (hosted || this.subscribedContextGraphs.get(id)?.coreHosted === true)
8137
+ return;
8138
+ await store.delete(id);
8139
+ deleted = true;
8140
+ });
8141
+ if (!deleted)
8142
+ continue;
8120
8143
  cleared++;
8121
8144
  clearedIds.push(id);
8122
8145
  }