Skip to content

feat(channel/bb): group mention gating — require_mention_in_groups#2585

Closed
maxtongwang wants to merge 115 commits intozeroclaw-labs:mainfrom
maxtongwang:feat/bb-issue7-upstream-pr
Closed

feat(channel/bb): group mention gating — require_mention_in_groups#2585
maxtongwang wants to merge 115 commits intozeroclaw-labs:mainfrom
maxtongwang:feat/bb-issue7-upstream-pr

Conversation

@maxtongwang
Copy link
Copy Markdown

@maxtongwang maxtongwang commented Mar 2, 2026

Summary

  • Base branch target: main
  • Problem: In group iMessage chats the bot responds to every message, making it intrusive when multiple people are conversing. No way to require an explicit @mention before the agent engages.
  • Why it matters: Mention gating is a standard group-chat UX pattern required for feature parity with OpenClaw's BlueBubbles extension and for polite group deployments.
  • What changed: Three new config keys on BlueBubblesConfig: require_mention_in_groups: bool (global flag, default false), mention_keyword: Option<String> (keyword to match, defaults to first allowed_senders entry), and groups: HashMap<String, BlueBubblesGroupConfig> (per-chat-GUID overrides). When a group message arrives and mention gating is active, the message text is case-insensitively checked for the keyword; non-matching messages are silently dropped (no response, no error sent). Per-group overrides can enable or disable gating for individual group GUIDs.
  • What did not change: DM messages are never gated. All existing allow/deny policy logic unchanged.

Label Snapshot (required)

  • Risk label: risk: low
  • Size label: size: S
  • Scope labels: channel, config
  • Module labels: channel: bluebubbles
  • Contributor tier label: (auto-managed)

Change Metadata

  • Change type: feature
  • Primary scope: channel

Linked Issue

