feat(ops-dashboard): cost-by-model, board deep-links, cleaner projects list#99
Merged
Conversation
- 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
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
uninformative Status column, link each row to the project board (was:
per-project cost dashboard).
rows now deep-link to
/projects/{name}?card={id}and open theCardPanelon arrival.recorded model bucket under
unknown); panel header reads "Cost bymodel · Agents on duty". An info tooltip surfaces the "last-model-wins"
attribution limit. "Agents on Duty" tab is unchanged.
ProjectShelllearned a?card=IDdeep-linkreceiver (open panel on mount, strip the param on close) via the
codebase's in-render state-marker pattern.
Test plan
/. Projects list is alphabetical and has no Status column.CardPanelpre-open for that card.?card=disappears from the URL; back button is not polluted./projects/A?card=A-1to/projects/B?card=B-2) opens the second panel correctly.Out of scope / follow-ups
agent_costsfield onDashboardData(Go + TS) and its fold inaggregateDashboards— kept for backward compat in this PR; remove in a follow-up once verified unused (the per-projectDashboardstill consumes it viaCostTable).?card=on click-driven panel opens). Today only inbound deep-link + close-strip are implemented.?card=IDdeep link refers to a card that was deleted or doesn't exist in this project.TokenUsageinterface is missing themodelfield that Go ships; align after the agent_costs cleanup.