Dialogue · Design notes · Studio
← Design notes

Studio — building a persona

Studio is "build a persona" as an app function, live at chat.xbbapp.com/builder. Name a figure (or describe one), give one identity-confirmation tap, and the machine runs unattended — gather with verbatim VERIFY, curate, author, mechanical checks, two cold auditors — and the persona lands in your Studio. Status: built · live at /builder. This page reflects the shipped code; it folds together the four earlier design notes (the v1 persona-builder, the v2 "one machine" redraw, the v2 UI mockup, and the Persona Studio home).

1 · what it is 2 · the one machine 3 · the build pipeline 4 · the Studio home 5 · the building page 6 · visibility & ownership

1 · What the Studio is

The persona pipeline began as a Claude-Code authoring kit — a human running a multi-session gather → author → cold-audit flow by hand. The Studio is that pipeline ported into the API-only server as an unattended state machine, with a home wrapped around it. One human decision remains — which identity — and everything after it is the machine's.

A persona is pure data — profile.md + tags.txt (+ a corpus/ of verified quotes for real figures). The Studio's job is to manufacture that data honestly: every claimed fact traceable to a fetched page, and every gap between what's known and what's written disclosed rather than hidden.

It opens to every logged-in user, gated only by a per-user spend cap (build spend counts against the same daily/weekly budget as chat). Your builds are yours — born private, listed only to you — until an admin publishes them into the shared library.

2 · The one machine

Five real builds during validation showed the original three build types (public record · your sources · invented) leaking into each other: almost every persona is a mixture of web-found, user-supplied, and AI-filled content. So there are not three pipelines — there is one build() path with switches. The switches:

switchin codewhat it controls
Referent classbtype = public · supplied · invented; identity kind = person | characterWho answers for the persona, and how much invention is legitimate. A character gets a CANON LOCK overlay; a revered real figure gets the REVERED overlay (faithful is ceiling and floor). An invented persona is its own ground truth — no corpus to contradict.
Evidence poolmetas = ingest_metas + web_metasWeb fan-out and your uploaded material are different axes, not substitutes. A documented public figure gets the full web gather even when you supply files; the material is your emphasis ("it leads where both speak"), never a replacement. Both pass the same VERIFY discipline — a quote enters the corpus only as a normalized substring of the page it came from.
The metercoverage_gate() (web) · supplied_gate() (material)Counts verified quotes, distinct domains, and thin facets — how far the evidence actually stretches. Thin → one targeted re-gather. The material meter is lenient (SUPPLIED_MIN_ITEMS = 25 is the floor); the one refusal in the whole machine is failed: nothing-to-build-from when nothing verifies at all.
Imaginationdecide_imagination()faithful · extended · freeSystem-set, not a user dial. The referent class sets a ceiling; the meter picks the notch within it. The app never exposes this control (the CLI may request up to the ceiling with --imagination).
Provenanceinline tags + compute_made_from()The disclosure. In-prose tags mark what isn't corpus-attested — [public] (true but uncited), [inferred], [hedged], [paraphrase], [representative construction]; corpus-grade facts carry no tag and quotation marks appear only on verbatim lines. The made-from bar is the outward summary.

The cap and the notch

Imagination is decided in two moves: the referent class caps how far invention may reach, and the evidence decides where inside that cap the build actually sits.

REFERENT CLASS ← the reachable span (ceiling) · ● recommended notch revered figure faithful is ceiling & floor real person extended is the ceiling ≥ 25 verified items pulls the notch here character · invented its own ground truth faithful extended free
The coral bar is how far the class lets invention reach; the filled dot is where decide_imagination() actually sets it. A revered figure is pinned to faithful; a real person defaults to extended but a rich corpus (≥25 verified items) pulls it back to faithful; a character or invented persona runs free. The user never turns this dial.

Provenance — the made-from bar

At publish, compute_made_from() renders one bar: a solid attested share sized by the imagination notch (faithful ≈ 85% · extended ≈ 60% · free ≈ 45%), split web-vs-material by where the verified items came from, and the remainder marked AI fills. It rides the confirm screen as a forecast (hatched) and the finished report as the result (solid), and a compact version sits on the persona card.

