Skip to content

fix(skills): restore pending Workshop approval buttons - #139248

Merged
steipete merged 1 commit into
mainfrom
fix/skills-workshop-approval-owner-takeover
Sep 5, 2026
Merged

steipete merged 1 commit into
mainfrom
fix/skills-workshop-approval-owner-takeover

Conversation

@steipete

@steipete steipete commented Sep 5, 2026 •

Copy link
Copy Markdown
Contributor

Closes #135291
Supersedes #135406.

What Problem This Solves

Fixes an issue where Telegram users requesting a Skill Workshop lifecycle action received no approval buttons when skills.workshop.approvalPolicy was pending. The Gateway rejected the request before delivering the approval to the authorized owner's DM.

Why This Change Was Made

Workshop policy now supplies its workspace-skills approval owner to the existing signed Gateway transport. The Gateway retains its fail-closed owner and live-run checks. Lifecycle action recognition and approval text share one table, removing duplicated policy while preserving titles, descriptions, severity, decisions, and timeout.

This maintainer takeover preserves @gaoanze888's producer-side fix from #135406, reported by @khaisis in #135291 and reproduced on 2026.9.1 by @stemkat100. The new regression verifies the owner in the signed Gateway request for apply, reject, quarantine, and restore.

User Impact

Operators using pending Workshop approvals receive the existing decision controls in their configured Telegram DM, including when the request starts in a group. Approving lets the lifecycle action finish and produces a visible result in the originating chat. Automatic policy behavior is unchanged.

Evidence

Verified head: 4ee7ea7e0c05711c0adaa3b745a276c8f1b0e711.

  • Four new signed-owner cases fail against the pre-fix policy, solely because the decoded identity lacks approvalOwnerPluginId: "workspace-skills".
  • With the candidate restored, 86 tests pass across policy (6), signed runtime identity (33), Gateway authority (5), embedded approvals (26), and Telegram approval handler (16).
  • Changed-file format/lint/type/guard checks and the full private-QA build pass on sanitized AWS Crabbox. No candidate code ran locally.
  • Both independent reviews (uncommitted and committed branch) found no actionable defects. Exact-head CI passed; the repository CI watcher returned GREEN.
  • The real Gateway fixture passes for DM and group origins, observing the actual private card, Allow Once/Deny controls, callback acknowledgement, recorded user decision, applied proposal receipt, final originating-chat reply, and terminal card with no buttons. Each case makes three model requests and stops its Gateway cleanly.

Crabbox validation/build: run_c237dcb87132. Its subsequent standalone fixture launch hit a syntax error before Gateway startup; the corrected fixture reused the verified candidate build in successful run_f067cb152f81. The retry rechecked the artifact SHA, clean tracked tree, toolchain pin, and absent IAM credentials. Both runs used a fresh HOME, public networking, no Tailscale, and no credential hydration.

Commands executed remotely:

CI=1 OPENCLAW_VITEST_MAX_WORKERS=2 node scripts/run-vitest.mjs src/agents/tools/gateway.runtime-identity.test.ts -t 'signs the Skill Workshop'
CI=1 OPENCLAW_VITEST_MAX_WORKERS=2 node scripts/run-vitest.mjs src/skills/workshop/policy.test.ts src/agents/tools/gateway.runtime-identity.test.ts src/gateway/server-methods/plugin-approval.agent-runtime.test.ts src/agents/agent-tools.before-tool-call.embedded-mode.test.ts extensions/telegram/src/approval-handler.runtime.test.ts
CI=1 node scripts/check-changed.mjs -- src/skills/workshop/policy.ts src/skills/workshop/policy.test.ts src/agents/tools/gateway.runtime-identity.test.ts
OPENCLAW_BUILD_PRIVATE_QA=1 pnpm build
node --import tsx /tmp/pr135406-proof-design/workshop-approval-proof.mjs /work/crabbox/cbx_24d5ded707e7/fresh-pr-openclaw-openclaw-139248 .artifacts/qa-e2e/pr135406-after

