Skip to content

CarPlay fit

Triage a board from a car and you hit the same wall every drive. About half the sessions parked in needs_input want a credential pasted into a console, a test run on your Mac, or a long document read. The other half want a yes or no, a merge call, or a short reply. From the driver’s seat the two halves look the same, so every drive re-triages the laptop-only rows from scratch.

CarPlay fit is the mark that tells them apart. It is a judgement about the session’s next human action, with a reason, a provenance and a staleness rule. It is not a tag, and it decides nothing about when a session runs.

ColumnValuesMeans
carplay_fitcarplay_ok, laptop_only, or noneThe decision: can the next human action be taken by voice from a car?
carplay_fit_reasondecision, short_answer, long_read, manual_steps, credential, local_device, nothing_neededWhat kind of action the session is waiting on.
carplay_fit_sourceauto, human, assistant, sessionWho wrote the mark.
carplay_fit_set_ata timeWhen.
carplay_fit_line_countan integerThe session’s transcript line count when the mark was written.

The five travel together through one writer, Sessions::SetCarplayFit, which every surface calls: the status-summary harvest, the web UI, the REST endpoint and the set_carplay_fit MCP action.

Nothing in Zimmer’s scheduling reads these columns. Not the state machine, not the spot queue, not precedence, not the quota gate, not triggers or wake-ups. The board’s order is untouched too: a laptop-only session keeps its place for the laptop. The mark changes which rows a caller asks for, never when anything runs. That is the same contract board visibility has, and it is the one to keep.

Derived at every rest, overridable by hand

Section titled “Derived at every rest, overridable by hand”

The ordinary writer is the status summary. The fork that writes the blurb when a session comes to rest is asked, in the same turn, to end its answer with one machine-read line:

carplay-fit: laptop_only; reason: credential

StatusSummaryAnswer strips that line from the stored blurb — any last line opening with carplay-fit:, parseable or not, so the reader never sees one — and writes the mark as an auto one, stamped at the blurb’s own line count. An answer that is only the trailer is a summary failure, not a mark: there is no blurb to stamp it against. It costs no second inference: the blurb’s first sentence already says what the human must do, and the fork holding the conversation is the one thing that can say whether that is voice-able. A summary that fails, or answers without the line, leaves the mark untouched and the session reads as untagged, which is the honest answer.

Three other writers can override it:

  • human — the CarPlay fit control on the session page, the User view row and the phone’s bottom sheet, and PATCH /api/v1/sessions/:id/carplay_fit from the iOS app acting as its person: the CarPlay fit item in the app’s session menu, which names the mark in force and offers each side with its reasons, or Clear.

    On the session page (beside the heart and bell icons) and on each User view row (beside Snooze and Trash) the control is a car icon that toggles CarPlay-ok. It reads as on while a current CarPlay-ok mark stands, whoever wrote it. Clicking it on writes a human CarPlay-ok, replacing a laptop-only mark and its reason if one stands. Clicking it off clears the mark, the same as Clear mark, rather than writing laptop-only: “not CarPlay-ok” does not mean the session needs a laptop. A mark Zimmer, an assistant or the session wrote gets a dashed outline, and a solid one is yours. Laptop-only shows as the car struck through. The chevron beside the car opens the full set of choices: Laptop only, a reason, or Clear. The phone’s bottom sheet keeps the full menu.

  • assistant — action_session → set_carplay_fit on the full sessions surface, which is what Live Mode calls during a drive after reading a summary.

  • session — the same action on the self_session surface, a session speaking about itself. Unlike set_visibility, which that surface excludes, “what I need from the human next” is exactly self-knowledge; the open-pr skill holding a PR for a merge decision is the obvious caller.

Precedence is enforced in the writer. At the same transcript position human beats assistant and session, which beat auto; equal rank overwrites. A write the rule refuses is reported, not raised — the MCP action answers with “CarPlay Fit Kept” and the reason — because the caller that loses is usually the summary fork, and a lost precedence contest is not an error.

A mark is current while the session has not written a line past carplay_fit_line_count, and stale once it has. That is the status summary’s own freshness model, reused on purpose: a session that became laptop-only after opening a PR that needs a diff read has moved, and the mark written before that move was about an earlier state. Every filter treats a stale mark as untagged; every summary line names it as stale, with how many messages have landed since. When the session next comes to rest, the fork writes a fresh auto mark, and a mark written against a later position always wins whoever wrote it.

There is no sweeper and no expiry job. Staleness is a comparison on read, which is what guarantees no background writer can ever race the lifecycle.

Every listing takes the same carplay_fit filter, and every one of them leaves it unset by default, for the reason visibility does: a laptop-only session is still the session a duplicate check is looking for.

ValueReturns
carplay_okMarked good for a driver, and the mark is current.
laptop_onlyMarked as needing a screen or a keyboard, and the mark is current.
untaggedNever marked, or marked against an earlier transcript.
not_laptop_onlycarplay_ok plus untagged. The driving default.

The filter is on the dashboard’s Filters form, the iOS app’s Filters sheet, on GET /api/v1/sessions and /sessions/search, and on the quick_search_sessions and get_user_view MCP tools. get_user_view rows always carry a **CarPlay fit:** line; quick_search_sessions and get_session carry one whenever a mark exists, stale ones included.

A session already resting on the board when this shipped rested before the fork learned to answer the question, and most such sessions will not rest again for days. BackfillCarplayFitFromStatusSummaries is the post-deploy task that covers them: it walks every unmarked needs_input session that has a ready blurb and enqueues CarplayFitBackfillJob for it, which runs CarplayFitClassifier — one short Haiku completion over the stored blurb, through the same one-shot path a session title uses — and writes an auto mark at the blurb’s line count. The blurb itself is left exactly as it was. Its answer is in post_deploy_task_runs on /health, and the marks are on the board.

When the client driving a Live conversation is CarPlay, LiveConversations::SystemPrompt adds one line to the mechanics section Zimmer writes: that the person is in a car, that sessions carry a CarPlay fit, and which tools filter and set it. Zimmer states the fact; what to do with it belongs to the live-mode root’s own modes/voice.md, which is where the driving guidance lives. See Live Mode.