Documentation
¶
Overview ¶
Package db persists parsed budgetclaw events and their per-day rollups in a local SQLite database.
Two tables:
events one row per (uuid) billable assistant message.
UUID is the primary key and dedupe guarantee: re-inserting
the same event is a no-op. Frozen historical fact.
rollups one row per (project, git_branch, day) aggregate.
Updated atomically with the event insert so the budget
evaluator can do O(1) reads for cap checks.
SQLite is opened with WAL journal mode on file-backed databases, enabling the CLI to read while the watcher writes from a separate process. The driver is modernc.org/sqlite (pure Go, no cgo) so the static-binary pledge holds.
Costs are passed in from the caller at insert time and stored as historical fact. The db package has no dependency on the pricing table. A future Anthropic rate change will not retroactively re-price old events.
Day boundaries in rollups are UTC. Budget evaluators that need local-timezone semantics should use RollupSum over a UTC time range computed from the user's tz, not the day string directly.
Index ¶
- type DB
- func (d *DB) Close() error
- func (d *DB) Insert(ctx context.Context, e *parser.Event, costUSD float64) error
- func (d *DB) Reset(ctx context.Context) error
- func (d *DB) RollupForDay(ctx context.Context, project, branch string, day time.Time) (*Rollup, error)
- func (d *DB) RollupSum(ctx context.Context, project, branch string, start, end time.Time) (*Rollup, error)
- func (d *DB) StatusByProject(ctx context.Context, start, end time.Time) ([]Rollup, error)
- type Rollup
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
This section is empty.
Types ¶
type DB ¶
type DB struct {
// contains filtered or unexported fields
}
DB wraps a *sql.DB with budgetclaw-specific methods.
func Open ¶
Open opens or creates the state database.
If path is empty, Open resolves the default location via paths.StateDir() (honoring XDG_STATE_HOME) and creates any missing parent directories. If path is the literal string ":memory:", Open returns an in-memory database suitable for tests.
The schema is applied on every call. Existing tables are not recreated thanks to IF NOT EXISTS.
func OpenMemory ¶
OpenMemory returns an in-memory database for tests. Equivalent to Open(":memory:") but avoids forcing test code to know the magic string.
func (*DB) Insert ¶
Insert stores a billable event and updates its rollup row inside a single transaction. Idempotent on e.UUID: re-inserting the same event is a no-op (the rollup is not double-counted).
costUSD is passed in so the db package stays independent of the pricing table. Callers should compute it via pricing.CostForModel before calling Insert.
func (*DB) Reset ¶ added in v0.1.4
Reset truncates the events and rollups tables so a subsequent backfill can re-attribute every historical event from scratch. Used by `budgetclaw backfill --rebuild` after a pricing-table correction lands: existing rollups are stuck at the old (wrong) rate because Insert is idempotent on uuid, so the only way to recompute them is to wipe and replay.
The tables are truncated inside a single transaction so a crash mid-reset cannot leave half-empty state behind.
func (*DB) RollupForDay ¶
func (d *DB) RollupForDay(ctx context.Context, project, branch string, day time.Time) (*Rollup, error)
RollupForDay returns the rollup row for a specific (project, branch, day). Returns (nil, nil) if the row does not exist — "nothing spent today" is a valid state, not an error.
func (*DB) RollupSum ¶
func (d *DB) RollupSum(ctx context.Context, project, branch string, start, end time.Time) (*Rollup, error)
RollupSum returns the sum across a date range for a single (project, branch). Range is inclusive on both ends. Returned Rollup has empty Day and (Project, GitBranch) copied from args so callers don't need to re-thread them.
Used by the budget evaluator for weekly/monthly caps, and by the CLI for "status --period=week" output.
func (*DB) StatusByProject ¶
StatusByProject returns rollup totals grouped by (project, branch) across a date range. Ordered by project then branch for deterministic CLI output.
Empty result is not an error — a user with nothing tracked yet gets a nil slice and no rows.
type Rollup ¶
type Rollup struct {
Project string
GitBranch string
Day string
EventCount int
InputTokens int
OutputTokens int
CacheReadTokens int
CacheWrite5mTokens int
CacheWrite1hTokens int
CostUSD float64
}
Rollup is one (project, branch, day) aggregate. For range-sum queries, Day is empty because the result spans multiple days.