How Budget Enforcement Works with Hard-Stop Auto-Pause Behavior in Paperclip

Paperclip's hard-stop auto-pause mechanism automatically halts agents, projects, or entire companies when spending limits are breached, requiring manual approval before work can resume.

Budget enforcement in Paperclip operates through real-time evaluation of budget policies against aggregated cost events. The implementation centers on server/src/services/budgets.ts, which orchestrates status detection, scope pausing, incident creation, and approval-based resumption. This article breaks down the complete flow from threshold detection to manual override.

Detecting Budget Threshold Violations

The enforcement pipeline begins with budgetStatusFromObserved, which compares observed spending against policy limits. Located at lines 66-73 of server/src/services/budgets.ts, this function returns "hard_stop" when the observed amount reaches or exceeds the configured budget amount.

// Simplified flow when processing a cost event
const status = budgetStatusFromObserved(
  observed,           // aggregated cents spent
  policy.amount,      // budget limit in cents
  policy.warnPercent  // optional warning threshold (e.g., 80)
);
// Returns: "ok" | "warning" | "hard_stop"

The function evaluates three states: normal operation ("ok"), approaching limit ("warning"), and limit exceeded ("hard_stop"). The hard-stop outcome triggers the complete auto-pause cascade described below.

The Auto-Pause Cascade: Three Scope Types

When budgetStatusFromObserved returns "hard_stop", pauseAndCancelScopeForBudget (lines 52-58) initiates scope-specific pausing followed by in-flight work cancellation.

Pausing Individual Agents

For agent-scoped budgets, pauseScopeForBudget (lines 16-25) performs a direct status update:

// Agent pause: status becomes "paused", reason tracked
{
  status: "paused",
  pauseReason: "budget",
  pausedAt: new Date().toISOString()
}

This immediately prevents the agent from accepting new tasks while preserving its configuration.

Pausing Projects

Project-scoped budgets receive a lighter touch (lines 29-36). The project retains its underlying status but gains pause metadata:

// Project pause: status unchanged, but pause fields populated
{
  pauseReason: "budget",
  pausedAt: timestamp,
  // status remains as-is (e.g., "active")
}

This design allows project-level pausing without disrupting shared infrastructure that other projects might depend on.

Pausing Entire Companies

At the company scope (lines 41-48), the pause is absolute:

// Company pause: global status change affects all child entities
{
  status: "paused",
  pauseReason: "budget"
}

A company-level hard-stop freezes all operations across every agent and project within that organization.

Canceling In-Flight Work

After pausing, pauseAndCancelScopeForBudget optionally invokes cancelWorkForScope to terminate running jobs. This prevents partially-completed work from accruing additional costs after the pause takes effect. The cancellation hook accepts the paused scope's identifiers and propagates termination signals to downstream executors.

The optional nature of this step allows configuration-dependent behavior—some deployments may prefer to let current jobs finish while blocking new ones, while stricter configurations demand immediate termination.

Incidents, Approvals, and the Override Workflow

Critical to the hard-stop design is the mandatory human review. createIncidentIfNeeded (lines 80-92) executes two actions for hard-stop breaches:

  1. Logging: Creates a budget incident record documenting the breach amount, timestamp, and affected scope
  2. Approval creation: Generates a budget-override approval with type "hard" that must be explicitly granted before resumption
// After detecting hard_stop and pausing:
await createIncidentIfNeeded(policy, "hard", observed);
// Creates incident + approval; scope remains paused until approval granted

The approval workflow enforces organizational oversight. No automatic resumption occurs— even if additional funding becomes available, an authorized user must explicitly approve the override.

Resuming After Override Approval

Once approved, resumeScopeFromBudget (lines 61-78) reverses the pause state with scope-specific logic:

Scope Type Resume Behavior
Agent status → "idle", pauseReason cleared
Project pauseReason and pause timestamp cleared
Company status restored, pauseReason cleared
// Example: resuming a paused agent after approval
await resumeScopeFromBudget(policy);
// Agent now eligible for new work assignments

This symmetric design ensures that pause and resume operations maintain state consistency across all scope levels.

Policy Summary and UI Integration

buildPolicySummary (lines 40-48) exposes budget status to frontend consumers. The summary includes:

  • Computed status ("hard_stop" when threshold exceeded)
  • Current pauseReason propagated from the scope row
  • Formatted amounts and window information

The UI layer—specifically ui/src/lib/status-card-state.ts—maps pauseReason: "budget" to a "Paused — budget" badge, providing immediate visual indication of why operations halted. Supporting utilities in ui/src/lib/utils.ts format budget displays with appropriate suffixes like /mo for monthly windows.

Complete Budget Policy Example

// Creating a hard-stop enabled policy via API
const policy = {
  companyId: "c123",
  scopeType: "agent",              // "agent" | "project" | "company"
  scopeId: "a456",
  metric: "billed_cents",          // cost aggregation field
  windowKind: "calendar_month_utc", // reset cadence
  amount: 500_00,                  // $500.00 (cents)
  warnPercent: 80,                 // notify at $400.00
  hardStopEnabled: true,           // ENABLE auto-pause
  notifyEnabled: true,             // alert on warning/hard-stop
  isActive: true
};

With hardStopEnabled: true, this policy transforms from a monitoring tool into an enforcement mechanism with automatic operational consequences.

Summary

  • Threshold detection: budgetStatusFromObserved compares observed spending against policy.amount, returning "hard_stop" on breach (lines 66-73)
  • Scope pausing: pauseScopeForBudget applies appropriate pause semantics to agents, projects, or companies (lines 16-48)
  • Work cancellation: pauseAndCancelScopeForBudget optionally terminates in-flight jobs via cancelWorkForScope (lines 52-58)
  • Mandatory review: createIncidentIfNeeded generates a hard-type approval that blocks automatic resumption (lines 80-92)
  • Controlled resumption: resumeScopeFromBudget restores operational status only after explicit approval (lines 61-78)

Frequently Asked Questions

What triggers a hard-stop auto-pause in Paperclip?

A hard-stop triggers when aggregated spending for a budget policy's scope (agent, project, or company) reaches or exceeds the configured amount. The budgetStatusFromObserved function evaluates this on every cost event, comparing observed cents against the policy limit.

Can paused scopes resume automatically?

No. Hard-stop pauses require explicit manual approval through the budget-override workflow. The createIncidentIfNeeded function creates a hard-type approval record that must be granted before resumeScopeFromBudget can clear the pause state.

What's the difference between warning and hard-stop states?

Warnings ("warning") alert users when spending crosses warnPercent of the budget but allow continued operation. Hard-stops ("hard_stop") actively pause the affected scope, cancel optional in-flight work, and enforce a mandatory approval gate before any resumption.

How does scope type affect pause behavior?

Agent pauses change status to "paused" directly. Project pauses set pauseReason without altering underlying status. Company pauses apply a global status: "paused" affecting all child entities. Each scope type uses pauseScopeForBudget with tailored update logic.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →