# FOR_CLAUDE.md — how to drive the user's live views

You (Claude) are connected to a **session-based remote-control** MCP. The user has
a browser tab open on one of their session's views; your job is to push the right
content to the right view and bring their attention to it. This file is the
etiquette for doing that well. It's a **living stub** — the view catalog and rules
will grow as more interfaces land.

## The model

- **Everything is session-based.** The user opens `/d/{session_id}` in a browser
  and hands you the `session_id`. Every push tool takes `session_id` as an
  argument and attaches content to **that** session only. Never invent a
  `session_id`, and never act on a session you weren't given (there is, by design,
  no way to list or discover sessions — the id is the only key).
- **The user watches ONE view at a time.** A view is a page (`home`, `announce`,
  `validate`, `classify`, `eb`, `eb_xics`, `mirror`, `network`, `notepad`, `journal`, `dashboard`). Pushing content updates
  that view's cached state and updates
  any open tab already on it live — but if the user is looking at a *different*
  view, they will not see your push until their tab navigates there.

## The golden rule: **push, then navigate — every single time**

Treat a push and its `navigate_to` as a **single, inseparable action**. Whenever
you push data to a view, your **very next tool call** must be
`navigate_to(session_id, <that view>)` so the user's tab actually switches to what
you just populated. The user's tab never moves on its own — a push without a
navigate leaves them staring at the previous page, and your result is invisible.

```
set_structure_validation(session_id, ... )     # 1. populate the validate view
navigate_to(session_id, "validate")            # 2. bring the user to it — ALWAYS
```

If you called a push tool and your next call isn't `navigate_to`, you've stranded
the user on the wrong window. No exceptions, no "navigate once at the end."

Say it in chat too, briefly: "I've laid out the MS/MS on your Validation view and
switched you to it."

## Say why on the page itself: **annotate_view**

Every view has a commentary banner at the top. Fill it with
`annotate_view(session_id, page, why, what?)` so your reasoning rides *with* the
data, not only in chat — the human reads why you put this here and what to look at
right where they're looking. Do it between the push and the navigate:

```
show_eb_xics(session_id, task_id, row_ids=[...])          # 1. push the data
annotate_view(session_id, "eb_xics",                      # 2. say why + what to see
              why="These three ions co-elute at 4.2 min — likely one compound.",
              what="Check the peaks line up; the [M+H]+ trace is blue.")
navigate_to(session_id, "eb_xics")                        # 3. bring the user to it
```

`why` is the finding/decision/ask; `what` (optional) points at what to look for.
It's latest-wins and survives live data re-pushes, so re-annotate whenever you
change what a view is showing. Keep it to a sentence or two.

## Building a narrative: the Journal

`journal` is the one **append-only** view — every other page is latest-wins, but
the Journal *accumulates*. It's for telling the story of an analysis: interleave
prose with snapshots of the evidence behind it.

- Write the connective prose with `journal_note(session_id, markdown)` (a small
  safe markdown subset — headings, bold/italic, `code`, `- ` bullets, links).
- Pin the evidence by passing `pin=True` to a `show_*` tool — it pushes to its own
  view **and** appends a frozen snapshot cell to the Journal. E.g.
  `show_eb_xics(session_id, task_id, row_ids=[...], pin=True)`.
- Pinned cells are **snapshots**: they don't change when you later re-push live
  data, so the narrative reads as it happened. `clear_journal` starts a fresh
  thread.

It is a narrative of typed widgets, **not** a code notebook — there is no code
cell and nothing you write is executed.

## Composing a dashboard: the Dashboard grid

`dashboard` is a fixed **4×2 grid** you arrange — the journal is a *stream*, the
dashboard is a *board* you keep several live views on at once. Latest-wins per
slot; other slots are untouched.

**Geometry.** 8 slots, numbered **row-major**: slots `0 1 2 3` are the top row,
`4 5 6 7` the bottom. Reading order is left-to-right, top-to-bottom.

**Knowing what goes where — don't place blind.** The layout is yours to decide,
but stay oriented:

- **Read before you place.** Call `get_dashboard(session_id)` to see which slots
  are filled (and with what) and which are free (`free[0]` is the next empty one).
  Every place/clear call also echoes the grid map, so you always know the state.
- **Compose deliberately, reading-order = importance.** Put the thing you most
  want seen first in slot 0; fill left-to-right, top-to-bottom. Keep related
  widgets adjacent (e.g. a feature's XIC next to its mirror plot). A short
  `dashboard_place_note` in slot 0 makes a good title/summary for the board.
- **Update in place.** Re-placing into a slot replaces just that widget — refresh
  one panel without disturbing the others.

**How to fill it:**

- Data widget → pass `dashboard_slot=N` to a `show_*` tool (it places the same
  snapshot it would `pin`): `show_eb_xics(..., dashboard_slot=1)`,
  `show_network_component(..., dashboard_slot=2)`, `show_mirror(..., dashboard_slot=3)`.
- Prose → `dashboard_place_note(session_id, slot, markdown)`.
- `clear_dashboard_cell(session_id, slot)` empties one; `clear_dashboard` empties all.
- `pin` (journal) and `dashboard_slot` (grid) are independent — a single `show_*`
  call can push to its own view, pin to the journal, AND fill a dashboard slot.

**Dashboard vs journal:** dashboard = a *simultaneous* board of a few current
views (a cockpit you keep updating); journal = a *sequential* narrative that
accumulates (the story of how you got there). Use the dashboard to monitor,
the journal to recount.

## The views and their push tools

| View (page name) | What it's for | Push with |
|---|---|---|
| `home` | Session overview / board — a heading + freeform labeled cards | `set_session_display` |
| `announce` | A single headline + optional body — status, a summary, "here's what I found" | `set_announcement` |
| `validate` | MS/MS structure validation — spectrum + reference USI + candidate SMILES | `set_structure_validation` |
| `classify` | NPClassifier compound-class breakdown for a SMILES | `classify_structure` |
| `eb` | GNPS2 Everything-Bagel features — feature table, XIC, MS/MS | `prewarm_eb_task`, `list_eb_features`, `show_eb_feature` |
| `eb_xics` | Batch-XIC overlay — several features' XICs (or one m/z across samples) on one chart | `show_eb_xics` |
| `mirror` | MS/MS mirror plot — a query spectrum vs a reference (library hit), matched peaks highlighted | `show_mirror` |
| `network` | Molecular network — a component's nodes/edges, seeds & propagation candidates | `prewarm_network_task`, `list_network_components`, `show_network_component`, `highlight_network_nodes` |
| `notepad` | A shared scratchpad — free text both you and the user can edit | `set_notepad` / `get_notepad` |
| `journal` | An append-only narrative — prose interleaved with pinned evidence (XICs, features, structures, networks) | `journal_note`; `pin=True` on the `show_*` tools |
| `dashboard` | A composable 4×2 grid of widgets you arrange (latest-wins per slot) | `dashboard_place_note`; `dashboard_slot=N` on the `show_*` tools |

`navigate_to(session_id, page)` accepts exactly these page names (`home`,
`announce`, `validate`, `classify`, `eb`, `eb_xics`, `mirror`, `network`, `notepad`, `journal`, `dashboard`) — a **name**,
never a URL. It moves only that session's viewers. Every push tool above has a
matching page name; after you push, navigate to that same name.

## Choosing a view: start simple, then go deep

- **Prefer the simplest view that makes the point.** For a summary, a status, or a
  heads-up, use `announce`. Reserve the richer, specialized views (`validate`,
  `eb`) for when the detail actually earns them.
- **You may push to multiple views**, but **navigate the user to ONE to start** —
  the simplest, most relevant one — and mention the others exist. For example:
  post a one-line `announce` summary ("3 candidates matched"), also lay out the
  full spectrum on `validate`, then navigate them to `announce` first and say
  "open the Validation view for the full spectra." Let them go deeper on their own
  or ask you to switch.
- When in doubt, lead with `announce` (the overview) and offer to drill in.

## Etiquette

- **Navigate deliberately.** Each `navigate_to` yanks the user's tab. Don't bounce
  them between views; move them once, with intent.
- **Latest-wins.** Re-pushing to a view replaces its content. If you're iterating,
  the user sees each update live *if* they're on that view — another reason to
  navigate them there first.
- **Narrate transitions** in chat when you move them ("Switching you to the EB
  feature view now.").
- **Match the view to the data.** Don't force a spectrum into an announcement or a
  paragraph into the validation view.

---

*Roadmap: more views are coming. When a new interface lands it becomes a new page
name here with its own push tool; the same push-then-navigate + start-simple rules
apply. Keep this file in sync with the view catalog.*
