Skip to content

feat(usage): break down usage by project - #9015

Draft
saphid wants to merge 4 commits into
pingdotgg:mainfrom
saphid:saphid/usage-project-upstream
Draft

saphid wants to merge 4 commits into
pingdotgg:mainfrom
saphid:saphid/usage-project-upstream

Conversation

@saphid

@saphid saphid commented Sep 1, 2026 •

Copy link
Copy Markdown
Contributor

Usage totals show which provider and model spent the money, but not which project. When several workspaces share one environment, that's the question users actually have.

Depends on #9014. This branch contains #9014's commits (d51a7bb37c, 139c20b07a, dcd5bb4246) followed by this PR's single commit dcbaf50868. Review only the project increment.

Rebased onto main 66129c6fd5 (2026-09-25). Main removed ProjectionProjectRepository.listAll as unused (#9917); this PR needs it, so the commit restores that one method verbatim. Main also deleted UsagePage.test.tsx (#13104, no static-markup tests), so the project-breakdown page tests that lived there were dropped with it; attribution, merge and filter logic keep their server and usageMerge coverage. Merge #9014 first; the PR stays in draft until then.

How it works

  • Server. Claude and Codex transcripts already record each session's working directory. The scanner now keeps that cwd (Codex follows turn_context changes). At scan time it resolves the cwd against the environment's project list (ProjectionProjectRepository.listAll, which includes soft-deleted projects so their past spend stays attributed). The deepest workspace root wins. Each bucket is tagged project, outside, or unknown. Grok logs carry no folder, and a project-list read failure also yields unknown, never a misleading "outside".
  • Contract. UsageBucket gains optional project, projectId, and projectAttribution; USAGE_CONTRACT_VERSION goes 5 → 6. Older servers keep merging, and their buckets count as unknown attribution. The scan cache goes v3 → v4 so cached records carry their cwd; this costs one full rescan after upgrade.
  • Shared merge. mergeUsage rolls buckets up into projects, keyed per environment, so equal ids or titles on two servers stay separate. An optional projectFilter narrows every other figure. Session counts can't be split by project, so they're hidden while a filter is active. Fully unpriced projects reuse main's unpriced treatment (fix(usage): flag unpriced model activity instead of showing $0.00 #11021) instead of showing $0.00.
  • Web. The breakdown gains a Project view, ranked by the selected metric. A project picker (All projects / each project / Outside projects) narrows the page; "All projects" clears it.
  • Mobile. Not in this PR. Mobile totals still merge correctly with the v6 contract; the project view can follow separately.

Screenshots

Isolated dev server, synthetic Claude transcripts for two projects (Atlas, Beacon) plus work in an unregistered folder, 30-day window.

Before (#9014 head 0f7cd7f00d): model and day breakdowns only.

Before: Usage page with Model and Day breakdowns

After (80985a75ad): Project breakdown. The rows add up to the $36.04 headline, and unregistered work appears as "Outside projects".

After: Project breakdown with Beacon, Atlas and Outside projects

After, Atlas selected in the picker: headline, chart, totals and table all narrow to $12.23.

After: Atlas filter narrows the whole page

Verification

  • After the rebase: vp test run apps/server/src/usage/ apps/server/src/persistence/ apps/web/src/components/usage/ apps/web/src/state/ packages/shared/src/usageMerge.test.ts packages/shared/src/usageFormat.test.ts: 52 files, 303 of 305 pass. The two failures (uses explicit account settings…, does not share an in-flight scan… in UsageService.test.ts) fail identically on unmodified main on macOS and are unrelated. New coverage: service-level attribution (project vs outside), unknown attribution on repository failure, the resolver's nesting/boundary/deleted-project rules, scan-cache cwd round trip, Codex cwd changes, and merge filtering/namespacing/unpriced projects.
  • Typecheck passes for t3, @t3tools/web, @t3tools/shared, @t3tools/contracts, @t3tools/mobile. vp lint and vp fmt pass on the touched files.
  • A cross-provider (Codex) review was skipped because Codex weekly quota was at 7%.

Rework note

This head replaces the earlier cumulative stack. The refresh-token cache bypass, retained refresh statuses, refresh error toasts/alerts, the segmented Input variant, and the three unshipped contract bumps (v6–v8) are gone. They weren't needed for project attribution; any that are still wanted belong in their own PRs.

Coordination trace: T3 thread 7227674d-e4cd-4b7a-b0d4-08e32d2e1e0e

Reworked with Claude Opus 5 in the Claude Code harness (via T3 Code).

🤖 Generated with Claude Code

@github-actions github-actions Bot added vouch:trusted PR author is trusted by repo permissions or the VOUCHED list. size:L 100-499 changed lines (additions + deletions). labels Sep 1, 2026
Comment thread apps/web/src/components/usage/UsagePage.tsx Outdated
Comment thread apps/server/src/usage/usageScanCache.ts Outdated
Comment thread packages/shared/src/usageMerge.ts Outdated
Comment thread apps/web/src/components/usage/UsagePage.tsx Outdated
@saphid
saphid force-pushed the saphid/usage-project-upstream branch from 684786f to 117bf75 Compare September 1, 2026 11:12
@github-actions github-actions Bot added size:XL 500-999 changed lines (additions + deletions). and removed size:L 100-499 changed lines (additions + deletions). labels Sep 1, 2026
Comment thread apps/server/src/usage/usageTranscripts.ts
Comment thread apps/web/src/components/usage/UsageProviderChart.tsx Outdated
Comment thread docs/user/usage.md Outdated
Comment thread apps/server/src/usage/UsageService.ts
@saphid
saphid force-pushed the saphid/usage-project-upstream branch from 117bf75 to 867cc41 Compare September 1, 2026 11:27
Comment thread apps/web/src/components/usage/UsagePage.tsx Outdated
@saphid
saphid force-pushed the saphid/usage-project-upstream branch 2 times, most recently from a59e015 to 6ca946c Compare September 1, 2026 11:56
Comment thread apps/web/src/components/usage/UsagePage.tsx
@saphid
saphid force-pushed the saphid/usage-project-upstream branch 2 times, most recently from 79c9fc2 to eb2a16a Compare September 1, 2026 12:29
Comment thread apps/server/src/usage/usageTranscripts.ts
Comment thread apps/web/src/components/usage/UsagePage.tsx
@saphid
saphid force-pushed the saphid/usage-project-upstream branch from eb2a16a to 7178d4d Compare September 1, 2026 12:46
Comment thread packages/shared/src/usageMerge.ts Outdated
@saphid
saphid force-pushed the saphid/usage-project-upstream branch 2 times, most recently from 82b25d2 to dce69bb Compare September 1, 2026 13:13
Comment thread apps/web/src/components/usage/UsagePage.tsx Outdated
@saphid
saphid force-pushed the saphid/usage-project-upstream branch from dce69bb to 13d4b24 Compare September 1, 2026 13:23
Comment thread apps/web/src/components/usage/UsagePage.tsx Outdated
@saphid
saphid force-pushed the saphid/usage-project-upstream branch from 13d4b24 to da35181 Compare September 1, 2026 13:31
Comment thread apps/web/src/components/usage/UsagePage.tsx Outdated
@github-actions github-actions Bot removed the size:XL 500-999 changed lines (additions + deletions). label Sep 5, 2026
@github-actions github-actions Bot added the size:XXL 1,000+ changed lines (additions + deletions). label Sep 5, 2026
Comment thread apps/mobile/src/features/usage/UsageRouteScreen.tsx Outdated
Comment thread apps/mobile/src/features/usage/UsageRouteScreen.tsx Outdated
Comment thread apps/server/src/usage/UsageService.ts Outdated
Comment thread packages/contracts/src/usage.ts Outdated
Comment thread apps/web/src/state/usage.ts Outdated
saphid added a commit to saphid/t3code that referenced this pull request Sep 11, 2026
The Usage page can drill project, provider and model totals down to the
threads and subagents that produced them. The server maps provider
sessions and worktrees to T3 threads with bounded rows, and the web and
mobile clients filter by environment, project and provider ownership.

Includes the stacked usage time-range (pingdotgg#9014) and project-breakdown
(pingdotgg#9015) changes this feature builds on.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
saphid added a commit to saphid/t3code that referenced this pull request Sep 11, 2026
Carries the cumulative content of pingdotgg#9014, pingdotgg#9015 and pingdotgg#9016, rebased onto
current main. This PR's own change is the following commit.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
saphid added a commit to saphid/t3code that referenced this pull request Sep 11, 2026
Price cache creation as its own estimated component in usage summaries,
the model/project/thread breakdowns and the daily thread chart, keeping
unknown cache-write cost unavailable rather than zero.

Carries the stacked usage work from pingdotgg#9014, pingdotgg#9015, pingdotgg#9016 and pingdotgg#9017, rebased
onto main and reconciled with upstream's per-model unpriced flag (pingdotgg#11021).

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@saphid
saphid force-pushed the saphid/usage-project-upstream branch from a3eb8de to 80985a7 Compare September 11, 2026 07:22
@github-actions github-actions Bot added size:XL 500-999 changed lines (additions + deletions). and removed size:XXL 1,000+ changed lines (additions + deletions). labels Sep 11, 2026
@saphid
saphid force-pushed the saphid/usage-project-upstream branch from 80985a7 to 6d45473 Compare September 24, 2026 12:23
@github-actions github-actions Bot added size:XXL 1,000+ changed lines (additions + deletions). and removed size:XL 500-999 changed lines (additions + deletions). labels Sep 24, 2026
@saphid
saphid force-pushed the saphid/usage-project-upstream branch from 6d45473 to dcbaf50 Compare September 24, 2026 20:37
saphid and others added 4 commits September 26, 2026 07:48
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Main's shadcn(no-restyle) rule forbids restyling <Input>, so the date range
field styling moves to a plain wrapper span with the same classes, and the
zoom hint uses the text-3xs token instead of text-[10px].

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Usage totals now split by the T3 project whose workspace root contains each
session's working directory. The server records the cwd from Claude and Codex
transcripts, resolves it against the project list at scan time, and tags each
bucket as project, outside, or unknown. The web Usage page gains a Project
breakdown and a project picker that narrows every figure on the page.

ProjectionProjectRepository.listAll is restored (removed as unused in pingdotgg#9917)
because project attribution reads every project, including soft-deleted ones.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@saphid
saphid force-pushed the saphid/usage-project-upstream branch from dcbaf50 to 1cc9679 Compare September 25, 2026 21:58

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:XXL 1,000+ changed lines (additions + deletions). vouch:trusted PR author is trusted by repo permissions or the VOUCHED list.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant