Context
Operator directive this morning (2026-07-20): verify that A2A wake messages actually reach the kimi-code harness seat (@neo-kimi-iris). The seat's wake subscription was registered this morning (WAKE_SUB:ad94a336-7e4a-4534-9efb-331f413bb276, trigger SENT_TO_ME, adapter kimi-server), completing the day-1 handover pickup item. The fire/no-fire proof (#12913 shape) then isolated a hard breakage: the kimi CLI auto-updated v0.27.0 → v0.28.0 between sessions, and v0.28 deprecates kimi server ("no longer works — use kimi web instead") — and kimi web no longer writes ~/.kimi-code/server/lock. The adapter merged one day earlier (PR #15588) reads exactly that file.
The Problem
Verified evidence chain, all at current dev head on the Iris seat:
- Fire path works up to the delivery seam. Subscription registered 10:42Z → wake daemon detected the wake-worthy edge → correctly selected the
kimi-server route → delivery attempt failed closed: kimi-server requires a readable server lock at '/Users/tobiasuhlig/.kimi-code/server/lock' (ENOENT) → 5 attempts → wake dropped (.neo-ai-data/wake-daemon/wake-daemon.log 10:50:07Z / 10:50:19Z, WAKE_SUB:ad94a336).
- No-fire path verified: a
wakeSuppressed: true message produced zero daemon activity for the subscription.
- v0.28 contract change, verified empirically:
kimi server prints a deprecation notice and exits non-zero; kimi web --no-open (v0.28.0) listens on loopback but writes no server/lock. Instead it writes ~/.kimi-code/server/instances/{server_id}.json shaped {server_id, pid, host, port, started_at, heartbeat_at, host_version} (heartbeat refreshes while the server lives).
- What survived the version bump: the bearer token (
~/.kimi-code/server.token — byte-identical across the bump), the REST route (POST /api/v1/sessions/{session_id}/prompts present in v0.28's /openapi.json), and the wake-envelope contract (~/.kimi-code/wake-envelope.json, written by the seat's SessionStart hook).
- Delivery seam itself proven intact: a direct loopback POST to
/api/v1/sessions/{live-TUI-session-id}/prompts injected a user message into the running TUI session mid-turn (delivery preempts the in-flight turn — steer-class semantics). So TUI sessions ARE wake-addressable; only lock discovery is broken.
Net: the daemon↔harness pipeline is correct end-to-end except one contract — server-coordinate discovery — which drifted in a same-day harness release.
The Architectural Reality
ai/daemons/wake/daemon.mjs — deliverViaKimiServer resolves coordinates at L1146-1150: lockPath = harnessTargetMetadata.lockPath override || ~/.kimi-code/server/lock; then reads + validates the lock (loopback host, port range) at L1177-1204, posts to the prompts route, and expects HTTP 200 + code: 0. The JSDoc contract block at L1125-1130 documents the v0.27 lock/token files as THE contract. The adapter deliberately fails closed (no fallback to osascript/tmux) so a misconfigured route surfaces loudly rather than delivering into the wrong surface.
- Unit contracts live in
test/playwright/unit/ai/daemons/wake/daemon.spec.mjs (the 50-contract suite from PR #15588).
- Peer adapters with the same discovery shape (
opencode-server, codex-app-server) resolve coordinates from their own envelope/lock files — this ticket does not audit them (see Out of Scope).
- Related discussion substrate: D#15595's divergence window surfaced this class as OQ11 candidate — seat-local edge contracts across harness releases (comment DC_kwDODSospM4BDhUV). This ticket is the narrow operational fix, independent of that ideation track.
The Fix
Refactor lock discovery inside deliverViaKimiServer (plus a small helper) into a precedence chain:
- Explicit override wins —
harnessTargetMetadata.lockPath (existing test seam; unchanged).
- Legacy v0.27 path —
~/.kimi-code/server/lock, honored when present (back-compat for pinned seats).
- v0.28 instances scan — read
~/.kimi-code/server/instances/*.json, select the entry with the freshest heartbeat_at, and validate: pid liveness (process.kill(pid, 0)), heartbeat freshness (≤ ~60s), loopback host, integer port in range, non-empty server_id/host_version. Two or more live instances → fail closed with a descriptive ambiguity error (consistent with the adapter's existing fail-closed discipline; no wrong-instance routing). Zero live entries → fail closed with an actionable "no running kimi server found" error naming both contract shapes.
Update the JSDoc contract block (~L1125-1130) to document the two-shape discovery contract, and extend daemon.spec.mjs with focused contracts: override precedence, legacy path, instances path, freshest-heartbeat selection, ambiguity fail-closed, stale heartbeat fail-closed.
Contract Ledger Matrix
| Target Surface |
Source of Authority |
Proposed Behavior |
Fallback |
Docs |
Evidence |
harnessTargetMetadata.lockPath |
daemon.mjs:1148 (current code) |
Unchanged; override wins |
— |
adapter JSDoc + openapi.yaml |
existing code + #15588 contracts |
~/.kimi-code/server/lock |
kimi CLI v0.27.0 observed behavior |
Honored when present (back-compat) |
instances scan when absent |
adapter JSDoc |
yesterday's live probe (server ran 2026-07-19 22:03Z) |
~/.kimi-code/server/instances/{server_id}.json |
kimi CLI v0.28.0 observed behavior (file shape cat'd) |
Primary discovery when legacy absent |
fail closed (ambiguous/stale/none) |
adapter JSDoc |
this morning's kimi web run + file contents |
~/.kimi-code/server.token |
v0.27 + v0.28 observed (byte-identical value) |
Unchanged |
— |
adapter JSDoc |
token compare this morning |
POST /api/v1/sessions/{id}/prompts |
v0.28 /openapi.json |
Unchanged |
— |
adapter JSDoc |
live in-session injection proof this morning |
~/.kimi-code/wake-envelope.json |
SessionStart hook + daemon reads |
Unchanged |
— |
hook + adapter JSDoc |
envelope written 08:54:01Z today |
Decision Record impact
none — no ADR governs wake-adapter coordinate discovery. ADR 0014 classifies the wake-delivery lane as local-only but says nothing about harness coordinate mechanics.
Acceptance Criteria
Out of Scope
- Seat-launch-contract policy (whether seats run
kimi web resident, OS-service install, version pinning) — operator/harness-policy decision, surfaced in D#15595 OQ11.
opencode-server / codex-app-server adapter contract audits (separate tickets if their vendors drift).
- Wake-delivery preemption semantics (steer-class interruption of in-flight turns) — observed and noted, not changed here.
Avoided Traps
- Per-seat
lockPath override pointing at a hand-maintained symlink — recreates per-seat folk state (the exact class D#15595 divergence #8 flags); the discovery must derive from the harness's own files.
- Pinning the kimi CLI version — fights the harness's default auto-update; unenforceable across seats; treats a contract-drift class as a one-off.
- Treating ENOENT as transient retry — it is a contract change; retries just burn 5 attempts and drop the wake.
Related
- #15579 (kimi-server wake adapter ticket) · PR #15588 (merged adapter, v0.27 contract)
- #15580 · PR #15587 (turn-presence adapter, @neo-gpt — open)
- D#15595 comment DC_kwDODSospM4BDhUV (OQ11 candidate; context only — this ticket is NOT graduated from that Discussion)
#12913 (fire/no-fire proof shape)
Origin harness session: session_e86fa9f0-866e-45e8-a6df-d7bb6dd4d8b5 (kimi-code v0.28.0, Iris day-2 seat session; day-1 MC session: 958d6302-181d-40ae-beda-4c3790d3220d)
Retrieval Hint: query_raw_memories: "kimi-server wake adapter v0.28 server instances lock discovery" · anchor files ai/daemons/wake/daemon.mjs, test/playwright/unit/ai/daemons/wake/daemon.spec.mjs
Live latest-open sweep: checked latest 20 open issues at 2026-07-20T11:02Z (106 open total); no equivalent found. A2A in-flight sweep: list_messages all/30 at 11:03Z; no competing claim on this scope. KB semantic sweep (type=ticket) + local resources/content/** grep: no equivalent.
Context
Operator directive this morning (2026-07-20): verify that A2A wake messages actually reach the kimi-code harness seat (
@neo-kimi-iris). The seat's wake subscription was registered this morning (WAKE_SUB:ad94a336-7e4a-4534-9efb-331f413bb276, triggerSENT_TO_ME, adapterkimi-server), completing the day-1 handover pickup item. The fire/no-fire proof (#12913shape) then isolated a hard breakage: the kimi CLI auto-updated v0.27.0 → v0.28.0 between sessions, and v0.28 deprecateskimi server("no longer works — usekimi webinstead") — andkimi webno longer writes~/.kimi-code/server/lock. The adapter merged one day earlier (PR #15588) reads exactly that file.The Problem
Verified evidence chain, all at current
devhead on the Iris seat:kimi-serverroute → delivery attempt failed closed:kimi-server requires a readable server lock at '/Users/tobiasuhlig/.kimi-code/server/lock' (ENOENT)→ 5 attempts → wake dropped (.neo-ai-data/wake-daemon/wake-daemon.log10:50:07Z / 10:50:19Z,WAKE_SUB:ad94a336).wakeSuppressed: truemessage produced zero daemon activity for the subscription.kimi serverprints a deprecation notice and exits non-zero;kimi web --no-open(v0.28.0) listens on loopback but writes noserver/lock. Instead it writes~/.kimi-code/server/instances/{server_id}.jsonshaped{server_id, pid, host, port, started_at, heartbeat_at, host_version}(heartbeat refreshes while the server lives).~/.kimi-code/server.token— byte-identical across the bump), the REST route (POST /api/v1/sessions/{session_id}/promptspresent in v0.28's/openapi.json), and the wake-envelope contract (~/.kimi-code/wake-envelope.json, written by the seat's SessionStart hook)./api/v1/sessions/{live-TUI-session-id}/promptsinjected a user message into the running TUI session mid-turn (delivery preempts the in-flight turn — steer-class semantics). So TUI sessions ARE wake-addressable; only lock discovery is broken.Net: the daemon↔harness pipeline is correct end-to-end except one contract — server-coordinate discovery — which drifted in a same-day harness release.
The Architectural Reality
ai/daemons/wake/daemon.mjs—deliverViaKimiServerresolves coordinates at L1146-1150:lockPath=harnessTargetMetadata.lockPathoverride ||~/.kimi-code/server/lock; then reads + validates the lock (loopback host, port range) at L1177-1204, posts to the prompts route, and expects HTTP 200 +code: 0. The JSDoc contract block at L1125-1130 documents the v0.27 lock/token files as THE contract. The adapter deliberately fails closed (no fallback to osascript/tmux) so a misconfigured route surfaces loudly rather than delivering into the wrong surface.test/playwright/unit/ai/daemons/wake/daemon.spec.mjs(the 50-contract suite from PR #15588).opencode-server,codex-app-server) resolve coordinates from their own envelope/lock files — this ticket does not audit them (see Out of Scope).The Fix
Refactor lock discovery inside
deliverViaKimiServer(plus a small helper) into a precedence chain:harnessTargetMetadata.lockPath(existing test seam; unchanged).~/.kimi-code/server/lock, honored when present (back-compat for pinned seats).~/.kimi-code/server/instances/*.json, select the entry with the freshestheartbeat_at, and validate: pid liveness (process.kill(pid, 0)), heartbeat freshness (≤ ~60s), loopback host, integer port in range, non-emptyserver_id/host_version. Two or more live instances → fail closed with a descriptive ambiguity error (consistent with the adapter's existing fail-closed discipline; no wrong-instance routing). Zero live entries → fail closed with an actionable "no running kimi server found" error naming both contract shapes.Update the JSDoc contract block (~L1125-1130) to document the two-shape discovery contract, and extend
daemon.spec.mjswith focused contracts: override precedence, legacy path, instances path, freshest-heartbeat selection, ambiguity fail-closed, stale heartbeat fail-closed.Contract Ledger Matrix
harnessTargetMetadata.lockPathdaemon.mjs:1148(current code)openapi.yaml~/.kimi-code/server/lock~/.kimi-code/server/instances/{server_id}.jsonkimi webrun + file contents~/.kimi-code/server.tokenPOST /api/v1/sessions/{id}/prompts/openapi.json~/.kimi-code/wake-envelope.jsonDecision Record impact
none— no ADR governs wake-adapter coordinate discovery. ADR 0014 classifies the wake-delivery lane as local-only but says nothing about harness coordinate mechanics.Acceptance Criteria
lockPathoverride → legacyserver/lock→server/instances/*.jsonscanheartbeat_at; validates pid liveness, heartbeat freshness, loopback host, port rangetest/playwright/unit/ai/daemons/wake/daemon.spec.mjsextended for all new paths; existing 50 contracts still greenOut of Scope
kimi webresident, OS-service install, version pinning) — operator/harness-policy decision, surfaced in D#15595 OQ11.opencode-server/codex-app-serveradapter contract audits (separate tickets if their vendors drift).Avoided Traps
lockPathoverride pointing at a hand-maintained symlink — recreates per-seat folk state (the exact class D#15595 divergence #8 flags); the discovery must derive from the harness's own files.Related
#12913(fire/no-fire proof shape)Origin harness session:
session_e86fa9f0-866e-45e8-a6df-d7bb6dd4d8b5(kimi-code v0.28.0, Iris day-2 seat session; day-1 MC session:958d6302-181d-40ae-beda-4c3790d3220d)Retrieval Hint:
query_raw_memories: "kimi-server wake adapter v0.28 server instances lock discovery"· anchor filesai/daemons/wake/daemon.mjs,test/playwright/unit/ai/daemons/wake/daemon.spec.mjsLive latest-open sweep: checked latest 20 open issues at 2026-07-20T11:02Z (106 open total); no equivalent found. A2A in-flight sweep:
list_messagesall/30 at 11:03Z; no competing claim on this scope. KB semantic sweep (type=ticket) + localresources/content/**grep: no equivalent.