Skip to content

perf: instrument the mobile↔host paths so latency claims are measurable #189

Description

@0xSero

Follow-up to the Local Studio review on #186. Today there is no way to answer "did that change make it faster or slower?" for any mobile↔host path — every latency claim in recent PRs is a stopwatch observation on one machine.

The hooks to fix that mostly exist already and are unused.

What we can measure today but don't

_alleycat_seq is a free ordering and gap oracle. Every frame the daemon sends carries a monotonic sequence number, and Connect returns SessionInfo { attached, current_seq, floor_seq }. That is enough to compute, per reconnect: whether we resumed or drifted, how many frames were replayed, and how far the client had fallen behind. None of it is recorded.

kittylitter probe already reproduces the phone exactly, including --resume-from / --repeat-resume-from. tools/scripts/local-studio-e2e-proof.sh (added in #188) drives it for correctness; it emits no timings.

Local Studio's gateway carries per-section freshness. Freshness { observed_at, age_ms, max_age_ms, stale } and a requestId on every response. Unused, because the gateway itself is unused — but the same shape is the right target if the controller plane is ever revived.

refresh_models now fans out concurrently with a 20s per-runtime timeout (MODEL_LIST_RUNTIME_TIMEOUT) and records nothing about which runtime was slow. The timeout was presumably chosen from observation, not data.

Proposal

1. Emit structured timing spans from shared Rust. The crate already uses tracing. Add spans with a stable field set on the four paths that dominate perceived latency:

  • connect — dial → Connect response → first frame, plus attached and current_seq - resume_from.
  • model_list — per runtime kind, so a slow Local Studio catalog is attributable rather than folded into one number.
  • thread_start → first turn/start accepted. This is the "new session feels slow" metric that 6666098 and 1a2c5c1 were chasing without instrumentation.
  • turnturn/start → first assistant delta → turn/completed. Time-to-first-token matters far more than total here.

Fields, not messages, so they can be aggregated. These already surface in the simulator via stdout capture (see the daemon-debugging notes) with no extra plumbing.

2. Add a --timings flag to kittylitter probe. It already knows when it sent each request and when each frame arrived. Printing a small JSON summary (connect ms, response ms, time-to-first-notification, frame count, terminal method) turns the existing E2E harness into a benchmark for free.

3. Extend local-studio-e2e-proof.sh to record those timings per scenario and write them next to the existing evidence. Two immediate uses: a regression check against a committed baseline, and an actual data-driven answer for the MODEL_LIST_RUNTIME_TIMEOUT value.

4. Track three numbers over time, not a dashboard. Cold-launch reconnect to first usable thread list; new-session tap to first rendered user message; turn send to first assistant token. Everything else is diagnostic detail for when one of those three regresses.

Why this ordering

1–2 are small and unlock 3. 4 is the only part that needs a decision about where numbers get stored. None of it requires the controller/gateway plane, so it is unaffected by how the architecture question in #186 is resolved.

Non-goal

No telemetry leaving the device. Per the repo's existing stance, mobile logs stay local — this is about making local runs measurable and CI/manual runs comparable, not about collecting from users.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or requestpriority: nextImportant follow-up after critical correctness work

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions