Five-gate fail-closed admission
apps/backend/internal/office/service/budget_admission.go ↗Runs the five gates in order and returns false only after driving the run to its terminal, deferred, or cancelled state.
Gate sequence
func (si *SchedulerIntegration) admitRun(ctx context.Context, run *models.Run, agent *models.AgentInstance) bool {
provenance := shared.ClassifyRunProvenance(run.Reason)
if agent.WorkspaceID == "" {
incBudgetCancelledNoWorkspace(provenance)
return si.cancelBudgetRun(ctx, run, agent, "no_resolvable_workspace", "run_budget_workspace_unresolvable", nil)
}
if si.svc.budgetChecker == nil {
if provenance == shared.RunProvenanceAttended {
return true
}
incBudgetBlockedAbsentEvaluator(provenance)
return si.cancelBudgetRun(ctx, run, agent, "no_budget_evaluator", "run_budget_no_evaluator", nil)
}
projectID, res := si.resolveRunProject(ctx, run.Payload)
// ... gates 3-5 follow
result, err := si.svc.EvaluatePreLaunch(ctx, agent.WorkspaceID, agent.ID, projectID, hasProject, provenance, now)
if err != nil {
var upErr *models.UnevaluatedPolicyError
if errors.As(err, &upErr) {
return si.admitBudgetDeferral(ctx, run, agent, budgetDeferralUnevaluatedPolicy, upErr.PolicyID)
}
return si.admitBudgetDeferral(ctx, run, agent, budgetDeferralEvaluatorFault, "")
}
if result.Decision != models.PreLaunchDecisionLaunch {
return si.finishPolicyBlock(ctx, run, agent, result.DecidingPolicy)
}
return si.admitDefaultCeilingGate(ctx, run, agent, provenance, result, degradedAdmitted, now)
}
Default ceiling gate
func (si *SchedulerIntegration) admitDefaultCeilingGate(ctx context.Context, run *models.Run, agent *models.AgentInstance, provenance shared.RunProvenance, result models.PreLaunchResult, degradedAdmitted bool, now time.Time) bool {
if provenance != shared.RunProvenanceUnattended || result.WorkspaceDailyBlockingSuperseded {
if degradedAdmitted {
incBudgetAdmittedDegradedWindow(provenance)
}
return true
}
def, defErr := si.svc.EvaluateDefaultCeiling(ctx, agent.WorkspaceID, now)
if defErr != nil {
return si.admitBudgetDeferral(ctx, run, agent, budgetDeferralEvaluatorFault, "")
}
if def.LimitExceeded || def.DegradationBlocked {
return si.finishPolicyBlock(ctx, run, agent, &def)
}
incBudgetAdmittedDefault(provenance)
return true
}
Two-phase pre-launch evaluation
apps/backend/internal/office/costs/prelaunch.go ↗Evaluates every applicable policy up front, then selects the first blocker by created_at, so a later error cannot hide behind an early block.
Window start
func windowStart(period models.BudgetPeriod, at time.Time) (start time.Time, ok bool) {
u := at.UTC()
switch period {
case models.BudgetPeriodDaily:
return time.Date(u.Year(), u.Month(), u.Day(), 0, 0, 0, 0, time.UTC), true
case models.BudgetPeriodMonthly:
return time.Date(u.Year(), u.Month(), 1, 0, 0, 0, 0, time.UTC), true
case models.BudgetPeriodYearly:
return time.Date(u.Year(), time.January, 1, 0, 0, 0, 0, time.UTC), true
case models.BudgetPeriodTotal:
return time.Time{}, true
default:
return time.Time{}, false
}
}
Evaluate then select
func (s *CostService) EvaluatePreLaunch(ctx context.Context, workspaceID, agentInstanceID, projectID string, hasProject bool, provenance shared.RunProvenance, at time.Time) (PreLaunchResult, error) {
policies, err := s.repo.ListBudgetPolicies(ctx, workspaceID)
if err != nil {
return PreLaunchResult{}, err
}
applicable := preLaunchApplicablePolicies(policies, agentInstanceID, projectID, hasProject)
results := make([]PreLaunchPolicyResult, 0, len(applicable))
survivors := make([]*PreLaunchPolicyResult, 0, len(applicable))
for _, p := range applicable {
result, survives, evalErr := s.evaluateOnePolicy(ctx, workspaceID, p, at, provenance)
if evalErr != nil {
return PreLaunchResult{}, evalErr
}
results = append(results, result)
if survives {
survivors = append(survivors, &results[len(results)-1])
}
}
sort.Slice(survivors, func(i, j int) bool {
if !survivors[i].CreatedAt.Equal(survivors[j].CreatedAt) {
return survivors[i].CreatedAt.Before(survivors[j].CreatedAt)
}
return survivors[i].PolicyID < survivors[j].PolicyID
})
decision, decidingPolicy := selectPreLaunchDecision(survivors)
return PreLaunchResult{Decision: decision, DecidingPolicy: decidingPolicy, Policies: results, WorkspaceDailyBlockingSuperseded: workspaceDailyBlockingSuperseded(survivors)}, nil
}
Built-in default ceiling
apps/backend/internal/office/costs/default_ceiling.go ↗Provides a workspace daily ceiling that applies when no blocking daily policy exists, using the same spend window as policies.
Default constant and accessors
const DefaultCeilingSubcents int64 = 500_000
func (s *CostService) GetWorkspaceBudgetDefault(ctx context.Context, workspaceID string) (int64, error) {
limitSubcents, found, err := s.repo.GetWorkspaceBudgetDefault(ctx, workspaceID)
if err != nil {
return 0, err
}
if !found {
return DefaultCeilingSubcents, nil
}
return limitSubcents, nil
}
func (s *CostService) SetWorkspaceBudgetDefault(ctx context.Context, workspaceID string, limitSubcents int64) error {
if limitSubcents <= 0 {
return fmt.Errorf("%w: limit_subcents must be positive, got %d", ErrInvalidBudgetPolicy, limitSubcents)
}
return s.repo.SetWorkspaceBudgetDefault(ctx, workspaceID, limitSubcents)
}
Evaluation as daily blocking policy
func (s *CostService) EvaluateDefaultCeiling(ctx context.Context, workspaceID string, at time.Time) (PreLaunchPolicyResult, error) {
limitSubcents, err := s.GetWorkspaceBudgetDefault(ctx, workspaceID)
if err != nil {
return PreLaunchPolicyResult{}, err
}
start, _ := windowStart(models.BudgetPeriodDaily, at)
window, err := s.repo.SpendWindowForWorkspace(ctx, workspaceID, start, true, at)
if err != nil {
return PreLaunchPolicyResult{}, fmt.Errorf("spend window for default ceiling: %w", err)
}
return PreLaunchPolicyResult{
IsDefault: true,
ScopeType: models.BudgetScopeWorkspace,
Period: models.BudgetPeriodDaily,
ActionOnExceed: models.BudgetActionBlockNewTasks,
LimitSubcents: limitSubcents,
PricedSubcents: window.PricedSubcents,
Degraded: window.Degraded,
LimitExceeded: window.PricedSubcents >= limitSubcents,
DegradationBlocked: degradationBlocks(window.Degraded, window.PricedSubcents, limitSubcents, shared.RunProvenanceUnattended),
}, nil
}
Pricing degradation at 50 percent
apps/backend/internal/office/costs/degradation.go ↗Blocks an unattended run when an unpriced event exists and priced spend already reaches half the limit, without overflow.
Degradation check
func degradationBlocks(degraded bool, pricedSubcents, limitSubcents int64, provenance shared.RunProvenance) bool {
return degraded && provenance == shared.RunProvenanceUnattended && pricedSubcents >= limitSubcents-pricedSubcents
}
Scope-correct spend windows
apps/backend/internal/office/repository/sqlite/spendwindow.go ↗Shares one query shape for priced spend and degraded flag, scoped by workspace, agent, or project, with a half-open window.
Shared query
func (r *Repository) spendWindowQuery(ctx context.Context, scopeWhere string, scopeArgs []interface{}, start time.Time, hasStart bool, before time.Time) (models.SpendWindow, error) {
query := `
SELECT
COALESCE(SUM(CASE WHEN e.cost_source = 'unpriced' THEN 0 ELSE e.cost_subcents END), 0) AS priced_subcents,
COALESCE(SUM(CASE WHEN e.cost_source = 'unpriced' THEN 1 ELSE 0 END), 0) > 0 AS degraded
FROM office_cost_events e
` + scopeWhere + ` AND e.occurred_at < ?`
args := append(append([]interface{}{}, scopeArgs...), before.UTC())
if hasStart {
query += ` AND e.occurred_at >= ?`
args = append(args, start.UTC())
}
var out models.SpendWindow
err := r.ro.QueryRowxContext(ctx, r.ro.Rebind(query), args...).Scan(&out.PricedSubcents, &out.Degraded)
return out, err
}
Workspace scope via agent_profiles
func (r *Repository) SpendWindowForWorkspace(ctx context.Context, workspaceID string, start time.Time, hasStart bool, before time.Time) (models.SpendWindow, error) {
const where = `JOIN agent_profiles a ON a.id = e.agent_profile_id WHERE a.workspace_id = ?`
return r.spendWindowQuery(ctx, where, []interface{}{workspaceID}, start, hasStart, before)
}
Run provenance classification
apps/backend/internal/office/shared/runprovenance.go ↗Maps a run reason to attended or unattended using a 20-literal allowlist, with unattended as the safe default.
Allowlist and classifier
var attendedRunReasons = map[string]struct{}{
"task_assigned": {},
"task_comment": {},
"task_review_requested": {},
"task_changes_requested": {},
"task_blockers_resolved": {},
"task_children_completed": {},
"approval_resolved": {},
"routine_dispatch_event": {},
"manual_resume_after_failure": {},
"task_mentioned": {},
"task_reopened": {},
"task_reopened_via_comment": {},
"task_unblocked": {},
"task_ready_to_close": {},
"stage_pending": {},
"stage_changes_requested": {},
"review_started": {},
"approval_started": {},
"blockers_resolved": {},
"children_completed": {},
}
func ClassifyRunProvenance(reason string) RunProvenance {
if _, ok := attendedRunReasons[reason]; ok {
return RunProvenanceAttended
}
return RunProvenanceUnattended
}