@cotal-ai/manager 0.133.3 → 0.133.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/manager.js CHANGED
@@ -20,10 +20,10 @@ import { RunHosting } from "./run-hosting.js";
20
20
  import { enrolledAcl } from "./remote-authority.js";
21
21
  import { rejectionText } from "./rejection-text.js";
22
22
  import { controlSession } from "./control-session.js";
23
- import { MAX_AGENTS, parseResumeCommitArgs, parseResumeControlArgs, parseResumeFinalizeArgs, readSeatRecord, removeSeatRecord, writeSeatRecord } from "./resume.js";
23
+ import { MAX_AGENTS, parseResumeCommitArgs, parseResumeControlArgs, parseResumeFinalizeArgs, readSeatRecord, removeSeatRecord, supervisePolicy, writeSeatRecord } from "./resume.js";
24
24
  import { TranscriptReceiver } from "./transcript-receive.js";
25
- // Unit B (the static §13.1 lifecycle executor): the shared grammar/stores from core plus the
26
- // manager-side adapter (transport + slot orchestration + the F1 terminal) — see static-lifecycle.ts.
25
+ // The static §13.1 lifecycle executor: the shared grammar/stores from core plus the
26
+ // manager-side adapter (transport + slot orchestration + the terminal) — see static-lifecycle.ts.
27
27
  import { jetstream, jetstreamManager } from "@nats-io/jetstream";
28
28
  import { Kvm } from "@nats-io/kv";
29
29
  import { recordsBucket, epAuthBucket, ensureAuthorityStores, ensureContractStore, createEndpointStreams, contractStoreContext, publishContractArtifact, contractArtifactCanonicalBytes, standaloneConnectOpts, credentialIdOf, STANDING_RENEWABLE_TTL_SEC as MANAGED_STATIC_TTL_SEC, STATIC_GENERATION_HORIZON_SEC, issuedHorizon, newArtifactSigner, RotatingSigner, generationAnchor, sessionsBucket, SESSION_GRANT_MAX_TTL_MS, REMOTE_MANAGER_IDENTITY_NAMES, } from "@cotal-ai/core";
@@ -144,7 +144,7 @@ const STARTUP_RECONCILING = "startup static lifecycle reconciliation";
144
144
  * leaves the durable alias held and requires a later manager start. */
145
145
  export const STATIC_RECONCILE_RETRY_DELAYS_MS = [1_000, 5_000, 30_000];
146
146
  const STATIC_RECONCILE_MAX_ATTEMPTS = STATIC_RECONCILE_RETRY_DELAYS_MS.length + 1;
147
- /** The STABLE retirement opId for one lifecycle (#29 piece 3): deterministic from the uid, so a
147
+ /** The STABLE retirement opId for one lifecycle: deterministic from the uid, so a
148
148
  * despawn retry, a same-name-spawn nudge, and the auth service's boot resume all drive the SAME
149
149
  * operation (the rail's idempotence table needs exactly one op per retiring incarnation). 26 hex
150
150
  * chars = in the lifecycle-token grammar `[a-z0-9]{26,32}`, collision-resistant. */
