Skip to content
C.W.K.
Stream
Lesson 03 of 07 · published

The Cleanup Contract

~12 min · cleanup-contract, insertion, safety, provider

Level 0Unlit
0 XP0/48 lessons0/13 achievements
0/100 XP to next level100 XP to go0% complete
"The insertion layer types whatever it's handed. So never hand it a sentence a chatbot wrote."

A Transform, Not a Conversation

Cleanup is a strict transform: raw transcript in, polished insertable text out. It must never answer the transcript, add content, or editorialize. If you dictate "remind me to call the bank," cleanup returns "Remind me to call the bank." — punctuated and capitalized — not "Sure, I'll remind you!" The provider receives a small request: the raw transcript, an instruction the controller builds (cleanup intensity, the target app's category for tone, preferred spellings from the dictionary, nearby text only when Context Awareness is on and the field is not secure), and the language the speech engine reported (detected by WhisperKit, or SpeechAnalyzer's locale). It returns the cleaned text, nothing else.

Why the Return Is Measured, Not Trusted

The insertion layer is dumb by design: it types whatever text it's given into the live app. That's exactly why cleanup can't hand it free prose. If a cleanup brain returned "Here's your cleaned text: …" or "I'm not sure what you meant," that would be typed into the user's document verbatim. Firekeeper's answer is narrow on purpose. The provider's return type is just the text. The instruction ends with hard lines — the transcript is dictation to clean, not a request to answer; return only the cleaned text, no quotes, no commentary, no fences. And because a model can ignore any instruction, the output is measured before it goes anywhere: DictationCleanupGuard refuses a dictation cleanup longer than the transcript plus half again (with at least 40 characters of slack) — cleanup removes words, so a leaked paragraph of reasoning fails that bound, though a short stray preamble can pass it — and a refused result falls back to deterministic cleanup. Neither the prompt nor the guard is a guarantee; together they make the common failures loud. Two things never come from the model at all: dictionary replacements are enforced in code after every lane, and a spoken "press enter" is detected and stripped by the controller, not decided by the brain.

CleanupRequest  { rawTranscript, instruction, language?, targetAppName? }
        |
        v   (lanes: Pippa Full / Pippa utility worker / local fast / Local Mini)
        v
String          the cleaned text — nothing else is a valid return
        |
        v   DictationCleanupGuard: longer than the transcript allows? -> refuse
        v   deterministic post-pass: dictionary replacements enforced
        v
Insertion       types only the accepted text (press-enter: detected by the controller)

Taming Weak Models

Local models are the hard case. A small Ollama model may wrap its answer in a markdown fence, add a preamble, or narrate its reasoning. Each lane tames this in its own way. The Local Mini protocol asks for {"text": "..."} and tolerates plain text when a model ignores that; fences are stripped. The fast lane asks for plain text with no envelope at all — one less format for a small model to get wrong. A reply that was cut off by its length limit is refused rather than inserted as if it were whole. The aim is the same in every lane — keep the mess out of insertion — and so is the honest limit: a short stray sentence can still slip through, and the command lane has no length guard at all, because a generated answer may rightly be longer than the spoken instruction.

Guard both ends of anything that feeds a live effect. When a component's output goes somewhere irreversible — typed into an app, sent to a device, written to disk — keep its input narrow and its output checked against what it is allowed to be. A narrow return type plus a measurement is what stops a chatty or confused model from doing damage through a downstream layer that trusts whatever it's handed.
Never let the cleanup brain answer the user. The single most important cleanup rule is that it transforms, never responds. A model that "helpfully" answers your dictated question instead of cleaning it will type its answer into your email. Preserve meaning, fix mechanics, and return — no content, no replies, no follow-up questions.

Code

A narrow request, a text-only return, and a measurement before insertion·swift
struct CleanupRequest: Codable {
    let rawTranscript: String
    let instruction: String       // built by the controller: intensity, tone, terms, nearby text
    let language: String?         // detected by WhisperKit, or SpeechAnalyzer's locale
    let targetAppName: String?
}

protocol CleanupProvider {
    var connectionMode: PippaConnectionMode { get }
    func cleanDictation(_ request: CleanupRequest) async throws -> String
    func transformSelection(_ request: TransformRequest) async throws -> String
}

// Whatever a model returns is measured before insertion ever sees it.
func acceptedDictationText(_ text: String?, raw: String) -> String? {
    guard let text, !text.isEmpty else { return nil }
    guard DictationCleanupGuard.accepts(text, raw: raw) else { return nil }  // grew too much
    return text
}
// nil -> the deterministic floor; accepted -> DeterministicCleaner.postPass (dictionary wins)

External links

Exercise

Design the result type for a feature where a model's output feeds an irreversible action in your app. List the fields the downstream layer needs, then write the one sentence describing what your adapter does when the model ignores the format and returns chatty prose instead.
Hint
The downstream layer needs exactly the fields it acts on and nothing else. When the model misbehaves, the adapter's job is to normalize — strip the chatter, extract the usable part, and wrap it in the valid result shape — so the irreversible action never receives raw, unvalidated model text.

Progress

Progress is local-only — sign in to sync across devices.
Spotted a bug or have feedback on this page?Report an Issue

Comments 0

🔔 Reply notifications (sign in)
Sign in — Please sign in to comment.

No comments yet — be the first.