Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 40 additions & 4 deletions docs/attention.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ creation), ties broken by the item's key:
| kind | what | where it comes from | how you act on it |
|---|---|---|---|
| Question | An agent called `attention_ask`, or muster raised one itself, shown as asked by `muster`: a plugin's mention of an in-progress task when you have no chief ([task routing](plugins.md#routing-a-task-to-the-chief)), or an agent-mail message your chief did not act on ([plugins](plugins.md#a-message-the-chief-does-not-act-on)); nothing waits on its answer. | `attention_asks` (migration 0025; muster's own asker, 0028; the message link, 0051) | Tap one of the agent's options, or type an answer. **Dismiss without answering** withdraws it. |
| Session waiting | A mirrored Claude Code **or Hermes** window whose session reports `waiting` (a permission prompt, a question; for Hermes an approval, a clarify question or a sudo password). Shown with **what it waits on** (below), its focus title, its window @ host and its age. | The mirror hub's live window list; nothing is stored. | **Open the session** (the rendered view). A Hermes **approval** on a host with `-hermes-answer` also offers **Allow once** / **Deny**, answered through Hermes's gateway ([mirroring.md](mirroring.md)). It leaves the list when the session stops waiting. |
| Session waiting | A mirrored Claude Code **or Hermes** window whose session reports `waiting` (a permission prompt, a question; for Hermes an approval, a clarify question or a sudo password). Shown with **what it waits on** (below), its focus title, its exact tmux target ([`<where>`](#how-a-window-is-named)) and its age. | The mirror hub's live window list; nothing is stored. | **Open the session** (the rendered view). A Hermes **approval** on a host with `-hermes-answer` also offers **Allow once** / **Deny**, answered through Hermes's gateway ([mirroring.md](mirroring.md)). It leaves the list when the session stops waiting. |
| Session question | A mirrored session's `AskUserQuestion`, registered by the `muster-mirror ask` hook and shown in the terminal and the rendered view at once. | The open question (`internal/askq`), live; it leaves the queue once answered in either place, dismissed, replaced by a newer question, or its session is gone. Such a window is this ONE item — its question, or, when it asks several, each one's first line, numbered — never also "Session waiting". | **Answer in the session**: the rendered view, where the question card is. |
| Task to route | A plugin handed a task to your chief ([task routing](plugins.md#routing-a-task-to-the-chief)) and the chief has not routed it: there is no chief, it is not running or busy, or it took the event and did not act. Shown as the task's number and title. | `plugin_messages` task events with no routing or start recorded for their task at or after the event's latest delivery attempt, or its creation if it has had none (audit rows), whose task is not complete or dismissed; nothing is copied. | **Open the task**. It leaves the list when the chief routes the task or starts an agent on it, or the task is complete or dismissed. No notification of its own: the task's "New task" push already went. |
| Reply draft | Your chief drafted a reply to a plugin message. Shown as the message's sender and its first 160 characters, and the draft reply's first 160. | `plugin_reply_drafts`, pending ones; nothing is copied. | Not on the Ask tab: with [plugins](plugins.md) on, the page has a second tab, **Reply drafts** (below), whose label carries the count ("Reply drafts (2)"). The draft is still in the badge's count, its push and `attention_list`. |
Expand Down Expand Up @@ -108,8 +108,44 @@ command, path or URL, never what Claude said.
| item | title | body |
|---|---|---|
| a question (an agent's, the chief's, muster's own) | `<asker> is asking` | the question, on one line, then `Options: a · b · c` when it offered any — except muster's own ask about a [plugin message nobody acted on](plugins.md#a-message-the-chief-does-not-act-on), whose long fixed sentence would fill the body: its body is `From: <sender> · Subject: <subject>`, then the option (the page shows the whole question) |
| a held question (a session's `AskUserQuestion`) | `<window> @ <host> is asking` | the question (each one's first line, numbered, when it asks several), then one question's choices (in the push only: `attention_list` carries no `options` for it, as it is answered on its question card, not with `attention_answer`) |
| a waiting session | `A session is waiting on you` | `Waiting: <tool name> · <window> @ <host>`, or `Waiting: <window> @ <host>` when its host names no tool |
| a held question (a session's `AskUserQuestion`) | `<where> is asking` | the question (each one's first line, numbered, when it asks several), then one question's choices (in the push only: `attention_list` carries no `options` for it, as it is answered on its question card, not with `attention_answer`) |
| a waiting session | `A session is waiting on you` | `Waiting: <tool name> · <where>`, or `Waiting: <where>` when its host names no tool |

`<where>` is the window's exact tmux target — see [How a window is named](#how-a-window-is-named).

### How a window is named

Every attention text that names a mirrored window — a chief nudge's ask, a
waiting session, a session question, a mirrored session's own ask (its source
on the queue, its page's "*<source>* asks" header and the asks block of its
session page), an answered ask's card and its delivery line ("Queued for …",
"Waiting for … to read it"), and their notifications — names it the same way
(`mirror.Where`), so you can go straight to it in a terminal. The chief reads
the same text as `where` (`fleet_list`, `sessions_overview`, `fleet_report`,
a session question's block, its session hints) and is told to give it exactly
when it points you at a session:

- `tmux <session>:<window index> @ <host>` — the tmux session name and window
index, exactly as tmux's own target syntax takes them
(`tmux build:4 @ laptop`);
- led by the host's codename for the window when it has one
(`mango:1 · tmux scratch3:5 @ laptop`). The codename is a display name,
not a tmux name, so it is never the only one.

The index is a snapshot: tmux renumbers windows, so the text says where the
window was when it was written (a chief nudge's ask, when it was raised; the
queue, its page and a notification, when they were drawn or sent). The **View
in session** link finds the window by its Claude session id when you follow
it, and is the source of truth. A mirrored session's ask whose Claude session
no window shows now reads as its stored name and host followed by
`(not shown in any mirrored window now)` — muster names no target it cannot
see; one whose host has just dropped (the hub keeps listing it briefly) is
followed by `(host offline)`. An agent's ask is named by the agent.

The target is shown **verbatim**: a page or notification plains a title's
markdown, but only the codename around the target, never the
`tmux <session>:<index> @ <host>` part — a session named `_scratch_` is shown
as `_scratch_`, not `scratch`.

Each line is folded to one line, anything shaped like a secret is redacted, and
the body is cut to 200 characters on a character boundary with a visible `…`
Expand Down Expand Up @@ -643,7 +679,7 @@ you, your mirror hosts' busy and idle Claude windows, and your
| `items[].key` | The item's id (`ask-<id>`, `session-<hash>`, `draft-<id>`, `task-<id>`). A held question has its window's `session-…` key. |
| `items[].kind` | `ask`, `session`, `question`, `plugin` (a reply draft) or `task`. |
| `items[].host` | The mirror host it is on; `""` for an agent's ask, muster's own, a reply draft and a task. |
| `items[].title`, `items[].why` | What its notification says: the source as a title, and the first line of the notification body. Secrets are redacted, a waiting session is `Waiting: <tool> · <window> @ <host>`, a reply draft is who and where only. Never Claude's last words, an ask's options or its suggested replies. |
| `items[].title`, `items[].why` | What its notification says: the source as a title, and the first line of the notification body. Secrets are redacted, a waiting session is `Waiting: <tool> · <where>` (its exact tmux target, [How a window is named](#how-a-window-is-named), kept verbatim), a reply draft is who and where only. Never Claude's last words, an ask's options or its suggested replies. |
| `items[].since`, `items[].age_secs` | When it started waiting (Unix seconds) and `ts` minus that; `0` and `0` when unknown. |
| `items[].offline` | A session item whose host dropped within the last 5 minutes, shown as last seen. |
| `items[].url` | Where to act on it, on `MUSTER_PUBLIC_URL`: the [tap route](push.md) `/attention/open/<key>` for an ask or a session (resolved when opened), the item's own page for a draft or a task. **`""` when `MUSTER_PUBLIC_URL` is not set**: the summary names no origin it was not told. |
Expand Down
8 changes: 7 additions & 1 deletion docs/chief-nudges.md
Original file line number Diff line number Diff line change
Expand Up @@ -376,7 +376,13 @@ chief judged; if it cannot read the session's state, the nudge stays queued and
is tried again.

**An ask** is an attention ask on your queue, asked by *your chief*, naming the
session's focus title and where it runs, the chief's question and its options.
session's focus title and where it runs, the chief's question and its options:
"*<focus title>* (*<where>*) stopped and is waiting on you: …", where *<where>*
is a mirrored window's exact tmux target as of when the ask was raised
(`Fix CI (tmux build:4 @ laptop)`, or
`(mango:1 · tmux scratch3:5 @ laptop)` when its host gives the window a
codename — [attention.md](attention.md#how-a-window-is-named)), and an agent's
`(agent <name>)`.
It is pushed like any ask, and its page and its card on the Asks list link the
session it is about (**View in session**: the window now running that Claude
session, or the agent's page at the conversation that stalled), found through
Expand Down
6 changes: 3 additions & 3 deletions docs/focus.md
Original file line number Diff line number Diff line change
Expand Up @@ -171,10 +171,10 @@ the chief that focus is data, never instructions. Neither makes the text safe

| tool | who | what |
|---|---|---|
| `fleet_list` | chief | every session: id, kind, name, status, focus title, and `focus_problem` (the reason) when its focus is missing or stale |
| `fleet_list` | chief | every session: id, kind, name, status, a mirrored window's `where` (its exact tmux target, [attention.md](attention.md#how-a-window-is-named)), focus title, and `focus_problem` (the reason) when its focus is missing or stale |
| `focus_search(query, limit, since_hours?)` | chief | full-text search over titles (weighted higher) and descriptions; id, kind, title and a 120-character snippet per hit. With `since_hours` (1–24) it searches every focus each session had in that window instead of its current one, and each hit carries when that focus was written |
| `sessions_overview(limit?)` | chief | every live session (running agents, online mirrored windows) active in the last 24 h, newest-active first: id, kind, name, host, status, current focus, the distinct focus titles of the last 6 h with their times, its pending attention item, and its last three user and assistant messages (300 characters each, secrets redacted, no tool output). A session whose transcript does not arrive within 5 s reads `"transcript": "unavailable (timed out)"`; all the reads share a 12 s budget, and a session it cuts or never reaches reads `"unavailable (overview budget)"`. The answer is capped at 32 KiB and says how many sessions it left out |
| `fleet_report(since, until?)` | chief | every session of the owner's — running agents and mirrored windows, **ended ones included** — with focus activity between `since` and `until` (RFC 3339; `until` defaults to now; at most 4 days apart), most recently active first: id, kind, name, host, repo, state (`waiting`: an attention item is pending; `active`: live now; `ended`), first and last seen, the distinct focus titles of the window with their times (at most 20 per session, the newest), and the pull requests it created or pushed. The chief itself is left out. Capped at 32 KiB; `coverage` says how many sessions were left out and what did not answer (an offline host, a store that could not be read). It shows the owner **everything**, hosts included and unfiltered, because its answer never leaves muster; the same report answering a [fleet-report request](plugins.md#a-fleet-report-request) is narrowed to `MUSTER_FLEET_REPORT_PATHS`, with no hosts and secret-shaped strings removed |
| `sessions_overview(limit?)` | chief | every live session (running agents, online mirrored windows) active in the last 24 h, newest-active first: id, kind, name, host, a mirrored window's `where`, status, current focus, the distinct focus titles of the last 6 h with their times, its pending attention item, and its last three user and assistant messages (300 characters each, secrets redacted, no tool output). A session whose transcript does not arrive within 5 s reads `"transcript": "unavailable (timed out)"`; all the reads share a 12 s budget, and a session it cuts or never reaches reads `"unavailable (overview budget)"`. The answer is capped at 32 KiB and says how many sessions it left out |
| `fleet_report(since, until?)` | chief | every session of the owner's — running agents and mirrored windows, **ended ones included** — with focus activity between `since` and `until` (RFC 3339; `until` defaults to now; at most 4 days apart), most recently active first: id, kind, name, host, a listed window's `where` (this door only: the fleet-report reply below never carries it), repo, state (`waiting`: an attention item is pending; `active`: live now; `ended`), first and last seen, the distinct focus titles of the window with their times (at most 20 per session, the newest), and the pull requests it created or pushed. The chief itself is left out. Capped at 32 KiB; `coverage` says how many sessions were left out and what did not answer (an offline host, a store that could not be read). It shows the owner **everything**, hosts included and unfiltered, because its answer never leaves muster; the same report answering a [fleet-report request](plugins.md#a-fleet-report-request) is narrowed to `MUSTER_FLEET_REPORT_PATHS`, with no hosts and secret-shaped strings removed |

All four are on the agent gateway and on `/mcp`. A subject is spelled
`agent:<id>` or `mirror:<claude session id>`, as `fleet_list` and
Expand Down
5 changes: 5 additions & 0 deletions internal/agentspec/instructions.go
Original file line number Diff line number Diff line change
Expand Up @@ -275,6 +275,11 @@ const chiefLiveReads = `This costs you less than it sounds. Everything you need
every time you fetch it — so you do not need to remember the state of the work, you
need to go and look at it. Answer from the read, every time.

When you point your owner at a mirrored session — in a question you ask them or
anywhere else — give its ` + "`where`" + ` exactly as your read of the fleet shows it: the
terminal session and window index on its host. A window's codename or title alone is
not a place they can find.

`

// chiefCairnSection is concatenated ONLY when the store credential is configured.
Expand Down
2 changes: 1 addition & 1 deletion internal/agentspec/instructions_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -352,7 +352,7 @@ func TestNoTextReachesTheSupervisorPromptWithoutAPin(t *testing.T) {
{
name: "chiefLiveReads",
text: chiefLiveReads,
want: "84ea975f062c83be67aec7d3c1257c32ddc3bd87652ec95b54adbf96b0a2babb",
want: "783e3e0b5a8fef0abf423065d3d811b45e042ee5e41ba7ff560f02c986f6b30f",
min: 150,
},
{
Expand Down
Loading
Loading