Skip to content

fix(exec): emit approval timeout suppression events#98301

Closed
LiLan0125 wants to merge 2 commits into
openclaw:mainfrom
LiLan0125:fix/98279-approval-timeout-suppressed-event
Closed

fix(exec): emit approval timeout suppression events#98301
LiLan0125 wants to merge 2 commits into
openclaw:mainfrom
LiLan0125:fix/98279-approval-timeout-suppressed-event

Conversation

@LiLan0125

Copy link
Copy Markdown
Contributor

Summary

  • Adds a public exec.approval.timeout-suppressed diagnostic event for stale exec approval timeout follow-ups that are intentionally dropped after a session rebound.
  • Emits the event from both suppression paths: direct follow-up delivery and gateway preflight.
  • Carries only bounded operator metadata: approvalId, source, and reason; no session key, route id, command text, or channel target is added.
  • Projects the event into diagnostic stability snapshots and preserves safe approval ids when persisted stability bundles are read back.
  • Updates the plugin SDK surface budget for the new public diagnostic event type.

What Problem This Solves

When an exec approval timeout follow-up is stale because the target session was rebound by reset/new-session flow, OpenClaw intentionally suppresses the follow-up. Before this patch, that suppression was only visible through a free-form log line, so operator tooling could not reliably distinguish an intentional stale suppression from lost delivery or a stuck approval path.

Closes #98279

Why This Change Was Made

The existing stale-session guards already know when the approval-time session no longer matches the current session. The fix emits a structured diagnostic event at those existing guards without changing delivery, dedupe, session mutation, or approval resolution behavior.

The event name follows the issue wording: exec.approval.timeout-suppressed. The payload uses source: "agent-direct" | "gateway-preflight" to identify which suppression point fired and reason: "session-rebound" for a stable low-cardinality reason code.

User Impact

Operators and diagnostics consumers can subscribe to the public diagnostic stream or inspect stability/support bundles to see when an approval timeout follow-up was intentionally suppressed. Existing end-user behavior is unchanged.

Change Type

  • Bug fix
  • Feature
  • Refactor
  • Docs
  • Security
  • Chore

Scope

  • Gateway
  • Skills
  • Auth
  • Memory
  • Integrations
  • API
  • UI/UX
  • CI

Evidence

  • node scripts/run-vitest.mjs src/agents/bash-tools.exec-approval-followup.test.ts passed.
  • node scripts/run-vitest.mjs src/infra/diagnostic-events.test.ts src/logging/diagnostic-stability.test.ts src/logging/diagnostic-stability-bundle.test.ts passed.
  • OPENCLAW_VITEST_MAX_WORKERS=1 node scripts/run-vitest.mjs run --config test/vitest/vitest.gateway.config.ts src/gateway/server-methods/agent.test.ts -t "drops a stale exec approval followup at preflight" exited 0.
  • node scripts/plugin-sdk-surface-report.mjs --check passed.
  • OPENCLAW_OXLINT_SKIP_PREPARE=1 node scripts/run-oxlint.mjs --tsconfig config/tsconfig/oxlint.core.json <changed files> passed.
  • timeout 360s node scripts/run-tsgo.mjs -p test/tsconfig/tsconfig.core.test.json --incremental --tsBuildInfoFile .artifacts/tsgo-cache/core-test.tsbuildinfo passed.
  • timeout 180s node scripts/run-tsgo.mjs -p extensions/diagnostics-otel/tsconfig.json --incremental --tsBuildInfoFile .artifacts/tsgo-cache/diagnostics-otel.tsbuildinfo passed.
  • timeout 180s node scripts/run-tsgo.mjs -p extensions/diagnostics-prometheus/tsconfig.json --incremental --tsBuildInfoFile .artifacts/tsgo-cache/diagnostics-prometheus.tsbuildinfo passed.
  • git diff --check passed.

Real behavior proof

Behavior addressed: A stale approval-timeout follow-up is suppressed and emits a structured exec.approval.timeout-suppressed event with the approval id, suppression source, reason, and dispatcher timestamp.

Environment tested: Linux workspace, Node 22.22.0, current main plus this patch.

Steps run after the patch: Ran node --import tsx --no-warnings -e ... and imported the real sendExecApprovalFollowup, onDiagnosticEvent, and waitForDiagnosticEventsDrained functions. The script seeded a temp session store where agent:main:main had already rebound from session-original to session-after-reset, then sent an approval-timeout follow-up for the original session.

Evidence after fix:

[agents/exec-approval-followup] Dropping stale denied exec approval followup req-proof-rebound: session agent:main:main was rebound before the approval resolved
{
  "result": false,
  "events": [
    {
      "type": "exec.approval.timeout-suppressed",
      "approvalId": "req-proof-rebound",
      "source": "agent-direct",
      "reason": "session-rebound",
      "seq": 2,
      "ts": 1782863776172
    }
  ]
}

Observed result after the fix: The follow-up returned false, no delivery was attempted, and the public diagnostic subscriber received exactly the new suppression event with approvalId, source, reason, and ts.

What was not tested: A live external channel timeout with real user interaction was not exercised; the proof calls the production follow-up function directly with a real session-store rebound scenario.

Root Cause

  • Root cause: stale approval-timeout follow-ups were intentionally dropped after session rebound, but the drop paths only wrote free-form log messages.
  • Missing guardrail: the existing diagnostic event stream and stability recorder had no event type for this suppression.

Regression Test Plan

  • Direct follow-up test asserts the public diagnostic stream receives exec.approval.timeout-suppressed when a denied timeout follow-up is stale.
  • Gateway preflight test asserts the event is emitted before the rebound session is touched.
  • Diagnostic events test confirms public subscribers receive the event.
  • Stability recorder test confirms snapshots include approval id, source, reason, and timestamp.
  • Stability bundle test confirms safe approval ids survive imported bundle sanitization and unsafe ids are dropped.

Impact Assessment

  • Scope: additive diagnostic event emitted from two existing stale-session guards.
  • Downstream: public diagnostic stream and stability/support-bundle readers gain a new event type; delivery behavior and session mutation are unchanged.
  • Edge cases: empty or malformed imported approval ids are not preserved by the bundle reader because it only keeps safe diagnostic code strings.
  • Hard-coded values: no runtime configuration defaults were added; event values are derived from the suppression path and approval id already present at the call site.
  • Existing fixes: open PRs fix(exec): emit structured event when stale approval followups are suppressed (fixes #98279) #98292 and fix(gateway): emit stale exec approval followup diagnostics #98293 were checked. This patch keeps the event payload narrow, covers direct and gateway suppression paths, preserves persisted stability correlation, and includes production-function proof.

Security Impact

  • New permissions: No
  • Secrets handling changed: No
  • New network calls: No
  • Payload text, command text, session keys, chat ids, and route ids are not added to the event.

@openclaw-barnacle openclaw-barnacle Bot added gateway Gateway runtime scripts Repository scripts agents Agent runtime and tooling size: M labels Jul 1, 2026
@clawsweeper

clawsweeper Bot commented Jul 1, 2026

Copy link
Copy Markdown
Contributor

Thanks for the contribution. ClawSweeper proposes closing this for now: the implementation may be reasonable, but passing review and proof does not establish that OpenClaw should add this product surface.

Close: the patch is coherent and proof-backed, but it adds new public diagnostic event vocabulary and persisted stability metadata while competing candidate PRs use different contracts and no maintainer has selected the product/API direction.

Root-cause cluster
Relationship: fixed_by_candidate
Canonical: #98279
Summary: This PR is one candidate fix for the open structured stale approval-timeout suppression event issue; sibling PRs target the same request with different coverage or event vocabulary.

Members:

Proposal only: this assessment does not dispatch repair, suppress jobs, mutate sibling items, close, or merge anything.

This is a proposal only until the separate default-off apply policy is enabled and all live maintainer-signal checks pass. A maintainer can sponsor the direction, request a narrower version, or apply clawsweeper:human-review to keep it open.

Review details

Best possible solution:

Keep #98279 as the canonical request until a maintainer selects one stable public diagnostic contract and sponsors a single implementation.

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

Yes, at source level: current main has direct and gateway stale suppression branches that only log or return an ok dropped summary, while public diagnostic subscribers filter out log.record. I did not run a live external-channel approval-timeout scenario in this read-only review.

Is this the best way to solve the issue?

Conditionally yes: emitting at the existing stale drop points is the narrow implementation shape, but maintainers still need to accept one public event vocabulary across the competing PRs.

Security review:

Security review cleared: No concrete security or supply-chain issue found; the diff adds bounded diagnostic event emission, stability sanitization, OTEL handling, tests, and script budget updates without dependency, workflow, secret, or package execution changes.

AGENTS.md: found and applied where relevant.

What I checked:

  • Repository policy read: The full root AGENTS.md and scoped guides for agents, gateway, gateway server methods, extensions, and scripts were read; the public plugin/operator diagnostic contract and compatibility guidance directly affected the verdict. (AGENTS.md:24, 7006e6fcac9b)
  • Current main direct suppression gap: Current main drops stale direct exec approval follow-ups by logging and returning false, without a structured diagnostic event at the suppression point. (src/agents/bash-tools.exec-approval-followup.ts:389, 7006e6fcac9b)
  • Current main gateway preflight gap: Current main's gateway preflight rebound guard logs, records an ok dropped payload, and returns before any structured diagnostic event is emitted. (src/gateway/server-methods/agent.ts:1495, 7006e6fcac9b)
  • Current public diagnostics do not expose logs: onDiagnosticEvent filters out log.record, so the existing free-form log line is not the operator-facing structured event requested by the linked issue. (src/infra/diagnostic-events.ts:1304, 7006e6fcac9b)
  • PR adds public diagnostic contract: The PR diff adds DiagnosticExecApprovalTimeoutSuppressedEvent with event type, approvalId, source, and reason fields, then adds it to public diagnostic payload and async dispatch surfaces. (src/infra/diagnostic-events.ts:517, 24ce91e41694)
  • Competing public vocabulary: Related open PR fix(gateway): emit stale exec approval followup diagnostics #98293 targets the same issue with a different public event name and payload vocabulary, so green CI cannot choose the supported contract.

Likely related people:

  • openperf: Authored the merged stale exec approval follow-up suppression change that added the direct and gateway preflight drop behavior now being instrumented. (role: introduced behavior; confidence: high; commits: 2ffbea20d2f6; files: src/agents/bash-tools.exec-approval-followup.ts, src/gateway/server-methods/agent.ts)
  • obviyus: Live GitHub path history shows recent changes around the gateway agent preflight area that this PR instruments. (role: recent gateway area contributor; confidence: medium; commits: 8829dc4aae5b, 35d06e6356a6; files: src/gateway/server-methods/agent.ts)
  • amknight: Recent diagnostic event and telemetry work touched the public diagnostic event contract area that this PR extends. (role: recent diagnostics contract contributor; confidence: medium; commits: 527f20f4b5e5, aeab5f841832; files: src/infra/diagnostic-events.ts, extensions/diagnostics-otel/src/service.ts)
  • vincentkoc: Recent diagnostic stability and diagnostics-otel changes are adjacent to the new event projection and OTEL handling surfaces. (role: recent diagnostics and stability contributor; confidence: medium; commits: 49e95c53083d, b6714bf10902; files: src/logging/diagnostic-stability.ts, extensions/diagnostics-otel/src/service.ts)

Codex review notes: model internal, reasoning high; reviewed against 7006e6fcac9b.

@clawsweeper clawsweeper Bot added proof: sufficient ClawSweeper judged the real behavior proof convincing. rating: 🐚 platinum hermit Good normal PR readiness with ordinary maintainer review expected. status: 👀 ready for maintainer look ClawSweeper has no concrete contributor-facing blocker left for this PR. P2 Normal backlog priority with limited blast radius. merge-risk: 🚨 compatibility 🚨 May break existing users, config, migrations, defaults, or upgrade paths. labels Jul 1, 2026
@clawsweeper clawsweeper Bot added rating: 🦪 silver shellfish Thin PR readiness signal; proof, validation, or implementation needs work. status: ⏳ waiting on author ClawSweeper has contributor-facing work open and is waiting for author action. and removed rating: 🐚 platinum hermit Good normal PR readiness with ordinary maintainer review expected. status: 👀 ready for maintainer look ClawSweeper has no concrete contributor-facing blocker left for this PR. labels Jul 1, 2026
@openclaw-barnacle openclaw-barnacle Bot added the extensions: diagnostics-otel Extension: diagnostics-otel label Jul 1, 2026
@clawsweeper clawsweeper Bot added rating: 🐚 platinum hermit Good normal PR readiness with ordinary maintainer review expected. and removed rating: 🦪 silver shellfish Thin PR readiness signal; proof, validation, or implementation needs work. labels Jul 1, 2026
@LiLan0125

Copy link
Copy Markdown
Contributor Author

@clawsweeper re-review

Fixed the P2 finding: diagnostics-otel now explicitly ignores exec.approval.timeout-suppressed in the diagnostic event switch.

Verified:

  • OPENCLAW_OXLINT_SKIP_PREPARE=1 node scripts/run-oxlint.mjs --tsconfig config/tsconfig/oxlint.extensions.json extensions/diagnostics-otel/src/service.ts
  • timeout 180s node scripts/run-tsgo.mjs -p extensions/diagnostics-otel/tsconfig.json --incremental --tsBuildInfoFile .artifacts/tsgo-cache/diagnostics-otel.tsbuildinfo
  • GitHub checks on 24ce91e: check-lint pass; check-additional-extension-bundled pass.

@clawsweeper clawsweeper Bot added status: 👀 ready for maintainer look ClawSweeper has no concrete contributor-facing blocker left for this PR. and removed status: ⏳ waiting on author ClawSweeper has contributor-facing work open and is waiting for author action. labels Jul 1, 2026
@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.

@steipete

steipete commented Jul 1, 2026

Copy link
Copy Markdown
Contributor

Thanks for the implementation. Closing this as superseded by #98293, landed as 9f98b6e.

The selected patch uses the narrower exec.approval.followup_suppressed contract because the stale guard can suppress both denied/timeout follow-ups and completed-result follow-ups. It covers the same direct-delivery and gateway-preflight owner paths, preserves only privacy-safe operational metadata, and reached the result with a smaller change surface plus focused regression, stability-bundle, SDK-budget, docs, and exact-head CI proof.

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

Labels

agents Agent runtime and tooling extensions: diagnostics-otel Extension: diagnostics-otel gateway Gateway runtime merge-risk: 🚨 compatibility 🚨 May break existing users, config, migrations, defaults, or upgrade paths. P2 Normal backlog priority with limited blast radius. proof: sufficient ClawSweeper judged the real behavior proof convincing. rating: 🐚 platinum hermit Good normal PR readiness with ordinary maintainer review expected. scripts Repository scripts size: M status: 👀 ready for maintainer look ClawSweeper has no concrete contributor-facing blocker left for this PR.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Emit structured event when stale approval-timeout follow-ups are suppressed

2 participants