Repository navigation
Open
Labels
app/pm-stackpm-stack (Pad/Grist) in developing-today/codepm-stack (Pad/Grist) in developing-today/codeeffort/mMedium: expected under 1 day of workMedium: expected under 1 day of workkind/featureNew feature or capability requestNew feature or capability requestpriority/normalOrdinary priority (the default)Ordinary priority (the default)source/creatorFiled by the new-issue-creator jobFiled by the new-issue-creator jobtask/triageQueue packet kind: verify, reproduce, and route this itemQueue packet kind: verify, reproduce, and route this item
Description
Part of epic #1184; implementation follow-up of design issue #1259 (spend rollups). Design note: doc/spend-rollups-design-1259.md (PR linked from #1259).
Scope
Build the spend-attribution projection in the pm-stack aggregator: a versioned, pure projection that turns existing per-session usage records into per-issue / per-epic / per-project spend facts. Consume existing sources only — do NOT build new collectors:
pm-stack/opencode-usage-exportdocuments (per-session tokens + cost,parentId,directory, model/provider) — slice 1 of External-system observability: unified PM + queue + quota attribution (webhooks, hot list, session IDs, projections) #2072.- The aggregator usage snapshot store (
pm-stack/aggregator/src/snapshots.ts, append-onlyusage-YYYY-MM.jsonl).
Attribution rules (per the design note)
- Queue-bound sessions resolve exactly: lane-ledger session binding → queue task → issue number.
- Fleet-worktree sessions resolve from the worktree directory name (
~/git/fleet/<task>-<issue>-<slug>). - Sub-sessions roll up to their root session (
parentId); never double-counted. - Epic/project resolution reuses the existing hierarchy seams (
epicRef/ parent chains inpm-stack/aggregator/src/model.ts, rollups insrc/epics.ts); Project Area is a display grouping, not an attribution key. - Anything unresolved lands in an explicit unattributed bucket with a reason code (
no-binding,no-worktree,parent-left-pool,source-gap). Unattributed spend is shown, never dropped or guessed onto an epic. - Every projected fact carries
attributionVersion; raw source records are never rewritten.
Acceptance criteria
- Projection is a pure, tested module: same inputs + same version → identical output.
- Unit tests cover: bound session, worktree session, sub-session rollup, each unattributed reason code, and a session whose parent chain leaves the fetched pool.
- Totals reconcile: sum(attributed) + unattributed = source totals, per unit, asserted in tests.
- No new collector, exporter, or instrumentation in OpenCode/OpenChamber.
Checks
bun testandbunx tsc --noEmitinpm-stack/aggregatorpass (CI on the PR).
Metadata
Metadata
Assignees
Labels
app/pm-stackpm-stack (Pad/Grist) in developing-today/codepm-stack (Pad/Grist) in developing-today/codeeffort/mMedium: expected under 1 day of workMedium: expected under 1 day of workkind/featureNew feature or capability requestNew feature or capability requestpriority/normalOrdinary priority (the default)Ordinary priority (the default)source/creatorFiled by the new-issue-creator jobFiled by the new-issue-creator jobtask/triageQueue packet kind: verify, reproduce, and route this itemQueue packet kind: verify, reproduce, and route this item
Activity
dezren39 commentedon Nov 3, 2026
agent-note:v1
state: triaged (coverage pass)
Coverage pass 2026-11-03: reviewed in the fleet-wide per-issue pass (drift / overlap / already-handled / stale / blockers / ordering). Verdict: still live; queued in repo-health-code-pilot as triage at priority 40 (priority/normal). If this is already handled at current main, the triage closes it with evidence; if it needs splitting, the triage files the split.
Found via: coverage pass 2026-11-03
dezren39 commentedon Nov 3, 2026
🤖 Agent note · picked up
1 remaining item
dezren39 commentedon Nov 3, 2026
🤖 Agent note · done (triage)
Verdict: still live, unblocked, fully specified. Verified at origin/main
dda1def97:attributionVersionappears nowhere inpm-stack/aggregator(src or tests), and there is no spend/attribution projection module inaggregator/src— the grep hits for "attribution" are quota/saas extraction, not this projection.doc/spend-rollups-design-1259.mdis on main.pm-stack/opencode-usage-export/opencode-usage-export.mjsemits per-session docs withparentIdanddirectory;pm-stack/aggregator/src/snapshots.tsis the append-onlyusage-YYYY-MM.jsonlstore; hierarchy seams exist (model.ts:109 epicRef, rollups inepics.ts).Routing: implementation enqueued as task 8769 (priority 40) with this issue's scope, attribution rules, and acceptance carried verbatim — pure versioned projection, unattributed bucket with the four reason codes, reconciliation invariant asserted in tests,
bun test+bunx tsc --noEmitas the checks. This issue stays open for task 8769.dezren39 commentedon Nov 3, 2026
🤖 Agent note · picked up
dezren39 commentedon Nov 3, 2026
🤖 Agent note · handed off
d577df915f61669eb09d798c1627f9d2a1409c7d)./api/healthassertions fail; full typecheck reports the existingHealthView.conflictsfixture error.