Clean successor to V3 (forge: lilith/atlilith). Seeded from local Mac working tree at ~/Code/@projects/@cocottetech/. node_modules and build artifacts excluded via .gitignore. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
17 KiB
cross-surface-fanout — "apply to all" as the canonical management interaction
Goal
The primary way Quinn manages her surfaces is through conversation in CocotteAI chat, not by opening per-surface screens. The keystone interaction is cross-surface fanout: Quinn states a direction once ("more tour-focused, less local, hike rates 15%"), Cocotte interprets, drafts per-surface adaptations, presents one approval card, and dispatches across N surfaces in parallel.
This brief defines:
- the conversational grammar of cross-surface dispatch ("apply to all escort," "everywhere except Eros," etc.)
- the per-surface adaptation engine (same logical change → surface-specific render via
personas.facets) - the cross-surface approval card (one decision, N actions)
- the progress + reconciliation UX (live streaming, retry-failed-only, partial-success)
- the rollback model when a fanout goes wrong
Subsumes / extends brief H §H4 (multi-surface approval card sketch) and reframes surface-tryst.brief.md — every per-surface brief now declares whether actions on it are participants in fanouts.
Recommended read order: 00-system-voice.md → A-chat-surface.brief.md → H-recurring-chores.brief.md → this brief → surface-tryst.brief.md §14.
§1 Why conversation, not screens
The 8-screen Tryst set (tryst-profile-editor.screen.md, etc.) gives the impression that Quinn opens those screens to manage Tryst. She doesn't. Per Quinn's stated workflow (2026-05-18):
- Quinn's #1 time-cost is per-platform repetition of the same logical change across 12+ directories + 11 content surfaces. Editing each in turn — even with great screens — recreates the time-cost CocotteAI is built to eliminate.
- Conversation lets Quinn state the intent once, in natural language; Cocotte does the per-surface translation; Quinn approves once.
- Screens still matter — as inspectors ("show me Tryst as a client sees it") and as emergency-edit fallbacks ("Cocotte got that wrong, let me fix it manually"). But they are not the canonical path.
The corpus should reflect this hierarchy. Every operate-on per-surface brief gets a §14 declaring its participation in fanout; every screen gets a header reframing its role.
§2 The conversational grammar
Quinn issues fanout commands in natural language; ai-copilot parses + routes. Recognized patterns:
§2a Audience selectors
| Phrase | Resolved set |
|---|---|
| "all escort platforms" / "all directories" / "all booking platforms" | All active N2 surfaces (per O) |
| "all content platforms" / "all socials" | All active N1 surfaces |
| "everywhere I'm active" | Every connected surface across N1+N2 (excluding pending/blocked) |
| "the tier-1 directories" / "the anchor surfaces" | Per-persona-config flag anchor=true (Quinn-marked) |
| "X, Y, Z" / "Tryst and TS4R" | Explicit list |
| "everywhere except X" / "everywhere but Eros" | All - X |
| "high-tier only" | Per-persona-config flag tier='high' |
| "where I'm verified" | Surfaces where verified=true on persona facet |
| "my bookings stack" / "directories where I bump" | N2 surfaces with active H1 policies |
Cocotte echoes the resolved set in the approval card: "Applying to 12 directories: Tryst, TS4R, Slixa, Eros, Seeking, PrivateDelights, TSEscorts, AdultSearch, AdultLook, EroticMonkey, SkipTheGames, MegaPersonals. Excluded: Eros (per your filter)."
§2b Action verbs
| Verb family | Examples | Per-surface adaptation |
|---|---|---|
| Profile copy | "rewrite my bio more playful" / "drop the corporate line" / "say I'm based in SF, touring Berlin Oct" | About-me text drafted; per-surface adapted to char limits + tone (persona facet) |
| Rates | "hike rates 15% everywhere" / "set incall 1h to $400 across directories" / "match my Tryst rates on TS4R" | Tabular field updates; per-surface schema differences handled |
| Photos | "set my hero to the red dress" / "remove the office photo from everywhere" | Per-surface slot mapping; photo asset shared by reference |
| Tours | "I'll be in Berlin Oct 3–7" | H §H3 multi-surface tour fan-out; existing pattern |
| Visibility | "pause all my bumps" / "vacation mode through Friday" | H §H1 policy fan-out |
| Blocklist / safety | "block @felix everywhere" / "no real-name mentions anywhere" | K §K1–K2 entries fanned with per-surface scope |
§2c Constraint qualifiers
Quinn often pairs commands with constraints Cocotte must honor:
- "but keep Eros wording corporate" — surface-specific override despite a global change
- "draft only, don't push yet" — approval-card surfaced but stays in pending until separate approve
- "if it pushes the price above my Tryst tier cap, skip Tryst" — per-surface conditional
- "anonymize names" — applies the K2 phrase-blocklist as part of the draft
Cocotte's parsing surface (per voice.md) handles these as modifiers on the primary verb; the approval card shows the resolved per-surface plan including any skipped/conditional rows.
§3 The per-surface adaptation engine
Same logical change ≠ same per-surface render. Adaptation is driven by personas.facets[surface_id] config, which holds:
{
voice_lean: 'hearth' | 'working' | 'plain', // per voice.md §V4
char_limit_about: number, // surface-specific
char_limit_caption: number,
banned_phrases_inherited: string[], // K2 personal + surface-specific
rate_schema: 'tryst' | 'ts4r' | 'slixa' | 'simple' | 'tier-table',
posting_mode: 'real-api' | 'web-session' | 'manual-only',
nsfw_allowed: boolean,
cross_post_to: string[], // implicit fanout target hints
anchor: boolean, // marks tier-1 surfaces
tier: 'high' | 'mid' | 'long-tail',
// ... surface-specific extensions
}
When Quinn says "rewrite my bio more playful":
- Cocotte's
strategistreads the active facets across resolved-audience surfaces. - For each surface, Cocotte composes a per-surface draft: same intent, surface-specific voice + char limit + banned-phrase filter.
- Tryst (hearth-leaning, 500-char limit, "real name" banned) gets one draft.
- Slixa (working-leaning, 250-char limit) gets a tighter draft.
- TSEscorts (~2800 chars, ✦ spacers convention) gets a longer draft with formatting.
The approval card surfaces all N drafts in one view (collapsed by default; tap a surface row to expand its draft for inline edit).
§4 The cross-surface approval card
Anatomy (extends approval-card.screen.md kind=cross-surface-fanout):
┌─────────────────────────────────────────────────┐
│ ┃ ▢ More playful bio across 12 directories │ stakes color + header
│ ┃ Audience: all escort directories │ resolved audience echo
│ ┃ Excluded: Eros (per your filter) │
│ ┃ │
│ ┃ Per-surface drafts: │
│ ┃ ▶ Tryst · 432 chars · hearth · OK │ one row per surface
│ ┃ (tap to expand draft + edit) │
│ ┃ ▶ TS4R · 380 chars · hearth · OK │
│ ┃ ▶ Slixa · 248 chars · working · OK │
│ ┃ ▶ Seeking · 410 chars · working · OK │
│ ┃ ▶ PD · 412 chars · hearth · ⚠ phrase │ K2 phrase hit
│ ┃ ▶ TSEscorts · 2780/2800 chars · hearth · OK │
│ ┃ ▶ AdultSearch · 2790/2800 chars · hearth · OK │
│ ┃ … 5 more │
│ ┃ │
│ ┃ One surface flagged a banned phrase │ per-row issues summary
│ ┃ ⓘ "real name" in PD draft — strategist │
│ ┃ suggested a replacement │
│ ┃ │
│ ┃ [ Edit per-surface ] [ Set aside ] [ Apply ] │
└─────────────────────────────────────────────────┘
States (operate-on cross-surface approval card):
- Default — drafts ready, no issues.
- Issues flagged — ≥1 surface row shows ⚠ chip; banner summarizes.
- Editing per-surface (tap "Edit per-surface") — opens a sheet listing all surfaces; per-row inline-editor; "Done" returns to card.
- Editing a single surface inline (tap a row's chevron) — that row expands to show full draft + edit; collapse to compact.
- Mid-apply — Apply tapped; card animates to a progress strip: per-surface rows with spinners.
- Apply complete — collapses to receipt: "Updated 11 of 12. PD failed (rate-limited)."
- Partial failure — failed-rows surface with retry chip + "Retry failed only" affordance.
- Apply cancelled mid-flight — per brief M: in-flight per-surface actions complete (or abort where safe); successful surfaces stay; failed/in-flight surfaces revert if rollback supported.
- Conflict (someone changed Tryst from another device mid-fanout) — per brief M §M7: 3-way diff per affected surface; surface-by-surface resolution.
§5 Progress + reconciliation
Apply triggers parallel dispatch to N specialists. Quinn sees:
- Live progress strip: per-surface rows with status glyphs (⌛ in-flight / ✓ done / ✗ failed / ⏸ skipped). Updates every ~500ms via SSE from platform.api.
- Per-surface duration: visible after completion (helps Quinn understand which surfaces are slow / problematic).
- Receipt collapse: when all rows complete, card collapses to a single chat receipt bubble; long-press to re-expand the full per-surface result.
Per-surface failures don't roll back successes by default. Quinn explicitly chooses:
- Retry failed only — re-attempt just the failed rows.
- Rollback successful — undo the changes on the rows that succeeded (only available where the action has a
rollbackimplementation per_engineering-surface-adapter-container.mdLayer 6). - Accept partial — mark partial-state as final; failed rows stay un-updated (most common; Quinn fixes failed surfaces individually later).
§6 Stakes inheritance
Cross-surface fanout inherits stakes per the highest-stakes participating surface, per voice.md + F §F1:
- Bumping availability across 12 directories: low stakes (each surface is low; multiplied is still low).
- Rewriting profile copy across 12 directories: medium stakes (reputation-shaping; mistakes are reversible but visible).
- Changing rates 15% across 12 directories: high stakes (financially material; harder to walk back; clients may have seen the change before rollback).
- Tour announcement (H §H3): medium to high depending on tour duration + visibility.
The approval card uses the resolved stakes for its color band + confirmation friction (high-stakes gets the M §M3 high-stakes-interrupt pattern).
§7 Conversational entry points
These all route into the same fanout machinery:
- Direct ask: "rewrite my bio across all escort platforms" → strategist drafts → approval card.
- Indirect repositioning: "I'm going tour-only" → ai-copilot proposes a plan (multiple fanout actions chained: change rates + rewrite bios + adjust home-cities + pause local-only surfaces) → each step is its own approval card.
- Quick correction: "you said 'no rush' — change that to 'unhurried' everywhere" → micro-fanout; minimal card.
- Voice command: same grammar over voice (per voice-input-settings.screen.md).
- From a single surface's chat: Quinn in direct-conversation with
bookings-trystsays "do this on TS4R too" → ai-copilot promotes the conversation context, surfaces a 2-surface fanout card.
§8 What's NOT fanned out
Some actions are inherently per-surface:
- Tryst home-cities (per surface-tryst §7): unique tier-gated cadence model; doesn't generalize.
- OF subscription pricing tiers: structure unique to OF; no cross-surface analog.
- Sumsub KYC re-up: per-surface verification; can't be "applied to all."
- Connect / re-auth: per-surface auth-grant; never a fanout.
Per-surface brief §14 (introduced for surface-tryst) declares which actions are fanout-participants and which are per-surface-only.
§9 Failure dictionary (cross-surface)
| Failure | Behavior |
|---|---|
| Some surfaces fail, others succeed | Receipt shows partial; Quinn picks retry/rollback/accept |
| Audience resolves to 0 surfaces (e.g. "apply to escort platforms" but Quinn isn't connected to any) | Pre-card warning: "I don't see any escort platforms connected yet. Connect one first?" Routes to surfaces-settings.screen.md |
| All surfaces fail (e.g. backend down) | Per M §M2b: degraded banner; nothing applied; queued for replay |
| Quinn cancels mid-apply | In-flight completes or aborts (per surface); successful rows stay; receipt shows interrupted state |
| Specialist-degraded mid-apply (one specialist offline) | That specialist's row stays in ⏸ skipped state; other rows continue; degraded specialist surfaces M §M2a banner |
| Captcha required on N specialists during fanout | HITL: Quinn pings to solve one challenge per affected surface, sequentially. Cocotte attempts to batch / dedup if same captcha; defer to L3 captcha-solver capability |
| Rate-limited across multiple surfaces simultaneously | Cocotte spreads dispatch over time (jitter); banner notes "spreading over N minutes" |
§10 Generalization across surface categories
Fanout works across N1 (content) + N2 (directories) + N7 (reviews-aggregators — flag-fanout) + N6 (screening — submit-fanout). NOT across N5 (commerce — money-touching) or N8 (bio-links — synced separately).
Per-surface brief §14 declares participation:
- Operate-on surfaces (N1, N2): typically full fanout participants — bumps / posts / profile / rates.
- Consume-from surfaces (N6 screening, N7 reviews): partial — only submission actions fan out (submit-to-blocklist, flag-as-defamatory).
- Connect-only surfaces (N5): no fanout.
States to design (for an iOS-level designer / engineer)
- Cross-surface approval card — all 9 states from §4 above.
- Per-surface expanded inline editor (nested within the card).
- Live progress strip animation.
- Partial-success receipt + retry-failed UI.
- Rollback confirmation (high-stakes pattern).
- 3-way conflict resolution per surface (M §M7 inheritance).
- Conversational entry points: the chat-side text Cocotte uses to confirm a parsed fanout intent before rendering the card ("Got it — bio change across 12 directories. Drafting...").
- Voice-mode fanout: how does Cocotte read the per-surface plan over TTS? (Lean: summary only, "12 drafts ready, want to see them?" — don't read all 12 aloud.)
Open questions
- Per-surface adaptation quality bar: how aggressively should Cocotte rewrite per-surface vs minimally transform? Lean conservative — preserve Quinn's intent; surface adaptations as suggestions she can override per-row.
- Cross-surface undo TTL: how long after fanout completion can Quinn say "actually undo that"? Lean 24h for low-stakes, 1h for high-stakes; configurable per brief I trust-panel.
- Streaming approval review: should the per-surface drafts appear progressively (faster surfaces first) or all-at-once? Lean all-at-once for cognitive cohesion; streaming makes the card unstable.
- Conflict with H3 tours: a fanout that touches profile-text mid-tour might overlap with the tour-specific copy. Resolution rules need to be explicit.
- Org-level fanout (W brief): when Quinn admins Demimonde + another member, can a fanout target the member's surfaces too? Org-overlay decision; defer.
Related
- 00-system-voice.md — voice register for per-surface adaptation; "more playful" parsing.
- A-chat-surface.brief.md — primary surface where this lives.
- H-recurring-chores.brief.md §H4 — multi-surface approval card; this brief extends.
- approval-card.screen.md — cross-surface variant of the approval card.
- L-specialists-fleet.brief.md — specialists dispatched in parallel.
- I-audit-trust-replay.brief.md — each per-surface dispatch is its own
agent_actionsrow; the fanout itself is a parent row withtarget_kind=cross_surface_fanout. - M-error-degraded-modes.brief.md — partial-fail / cancel / conflict patterns.
- surface-tryst.brief.md §14 — Tryst-specific fanout participation.
- _engineering-surface-adapter-container.md Layer 6 — per-action rollback support drives the rollback-successful affordance.
Out of scope
- Org-level fanouts spanning multiple members' surfaces — W brief territory.
- Predictive "you probably want to fan this out" prompts — defer to a future strategist enhancement.
- Cross-tenant fanout (a coop-shared template applied across multiple providers) — Y brief marketplace; out of P0/P1.