The fixture uses createQaGatewayChild from extensions/qa-lab/api.ts with built dist/entry.js, fresh state, and separate unused ports. The mock provider invokes the real Workshop create/apply tools. Production policy, signing, Gateway registration, Telegram delivery, and callback handling produce the following synthetic transcript verdict:

{
  "head": "4ee7ea7e0c05711c0adaa3b745a276c8f1b0e711",
  "provider": "aws",
  "lease": "cbx_24d5ded707e7",
  "run": "run_f067cb152f81",
  "runtime": "openclaw",
  "externalAPIs": "synthetic Telegram Bot API and OpenAI Responses",
  "cases": [
    {
      "origin": "dm",
      "originChatId": 1357,
      "approvalDm": 1357,
      "approvalOwner": "workspace-skills",
      "cardMessageId": 9001,
      "buttons": [
        "Allow Once",
        "Deny"
      ],
      "callbackUpdateId": 2,
      "decision": "allow-once",
      "terminalReason": "user",
      "appliedProposal": "approval-dm-proof-20260905-d40349281f",
      "finalMessageId": 9002,
      "terminalCardButtons": [],
      "providerRequests": 3,
      "gatewayCleanup": "confirmed-stopped",
      "passed": true,
      "gatewayPort": 33369
    },
    {
      "origin": "group",
      "originChatId": -1002468135790,
      "approvalDm": 1357,
      "approvalOwner": "workspace-skills",
      "cardMessageId": 9001,
      "buttons": [
        "Allow Once",
        "Deny"
      ],
      "callbackUpdateId": 2,
      "decision": "allow-once",
      "terminalReason": "user",
      "appliedProposal": "approval-group-proof-20260905-2917eb2617",
      "finalMessageId": 9003,
      "terminalCardButtons": [],
      "providerRequests": 3,
      "gatewayCleanup": "confirmed-stopped",
      "passed": true,
      "gatewayPort": 45547
    }
  ]
}

Proof limits: this exercises a real Gateway with the OpenClaw runtime and synthetic Telegram/model APIs. The shared Codex host-tool contract was directly inspected at pinned rust-v0.153.4; no live Codex-provider call is claimed. Native Telegram Test Server proof was unavailable because the Convex CLI is absent and this contributor-code lane remains secretless.

The September 5 ClawSweeper review's delivery/callback/result/terminal-card follow-up and the original PR's signed-owner regression request are now addressed by the evidence above.

git diff --numstat against the main baseline:

Classification Added Removed Net
Production 27 43 -16
Tests 38 0 +38

Named follow-up: Bind trusted-policy approvals to their registered plugin owner. The separate registered-policy path in src/plugins/trusted-tool-policy.ts forwards an evaluator's optional approval owner, whereas ordinary hooks stamp the registry owner. It needs an external-plugin fixture and signed-boundary proof. No bundled production registrant was found; the core Workshop path repaired here is independent of that API.

Carry the core Workshop policy owner through the existing signed approval
transport, so pending lifecycle actions can reach authorized Telegram DMs.
Consolidate lifecycle action metadata and verify the signed owner boundary.

Fixes #135291. Based on #135406 by @gaoanze888; reported by @khaisis.
Production LOC: +27/-43 (-16); tests: +38/-0.

Co-authored-by: gaoanze <gaoanze@meituan.com>
@clawsweeper

clawsweeper Bot commented Sep 5, 2026 •

Copy link
Copy Markdown
Contributor

🦞👀
ClawSweeper picked this up.

Pull request received. I will update this pull request when review starts.

ClawSweeper review in progress

ClawSweeper is reviewing this revision. This supersedes any previous blocked status.

View the workflow run.

@openclaw-barnacle openclaw-barnacle Bot added agents Agent runtime and tooling size: S maintainer Maintainer-authored PR labels Sep 5, 2026
@clawsweeper clawsweeper Bot added P1 High-priority user-facing bug, regression, or broken workflow. proof: telegram-e2e This PR needs Telegram Test Server proof with the repository E2E skill. 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 Sep 5, 2026
@clawsweeper

