@agfpd/iapeer 0.2.77 → 0.2.79

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (113) hide show
  1. package/LICENSE +202 -0
  2. package/README.md +2 -2
  3. package/package.json +21 -2
  4. package/src/cli/index.ts +95 -78
  5. package/src/connect/index.ts +4 -4
  6. package/src/core/constants.ts +3 -3
  7. package/src/core/normalize.ts +2 -2
  8. package/src/create/index.ts +1 -1
  9. package/src/daemon/index.ts +5 -5
  10. package/src/daemon/main.ts +9 -9
  11. package/src/enable/index.ts +19 -18
  12. package/src/enable/provisionCommand.ts +10 -10
  13. package/src/identity/index.ts +2 -2
  14. package/src/identity/profileStandard.ts +8 -8
  15. package/src/init/index.ts +10 -10
  16. package/src/install/index.ts +2 -2
  17. package/src/install/signing.ts +3 -4
  18. package/src/launch/adapters/claude.ts +7 -7
  19. package/src/launch/adapters/codex.ts +5 -6
  20. package/src/launch/canary.ts +8 -8
  21. package/src/launch/cmdlog.ts +3 -3
  22. package/src/launch/composeSystemPrompt.ts +2 -1
  23. package/src/launch/index.ts +5 -6
  24. package/src/launch/invocation.ts +1 -1
  25. package/src/launch/launchd.ts +14 -15
  26. package/src/launch/launchdRun.ts +6 -6
  27. package/src/launch/nativeMemory.ts +9 -9
  28. package/src/launch/ptyHost.ts +1 -1
  29. package/src/launch/readyGateModel.ts +3 -3
  30. package/src/launch/types.ts +7 -7
  31. package/src/lifecycle/eventlog.ts +2 -2
  32. package/src/lifecycle/index.ts +21 -21
  33. package/src/lifecycle/queue.ts +1 -1
  34. package/src/onboard/index.ts +19 -13
  35. package/src/onboard/memory.ts +5 -5
  36. package/src/onboard/steps.ts +8 -9
  37. package/src/provision/index.ts +2 -3
  38. package/src/runtime/deploy.ts +2 -2
  39. package/src/runtime/index.ts +4 -4
  40. package/src/shadow/gate.ts +6 -6
  41. package/src/shadow/index.ts +2 -2
  42. package/src/status/index.ts +11 -11
  43. package/src/storage/index.ts +1 -1
  44. package/src/supervisor/daemon.ts +20 -13
  45. package/src/transport/index.ts +11 -11
  46. package/src/update/index.ts +4 -4
  47. package/src/cli/cli.test.ts +0 -603
  48. package/src/cli/listTui.test.ts +0 -71
  49. package/src/codec/codec.test.ts +0 -271
  50. package/src/connect/connect.test.ts +0 -163
  51. package/src/core/constants.test.ts +0 -32
  52. package/src/core/normalize.test.ts +0 -98
  53. package/src/create/create.test.ts +0 -143
  54. package/src/daemon/daemon-http.test.ts +0 -114
  55. package/src/daemon/daemon.test.ts +0 -267
  56. package/src/daemon/main.test.ts +0 -413
  57. package/src/enable/enable.test.ts +0 -95
  58. package/src/enable/provisionCommand.test.ts +0 -107
  59. package/src/identity/identity.test.ts +0 -279
  60. package/src/identity/profileStandard.test.ts +0 -204
  61. package/src/init/init.test.ts +0 -215
  62. package/src/init/runtime-resolve.test.ts +0 -49
  63. package/src/install/install.test.ts +0 -66
  64. package/src/install/signing.test.ts +0 -84
  65. package/src/launch/bootdeliver.test.ts +0 -108
  66. package/src/launch/bootstrap.test.ts +0 -56
  67. package/src/launch/canary.test.ts +0 -346
  68. package/src/launch/cmdlog.test.ts +0 -94
  69. package/src/launch/codexHooksTrust.test.ts +0 -248
  70. package/src/launch/composeSystemPrompt.layers.test.ts +0 -445
  71. package/src/launch/composeSystemPrompt.test.ts +0 -145
  72. package/src/launch/hostComposerOccupied.test.ts +0 -63
  73. package/src/launch/invocation.test.ts +0 -174
  74. package/src/launch/launch.test.ts +0 -359
  75. package/src/launch/launchd.test.ts +0 -499
  76. package/src/launch/nativeMemory.test.ts +0 -193
  77. package/src/launch/ptyHost.test.ts +0 -38
  78. package/src/launch/readyGateModel.test.ts +0 -72
  79. package/src/launch/sockdir.test.ts +0 -74
  80. package/src/lifecycle/eventlog.test.ts +0 -114
  81. package/src/lifecycle/lifecycle.test.ts +0 -1056
  82. package/src/lifecycle/queue.test.ts +0 -185
  83. package/src/onboard/memory.test.ts +0 -157
  84. package/src/onboard/onboard.test.ts +0 -39
  85. package/src/onboard/steps.test.ts +0 -201
  86. package/src/provision/provision.test.ts +0 -288
  87. package/src/registry/registry.test.ts +0 -432
  88. package/src/runtime/runtime.test.ts +0 -226
  89. package/src/runtime/update.test.ts +0 -192
  90. package/src/shadow/enumerate-skip.test.ts +0 -67
  91. package/src/shadow/gate.test.ts +0 -286
  92. package/src/shadow/install.test.ts +0 -153
  93. package/src/shadow/shadow.test.ts +0 -42
  94. package/src/status/status.test.ts +0 -208
  95. package/src/storage/peers-home.test.ts +0 -34
  96. package/src/storage/rotatelog.test.ts +0 -44
  97. package/src/storage/storage.test.ts +0 -65
  98. package/src/supervisor/attached-marker.test.ts +0 -84
  99. package/src/supervisor/boot.test.ts +0 -66
  100. package/src/supervisor/client.attach.test.ts +0 -107
  101. package/src/supervisor/daemon.test.ts +0 -213
  102. package/src/supervisor/deliver.test.ts +0 -58
  103. package/src/supervisor/panelog.test.ts +0 -95
  104. package/src/supervisor/protocol.test.ts +0 -222
  105. package/src/supervisor/selfargv.test.ts +0 -22
  106. package/src/supervisor/spinner-strip.test.ts +0 -52
  107. package/src/transport/compactDone.test.ts +0 -144
  108. package/src/transport/composerQueue.test.ts +0 -204
  109. package/src/transport/hostControl.test.ts +0 -87
  110. package/src/transport/hostDeliver.test.ts +0 -137
  111. package/src/transport/hostLive.test.ts +0 -50
  112. package/src/transport/routesend-wake.test.ts +0 -357
  113. package/src/update/update.test.ts +0 -208
