fix(plugins): serialize binding approval saves#88945
Conversation
|
Codex review: needs maintainer review before merge. Reviewed June 14, 2026, 1:02 PM ET / 17:02 UTC. Summary PR surface: Source +9, Tests +118. Total +127 across 2 files. Reproducibility: yes. at source level: current main updates the approvals cache and then awaits an unqueued JSON write from the allow-always approval path, and the linked issue supplies an interleaving reproduction. I did not run the repro in this read-only review. Review metrics: 1 noteworthy metric.
Merge readiness Overall follows the weaker of proof and patch quality, so missing proof can cap an otherwise strong patch. Rank-up moves:
Next step before merge
Security Review detailsBest possible solution: Land this narrow shared save-chain fix for the shipped JSON approval file, while keeping the broader SQLite runtime-state migration in the separate database-first PR. Do we have a high-confidence way to reproduce the issue? Yes at source level: current main updates the approvals cache and then awaits an unqueued JSON write from the allow-always approval path, and the linked issue supplies an interleaving reproduction. I did not run the repro in this read-only review. Is this the best way to solve the issue? Yes: the shared resolver is used by Discord, Telegram, and Slack approval callbacks, so serializing the common persistence path is narrower and cleaner than channel-specific fixes. The broader SQLite migration remains a separate refactor, not a prerequisite for this bug fix. AGENTS.md: found and applied where relevant. Codex review notes: model internal, reasoning high; reviewed against 7e12a3326dd2. Label changesLabel changes:
Label justifications:
Evidence reviewedPR surface: Source +9, Tests +118. Total +127 across 2 files. View PR surface stats
What I checked:
Likely related people:
What the crustacean ranks mean
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
|
|
Thanks for merging this one too, @vincentkoc! Appreciate you landing the binding-approval serialization fix. 🙏 |
Summary
What problem does this PR solve?
allow-alwayssaves cannot write stale approval state after a newer save updates the in-memory cache.Why does this matter now?
#64065 remains open because Race C can lose persistent plugin-binding approvals on restart when concurrent approval saves race on
~/.openclaw/plugin-binding-approvals.json.What is the intended outcome?
Concurrent persistent plugin-binding approvals finish with disk state matching the latest in-memory approval file.
What is intentionally out of scope?
Race A and Race B from #64065 are left unchanged because the issue review already classifies them as mitigated by existing behavior.
What does success look like?
Two overlapping
allow-alwaysapprovals can both resolve successfully and the persisted approval file still contains the expected approvals after both writes complete.What should reviewers focus on?
The
saveApprovalsqueueing behavior and the out-of-order write regression coverage inconversation-binding.test.ts.Linked context
Which issue does this close?
Closes #64065
Which issues, PRs, or discussions are related?
Related #81402
Was this requested by a maintainer or owner?
The ClawSweeper review on #64065 requested a focused Race C repair with regression coverage.
Real behavior proof (required for external PRs)
allow-alwaysapproval saves should not leaveplugin-binding-approvals.jsonmissing a newer approval scope.requestPluginConversationBinding/resolvePluginConversationBindingApprovalmodules, real session-binding adapters, and a real temporary HOME approval file.node --import tsx/esm --input-type=modulescript that registered Discord adapters fordefaultandwork, requested two plugin bindings, resolved both withallow-alwaysviaPromise.all, then read.openclaw/plugin-binding-approvals.jsonfrom the temporary HOME.approved, and the real approval file contained bothdefaultandworkapproval scopes after the overlapping save path completed.crabboxfailed its basic binary sanity check, so the remote-wrapper form ofcheck:changedcould not start from this machine; I ran the local changed-check child path directly as supplemental validation.writeJsoncan complete after a newer in-memory approval state.Tests and validation
Which commands did you run?
node scripts/run-vitest.mjs src/plugins/conversation-binding.test.tspnpm exec oxfmt --check --threads=1 src/plugins/conversation-binding.ts src/plugins/conversation-binding.test.ts CHANGELOG.mdpnpm exec oxlint src/plugins/conversation-binding.ts src/plugins/conversation-binding.test.tsgit diff --checkOPENCLAW_CHECK_CHANGED_REMOTE_CHILD=1 OPENCLAW_CHANGED_LANES_RAW_SYNC=1 CI=1 PNPM_CONFIG_VERIFY_DEPS_BEFORE_RUN=false pnpm check:changedWhat regression coverage was added or updated?
Added
serializes overlapping always-allow approval writes, which blocks the first approval write, starts a second approval, verifies the second approval is still waiting, then confirms the persisted approval file contains both approval scopes after the queue drains.What failed before this fix, if known?
Before this fix, competing
saveApprovalscalls could await independentwriteJsoncalls, allowing an older file snapshot to persist after newer in-memory approval state was already visible.If no test was added, why not?
N/A.
Risk checklist
Did user-visible behavior change? (
Yes/No)No.
Did config, environment, or migration behavior change? (
Yes/No)No.
Did security, auth, secrets, network, or tool execution behavior change? (
Yes/No)No.
What is the highest-risk area?
Persistent plugin approval saving now waits behind an earlier approval save, so a slow or failed earlier disk write can delay later approval persistence.
How is that risk mitigated?
Each caller still receives its own write error if its save fails, the chain catches prior failures before accepting later writes, and the cache remains updated synchronously before disk persistence just like before.
Current review state
What is the next action?
Maintainer review and CI.
What is still waiting on author, maintainer, CI, or external proof?
Nothing known from the author side.
Which bot or reviewer comments were addressed?
Addressed the ClawSweeper #64065 recommendation to land a narrow Race C repair with regression coverage.