@@ -450,7 +450,7 @@ export class Manager {
450
450
  /** Expiry stamps (`startedAt + MIN_LIFETIME`) for slots that freed while still young — a
451
451
  * count-only, lazily-pruned recycle floor (P4c). Pruned + summed into the ceiling gate. */
452
452
  cooling = [];
453
- /** Names RESERVED PENDING RETIREMENT (#29 piece 3): a despawned agent's name stays held until
453
+ /** Names RESERVED PENDING RETIREMENT: a despawned agent's name stays held until
454
454
  * the auth plane confirms its lifecycle's retirement TERMINAL over the auth-admin rail — the
455
455
  * alias-reuse gate that closes the same-name despawn→respawn race at its root. An UNCERTAIN
456
456
  * outcome (rail down, timeout) keeps the hold with the last attempt's copy; a same-name spawn
@@ -471,7 +471,7 @@ export class Manager {
471
471
  * same-name SUCCESSOR (which can spawn after the hold clears but before this flight's `nc.close`
472
472
  * yield settles) never joins the predecessor's rail request and skips its own retirement. */
473
473
  retiringFlight = new Map();
474
- /** Wire principals of RETIRED static incarnations (Unit B, F5(a)): populated at every completed
474
+ /** Wire principals of RETIRED static incarnations: populated at every completed
475
475
  * static terminal and from the boot sweep's retired slot rows, so a copied credential of a
476
476
  * retired incarnation is refused at the control surface even across a manager restart. The
477
477
  * durable truth is the slot row + principal-keyed head; this set is the in-memory index of it
@@ -678,7 +678,7 @@ export class Manager {
678
678
  * does not re-acquire: a manager that is stopping never puts its own key back. It also closes the
679
679
  * lifecycle fence ({@link beginLifecycle}), so no spawn can launch a seat the stop never sees. */
680
680
  leaseStopping = false;
681
- /** The class-2 renewal owner's half-TTL schedule (D5 slice 5); armed only on auth meshes. */
681
+ /** The class-2 renewal owner's half-TTL schedule; armed only on auth meshes. */
682
682
  credRenewTimer;
683
683
  /** #2073: the `endpoint-serve` credential's OWN renewal, armed from its `[renewAt, exp)` window
684
684
  * (`credsRenewalDelayMs`) rather than left to `credRenewTimer`'s boot-phased TTL/4 tick, which can
@@ -1182,7 +1182,7 @@ export class Manager {
1182
1182
  // EPHEMERAL provisioner connection per spawn (withProvisioner); destructive purge mints a PURGER per
1183
1183
  // call. So the always-on daemon holds no DM/DLV read, no consumer-create, no stream-admin tamper.
1184
1184
  //
1185
- // STANDING RENEWAL (D5 slice 5, class 1): the manager holds the DATA signing seed, so it is its
1185
+ // STANDING RENEWAL (class 1): the manager holds the DATA signing seed, so it is its
1186
1186
  // own renewal owner — the cred rides the endpoint's SOURCE seam and self-remints (same identity,
1187
1187
  // pinned by the endpoint) ahead of each bounded supervisor JWT's expiry. A copied supervisor
1188
1188
  // cred is broker-dead within the matrix TTL.
@@ -1197,7 +1197,7 @@ export class Manager {
1197
1197
  // lifecycle-keyed (SPEC 13.1, fail-before-presence). The manager process is the top of its
1198
1198
  // own launch chain (the operator command IS its launcher): its incarnation uid is the
1199
1199
  // per-process `managerLifecycleUid` field (also the `managerInstance` audit coordinate on
1200
- // every static activation, Unit B).
1200
+ // every static activation).
1201
1201
  lifecycleUid: this.remoteAuthority?.lifecycleUid ?? this.managerLifecycleUid,
1202
1202
  // The supervisor serves control + watches presence; it never consumes chat/dm/task
1203
1203
  // (no message handler). consume:false avoids binding consumers it doesn't use — and
@@ -1267,7 +1267,7 @@ export class Manager {
1267
1267
  const s = this.serviceServe;
1268
1268
  return s?.handle && !s.nc.isClosed() && !this.transportDown.has(s.nc) ? "bound" : "unbound";
1269
1269
  });
1270
- // Unit B (static §13.1): after this instance holds its lease, collect the durable static rows
1270
+ // Static §13.1: after this instance holds its lease, collect the durable static rows
1271
1271
  // now, but do not let their exact-op terminals make the whole space unreachable. The service
1272
1272
  // comes up below, then the sweep overlaps the remaining registration work. Two-window
1273
1273
  // heal-then-register is slower than one executor, but the overlap is still the no-outage
@@ -1280,7 +1280,7 @@ export class Manager {
1280
1280
  // service endpoint registered below. The old three-tier rail (self/manager/admin) is deleted;
1281
1281
  // `ctl.delivery`/`ctl.delivery-admin` (the delivery daemon) and `ctl.auth-admin` (the auth
1282
1282
  // plane) are separate services and keep their rails.
1283
- // D5 slice 5 class 2: the manager is the CLASS-2 RENEWAL OWNER — the one control-plane process
1283
+ // Class 2: the manager is the CLASS-2 RENEWAL OWNER — the one control-plane process
1284
1284
  // that is resident in EVERY mesh mode (foreground `up`, `up --detach`, same-root refresh) and
1285
1285
  // holds the signer. Ordered initial pass NOW (ensureControlPlane starts delivery BEFORE the
1286
1286
  // manager, so the daemon's launch-time creds write always precedes this — no write race), then
@@ -1633,7 +1633,7 @@ export class Manager {
1633
1633
  }
1634
1634
  return false;
1635
1635
  }
1636
- /** One class-2 renewal pass (D5 slice 5): re-sign `.cotal/delivery.creds` + `.cotal/membership-rw.creds`
1636
+ /** One class-2 renewal pass: re-sign `.cotal/delivery.creds` + `.cotal/membership-rw.creds`
1637
1637
  * for their existing nkeys, then request the delivery daemon's EXPLICIT `reloadCreds` adoption on the
1638
1638
  * delivery-admin rail and persist the audit record (`.cotal/renewal.<spaceKey>.json`) that `cotal doctor auth`
1639
1639
  * renders — so "file re-signed" and "daemon adopted" are distinguishable states. A missing daemon
@@ -1695,7 +1695,7 @@ export class Manager {
1695
1695
  if (this.daemonRenewalOwner)
1696
1696
  writeRenewalRecord(this.workspaceRoot, this.space, { ts: new Date().toISOString(), owner: "manager", results, adoption });
1697
1697
  this.warnOnSystemCredExpiry();
1698
- // F5(b) (Unit B): the MANAGER is the renewal owner for its managed-static agent creds —
1698
+ // The MANAGER is the renewal owner for its managed-static agent creds —
1699
1699
  // supervisor-side PUSH remint for recorded LIVE slots (the child JWT is never proof of
1700
1700
  // incarnation; a copied credential cannot drive this and is stranded at its own row's TTL).
1701
1701
  // Same class-2 mechanics as the daemon creds: re-sign the file for the SAME nkey; the
@@ -1928,7 +1928,7 @@ export class Manager {
1928
1928
  for (const file of SYSTEM_CREDS_FILES) {
1929
1929
  let health;
1930
1930
  try {
1931
- // The pair is PER-SPACE as of P7, so the location comes from the resolver and never from a
1931
+ // The pair is PER-SPACE, so the location comes from the resolver and never from a
1932
1932
  // path built here (§2 rule 1). Building `.cotal/<kind>` instead would not warn on a wrong
1933
1933
  // file — it would silently warn on NOTHING, for every provisioned root, forever, which is
1934
1934
  // exactly the signal #338 added this for. The resolver may also refuse (§2 rules 3, 4); that
@@ -2427,6 +2427,11 @@ export class Manager {
2427
2427
  }
2428
2428
  catch (e) {
2429
2429
  failures.push(`${a.name}: stop failed: ${rejectionText(e)}`);
2430
+ // A refused hard stop never reached the child, so waiting for its exit could only time out
2431
+ // and restate this failure; the cut path reports it the same way. A delegated seat's
2432
+ // deprovision can fail after its retirement already closed the seat, so that wait stays.
2433
+ if (!a.handedOff)
2434
+ return;
2430
2435
  }
2431
2436
  try {
2432
2437
  await this.awaitHandleExit(a.handle);
@@ -2881,13 +2886,13 @@ export class Manager {
2881
2886
  if (managed)
2882
2887
  managed.suppressCleanup = false;
2883
2888
  }
2884
- // Unit B (F3, distsys/security CONDITIONAL @ 9e13648): the boot sweep DEFERRED every active
2889
+ // distsys/security CONDITIONAL @ 9e13648: the boot sweep DEFERRED every active
2885
2890
  // slot while a resume was pending (it could not know which would be adopted). Now adoption
2886
2891
  // is complete and `this.agents` is EXACTLY the adopted set, so re-sweep to terminalize any
2887
2892
  // active slot the resume did NOT claim — a durable ACTIVE ORPHAN (crashed after slot->active
2888
2893
  // before agents.set, then not in the resumed inventory). This runs while `resumeRequired` is
2889
2894
  // still true, so no ordinary spawn can race it (beginLifecycle refuses non-resume ops), and
2890
- // it closes both the alias wedge AND the F5(a) gap (the orphan's principal enters
2895
+ // it closes both the alias wedge AND the membership-gate gap (the orphan's principal enters
2891
2896
  // retiredPrincipals, so a copied JWT is refused). Best-effort + loud: a sweep failure must
2892
2897
  // not fail the finalize (the next non-resume boot re-drives it), but it is never swallowed.
2893
2898
  if (this.auth && !this.userMode)
@@ -3000,7 +3005,7 @@ export class Manager {
3000
3005
  /** The ONE shared control-admission chokepoint BOTH dispatch doors
3001
3006
  * run — the v0.3 `ctl` door ({@link handle}) and the v0.4 `ep` service handlers
3002
3007
  * ({@link serveGated}): the maintenance/resume fence (`beginLifecycle`: a resume-pending or
3003
- * non-active manager accepts no ordinary control work) and then the F5(a) membership gate
3008
+ * non-active manager accepts no ordinary control work) and then the membership gate
3004
3009
  * ({@link lifecycleMembershipRefusal}: a retiring/terminalizing/retired managed incarnation's
3005
3010
  * AUTHENTICATED principal holds no control authority even with a valid JWT). Refusal carries
3006
3011
  * WHICH fence refused so the ep door can map onto the §13.3 catalog; admission returns the
@@ -3018,7 +3023,7 @@ export class Manager {
3018
3023
  }
3019
3024
  /** Run one v0.4 service-command handler through the SHARED admission chokepoint
3020
3025
  * ({@link admitControl}) on the broker-authenticated caller principal, mapping the two fences
3021
- * onto the §13.3 catalog: maintenance/resume → `unavailable`, F5(a) membership →
3026
+ * onto the §13.3 catalog: maintenance/resume → `unavailable`, membership →
3022
3027
  * `permission-denied`. The serve boundary publishes the structured error reply. */
3023
3028
  async serveGated(ctx, fn) {
3024
3029
  const caller = principalKey(ctx.subject.caller.owner, ctx.subject.caller.actor).key;
@@ -3705,7 +3710,7 @@ export class Manager {
3705
3710
  * returned: a caller that must prove exit (shutdown, a failed resume) reports it as the cause at
3706
3711
  * once instead of waiting out a stop the runtime already refused. */
3707
3712
  stopHandle(a, graceful) {
3708
- // The F5 TERMINALIZING latch (Unit B): flipped SYNCHRONOUSLY, before any await anywhere on
3713
+ // The TERMINALIZING latch: flipped SYNCHRONOUSLY, before any await anywhere on
3709
3714
  // this stop path — later renewals refuse, while an already-admitted renewal drains before the
3710
3715
  // durable terminal begins.
3711
3716
  a.terminalizing = true;
@@ -4130,7 +4135,7 @@ export class Manager {
4130
4135
  freeSlot(a, floor, cause, acceptedBeforeFence = false) {
4131
4136
  if (this.agents.get(a.name) !== a)
4132
4137
  return; // already freed (exit raced despawn, etc.)
4133
- // F5 latch (Unit B): also covers exit/reap paths that never rode stopHandle.
4138
+ // The terminalizing latch also covers exit/reap paths that never rode stopHandle.
4134
4139
  this.logSeatReaped(a, cause);
4135
4140
  a.terminalizing = true;
4136
4141
  this.agents.delete(a.name);
@@ -4154,7 +4159,7 @@ export class Manager {
4154
4159
  // is usually the seat ending, but a stop that could not prove the process gone leaves one the
4155
4160
  // terminal must still address before it frees the alias.
4156
4161
  const target = { ...this.teardownTarget(a), runtime: a.handle?.reference };
4157
- // #29 piece 3: on a USER mesh the name is RESERVED PENDING RETIREMENT — despawn started this
4162
+ // On a USER mesh the name is RESERVED PENDING RETIREMENT — despawn started this
4158
4163
  // lifecycle's FULL teardown (footprint + standing-authority revoke + the auth-side retirement),
4159
4164
  // and the alias frees only when all of it completes (not the retirement alone). The detached
4160
4165
  // deprovision below drives it; a failed revoke or an unreachable rail keeps the name held,
@@ -4168,10 +4173,10 @@ export class Manager {
4168
4173
  this.retiring.set(a.name, { opId: retireOpId(a.lifecycleUid), owner: p.owner, actor: p.actor, target, startedAt: Date.now() });
4169
4174
  }
4170
4175
  else if (this.auth) {
4171
- // Unit B: a STATIC lifecycle now also holds its name pending its own terminal (the F1
4176
+ // A STATIC lifecycle now also holds its name pending its own terminal (the
4172
4177
  // static retirement the detached deprovision below drives) — the alias frees only when the
4173
4178
  // gate+head terminal completes, exactly the user-mode discipline. The wire principal is the
4174
- // incarnation-unique nkey (F5-bind); owner is the dev owner.
4179
+ // incarnation-unique nkey; owner is the dev owner.
4175
4180
  this.retiring.set(a.name, { opId: retireOpId(a.lifecycleUid), owner: DEV_OWNER, actor: a.id, target, startedAt: Date.now() });
4176
4181
  }
4177
4182
  // Auth mode: tear down the departed agent's minted broker footprint + creds file (#159 B2). The
@@ -4269,7 +4274,7 @@ export class Manager {
4269
4274
  if (!this.auth)
4270
4275
  return; // guaranteed by deprovision; re-checked for the deprovisionBroker narrowing
4271
4276
  if (!this.userMode && !a.userOwner) {
4272
- // Unit B: a STATIC lifecycle retires through the F1 terminal barrier — freeze → head
4277
+ // A STATIC lifecycle retires through the terminal barrier — freeze → head
4273
4278
  // retiring → B1 ledger revoke → footprint cleanup (creds file + broker durables/ACL, INSIDE
4274
4279
  // the barrier) → gate retired → head retired → alias free. The eviction step is the process
4275
4280
  // kill the stop path already performed (static's best-effort eviction).
@@ -4351,7 +4356,7 @@ export class Manager {
4351
4356
  }
4352
4357
  if (shredFailure)
4353
4358
  throw new Error(shredFailure);
4354
- // #29 piece 3: after the footprint teardown, ask the AUTH plane to RETIRE the lifecycle over
4359
+ // After the footprint teardown, ask the AUTH plane to RETIRE the lifecycle over
4355
4360
  // the auth endpoint rail. The rail re-checks the SERVE-ISSUANCE GATE at serve time (not the
4356
4361
  // space-manager lease - that check was replaced in 02794b2f) and refuses unless the registration
4357
4362
  // this request names belongs to our own principal; the terminal (or an already-retired answer)
@@ -4359,7 +4364,7 @@ export class Manager {
4359
4364
  // their operator copy — legible, retryable, never a silent half-state.
4360
4365
  await this.requestRetirement(a);
4361
4366
  }
4362
- /** Request the auth-side retirement of a departed agent's lifecycle (#29 piece 3): an ephemeral
4367
+ /** Request the auth-side retirement of a departed agent's lifecycle: an ephemeral
4363
4368
  * `retirement-requester` credential (request + reply only), the generic `retireLifecycle` op,
4364
4369
  * a STABLE opId (derived from the lifecycleUid, so every retry re-drives the SAME operation),
4365
4370
  * and the four-outcome handling in operator vocabulary. */
@@ -5544,8 +5549,15 @@ export class Manager {
5544
5549
  opts = ownLists(opts);
5545
5550
  if (opts.resolved)
5546
5551
  opts.resolved = ownLists(opts.resolved);
5547
- if (opts.supervise)
5552
+ if (opts.supervise) {
5548
5553
  opts.supervise = { restarts: opts.supervise.restarts, windowMs: opts.supervise.windowMs };
5554
+ // The budget is checked here, where it is held, not only at the wire: an in-process caller of
5555
+ // this export is refused before anything launches, rather than getting a seat that joins and is
5556
+ // torn down when its seat record will not take the policy (#3298).
5557
+ const policy = supervisePolicy.safeParse(opts.supervise);
5558
+ if (!policy.success)
5559
+ return { ok: false, error: `supervise.${policy.error.issues[0]?.path.join(".")}: expected a positive integer` };
5560
+ }
5549
5561
  let runtime;
5550
5562
  try {
5551
5563
  await this.loadSeatRuntime(opts.runtime);
@@ -5766,7 +5778,7 @@ export class Manager {
5766
5778
  // #966: a seat labelled `manager` that cannot spawn is a worker wearing a label, and the label
5767
5779
  // is what other agents ROUTE on (role is the anycast address). The gap is silent today because
5768
5780
  // the mismatch is only discovered minutes in, when the seat first reaches for a worker. The
5769
- // persona write path takes content only (role/capabilities are POLICY, P6), so a peer-defined
5781
+ // persona write path takes content only (role/capabilities are POLICY), so a peer-defined
5770
5782
  // persona can never fix this by declaring the capability — the only honest outcomes are "the
5771
5783
  // persona grants spawn" or "say so now". Refuse at accept, before any reserve/mint, naming the
5772
5784
  // exact remediation for each author: an operator edits the file to add the grant; a peer that
@@ -5801,7 +5813,7 @@ export class Manager {
5801
5813
  });
5802
5814
  if (policyRefusal)
5803
5815
  return { ok: false, error: policyRefusal };
5804
- // The alias-reuse gate (#29 piece 3): a name whose previous agent is still retiring REFUSES
5816
+ // The alias-reuse gate: a name whose previous agent is still retiring REFUSES
5805
5817
  // legibly (never a silent suffix), and the refusal re-drives the FULL durable teardown so
5806
5818
  // "retry the spawn" is also the nudge. It routes through `deprovision` (not `requestRetirement`
5807
5819
  // alone) so a retry re-drives the standing-authority revoke (INT-2) AND the broker cleanup before
@@ -6048,11 +6060,11 @@ export class Manager {
6048
6060
  }
6049
6061
  }
6050
6062
  else if (this.auth) {
6051
- // Unit B (§13.1): reserve + activate this incarnation's DURABLE identity BEFORE any
6052
- // broker footprint — the F3 outer spawn intent first (slot row, phase `provisioning`),
6063
+ // §13.1: reserve + activate this incarnation's DURABLE identity BEFORE any
6064
+ // broker footprint — the outer spawn intent first (slot row, phase `provisioning`),
6053
6065
  // then the SHARED core activation saga (reserve uid -> gate frozen -> head CAS -> reopen
6054
6066
  // LAST) over the key-pinned executor. The wire AUTHORITY principal is the incarnation-
6055
- // unique nkey (F5-bind); the alias is protected by the name-keyed slot + freeSlot hold.
6067
+ // unique nkey; the alias is protected by the name-keyed slot + freeSlot hold.
6056
6068
  await this.withLifecycleExecutor({ owner: DEV_OWNER, actor: identity.id, lifecycleUid, alias: name }, (t) => activateStaticLifecycle(t, { owner: DEV_OWNER, alias: name, actor: identity.id, lifecycleUid, managerInstance: this.managerLifecycleUid, ownerInstanceId: this.managerInstanceId, runtime: custody }));
6057
6069
  // From here the DURABLE registration exists: arm the rollback BEFORE minting, so a throw
6058
6070
  // between activation and provisioning still drives the exact-op static terminal (the
@@ -6062,7 +6074,7 @@ export class Manager {
6062
6074
  // — the shared onboarding step (provisionAgent). It runs on a short-lived PROVISIONER connection
6063
6075
  // (NOT the supervisor's long-lived endpoint), so the DM/DLV consumer-create surface exists only
6064
6076
  // for the provisioning window, never as a standing grant on the always-on daemon (residual 2).
6065
- // F5(b): the credential is BOUNDED (`expiresAt`) — the manager push-renews it ahead of expiry.
6077
+ // The credential is BOUNDED (`expiresAt`) — the manager push-renews it ahead of expiry.
6066
6078
  const now = Math.floor(Date.now() / 1000);
6067
6079
  const exp = now + MANAGED_STATIC_TTL_SEC;
6068
6080
  // The credential is an ISSUANCE (SPEC 13.15): its caller rails carry a fresh generation
@@ -6234,7 +6246,7 @@ export class Manager {
6234
6246
  }
6235
6247
  : {}),
6236
6248
  };
6237
- // Unit B: the DURABLE slot takes the `active` phase before the in-memory row takes the
6249
+ // The DURABLE slot takes the `active` phase before the in-memory row takes the
6238
6250
  // name — a crash between the two leaves an active-but-unadopted slot the boot sweep
6239
6251
  // terminalizes (never an untracked orphan). Static auth only; a failed CAS fails the spawn
6240
6252
  // (the finally's rollback then drives the exact-op terminal).
@@ -6899,7 +6911,7 @@ export class Manager {
6899
6911
  if (!batchReserved)
6900
6912
  this.reserved.set(entry.name, resumeOwner(entry));
6901
6913
  try {
6902
- // Unit B F5(b): recover the STATIC identity's nkey seed from the adopted credential (the
6914
+ // Recover the STATIC identity's nkey seed from the adopted credential (the
6903
6915
  // creds file embeds it) so the manager stays this incarnation's RENEWAL OWNER across a
6904
6916
  // preserve/resume — without it the adopted cred would die loud at its TTL with no remint.
6905
6917
  let adoptedSeed;
@@ -7493,7 +7505,7 @@ export class Manager {
7493
7505
  return undefined;
7494
7506
  }
7495
7507
  /** Run one static §13.1 lifecycle OPERATION over an ephemeral, key-pinned `lifecycle-executor`
7496
- * connection (Unit B): the credential's grants name exactly ONE incarnation's head/uid/gate/
7508
+ * connection: the credential's grants name exactly ONE incarnation's head/uid/gate/
7497
7509
  * cred-family/slot keys, so the write authority exists only for this operation's window and
7498
7510
  * can move nothing else. The transport is the direct-KV binding the shared core saga drives. */
7499
7511
  async withLifecycleExecutor(pin, fn) {
@@ -9810,11 +9822,11 @@ export class Manager {
9810
9822
  const evidence = await requireRuntimeReap(runtime, a.runtime);
9811
9823
  console.error(`static retirement ${a.name}: orphan seat process ${evidence.detail}`);
9812
9824
  }
9813
- /** The static F1 terminal for one departed incarnation (Unit B): delegates the gate/head CAS
9825
+ /** The static terminal for one departed incarnation: delegates the gate/head CAS
9814
9826
  * sequence to the shared core saga over the executor transport; the footprint teardown (creds
9815
9827
  * file + broker durables/ACL) runs INSIDE the barrier as its cleanup step. On completion the
9816
- * wire principal joins {@link retiredPrincipals} (the F5 refusal index) and the name hold
9817
- * clears (ABA-guarded by uid). A PRE-UNIT-B lifecycle (no slot row — spawned before the
9828
+ * wire principal joins {@link retiredPrincipals} (the retired-principal refusal index) and the name hold
9829
+ * clears (ABA-guarded by uid). A lifecycle with no slot row (spawned before the
9818
9830
  * durable registry existed) has nothing to terminalize: its footprint teardown runs directly
9819
9831
  * and the hold clears, the honest upgrade path. `onDeprovision` receives the accounting of the
9820
9832
  * broker teardown this call ran, on success or on a {@link DeprovisionError}. */
@@ -9875,7 +9887,7 @@ export class Manager {
9875
9887
  await this.withLifecycleExecutor({ owner: DEV_OWNER, actor: a.id, lifecycleUid: a.lifecycleUid, alias: a.name }, async (t) => {
9876
9888
  const slot = await readStaticSlot(t, DEV_OWNER, a.name);
9877
9889
  if (slot === undefined || slot.row.lifecycleUid !== a.lifecycleUid) {
9878
- // No durable registration for THIS incarnation: a pre-Unit-B spawn (or a slot already
9890
+ // No durable registration for THIS incarnation: a spawn from before the registry (or a slot already
9879
9891
  // replaced by a successor — then this stale teardown must not touch the registry at all).
9880
9892
  await reapThenCleanup();
9881
9893
  return;
@@ -10125,7 +10137,7 @@ export class Manager {
10125
10137
  for (const session of this.chatExpiry.remaining())
10126
10138
  console.error(`! chat session ${session.alias} (uid ${session.lifecycleUid}) was not retired because ${why}; the next start of this manager instance retires it`);
10127
10139
  }
10128
- /** F5(b) push renewal of ONE live managed-static credential (Unit B): re-mint the SAME nkey
10140
+ /** Push renewal of ONE live managed-static credential: re-mint the SAME nkey
10129
10141
  * identity with the SAME scope (recorded on the managed row at spawn) and a fresh bounded
10130
10142
  * exp, ledger the new credentialId (slot record first, then the row, then the file — a
10131
10143
  * credential is never materialized before its ledger row exists), and re-sign the SAME
@@ -10183,11 +10195,11 @@ export class Manager {
10183
10195
  this.refreshSeatRecord(a);
10184
10196
  console.error(`managed cred renewal ${a.name}: re-signed for the same identity (exp +${exp - now}s); the agent endpoint's source re-read adopts it`);
10185
10197
  }
10186
- /** The Unit B reconciliation (F3 "no active orphan"): ensure the authority stores, then sweep
10198
+ /** The static-slot reconciliation ("no active orphan"): ensure the authority stores, then sweep
10187
10199
  * every durable slot row and act by the TOTAL resume table — `provisioning`/`terminalizing`
10188
10200
  * re-drive the exact-op terminal; an `active` row survives ONLY when a LIVE managed agent this
10189
10201
  * manager process owns backs it at the same uid (`adopted`), else no managed owner claimed it and
10190
- * its broker authority is contained and terminalized; `retired` rows seed the F5 refusal index. Two call sites: the BOOT sweep
10202
+ * its broker authority is contained and terminalized; `retired` rows seed the retired-principal refusal index. Two call sites: the BOOT sweep
10191
10203
  * (`postAdoption=false`, under the lease before control serving) DEFERS active-non-adopted
10192
10204
  * slots while a resume is still pending (adoption runs after it); the POST-ADOPTION sweep
10193
10205
  * (`postAdoption=true`, inside finalizeResume while `resumeRequired` still fences ordinary
@@ -10479,7 +10491,7 @@ export class Manager {
10479
10491
  const restartRows = [];
10480
10492
  for (const row of slotRows) {
10481
10493
  if (row.phase === "retired") {
10482
- // A retirement is a GLOBAL refusal fact — seed the F5 index for EVERY retired incarnation
10494
+ // A retirement is a GLOBAL refusal fact — seed the retired-principal index for EVERY retired incarnation
10483
10495
  // regardless of which instance owned it, so a sibling-retired incarnation's copied credential
10484
10496
  // is refused at this control surface too. Ownership gates only the DESTRUCTIVE sweep below.
10485
10497
  this.retiredPrincipals.add(principalKey(row.owner, row.actor).key);
@@ -10507,7 +10519,7 @@ export class Manager {
10507
10519
  }
10508
10520
  // ADOPTION is genuine membership: a slot backed by a live managed agent THIS process owns
10509
10521
  // at the SAME uid is never an orphan (empty at boot; exactly the adopted set at the
10510
- // post-adoption sweep — the fix for the F3 resume hole).
10522
+ // post-adoption sweep — the fix for the resume hole).
10511
10523
  const live = this.agents.get(row.alias);
10512
10524
  const adopted = live !== undefined && live.lifecycleUid === row.lifecycleUid;
10513
10525
  if (adopted) {
@@ -10637,7 +10649,7 @@ export class Manager {
10637
10649
  this.releaseStaticReconcileSweep();
10638
10650
  }
10639
10651
  }
10640
- /** The F5(a) membership gate (Unit B, the F5-bind design): decide a control caller by its
10652
+ /** The membership gate: decide a control caller by its
10641
10653
  * AUTHENTICATED wire principal. A LIVE managed slot passes (unless terminalizing); a RETIRING
10642
10654
  * hold or a RETIRED static incarnation refuses even with a tier-valid JWT (the
10643
10655
  * copied-credential vector — its subject can never collide with a successor's, so this match
@@ -10647,16 +10659,16 @@ export class Manager {
10647
10659
  for (const a of this.agents.values()) {
10648
10660
  if (this.managedPrincipal(a) === caller)
10649
10661
  return a.terminalizing
10650
- ? `the caller's lifecycle ${a.lifecycleUid} is terminalizing; control is refused from the first terminal step (F5)`
10662
+ ? `the caller's lifecycle ${a.lifecycleUid} is terminalizing; control is refused from the first terminal step`
10651
10663
  : undefined;
10652
10664
  }
10653
10665
  for (const [name, hold] of this.retiring) {
10654
10666
  const held = hold.target.userOwner ? hold.target.id : principalKey(DEV_OWNER, hold.target.id).key;
10655
10667
  if (held === caller)
10656
- return `the caller's lifecycle ${hold.target.lifecycleUid} (name "${name}") is retiring; a retiring incarnation's credential holds no control authority (F5)`;
10668
+ return `the caller's lifecycle ${hold.target.lifecycleUid} (name "${name}") is retiring; a retiring incarnation's credential holds no control authority`;
10657
10669
  }
10658
10670
  if (this.retiredPrincipals.has(caller))
10659
- return "the caller's lifecycle is retired; a retired incarnation's credential holds no control authority (F5)";
10671
+ return "the caller's lifecycle is retired; a retired incarnation's credential holds no control authority";
10660
10672
  return undefined;
10661
10673
  }
10662
10674
  /** Open a short-lived PROVISIONER connection, run the onboarding ops on it, and drain it (closure (ii),
@@ -10717,7 +10729,7 @@ export class Manager {
10717
10729
  /** Persist a peer-defined persona as config. After this, `start name` auto-discovers
10718
10730
  * .cotal/agents/<name>.md and the connector applies its persona/model at spawn.
10719
10731
  *
10720
- * CONTENT vs POLICY (P6): `capabilities` and `owner` still have no slot, so a peer cannot
10732
+ * CONTENT vs POLICY: `capabilities` and `owner` still have no slot, so a peer cannot
10721
10733
  * self-grant spawn or claim ownership. Channel grants, role, and agent are content: they may
10722
10734
  * arrive as explicit arguments or as a leading frontmatter block inside `persona`. A prompt
10723
10735
  * that starts with `---` is parsed and merged (explicit args win) so the written file has one