Skip to content

Omit approved candidates from shell prompts - #1830

Merged
Aaronontheweb merged 2 commits into
devfrom
fix/approval-prompt-unapproved-candidates
Aug 9, 2026
Merged

Omit approved candidates from shell prompts#1830
Aaronontheweb merged 2 commits into
devfrom
fix/approval-prompt-unapproved-candidates

Conversation

@Aaronontheweb

Copy link
Copy Markdown
Collaborator

Summary

  • Keep one ordered approval disposition for each shell candidate occurrence.
  • Remove only candidates that an existing session or persistent grant covers.
  • Preserve the full command text while prompts, retries, and persisted choices use the exact remaining candidates.
  • Preserve duplicate verbs in different directories as separate authorization scopes.

Security and compatibility

  • Validate the complete candidate sequence and the aggregate approval result before prompt narrowing.
  • Keep the broad prompt and fail closed when a service returns inconsistent candidate data.
  • Keep prior behavior for approval-service implementations that return only aggregate results.
  • Preserve the original public ToolApprovalCheckResult constructor. The new candidate detail is an additive init property.

An adversarial review found and verified fixes for incomplete candidate accounting, missing end-to-end directory proof, and JSON constructor compatibility. The final review passed with no blockers.

Verification

  • dotnet build -c Release --no-restore
  • Actor tests: 2,918 passed
  • Security tests: 685 passed
  • Rebased approval tests: 482 passed
  • Header verification passed
  • Slopwatch: 0 issues
  • git diff --check passed

Closes #972

@Aaronontheweb
Aaronontheweb enabled auto-merge (squash) August 9, 2026 02:57
[Fact(SkipUnless = nameof(IsPosix), Skip = "POSIX-only shell directory semantics")]
public async Task Authorization_evaluation_preserves_directory_for_duplicate_verb_candidates()
{
var root = Path.Combine(Path.GetTempPath(), $"netclaw-prompt-scope-{Guid.NewGuid():N}");
public async Task Authorization_evaluation_preserves_directory_for_duplicate_verb_candidates()
{
var root = Path.Combine(Path.GetTempPath(), $"netclaw-prompt-scope-{Guid.NewGuid():N}");
var approvedDirectory = Path.Combine(root, "approved");
{
var root = Path.Combine(Path.GetTempPath(), $"netclaw-prompt-scope-{Guid.NewGuid():N}");
var approvedDirectory = Path.Combine(root, "approved");
var unapprovedDirectory = Path.Combine(root, "unapproved");
var tempFile = Path.GetTempFileName();
try
{
var grantDir = Path.Combine(Path.GetTempPath(), "netclaw-approval", "repo");
try
{
var grantDir = Path.Combine(Path.GetTempPath(), "netclaw-approval", "repo");
var approvedDir = Path.Combine(grantDir, "src");
{
var grantDir = Path.Combine(Path.GetTempPath(), "netclaw-approval", "repo");
var approvedDir = Path.Combine(grantDir, "src");
var unapprovedDir = Path.Combine(Path.GetTempPath(), "netclaw-approval", "external");
@Aaronontheweb Aaronontheweb added shell Issues related to the shell tool, since it has the largest security perimeter. security Security-related changes UX/DX UI / UX / DX friction issue or user-facing annoyances. labels Aug 9, 2026
@Aaronontheweb

Copy link
Copy Markdown
Collaborator Author

Concrete prompt behavior

This change keeps the full command visible, but narrows the approvable candidate set to exact occurrences that still lack a grant.

Existing session grant

Command:

git status && git push

If git status already has a session grant, the old prompt listed both git status and git push. The new prompt lists only git push.

Same verb in two directories

Command:

git -C /work/repo-a push && git -C /work/repo-b push

If git push is approved only for /work/repo-a, the prompt contains only the git push occurrence for /work/repo-b. The two identical verbs are not interchangeable because their effective directories differ. An approval or one-time retry created from the prompt therefore covers only the unapproved occurrence.

Pipeline with a partial grant

Command:

git push | curl https://example.invalid

If git push already has a matching grant, the prompt and retry candidate set contain only curl. The full pipeline remains visible in the prompt header.

Fail-closed behavior

The executor validates the complete ordered candidate result before narrowing. If an approval-service result omits, swaps, or contradicts a candidate, Netclaw keeps the broad prompt and does not treat the command as previously approved.

Adversarial review

The adversarial reviewer found three blockers during development:

  1. The first result shape did not prove complete candidate accounting.
  2. The tests did not prove duplicate verbs with distinct directories through the parser and executor seam.
  3. A second public constructor could make System.Text.Json constructor selection ambiguous.

All three were fixed. The final adversarial review returned PASS with no blockers.

@Aaronontheweb
Aaronontheweb merged commit 66c050d into dev Aug 9, 2026
29 of 30 checks passed
@Aaronontheweb
Aaronontheweb deleted the fix/approval-prompt-unapproved-candidates branch August 9, 2026 03:42
@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

security Security-related changes shell Issues related to the shell tool, since it has the largest security perimeter. UX/DX UI / UX / DX friction issue or user-facing annoyances.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Approval prompt lists already-approved verbs alongside new ones (UX confusion)

1 participant