Context
Operator, 2026-07-21: "maybe our biggest friction item: getting wake messages delivered into your TUI (not a serverside sibling session)."
The live cost is visible every session: peers suppress wakes to the Iris seat and the human relays content by hand (twice more on 2026-07-21 alone — Euclid's intakes on #15666/#15667 and on this ticket both state "direct Kimi wake suppressed due split-brain risk; operator may relay").
Amended 2026-07-22T00:2xZ per Euclid's intake (needs-narrowing + needs-contract-alignment, comment on this ticket): the kitty terminal-injection prescription is withdrawn — source-proven unsafe. Replaced by the research-neutral owner-ingress comparison + Contract Ledger below. No implementation branch opens from the prior body.
Lineage (corrective successor)
#15579 / PR #15588 shipped the kimi-server wake adapter; its receipt proved REST delivery to a session ID — not that the human-driven TUI owned that session.
#15596 / #15597 shipped v0.28 coordinate discovery under the since-falsified "live TUI adoption works" assumption.
#13796 owns shared stop-hook semantics — a different layer.
The Problem (split-brain, source-proven at 1d7fa14474)
Neo treats HTTP 200 + code === 0 from the loopback web server as delivered (ai/daemons/wake/daemon.mjs:1301-1323, 1759-1761) — that proves acceptance by the web process, not ownership by the running TUI. The pinned Kimi source (ec88d352…) supplies the process boundary:
- The Node SDK constructs a new
KimiCore per caller process.
- The REST prompt route calls
resume(sessionId) — a persisted-but-cold session is materialized in that process (session liveness is a process-local Map).
- The prompt enqueues into that process-local scope.
So the same persisted session can be live in two processes, and a prompt appearing in shared history is not evidence the TUI owned the turn. AC2 must require execution-owner proof, not session-history visibility.
Additionally verified on the Iris seat: WAKE_SUB:ad94a336-… pins a lockPath whose instance file no longer exists (server/instances/ empty), so today's route hard-errors; an interactive TUI registers no loopback instance (only kimi web does); the wake envelope correctly carries the live TUI's sessionId but no receiving surface exists for it.
The Architectural Reality
- Cross-harness parity exists elsewhere (opencode-server REST, codex-app-server, Claude instance-addressable routing). Kimi TUI is the only seat class without an inbound path.
- Why raw kitty injection is rejected (Euclid's evidence): the Kimi window title carries only session title/product name — no idle/active-turn/approval/typing/epoch state; and when an approval panel owns focus, Enter submits the selected decision. A
send-text digest + Enter can mutate an approval or collide with unsent operator input, and yields no causal TUI-turn receipt. This conflicts with ADR 0002 §5.2 (unknown presence → next_turn; no injection over userTyping; bridge fallback must degrade unless it can prove idle or append-only safety).
- A same-process seam exists upstream (cron fire →
agent.turn.steer(...); KIMI_CRON_MANUAL_TICK=1 + SIGUSR1 manual tick), but upstream labels it a bench affordance, warns a process may host main agent plus subagents, and manual mode disables automatic cron polling. Research candidate only, with a mandatory negative matrix.
ADR successor-risk: adr-amendment-required if raw title-addressed terminal input remained the route (contradicts ADR 0002 §5.2). adr-aligned under the owner-native ingress route chosen below.
The Fix (research-neutral owner-ingress comparison — ordered)
- Upstream-supported TUI ingress / owner registration via the in-process turn API (the durable route; if it does not exist, the deliverable is the upstream proposal, not a Neo workaround).
- Constrained manual-tick/cron proof to learn whether the owner-native seam is usable without scheduler or subagent regressions — bounded prototype, falsifying: listener fan-out, active-turn buffering, session switch, PID reuse, cron expiry, existing-cron regressions.
- Kitty only with a separate state-and-ack bridge that proves idle/append-only delivery — the current title +
send-text proposal cannot, so kitty is not the route unless that bridge materializes.
First executable artifact: the bounded owner-native ingress probe + its negative matrix. No terminal-injection code.
Contract Ledger Matrix
| Contract |
Source of Authority |
Required Behavior |
Fallback / Evidence |
| Execution owner |
Kimi process-local session lifecycle |
target {agentIdentity, sessionId, process epoch}, never sessionId alone |
stale/reused PID and second-process probes fail closed |
| Presence / input safety |
ADR 0002 §5.2 |
no terminal mutation during active turn, approval dialog, or unsent operator input |
next_turn / stored unread |
| Delivery acknowledgement |
wake event + owning TUI turn |
correlate wake nonce/message IDs to a turn started/steered by that exact process epoch |
HTTP/OS acceptance alone is NOT delivered |
| Subscription migration |
current kimi-server coordinate |
atomically clear the obsolete web lockPath when switching adapter shape |
rollback restores stored-unread, never the twin |
| Security |
transport capability |
least privilege; no globally open kitty remote control |
negative authorization test + documented revocation |
Acceptance Criteria
Out of Scope
kimi web sibling-session routing improvements (the sibling IS the anti-goal).
- Presence/turn-presence wiring (#15658, merged).
- Wake policy (
#15405); #13796's generic adapter surface (different layer).
Avoided Traps / Gold Standards Rejected
- Raw title-addressed terminal injection (the original prescription — rejected per Euclid's intake): no presence/approval state in the title; Enter submits focused approval decisions; no causal receipt; contradicts ADR 0002 §5.2.
- Pointing the subscription at a
kimi web sibling and calling it delivered — that IS the split-brain.
- Suppressing every wake to the seat as the "fix" — suppresses the wake contract itself.
- Session-history visibility as delivery proof — the twin writes the same store; owner proof requires the process epoch.
Related
- Lineage:
#15579 / PR #15588, #15596 / #15597 (corrective successor); #13796 (different layer); ADR 0002 §5.2 (presence/no-clobber contract)
- #15586 (umbrella),
#11829 (broader wake-driver lane), #15405, #11822 lineage (Claude sibling routing)
- #15660 (boot surfaces — the wake envelope is a SessionStart artifact)
- Origin Session ID: eb9be68e-9401-4ecd-9762-ef519b4091ed
Sweeps: live open-issue search (kimi wake, wake delivery, TUI, split-brain) at 2026-07-21T21:50Z — no exact duplicate. KB semantic clean. Operator recollection of a Euclid-authored ticket VBA-falsified; Euclid confirmed corrective-successor status + this narrowing (2026-07-21/22).
Retrieval Hint: "kimi TUI wake split-brain execution owner process epoch owner-native ingress"; anchors ai/daemons/wake/daemon.mjs:1301-1323, Kimi source ec88d352 (prompts.ts resume, sessionLifecycleService process-local Map).
Context
Operator, 2026-07-21: "maybe our biggest friction item: getting wake messages delivered into your TUI (not a serverside sibling session)."
The live cost is visible every session: peers suppress wakes to the Iris seat and the human relays content by hand (twice more on 2026-07-21 alone — Euclid's intakes on
#15666/#15667and on this ticket both state "direct Kimi wake suppressed due split-brain risk; operator may relay").Amended 2026-07-22T00:2xZ per Euclid's intake (needs-narrowing + needs-contract-alignment, comment on this ticket): the kitty terminal-injection prescription is withdrawn — source-proven unsafe. Replaced by the research-neutral owner-ingress comparison + Contract Ledger below. No implementation branch opens from the prior body.
Lineage (corrective successor)
#15579/ PR#15588shipped the kimi-server wake adapter; its receipt proved REST delivery to a session ID — not that the human-driven TUI owned that session.#15596/#15597shipped v0.28 coordinate discovery under the since-falsified "live TUI adoption works" assumption.#13796owns shared stop-hook semantics — a different layer.The Problem (split-brain, source-proven at
1d7fa14474)Neo treats HTTP 200 +
code === 0from the loopback web server asdelivered(ai/daemons/wake/daemon.mjs:1301-1323, 1759-1761) — that proves acceptance by the web process, not ownership by the running TUI. The pinned Kimi source (ec88d352…) supplies the process boundary:KimiCoreper caller process.resume(sessionId)— a persisted-but-cold session is materialized in that process (session liveness is a process-localMap).So the same persisted session can be live in two processes, and a prompt appearing in shared history is not evidence the TUI owned the turn. AC2 must require execution-owner proof, not session-history visibility.
Additionally verified on the Iris seat:
WAKE_SUB:ad94a336-…pins alockPathwhose instance file no longer exists (server/instances/empty), so today's route hard-errors; an interactive TUI registers no loopback instance (onlykimi webdoes); the wake envelope correctly carries the live TUI'ssessionIdbut no receiving surface exists for it.The Architectural Reality
send-textdigest + Enter can mutate an approval or collide with unsent operator input, and yields no causal TUI-turn receipt. This conflicts with ADR 0002 §5.2 (unknown presence →next_turn; no injection overuserTyping; bridge fallback must degrade unless it can prove idle or append-only safety).agent.turn.steer(...);KIMI_CRON_MANUAL_TICK=1+ SIGUSR1 manual tick), but upstream labels it a bench affordance, warns a process may host main agent plus subagents, and manual mode disables automatic cron polling. Research candidate only, with a mandatory negative matrix.ADR successor-risk:
adr-amendment-requiredif raw title-addressed terminal input remained the route (contradicts ADR 0002 §5.2).adr-alignedunder the owner-native ingress route chosen below.The Fix (research-neutral owner-ingress comparison — ordered)
send-textproposal cannot, so kitty is not the route unless that bridge materializes.First executable artifact: the bounded owner-native ingress probe + its negative matrix. No terminal-injection code.
Contract Ledger Matrix
{agentIdentity, sessionId, process epoch}, neversessionIdalonenext_turn/ stored unreaddeliveredkimi-servercoordinatelockPathwhen switching adapter shapeAcceptance Criteria
{agentIdentity, sessionId, process epoch}and a causal turn receipt (turn started/steered by that epoch).[L3-deferred — operator handoff needed]— requires the merged daemon + a standing seat poll; unreachable from any unmerged PR head (see PR #15712 PMV).[L3-deferred — operator handoff needed]for the live spot matrix (approval/draft/active-turn are harness-idle-gated by construction; stale-epoch, confinement, torn-line, and append-mid-consume are spec-covered on the PR branch); the full live sweep is post-merge.lockPathon the seat's subscription is retired/cleared under a non-web route (not "repaired" into another server coordinate). (Seat-side retirement done 2026-07-22 — stale subscription unsubscribed, not re-pointed; PR #15712 makes retirement atomic inWakeSubscriptionServiceon pull-bridge selection.)Out of Scope
kimi websibling-session routing improvements (the sibling IS the anti-goal).#15405);#13796's generic adapter surface (different layer).Avoided Traps / Gold Standards Rejected
kimi websibling and calling it delivered — that IS the split-brain.Related
#15579/ PR#15588,#15596/#15597(corrective successor);#13796(different layer); ADR 0002 §5.2 (presence/no-clobber contract)#11829(broader wake-driver lane),#15405,#11822lineage (Claude sibling routing)Sweeps: live open-issue search (
kimi wake,wake delivery,TUI,split-brain) at 2026-07-21T21:50Z — no exact duplicate. KB semantic clean. Operator recollection of a Euclid-authored ticket VBA-falsified; Euclid confirmed corrective-successor status + this narrowing (2026-07-21/22).Retrieval Hint: "kimi TUI wake split-brain execution owner process epoch owner-native ingress"; anchors
ai/daemons/wake/daemon.mjs:1301-1323, Kimi sourceec88d352(prompts.ts resume, sessionLifecycleService process-local Map).