# DO-008 — Ambient AI (Replace Chat-Panel Pattern)

**Date:** 2026-07-02
**Status:** Locally binding via OSCR-
**Supersedes:** the SheetMin / SheetFull / BackgroundNav pattern from
Phase 7 (see `pact/assistant/state-machine.ts` v0.1.0).

---

## Decision

Oscar replaces the modal chat-panel pattern (SheetMin / SheetFull /
BackgroundNav / 4-state machine) with an **ambient AI pattern**:

- The input bar at the bottom of every page **is** the chat.
- A status pill above the bar shows live progress during long operations.
- Toasts at the top of the screen surface completed AI work, errors,
  and suggestions.
- The AI never overwrites the user's text without explicit consent.
  All suggestions are drafts; the user taps **Apply** to accept.

The modal chat panel is preserved for desktop only (right-side
conversation drawer) and is hidden on viewports < 768px.

## Why

Operator feedback (2026-07-02 17:21 UTC, 17:55 UTC, 18:02 UTC):

> "the close button didn't work either"
> "instant reaction so the user knows something is happening. dead space
>  seems broken"
> "if ai is aware of what page is open and what's been done on that page
>  they can answer and communicate through the input bar and toast system"

Three concrete failures of the chat-panel pattern we observed on a
360x640 mobile viewport:

1. The SheetMin panel covers 55% of the screen. The user can't see
   the page they're asking about while the chat is open.
2. The state-machine `tap-collapse` mapped to a `background-nav` icon
   (a tiny floating bubble). Users thought the close button was broken
   because the chat disappeared and the icon was too small to notice.
3. The "Apply" pattern was missing — the AI overwrote the Notes body
   in place as it streamed. If the user changed their mind, the
   original was gone.

## The architecture

| Surface | Mobile | Desktop | Purpose |
|---|---|---|---|
| Input bar | bottom, always | bottom, always | Type + send a question, see live status |
| Status pill | above bar, during long ops | above bar, during long ops | "Rewriting… 12s" + pulsing dots |
| Toast (result) | top, slide-in, 5s | top, slide-in, 5s | Completed work with Apply/Discard buttons |
| Toast (error) | top, persistent until × | top, persistent | "Couldn't reach Oscar" + Retry |
| Toast (info) | top, 3s | top, 3s | "Brain downloaded" etc. |
| Conversation drawer | hidden | right-side, slide-in | Last 6 turns of Q&A history |

## The Apply pattern

Every AI action that would change user content follows the contract:

1. The AI produces a **draft**.
2. The user's content is **not modified** during streaming.
3. On completion, a toast appears with the draft summary.
4. The user has three choices:
   - **Apply** — replaces the user's content with the draft.
   - **Preview** — shows the full draft in a modal/expandable toast.
   - **Discard** — dismisses the toast; the user's content is unchanged.
5. The original content is always available via the standard undo
   action for at least 30 seconds after Apply.

## Context propagation

`useAssistantContext()` hook lets any view register its state with
the AI. The hook is called in `Blog`, `Surveys`, `Settings`, etc.
The registered context is included as a system-prompt prefix in every
`askOscar` call:

```ts
useAssistantContext({
  page: '/notes/123',
  summary: 'Blog post about the beach trip',
  selectedText: editorSelection,
  draft: draft, // the form state
});
```

This is how the AI knows what "this" means when the user types
"rewrite this casually" — `this` is the current `draft.body` and
the current page is `/notes/123`.

## Cancellation

- The × on the status pill **stops waiting**. The Ollama request is
  abandoned (not actually cancelled — Ollama has no AbortController on
  the server side; the request finishes in the background and is
  discarded by the client when it returns).
- Cancellation is explicit. A long op that completes in the background
  is silently dropped — it does not update the UI after the user has
  moved on.

## FVW alignment

- FVW v8 §35 (Contextual Surfaces) — the AI surfaces are contextual to
  the page the user is on. The Apply pattern is a §35 commitment:
  AI never overwrites, it suggests.
- FVW v8 §40 (Multimodal Capture) — the input bar is the §40 capture
  surface. The status pill is the §40 progress feedback.
- FVW v8 §15.3 (Sovereignty) — ambient AI runs on operator-owned
  infrastructure (the VPS). The browser-side 0.5B brain is also
  operator-controlled (downloaded once, cached locally).

## Open work

- `src/assistant/ambient.ts` — context hook
- `src/assistant/InputBar.tsx` — 3-mode state machine
- `src/assistant/StatusPill.tsx` — live progress display
- `src/assistant/ResultToast.tsx` — apply-pattern result toast
- `src/assistant/ConversationDrawer.tsx` — desktop history
- Update `Blog`, `Settings`, `Surveys` to use the ambient pattern
- Delete `SheetMin`, `SheetFull`, `BackgroundNav`, `state-machine.ts`
- New tests for ambient context + status pill + toast apply/discard
- Update `pact/dna/copilot-style.md` with the ambient patterns
- Append to `pact/llm-tier-research.md` with the §8 lessons

## Acceptance criteria

- [ ] Input bar shows live progress with elapsed seconds
- [ ] User can type a follow-up question during a long op
- [ ] × on the status pill cancels the in-flight request
- [ ] AI never overwrites user content without Apply
- [ ] Toasts stack, max 3 visible, swipe to dismiss
- [ ] Mobile (< 768px) never renders the conversation drawer
- [ ] All 222 existing tests still pass
- [ ] On 360x640 viewport, the bar+status pill cover ≤ 25% of height
