# Agent Activity Panel

> One view of what your agents are doing across every gem, and a classified record of the files they touched.

The Agent Activity Panel does two things: it shows **what your agents are doing**, and it shows **the files they touched**. Both live in one panel, split across two tabs, so you can check on a whole workspace without switching between gems and shards one at a time.

## Opening the panel

There are two ways in, depending on the scope you want:

- **All gems**: click the **diamond icon** at the top (or left end, in top mode) of the [crystal rail](/docs/crystal-rail/). This opens the panel across every open gem.
- **One gem**: click a gem's tile in the rail once to bring it into focus, then click it **again** to open the panel scoped to just that gem.

The panel opens on **agents │ touches**. The agents tab is the live view; the touches tab is the record of what got written where.

## agents

The agents tab gathers each agent's activity and token usage in one place, so you can see at a glance which agents are working, which are idle, and where your tokens are going across the whole workspace.

For a deeper look at how crystl accounts for tokens per turn, including what each plugin and skill costs, see [context load tracking](/docs/context-load/).

## touches

An [isolated shard](/docs/isolated-sessions/) promises that your agent's work is a branch you can read, merge, or throw away. Two kinds of activity escape that promise and leave nothing behind: a write that lands **outside** the worktree (a home dotfile, another project's directory, a global install) has no diff and no undo, and a **credential read** is invisible after the fact even though agents are routinely instructed to go and read `.env.local` for service keys.

Neither is a thing to stop. Both are things you want to be able to look at after an unattended [fan-out](/docs/fanout/) has been running while you slept. So crystl records them, and the touches tab is where you read them back.

### What gets recorded, and what you see by default

Every file an agent **writes** through a tool call that names it is recorded, and so is every **read of a credential path**. Reading anything else produces no entry: this is not a history of every file your agent opened. ("Names it" is doing real work in that sentence — see [what it cannot see](#what-it-cannot-see).)

Each recorded path is then classified, and the tab shows you the classes worth looking at. Labels are terse on purpose — the row is one narrow line carrying the gem, the shard, the action, a count, the tool and an age, so each class says `verb + target` and nothing else:

| Shown by default | What it is |
|---|---|
| **wrote credential** | A write to a credential path — `.env*`, `~/.ssh`, `~/.aws`, `~/.npmrc`, `*.pem`, and the like — **wherever it lives**, including inside the shard's own repo. |
| **read credential** | A read of one of those paths, again wherever it lives. |
| **wrote main checkout** | An isolated shard writing into its own gem's mainline checkout — outside its worktree, and in no branch at all. It will not merge when the worktree merges and will not vanish when it is discarded. |
| **wrote other worktree** | An isolated shard writing into a *different* worktree of the same gem, so the change lands in another agent's branch. |
| **wrote other gem** | A write under a *different* open gem's root. |
| **wrote shared config** | A write through one of the config symlinks crystl manages (`CLAUDE.md`, `AGENTS.md`, `.mcp.json`, `.claude/`) into the gem that owns it. |
| **wrote outside project** | A write anywhere else: home dotfiles, system paths, global installs. |

Two more classes are recorded but sit behind the panel's **show all** toggle:

| Behind show all | What it is |
|---|---|
| **wrote in bounds** | A write under the shard's own root. Fully reversible — it's in the diff, or you discard the branch — so it doesn't need your attention. |
| **wrote scratch/cache** | A write into a scratch or cache location, matched *after* symlinks are resolved, so `/tmp` still counts as scratch despite being a symlink to `/private/tmp` on macOS. |

That split is the whole point. A badge that counts ordinary in-repo edits is a badge nobody believes, so routine work is kept out of the default view rather than out of the record.

**A credential write outranks location, deliberately.** A write to a `.env` sitting inside the project is classified as a credential write, not as ordinary in-bounds work, so it surfaces. Reading `.env.local` is the documented normal workflow and shows up as exactly that; writing `~/.ssh/authorized_keys`, `~/.aws/credentials`, `~/.npmrc`, or a project `.env` is key injection or a silent credential rotation. Without that precedence an in-repo `.env` write would be filed as routine and never seen, which would make the record blindest exactly where it matters most.

### Reading a row

One row per path, aggregated: repeated edits to the same file raise a count instead of flooding the list.

**path · gem · shard · what it did · count · tool · age**

"What it did" is the plain-words class from the tables above, so a row reads as a sentence rather than as a category you have to decode. Two actions sit at the end: **reveal** opens the file in Finder, and **seen** marks the row reviewed.

Colour ranks the surfaced classes against each other:

- **Red — wrote credential.** The top of the scale, wherever the file lives.
- **Amber — read credential, wrote main checkout, wrote other worktree, or wrote other gem.** The rows you most often actually need to look at.
- **Plain — wrote shared config, or wrote outside project.** Surfaced, but not shouting.

The path is shown both as the tool reported it and as it actually resolved, when those differ. That is what makes a symlink escape legible: `worktree/link → ~/.zshrc` reads as the escape it is, while still counting as one row per real destination.

### The badge and the card

A **flag badge (⚑N) below the shard's tab** counts that shard's **unreviewed surfaced** entries. It's tinted to the shard's own color and sits under the tab beside the [↑N merge badge](/docs/isolated-sessions/#from-the-shard-tab), following the same grammar, so a shard that did something worth a look says so without the panel open. **Click the flag** to open this panel on the **touches** tab and read exactly what it counted.

<img src="/docs/shard-badges.svg" alt="The shard bar: a ↑2 merge badge below the isolated opal tab and a ⚑1 touches flag below the jade tab, each in its shard's color, with a legend explaining both." style="width:100%;max-width:660px;border-radius:12px;margin:0.5rem 0;" loading="lazy" />

Reviewed is an explicit action. Opening or scrolling the panel does not clear the badge, because a glance would silently discard the exact count you meant to act on. Click **seen** on a row and it clears; if that same path is touched again afterwards the row goes back to unreviewed, since the count and the last-seen time both changed.

You get **one notification card per shard per app run**, on that shard's first surfaced entry, with a **review** button. After that the shard stays silent. A card per event would be noise you switch off within a day.

### Remote shards say so

crystl classifies paths on **local** shards. A remote shard over SSH reports **remote path coverage unavailable** rather than a count of zero. A zero would read as "this worker touched nothing," which is the one wrong answer this tab must never give. Reveal-in-Finder is local-only for the same reason.

### Three things to know

**It is in memory, and it clears when crystl restarts.** There is no file on disk keeping this. The record is there to answer "what did the fan-out do while I was asleep," and the app stays running across that; quit crystl and it starts empty. Closing a shard or a gem does **not** clear it, though. Finishing with a worker is a normal end-of-quest action and it must not erase what that worker did, so entries from closed shards stay readable.

**It is a record, not a gate.** Nothing here blocks, prompts, or changes what gets approved. The [auto-approve modes](/docs/approval-modes/) keep exactly the meaning they had before, and **all still means all**. This is on purpose rather than a limitation: an approval prompt raised at 2am under **all** is not a gate, because nobody is there to answer it. It just parks the worker until morning and turns an unattended run into a stalled one. Recording is the only intervention that still means something when nobody is watching.

**Only the count leaves the machine.** The bridge carries a shard's unreviewed count and its coverage state to the phone and to `crystl shards`. The paths never go anywhere. That is a deliberate line, not an oversight: a list of the paths your agents read is an index of where your credentials live on this machine. It is excluded from [`crystl report`](/docs/cli/#report) and from `--diagnostics`, which stays a numbers-only health snapshot. Paths are all that is ever recorded, too, never file contents or tool output.

### What it cannot see

Worth knowing, so you read the tab for what it is:

- **Arbitrary shell writes are invisible.** `cp`, `>`, `sed -i`, `npm install -g`, and installer scripts write to unbounded destinations, and a heuristic there would look like coverage while missing most of it. Structured writes are covered: your agent's ordinary write and edit tools, and Codex's `apply_patch` (every file in a patch, checked separately).
- **Credential reads in shell commands are caught**, via a conservative scan for the short list of credential paths, so `cat ~/.ssh/…` and `source .env` do register. Variable indirection and generated paths do not.
- **Only agents running in crystl shards** are recorded. This is not machine-wide.
- **Remote paths** are not classified yet, and say so instead of guessing.

## Related

- [isolated sessions](/docs/isolated-sessions/#the-write-guard): the hook-based guard for supported structured writes, plus the narrower Bash self-destruction guard
- [approval modes](/docs/approval-modes/): the three modes, unchanged by any of the above
- [context load tracking](/docs/context-load/): per-turn token accounting behind the agents tab

---
Source: https://crystl.dev/docs/agent-activity-panel/
