First round run after the #2988 write-path fixes shipped, and the first with real screenshots. Rounds 1–3 asked "did the AI read the business correctly?" and the answer was consistently yes. This round asks the next question — "does the founder get a playbook worth reading?" — and the answer is no, for one specific, fixable reason.
Captured with a new Playwright harness (tests/e2e/brain-parity-capture.spec.ts) so the
next round is a re-run, not another hand-driven browser session. Full-page captures — nothing
cropped at the fold. Mobile is graded first: the canon takes the worse of the two widths.
The design artifact is not silent on what this row should look like. The binding rule is
V-12 / P0-1, stated in brain.html:3403 (frame f22, Active section):
“ONE playbook row, TWO sheets — never a zone toggle… the row stays one
because a founder reads ‘does this sound and look like me?’ as one question; the
sheets stay two because the material behind it doesn’t share a shape.”
Every rendered instance in the artifact agrees — :1605, :1767,
:1938, :2283, :3175 all draw ~6–10 words per facet:
Branding (sound) Warm, direct, a little playful. · (look) Earthy neutrals, film-grain photos.
That is roughly six words per facet. Here is the same row in production:
Spiritual, gentle, introspective, and professional. The tone is inviting and focused on healing and self-discovery. Professional, reflective, and appreciative. The tone is elevated and community-focused, emphasizing empowerment and shared experiences. Mystical, artistic, and slightly cryptic. The tone is elevated and intentional, focusing on the spiritual or symbolic nature of the artwork. Spiritual, artistic, and therapeutic. The tone is ethereal, grounded, and intentional… [1,452 chars total]
| Facet | Artifact spec | Production | Overrun | Repetition |
|---|---|---|---|---|
| (sound) | ~6 words | 192 words / 1,452 chars / 22 fragments | ~32× | “The tone is” ×11 · “artistic” ×9 · “spiritual” ×7 |
| (look) | ~6 words | 282 words / 1,868 chars / 29 fragments | ~47× | “The aesthetic” ×6 · “earthy” ×6 · “bohemian” ×4 |
Two sentence-pairs appear verbatim twice inside the same block. The content is not merely long — it is one description per screenshot, concatenated, with no synthesis pass.
Three lines of production code explain the whole thing.
const voiceLine = voiceInstructions || profile.tone?.trim() || '';
const lookLine = hasStyleCard ? 'Colours, type, logo — from your site' // short, artifact-shaped : profile.visual_tone?.trim() || ''; // raw 1,868-char dump
const merged = [currentCustomInstructions, ...dedupeNearIdenticalNotes(voiceNotes)]
.filter(Boolean)
.join('\n\n');
// …and the file's own comment at L120:
// "This is NOT the real fix (an LLM synthesis pass …)"
_shared/style-harvest.ts:1-24 declares a BINDING scope wall on the style card:
extraction is “from a founder’s website CSS + HTML”, with
“NO design-system-from-a-screenshot” and “NO vision-as-primary-palette
— the palette is 100% CSS-derived.” So an Instagram-only founder
structurally cannot populate brand_style_card. The ternary always falls to the
raw column. The short line isn’t a summariser — it is a placeholder only the website
path can ever reach. Rounds 2 and 3 were both Instagram-only, which is why this surfaced now.
Retreat and workshop hosts — the launch ICP — are disproportionately Instagram-first.
So the answer to “should it be bullet points?” is narrower than it looked. The design already
says what the row should be, and it is not a paragraph or a bullet list — it is a
one-line summary, with the full detail living in the (sound) and (look)
sheets behind it. The build honours that on one path and ignores it on the other. The missing
piece is a synthesis step, not a formatting choice.
per-screenshot vision notes
│
▼
dedupeNearIdenticalNotes() ← #2988 interim fix: collapses near-duplicates
│
▼
.join('\n\n') ─────────────────► brain_profile.custom_instructions / visual_tone
│ │
│ ┌───────────────────┴───────────────────┐
│ ▼ ▼
│ hasStyleCard === true hasStyleCard === false
│ (website crawl ran) (Instagram-only)
│ │ │
│ ▼ ▼
│ short canned line ✓ raw column dumped ✗
│
└── MISSING: a synthesis pass that turns N notes into ONE summary line
+ keeps the full text for the sheet behind the door
Worth separating, because the three need different fixes and only one is a genuine data loss.
| What it looks like | What it actually is | Verdict |
|---|---|---|
“What you sell” shows 5, then view all 10 → |
All 10 offerings are stored. PLAYBOOK_LIST_PREVIEW_LIMIT caps the preview at 5 with a disclosure (PlaybookPage.tsx:739). But the disclosure leads nowhere complete — see the row below. |
Count disclosed, content not |
Offering description / details visibility |
Corrected in Round 5 — narrower than first stated. PlaybookDepthSheet.tsx:107 defaults readOnly = false, and the /brain call sites (PlaybookPage.tsx:1673,1695) never pass it — so the signed-in founder does see both fields behind view all 10 →, as editable textareas. The gap is the read-only branch (BrainFocusPanel.tsx:166,199, readOnly={isGuest}): guests and shared-preview viewers see name + price + dates only. Separately, exportPlaybook.ts:145 still drops details and dates from the markdown export. |
Real, but guest + export only |
| Branding line ends in “…” | The opposite of truncation — the field is over-full and CSS line-clamps it. Nothing is missing; too much was written. And the clamp is the only thing hiding it: Round 5 opened the (look) door and found HowYouLookSheet’s “THE VIBE” block rendering the same 1,868-char concatenation unclamped. The overflow follows you through the door — there is no synthesis anywhere in the look vertical, row or sheet. |
Symptom of §3 — and worse in the sheet |
| “Where & how you operate” is empty | Bali appears at least twice in the Round 3 raw extraction — in the IG bio (“Art as expression of the soul ☉ Bali”) and in a vision description (“natural settings (Bali)”) — yet the field is empty. The mapper is not missing — see below. | Open — two candidates |
organize-content.ts:113, “location: city / region /
country exactly as written”), the parser keeps it
(organize-classify-output.ts:245), the mapper proposes it
(organizeCleanToProposals.ts:156-157, pushBusiness('location', location)),
and the page renders it (PlaybookPage.tsx:714-717). So an empty field is exactly one
of two things: (a) the classify pass never extracted it — ☉ Bali is
an emoji-decorated bio fragment and the prompt gives no example of that shape; or (b) the
proposal was generated and the founder never tapped ✓ Use — this is a
confirm-spine, nothing reaches brain_profile until confirmed, and an unconfirmed
proposal is indistinguishable from a missed one after the fact.
These are cheaply distinguishable: run one read and look at whether a location
card appears in the composer before confirm. Appears → confirm-UX problem.
Doesn’t → one added prompt example fixes it.
visual_style field, which maps to the brand-look column. Nothing lifts a location out
of an aesthetic description, and nothing should.
Three other extracted facts are genuinely never stored:
| Fact | Why it vanishes | Verdict |
|---|---|---|
linktr.ee/AmbreStudio |
organizeCleanToProposals.ts:75-78 — isRealUrl requires /^https?:\/\//i. Instagram displays bio links without the scheme, so the link fails the test and is silently dropped. Compounding: the screenshot path’s contact_info shape is email/phone/address only — no socials key at all, unlike the website path. |
Bug — two causes |
| Bilingual IT/EN | Zero hits for language anywhere in src/types/brain.ts. Never extracted, nowhere to put it. |
No field exists |
| 343 posts / 7,184 followers | Actively excluded — _shared/brain-prompts.ts:359-360 instructs “do NOT add UI chrome (likes, timestamps, button labels)”, and follower counts read as chrome. |
Deliberate — worth re-confirming |
· — where a price
would go. I checked the raw extraction before calling this a defect — this Instagram profile
genuinely publishes no prices, so an empty price is honest, not lost.
/brain actually mounts. Round 5 verified the sheet mounts editable on the
founder’s own page, so she does see both fields. The real gap: the guest / shared-preview
read-only branch omits them, and the markdown export drops details + dates.
Still worth fixing, but smaller than first filed.isRealUrl demands https://; IG renders bio links without
it. Separately, the screenshot path’s contact_info has no socials slot at all, so
even a well-formed link would have nowhere to land.hasStyleCard === true branch and likely look fine — which is precisely why the
business matrix needs widening.