> ## Documentation Index
> Fetch the complete documentation index at: https://hc.pillargtm.com/llms.txt
> Use this file to discover all available pages before exploring further.

# BUILDER safety rails

> Every guardrail BUILDER ships with: shadow mode, rate limits, circuit breaker, kill switch, Guarantee runtime.

BUILDER's biggest design constraint isn't "what can it do" — it's "what's it prevented from doing without explicit human approval." This page is the catalog.

## Per-rule guardrails

<CardGroup cols={2}>
  <Card title="Mandatory shadow mode" icon="eye">
    Every new rule starts in shadow mode. **Cannot bypass this.** The system enforces a 7-day minimum in shadow before promotion to propose, and a 14-day minimum in propose with an 80%+ approval rate before promotion to execute.
  </Card>

  <Card title="Per-account rate limit" icon="gauge">
    Default: 1 fire per account per day per rule. Configurable per-rule (`rate_limit_per_account_per_day`). Prevents a runaway loop where Rule A fires Signal X which triggers Rule A again on the same account in the same minute.
  </Card>

  <Card title="Per-rule rate limit" icon="gauge-high">
    Default: 50 fires per hour per rule. Configurable per-rule (`rate_limit_per_hour`). Prevents a single misconfigured rule from saturating the action pipeline org-wide.
  </Card>

  <Card title="Circuit breaker" icon="bolt-slash">
    Default: 5 errors in 10 minutes auto-disables the rule. Auto-disabled rules show a red AUTO-DISABLED badge with the failure reason. The customer's Architect must manually re-enable after fixing the root cause — the system never auto-re-enables.
  </Card>

  <Card title="Postgres advisory locks" icon="lock">
    The action-executor takes a per-(rule\_id, entity\_id) advisory lock before executing. Two concurrent BUILDER cron ticks cannot act on the same rule + same account simultaneously.
  </Card>

  <Card title="Action idempotency keys" icon="fingerprint">
    Each action invocation includes an idempotency key in `builder_runs.provenance`. If the same rule + entity + action fires twice within the dedup window, the second invocation no-ops with a stamped reason. Prevents double-charges, duplicate task creation, etc.
  </Card>
</CardGroup>

## Org-level guardrails

<CardGroup cols={2}>
  <Card title="Global kill switch" icon="hand">
    Admin-only emergency stop. From the `/builder` dashboard, an Admin clicks the red **🛑 Kill switch** button, enters a reason (5+ chars), and **every active rule in the org is set to `is_active=false` within \~1 second**.
  </Card>

  <Card title="Approval expiry" icon="clock-rotate-left">
    Default: propose-mode actions expire 48h after creation if no human decides on them. Hard-coded for v1. Expired approvals stay in the queue with an EXPIRED status pill — the action does not execute.
  </Card>

  <Card title="High-impact action gating" icon="triangle-exclamation">
    Marking a rule `is_high_impact = true` requires Architect+ role to ever promote out of shadow. Subject to the Guarantee downgrade gate. Logged with `severity: "high"` in `governance_log`. Surfaced with a HIGH IMPACT pill in the UI.
  </Card>

  <Card title="Kill-switch reason audit" icon="clipboard-list">
    Each rule's `auto_disabled_reason` is stamped with the kill-switch reason so the audit trail is preserved. Releasing the kill switch does NOT auto-re-enable rules — that's intentional. It forces a triage step where someone has to look at each rule.
  </Card>
</CardGroup>

## Guarantee runtime (Phase 8 §E)

The biggest safety feature. Every BUILDER execution passes through three verification gates before the action runs.

<CardGroup cols={3}>
  <Card title="Pre-execution freshness check" icon="clock">
    Before the rule even evaluates, the engine checks underlying data freshness: scoring outputs ≤ 6 hours old, signals ≤ 2 hours old, CRM-synced fields ≤ 24 hours old. Configurable per-rule.
  </Card>

  <Card title="Real formula version stamping" icon="hashtag">
    Every rule that references a `formula_name` in its conditions gets the formula's current version stamped on the run (sha256 hash of `scoring_config` rows). When formulas change, future runs carry the new version stamp.
  </Card>

  <Card title="Optimistic concurrency check" icon="lock-open">
    Right before BUILDER executes, it re-reads the target entity's `updated_at`. If a human modified the account between when BUILDER decided to act and when it would have acted, BUILDER aborts with a `state_changed` log entry.
  </Card>
</CardGroup>

**The downgrade rule:** if freshness fails AND the action is high-impact, the rule's verdict is `guarantee_verified: false` and the action is **automatically downgraded** from `execute` mode to `propose` mode. The downgraded proposal lands in the approval queue with the freshness reason stamped — a human has to confirm before it runs.

Standard-impact actions on stale data still execute (logged as `governance_status: "unverified"`) but get telemetry. Only high-impact gets the safety downgrade.

## Audit trail

Every BUILDER run — matched or not — writes a row to `builder_runs` with full context:

<CardGroup cols={2}>
  <Card title="Trigger source" icon="bell">
    Which event, which cron tick, which SLA breach.
  </Card>

  <Card title="Condition diagnostics" icon="filter">
    Every predicate's verdict with the actual values that were tested.
  </Card>

  <Card title="Actions proposed + executed" icon="list-check">
    The full action body for both proposed and executed actions.
  </Card>

  <Card title="Guarantee verification" icon="shield-halved">
    `guarantee_verified` boolean + reason text + freshness check + formula versions used + concurrency-check timestamp.
  </Card>
</CardGroup>

Every executed action ALSO writes to `governance_log` with the rule, the run, the action body, and the Guarantee status. You can trace from "this email got sent" → "this run fired the rule" → "this evaluation said the conditions matched" → "this signal triggered it" → "this scoring formula said the account was at risk" → "this CRM field changed". Full lineage. One click to audit.

## What BUILDER will NOT do under any circumstance

These are the lines. If any of them are crossed, file a security ticket immediately.

<CardGroup cols={2}>
  <Card title="Execute without writing to builder_runs" icon="ban">
    Every action is logged. No exceptions.
  </Card>

  <Card title="Execute while kill switch engaged" icon="ban">
    Every cron tick checks the org's kill-switch state.
  </Card>

  <Card title="Execute the same (rule, entity) twice without idempotency" icon="ban">
    Advisory locks + idempotency keys both prevent this.
  </Card>

  <Card title="Bypass the 7-day shadow gate" icon="ban">
    Even on a rule that's been deleted and re-authored.
  </Card>

  <Card title="Execute high-impact on stale data without a human" icon="ban">
    The Guarantee downgrade gate enforces this.
  </Card>

  <Card title="Cross-tenant access" icon="ban">
    Every query is `org_id`-scoped at SQL + RLS-enforced at Postgres.
  </Card>
</CardGroup>