MADE FROM — example, extended notch web · 40% your sources · 20% AI fills · 40% The solid attested share (web + your sources) grows toward 85% as the notch moves to faithful; the AI-fills remainder shrinks.

3 · The build pipeline

One build() state machine runs eleven stages, front to back. Every stage is a breakpoint: a kill or crash pauses the build (state persists in _job.json), it never loses work — a resumed build skips whatever's already on disk.

Identify Gather Author Verify Publish
#stage · labelwhat it doesruns on
1Identity lock identitySERP probes fan out; the model resolves candidate identities (person/character · living · record rich/thin · revered · rights). Supports descriptive search (no name → "the best realtor in the Bay Area", ranked by independent coverage). The one human tap picks a candidate.v4-pro
2Ingest your sources ingestOnly when material is supplied. Mines uploaded files (.txt/.md/.pdf/.epub) + endorsed links into supplied facets, same VERIFY discipline as the web; auto-detects transcript-vs-polished per document.v4-flash
3Gather gatherPer-facet loop over five facets (biography · worldview · interviews · manner · positions): PLAN → SEARCH → TRIAGE → FETCH+extract → MINE → VERIFY → GATE, up to three rounds.v4-flash
4Coverage probe coverageOne thinking call — "what famous episodes of this life are missing?" — seeds a targeted gather round for the gaps quota-hunting skipped.v4-pro
5Curate curateWrites sources.md (reliability tiers · conflicts · a knowledge-horizon paragraph); drops lock-mismatched URLs from disk.v4-pro
6Coverage gate gate"The meter" in code — floors on verified quotes / distinct domains / thin facets, triggers one re-gather, sets the imagination notch. The one refusal lives here.code
7Author authorWrites profile.md + tags.txt from the kit template and the corpus, with stacked overlays (supplied / character / revered). Code owns the slug + ULID.v4-pro
8Mechanical checks checksFrontmatter card keys · 9 required sections · banned analyst labels · quote oracle (verbatim) · tags rubric · single-source shingle scan.code
9Patch patchRepairs against the findings list — and is never allowed to leave the profile mechanically worse (net-harm patches revert).v4-pro
10Cold audit ×2 auditTwo adversarial cold auditors (seats A/B), worst-of verdict (PASS / FIX-THEN-PASS / RE-AUTHOR), then a fix-verify loop; RE-AUTHOR buys one full re-author + re-panel. A dead seat degrades to one voice.2 models
11Built publishComputes made_from, generates the 4-prompt live-room test checklist (supplied builds), finishes the job. "Publish" now means admin-publish; the build's own final state is Built.code

The per-step model matrix

Each stage's model + thinking level is a preset (PRESETS — a default and an all-deepseek), console-editable at /api/admin/builder-config. The running spine shows a model chip per stage — with two deliberate exceptions: checks/gate show a code chip, and the auditors' models are never revealed ("don't leak the auditor").

stagemodel · thinking (default preset)
identityv4-pro · off
gather · ingestv4-flash · off
curate · coveragev4-pro · high
authorv4-pro · high
patchv4-pro · off
audit A / audit Bv4-pro · high  /  gemini-3.5-flash · high  (hidden in the UI)

4 · The Studio home

The home lives at /builder: a list of the personas you made plus a + New build form. The list is owner-scoped — only builds where built_by_uid is you (CLI / Claude-Code builds have no uid and never appear) — in three sections: In progress (parked or running), Finished (built), and Deleted (dimmed, with Restore).

Each row is the house cast-card — slug-hashed avatar colour, name + local name, role, a chip row, and a made-from mini-bar. The chips read the record: a status chip (built = green · pick-identity / awaiting-confirm = yellow · building = slate shimmer · interrupted = muted · failed = red), a visibility chip (public = coral · private = muted), a type chip (your sources = plum · invented = lilac), and a cost chip in gold.

The ⋮ menu — status-appropriate

stateactions
built (published-to-you)Built summary (opens the step-4 report) · Delete
parked probeContinue · Delete
interruptedResume · Delete
failed / abortedRestart · Delete
deletedRestore only