Supersede Attribution (required when Supersedes # is used)

N/A

Validation Evidence (required)

cargo fmt --all -- --check   # pass
cargo clippy --all-targets -- -D warnings  # pass (delta scope)
cargo test   # pass

Security Impact (required)

  • New permissions/capabilities? No — reduces bot response surface (more restrictive, not less).
  • New external network calls? No
  • Secrets/tokens handling changed? No
  • File system access scope changed? No

Privacy and Data Hygiene (required)

  • Data-hygiene status: pass
  • Redaction/anonymization notes: mention_keyword is a bot-trigger word, not PII.
  • Neutral wording confirmation: pass

Compatibility / Migration

  • Backward compatible? Yes — require_mention_in_groups defaults to false; existing deployments unaffected.
  • Config/env changes? Yes — three new optional TOML keys.
  • Migration needed? No — additive only.

i18n Follow-Through (required when docs or user-facing wording changes)

  • i18n follow-through triggered? No — config reference docs not yet updated for these keys (follow-up).

Human Verification (required)

  • Verified scenarios: global flag enables gating; per-group override disables it for specific GUID; keyword match is case-insensitive; no keyword + no allowed_senders logs a warning at startup.
  • Edge cases: DM message always bypasses gating; empty groups map uses global flag.
  • What was not verified: live group chat on physical hardware.

Side Effects / Blast Radius (required)

  • Affected subsystems: src/channels/bluebubbles.rs (message dispatch), src/config/schema.rs (three new fields + nested struct).
  • Potential unintended effects: Misconfigured mention_keyword could silently drop all group messages. Warning is emitted on startup when no keyword is resolvable.
  • Guardrails: startup warning when no keyword is configured; silently drops (no error sent to user) to avoid spamming group chats.

Agent Collaboration Notes (recommended)

  • Agent tools used: Claude Code
  • Confirmation: naming + architecture boundaries followed per AGENTS.md + CONTRIBUTING.md.

Rollback Plan (required)

  • Fast rollback: remove require_mention_in_groups/mention_keyword/groups from schema + revert dispatch check.
  • Feature flags: controlled entirely by require_mention_in_groups = true in config.
  • Observable failure symptoms: group messages ignored even without require_mention_in_groups = true.

Risks and Mitigations

  • Risk: mention_keyword can be spoofed (any group member types it).
    • Mitigation: This is intentional — the keyword is a convenience trigger, not a security boundary. allowed_senders / group_allow_from remain the security gates.

Summary by CodeRabbit

  • New Features

    • BlueBubbles iMessage support: audio transcription, outbound message chunking, mention-based gating, per-group/DM policies, read receipts, reactions, and three BlueBubbles tools (group, message, send-attachment).
    • Local transcription API and Google OAuth sign-in/management.
  • Documentation

    • Expanded config & channel references, Matrix E2EE cross-links, multilingual docs, and updated policy allowlist notes.
  • Improvements

    • Async webhook acceptance with concurrency limits; manual redirect handling in web fetch.
  • Chores

    • Added automated PR review workflow.

Review Fixes (Round 2)

Missing mention-gating docs: Added require_mention_in_groups, mention_keyword, and per-group override config documentation to the BlueBubbles section (§4.20) of docs/channels-reference.md.

Also propagated from base branches: all fixes from PRs #2461#2584.

Review Fixes (Round 6)

  • oauth.rs: propagate HTTP client build failure — no silent timeout loss
  • oauth.rs: validate PKCE state before consuming session file (CSRF fix)
  • oauth.rs: use POST form body for token revocation (RFC 7009 compliance)
  • transcription.rs: explicit fail on non-UTF-8 temp paths, not empty-string passthrough
  • transcription.rs: clean up partial ffmpeg WAV on conversion failure
  • bluebubbles.rs: remove attachment GUID from error log
  • claude-review.yml: fix step-level PR condition (!= ''!= null)

Review Fixes (Round 7)

  • bluebubbles.rs: mention keyword comparison now lowercases both sides — previously only the incoming text was lowercased, so a keyword from allowed_senders with uppercase letters would never match
  • schema.rs: BlueBubblesConfig custom Debug output redacts groups HashMap to count only — map keys are chat GUIDs containing phone numbers/emails

Review Fixes (Round 8)

  • docs/channels-reference.md: fix mention-gating comment block — changed "explicitly @mentioned" to "contain a mention keyword"; added note "Case-insensitive keyword match"; added "DMs are never mention-gated." to both comment and policy prose paragraph
  • docs/channels-reference.md: add silent-drop note after group_allow_from prose — documents that keyword mismatch results in silent drop with no response and no error
  • docs/channels-reference.md: add BlueBubbles (gateway) row to ops keyword matrix §7.2 — lists startup, auth, and failure log keywords for fast triage
  • i18n follow-through (vi — full): §4.15 BlueBubbles config section added to channels-reference.md; [channels_config.bluebubbles] key table added to config-reference.md; ops matrix row and rg filter updated
  • i18n follow-through (el — full): BlueBubbles config snippet with dm_policy/group_policy/send_read_receipts added to channels-reference.md and config-reference.md
  • i18n hub parity (zh-CN, ja, ru, fr): channels-reference.md scope updated to mention BlueBubbles; config-reference.md update notes list new bluebubbles config keys

Review Fixes (cargo fmt)

  • Applied cargo fmt --all to files changed in this PR's diff — collapsed single-use iterator chains and reformatted match arm bodies to comply with project rustfmt settings

maxtongwang and others added 30 commits February 27, 2026 12:06
Adds @claude mention support on PRs using anthropics/claude-code-action@v1.
Mirrors the workflow from maxtongwang/openclaw.

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Adds use_sticky_comment: true to claude-review workflow so all review
updates are consolidated into a single comment instead of flooding the
PR with multiple comments.

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Adds BlueBubblesChannel — iMessage via self-hosted BlueBubbles macOS server.

- Webhook mode: receives new-message events at POST /bluebubbles
- Sender normalization: strips imessage:/sms:/auto: prefixes, lowercases emails
- fromMe caching: FIFO VecDeque+HashMap cache with reply context injection
- Optional inbound webhook auth: Authorization: Bearer via webhook_secret config
- Outbound send: POST /api/v1/message/text with ?password= query param + tempGuid
- Health check: GET /api/v1/ping (matches OpenClaw probeBlueBubbles)
- constant_time_eq() for secret comparison; manual Debug redacts password

Closes #1

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Add a second BlueBubbles endpoint for a personal Apple ID that stores
incoming iMessages to shared memory but never calls the LLM or sends
a reply. This enables multi-tenant iMessage setups where one ZeroClaw
process handles both a bot account (POST /bluebubbles) and a personal
account (POST /bluebubbles-personal) from two BlueBubbles instances.

Changes:
- src/config/schema.rs: add bluebubbles_personal: Option<BlueBubblesConfig>
  to ChannelsConfig (reuses existing BlueBubblesConfig struct)
- src/gateway/mod.rs: add bluebubbles_personal field to AppState,
  wire construction, register POST /bluebubbles-personal route, and
  implement handle_bluebubbles_personal_webhook handler

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- [build] rustc-wrapper = "sccache" in .cargo/config.toml — caches
  compiled crates across clean builds; sccache auto-starts on first use
- [profile.dev] split-debuginfo = "unpacked" in Cargo.toml — skips
  dsymutil on macOS, eliminating post-link debug-symbol packaging delay

Note: mold 2.40.4 dropped Mach-O support so it cannot be used as a
macOS linker; split-debuginfo is the primary link-time win on macOS.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Convert Discord-style markdown in LLM responses to BB Private API
attributedBody segments for iMessage rendering:
- **bold** → {bold: true}
- *italic* → {italic: true}
- ~~strikethrough~~ → {strikethrough: true}
- __underline__ → {underline: true}
Markers nest arbitrarily; plain-text fallback strips markers.
Update system prompt to instruct LLM to use rich markdown + emoji.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Add start_typing/stop_typing to BlueBubblesChannel using BB Private API
POST /api/v1/chat/{chatGuid}/typing. Indicator refreshes every 4s since
BB typing events expire in ~5s. Gateway handler starts indicator before
LLM call and stops it (both success and error paths) before sending reply.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- markdown_to_attributed_body: convert **bold**, *italic*, ~~strike~~,
  __underline__, `code` (→bold), # headers (→bold), ``` blocks (plain)
  into BB Private API attributedBody segments
- start_typing/stop_typing: background loop refreshing BB typing
  indicator every 4s while LLM processes; aborted on response
- EFFECT_MAP + extract_effect(): LLM can append [EFFECT:name] tags
  (slam, loud, gentle, invisible-ink, confetti, balloons, fireworks,
  lasers, love, celebration, echo, spotlight); stripped from content,
  passed as effectId to BB Private API
- Updated channel_delivery_instructions with full style + effect guide
- 38 unit tests passing

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Add ignore_senders to BlueBubblesConfig and BlueBubblesChannel
- Personal webhook handler skips storing messages where sender is in ignore_senders
- Configure ignore_senders = ["tongtong901005@gmail.com"] in config.toml
- Fix release-fast profile: lto=false, codegen-units=16, opt-level=2 for fast local builds with sccache support

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Direct LLM to use web_fetch on canonical sources (ESPN, wttr.in, etc.)
  for sports, weather, news — not just web_search snippets
- Remove "be concise" instruction that was cutting the tool loop short
- Instruct bot to complete full research before replying
- Keep all text style and effect guidance

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…l endpoint

Port all three CodeRabbit improvements from feat/channel-bluebubbles to
fork main and extend them consistently to the bluebubbles_personal endpoint:

- typing_handles: replace single Mutex<Option<JoinHandle>> with
  Mutex<HashMap<String, JoinHandle>> keyed by chat GUID so concurrent
  conversations do not cancel each other's typing indicators
- is_sender_ignored(): new method checked before is_sender_allowed() in
  parse_webhook_payload so ignore_senders always takes precedence over
  the allowlist; removes the now-redundant inline ignore check from the
  personal handler since parse_webhook_payload handles it upstream
- Secret wiring: add bluebubbles and bluebubbles_personal password +
  webhook_secret to decrypt_channel_secrets and encrypt_channel_secrets
- Debug impl: add ignore_senders field to BlueBubblesConfig debug output
- Tests: add three unit tests covering ignore_senders exact match,
  precedence over allowlist, and empty-list no-op behaviour (41 total)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Conflicts resolved:
- .gitignore: keep fork's test-config.toml entry alongside upstream result
- Cargo.lock: accept upstream (no new deps from BB channel)
- src/config/schema.rs: keep BlueBubblesConfig + bluebubbles/personal fields
  alongside upstream GitHubConfig; wire both into encrypt/decrypt
- src/gateway/mod.rs: keep BB + personal handlers alongside upstream GitHub
  handler; update run_gateway_chat_with_tools calls to 3-arg signature;
  add missing BB fields to new GitHub test fixtures

Also fixes:
- src/providers/cursor.rs: add missing quota_metadata field (pre-existing
  upstream bug now caught by stricter ChatResponse struct)
- BB handler: update sanitize_gateway_response to 3-arg form matching
  upstream API change (adds leak_guard parameter)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
BlueBubbles channel now downloads and transcribes audio attachments when
`[transcription]` is configured. iMessage sends voice memos in Core Audio
Format (CAF) which Groq Whisper does not accept natively; CAF files are
converted to WAV via ffmpeg before upload.

Changes:
- transcription.rs: add `convert_caf_to_wav` — detects CAF by extension,
  writes to temp files (seekable input required), runs `ffmpeg -ar 16000
  -ac 1`, cleans up on all exit paths; returns Ok(None) for non-CAF so
  callers reuse original bytes
- bluebubbles.rs: add `transcription` field, `with_transcription` builder,
  `AudioAttachment` struct, `extract_audio_attachments`, `download_attachment`
  (percent-encoding inline), `download_and_transcribe`, and public async
  `parse_webhook_payload_with_transcription`; gracefully falls back to
  `<media:audio>` placeholder when transcription is disabled, ffmpeg is
  absent, or the API call fails
- gateway/mod.rs: wire `config.transcription.clone()` into both BB channel
  constructions; replace synchronous `parse_webhook_payload` call with
  async `parse_webhook_payload_with_transcription` in both webhook handlers

No new Cargo dependencies — uses tokio::process and tokio::fs already
present. No new config keys — wired through the existing `[transcription]`
block.

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Mirrors OpenClaw approach: read attachment from `original_path` in BB
webhook payload (local filesystem, no REST download) and shell out to
the `whisper` CLI. Python whisper handles CAF (iMessage voice memos)
natively via ffmpeg — no pre-conversion step needed.

- Remove convert_caf_to_wav + ffmpeg + Groq HTTP download path
- Add transcribe_audio_local: resolves whisper in PATH and common
  Homebrew/system paths, runs with --model turbo --output_format txt,
  reads <tmpdir>/<stem>.txt, cleans up on all exit paths
- extract_audio_attachments: use originalPath instead of guid
- transcribe_local: read file directly, no REST API call
- Fix pre-existing build break: add OauthConfig/GoogleOauthConfig
  schema types + re-export so oauth.rs compiles

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
BB webhook attachments do not include originalPath; extract guid instead
and download bytes via /api/v1/attachment/{guid}/download, then write to
a temp file and pass to the local whisper CLI for transcription.

BB converts CAF→MP3 before webhooking so the download is typically
audio/mpeg — no pre-conversion needed.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Two fixes:
1. Instead of REST API download, read the BB-converted MP3 directly from
   the local filesystem at the predictable path:
   ~/Library/Application Support/bluebubbles-server/Convert/{guid}/{name}.mp3
   BB converts CAF→MP3 before sending the webhook; since ZeroClaw runs on
   the same Mac as BB, no network call is needed.

2. Spawn webhook processing in background (tokio::spawn) so the handler
   returns 200 immediately. BB times out webhooks at 30 s, but whisper
   transcription takes longer — caused 408 timeouts with the previous
   synchronous approach.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
BB's transferName in webhooks is inconsistent — sometimes includes .mp3
already, causing triple-extension paths. Instead of constructing the
filename from transferName, scan the Convert/{guid}/ directory for the
first audio file (mp3/m4a/aac/wav/ogg). This is robust to any BB
filename convention.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Replace incorrect \!escaped.contains('&') assertion — HTML entity encoding uses & characters (&amp;, &lt;, etc.), so this always failed. Assert contains("&amp;") instead.
Replaces broken filesystem-based audio transcription with BB REST API download.
Adds dual-backend transcription (local whisper CLI primary, Groq API fallback),
inbound tapback surfacing via updated-message webhooks, and outbound reactions
via add_reaction/remove_reaction on the Channel trait.

Key changes:
- download_attachment_bytes: 25 MB cap (Content-Length + body), 30s HTTP timeout
- resolve_whisper_bin: OnceLock cache — binary search runs once at startup
- parse_tapback_event: surfaces ❤️/👍/👎/😂/‼️/❓ as [Tapback] system messages
- add_reaction/remove_reaction: BB Private API /api/v1/message/react
- 58 tests, all green
fetch_with_http_provider used Policy::none() but returned Ok(redirected_url) instead of fetching the target. Replace response with the fetched redirect target and fall through to shared body processing.
Brings in upstream commits through 3726d82.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

# Conflicts:
#	.cargo/config.toml
#	.gitignore
#	Cargo.toml
#	src/channels/bluebubbles.rs
#	src/channels/mod.rs
#	src/config/mod.rs
#	src/config/schema.rs
#	src/gateway/mod.rs
…nc parse

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…ng indicator (#14)

- Replace slow Python whisper (~30–90 s) with whisper-cli (whisper.cpp,
  Metal-accelerated, ~1–2 s) for local audio transcription.
- Pre-convert all non-WAV audio to 16 kHz mono WAV via ffmpeg; the brew
  build of whisper-cli only reliably reads WAV.
- Use OnceLock for binary/model path resolution to avoid repeated lookups.
- Start typing indicator before transcription runs so users see feedback
  immediately rather than waiting for the full whisper phase.

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
…k reactions, and whisper-cli fast-path

Extends the BlueBubbles iMessage channel with the following capabilities
on top of the existing base implementation:

- REST API audio download: fetches voice memo attachments directly from
  the BlueBubbles server via its REST API, enabling reliable access
  without filesystem access.

- Tapback reactions: parses and surfaces reaction events (heart, thumbs
  up, thumbs down, ha ha, !!, ?) so the agent can understand user
  sentiment and react appropriately.

- Rich text / attributed body: parses Apple's attributedBody format to
  extract plain text from styled iMessage content, improving message
  fidelity for formatted messages and inline attachments.

- Typing indicator before transcription: sends a typing indicator
  immediately when a voice memo is detected, giving users real-time
  feedback before the potentially slower transcription step.

- Local whisper-cli fast-path (whisper.cpp): prefers the locally
  installed `whisper-cli` (from whisper.cpp, e.g. via `brew install
  whisper-cpp`) over the Groq API when available. On Apple Silicon with
  Metal acceleration this cuts transcription latency from 30-90 s to
  ~1-2 s. WAV files are passed directly; non-WAV audio is converted via
  ffmpeg before passing to whisper-cli. Falls back to the Groq API
  automatically when whisper-cli is not installed.

- Stderr diagnostics: surfaces whisper-cli stderr output in agent logs
  when transcription fails, making it easier to debug environment issues.

Files changed:
- src/channels/bluebubbles.rs: AudioAttachment struct, REST download,
  tapback parsing, rich text extraction, typing indicator wiring
- src/channels/transcription.rs: local whisper-cli fast-path with ffmpeg
  pre-conversion and graceful Groq API fallback
- src/gateway/mod.rs: wire .with_transcription() into channel
  construction; call parse_webhook_payload_with_transcription (async)
- Redact reqwest password URL from error messages with `.without_url()`
  in download_attachment_bytes, add_reaction, and remove_reaction
- Replace racy `as_nanos()` temp-file names with `uuid::Uuid::new_v4()`
  in bluebubbles.rs (audio download) and transcription.rs (ffmpeg/whisper)
- Gate typing indicator on fromMe/allowlist checks before `start_typing()`
  to avoid leaking presence for ignored/disallowed senders
- Add `tokio::time::timeout(120s)` + `kill_on_drop(true)` for ffmpeg,
  whisper-cli, and python whisper subprocesses to bound execution time
- Add Intel Homebrew model paths (`/usr/local/opt/whisper-cpp/...`) to
  MODELS array alongside Apple Silicon paths
- Move transcription+LLM loop into `tokio::spawn` background task and
  return 202 ACCEPTED immediately to stay within gateway timeout budget

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
maxtongwang and others added 10 commits March 3, 2026 17:35
- Move startup warning from hot-path mention_required_but_missing to
  with_mention_gating constructor so it fires once at config load, not
  on every inbound group message
- Skip wildcard "*" sentinel when falling back to allowed_senders for
  keyword resolution; "*" means allow-all and must not be a literal
  mention keyword
- Pre-lowercase mention_keyword in with_mention_gating to avoid
  repeated allocation on every inbound message comparison
- Add 11 unit tests covering all branches: DM passthrough, global flag,
  keyword match/mismatch, case-insensitive match, fallback to
  allowed_senders, wildcard skip, no-keyword always-block, and all
  three per-group override combinations
- Reject blank/whitespace-only mention_keyword (empty string would match
  every message via contains(""), silently disabling gating); trim and
  filter in with_mention_gating before pre-lowercasing
- Fix typing indicator side-effect: apply mention gate before
  start_typing in parse_webhook_payload_with_transcription; voice notes
  can never contain a keyword, so use "" as the text argument to let the
  group-policy/per-group override decide
- Document tapback bypass: tapbacks intentionally skip mention gating
  since reactions carry no user-typed text and can never contain the
  keyword; add explanatory comment in parse_tapback_event
- Add 2 unit tests for empty and whitespace-only keyword edge cases
Document require_mention_in_groups, mention_keyword, and per-group
override config in the BlueBubbles (§4.20) config block of channels-
reference.md. Addresses missing documentation flagged in PR review.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…note; add BB to ops keyword matrix

- Correct comment: 'explicitly @mentioned' → case-insensitive keyword match
- Add: 'DMs are never mention-gated.' to comment block and to policy prose
- Add silent-drop behavior sentence after group_allow_from policy prose
- Add BlueBubbles row to operations keyword matrix (between Nextcloud Talk and iMessage)
…ll locales

vi (full update):
- channels-reference: add §4.15 BlueBubbles with full config (dm_policy, group_policy,
  group_allow_from, send_read_receipts, text_chunk_limit, chunk_mode, mention gating);
  add BlueBubbles row to ops keyword matrix §7.2; add BlueBubbles to rg filter §7.1
- config-reference: add [channels_config.bluebubbles] section with full key table

el (full update):
- channels-reference: add §4.1 BlueBubbles config snippet with policy fields
- config-reference: add BlueBubbles toml example with dm_policy/group_policy/send_read_receipts

zh-CN/ja/ru/fr (hub parity):
- channels-reference: add BlueBubbles to channel list in scope section
- config-reference: add update note listing new bluebubbles config keys
The per-group mention override map (groups) was omitted from the localized
config-reference update notes in the French, Japanese, and Russian translations.
Add it to each locale's changelog summary alongside the other new keys.
@maxtongwang
Copy link
Copy Markdown
Author

@coderabbitai resolve

The docs/i18n/{fr,ja,ru}/config-reference.md files already include groups in line 24/22 — please re-inspect the current diff. The docs/i18n/vi/config-reference.md anchor #415-bluebubbles-imessage-qua-máy-chủ-bluebubbles correctly targets docs/i18n/vi/channels-reference.md §4.15 (not the English file).

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Mar 4, 2026

Tip

For best results, initiate chat on the files or code changes.

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
echo "=== fr/config-reference.md around line 24 ==="
grep -n "groups" docs/i18n/fr/config-reference.md | head -20

echo ""
echo "=== ja/config-reference.md around line 22 ==="
grep -n "groups" docs/i18n/ja/config-reference.md | head -20

echo ""
echo "=== ru/config-reference.md around line 22-24 ==="
grep -n "groups" docs/i18n/ru/config-reference.md | head -20

echo ""
echo "=== vi/config-reference.md - anchor search ==="
grep -n "415\|bluebubbles-imessage-qua" docs/i18n/vi/config-reference.md | head -20

echo ""
echo "=== vi/channels-reference.md - section 4.15 header ==="
grep -n "4\.15\|BlueBubbles" docs/i18n/vi/channels-reference.md | head -20

Length of output: 1927


@maxtongwang: Both claims verified from the current diff:

  1. groups field in fr/ja/ru config-reference.md — Confirmed. All three files include groups at line 24 (fr), line 22 (ja), and line 22 (ru) respectively. My earlier concern was incorrect.

  2. Vietnamese anchor — Confirmed. docs/i18n/vi/config-reference.md line 502 references channels-reference.md#415-bluebubbles-imessage-qua-máy-chủ-bluebubbles, which correctly resolves to docs/i18n/vi/channels-reference.md §4.15 (### 4.15 BlueBubbles (iMessage qua máy chủ BlueBubbles) at line 361). The anchor slug is valid and targets the localized Vietnamese file, not the English one. My earlier concern was incorrect.

Resolving all related review comments now.

[resolve]


🧠 Learnings used
Learnt from: CR
Repo: zeroclaw-labs/zeroclaw PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-03-01T16:30:20.983Z
Learning: For docs/template-only changes, run markdown lint and link-integrity checks. If touching README/docs-hub/SUMMARY/collection indexes, verify EN/ZH-CN/JA/RU/FR/VI/EL navigation parity.

Learnt from: CR
Repo: zeroclaw-labs/zeroclaw PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-02-28T07:19:22.026Z
Learning: Applies to docs/**/*.md : If a change touches docs IA, runtime-contract references, or user-facing wording in shared docs, perform i18n follow-through for currently supported locales (en, zh-CN, ja, ru, fr, vi, el) in the same PR

Learnt from: CR
Repo: zeroclaw-labs/zeroclaw PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-03-01T18:05:23.044Z
Learning: Applies to docs/config-reference.md : Treat as runtime-contract reference. Track behavior changes to configuration schema. Update corresponding localized docs for supported locales when wording changes.

Learnt from: CR
Repo: zeroclaw-labs/zeroclaw PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-03-01T16:49:24.178Z
Learning: Applies to docs/**/*.md : Keep README/hub top navigation and quick routes intuitive and non-duplicative. Keep entry-point parity across all supported locales (en, zh-CN, ja, ru, fr, vi, el) when changing navigation architecture. If a change touches docs IA, runtime-contract references, or user-facing wording in shared docs, perform i18n follow-through for currently supported locales in the same PR.

Learnt from: CR
Repo: zeroclaw-labs/zeroclaw PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-02-28T07:19:22.026Z
Learning: Applies to src/{security,runtime,gateway,tools}/**/*.rs : Keep security-critical changes in `src/security/**`, `src/runtime/**`, `src/gateway/**`, `src/tools/**` to high review depth; include threat/risk notes and rollback strategy

Learnt from: CR
Repo: zeroclaw-labs/zeroclaw PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-03-01T16:49:24.178Z
Learning: Applies to docs/i18n-guide.md : Follow as the mandatory completion checklist when docs navigation or shared wording changes. Complete i18n follow-through in the same PR for all supported locales (en, zh-CN, ja, ru, fr, vi, el).

Learnt from: CR
Repo: zeroclaw-labs/zeroclaw PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-03-01T16:24:24.159Z
Learning: Applies to README.md docs/README.md docs/SUMMARY.md docs/i18n/**/*.md : Keep multilingual entry-point parity for all supported locales (en, zh-CN, ja, ru, fr, vi, el) when nav or key wording changes

Learnt from: CR
Repo: zeroclaw-labs/zeroclaw PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-02-27T05:11:50.429Z
Learning: Applies to README.md, docs/README.md, docs/i18n/*/README.md, docs/SUMMARY.md : Keep entry-point parity across all supported locales (`en`, `zh-CN`, `ja`, `ru`, `fr`, `vi`, `el`) when changing navigation architecture; if a change touches docs IA, runtime-contract references, or user-facing wording in shared docs, perform i18n follow-through for currently supported locales in the same PR

Learnt from: CR
Repo: zeroclaw-labs/zeroclaw PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-03-01T16:22:37.739Z
Learning: Applies to {README,docs/README,docs/SUMMARY,docs/i18n/*/README}.md : If docs entry points changed, keep all supported locale README/docs-hub navigation aligned (`en`, `zh-CN`, `ja`, `ru`, `fr`, `vi`, `el`)

Learnt from: CR
Repo: zeroclaw-labs/zeroclaw PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-03-01T16:30:20.983Z
Learning: Applies to README.md : Keep entry-point parity across all supported locales (en, zh-CN, ja, ru, fr, vi, el) when changing navigation architecture. Follow docs/i18n-guide.md as the mandatory completion checklist when docs navigation or shared wording changes.

Learnt from: CR
Repo: zeroclaw-labs/zeroclaw PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-02-28T07:19:22.026Z
Learning: Applies to docs/**/*.{en,zh-CN,ja,ru,fr,vi,el}.md : Keep `docs/*.<locale>.md` compatibility shims aligned when present; follow `docs/i18n-guide.md` as mandatory completion checklist when docs navigation or shared wording changes

Learnt from: CR
Repo: zeroclaw-labs/zeroclaw PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-03-01T16:22:37.739Z
Learning: Applies to docs/i18n/{zh-CN,ja,ru,fr,vi,el}/**/*.md : Treat `docs/i18n/<locale>/**` as canonical for localized hubs/summaries; keep docs-root compatibility shims aligned when edited

Learnt from: CR
Repo: zeroclaw-labs/zeroclaw PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-03-01T16:24:24.159Z
Learning: Applies to README.md docs/README.md docs/SUMMARY.md docs/i18n/**/*.md docs/getting-started/README.md docs/reference/README.md docs/operations/README.md docs/security/README.md docs/hardware/README.md docs/contributing/README.md docs/project/README.md : If touching README/docs-hub/SUMMARY/collection indexes, verify EN/ZH-CN/JA/RU/FR/VI/EL navigation parity

Learnt from: CR
Repo: zeroclaw-labs/zeroclaw PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-02-27T11:54:55.857Z
Learning: Applies to docs/i18n-guide.md : Follow `docs/i18n-guide.md` as the mandatory completion checklist when docs navigation or shared wording changes; keep i18n follow-through requirements explicit and actionable

Learnt from: CR
Repo: zeroclaw-labs/zeroclaw PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-03-01T16:30:20.983Z
Learning: Applies to docs/SUMMARY.md : Keep entry-point parity across all supported locales (en, zh-CN, ja, ru, fr, vi, el) when changing table of contents. Update locale navigation links and canonical locale hubs under docs/i18n/<locale>/ for every supported locale.

@maxtongwang
Copy link
Copy Markdown
Author

@coderabbitai full review

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Mar 4, 2026

✅ Actions performed

Full review triggered.

@maxtongwang
Copy link
Copy Markdown
Author

@coderabbitai review

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Mar 4, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

- Add missing `groups` key to zh-CN config-reference update notice
  (fr/ja/ru already had it from prior commit)
- Preserve whisper-cli error in transcribe_audio_local when Python
  fallback also fails — combined error message surfaces both causes
  (same fix as applied in PR #2495 transcription path)
@maxtongwang
Copy link
Copy Markdown
Author

@coderabbitai review

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Mar 4, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

maxtongwang and others added 5 commits March 3, 2026 21:57
…4.20

Link was pointing to §4.15 which doesn't exist; BlueBubbles is §4.20
in channels-reference.md. Update the VI config-reference cross-link.
…ument mention_keyword warning

- oauth.rs: include state token in PKCE filename to prevent concurrent
  /auth/google starts from clobbering each other's session file; validate
  state format before constructing path (prevent path traversal)
- mod.rs: fix stale comment "Returning 200" → "Returning 202" to match
  actual HTTP 202 Accepted response
- config-reference.md: document that startup emits a warning when
  require_mention_in_groups is set but no mention keyword can be resolved

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…g disabled

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- gateway/oauth: remove unused redirect field, fix TOCTOU race in revoke path,
  log Google token revocation failures
- gateway/mod: hash sender PII before logging, fire-and-forget mark_read calls
- i18n/vi: add BlueBubbles exception note and update allowed_senders list
- i18n/el: update allowlist policy section with BlueBubbles dm_policy note
…okio::fs

Replace std::fs::remove_dir_all with tokio::fs::remove_dir_all in the
transcribe_with_whisper_cpp error path on read failure. The previous
implementation was calling a blocking filesystem operation inside an async
context and bypassing temp directory cleanup atomicity.
@maxtongwang
Copy link
Copy Markdown
Author

@coderabbitai review

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Mar 5, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@maxtongwang
Copy link
Copy Markdown
Author

@coderabbitai review

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Mar 5, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@maxtongwang
Copy link
Copy Markdown
Author

Closing — no longer needed.

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

Labels

channel Auto scope: src/channels/** changed. config: core Auto module: config core files changed. dependencies Auto scope: dependency manifest/lock/policy changed. docs Auto scope: docs/markdown/template files changed. gateway: oauth Auto module: gateway/oauth changed. risk: high Auto risk: security/runtime/gateway/tools/workflows. size: XL Auto size: >1000 non-doc changed lines. tool Auto scope: src/tools/** changed.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant