PR #3520
Sections
Review

feat(office): fail closed on budget admission for unattended runs

main ← feature/office-budget-must-f-pia 88 files +4200 −180 PR #3520 ↗

Unattended Office runs now fail closed through five admission gates, a built-in daily default ceiling, and pricing-degradation blocking, so no run launches without a complete and inert budget check.

Why this change

The old pre-launch check could fail open: a missing evaluator, a query error, or zero policies let an unattended run launch with no ceiling, and the check also mutated state while deciding.

What it does

Architecture, end to end

A run enters admitRun after checkout. Provenance is classified once, then gates 1 to 5 run in order. Gate 4 evaluates all policies via EvaluatePreLaunch, gate 5 evaluates the default ceiling, and the result drives launch, block, defer, or cancel.

flowchart LR
  Run[Run queued] --> Checkout[checkoutTask]
  Checkout --> Prov[ClassifyRunProvenance]
  Prov --> G1{Gate 1: workspace}
  G1 -- empty --> Cancel1[cancel: no workspace]
  G1 -- ok --> G2{Gate 2: evaluator}
  G2 -- nil + unattended --> Cancel2[cancel: no evaluator]
  G2 -- ok --> G3{Gate 3: evaluator call}
  G3 -- error --> Defer[defer: evaluator fault]
  G3 -- ok --> G4{Gate 4: policies}
  G4 --> Eval[EvaluatePreLaunch]
  Eval --> Sel{first blocker?}
  Sel -- yes --> Block[block: limit or degraded]
  Sel -- no --> G5{Gate 5: default ceiling}
  G5 --> DefEval[EvaluateDefaultCeiling]
  DefEval -- over limit or degraded --> Block
  DefEval -- under --> Launch[launch]
  G2 -- attended + nil --> Launch

Key code changes

Drag to pan. Use the + and − buttons to zoom. Click a node to open the full code. The arrows show how the parts interact.

drag to pan · +/− to zoom · click a node for details

func (si *SchedulerIntegration) admitRun(ctx context.Context, run *models.Run, agent *models.AgentInstance) bool
Click for details →

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
}
func (s *CostService) EvaluatePreLaunch(ctx context.Context, workspaceID, agentInstanceID, projectID string, hasProject bool, provenance shared.RunProvenance, at time.Time) (PreLaunchResult, error)
Click for details →

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
}
func (s *CostService) EvaluateDefaultCeiling(ctx context.Context, workspaceID string, at time.Time) (PreLaunchPolicyResult, error)
Click for details →

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
}
func degradationBlocks(degraded bool, pricedSubcents, limitSubcents int64, provenance shared.RunProvenance) bool
Click for details →

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
}
func (r *Repository) SpendWindowForWorkspace(ctx context.Context, workspaceID string, start time.Time, hasStart bool, before time.Time) (models.SpendWindow, error)
Click for details →

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)
}
func ClassifyRunProvenance(reason string) RunProvenance
Click for details →

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
}
Read the changes as a list

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
}

Data and storage

New and changed storage and model types that support the fail-closed admission path.

FieldTypeNotes
office_budget_default_settings.workspace_idTEXT PKOne row per workspace, holds the operator override for the built-in ceiling
office_budget_default_settings.limit_subcentsINTEGERPositive subcents, last-write-wins via INSERT ON CONFLICT
models.SpendWindow.PricedSubcentsint64Sum of priced events in the window, unpriced events excluded
models.SpendWindow.DegradedboolTrue when any unpriced event exists in the same window
models.PreLaunchResult.Decisionenumlaunch, blocked_by_limit, or blocked_by_degradation
models.PreLaunchPolicyResult.IsDefaultboolTrue for the built-in default, PolicyID is empty in that case
models.PolicyValidationIssuesstructFive flags for skipped policies: period, limit, scope, action, empty scope_id
office_cost_events.cost_sourceTEXTpriced vs unpriced, drives the degraded flag

Risk

7 / 10 High
1 low5 medium10 high

Why this score

  • Admission now blocks unattended runs on any evaluator fault, so a transient DB error defers all unattended work until retry succeeds.
  • New spend-window queries and period logic change how every budget window is computed, including the default ceiling.
  • A new table and two new API routes add persistent state and surface area that must stay consistent with policy evaluation.

Trade-offs and review notes

Where to look first

  1. Verify gate order and dispositions in budget_admission.go, especially the project-resolution four-way split and the MaxRetryCount without escalation path.
  2. Check EvaluatePreLaunch two-phase logic: all policies evaluated, survivors sorted by created_at, first blocker wins, and inertness holds.
  3. Confirm degradationBlocks uses limit-priced without overflow and applies to both policies and the default for unattended runs only.
  4. Review SpendWindowForWorkspace join via agent_profiles and the half-open [start, before) window shared by policies and default.
  5. Confirm default ceiling API validates positive limits, never appears in the policy list, and the frontend card is distinct from policy cards.