Delete is a centred confirm (Cancel / red Delete). A built persona soft-deletes — a tombstone; the files stay, chats where it's seated keep working, and it's restorable — while a parked or failed scaffold hard-deletes and frees the slug. Clone, promote, and demote are not here — they're admin-console verbs (see §6); the owner's Studio does build / delete / restore.

A pinned spend meter appears only when a cap is set (it dims + New when over). Build spend counts toward the same daily/weekly cap as chat.

How it's embedded in the app

5 · The building page

A running build renders a spine — one row per stage, driven by an SSE snapshot-then-tail over the job journal: a status dot (· / ● / ✓ / ✗), the stage name (shimmering while live), a model or code chip, the stage message, and sub-rows (gather facets /, ingest docs, audit seats — a dead seat gets a "restart from here" button). A sticky ticker shows stage · $ · elapsed.

Level-one progress — hiding the internals

Regular users don't need the machine-room. When the admin flag studio_details ("Show build details", under Who sees what → Persona Studio, admin-on / tester-off) is off, the page adds html.hide-details and the spine collapses to the level-one named progress — just the dots and stage names, Identity lock → Built. The finished report collapses to its headline too: the verdict and the built-from bar stay (plus the cost bill if studio_costs is on); coverage-by-facet, the audit.md / sources.md folds, and the test checklist are hidden.

REGULAR USER — level one Identity lock Ingest your sources Gather Author Cold audit ×2 Built verdict · built-from bar only ADMIN — studio_details ON Gather v4-flash 5 facets · 41 verified quotes · 12 domains ✓ biography✓ worldview◔ manner (thin) Author v4-pro profile.md + tags.txt · extended notch Cold audit ×2 seat A · seat B — worst-of verdict then: coverage-by-facet · audit.md · sources.md · the 4-prompt live-room test checklist · the cost bill
The same build, two audiences. studio_details off (left) leaves only the named progress and the verdict; on (right) it exposes the per-stage detail lines, model chips, facet/seat sub-rows, and the full finished report. Shipped in v422.

You can leave — we'll notify you

While a build runs, a reassurance card sits at the foot of the page: "Building in the background. You can leave this page — we'll notify you the moment it's ready." — a bell icon and a ← Go back button, both inside the box. The build keeps running server-side; a notice fires build:done / build:failed over the user's SSE stream (a toast, the ≡ dot, the notification centre). A mutex on the confirm screen enforces one build at a time per user.

The finished report

Top to bottom: the verdict headline ("✓ built · 7m 58s" or "✗ reason · time") · the built-from bar (solid) · coverage by facet (verified-item counts, a source chip, a "thin" mark) · the cost bill (by-stage and by-API tables) · the audit.md and sources.md folds · the live-room test checklist (supplied builds — the kit's mandatory 4-prompt extrapolation test) · and the actions View profile / Resume build / All builds. Everything below the built-from bar is what studio_details hides.

6 · Visibility & ownership

A build is born private — listed only to you, seatable only by you. Making it available to everyone is an admin act, and it never happens to your work behind your back.

Two axes × two values — visibility: private | public · deletion: alive | deleted — plus immutable created_by. One predicate every discovery surface calls: alive AND (public OR mine). One invariant: an admin never raises the visibility of someone else's persona — promotion crosses ownership by copy (clone → test → promote).

Inside the Studio you see only the parts that touch your own builds: born-private, the public/private chip, and Delete / Restore (deleting requires private first, so public always means alive-and-listed). Clone / promote / demote live in the admin console's Personas tab. The full model — the state graph, the five verbs, the seating door, and every leak the audit closed — is its own page:

Persona visibility — permission & lifecycle

Studio · the persona factory, as shipped · built & live at chat.xbbapp.com/builder. This page folds together four earlier notes (persona-builder v1 · builder-v2 "one machine" · the v2 UI mockup · Persona Studio home) into one current-implementation record. Source of truth: lib/builder.py · lib/builder-ui.html · lib/run_room.py. Related: Persona visibility · Vibes content engine · Current functions.