Skip to content

[AAASM-4129] 🔒 (gateway): Fail closed on pending verdict without an approval channel#234

Merged
Chisanan232 merged 2 commits into
masterfrom
v0.0.1/AAASM-4129/pending_verdict_fail_closed
Jul 5, 2026
Merged

[AAASM-4129] 🔒 (gateway): Fail closed on pending verdict without an approval channel#234
Chisanan232 merged 2 commits into
masterfrom
v0.0.1/AAASM-4129/pending_verdict_fail_closed

Conversation

@Chisanan232

Copy link
Copy Markdown
Contributor

Target

  • Task summary:

    A pending (approval-required) verdict from the runtime was silently downgraded to allow at the SDK layer. The native gateway client's waitForApproval stub resolved { denied: false }, which won the Promise.race against the approval timeout in enforceGovernance, so the wrapped tool ran with no approval ever solicited — even under enforce. Node was the only SDK of the three that did this: python's _resolve_pending_approval and go's WaitForApproval both DENY a pending verdict when no approval channel is wired.

    This makes a pending verdict fail closed under failClosed (enforce): waitForApproval now resolves { denied: true, reason } when no approval channel is configured, so the wrapper throws PolicyViolationError and the tool never runs. Advisory postures (observe / disabled / unset) stay neutral so a missing approval channel never blocks the agent — matching the existing check() fault posture.

  • Task tickets:

    • Task ID: AAASM-4129.
    • Relative task IDs:
      • N/A.
    • Relative PRs:
      • N/A.
  • Key point change (optional):

    • As-is: pending verdict → waitForApproval resolves { denied: false } → tool executes with no approval.
    • To-be: pending verdict under enforce with no wired approval channel → { denied: true }PolicyViolationError, tool blocked. Non-enforce postures unchanged (fail-open).
    • Scope: change is confined to createNativeGatewayClient (the only client that can emit pending: true); the no-op client's check() never returns pending, so it is untouched.

Effecting Scope

  • Action Types:
    • 🔧 Fixing bug
    • 🍀 Improving something (performance, code quality, security, etc.)
  • Scopes:
    • 🪡 API client and transport
    • ⛑️ Error handling
    • 🧪 Testing
      • 🧪 Unit testing
  • Additional description:
    No new dependencies. No public API change. No native (Rust) glue touched — pure TypeScript.

Description

  • src/gateway/client.ts: createNativeGatewayClient.waitForApproval now denies under failClosed (enforce) with an explanatory reason; stays neutral in advisory postures.
  • tests/native-gateway-enforcement.test.ts: adds a test that a pending verdict + enforce + no approval channel blocks the tool (never runs its body); clarifies the existing advisory-posture PENDING test.

How to verify

  • pnpm typecheck && pnpm lint clean.
  • pnpm test — full suite green (the new PENDING under enforce ... fails closed case plus the unchanged advisory PENDING case).

Closes AAASM-4129.

🤖 Generated with Claude Code

https://claude.ai/code/session_01R7vqjjo5nrebYNt8WnCNbz

Chisanan232 and others added 2 commits July 4, 2026 23:45
A `pending` verdict routed to waitForApproval but the native client stub
resolved `{denied:false}`, so an approval-required verdict was silently
downgraded to allow even under enforce. Deny under failClosed when no
approval channel is wired, matching python (_resolve_pending_approval)
and go (WaitForApproval). Advisory postures stay neutral.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01R7vqjjo5nrebYNt8WnCNbz
Add a native-gateway test that a pending verdict with no wired approval
channel blocks the tool (never runs its body) under enforce, and clarify
the advisory-posture PENDING case stays fail-open.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01R7vqjjo5nrebYNt8WnCNbz
@codecov

codecov Bot commented Jul 4, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

@sonarqubecloud

sonarqubecloud Bot commented Jul 4, 2026

Copy link
Copy Markdown

@Chisanan232

Chisanan232 commented Jul 5, 2026

Copy link
Copy Markdown
Contributor Author

Review — AAASM-4129 pending verdict fails closed

Verdict: Approve-ready (comment only, no approval per review protocol). All four dimensions green.

1. CI — 25/25 checks passing, 0 failed. No fix needed.

2. Scope vs ticket — Matches AAASM-4129 exactly. The ticket calls out createNativeGatewayClient.waitForApproval silently downgrading a pending verdict to allow (node was the only SDK doing so; py _resolve_pending_approval and go WaitForApproval both deny). Fix is confined to that one client; the noop client (:41) never emits pending and is correctly left untouched. Tests cover both the new enforce-blocks case and the clarified advisory-neutral case.

3. Side effects — Verified the gate is failClosed = enforcementMode === "enforce", shared with the existing check() fault posture:

  • enforce{ denied: true, reason } → wrapper throws PolicyViolationError, tool never runs (new test asserts execute not called).
  • observe / disabled / unset → { denied: false } → neutral; a missing approval channel never blocks the agent (existing advisory PENDING test still green).
  • No legitimately-wired approval callback exists to break — the native client's waitForApproval is a stub and a caller-supplied gatewayClient carries its own impl. Existing gateway/enforcement tests all pass.

4. FE — N/A.

Local validation (worktree, pnpm install && typecheck && lint && test): typecheck clean, ESLint no issues, 358 passed / 2 skipped (52 files passed / 1 skipped). No packaging-lock flake tripped this run.

Touches only src/gateway/client.ts + tests/native-gateway-enforcement.test.ts — no overlap with #235; the two are independently mergeable.

— Claude Code

@Chisanan232 Chisanan232 merged commit 927f866 into master Jul 5, 2026
25 checks passed
@Chisanan232 Chisanan232 deleted the v0.0.1/AAASM-4129/pending_verdict_fail_closed branch July 5, 2026 00:41
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.

1 participant