clawsweeper Bot commented Sep 5, 2026 •

Copy link
Copy Markdown
Contributor

Codex review: blocked before merge. Reviewed September 5, 2026, 1:59 PM ET / 17:59 UTC.

ClawSweeper review

What this changes

Restores pending Skill Workshop approval requests by supplying their signed owner, consolidates lifecycle action descriptions, and adds regression coverage for all four lifecycle actions.

Merge readiness

⛔ Blocked before merge - 2 items remain

Keep open: current main still contains the reported defect, and this focused repair has no actionable correctness findings. The maintainer takeover preserves the earlier contributor’s fix and adds useful signing-boundary coverage.

Priority: P1
Reviewed head: 4ee7ea7e0c05711c0adaa3b745a276c8f1b0e711

Review scores

Measure Result What it means
Overall readiness 🐚 platinum hermit (4/6) A focused producer repair with meaningful signing regression coverage and no blocking code findings.
Proof confidence 🌊 off-meta tidepool Not applicable: The ordinary external-contributor proof gate does not apply to this maintainer-authored PR. Its captured evidence exercises Workshop policy and signing with mocked Gateway I/O; the separately declared owner-DM delivery and callback verification remains pending before landing.
Patch quality 🐚 platinum hermit (4/6) No actionable review findings were identified.

Verification

Check Result Evidence
Real behavior Not applicable Not applicable: The ordinary external-contributor proof gate does not apply to this maintainer-authored PR. Its captured evidence exercises Workshop policy and signing with mocked Gateway I/O; the separately declared owner-DM delivery and callback verification remains pending before landing.
Evidence reviewed 12 items Introduced change and scope: The pinned merge-base-to-head diff changes only the Workshop policy and two tests. The producer supplies workspace-skills through the existing approval contract; action recognition, descriptions, decisions, and timeout retain their previous semantics.
Still necessary on current main: Fetched main still constructs requireApproval without pluginId at lines 208–216. The unchanged transport reads approval.pluginId, while the Gateway rejects signed requests without an approval owner. The reported failure therefore remains source-reproducible.
Canonical producer-to-transport path: The final-parameter policy calls the Workshop resolver, then requestPluginToolApproval passes its owner to withGatewayToolApprovalOwner. That wrapper preserves the admitted caller and scopes the owner to this request. Repairing the producer avoids weakening the Gateway or adding Telegram-specific policy.
Findings None None.
Security None None.

How this fits together

Skill Workshop manages proposed changes to an agent’s reusable skills. Its approval policy sends pending lifecycle actions through the Gateway, which delivers decision controls and releases or blocks execution.

flowchart TD
  A[Agent requests Workshop action] --> B{Approval policy}
  B -->|Automatic| F[Workshop action executes]
  B -->|Pending| C[Workshop supplies approval owner]
  C --> D[Gateway validates signed request]
  D --> E[Authorized owner receives decision controls]
  E -->|Allow and run remains active| F
  E -->|Deny or expire| G[Action remains blocked]
Loading

Before merge

  • Resolve merge risk (P1) - The captured evidence does not yet establish the complete group-origin request → private owner card → callback → Workshop result flow.
  • Complete next step (P2) - Complete the existing review follow-up by attaching the declared sanitized delivery-and-resolution results before landing.
Agent review details

Security

None.

PR surface

Source -16, Tests +38. Total +22 across 3 files.

View PR surface stats
Area Files Added Removed Net
Source 1 27 43 -16
Tests 2 38 0 +38
Docs 0 0 0 0
Config 0 0 0 0
Generated 0 0 0 0
Other 0 0 0 0
Total 3 65 43 +22

Review metrics

Metric Value Why it matters
Production versus test LOC Production −16 net; tests +38 net Consolidating lifecycle metadata absorbs the repair while adding coverage at the signed-request boundary.

Root-cause cluster

Relationship: fixed_by_candidate
Canonical: #135291
Summary: This PR is a candidate repair for the reported missing signed owner; the earlier contributor PR addresses the same defect.

Members:

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

