Skip to content

feat(pm-stack): project spend attribution facts (#4822) - #5164

Open
dezren39 wants to merge 2 commits into
mainfrom
queue/8769-4822-spend-attribution-projection
Open

dezren39 wants to merge 2 commits into
mainfrom
queue/8769-4822-spend-attribution-projection

Conversation

@dezren39

@dezren39 dezren39 commented Nov 3, 2026 •

Copy link
Copy Markdown
Member

Summary

usage sessions
  → group children under root (sum each session once)
  → exact lane binding, else unambiguous fleet worktree, else reason-coded unattributed
  → issue / epic / explicitly mapped project facts

Adds a pure v1 projection in pm-stack/aggregator/src/spend-attribution.ts. It conserves input, output, reasoning, cache-read, and cache-write tokens independently; failed/missing exports stay unknown (source-gap), not zero. Area is not used as a project key, and list-price estimates are not treated as dollars.

Evidence

  • Before: no spend-attribution projection/version existed in the aggregator.
    After: bun test test/spend-attribution.test.ts — 7 pass; focused strict TypeScript check of the new module and tests — pass; git diff --check — pass.
  • Full local bun test: 1425 pass, 2 failures in existing /api/health assertions in test/api-health-comments.test.ts (outside this diff). Full bunx tsc --noEmit reports the existing test/overview.test.ts HealthView.conflicts fixture error; the changed files pass the focused strict check.

Merge Danger

Door: two-way

Blast Radius: low — new pure module and unit tests; no collector, exporter, or UI wiring changes.

Closes #4822.


Autofix with [code]smith
Open in [code]smith · Need help on this PR? Tag @codesmith-bot with what you need.

@dezren39
dezren39 requested a review from a team as a code owner November 3, 2026 21:36
@coderabbitai

coderabbitai Bot commented Nov 3, 2026 •

Copy link
Copy Markdown

Warning

Review limit reached

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

Next included review available in 55 minutes.

Check out review usage here.

View limit details

Limit details: You’ve used the included review currently available.

Learn how review limits work.

Review configuration:

Run configuration
  • Source mode: Auto
  • Repository: Unavailable
  • Branch: Unavailable
  • File: No configuration file from this repository applied
  • Commit: Unavailable
  • Layers: defaults
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: 6d93f4f1-0322-4595-a172-1aaf688845b9
Commits

Reviewing files that changed from the base of the PR and between 464467b and d577df9.

Files selected for processing: 2
  • pm-stack/aggregator/src/spend-attribution.ts
  • pm-stack/aggregator/test/spend-attribution.test.ts
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

Share

CodeRabbit Verify

  • Run CodeRabbit Verify to test the changes in this pull request.

@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Nov 3, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-11-03T21:40:06.168004Z ab4c62f PR opened
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: ab4c62fe30

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +208 to +210
const tokens = sumTokens(group.sessions.map((session) => session.tokens));
const providerID = root?.providerID ?? group.sessions[0]?.providerID ?? null;
const modelID = root?.modelID ?? group.sessions[0]?.modelID ?? null;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Preserve provider and model boundaries when grouping child usage

When a child session uses a different provider or model from its root, this sums both sessions' tokens but labels the entire fact with the root's provider/model. For example, an OpenAI root with an Anthropic child produces an OpenAI-tagged fact containing Anthropic consumption, while reconciliation still reports success. This loses the per-pool accounting required by doc/spend-rollups-design-1259.md (§Units), and downstream consumers cannot recover the breakdown from these facts. Retain the root's issue attribution while splitting usage facts by provider/model.

Useful? React with 👍 / 👎.

const tokens = sumTokens(group.sessions.map((session) => session.tokens));
const providerID = root?.providerID ?? group.sessions[0]?.providerID ?? null;
const modelID = root?.modelID ?? group.sessions[0]?.modelID ?? null;
const binding = root ? bindingsBySession.get(root.id) : undefined;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Consult exact bindings even when the root session is absent

When an exported child references a parent outside the source window, collectRootGroups retains that missing parent's ID as group.rootSessionId, but this guard prevents looking up its binding. Thus a supplied binding for that exact ID is discarded and all child usage becomes unattributed despite authoritative issue information being available. This is a realistic input because opencode-usage-export.mjs filters sessions independently by time_updated >= ?. Look up the binding using group.rootSessionId regardless of whether the root's session row exists; the missing-parent reason can still record the incomplete source context.

Useful? React with 👍 / 👎.

@dezren39

dezren39 commented Nov 7, 2026

Copy link
Copy Markdown
Member Author

Stale-PR warden: no activity for 3 days. Resolution task: #6912 — this PR should be merged, fixed, or closed.

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

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

impl(pm-stack): spend attribution projection — session to issue/epic resolution, unattributed shown (#1259 slice 1)

1 participant