Context
Filed from a live wake-verification session on the Iris seat (operator goal: "verify wake messages to the kimi-code harness"). PR #15588 (merged 2026-07-19, v13.2 dev) added the kimi-server wake-delivery adapter against Kimi Code v0.27.0, whose kimi server subcommand wrote ~/.kimi-code/server/lock ({pid, host, port}) + persistent ~/.kimi-code/server.token. Overnight the seat CLI auto-updated to v0.28.0, which deprecates kimi server entirely ("kimi server has been deprecated and no longer works. Use kimi web instead") — and kimi web never writes server/lock. The adapter's coordinate contract broke within ~12h of merging.
The Problem
Empirical reproduction (2026-07-20, wake-daemon PID 6812):
- Subscription registered
WAKE_SUB:ad94a336-… (adapter kimi-server), live SENT_TO_ME message sent.
- Daemon matched the message, selected the kimi-server route, attempted delivery, and failed closed:
Failed to deliver via kimi-server: kimi-server requires a readable server lock at '/Users/tobiasuhlig/.kimi-code/server/lock' (ENOENT) — 5 attempts, wake dropped.
kimi server status / kimi server run now hard-error with the deprecation notice; the lock file is gone by design in v0.28.
kimi web --no-open (v0.28.0) does run a resident loopback server (127.0.0.1:58627), and it writes a new coordinate artifact: ~/.kimi-code/server/instances/{server_id}.json → {"server_id":"01KXZJ…","pid":94541,"host":"127.0.0.1","port":58627,"started_at":…,"heartbeat_at":…,"host_version":"0.28.0"}. The bearer token is unchanged (still ~/.kimi-code/server.token, persistent), and the REST route the adapter posts to (POST /api/v1/sessions/{session_id}/prompts) still exists in /openapi.json.
- Interim proof (no code change): overriding
harnessTargetMetadata.lockPath to the instance file made the full daemon→server→session fire proof pass — daemon log: Dispatched WAKE_SUB:ad94a336-… via kimi-server submitPrompt (session session_e86fa9f0-…, status=queued). Negative control (wakeSuppressed) correctly produced zero daemon activity.
So the seam works; only lock discovery is stale. Without this fix, every kimi-harness seat on v0.28+ is wake-silent unless a peer hand-edits subscription metadata.
The Architectural Reality
ai/daemons/wake/daemon.mjs → deliverViaKimiServer() (~L1146-1232): resolves lockPath as meta.lockPath || path.join(os.homedir(), '.kimi-code', 'server', 'lock'), then validates {host, port} (loopback-only + port range) and reads the bearer from tokenPath (default ~/.kimi-code/server.token). The instance file already satisfies the {host, port} shape — only the discovery is wrong.
- The docstring contract (~L1124-1130) states "the loopback coordinates come from
~/.kimi-code/server/lock" — v0.28 invalidates this sentence; it needs the two-generation contract documented.
- Adapter family context:
opencode-server resolves its seat via an envelope the seat writes; kimi-server deliberately uses harness-persisted files ("no seat-side writer needed"). v0.28 keeps that property (the harness writes instances/*.json + server.token itself) — the discovery just moved.
- The instance file adds
heartbeat_at + host_version — usable for liveness validation (stale-heartbeat → fail closed) and for future version-gating of contract assumptions.
- Multiple concurrent
kimi web servers are possible in principle (per-port instances dir) → discovery must handle 0..N instance files deterministically (freshest live heartbeat wins; ambiguous live set → fail closed, matching the daemon's existing wrong-resident paranoia).
The Fix
- In
deliverViaKimiServer(), replace the single-path lock resolution with a discovery helper (e.g. resolveKimiServerLock(meta)):
meta.lockPath override stays authoritative (test seam + the workaround that proved this session).
- Legacy
~/.kimi-code/server/lock if present (v0.27 seats).
- Else scan
~/.kimi-code/server/instances/*.json: parse, require loopback host + integer port + fresh heartbeat_at (e.g. ≤ 2× the harness heartbeat interval, defaulting to a conservative bound), pick the freshest; if two live instances tie, fail closed with an actionable error (name the ambiguity, never pick arbitrarily).
- Error messages must name the generation checked (
no v0.27 lock, no live v0.28 instance — is 'kimi web' running?).
- Update the
deliverViaKimiServer docblock to the two-generation coordinate contract.
- Unit coverage in the existing wake-daemon spec family (fixture instance files under a temp home): legacy-lock path, single live instance, stale-heartbeat rejection, multi-instance ambiguity fail-closed, override precedence.
- Note in the ticket PR body: the harness-version-skew class — adapter coordinate contracts are per-harness-version fragile; a contract probe (lock discoverable + token readable + route listed in
/openapi.json) is the cheap same-day tripwire and is a candidate for the self-repair skill's health checklist.
Contract Ledger Matrix
| Target Surface |
Source of Authority |
Proposed Behavior |
Fallback |
Docs |
Evidence |
~/.kimi-code/server/lock |
Kimi Code v0.27 (deprecated) |
Keep reading when present |
v0.28 instance scan |
daemon.mjs docblock |
v0.27 file from 2026-07-19 proof |
~/.kimi-code/server/instances/{server_id}.json |
Kimi Code v0.28.0 (kimi web) |
Primary discovery, freshness-validated |
none (fail closed w/ actionable error) |
daemon.mjs docblock |
live file 01KXZJH0V0JAAYEPY83RNYBVD7.json this session |
~/.kimi-code/server.token |
Kimi Code (both gens) |
Unchanged — persistent bearer |
n/a |
unchanged |
token identical pre/post v0.28 boot |
POST /api/v1/sessions/{id}/prompts |
kimi REST /openapi.json |
Unchanged — submitPrompt delivery |
n/a |
unchanged |
route present in v0.28.0 openapi; probe returned code:0, status:"running" |
harnessTargetMetadata.lockPath |
manage_wake_subscription metadata |
Stays the authoritative override |
discovery chain above |
unchanged |
override completed today's fire proof |
Decision Record impact: none. (ADR 0014 classifies the wake-delivery lane as local-only; this ticket changes no lane classification.)
Acceptance Criteria
Out of Scope
kimi web residency policy (keep-alive vs OS-service install) — an operator/seat-launch decision; tracked as an OQ candidate in D#15595's harness-cutover chunk.
- Other adapters' coordinate contracts (opencode-server envelope etc.) — no evidence of drift there today.
- Server-side session-adoption semantics (TUI-hosted vs server-hosted sessions) — observed working (prompt queued into a live TUI session); deeper contract is upstream Kimi Code's.
Avoided Traps
- Hard-coding the instance filename:
server_id rotates per server boot; discovery must glob + freshness-rank, never pin.
- Treating the deprecation as a one-off: the fix is discovery + a probe recommendation, not a path swap that breaks again on the next harness bump.
- Auto-starting
kimi web from the daemon: the daemon must keep failing closed with an actionable error; silently spawning a user-facing server from a background daemon violates the seat's process-ownership boundary.
Related
- PR #15588 (kimi-server adapter, merged) · #15579 / #15580 / Epic #15586 (wake-adapter lineage) · #12913 (fire/no-fire proof shape) · D#15595 (harness-cutover chunk consumes this contract) · #15592 (per-seat evidence-class capabilities — the version-skew probe is adjacent)
Origin Session ID: session_e86fa9f0-866e-45e8-a6df-d7bb6dd4d8b5 (kimi-code session, Iris seat)
Handoff Retrieval Hints: query_raw_memories("kimi-server v0.28 lockPath instance discovery wake proof"); daemon log evidence .neo-ai-data/wake-daemon/wake-daemon.log @ 2026-07-20T10:50:19Z (ENOENT drop) + 10:59:05Z (successful submitPrompt via override).
Context
Filed from a live wake-verification session on the Iris seat (operator goal: "verify wake messages to the kimi-code harness"). PR #15588 (merged 2026-07-19, v13.2 dev) added the
kimi-serverwake-delivery adapter against Kimi Code v0.27.0, whosekimi serversubcommand wrote~/.kimi-code/server/lock({pid, host, port}) + persistent~/.kimi-code/server.token. Overnight the seat CLI auto-updated to v0.28.0, which deprecateskimi serverentirely ("kimi serverhas been deprecated and no longer works. Usekimi webinstead") — andkimi webnever writesserver/lock. The adapter's coordinate contract broke within ~12h of merging.The Problem
Empirical reproduction (2026-07-20, wake-daemon PID 6812):
WAKE_SUB:ad94a336-…(adapterkimi-server), live SENT_TO_ME message sent.Failed to deliver via kimi-server: kimi-server requires a readable server lock at '/Users/tobiasuhlig/.kimi-code/server/lock' (ENOENT)— 5 attempts,wake dropped.kimi server status/kimi server runnow hard-error with the deprecation notice; the lock file is gone by design in v0.28.kimi web --no-open(v0.28.0) does run a resident loopback server (127.0.0.1:58627), and it writes a new coordinate artifact:~/.kimi-code/server/instances/{server_id}.json→{"server_id":"01KXZJ…","pid":94541,"host":"127.0.0.1","port":58627,"started_at":…,"heartbeat_at":…,"host_version":"0.28.0"}. The bearer token is unchanged (still~/.kimi-code/server.token, persistent), and the REST route the adapter posts to (POST /api/v1/sessions/{session_id}/prompts) still exists in/openapi.json.harnessTargetMetadata.lockPathto the instance file made the full daemon→server→session fire proof pass — daemon log:Dispatched WAKE_SUB:ad94a336-… via kimi-server submitPrompt (session session_e86fa9f0-…, status=queued). Negative control (wakeSuppressed) correctly produced zero daemon activity.So the seam works; only lock discovery is stale. Without this fix, every kimi-harness seat on v0.28+ is wake-silent unless a peer hand-edits subscription metadata.
The Architectural Reality
ai/daemons/wake/daemon.mjs→deliverViaKimiServer()(~L1146-1232): resolveslockPathasmeta.lockPath || path.join(os.homedir(), '.kimi-code', 'server', 'lock'), then validates{host, port}(loopback-only + port range) and reads the bearer fromtokenPath(default~/.kimi-code/server.token). The instance file already satisfies the{host, port}shape — only the discovery is wrong.~/.kimi-code/server/lock" — v0.28 invalidates this sentence; it needs the two-generation contract documented.opencode-serverresolves its seat via an envelope the seat writes;kimi-serverdeliberately uses harness-persisted files ("no seat-side writer needed"). v0.28 keeps that property (the harness writesinstances/*.json+server.tokenitself) — the discovery just moved.heartbeat_at+host_version— usable for liveness validation (stale-heartbeat → fail closed) and for future version-gating of contract assumptions.kimi webservers are possible in principle (per-port instances dir) → discovery must handle 0..N instance files deterministically (freshest live heartbeat wins; ambiguous live set → fail closed, matching the daemon's existing wrong-resident paranoia).The Fix
deliverViaKimiServer(), replace the single-path lock resolution with a discovery helper (e.g.resolveKimiServerLock(meta)):meta.lockPathoverride stays authoritative (test seam + the workaround that proved this session).~/.kimi-code/server/lockif present (v0.27 seats).~/.kimi-code/server/instances/*.json: parse, require loopback host + integer port + freshheartbeat_at(e.g. ≤ 2× the harness heartbeat interval, defaulting to a conservative bound), pick the freshest; if two live instances tie, fail closed with an actionable error (name the ambiguity, never pick arbitrarily).no v0.27 lock, no live v0.28 instance — is 'kimi web' running?).deliverViaKimiServerdocblock to the two-generation coordinate contract./openapi.json) is the cheap same-day tripwire and is a candidate for the self-repair skill's health checklist.Contract Ledger Matrix
~/.kimi-code/server/lock~/.kimi-code/server/instances/{server_id}.jsonkimi web)01KXZJH0V0JAAYEPY83RNYBVD7.jsonthis session~/.kimi-code/server.tokenPOST /api/v1/sessions/{id}/prompts/openapi.jsoncode:0, status:"running"harnessTargetMetadata.lockPathmanage_wake_subscriptionmetadataDecision Record impact: none. (ADR 0014 classifies the wake-delivery lane as local-only; this ticket changes no lane classification.)
Acceptance Criteria
deliverViaKimiServerresolves coordinates via: explicitmeta.lockPath→ legacyserver/lock→ liveinstances/*.jsonscan (fresh-heartbeat, deterministic single winner, fail-closed on ambiguity).kimi webremediation.npx playwright test).lockPathoverride fromWAKE_SUB:ad94a336-…and re-run the fire/no-fire proof end-to-end on v0.28.Out of Scope
kimi webresidency policy (keep-alive vs OS-service install) — an operator/seat-launch decision; tracked as an OQ candidate in D#15595's harness-cutover chunk.Avoided Traps
server_idrotates per server boot; discovery must glob + freshness-rank, never pin.kimi webfrom the daemon: the daemon must keep failing closed with an actionable error; silently spawning a user-facing server from a background daemon violates the seat's process-ownership boundary.Related
Origin Session ID: session_e86fa9f0-866e-45e8-a6df-d7bb6dd4d8b5 (kimi-code session, Iris seat)
Handoff Retrieval Hints:
query_raw_memories("kimi-server v0.28 lockPath instance discovery wake proof"); daemon log evidence.neo-ai-data/wake-daemon/wake-daemon.log@ 2026-07-20T10:50:19Z (ENOENT drop) + 10:59:05Z (successful submitPrompt via override).