Merge-risk options

Maintainer options:

  1. Decide the mitigation before merge
    Keep one producer-owned approval path that preserves pending-mode decisions, private delivery, and live-run checks without changing automatic mode.
  2. Pause or close
    Do not merge this PR until maintainers decide whether the risk is worth taking.

Technical review

Best possible solution:

Keep one producer-owned approval path that preserves pending-mode decisions, private delivery, and live-run checks without changing automatic mode.

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

Yes, from source: a pending Workshop lifecycle action omits pluginId on current main, leaving the signed owner absent and triggering the Gateway rejection. This review did not execute the runtime.

Is this the best way to solve the issue?

Yes. Supplying the owner at the Workshop producer reuses the existing transport and preserves Gateway validation; a Telegram workaround or relaxed owner check would repair the wrong boundary.

AGENTS.md: found and applied where relevant.

Codex review notes: model internal, reasoning high; reviewed against 925fb0c3353c.

Labels

Label justifications:

  • P1: The linked report documents a broken configured approval workflow, and current main still rejects its ownerless requests.
  • rating: 🐚 platinum hermit: Overall readiness is 🐚 platinum hermit; proof is 🌊 off-meta tidepool and patch quality is 🐚 platinum hermit.
  • status: 👀 ready for maintainer look: ClawSweeper has no concrete contributor-facing blocker left for this PR. Not applicable: The ordinary external-contributor proof gate does not apply to this maintainer-authored PR. Its captured evidence exercises Workshop policy and signing with mocked Gateway I/O; the separately declared owner-DM delivery and callback verification remains pending before landing.
  • proof: telegram-e2e: This PR needs Telegram Test Server proof with the repository E2E skill. Restored approval buttons and their terminal state are observable Telegram behavior; the root policy also permits the declared isolated Gateway harness evidence.

Evidence

What I checked:

  • Introduced change and scope: The pinned merge-base-to-head diff changes only the Workshop policy and two tests. The producer supplies workspace-skills through the existing approval contract; action recognition, descriptions, decisions, and timeout retain their previous semantics. (src/skills/workshop/policy.ts:192, 4ee7ea7e0c05)
  • Still necessary on current main: Fetched main still constructs requireApproval without pluginId at lines 208–216. The unchanged transport reads approval.pluginId, while the Gateway rejects signed requests without an approval owner. The reported failure therefore remains source-reproducible. (src/skills/workshop/policy.ts:208, 925fb0c3353c)
  • Canonical producer-to-transport path: The final-parameter policy calls the Workshop resolver, then requestPluginToolApproval passes its owner to withGatewayToolApprovalOwner. That wrapper preserves the admitted caller and scopes the owner to this request. Repairing the producer avoids weakening the Gateway or adding Telegram-specific policy. (src/agents/agent-tools.before-tool-call.approval.ts:289, 4ee7ea7e0c05)
  • Gateway authority remains intact: The handler rejects inactive authority and missing signed owners before creating an approval, then takes owner and origin from the signed identity. Existing tests cover closed authority, closure after handshake, ownerless requests, and forged request metadata. (src/gateway/server-methods/plugin-approval.ts:113, 4ee7ea7e0c05)
  • Execution and callback boundaries: Telegram checks the callback sender before resolving an approval; Gateway resolution applies current channel custody and pending-record checks. The approval manager refuses allowed projections after runtime authority closes, and harness policy execution rechecks authority after awaiting the policy. (src/gateway/exec-approval-manager.ts:718, 4ee7ea7e0c05)
  • Regression coverage and its boundary: The new table exercises apply, reject, quarantine, and restore_collection through the production policy and signing helpers, verifies the decoded owner, and checks allow/deny outcomes. Gateway I/O is mocked, so this test does not establish card delivery, callback completion, or the resulting skill mutation. Tests were inspected, not executed during this read-only review. (src/agents/tools/gateway.runtime-identity.test.ts:603, 4ee7ea7e0c05)

Likely related people:

  • steipete: Suggested for follow-up; no historical authorship or introduction is verified. (role: unverified routing candidate; confidence: low)
  • Harjoth Khara: Suggested for follow-up; no historical authorship or introduction is verified. (role: unverified routing candidate; confidence: low)

Rank-up moves

Optional improvements that raise the rating; they are not merge blockers.

  • Attach the already-declared sanitized owner-DM delivery, callback resolution, Workshop result, and terminal-card evidence.

Rating scale

Score Internal tier Crab rank Meaning
6/6 S 🦀 challenger crab Exceptional readiness
5/6 A 🦞 diamond lobster Very strong readiness
4/6 B 🐚 platinum hermit Good normal PR; ordinary maintainer review
3/6 C 🦐 gold shrimp Useful, but confidence is limited
2/6 D 🦪 silver shellfish Proof or implementation needs work
1/6 F 🧂 unranked krab Not merge-ready
N/A NA 🌊 off-meta tidepool Rating does not apply

Overall follows the weaker of proof and patch quality.
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.

Workflow

  • 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.

History

Review history (1 earlier review cycle)
  • reviewed 2026-09-05T17:41:35.366Z sha 4ee7ea7 :: blocked before merge. :: none

@steipete
steipete marked this pull request as ready for review September 5, 2026 17:52
@steipete
steipete merged commit 911d7d5 into main Sep 5, 2026
264 of 265 checks passed
@steipete
steipete deleted the fix/skills-workshop-approval-owner-takeover branch September 5, 2026 18:38
@steipete

steipete commented Sep 5, 2026

Copy link
Copy Markdown
Contributor Author

Merged via squash.

vincentkoc added a commit that referenced this pull request Sep 5, 2026
* commit '911d7d5b0c107f7274f2415ca9655dcc6105f0e8': (867 commits)
  fix(skills): deliver pending Workshop approval controls (#139248)
  fix(gateway): deliver loopback ask_user prompts to the originating channel (#138831)
  docs: keep root agent instructions focused on repository policy (#139258)
  fix(matrix): exclude archived state roots from scans and selection (#112329)
  perf(agents): prepare exact tool names once per catalog batch (#139267)
  fix: preserve Swarm progress and simplify owner plumbing (#139269)
  fix(tailscale): release claims after gateway process-group exit (#139261)
  refactor(cli): simplify lazy command registration (#139108)
  test(ui): retain synthetic widget failure evidence (#139229)
  fix(buzz): keep implicit replies at thread root (#138879)
  test(diagnostics): await graceful Gateway shutdown before cleanup (#139247)
  fix(ci): unblock releases with different tooling package managers (#139259)
  feat(ui): add social preview links for private sessions (#139250)
  fix(codex): retain failed conversations for guarded continuation (#139246)
  chore(ui): refresh control ui locales (#139200)
  fix(ci): run browser checks for real-gateway inputs (#139256)
  fix(llama-cpp): reconcile managed router state before requests (#136363)
  chore(i18n): refresh native locales (#139237)
  refactor(worker): unify inference payload handling (#139245)
  refactor(terminal): replay wrapped prefixes in one pass (#139240)
  ...
github-actions Bot pushed a commit to Desicool/openclaw that referenced this pull request Sep 6, 2026
)

Carry the core Workshop policy owner through the existing signed approval
transport, so pending lifecycle actions can reach authorized Telegram DMs.
Consolidate lifecycle action metadata and verify the signed owner boundary.

Fixes openclaw#135291. Based on openclaw#135406 by @gaoanze888; reported by @khaisis.
Production LOC: +27/-43 (-16); tests: +38/-0.

Co-authored-by: gaoanze <gaoanze@meituan.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

agents Agent runtime and tooling maintainer Maintainer-authored PR P1 High-priority user-facing bug, regression, or broken workflow. proof: telegram-e2e This PR needs Telegram Test Server proof with the repository E2E skill. rating: 🐚 platinum hermit Good normal PR readiness with ordinary maintainer review expected. size: S 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.

[Bug]: Skill Workshop approval buttons missing in Telegram DM on 2026.8.1

1 participant