imagined.
this page exists for two readers: a user who wants to understand how the "imagined reply" feature works, and an apple app reviewer evaluating whether the app complies with guidelines 1.1, 1.1.6, 4.0, 5.1.1, and 5.6. it is written to be useful to both.
summary for app review:
imagined reply is an opt-in feature, off by default. when on, it generates clearly-labeled fictional bubbles using a local word-bank engine (ReplyEngine.swift + Templates.swift + WordBanks.swift). it never queries an external api. it never contacts the real-world person whose name the user assigned to a thread. every imagined bubble carries a visible "imagined" tag in the ui (MessageBubble.isImagined), and the persisted message model has a Bool field of the same name so the label cannot be lost across sessions. the feature is positioned and worded as fiction, not as a re-creation of any specific person.
01what the user experiences
when the user taps the "imagined reply" checkbox while creating a ghost — and only then — the app will, after the user sends a message, render a small number of fictional bubbles on the recipient side of the chat. each bubble:
- appears with a typing-indicator first, then resolves to a sentence
- has a small monospace label "imagined" rendered immediately below it
- is stored on disk with isImagined: true
- is generated locally and is never sent to anyone
the checkbox itself is described in copy as: "fictional. words assembled on your phone, never theirs, never sent. each bubble is labeled 'imagined'. off by default."
02how the engine works (technical)
the imagined reply system is a deterministic-where-possible, slot-based template engine. it has three parts:
- tone profile (ToneProfile.swift): inspects the user's most recent message for a few sentiment keywords ("sorry", "miss", "fuck", "closure", etc.) and selects one of six neutral sentiment buckets (neutral, longing, apologetic, angry, closure, sad).
- templates (Templates.swift): each sentiment bucket has 6–12 sentence skeletons with named slots like {opener}, {filler}, {regret}.
- word banks (WordBanks.swift): each slot has 20–50 generic candidate words/phrases. one is chosen at random, never specific to any real person.
the engine is plain swift. it imports only Foundation. there is no URLSession call anywhere in the codebase. there is no model file (no .mlmodel, no .gguf, no .bin). there is no on-device large language model. there is no integration with apple intelligence, openai, anthropic, google, or any other ai provider.
03what the engine cannot do
the engine has no memory of who the user is writing to beyond the generic tone bucket selected from the platform context. it cannot:
- imitate a specific person's writing style — it has never been trained on anyone's data
- reference real events from the user's life — it has no access to anything outside the current message text
- produce content that "feels like" the actual recipient — outputs are intentionally generic, drawn from the same word pools regardless of the ghost's name
- send anything anywhere — it returns strings to the swift caller, which writes them into the local Message store
04why we provide it at all
some users — particularly those processing the silence after a relationship ends — find that an empty chat surface intensifies rumination. having a fictional, clearly-labeled completion of the "what would they say" loop, generated by the user's own phone from generic word banks, helps close the open thread mentally without involving the real person. the visible "imagined" tag on every reply preserves the boundary between fiction and reality.
05what we do not claim
ghost does not claim:
- that imagined replies represent what the real person would actually say
- that the feature provides therapeutic, medical, or psychological benefit
- that the feature substitutes for professional support
- that the feature is "ai" in the foundation-model sense, or that it is "intelligent"
06safeguards
- opt-in: off by default; turning it on requires an explicit checkbox tap during ghost creation
- visible labeling: every imagined bubble has a "imagined" tag rendered directly below it (see MessageBubble.swift when isImagined is true)
- persistent labeling: the isImagined bool is part of the persisted Message codable, so labels survive app restarts
- local-only: the engine cannot reach external services because the app makes no network requests
- de-branded contexts: the platform picker uses generic labels ("dating app", "messenger", "encrypted chat", "work", "friend") rather than third-party brand names
07relevant source files
- GHOST/Reply/ReplyEngine.swift — orchestrator
- GHOST/Reply/Templates.swift — sentence skeletons
- GHOST/Reply/WordBanks.swift — slot vocabularies
- GHOST/Reply/ToneProfile.swift — sentiment classifier (keyword-based)
- GHOST/Models/Message.swift — isImagined: Bool field
- GHOST/Components/MessageBubble.swift — visible "imagined" tag
- GHOST/Views/NewGhostView.swift — opt-in checkbox
- GHOST/Views/ComposeView.swift — sets isImagined: true on every generated reply
08contact for app review
if you are reviewing this app and need any clarification, or want a build with imagined reply pre-enabled and pre-seeded for screenshot review, please contact hello@ghost.app and we will respond within 24 hours.