Skip to content

fix(approvals): bypass approval for background job control - #1817

Merged
Aaronontheweb merged 6 commits into
devfrom
fix/check-background-job-approval
Aug 8, 2026
Merged

fix(approvals): bypass approval for background job control#1817
Aaronontheweb merged 6 commits into
devfrom
fix/check-background-job-approval

Conversation

@Aaronontheweb

@Aaronontheweb Aaronontheweb commented Aug 8, 2026

Copy link
Copy Markdown
Collaborator

Why

closes #1818

shell_execute approves the process before it creates a background job.

check_background_job controls that existing job. It does not create a new shell invocation.

The old path sent this tool through the approval system. An approval retry could fail and leave an incomplete tool-call history.

Contract

  • Job creation requires the normal shell_execute approval.
  • A status query does not require approval.
  • A cancellation does not require approval.
  • The approval policy does not apply to check_background_job.
  • Shell mode and the Personal audience restriction still apply.
  • The tool audience profile still controls access.
  • The job manager still requires the same session, audience, and boundary.

Implementation

The access policy returns an authorized job-lifecycle decision before the approval gate.

The change removes SelectShellApprovalMatcher. Real shell commands still use ShellApprovalMatcher.

The executor test supplies an approval service that throws on every call. Both status and cancellation pass without contact with that service.

Verification

dotnet test src/Netclaw.Actors.Tests/Netclaw.Actors.Tests.csproj -c Release --no-restore
# Passed: 2866

dotnet slopwatch analyze
# Scan complete: 0 issues

pwsh ./scripts/Add-FileHeaders.ps1 -Verify
# All files have headers.

git diff --check
# Passed

check_background_job is a shell-coupled tool (ToolAccessPolicy.IsShellCoupledTool)
that routes through ShellApprovalMatcher, whose IsFailClosedOnPersonal returns
true unconditionally. That forces ToolApprovalMode.Approval for every Personal
invocation with no explicit override — including pure read-only status queries
(Cancel=false) scoped to the caller's own jobs.

On a non-interactive turn (reminder/webhook), nothing can answer the prompt, so
the session wedges waiting on an approval that never arrives. This is the
repro-only half of a red-green change; the fix follows separately.

Three cases:
- status query (interactive): must not require approval
- status query (non-interactive automation): must not require approval
- cancel: MUST keep requiring approval (mutation)
…ries

check_background_job is a shell-coupled tool that routed through
ShellApprovalMatcher, whose IsFailClosedOnPersonal returns true
unconditionally. Every Personal invocation therefore forced
ToolApprovalMode.Approval — including read-only status queries scoped to
the caller's own jobs. On a non-interactive turn (reminder, webhook,
sub-agent without an approval bridge) nothing can answer the prompt, so
the session wedged waiting on an approval that never arrives.

Add BackgroundJobApprovalMatcher:
- status query (Cancel=false): IsFailClosedOnPersonal=false, so it falls
  through to the profile default (Auto) and runs without a prompt. The
  job manager enforces an exact SessionId+Audience+Boundary match on
  QueryBackgroundJob, so a status query cannot read another session's
  jobs.
- cancel (Cancel=true): IsFailClosedOnPersonal=true, stays approval-
  gated — killing a running process is a mutation.

Launching a background job is unaffected: it is a shell_execute call
with _background:true, which still routes through ShellApprovalMatcher
and the ordinary shell approval gate.

Also add a pipeline-level regression test driving a non-interactive,
VerifiedAutomation turn through the executor.
…tatus query

Drives a non-interactive (InteractiveApproval=Unavailable), Personal,
TrustedInstance turn through DispatchingToolExecutor:
- status query (Cancel=false) must not throw ToolApprovalRequiredException
- cancel (Cancel=true) must still throw

Verified red against the pre-fix ToolAccessPolicy and green with
BackgroundJobApprovalMatcher.
Comment thread src/Netclaw.Actors.Tests/Tools/DispatchingToolExecutorTests.cs Fixed
…st class

Adversarial review findings (pr-review-specialist on PR #1817):

HIGH: IsCancelRequest accepted a strict subset of the argument shapes the
generated tool binding accepts. The binding falls back to case-insensitive
and normalized key matching and coerces JsonElement/string values, so
{"cancel": true} or {"Cancel": "true"} bound as a real cancel while the
matcher treated them as a status query and auto-allowed — cancel fired with
no approval. Delegate cancellation detection to ToolArgumentHelper.GetBoolStrict,
the same helper the binding uses, so the approval decision and execution
decision agree by construction.

MED: add a 13-shape cancel value matrix test (CLR bool, JsonElement bool,
JsonElement string, CLR string, null, garbage, numeric) asserting matcher
parity with the binding, plus key-variant tests (lowercase, normalized,
trailing-space) and a ToolOverrides-precedence test proving an operator
can still force Approval for status queries.

LOW: move the pipeline-level executor regression into a dedicated
Akka.Hosting.TestKit-based class (CheckBackgroundJobApprovalTests) instead
of an inline ActorSystem + nested actor in DispatchingToolExecutorTests.
Comment thread src/Netclaw.Actors/Jobs/BackgroundJobApprovalMatcher.cs Fixed
@Aaronontheweb Aaronontheweb added bug Something isn't working security Security-related changes tools Issues related to agent tools: file_read, web_search, shell_execute, image processing, etc. labels Aug 8, 2026
@Aaronontheweb Aaronontheweb changed the title test(approvals): reproduce check_background_job approval wedge fix(approvals): check_background_job status queries no longer require approval Aug 8, 2026
@Aaronontheweb

Copy link
Copy Markdown
Collaborator Author

Closes #1818

@Aaronontheweb Aaronontheweb changed the title fix(approvals): check_background_job status queries no longer require approval fix(approvals): manage background jobs without second approval Aug 8, 2026
@Aaronontheweb Aaronontheweb changed the title fix(approvals): manage background jobs without second approval fix(approvals): bypass approval for background job control Aug 8, 2026

@Aaronontheweb Aaronontheweb left a comment

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@Aaronontheweb
Aaronontheweb enabled auto-merge (squash) August 8, 2026 19:49
@Aaronontheweb
Aaronontheweb merged commit 4300019 into dev Aug 8, 2026
21 checks passed
@Aaronontheweb
Aaronontheweb deleted the fix/check-background-job-approval branch August 8, 2026 20:11
@Aaronontheweb Aaronontheweb mentioned this pull request Aug 26, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working security Security-related changes tools Issues related to agent tools: file_read, web_search, shell_execute, image processing, etc.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

check_background_job status queries require approval and wedge non-interactive turns

1 participant