ZeroClaw
ZeroClaw puts WhatsApp approvals under explicit policy
ZeroClaw proposes approval requests for WhatsApp Web, guard-owned approval tokens for WhatsApp Cloud and policy enforcement across both WhatsApp Web modes. The three records align user prompts, token custody and channel authorization around one approval boundary.
zeroclaw-labs/zeroclaw PR #9385 is the inspected primary source: “feat(channels): implement request_approval for WhatsApp Web.” The related records below were inspected as supporting context rather than independent confirmation.
The facts
- zeroclaw-labs/zeroclaw PR #9385 was created 2026-07-26T13:18:52Z and was open at inspection; its title is “feat(channels): implement request_approval for WhatsApp Web.” - zeroclaw-labs/zeroclaw PR #9612 was created 2026-08-01T09:32:22Z and was open at inspection; its title is “fix(channels): tie the WhatsApp Cloud approval token to a guard so no exit orphans it.” - zeroclaw-labs/zeroclaw PR #9609 was created 2026-08-01T05:13:03Z and was open at inspection; its title is “fix(channels): enforce WhatsApp Web chat policies under both modes.” - The repository reported 32562 stars, 718 open issues, default branch master, and last push 2026-08-11T23:50:07Z. - The latest tagged-release baseline checked was v0.8.4 published 2026-08-02T21:00:00Z.
What changed
zeroclaw-labs/zeroclaw PR #9385 reports: Summary **Base branch:** master **What changed and why:** Implements request_approval for the WhatsApp Web transport, so a tool in always_ask (or one the risk profile will not auto-approve) is decided by a human in the chat the message came from rather than denied by default. Web was the transport where this was missing. Registration reserves its token under the map lock through the entry API, inserting only into a vacant slot and retrying while a code is occupied. Without that, two concurrent prompts could pick the same six-character code and the second would replace the first's pending sender. Every registration carries a generation id, and all cleanup paths compare it before removing. A resolver can take an entry out while a cleanup branch is still in flight, and a later request can then reserve the same code, so removal by token alone can delete an unrelated request. A reply is honoured only when it comes from the same chat the prompt went to and from a peer the alias is authorized to take instructions from,
zeroclaw-labs/zeroclaw PR #9612 reports: Summary **Base branch:** master **What changed and why:** request_approval registers a token in the process-global PENDING_APPROVALS map before it does anything else, and that token is a bearer credential for a tool-call decision. Two exits left an entry behind, so the decision stayed resolvable by whoever learned the code for the lifetime of the process. The send is the first thing after registration, so a transient send failure propagated out through? with the entry already in the map and nothing later in the function ran. That path needs no cancellation to reach, which makes it the cheaper of the two. Discord and Signal already remove explicitly before returning a send error, so the Cloud transport was the odd one out rather than the pattern being unknown here. Cancellation is the other. Every explicit cleanup lives inside the async body, so none of it runs when the future is dropped, which a caller-side timeout or a shutdown both do. Drop is the only hook that survives that, so the entry's lifetime is now tied to
zeroclaw-labs/zeroclaw PR #9609 reports: Summary **Base branch:** master **What changed and why:** dm_policy and group_policy were read only inside the mode = "personal" branch, so a business-mode deployment validated both keys and then consulted neither. dm_policy defaults to allowlist, so such a channel reads as restrictive while answering every message it receives. The decision now runs under both modes. The decision moves into chat_type_policy_decision, which takes no WhatsAppWebMode. A function that cannot see the mode cannot quietly start depending on one again, which is how the original defect arose. composed_chat_policy_decision layers the personal-only self-chat exception on top. self_chat_mode stays personal-only; it describes a personal account talking to itself. The fromMe guard also stays in the personal branch, and that scoping is a property of the branch rather than of the account. Business mode is still a linked-device session and WhatsApp mirrors the operator's outbound messages as fromMe in either mode, so the residual path #6353 closed for personal mode remains open under business mode. It predates this PR and is called out rather than asserted away; the book says
Why it matters
An approval prompt is theater if another channel mode bypasses policy or an exit path leaves the token orphaned. Chat authorization must survive disconnects and match the identity that receives the question. The operator test is whether visible output, retained state and authority still describe the same event after retries, background work or restart.
Current
The primary record was open when captured on 2026-08-12. The tagged-release baseline was v0.8.4 published 2026-08-02T21:00:00Z. Repository metadata, full source bodies, current pull requests and issues, releases, Google News, Hacker News, Lobsters, Metamesh and the rendered ClawCharts row were inspected. ClawCharts selected the subject; it did not prove the claim.
Evidence
The primary URL is https://github.com/zeroclaw-labs/zeroclaw/pull/9385. Supporting records are https://github.com/zeroclaw-labs/zeroclaw/pull/9612 and https://github.com/zeroclaw-labs/zeroclaw/pull/9609. Source bodies, timestamps and states are preserved in the daily evidence bundle. Test counts and reproductions remain attributed to their authors unless identified as independently rerun.
Source boundary
Open work is described as open, closed work as closed, and operator reports as reports. A pull request is evidence of proposed or reviewed direction, not proof of a shipped release. Search residue, package mirrors and historically published source spines were excluded.
Operator take
Bind approval tokens to chat, request and expiry; revoke them on every exit; and exercise identical policy tests across linked-device and cloud transports. Preserve a before-state receipt, make the smallest reversible change, and verify the original failure independently.
Caveat
Public project records are mutable. Status, scope and evidence can change after publication. This brief records the inspected state and does not authorize changes to a reader’s deployment.
Source inspected; source state, environment and release boundary remain explicit.