feat: git hygiene (branch/preview reaping + cleanup sweep) and panel charts; work sessions under Git (#548)

* feat(panel): session-start, 7d overview spend, and 30d business spend charts

* feat(panel): surface work sessions as a Git page tab (route was orphaned)

* feat(git): reap spent task branches and render previews at lifecycle chokepoints; guarded stale-branch sweep

* feat(panel): stale-branch cleanup button on the Git page

* fix(git,panel): cursor-resumable sweep, force-delete spent refs, local filter state

* docs(map,rag): branch/preview reaping, cleanup sweep, git-tab work sessions, wave-2 charts

---------

Co-authored-by: Renn F <rennf93@users.noreply.github.com>
This commit is contained in:
Renzo F
2026-07-18 00:44:48 +02:00
committed by GitHub
co-authored by Renn F
parent 885d6bbe83
commit 496c24d186
39 changed files with 1841 additions and 222 deletions
@@ -32,6 +32,13 @@ vi.mock("@/hooks/use-tasks", () => ({
refetch: vi.fn(),
}),
}));
vi.mock("@/hooks/use-usage", () => ({
useUsageTimeSeries: () => ({
data: undefined,
isLoading: false,
refetch: vi.fn(),
}),
}));
vi.mock("../team-health-cards", () => ({
TeamHealthCards: () => <div>TeamHealthCardsStub</div>,
@@ -87,6 +94,9 @@ vi.mock("../usage-overview-panel", () => ({
vi.mock("../scorecard-overview-panel", () => ({
ScorecardOverviewPanel: () => <div>ScorecardOverviewPanelStub</div>,
}));
vi.mock("../cost-trend-chart", () => ({
CostTrendChart: () => <div>CostTrendChartStub</div>,
}));
import { CommandCenter } from "../command-center";
@@ -106,6 +116,7 @@ describe("CommandCenter", () => {
"AuditorAlertsPanelStub",
"UsageOverviewPanelStub",
"ScorecardOverviewPanelStub",
"CostTrendChartStub",
"TeamHealthCardsStub",
"CeoApprovalQueueStub",
"StrategySignalsPanelStub",
@@ -0,0 +1,37 @@
import { describe, it, expect } from "vitest";
import { render, screen } from "@testing-library/react";
import { CostTrendChart } from "../cost-trend-chart";
import type { UsageTimePoint } from "@/types";
function buildPoint(overrides: Partial<UsageTimePoint> = {}): UsageTimePoint {
return {
bucket: new Date().toISOString(),
tokens_input: 1000,
tokens_output: 500,
total_tokens: 1500,
cost_usd: 1.23,
...overrides,
};
}
describe("CostTrendChart", () => {
it("renders the card title", () => {
render(<CostTrendChart data={[buildPoint()]} isLoading={false} />);
expect(screen.getByText("Spend Trend (7d)")).toBeInTheDocument();
});
it("shows an empty state when there is no data", () => {
render(<CostTrendChart data={[]} isLoading={false} />);
expect(screen.getByText("No usage data")).toBeInTheDocument();
});
it("shows an empty state when data is undefined", () => {
render(<CostTrendChart data={undefined} isLoading={false} />);
expect(screen.getByText("No usage data")).toBeInTheDocument();
});
it("does not show the empty state while loading", () => {
render(<CostTrendChart data={[]} isLoading />);
expect(screen.queryByText("No usage data")).not.toBeInTheDocument();
});
});
@@ -7,6 +7,7 @@ import {
useRecentActivity,
} from "@/hooks/use-dashboard";
import { useTasks } from "@/hooks/use-tasks";
import { useUsageTimeSeries } from "@/hooks/use-usage";
import { usePageRefresh } from "@/hooks";
import { TeamHealthCards } from "./team-health-cards";
import { KeyMetricsPanel } from "./key-metrics-panel";
@@ -25,6 +26,7 @@ import type { Activity } from "./activity-item";
import { Button } from "@/components/ui/button";
import { UsageOverviewPanel } from "./usage-overview-panel";
import { ScorecardOverviewPanel } from "./scorecard-overview-panel";
import { CostTrendChart } from "./cost-trend-chart";
import {
Tooltip,
TooltipContent,
@@ -62,6 +64,11 @@ export function CommandCenter() {
isError: errorActivity,
refetch: refetchActivity,
} = useRecentActivity(24);
const {
data: costTrend,
isLoading: loadingCostTrend,
refetch: refetchCostTrend,
} = useUsageTimeSeries("7d");
const { register, unregister } = usePageRefresh();
@@ -79,6 +86,9 @@ export function CommandCenter() {
() => {
void refetchActivity();
},
() => {
void refetchCostTrend();
},
];
callbacks.forEach((cb) => register(cb));
return () => {
@@ -91,6 +101,7 @@ export function CommandCenter() {
refetchFlags,
refetchTasks,
refetchActivity,
refetchCostTrend,
]);
const hasError = errorOverview || errorFlags || errorTasks || errorActivity;
@@ -156,6 +167,8 @@ export function CommandCenter() {
<ScorecardOverviewPanel />
</div>
<CostTrendChart data={costTrend} isLoading={loadingCostTrend} />
{/* Section 2: Team Health (team cards + Task Intake + Secretary) */}
<section>
<HelpTip label="Per-team blocked ratio and throughput, plus the on-demand agents">
@@ -0,0 +1,109 @@
"use client";
import {
AreaChart,
Area,
XAxis,
YAxis,
CartesianGrid,
Tooltip,
ResponsiveContainer,
} from "recharts";
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
import { Skeleton } from "@/components/ui/skeleton";
import { HelpTip } from "@/components/ui/help-tip";
import type { UsageTimePoint } from "@/types";
interface CostTrendChartProps {
data: UsageTimePoint[] | undefined;
isLoading: boolean;
}
function formatBucket(bucket: string): string {
const d = new Date(bucket);
return d.getMonth() + 1 + "/" + d.getDate();
}
function fmtCost(n: number): string {
return "$" + n.toFixed(2);
}
/**
* Compact daily spend trend for the Command Center landing page — reuses
* GET /usage/time-series (period=7d, daily buckets) so the CEO sees where
* the current-period totals in UsageOverviewPanel came from at a glance.
*/
export function CostTrendChart({ data, isLoading }: CostTrendChartProps) {
const chartData = (data ?? []).map((p) => ({
day: formatBucket(p.bucket),
Cost: p.cost_usd,
}));
return (
<Card>
<CardHeader className="pb-2">
<HelpTip label="Provider-priced agent-session cost per day over the last 7 days">
<CardTitle className="text-base">Spend Trend (7d)</CardTitle>
</HelpTip>
</CardHeader>
<CardContent>
{isLoading ? (
<Skeleton className="h-40 w-full" />
) : chartData.length === 0 ? (
<p className="text-sm text-muted-foreground text-center py-10">
No usage data
</p>
) : (
<ResponsiveContainer width="100%" height={160}>
<AreaChart
data={chartData}
margin={{ top: 4, right: 8, left: 0, bottom: 0 }}
>
<defs>
<linearGradient id="fillCost" x1="0" y1="0" x2="0" y2="1">
<stop
offset="5%"
stopColor="var(--chart-3)"
stopOpacity={0.8}
/>
<stop
offset="95%"
stopColor="var(--chart-3)"
stopOpacity={0.1}
/>
</linearGradient>
</defs>
<CartesianGrid strokeDasharray="3 3" className="opacity-20" />
<XAxis
dataKey="day"
tick={{ fontSize: 10 }}
axisLine={false}
tickLine={false}
/>
<YAxis
tickFormatter={fmtCost}
tick={{ fontSize: 10 }}
axisLine={false}
tickLine={false}
width={44}
/>
<Tooltip
formatter={(value) => [
fmtCost(typeof value === "number" ? value : 0),
"Cost",
]}
contentStyle={{ fontSize: 12 }}
/>
<Area
type="monotone"
dataKey="Cost"
stroke="var(--chart-3)"
fill="url(#fillCost)"
/>
</AreaChart>
</ResponsiveContainer>
)}
</CardContent>
</Card>
);
}