@llblab/pi-telegram 0.53.2 → 0.54.0

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.
@@ -61,7 +61,7 @@ The repository uses a **Flat Domain DAG**:
61
61
 
62
62
  ### Live-Rebind Module Map
63
63
 
64
- Release `0.53.0` adds the [live-rebind channel](./multi-instance-bus.md#live-rebind-channel) and bounded old-Thread cleanup. It advertises live save/apply/settle, command-set and selected-menu-delivery through protocol v2, retaining Workspace Restore for legacy continuity. A loaded 0.53.0 build prefers the live path; an instance still running an older build keeps Restore until it is reloaded. Windows runs the same channel and strict reads, exercised by the CI matrix; there is no platform-specific alternate protocol.
64
+ Release `0.53.0` adds the [live-rebind channel](./multi-instance-bus.md#live-rebind-channel) and bounded old-Thread cleanup. It advertises live save/apply/settle, command-set and selected-menu-delivery through bus protocol v3, retaining Workspace Restore for legacy continuity. A loaded 0.53.0 build prefers the live path; an instance still running an older build keeps Restore until it is reloaded. Windows runs the same channel and strict reads, exercised by the CI matrix; there is no platform-specific alternate protocol.
65
65
 
66
66
  The channel reuses existing owners rather than adding a parallel command bus, queue or recovery store:
67
67
 
@@ -84,7 +84,7 @@ Only the live cleanup origin uses the approved historical-protection reduction.
84
84
  - `workspace-identity`: Exact session/CWD normalization, bounded directory/session keys and local instance-slot encoding. Owns `TelegramWorkspaceBindingIdentity`, the precomputed-key constructor and the shared key-length bound used by collision handling; it depends only on Node crypto/path and existing platform/CWD defaults. Threads, Sync, Routing and both Bus roles consume identity functions directly from this owner; Threads retains old identity exports as compatibility reexports. These value calls do not borrow Threads storage or effect authority. No store, target allocation, admission, callbacks or reverse dependency; constructing a key grants no storage-reference or custody authority.
85
85
  - `target`: Pure `{ chatId, threadId? }` address-value identity. `areTelegramTargetsEqual` compares both fields exactly; a missing Thread differs from Thread zero, and object identity is no shortcut. `getTelegramTargetKey` keeps the canonical `private` sentinel. Delivery, activity and Workspace domains reuse these functions; optional-target and differently keyed policies remain local. Address equality never grants transport, execution or deletion authority.
86
86
  - `process-identity`: PID liveness and stable process-birth identity: Linux `/proc` start ticks, macOS `ps` start time and Windows creation ticks read through PowerShell (cached only for the current process). Only an absent PID (`ESRCH`) or a mismatched birth proof yields `dead`; a live PID without a birth proof (inaccessible metadata, such as another user's process, or a generation fallback) is `unverifiable`. It is a leaf over Node child-process/crypto/fs/os so Locks, Bus, Journal, Workspace admission and Generative Apps share one proof without durable storage depending on the IPC protocol. Liveness never grants authority by itself.
87
- - `bus` / `bus-api` / `bus-leader` / `bus-follower` / `ownership`: Threaded Mode multi-instance bus contracts, profile-scoped process/endpoint identity, local leader/follower IPC, leader-only orchestration, follower-side manual registration/session runtime, follower-routed Bot API calls, and live message ownership. `bus` owns session-native protocol v2 independently from package build, canonical capabilities, compatibility, process runtime identity, profile-aware endpoints, IPC primitives, and replacement-invalidated non-routing registry observations. Registration rejects missing/mismatched protocol before provisioning while preserving compatible package skew; negotiated identity reaches status/state. Leader runtime, leader envelope handling, follower assembly, and follower registration construction require explicit protocol identity, preventing identity-less composition at both production and low-level runtime boundaries. `bus-leader` owns leader envelope handling and polling/server/prune orchestration; its Workspace-admission assembly holds a chat-wide lease across leader provisioning and profile-wide leases across follower provisioning/registration, disconnect/dead cleanup, leader/follower rename, display preference/reconciliation, and detached post-provision cleanup through its delayed API/store settlement. It consumes the profile operation runtime shared with Sync and routing; admission precedes that process-local mutation gate, fence rejection occurs before mutation, and release follows complete settlement. `bus-follower` owns registration/ack negotiation, active leader-auth/election state, heartbeat, authenticated clients, forwarded receiving, and recovery. Its production assembly holds profile-wide admission across follower-to-leader promotion, so a retained fence rejects it before leadership acquisition or store mutation. Production leader composition resolves the admission assembly at each operation boundary. The proof-aware follower Restore receiver replaces legacy target replacement: the old `leader.replaceFollowerTarget` producer, parser and receiver are removed, so even authenticated old-format requests are rejected before recipient effects. Profile admission surrounds exact retained-operation, authenticated leader/context, registration-generation, session/CWD and canonical binding/slot checks. `apply` updates only local registration after the leader's canonical commit; an already matching target does not switch again. `inspect` never switches targets and reports the observed local target plus readiness. Neither mode persists canonical state, manufactures deletion/probe evidence or dispatches input. Validated loading alone does not authorize the recipient effect. `threads.withWorkspaceRestoreSnapshot` now holds the canonical transaction while checking the exact retained intent, binding protection and recovery evidence, then supplies fresh binding/owner data to a synchronous read-only observation. Both `apply` and `inspect` run their existing recipient checks, local application and readiness construction inside this boundary, with fresh scoped-intent and live-context checks. The callback contract excludes asynchronous observers; neither cached projection nor callback completion grants canonical persistence. A follower with `canPersist: false` can observe but still cannot use the leader's registration publisher. Native regressions reject receipts, binding regression, owner detachment, intent adoption and context replacement arriving after load; valid reobservation never repeats apply. The transaction spans the local effect, releases on failure, and neither receiver mode changes canonical bytes. The leader recipient path uses the same observation: session/CWD/slot and active-owner selection consume its fresh binding/owner rows, not the warm store, and the transaction spans local identity application and readiness construction. Full-capacity native worker fixtures reject post-grant receipts, binding/owner regression and context changes both before first apply and during inspection after a lost apply reply, preserving the pending original and issued grant without undoing an earlier valid apply. These fixtures do not authorize activation or live fault injection. A composed capability-gated controller and authenticated envelope connect this receiver across native IPC, with one transport attempt and exact readiness-response validation; see the [bus contract](./multi-instance-bus.md#protocol-identity-and-compatibility). Successor inspection can verify a changed follower registration or process only against its actual session/CWD, one current canonical owner, the unchanged binding/slot and observed local target; an old local target remains not ready. Successors cannot use the original apply grant. Follower target preparation uses `threads.assertWorkspaceRestoreRegistration` before recovery allocation, visibility probes and owner transfer, after probe awaits, and before returning the prepared target. The read-only precondition checks fresh canonical and recovery evidence under the snapshot transaction; a protected target requires its exact binding key, slot and relocated target, never the old target. Lost evidence or late recovery conflicts cannot fall through to visibility-error recovery or leave a transient claim held. This is not registration/transport authority: callers still own authentication, profile/session/leader/generation checks and Workspace admission. Bootstrap now re-enters profile admission and the shared mutation gate for `commitWorkspaceRestoreRegistration`; declared Workspace bindings are checked again in the publication snapshot, even without retained Restore. The synchronous callback rechecks captured leader epoch, profile and operator before writing. Missing, unused, reused or expired publication authority cannot report success; the post-commit ACK rechecks current registration identity, allowing heartbeat, connection-time and Thread-name metadata changes. Lost post-publication authority never removes independently published state. Native authenticated bootstrap tests cover the real transaction and late recovery/authority changes. The root protocol identity now advertises `workspace-restore-v1`; every participating peer must run this build before its Restore is used.
87
+ - `bus` / `bus-api` / `bus-leader` / `bus-follower` / `ownership`: Threaded Mode multi-instance bus contracts, profile-scoped process/endpoint identity, local leader/follower IPC, leader-only orchestration, follower-side manual registration/session runtime, follower-routed Bot API calls, and live message ownership. `bus` owns session-native protocol v3 independently from package build, canonical capabilities, compatibility, process runtime identity, profile-aware endpoints, IPC primitives, and replacement-invalidated non-routing registry observations. Registration rejects missing/mismatched protocol before provisioning while preserving compatible package skew; negotiated identity reaches status/state. Leader runtime, leader envelope handling, follower assembly, and follower registration construction require explicit protocol identity, preventing identity-less composition at both production and low-level runtime boundaries. `bus-leader` owns leader envelope handling and polling/server/prune orchestration; its Workspace-admission assembly holds a chat-wide lease across leader provisioning and profile-wide leases across follower provisioning/registration, disconnect/dead cleanup, leader/follower rename, display preference/reconciliation, and detached post-provision cleanup through its delayed API/store settlement. It consumes the profile operation runtime shared with Sync and routing; admission precedes that process-local mutation gate, fence rejection occurs before mutation, and release follows complete settlement. `bus-follower` owns registration/ack negotiation, active leader-auth/election state, heartbeat, authenticated clients, forwarded receiving, and recovery. Its production assembly holds profile-wide admission across follower-to-leader promotion, so a retained fence rejects it before leadership acquisition or store mutation. Production leader composition resolves the admission assembly at each operation boundary. The proof-aware follower Restore receiver replaces legacy target replacement: the old `leader.replaceFollowerTarget` producer, parser and receiver are removed, so even authenticated old-format requests are rejected before recipient effects. Profile admission surrounds exact retained-operation, authenticated leader/context, registration-generation, session/CWD and canonical binding/slot checks. `apply` updates only local registration after the leader's canonical commit; an already matching target does not switch again. `inspect` never switches targets and reports the observed local target plus readiness. Neither mode persists canonical state, manufactures deletion/probe evidence or dispatches input. Validated loading alone does not authorize the recipient effect. `threads.withWorkspaceRestoreSnapshot` now holds the canonical transaction while checking the exact retained intent, binding protection and recovery evidence, then supplies fresh binding/owner data to a synchronous read-only observation. Both `apply` and `inspect` run their existing recipient checks, local application and readiness construction inside this boundary, with fresh scoped-intent and live-context checks. The callback contract excludes asynchronous observers; neither cached projection nor callback completion grants canonical persistence. A follower with `canPersist: false` can observe but still cannot use the leader's registration publisher. Native regressions reject receipts, binding regression, owner detachment, intent adoption and context replacement arriving after load; valid reobservation never repeats apply. The transaction spans the local effect, releases on failure, and neither receiver mode changes canonical bytes. The leader recipient path uses the same observation: session/CWD/slot and active-owner selection consume its fresh binding/owner rows, not the warm store, and the transaction spans local identity application and readiness construction. Full-capacity native worker fixtures reject post-grant receipts, binding/owner regression and context changes both before first apply and during inspection after a lost apply reply, preserving the pending original and issued grant without undoing an earlier valid apply. These fixtures do not authorize activation or live fault injection. A composed capability-gated controller and authenticated envelope connect this receiver across native IPC, with one transport attempt and exact readiness-response validation; see the [bus contract](./multi-instance-bus.md#protocol-identity-and-compatibility). Successor inspection can verify a changed follower registration or process only against its actual session/CWD, one current canonical owner, the unchanged binding/slot and observed local target; an old local target remains not ready. Successors cannot use the original apply grant. Follower target preparation uses `threads.assertWorkspaceRestoreRegistration` before recovery allocation, visibility probes and owner transfer, after probe awaits, and before returning the prepared target. The read-only precondition checks fresh canonical and recovery evidence under the snapshot transaction; a protected target requires its exact binding key, slot and relocated target, never the old target. Lost evidence or late recovery conflicts cannot fall through to visibility-error recovery or leave a transient claim held. This is not registration/transport authority: callers still own authentication, profile/session/leader/generation checks and Workspace admission. Bootstrap now re-enters profile admission and the shared mutation gate for `commitWorkspaceRestoreRegistration`; declared Workspace bindings are checked again in the publication snapshot, even without retained Restore. The synchronous callback rechecks captured leader epoch, profile and operator before writing. Missing, unused, reused or expired publication authority cannot report success; the post-commit ACK rechecks current registration identity, allowing heartbeat, connection-time and Thread-name metadata changes. Lost post-publication authority never removes independently published state. Native authenticated bootstrap tests cover the real transaction and late recovery/authority changes. The root protocol identity now advertises `workspace-restore-v1`; every participating peer must run this build before its Restore is used.
88
88
  - `sync`: demand-driven Telegram reconciliation, mutable sync-slice state, nested provisioning activity, and local assumption policy. It does not own a complete Telegram bot read-model; Bot API lacks a complete topic/thread listing surface. It owns sync slices, invalidation triggers, config-persist invalidation sequencing, exact-target admitted stale-topic API recovery, observation intake, status/debug freshness, paired manual-disconnect/session-restart cleanup assembly, prepared quiescent leader-quit detachment, and reconciliation scheduling across bot identity, pairing assumptions, live target bindings, reservations, and transport health after meaningful observable signals. Production topic lifecycle and disconnect/restart cleanup enter profile-wide admission before the shared Workspace operation gate. Lifecycle admission spans observation-driven store settlement; cleanup admission spans intent publication, Telegram cleanup, binding mutation, durable settlement, and transport release. It should call narrower domain primitives rather than letting `index.ts`, `threads`, or `status` accumulate cross-cutting reconciliation policy.
89
89
  - `thread-reconciler`: Threaded Mode control-plane planning for Telegram thread/tab lifecycle. It owns the reconciliation state machine (`stable`, `provisioning`, `sync-required`, `cleanup-required`), pure plans, proof-before-delete rules, pending-provision protection, fresh-creation grace windows, leader-epoch checks, and the single policy authority for destructive thread cleanup actions. It excludes live Telegram API calls, inbound routing, menu rendering, and direct persistence.
90
90
  - `thread-cleanup-manager`: Disconnected proof-only admission planning for manual inactive-Workspace cleanup. It emits exact profile/binding/target snapshots only when durable inactivity exists, all live-owner/accepted-work/delivery evidence is `clear`, identities are unique, and no reservation, provision, or cleanup competes. Missing, malformed, duplicate, or `unknown` evidence returns no candidates; age and ordering never create authority. Its disconnected bounded profile/token-scoped work store atomically persists exact candidate snapshots, records one exact Workspace-issued deletion permit as outcome-unknown, rejects mismatched permits, and confirms deleted state idempotently; strict reads reject malformed/private-file ambiguity. A disconnected executor requires an injected exclusive Workspace deletion boundary across fresh planner evidence, exact retained-snapshot comparison, permit acquisition/recording, delete callback, and confirmation. The future fence owner must acquire and recheck in admission-ledger order; a retirement fence cannot be nested inside an active ordinary admission lease. Regressions prove drift stops before permit, unavailable/already-issued state cannot fabricate authority, and ambiguous delete remains outcome-unknown without replay. A production-shaped adapter preserves full Workspace records through external-protection resolution, then snapshots exact cleanup fields, reservations, provisions, and cleanup intents; protection exceptions become `unknown`, while source failures propagate fail-closed. Production Settings exposes **Review inactive tabs** only: profile-wide admission surrounds fresh evidence capture, one canonical 128-bit-digest work-set is retained, and a separate summary reports proven count, explicit no-deletion state, and Back navigation. The exact confirmation callback fits Telegram's 64-byte bound and accepts only canonical work-set IDs. **Clean inactive tabs** renders only when composition supplies a destructive port; production intentionally omits it, so malformed/stale callbacks fail closed and review remains non-destructive. Permit composition must not call `acquireRetirementFence()` because retirement remains pressure-only. The one admission-ledger fence now carries discriminated `pressure-retirement | manual-thread-cleanup` authority, treats legacy missing kind as pressure, exposes `acquireThreadCleanupFence()`, includes kind in exact comparison/permits, and remains profile-singleton. Cleanup-specific adopt/issue/absence/release/complete APIs preserve existing retirement callers and reject cross-kind use. Review admission releases before cleanup-fence acquisition; that fence then spans exact full-record/protection revalidation, sole permit issuance, work-set recording, one delete attempt, absence confirmation, binding/work-set commit, and fence completion. The v1-compatible schema and kind-specific acquire/adopt/issue/absence/release/complete methods are implemented; pressure methods reject manual fences and cleanup methods reject pressure fences. A disconnected permit runtime acquires the cleanup fence, revalidates under it, releases drifted unissued fences, refuses already-issued replay, and retains `commit-ready` until an injected durable commit succeeds. `threads.commitInactiveWorkspaceCleanup()` now removes only an exact full inactive binding after rechecking local records, claims, reservations, provisions, cleanups, and retirement intents; the retained cleanup candidate carries sufficient exact cwd/workspace/instance/global-slot/binding/target/inactivity/update commit identity, and under the retained fence exact absence closes commit-unknown retry without reconstructing the deleted full binding. Commit composition removes the binding before confirming the work-set, and failure retains `commit-ready`. A hidden coordinator validates canonical review identity, resolves the full binding, records the sole permit before one injected delete call, then commits binding, work-set, and fence. A `commit-ready` retry finishes without another delete; ambiguous deletion remains `deletion-issued` and is never replayed. A typed Settings-port adapter exposes this coordinator only when explicitly supplied and reports deleted, outcome-unknown, and blocked counts. Fake-port tests exercise the callback lifecycle and successor recovery: takeover requires injected proof, exact fence identity is preserved, live/unverifiable predecessors fail closed, and `commit-ready` resumes without deletion replay. Cross-process workers prove stale `prepared` contenders call fake transport once: deletion requires successful work-set permit CAS, and redundant fences over deleted entries settle without replay. Work-set pre-rename failure and lost post-rename acknowledgement retain recoverable fence truth; durable `deleted` completes that fence without another transport call. Binding-snapshot pre-rename ownership loss reloads and restores the exact binding, while post-rename acknowledgement loss reloads durable absence as successful commit. Cleanup runtime composition additionally requires candidate/work-set/runtime/fence profile equality before mutation and captures one stable successor owner snapshot for adoption. The optional Settings port reports only redacted exact-authority recovery classes: commit pending, deletion outcome unknown with no retry, or unavailable authority. It exposes no target, path, token, or transport details. Production still omits `cleanInactiveThreads`, so Bot API activation remains separate.
@@ -142,7 +142,7 @@ Mirrored domain regressions live in `/tests/*.test.ts`. Shared test fixtures sho
142
142
 
143
143
  Telegram configuration lives in `~/.pi/agent/telegram.json`. Bot/session identity (`botToken`, `botUsername`, `botId`, `allowedUserId`) persists only under `profiles.default` or `profiles.<name>`; shared handlers and assistant/voice/time settings stay top-level. Per-profile polling/admission state lives only in the durable update journal as `acceptedThroughUpdateId`. Authoritative transport ownership lives separately in the `transport` section of the pi-telegram-private `~/.pi/agent/tmp/pi-telegram/state.json`, one profile entry per `default` or validated named profile (see [Consolidated Runtime Root](#consolidated-runtime-root)); unrelated extensions never read or write this file.
144
144
 
145
- `telegram.json` is one global cross-instance configuration document. Ordinary reads rely on atomic publication and do not take the mutation guard. Every cooperating Pi instance persists only its recursive delta from the snapshot it loaded, merges that delta into the latest disk document inside `telegram.json.transaction`, and publishes atomically only when the semantic result differs; a no-op merge adopts the newer disk snapshot in memory without replacing the file. Unrelated global and profile changes therefore survive stale writers. Two serialized writers changing the same leaf use commit order, so the later local delta wins. A non-transactional external editor cannot participate in that conflict protocol: it should write through same-directory atomic replacement while Pi is idle, then let instances reload; an editor racing the transaction may lose its same-leaf change and must retry from the resulting file.
145
+ `telegram.json` is one global cross-instance configuration document. Ordinary reads rely on atomic publication and do not take the mutation guard. Only config writes and sender admission enter `telegram.json.transaction`; the update journal never uses it. Every cooperating Pi instance persists only its recursive delta from the snapshot it loaded, merges that delta into the latest disk document inside `telegram.json.transaction`, and publishes atomically only when the semantic result differs; a no-op merge adopts the newer disk snapshot in memory without replacing the file. Unrelated global and profile changes therefore survive stale writers. Two serialized writers changing the same leaf use commit order, so the later local delta wins. A non-transactional external editor cannot participate in that conflict protocol: it should write through same-directory atomic replacement while Pi is idle, then let instances reload; an editor racing the transaction may lose its same-leaf change and must retry from the resulting file.
146
146
 
147
147
  ### Setup Flow
148
148
 
@@ -216,11 +216,11 @@ The journal prerequisite now exists as the optional `withPairedAdmission` port.
216
216
 
217
217
  The source constraints are concrete. `createTelegramBusFollowerDurableAdmissionRuntime()` appends each authenticated delivery without a cursor; its receiver checks secret, registration generation, recipient binding and source ID, but production journal factories do not yet select the paired-only port. A synthetic store witness admits IDs `20 → 10` into v1 and retains both; substituting cursor-ordered v2 with a fabricated maximum cursor suppresses `10`. The lifecycle's `bind()` currently constructs the worker, resumes terminal entries, and performs dead-owner cleanup before `worker.start()`, so checking migration only at `start()` is too late.
218
218
 
219
- - **New deliveries:** Preserve transport authentication, exact generation/binding/delivery identity and current-session checks. Before append, acquire Workspace admission → config transaction → journal transaction. A config-owned paired-only seam must confirm the envelope sender is the exact persisted owner of the expected profile/token and refresh that authority in the receiving process before worker signaling. It must never create an owner. Boolean "profile is paired" alone is insufficient; a follower cache loaded before another process's grant may still be unpaired. The guard belongs inside the journal's already-Workspace-admitted synchronous append path, not around public `appendBatch()` or async `admit()`: those wrappers would reacquire Workspace under config or span the wrong boundary. Release these admission/transaction guards before worker signaling and ACK. The existing polling observation port selects v2, so neither it nor the automatic first-contact publisher substitutes for a paired-only v1 seam.
219
+ - **New deliveries:** Preserve transport authentication, exact generation/binding/delivery identity and current-session checks. Before append, acquire Workspace admission → config transaction (sender authority) → journal source serialization → journal transaction. A config-owned paired-only seam must confirm the envelope sender is the exact persisted owner of the expected profile/token and refresh that authority in the receiving process before worker signaling. It must never create an owner. Boolean "profile is paired" alone is insufficient; a follower cache loaded before another process's grant may still be unpaired. The guard belongs inside the journal's already-Workspace-admitted synchronous append path, not around public `appendBatch()` or async `admit()`: those wrappers would reacquire Workspace under config or span the wrong boundary. Release these admission/transaction guards before worker signaling and ACK. The existing polling observation port selects v2, so neither it nor the automatic first-contact publisher substitutes for a paired-only v1 seam.
220
220
  - **Source guarantee:** Only the updated leader's exclusion-gated, sender-authorized path may forward content. Followers cannot offer pairing candidates. Bind this contract to the negotiated producer/consumer capability and exact registration; an envelope assertion is not consent. Do not advertise the capability while using an ungated producer or receiver. Mixed/old runtimes remain an operator-coordinated activation prohibition, not a software-enforced downgrade claim.
221
221
  - **Legacy followers:** A configured profile preserves validated v1 accepted work, receipts, births/generations and handoffs without a schema rewrite. If the profile is unpaired, any retained follower source entry—pending, retry, terminal or queued—blocks source startup and a new UI grant until exact operator-authorized reconciliation. Preserve bytes; do not auto-clear, quarantine, assign a maximum cursor, or silently relabel the work. This deliberately narrows automatic legacy migration. Reconcile such state before manual preconfiguration too; no retroactive consent guarantee is inferred from an operator editing the config file.
222
- - **Readiness and migration:** Use complete, bounded, strict read-only inspection of the current profile's polling journal and discovered follower snapshots/segments, not recovery-capable `read()` as a preflight. Unknown identity, unreadable/corrupt evidence or incomplete inventory blocks readiness. Stop and await the old local worker, then re-resolve/revalidate profile/token/registration binding before preparation; the binding captured before the await cannot authorize successor startup. Place preparation before worker construction, terminal retry, queue-owner cleanup and handoff consumers, and publish lifecycle availability only after it succeeds. Also cover non-worker readers: Workspace journal protection capture currently calls recovery-capable `read()` and must not repair/reset evidence ahead of readiness. Discovery alone is insufficient: strict inspection must reject symlinks/unknown identities, bound the inventory, and close namespace creation, recovery and handoff paths through config serialization or explicit quiescence. Operator-coordinated quiescence of other legacy consumers remains necessary. Preserve fixed recovery identities; no "ready" sidecar or cached boolean grants authority.
223
- - **Final grant:** Recheck source readiness inside the queued publication's existing config transaction, under exact transport ownership, only when creating a previously absent owner. New follower writers must use the same config serialization. The check cannot reacquire config or await; journal locks follow config. Recheck candidate/session/profile/token/deadline immediately before rename after inspection. A startup-only inventory or checks outside that transaction do not close the append/grant race.
222
+ - **Readiness and migration:** Use complete, bounded, strict read-only inspection of the current profile's polling journal and discovered follower snapshots/segments, not recovery-capable `read()` as a preflight. Unknown identity, unreadable/corrupt evidence or incomplete inventory blocks readiness. Stop and await the old local worker, then re-resolve/revalidate profile/token/registration binding before preparation; the binding captured before the await cannot authorize successor startup. Place preparation before worker construction, terminal retry, queue-owner cleanup and handoff consumers, and publish lifecycle availability only after it succeeds. Also cover non-worker readers: Workspace journal protection capture currently calls recovery-capable `read()` and must not repair/reset evidence ahead of readiness. Discovery alone is insufficient: strict inspection must reject symlinks/unknown identities, bound the inventory, and close namespace creation, recovery and handoff paths through journal source serialization or explicit quiescence. Operator-coordinated quiescence of other legacy consumers remains necessary. Preserve fixed recovery identities; no "ready" sidecar or cached boolean grants authority.
223
+ - **Final grant:** Recheck source readiness inside the queued publication's existing config transaction, under exact transport ownership, only when creating a previously absent owner. New paired follower writers must use the same config admission. The check cannot reacquire config or await; journal locks follow config. Recheck candidate/session/profile/token/deadline immediately before rename after inspection. A startup-only inventory or checks outside that transaction do not close the append/grant race.
224
224
 
225
225
  Required witnesses before accepting this policy: out-of-order first deliveries survive; a legitimate forwarded message after a peer grant refreshes a stale follower cache without writing a grant; unpaired/different-owner/stale-generation admission has no publication or wakeup; unpaired retained follower data survives restart and blocks UI publication; configured legacy receipts remain intact; preparation precedes every recovery consumer; inventory/append/grant contention cannot produce a ready-but-unexcluded path. If these cannot be proved without another durable authority, reopen the source design rather than widening v2 by assumption.
226
226
 
@@ -268,12 +268,12 @@ Source audit confirms that canonical inventory is not yet usable as a production
268
268
 
269
269
  #### Source Operation Serialization
270
270
 
271
- `config.withSourceSerialization(operation)` is a lock-only prerequisite with completed isolated implementation review. Tests cover unchanged config/cache across absent, corrupt and changed-authority states, callback result/error preservation, release/retry, and actual cross-process contention with observation, paired admission and owner-fenced grant. It invokes trusted synchronous code under the existing private config transaction without reading/adopting config, validating the active profile/token/sender, or exposing credentials or a lock capability. The callback's own result is not authority. Acquire required Workspace admission first; never nest config admission or acquire owners inside it. Promise-returning callbacks are outside the contract: their continuations run after lock release, not as protected asynchronous transactions.
271
+ `Journal.createTelegramJournalSourceSerialization()` is the journal-owned lock-only source serializer at `tmp/pi-telegram/runtime/journals.transaction`; it never touches `telegram.json` or its transaction. Tests cover callback result/error preservation, release/retry, an unchanged (even unreadable) config, and that holding it never blocks pairing observation, paired admission or owner-fenced grant in another process. It reads no config, validates no profile/token/sender, and exposes no lock capability. The callback's own result is not authority. Acquire required Workspace admission and any sender (config) admission first; never acquire owners inside it. Promise-returning callbacks are outside the contract: their continuations run after lock release, not as protected asynchronous transactions.
272
272
 
273
273
  The opt-in journal-store composition now separates continuations rather than adding a second prepared-worker projection. Independent review covers all 12 stateful methods, source-callback rejection before journal locking/read/recovery, actual contention, publication-failure release/retry and unchanged receipt CAS:
274
274
 
275
275
  - `Append`: Canonicalize once → Workspace admission → choose polling observation or paired-human admission → private journal transaction. Without a sender-admission mode, use the lock-only continuation when configured. Do not wrap an admission callback in another config transaction.
276
- - `Other store operations`: Lock-only config serialization → opt-in `sourceAccess` family acquisition → private journal transaction → direct consumption of that operation-local evidence. The same ordering applies after append's selected admission continuation. Acquiring before journal staging is valid only when source serialization excludes all participating family writers through consumption. Without `sourceAccess`, ordinary recovery-capable `readCurrent()` remains unchanged. Ordinary `read()` is optimistic: it first runs the same strict, repair-free evidence read without config or journal guards, because atomic publication makes it safe; only a refused or reconciliation-requiring read retries once through this serialized path, which owns repair. Receipt/owner/handoff CAS and process-birth proof stay authoritative; no caller-supplied lock-held bypass or ambient reentrancy depth is exposed.
276
+ - `Other store operations`: Lock-only journal source serialization → opt-in `sourceAccess` family acquisition → private journal transaction → direct consumption of that operation-local evidence. The same ordering applies after append's selected admission continuation. Acquiring before journal staging is valid only when source serialization excludes all participating family writers through consumption. Without `sourceAccess`, ordinary recovery-capable `readCurrent()` remains unchanged. Ordinary `read()` is optimistic: it first runs the same strict, repair-free evidence read without config or journal guards, because atomic publication makes it safe; only a refused or reconciliation-requiring read retries once through this serialized path, which owns repair. Receipt/owner/handoff CAS and process-birth proof stay authoritative; no caller-supplied lock-held bypass or ambient reentrancy depth is exposed.
277
277
  - `Resource binding`: Participating continuations must use the same actual config resource. Arbitrary injected callbacks cannot prove that themselves; real factory/operation tests must establish one acquisition per operation. The dispatcher is implemented and locally reviewed. `sourceAccess` requires the lock-only continuation and captures an independently approved directory plus physical inspection limits; its store integration is independently reviewed, including generated-state refusal remediation. Production wiring remains absent.
278
278
  - `Evidence`: Real isolated probes show an outer observation plus existing admitted append times out on nested config acquisition. Current-token admission can reject an otherwise valid old-source receipt completion after token rotation; a lock-only transaction still permits exact receipt CAS while rejecting a wrong acquisition ID. Current observation also permits profile-switch/revocation cases, so it is not a lifetime fence. A journal `read()` can repair an empty foreign snapshot while another process holds config, proving append-only coverage is insufficient. These are storage/order witnesses, not permission for obsolete handlers to execute.
279
279
  - `Remaining gates`: Serialization does not provide exact reference reconciliation, old-token migration policy, settled-handler or legacy-snapshot barriers, executable receipt freshness, whole-root quiescence, all-writer closure or final-grant readiness. The whole-root census cannot run inside a journal transaction. Final grant requires an internal already-in-config-transaction check, not reacquisition of this wrapper. Production stores, inspectors and UI remain disconnected from this candidate.
@@ -308,7 +308,7 @@ Inline counterexamples reject two standalone remedies: follower completion reten
308
308
 
309
309
  A further owned probe uses the actual runtime binding and owner/lifecycle/worker assembly, rather than changing an existing routing runtime's identity. After one lost ACK, the recipient finishes its follower source; the old leader stops; registration becomes false and leader authority becomes true for the same recipient instance/context. Starting the assembly's leader lifecycle executes the retained primary source locally. Both journals finish empty after two sink invocations and only one forwarding request. Transport/election authority remains fixture-supplied: this is not OS election, live promotion, Pi model or Telegram API evidence. The inspected production promotion path stops registration and starts locked polling; the assembly independently binds the two journals and does not reconcile their delivery custody.
310
310
 
311
- The correction therefore needs durable delivery custody, not merely a completion cache or an origin-presence predicate. Existing queue handoff already changes exact ownership in one journal: offer freezes the donor, acceptance CAS installs the recipient, and an ambiguous ACK cannot restore donor authority. Its API requires an already queued receipt; raw input must not masquerade as a queued Pi prompt/control. The operator approved continuing with one authoritative input record and explicit execution ownership, with bus delivery transferring/reference-waking that authority instead of creating a second executable copy. This is logical uniqueness per input, not a requirement to combine every profile into one physical file. Legacy journals and their receipts remain intact until separately authorized reconciliation. The ownership contract below governs the next local implementation slice; storage encoding, raw-owner recovery and lifecycle integration remain separately evidenced, and no production activation is implied. A disconnected worker-facing adapter now proves the first integration seam: acquire and start precede handler execution; complete removes exact custody; a single-input queued outcome atomically becomes its durable queue receipt; deferred returns the running receipt; and an ambiguous running claim never executes again. A session-scoped extension retains deferred receipts and atomically groups them into one queue receipt; consumed raw receipts cannot later settle. Late deferred complete/grouped-queue outcomes require that same retained session receipt, so stale or missing callbacks fail closed. Handler failure and fresh-session observation of retained `running` discard local settlement capability, preventing synthetic completion of outcome-unknown work. A disconnected custodied admission handle now runs the existing registry/default routing inside that session; immediate and late reports settle through the same exact receipt authority. Lifecycle bindings may opt into a strict v3 worker port: v3 snapshots require custodied execution, legacy raw completion/queue/failure methods throw, and custodied handler failure remains blocked running work rather than legacy retry state. A gated resolver reads no v3 dependency while disabled; when enabled, its runtime identity binds both source runtime and recipient execution binding so either change forces lifecycle replacement. Real-v3 assembly evidence preserves retained running input across follower registration-generation replacement and follower-to-leader promotion without another handler call or settlement. Worker selection skips running, foreign-ready, and handoff-frozen inputs while draining independent tail; each custodied commit yields and rereads durable state before unresolved work reports `execution` or `input-custody`. Diagnostics expose only update ID and `running-outcome-unknown`, `foreign-ready`, `handoff-frozen`, or `legacy-retry-state`, never owner, acquisition, binding, payload, path, or token details. Running outcome-unknown wins mixed-class priority. Legacy retry/failed state under v3 is quarantined while independent pending tail drains; it is never replayed through the legacy failure policy. A standalone disposition authority now hashes immutable failure metadata without retaining update payload and permits only exact `requeue-v3|discard` operator intent. Claimed/provenanced entries and generic terminal `retry` are excluded. The bounded segmented operator-disposition audit now accepts a discriminated legacy-custody record, avoiding a second log. Its v3-only mutation is reachable only through an injected operator authorization seam and profile admission: exact evidence atomically becomes unclaimed pending for `requeue-v3` or is removed for `discard`; duplicate authority is idempotent and collisions fail closed. Production omits authorization. The strict worker port now constructs an explicit four-method custody projection rather than returning the structurally narrowed backing store; runtime reflection proves operator listing and disposition are absent. This closes a former capability leak hidden by TypeScript's width subtyping. The disconnected operator runtime requires a caller-owned exact binding-reference scope around every list/apply call and retains no store reference. The scope must span the operation, not only lookup, so retirement/pruning cannot race a stale binding; removal, replacement or recovery mismatch fails closed. The existing bounded reference registry now has an `operator-disposition` class for this composition, but remains process-local evidence only. No production or Telegram command owns this runtime. A source invariant forbids the composition root from acquiring writer closure, installing protocol mode, executing cutover or migration publication, supplying legacy disposition authorization, or constructing the operator runtime. A separate strict non-repairing source-inspection port returns only update ID, retry/failed state, attempt count, bounded failure class and evidence SHA-256; it does not enter writer/source mutation serialization; update payload, failure summary and execution authority remain inside the journal owner. Duplicate reconciliation normalizes exact retained authority before invoking the injected authorizer; malformed extra fields never cross that boundary. If snapshot publication is commit-unknown, exact retry finds the durable audit and performs no second transition; a new disposition cannot target the resulting pending entry. Running custody still has no admissible legacy evidence. A real-v3 replacement regression begins with retained `running` and proves neither the old nor replacement session can execute or settle it after recipient identity changes. Source `recoveryKey` and recipient execution binding are distinct mandatory identities. An assembled regression proves late reports from two deferred sources publish one grouped durable queue receipt. Grouped late reports retain one exact duplicate result for each remaining source callback; the worker accepts the same already-published queue authority idempotently and rejects conflicts. Lifecycle assembly selects this path only for an optional binding-owned custody port. Its strict v3 worker adapter permits exact queued completion but throws before legacy raw completion, queue, or failure mutation; handler errors remain running outcome-unknown. The worker consumes already-durable results without legacy completion/queue writes, synchronizes late claims, and lifecycle assembly selects this path only from an optional binding-owned custody port plus exact binding key. The strict v3 binding also exposes queued receipt offer/accept/cancel through serialized admission wrappers; lifecycle lookup now proves exact recipient acceptance and exact duplicate retries return the same owner. Recipient worker projection is queried only with the matching journal binding key and survives lifecycle replacement. These APIs transfer Pi queue authority and remain distinct from raw input handoff. Cancelling a frozen raw handoff and signaling the worker makes the input executable and clears its redacted blocked projection. A legacy journal writer cannot clear quarantined v3 retry evidence because strict source-version selection rejects that mutation; any future operator disposition therefore needs explicit v3 authority separate from worker settlement. A disconnected bus admission mode validates delivery, source and recipient identity, then wakes the existing durable source reference without journaling the forwarded carrier as a second executable copy. Duplicate delivery can repeat only the wake; stale registration generation cannot wake, and selection fails closed without wake authority rather than falling back to copy admission. The gated delivery identity carries an optional bounded source `recoveryKey` outside the unchanged legacy delivery hash. Reference mode requires that key, bounded exact acquisition/handoff IDs, and explicit authenticated-transport proof; missing or mixed legacy evidence is rejected before wake. Legacy copy mode does not require or infer these fields. Wake carries the complete reference into a disconnected binding-owned resolver. It requires exact recovery key, recipient binding/live owner, update, acquisition and accepted handoff on one unfrozen `ready` claim before signaling; stale, running, missing or mismatched evidence cannot wake or mutate. An authenticated receiver composition against a real shared v3 journal now proves exact and duplicate delivery only signal the accepted ready claim; stale acquisition or registration generation cannot wake or fall back to copy admission. A disconnected sender selector requires mutual `input-custody-reference-v1` capability, and its constructor accepts only an exact accepted handoff when producing recovery/acquisition/handoff evidence. The dedicated `leader.wakeInputCustody` envelope carries no executable Telegram carrier and is forced through authenticated reference admission; legacy durable admission rejects it. A shared-v3 regression composes accepted handoff, leader resolver, mutually capable forwarder, authenticated payload-free receiver and exact wake; exact request replay reuses the cached ACK without another signal. A recipient adapter resolves recovery/binding/live-owner authority, performs exact acceptance CAS, verifies the returned handoff, and only then exposes the reference and signals. The payload-free `leader.offerInputCustodyHandoff` envelope carries exact source/handoff and recipient lifetime evidence into it; parsing, authentication, capability, binding and handler gates fail closed, while duplicate acceptance returns durable duplicate evidence. A real-v3 composition runs donor offer-before-send through the authenticated handoff receiver; acceptance CAS signals once, and client replay reconciles locally without another request. No immediate wake follows acceptance because it would duplicate signaling; payload-free wake remains only an idempotent recovery nudge for already accepted custody. Unknown outcomes remain frozen; after recipient acceptance, retry first reconciles the accepted shared-journal reference and returns duplicate settlement without re-offer, transport, or new authority. Full handoff-then-wake request composition remains disconnected. Frozen pre-accept lookup yields no reference, preventing transport. Missing reference fails retryably before transport; mixed peers retain legacy carrier delivery. A disconnected leader resolver reads shared v3 state without mutation and returns a forward reference only for the exact recipient-owned unfrozen `ready` claim; donor-frozen, running, stale binding or owner mismatch returns no reference. A single optional custody bus bundle derives acceptance, wake and forward-reference resolution from one recovery-key lookup carrying the same recipient binding/live owner, journal and signal authority. Lifecycle assembly/runtime binding exposes that bundle only when supplied; losing its active authority disables lookup and makes acceptance/wake fail closed together. A follower transport adapter rereads the optional bundle for every capability check, acceptance, wake and forward lookup. Removing the bundle on downgrade/reconnect simultaneously disables capability/resolution and makes stale operations fail closed without cached authority. A disconnected receiver assembly rereads these ports across downgrade/reconnect: bundle removal rejects before handlers, replacement generation uses only the new bundle, and stale generation cannot call either bundle or legacy admission. Live target ownership now carries the exact current registration protocol identity into forwarder selection: capability upgrade enables reference mode and downgrade returns to legacy, while persisted records grant neither mode. Forwarders may additionally revalidate current ownership after reference resolution and immediately before transport. If generation, binding, or protocol changed, they return `recipient-ownership-stale` without sending, preventing an old capable snapshot from crossing reconnect. Production follower-client composition uses one canonical live-registry validator immediately before transport. Removal, same-generation protocol replacement, or generation replacement invalidates old ownership; only a fresh current projection passes. The receiver generation check remains an independent second fence. A post-accept retry using stale ownership is rejected before transport after registry replacement, so it cannot reach either receiver execution or its cached ACK. Production still omits the custody bundle and capability; A disconnected readiness evaluator requires requested absent/v3 source, proven legacy-writer exclusion, completed historical migration and capability-ready peers; all mixed, legacy, unsupported, ambiguous or unknown evidence returns a bounded blocker. A disconnected lazy resolver reads no source/peer evidence while disabled or blocked by writer/migration proof, maps inspection loss to source-unready and inventory loss to capability mismatch, and creates no journal/lifecycle binding. Read-only adapters now normalize real strict family inspection as absent/v3/legacy/ambiguous and live follower generation/protocol inventory as ready/legacy/unknown; corrupt source and missing live identity fail closed without mutation. A real disk/registry composition rereads on every call: absent/v3 plus peers advertising durable admission and custody reference enables; peer downgrade, source corruption, or missing identity blocks; replacement upgrade re-enables. Migration and writer exclusion now enter through typed versioned evidence bound to exact profile/recovery identity; missing, unknown, incomplete, present-writer or mismatched evidence blocks before source/peer inspection. A disconnected retained-evidence store adds a bounded strict v1 codec, revision CAS under injected serialization, kind-specific validation and injected publication authority. Malformed, stale, unauthorized or cross-kind evidence never reaches the persistence port. Writer and migration records in one snapshot must share exact profile/recovery identity; even authorized mixed-identity publication fails before persistence. No current component may mint writer exclusion because existing census/serialization cannot prove arbitrary writer or consumer absence. Lifecycle runtime now admits an injected logical source lease: bind acquires, replacement releases/reacquires, shutdown releases despite retaining a reusable binding object, and restart reacquires. Release failure cannot retain runtime authority. One bounded exact-release process-local registry now backs production leader/follower lifecycle leases; capacity and stale release fail closed, and shutdown/restart/replacement update inventory. It is neither durable nor whole-root proof. The registry also scopes sync/async operations and releases on return, throw or rejection. Production status/polling cursor reads, cursor cutover and bootstrap entry reads now use short-lived leader leases; missing binding acquires none. Workspace-retirement protection now scopes shared, retained-binding and discovered journal reads through the same registry and releases before planning continues, including read failure. A composition invariant now rejects direct resolver journal reads in the entrypoint. Remaining production resolver uses are leased lifecycle bindings, scoped polling/retirement operations, or queue-handoff recovery-key computation without journal I/O. Composition invariants reject raw store construction in `index.ts`, require follower durable writes through the leased lifecycle, scope the sole cursor append, and prove current root/API exports expose no journal mutation factory. Old-process exclusion now has a pure evaluator, but not an inventory authority: exact profile/recovery identity, caller-proven complete writer list and process-birth liveness are mandatory. Alive means present; mismatch/incomplete/unverifiable means unknown; only every listed birth proven dead means excluded. Registry absence is never death. The journal transaction owner now exposes an optional outer writer-admission seam through all binding factories. It gates append, mutation and ordinary read (which may repair) before source serialization/journal locking; denial writes nothing, while strict inspection remains read-only. Production leaves it unset. The approved preparation direction is one third Workspace-ledger destructive kind, `journal-writer-closure`, not another ledger and not a cleanup/retirement alias. Its discriminated payload binds profile/recovery identity and closure operation ID but has no target, deletion permit or absence phase. Acquisition requires zero ordinary leases; while held, ordinary profile admission fails. The journal writer seam will acquire/release one ordinary profile lease before config/journal locking, so closure and writers serialize through ledger transactions without nesting the config transaction. Current lock-order regression proves denial never enters pairing/source serialization and allowed append enters writer admission first. Legacy missing-kind fences remain pressure retirement. The third kind and strict identity-only payload codec reject deletion fields and identity drift while retirement/cleanup fence types remain narrowed. The top-level ledger union is integrated across admission, retirement coordination, and cleanup ports: closure blocks ordinary admission and competing destructive work globally, owns no Thread slot, and every deletion operation narrows the kind before accessing deletion authority. Compile-time deletion fence/permit authority is also a dedicated retirement-or-cleanup union; `journal-writer-closure` cannot inhabit it. Exact closure acquisition/release is now available on the ledger but remains disconnected from production. Acquisition requires zero leases, resumes the exact durable authority after lost ACK without republishing, rejects same-operation drift, and blocks other destructive work; release requires exact current-owner authority. The optional journal-writer adapter acquires a profile lease before entering the journal seam and retains its ID after ambiguous acquisition. Release failure is diagnostic-only after the journal operation settles: retaining a possible lease blocks closure safely, while throwing from `finally` would falsely erase the known operation outcome. The existing binding runtime propagates one supplied adapter to leader, active follower, recipient and path-discovered journals; regression coverage closes all four classes. Production omission still means no writer closure activation. Writer-exclusion evidence cannot yet be minted truthfully inside this fence: releasing it allows a newly starting legacy writer, retaining it blocks v3 writers too, and retained v1 `excluded` evidence records neither closure operation nor startup authority. Closure publication therefore requires a prior durable operator-authorized startup-exclusion authority plus protocol-class admission semantics. A strict standalone authority schema now binds profile/recovery, closure operation, inventory SHA-256, operator authority ID, explicit enforced/revoked status, authorization time, and `custody-v3` as the sole allowed protocol. It is retained by the readiness store through strict revisioned CAS, injected publication authorization and cross-kind identity checks. The full candidate is decoded and normalized before authorization, so malformed/extra evidence never crosses the authority callback; enforced and revoked states are retained exactly. Proven readiness now consumes it fail-closed: `excluded` writer evidence must link the same enforced authority ID, closure operation and inventory digest. Legacy unlinked evidence, revocation or any link drift blocks activation, and the store rejects conflicting linked authorities. Proven readiness also requires the durable installed protocol mode to match the same profile/recovery, startup authority, closure and inventory digest. A crash before mode installation or before linked exclusion publication therefore remains disabled. The non-nesting operator coordinator now enforces the safe order: verify retained enforced authority → evaluate exact inventory/liveness under closure → atomically install mode → reread authority → CAS-publish linked exclusion. A pre-publication revocation or race leaves mode installed but no readiness evidence, so activation remains disabled; exact retry reconciles installed mode and existing evidence without duplicate authority. Production has no caller. Migration completion now has a standalone strict authority binding the same cutover identities plus a complete historical source/disposition digest, resulting `absent|v3` family, operator authorization time and explicit revocation. It is now retained through authorized revisioned CAS and consumed by proven readiness only when migration evidence links the same migration/startup/closure/inventory authority. Revocation, legacy unlinked evidence, or a live source family different from the authorized `absent|v3` result blocks activation on every resolution. The disconnected migration publisher rereads retained authority, compares the exact historical inventory digest and inspects the live source before CAS-publishing linked completion. Exact retry is revision-stable; it performs no migration or source mutation. Its strict standalone Workspace mode codec now binds `custody-v3` to exact profile/recovery, startup authority, closure operation, inventory digest, installer owner and install time. The ledger now retains it by atomically replacing the matching zero-lease closure; fence+mode state is invalid and exact lost-ACK installation resumes without republish. While mode exists, generic `journal-write` and new closure acquisition fail closed. Dedicated v3 writer admission must present matching recovery, startup authority, closure and inventory digest; operation kind alone never grants access. While mode is active, generic `journal-write` and `journal.*` admission requires an already-held same-owner dedicated v3 profile lease. This permits nested append/input admission under the outer writer seam but rejects caller-chosen journal operation names as authority. Production installs no mode. Mode→closure publication is crash-proven: after an acknowledged-unknown atomic rename, a fresh ledger resumes the exact closure without invoking authorization again or restoring mode. Re-closing an installed mode is possible only through an injected production-unset authorizer: with zero leases, one ledger transaction replaces the exact recovery-bound mode with a new closure; active writers block before authorization and denied callbacks leave mode unchanged. Exact closure retry handles lost publication ACK. A compile probe established that this must be one compatibility cohort across the ledger, retirement coordinator, and cleanup-manager ledger port; publishing a wider snapshot before those consumers narrow by kind is forbidden. Required slices are: generalize fence payload codec without changing existing kinds; add closure acquire/exact release only; wire writer admission; then allow complete inventory capture and evidence publication while the same fence remains held. No step may infer external writer absence. Exported legacy factories and arbitrary installed-package consumers remain outside process-local proof; writer-exclusion minting therefore requires package/API closure or explicit retirement, and rollout stays gated. Optional lifecycle exposure and replacement invalidation remain disconnected. Production bindings expose neither port. Its optional late-settlement callback lets the worker clear an exact deferred projection or register already-durable queue authority without repeating a legacy journal mutation. The real worker drain accepts an optional already-settled custody result: completion rereads durable state without legacy removal, while outcome-unknown blocks without creating retry authority. Production still selects only the legacy path.
311
+ The correction therefore needs durable delivery custody, not merely a completion cache or an origin-presence predicate. Existing queue handoff already changes exact ownership in one journal: offer freezes the donor, acceptance CAS installs the recipient, and an ambiguous ACK cannot restore donor authority. Its API requires an already queued receipt; raw input must not masquerade as a queued Pi prompt/control. The operator approved continuing with one authoritative input record and explicit execution ownership, with bus delivery transferring/reference-waking that authority instead of creating a second executable copy. This is logical uniqueness per input, not a requirement to combine every profile into one physical file. Legacy journals and their receipts remain intact until separately authorized reconciliation. The ownership contract below governs the next local implementation slice; storage encoding, raw-owner recovery and lifecycle integration remain separately evidenced, and no production activation is implied. A disconnected worker-facing adapter now proves the first integration seam: acquire and start precede handler execution; complete removes exact custody; a single-input queued outcome atomically becomes its durable queue receipt; deferred returns the running receipt; and an ambiguous running claim never executes again. A session-scoped extension retains deferred receipts and atomically groups them into one queue receipt; consumed raw receipts cannot later settle. Late deferred complete/grouped-queue outcomes require that same retained session receipt, so stale or missing callbacks fail closed. Handler failure and fresh-session observation of retained `running` discard local settlement capability, preventing synthetic completion of outcome-unknown work. A disconnected custodied admission handle now runs the existing registry/default routing inside that session; immediate and late reports settle through the same exact receipt authority. Lifecycle bindings may opt into a strict v3 worker port: v3 snapshots require custodied execution, legacy raw completion/queue/failure methods throw, and custodied handler failure remains blocked running work rather than legacy retry state. A gated resolver reads no v3 dependency while disabled; when enabled, its runtime identity binds both source runtime and recipient execution binding so either change forces lifecycle replacement. Real-v3 assembly evidence preserves retained running input across follower registration-generation replacement and follower-to-leader promotion without another handler call or settlement. Worker selection skips running, foreign-ready, and handoff-frozen inputs while draining independent tail; each custodied commit yields and rereads durable state before unresolved work reports `execution` or `input-custody`. Diagnostics expose only update ID and `running-outcome-unknown`, `foreign-ready`, `handoff-frozen`, or `legacy-retry-state`, never owner, acquisition, binding, payload, path, or token details. Running outcome-unknown wins mixed-class priority. Legacy retry/failed state under v3 is quarantined while independent pending tail drains; it is never replayed through the legacy failure policy. A standalone disposition authority now hashes immutable failure metadata without retaining update payload and permits only exact `requeue-v3|discard` operator intent. Claimed/provenanced entries and generic terminal `retry` are excluded. The bounded segmented operator-disposition audit now accepts a discriminated legacy-custody record, avoiding a second log. Its v3-only mutation is reachable only through an injected operator authorization seam and profile admission: exact evidence atomically becomes unclaimed pending for `requeue-v3` or is removed for `discard`; duplicate authority is idempotent and collisions fail closed. Production omits authorization. The strict worker port now constructs an explicit four-method custody projection rather than returning the structurally narrowed backing store; runtime reflection proves operator listing and disposition are absent. This closes a former capability leak hidden by TypeScript's width subtyping. The disconnected operator runtime requires a caller-owned exact binding-reference scope around every list/apply call and retains no store reference. The scope must span the operation, not only lookup, so retirement/pruning cannot race a stale binding; removal, replacement or recovery mismatch fails closed. The existing bounded reference registry now has an `operator-disposition` class for this composition, but remains process-local evidence only. No production or Telegram command owns this runtime. A source invariant forbids the composition root from acquiring writer closure, installing protocol mode, executing cutover or migration publication, supplying legacy disposition authorization, or constructing the operator runtime. A separate strict non-repairing source-inspection port returns only update ID, retry/failed state, attempt count, bounded failure class and evidence SHA-256; it does not enter writer/source mutation serialization; update payload, failure summary and execution authority remain inside the journal owner. Duplicate reconciliation normalizes exact retained authority before invoking the injected authorizer; malformed extra fields never cross that boundary. If snapshot publication is commit-unknown, exact retry finds the durable audit and performs no second transition; a new disposition cannot target the resulting pending entry. Running custody still has no admissible legacy evidence. A real-v3 replacement regression begins with retained `running` and proves neither the old nor replacement session can execute or settle it after recipient identity changes. Source `recoveryKey` and recipient execution binding are distinct mandatory identities. An assembled regression proves late reports from two deferred sources publish one grouped durable queue receipt. Grouped late reports retain one exact duplicate result for each remaining source callback; the worker accepts the same already-published queue authority idempotently and rejects conflicts. Lifecycle assembly selects this path only for an optional binding-owned custody port. Its strict v3 worker adapter permits exact queued completion but throws before legacy raw completion, queue, or failure mutation; handler errors remain running outcome-unknown. The worker consumes already-durable results without legacy completion/queue writes, synchronizes late claims, and lifecycle assembly selects this path only from an optional binding-owned custody port plus exact binding key. The strict v3 binding also exposes queued receipt offer/accept/cancel through serialized admission wrappers; lifecycle lookup now proves exact recipient acceptance and exact duplicate retries return the same owner. Recipient worker projection is queried only with the matching journal binding key and survives lifecycle replacement. These APIs transfer Pi queue authority and remain distinct from raw input handoff. Cancelling a frozen raw handoff and signaling the worker makes the input executable and clears its redacted blocked projection. A legacy journal writer cannot clear quarantined v3 retry evidence because strict source-version selection rejects that mutation; any future operator disposition therefore needs explicit v3 authority separate from worker settlement. A disconnected bus admission mode validates delivery, source and recipient identity, then wakes the existing durable source reference without journaling the forwarded carrier as a second executable copy. Duplicate delivery can repeat only the wake; stale registration generation cannot wake, and selection fails closed without wake authority rather than falling back to copy admission. The gated delivery identity carries an optional bounded source `recoveryKey` outside the unchanged legacy delivery hash. Reference mode requires that key, bounded exact acquisition/handoff IDs, and explicit authenticated-transport proof; missing or mixed legacy evidence is rejected before wake. Legacy copy mode does not require or infer these fields. Wake carries the complete reference into a disconnected binding-owned resolver. It requires exact recovery key, recipient binding/live owner, update, acquisition and accepted handoff on one unfrozen `ready` claim before signaling; stale, running, missing or mismatched evidence cannot wake or mutate. An authenticated receiver composition against a real shared v3 journal now proves exact and duplicate delivery only signal the accepted ready claim; stale acquisition or registration generation cannot wake or fall back to copy admission. A disconnected sender selector requires mutual `input-custody-reference-v1` capability, and its constructor accepts only an exact accepted handoff when producing recovery/acquisition/handoff evidence. The dedicated `leader.wakeInputCustody` envelope carries no executable Telegram carrier and is forced through authenticated reference admission; legacy durable admission rejects it. A shared-v3 regression composes accepted handoff, leader resolver, mutually capable forwarder, authenticated payload-free receiver and exact wake; exact request replay reuses the cached ACK without another signal. A recipient adapter resolves recovery/binding/live-owner authority, performs exact acceptance CAS, verifies the returned handoff, and only then exposes the reference and signals. The payload-free `leader.offerInputCustodyHandoff` envelope carries exact source/handoff and recipient lifetime evidence into it; parsing, authentication, capability, binding and handler gates fail closed, while duplicate acceptance returns durable duplicate evidence. A real-v3 composition runs donor offer-before-send through the authenticated handoff receiver; acceptance CAS signals once, and client replay reconciles locally without another request. No immediate wake follows acceptance because it would duplicate signaling; payload-free wake remains only an idempotent recovery nudge for already accepted custody. Unknown outcomes remain frozen; after recipient acceptance, retry first reconciles the accepted shared-journal reference and returns duplicate settlement without re-offer, transport, or new authority. Full handoff-then-wake request composition remains disconnected. Frozen pre-accept lookup yields no reference, preventing transport. Missing reference fails retryably before transport; mixed peers retain legacy carrier delivery. A disconnected leader resolver reads shared v3 state without mutation and returns a forward reference only for the exact recipient-owned unfrozen `ready` claim; donor-frozen, running, stale binding or owner mismatch returns no reference. A single optional custody bus bundle derives acceptance, wake and forward-reference resolution from one recovery-key lookup carrying the same recipient binding/live owner, journal and signal authority. Lifecycle assembly/runtime binding exposes that bundle only when supplied; losing its active authority disables lookup and makes acceptance/wake fail closed together. A follower transport adapter rereads the optional bundle for every capability check, acceptance, wake and forward lookup. Removing the bundle on downgrade/reconnect simultaneously disables capability/resolution and makes stale operations fail closed without cached authority. A disconnected receiver assembly rereads these ports across downgrade/reconnect: bundle removal rejects before handlers, replacement generation uses only the new bundle, and stale generation cannot call either bundle or legacy admission. Live target ownership now carries the exact current registration protocol identity into forwarder selection: capability upgrade enables reference mode and downgrade returns to legacy, while persisted records grant neither mode. Forwarders may additionally revalidate current ownership after reference resolution and immediately before transport. If generation, binding, or protocol changed, they return `recipient-ownership-stale` without sending, preventing an old capable snapshot from crossing reconnect. Production follower-client composition uses one canonical live-registry validator immediately before transport. Removal, same-generation protocol replacement, or generation replacement invalidates old ownership; only a fresh current projection passes. The receiver generation check remains an independent second fence. A post-accept retry using stale ownership is rejected before transport after registry replacement, so it cannot reach either receiver execution or its cached ACK. Production still omits the custody bundle and capability; A disconnected readiness evaluator requires requested absent/v3 source, proven legacy-writer exclusion, completed historical migration and capability-ready peers; all mixed, legacy, unsupported, ambiguous or unknown evidence returns a bounded blocker. A disconnected lazy resolver reads no source/peer evidence while disabled or blocked by writer/migration proof, maps inspection loss to source-unready and inventory loss to capability mismatch, and creates no journal/lifecycle binding. Read-only adapters now normalize real strict family inspection as absent/v3/legacy/ambiguous and live follower generation/protocol inventory as ready/legacy/unknown; corrupt source and missing live identity fail closed without mutation. A real disk/registry composition rereads on every call: absent/v3 plus peers advertising durable admission and custody reference enables; peer downgrade, source corruption, or missing identity blocks; replacement upgrade re-enables. Migration and writer exclusion now enter through typed versioned evidence bound to exact profile/recovery identity; missing, unknown, incomplete, present-writer or mismatched evidence blocks before source/peer inspection. A disconnected retained-evidence store adds a bounded strict v1 codec, revision CAS under injected serialization, kind-specific validation and injected publication authority. Malformed, stale, unauthorized or cross-kind evidence never reaches the persistence port. Writer and migration records in one snapshot must share exact profile/recovery identity; even authorized mixed-identity publication fails before persistence. No current component may mint writer exclusion because existing census/serialization cannot prove arbitrary writer or consumer absence. Lifecycle runtime now admits an injected logical source lease: bind acquires, replacement releases/reacquires, shutdown releases despite retaining a reusable binding object, and restart reacquires. Release failure cannot retain runtime authority. One bounded exact-release process-local registry now backs production leader/follower lifecycle leases; capacity and stale release fail closed, and shutdown/restart/replacement update inventory. It is neither durable nor whole-root proof. The registry also scopes sync/async operations and releases on return, throw or rejection. Production status/polling cursor reads, cursor cutover and bootstrap entry reads now use short-lived leader leases; missing binding acquires none. Workspace-retirement protection now scopes shared, retained-binding and discovered journal reads through the same registry and releases before planning continues, including read failure. A composition invariant now rejects direct resolver journal reads in the entrypoint. Remaining production resolver uses are leased lifecycle bindings, scoped polling/retirement operations, or queue-handoff recovery-key computation without journal I/O. Composition invariants reject raw store construction in `index.ts`, require follower durable writes through the leased lifecycle, scope the sole cursor append, and prove current root/API exports expose no journal mutation factory. Old-process exclusion now has a pure evaluator, but not an inventory authority: exact profile/recovery identity, caller-proven complete writer list and process-birth liveness are mandatory. Alive means present; mismatch/incomplete/unverifiable means unknown; only every listed birth proven dead means excluded. Registry absence is never death. The journal transaction owner now exposes an optional outer writer-admission seam through all binding factories. It gates append, mutation and ordinary read (which may repair) before source serialization/journal locking; denial writes nothing, while strict inspection remains read-only. Production leaves it unset. The approved preparation direction is one third Workspace-ledger destructive kind, `journal-writer-closure`, not another ledger and not a cleanup/retirement alias. Its discriminated payload binds profile/recovery identity and closure operation ID but has no target, deletion permit or absence phase. Acquisition requires zero ordinary leases; while held, ordinary profile admission fails. The journal writer seam will acquire/release one ordinary profile lease before config/journal locking, so closure and writers serialize through ledger transactions without nesting the config transaction. Current lock-order regression proves denial never enters pairing/source serialization and allowed append enters writer admission, then pairing (config) admission, then source serialization. Legacy missing-kind fences remain pressure retirement. The third kind and strict identity-only payload codec reject deletion fields and identity drift while retirement/cleanup fence types remain narrowed. The top-level ledger union is integrated across admission, retirement coordination, and cleanup ports: closure blocks ordinary admission and competing destructive work globally, owns no Thread slot, and every deletion operation narrows the kind before accessing deletion authority. Compile-time deletion fence/permit authority is also a dedicated retirement-or-cleanup union; `journal-writer-closure` cannot inhabit it. Exact closure acquisition/release is now available on the ledger but remains disconnected from production. Acquisition requires zero leases, resumes the exact durable authority after lost ACK without republishing, rejects same-operation drift, and blocks other destructive work; release requires exact current-owner authority. The optional journal-writer adapter acquires a profile lease before entering the journal seam and retains its ID after ambiguous acquisition. Release failure is diagnostic-only after the journal operation settles: retaining a possible lease blocks closure safely, while throwing from `finally` would falsely erase the known operation outcome. The existing binding runtime propagates one supplied adapter to leader, active follower, recipient and path-discovered journals; regression coverage closes all four classes. Production omission still means no writer closure activation. Writer-exclusion evidence cannot yet be minted truthfully inside this fence: releasing it allows a newly starting legacy writer, retaining it blocks v3 writers too, and retained v1 `excluded` evidence records neither closure operation nor startup authority. Closure publication therefore requires a prior durable operator-authorized startup-exclusion authority plus protocol-class admission semantics. A strict standalone authority schema now binds profile/recovery, closure operation, inventory SHA-256, operator authority ID, explicit enforced/revoked status, authorization time, and `custody-v3` as the sole allowed protocol. It is retained by the readiness store through strict revisioned CAS, injected publication authorization and cross-kind identity checks. The full candidate is decoded and normalized before authorization, so malformed/extra evidence never crosses the authority callback; enforced and revoked states are retained exactly. Proven readiness now consumes it fail-closed: `excluded` writer evidence must link the same enforced authority ID, closure operation and inventory digest. Legacy unlinked evidence, revocation or any link drift blocks activation, and the store rejects conflicting linked authorities. Proven readiness also requires the durable installed protocol mode to match the same profile/recovery, startup authority, closure and inventory digest. A crash before mode installation or before linked exclusion publication therefore remains disabled. The non-nesting operator coordinator now enforces the safe order: verify retained enforced authority → evaluate exact inventory/liveness under closure → atomically install mode → reread authority → CAS-publish linked exclusion. A pre-publication revocation or race leaves mode installed but no readiness evidence, so activation remains disabled; exact retry reconciles installed mode and existing evidence without duplicate authority. Production has no caller. Migration completion now has a standalone strict authority binding the same cutover identities plus a complete historical source/disposition digest, resulting `absent|v3` family, operator authorization time and explicit revocation. It is now retained through authorized revisioned CAS and consumed by proven readiness only when migration evidence links the same migration/startup/closure/inventory authority. Revocation, legacy unlinked evidence, or a live source family different from the authorized `absent|v3` result blocks activation on every resolution. The disconnected migration publisher rereads retained authority, compares the exact historical inventory digest and inspects the live source before CAS-publishing linked completion. Exact retry is revision-stable; it performs no migration or source mutation. Its strict standalone Workspace mode codec now binds `custody-v3` to exact profile/recovery, startup authority, closure operation, inventory digest, installer owner and install time. The ledger now retains it by atomically replacing the matching zero-lease closure; fence+mode state is invalid and exact lost-ACK installation resumes without republish. While mode exists, generic `journal-write` and new closure acquisition fail closed. Dedicated v3 writer admission must present matching recovery, startup authority, closure and inventory digest; operation kind alone never grants access. While mode is active, generic `journal-write` and `journal.*` admission requires an already-held same-owner dedicated v3 profile lease. This permits nested append/input admission under the outer writer seam but rejects caller-chosen journal operation names as authority. Production installs no mode. Mode→closure publication is crash-proven: after an acknowledged-unknown atomic rename, a fresh ledger resumes the exact closure without invoking authorization again or restoring mode. Re-closing an installed mode is possible only through an injected production-unset authorizer: with zero leases, one ledger transaction replaces the exact recovery-bound mode with a new closure; active writers block before authorization and denied callbacks leave mode unchanged. Exact closure retry handles lost publication ACK. A compile probe established that this must be one compatibility cohort across the ledger, retirement coordinator, and cleanup-manager ledger port; publishing a wider snapshot before those consumers narrow by kind is forbidden. Required slices are: generalize fence payload codec without changing existing kinds; add closure acquire/exact release only; wire writer admission; then allow complete inventory capture and evidence publication while the same fence remains held. No step may infer external writer absence. Exported legacy factories and arbitrary installed-package consumers remain outside process-local proof; writer-exclusion minting therefore requires package/API closure or explicit retirement, and rollout stays gated. Optional lifecycle exposure and replacement invalidation remain disconnected. Production bindings expose neither port. Its optional late-settlement callback lets the worker clear an exact deferred projection or register already-durable queue authority without repeating a legacy journal mutation. The real worker drain accepts an optional already-settled custody result: completion rereads durable state without legacy removal, while outcome-unknown blocks without creating retry authority. Production still selects only the legacy path.
312
312
 
313
313
  ##### Input Custody Contract
314
314
 
@@ -362,7 +362,7 @@ Read-only migration probes constrain that comparison. Publishing a v2 snapshot o
362
362
  - Session replacement through `reload`, `new`, `resume`, or `fork` suspends polling/watchers without releasing ownership or publishing inactivity so the next session in the same process can resume. Late shutdown cannot clear a replacement context even when its identity is reused: the captured session generation must still match. For replacements other than `/resume`, a registered follower snapshots its assigned target into a short-lived same-process handoff, stops the old receiver/heartbeat, and re-registers through the live leader without marking or replacing its Telegram thread. Local `/resume` suppresses that source-target handoff and selects the destination session's binding instead. Hard process termination cannot run graceful teardown, so stale recovery retains its restart-hint path.
363
363
  - Live external owners require explicit takeover confirmation. Long-lived timers compare against snapshotted owner identity and stop local transport work when the slot no longer matches.
364
364
  - The profile's `transport` section owns only Telegram transport control. Local extension and accepted queue state remain per Pi instance when ownership moves, but previews, final delivery, dispatch transport mutations, and delayed Bot API work fail closed until exact direct or follower authority becomes valid again.
365
- - Exact ownership remains checked every second, while the durable owner heartbeat refresh runs every two seconds and becomes stale after eight seconds. Every acquisition, refresh, release, takeover, and stale recovery is one `transport`-section transaction through the shared `runtime/state.json.transaction` guard (see [Consolidated Runtime Root](#consolidated-runtime-root)); it fences stale recovery and delayed release against replacement-owner ABA and fails closed on malformed state, unverifiable ownership, contention timeout, or unsupported filesystem behavior. Publication uses private staging below `runtime/` and atomic rename.
365
+ - Exact ownership remains checked every second and re-confirmed every two seconds by lock-free reads; the leader writes no heartbeat, and a live owner is replaceable only by dead-PID or bus liveness proof (see [Leader Election](./multi-instance-bus.md)). Every acquisition, entry migration, release, takeover, and stale recovery is one `transport`-section transaction through the shared `runtime/state.json.transaction` guard (see [Consolidated Runtime Root](#consolidated-runtime-root)); it fences stale recovery and delayed release against replacement-owner ABA and fails closed on malformed state, unverifiable ownership, contention timeout, or unsupported filesystem behavior. Publication uses private staging below `runtime/` and atomic rename.
366
366
  - `state.json` is authoritative and private: `transport` (ownership), `workspace` (canonical Workspace/Restore evidence) and `admission` per profile. The non-canonical runtime/roster/diagnostics projection is the `runtime` section, published only by the transport owner without touching canonical sections; it and `logs.jsonl` never grant routing authority. The leader reads optional follower slot hints from it; a missing or malformed projection is ignored. Followers remain authenticated bus registrations rather than transport/Workspace/runtime writers; they may publish their process-owned admission leases.
367
367
  - Ordinary ownership and state mutations fail closed on a malformed or foreign `state.json`; read-only ownership queries report no owner, not proof of process absence. The legacy whole-file reset is not wired for the shared envelope. A non-election leader start performs the [damaged-state reset](#damaged-state-reset-operator-approved); section-scoped recovery is not planned. Startup best-effort removes obsolete current-runtime root/session `recovery/` directories, never the pre-0.52.0 tree.
368
368
 
@@ -390,7 +390,7 @@ This bounded structure design is applied locally: Workspace identity has a reusa
390
390
 
391
391
  **Kernel (Locks).** `readTelegramRuntimeState` and `mutateTelegramRuntimeStateSection` own physical authority. Reads neither create nor repair files and refuse unsupported/malformed envelopes, non-private/non-regular sources and changed physical observations; missing state is an empty envelope only when the initial observation positively reports absence. A publisher acquires one shared `runtime/<state basename>.transaction`, reads the latest envelope, supplies detached current-section/sibling observations to one synchronous reducer and replaces only its named profile/section; semantic no-ops keep file identity. Authority is rechecked before mutation, after the reducer, before writing, immediately before atomic rename and before a positive acknowledgement. Nested transactions for the same path are refused. A lost rename reply or post-publication authority loss returns `publication-unknown`; the committed fact remains and never grants replay or rollback.
392
392
 
393
- **Transport.** `createTelegramLockRuntime({ statePath })` keeps acquire/election/expected-owner/heartbeat/release semantics in its profile's `transport` section, preserving sibling fields and the polling pointer through release and succession. Read-only ownership queries fail closed (no owner) on unreadable state so unrelated Pi hooks never crash; acquisition/publication still validate and refuse without replacing it. `publishStateSectionIfOwned` publishes only Workspace or runtime data under exact retained owner and caller authority with an optional `expectedScope`; it never publishes transport/admission or borrows a surrounding `commitIfOwned` grant.
393
+ **Transport.** `createTelegramLockRuntime({ statePath })` keeps acquire/election/expected-owner/unresponsive-owner/release semantics in its profile's `transport` section, preserving sibling fields and the polling pointer through release and succession. Read-only ownership queries fail closed (no owner) on unreadable state so unrelated Pi hooks never crash; acquisition/publication still validate and refuse without replacing it. `publishStateSectionIfOwned` publishes only Workspace or runtime data under exact retained owner and caller authority with an optional `expectedScope`; it never publishes transport/admission or borrows a surrounding `commitIfOwned` grant.
394
394
 
395
395
  **Session-bound grant.** `Locks.createTelegramOwnedStateAuthorityCapture(lock, session)` captures the exact current Pi context, session generation and owned leader epoch before the caller's first await through a structural session port (Locks does not import Lifecycle). The grant stays current only while that context/generation is current, the lock is owned for that context and the epoch is unchanged. Session replacement, same-context restart, clear, release and release-then-reacquire (a new epoch, as disconnect/connect does) revoke it; a successor captures a fresh grant and never renews the old one. Through the real Workspace store, a persist captured before a new session refuses without changing bytes while the successor publishes; an epoch-only capture would have committed that stale write.
396
396
 
@@ -549,7 +549,7 @@ Queued semantic authority has no elapsed-time lease. A timeout cannot prove eith
549
549
 
550
550
  The initial `offset: -1` cursor bootstrap is allowed only when both cursor and journal are absent or empty. Thereafter process-level ordering is journal atomic rename → one monotonic offset atomic rename → worker signal. Failure before journal publication leaves the offset unchanged; failure after journal publication but before offset publication permits Telegram redelivery and journal dedupe; failure after offset publication but before worker signal replays from the journal on restart. Queue-owner, retry, terminal, handoff, and completion transitions use the same journal publication primitive and therefore share this process-crash boundary. The final completion window is at-least-once, so replay-sensitive external effects must use `update_id` or the stable delivery id as an idempotency key.
551
551
 
552
- Threaded Mode forwarding is a two-journal handoff. Only peers that mutually advertise protocol v1 and `durable-follower-admission-v1` may route or become election-eligible. The follower validates its exact binding and registration generation, durably appends the source-bound delivery, and only then returns the exact receipt. The leader classifies each attempt as `accepted`, `retryable`, or `terminal-rejected` with its delivery identity and failure class; only `accepted` with the expected `deliveryId` and `sourceUpdateId` may complete leader journal authority. Missing, negative, stale-generation, or mismatched-receipt acknowledgements remain durable, and a callback error answer is only an operator-facing side effect. The follower's bounded [delivery replay window](./multi-instance-bus.md) acknowledges a repeated `deliveryId` without re-execution.
552
+ Threaded Mode forwarding is a two-journal handoff. Only peers that share the current base protocol and mutually advertise `durable-follower-admission-v1` may route or become election-eligible. The follower validates its exact binding and registration generation, durably appends the source-bound delivery, and only then returns the exact receipt. The leader classifies each attempt as `accepted`, `retryable`, or `terminal-rejected` with its delivery identity and failure class; only `accepted` with the expected `deliveryId` and `sourceUpdateId` may complete leader journal authority. Missing, negative, stale-generation, or mismatched-receipt acknowledgements remain durable, and a callback error answer is only an operator-facing side effect. The follower's bounded [delivery replay window](./multi-instance-bus.md) acknowledges a repeated `deliveryId` without re-execution.
553
553
 
554
554
  Delivery ids derive only from envelope kind, source `update_id`, and stable recipient binding. Live registration generation remains a separate attempt fence. Message ownership carries the stable binding and rebinds to its current authenticated follower registration after replacement. `ownership.getForwardOwnership()` projects the protocol identity from the exact matching live instance, registration generation, and binding on every lookup; protocol is not cached with message history. The same port serves messages, edits, callbacks, and reactions, including callbacks without a Thread ID. If a cache lookup returns a foreign record but no matching durable-capable protocol can be projected, that record remains foreign and fails forwarding validation rather than falling through to local execution. The final bus validator independently rechecks generation, binding, and complete protocol identity before transport. Lost acknowledgements therefore retain the same delivery identity; only the exact durable receipt completes the source. Package build skew is allowed only while protocol version and capabilities remain compatible.
555
555
 
@@ -54,12 +54,12 @@ The operator experience:
54
54
  - Do not couple the first design to Pi sessions if live instance ownership is the better runtime truth.
55
55
  - Do not expose arbitrary group participants to prompts, controls, or artifacts.
56
56
  - Do not require Threaded Mode for classic private-chat users; classic private-chat mode remains valid and should not receive slot/thread-name guidance.
57
- - Do not implement leader election through unsafe lock stealing without heartbeats or stale-owner checks.
57
+ - Do not implement leader election through unsafe lock stealing without stale-owner proof (dead PID, or a bus-proven unresponsive owner).
58
58
 
59
59
  ## Terms
60
60
 
61
61
  - `Telegram bus`: The singleton local capability to poll Telegram updates and send Telegram API calls for one bot token.
62
- - `Leader`: The live Pi instance that currently owns the Telegram bus and calls `getUpdates`; this is an ephemeral role transferable after stale heartbeat detection.
62
+ - `Leader`: The live Pi instance that currently owns the Telegram bus and calls `getUpdates`; this is an ephemeral role transferable after its PID dies or the bus proves it unresponsive.
63
63
  - `Follower`: A live Pi instance that wants Telegram presence but routes Telegram API access through the leader.
64
64
  - `Bus lifecycle`: Transient recovery state only. Stable identity is the bus role (`leader` / `follower`); lifecycle surfaces exceptional handoff states such as `electing`, not duplicate roles with labels like `leader-active`.
65
65
  - `Agent instance`: A running Pi process/session with its own extension state, queue, active turn, model, tools, and lifecycle hooks.
@@ -80,7 +80,7 @@ tmp/pi-telegram/state.json / profiles.<profile>.transport -> polling/control own
80
80
  Threaded Mode meaning:
81
81
 
82
82
  ```text
83
- tmp/pi-telegram/state.json / profiles.<profile>.transport -> bus leader identity + heartbeat
83
+ tmp/pi-telegram/state.json / profiles.<profile>.transport -> bus leader identity (no periodic heartbeat)
84
84
  ```
85
85
 
86
86
  Followers do not poll. They register with the leader and receive routed inbound updates from it. Followers still own their local queue, active-turn state, previews, final delivery planning, model switches, and Pi lifecycle. The leader owns only Telegram transport and update fanout. Same-session context refresh preserves bus membership; a changed Pi session ID suspends the old receiver/heartbeat and requires acknowledged successor registration before readiness, without an explicit Thread disconnect. A new follower process also attempts a restore-only registration at session startup when the selected profile's local state contains a Workspace binding for the exact `cwd` and session ID. The recent-event log distinguishes missing session bindings, disabled Threaded Mode, leader-side restore refusal, and a restore that returned without connecting; none of these refusals silently provisions a new Thread. The Telegram bus belongs to the local set of cooperating visible Pi instances rather than to the first terminal session forever: if the visible terminal leader exits, a live registered follower can take over leadership.
@@ -255,13 +255,13 @@ Routing authority remains the authenticated live owner plus exact numeric Telegr
255
255
 
256
256
  ## Leader Election
257
257
 
258
- Leader election is heartbeat-gated and lock-backed. The polling owner checks exact lock ownership every second, refreshes its durable lease every two seconds, and becomes stale after eight seconds; the serialized expected-owner transaction remains the final cross-platform election authority.
258
+ Leader election is liveness-proof-gated and lock-backed. Since 0.54 the leader writes no periodic heartbeat: the transport section changes only on acquire, release, takeover or entry migration, and the owner's one- and two-second ownership checks are lock-free reads. A dead PID is stale immediately. A live PID is replaceable only with bus proof: an authenticated side-effect-free `bus.probe` to the leader endpoint that either stays unanswered for the eight-second window, or finds a refusing endpoint twice one window apart (a just-started leader binds well within it). Any reply, including an older leader rejecting the unknown kind, proves it alive; a missing endpoint proves nothing, because a live classic-mode leader has none. Classic mode therefore never takes over a live PID automatically: `/telegram-connect` asks the operator. Entries written by older releases still carry `heartbeatMs` and keep the eight-second stale rule. The serialized expected-owner transaction, binding the proof to the exact owner epoch, remains the final cross-platform election authority.
259
259
 
260
260
  1. On startup, read the Telegram lock.
261
261
  2. If no leader exists, acquire leadership and start polling.
262
262
  3. If a live leader exists, restore a remembered profile-scoped exact-`cwd` follower binding automatically; otherwise wait for explicit `/telegram-connect` rather than provisioning a new Thread from startup alone.
263
- 4. If the leader heartbeat is stale, attempt an atomic leadership takeover; ordinary `/telegram-connect` on a follower is not a leadership move while the leader is live.
264
- 5. Heartbeat acknowledgements carry the authenticated live follower-slot roster. If several followers detect stale leadership, the lowest observed live slot attempts promotion immediately; higher slots defer one bounded election grace and re-check the lock. Atomic compare/write acquisition remains the final ownership authority, and a missing lower-slot follower cannot block a higher survivor beyond that grace.
263
+ 4. If the leader PID is dead, or its re-registration fails and the bus proves it unresponsive, attempt an atomic leadership takeover; ordinary `/telegram-connect` on a follower is not a leadership move while the leader is live.
264
+ 5. Heartbeat acknowledgements carry the authenticated live follower-slot roster. If several followers detect stale or unresponsive leadership, the lowest observed live slot attempts promotion immediately; higher slots defer one bounded election grace and re-check the lock. Atomic compare/write acquisition remains the final ownership authority, and a missing lower-slot follower cannot block a higher survivor beyond that grace.
265
265
 
266
266
  Followers first try to re-register after leader reload or unknown-heartbeat responses, carrying their last known target, slot, and thread name so the new leader can reuse the same binding. Follower Bot API calls already admitted by the active Pi turn wait for that bounded re-registration and capture its new exact generation before entering transport; they do not fail merely because recovery temporarily cleared local registration, and they never replay after an ambiguous transport commit. After the grace window followers promote only when the exact observed leader lease has become stale or inactive; an unavailable IPC endpoint never authorizes replacing a still-live owner. If the exact carried target is absent from persisted bindings, the leader first runs the same synchronous visibility probe: success recovers it instead of creating another Telegram thread, explicit stale evidence provisions a replacement, and ambiguous failure rejects registration. An ambiguous absent-target probe persists only non-routable `probe-required` restoration evidence, so targetless retries, successor follower processes with the same exact Workspace claim, and leader reloads probe that exact target again instead of activating it or provisioning a speculative replacement. A carried slot survives only when that slot remains free. Every successful reuse refreshes the binding timestamp. The leader never restores persisted followers into the live registry speculatively. Absent follower records remain durable restart hints until explicit stale, deleted, offline, or reconciliation evidence invalidates them; only fresh authenticated registration creates live routing authority. This preserves real thread bindings through reload and process-absence gaps without allowing historical records or competing pollers to masquerade as live state.
267
267
 
@@ -269,7 +269,7 @@ Followers first try to re-register after leader reload or unknown-heartbeat resp
269
269
 
270
270
  ### Protocol identity and compatibility
271
271
 
272
- The session-native local wire contract has protocol version `2`, independent from the npm package version. Follower registration and the leader acknowledgement carry `{ protocolVersion, runtimeBuild, capabilities }`. Capability names are canonical, unique, and sorted. A leader rejects missing or mismatched protocol identity before provisioning a target or publishing the follower into live routing; a strict follower likewise rejects an acknowledgement without compatible leader identity. Different package builds remain compatible when their protocol versions agree. `durable-follower-admission-v1` gates source forwarding, while `queue-handoff-v1` independently gates live semantic queue transfer; every participant in a routed handoff must advertise it. `workspace-follower-auto-connect-v1` gates restore-only startup admission. Session identity is part of protocol v2 rather than an optional capability. Every cwd-scoped v2 registration carries the exact bounded session ID; missing identity is rejected before provisioning or publication. Reconnect, replacement, and promotion preserve it. Protocol-v1 peers are rejected by the base version check, avoiding mixed semantic branches. `thread-display-mode-v1` gates exact-generation follower display-setting requests and Letters/Directories require compatible connected followers; returning to Names allows legacy peers. Registration checks the current display-mode requirement before provisioning and again before live publication. The leader serializes config persistence and title application. `workspace-thread-rename-v1` independently gates follower rename requests whose exact registration generation is checked before the leader mutates Telegram and persists the Workspace binding. `session-replacement-intent-v1` gates Telegram `/new` from a follower Thread: followers never write `state.json`, so `follower.publishSessionReplacement` asks the leader to CAS-publish the durable intent only when the live registration generation, leader epoch, Profile, exact `cwd`, source session, Thread target, and the leader's own Workspace binding slot/name all match, with a bounded unexpired TTL. The intent records the source runtime instance; only a successor registration from that instance or its authenticated same-process handoff (`previousInstanceId`) may re-key the binding, and `follower.settleSessionReplacement` claims it once only after the leader's store shows that successor session bound to the same target. A missing capability, stale generation, or mismatch fails the command closed.
272
+ The session-native local wire contract has protocol version `3`, independent from the npm package version. Version 3 moved journal source serialization from the `telegram.json` transaction to the journal-owned `runtime/journals.transaction`; because v2 peers would not exclude v3 journal writers, the base version check rejects them instead of mixing lock semantics. Follower registration and the leader acknowledgement carry `{ protocolVersion, runtimeBuild, capabilities }`. Capability names are canonical, unique, and sorted. A leader rejects missing or mismatched protocol identity before provisioning a target or publishing the follower into live routing; a strict follower likewise rejects an acknowledgement without compatible leader identity. Different package builds remain compatible when their protocol versions agree. `durable-follower-admission-v1` gates source forwarding, while `queue-handoff-v1` independently gates live semantic queue transfer; every participant in a routed handoff must advertise it. `workspace-follower-auto-connect-v1` gates restore-only startup admission. Session identity has been part of the base protocol since v2 rather than an optional capability. Every cwd-scoped registration carries the exact bounded session ID; missing identity is rejected before provisioning or publication. Reconnect, replacement, and promotion preserve it. Older-protocol peers are rejected by the base version check, avoiding mixed semantic branches. `thread-display-mode-v1` gates exact-generation follower display-setting requests and Letters/Directories require compatible connected followers; returning to Names allows legacy peers. Registration checks the current display-mode requirement before provisioning and again before live publication. The leader serializes config persistence and title application. `workspace-thread-rename-v1` independently gates follower rename requests whose exact registration generation is checked before the leader mutates Telegram and persists the Workspace binding. `session-replacement-intent-v1` gates Telegram `/new` from a follower Thread: followers never write `state.json`, so `follower.publishSessionReplacement` asks the leader to CAS-publish the durable intent only when the live registration generation, leader epoch, Profile, exact `cwd`, source session, Thread target, and the leader's own Workspace binding slot/name all match, with a bounded unexpired TTL. The intent records the source runtime instance; only a successor registration from that instance or its authenticated same-process handoff (`previousInstanceId`) may re-key the binding, and `follower.settleSessionReplacement` claims it once only after the leader's store shows that successor session bound to the same target. A missing capability, stale generation, or mismatch fails the command closed.
273
273
 
274
274
  The current root advertises `workspace-restore-v1`; all participating peers and potential leader successors must be upgraded before operator-controlled use. The retired `leader.replaceFollowerTarget` envelope is no longer parsed or produced, even with current authentication and registration generation. Its `leader.workspaceRestore` envelope carries only an operation ID, exact recipient registration generation and `apply`/`inspect` mode; the receiver derives binding and destination from the retained intent, not caller-supplied target data. Authentication and an explicitly enabled receiver are mandatory. The controller checks both peers' capability and protocol compatibility, captures live registration/session/slot/endpoint authority, sends once without transport retry, then validates the request, operation, recipient, observed target, slot and boolean readiness against the current registration. A lost response leaves the durable issuance consumed; a separate read-only inspection can prove readiness without applying again. Neither the controller nor receiver settles source input or writes follower-owned canonical state. A successor registration/process may inspect only its actual same-session canonical binding and local target; readiness records the new observer separately without changing the original issuance. An old local target cannot be repaired through inspection, and successor apply is rejected. Interrupted startup, terminal source/cleanup settlement and live behavior were accepted in the operator's 0.52.0 live smoke.
275
275
 
@@ -287,7 +287,7 @@ As part of staging, the exact recipient presents the token and atomically receiv
287
287
 
288
288
  ### Live-Rebind Channel
289
289
 
290
- The 0.53.0 channel preserves protocol v2 and negotiates live rebinding independently from package version. Release 0.53.0 advertises the following capabilities; an instance still running an older build does not acquire them until it is reloaded. Compatible package versions alone cannot supply missing capabilities:
290
+ The 0.53.0 channel preserved protocol v2 and negotiates live rebinding independently from package version; 0.54 moves the base protocol to v3 without changing these capabilities. Release 0.53.0 advertises the following capabilities; an instance still running an older build does not acquire them until it is reloaded. Compatible package versions alone cannot supply missing capabilities:
291
291
 
292
292
  - `live-thread-rebind-save-v1`, `live-thread-rebind-apply-v1`, `live-thread-rebind-settle-v1`: Exact recipient save/apply/release and source settlement through the shared channel.
293
293
  - `live-thread-rebind-command-set-v1`: One `selectedCommand: { name, target }` descriptor and `observe-command` / `command-observed` completion observation, rather than separate status/control wire forms. Retired descriptors and capability names refuse without a compatibility shim.
@@ -513,7 +513,7 @@ Rules:
513
513
 
514
514
  Current state under the agent dir:
515
515
 
516
- - `tmp/pi-telegram/state.json` `profiles.<profile>.transport`: authoritative transport owner for `default` or a named profile, holding the bus leader identity, capability secret, heartbeat, generation, cleanup fencing epoch and polling-journal pointer. Mutations serialize through `runtime/state.json.transaction`; followers never write it. The local bus endpoint is derived from the agent directory; a `busSocketPath` field is tolerated but not required.
516
+ - `tmp/pi-telegram/state.json` `profiles.<profile>.transport`: authoritative transport owner for `default` or a named profile, holding the bus leader identity, capability secret, generation, cleanup fencing epoch and polling-journal pointer. Mutations serialize through `runtime/state.json.transaction`; followers never write it. The local bus endpoint is derived from the agent directory; a `busSocketPath` field is tolerated but not required.
517
517
  - `profiles.<profile>.workspace`: canonical Workspace/thread state; `profiles.<profile>.runtime`: volatile observable/debug projection, never routing authority, with `source: "snapshot"` and `writtenAtMs`. Every process on one profile reads this shared file, but only the active transport owner may publish either section; followers become writers only after promotion. Runtime publication never touches the canonical section, so a stale status writer cannot erase leader records. The `runtime` section mirrors `/telegram-status`-style projections: `runtime` identifies leader/follower role, lifecycle activity, and the exact polling phase/progress snapshot; `liveRoster` mirrors followers/current targets/reservations; `diagnostics` mirrors status/debug signals. The `workspace` section keeps top-level `bot` capability state such as `threadMode: "unknown" | "enabled" | "disabled"`; `threads` stores current routeable bindings; `workspaceBindings` stores dormant exact-`cwd` target/name/slot reuse hints; `bot.lastSlot` stores the compact slot cursor used when all current threads are gone; and `reservations` records short-lived slot collision guards.
518
518
  - Local bus endpoints: Unix-like platforms expose stable `tmp/pi-telegram/runtime/bus.<hash>.sock` (leader) and `runtime/f.<hash>.sock` (follower) symlinks backed by colocated private generation sockets, with bounded private external shortening only for over-long paths; native Windows uses deterministic named pipes under `\\.\pipe\pi-telegram-...`. These are transient IPC endpoints, not durable routing state.
519
519
 
@@ -528,13 +528,13 @@ All files containing routing, chat ids, thread ids, or process details use priva
528
528
  ### Leader exits cleanly
529
529
 
530
530
  - Leader stops polling and marks itself offline.
531
- - Followers detect missing heartbeat.
531
+ - Follower heartbeats go unanswered and the released or dead owner is stale.
532
532
  - One follower promotes itself after jitter/tie-break.
533
533
  - New leader resumes `getUpdates` from the persisted offset if safe.
534
534
 
535
535
  ### Leader crashes
536
536
 
537
- - Followers detect stale heartbeat.
537
+ - Followers see a dead PID, or prove a hung leader unresponsive over the bus.
538
538
  - One follower promotes itself.
539
539
  - Some updates may be delayed or skipped depending on offset persistence; dispatcher design must define this explicitly.
540
540
 
@@ -606,7 +606,7 @@ Leader command Restore now binds `updates.bindTelegramUpdateCompletionAcceptance
606
606
 
607
607
  A source-journal completion hint now also selects a ready leader Restore with retained completed or queued acceptance and unissued cleanup. Fresh admission and strict scoped ACK queries precede executor adoption. A read-only canonical observation requires one exact active binding/session/CWD/slot/target and one current leader owner with matching instance, CWD and profile key; local target equality alone cannot re-key the operation. The existing inspect-only controller preserves the original recipient/grants and observes the same-session current leader without applying identity, invoking a handler or sending follower RPC. After its await, immutable proofs are read again before settling only their proven originals. Live fences remain in storage authority callbacks; canonical observations stay outside the snapshot transaction and are repeated through cleanup awaits and before retirement. An owner change after close retains issued cleanup rather than deleting or retiring. Exact already-retained local readiness needs no republication. Native cold cases cover same-instance/successor continuation, partial/missing/foreign evidence, wrong local session/CWD/target/owner, post-await proof/recipient changes, both canonical publication rename boundaries and unknown/issued cleanup, preserving source bytes throughout. Queued ACK continuation uses the same pre-adoption proof and canonical-owner fences. After recipient inspection it rereads unproven scopes and publishes `queue-completed`, grouping only matching receipts/kinds; admission-only, missing or partial proof cannot release another original. Native single-/multi-receipt cases preserve issued grants and source bytes across canonical publication faults, with or without a prior admission fact. Producer execution, successor startup/ownership and accepted-work clearance are distinct preconditions; these fixtures do not prove actual interrupted Pi startup. Scoped ACKs still never expire, evict or release.
608
608
 
609
- The worker now has a prepared optional `beforeQueueReceiptPublished(receipt, owner, ctx, isCurrent)` barrier. Exact durable queue admission retains the original, but neither cached readiness nor its downstream observer publishes until the asynchronous acceptance publisher succeeds. Publication requires a captured exact receipt-inspection capability before and after the await, with current process/session/context/binding checks. Receipt and owner arguments are detached; grouped reports and concurrent snapshot refresh share one pending publication. Same-process cold receipt reconstruction uses that barrier without running the original handler. Callback failures hold queued work; readiness observers remain diagnostic after successful publication. Ordinary commit failures still release deferred claims synchronously. `waitForDrain` covers in-flight publication. Serialized production journal bindings now compose `inspectQueuedReceipt(expected)` with their existing strict private sibling and derive `isQueueReceiptCurrent` from the exact active recovery key. The read-only v1 observer requires every canonical ordered source ID, receipt/kind, full owner/acquisition and absence of a handoff offer. It returns detached source digests of the retained **queued** originals plus normalized-owner SHA-256; those digests are not pre-queue pending hashes or removal ACKs. Query mismatch yields no proof; malformed, corrupt, foreign or unprepared evidence throws without repair, rebinding or source mutation. Inspection uses lock-only source/config serialization, not writer admission, so observing exact accepted work does not borrow persistence permission. Native fixtures cover whole groups, owner/acquisition mismatch, offers, retained byte continuity, cold/detached proof, post-await completion/corruption and read-only authority. The production worker now wires routing's `beforeQueueReceiptPublished` publisher. It selects only retained operations overlapping that exact active-journal receipt, then reacquires fresh profile Workspace admission; ordinary receipts remain a no-op for Restore policy. A selected operation must be ready with its issued routing, exact current executor/operator and leader recipient. Canonical read-only binding/owner checks and live session/CWD/slot/target establish recipient authority before each `queued` acceptance. Exact receipt/source and owner-hash evidence is retained in the existing operation before readiness. Storage's authority predicate checks only live fences to avoid reacquiring its held snapshot transaction; canonical CAS plus pre/post snapshot reads preserve binding protection. Lost publication reply reconciles only an identical retained request/executor/operator/proof. After publication the publisher re-reads the whole receipt, and after admission returns it rechecks captured recipient guards; changed proof or authority suppresses readiness without rolling back accepted local queue work. No apply, prompt enqueue, deletion or cleanup is repeated by this publisher. Same-process worker reconstruction can republish only acceptance from its still-exact receipt; it is not actual interrupted Pi startup or a foreign-session successor grant.
609
+ The worker now has a prepared optional `beforeQueueReceiptPublished(receipt, owner, ctx, isCurrent)` barrier. Exact durable queue admission retains the original, but neither cached readiness nor its downstream observer publishes until the asynchronous acceptance publisher succeeds. Publication requires a captured exact receipt-inspection capability before and after the await, with current process/session/context/binding checks. Receipt and owner arguments are detached; grouped reports and concurrent snapshot refresh share one pending publication. Same-process cold receipt reconstruction uses that barrier without running the original handler. Callback failures hold queued work; readiness observers remain diagnostic after successful publication. Ordinary commit failures still release deferred claims synchronously. `waitForDrain` covers in-flight publication. Serialized production journal bindings now compose `inspectQueuedReceipt(expected)` with their existing strict private sibling and derive `isQueueReceiptCurrent` from the exact active recovery key. The read-only v1 observer requires every canonical ordered source ID, receipt/kind, full owner/acquisition and absence of a handoff offer. It returns detached source digests of the retained **queued** originals plus normalized-owner SHA-256; those digests are not pre-queue pending hashes or removal ACKs. Query mismatch yields no proof; malformed, corrupt, foreign or unprepared evidence throws without repair, rebinding or source mutation. Inspection uses lock-only journal source serialization, not writer admission, so observing exact accepted work does not borrow persistence permission. Native fixtures cover whole groups, owner/acquisition mismatch, offers, retained byte continuity, cold/detached proof, post-await completion/corruption and read-only authority. The production worker now wires routing's `beforeQueueReceiptPublished` publisher. It selects only retained operations overlapping that exact active-journal receipt, then reacquires fresh profile Workspace admission; ordinary receipts remain a no-op for Restore policy. A selected operation must be ready with its issued routing, exact current executor/operator and leader recipient. Canonical read-only binding/owner checks and live session/CWD/slot/target establish recipient authority before each `queued` acceptance. Exact receipt/source and owner-hash evidence is retained in the existing operation before readiness. Storage's authority predicate checks only live fences to avoid reacquiring its held snapshot transaction; canonical CAS plus pre/post snapshot reads preserve binding protection. Lost publication reply reconciles only an identical retained request/executor/operator/proof. After publication the publisher re-reads the whole receipt, and after admission returns it rechecks captured recipient guards; changed proof or authority suppresses readiness without rolling back accepted local queue work. No apply, prompt enqueue, deletion or cleanup is repeated by this publisher. Same-process worker reconstruction can republish only acceptance from its still-exact receipt; it is not actual interrupted Pi startup or a foreign-session successor grant.
610
610
 
611
611
  Native full-capacity leader Restore cases exercise positive queued publication, both rename sides, a forged returned intent with no proof, missing/unreadable receipt or a changed source digest on proof reread, authority loss, recipient change before publication/after the proof reread/after admission, and same-process worker reconstruction. Each original stays queued under its durable owner, exactly one prompt is accepted, and uncertain acceptance never publishes readiness or triggers cleanup. A native two-original media-group producer now exercises the same contract through its real unbound-group debounce, shared chooser/Restore selection, journal and worker. At full A–Z capacity, it relocates the same binding/slot and enqueues one combined prompt with one full receipt. A failed or unproven second acceptance retains the first proof but holds the entire receipt; lost post-rename reply reconciles both. A changed second digest on reread or an actual native whole-group handoff offer withholds readiness without undoing accepted work. Same-process worker reconstruction completes partial proof, preserves the first acceptance byte-for-byte, and emits one readiness observation without journal mutation or another enqueue/apply. Duplicate acceptance publication leaves canonical bytes unchanged. These are grouped prompt fixtures, not all remaining control/local outcomes or actual Pi startup acceptance.
612
612
 
@@ -643,7 +643,7 @@ This inspection authorizes no reload/restart, live-state mutation, fault injecti
643
643
  ### Split brain
644
644
 
645
645
  - Two leaders calling `getUpdates` is the main safety failure.
646
- - Lock heartbeat/takeover must be atomic enough to prevent this under normal local concurrency.
646
+ - Lock takeover must be atomic enough to prevent this under normal local concurrency.
647
647
  - Persistent competing `getUpdates` clients trigger a full transport stand-down even when the local lock still appears owned; the stopped runtime preserves accepted local work and releases only its exact ownership. See [Runtime Ownership](./architecture.md#runtime-ownership) for the threshold, diagnostic, and reconnect contract.
648
648
 
649
649
  ## Security Boundaries
@@ -657,7 +657,7 @@ This inspection authorizes no reload/restart, live-state mutation, fault injecti
657
657
 
658
658
  ## Acceptance Criteria
659
659
 
660
- - [x] The lock semantics are redesigned as Telegram bus leadership with heartbeat and stale takeover rules.
660
+ - [x] The lock semantics are redesigned as Telegram bus leadership with liveness proof and stale takeover rules.
661
661
  - [x] A first-class `TelegramTarget` can represent classic private chats and thread destinations.
662
662
  - [x] The bridge can run in classic mode with unchanged private-chat behavior.
663
663
  - [x] A live Pi instance can register as a follower when another live instance is leader.
@@ -676,7 +676,7 @@ Open live client and native Windows evidence gates for future behavior belong in
676
676
  - Bus semantics are the feature frame: Telegram threads are one Telegram UI substrate for a local multi-instance bus.
677
677
  - `TelegramTarget` and target-key helpers represent classic private chats and thread destinations.
678
678
  - Outbound ports, previews, replies, voice, attachments, chat actions, menus, sections, buttons, queue mutations, and direct local delivery carry target metadata where needed.
679
- - The transport lock distinguishes live bus leadership from ordinary classic ownership through heartbeat, leader epoch, and stale takeover rules.
679
+ - The transport lock distinguishes live bus leadership from ordinary classic ownership through leader epoch, liveness proof, and stale takeover rules.
680
680
  - The leader records live follower registration, heartbeat, thread identity, slot, and target mapping; followers do not poll `getUpdates`.
681
681
  - Local IPC is the default internal bus. Registered followers receive normalized inbound updates and send allowlisted, target-scoped Bot API calls through the leader.
682
682
  - Thread targets are current-state bindings, not historical delivery addresses. Stable restart hints require a fresh authenticated follower registration before they become live routing authority; stale/offline/failed entries remain reconciliation evidence only.
@@ -385,6 +385,8 @@ export function createTelegramManualFollowerProfileKeyResolver(input: {
385
385
 
386
386
  export interface TelegramBusFollowerElection {
387
387
  expectedOwner?: TelegramLockEntry;
388
+ /** The expected owner is bus-proven unresponsive; the lock CAS still requires it to be the current owner. */
389
+ unresponsive?: boolean;
388
390
  }
389
391
 
390
392
  export type TelegramBusFollowerPromotionHandler<TContext> = (
@@ -568,6 +570,8 @@ export interface TelegramBusFollowerHeartbeatRecoveryHandlerDeps<TContext> {
568
570
  scheduleRetry?: (retry: () => void, delayMs: number) => void;
569
571
  getActiveContext?: () => TContext | undefined;
570
572
  promotionGraceMs?: number;
573
+ /** Bus liveness proof for a live-PID leader that no longer answers; replaces the retired file heartbeat. */
574
+ proveLeaderUnresponsive?: (owner: TelegramLockEntry) => Promise<boolean>;
571
575
  recordRuntimeEvent: (
572
576
  category: string,
573
577
  error: unknown,
@@ -2410,6 +2414,13 @@ export function createTelegramBusFollowerHeartbeatRecoveryHandler<TContext>(
2410
2414
  ) {
2411
2415
  return;
2412
2416
  }
2417
+ if (await deps.proveLeaderUnresponsive?.(graceState.lock)) {
2418
+ await promoteToLeader(error, ctx, initialBinding, {
2419
+ expectedOwner: graceState.lock,
2420
+ unresponsive: true,
2421
+ });
2422
+ return;
2423
+ }
2413
2424
  deps.setLifecyclePhase(undefined);
2414
2425
  safeUpdateStatus(ctx);
2415
2426
  deps.recordRuntimeEvent(
package/lib/bus-leader.ts CHANGED
@@ -3371,6 +3371,14 @@ export function createTelegramBusLeaderEnvelopeHandler(
3371
3371
  return createUnauthorizedBusAck(envelope.requestId);
3372
3372
  }
3373
3373
  switch (envelope.kind) {
3374
+ case "bus.probe":
3375
+ // Liveness only: no registry, routing, journal or Bot API effect.
3376
+ return {
3377
+ kind: "bus.ack" as const,
3378
+ requestId: envelope.requestId,
3379
+ ok: true,
3380
+ protocol: deps.protocolIdentity,
3381
+ };
3374
3382
  case "follower.register":
3375
3383
  case "follower.restoreWorkspace": {
3376
3384
  const compatibility = getTelegramBusProtocolCompatibility({