A running list of improvements to tackle in future versions of Scout.app and the surrounding Scout system. Items at the top are next-up; items below are nice-to-haves.
- **Action Items "Soon" cluster — delete/edit comments + custom snooze date
- preserve original section kind.** Four backlog items shipped together:
scoutctl action-items delete-commentandedit-commentare new CLI verbs (with paired Python tests). Both take--by-id/--subjectfor the task and--index/--textfor the specific comment; delete drops the line atomically, edit preserves the original indent + author prefix and replaces only the body. Thesnoozed-untilmarker is filtered from the selector index so--index 1always points at the first real user-authored comment.CommentListViewgrows a hover-revealed pencil/trash row per comment. Edit flips the row into aTextEditorwith⌘↵to save; delete confirms via an.alertand then routes through the newWriteOp.deleteComment/WriteOp.editCommentcases onActionItemsWriter. Git history is the archive (writer already commits each op path-scoped).SnoozePopoverViewgains an "Other date…" row that pivots the popover into a graphicalDatePickerpanel. The earlier macOS-26 epoch-leak bug is sidestepped by initializing the picker's@Stateto.distantPastand lazy-seeding to tomorrow inside the custom panel'sonAppear— the picker never relies on aninit-timeStatevalue.scoutctl action-items snoozeaccepts--from-kind <kind>which records the source section as(from-kind: urgent)in thesnoozed-untilmarker. The app passes the current section'sActionSection.Kind.rawValueon every snooze, the parser extracts the hint (and the carry-in line's optional, was urgenttail) into a newActionTask.snoozedFromKindfield, andTaskCardView.effectiveKinduses it for the gutter color. An urgent task that lands under## 🛌 Snoozedon the target day now keeps its visual urgency. Follow-up: the consolidation skill side (emitting the, was <kind>tail on the carry-in line in the target day's file) is still out-of-scope here — captured in BACKLOG below.
- Usage Rail Card + Connector Health (Phase 1).
BudgetRailCardreplaced byUsageRailCard— reads tokens from a new.scout-logs/session-tokens.jsonltracker fed by a Stop hook (~/Scout/scripts/sum-session-tokens.sh) that sumsmessage.usage.*across the session transcript at the end of every run. Shows today / week token totals + per-model share; dollar cost hidden on purpose (wrong lens on a Claude team-plan seat — included up to quota, overage-only billing). Also addedConnectorHealthRailCard+ top-of-windowConnectorAlertBannerover the already-wired shell telemetry pipeline (connector-calls-*.jsonl,connector-alerts.log); acks persist to.scout-cache/connector-alerts-acked.jsonwith fingerprint-scoped GC so acking today's CRITICAL doesn't suppress tomorrow's. Oldusage-tracker.jsonlpath +budget-check.shdispatcher gate intentionally left untouched — Phase 2 replaces them with/usagequota data (see below). - Launch Claude — split menu with Ghostty/tmux, Claude Desktop Chat, and
Claude Desktop Cowork. The old single "Launch Claude" button drove an
AppleScript that pressed ⌘T and typed into Ghostty, which quietly failed
whenever Accessibility permission wasn't granted to the current bundle
ID. Rewritten as a Menu with three targets. The Ghostty path detects a
running tmux server (via
tmux list-sessionsagainst/tmp/tmux-$UID/ default, since macOS GUI apps get a different TMPDIR) and spawns a new tmux window withclaudein the Scout directory — the only reliable way to get a fresh terminal surface when Ghostty's macOS config setscommand = tmux new-session -A. Claude Desktop paths openclaude://claude.ai/new?q=…orclaude://cowork/new?q=…. The full action-item context (subject + body + prior comments + deep-link URLs) is copied to the clipboard on every launch as a reliable ⌘V fallback. - Schedules tab. Full CRUD on
com.scout.*.plistfiles from within Scout.app — edit times, add new schedules (including the long-missing research cadence), delete unwanted ones. Saving writes both the live copy in~/Library/LaunchAgents/and the repo copy in~/Scout/launchd/, reloads vialaunchctl bootout/bootstrap, and makes a path-scoped git commit. Also refactoredLaunchdScheduleServiceto use the sharedPlistIOhelper, which fixed a latent off-by-one weekday-convention bug.
- Consolidation-side
, was <kind>carry-in tail. The app-side half of the section-kind preservation shipped 2026-05-25 —scoutctl snoozerecords(from-kind: <kind>)on the source-day marker and the parser reads both that and an optional, was <kind>tail on the carry-in line. The remaining work is teaching the consolidation / morning-briefing skill to emit_(carried in from YYYY-MM-DD, was urgent)_(or equivalent) instead of the bare_(carried in from YYYY-MM-DD)_when the source had a(from-kind: …)marker. Once that lands, a task snoozed from🔴 Urgenton Monday and rendered under🛌 Snoozedon Wednesday will keep its urgent gutter without any further app changes.
- Settings → "Test launch" button for Claude Code. Surfaced during #12
smoke testing. A button in the Claude Code card that attempts a launch
with the current config (path + terminal) right where you configure it,
so you can validate the
claudepath, terminal target, and Automation permission without going to an action item and hitting an error. Would also be the natural place to confirm a custom command works. Deferred from #12 to keep that PR scoped; the empty-custom case now shows inline validation in the meantime. - Launch Claude — broader terminal + shell support. Mostly addressed
by #12: Settings now offers a "Open Claude Code in" picker —
Auto / Terminal.app / iTerm2 / Custom command — plus a custom
claudebinary path override. Auto prefers Ghostty/tmux and falls back to Terminal.app. iTerm2 and Terminal.app launch via AppleScript; kitty / WezTerm / any other terminal (and any future Linux/Windows host) are covered by the custom-command template ({cwd}/{claude}). Remaining follow-ups: (a) verify the non-tmux Ghostty--command=fallback renders a fresh window when the primary instance hasn't set acommand = tmux …override; (b) consider a.command-file drop as an alternative to AppleScript for Terminal.app (avoids the Automation permission prompt). - Launch Claude — resolve
claudepath off the main thread. Follow-up from the #12 code review.ClaudeLauncher.resolveClaudePath(override:)runs synchronously on the main thread; when no override is set andclaudeisn't at a probed location, the login-shell fallback ($SHELL -lc "command -v claude"+waitUntilExit()) can block the UI for ~50–300 ms. Pre-existing (carried over from the oldlaunchGhostty) but #12 widened the surface (Terminal.app / iTerm2 targets now hit it too). The naive fix — offloading the wholelaunchoff-main — is unsafe becauseNSAppleScript.executeAndReturnErrormust run on the main thread. Correct fix: make the launch path async, resolve the path on a background executor, then hop back to the main actor for the NSWorkspace / NSAppleScript invocation. (The Settings placeholder probe is already off-main viaTask.detached; this is only about the launch path.) - Task-relevant cwd for Launch Claude. Currently every Ghostty launch
opens in
~/Scout. If the task's deep links include a GitHub PR URL (e.g.github.com/acme/mcp-server/pull/42), we could try common clone locations (~/<repo>,~/code/<repo>,~/src/<repo>) and cd there instead. Falls back to~/Scoutwhen nothing matches. - Keyboard navigation. Arrow keys to move focus between cards; Enter to open the composer on the focused card.
- Bulk actions. Multi-select cards → mark done / snooze all together.
- Drag-and-drop reorder within a section, writing a stable ordering marker back to the markdown.
- Pinned filter presets. Save a filter combination (e.g. "Urgent + Watching, Open only") and restore it with one click.
- In-card deep-link inline preview. Hover a Linear chip → preview the issue title / status without leaving the app.
- Usage Rail Card — Phase 2 (quota bars + dispatcher gate). Phase 1
shipped tokens + dollar derivation; Phase 2 surfaces the three
/usagebars (current session, current week all-models, current week Sonnet-only) and replaces the dollar-basedbudget-check.shdispatcher gate with a quota-based one. Viable delivery path confirmed during Phase 1 spike: therate_limits.{five_hour,seven_day}.used_percentageobject is exposed tostatusLinehooks but NOT to any hook that fires forclaude -p. So the implementation is: wrap the global~/.claude/settings.jsonstatusLinecommand (currently points at claude-hud) with a tee that dumps the JSON to.scout-cache/usage-quota.jsonbefore invoking claude-hud unchanged. Interactive Claude Code sessions (which Jordan runs throughout the workday) keep the file fresh; it's per-account data so it doesn't matter who triggers the refresh. Needs its own spec/brainstorm — deferred from Phase 1 to keep scope honest. - "Run now" should refresh the heartbeat schedule. Clicking Run now
from the heartbeat table fires the job, but the scheduled row sits there
unchanged — the next-fire timestamp doesn't shift and the row isn't
removed. Expected: either drop the row until the next cron tick recomputes
it, or have
LaunchdScheduleService.recompute()fire immediately afterRunnerService.runNowcompletes so the same item doesn't keep looking "queued" at the past time.
-
Usage / Connector Health — Phase 1 follow-ups from final review. Small polish items surfaced by the final branch review on 2026-04-22, none blocking:
SessionTokensService.tokens(for sessionId:)lookup — spec-listed but not implemented since nothing currently calls it. Add before the per-run stats pane starts consuming token data.- "View full report" button on
ConnectorHealthRailCardfooter that opens a sheet rendering~/Scout/knowledge-base/connector-health.mdas markdown. Called out in the spec, not in the plan. - "Open auth settings" button in
ConnectorAlertBannerpopover — deep-links toclaude.ai/settings/connectorsfor Google/Granola or copiesgh auth login/ plugin re-auth commands to clipboard. Called out in the spec, not in the plan. connector-alerts.logfixture trimmed to one CRITICAL line during implementation to match the test assertion — WARNING-level alert path currently has no test coverage. Add a fixture + test for WARNING parsing and rendering.ConnectorAlert.fingerprintuses rawconnector|level|first_seenstring; spec called forsha256(...). Semantically equivalent today but will diverge if shell side ever writes its own fingerprints. Align before any shell-side ack coordination work.ConnectorAckStorewraps its dict in aDispatchQueuethough all callers are@MainActor. Swift 6 strict mode will warn; simplify to plain@MainActorstate when the time comes.
-
Activity heatmap should adapt to available history. Hardcoded 52-week grid is overkill when Scout only has ~10 days of data — 99% of cells are empty and the real activity crowds into one column on the right. Should auto-scale: show from the first-recorded run (minimum 4 weeks, max 12 months) through today so cells fill the width proportionally. Header label ("Activity — last 12 months") should reflect the actual range being rendered.
-
SessionLogService.reconcile()orphan-sweep end-to-end test. Task 5 of the session-status-parser refactor (shipped 2026-04-21) wired the orphan sweep into bothloadInitial()andreconcile(), but onlyloadInitial()has end-to-end test coverage. A regression in the reconcile path would land silently. Needs a controllableFileSystemEventSourcetest double (possiblyAsyncStream.Continuation-backed) sinceNoopFSdoesn't emit events.
Decomposed from the broader "make Control Center as good as possible" ask on 2026-04-21. Recommended build order below. Each lands on the Run detail pane, so the first one (stats) improves the surface that the next two render onto.
-
View / interact with a running SCOUT session (RESEARCH-GATED). The fundamentally missing capability — today every scheduled scout run is a sealed box.
run-scout.shinvokesclaude --print ...with--remote-control-session-name-prefix "scout", but the comment in that script already flags that-pmode does not support remote control. Scout.app's Runs tab shows only what the session wrote to.scout-logs/scout-*.logafter the fact. Jordan has no way to watch a run as it happens, pause it, answer a mid-run question, or drop in to course-correct. This is a research problem first, not a build problem. Open questions to investigate (tracked in~/Scout/docs/Wishlist.mdas a dreaming research topic):- Can scheduled runs use a non-
-pClaude Code mode that keeps remote control open while still being headless enough for launchd? - Is there a Claude Code "background session" or "detached session" pattern that Scout.app could attach to live?
- As a minimum-viable start, can the app stream
tail -fof the active run's log file (detectable via the lock file.scout-session.lock+ filename pattern) into a live terminal view — read-only, but at least Jordan can see what the session is doing right now? - Claude Agent SDK alternative: if Scout moved off the
claudeCLI to the Agent SDK, would that open up richer streaming / interaction? (Bigger rewrite, but may be where the answer lives.) - Is there value in a hybrid — scheduled launches stay
-p, but a "Take over this run" button spawns a parallel interactive session primed with the current run's context? (Doesn't actually attach to the running session, but lets Jordan intervene without waiting for it to finish.) Once research narrows the options, build out the read-only streaming view first (low-risk, high daily value) before attempting true attach/interact.
- Can scheduled runs use a non-
-
Per-run stats pane (v1).
RunDetailViewtoday shows cost / errors / log-size. Add: duration (from startedAt + endedAt), diffstats for the commits in the run's time window (files touched, lines +/-), tool-use counts parsed from the log, and tokens in/out if they can be pulled from.scout-logs/usage-tracker.jsonl. No new surface — reuses the existing pane. Should ship before the two items below so they land on a cleaner detail pane. Adjacent: the existing "Budget panel shows $0" item is about the source-of-truth for cost, which feeds this pane. -
Per-run feedback loop (app → dreaming). Compose feedback from the Run detail pane → persist to a per-run file under
.scout-feedback/(or similar) → teachDREAMING.mdPhase 1 to read it alongside the two feedback channels that already exist (Slack DM reactions/replies, inline//==<< ... >>==//KB comments). Open questions for the spec: file format (YAML vs. markdown), whether the feedback dir is git-tracked, whether dreaming acknowledges what it processed (so the same feedback isn't re-ingested), and whether the composer supports ratings/tags or is pure prose. Prior art: the two existing channels are documented in~/Scout/DREAMING.mdPhase 1. -
Knowledge-graph-touched visualization. Show every file and KB node a run touched, rendered as a graph overlay on the Run detail pane. Two phases, ship them separately:
- v1 (derived — ships first). Parse commits-in-window → modified
files →
[[wikilink]]edges from each modified.md→ force-directed SpriteKit / Canvas / WebView view. Uses data we already have; no changes torun-*.shor skills required. Most of the design work is layout engine choice, interaction (click node → open file), and how the view is anchored in the pane (tab? overlay? modal?). - v2 (explicit emit). Each SCOUT session drops a sidecar JSON
listing files read / written / mentioned during the run. Requires
hooking
run-*.shto capture tool-use events from Claude Code and discipline across every skill to not skip emit. Weeks, not days — only worth building once v1 is proven to be worth looking at.
- v1 (derived — ships first). Parse commits-in-window → modified
files →
- Triggers tab — manage event-based fire conditions. Companion UI for
the event-trigger architecture being built into the scout-plugin engine
(full design in
~/Scout/knowledge-base/projects/scout/scout-event-triggers.mdand~/scout-plugin/docs/specs/event-triggers.md). Today's Schedules tab surfaces time-based slots (schedule.yaml); the new Triggers tab surfaces event-based fire conditions (triggers.yaml) with the same CRUD pattern Plan 6 established for Schedules. Status: gated on enginetriggers_v1manifest flag. Don't build the UI until the engine matcher is shipping fires. Two delivery phases:- Phase 1 — read-only Triggers tab. Lists all configured triggers
from
triggers.yaml, shows enabled/disabled state, last-fire-ts, daily fire count vs. cap, source connector liveness (pulled from the sameConnectorHealthServicealready in Scout.app v0.1.6). Per-row "Recent fires" inline expansion reads.scout-logs/trigger-fires-*.jsonland renders the last 20 fires with their matched event payload + the dispatched action's outcome. Enables Jordan to see what's firing without touching YAML. - Phase 2 — form-based editor. Mirror the Schedules tab CRUD
affordance (
Scout/Services/ScheduleEditService.swiftis the pattern to copy): edit existing triggers in-place, add new ones from a source-specific template, delete with confirmation. Each source'sSUPPORTED_MATCH_TYPESbecomes a dropdown; action-kind picker drives a conditional form (notify → surfaces picker; run_skill → skill picker populated from installed skills; interactive → no extra fields). Save writes throughTriggerEditService(new, modeled onScheduleEditService) → atomic temp-file + rename + mtime stale-check + path-scoped git commitscoutctl trigger reloadinvocation.
- Phase 1 — read-only Triggers tab. Lists all configured triggers
from
- Trigger-fire notifications surface in Scout.app menu bar. When a
trigger with
action.kind: interactivefires, the matchingneeds-jordan.mdartifact needs a visible affordance — menu-bar badge count + dropdown listing pending interactive triggers with "open in Claude Code" button (reuses the existing Launch Claude split-menu pattern). Don't compete with the Action Items view; this is a parallel surface for trigger-driven asks specifically.
- Trigger fire history view. Beyond per-row recent fires, a full-tab history showing all trigger fires across all triggers, filterable by time range / source / action outcome. Reuses the Action Items table-vs-cards toggle pattern from Plan 7.
- Trigger simulation / dry-run. Right-click a trigger → "Test against
last 24h of events" → invokes
scoutctl trigger test <id> --dry-runand renders the would-fire list inline. Useful for tuning a match filter without enabling the trigger.
- Scout-session awareness of snoozes. Consolidation / dreaming sessions
should read the target-day
_(carried in from YYYY-MM-DD)_annotations so a task's snooze lineage is visible to the briefing prompt. Right now a snooze removes the source line and future sessions see a gap in the thread unless theygit logit. - Comment-deletion helper CLI. Paired with the app-side delete (above).
- Telemetry for writer errors.
ActionItemsWriterclassifies failures (.noMatch/.ambiguous/.environment/.other); counting those per day in a sidecar log would make divergence between Scout.app and the Obsidian workflow easier to spot.
- Env banner copy drift. The "missing: …" banner line lists scripts by
filename; if the set of required CLIs changes, we need to keep the banner
and
ActionItemsEnvironmentCheck.requiredScriptsin sync manually. - FilterChipsView's dual "all" representation.
filter.kinds == []andfilter.kinds == {every known kind}are treated as equivalent in the filter pipeline. Currently normalised to[]after every toggle but worth simplifying the data model so only one representation is valid.