# Feature Landscape — v1.7.1 Seamlessification II

**Domain:** Live-agent UX polish for the `spt` plugin (Rust binary + Claude Code skills)
**Researched:** 2026-05-16
**Scope:** Six discrete polish items. This is NOT a greenfield surface — every item touches code that already exists. Categories are framed relative to the *current* shipped behavior, not "what could exist in theory."

---

## Per-Item Breakdown

Each item is rated independently; "table stakes" here means "minimum to call the item done," "differentiator" means "lift over current behavior that users would notice," and "anti-feature" means "scope we should actively reject."

### Item 1 — `list` fn overhaul (`$OWL list` + `$LIVE list`)

| Category | Behavior | Complexity | Notes |
|----------|----------|------------|-------|
| Table stakes | Default = online-only (no flag) | Low | Flip current default — already supported via existing `online_only: bool` param at `src/owl/list.rs:20` / wired identically into `src/live/list.rs` |
| Table stakes | `--all` includes offline | Low | Single bool flag; same code path with `online_only=false` |
| Table stakes | `--offline` exclusive (offline-only) | Low | New code path — current list has no offline-only variant. Add a 3-state enum: `Online` (default) / `All` / `OfflineOnly` |
| Table stakes | `--here` repo-scoped (filter by `info.json::cwd` matching current cwd) | Low-Med | `InfoJson.cwd` already exists (`src/common/types.rs:51`); canonicalize-compare lifted from `pick_spec::build_resolve_spec` |
| Differentiator | `--here` composes with `--all` / `--offline` | Low | Orthogonal axes — repo-scope is a filter modifier, online-state is a filter mode |
| Anti-feature | `--online` flag | — | Redundant with default; adding it implies the default is something else. Reject. |
| Anti-feature | Numeric flags (`--limit N`, `--since 1h`) | — | Not in milestone; out of scope |
| Anti-feature | JSON output mode | — | Already covered by `list_result()` structured variant (`src/owl/list.rs:179`); CLI surface stays human-readable |

**Common-case pattern (industry):** `git branch`, `docker ps`, `kubectl get pods`, `tmux list-sessions` all default to *active/relevant only*, with `--all` (or `-a`) expanding to include stopped/inactive entries. **Default-online matches this convention.** `docker ps -a` does NOT have a corresponding `-o`; the "show running" is the unflagged default. Strong precedent for rejecting `--online`.

**Replacement vs additive question:** `--all` should *replace* the default (additive makes no sense — default is already a subset of all). `--offline` is *exclusive* (replaces). These three modes (`online` / `all` / `offline`) are mutually exclusive. Implement as a single tri-state, not bitflags.

