Greptile review on the original PR flagged that the four extractors used in
`hasInboundUserContent` (`extractText`, `extractMediaPlaceholder`,
`extractContactContext`, `extractLocationData`) do not surface
`buttonsResponseMessage`, `listResponseMessage`, `templateButtonReplyMessage`,
or `interactiveResponseMessage`. The gate would have silently dropped real
user button/list selections as if they were receipts.
Add a `hasInteractiveResponseContent(message)` helper that checks for the
four reply-message keys directly, and call it from `hasInboundUserContent`
both at the root and after walking `buildMessageChain` (so wrapped
ephemeral/viewOnce envelopes still surface their interactive responses).
5 new test cases cover all four reply types plus the wrapped-ephemeral path.
Sign-Off: hclsys
`checkInboundAccessControl` is currently called for every `messages.upsert`
notify entry, including receipts, typing indicators, presence updates, and
protocol messages that arrive on the same Baileys stream as real inbound
messages. With `dmPolicy: pairing`, this lets the gateway send a pairing
verification reply to a peer who never actually typed anything — e.g. when
Master sends an outbound message to a new JID and the receipt round-trip
arrives before the recipient ever replies.
Add a fast O(1) `hasInboundUserContent(message)` helper in
`extensions/whatsapp/src/inbound/extract.ts` that returns true iff any of
the existing extractors would surface user-visible content
(`extractText`, `extractMediaPlaceholder`, `extractContactContext`,
`extractLocationData`). Call it at the top of `normalizeInboundMessage`
right after the existing `fromMe` recent-outbound-echo guard, before
`checkInboundAccessControl`. Non-content events bail out cleanly with no
pairing side effects, no read-receipt, no enqueue.
Existing safeguards (recent-outbound echo skip, status/broadcast filter)
stay intact. All four content extractors are pure object-tree walks with
no I/O, so the gate adds only microseconds per upsert event.
Sign-Off: hclsys
For 054b2e1b7e: docs/install/docker.md "Storage and persistence" now
records that the bundled docker-compose.yml falls back to
${HOME}/.openclaw (and ${HOME}/.openclaw/workspace for the workspace
mount), or /tmp/.openclaw when HOME is also unset, when
OPENCLAW_CONFIG_DIR / OPENCLAW_WORKSPACE_DIR are not provided. That
matches the new default expressions in the compose file and prevents an
empty-source volume spec on bare environments.