Skip to content

feat(ops-dashboard): cost-by-model, board deep-links, cleaner projects list#99

Merged
mhersson merged 10 commits into
mainfrom
feat/ops-dashboard-fixes
May 18, 2026
Merged

feat(ops-dashboard): cost-by-model, board deep-links, cleaner projects list#99
mhersson merged 10 commits into
mainfrom
feat/ops-dashboard-fixes

Conversation

@mhersson
Copy link
Copy Markdown
Owner

Summary

  • Projects pane: sort alphabetically (was: by card count), drop the
    uninformative Status column, link each row to the project board (was:
    per-project cost dashboard).
  • Top Cards pane: delete the always-wrong "Full breakdown" button;
    rows now deep-link to /projects/{name}?card={id} and open the
    CardPanel on arrival.
  • Cost panel: bar chart is now grouped by model (cards with no
    recorded model bucket under unknown); panel header reads "Cost by
    model · Agents on duty". An info tooltip surfaces the "last-model-wins"
    attribution limit. "Agents on Duty" tab is unchanged.
  • Infrastructure: ProjectShell learned a ?card=ID deep-link
    receiver (open panel on mount, strip the param on close) via the
    codebase's in-render state-marker pattern.

Test plan

  • Open /. Projects list is alphabetical and has no Status column.
  • Click a project row — lands on the board, not the cost dashboard.
  • Top Cards: no "Full breakdown" button.
  • Click a Top Cards row — board opens with the CardPanel pre-open for that card.
  • Close the panel (X, Esc, project change) — ?card= disappears from the URL; back button is not polluted.
  • Cost panel header reads "Cost by model · Agents on duty"; first tab shows one row per model sorted by cost; tooltip text appears on the info icon.
  • Cards untagged with a model show in an "unknown" bucket.
  • Agents on Duty tab still lists active agents and links to per-project dashboards.
  • Cross-project SPA nav (sidebar click from /projects/A?card=A-1 to /projects/B?card=B-2) opens the second panel correctly.

Out of scope / follow-ups

  • Dead agent_costs field on DashboardData (Go + TS) and its fold in aggregateDashboards — kept for backward compat in this PR; remove in a follow-up once verified unused (the per-project Dashboard still consumes it via CostTable).
  • Full URL ↔ panel sync (writing ?card= on click-driven panel opens). Today only inbound deep-link + close-strip are implemented.
  • "Card not found" UX when a ?card=ID deep link refers to a card that was deleted or doesn't exist in this project.
  • TS TokenUsage interface is missing the model field that Go ships; align after the agent_costs cleanup.

mhersson added 10 commits May 18, 2026 05:34
- Sort projects alphabetically (was: by card count)
- Row link points at the project board, not the cost dashboard
- Drop the Status column and its dead derivation in utils.ts
- Add ModelCost struct and DashboardData.ModelCosts field
- Bucket cards with no model under "unknown" so totals reconcile
- Mirror the existing agent_costs aggregation in service_dashboard.go
- Rename the per-token rate type ModelCost -> ModelRate to free the
  ModelCost identifier for the dashboard aggregation
- Add ModelCost interface in web/src/types/index.ts
- Fold per-project model_costs in aggregateDashboards
- Cover same-model summing and distinct-model separation in tests
- Swap the first tab of CostAgentsPanel from "Cost by Agent" to "Models"
- Rename panel header to "Cost by model · Agents on duty"
- Add tooltip explaining the last-model-wins attribution
- Wire aggregated.model_costs through AllProjectsDashboard
- Open the CardPanel on mount when ?card=ID is in the URL
- Strip ?card= from the URL when the panel closes (replace history)
- Click-driven opens do not sync the URL (deferred follow-up)
- Row links point at /projects/{name}?card={id}, opening the panel via ProjectShell's receiver
- Delete the "Full breakdown" button (always pointed at one arbitrary project's cost dashboard)
- Aligns with the existing service.ModelRate type
- Removes nominal collision with the new service.ModelCost
  aggregation type used by the dashboard
- YAML key `token_costs` is unchanged
- Sort agent_costs and model_costs by estimated_cost_usd desc
  with tiebreak on id/model asc, for deterministic wire output
- Skip cards whose token usage is all-zero from the model bucket
  so they don't inflate the "unknown" model card_count
- Pull ?card= state machine out of ProjectShell into a dedicated
  hook (inbound, dead-link strip, outbound, project-change reset)
- Strip ?card= from the URL when the deep-linked card does not
  exist, so dead links don't linger across interactions
- URL-encode project and card_id when building TopCardsPanel
  links to harden against project names with reserved chars
- Cover the dead-link case with a new ProjectShell test
- Tab badge counts now show the full bucket length, not the
  display-cap (top-N is for the rendered list only)
- Drop the static "Cost by model · Agents on duty" header; the
  tab labels already describe the active view
- Rename the cost tab id 'cost' -> 'models' for consistency with
  what the tab actually shows
- Convert the info-icon disclosure to a real button so keyboard
  users can reach the tooltip; sibling of the tab button to avoid
  invalid nested-button structure
- Document model_costs alongside agent_costs in the dashboard
  endpoint reference, including the "unknown" bucket convention
@mhersson mhersson merged commit ce2a4c1 into main May 18, 2026
7 checks passed
@mhersson mhersson deleted the feat/ops-dashboard-fixes branch May 18, 2026 06:31
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant