LearnNewsExamplesServices
Frontmatter
id15596
titlekimi-server wake adapter: v0.28 instance-file lock discovery
stateClosed
labels
bugairegression
assigneesneo-kimi-iris
createdAtJul 20, 2026, 1:04 PM
updatedAtJul 20, 2026, 2:54 PM
githubUrlhttps://github.com/neomjs/neo/issues/15596
authorneo-kimi-iris
commentsCount1
parentIssuenull
subIssues[]
subIssuesCompleted0
subIssuesTotal0
contentTrust
projected
quarantined0
signals[]
blockedBy[]
blocking[]
closedAtJul 20, 2026, 2:54 PM

kimi-server wake adapter: v0.28 instance-file lock discovery

Closed Backlog/active-chunk-8 bugairegression
neo-kimi-iris
neo-kimi-iris commented on Jul 20, 2026, 1:04 PM

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):

  1. Subscription registered WAKE_SUB:ad94a336-… (adapter kimi-server), live SENT_TO_ME message sent.
  2. 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.
  3. kimi server status / kimi server run now hard-error with the deprecation notice; the lock file is gone by design in v0.28.
  4. 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.
  5. 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.mjsdeliverViaKimiServer() (~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

  1. 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?).
  2. Update the deliverViaKimiServer docblock to the two-generation coordinate contract.
  3. 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.
  4. 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

  • deliverViaKimiServer resolves coordinates via: explicit meta.lockPath → legacy server/lock → live instances/*.json scan (fresh-heartbeat, deterministic single winner, fail-closed on ambiguity).
  • Error text names both generations and the kimi web remediation.
  • Docblock updated to the two-generation contract.
  • Unit specs: legacy path, single live instance, stale heartbeat, multi-instance ambiguity, override precedence — all green in the custom unit config (never default npx playwright test).
  • Post-merge (seat-level, flagged): remove the hand-set lockPath override from WAKE_SUB:ad94a336-… and re-run the fire/no-fire proof end-to-end on v0.28.

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).