@engramx/idl 0.16.0-rc.1

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.
@@ -0,0 +1,1692 @@
1
+ import type { Principal } from '@icp-sdk/core/principal';
2
+ import type { ActorMethod } from '@icp-sdk/core/agent';
3
+ import type { IDL } from '@icp-sdk/core/candid';
4
+
5
+ export interface ActivationFunnel {
6
+ spawnsToday: bigint;
7
+ reserved: bigint;
8
+ spawnCeiling: bigint;
9
+ reservedStale: bigint;
10
+ dailyCap: bigint;
11
+ provisioned: bigint;
12
+ }
13
+ export interface AdminRoles {
14
+ roles: Array<RoleGrant>;
15
+ }
16
+ export interface AnchorConflict {
17
+ submittedHead: Uint8Array | number[];
18
+ atNs: bigint;
19
+ storedHead: Uint8Array | number[];
20
+ submittedTotal: bigint;
21
+ storedTotal: bigint;
22
+ }
23
+ export interface AnchorStats {
24
+ lastAnchorAtNs: [] | [bigint];
25
+ engramsAnchored: bigint;
26
+ totalAnchors: bigint;
27
+ engramsWithConflicts: bigint;
28
+ }
29
+ export interface AnchorView {
30
+ total: bigint;
31
+ disputed: boolean;
32
+ atNs: bigint;
33
+ head: Uint8Array | number[];
34
+ }
35
+ export interface AuditEntry {
36
+ id: bigint;
37
+ entryHash: Uint8Array | number[];
38
+ callerRole: Role;
39
+ operation: string;
40
+ details: string;
41
+ caller: Principal;
42
+ success: boolean;
43
+ timestampNs: bigint;
44
+ }
45
+ export interface AuditHead {
46
+ headHash: Uint8Array | number[];
47
+ size: bigint;
48
+ }
49
+ export interface CertifiedAnchor {
50
+ certificate: [] | [Uint8Array | number[]];
51
+ leaf: CertifiedAnchorLeaf;
52
+ witness: Uint8Array | number[];
53
+ }
54
+ export interface CertifiedAnchorLeaf {
55
+ total: bigint;
56
+ disputed: boolean;
57
+ atNs: bigint;
58
+ head: Uint8Array | number[];
59
+ wasmHash: string;
60
+ conflicts: bigint;
61
+ version: bigint;
62
+ refusedConflicts: bigint;
63
+ engram: Principal;
64
+ }
65
+ export interface CertifiedAuditHead {
66
+ total: bigint;
67
+ certificate: [] | [Uint8Array | number[]];
68
+ headHash: Uint8Array | number[];
69
+ size: bigint;
70
+ witness: [] | [Uint8Array | number[]];
71
+ }
72
+ export interface CertifiedStanding {
73
+ certificate: [] | [Uint8Array | number[]];
74
+ leaf: StandingLeaf;
75
+ witness: Uint8Array | number[];
76
+ }
77
+ export interface ChangelogEntry {
78
+ atNs: bigint;
79
+ wasmHash: string;
80
+ releaseNotes: string;
81
+ version: string;
82
+ }
83
+ export type ControlStep = { EngramMustMove: null } | { MovedByRegistry: null };
84
+ export type CustodyMode = { Sovereign: null } | { Managed: null };
85
+ export interface Dispute {
86
+ statement: string;
87
+ atNs: bigint;
88
+ }
89
+ export type EdgeEvent = { Ended: Ending } | { Added: EnrolmentProof };
90
+ export type EndReason =
91
+ | { Resigned: null }
92
+ | { OwnerRevoked: null }
93
+ | { Reset: null }
94
+ | { KillSwitch: null }
95
+ | { GuardianRevoked: null };
96
+ export interface Ending {
97
+ day: bigint;
98
+ key: Principal;
99
+ invite: string;
100
+ reason: EndReason;
101
+ }
102
+ export interface EngagementRow {
103
+ seq: bigint;
104
+ top: Principal;
105
+ ownerOnList: boolean;
106
+ openedThroughParent: boolean;
107
+ replacesAnother: boolean;
108
+ ended: [] | [RowEnding];
109
+ abandonedDay: [] | [bigint];
110
+ resetDays: Array<bigint>;
111
+ sameRootAsPrevious: [] | [boolean];
112
+ firstOutsideOnTree: boolean;
113
+ topOnList: boolean;
114
+ rootIsTopOwner: boolean;
115
+ rootOnList: boolean;
116
+ startedDay: bigint;
117
+ engram: Principal;
118
+ }
119
+ export interface EngramRecordView {
120
+ status: EngramStatus;
121
+ owner: Principal;
122
+ createdAtNs: bigint;
123
+ canisterId: Principal;
124
+ }
125
+ export type EngramStatus = { Active: null } | { Deleted: null } | { Frozen: null };
126
+ export interface EngramWasmInfo {
127
+ chunkBytes: bigint;
128
+ hash: string;
129
+ totalBytes: bigint;
130
+ chunks: bigint;
131
+ }
132
+ export interface EnrolmentProof {
133
+ signature: Uint8Array | number[];
134
+ enrolledKey: Principal;
135
+ rootPubkey: Uint8Array | number[];
136
+ inviteCode: string;
137
+ }
138
+ export type Error =
139
+ | { Invalid: string }
140
+ | { NotFound: string }
141
+ | { Refused: string }
142
+ | { Other: string }
143
+ | { Unavailable: string }
144
+ | { Conflict: string };
145
+ export interface FleetCycleHealth {
146
+ unreadable: bigint;
147
+ deleted: bigint;
148
+ total: bigint;
149
+ active: bigint;
150
+ thresholdCycles: bigint;
151
+ scanned: bigint;
152
+ probed: bigint;
153
+ frozen: bigint;
154
+ sovereignSkipped: bigint;
155
+ lowCycles: bigint;
156
+ }
157
+ export interface FleetSummary {
158
+ registrySinceNs: [] | [bigint];
159
+ uniqueOwners: bigint;
160
+ totalCyclesAllocated: bigint;
161
+ engramWasmHash: string;
162
+ cycleReserve: bigint;
163
+ registryVersion: string;
164
+ sovereignEngrams: bigint;
165
+ totalEngrams: bigint;
166
+ activeEngrams: bigint;
167
+ }
168
+ export interface GenesisNote {
169
+ predecessorSize: [] | [bigint];
170
+ predecessorHeadHash: [] | [Uint8Array | number[]];
171
+ note: string;
172
+ version: string;
173
+ genesisAtNs: bigint;
174
+ }
175
+ export interface LastUpgrade {
176
+ atNs: bigint;
177
+ changedAtNs: [] | [bigint];
178
+ upgraded: boolean;
179
+ template: string;
180
+ }
181
+ export interface MeterReading {
182
+ realizedPerDay: [] | [bigint];
183
+ idlePerDay: [] | [bigint];
184
+ cycles: bigint;
185
+ freezeLineCycles: bigint;
186
+ }
187
+ export interface OpenedEngram {
188
+ cutOffAtNs: [] | [bigint];
189
+ owner: Principal;
190
+ child: Principal;
191
+ }
192
+ export interface OpenedThrough {
193
+ cutOffAtNs: [] | [bigint];
194
+ child: Principal;
195
+ parent: Principal;
196
+ }
197
+ export interface OpeningCounts {
198
+ reopened: bigint;
199
+ openedThroughParent: bigint;
200
+ cutOff: bigint;
201
+ abandoned: bigint;
202
+ spawnedEver: bigint;
203
+ reservationAttemptsRefusedAtCapacity: bigint;
204
+ spawnAttemptsRefusedAtCap: bigint;
205
+ }
206
+ export interface Page {
207
+ total: bigint;
208
+ withheld: bigint;
209
+ items: Array<[Principal, UpdatePreference]>;
210
+ }
211
+ export interface Page_1 {
212
+ total: bigint;
213
+ withheld: bigint;
214
+ items: Array<EngramRecordView>;
215
+ }
216
+ export interface Page_2 {
217
+ total: bigint;
218
+ withheld: bigint;
219
+ items: Array<EngagementRow>;
220
+ }
221
+ export interface Page_3 {
222
+ total: bigint;
223
+ withheld: bigint;
224
+ items: Array<AuditEntry>;
225
+ }
226
+ export interface Page_4 {
227
+ total: bigint;
228
+ withheld: bigint;
229
+ items: Array<ChangelogEntry>;
230
+ }
231
+ export interface Page_5 {
232
+ total: bigint;
233
+ withheld: bigint;
234
+ items: Array<Principal>;
235
+ }
236
+ export interface Page_6 {
237
+ total: bigint;
238
+ withheld: bigint;
239
+ items: Array<AnchorConflict>;
240
+ }
241
+ export interface PairedSoon {
242
+ opened: bigint;
243
+ pairedWithin7Days: bigint;
244
+ }
245
+ export interface PrivateMetrics {
246
+ registrySinceNs: [] | [bigint];
247
+ cyclesLastUpdatedNs: [] | [bigint];
248
+ uniqueOwners: bigint;
249
+ totalCyclesAllocated: bigint;
250
+ engramWasmHash: string;
251
+ cycleReserve: bigint;
252
+ registryVersion: string;
253
+ sovereignEngrams: bigint;
254
+ totalEngrams: bigint;
255
+ activeEngrams: bigint;
256
+ }
257
+ export interface ReferralCounts {
258
+ firstOrReopened: PairedSoon;
259
+ openedThroughParentLive: bigint;
260
+ openedThroughParentCutOff: bigint;
261
+ throughParent: PairedSoon;
262
+ agentsByTreesSpanned: TreesSpanned;
263
+ }
264
+ export type ReleaseClass = { Security: null } | { Feature: null };
265
+ export interface ReservationView {
266
+ reservedAtNs: bigint;
267
+ }
268
+ export interface ReserveGateConfig {
269
+ enabled: boolean;
270
+ inviteIssuer: Principal;
271
+ trustedSigners: Array<Principal>;
272
+ }
273
+ export type Result = { Ok: null } | { Err: Error };
274
+ export type Result_1 = { Ok: Principal } | { Err: Error };
275
+ export type Result_10 = { Ok: Page_3 } | { Err: Error };
276
+ export type Result_11 = { Ok: Array<OpenedEngram> } | { Err: Error };
277
+ export type Result_12 = { Ok: Page_5 } | { Err: Error };
278
+ export type Result_13 = { Ok: Page_6 } | { Err: Error };
279
+ export type Result_14 = { Ok: [] | [string] } | { Err: Error };
280
+ export type Result_15 = { Ok: ReferralCounts } | { Err: Error };
281
+ export type Result_16 = { Ok: OpeningCounts } | { Err: Error };
282
+ export type Result_17 = { Ok: PrivateMetrics } | { Err: Error };
283
+ export type Result_18 = { Ok: Uint8Array | number[] } | { Err: Error };
284
+ export type Result_19 = { Ok: OpenedThrough } | { Err: Error };
285
+ export type Result_2 = { Ok: ControlStep } | { Err: Error };
286
+ export type Result_20 = { Ok: CertifiedStanding } | { Err: Error };
287
+ export type Result_21 = { Ok: FleetCycleHealth } | { Err: Error };
288
+ export type Result_3 = { Ok: MeterReading } | { Err: Error };
289
+ export type Result_4 = { Ok: bigint } | { Err: Error };
290
+ export type Result_5 = { Ok: CustodyMode } | { Err: Error };
291
+ export type Result_6 = { Ok: Array<string> } | { Err: Error };
292
+ export type Result_7 = { Ok: Page } | { Err: Error };
293
+ export type Result_8 = { Ok: Page_1 } | { Err: Error };
294
+ export type Result_9 = { Ok: Page_2 } | { Err: Error };
295
+ export type Role =
296
+ | { Anonymous: null }
297
+ | { Viewer: null }
298
+ | { Engram: null }
299
+ | { Root: null }
300
+ | { Buyer: null }
301
+ | { WasmAdmin: null }
302
+ | { Guardian: null }
303
+ | { Seller: null }
304
+ | { Admin: null }
305
+ | { Agent: null }
306
+ | { Other: null }
307
+ | { Owner: null };
308
+ export interface RoleGrant {
309
+ role: { Root: null } | { WasmAdmin: null } | { Admin: null } | { Other: string };
310
+ holder: Principal;
311
+ }
312
+ export interface RowEnding {
313
+ day: bigint;
314
+ reason: EndReason;
315
+ }
316
+ export interface StandingLeaf {
317
+ disputes: bigint;
318
+ live: bigint;
319
+ root: Principal;
320
+ endedKillSwitch: bigint;
321
+ version: bigint;
322
+ endedReset: bigint;
323
+ firstEngagementDay: bigint;
324
+ lastDisputeDay: [] | [bigint];
325
+ endedResigned: bigint;
326
+ endedGuardianRevoked: bigint;
327
+ endedOwnerRevoked: bigint;
328
+ }
329
+ export interface TreesSpanned {
330
+ one: bigint;
331
+ two: bigint;
332
+ threeToFour: bigint;
333
+ fivePlus: bigint;
334
+ }
335
+ export type UpdatePreference = { ManualOnly: null } | { AutoUpdate: null };
336
+ export interface _SERVICE {
337
+ /**
338
+ * / Abandon the caller's engram. Owner-initiated, terminal, and NOT deletion: the record stays,
339
+ * / stays readable, stays verifiable. It frees the owner to create ONE new engram, which will
340
+ * / permanently and publicly name this one as its predecessor.
341
+ * /
342
+ * / This is deliberately NOT a recovery path. Nothing moves: content stays in the old canister,
343
+ * / and it cannot move — the vetKD memory key and the tECDSA/EVM address both derive from the
344
+ * / CANISTER ID, so an engram's ciphertext and wallet cannot follow it anywhere (engramx
345
+ * / docs/ENCRYPTION-OPERATIONS.md, "Canister-id binding"). Abandoning is starting over, not
346
+ * / migrating, and the caller must be told so before they do it.
347
+ */
348
+ abandonMyEngram: ActorMethod<[], Result>;
349
+ addAuditViewer: ActorMethod<[Principal], Result>;
350
+ /**
351
+ * / Add a changelog entry (admin only)
352
+ */
353
+ addChangelogEntry: ActorMethod<[string, string], Result>;
354
+ /**
355
+ * / ⟨2026-09-28, owner decision: an agent may append a dated dispute to its own record; nothing is
356
+ * / deleted; nobody adjudicates⟩ The CALLER appends one dated statement to ITS OWN track record.
357
+ * /
358
+ * / AGENT-ONLY BY CONSTRUCTION: the record written is `msg.caller`'s, so nobody can write to
359
+ * / another agent's record, and a caller with no recorded engagement is REFUSED (there is no record
360
+ * / of theirs to dispute). Bounded: `Engagements.MAX_DISPUTE_BYTES` per statement,
361
+ * / `Engagements.MAX_DISPUTES_PER_AGENT` per agent, both refused loudly with nothing stored.
362
+ * /
363
+ * / It edits nothing and deletes nothing — not an engagement, not an earlier statement — and
364
+ * / nobody at the registry reads, answers, or rules on it. The certified summary then shows that a
365
+ * / dispute exists and its day; the statement's words stay out of it, readable by the agent through
366
+ * / `listMyAgentDisputes`. The append is recorded in the chained audit log (length, not words).
367
+ * /
368
+ * / ⟨2026-09-28⟩ The caller is the agent's ROOT key — records are kept per agent (owner decision:
369
+ * / one root key per agent, with an enrolled key per client), so a key for one engram has no record
370
+ * / here and is refused like any stranger. Disputing is the root's act, not an engram key's.
371
+ */
372
+ appendAgentDispute: ActorMethod<[string], Result>;
373
+ /**
374
+ * / Take sole custody of your engram: the registry REMOVES ITSELF as a controller, leaving the
375
+ * / owner and the engram canister itself, and your update preference becomes `#ManualOnly`.
376
+ * /
377
+ * / Refuses unless you own an engram and it is `#Active`.
378
+ * /
379
+ * / ⚠ WHAT YOU GIVE UP, none of which is obvious from the name — the registry loses controllership,
380
+ * / so from here on:
381
+ * / · `upgradeMyEngram` and `upgradeEngram` can no longer install code on it. You upgrade it
382
+ * / yourself, including security fixes.
383
+ * / · `canister_status` is no longer readable by the registry, so your engram drops out of
384
+ * / `fleetCycleHealth` — it is counted in `sovereignSkipped` rather than watched.
385
+ * /
386
+ * / ⚠ IT IS REVERSIBLE, which is the fact most worth publishing: the engram's own
387
+ * / `returnToManaged` adds the registry back ALONGSIDE the owner. Sovereignty is a door, not a
388
+ * / cliff.
389
+ */
390
+ claimSovereignty: ActorMethod<[], Result>;
391
+ /**
392
+ * / Assemble the pushed chunks into the engram template and install it as the current one.
393
+ * /
394
+ * / ⚠ THE HASH IS VERIFIED, exactly as in `setEngramWasm`: the assembled bytes are hashed and
395
+ * / compared, so this also catches a chunk that arrived out of order or was dropped — the
396
+ * / reassembly is checked, not just the transport. A mismatch refuses and nothing is installed.
397
+ * /
398
+ * / Refuses `#Conflict("No chunks uploaded")` when nothing was pushed, rather than installing an
399
+ * / empty template.
400
+ * /
401
+ * / Contrast `backupFinalize`, which does NOT verify its digest. These two look like the same
402
+ * / protocol and are not, and the difference matters most on this one: it decides what code every
403
+ * / engram in the fleet will run.
404
+ */
405
+ engramWasmFinalize: ActorMethod<[string], Result_4>;
406
+ /**
407
+ * / Chunked WASM upload — for WASMs that exceed single-call ingress limits.
408
+ * / Flow: engramWasmInit → engramWasmPushChunk (×N) → engramWasmFinalize(hash)
409
+ * / ⟨0.16.0 — CORRECTED 2026-08-19⟩ This line named all THREE pre-rename methods, none of which
410
+ * / this canister serves. It is the only written record of the upload protocol, and the one
411
+ * / hand-IDL client that follows it (engramx scripts/upload-engram-wasm.mjs) had drifted the same
412
+ * / way — the upload died at its first call with IC0536. A stale flow comment is not cosmetic here.
413
+ */
414
+ engramWasmInit: ActorMethod<[], Result>;
415
+ /**
416
+ * / Push one chunk of a chunked template upload started by `engramWasmInit`. Returns the running
417
+ * / chunk count. WASM-admin only, like every step of this family.
418
+ * /
419
+ * / Two `#Conflict` ceilings, and BOTH tell you to restart rather than to retry: too many chunks,
420
+ * / and total bytes over the cap. Neither is recoverable by pushing again — call `engramWasmInit`
421
+ * / to clear and begin afresh. The messages say so because a caller who retries a chunk against a
422
+ * / full buffer will retry forever.
423
+ */
424
+ engramWasmPushChunk: ActorMethod<[Uint8Array | number[]], Result_4>;
425
+ /**
426
+ * / Fleet cycle-health scan. Admin or metrics-viewer, PAGINATED with a hard max of 50 per page —
427
+ * / each managed engram costs one `canister_status` await, so pages stay small to bound the
428
+ * / message. A caller must expect to iterate.
429
+ * /
430
+ * / ⚠ IT DOES NOT SEE THE WHOLE FLEET, and reading it as though it does is the error to avoid.
431
+ * / SOVEREIGN engrams are skipped: the registry is not their controller, so `canister_status`
432
+ * / would trap. They are counted in `sovereignSkipped` rather than silently omitted — an
433
+ * / unwatchable engram and a healthy one must not look the same. A trap for any other reason
434
+ * / lands in `unreadable`, never fatal to the scan.
435
+ * /
436
+ * / COUNTS ONLY — no per-engram data leaves the canister.
437
+ * /
438
+ * / `thresholdCycles = 0` means the default 1T. An engram ALSO counts as `lowCycles` below twice
439
+ * / its LIVE freezing reservation, which is memory-scaled, so a large engram can be low at a
440
+ * / balance a small one would be comfortable at.
441
+ * /
442
+ * / ⟨L7⟩ UPDATE, NOT QUERY, for BOTH of the reasons this law recognises — which is why it is worth
443
+ * / stating rather than leaving to be inferred from a body that scans a fleet. It awaits
444
+ * / `ic.canister_status` once per engram in the page, so no query could produce the figures at all;
445
+ * / and it calls `denied()` on refusal, a state write a query cannot perform, so making it a query
446
+ * / would also retire the record of who was turned away (ADR-0002's class, the same one
447
+ * / `listEngrams` settled at its own declaration).
448
+ * /
449
+ * / ⟨Marker added 2026-08-23. The census row for this method already cited L7 and the site did not
450
+ * / carry it — invisible to `check-update-for-read.mjs`, whose population is read-VERB names and
451
+ * / this one is a noun. That gate now reads the census's L7 citations as a second population for
452
+ * / exactly this blind spot.⟩
453
+ */
454
+ fleetCycleHealth: ActorMethod<[bigint, bigint, bigint], Result_21>;
455
+ /**
456
+ * / PUBLIC: ⟨0.16.0, L2.4⟩ `fleetSummary` — the ONE public projection of this registry,
457
+ * / replacing `status()` and ABSORBING `publicMetrics()`.
458
+ * /
459
+ * / Two public aggregates of one canister was the same L2.4 defect the engram had with
460
+ * / status()/metrics(): both answered "what is this registry like", they disagreed about which
461
+ * / counters they carried (status had cycles and the wasm hash, publicMetrics had unique owners,
462
+ * / sovereign engrams and registrySince), and a caller had to know which one held the field they
463
+ * / wanted. storage-sweep.sh cross-checked one against the other to infer a fact neither stated.
464
+ * /
465
+ * / Merged, not concatenated: `totalEngrams` and `activeEngrams` appeared in BOTH and are counted
466
+ * / once here, by the same pass.
467
+ * /
468
+ * / Everything in it identifies nobody — counts, a version, a hash, a cycle balance — which is why
469
+ * / it is ungated and marked for the L8 gate-grep.
470
+ */
471
+ fleetSummary: ActorMethod<[], FleetSummary>;
472
+ /**
473
+ * / When an engram was abandoned, if it was.
474
+ * /
475
+ * / PUBLIC: a stranger evaluating an engram must be able to see that its owner walked away from
476
+ * / it. Free, like getRentStatus — and for the same reason, since abandonment is the fact that
477
+ * / makes a lapsed rent term mean something different.
478
+ */
479
+ getAbandonedAtNs: ActorMethod<[Principal], [] | [bigint]>;
480
+ /**
481
+ * / Acquisition -> activation funnel (deferred provisioning). reserved = signed up but the canister
482
+ * / hasn't spawned yet (each holds a live cycle earmark); provisioned = active engrams (activated);
483
+ * / reservedStale = past the TTL and pending prune (lapsed earmarks, released on the next reserve/
484
+ * / provision) — the "stuck between acquisition and activation" cohort.
485
+ * / Conversion = provisioned / (reserved + provisioned). (Query: can't prune here, so stale may be > 0.)
486
+ * /
487
+ * / PUBLIC: anonymous fleet observability — four counts, no principal and no per-engram row. The
488
+ * / fleet's size and how much of it is stuck are claims this project makes about itself, and a
489
+ * / claim nobody can check is not one.
490
+ */
491
+ getActivationFunnel: ActorMethod<[], ActivationFunnel>;
492
+ /**
493
+ * / PUBLIC: who holds authority here. A refusal a caller cannot attribute is a refusal they
494
+ * / cannot appeal, so the roster is readable by everyone the roster can refuse.
495
+ * /
496
+ * / ⟨0.16.0, L2.2⟩ The shared `AdminRoles` shape, not a registry-specific record. It was
497
+ * / `{ admin; wasmAdmin }`, which could not express the ROOT identity at all — the principal that
498
+ * / bootstraps the first admin and can reclaim the role was simply absent from the published
499
+ * / answer, so "who can take this canister back" was a fact only the source told you. A list of
500
+ * / (role, holder) carries every authority this canister has, and the same shape on every canister
501
+ * / means one reader answers the question fleet-wide.
502
+ */
503
+ getAdminRoles: ActorMethod<[], AdminRoles>;
504
+ /**
505
+ * / PUBLIC: ⟨0.16.0⟩ RENAMED from `operatorEdgeStats`, and the GATE IS DROPPED.
506
+ * /
507
+ * / The old refusal existed because a ZEROED aggregate reads as "no agent runs any engram" — a
508
+ * / growth figure the status console renders as fact — and an unreadable aggregate must show as
509
+ * / UNKNOWN, never as zero. That reasoning was right about the danger and wrong about the remedy:
510
+ * / two counts that name nobody are not a disclosure, and gating them created the very
511
+ * / unreadable-aggregate problem the comment was worried about. Answering everyone removes both
512
+ * / the fabricated zero AND the need to distinguish it from a real one.
513
+ * /
514
+ * / ⟨2026-09-28⟩ `distinctOperators` now counts distinct AGENTS (roots), not pairings: the index is
515
+ * / keyed by root since the owner chose one root key per agent with an enrolled key per client, so
516
+ * / an agent on twenty-five engrams counts once. The field keeps its name because a hand-written
517
+ * / reader of it exists outside this repo; the meaning is the one that name always claimed.
518
+ * / ⟨CORRECTED 2026-09-29: one name for one thing — both fields are renamed.⟩
519
+ * / · `agents` (was `distinctOperators`): agents, by root key, with at least one live
520
+ * / engagement. The old name was kept for a hand-written reader outside this repo, which has
521
+ * / since had to translate it back to "agents" in two places.
522
+ * / · `liveEngagements` (was `totalEdges`): agent–engram engagements live NOW, one per agent
523
+ * / per engram. "Total" read as all-time, but an engagement leaves this count the moment it
524
+ * / ends (the live index forgets it; the agent's record keeps it — `getAgentStanding`), and
525
+ * / "edge" is a word for the index, not for anything a reader counts.
526
+ * / Both come from the live index (`agentToEngrams`), which `sysRecordAgentEdge` updates in
527
+ * / the same step as the agent's record.
528
+ */
529
+ getAgentEdgeStats: ActorMethod<[], { agents: bigint; liveEngagements: bigint }>;
530
+ /**
531
+ * / ⟨2026-09-28, owner decisions: build the full agent track record; an agent may read its own
532
+ * / record⟩ ONE AGENT'S CERTIFIED TRACK RECORD — counts by ending reason, live engagements, the
533
+ * / day of the first engagement, and whether (and on what day) the agent disputed it.
534
+ * /
535
+ * / SERVES THE STORED BYTES, on the `getCertifiedAnchor` pattern: the leaf is decoded from exactly
536
+ * / what `putStandingLeaf` certified, so what is served equals what is proven by construction.
537
+ * / Verify as for `getCertifiedAnchor`, path ["standing", <agent principal>].
538
+ * /
539
+ * / OPEN, with one refusal. A proof a caller must hold a role to fetch rests on the authority it
540
+ * / was meant to replace, so there is no role gate. But a record spanning fewer than
541
+ * / `Engagements.STANDING_MIN_DISTINCT_ENGRAMS` distinct engrams is served ONLY to the agent itself:
542
+ * / at one or two engrams the summary IS their owners' experience, which is identification by
543
+ * / inference (R-6 §4.5 L4-1 — our own reading of the law, not legal advice). That refusal is
544
+ * / LOUD, names this method, and covers a record of zero engagements with the same words, so the
545
+ * / refusal itself does not distinguish "never engaged" from "engaged in one to four engrams".
546
+ * / The agent itself reading an empty record gets `#NotFound` — it is asking about itself, so
547
+ * / there is nothing to hide from it.
548
+ * /
549
+ * / ⟨2026-09-28, review finding OPS-2⟩ A THIN SELF-READ IS UNCERTIFIED: below the threshold the
550
+ * / record is not in the certified tree at all (its hash would leak it through every other
551
+ * / witness), so the agent is served its summary folded from the records, with `certificate =
552
+ * / null` and an empty `witness`. At and above the threshold every reader gets the stored bytes
553
+ * / with their proof.
554
+ * /
555
+ * / ⟨2026-10-01, the owner's ruling of that day (2 A)⟩ The threshold counts TOPS, not engrams
556
+ * / (`Engagements.distinctTops`): engrams opened from one engram count as one, and an engram counts
557
+ * / as one with the engrams it replaced. The refusal's sentence says so.
558
+ * /
559
+ * / WHAT IT NEVER DOES: score, rank, recommend, or refuse anyone work. The decision to refuse an
560
+ * / agent belongs to the owner's own engram (L4-3).
561
+ * /
562
+ * / ⟨2026-09-28, owner decision: one root key per agent, with an enrolled key per client⟩ `agent`
563
+ * / is the agent's ROOT principal — the identity an owner sees as `root` on each key in their own
564
+ * / `listAgents`. "The agent itself" is a caller holding that root key. A key for one engram has no
565
+ * / record of its own here; asking about one gets the refusal or, from the key itself, `#NotFound`.
566
+ */
567
+ getAgentStanding: ActorMethod<[Principal], Result_20>;
568
+ /**
569
+ * / Fleet-wide anchoring liveness. Exists because ABSENCE is otherwise uninformative: an engram
570
+ * / that never wrote, one still on a template without the push, and one whose push is broken all
571
+ * / render as an empty getAnchors. Nothing distinguishes the first from the third, so a silently
572
+ * / dead push looks exactly like a quiet engram (consumer finding, 2026-08-03).
573
+ * /
574
+ * / A non-zero engramsAnchored with a recent lastAnchorAtNs is positive evidence that the path is
575
+ * / alive SOMEWHERE, which is what makes an individual engram's silence interpretable.
576
+ * /
577
+ * / PUBLIC: the same reader who needs the anchors needs to know whether to believe their absence.
578
+ */
579
+ getAnchorStats: ActorMethod<[], AnchorStats>;
580
+ /**
581
+ * / PUBLIC: the chain head exists to be compared, so an external monitor can detect tampering
582
+ * / without admin access (D3). A head only whoever runs this deployment can read witnesses nothing.
583
+ */
584
+ getAuditHead: ActorMethod<[], AuditHead>;
585
+ /**
586
+ * / An engram's LATEST witnessed anchor, certified. The leaf (Types.CertifiedAnchorLeaf, Candid —
587
+ * / decode with to_candid's standard encoding) carries the anchor AND its conflict count, so the
588
+ * / certified answer is self-contained: a verifier learns "this head, at this count, disputed or
589
+ * / not" from subnet-signed data alone. null = no anchor witnessed (which getAnchorStats makes
590
+ * / interpretable). Same verification recipe as getCertifiedHead, path ["anchors", <principal>].
591
+ * /
592
+ * / PUBLIC: a proof a caller must be authorized to fetch rests on the authority it was meant to
593
+ * / replace. The certificate's whole value is that a stranger can check it against the NNS root
594
+ * / key without trusting this registry, and a stranger is exactly who cannot hold a role here.
595
+ */
596
+ getCertifiedAnchor: ActorMethod<[Principal], [] | [CertifiedAnchor]>;
597
+ /**
598
+ * / ⟨0.16.0, L2.2⟩ RENAMED BACK to `getCertifiedAuditHead`, the fleet-wide name.
599
+ * /
600
+ * / It was renamed AWAY from that on 2026-08-03, deliberately and for a good reason: the semantics
601
+ * / changed (certified_data became a merkle tree root covering the head AND the anchor set, so the
602
+ * / old certified_data == headHash byte-compare was gone), and a loud method-not-found was judged
603
+ * / better than a stale verifier quietly reporting verified=false forever.
604
+ * /
605
+ * / THAT ARGUMENT'S PREMISE IS NOW FALSE. It protected deployed verifiers; B4 restarts every chain
606
+ * / at the 0.16.0 genesis and there are none. What remains is a fleet where three canisters answer
607
+ * / the same question under two names, which is the cost the rename was paying to avoid a hazard
608
+ * / that no longer exists. `witness` becomes `?Blob` to match the shared type — null where a
609
+ * / canister has no witness to give, rather than an empty blob that decodes as one.
610
+ * /
611
+ * / Verify: (1) validate `certificate` against the IC root key; (2) extract
612
+ * / /canister/<id>/certified_data from it; (3) reconstruct the root of `witness` (IC-spec hash
613
+ * / tree, CBOR); (4) require equality; (5) lookup_path(["audit_head"]) in the witness == headHash.
614
+ * / total = cumulative entries the chained head covers (monotonic across trims); size = ring
615
+ * / occupancy (display). Anchoring keys on `total` so it stays correct once the ring trims.
616
+ * /
617
+ * / PUBLIC: the certificate is the point. It lets a stranger verify against the NNS root key
618
+ * / instead of trusting this replica, and a proof that requires a role to fetch rests on exactly
619
+ * / the authority it was introduced to remove.
620
+ */
621
+ getCertifiedAuditHead: ActorMethod<[], CertifiedAuditHead>;
622
+ /**
623
+ * / The ATTESTABLE custody-mode fact (agent-native model item 19): "managed" | "sovereign" |
624
+ * / "unknown", derived from the registry's OWN transition records (claimSovereignty /
625
+ * / returnToManaged maintain sovereignEngramIds) — NOT from a canister_status probe, which needs
626
+ * / controllership and traps for exactly the sovereign engrams it should describe. Public, free,
627
+ * / forever (free-parity): custody mode is a trust fact every warranted proof will carry —
628
+ * / managed = engramx.ai can warrant its own controller conduct; sovereign = registry-side facts
629
+ * / only. (Money-origin, by contrast, is payer-blind and never a record fact.)
630
+ * / PUBLIC: ⟨0.16.0, L3⟩ Custody as a VARIANT, and the no-claim case says WHY rather than just
631
+ * / being a third string.
632
+ * /
633
+ * / ⟨CORRECTED 2026-08-24⟩ This block named an `#Unknown` ARM. There is none, and never was in
634
+ * / the shipped type: `CustodyMode` is `#Managed | #Sovereign`, and the no-claim case answers
635
+ * / `#Err(#NotFound)` — see the `case null` below. The argument the block makes is unchanged and
636
+ * / correct; the mechanism it named was not the one implemented, and a reader trusting it would
637
+ * / have written a match arm that does not compile.
638
+ * /
639
+ * / "unknown" as text sat beside "sovereign" and "managed" as if it were a third custody mode. It
640
+ * / is not a mode at all — it is this registry declining to make a claim about a canister it did
641
+ * / not spawn, which is a different kind of answer and now looks like one.
642
+ * /
643
+ * / Derived from records, never probed: a canister_status call would trap for exactly the
644
+ * / canisters we are not the controller of, i.e. the sovereign ones this read exists to identify.
645
+ */
646
+ getCustodyMode: ActorMethod<[Principal], Result_5>;
647
+ /**
648
+ * / Cycles-health probe — slim, cycles-only shape; the money fields moved to the cashier's
649
+ * / getCycleReserveStatus. Read by health-alert.mjs in registry mode.
650
+ * /
651
+ * / PUBLIC: whether this registry can still afford to spawn is a fact every prospective owner
652
+ * / depends on and none of them can otherwise observe. Cycles only, by construction — ADR-0005
653
+ * / keeps the money on the cashier, so there is no balance here to withhold.
654
+ */
655
+ getCycleReserveStatus: ActorMethod<
656
+ [],
657
+ { cycleBalance: bigint; minOperatingReserveCycles: bigint }
658
+ >;
659
+ /**
660
+ * / The engram's permanent spawn ordinal — public: a number is a provable, non-sensitive
661
+ * / fact (its assignment is in the hash-chained audit log). null = spawned before numbering
662
+ * / existed, or by another registry.
663
+ * /
664
+ * / ⟨0.16.0, tier-2 ruling 2026-08-17⟩ RENAMED from `sysEngramNumberOf`. The `sys` prefix names
665
+ * / the CALLER TIER a method was built for, and this one was never gated — it is a bare public
666
+ * / query, as the line above says. A prefix that reads as an authorization claim while carrying
667
+ * / none is worse than no prefix: it invites a reader to assume a gate that does not exist, and
668
+ * / an L8 sweep grepping `sys*` would have counted this as tiered surface it is not.
669
+ * /
670
+ * / PUBLIC: the spawn ordinal is a permanent, dual-recorded fact about an engram that its owner
671
+ * / publishes by existing — engram #7 is #7 to everyone. It identifies a canister, never a person.
672
+ * /
673
+ * / ⟨RENAMED 2026-10-02 from `getEngramNumber`⟩ The engram serves `getEngramNumber()` with no
674
+ * / argument; this registry served the same name taking the engram's id. One name with two arities
675
+ * / on two canisters is what a hand-written client cannot tell apart (a structural actor type
676
+ * / matches either target), so it gets the rule `getMarketCanister` already follows: distinct
677
+ * / names on distinct canisters. `…Of` says what the argument is for.
678
+ */
679
+ getEngramNumberOf: ActorMethod<[Principal], [] | [bigint]>;
680
+ /**
681
+ * / ⟨2026-10-01, the owner's ruling of that day (2 A)⟩ What a lasting key opened through a parent:
682
+ * / the engram, the engram it was opened from, and the cut-off's time if there is one. GATED:
683
+ * / readable by the key itself (an agent asking about its own), the owner of the engram it was
684
+ * / opened from (whose console shows it), an admin or an audit viewer. Anyone else is REFUSED — the
685
+ * / same words whether or not the key opened anything, so the refusal tells a stranger nothing.
686
+ * / `#NotFound` only to a permitted reader, for a key that opened nothing: a true answer, like
687
+ * / `listMyAgentDisputes`'s empty list.
688
+ */
689
+ getEngramOpenedThrough: ActorMethod<[Principal], Result_19>;
690
+ /**
691
+ * / The i-th 1 MiB slice of the attested engram wasm (0-indexed). The caller reassembles all
692
+ * / chunks in order and MUST verify sha256(reassembled) == getEngramWasmHash before installing —
693
+ * / the hash is the attestation; the bytes are a convenience. Out-of-range index → empty blob.
694
+ * / PUBLIC: ⟨0.16.0, L11⟩ An out-of-range index is `#NotFound`, not an empty blob.
695
+ * /
696
+ * / The empty blob was indistinguishable from a legitimately empty chunk AND from "no wasm is
697
+ * / staged at all" — and this is the surface a verifier walks to reassemble a template and check
698
+ * / its hash. A reassembler that reads `""` as end-of-stream cannot tell a finished download from
699
+ * / a truncated one, so it would hash a partial wasm and report a mismatch against the official
700
+ * / list, which reads as "this template is unofficial" rather than "I stopped reading too early".
701
+ */
702
+ getEngramWasmChunk: ActorMethod<[bigint], Result_18>;
703
+ /**
704
+ * / The stored template's release class — FREE and UNGATED, because it is the checkable half of a
705
+ * / promise made to people who are not our customers. Pair it with getRentStatus to verify the
706
+ * / funding rule was applied: #Security funds every reachable engram; #Feature funds metered ones.
707
+ * / PUBLIC: ⟨0.16.0, L3⟩ The release class as a VARIANT, not a stringly-typed enum.
708
+ * /
709
+ * / It returned "security" or "feature" as `Text`, so every consumer compared strings and no
710
+ * / compiler anywhere could catch a typo or a third class arriving. A variant makes the set closed
711
+ * / and the comparison checked — and this value gates whether an upgrade is funded, so a caller
712
+ * / that silently fails to match "security" funds nothing and reports no error.
713
+ */
714
+ getEngramWasmClass: ActorMethod<[], ReleaseClass>;
715
+ /**
716
+ * / The WASM hash of the engram template this registry will install.
717
+ * /
718
+ * / PUBLIC: it is the thing `isOfficialEngramWasm` is checked AGAINST. An owner about to accept a
719
+ * / spawn, and a stranger auditing one that already happened, both need to read what this registry
720
+ * / says it installs — from the registry, not from a release note that could describe a different
721
+ * / build.
722
+ */
723
+ getEngramWasmHash: ActorMethod<[], string>;
724
+ /**
725
+ * / Total attested-wasm size + chunk size, so a puller knows how many getEngramWasmChunk calls to make.
726
+ * /
727
+ * / PUBLIC: the pull path it describes is itself ungated, so a gate here would only stop a caller
728
+ * / planning the calls it is already permitted to make.
729
+ */
730
+ getEngramWasmInfo: ActorMethod<[], EngramWasmInfo>;
731
+ /**
732
+ * / ⟨L7⟩ UPDATE, NOT QUERY, and NOT because of a system call — this body awaits nothing. The
733
+ * / update exists so the DENIAL IS RECORDED: `denied()` is a state write, and a query cannot
734
+ * / write. Exactly the argument `listEngrams` settled at its own declaration, and the same
735
+ * / conclusion: a query here would silently retire the record of who was refused.
736
+ * /
737
+ * / So L7's "the denial-logging motive for update-for-read is retired by L1" does not hold at this
738
+ * / site. L1 retired the motive of trapping-to-refuse; it did not give queries the ability to
739
+ * / write, which is the thing this actually depends on.
740
+ */
741
+ getFleetMetrics: ActorMethod<[], Result_17>;
742
+ /**
743
+ * / PUBLIC: a genesis note that only privileged callers could read would defeat its purpose —
744
+ * / the reader who most needs it is the stranger deciding whether to trust this chain.
745
+ */
746
+ getGenesisNote: ActorMethod<[], GenesisNote>;
747
+ /**
748
+ * / What the last upgrade attempt actually DID — the additive answer to "was I upgraded, or was
749
+ * / I already current?". A #Skipped result variant would have broken every deployed decoder and
750
+ * / scored a skip as a FAILURE in our own tooling (which greps the result for "Ok"). PUBLIC:
751
+ * / an owner deciding whether their engram got a security fix should not need a key to find out.
752
+ * / ⚠ `at` is the time of the last upgrade CALL, including a no-op on an already-current engram.
753
+ * / It does NOT date the engram's running code — a re-run fleet push marches `at` forward while
754
+ * / nothing changes. Use `changedAt` for that: it is stamped only when code actually changed, and
755
+ * / is null for an engram that has never been upgraded since this field shipped.
756
+ * / ⟨0.16.0, L4⟩ `atNs`/`changedAtNs`: ns timestamps carry the suffix. The honesty split the two
757
+ * / fields encode is unchanged — `atNs` is when THIS registry last upgraded the canister, and
758
+ * / `changedAtNs` is when its module hash last moved by any hand, so a self-upgrade or a manual
759
+ * / install shows up as a change with no matching upgrade. Renaming here is free: the record is
760
+ * / built at the query boundary, and the stable state it reads keeps its own spelling.
761
+ * / ⟨CORRECTED 2026-10-02: "by any hand … shows up" is false, and so is "stamped only when code
762
+ * / actually changed" above, read without its limit.⟩ `changedAtNs` is written at exactly two
763
+ * / places, both right after THIS registry's own successful install on the engram
764
+ * / (`upgradeMyEngram` and `upgradeEngram`), in the same step that writes `atNs` with
765
+ * / `upgraded = true`. Nothing here reads the engram's module hash from the network to stamp it. So
766
+ * / an install made by any other controller — a manual upgrade or reinstall — or a sovereign
767
+ * / engram's own upgrade does NOT move it, and does NOT show up in this read: it dates only the code
768
+ * / changes this registry made. To see a change made by another hand, read the engram's module hash
769
+ * / from the network directly (it is public in the canister's state) and check it with
770
+ * / `isOfficialEngramWasm`; nothing in this canister makes that comparison for you.
771
+ */
772
+ getLastUpgrade: ActorMethod<[Principal], [] | [LastUpgrade]>;
773
+ /**
774
+ * / Named `getMarketCanister`, NOT `getMarketId` — the engram already serves `getMarketId`, and a
775
+ * / hand-written client IDL that names the same method on two canisters goes undetected by both
776
+ * / drift gates (a structural actor type matches either target). Distinct names on distinct
777
+ * / canisters is the cheap prevention.
778
+ * /
779
+ * / PUBLIC: every newborn engram is told this id at spawn and gates its signing on it, so a
780
+ * / counterparty deciding whether to trust an engram's market claim needs to read the same value
781
+ * / the engram was given. `null` says the pointer is unset, which is a fact worth serving too —
782
+ * / it is why `requireMarket` refuses fleet-wide.
783
+ */
784
+ getMarketCanister: ActorMethod<[], [] | [Principal]>;
785
+ /**
786
+ * / The metrics reader every newborn engram is told — `null` until wired.
787
+ * /
788
+ * / PUBLIC: an owner deciding whether to turn their engram's reader off needs to know who it is,
789
+ * / and a stranger comparing an engram's own copy (which it serves) against the registry's needs
790
+ * / the same value. A canister id is not a secret; a pointer that will not name its holder cannot
791
+ * / be checked by anyone.
792
+ */
793
+ getMetricsReader: ActorMethod<[], [] | [Principal]>;
794
+ /**
795
+ * / The caller's engram record, or `null` if they have none. One engram per owner, so this is a
796
+ * / single record rather than a page.
797
+ */
798
+ getMyEngram: ActorMethod<[], [] | [EngramRecordView]>;
799
+ /**
800
+ * / The caller's reservation, if they signed up but have not yet provisioned — no canister has
801
+ * / spawned yet. `null` once `provisionEngram` succeeds, or if they never reserved.
802
+ * /
803
+ * / This is also how the half-finished state is made visible: reserve and provision are two calls,
804
+ * / so a caller can hold a reservation with no engram. Seeing it here is what tells them to retry
805
+ * / provision rather than reserve again.
806
+ */
807
+ getMyReservation: ActorMethod<[], [] | [ReservationView]>;
808
+ /**
809
+ * / ⟨2026-10-02⟩ How many engrams were ever opened, and how — lifetime sizes of state this registry
810
+ * / never deletes, plus the two counters of refused openings (`Funnel.OpeningCounts` names each).
811
+ * / All O(1); a reader differences them day to day.
812
+ * /
813
+ * / CALLERS: the admin and the audit viewers (the deployment's metrics reader holds a viewer
814
+ * / grant). Anyone else is REFUSED by name with their class — never a zero, which would read as
815
+ * / "nothing was ever opened". A QUERY: counts only, and cheap; its refusal is not in the denial
816
+ * / log (`denied` says why a query cannot write).
817
+ */
818
+ getOpeningCounts: ActorMethod<[], Result_16>;
819
+ /**
820
+ * / ⟨2026-10-02⟩ How far agents reach across owners — COUNTS ONLY: agents by how many trees their
821
+ * / record spans (1 · 2 · 3–4 · 5+), engrams opened through a parent that are live or cut off, and
822
+ * / engrams that paired an agent within seven days of opening. No principal, no row, no per-agent
823
+ * / figure (`Funnel.referralCounts` says what is folded and how `exclude` drops a tree).
824
+ * /
825
+ * / `exclude`: the deployment's own accounts (owner keys, agent roots, engram ids), at most 1,000.
826
+ * / An agent on it is dropped whole; an engagement whose engram's tree touches it is dropped.
827
+ * /
828
+ * / CALLERS: the admin and the audit viewers (the deployment's metrics reader holds a viewer grant),
829
+ * / weekly. Anyone else is REFUSED by name with their class — never zeros. A QUERY: its cost grows
830
+ * / with the agents and engrams it walks (one pass over each), so a reader asks weekly, not hourly.
831
+ */
832
+ getReferralCounts: ActorMethod<[Array<Principal>], Result_15>;
833
+ /**
834
+ * / PUBLIC: the biller identity is an operational fact a
835
+ * / payer's cashier must verify BEFORE taking money (paying into a cashier the registry does not
836
+ * / recognize as biller would collect funds it cannot convert into credit).
837
+ */
838
+ getRentBiller: ActorMethod<[], [] | [Principal]>;
839
+ /**
840
+ * / Public transparency: is the gate armed, and which identity issuers are exempt?
841
+ * / ⟨0.16.0 — census row⟩ Gains `inviteIssuer`, closing the R5 setter-without-a-getter gap
842
+ * / without adding a method: `setInviteIssuer` could write a principal that nothing could read
843
+ * / back, so "who may mint reserve invites" was unanswerable from outside.
844
+ * /
845
+ * / It serves the EFFECTIVE issuer, not the raw `?Principal`. The stored value is null until
846
+ * / someone sets it and `effectiveInviteIssuer()` then falls back to the admin — which is what
847
+ * / `mintReserveInvites` actually gates on. Publishing the option would make every caller
848
+ * / re-implement that fallback to learn the same fact, and a caller that forgot would read `null`
849
+ * / as "nobody may mint", which is the opposite of the truth.
850
+ * /
851
+ * / PUBLIC: whether the reserve gate is armed, and who may mint into it, decides whether a
852
+ * / stranger can sign up at all. An admission rule only the admitted can read is not a rule a
853
+ * / prospective user can act on.
854
+ */
855
+ getReserveGateConfig: ActorMethod<[], ReserveGateConfig>;
856
+ /**
857
+ * / PUBLIC: transparency without a doxx — how many principals the deployment currently screens (0 = seam
858
+ * / disarmed, the shipped default). The list itself is never enumerable publicly.
859
+ */
860
+ getScreeningDenylistSize: ActorMethod<[], bigint>;
861
+ /**
862
+ * / How many engram template releases this registry has declared `#Security` — the class that
863
+ * / skips any delay and is funded for every engram. ⟨2026-09-28, owner decision: publish the
864
+ * / count only; the status page shows the number.⟩ It moves only when a template's class is
865
+ * / declared INTO `#Security` (`setEngramWasmClass`), never on `#Feature`, never down.
866
+ * / ⟨CORRECTED 2026-09-29⟩ It counts TEMPLATES, not declarations: each distinct template (by its
867
+ * / module hash) counts once, the first time it is declared `#Security`. Re-declaring it — which
868
+ * / the deploy does after every upload — or flipping it to `#Feature` and back changes nothing.
869
+ * / Before this, one security release deployed five times counted five.
870
+ * /
871
+ * / PUBLIC: it is the checkable half of an unenforced policy — the owner published the intention
872
+ * / and the feature/security rule without code holding us to it, so the one thing a stranger can
873
+ * / watch is how often we reach for the class that skips the wait. A gated count would be a check
874
+ * / on us that only we can read.
875
+ */
876
+ getSecurityReleaseCount: ActorMethod<[], bigint>;
877
+ /**
878
+ * / ⟨2026-10-01, the owner's ruling of that day (2 A)⟩ Cumulative cycles accepted at the sponsored
879
+ * / door (`sysSpawnChild`) and forwarded into engrams opened through another — cycles the calling
880
+ * / engrams attached, never the registry's own. Kept apart from the grant counter
881
+ * / (`totalCyclesAllocated`) so neither figure is inflated by the other.
882
+ * /
883
+ * / PUBLIC: for the reason `getUpgradeSubsidyTotal` is — a number we would not publish is one we
884
+ * / should not forward. It names no engram and no owner. (⟨2026-10-01⟩ The marker was first written
885
+ * / "PUBLIC," and a rationale check that looks for the colon form read the method as unexplained.)
886
+ */
887
+ getSponsoredCyclesTotal: ActorMethod<[], bigint>;
888
+ /**
889
+ * / Which canister's money chain this registry anchors — `null` until wired, the honest unwired
890
+ * / answer rather than a placeholder principal.
891
+ * /
892
+ * / PUBLIC: an anchor is a claim that one chain witnesses another, and a claim that will not name
893
+ * / its counterparty cannot be checked by anyone. A canister id is not a secret; withholding it
894
+ * / would leave a verifier holding an anchor with nothing to anchor it to.
895
+ */
896
+ getTreasuryAnchorId: ActorMethod<[], [] | [Principal]>;
897
+ /**
898
+ * / ⟨0.16.0, L11⟩ `null` MEANS NEVER CHOSE. It used to mean `#AutoUpdate`.
899
+ * /
900
+ * / Returning the shipped default for a caller who has never expressed a preference is a
901
+ * / fabricated CONSENT: the registry answered "this owner opted into automatic upgrades" when the
902
+ * / truth was "this owner has never been asked". The two are different facts and only one of them
903
+ * / is a decision, and `listUpdatePrefs`' own refusal message names the same hazard from the other
904
+ * / end — a successor seeded from those defaults silently reverses every explicit ManualOnly choice.
905
+ * /
906
+ * / The default still APPLIES; it is just no longer REPORTED as a choice. A caller that wants
907
+ * / effective behaviour reads null as "AutoUpdate applies", and one that wants to know whether the
908
+ * / owner ever decided can finally tell.
909
+ */
910
+ getUpdatePreference: ActorMethod<[], [] | [UpdatePreference]>;
911
+ /**
912
+ * / Which template an engram has already been FUNDED for, if any — the anti-drain ledger made
913
+ * / observable. Admin/viewer-gated: it reveals which engrams needed help, which is a nudge toward
914
+ * / who is underfunded.
915
+ * /
916
+ * / This exists because the ledger was the least-verifiable thing in the release. It is the guard
917
+ * / that bounds funding to one deposit per engram per release even when install_code never
918
+ * / succeeds — closing the interleave drain where an owner burns the deposit between our
919
+ * / canister_status read and the install, leaving the module hash unchanged and the engram
920
+ * / eligible again. Its upgrade-coverage was "acknowledged", not "tested", precisely because
921
+ * / nothing could observe it. Untestable state guarding against a farming attack is the wrong
922
+ * / thing to leave unobserved.
923
+ * / ⟨0.16.0⟩ The refusal becomes a value, and the envelope finally separates the two nulls.
924
+ * /
925
+ * / `?Text` had to carry both "you may not ask" and "this engram was never funded for an upgrade"
926
+ * / — and the second is a claim about OUR OWN SPEND, not an absence of one. `#Ok(null)` now means
927
+ * / never funded, `#Err(#Refused)` means not your business, and no caller has to guess which.
928
+ */
929
+ getUpgradeFundedTemplate: ActorMethod<[Principal], Result_14>;
930
+ /**
931
+ * / Cumulative cycles the registry has granted to engrams for upgrades — the meter on the
932
+ * / never-gated pull path.
933
+ * /
934
+ * / PUBLIC: a subsidy we would not publish is one we should not spend. Free and ungated.
935
+ */
936
+ getUpgradeSubsidyTotal: ActorMethod<[], bigint>;
937
+ /**
938
+ * / Current upgrade top-up floor. PUBLIC: it is a spending policy, and an owner deciding whether
939
+ * / they must fund an upgrade themselves deserves to know whether the registry will.
940
+ */
941
+ getUpgradeTopUpFloor: ActorMethod<[], bigint>;
942
+ /**
943
+ * / PUBLIC: a wallet calls this BEFORE its user has any standing with this registry — that is what
944
+ * / the call is for — so gating it would make the message unreadable at exactly the moment it is
945
+ * / needed, and a signature given without it is a signature given blind.
946
+ */
947
+ icrc21_canister_call_consent_message: ActorMethod<
948
+ [
949
+ {
950
+ arg: Uint8Array | number[];
951
+ method: string;
952
+ user_preferences: {};
953
+ },
954
+ ],
955
+ | {
956
+ Ok: {
957
+ metadata: {
958
+ utc_offset_minutes: [] | [number];
959
+ language: string;
960
+ };
961
+ consent_message: { GenericDisplayMessage: string };
962
+ };
963
+ }
964
+ | { Err: { description: string; error_code: bigint } }
965
+ >;
966
+ /**
967
+ * / Is `hash` (lowercase hex sha256) a build we published? The trust question for an engram whose
968
+ * / code we cannot verify by controllership — a sovereign engram, or a managed one whose owner is
969
+ * / a co-controller and could have installed their own wasm.
970
+ * /
971
+ * / PUBLIC: this IS the verification surface, and a verification surface nobody can call verifies
972
+ * / nothing. Free and ungated for the same reason.
973
+ */
974
+ isOfficialEngramWasm: ActorMethod<[string], boolean>;
975
+ /**
976
+ * / "Is this a registered, `#Active` engram?" — no owner argument, only a Bool. The cashier uses
977
+ * / it to authorize an engram funding its OWN cycles: on that path the caller IS the engram
978
+ * / canister, and this confirms it is a live one.
979
+ * /
980
+ * / PUBLIC: it returns strictly less than `verifyEngramActiveOwner` above and leaks no
981
+ * / canisterId→owner mapping — the very leak that kept the deleted `getEngramByCanisterId` gated.
982
+ * / Gating a strictly narrower answer than the ungated one beside it would protect nothing.
983
+ */
984
+ isRegisteredActiveEngram: ActorMethod<[Principal], boolean>;
985
+ /**
986
+ * / ⟨0.16.0⟩ RENAMED from `engramsOperatedBy`; the refusal becomes a value.
987
+ * /
988
+ * / `[]` here is a SUBSTANTIVE CLAIM ABOUT ANOTHER PRINCIPAL'S FLEET — "this agent works
989
+ * / nowhere" — handed to a caller who merely lacks a grant. Readable by an admin, an audit viewer,
990
+ * / or that agent about itself.
991
+ */
992
+ listAgentEngrams: ActorMethod<[Principal], Result_12>;
993
+ /**
994
+ * / Contradictory submissions, oldest-first. Empty is the normal case. Never trimmed.
995
+ * /
996
+ * / ⟨0.16.0, L3 — PAGED 2026-08-18⟩ This returned the WHOLE list, and the list is one an engram
997
+ * / can grow itself: `sysAnchorHead` appends a conflict on every contradictory submission and the
998
+ * / policy is deliberately never-evict. An uncapped reply over a caller-growable list eventually
999
+ * / exceeds the response limit, and an oversized Candid reply does not truncate — it fails to
1000
+ * / decode ENTIRELY, so the conflicts become unreadable. That is the exact opposite of what a
1001
+ * / never-evicted evidence store is for: the record survives and nobody can read it.
1002
+ * /
1003
+ * / `total` is the full conflict count, so a caller holding one page still learns how many exist.
1004
+ * /
1005
+ * / ⚠ THE WRITE SIDE IS STILL UNBOUNDED and is NOT fixed here. An engram can append to its own
1006
+ * / conflict list without limit, which is unbounded state on a shared canister paid for by the
1007
+ * / registry's cycles. Capping it means deciding what to drop from an evidence store whose whole
1008
+ * / promise is that nothing is dropped — a retention ruling, not a shape fix. Recorded in the
1009
+ * / backlog rather than decided here.
1010
+ * / ⟨CORRECTED 2026-09-28 — the paragraph above is no longer true: the owner ruled "never delete;
1011
+ * / cap submissions; publish the refusal count". Nothing is dropped; the WRITE side is now bounded
1012
+ * / at `AnchorConflicts.MAX_CONFLICTS_PER_ENGRAM` rows per engram, `sysAnchorHead` refuses further
1013
+ * / contradictions by name, and the refusals are counted in the certified leaf's
1014
+ * / `refusedConflicts`. So this list never exceeds that bound and the page clamp below is ample.⟩
1015
+ * /
1016
+ * / PUBLIC: a conflict is the anchoring system reporting against ITSELF, and a self-report only
1017
+ * / its author can read is worth nothing. Withholding it would let this registry publish anchors
1018
+ * / while keeping the evidence that two of them disagree — the fabricate-on-refusal shape aimed at
1019
+ * / the one reader the anchors exist for.
1020
+ */
1021
+ listAnchorConflicts: ActorMethod<[Principal, bigint, bigint], Result_13>;
1022
+ /**
1023
+ * / Witnessed heads for an engram, oldest-first. PUBLIC and ungated — an anchor a stranger cannot
1024
+ * / read is not an anchor (ratified: "recorded where counterparties can check them").
1025
+ * /
1026
+ * / Each entry carries `disputed`, derived at read time from the conflict log. Without it a
1027
+ * / counterparty reading ONLY this query would take an already-contradicted head at face value —
1028
+ * / on a same-total/different-head conflict the registry keeps both, the original here and the
1029
+ * / contradiction in getAnchorConflicts. Requiring readers to fetch two queries and join them
1030
+ * / correctly is a requirement most will not meet; carrying the flag inline removes the trap.
1031
+ * / (Consumer finding, a consumer 2026-08-03.)
1032
+ * /
1033
+ * / ⚠ This query is NOT certified. The certificate slot is held by the registry's own audit head,
1034
+ * / and a canister has exactly one. A reader therefore trusts the replica or boundary node that
1035
+ * / answered — anchoring is verifiable by a COOPERATIVE counterparty, not an adversarial one.
1036
+ * / ⟨CORRECTED 2026-10-02: the reason is out of date and the conclusion is too broad.⟩ Since
1037
+ * / 2026-08-03 the registry's one certified value is the root of a labelled tree (the "Certified
1038
+ * / state tree" note near the top of this file), not the bare audit head, and the tree carries each
1039
+ * / engram's LATEST anchor. So the latest checkpoint IS checkable by a sceptical outsider:
1040
+ * / `getCertifiedAnchor` serves it with a certificate and a witness, checked against the network's
1041
+ * / root key without trusting the replica that answered. What stays true is narrower: THIS query,
1042
+ * / the full history oldest-first, is not certified, because only the latest anchor per engram is in
1043
+ * / the tree. For an earlier checkpoint a reader trusts the answering replica, or kept the
1044
+ * / certified latest anchor when it was the latest.
1045
+ * /
1046
+ * / PUBLIC: an anchor is evidence offered to a third party, so the third party has to be able to
1047
+ * / read it. Gating this would leave the registry asserting that an engram's history is witnessed
1048
+ * / while refusing to show the witness — which is the shape of claim nobody should accept.
1049
+ * /
1050
+ * / ⟨2026-10-02, decided: KEPT a bare list⟩ Every other list on these canisters is a `Page`; this
1051
+ * / one stays `[AnchorView]`, and the choice is recorded so the free moment does not pass silently.
1052
+ * / It is bounded by construction — at most `MAX_ANCHORS` (64) per engram, log-spaced with the
1053
+ * / genesis anchor pinned — and nothing is withheld from anyone (it is public), so a page's `total`
1054
+ * / would equal the length and its `withheld` would always be 0: two fields that could never say
1055
+ * / anything. A cap that is part of the ratified design is not the unbounded growth paging exists
1056
+ * / for. WHAT WOULD MAKE THIS WRONG: the ring outgrowing one reply, or rows withheld per caller —
1057
+ * / then it pages, and that is a break.
1058
+ */
1059
+ listAnchors: ActorMethod<[Principal], Array<AnchorView>>;
1060
+ /**
1061
+ * / ⟨0.16.0, L1 + L2.1⟩ RENAMED from `readAuditLog`, and the refusal is now a VALUE.
1062
+ * /
1063
+ * / It refused by `Debug.trap`, which was the right call when the alternative was a fabricated
1064
+ * / answer — and the fabrication it replaced is worth restating, because it is the sharpest one
1065
+ * / this canister ever produced. The old empty answer carried `headHash = Text.encodeUtf8("")`,
1066
+ * / the GENESIS SENTINEL. An unauthorized caller was not told "empty log"; they were told THIS
1067
+ * / REGISTRY HAS NEVER BEEN WRITTEN TO, in a tamper-evident chain, which is exactly the false
1068
+ * / answer an external verifier would act on.
1069
+ * /
1070
+ * / A trap is still not the right shape. It is not a value: it reaches a Motoko caller as an
1071
+ * / uncatchable failure and a JS caller as a rejection, so a client that wants to say "you may not
1072
+ * / read this, here is what you CAN read" has to parse a rejection string to do it. `#Err(#Refused)`
1073
+ * / carries the same sentence as data. The message survives verbatim — it earned its wording.
1074
+ * /
1075
+ * / The inline record dies for a `Page`: `earliestId` was redundant with the ring's own paging,
1076
+ * / and `size`/`headHash` are `getAuditHead`'s answer, published free to everyone.
1077
+ */
1078
+ listAuditEntries: ActorMethod<[bigint, bigint], Result_10>;
1079
+ /**
1080
+ * / ⟨0.16.0, L2.2⟩ RENAMED from `getMetricsViewers` — the viewer triple unifies on "audit viewer"
1081
+ * / across every canister, because a roster that grants audit reads should not be named after
1082
+ * / metrics on one canister and audit on another.
1083
+ * /
1084
+ * / MEASURED FABRICATION, kept in the record: this arm answered `(vec {})` to an ANONYMOUS caller
1085
+ * / on 2026-08-11 while viewers existed. An empty vec reads as "nobody holds read-only ops access"
1086
+ * / when the truth was "you are not on the roster".
1087
+ */
1088
+ listAuditViewers: ActorMethod<[], Result_12>;
1089
+ /**
1090
+ * / PUBLIC: the release history, paged.
1091
+ * /
1092
+ * / ⟨0.16.0, L2.1⟩ A `Page`, not a bare vec. The old shape returned the WHOLE changelog on every
1093
+ * / call with no way to say it had been truncated — and a list that cannot say "there is more"
1094
+ * / looks complete at exactly the size where it stops being so.
1095
+ */
1096
+ listChangelogEntries: ActorMethod<[bigint, bigint], Page_4>;
1097
+ /**
1098
+ * / ⟨2026-10-01, the owner's ruling of that day (2 A)⟩ The engrams opened through `engram`, each with
1099
+ * / its owner now and its cut-off time if any. GATED: readable by `engram`'s owner, an admin or an
1100
+ * / audit viewer. The link says who works for whom, so it is not public (unlike the predecessor
1101
+ * / chain, which exists to be walked by a stranger).
1102
+ * /
1103
+ * / REFUSED LOUDLY, never `[]`, to anyone else (ADR-0002): an empty list handed to a caller who
1104
+ * / merely lacks a grant would be a claim about another owner's engram. A QUERY, so the refusal is
1105
+ * / not in the denial log (`denied` says why a query cannot write).
1106
+ * /
1107
+ * / Built by a pass over `parentOf` rather than from a second, reverse index: one stored fact and a
1108
+ * / pass bounded by the number of opened engrams, instead of two facts to keep in step.
1109
+ */
1110
+ listChildrenOf: ActorMethod<[Principal], Result_11>;
1111
+ /**
1112
+ * / ⟨0.16.0⟩ RENAMED from `readDenialLog`; the refusal becomes a value.
1113
+ * /
1114
+ * / An empty denial log reads as NOBODY HAS EVER BEEN REFUSED HERE, which is the opposite of what
1115
+ * / this log exists to record — so "empty" was never an acceptable answer to a caller who simply
1116
+ * / lacks the grant. The old trap said so; this says the same thing as data.
1117
+ */
1118
+ listDenialEntries: ActorMethod<[bigint, bigint], Result_10>;
1119
+ /**
1120
+ * / ⟨2026-10-02⟩ ONE ROW PER ENGAGEMENT, for the deployment's own count of use — DAYS, never
1121
+ * / instants, and NEVER the agent's root, enrolled key or invite.
1122
+ * /
1123
+ * / CALLERS: the admin, and the audit viewers — the reader the deployment runs for this purpose
1124
+ * / holds a viewer grant and calls this once a day. Anyone else is REFUSED by name, with their
1125
+ * / class (the anonymous principal, an engram, the template key, an engram's owner, a stranger) —
1126
+ * / never an empty page, which is what a lost history would also look like (ADR-0002).
1127
+ * /
1128
+ * / ARGUMENTS. `sinceDay`: any instant, floored to its UTC day; a row is served when the LATEST of
1129
+ * / its start, its ending, its engram's abandonment and any reset since it started is on or after
1130
+ * / that day — so an ending recorded after a reader's cursor passed the start is served on the
1131
+ * / reader's next pass. `start`: the first position in the recording order to serve from; ask
1132
+ * / again from the last row's `seq` + 1. `limit`: rows wanted, at most 1,000; a page shorter than
1133
+ * / the limit is the last. `companyList`: the deployment's own accounts (owner keys, agent roots,
1134
+ * / engram ids), at most 1,000, folded into each row's yes/no `…OnList` answers and into
1135
+ * / `firstOutsideOnTree`; pass `[]` and every engagement is outside by that list ("includes us").
1136
+ * /
1137
+ * / The reply is a page whose `total` is every engagement ever recorded here. Each row's fields
1138
+ * / are described at `Funnel.EngagementRow`; the walk and its cost at `Funnel.engagementRows`.
1139
+ * /
1140
+ * / ⟨L7⟩ An UPDATE, not a query, so a refused attempt is RECORDED: `denied()` is a state write,
1141
+ * / and a query cannot write (the reason `listEngrams` is an update). This is the most sensitive of
1142
+ * / the three reads — one row per engagement — so who was turned away is worth a record.
1143
+ */
1144
+ listEngagementRows: ActorMethod<[bigint, bigint, bigint, Array<Principal>], Result_9>;
1145
+ /**
1146
+ * / ⟨0.16.0, L1⟩ THE `authorized : ?Bool` FLAG IS GONE, AND SO IS EVERYTHING BUILT AROUND IT.
1147
+ * /
1148
+ * / The flag was the most carefully-argued compromise in this canister, and every word of that
1149
+ * / argument rested on ONE premise: this method could not refuse. It is an UPDATE because
1150
+ * / `denied()` is a state write, and a trap would roll that write back — trading a false answer
1151
+ * / for a missing audit trail, which is strictly worse. So the refusal had to be smuggled out
1152
+ * / beside a fabricated `{ []; 0 }` payload, and readers had to be taught to check a sibling flag
1153
+ * / before believing a total.
1154
+ * /
1155
+ * / THE ENVELOPE REMOVES THE PREMISE. `#Err(#Refused)` is a VALUE, not a trap: the message
1156
+ * / commits, the denial is recorded, and the caller gets a refusal they cannot mistake for data.
1157
+ * / There is no masked payload left to check a flag against.
1158
+ * /
1159
+ * / What goes with it: the three-state ?true/?false/null protocol and the "a reader that collapses
1160
+ * / null to false fabricates a refusal" rule; the ADR-0018 opt-from-birth ceremony this field was
1161
+ * / the motivating example for; the fleet collector's dual-IDL split; and the cross-check against
1162
+ * / the public engram count that storage-sweep.sh used as a discriminator. All of it was
1163
+ * / scaffolding around "we cannot say no in a value", and none of it is load-bearing once we can.
1164
+ * /
1165
+ * / ⚠ CENSUS CONFLICT, resolved at the site. The census says "query again" — reasoning that the
1166
+ * / only thing forcing an update was the masking problem. It was not: the update exists so the
1167
+ * / DENIAL IS RECORDED, and a query cannot write, so making this a query would silently retire
1168
+ * / the audit trail the original comment correctly called worth more than the false answer.
1169
+ * / Staying an update keeps both — the legible refusal AND the record of who was refused — which
1170
+ * / is strictly better than either option the old design had to choose between.
1171
+ * /
1172
+ * / ⟨L7⟩ MARKER ADDED 2026-08-23, reasoning above UNCHANGED. This method's reason was already
1173
+ * / written and correct; what it lacked was the glyph a gate can see. Its case is the one L7's
1174
+ * / own text had to be corrected for — the forcing call is not a system API at all, it is
1175
+ * / `denied()`, a state write. See `scripts/check-update-for-read.mjs`.
1176
+ */
1177
+ listEngrams: ActorMethod<[bigint, bigint], Result_8>;
1178
+ /**
1179
+ * / ⟨2026-09-28, owner decision: an agent may read its own record⟩ The caller's OWN dispute
1180
+ * / statements, oldest first, verbatim. Keyed by `msg.caller`, so it cannot be pointed at anyone
1181
+ * / else, and an empty list is a true answer about the caller ("you have appended none"), never a
1182
+ * / refusal in disguise. Without this read the statements would be a record nobody can read —
1183
+ * / the failure mode `listAnchorConflicts`' declaration names.
1184
+ */
1185
+ listMyAgentDisputes: ActorMethod<[], Array<Dispute>>;
1186
+ /**
1187
+ * / Every hash we have published, current first. Union of the explicit list and the changelog's
1188
+ * / per-release stamps, so it is complete across the boundary where the list was introduced.
1189
+ * /
1190
+ * / PUBLIC: the same standing as `isOfficialEngramWasm` above — that one answers about a hash you
1191
+ * / already hold, this one is how you get the set to compare against.
1192
+ */
1193
+ listOfficialEngramWasmHashes: ActorMethod<[], Array<string>>;
1194
+ /**
1195
+ * / The FULL predecessor lineage, newest-first: [immediate predecessor, its predecessor, …].
1196
+ * / Empty for an engram that replaced nothing.
1197
+ * /
1198
+ * / Why transitive (owner-ratified 2026-08-02, "free to leave, impossible to hide"): abandonment
1199
+ * / is deliberately NOT rate-limited — friction punishes the owner who mis-provisions on day one,
1200
+ * / and it does not stop anyone determined. What protects the record is that walking away stays
1201
+ * / VISIBLE. Single-hop visibility does not: A→B→C is three cheap steps, and a stranger checking C
1202
+ * / would see only B and read it as a near-clean record. Reputation laundering with extra hops is
1203
+ * / still laundering, and it would be our own feature performing it. The chain is the answer, not
1204
+ * / the cooldown.
1205
+ * /
1206
+ * / TERMINATION: `seen` is not defensive dressing. predecessorOf is written once per engram at
1207
+ * / creation and never rewritten, so a cycle should be impossible — but this is a QUERY over
1208
+ * / stable state that a future migration or a restore could corrupt, and an unbounded loop in a
1209
+ * / query does not return an error, it burns the instruction limit and the caller gets nothing
1210
+ * / back. Terminating on a repeat is strictly better than trapping, and the length cap bounds the
1211
+ * / walk even if `seen` itself were somehow defeated.
1212
+ * /
1213
+ * / PUBLIC: this is the anti-laundering chain — it says an engram is a successor rather than a
1214
+ * / fresh start. Abandon-and-respawn is reputation laundering performed by our own feature unless
1215
+ * / a stranger can walk this, so the walk has to be answerable to a stranger.
1216
+ * /
1217
+ * / ⟨2026-09-30, the owner's answer 3 of that date⟩ An engram can now name more than one
1218
+ * / predecessor (`ControlHandover.predecessorsFor`), so the lineage is walked a generation at a time:
1219
+ * / every engram this one named, then every engram those named, each listed once. A chain with one
1220
+ * / predecessor per engram reads exactly as before. The walk is `ControlHandover.lineage`, where
1221
+ * / test/control-handover.test.mo runs it; the `seen` rule and the length cap above still hold.
1222
+ */
1223
+ listPredecessors: ActorMethod<[Principal], Array<Principal>>;
1224
+ /**
1225
+ * / ⟨0.16.0⟩ The refusal becomes a value.
1226
+ * /
1227
+ * / `total = 0` reads as "nobody has expressed a preference", and a successor registry seeded from
1228
+ * / that zero applies the SHIPPED DEFAULT — silently reversing every explicit ManualOnly consent
1229
+ * / choice in the fleet. That is the consequence this refusal exists to prevent, and it belongs in
1230
+ * / the message a caller actually receives.
1231
+ */
1232
+ listUpdatePrefs: ActorMethod<[bigint, bigint], Result_7>;
1233
+ /**
1234
+ * / Mint one-shot reserve invites (invite-issuer or admin). Codes use IC-secure randomness over a
1235
+ * / 32-symbol no-ambiguous alphabet (8 chars ≈ 40 bits). Batch-capped; table hard-capped.
1236
+ */
1237
+ mintReserveInvites: ActorMethod<[bigint], Result_6>;
1238
+ /**
1239
+ * / The PROBED twin of `getCustodyMode`: same question, asked of the IC rather than of our
1240
+ * / records — hence the `probe` verb rather than a second noun.
1241
+ * /
1242
+ * / ⟨0.16.0, L2.2⟩ RENAMED from `getSovereigntyStatus`. Both methods answer the same type,
1243
+ * / `Result<CustodyMode>`, so the old name introduced a SovereigntyStatus noun that no type in
1244
+ * / this interface has. A reader comparing the two names had to open both to learn they were
1245
+ * / one question; comparing the stems now shows it. What differs is HOW the answer is obtained
1246
+ * / (a live canister_status probe vs the transition records), which is exactly what a verb is
1247
+ * / for. The arity difference is real and stays: `getCustodyMode` answers about any canister,
1248
+ * / this one about the CALLER's own engram.
1249
+ * /
1250
+ * / ⟨0.16.0, L7⟩ Both arms of the old `#Unknown` move into the envelope, and they are DIFFERENT
1251
+ * / facts that the single tag was flattening: "you have no engram here" is #NotFound, while
1252
+ * / "canister_status did not answer" is #Unavailable and means retry.
1253
+ * /
1254
+ * / The distinction matters more than usual because of what a wrong answer would say.
1255
+ * / canister_status traps for reasons unrelated to controllership — an engram stopped, frozen out
1256
+ * / of cycles, or a transient subnet error — so a trap does NOT prove the registry was removed as
1257
+ * / a controller. Reporting #Sovereign there would tell an owner they had achieved sovereignty
1258
+ * / they do not have.
1259
+ * /
1260
+ * / ⟨L7⟩ UPDATE, NOT QUERY, and this is the system call that forces it: the body awaits
1261
+ * / `ic.canister_status`. That is also the whole point of the `probe` verb — the value is read
1262
+ * / LIVE from the management canister rather than from a stored transition record, so a query
1263
+ * / could not produce it at all, not merely more cheaply.
1264
+ */
1265
+ probeCustodyMode: ActorMethod<[], Result_5>;
1266
+ /**
1267
+ * / ⟨0.16.0⟩ `Ok` CARRIES THE PRINCIPAL DIRECTLY, not a one-field record wrapping it.
1268
+ * /
1269
+ * / `#Ok : { canisterId : Principal }` was a record with nothing to disambiguate: the success of a
1270
+ * / spawn IS the canister it produced. The wrapper cost every caller a field access to reach the
1271
+ * / only thing the call returns, and invited a second field later that would belong somewhere else.
1272
+ */
1273
+ provisionEngram: ActorMethod<
1274
+ [
1275
+ | []
1276
+ | [
1277
+ {
1278
+ walletProof: [] | [Uint8Array | number[]];
1279
+ invite: [] | [string];
1280
+ genesis: [] | [{ content: Uint8Array | number[]; path: string }];
1281
+ },
1282
+ ],
1283
+ ],
1284
+ Result_1
1285
+ >;
1286
+ removeAuditViewer: ActorMethod<[Principal], Result>;
1287
+ /**
1288
+ * / ⟨0.16.0⟩ `Ok` IS THE ANSWER — the `{ reserved : Bool }` wrapper is gone.
1289
+ * /
1290
+ * / It could only ever hold `true`: the false case was already an `#Err`. A boolean whose only
1291
+ * / legal value is true is a field that teaches callers to branch on something that never varies,
1292
+ * / and the one that eventually forgets reads a refusal as a reservation.
1293
+ */
1294
+ reserveEngram: ActorMethod<
1295
+ [
1296
+ | []
1297
+ | [
1298
+ {
1299
+ walletProof: [] | [Uint8Array | number[]];
1300
+ invite: [] | [string];
1301
+ },
1302
+ ],
1303
+ ],
1304
+ Result
1305
+ >;
1306
+ /**
1307
+ * / Return to managed: user calls AFTER adding registry back as controller
1308
+ */
1309
+ returnToManaged: ActorMethod<[], Result>;
1310
+ /**
1311
+ * / Set admin. Authority decision lives in the pure Auth module (unit-tested); this func keeps only
1312
+ * / the async controller check + applies the outcome. When admin is null: REGISTRY_ROOT or a canister
1313
+ * / controller can set it. When admin is set: the current admin hands off, or REGISTRY_ROOT reclaims.
1314
+ */
1315
+ setAdmin: ActorMethod<[Principal], Result>;
1316
+ /**
1317
+ * / Upload the engram template in ONE message — the single-shot path. For a wasm too large to
1318
+ * / fit, use `engramWasmInit` → `engramWasmPushChunk` × N → `engramWasmFinalize` instead; the two
1319
+ * / paths produce the same result and differ only in how the bytes arrive.
1320
+ * /
1321
+ * / ⚠ THE HASH IS VERIFIED HERE, unlike the one on `backupInit`. This computes sha256 over the
1322
+ * / bytes it received and refuses `#Invalid` when it does not match, naming both values. So
1323
+ * / passing a hash is a CHECK, not a label — the opposite of the backup protocol, whose digest is
1324
+ * / stored unverified. Two chunked-upload families with the same shape and opposite semantics is
1325
+ * / exactly the pair a reader generalises across wrongly, and the dangerous direction is assuming
1326
+ * / the backup is verified.
1327
+ * /
1328
+ * / Gated on the WASM ADMIN — a distinct role from admin and root, because deciding what code the
1329
+ * / whole fleet runs is a different authority from operating the registry.
1330
+ */
1331
+ setEngramWasm: ActorMethod<[Uint8Array | number[], string], Result>;
1332
+ /**
1333
+ * / Declare the stored template's release class. Wasm-admin only, and deliberately a SEPARATE call
1334
+ * / from the upload: "this is a security release" is a promise to every owner who is not paying
1335
+ * / us, so it should be its own decision with its own audit line — not a parameter someone passes
1336
+ * / by habit. Always re-declare AFTER uploading; an upload resets this to #Feature.
1337
+ */
1338
+ setEngramWasmClass: ActorMethod<[ReleaseClass], Result>;
1339
+ /**
1340
+ * / Record the predecessor's final head beside this chain's genesis. Admin-only, and ONE-SHOT.
1341
+ * /
1342
+ * / One-shot because a note that can be rewritten is not evidence. Once a predecessor head is
1343
+ * / published, changing it would let the same surface that admits a wipe also revise the account
1344
+ * / of it — which is exactly the power this note exists to deny.
1345
+ * /
1346
+ * / ⟨RENAMED 2026-10-02 from `sysSetGenesisPredecessor` — the sys-tier retier ruled 2026-08-24,
1347
+ * / executed under the owner's 2026-10-02 ruling to carry out the pre-cut interface decisions as
1348
+ * / recommended.⟩ CALLER: this registry's admin, a person, once, after the predecessor chain's
1349
+ * / final head is captured. The `sys` prefix names the canister-to-canister tier, so an
1350
+ * / admin-called method inside it made "a `sys` method has no human caller" false and therefore
1351
+ * / uncheckable. The audit label moves with the name (`setGenesisPredecessor.denied`).
1352
+ */
1353
+ setGenesisPredecessor: ActorMethod<[Uint8Array | number[], bigint], Result>;
1354
+ /**
1355
+ * / Set (or clear) the least-privilege invite-mint key (admin-only). Mirrors setWasmAdmin.
1356
+ */
1357
+ setInviteIssuer: ActorMethod<[[] | [Principal]], Result>;
1358
+ setMarketCanister: ActorMethod<[[] | [Principal]], Result>;
1359
+ /**
1360
+ * / Designate the metrics reader (admin only; `null` clears it, and newborns are then told none).
1361
+ * / Canister principals only — see the variable's declaration.
1362
+ */
1363
+ setMetricsReader: ActorMethod<[[] | [Principal]], Result>;
1364
+ /**
1365
+ * / Admin-tunable liveness floor for the registry's own cycles (a frozen registry = the whole
1366
+ * / managed fleet stops). Monitored externally via getCycleReserveStatus.
1367
+ */
1368
+ setMinCycleReserve: ActorMethod<[bigint], Result>;
1369
+ setRentBiller: ActorMethod<[[] | [Principal]], Result>;
1370
+ /**
1371
+ * / Flip the reserve gate (admin-only; audit-logged). OFF restores fully-open admission.
1372
+ */
1373
+ setReserveGateEnabled: ActorMethod<[boolean], Result>;
1374
+ setScreeningDenylist: ActorMethod<[Array<Principal>], Result>;
1375
+ setSpawnCeiling: ActorMethod<[bigint], Result_4>;
1376
+ setTreasuryAnchorId: ActorMethod<[[] | [Principal]], Result>;
1377
+ /**
1378
+ * / Set the wallet-canister allowlist for the wallet-rooted exemption (admin-only). Small by
1379
+ * / design — these are identity ISSUERS (e.g. Internet Identity), not users.
1380
+ */
1381
+ setTrustedSigners: ActorMethod<[Array<Principal>], Result>;
1382
+ /**
1383
+ * / Choose whether this registry may upgrade your engram automatically. The caller must own an
1384
+ * / ACTIVE engram; `#ManualOnly` means only `upgradeMyEngram` moves it, never `upgradeEngram`.
1385
+ * /
1386
+ * / `claimSovereignty` sets this to `#ManualOnly` as a side effect, since a registry that is no
1387
+ * / longer a controller could not honour anything else.
1388
+ */
1389
+ setUpdatePreference: ActorMethod<[UpdatePreference], Result>;
1390
+ /**
1391
+ * / Set the spawn-ceiling dial (admin-only, audit-logged, bounded 1..SPAWN_CEILING_MAX).
1392
+ * / The effective daily cap stays min(ceiling, reserve-derived) — raising the dial never
1393
+ * / bypasses the funding constraint.
1394
+ * / Admin dial for the upgrade top-up floor (0 = disabled). Capped at one endowment so the dial
1395
+ * / itself cannot turn upgrades into an unbounded drain.
1396
+ */
1397
+ setUpgradeTopUpFloor: ActorMethod<[bigint], Result_4>;
1398
+ /**
1399
+ * / Root admin assigns the role key (pass null to clear, reverting the role to the root admin).
1400
+ */
1401
+ setWasmAdmin: ActorMethod<[[] | [Principal]], Result>;
1402
+ /**
1403
+ * / An engram witnesses its own audit head. Callable ONLY by a registered engram — msg.caller is
1404
+ * / the engram principal, so attribution needs no signature and cannot be forged by a third party.
1405
+ * /
1406
+ * / The registry witnesses; it does not adjudicate. The anchoring design carries the write table.
1407
+ * / Two shapes are permanently recorded as conflicts rather than rejected, because a rejection
1408
+ * / leaves no evidence and evidence is the entire product here:
1409
+ * / - same total, different head → the log was rewritten under a fixed count
1410
+ * / - total went backwards → an append-only count cannot decrease honestly
1411
+ * / ⟨2026-09-28, owner decision: never delete; cap submissions; publish the refusal count⟩ One
1412
+ * / exception to "recorded rather than rejected": an engram that already has
1413
+ * / `AnchorConflicts.MAX_CONFLICTS_PER_ENGRAM` recorded contradictions gets `#Conflict` naming
1414
+ * / this method, and the refusal is COUNTED in its certified leaf (`refusedConflicts`). The
1415
+ * / evidence is not lost — the recorded ones stay forever and the count certifies that more came.
1416
+ * / ⟨0.16.0, L2.3⟩ RENAMED from `anchorHead` — a mutation wearing a noun, and a `sys` edge that
1417
+ * / did not say so. It is called canister-to-canister (an engram or the market pushing its head
1418
+ * / here), never by a person, and the `sys` prefix is what tells a reader scanning the surface
1419
+ * / which methods are machine-to-machine.
1420
+ * /
1421
+ * / The market already calls it under this name, which is how the rename got its urgency: a
1422
+ * / hand-written client IDL type-checks against a method that does not exist.
1423
+ */
1424
+ sysAnchorHead: ActorMethod<[Uint8Array | number[], bigint], Result>;
1425
+ /**
1426
+ * / ⟨2026-09-29, review PR-3: a half-finished change of owner⟩ A SOVEREIGN engram reports that it
1427
+ * / moved control of its code to `newOwner`, which ends the change of owner this registry marked
1428
+ * / when it moved its record (`ControlHandover.Pending`). Until this arrives the registry refuses the
1429
+ * / new owner's control-changing calls on the engram, because it cannot see a sovereign engram's
1430
+ * / controllers. Gated like `sysNotifyOwnershipTransfer` (the caller must BE the engram), and only for
1431
+ * / an engram recorded as sovereign: a managed engram's transfer ends inside
1432
+ * / `sysNotifyOwnershipTransfer`, where the registry moves control itself. `#Ok` when nothing is
1433
+ * / marked (a report sent again), refused by name when the mark is for another owner.
1434
+ * /
1435
+ * / TRUST: the same as `sysNotifyOwnershipTransfer` and `sysRevertOwnershipTransfer` — the
1436
+ * / registry takes a sovereign engram's word, and whoever controls a sovereign engram's code can
1437
+ * / make it say anything. WHAT WOULD MAKE THIS WRONG: an engram that reports control moved before
1438
+ * / it has moved it (the engram's `transferOwnership` calls this only after its own
1439
+ * / `update_settings` succeeded).
1440
+ */
1441
+ sysConfirmOwnershipTransfer: ActorMethod<[Principal], Result>;
1442
+ /**
1443
+ * / Single-engram metering probe — the push-on-payment sizing read. Biller-gated and
1444
+ * / cycles-attached like the sweep; same row semantics, one engram, no heartbeat stamp
1445
+ * / (a single probe is not a completed round).
1446
+ */
1447
+ sysMeterEngram: ActorMethod<[Principal], Result_3>;
1448
+ /**
1449
+ * / ⟨2026-10-02⟩ An engram tells this registry that its owner reset it.
1450
+ * /
1451
+ * / CALLER: a registered engram, about itself — `canisterToOwner` membership, checked first
1452
+ * / (ADR-0020: every `sys` method names and checks its caller). The engram sends it from its reset
1453
+ * / path. WHY IT EXISTS: a reset purges the engram's roster, and the endings it sends reach this
1454
+ * / registry only for agents still active, so a reset after the last agent left reached this
1455
+ * / registry nowhere and the engram read as kept.
1456
+ * /
1457
+ * / Records the DAY the registry heard it (`resetDaysOf`), once per day: a second notice the same
1458
+ * / day answers `#Ok` and writes nothing. Past `Funnel.MAX_RESET_DAYS_PER_ENGRAM` days it is refused
1459
+ * / `#Conflict` and nothing is stored. Served only on `listEngagementRows`' rows, to the admin and
1460
+ * / audit viewers — never public.
1461
+ */
1462
+ sysNoteReset: ActorMethod<[], Result>;
1463
+ /**
1464
+ * / ⟨0.16.0, RULED 2026-08-17, BUILT 2026-08-20⟩ The engram tells this registry that custody moved.
1465
+ * /
1466
+ * / WHAT IT FIXES: `transferOwnership` lives on the ENGRAM and, until this method existed, told the
1467
+ * / registry nothing. `verifyEngramActiveOwner` — the cashier's money gate — therefore kept
1468
+ * / answering for the ORIGINAL owner, so a new owner could not fund the engram they now own. The
1469
+ * / old comment above `verifyEngramActiveOwner` called that gap "by design"; it was not, and the
1470
+ * / 2026-08-17 ruling shipped this seam to close it inside the free-break window.
1471
+ * /
1472
+ * / THE TELLER PROBLEM IS ALREADY SOLVED NEXT DOOR, and this deliberately copies the answer rather
1473
+ * / than inventing one: `sysNotifyOperatorEdge` below takes `msg.caller` and looks it up in
1474
+ * / `canisterToOwner`. ⟨2026-09-28: that method is now `sysRecordAgentEdge`; the identity
1475
+ * / check is the same.⟩ The caller IS the engram whose ownership changed, and this registry spawned
1476
+ * / it, so no claim in the argument needs trusting — the identity is the authority.
1477
+ * /
1478
+ * / ⟨2026-09-28: the mirror is now `sysRecordAgentEdge`, and the difference below is gone — the
1479
+ * / silent `()` notify was deleted and its successor answers `Wire.Result` too, for this reason.⟩
1480
+ * / ONE DELIBERATE DIFFERENCE FROM THAT MIRROR: it returns `Wire.Result`, where the agent-edge
1481
+ * / notify returns `()` and silently ignores strangers. The engram must COMMIT ONLY ON `#Ok` (see
1482
+ * / its `transferOwnership`), so a silent refusal here would be read as success there and put the
1483
+ * / two canisters back into exactly the disagreement this seam exists to end. A refusal that cannot
1484
+ * / be heard is worse than no refusal.
1485
+ * /
1486
+ * / COLLISION IS REFUSED, NOT ABSORBED (owner ruling 2026-08-20). `engrams` is keyed BY OWNER, and
1487
+ * / three guards enforce one-engram-per-owner. Relaxing that rule was considered and declined: the
1488
+ * / spawn path's own comment warns that "a change to that rule must touch all three, and grepping
1489
+ * / the error TEXT finds only two of them", so the relaxation is the option most likely to leave a
1490
+ * / guard behind. The check below mirrors the spawn guard EXACTLY — active AND not abandoned —
1491
+ * / because an abandoned engram is not a live claim on its owner's slot.
1492
+ * /
1493
+ * / ⟨2026-09-29, owner ruling follow-up 8 (option A): a change of owner hands over control of the
1494
+ * / code⟩ THIS METHOD NOW MOVES CONTROL TOO, or tells the engram to. It used to move the record and
1495
+ * / nothing else, so the former owner stayed a controller and could still replace the code. Now:
1496
+ * / · MANAGED (the registry is a controller): the registry sets the controllers to itself and the
1497
+ * / new owner and answers `#MovedByRegistry`. If that fails, the record is put back and the
1498
+ * / answer is `#Err` — the engram then does not change owner.
1499
+ * / · SOVEREIGN (the registry is not a controller): the registry moves its record and answers
1500
+ * / `#EngramMustMove`; the engram changes its own controllers, and calls
1501
+ * / `sysRevertOwnershipTransfer` if it cannot.
1502
+ * / The record moves BEFORE the controller change, not after, so the one-engram-per-owner check and
1503
+ * / the move happen in one message — a spawn by the new owner during the await then meets the
1504
+ * / moved record and is refused. A report of the owner the registry already records is a RETRY of
1505
+ * / a transfer the engram did not finish, and moves control again (that is the way out `stuck`
1506
+ * / names). The rules are in `src/shared/ControlHandover.mo`.
1507
+ */
1508
+ sysNotifyOwnershipTransfer: ActorMethod<[Principal], Result_2>;
1509
+ /**
1510
+ * / ⟨2026-09-28, owner decision: build the full agent track-record design for the launch release⟩
1511
+ * / An engram tells this registry that one of its agents started, or ended and WHY.
1512
+ * /
1513
+ * / REPLACES `sysNotifyOperatorEdge(operator, added) : ()`, which is DELETED rather than kept
1514
+ * / beside this (the owner's building-phase rule of 2026-09-27: no backward compatibility; the
1515
+ * / next release installs every engram fresh). Three things changed, each on purpose:
1516
+ * / 1. THE REASON IS REQUIRED. `#Ended` carries an `Engagements.EndReason` sent from the one
1517
+ * / engram site that knows it. There is no reasonless ending and no "unknown" arm.
1518
+ * / 2. IT IS ACKNOWLEDGED. The old method returned `()` and silently ignored a stranger, so the
1519
+ * / engram discarded the outcome and a lost ending was invisible on both sides. This answers
1520
+ * / `Wire.Result`; the engram logs any non-`#Ok` where its owner can see it.
1521
+ * / 3. ONE RECORD ARGUMENT, not positional args — the same reshape `signX402Payment` took, so a
1522
+ * / later field is a named addition rather than a tuple that silently shifts.
1523
+ * /
1524
+ * / AUTH: the caller MUST be an engram this registry spawned (`canisterToOwner` membership), so an
1525
+ * / engram can only assert edges about ITSELF — `msg.caller` IS the engram, and no claim in the
1526
+ * / argument needs trusting. Idempotent: a repeated `#Added` mints no duplicate and a repeated
1527
+ * / `#Ended` writes nothing, and both answer `#Ok` because the state the caller asked for holds.
1528
+ * /
1529
+ * / It writes three things and nothing else: the live reverse index (`agentToEngrams`, which
1530
+ * / still forgets an agent at its last edge), the remembered engagement (`agentEngagements`,
1531
+ * / which never forgets), and that agent's certified summary (`putStandingLeaf`).
1532
+ * / ⟨CORRECTED 2026-10-02, beside the sentence above⟩ Five things now: a recorded ending can also
1533
+ * / cut off an engram opened through this one (`cutOff`, since 2026-10-01), and a start that opens
1534
+ * / an engagement takes the next place in the recording order (`engagementOrder`), in the same step
1535
+ * / as the record, so the order never names a record that is not there.
1536
+ * /
1537
+ * / ⟨RESHAPED 2026-09-28, owner decision: one lasting identity per agent — one root key per agent,
1538
+ * / with an enrolled key per client⟩ The subject is the agent's ROOT (`ev.root`), not the key that
1539
+ * / operates on the engram. The engram verified the root's signed enrolment statement before it
1540
+ * / admitted the key, and this registry trusts the engram for that exactly as it trusts it for the
1541
+ * / ending's reason — `msg.caller` is an engram this registry spawned. So every structure written
1542
+ * / here is keyed by agent: one agent across twenty-five engrams is one record, one live-index
1543
+ * / row and one summary. The key never reaches the registry; the plan's separate new `sys-` method
1544
+ * / for the root is not built — this method was itself new in the same release, so it carries the
1545
+ * / root instead of the key, and a second method would be a parallel path for one fact.
1546
+ * / ⟨CORRECTED 2026-09-28, review finding OPS-1: the trust in this paragraph was misplaced, and
1547
+ * / "the key never reaches the registry" is no longer true. An engram's owner is a controller of
1548
+ * / its canister and can install code that reports any root, so a start now carries the root's
1549
+ * / enrolment statement (`Engagements.EnrolmentProof`) and THIS method verifies it
1550
+ * / (`EdgeProof.rootOf`), and refuses past `Engagements.MAX_ENGAGEMENTS_PER_ENGRAM` (OPS-3). The
1551
+ * / enrolled key therefore DOES reach the registry, stored on the engagement record and never
1552
+ * / served. The ending's reason is still the owner's assertion; the agent's remedy for a false
1553
+ * / one is its dispute. Records stay keyed by the agent's root, as above.⟩
1554
+ * / ⟨MOVED 2026-09-29, review finding LIFE-11: the correction above stood in the middle of this
1555
+ * / paragraph, so the sentence it corrects came after it and read as the last word — in this file
1556
+ * / and in the published registry.did.⟩
1557
+ * / ⟨2026-09-29, review finding LIFE-1⟩ An engagement is matched on the enrolled key AND the invite
1558
+ * / its statement names, not on the key alone: one statement opens at most one engagement, and a
1559
+ * / key the engram re-admits after a reset, under a fresh statement for a fresh invite, opens a new
1560
+ * / one (`Engagements.admit`).
1561
+ * / ⟨CORRECTED 2026-09-29, review finding LIFE-2: "a repeated `#Ended` writes nothing, and both
1562
+ * / answer `#Ok`" is now narrower. An ending names the engagement it ends (the enrolled key and the
1563
+ * / invite) and the day the engram recorded it, because a lost ending is now RE-SENT by the
1564
+ * / engram's `resyncAgentEdges`. The same ending again answers `#Ok` and writes nothing; one
1565
+ * / giving another reason for an engagement already ended is refused `#Conflict`; one naming an
1566
+ * / engagement never recorded here is refused `#NotFound` (`Engagements.closeVerdict`).⟩
1567
+ */
1568
+ sysRecordAgentEdge: ActorMethod<[{ root: Principal; event: EdgeEvent }], Result>;
1569
+ /**
1570
+ * / ⟨2026-09-29, owner ruling follow-up 8⟩ A SOVEREIGN engram could not change its own controllers
1571
+ * / after this registry moved its record, and asks for the record back so the transfer does not
1572
+ * / complete. Gated like `sysNotifyOwnershipTransfer` (the caller must BE the engram), and only for
1573
+ * / an engram the registry records as sovereign — a managed engram's controllers are this
1574
+ * / registry's to move, and its record is put back inside `sysNotifyOwnershipTransfer` itself.
1575
+ * / Refuses, saying why, when the record cannot be put back (`ControlHandover.revertRefusal`); the
1576
+ * / engram then answers its owner with `ControlHandover.stuck`.
1577
+ */
1578
+ sysRevertOwnershipTransfer: ActorMethod<[Principal], Result>;
1579
+ /**
1580
+ * / ⟨2026-10-01, the owner's ruling of that day: the October concept's decision 1, "build it now"⟩
1581
+ * / THE SPONSORED DOOR. An engram opens an engram for one of its agents: the caller is the engram
1582
+ * / (the engram it is opened from — `parentOf` in code), `ownerKey` is the agent's LASTING key, which
1583
+ * / owns what is opened, and the cycles the opened engram lives on are the ones the caller ATTACHES.
1584
+ * /
1585
+ * / CALLER: a registered engram — `canisterToOwner` membership, checked first, exactly as
1586
+ * / `sysRecordAgentEdge` checks it (ADR-0020: every `sys` method names and checks its caller). The
1587
+ * / engram's own rules (the owner's standing allowance for this agent, its frozen-payments state,
1588
+ * / its balance) are the engram's to check before it calls; this method checks what only the
1589
+ * / registry can know.
1590
+ * /
1591
+ * / NOTHING IS ACCEPTED UNTIL EVERY CHECK HAS PASSED (`ChildDoor.admission`, cheapest first), so
1592
+ * / every refusal before the accept returns the attached cycles to the caller by construction —
1593
+ * / the IC returns unaccepted cycles when the call replies. After the accept:
1594
+ * / · the network refuses the canister → what it refunded is deposited back to the caller, and
1595
+ * / the reply says how much came back;
1596
+ * / · the template will not install after one retry → the cycles are INSIDE the new canister and
1597
+ * / no canister can withdraw another's cycles. The one honest exception to "returned on every
1598
+ * / failure": nothing is recorded, and the reply names the canister, its controllers (this
1599
+ * / registry and the key) and what the key can do with it;
1600
+ * / · the key came to own an engram during the awaits → the new canister is named, nothing is
1601
+ * / recorded (the same third guard `spawnEngramFor` carries).
1602
+ * /
1603
+ * / WHAT IT DOES NOT DO, deliberately: it never attaches the registry's own cycles (the grant stays
1604
+ * / with first engrams); it never writes a predecessor (a key with any record was refused); it never
1605
+ * / consults the reserve gate (the parent owner's approval is this door's admission — the gate's
1606
+ * / arming note stays on `provisionEngram`); it never tells the calling engram anything but this
1607
+ * / reply. No payment AMOUNT reaches it (ADR-0005): it carries cycles and a key, nothing else.
1608
+ * /
1609
+ * / THE RATE LIMIT ⟨2026-10-01, evening — the owner's ruling "1B"; until then this door shared
1610
+ * / first engrams' limiter in full: their 24-hour count, their in-flight count and
1611
+ * / `effectiveDailyCap`⟩. The door shares only the DIAL (`spawnCeiling`) and the WINDOW LENGTH (24
1612
+ * / hours). Its count is its own (`sponsoredCreationTimestampsBuf`, pruned by
1613
+ * / `pruneSponsoredWindow`), its in-flight count is its own (`sponsoredInFlight`), and its cap is
1614
+ * / `spawnCeiling` alone — the reserve-derived term never applies, because the opened engram lives
1615
+ * / on the cycles the caller attached and the registry funds nothing. So an opening never uses up a
1616
+ * / first engram's slot, and is never refused for the registry's own low balance. The per-key
1617
+ * / pending mark (`createEngramPending`) stays shared: it is about ONE key, not a count, and a key
1618
+ * / must not be mid-spawn at both doors at once.
1619
+ * /
1620
+ * / THE CUT-OFF AT BIRTH. If the caller ended the key's engagement while the canister was being
1621
+ * / created (the ending is recorded by `sysRecordAgentEdge` between this method's awaits), the
1622
+ * / opened engram is recorded AND cut off in the same step — the cycles are already in it, so the
1623
+ * / key keeps it, but it starts on nobody's record. Without this the ending's cut-off writer would
1624
+ * / have found no `openedThroughParent` row yet, and the engram would never be cut off.
1625
+ * / WHAT WOULD MAKE THIS WRONG: a second path that writes `parentOf`, or an `await` placed between
1626
+ * / the post-await re-check and the record below.
1627
+ */
1628
+ sysSpawnChild: ActorMethod<[Principal], Result_1>;
1629
+ /**
1630
+ * / Admin: upgrade one engram to the current template. RESPECTS the owner's update preference —
1631
+ * / an engram set to `#ManualOnly` is not moved by this method, which is what makes the preference
1632
+ * / meaningful rather than advisory. The owner's own `upgradeMyEngram` is unaffected either way.
1633
+ */
1634
+ upgradeEngram: ActorMethod<[Principal], Result>;
1635
+ /**
1636
+ * / Upgrade YOUR OWN engram to the registry's current template. Self-service — no admin, no
1637
+ * / queue, no approval.
1638
+ * /
1639
+ * / ⚠ THE PULL IS ALWAYS FUNDED, regardless of release class or rent state, and that is a
1640
+ * / commitment rather than an implementation detail: `install_code` needs the balance to cover the
1641
+ * / install ABOVE the freezing threshold, so the registry lifts the engram over that bar if cycles
1642
+ * / are what is stopping it. Refusing an owner their own fix for nonpayment is the ransom the
1643
+ * / continuity-rent model forbids, and withholding a security fix would be worse than anything
1644
+ * / that model gates. The lift is best-effort: if it fails, the install still runs and returns the
1645
+ * / REAL error, so a top-up problem can never masquerade as an upgrade problem or hide one.
1646
+ * / ⟨2026-09-28, owner decision: the registry only pays for upgrades of engrams running our
1647
+ * / published code⟩ One exception to "always funded": an engram whose installed module is not one
1648
+ * / this registry published is not lifted (`fundUpgradeIfNeeded`, audit
1649
+ * / `upgrade.topUp.unpublishedCode`). The install is still attempted — the owner is never refused
1650
+ * / their fix, only our cycles for it.
1651
+ * / ⟨2026-09-29⟩ And when that install then FAILS, the reply says why the registry did not pay and
1652
+ * / what the owner can do (`TemplatePolicy.unfundedUpgradeFailure`) — before, the refusal was only
1653
+ * / in the registry's own log and the owner saw a bare out-of-cycles error. Same reply type.
1654
+ * / ⟨2026-10-01, the owner's ruling of that day (2 A)⟩ A second exception beside the first: an
1655
+ * / engram opened through a parent (`parentOf`, written by `sysSpawnChild`) is funded by the
1656
+ * / registry never — not at birth, not at an upgrade (`fundUpgradeIfNeeded`, audit
1657
+ * / `upgrade.topUp.childEngram`). The install is still attempted; the owner is never refused their
1658
+ * / fix, only our cycles for it, and a failed install says so
1659
+ * / (`TemplatePolicy.childUnfundedUpgradeFailure`).
1660
+ * /
1661
+ * / An engram already on the current template is a silent no-op. A SOVEREIGN engram gets
1662
+ * / "registry is no longer a controller" — accurate, because it is no longer ours to install on.
1663
+ * /
1664
+ * / ⚠ THERE IS NO REGISTRY-SIDE SIZE PRE-FLIGHT. The FD budget check lives in
1665
+ * / the deployment's fleet-upgrade tooling, so a caller reaching this method directly does not
1666
+ * / get it.
1667
+ */
1668
+ upgradeMyEngram: ActorMethod<[], Result>;
1669
+ /**
1670
+ * / True iff `canisterId` is a registered, `#Active` engram owned by `claimedOwner`. The cashier's
1671
+ * / money gate: fund ONLY an active engram the paying caller owns, since minting cycles to a
1672
+ * / frozen or deleted canister strands the funds.
1673
+ * /
1674
+ * / PUBLIC: it CONFIRMS a claim the caller must already hold — both the canister id and the owner
1675
+ * / — so it discloses no mapping either way. That is the difference between this and the deleted
1676
+ * / `getEngramByCanisterId`, which was gated precisely because it turned one id into the other.
1677
+ * /
1678
+ * / ⚠ WHAT IT DOES NOT ATTEST. The answer is the owner AS THIS REGISTRY RECORDS IT. Custody can
1679
+ * / move on the engram via `transferOwnership`, and while `sysNotifyOwnershipTransfer` now closes
1680
+ * / that gap going forward, a caller for whom CURRENCY matters should compose with the engram's
1681
+ * / `listOwnershipTransfers` — current custody is the `to` of its latest entry — as the /verify
1682
+ * / born-clean badge does. A query cannot make inter-canister calls, so this one cannot ask.
1683
+ * /
1684
+ * / ⟨Promoted from `//` to `///` 2026-08-21. Everything above was written as plain comments, so
1685
+ * / registry.did published the cashier's money gate with NO explanation at all while 53 lines of
1686
+ * / it sat in the source. The correction history stays `//` on purpose — it is addressed to an
1687
+ * / editor, and only what a CALLER needs belongs in the interface.⟩
1688
+ */
1689
+ verifyEngramActiveOwner: ActorMethod<[Principal, Principal], boolean>;
1690
+ }
1691
+ export declare const idlFactory: IDL.InterfaceFactory;
1692
+ export declare const init: (args: { IDL: typeof IDL }) => IDL.Type[];