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>
140 lines
9.1 KiB
Markdown
140 lines
9.1 KiB
Markdown
# 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 P0–P4 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.
|