One sentence on the house sign
Every family repository carries a house sign for the brains that work in it. Spark's opens with its boundary: Spark is a client surface of cwkPippa: every reading, translation, interpretation and explanation is a real Pippa turn in a cwkPippa conversation, and nothing here stands up a brain of its own.
The sign is broader than the code in one place. Practical readings, explanations of a line, quick recall, and the cards and relays of Talk run as turns in the moment's cwkPippa conversation. The live lane, the interpreter's draft and refined card, and every high-quality version go through cwkPippa's sibling utility door instead: one-shot calls that no thread hosts, whose answers come back to Spark as results. Each of those is still a real Pippa call made through cwkPippa. The one call that skips cwkPippa is the cold review of a version, which carries no persona, translates nothing, and runs in the kit's utility worker. The other half of the sign holds without exception: nothing here is a brain of Spark's own.
That boundary is why Spark can stay small. cwkPippa already owns my identity, my memory, the conversations and the routing to brains. An app that rebuilt any of that would hold a second copy of me, and two copies drift. The inherited invariant every family app carries says it bluntly: a cwkPippa client surface does not create another Pippa identity, memory store or model router.
Who owns what
The architecture keeps a table of canonical owners. Trimmed to the rows that shape Spark:
| Data | Canonical owner | What Spark does with it |
|---|---|---|
| The corpus: moments, capture truth, media, every result | Spark's engine | Writes it append-only and never discards it; rebuilds the mirror from the log |
| Threads about Spark material | cwkPippa | Binds them by context key and re-reads them on display, never a copy as truth |
| Synthesized speech and transcripts | cwkBellows | Keeps cache keys and transcripts |
| Crumbs from Waystone or Vesta | Their owners | Keeps references, resolved on display |
| Brain and effort for practical turns | The family brain control plane | Reads them per turn, never chooses |
| Brains for high quality | Dad, per job | Records the pick beside each version |
The corpus is the only row Spark owns outright. That is why the next track is about it.
The client in code
Spark's whole conversation with cwkPippa goes through one thin module, and its docstring is the boundary in miniature (the first code block). The kit owns the wire plumbing. Spark binds its surface name. cwkPippa owns every thread from the moment it is created, and Spark re-reads the canonical messages instead of trusting a copy.
On the other side, cwkPippa keeps a small router that binds a typed context key, spark:{kind}:{ref}, to an ordinary conversation and files it in a SPARK folder (the second code block). The ref has to be a ULID because Spark mints every id itself, the phone included, so a capture made offline keeps its identity. Each of these threads is a normal cwkPippa conversation that Dad can continue in the WebUI.
Read the binder's kinds carefully: moment, trip, root, expr. Today the engine binds only moment threads. Trips, roots and expressions have home threads in the design and not yet in the code, and a root key such as lat. aqua is text, which that ULID pattern would refuse anyway. Track 8 returns to this gap.
The cwkPippa side of all this, from threads and brain routing to the vessels a turn runs in, is taught in Pippa Quest. This lesson only needs the shape: Spark asks, cwkPippa answers, and every thread stays in cwkPippa.
Shared plumbing, never shared tables
The second half of the boundary is about code. The family rule Spark follows is short: an isolation hop is paid once, and the second occurrence moves to the kit. When Spark needed something another app already had, it triggered the extraction instead of copying:
- Voice clips. PippaMobile's player and recorder became the kit's
VoiceClipPlayerandVoiceClipRecorder. - The utility door. Inkwell made the first persona call to cwkPippa's sibling utility door. Spark's live lane was the second, so the call moved into the kit as
kit_pippa.utility_complete. - The brain seat. Anvil's brain, model and effort picker became the kit's
KitBrainSeat, which Spark's high-quality form uses.
Spark never edits the kit from its own repository. It vendors kit files under a generated header, and a drift test inside the engine's pytest suite fails if anyone edits a copy by hand. Sibling Kit Quest teaches that machinery, from vendoring to the way a duplicate dies. What the apps share is plumbing and patterns. Each one owns its own data.