Context
The jaw-manager dashboard sidebar lists every running instance, but currently gives no signal when one of those instances has activity (a response just arrived, an in-progress reply, etc.). The user has to switch into each preview to see whether anything happened.
Today's behaviour:
default :3457 ← no signal even if a reply just streamed
cli-jaw-3458 ← no signal even if a long-running command finished
cli-jaw-3459 ← no signal even if an unread response is waiting
Desired behaviour (Slack/Discord-style):
default :3457 (1) ← something arrived since you last viewed it
cli-jaw-3458 ●in-progress
cli-jaw-3459 (3)
Proposed scope
Data path
Add a new field to DashboardInstance (or a sibling DashboardInstanceActivity) that reports:
type DashboardInstanceActivity = {
inProgress: boolean; // a turn is currently running on the target
unreadCount: number; // turns completed since the dashboard last
// marked this instance as 'viewed'
lastActivityAt: string | null;
};
Source of truth: the target's existing /api/events or /api/runtime stream. The dashboard already polls each instance for health; it can poll a small /api/activity-summary (or piggy-back on the existing endpoint) for these counters.
UI path
InstanceRow (public/manager/src/components/InstanceRow.tsx) renders one of:
- in-progress: small spinner / pulsing dot + label
running
- unread > 0: numeric pill
(N) next to the title, accent color
- otherwise: nothing (current behaviour)
Mark-as-read trigger: when the user opens the instance in the workbench preview tab, or clicks the row, the dashboard sends POST /api/dashboard/activity/seen { port } to reset the counter.
Persistence
lastSeenAt per port stored in dashboard registry (registry.ts already persists per-instance state).
- Counter is recomputed from
lastActivityAt vs lastSeenAt on every scan, not stored as an absolute number — avoids drift when the dashboard restarts.
Acceptance
Non-goals
- No deep activity stream UI in the sidebar (that already exists in ActivityDock).
- No push/notification API.
- No cross-device sync.
Links
- Plan tracker:
devlog/_plan/260426_jaw_manager_dashboard/10.10_dashboard_lifecycle_persistence.md (sibling slice — different concern, both under jaw-manager 10.x)
- Component:
public/manager/src/components/InstanceRow.tsx
- Registry:
src/manager/registry.ts (will hold lastSeenAt)
Context
The jaw-manager dashboard sidebar lists every running instance, but currently gives no signal when one of those instances has activity (a response just arrived, an in-progress reply, etc.). The user has to switch into each preview to see whether anything happened.
Today's behaviour:
Desired behaviour (Slack/Discord-style):
Proposed scope
Data path
Add a new field to
DashboardInstance(or a siblingDashboardInstanceActivity) that reports:Source of truth: the target's existing
/api/eventsor/api/runtimestream. The dashboard already polls each instance for health; it can poll a small/api/activity-summary(or piggy-back on the existing endpoint) for these counters.UI path
InstanceRow(public/manager/src/components/InstanceRow.tsx) renders one of:running(N)next to the title, accent colorMark-as-read trigger: when the user opens the instance in the workbench preview tab, or clicks the row, the dashboard sends
POST /api/dashboard/activity/seen { port }to reset the counter.Persistence
lastSeenAtper port stored in dashboard registry (registry.tsalready persists per-instance state).lastActivityAtvslastSeenAton every scan, not stored as an absolute number — avoids drift when the dashboard restarts.Acceptance
(N)badge when the target has completed N turns since the dashboard last marked the row as seen.lastSeenAt).Non-goals
Links
devlog/_plan/260426_jaw_manager_dashboard/10.10_dashboard_lifecycle_persistence.md(sibling slice — different concern, both under jaw-manager 10.x)public/manager/src/components/InstanceRow.tsxsrc/manager/registry.ts(will holdlastSeenAt)