@@ -185,7 +185,7 @@ function defaultEntrypointArgv(env: NodeJS.ProcessEnv = process.env): string[] {
185
185
  // ─────────────────────────────────────────────────────────────────────────────
186
186
 
187
187
  /** Resolve the current gui-domain uid for `launchctl bootstrap gui/<uid>`. NEVER
188
- * falls back to 0 (that would aim the ROOT gui domain — audit #29); a non-numeric
188
+ * falls back to 0 (that would aim the ROOT gui domain); a non-numeric
189
189
  * `id -u` result throws. */
190
190
  function currentUid(): string {
191
191
  const r = spawnSync('id', ['-u'], { encoding: 'utf8' })
@@ -216,12 +216,11 @@ function isLaunchdLoaded(label: string, uid: string): boolean {
216
216
  }
217
217
 
218
218
  // ─────────────────────────────────────────────────────────────────────────────
219
- // UNDEAD-JOB-SAFE bootstrap core (boris's live find 10.06, connect acceptance):
220
- // after `launchctl bootout` launchd dismantles the job ASYNCHRONOUSLY — an
221
- // immediate `bootstrap` hits the still-listed "undead" job and fails with
222
- // exit 5 "Input/output error" (the known PP race class, canon «Жизненный цикл
223
- // запуска persistent-peer и точки гонки»). On the connect flow that left the
224
- // WHOLE fleet's telegram router down. This core makes every restart-shaped flow
219
+ // UNDEAD-JOB-SAFE bootstrap core: after `launchctl bootout` launchd dismantles
220
+ // the job ASYNCHRONOUSLY — an immediate `bootstrap` hits the still-listed
221
+ // "undead" job and fails with exit 5 "Input/output error" (the known
222
+ // persistent-peer race class). On the connect flow this left the WHOLE fleet's
223
+ // telegram router down. This core makes every restart-shaped flow
225
224
  // (stop→start, connect router restart, update-runtime) survive the race:
226
225
  // (1) WAIT-FOR-GONE: while the job is still listed, poll `print` up to
227
226
  // goneTimeoutMs. Vanished → proceed to bootstrap. STILL listed at the
@@ -363,13 +362,13 @@ export function cycleDaemonCore(
363
362
  * Restart the foundation daemon (`com.agfpd.iapeer`) onto the binary currently on
364
363
  * disk — `launchctl bootout` + `bootstrap`, DELIBERATELY NOT `kickstart -k`.
365
364
  *
366
- * LWCR (launch constraints, the deploy bomb — PROVEN LIVE 12.06 twice): after a
365
+ * LWCR (launch constraints, the deploy bomb — PROVEN LIVE twice): after a
367
366
  * binary replacement, launchd's managed Launch Constraint for the REGISTERED job
368
367
  * can still pin the OLD binary's signature — a kickstart then respawns into an
369
368
  * EX_CONFIG(78) crash-loop (`state = spawn scheduled`, exit 78 from launchd
370
- * itself; the binary runs fine by hand). Reproduced on com.iapeer.arthur
371
- * (telegram-runtime's v0.14.0 deploy, bridge down 6 min) AND on com.agfpd.iapeer
372
- * itself (the 0.2.43 deploy — daemon down until the manual cure). The ONLY cure
369
+ * itself; the binary runs fine by hand). Reproduced on a com.iapeer.* infra job
370
+ * (a telegram-runtime deploy, bridge down 6 min) AND on com.agfpd.iapeer
371
+ * itself (a daemon deploy — daemon down until the manual cure). The ONLY cure
373
372
  * is a full re-registration: bootout drops the job (and its constraint),
374
373
  * bootstrap registers it against the binary now on disk. kickstart is
375
374
  * structurally insufficient for this class.
@@ -441,14 +440,14 @@ export function launchctlBootstrap(
441
440
  // real launchd job regardless of where the plist file lives or what IAPEER_ROOT is.
442
441
  // So the skip MUST consult BOTH the passed env AND the PROCESS env: a test harness
443
442
  // that passes an explicit env (isolated IAPEER_ROOT) but omits the flag would
444
- // otherwise bypass the guard and load a real job (this exact hole bit B1 once). The
443
+ // otherwise bypass the guard and load a real job (this exact hole bit once). The
445
444
  // process-level flag (set by `bun test`) forces the skip even then — mirror of the
446
445
  // registry's fail-closed sandbox lesson. A live e2e proof runs with NEITHER flag set.
447
446
  if (env.IAPEER_TEST_SANDBOX === '1' || process.env.IAPEER_TEST_SANDBOX === '1') {
448
447
  return { state: 'skipped-sandbox', label, detail: 'IAPEER_TEST_SANDBOX=1 — not loading a real launchd job' }
449
448
  }
450
449
  const uid = currentUid()
451
- // UNDEAD-JOB-SAFE core (boris's connect-acceptance find): wait for a booted-out
450
+ // UNDEAD-JOB-SAFE core: wait for a booted-out
452
451
  // job to actually vanish, then bootstrap with backoff. A genuinely LIVE job
453
452
  // reads 'already-loaded' (idempotent no-op, same semantics as before); only a
454
453
  // job that stays failing through every attempt reads 'failed'.
@@ -525,7 +524,7 @@ export function installAlwaysOnPlist(opts: InstallAlwaysOnPlistOptions): string
525
524
  // Resolve home the SAME way launchAgentsDir does (env.HOME first) so a test/
526
525
  // sandbox overriding HOME keeps the PATH fallback and the plist dir in step.
527
526
  const home = env.HOME?.trim() || homedir()
528
- const defaultPath = `${home}/.bun/bin:${home}/.local/bin:/opt/homebrew/bin:/usr/bin:/bin`
527
+ const defaultPath = `${home}/.bun/bin:${home}/.local/bin:/opt/homebrew/bin:/opt/homebrew/sbin:/usr/local/bin:/usr/bin:/bin`
529
528
  const environment: Record<string, string> = {
530
529
  PEER_PERSONALITY: opts.personality,
531
530
  PEER_RUNTIME: opts.runtime,
@@ -533,7 +532,7 @@ export function installAlwaysOnPlist(opts: InstallAlwaysOnPlistOptions): string
533
532
  PATH: opts.path ?? env.PATH ?? defaultPath,
534
533
  }
535
534
  // Propagate the non-default path overrides into the plist env (mirror of the daemon
536
- // plist, audit #26): in PRODUCTION these are unset → nothing is baked, the always-on
535
+ // plist): in PRODUCTION these are unset → nothing is baked, the always-on
537
536
  // session uses the real ~/.iapeer + /tmp sockets (correct). In a SANDBOX they ARE set
538
537
  // → baked, so a sandboxed infra peer's run-infra resolves the SAME isolated root +
539
538
  // socket dir the provision used (its tmux endpoint lands in the sandbox, not /tmp).
@@ -46,11 +46,11 @@ function sessionAlive(sock: string, identity: string): boolean {
46
46
  * last one) — the signal-exit counterpart of lifecycle.killSession, local to avoid
47
47
  * a launch ⇆ lifecycle import. Canary-signaled first: this is a DELIBERATE stop.
48
48
  *
49
- * Closes the live грабля «bootout не убивает поллера» (boris 10.06, second
50
- * strike): `launchctl bootout` TERMs THIS watcher process, but the detached tmux
51
- * server — and the runtime poller inside it — survived holding STALE in-memory
52
- * state (e.g. a notifier trigger's old target after a same-id replace), so a
53
- * plain bootout+bootstrap was NOT a real restart. With the teardown, the session
49
+ * Fixes the defect where bootout did not kill the poller: `launchctl bootout`
50
+ * TERMs THIS watcher process, but the detached tmux server — and the runtime
51
+ * poller inside it — survived holding STALE in-memory state (e.g. a notifier
52
+ * trigger's old target after a same-id replace), so a plain bootout+bootstrap was
53
+ * NOT a real restart. With the teardown, the session
54
54
  * dies with its watcher: bootout = full stop, bootstrap = fresh bring-up that
55
55
  * re-reads durable state. `iapeer stop` (bootout + killSession) is unchanged —
56
56
  * both paths now converge on the same end state.
@@ -196,7 +196,7 @@ export async function runAlwaysOn(personality: string, runtime: string, cwd: str
196
196
  }
197
197
  // Signal-initiated exit (bootout / shutdown / kickstart -k) tears the session
198
198
  // down WITH this watcher — without it the detached tmux poller outlived bootout
199
- // holding stale in-memory state (см. teardownAlwaysOnSession). A natural session
199
+ // holding stale in-memory state (see teardownAlwaysOnSession). A natural session
200
200
  // death (stop=false) skips this: there is nothing to tear down, exit 0 →
201
201
  // KeepAlive respawns a fresh bring-up.
202
202
  // Signal-initiated stop (bootout / shutdown / kickstart -k) tears the session down WITH this watcher.
@@ -1,5 +1,5 @@
1
1
  // Native runtime-memory levers (контракт «Слот памяти» §Native-память рантаймов
2
- // при занятом слоте, agreed 10.06). The CORE canonizes the lever FORMS — runtime
2
+ // при занятом слоте). The CORE canonizes the lever FORMS — runtime
3
3
  // knowledge lives in the runtime layer, like deliveryMarkers/buildArgv:
4
4
  //
5
5
  // claude: merge `"autoMemoryEnabled": false` into `<cwd>/.claude/settings.json`
@@ -8,8 +8,8 @@
8
8
  //
9
9
  // Both files CARRY FOREIGN BLOCKS (plugin enables, statusline wrappers, …) —
10
10
  // every write here is a NO-CLOBBER merge (parse/patch + atomic temp+rename),
11
- // never a rewrite. Forms verified live on the fleet (12/12 + behavioral smoke
12
- // 09.06; codex reads project-local config when the cwd is TRUSTED — see
11
+ // never a rewrite. Forms verified live on the fleet (12/12 + behavioral smoke;
12
+ // codex reads project-local config when the cwd is TRUSTED — see
13
13
  // preTrustCodexCwd).
14
14
  //
15
15
  // `off` writes the explicit disable; `on` REMOVES the key (restores the
@@ -130,8 +130,8 @@ function applyCodexLever(cwd: string, state: NativeMemoryState): LeverOutcome {
130
130
  * `[projects."<path>"] trust_level = "trusted"` blocks. Honors $CODEX_HOME
131
131
  * first, SAME as init's codexConfigPath — the two writers of this shared prod
132
132
  * file must respect one override set, or a sandbox isolating one path still
133
- * leaks through the other (live-caught by iapeer-memory's D4 11.06: an
134
- * IAPEER_ROOT sandbox pre-trusted a throwaway cwd into the PROD config). */
133
+ * leaks through the other (live defect: an IAPEER_ROOT sandbox pre-trusted a
134
+ * throwaway cwd into the PROD config). */
135
135
  export function codexGlobalConfigPath(env: NodeJS.ProcessEnv = process.env): string {
136
136
  const codexHome = env.CODEX_HOME?.trim()
137
137
  if (codexHome) return join(codexHome, 'config.toml')
@@ -152,8 +152,8 @@ export function codexGlobalConfigPath(env: NodeJS.ProcessEnv = process.env): str
152
152
  export function preTrustCodexCwd(cwd: string, env: NodeJS.ProcessEnv = process.env): LeverOutcome {
153
153
  const path = codexGlobalConfigPath(env)
154
154
  try {
155
- // Codex keys trust on the RESOLVED real path (live fact, 10.06 acceptance: the
156
- // /tmp → /private/tmp symlink made a literal-path entry MISS and left the lever
155
+ // Codex keys trust on the RESOLVED real path (live fact: the /tmp →
156
+ // /private/tmp symlink made a literal-path entry MISS and left the lever
157
157
  // inert — trust entries written by codex itself are all /private/tmp/...).
158
158
  let real = cwd
159
159
  try {
@@ -175,8 +175,8 @@ export function preTrustCodexCwd(cwd: string, env: NodeJS.ProcessEnv = process.e
175
175
  /**
176
176
  * Remove a peer's pre-trust entry from the codex GLOBAL config — the reap-side
177
177
  * counterpart of preTrustCodexCwd (backlog «remove не чистит pre-trust», made
178
- * live by iapeer-memory's D4 11.06: every sandboxed/throwaway codex birth left
179
- * a stale `[projects."<cwd>"]` block in the PROD config forever). Removes the
178
+ * live by a defect: every sandboxed/throwaway codex birth left a stale
179
+ * `[projects."<cwd>"]` block in the PROD config forever). Removes the
180
180
  * section for BOTH the resolved and the literal path form (the writer stores
181
181
  * realpath, but a since-deleted cwd cannot be resolved anymore — match either),
182
182
  * strictly section-scoped: from the `[projects."<p>"]` header up to the next
@@ -27,7 +27,7 @@ export const HOST_COLS = 220
27
27
  export const HOST_ROWS = 50
28
28
 
29
29
  /** Per-peer pty-hosting decision. pty-supervisor hosting is the DEFAULT — a fresh peer is hosted under
30
- * the supervisor (not `tmux new-session`) WITHOUT any marker (Артур: «pty из коробки»). A peer opts OUT
30
+ * the supervisor (not `tmux new-session`) WITHOUT any marker (pty out of the box). A peer opts OUT
31
31
  * to the legacy tmux path with a `<logDir>/<identity>.no-pty-host` marker (the dev/debug escape, valid
32
32
  * only while tmux still exists). Read in TWO places, both of which want "is this peer pty-hosted": the
33
33
  * spawn-gate (launch — host vs tmux) and the shadow observer (skip pty-hosted peers, they have no tmux
@@ -6,7 +6,7 @@
6
6
  // emulation of the daemon's pane-log — the SAME raw child bytes tmux renders), keeping the SAME
7
7
  // production predicate (adapter.isInputReady) and an UNCONDITIONAL capture-pane fallback.
8
8
  //
9
- // Why this is low-radius (boris's frame): it is a viewport-SOURCE swap, NOT a predicate change.
9
+ // Why this is low-radius: it is a viewport-SOURCE swap, NOT a predicate change.
10
10
  // The model verdict was validated 0-divergence vs capture-pane on the readiness predicate by the
11
11
  // burn-in gate (incl. the codex resume-with-history splash-off-screen center cell) before any peer
12
12
  // is flipped. It touches ONLY the isInputReady INPUT — dialog detection upstream and the delivery +
@@ -127,10 +127,10 @@ export async function paneLogViewport(log: string, cols: number, rows: number):
127
127
  * tmux predicate's capture-fail-is-busy stance — prefer a false-busy hold over a false-free paste into
128
128
  * a human composer.
129
129
  *
130
- * NB (sequence, boris): this is the DETECTION primitive. The end-to-end gate (attached-supervisor-
130
+ * NB (sequence): this is the DETECTION primitive. The end-to-end gate (attached-supervisor-
131
131
  * client + enqueue + host-aware drain) lands with the attach client in Ф0b-3 — until a human can
132
132
  * attach to a hosted session there is no human composer to protect. The caller composes the pane-log
133
- * path from logDir+identity (the threaded `logDir` finding), mirroring `waitHostReady`.
133
+ * path from logDir+identity, mirroring `waitHostReady`.
134
134
  */
135
135
  export async function paneLogComposerOccupied(log: string, cols: number, rows: number, runtime: string): Promise<boolean> {
136
136
  const term = await loadPaneLogModel(log, cols, rows)
@@ -30,7 +30,7 @@ export type { PublicPeerSummary } from '../registry/index.ts'
30
30
  // in organically here — no special-casing.
31
31
  // 5. Doctrine fragments: every `*.md` in the `.iapeer/fragments/` subdir, global
32
32
  // (~/.iapeer/fragments/) + local (<cwd>/.iapeer/fragments/) merged per stem.
33
- // PRIMITIVE-owned, machine-regenerated (iapeer-memory's guide + note index, …)
33
+ // PRIMITIVE-owned, machine-regenerated (e.g. a memory provider's guide + note index, …)
34
34
  // — a dedicated namespace kept OUT of the hand-authored IAPEER.md doctrine so
35
35
  // the auto-writer and the human writer never share a file. Sits LAST: the most
36
36
  // volatile layer, so edits to it never disturb the stable prefix above.
@@ -160,13 +160,13 @@ export interface LaunchSpec {
160
160
  }
161
161
 
162
162
  // ─────────────────────────────────────────────────────────────────────────────
163
- // Delivery markers — the tui submit surface (owned by the adapter, 07.06 refactor)
163
+ // Delivery markers — the tui submit surface (owned by the adapter)
164
164
  // ─────────────────────────────────────────────────────────────────────────────
165
165
 
166
166
  /**
167
167
  * Markers the transport submit path (submitIntoTui) needs to detect that a
168
168
  * bracketed paste landed in the input row before pressing Enter. Contract refactor
169
- * 07.06 (docs/Рантайм-адаптеры): the ADAPTER owns delivery markers — they moved out
169
+ * (docs/Рантайм-адаптеры): the ADAPTER owns delivery markers — they moved out
170
170
  * of the transport's hardcoded `PROMPT_GLYPHS = ['❯','›']` union so the generic
171
171
  * submit logic carries NO runtime strings and a new runtime ships its own glyph
172
172
  * with its adapter, not by editing transport.
@@ -227,8 +227,8 @@ export interface RuntimeAdapter {
227
227
 
228
228
  /**
229
229
  * Delivery markers for the transport submit path (submitIntoTui) — the input-
230
- * prompt glyph(s) and optional "paste landed" patterns. The adapter OWNS them
231
- * (07.06 refactor): transport reads them from here instead of a hardcoded glyph
230
+ * prompt glyph(s) and optional "paste landed" patterns. The adapter OWNS them:
231
+ * transport reads them from here instead of a hardcoded glyph
232
232
  * union. A router declares `{ promptGlyphs: [] }` (no submit surface).
233
233
  */
234
234
  deliveryMarkers: DeliveryMarkers
@@ -236,8 +236,8 @@ export interface RuntimeAdapter {
236
236
  /**
237
237
  * If set, launch REFUSES unless the peer's intelligence equals this value
238
238
  * (fail-loud). telegram → 'natural' — it is a human channel; launching an
239
- * artificial/absent peer on it is a category error (PP held a FATAL guard in two
240
- * places). Most adapters omit it (no nature gate). Source of intelligence →
239
+ * artificial/absent peer on it is a category error (the persistent-peer path held
240
+ * a FATAL guard in two places). Most adapters omit it (no nature gate). Source of intelligence →
241
241
  * docs/Идентичность; enforced by the launch primitive against LaunchSpec.intelligence.
242
242
  */
243
243
  requiresIntelligence?: Intelligence
@@ -1,6 +1,6 @@
1
1
  // Lifecycle event log — the daemon's DURABLE, ROTATED trace of every lifecycle
2
- // DECISION it makes. This is the observability gap the boris-fresh incident hit:
3
- // a peer woke fresh and there was NO record of when/how its prior session ended,
2
+ // DECISION it makes. This closes an observability gap: a peer could wake fresh
3
+ // with NO record of when/how its prior session ended,
4
4
  // nor of the daemon's fresh-vs-resume reasoning, because superviseTick's outcomes
5
5
  // were dropped and the daemon never wrote a decision line anywhere.
6
6
  //
@@ -76,16 +76,16 @@ export interface LifecycleConfig {
76
76
  /** wake_policy:ephemeral M2 — the quiet window (seconds of activity-proxy silence)
77
77
  * after which an ARMED ephemeral session (it has sent its outbound reply) is
78
78
  * reaped. Long enough for the post-reply housekeeping writes (operative notes)
79
- * to keep resetting it; far below idleSecs. ASSUMPTION (boris, by design): the
79
+ * to keep resetting it; far below idleSecs. ASSUMPTION (by design): the
80
80
  * transcript mtime is a LIVENESS proxy — "no longer writing" — not a semantic
81
81
  * "done" signal. */
82
82
  ephemeralQuietSecs: number
83
83
  /** wake_policy:ephemeral — the UNARMED idle bound (seconds): an ephemeral session
84
84
  * that never armed (finished silently / lost its arm to a daemon-restart window)
85
- * is reaped after this much activity-proxy silence. Live case (scriber 10.06):
86
- * a worker that ended «тихо» without its final outbound stalled its M3 FIFO for
87
- * the FULL generic idleSecs (1 h) — with serial drain that blocks the whole
88
- * conveyor per silent worker. Bound chosen ≫ the legitimate silent-tool case
85
+ * is reaped after this much activity-proxy silence. Live case: a worker that
86
+ * ended silently without its final outbound stalled its M3 FIFO for the FULL
87
+ * generic idleSecs (1 h) — with serial drain that blocks the whole conveyor per
88
+ * silent worker. Bound chosen ≫ the legitimate silent-tool case
89
89
  * (sleep-180) and ≪ idleSecs. DOCUMENTED RISK: an ephemeral worker whose tool
90
90
  * stays silent longer than this MID-TASK is reaped and its consumed queue item
91
91
  * is lost — ephemeral workers must emit activity (or their reply) within it. */
@@ -215,7 +215,7 @@ export function clearStopped(cfg: LifecycleConfig, identity: string): void {
215
215
  //
216
216
  // .idle-reaped : the CLEAN-PARK marker — written when the daemon idle-reaps the
217
217
  // session AND by the deliberate `stop` verb (both are daemon/operator-initiated
218
- // parks, not faults; boris 10.06: stop→start must survive ≥ idle-reap).
218
+ // parks, not faults; stop→start must survive ≥ idle-reap).
219
219
  // Presence on the next wake = session was parked cleanly = RESUME-eligible.
220
220
  // ABSENT on a dead session = it died on its own (crash / self-close) = FRESH.
221
221
  // (resolver branch 3.)
@@ -226,9 +226,9 @@ export function clearStopped(cfg: LifecycleConfig, identity: string): void {
226
226
  // .topic : the topic the current/last session last WORKED ON (executor
227
227
  // fresh-vs-resume discriminator). Written at wake (the boot topic) AND on
228
228
  // every live delivery carrying a different non-empty topic (the daemon/CLI
229
- // compositions wire transport's noteLiveTopic seam to writeTopic — defect
230
- // 11.06: a wake-only marker goes stale over a long session and false-freshes
231
- // the stop→start resume the clean park promised).
229
+ // compositions wire transport's noteLiveTopic seam to writeTopic — defect:
230
+ // a wake-only marker goes stale over a long session and false-freshes the
231
+ // stop→start resume the clean park promised).
232
232
  // .ephemeral-armed : wake_policy:ephemeral M2 — set by the DAEMON when it routes
233
233
  // an OUTBOUND send from an ephemeral worker (its single final reply, ADR-006:
234
234
  // workers send no intermediate messages, so outbound ⇒ the task is answered).
@@ -404,8 +404,8 @@ export function writeTopic(cfg: LifecycleConfig, identity: string, topic: string
404
404
  * and the M3 `.queue/` dir — everything matching `<identity>.*` (the dot
405
405
  * delimiter keeps `claude-bob` from ever matching `claude-bob2.*`).
406
406
  *
407
- * Consumer: `iapeer remove` (live defect, boris 10.06 cutover): a removed peer's
408
- * stale `.stopped`/`.idle-reaped` survived in state/lifecycle, and a NEWBORN peer
407
+ * Consumer: `iapeer remove` (live defect): a removed peer's stale
408
+ * `.stopped`/`.idle-reaped` survived in state/lifecycle, and a NEWBORN peer
409
409
  * REUSING the personality inherited the dead namesake's parking — the daemon
410
410
  * refused to wake it (`mode=refused cause=stopped`). Identity-keyed state must
411
411
  * die with the registry record. Deliberately NOT called at birth: provision runs
@@ -538,7 +538,7 @@ export function resolveWakeMode(
538
538
  return { resume: false, cause: 'ephemeral-policy' }
539
539
  }
540
540
  // 3a. NOT parked-clean → FRESH either way, but tell the two cases apart in the
541
- // cause (boris finding, 10.06): a peer with NO transcript at all never ran here —
541
+ // cause: a peer with NO transcript at all never ran here —
542
542
  // that is its FIRST-ever wake, not a crash. (A peer whose transcripts were rotated
543
543
  // away also reads first-wake — equally honest: there is nothing it could resume.)
544
544
  if (!hasIdleReaped(cfg, identity)) {
@@ -659,9 +659,9 @@ function sessionAlive(sock: string, identity: string): boolean {
659
659
  return hostSessionAlive(identity) || tmux(sock, 'has-session', '-t', identity).ok
660
660
  }
661
661
 
662
- /** Death-class tag for a gone session (live case: iapeer-memory 10.06 — the WHOLE
663
- * tmux server died by SIGKILL-class and exits.log stayed empty, because the
664
- * pane-died hook needs a living tmux event loop). Two distinguishable classes:
662
+ /** Death-class tag for a gone session (live case: the WHOLE tmux server died by
663
+ * SIGKILL-class and exits.log stayed empty, because the pane-died hook needs a
664
+ * living tmux event loop). Two distinguishable classes:
665
665
  * - `session-gone` — the server on the socket still ANSWERS but the session is not
666
666
  * there: a pane died inside a living server → the pane-died hook had its chance,
667
667
  * exits.log should carry the cause.
@@ -1020,7 +1020,7 @@ export async function wakeOrSpawn(args: WakeArgs, deps: WakeDeps = {}): Promise<
1020
1020
  if (result.status === 'FAILED') {
1021
1021
  // enroll-on-FAILED (0.2.57): launch can return FAILED on a ready-gate false-negative
1022
1022
  // (model-did-not-process-task) while the tmux session + its process actually SURVIVE —
1023
- // observed live (ttlverify, 13.06). Since 0.2.55 removed the self-TTL, such a remnant
1023
+ // observed live. Since 0.2.55 removed the self-TTL, such a remnant
1024
1024
  // would be an UNBOUNDED orphan: no .session → invisible to superviseTick's idle-reap,
1025
1025
  // and no wall-clock backstop. If the session is in fact ALIVE, enroll it so the
1026
1026
  // activity-aware idle-reap stays the UNIVERSAL bound (a genuinely wedged session is
@@ -1141,7 +1141,7 @@ export function superviseTick(cfg: LifecycleConfig, deps: SuperviseDeps = {}): S
1141
1141
  }
1142
1142
  const sock = buildSocketPath(s.runtime, s.personality, cfg.sockDir)
1143
1143
  if (!sessionAlive(sock, s.identity)) {
1144
- // A DELIBERATE stop is not a death (boris 10.06): the stop verb parks clean
1144
+ // A DELIBERATE stop is not a death: the stop verb parks clean
1145
1145
  // (.stopped + .idle-reaped) and kills the session itself. Catch-up branch for
1146
1146
  // a stop that raced this tick (or a pre-fix stop): drop the state quietly —
1147
1147
  // no crash-loop entry, no death class — and ensure the clean-park marker so
@@ -1222,14 +1222,14 @@ export function superviseTick(cfg: LifecycleConfig, deps: SuperviseDeps = {}): S
1222
1222
  trace({ identity: s.identity, action: 'reaped-ephemeral', age: `${ageSecs}s`, outcome: 'ephemeral-done' })
1223
1223
  continue
1224
1224
  }
1225
- // UNARMED ephemeral idle bound (live case scriber 10.06): a worker that ended
1225
+ // UNARMED ephemeral idle bound (live case): a worker that ended
1226
1226
  // SILENTLY (no final outbound → never armed; or its arm was lost to a CLI/
1227
1227
  // daemon-restart window) used to wait out the FULL generic idleSecs (1 h) —
1228
1228
  // and the M3 serial drain waits for the session's death, so ONE silent worker
1229
1229
  // stalled its whole conveyor. This bound is the defense-in-depth backstop:
1230
1230
  // ≫ the legitimate silent-tool case (sleep-180), ≪ idleSecs. The ШТАТНЫЙ
1231
1231
  // silent-finish path is `iapeer self-done` (arm without waking anyone —
1232
- // Артур's invariant «нет пустых пробуждений» stays intact); this branch only
1232
+ // the invariant «нет пустых пробуждений» stays intact); this branch only
1233
1233
  // bounds the damage when a worker does neither. Policy reap: NO .idle-reaped
1234
1234
  // (ephemeral never resumes), NO recordDeath (the ring counts faults).
1235
1235
  if (isEphemeralPeer(s.cwd) && ageSecs > cfg.ephemeralUnarmedIdleSecs) {
@@ -1299,7 +1299,7 @@ export function superviseTick(cfg: LifecycleConfig, deps: SuperviseDeps = {}): S
1299
1299
  * no runtime so resolveWakeRuntime falls through to the registry default (H5 — registry
1300
1300
  * is the only source of wake-runtime truth). Pinning broke the fleet-switch procedure
1301
1301
  * (`default-runtime <rt>` + `self-fresh`): the peer silently resurrected on the OLD
1302
- * runtime, ignoring the just-flipped default (live incident 12.06, claude-iapeer). A
1302
+ * runtime, ignoring the just-flipped default (live incident). A
1303
1303
  * session deliberately living on a non-default runtime has explicit paths back (wake
1304
1304
  * with an explicit runtime); an owner /new returns the peer to its declared default.
1305
1305
  */
@@ -1376,7 +1376,7 @@ export async function drainEphemeralQueue(
1376
1376
  if (sessionAlive(sock, identity)) return null // one task per session — wait for its reap
1377
1377
  const item = peekEphemeralTask(cfg, identity)
1378
1378
  if (!item) return null
1379
- // Durable drain trace (boris acceptance (a)): which item, how deep the queue.
1379
+ // Durable drain trace: which item, how deep the queue.
1380
1380
  appendLifecycleEvent(
1381
1381
  cfg.eventLogDir,
1382
1382
  { ev: 'ephemeral-drain', identity, seq: item.seq, depth: ephemeralQueueDepth(cfg, identity) },
@@ -16,7 +16,7 @@
16
16
  // concurrent enqueues can never share a seq; the loser just advances. Content
17
17
  // is complete before the link lands (no partial reads).
18
18
  //
19
- // Retry semantics (boris acceptance (b)): the consumer PEEKS (reads without
19
+ // Retry semantics: the consumer PEEKS (reads without
20
20
  // removing), wakes the worker, and removes the item ONLY on READY — a failed
21
21
  // wake leaves the task at the head for the next supervise-tick drain. See
22
22
  // drainEphemeralQueue.
@@ -51,7 +51,15 @@ export interface OnboardOptions {
51
51
  // ─────────────────────────────────────────────────────────────────────────────
52
52
 
53
53
  function runtimeBin(runtime: OnboardRuntime, env: NodeJS.ProcessEnv): string {
54
- if (runtime === 'claude') return env.IAPEER_CLAUDE_BIN?.trim() || join(env.HOME?.trim() || homedir(), '.local', 'bin', 'claude')
54
+ if (runtime === 'claude') {
55
+ const override = env.IAPEER_CLAUDE_BIN?.trim()
56
+ if (override) return override
57
+ // Prefer the native installer location (~/.local/bin/claude), but fall back to the bare,
58
+ // PATH-resolved name so an npm/Homebrew claude installed elsewhere on PATH is not read as
59
+ // runtime-missing (symmetric with codex, which defaults to the bare name).
60
+ const native = join(env.HOME?.trim() || homedir(), '.local', 'bin', 'claude')
61
+ return isExecutable(native, env) ? native : 'claude'
62
+ }
55
63
  return env.IAPEER_CODEX_BIN?.trim() || 'codex'
56
64
  }
57
65
 
@@ -65,10 +73,9 @@ function isExecutable(binOrName: string, env: NodeJS.ProcessEnv = process.env):
65
73
  }
66
74
  }
67
75
  // bare name → PRESENCE probe over PATH (`command -v` semantics), NO spawn.
68
- // History (live finds 10.06): the original `--version` ANSWER probe hung forever
69
- // (three stray probes sat 25+ min); the 10 s timeout that replaced it then
70
- // DEGRADED a live-looking codex to 'runtime-missing' (boris's catch). ROOT CAUSE
71
- // (final, boris+iapeer-memory): macOS held the cask-updated codex on a GUI
76
+ // History: the original `--version` ANSWER probe hung forever (stray probes sat
77
+ // 25+ min); the 10 s timeout that replaced it then DEGRADED a live-looking codex
78
+ // to 'runtime-missing'. ROOT CAUSE: macOS held the cask-updated codex on a GUI
72
79
  // launch-approval dialog — EVERY invocation parked before main (observed as a
73
80
  // dyld hang) until the owner confirmed the dialog. NOT a non-tty class, not a
74
81
  // broken binary. The presence probe stays right regardless: the skip question is
@@ -94,8 +101,8 @@ function isExecutable(binOrName: string, env: NodeJS.ProcessEnv = process.env):
94
101
  */
95
102
  export function isMarketplaceRegistered(runtime: OnboardRuntime, env: NodeJS.ProcessEnv = process.env): boolean {
96
103
  const bin = runtimeBin(runtime, env)
97
- // HARD TIMEOUT — a runtime CLI can wedge before main on ANY invocation (live
98
- // 10.06: macOS launch-approval pending after a cask update parked codex — first
104
+ // HARD TIMEOUT — a runtime CLI can wedge before main on ANY invocation (e.g.
105
+ // macOS launch-approval pending after a cask update parked codex — first
99
106
  // `--version`, then this very `plugin marketplace list` after the presence
100
107
  // probe let the binary through). Timeout → status null → "not registered" →
101
108
  // the add (also time-bounded) decides; never a wedge.
@@ -132,7 +139,7 @@ export function registerMarketplace(
132
139
  ref: string = MARKETPLACE_REF,
133
140
  ): { ok: boolean; detail?: string } {
134
141
  const bin = runtimeBin(runtime, env)
135
- // Same hard timeout as the list probe (the pre-main wedge class — known live
142
+ // Same hard timeout as the list probe (the pre-main wedge class — a known
136
143
  // representative: macOS launch-approval pending after a cask update) — a wedged
137
144
  // add degrades to a loud 'failed' line instead of freezing the host phase.
138
145
  const r = spawnSync(bin, ['plugin', 'marketplace', 'add', ref], { encoding: 'utf8', timeout: 120_000 })
@@ -156,11 +163,10 @@ export function isMarketplaceRegisteredAs(
156
163
  }
157
164
 
158
165
  /**
159
- * Refresh a runtime's local marketplace SNAPSHOT (контракт §Плагин провайдера,
160
- * просьба iapeer-memory): a plugin registered in the marketplace AFTER the host's
161
- * last pull reads as «unknown plugin» until the snapshot updates. Verb names
162
- * verified live 10.06: claude `plugin marketplace update <name>`, codex
163
- * `plugin marketplace upgrade <name>`.
166
+ * Refresh a runtime's local marketplace SNAPSHOT (контракт §Плагин провайдера):
167
+ * a plugin registered in the marketplace AFTER the host's last pull reads as
168
+ * «unknown plugin» until the snapshot updates. Verb names: claude `plugin
169
+ * marketplace update <name>`, codex `plugin marketplace upgrade <name>`.
164
170
  */
165
171
  export function refreshMarketplace(
166
172
  runtime: OnboardRuntime,
@@ -1,5 +1,5 @@
1
- // Memory-slot onboard step (docs/Слот памяти — контракт memory provider.md,
2
- // agreed with iapeer-memory + boris 10.06). Host-phase, optional, DEFAULT-YES:
1
+ // Memory-slot onboard step (docs/Слот памяти — контракт memory provider.md).
2
+ // Host-phase, optional, DEFAULT-YES:
3
3
  // install the default memory provider package and run ITS OWN init-verb — the
4
4
  // provider writes the slot declaration and deploys its surfaces; the core never
5
5
  // writes the slot itself. The provider OWNS the install questions (two-mode
@@ -17,7 +17,7 @@ import { normalizeIntelligenceValue } from '../core/constants.ts'
17
17
  import { readPeersIndex } from '../registry/index.ts'
18
18
  import { readMemoryProvider, type MemoryProvider } from '../status/index.ts'
19
19
 
20
- /** The distribution default provider (Артур: memory is a first-class core option). */
20
+ /** The distribution default provider (memory is a first-class core option). */
21
21
  export const DEFAULT_MEMORY_PACKAGE = '@agfpd/iapeer-memory'
22
22
 
23
23
  export interface MemoryOnboardOptions {
@@ -56,7 +56,7 @@ function defaultRunInit(
56
56
  const probe = spawnSync('npm', ['view', `${pkg}@latest`, 'version'], { encoding: 'utf8', env })
57
57
  if (probe.status !== 0) return { status: probe.status, unavailable: true }
58
58
  // INHERITED stdio — the provider owns its install questions (tty interactive
59
- // happens here, once). npx bin-name nuance (cf. the 0.2.9 update грабли): with
59
+ // happens here, once). npx bin-name nuance (cf. the 0.2.9 update pitfall): with
60
60
  // the provider bin already on PATH npx runs the INSTALLED one — acceptable for
61
61
  // an idempotent init (unlike the self-update case where it was a structural bug).
62
62
  const r = spawnSync('npx', ['-y', pkg, ...args], { stdio: 'inherit', env })
@@ -84,7 +84,7 @@ export async function onboardMemoryProvider(opts: MemoryOnboardOptions = {}): Pr
84
84
  if (opts.skip) return { state: 'skipped-flag', provider: existing }
85
85
  if (existing) {
86
86
  if (existing.package === pkg) return { state: 'already', provider: existing }
87
- // Never silently install over an occupied slot (contract: ЗАПРЕЩЕНО).
87
+ // Never silently install over an occupied slot (contract: FORBIDDEN).
88
88
  return {
89
89
  state: 'refused-foreign',
90
90
  provider: existing,
@@ -1,7 +1,6 @@
1
1
  // Default-yes onboard steps for the distribution BACKBONE (design doc
2
- // docs/«Onboard костяка — notifier, telegram, каналы, update» — FINAL 10.06,
3
- // sanctioned by Артур; owners' facts baked: notifier ack «ноль вопросов»,
4
- // telegram self-config contract v0.10.0+).
2
+ // docs/«Onboard костяка — notifier, telegram, каналы, update»; baked facts:
3
+ // notifier «ноль вопросов», telegram self-config contract v0.10.0+).
5
4
  //
6
5
  // Backbone = core + memory slot + notifier-runtime + telegram-runtime. The onboard
7
6
  // step ORDER is significant: marketplace → notifier → TELEGRAM (creates the human
@@ -25,8 +24,8 @@ import {
25
24
  } from '../runtime/deploy.ts'
26
25
 
27
26
  // ─────────────────────────────────────────────────────────────────────────────
28
- // (а) notifier — default-yes, ZERO questions (owner-confirmed: the whole chain
29
- // has no prompt/stdin/tty dependency; all failures are fail-closed exit≠0).
27
+ // (а) notifier — default-yes, ZERO questions (the whole chain has no
28
+ // prompt/stdin/tty dependency; all failures are fail-closed exit≠0).
30
29
  // Prereq (not a question): `bun` on PATH — without it the npx shim exits non-zero
31
30
  // → SOFT SKIP with a clear message (design: unavailability is not a core error).
32
31
  // Idempotent re-onboard: install is manifest-gated (skipped), deploy is no-clobber
@@ -97,8 +96,8 @@ export async function onboardNotifierStep(opts: NotifierStepOptions = {}): Promi
97
96
  // writes interfaces.telegram.user_id itself; no prepare / interface-human calls.
98
97
  // Bot tokens are NOT asked here — they are per-channel (`iapeer connect telegram`).
99
98
  //
100
- // IDEMPOTENT re-onboard (boris's check, baked into the design): an EXISTING
101
- // natural peer in the registry → the step is a no-op/skip with a clear message —
99
+ // IDEMPOTENT re-onboard: an EXISTING natural peer in the registry → the step is
100
+ // a no-op/skip with a clear message —
102
101
  // NEVER an offer to create a second human peer.
103
102
  //
104
103
  // Interactive lives INSIDE the step (tty only); non-tty without the flags
@@ -222,8 +221,8 @@ export async function onboardTelegramStep(opts: TelegramStepOptions = {}): Promi
222
221
 
223
222
  try {
224
223
  // TELEGRAM_USER_ID rides the env into createPeer → initPeer → the package's
225
- // self-config hook, which writes interfaces.telegram.user_id itself (owner's
226
- // simplification, contract v0.10.0+ — no prepare / interface-human from the core).
224
+ // self-config hook, which writes interfaces.telegram.user_id itself
225
+ // (contract v0.10.0+ — no prepare / interface-human from the core).
227
226
  const r = await createPeer({
228
227
  personality,
229
228
  runtime: 'telegram',
@@ -122,8 +122,7 @@ export async function provisionPeer(opts: ProvisionPeerOptions): Promise<Provisi
122
122
  }
123
123
  }
124
124
  // Provider PROVISION command (контракт §Provision провайдера, declaration
125
- // v1.2 — ADR-009 inversion; решение Артура 10.06, схема сверена с
126
- // iapeer-memory 11.06): an occupied slot WITH a provision block shells into
125
+ // v1.2 — ADR-009 inversion): an occupied slot WITH a provision block shells into
127
126
  // the PROVIDER's command per agentic runtime (occasion=birth) — the core
128
127
  // knows nothing about surface forms. A failure is NEVER masked (a broken
129
128
  // provider must not hide) — LOUD warn; repair is provider-side (the core is
@@ -167,7 +166,7 @@ export async function provisionPeer(opts: ProvisionPeerOptions): Promise<Provisi
167
166
  }
168
167
  }
169
168
  // NB: the v1.1 plugin auto-install branch (an occupied slot WITH a `plugin`
170
- // block) was REMOVED 11.06 — the plugin form is retired ecosystem-wide
169
+ // block) was REMOVED — the plugin form is retired ecosystem-wide
171
170
  // (provider's ADR-017; contract §Плагин провайдера is a tombstone). A v1.2
172
171
  // declaration without a provision block provisions nothing here.
173
172
  }
@@ -6,7 +6,7 @@
6
6
  // per-peer self-config hook + auto-bootstrap, uniformly.
7
7
  //
8
8
  // MODE (b) "operator-add" (telegram human) does NOT come through here — it is a direct
9
- // `iapeer create maria --runtime telegram`. Both modes share the SAME per-peer
9
+ // `iapeer create alice --runtime telegram`. Both modes share the SAME per-peer
10
10
  // self-config hook (invoked inside createPeer→initPeer); deployRuntime is just the
11
11
  // enumeration of the declared set. "1 always-on infra peer = 1 plist", idempotent
12
12
  // (re-deploy of an existing peer does not duplicate or clobber).
@@ -24,7 +24,7 @@ import { createPeer, type CreatePeerResult } from '../create/index.ts'
24
24
  import { readRuntimeManifest, type RuntimeManifest, type RuntimePeerDecl } from './index.ts'
25
25
 
26
26
  // ─────────────────────────────────────────────────────────────────────────────
27
- // Built-in runtime → npm package registry (§6, Артур 08.06): onboard AUTO-resolves
27
+ // Built-in runtime → npm package registry (§6): onboard AUTO-resolves
28
28
  // the package for a known infra runtime and `npx`-installs it (the package self-
29
29
  // deploys its bin + manifest). The operator overrides per-runtime with --package.
30
30
  // FROZEN map; a runtime absent here needs an explicit --package.
@@ -10,7 +10,7 @@
10
10
  // declared set. Static.
11
11
  // (b) operator-add — a package whose peers are PEOPLE the package can't know ahead
12
12
  // (telegram: a human) declares NO peers; the operator adds one dynamically with
13
- // `iapeer create maria --runtime telegram`. Dynamic.
13
+ // `iapeer create alice --runtime telegram`. Dynamic.
14
14
  // BOTH converge on the SAME per-peer self-config hook ("configure runtime state for
15
15
  // peer X", idempotent). The hook is per-peer; only the enumeration/trigger differs.
16
16
  // So manifest.peers is OPTIONAL (mode b omits it); manifest.selfConfig is the shared
@@ -51,9 +51,9 @@ export interface RuntimePeerDecl {
51
51
  export interface RuntimeManifest {
52
52
  /** The runtime id this manifest describes (must match the folder it lives in). */
53
53
  runtime: Runtime
54
- /** OPTIONAL installed package version (the owners' self-install stamp — the
55
- * telegram owner's 10.06 obligation). `update-runtime` version-gates on it;
56
- * absent → no gate, the update re-installs idempotently. */
54
+ /** OPTIONAL installed package version (the package's self-install stamp).
55
+ * `update-runtime` version-gates on it; absent → no gate, the update
56
+ * re-installs idempotently. */
57
57
  version?: string
58
58
  /** OPTIONAL per-peer self-config hook (the shared contract both modes call). */
59
59
  selfConfig?: SelfConfigDescriptor