Skip to content

fix(exec): distinguish one-shot from ask=always in approval prompt#97106

Closed
ZOOWH wants to merge 9 commits into
openclaw:mainfrom
ZOOWH:fix/97069-approval-prompt-one-shot-message
Closed

fix(exec): distinguish one-shot from ask=always in approval prompt#97106
ZOOWH wants to merge 9 commits into
openclaw:mainfrom
ZOOWH:fix/97069-approval-prompt-one-shot-message

Conversation

@ZOOWH

@ZOOWH ZOOWH commented Jun 26, 2026

Copy link
Copy Markdown
Contributor

What Problem This Solves

When a command includes shell redirection (e.g. openclaw --version 2>&1), Allow Always is excluded because the command is one-shot. But the approval prompt shows "The effective approval policy requires approval every time" — falsely blaming the policy when the real reason is the command itself.

Root Cause

Two approval renderers (exec-approval-reply.ts, exec-approval-forwarder.ts) check if (!allowedDecisions.includes("allow-always")) and always output the same policy message, regardless of whether allow-always is excluded due to ask=always or kind=one-shot.

Fix

Use the ask field (already available in both renderers' params) to distinguish:

  • ask=always → policy message (unchanged)
  • Other known ask → one-shot message (new)
  • ask absent → policy message (safe default)

Closes #97069

Change Type

  • Bug fix

Scope

  • Infrastructure (exec approval reply + forwarder)

Evidence

Real production function proof (node --import tsx):

=== ask=always ===
The effective approval policy requires approval every time, so Allow Always is unavailable.
✅ correct policy message

=== one-shot (shell redirect) ===
Allow Always is unavailable because this command cannot be saved for future use
(e.g., shell redirection or one-shot arguments).
✅ correct one-shot message

Test coverage:

  • ask=always → policy message preserved
  • ask=on-miss with no allow-always → one-shot reason shown
  • ask absent/null → safe fallback to policy message

Security Impact

  • New permissions? No
  • Secrets handling changed? No
  • New network calls? No

When Allow Always is unavailable because a command is one-shot (e.g.
shell redirection), show the correct reason instead of falsely claiming
the effective policy requires approval every time.

Closes openclaw#97069
@clawsweeper

clawsweeper Bot commented Jun 26, 2026

Copy link
Copy Markdown
Contributor

Codex review: needs real behavior proof before merge. Reviewed June 30, 2026, 10:05 PM ET / 02:05 UTC.

Summary
The PR adds a shared exec approval helper, updates reply and forwarder copy for missing Allow Always, and adds reply-path tests for policy, one-shot, and missing-ask cases.

PR surface: Source +36, Tests +56. Total +92 across 4 files.

Reproducibility: yes. The linked report gives concrete redirected-command steps, and source inspection shows current main collapses ask=always and one-shot persistence into the same missing Allow Always decision while the PR head still loses the reason on several surfaces.

Review metrics: 1 noteworthy metric.

  • Approval reason plumbing: 2 renderers changed, 0 producer/detail/view shapes changed. The patch changes reply and forwarder rendering but does not carry the underlying unavailable reason through production approval data surfaces.

Merge readiness
Overall: 🧂 unranked krab
Proof: 🦪 silver shellfish
Patch quality: 🧂 unranked krab
Result: blocked until stronger real behavior proof is added.

Overall follows the weaker of proof and patch quality, so missing proof can cap an otherwise strong patch.

Rank-up moves:

  • Thread the unavailable reason through request/detail/view producers and all affected renderers.
  • [P1] Add fresh current-head Telegram Desktop or equivalent production-path proof for the redirected one-shot approval prompt.
  • [P1] Coordinate with maintainers on the canonical fix among the overlapping linked PRs.

Proof guidance:

  • [P1] Needs stronger real behavior proof before merge: Mantis shows Telegram Desktop before/after proof for the initial candidate commit, but current head changed the missing-ask fallback and has no fresh production-path proof after that behavior change. The contributor should add current-head proof with private details redacted, then update the PR body or ask for @clawsweeper re-review.

Mantis proof suggestion
A native Telegram Desktop capture would directly prove the visible approval prompt text after the missing reason is threaded through the latest head. A maintainer can ask Mantis to capture proof by posting this exact PR comment:

@openclaw-mantis telegram desktop proof: verify redirected exec approval prompts show the non-persistable Allow Always reason on the latest PR head.

Risk before merge

  • [P1] A partial renderer-only fix can leave the same one-shot approval showing different or still-misleading explanations across Telegram native, foreground exec, reaction prompts, Control UI, and gateway resolve errors.
  • [P2] The visible Telegram proof was captured on an earlier candidate commit, before the current-head helper and missing-ask fallback changes.
  • [P1] Several open sibling PRs target the same linked bug, so maintainers still need to choose a canonical repair path.

Maintainer options:

  1. Thread Reason Data Before Merge (recommended)
    Pass the one-shot versus policy reason through the production approval data path and update every affected renderer before this branch lands.
  2. Pause For A Canonical Sibling
    If maintainers prefer one of the broader sibling implementations, pause this branch until a canonical fix for the linked bug is selected.
  3. Accept Reply-Only Coverage
    Maintainers could intentionally land only the reply and forwarder subset, but that would preserve known inconsistent approval-copy behavior.

Next step before merge

  • [P1] The remaining blocker combines a concrete code defect with maintainer canonical-path selection and fresh contributor proof, so this should stay in human review rather than dispatching repair markers from this read-only pass.

Security
Cleared: The diff changes local approval prompt copy/helpers and focused tests only; it adds no dependency, workflow, secret, install, package, or permission surface.

Review findings

  • [P1] Thread the unavailable reason before defaulting to policy copy — src/infra/exec-approvals.ts:1864
Review details

Best possible solution:

Carry a producer-owned Allow Always unavailable reason through approval request/detail/view models and render it consistently across reply, forwarder, Telegram native, reaction/manual fallback, foreground exec, Control UI, embedded reconstructed prompts, and gateway resolve errors.

Do we have a high-confidence way to reproduce the issue?

Yes. The linked report gives concrete redirected-command steps, and source inspection shows current main collapses ask=always and one-shot persistence into the same missing Allow Always decision while the PR head still loses the reason on several surfaces.

Is this the best way to solve the issue?

No. The shared helper is useful, but the best fix is to propagate the real unavailable reason from the producer through request/detail/view models instead of defaulting missing reason data back to policy copy.

Full review comments:

  • [P1] Thread the unavailable reason before defaulting to policy copy — src/infra/exec-approvals.ts:1864
    This fallback still emits the ask=always policy explanation whenever callers omit both ask and allowAlwaysPersistenceKind, but production paths such as Telegram native approval payloads, reconstructed tool prompts, and reaction prompt text still only carry allowed decisions or hard-coded copy. Redirected one-shot approvals can therefore keep the reported misleading text on those surfaces; carry a producer-owned one-shot reason through the request/detail/view path before rendering.
    Confidence: 0.94

Overall correctness: patch is incorrect
Overall confidence: 0.93

AGENTS.md: found and applied where relevant.

Codex review notes: model internal, reasoning high; reviewed against 816038e97a5d.

Label changes

Label justifications:

  • P2: This PR targets a bounded approval workflow regression that misleads users and adds approval friction without data loss or an outage.
  • merge-risk: 🚨 other: A partial renderer-only change can leave approval surfaces explaining the same one-shot command differently or keep the reported visible path unfixed.
  • rating: 🧂 unranked krab: Overall readiness is 🧂 unranked krab; proof is 🦪 silver shellfish and patch quality is 🧂 unranked krab.
  • status: 📣 needs proof: The PR needs real behavior proof before ClawSweeper can clear the contributor ask. Needs stronger real behavior proof before merge: Mantis shows Telegram Desktop before/after proof for the initial candidate commit, but current head changed the missing-ask fallback and has no fresh production-path proof after that behavior change. The contributor should add current-head proof with private details redacted, then update the PR body or ask for @clawsweeper re-review.
  • proof: 📸 screenshot: Contributor real behavior proof includes screenshot evidence. Mantis shows Telegram Desktop before/after proof for the initial candidate commit, but current head changed the missing-ask fallback and has no fresh production-path proof after that behavior change. The contributor should add current-head proof with private details redacted, then update the PR body or ask for @clawsweeper re-review.
  • mantis: telegram-visible-proof: Mantis should capture Telegram visible proof. The PR changes visible Telegram approval prompt text, so a short Telegram Desktop proof can directly show the fixed redirected-command prompt.
Evidence reviewed

PR surface:

Source +36, Tests +56. Total +92 across 4 files.

View PR surface stats
Area Files Added Removed Net
Source 3 40 4 +36
Tests 1 56 0 +56
Docs 0 0 0 0
Config 0 0 0 0
Generated 0 0 0 0
Other 0 0 0 0
Total 4 96 4 +92

What I checked:

  • PR-head fallback still preserves the reported bad copy: At PR head, getAllowAlwaysUnavailableReason returns the ask=always policy message whenever both ask and allowAlwaysPersistenceKind are missing, which is exactly the missing-data case still present on several prompt paths. (src/infra/exec-approvals.ts:1864, ef947edb148b)
  • Telegram native path still omits reason input: The Telegram native approval payload passes action decisions into the shared reply renderer but does not pass ask or an explicit one-shot persistence reason. (extensions/telegram/src/approval-handler.runtime.ts:89, ef947edb148b)
  • Embedded prompt reconstruction still omits reason input: The embedded reconstructed approval prompt passes allowedDecisions from tool details but no ask or one-shot reason, so the new helper can fall back to the misleading policy copy there too. (src/agents/embedded-agent-subscribe.handlers.tools.ts:691, ef947edb148b)
  • Reaction prompt remains hard-coded: The manual reaction prompt section still emits the effective-policy explanation whenever Allow Always is absent. (src/plugin-sdk/approval-reaction-runtime.ts:213, ef947edb148b)
  • Current main collapses policy and one-shot causes: Current main removes Allow Always for either ask === "always" or one-shot persistence, so allowed decisions alone do not preserve the reason. (src/infra/exec-approvals.ts:1824, 816038e97a5d)
  • Visual proof is stale for current head: The prepared media manifest shows ffprobe failed, but downloaded screenshots show a before/after Telegram prompt for candidate commit 635555a...; current PR head is ef947ed... after the missing-ask fallback changed. (635555a44c03)

Likely related people:

  • gumadeiras: Authored and merged the effective policy reporting/actions PR that added generic approval unavailable copy across reply, forwarder, gateway, and related approval surfaces; also authored the native approval lifecycle assembly PR. (role: introduced effective-policy approval presentation path; confidence: high; commits: ba735d015809, d78512b09d1d; files: src/infra/exec-approval-reply.ts, src/infra/exec-approval-forwarder.ts, src/gateway/server-methods/exec-approval.ts)
  • jesse-merhi: Authored the command planner rebuild that keeps unpersistable shell shapes one-shot and added typed unavailable decisions through the exec approval path. (role: feature-history owner for one-shot persistence behavior; confidence: high; commits: c9707ab635b9; files: src/infra/exec-approvals.ts, src/agents/bash-tools.exec-host-gateway.ts, src/agents/bash-tools.exec-host-node.ts)
  • vincentkoc: Current checkout blame for the central decision and renderer lines points to a recent broad snapshot commit, so this is useful recent provenance but weaker than the feature PR history. (role: recent current-main line provenance; confidence: low; commits: 51e0997c2bbd; files: src/infra/exec-approvals.ts, src/infra/exec-approval-reply.ts, src/infra/exec-approval-forwarder.ts)
What the crustacean ranks mean
  • 🦀 challenger crab: rare, exceptional readiness with strong proof, clean implementation, and convincing validation.
  • 🦞 diamond lobster: very strong readiness with only minor maintainer review expected.
  • 🐚 platinum hermit: good normal PR, likely mergeable with ordinary maintainer review.
  • 🦐 gold shrimp: useful signal, but proof or patch confidence is still limited.
  • 🦪 silver shellfish: thin signal; proof, validation, or implementation needs work.
  • 🧂 unranked krab: not merge-ready because proof is missing/unusable or there are serious correctness or safety concerns.
  • 🌊 off-meta tidepool: rating does not apply to this item.

Shiny media proof means a screenshot, video, or linked artifact directly shows the changed behavior. Runtime, network, CSP, and security claims still need visible diagnostics.

How this review workflow works
  • ClawSweeper keeps one durable marker-backed review comment per issue or PR.
  • Re-runs edit this comment so the latest verdict, findings, and automation markers stay together instead of adding duplicate bot comments.
  • A fresh review can be triggered by eligible @clawsweeper re-review comments, exact-item GitHub events, scheduled/background review runs, or manual workflow dispatch.
  • PR/issue authors and users with repository write access can comment @clawsweeper re-review or @clawsweeper re-run on an open PR or issue to request a fresh review only.
  • Maintainers can also comment @clawsweeper review to request a fresh review only.
  • Fresh-review commands do not start repair, autofix, rebase, CI repair, or automerge.
  • Maintainer-only repair and merge flows require explicit commands such as @clawsweeper autofix, @clawsweeper automerge, @clawsweeper fix ci, or @clawsweeper address review.
  • Maintainers can comment @clawsweeper explain to ask for more context, or @clawsweeper stop to stop active automation.

@clawsweeper clawsweeper Bot added rating: 🧂 unranked krab Not merge-ready due to missing proof or serious correctness/safety concerns. status: 📣 needs proof The PR needs real behavior proof before ClawSweeper can clear the contributor ask. mantis: telegram-visible-proof Mantis should capture Telegram visible proof. P2 Normal backlog priority with limited blast radius. merge-risk: 🚨 other 🚨 Merging this PR has meaningful risk outside the owned taxonomy. labels Jun 26, 2026
@clawsweeper
clawsweeper Bot temporarily deployed to qa-live-shared June 26, 2026 22:07 Inactive
- Fix forwarder path (exec-approval-forwarder.ts) same as reply path
- Default to policy message when ask is unavailable (null/undefined)
- Leave approval-reaction-runtime and bash-tools as-is (no ask info)

Closes openclaw#97069
@openclaw-mantis

Copy link
Copy Markdown
Contributor

Mantis Telegram Desktop Proof

Summary: Mantis captured native Telegram Desktop before/after GIFs showing the approval prompt reason change.

Main screenshot This PR screenshot
Baseline native Telegram Desktop screenshot Candidate native Telegram Desktop screenshot
Main This PR
Baseline native Telegram Desktop proof GIF Candidate native Telegram Desktop proof GIF

Motion-trimmed clips:

Raw QA files: https://artifacts.openclaw.ai/mantis/telegram-desktop/pr-97106/run-28267919029-1/index.json

@clawsweeper

clawsweeper Bot commented Jun 26, 2026

Copy link
Copy Markdown
Contributor

🦞🧹
ClawSweeper re-review requested.

I asked ClawSweeper to review this item again.
Action: item re-review queued (workflow sweep.yml, event repository_dispatch).
Result: the existing ClawSweeper review comment will be edited in place when the review finishes.

ZOOWH added 2 commits June 29, 2026 19:38
…n through approval data

Export a single source-of-truth function from exec-approvals.ts that
all renderers use to show why Allow Always is unavailable. Supports
explicit allowAlwaysPersistenceKind for paths that carry it, and
infers from ask for paths that don't.

Closes openclaw#97069
@ZOOWH

ZOOWH commented Jun 29, 2026

Copy link
Copy Markdown
Contributor Author

@clawsweeper re-review

@clawsweeper

clawsweeper Bot commented Jun 29, 2026

Copy link
Copy Markdown
Contributor

🦞🧹
ClawSweeper re-review requested.

I asked ClawSweeper to review this item again.
Action: item re-review queued (workflow sweep.yml, event repository_dispatch).
Result: the existing ClawSweeper review comment will be edited in place when the review finishes.

ZOOWH added 3 commits June 29, 2026 19:57
Add production-shaped tests for getAllowAlwaysUnavailableReason through
the reply path: explicit one-shot kind and ask=always policy override.
@ZOOWH

ZOOWH commented Jun 29, 2026

Copy link
Copy Markdown
Contributor Author

@clawsweeper re-review

Addressed P1: added shared getAllowAlwaysUnavailableReason in exec-approvals.ts as single source of truth. All renderers (reply + forwarder) now use it. Added allowAlwaysPersistenceKind param for paths that carry explicit kind. Added production-shaped tests for both explicit one-shot kind and ask=always policy override.

@clawsweeper

clawsweeper Bot commented Jun 29, 2026

Copy link
Copy Markdown
Contributor

🦞🧹
ClawSweeper re-review requested.

I asked ClawSweeper to review this item again.
Action: item re-review queued (workflow sweep.yml, event repository_dispatch).
Result: the existing ClawSweeper review comment will be edited in place when the review finishes.

@clawsweeper clawsweeper Bot added status: 📣 needs proof The PR needs real behavior proof before ClawSweeper can clear the contributor ask. and removed proof: sufficient ClawSweeper judged the real behavior proof convincing. status: ⏳ waiting on author ClawSweeper has contributor-facing work open and is waiting for author action. labels Jun 29, 2026
@ZOOWH

ZOOWH commented Jun 30, 2026

Copy link
Copy Markdown
Contributor Author

@clawsweeper re-review

@ZOOWH

ZOOWH commented Jul 1, 2026

Copy link
Copy Markdown
Contributor Author

@clawsweeper re-review

@clawsweeper

clawsweeper Bot commented Jul 1, 2026

Copy link
Copy Markdown
Contributor

🦞🧹
ClawSweeper re-review requested.

I asked ClawSweeper to review this item again.
Action: item re-review queued (workflow sweep.yml, event repository_dispatch).
Result: the existing ClawSweeper review comment will be edited in place when the review finishes.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

mantis: telegram-visible-proof Mantis should capture Telegram visible proof. merge-risk: 🚨 other 🚨 Merging this PR has meaningful risk outside the owned taxonomy. P2 Normal backlog priority with limited blast radius. proof: 📸 screenshot Contributor real behavior proof includes screenshot evidence. rating: 🧂 unranked krab Not merge-ready due to missing proof or serious correctness/safety concerns. size: S status: 📣 needs proof The PR needs real behavior proof before ClawSweeper can clear the contributor ask.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug]: Approval prompt wrongly says effective policy requires approval every time for non-persistable redirected command

1 participant