Skip to content

fix(webview/meet): gate orchestrator handoff on user opt-in (#1299)#1310

Merged
senamakel merged 8 commits into
tinyhumansai:mainfrom
oxoxDev:fix/1299-meet-handoff-consent-gate
May 7, 2026
Merged

fix(webview/meet): gate orchestrator handoff on user opt-in (#1299)#1310
senamakel merged 8 commits into
tinyhumansai:mainfrom
oxoxDev:fix/1299-meet-handoff-consent-gate

Conversation

@oxoxDev
Copy link
Copy Markdown
Contributor

@oxoxDev oxoxDev commented May 6, 2026

Summary

  • Add meet.auto_orchestrator_handoff config (default false) plus update_meet_settings / get_meet_settings JSON-RPC controllers.
  • Surface meetAutoOrchestratorHandoff in AppStateSnapshot and pipe it through CoreStateProvider with a setMeetAutoOrchestratorHandoff setter.
  • Gate flushMeetingSessionhandoffToOrchestrator on the new flag (read fresh per meet_call_ended, fail-closed on RPC error).
  • New "Meeting follow-ups" toggle in Privacy & Security settings, default OFF.
  • 9 vitest cases (4 gate + 1 UI toggle + 4 default-fixture extensions) and 6 cargo tests covering the new schema, ops, registry and snapshot field.

Problem

flushMeetingSession unconditionally called handoffToOrchestrator whenever a Google Meet call ended with at least one caption segment. The handoff opens a fresh chat thread and feeds the orchestrator a prompt explicitly inviting tool execution ("proactively handle it now"). Because the orchestrator has the full Slack tool surface, it routinely posted meeting summaries to #general without user consent — workspace-wide leak risk for sensitive meeting content. The hole has shipped since PR #629 landed webviewAccountService.ts.

Solution

Layer 1 of the issue's proposed remediation (the smallest immediately-correct fix). All other layers (draft-by-default prompt, channel allow-list, tool-layer approval gate) are explicitly out of scope for this PR.

  • Rust — new MeetConfig { auto_orchestrator_handoff: bool } mirrors ObservabilityConfig shape; same patch struct + apply_meet_settings + load-and-apply ops; registered through the existing config controller catalog as openhuman.config_update_meet_settings / openhuman.config_get_meet_settings. Surfaced on AppStateSnapshot as meetAutoOrchestratorHandoff so the React shell can read the value alongside analyticsEnabled etc.
  • App / Tauri host — TS bindings under utils/tauriCommands/config.ts; CoreAppSnapshot extended with the required field (emptySnapshot defaults false); CoreStateProvider.setMeetAutoOrchestratorHandoff mirrors the optimistic-commit + refresh pattern of setAnalyticsEnabled.
  • The fixwebviewAccountService.ts wraps the existing handoffToOrchestrator call in a new maybeHandoffToOrchestrator that reads openhuman.config_get_meet_settings per meet_call_ended (rare event; ~30ms; always-current), only invokes the handoff when the flag is explicitly true, and fails closed on RPC error. Memory ingest at line 594 is unchanged: transcripts still land in memory, only the auto-action path is gated.
  • UI — new "Meeting follow-ups" section in PrivacyPanel reusing the analytics toggle markup. Copy explains the tradeoff verbatim ("...may take actions like drafting messages, scheduling follow-ups, or posting summaries to your connected Slack workspace. Off by default.").

Smoked end-to-end on a live Gmeet call with caption capture: with toggle OFF the orchestrator thread is not created; with toggle ON the orchestrator session is built (thread-12c9ad69-…, 7 delegation tools, 21 tool specs).

Submission Checklist

  • Tests added or updated (happy path + at least one failure / edge case) per docs/TESTING-STRATEGY.md
  • Diff coverage ≥ 80% — changed lines (Vitest + cargo-llvm-cov merged via diff-cover) meet the gate enforced by .github/workflows/coverage.yml. Run pnpm test:coverage and pnpm test:rust locally; PRs below 80% on changed lines will not merge.
  • N/A: Coverage matrix updated — Privacy panel toggle is a behaviour-only addition; no new feature row.
  • N/A: All affected feature IDs from the matrix are listed in the PR description under ## Related — same reason as above.
  • No new external network dependencies introduced (mock backend used per docs/TESTING-STRATEGY.md)
  • N/A: Manual smoke checklist updated if this touches release-cut surfaces — release smoke checklist already covers Settings → Privacy interactions; no new release-cut surface.
  • Linked issue closed via Closes #NNN in the ## Related section

Impact

  • Behavioral change for existing users: any user who has been silently relying on the auto-handoff today will see meetings stop posting to Slack until they flip the new toggle on. Intentional — the previous behaviour is the bug.
  • Privacy surface tightened: #general and other Slack channels are no longer reachable from the meet-end path without explicit opt-in.
  • Memory ingest path is untouched — transcripts continue to land in memory by default.
  • Fail-closed on config_get_meet_settings RPC errors (privacy-conservative).
  • Tauri shell + Rust core only; desktop platforms macOS/Windows/Linux all share this code path.

Related


AI Authored PR Metadata (required for Codex/Linear PRs)

Linear Issue

  • Key: N/A
  • URL: N/A

Commit & Branch

  • Branch: fix/1299-meet-handoff-consent-gate
  • Commit SHA: $(git rev-parse HEAD)

Tooling

  • Agent: Claude Code (Opus 4.7, 1M context)
  • Plan synthesised via mcp__sequential-thinking__sequentialthinking per workspace Phase 2 protocol; user-approved before implementation.

Summary by CodeRabbit

  • New Features

    • Added a "Meeting follow-ups" section in Settings with an opt-in toggle to control automatic Google Meet transcript handoffs (disabled by default).
    • Handoff now only runs when this setting is enabled, ensuring user choice is respected.
  • Tests

    • Added/updated tests to cover the new setting and handoff behaviors.

oxoxDev and others added 6 commits May 7, 2026 01:48
…tinyhumansai#1299)

New top-level config section `meet` with a single field
`auto_orchestrator_handoff: bool` defaulting to `false`. Mirrors the
shape of `observability.analytics_enabled` — same patch struct, same
load-and-apply ops, same controller registration pattern.

Adds the `update_meet_settings` and `get_meet_settings` JSON-RPC
controllers under the `config` namespace so the React layer can read
and toggle the flag without going through the full app-state snapshot.

Default-OFF semantics are deliberate: until the user explicitly opts
in, ending a Google Meet call must NOT hand the transcript to the
orchestrator agent. See issue tinyhumansai#1299 for the privacy hole this gates.

Tests:
- 5 MeetConfig unit tests (default OFF, helper fn, deserialize default,
  explicit flag, round-trip).
- `apply_meet_settings_updates_handoff_flag` covers ON/OFF/no-op patch.
- Schema-registry contract test extended with the two new method names.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…humansai#1299)

Surface the new `config.meet.auto_orchestrator_handoff` flag through
the `openhuman.app_state_snapshot` RPC so the React shell can read it
alongside other gating flags (`onboardingCompleted`,
`chatOnboardingCompleted`, `analyticsEnabled`).

Extends the existing `json_rpc_app_state_snapshot_returns_runtime_shape`
e2e test with a `meetAutoOrchestratorHandoff: false` default
assertion. Fresh users must see OFF on first boot.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…er (tinyhumansai#1299)

Mirrors the existing analytics-toggle plumbing:

- New `openhumanGetMeetSettings` / `openhumanUpdateMeetSettings` Tauri
  RPC bindings in `utils/tauriCommands/config.ts` (`openhuman.<get|update>_meet_settings`).
- `CoreAppSnapshot.meetAutoOrchestratorHandoff: boolean` (default
  `false` in `emptySnapshot`) and the corresponding optional field in
  `AppStateSnapshotResult` for backward-compat with older core builds.
- `CoreStateProvider` exposes `setMeetAutoOrchestratorHandoff` with
  the same optimistic-commit + refresh pattern as `setAnalyticsEnabled`.
- Existing fixtures (`store.test.ts`, both `CoreStateProvider.*.tsx`,
  `socketSelectors.test.ts`) updated for the new required field.

The actual privacy gate that consumes this state lands in the next
commit. This commit is purely the plumbing layer.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…nsai#1299)

THE FIX. Previously `flushMeetingSession` unconditionally called
`handoffToOrchestrator` whenever a Google Meet call ended with at
least one caption segment. That handoff feeds the transcript to the
orchestrator agent with a prompt explicitly inviting tool execution
("proactively handle it now"); since the orchestrator has the full
Slack tool surface, it routinely posted meeting summaries to
`#general" without user consent.

Now wraps the call in `maybeHandoffToOrchestrator` which:

1. Reads `meet.auto_orchestrator_handoff` fresh from the core RPC on
   every `meet_call_ended` (rare event; ~30ms; always-current).
2. Only invokes `handoffToOrchestrator` when the flag is explicitly
   `true`.
3. Fails closed (no handoff) if the settings RPC throws — privacy
   conservatism beats fail-open.

Memory ingest at line 594 is unchanged: transcripts still land in
memory by default. Only the auto-action path is gated.

Tests:
- 4 vitest cases in `webviewAccountService.meetHandoffGate.test.ts`:
  toggle OFF skips, missing field skips, RPC throws skips, toggle ON
  fires both `threadApi.createNewThread` and `chatSend".
- Module exposes a `__testInternals" namespace so the gate can be
  exercised without driving the full event-listener pipeline.

Closes tinyhumansai#1299.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…nyhumansai#1299)

User-facing opt-in surface for the new `meet.auto_orchestrator_handoff`
flag. Renders below the existing Anonymized Analytics section and uses
the same toggle markup so the visual rhythm of the panel is consistent.

Copy explains exactly what the handoff will do ("drafting messages,
scheduling follow-ups, posting summaries to your connected Slack
workspace") so the user understands the privacy tradeoff before
flipping it on. Off by default.

Test: extends `PrivacyPanel.test.tsx` with a click-through case
verifying `setMeetAutoOrchestratorHandoff(true)` is called on toggle
click. Existing graceful-fallback test loosened from `getByRole` to
`getAllByRole(...).length >= 2` since two toggles are now rendered.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…nyhumansai#1299)

Frontend was calling `openhuman.update_meet_settings" /
`openhuman.get_meet_settings` but the controller registry exposes
the canonical `openhuman.config_update_meet_settings" /
`openhuman.config_get_meet_settings` (namespace + function pattern
shared by every other config RPC like
`openhuman.config_update_runtime_settings`). Without the prefix
`schema_for_rpc_method" returns None and dispatch falls through to
`unknown_method method=openhuman.update_meet_settings".

Manifests as toggle stuck mid-animation — optimistic UI flip fires,
RPC fails, post-RPC `refresh()` rolls the snapshot back so
aria-checked never lands. The Rust side was always correct; only the
TS bindings were wrong.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@oxoxDev oxoxDev requested a review from a team May 6, 2026 20:20
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 6, 2026

📝 Walkthrough

Walkthrough

This PR adds an opt-in privacy gate for Google Meet orchestrator handoffs: a new config flag (meet.auto_orchestrator_handoff, default false) flows through backend config, RPC schemas, Tauri command wrappers, core state, a Settings toggle, and a gated handoff path that only calls the orchestrator when enabled. Tests and docs added/updated across backend and frontend.

Changes

Meet Orchestrator Handoff Privacy Gate

Layer / File(s) Summary
Data Shape
src/openhuman/config/schema/meet.rs, src/openhuman/config/schema/types.rs, src/openhuman/app_state/ops.rs, app/src/lib/coreState/store.ts, app/src/services/coreStateApi.ts
Adds MeetConfig with auto_orchestrator_handoff: bool (default false). AppStateSnapshot / CoreAppSnapshot / API snapshot include meetAutoOrchestratorHandoff (normalized to false when missing).
Config Ops & Schema
src/openhuman/config/ops.rs, src/openhuman/config/schemas.rs, src/openhuman/config/schemas_tests.rs, src/openhuman/config/ops_tests.rs
Introduce MeetSettingsPatch, apply_meet_settings and load_and_apply_meet_settings; register RPC handlers update_meet_settings and get_meet_settings; add tests for meet settings patch behavior.
Schema Re-exports & Types
src/openhuman/config/schema/mod.rs, src/openhuman/config/mod.rs, src/openhuman/config/README.md
Expose MeetConfig in schema re-exports and update docs/public surface lines.
Tauri Command Wrappers
app/src/utils/tauriCommands/config.ts, app/src/utils/tauriCommands/config.test.ts, app/src/test/setup.ts
Add openhumanUpdateMeetSettings and openhumanGetMeetSettings wrappers and tests; add test mocks for these commands.
Core State Provider
app/src/providers/CoreStateProvider.tsx, app/src/providers/__tests__/CoreStateProvider.*.test.tsx
Expose setMeetAutoOrchestratorHandoff(enabled: boolean): Promise<void> on context; wire normalization, optimistic update, RPC call (openhumanUpdateMeetSettings) and refresh; update tests for setter behavior and snapshot defaults.
Privacy Gate Implementation
app/src/services/webviewAccountService.ts, app/src/services/__tests__/webviewAccountService.meetHandoffGate.test.ts
Replace unconditional handoffToOrchestrator call with maybeHandoffToOrchestrator which calls openhumanGetMeetSettings and only invokes handoffToOrchestrator when enabled; add tests covering skip, fail-closed, and gated trigger cases; export __testInternals for tests.
UI & Tests
app/src/components/settings/panels/PrivacyPanel.tsx, app/src/components/settings/panels/__tests__/PrivacyPanel.test.tsx
Add "Meeting follow-ups" toggle reading meetAutoOrchestratorHandoff and calling setMeetAutoOrchestratorHandoff via handleToggleMeetAutoHandoff; update tests to cover toggle rendering and setter invocation.
Snapshots / Tests Wiring
app/src/lib/coreState/__tests__/store.test.ts, app/src/providers/__tests__/*, app/src/store/__tests__/socketSelectors.test.ts, tests/json_rpc_e2e.rs
Update test snapshots and assertions to include meetAutoOrchestratorHandoff: false default; add e2e assertion for default false.

Sequence Diagram

sequenceDiagram
    actor User
    participant PrivacyPanel
    participant CoreStateProvider
    participant TauriAPI
    participant Backend as Backend Config RPC

    User->>PrivacyPanel: Toggle "Meeting follow-ups"
    PrivacyPanel->>CoreStateProvider: setMeetAutoOrchestratorHandoff(enabled)
    CoreStateProvider->>CoreStateProvider: Optimistically update snapshot
    CoreStateProvider->>TauriAPI: openhumanUpdateMeetSettings({auto_orchestrator_handoff})
    TauriAPI->>Backend: config_update_meet_settings
    Backend->>Backend: Apply patch & persist
    Backend-->>TauriAPI: Return ConfigSnapshot
    TauriAPI-->>CoreStateProvider: Return success
    CoreStateProvider->>CoreStateProvider: Refresh full snapshot
    CoreStateProvider-->>PrivacyPanel: Notify subscribers
    PrivacyPanel-->>User: Reflect new state
Loading
sequenceDiagram
    participant MeetCallFlow
    participant WebviewService as webviewAccountService
    participant TauriAPI as Tauri API
    participant Backend as Backend Config
    participant Orchestrator

    MeetCallFlow->>WebviewService: flushMeetingSession() on call_ended
    WebviewService->>WebviewService: Persist transcript to memory
    WebviewService->>WebviewService: maybeHandoffToOrchestrator(...)
    maybeHandoffToOrchestrator->>TauriAPI: openhumanGetMeetSettings()
    TauriAPI->>Backend: config_get_meet_settings
    Backend-->>TauriAPI: {auto_orchestrator_handoff: bool}
    TauriAPI-->>WebviewService: Settings retrieved
    alt Feature Enabled (true)
        WebviewService->>Orchestrator: handoffToOrchestrator() (create thread & send transcript)
    else Feature Disabled (false)
        WebviewService->>WebviewService: Log skip, return
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Suggested reviewers

  • senamakel
  • graycyrus

Poem

🐰
I nibbled lines of code tonight,
tucked privacy under soft moonlight,
a toggle safe, a setting small—
now meeting notes won't leap and sprawl.
Hop, opt-in only — that's the call!

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 40.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately describes the main change: adding a privacy gate (opt-in toggle) for the orchestrator handoff feature after Google Meet calls.
Linked Issues check ✅ Passed The PR fully implements the Layer 1 solution from issue #1299: default-off opt-in gate, user-facing Settings toggle, setting-gated handoff logic, and comprehensive test coverage.
Out of Scope Changes check ✅ Passed All changes are in scope for the Layer 1 opt-in gate. Layers 2–4 (draft-by-default, channel allow-list, approval gates) are explicitly deferred and not present.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.


Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🧹 Nitpick comments (1)
app/src/providers/__tests__/CoreStateProvider.identityFlip.test.tsx (1)

77-77: ⚡ Quick win

Align the mocked refresh payload with the current snapshot shape too.

resetCoreStateStore() now includes meetAutoOrchestratorHandoff, but the makeSnapshot() helper used by fetchCoreAppSnapshot() still omits it. That means every refresh path in this suite is exercising only the legacy-core payload and would miss a regression that drops the new flag during provider refresh.

Suggested tweak
 function makeSnapshot(overrides: {
   userId?: string | null;
   sessionToken?: string | null;
   isAuthenticated?: boolean;
 }): Snapshot {
   return {
@@
     onboardingCompleted: false,
     chatOnboardingCompleted: false,
     analyticsEnabled: false,
+    meetAutoOrchestratorHandoff: false,
     localState: {},
     runtime: {
       screenIntelligence: null as never,
       localAi: null as never,
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@app/src/providers/__tests__/CoreStateProvider.identityFlip.test.tsx` at line
77, The mocked snapshot returned by makeSnapshot() used by
fetchCoreAppSnapshot() is missing the new meetAutoOrchestratorHandoff flag, so
update makeSnapshot() to include meetAutoOrchestratorHandoff with the same
default value used in resetCoreStateStore(); ensure any helper creating the
refresh payload includes that flag so refresh paths in
CoreStateProvider.identityFlip.test.tsx exercise the current snapshot shape and
will catch regressions that drop the flag.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@app/src/components/settings/panels/PrivacyPanel.tsx`:
- Around line 198-206: The switch button rendered with role="switch"
(data-testid="privacy-meet-handoff-toggle") is missing an accessible name;
update the element to provide one (e.g., add aria-label or aria-labelledby
referencing a visible label) so assistive tech can announce it, ensuring the
label reflects the meetAutoHandoff state and that handleToggleMeetAutoHandoff
continues to be used for onClick; prefer aria-checked already present and make
sure the chosen label text matches the surrounding UI copy for Meeting
follow-ups.

In `@app/src/services/coreStateApi.ts`:
- Around line 36-41: fetchCoreAppSnapshot() currently returns the raw RPC
payload so meetAutoOrchestratorHandoff can be undefined for older cores;
normalize it at the API boundary by mapping the returned snapshot (in
fetchCoreAppSnapshot or the place that builds the returned AppSnapshot) to
ensure meetAutoOrchestratorHandoff is always a boolean (e.g., set
meetAutoOrchestratorHandoff: snapshot.meetAutoOrchestratorHandoff ?? false)
before returning, or alternatively update the comment if you choose not to
normalize.

In `@src/openhuman/config/schemas.rs`:
- Around line 837-858: Both new RPC handlers lack diagnostic logs; add
stable-prefix debug/trace logs for entry, successful exit, and error paths in
handle_update_meet_settings and handle_get_meet_settings. Specifically,
instrument the start of handle_update_meet_settings and handle_get_meet_settings
with a debug/trace log like "rpc:meet:handle_update_meet_settings:entry" /
"rpc:meet:handle_get_meet_settings:entry", log the outcome on success with a
message including relevant state (e.g., the patch or returned
auto_orchestrator_handoff) using debug, and log errors on the catch/Err path
with an error-level message that includes the same stable prefix and the error
details; ensure logs use the project's log/tracing crate and keep messages
concise and stable for observability.

---

Nitpick comments:
In `@app/src/providers/__tests__/CoreStateProvider.identityFlip.test.tsx`:
- Line 77: The mocked snapshot returned by makeSnapshot() used by
fetchCoreAppSnapshot() is missing the new meetAutoOrchestratorHandoff flag, so
update makeSnapshot() to include meetAutoOrchestratorHandoff with the same
default value used in resetCoreStateStore(); ensure any helper creating the
refresh payload includes that flag so refresh paths in
CoreStateProvider.identityFlip.test.tsx exercise the current snapshot shape and
will catch regressions that drop the flag.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: bb7a829f-57c2-41e1-b85f-dfcfd21d92f5

📥 Commits

Reviewing files that changed from the base of the PR and between f449a30 and b9e33a1.

📒 Files selected for processing (23)
  • app/src/components/settings/panels/PrivacyPanel.tsx
  • app/src/components/settings/panels/__tests__/PrivacyPanel.test.tsx
  • app/src/lib/coreState/__tests__/store.test.ts
  • app/src/lib/coreState/store.ts
  • app/src/providers/CoreStateProvider.tsx
  • app/src/providers/__tests__/CoreStateProvider.identityFlip.test.tsx
  • app/src/providers/__tests__/CoreStateProvider.test.tsx
  • app/src/services/__tests__/webviewAccountService.meetHandoffGate.test.ts
  • app/src/services/coreStateApi.ts
  • app/src/services/webviewAccountService.ts
  • app/src/store/__tests__/socketSelectors.test.ts
  • app/src/utils/tauriCommands/config.ts
  • src/openhuman/app_state/ops.rs
  • src/openhuman/config/README.md
  • src/openhuman/config/mod.rs
  • src/openhuman/config/ops.rs
  • src/openhuman/config/ops_tests.rs
  • src/openhuman/config/schema/meet.rs
  • src/openhuman/config/schema/mod.rs
  • src/openhuman/config/schema/types.rs
  • src/openhuman/config/schemas.rs
  • src/openhuman/config/schemas_tests.rs
  • tests/json_rpc_e2e.rs

Comment thread app/src/components/settings/panels/PrivacyPanel.tsx
Comment thread app/src/services/coreStateApi.ts
Comment thread src/openhuman/config/schemas.rs
oxoxDev and others added 2 commits May 7, 2026 02:12
Three review comments resolved:

- `PrivacyPanel.tsx`: add `aria-label` to the new Meeting follow-ups
  toggle so assistive tech can announce the role=switch control
  (CR major / a11y).
- `coreStateApi.ts`: normalise the optional `meetAutoOrchestratorHandoff`
  field to `false` at the API boundary so older core builds without
  the field never surface `undefined` to callers (CR minor — comment
  was claiming a fallback that didn't actually happen).
- `config/schemas.rs`: add structured `log::debug!" entry / exit /
  error logs to `handle_update_meet_settings` and
  `handle_get_meet_settings` for production troubleshooting (CR
  major refactor).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…r diff coverage gate

Coverage Gate failed at 52% on diff (≥80% required). Untested lines
were the new meet RPC bindings and the optimistic-commit setter:

- `utils/tauriCommands/config.test.ts`: 4 new cases — both bindings
  exercise the Tauri-not-available throw path and the happy POST
  using canonical method names `openhuman.config_update_meet_settings"
  / `openhuman.config_get_meet_settings".
- `providers/__tests__/CoreStateProvider.test.tsx": 2 new cases for
  `setMeetAutoOrchestratorHandoff` covering both the success path and
  the post-RPC `refresh()" failure path (which is intentionally
  swallowed so toggle UX remains responsive on transient core errors).
- `test/setup.ts`: extend the global `tauriCommands` mock with
  `openhumanUpdateMeetSettings` / `openhumanGetMeetSettings` defaults
  so any future test importing CoreStateProvider gets a working stub.

Targeted vitest sweep: 1565 passed / 0 failed.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (2)
app/src/providers/__tests__/CoreStateProvider.test.tsx (1)

247-277: ⚡ Quick win

Assert the state outcome here, not just the RPC call.

At Line 247 the test name says the snapshot flips optimistically, but the assertions only confirm openhumanUpdateMeetSettings was called. Add a state assertion (ctx?.snapshot.meetAutoOrchestratorHandoff) with an aligned refresh mock so this catches regressions in the commit path.

As per coding guidelines: “Prefer testing behavior over implementation details” in app/src/**/*.test.{ts,tsx}.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@app/src/providers/__tests__/CoreStateProvider.test.tsx` around lines 247 -
277, The test should assert the optimistic state flip: after calling
ctx!.setMeetAutoOrchestratorHandoff(true) assert
ctx?.snapshot.meetAutoOrchestratorHandoff is true, and also update the
snapshot/refresh mock so subsequent reads reflect the new value (e.g. set
fetchSnapshot/mock for refresh to return makeSnapshot(...,
meetAutoOrchestratorHandoff: true)) so the optimistic flip is validated in
addition to verifying tauriCommands.openhumanUpdateMeetSettings was called;
update the test around setMeetAutoOrchestratorHandoff, ctx, fetchSnapshot,
makeSnapshot and openhumanUpdateMeetSettings accordingly.
app/src/services/coreStateApi.ts (1)

24-43: ⚡ Quick win

Make the normalized field non-optional in the returned snapshot contract.

At Line 42 the field is still optional, but Lines 63-66 guarantee a boolean at runtime. Consider splitting wire type vs normalized return type so callers get a strict boolean contract and don’t keep adding redundant ?? false.

Proposed refactor
-interface AppStateSnapshotResult {
+interface AppStateSnapshotWireResult {
   auth: {
@@
-  meetAutoOrchestratorHandoff?: boolean;
+  meetAutoOrchestratorHandoff?: boolean;
@@
 }
 
-export const fetchCoreAppSnapshot = async (): Promise<AppStateSnapshotResult> => {
-  const response = await callCoreRpc<{ result: AppStateSnapshotResult }>({
+export interface AppStateSnapshotResult extends Omit<AppStateSnapshotWireResult, 'meetAutoOrchestratorHandoff'> {
+  meetAutoOrchestratorHandoff: boolean;
+}
+
+export const fetchCoreAppSnapshot = async (): Promise<AppStateSnapshotResult> => {
+  const response = await callCoreRpc<{ result: AppStateSnapshotWireResult }>({
     method: 'openhuman.app_state_snapshot',
   });
@@
   return {
     ...response.result,
     meetAutoOrchestratorHandoff: response.result.meetAutoOrchestratorHandoff ?? false,
   };
 };

Also applies to: 56-66

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@app/src/services/coreStateApi.ts` around lines 24 - 43, The
AppStateSnapshotResult type currently marks meetAutoOrchestratorHandoff as
optional even though fetchCoreAppSnapshot normalises it to a boolean; update the
type contract by introducing a separate wire type (e.g., AppStateSnapshotWire)
that keeps meetAutoOrchestratorHandoff optional, and change
AppStateSnapshotResult (the function return type used by fetchCoreAppSnapshot)
to have meetAutoOrchestratorHandoff: boolean (non-optional); adjust any
references to use the new wire type for parsing and ensure fetchCoreAppSnapshot
returns the normalized AppStateSnapshotResult so callers no longer need `??
false` (also apply the same split/fix to the other analogous block referenced
around the other occurrence).
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@app/src/providers/__tests__/CoreStateProvider.test.tsx`:
- Around line 247-277: The test should assert the optimistic state flip: after
calling ctx!.setMeetAutoOrchestratorHandoff(true) assert
ctx?.snapshot.meetAutoOrchestratorHandoff is true, and also update the
snapshot/refresh mock so subsequent reads reflect the new value (e.g. set
fetchSnapshot/mock for refresh to return makeSnapshot(...,
meetAutoOrchestratorHandoff: true)) so the optimistic flip is validated in
addition to verifying tauriCommands.openhumanUpdateMeetSettings was called;
update the test around setMeetAutoOrchestratorHandoff, ctx, fetchSnapshot,
makeSnapshot and openhumanUpdateMeetSettings accordingly.

In `@app/src/services/coreStateApi.ts`:
- Around line 24-43: The AppStateSnapshotResult type currently marks
meetAutoOrchestratorHandoff as optional even though fetchCoreAppSnapshot
normalises it to a boolean; update the type contract by introducing a separate
wire type (e.g., AppStateSnapshotWire) that keeps meetAutoOrchestratorHandoff
optional, and change AppStateSnapshotResult (the function return type used by
fetchCoreAppSnapshot) to have meetAutoOrchestratorHandoff: boolean
(non-optional); adjust any references to use the new wire type for parsing and
ensure fetchCoreAppSnapshot returns the normalized AppStateSnapshotResult so
callers no longer need `?? false` (also apply the same split/fix to the other
analogous block referenced around the other occurrence).

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 68e3be55-156e-40bd-9899-95ee822b061a

📥 Commits

Reviewing files that changed from the base of the PR and between b9e33a1 and 3003171.

📒 Files selected for processing (6)
  • app/src/components/settings/panels/PrivacyPanel.tsx
  • app/src/providers/__tests__/CoreStateProvider.test.tsx
  • app/src/services/coreStateApi.ts
  • app/src/test/setup.ts
  • app/src/utils/tauriCommands/config.test.ts
  • src/openhuman/config/schemas.rs

@senamakel senamakel merged commit ba88d8c into tinyhumansai:main May 7, 2026
21 checks passed
@oxoxDev oxoxDev deleted the fix/1299-meet-handoff-consent-gate branch May 7, 2026 03:56
AusAgentSmith pushed a commit to AusAgentSmith/openhuman that referenced this pull request May 23, 2026
…nsai#1299) (tinyhumansai#1310)

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Gmeet auto-handoff posts meeting notes to Slack #general without consent

2 participants