Skip to content

feat(ai): auto-execute read-only Tier-2 calls under per_step approval (#3130) - #3156

Merged
ToddHebebrand merged 2 commits into
mainfrom
feat/3130-per-step-readonly-auto
Aug 5, 2026
Merged

feat(ai): auto-execute read-only Tier-2 calls under per_step approval (#3130)#3156
ToddHebebrand merged 2 commits into
mainfrom
feat/3130-per-step-readonly-auto

Conversation

@ToddHebebrand

Copy link
Copy Markdown
Collaborator

Decision on #3130

Yes to skipping the per-step prompt for read-only calls; no to the tier-wide option. Tier 2 is "low-risk mutations + audit" — auto-executing all of Tier ≤ 2 under per_step would have removed confirmation from ~60 mutating actions (policy activate/deactivate, bulk patch approve, backup profile delete, collect_evidence, revoke_elevation, …) and made per_step behaviorally identical to auto_approve. The narrow read-only allowlist gets the #3088 approval-fatigue win without that blast radius.

What changed

  • aiGuardrails.ts: two explicit allowlists — TIER2_READONLY_ACTIONS (execute_command {event_logs_list, file_list, list_processes}, file_operations {list}, manage_services {list}) and TIER2_READONLY_TOOLS (the nine single-purpose get/list/search catalog, contract, invoice, and quote tools). Matching Tier-2 resolutions now carry readOnly: true on the GuardrailCheck.
  • aiAgentSdk.ts: createSessionPreToolUse auto-executes readOnly Tier-2 calls under every approval mode, keeping the Tier-2 ai_tool_executions audit-ledger row (deliberately not a Tier-1 demotion — Tier 1 writes no ledger row, and recon reads stay in the audit trail per the SR5-01 precedent). A paused session still prompts for everything Tier 2+ — pause remains a hard brake. Helper (PAM) sessions are unaffected: their branch precedes the fast path and still cannot self-relax.
  • Audit honesty rider: the ai.tool.* audit event previously hard-coded approved: true for every tier ≥ 2 execution — false for auto_approve/plan auto-executions and about to get worse. It now records approved only when the specific call was explicitly decided, plus approvalMethod (per_step_user / action_intent / pam / plan_step / auto_approve_mode / read_only_auto).
  • Mirrors: Tier-2 copy in tierConfig.ts and the ai.mdx Aside updated to describe the read-only carve-out.

Tests

  • New aiGuardrails.readonly.contract.test.ts (real registry, no mocks): allowlist action pairs ⊆ TIER2_ACTIONS; every whole-tool entry is base tier 2 with no per-action tier-table presence (single-purpose reads only); readOnly resolves true through checkGuardrails and never leaks to mutating Tier-2, Tier-3, or unknown/missing commandTypes (fail-closed).
  • aiAgentSdk.test.ts: per_step read-only auto-exec (executing ledger row, no prompt, no intent), paused-session still prompts, mutating Tier-2 still bridges, and audit-detail assertions for read_only_auto vs per_step_user.
  • All affected API suites green under pinned Node 22.23.2 (265 tests / 7 files, including both AI guardrails: no contract test binds tierConfig.ts (customer-facing) to the real tier tables #2686 parity guards and the MCP effectiveTier suite); web suite 523/524 with the one failure a known source-scan flake that passes in isolation; API + web tsc --noEmit clean.

Closes #3130

🤖 Generated with Claude Code

…#3130)

Decision on #3130: yes to skipping the per-step prompt for reads, no to
doing it tier-wide — Tier 2 is "low-risk mutations + audit", so
auto-executing all of Tier <= 2 would have removed confirmation from ~60
mutating actions and made per_step indistinguishable from auto_approve.

Instead, two explicit read-only allowlists in aiGuardrails.ts
(TIER2_READONLY_ACTIONS for action-multiplexed tools, TIER2_READONLY_TOOLS
for single-purpose get/list/search tools) mark strictly-read Tier-2
resolutions with readOnly: true, and createSessionPreToolUse auto-executes
those under every approval mode — keeping the Tier-2 ai_tool_executions
audit-ledger row (deliberately NOT a Tier-1 demotion, which writes none).
A paused session still prompts for everything Tier 2+: pause stays a hard
brake.

Rider: the ai.tool.* audit event hard-coded `approved: true` for every
tier>=2 execution, misrepresenting auto-approved calls — and it would have
become actively misleading here. It now records `approved` only when the
specific call was explicitly decided, plus `approvalMethod`
(per_step_user / action_intent / pam / plan_step / auto_approve_mode /
read_only_auto).

New contract suite (aiGuardrails.readonly.contract.test.ts) pins the
allowlists structurally: action pairs must be a subset of TIER2_ACTIONS,
whole-tool entries must be base tier 2 with no per-action tier-table
presence, and readOnly must never leak outside the allowlists.

Closes #3130

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented Aug 5, 2026

Copy link
Copy Markdown

Deploying breeze with  Cloudflare Pages  Cloudflare Pages

Latest commit: 76b6c7d
Status: ✅  Deploy successful!
Preview URL: https://22bd46dc.breeze-9te.pages.dev
Branch Preview URL: https://feat-3130-per-step-readonly.breeze-9te.pages.dev

View logs

Review finding on #3156: the read-only fast path fired in
action_plan/hybrid_plan sessions too, returning before matchPlanStep — a
read that WAS the current plan step left currentPlanStepIndex
un-advanced, so the next real step read as a deviation, postToolUse
mis-attributed plan_step_complete to the stale index, and a plan ending
in a read never reached plan_complete.

Carve active plan execution out of the fast path: plan-matched reads
auto-execute through the plan branch (which advances the index), and an
unmatched read during a plan is a deviation that deliberately still
prompts, same as before. per_step — the #3088 fatigue scenario — is
unaffected.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@ToddHebebrand
ToddHebebrand merged commit 6c88233 into main Aug 5, 2026
56 checks passed
@ToddHebebrand
ToddHebebrand deleted the feat/3130-per-step-readonly-auto branch August 5, 2026 05:59
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[API] Decide: should read-only execute_command types auto-execute under the default per_step approval mode?

1 participant