**Edge cases:**
- `--all --offline` — error (mutually exclusive) OR `--offline` wins (clap convention prefers the latter; `git log --oneline --no-oneline` resolves to last-wins). Recommend: reject as error with clap `ArgGroup` mutual exclusion.
- `--here` with no cwd recorded on a perch (legacy info.json without cwd field) — exclude from `--here` output (conservative). Pre-cwd perches are pre-v1.5; vanishingly rare in practice.
- `--here` when cwd canonicalization fails on either side — treat the perch as "not here" (don't crash). Match `build_resolve_spec` behavior.
- Stale-online perches (`(stale->offline)` suffix) — count as offline for filtering purposes. Already softcleaned in the listing pass.
- Psyche / spine / touch / working perches stay hidden regardless of flags.

**Dependencies:** clap arg parsing in `src/owl/mod.rs` and `src/live/mod.rs` dispatchers; `PerchState` enum; existing `InfoJson.cwd` capture path.

---

### Item 2 — `argument-hint` coverage on `/spt:*` skills

**State of the world:** ALREADY 100% COVERED. Grep across `plugin/spt/skills/*/SKILL.md` shows all 17 skills have an `argument-hint:` line. The milestone item is effectively a *correctness audit*, not a new-feature add.

**Claude Code `argument-hint` spec (from [Anthropic frontmatter reference](https://github.com/anthropics/claude-code/blob/main/plugins/plugin-dev/skills/command-development/references/frontmatter-reference.md) and [slash-commands docs](https://code.claude.com/docs/en/slash-commands)):**

| Convention | Syntax | Semantic |
|-----------|--------|----------|
| Required positional | `<name>` | User must supply |
| Optional positional | `[name]` | User may supply |
| Optional flag with value | `[--period <seconds>]` | Flag and its value both optional |
| Free-text rest-arg | `<msg>` or `<message>` | Single value consuming remainder |
| Mutually-exclusive flags | `[<id>] \| --all` | Pipe character indicates choice |
| No arguments | `""` or `(no arguments)` | Empty string vs prose — both seen in wild |

**Audit findings (current repo):**

| Skill | Current hint | Verdict |
|-------|--------------|---------|
| `live` | `<id> [--period <seconds>]` | OK — but missing `[--auto]` once item 5 lands |
| `revive` | `<id> [--period <seconds>]` | OK |
| `listen` | `<id> [--reboot] [--block] [--once]` | OK |
| `send` | `<target> [--block]` | Missing `<msg-via-stdin>` indicator — stdin-fed bodies have no good `argument-hint` convention; leave as-is |
| `commune` | `<msg>` | Misleading — body is via stdin/file-drop, NOT a CLI arg. Should be `""` |
| `signoff` | `""` | OK |
| `new-alarm` | `<time_spec> -- <message>` | OK — `--` separator notation is unconventional but readable |
| `listen-stop` | `"[<id>] \| --all"` | OK — quoted to escape pipe in YAML |
| `live-stop` | `"[<id>] \| --all"` | OK |
| `reboot` | `<id>` | OK |
| `whoami` | `(no arguments)` | OK — prose-style, slightly inconsistent with `""` elsewhere |
| `psyche-download` | `""` | Wrong — `<id>` is required positional. Should be `[<id>]` since identity auto-detects |
| `clear-psyche` | `""` | Same as above — verify whether id is required |
| `context-save` | `<summary>` | OK |
| `list-ready` / `list-live` / `list-psyche` | (no `argument-hint` currently) | Need to add `[--all] [--offline] [--here]` after item 1 |

| Category | Item | Complexity |
|----------|------|------------|
| Table stakes | Audit + fix the ~5 inaccurate hints listed above | Low — text edits |
| Table stakes | Add `[--all] [--offline] [--here]` to all three `list-*` SKILL.md files after item 1 ships | Low |
| Table stakes | Add `[--auto]` to `live` SKILL.md after item 5 ships | Low |
| Differentiator | Standardize "no arguments" notation across skills (pick `""` OR `(no arguments)`, not both) | Low — pure consistency play |
| Anti-feature | Inventing new argument-hint syntax (e.g., `<msg-via-stdin>`) — Claude Code parses these as free-form display hints, not structured contracts, so creative syntax buys nothing | — |

**Edge case — pipe in YAML:** YAML treats `|` as a block-scalar indicator. Wrapping in quotes (`"[<id>] | --all"`) escapes it. Confirmed against current usage in `listen-stop` / `live-stop`.

**Dependencies:** None — pure markdown edits.

---

### Item 3 — Fresh-start live agent first-commune prompt

**Trigger broadening (per milestone spec):** Fire *any time agent identity is known-new*, not just on `pick-spec kind:"prompt-new"`. The richer set of triggers:

1. `pick-spec` returned `kind:"prompt-new"` and user picked/typed a name → known-new
2. `pick-spec --resolve` returned `in_this_repo_history:false, live:false, other_repos:[]` → known-new (no prior history anywhere)
3. `psyche-download` after `$LIVE start` returns `NO-CONTEXT` → known-new (no on-disk psyche file)

| Category | Behavior | Complexity |
|----------|----------|------------|
| Table stakes | Detect known-new (one of three triggers above) | Low — both `pick-spec` JSON and `psyche-download` already expose enough signal |
| Table stakes | After successful `$LIVE start`, before returning to user, surface a "first commune" prompt summarizing: (a) what agent identity is, (b) why context is empty, (c) suggested commune topics | Med — needs skill-doc rewrite in `plugin/spt/skills/live/SKILL.md` and possibly `revive/SKILL.md` |
| Differentiator | Pre-populate suggested commune body with current session context (recent user prompts, project name, etc.) | High — requires agent-side reasoning hooks. **Defer.** |
| Anti-feature | Blocking the start command until user authors commune | — | Live agent is usable without a first commune; blocking would feel like a setup wizard. Reject. |
| Anti-feature | Auto-firing a placeholder commune ("New agent, no context yet.") | — | Pollutes psyche history with non-content. Reject. |

**User-experience contract — block or fire-and-edit?**

Survey of comparable onboarding flows: `git init` (non-blocking — emits hint, returns), `npm init` (blocking interactive — but explicitly an init *wizard*), `claude /init` (fire-and-edit — generates CLAUDE.md, hands back). Pattern: tools embedded in a longer session prefer **non-blocking hint + invitation**; standalone setup wizards block.

**Recommendation:** **Fire-and-invite, not blocking.** After start succeeds, the skill outputs a clearly-formatted prompt block:

```
[FIRST-COMMUNE INVITATION]
This is a fresh agent (no Psyche context yet). Optionally describe:
- Why you're spinning up this agent
- What body of work it owns
- Anything Psyche should remember from session 1

To commune: /spt:commune (or skip and commune later)
```

User can type a commune body inline, ignore, or just continue. Friction stays low, intent is conveyed.

**Edge cases:**
- User fires `/spt:live <id>` for an id that exists in another repo (`other_repos:["foo"]`) and chose "Reuse here" — NOT fresh-start, psyche context will hydrate from the cross-repo file-drop. Suppress invitation.
- `psyche-download` returns context that's stale but non-empty — NOT fresh-start. Suppress.
- User uses `$LIVE fork` (Phase 26 primitive) — forked agent gets a new id but parent psyche context carries forward. Suppress invitation (fork ≠ fresh).
- Restart of a previously-signed-off agent via `$LIVE revive` where psyche-context file is still on disk — NOT fresh (history present). Suppress.

**Dependencies:** `pick-spec` JSON schema (already plumbed); `psyche-download` `NO-CONTEXT` exit signal; `live` skill markdown.

---

### Item 4 — `$LIVE pick-spec` "false-empty when all live" bug

**Bug located:** `src/live/pick_spec.rs:146-151`

```rust
let mut candidates: Vec<(String, i64)> = state
    .ids
    .iter()
    .filter(|e| !owlery::is_perch_online(&e.id))   // ← bug
    .map(|e| (e.id.clone(), agent_ids::iso_to_epoch(&e.last_active)))
    .collect();
```

The filter excludes online perches because **launching an online perch via `$LIVE start` would `COLLISION`**. The intent was correct for the call-graph that existed at Phase 26 design time: caller expected pick-spec to return *startable* agents only.

**Bug shape:** If all known agents in this repo are currently live, `candidates` is empty → falls to `0 => emit_prompt_new()` (line 158). The user sees "No agents in this repo's history yet" — a *lie* — and is offered starter role names as if this were a virgin repo.

**Verified by existing test:** `pick_one_online_agent_filters_to_prompt_new` at `pick_spec.rs:447` — the test *codifies* the buggy behavior as expected. Fix must update this test.

| Category | Behavior | Complexity |
|----------|----------|------------|
| Table stakes | Return live agents in the candidate set | Low — drop the `is_perch_online` filter |
| Table stakes | Mark each candidate with online state in the JSON schema | Low — add `"online": bool` to each option entry, OR new `"online_ids": [...]` companion array |
| Table stakes | Caller-side enforcement: when user picks a currently-live agent, treat as collision → re-prompt with "new name or cancel" | Med — `live/SKILL.md` flow needs a new dispatch branch when the picked label is in the `online_ids` set |
| Differentiator | Auto-suggest a fork target name when user picks a live agent (e.g., `doyle` live → suggest `doyle-2`) | Low-Med — single counter helper |
| Anti-feature | Allowing `$LIVE start <id>` of an already-live id (would still COLLISION at owl layer) | — | Defense in depth lives at the owl layer; pick-spec just needs to *surface* the live-ness so the skill can avoid it |
| Anti-feature | Returning psyche/spine/touch/working perches in the candidate list | — | Existing skip-logic preserved |

**Schema impact (additive — backward-compat):** Adding `"online": true/false` to each option's JSON entry is a non-breaking extension. Old skill consumers ignore it; new ones dispatch on it. Consistent with the schema-v1-frozen guarantee documented at `pick_spec.rs:6-11`.

**Edge cases:**
- All known agents live → `kind:"pick"` with all entries flagged `"online":true`; caller must produce a "no startable names — pick a new one or cancel" dialog. (Not `kind:"prompt-new"` — that misleads users into thinking the repo has no history.)
- Mixed online/offline → `kind:"pick"`, sort offline-first by recency (offline are immediately startable). Reserve live ones for the "do you want to fork?" branch.
- Exactly one offline + several live → currently emits `kind:"auto"` for the single offline. With fix, still emits `kind:"auto"` (one startable candidate), but the JSON should *also* surface the live ones so the user can fork instead. Consider `kind:"auto"` carrying a sibling `"also_live":[...]` array.
- `--resolve` mode is unaffected — already returns full state (in_this_repo / live / other_repos) and is the canonical fork-detection path.

**Dependencies:** schema-v1 JSON contract; `live/SKILL.md` dispatcher; existing tests that lock buggy behavior must update.

---

### Item 5 — `/spt:live --auto` flag + casual-language activation

**Three distinct mechanisms in one item:**

(A) `--auto` CLI flag — explicit user gesture
(B) SessionStart-surfaced pick-spec JSON — proactive
(C) Casual-language activation — phrase-trigger dispatch

| Category | Behavior | Complexity |
|----------|----------|------------|
| Table stakes (A) | `/spt:live --auto` picks most-recent offline agent + auto-resumes without picker | Low — already have `kind:"auto"` path; `--auto` short-circuits to "most-recent" when `kind:"pick"` would otherwise fire |
| Table stakes (B) | On SessionStart for non-live / non-listening sessions, the SessionStart hook emits the pick-spec JSON into context so agent can surface it on first prompt | Med — extend existing SessionStart hook in `plugin/spt/hooks/` |
| Table stakes (C) | Skill description includes activation phrases ("pick up where we left off", "keep going", "resume work") | Low — text edit in `plugin/spt/skills/live/SKILL.md` frontmatter `description` |
| Differentiator | "Obvious next body of work" heuristic — auto-resume picks not just the agent but the in-flight task | High — requires inspecting `.claude/{id}-commune.md`, recent psyche context, or a sentinel work file. **Scope down for v1.7.1.** |
| Anti-feature | Activating on *any* mention of "live" or "agent" — high false-positive risk | — | Phrase patterns must be specific and contextual |
| Anti-feature | Background auto-launching without user gesture | — | Always require user-visible affirmation, even if just an inline "I'm starting `doyle` — say 'no' to cancel" |

**Casual-language activation — how to detect WITHOUT false-positives:**

Claude Code activates skills based on the skill's `description` frontmatter matching against the user's intent. **There is no regex or hard pattern-match** — the model decides based on the phrasing. So the question becomes: what phrases in `description` minimize false-fires?

Survey of false-positive risks for common phrases:
- "pick up where we left off" — could mean: resume *any* prior conversation (git stash pop, prior file, prior PR). **Risk: medium.** Anchor by adding "live agent" qualifier in description.
- "keep going" — extremely common in mid-conversation continuance ("keep going on the refactor"). **Risk: high.** Reject as primary trigger.
- "resume work" — moderate. Could mean resume *a* task without invoking a live agent. **Risk: medium.**
- "go live" / "start live" / "live as" — already in current description, low FP. **Keep.**
- "resume my agent" / "wake up my agent" — agent-specific verbs, low FP. **Add.**

**Recommendation:** Description block should layer triggers by specificity:
1. Explicit (current): "go live", "start live", "live as"
2. Resume-specific: "resume my agent", "wake up my agent", "resume live"
3. Casual but agent-anchored: "pick up the agent from before", "continue with my live agent"
4. **Reject** unmodified "keep going" / "resume work" — too ambiguous.

The skill description IS the false-positive filter. Specificity is the only lever.

**"Obvious next body of work" decoupled from GSD:**

For a generic project (no GSD), candidate signals (ranked by reliability):
1. **Highest:** Last commune body in `.claude/{id}-commune.md` (if non-empty and recent) — explicit user-recorded intent
2. **High:** Last signoff body in `.claude/{id}-signoff.md` (post-session-end note about next steps)
3. **Medium:** Last entry in psyche-context file under "intentions" or "next" section (memformat-driven)
4. **Low:** git status (uncommitted work hints at in-flight task) — high noise
5. **Reject:** Cross-reference to issue trackers, calendar, etc. — out of scope

**Recommendation:** `--auto` reads (1) first, falls back to (2), gives up on the rest. Surface the resumed body as: "Resuming `<id>` — last context: `<one-line summary>`. Continue? (or describe new direction)"

**Edge cases:**
- `/spt:live --auto` when zero offline agents in this repo — fall through to normal `prompt-new` flow (don't error out)
- `/spt:live --auto` when target is a live agent (already running) — refuse with COLLISION, point at `/spt:list-live`
- SessionStart pick-spec injection conflicts with existing `<psyche-context>` injection (Phase 28) — they target different perch states (non-live vs live); should never both fire
- Phrase "pick up where we left off" said by user with NO live agents in history → SessionStart hook would have surfaced `kind:"prompt-new"` not auto-resume; phrase activates `/spt:live`, which then shows the picker. Correct, but worth UAT-ing.

**Dependencies:** SessionStart hook in `plugin/spt/hooks/`; `pick-spec` JSON (item 4 fix lands first); skill description text; `--auto` flag added to `$LIVE` clap surface.

---

### Item 6 — Version-change changelog notification

**No built-in mechanism exists** in Claude Code for plugin update notifications (verified via [issue #31462](https://github.com/anthropics/claude-code/issues/31462) — feature is being proposed but not shipped). Plugins are responsible for their own version-change detection.

**Required artifacts:**

1. **Embedded version sentinel** — already exists (plugin version is in `plugin/spt/.claude-plugin/plugin.json`; the binary knows its own version via `env!("CARGO_PKG_VERSION")`)
2. **Current-version file** — durable, per-user state outside the plugin install dir
3. **Changelog content** — markdown file shipped with the plugin
4. **Hook prompt** — SessionStart fires the diff display

**Current-version file location:**

| Option | Pro | Con |
|--------|-----|-----|
| `$SPT_HOME/spt-version` | Already-durable spt state root; survives plugin reinstall | Tightly couples plugin-version notion to runtime state |
| `%LOCALAPPDATA%\spt\last-seen-version.json` | Mirrors milestone spec; outside owlery dir keeps spt state clean | Need new path constant |
| `~/.claude/plugins/state/spt-version` | Sits inside Claude Code state | Volatile; user `/clear` semantics unclear |

**Recommendation:** `%LOCALAPPDATA%\spt\last-seen-version.json` (Windows) / `~/.spt/last-seen-version.json` (Unix). **Outside the owlery subdir** — owlery is per-perch runtime state. Version-watching is per-user, single-instance state. Same root, sibling file.

**Schema:**

```json
{
  "schema_version": 1,
  "last_seen": "1.10.10",
  "last_seen_at": "2026-05-16T14:32:00Z",
  "dismissed_versions": ["1.10.9"]
}
```

`dismissed_versions` lets the user say "shut up about this one" without losing future notifications. Optional but cheap.

| Category | Behavior | Complexity |
|----------|----------|------------|
| Table stakes | Detect plugin version mismatch (binary's compiled-in version vs `last-seen-version.json::last_seen`) | Low — `env!("CARGO_PKG_VERSION")` |
| Table stakes | SessionStart hook fires `owl.exe version-check` which writes new version + emits prompt if changed | Med — new owl subcommand + hook script |
| Table stakes | Display old→new diff with bulleted changelog excerpt for new version | Med — needs CHANGELOG.md parser, or just dump section between v-old and v-new headers |
| Table stakes | First-run (no last-seen file) = silent write, no prompt | Low — guard the diff display |
| Differentiator | Dismiss-this-version mechanism (`/spt:dismiss-changelog`) | Low — skill that appends to `dismissed_versions` |
| Differentiator | Render in compact format ("v1.10.9 → v1.10.10: fix X, add Y") instead of dumping raw markdown | Med — depends on CHANGELOG.md structure quality |
| Anti-feature | Toast/popup-style modal that blocks the agent | — | Inline prompt only; user keeps control |
| Anti-feature | Network fetch for changelog | — | Plugin ships changelog as a file in the install dir; no network |
| Anti-feature | Version skew warnings between binary version and plugin.json version | — | Handoff protocol (Phase 18.4/18.5) handles this; out of scope |

**Where should current-version live (final answer):** `$SPT_HOME/last-seen-version.json` — uses existing `SPT_HOME` env-override path. Matches the existing convention (`owlery/`, `last-seen-version.json` are both children of `$SPT_HOME`).

**Edge cases:**
- Binary handoff during version change (Phase 18.4) — handoff happens BEFORE SessionStart hook fires next session. Hook sees the new binary's version and writes correctly. Race-free.
- Plugin installed but never used — no `last-seen-version.json` exists. First SessionStart writes it silently with no prompt.
- Downgrade (new < last_seen) — treat as "version change," show downgrade notice. Don't silently swallow.
- Same version, re-fire SessionStart (`/clear`, `/compact`) — no prompt; version equality is the gate.
- CHANGELOG.md missing or unparseable — degrade to "Updated v1.10.9 → v1.10.10 (no changelog available)"
- User has multiple Claude Code sessions open during update — both hit SessionStart, both try to write `last-seen-version.json`. Atomic write (temp file + rename) per existing owl conventions handles it.

**Dependencies:** New owl subcommand `version-check`; new SessionStart hook entry (additive — doesn't conflict with existing env injection or psyche-context injection); CHANGELOG.md file maintained at repo root (needs verification of current state); `$SPT_HOME` resolver already in `src/common/owlery.rs`.

---

## Cross-Item Feature Dependencies

```
Item 4 (pick-spec bug fix) ──→ Item 3 (fresh-start detection uses pick-spec)
                            ╲
                             ╲→ Item 5 (--auto / SessionStart inject uses pick-spec)

Item 1 (list overhaul) ──→ Item 2 (argument-hint coverage on list-* skills)

Item 5 (--auto added) ──→ Item 2 (argument-hint update for live skill)

Item 6 (changelog notify) — standalone, no dependencies
```

**Recommended phase ordering:**
1. Item 4 first — unblocks 3 and 5; smallest, most isolated; existing test must update
2. Item 1 — small, foundational; flag plumbing exercises clap surface for item 5's `--auto`
3. Item 2 — quick audit pass; updates absorb items 1 and 5 changes
4. Item 3 — depends on item 4's schema extension
5. Item 5 — depends on items 1, 4; largest UX surface area
6. Item 6 — fully independent; can run in parallel with any of the above

---

## MVP Recommendation (if scope must compress)

Priority order if shipping in batches:
1. **Item 4 (pick-spec bug)** — actual bug fix, low risk, unblocks downstream
2. **Item 1 (list overhaul)** — high user-touch, low complexity
3. **Item 6 (changelog notify)** — addresses concrete user-facing gap (no built-in exists upstream)
4. **Item 5 (--auto + casual activation)** — biggest UX win but highest complexity
5. **Item 3 (fresh-start prompt)** — modest UX add
6. **Item 2 (argument-hint audit)** — cheap finish; do alongside 1+5 naturally

**Defer if needed:**
- Item 5's "obvious next body of work" auto-resume detection — ship `--auto` with most-recent-agent semantics only; defer task-level resume to v1.8
- Item 6's dismiss-version feature — ship core notify first; add dismiss in a quick follow-up

---

## Anti-Features (Cross-Cutting — Reject)

| Anti-Feature | Why Avoid | Instead |
|--------------|-----------|---------|
| `--online` flag on list | Redundant with default | Document the default in `--help` and skill docs |
| Blocking first-commune wizard | Imposes setup ceremony | Fire-and-invite, non-blocking |
| Auto-firing placeholder communes | Pollutes psyche history | Wait for real user intent |
| Activation on bare "keep going" / "resume" | High false-positive rate | Anchor triggers with "agent" / "live" qualifiers |
| Network-fetched changelogs | Adds runtime dep, latency, failure mode | Ship CHANGELOG.md in plugin dir |
| Modal blocking changelog dialog | Breaks agent flow | Inline single-fire SessionStart prompt |
| New JSON `kind` for "all live" pick-spec state | Breaks schema-v1 frozen guarantee | Extend existing `pick` shape with `"online": bool` per entry |
| GSD coupling in item 5 auto-resume | Not all users have GSD | Read commune/signoff/psyche files (universal) |

---

## Sources

- Code references (cited by file:line, all under `C:\Users\decid\Documents\projects\claude_skill_owl\`):
  - `src/live/pick_spec.rs:146-151` — bug location (filter excludes online agents)
  - `src/live/pick_spec.rs:158` — false-empty fallthrough to `emit_prompt_new`
  - `src/live/pick_spec.rs:447` — test codifying buggy behavior (`pick_one_online_agent_filters_to_prompt_new`)
  - `src/owl/list.rs:20` — `pub fn run(online_only: bool)`
  - `src/live/list.rs:19` — `pub fn run()` (no flag — always shows all live state)
  - `src/common/types.rs:51` — `InfoJson.cwd` field (foundation for `--here`)
- [Claude Code slash commands docs](https://code.claude.com/docs/en/slash-commands) — `argument-hint` syntax
- [Anthropic frontmatter reference](https://github.com/anthropics/claude-code/blob/main/plugins/plugin-dev/skills/command-development/references/frontmatter-reference.md) — frontmatter contract
- [Issue #31462 — Plugin update detection and upgrade workflow](https://github.com/anthropics/claude-code/issues/31462) — confirms no built-in changelog notification
- [Issue #50153 — Built-in update notification](https://github.com/anthropics/claude-code/issues/50153) — feature request, not shipped
- `.planning/PROJECT.md` — milestone goals + Phase 26 history
- `plugin/spt/skills/live/SKILL.md` — current `/spt:live` flow (auto-pick, fork, resolve)
