Skip to content
C.W.K.
Stream
Lesson 01 of 05 · published

A Client Surface Is Not a Second Brain

~12 min · architecture, boundaries, llm, ownership

Level 0Unsorted
0 XP0/36 lessons0/12 achievements
0/100 XP to next level100 XP to go0% complete

The Cheapest Wrong Turn in an Assistant-Shaped Product

Adding an assistant to an app is a few dozen lines. A provider key, a system prompt, a message array, a place to keep the history. Every surface that wants one can have one by lunchtime, and that is precisely the problem: the code is trivial and the consequences are permanent.

What a second brain actually creates is a second set of canonical facts. A conversation history that exists only here. A model choice that has to be changed in two places. A persona defined twice and drifting. None of that is visible on the day you add it, and all of it is expensive to unwind a year later once real conversations live on both sides.

Name the Owner, Then Bind to It

The alternative is to declare one canonical owner of identity, conversations, memory and routing, and make every other surface a client of it. A conversation started in the reader is an ordinary conversation belonging to that owner, addressable by a context key — this article, this shelf, this brief — and continuable from anywhere else that talks to the same owner.

The test for whether the boundary is real is simple: can a conversation begun here be continued somewhere else? If yes, you have a client. If it can only be continued here, you have a second brain wearing a client's name, whatever the architecture diagram says.

Keys Live With the Owner

The same argument settles credentials, and more sharply. Provider keys belong to the canonical owner, and a sibling surface that needs a capability calls a narrow internal route rather than holding a key of its own. Two reasons, and the second is the one people underrate: the obvious one is that a secret in one place is a secret in one place. The subtler one is that usage accounting stays whole — spend from every surface lands in one ledger, so the question "what did this cost us?" has a single answer instead of being assembled from several partial ones.

Narrow the Door

A shared internal route is a capability, so it should be as small as the job requires. One-shot completion, no conversation created, no tools, no shell, no persona unless explicitly requested. If a sibling later needs something with tools, that is a different door with its own scope — not a parameter on this one. A generic door with an ever-growing parameter list eventually offers every capability to every caller, which is the same failure as everyone holding a key, arriving more slowly.

Duplicating a capability is cheap; duplicating an owner is not. Before a surface grows its own model client, ask which facts would then exist twice — and remember that conversation history is a fact, so the answer is never none.

Code

A narrow one-shot door, and dialogue delegated to the canonical owner·python
# The narrow door. Deliberately NOT a general chat endpoint: one shot,
# no canonical conversation created, tools and shell hard-off at the
# door. A sibling needing tools gets its own scoped route instead of a
# parameter here -- a generic door with a growing parameter list ends
# up offering every capability to every caller.
async def utility_complete(
    prompt: str,
    *,
    brain: str,
    effort: str | None = None,
    persona_soul_id: str | None = None,   # opt-in, per call
) -> dict:
    body = {"prompt": prompt, "brain": brain, "effort": effort}
    if persona_soul_id:
        # The soul's text stays canonical with the OWNER. We send an id,
        # never a prompt -- otherwise the persona exists here too, and
        # now it is defined twice and free to drift.
        body["persona_soul_id"] = persona_soul_id
    return await post(f"{OWNER_ORIGIN}/api/sibling/utility/complete", body)


# Dialogue is different: it belongs to a typed binder on the owner, so a
# thread started here is an ORDINARY conversation over there, keyed by
# context and continuable from any other surface.
def ask_thread_key(kind: str, ident: int | str) -> str:
    assert kind in ("article", "tab", "brief")
    return f"{kind}:{ident}"      # article:4812 -- resolvable by the owner

External links

Exercise

In a system with more than one surface talking to the same third-party capability, list what each surface stores locally about that capability — credentials, history, configuration, prompts. Anything appearing in two lists is a fact with two owners. Pick the one most likely to drift and work out how you would even detect that it had.
Hint
The answer to 'how would I detect the drift' is usually 'a person notices the behavior differs', which means the detection latency is unbounded. That, rather than the storage cost, is the real argument for consolidating — a duplicated fact is not expensive, it is undetectably wrong.

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 inPlease sign in to comment.

No comments yet — be the first.