cocottetech/@platform/codebase/@features/ai-copilot/docs/context-switch.flow.md
natalie 1b719e1fd7 chore(bootstrap): initial V4 commit
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>
2026-05-18 08:11:41 -07:00

140 lines
9.1 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Flow — context switch (personal ⇄ Demimonde) — P5+ pre-design
> **P5+ pre-design.** Org overlay ships P5+ per plan. This flow defines what the switch *must look like* when it lands so P0P4 schema decisions (org-aware `user_id` + `org_id` from day one) stay forward-compatible. Pair with [W org overlay](./W-org-overlay.brief.md) §W2.
## Goal
Quinn moves between her personal scope and Demimonde org scope without losing her thread with ai-copilot. Drawers re-render against the new scope, the voice register shifts, and held cards block on mixed-scope conflicts before they commit.
## Triggers
- **Top-bar context chip** (W §W2). Tap the chip left of the settings gear (`Quinn ▾` or `Demimonde / Quinn ▾`) → switcher sheet opens.
- **Voice** (W-Q3, `[nice-to-have]` leaning supported). "Switch to Demimonde" / "switch back to personal" → ai-copilot answers with a vocal confirmation ("Switching to Demimonde — confirm?") before executing. Confirmation is required because mis-scoped actions are a high-stakes class of mistake.
- **Settings → S1 → org row** → opens roster, with a "switch into this scope" affordance.
## Step 1 — Switcher sheet open (W §W2 spec)
Sheet animates in from bottom. Three-row picker:
```
Switch context. Each scope is what Cocotte sees and acts on.
● Quinn (personal) [current]
Your own work. Persona, journals, your prospects.
○ Demimonde / Quinn (member)
Org-shared work. Prospects routed to the org,
shared content guidelines, org-wide audit.
○ Demimonde / org-wide (admin) [only if admin]
Full org view. Aggregate of all members.
Recent: switched from Demimonde 2h ago
```
The org-admin row only renders if Quinn's role on Demimonde is `Admin` (per W §W3). Non-members see permission-denied (see Edge cases).
## Step 2 — Scope choice
Quinn taps a row. Sheet does not auto-dismiss — a `Switch` confirm button at the bottom commits the choice. Tap outside cancels. This single confirm prevents fat-finger scope flips (especially under voice trigger).
## Step 3 — Every drawer re-renders against the new scope
Switch is **state-only**, no data moves (W §W9). On commit:
- The active chat surface stays mounted; thread scroll position preserved.
- Every open drawer (B1 content, B2 assets, B3 prospects, I audit, T analytics, U search, R tours) re-queries platform.api with the new `(user_id, org_id)` filter and re-renders in place.
- Personal-only surfaces (persona B5, journal Q, kill-switch K5, S2/S8 settings) ignore the switch — they stay personal regardless and hide the scope badge (W §W1).
- Org-only surfaces (org roster, org-wide policies, org-wide audit) only mount under an org scope; they unmount on switch-to-personal.
- The top-bar chip animates label + subtle background-material shift (W-Q4 lean: not a full color change).
## Step 4 — ai-copilot context-provider re-scope (W-Q2)
ai-copilot's chat memory **continues** — the thread Quinn was in stays open, prior turns remain visible and addressable. What changes is its **context providers**:
- The `prospects` provider re-binds to org-scoped prospects.
- The `audit` provider re-binds to the org's `agent_actions` slice.
- The `policies` provider switches from per-user defaults to Demimonde's org-wide policies (with member-overrides applied).
- Persona provider stays personal (persona is identity, never org-asset).
- Active retrieval is invalidated; the next turn re-grounds against the new scope.
If Quinn references prior-scope content in the next turn ("send that draft we just looked at"), ai-copilot resolves the reference against thread history but checks whether the referenced object is reachable in the new scope. If not, it surfaces a mixed-scope warning (Step 6).
## Step 5 — Voice register shift (W §W5)
Under Demimonde, ai-copilot's system-prompt receives an org-context addendum: *"You're acting on behalf of Demimonde. Decisions affect the org. Speak with that awareness."* The audible effect:
- Slightly more formal. Fewer hearth metaphors ("the pantry", "simmering" recede).
- "The org" framing replaces "you" in some contexts ("the org has three open prospects").
- Same TTS voice; prosodic delivery unchanged. Register shift is lexical, not vocal-character.
- Quinn perceives the change by ear — the register itself is a redundant signal that the chip is correct.
Returning to personal restores the full hearth/working/plain gradient per [voice](./00-system-voice.md).
## Step 6 — Mixed-scope warning if held card is from prior scope (W §W2)
If Quinn has an approval card open (mid-decision) when she initiates a switch, the card greys and a banner anchors at the top:
> This card is org-scoped. You're now in personal. Continue under org or cancel.
Plain register, per W §W5's high-stakes copy. Two affordances:
- **Continue under org** — reverts the context chip to org, returns Quinn to the card's native scope, switch is abandoned.
- **Cancel card** — discards the held action, commits the switch.
There is no "approve under wrong scope." That class of mistake is prevented structurally.
## Step 7 — Confirmation receipt
Once the switch commits and drawers settle, ai-copilot drops a single-line ambient message into the chat:
> Now acting on behalf of Demimonde. Drafts and decisions reflect the org. Say `switch back` to return to personal.
Working register. On switch-to-personal:
> Back to personal. Org work is paused on the org side.
If the switch was voice-triggered, this same line is also spoken via TTS (per W-Q3's vocal confirmation lean).
## Step 8 — Audit row writes
A single row is appended to `agent_actions`:
- `action_type = 'context_switched'`
- `user_id = quinn`
- `org_id = NULL` (personal target) or `org_id = demimonde` (org target)
- `outcome_json = { from_scope, to_scope, trigger: chip|voice|settings, device_id }`
- `created_at = now()`
The row writes under the **target** scope so org-admins see when members enter and leave Demimonde. Personal→personal noise is suppressed.
## Edge cases
- **Permission denied** — Quinn taps a row for an org she isn't a member of (e.g. deep-linked from a stale notification). Sheet shows the W in-the-wild copy: *"You're not a member of this org. Ask an admin to add you."* Plain register. Row is non-tappable; no audit row written.
- **Cross-context prospect handoff (W §W6)** — moving a personal prospect into Demimonde is its **own sub-flow**, not a side effect of context-switching. The switcher sheet does not migrate data. Handoffs run through the prospect drawer's "Share this prospect with Demimonde" affordance (B3), which has its own confirmation sheet and audit shape.
- **Cold-launch restores last-active scope (W-Q1)** — on app open, the chip is initialized to whatever scope Quinn was in when the previous session ended. New installs default to personal. No mid-flight switch is replayed.
- **Org membership revoked mid-session** — if Demimonde admin removes Quinn while she's in org scope: WebSocket push (per [cross-device-handoff](./cross-device-handoff.flow.md) §State sync) forces a scope-reset to personal. ai-copilot drops a plain-register line: *"Your access to Demimonde was removed. You're back in personal."* Any held org-scoped card is discarded with a toast.
- **Voice-trigger ambiguity** — "switch to demi" matches Demimonde fuzzily; ai-copilot confirms the full name before executing. No silent fuzzy-match scope changes.
- **Active org-scoped TTS readback in progress** — TTS finishes the current sentence under prior-scope voice, then the register shifts on the next utterance. Mid-sentence register flips sound stilted.
## Voice / TTS
Cocotte's acknowledgment line on entering org context (per W in-the-wild copy, working register):
> Now acting on behalf of Demimonde. Drafts and decisions reflect the org. Say `switch back` to return to personal.
On voice-triggered switch, the pre-commit confirmation (plain register, because mis-scoped action is high-stakes):
> Switching to Demimonde. Confirm?
TTS prosody is unchanged across scopes — single voice, lexical register shift only. Per [voice](./00-system-voice.md) V8 §6 read-aloud check: both lines pass as prose, not UI labels.
## Related
- [W org overlay](./W-org-overlay.brief.md) — parent brief; §W2 context chip + switcher is the source spec.
- [S settings-ia](./S-settings-ia.brief.md) §S1 — org switcher entry point (alternate trigger).
- [L specialists-fleet](./L-specialists-fleet.brief.md) §L1 + W §W4 — specialist scope (personal / scoped / org-only) determines which forks re-bind on switch.
- [I audit-trust-replay](./I-audit-trust-replay.brief.md) — `action_type='context_switched'` row + scope filter on the audit drawer.
- [A chat-surface](./A-chat-surface.brief.md) + W-Q2 — chat memory continuity across switches.
- [D onboarding](./D-onboarding.brief.md) — org-aware persona stays personal; switch never re-runs the seed interview.
- [cross-device-handoff](./cross-device-handoff.flow.md) — scope state syncs over the same WebSocket channel that powers device handoff.
- [00-system-voice](./00-system-voice.md) §V2 + W §W5 — register shift under org context.