Skip to content

fix(meet): mint unique correlationId per upcoming-meeting join#4349

Merged
graycyrus merged 2 commits into
tinyhumansai:mainfrom
graycyrus:fix/meet-autojoin-duplicate-request-id
Jul 1, 2026
Merged

fix(meet): mint unique correlationId per upcoming-meeting join#4349
graycyrus merged 2 commits into
tinyhumansai:mainfrom
graycyrus:fix/meet-autojoin-duplicate-request-id

Conversation

@graycyrus

@graycyrus graycyrus commented Jun 30, 2026

Copy link
Copy Markdown
Contributor

Summary

The Upcoming-meetings Join button passed the deterministic calendar_event_id (<event_id>_<start>, or <meet_url>@<start_ms> for id-less events) as the join correlationId. That correlationId is echoed back at call-end (BackendMeetTranscript) and copied verbatim into MeetCallRecord.request_id by recent_calls::build_record — which is simultaneously:

  • the recent-calls list selection key + React key, and
  • the per-call detail filename (call_details/<id>.json) that getMeetCallDetail(request_id) reads.

So re-joining the same calendar meeting produced two call records with an identical request_id: the history list highlighted both rows together, and the second call's transcript overwrote / was unreachable. Surfaced by the Meetings redesign (#4275 / #4308), but the collision was pre-existing.

MeetComposer and the background auto-join (calendar.rs:404) already mint a fresh crypto.randomUUID() per join — UpcomingTable was the lone offender.

Fix

  • UpcomingTable.handleJoin now mints a fresh crypto.randomUUID() per join and passes that as correlationId.
  • calendar_event_id stays the dedup / per-event-policy key (handleJoinPolicyChangesetEventPolicy) and the local button-disable state (setJoiningId) only — never the call identity.
  • No backend change: the core correctly uses correlationId as the call identity; the bug was a frontend misuse.

Test plan

Closes #4338

Summary by CodeRabbit

  • Bug Fixes

    • Improved meeting join handling so each join attempt uses a unique request identifier, reducing duplicate-request issues and preventing history/transcript mix-ups when rejoining.
    • Added clearer join logging to help track individual meeting joins more reliably.
  • Tests

    • Expanded automated coverage to verify each join attempt gets a fresh identifier, including repeated joins for the same meeting.

@coderabbitai

coderabbitai Bot commented Jun 30, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: b552a6ae-6e15-4df1-b007-61bbf97c7c02

📥 Commits

Reviewing files that changed from the base of the PR and between f15b142 and 9a286bb.

📒 Files selected for processing (2)
  • app/src/components/meetings/UpcomingTable.tsx
  • app/src/components/meetings/__tests__/UpcomingTable.test.tsx
🚧 Files skipped from review as they are similar to previous changes (2)
  • app/src/components/meetings/UpcomingTable.tsx
  • app/src/components/meetings/tests/UpcomingTable.test.tsx

📝 Walkthrough

Walkthrough

The change replaces the deterministic correlationId (previously derived from meeting.calendar_event_id) in UpcomingTable's handleJoin with a fresh UUID generated per join attempt via crypto.randomUUID(). Debug logging and tests are updated to validate this new non-deterministic behavior.

Changes

Per-join correlation id

Layer / File(s) Summary
Generate and pass unique correlation id
app/src/components/meetings/UpcomingTable.tsx
handleJoin now generates a unique correlationId via crypto.randomUUID() per join, logs it alongside meeting id and platform, and passes it to joinMeetViaBackendBot instead of meeting.calendar_event_id.
Assert unique ids in join tests
app/src/components/meetings/__tests__/UpcomingTable.test.tsx
Tests now assert correlationId is present and differs from 'evt-1', and a new test verifies two consecutive joins produce distinct correlationId values.

Estimated code review effort: 2 (Simple) | ~10 minutes

Possibly related PRs

  • tinyhumansai/openhuman#4308: Directly touches the same UpcomingTable join flow that previously used a deterministic calendar_event_id correlation id.

Poem

A rabbit hops with UUID in paw,
No more duplicate calls to gnaw!
Each join now gets its own bright name,
Transcripts safe, no overwrite shame.
Hop, hop, hooray — correlation's fresh and true! 🐇✨

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly describes the main change: generating a unique correlationId for each upcoming-meeting join.
Linked Issues check ✅ Passed The change matches #4338 by replacing deterministic join identity with a fresh UUID per join and updating tests for repeated joins.
Out of Scope Changes check ✅ Passed The PR stays focused on UpcomingTable join identity and test coverage, with no obvious unrelated code changes.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

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

@graycyrus graycyrus marked this pull request as ready for review July 1, 2026 08:15
@graycyrus graycyrus requested a review from a team July 1, 2026 08:15

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 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/meetings/__tests__/UpcomingTable.test.tsx`:
- Around line 205-209: The UpcomingTable test is clicking the Join button again
before the first `handleJoin` flow has fully finished. Update
`UpcomingTable.test.tsx` to wait for the first `joinMock` call and for the
button to become enabled again before triggering the second click, using the
existing `joinBtn`/`joinMock` assertions to anchor the fix and avoid
timing-dependent failures.
🪄 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: 9e70d5cc-59f7-4211-a64c-fefe462c7a26

📥 Commits

Reviewing files that changed from the base of the PR and between f84eec5 and e200270.

📒 Files selected for processing (2)
  • app/src/components/meetings/UpcomingTable.tsx
  • app/src/components/meetings/__tests__/UpcomingTable.test.tsx

Comment thread app/src/components/meetings/__tests__/UpcomingTable.test.tsx

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/openhuman/agent_meetings/bus.rs (1)

55-192: 📐 Maintainability & Code Quality | 🟠 Major | 🏗️ Heavy lift

Business logic in bus.rs should delegate to ops.rs.

The new policy-gating, conditional summarization, and Ask-notification steps (lines 69-80, 98-170) are substantial business logic embedded directly in EventHandler::handle. Per repo conventions, bus.rs should host the subscriber wiring while domain logic lives in ops.rs; consider extracting this into a single ops.rs function (e.g. handle_backend_meet_transcript(...)) that handle() simply awaits and logs.

As per coding guidelines: "src/openhuman/**/bus.rs: Put domain event subscriber implementations (EventHandler impls) in bus.rs." and "src/openhuman/**/ops.rs: Put domain business logic and RpcOutcome<T> handlers in ops.rs when a domain has logic."

🤖 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 `@src/openhuman/agent_meetings/bus.rs` around lines 55 - 192, The
EventHandler::handle implementation in bus.rs is carrying too much domain
behavior; move the policy gating, summary generation, Ask notification, and
memory-ingest flow into a dedicated ops.rs function such as
handle_backend_meet_transcript(...). Keep bus.rs limited to subscriber wiring
and a simple await/log wrapper that delegates to the ops function, preserving
the existing symbols create_meeting_thread_with_transcript,
generate_meeting_summary_bounded, and ingest_backend_meeting_transcript behind
the ops boundary.

Source: Coding guidelines

🤖 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 `@src/openhuman/agent_meetings/bus.rs`:
- Around line 159-170: The Ask notification in `generate_summary_on_demand` is
currently published unconditionally when `SummaryAction::Ask` matches, even
though the stored call detail may not exist if `record_backend_call_detail`
failed. Update the flow around the `publish_core_notification` call in `bus.rs`
so the Ask card is only sent after detail persistence is confirmed, using the
existing `request_id`-based call detail record as the gate. If persistence
cannot be verified, skip publishing the Ask card and keep the `tracing::info!`
logging aligned with the new condition.

In `@src/openhuman/agent_meetings/ops.rs`:
- Around line 435-477: generate_summary_on_demand currently saves the generated
summary via recent_calls::record_backend_call_detail and then fails the whole
flow if create_meeting_thread_with_transcript returns an error. Change this path
so thread creation is best-effort: keep the persisted summary, log the failure
with tracing, and still return success using the already-generated state instead
of propagating the error. Use generate_summary_on_demand and
create_meeting_thread_with_transcript as the key places to update, matching the
tolerant behavior used in bus.rs.

---

Outside diff comments:
In `@src/openhuman/agent_meetings/bus.rs`:
- Around line 55-192: The EventHandler::handle implementation in bus.rs is
carrying too much domain behavior; move the policy gating, summary generation,
Ask notification, and memory-ingest flow into a dedicated ops.rs function such
as handle_backend_meet_transcript(...). Keep bus.rs limited to subscriber wiring
and a simple await/log wrapper that delegates to the ops function, preserving
the existing symbols create_meeting_thread_with_transcript,
generate_meeting_summary_bounded, and ingest_backend_meeting_transcript behind
the ops boundary.
🪄 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: a2506591-a9b9-49c7-81ea-34386d93de34

📥 Commits

Reviewing files that changed from the base of the PR and between e200270 and f15b142.

📒 Files selected for processing (5)
  • app/src/components/meetings/__tests__/UpcomingTable.test.tsx
  • src/openhuman/agent_meetings/bus.rs
  • src/openhuman/agent_meetings/ops.rs
  • src/openhuman/agent_meetings/schemas.rs
  • src/openhuman/agent_meetings/summary.rs
🚧 Files skipped from review as they are similar to previous changes (1)
  • app/src/components/meetings/tests/UpcomingTable.test.tsx

Comment thread src/openhuman/agent_meetings/bus.rs Outdated
Comment thread src/openhuman/agent_meetings/ops.rs Outdated
graycyrus added 2 commits July 1, 2026 14:00
The Upcoming table's Join button passed the deterministic
`calendar_event_id` (<event_id>_<start>) as the join correlationId. That
correlationId is echoed back at call-end and copied verbatim into
`MeetCallRecord.request_id` — which is both the recent-calls list key/React
key and the per-call detail filename. Re-joining the same calendar meeting
therefore produced two call records sharing one request_id: the history list
double-highlighted both rows and the second call's transcript overwrote /
was unreachable.

Mint a fresh `crypto.randomUUID()` per join (mirroring MeetComposer and the
background auto-join in calendar.rs). `calendar_event_id` stays the
dedup/per-event-policy key and button-disable state only — never the call
identity.

Closes tinyhumansai#4338
Addresses CodeRabbit: the regression test clicked Join a second time before
handleJoin's finally cleared joiningId, so the (disabled) button could swallow
the click and make the two-joins assertion timing-dependent. Wait for the
button to re-enable first.
@graycyrus graycyrus force-pushed the fix/meet-autojoin-duplicate-request-id branch from f15b142 to 9a286bb Compare July 1, 2026 08:30
@coderabbitai coderabbitai Bot removed the bug label Jul 1, 2026
@graycyrus graycyrus merged commit 98925db into tinyhumansai:main Jul 1, 2026
25 of 31 checks passed
@github-project-automation github-project-automation Bot moved this from Todo to Done in Team Openhuman Jul 1, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Archived in project

Development

Successfully merging this pull request may close these issues.

Meeting auto-join: re-joining the same calendar event produces duplicate call records sharing one request_id

1 participant