P4: budget battery — DBBudget (rolling 7-day) over run.Budget
Second Tier-2 battery, plugging into run.Ports.Budget: - budget.go: skillexec's BudgetTracker / NoOpBudget / DBBudget moved clean (stdlib only). Check/Commit match run.Budget exactly (compile-time proof in run.go: NoOpBudget and *DBBudget are run.Budget). - storage.go: the BudgetStorage seam + SkillBudget domain, split out of mort's GORM file (the GORM impl stays in mort). - memory.go: NewMemory() — zero-dependency in-process BudgetStorage with the 7-day rolling-window rollover in Add. Tests: per-user cap enforced, window rolls over after 7 days, NoOp always allows. CI invariant: core imports ZERO from the budget battery. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,33 @@
|
||||
package budget
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
)
|
||||
|
||||
// BudgetStorage is the persistence seam behind DBBudget: one budget row per
|
||||
// user, with an atomic Add that rolls the 7-day window over transparently. Mort
|
||||
// backs this with GORM/MySQL (the skill_budgets table); Memory() is the
|
||||
// zero-dependency default; contrib/store adds a durable SQLite one.
|
||||
type BudgetStorage interface {
|
||||
// Initialize runs any schema setup. Safe to call repeatedly.
|
||||
Initialize(ctx context.Context) error
|
||||
// Get returns the user's current budget row, or (nil, nil) if none exists.
|
||||
Get(ctx context.Context, userID string) (*SkillBudget, error)
|
||||
// Add increments seconds_used + runs_count atomically, rolling the window
|
||||
// over when WindowStart is older than 7 days (reset to now, fresh count).
|
||||
// Creates the row if absent.
|
||||
Add(ctx context.Context, userID string, secondsUsed float64, now time.Time) error
|
||||
}
|
||||
|
||||
// SkillBudget is one user's rolling-window usage row.
|
||||
type SkillBudget struct {
|
||||
UserID string
|
||||
WindowStart time.Time
|
||||
SecondsUsed float64
|
||||
RunsCount int
|
||||
UpdatedAt time.Time
|
||||
}
|
||||
|
||||
// budgetWindow is the rolling window length the storage rolls over at.
|
||||
const budgetWindow = 7 * 24 * time.Hour
|
||||
Reference in New Issue
Block a user