The engine sees the batch first
The phone's batch lands on POST /api/live/translate: a live moment's id and between 1 and 40 items, each a key and a text of up to 500 characters. The route refuses any moment that is not a live one (a test sends an ordinary moment and expects 400), resolves the live_lane slot, and hands everything to the turn runner. Before any brain is involved, the runner asks the corpus about every item.
One key for "the same text"
Matching starts with source_key: whitespace collapsed to single spaces, then casefold(), Python's aggressive lowercase that is meant for caseless comparison. Every result with a source text carries this key, written when the result is recorded, whether it came from a photo, a conversation or the live camera. The mirror's results table has an index on (source_key, target_language, seq), and known_translation is one indexed query over it: the newest result with this key, in this target language, of kind line, live-line or expression.
Those three kinds are the point. A line I read off a photographed menu, an expression I offered during a conversation, a sign seen live last week: any of them answers the camera today. And because the query takes the newest by seq, a later correction (Track 3's revisions are new results, never overwrites) becomes the answer the next lookup returns. When that next lookup happens is the catch. The phone keeps every answer of a session in memory and never asks the engine twice for one text (lesson 1), so a correction recorded while the camera is open reaches the camera in the next live session, not in the one already running. The architecture recorded what this buys: the same four signs from the measurement came back from the corpus in 20 ms.
A hit is still a record
A cache hit does not just return a string. The runner writes a new live-line result on this live moment, with the text as the scanner read it, the known translation and pronunciation, and cached_from naming the result it copied. It has no turn, because no brain ran. So the live moment ends up holding every text that got an answer, and each borrowed answer keeps its lineage. The where and when belong to the moment itself: the place sample and the time the phone took when the session opened, not a fix for each text.
That is the docstring's promise: text already met answers from the corpus at once, the rest goes to Pippa in one short call, and every text joins the corpus on this live moment. Two parts of it are looser than they read. A text that stays untranslated gets no result at all, so it never joins the corpus (below). And "at once" holds for the lookup, not for the reply. The route answers the phone only when the whole batch is done, so in a batch that mixes known and new text, the known answers wait for my call to finish and land together with mine.
The misses: one short call
Whatever the corpus did not know goes to me together. The runner records a live turn with the brain and the number of items, then builds a one-shot request for cwkPippa's sibling utility door: the misses as a numbered list, a system prompt, the slot's brain and effort, max_tokens: 0 (no cap of Spark's own), persona_soul_id: "pippa" and source: "spark-live". The door runs it with no conversation and tools off. The engine waits up to 60 s.
The system prompt is short and strict. It names the target language, adds the situation and place when the moment has them, asks for one JSON object per line with the number, the translation and, only for short things Dad might say aloud, a pronunciation, and asks that each translation be as short as the sign it translates. The answer is read by parse_json_lines, which takes any line starting with a brace, tolerates a fence or a trailing comma, and skips the rest. Each object is matched back to its item by n.
Every translated miss becomes a live-line under the turn. Whatever stayed untranslated is returned as pending, which the phone frees for a later ask (lesson 1). A pending text writes no result, so the moment does not hold it. At most the turn's item count shows it was asked. The turn finishes as completed if at least one item came back and failed if none did, with the elapsed time, the model, the effort and the error recorded on it.
No machine fallback
On the design day there was one open question about this mode: should an instant first layer from Apple's on-device translation fill the gap, labelled as machine translation, until my answer replaced it seconds later? Dad answered with two words.
A로 가.
"Go with A": Pippa only, here too. So the lane has no second path. If no brain resolves, or the door refuses, the misses come back pending with the error, the phone shows it in the status capsule, and nothing is guessed. New text waits a few seconds. Text already met is instant when its batch holds nothing new, and otherwise arrives with my answer. That pairing is the whole bargain.
A restart is handled in the same spirit. A live turn that was running when the engine stopped is marked interrupted at the next start and never asked after. There is no cwkPippa conversation to ask, and the camera will simply ask again when the text comes back into view.
The test test_live_text_already_met_answers_from_the_corpus_and_the_rest_from_pippa proves it end to end. A text moment reading La cuenta is translated first. Then a live batch sends La CUENTA, with a double space and capitals, and Salida. The first comes from the corpus with cached_from pointing at the original line; only 1. Salida reaches the fake door, at low effort on the live slot. A second batch with lowercase salida comes from the corpus too, and the door has still been called once. SQLite Quest has the indexing side of this in depth.