Skip to content

feat(memory): wire usage_events table — billing pipeline scaffolded but never called #2429

@alekspetrov

Description

@alekspetrov

Problem

usage_events table is created on store init (internal/memory/store.go:171-187) and the RecordUsageEvent / RecordExecutionUsage writers exist in internal/memory/metering.go, but no production code calls them:

$ grep -rn "RecordUsageEvent\|RecordExecutionUsage" internal/ cmd/ --include="*.go" | grep -v "_test.go\|metering.go"
# (no results)

Result: SELECT COUNT(*) FROM usage_events0 rows, ever. The billing/metering pipeline is dead code.

All cost data still lives in executions.estimated_cost_usd (legacy single-row-per-execution model), which:

  • Doesn't separate task vs. token vs. compute vs. api_call event types (the whole reason usage_events was designed)
  • Has no user_id (multi-tenancy can't aggregate per-user spend)
  • Has no metadata field for per-event context (cache hit ratio, model variant, etc.)

Suggested fix

In internal/executor/dispatcher.go worker loop (around line 669, where SaveExecutionMetrics is called):

if result != nil && result.TokensTotal > 0 {
    if err := w.store.RecordExecutionUsage(exec.ID, exec.UserID, exec.ProjectPath, result); err != nil {
        w.log.Error("Failed to record usage event", slog.Any("error", err))
    }
}

RecordExecutionUsage already exists at metering.go:130-187 and emits 3 events per execution (task, token, compute). It just needs a caller.

Why it matters

  • Multi-user / team mode (per team_members, teams tables) needs per-user cost rollups. usage_events.user_id is the designed pivot.
  • Future budget enforcement / billing export depends on this table.
  • We currently can't answer "how much did GLM vs. Claude cost us this week" without parsing executions.metadata.

Verification

After fix: run any task, then SELECT * FROM usage_events ORDER BY timestamp DESC LIMIT 5 should show 3 rows per execution (task + token + compute event types).

Scope

Small: ~1 call site in dispatcher + plumb UserID through Execution struct if missing. Tests already exist in metering_test.go.

Refs

  • internal/memory/metering.go:110,130-187
  • internal/memory/store.go:171-187
  • internal/executor/dispatcher.go:669

Metadata

Metadata

Assignees

No one assigned

    Labels

    pilotPilot AI will work on thispilot-donePilot completed this

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions