Skip to content

observability(sentry): attach user id to Rust-core events (#3135)#3136

Open
oxoxDev wants to merge 7 commits into
tinyhumansai:mainfrom
oxoxDev:feat/3135-sentry-user-context
Open

observability(sentry): attach user id to Rust-core events (#3135)#3136
oxoxDev wants to merge 7 commits into
tinyhumansai:mainfrom
oxoxDev:feat/3135-sentry-user-context

Conversation

@oxoxDev
Copy link
Copy Markdown
Contributor

@oxoxDev oxoxDev commented Jun 1, 2026

Summary

  • Bind the signed-in user's id to the process-global Sentry scope on login / account switch, and clear it on logout, so Rust-core events carry user.id.
  • Adds set_sentry_user_id / clear_sentry_user helpers in src/core/observability.rs; wires them into credentials::ops::store_session (set) and clear_session (clear).
  • Only the id is sent — never email/name/IP — consistent with send_default_pii: false.

Problem

Direct-mode Composio errors raised from the Rust core ([composio-direct] list_connections failed, Invalid API key, etc.) land in the self-hosted Sentry tauri-rust / core-rust projects with no user context (userCount=0 on every direct-mode composio issue), so thousands of events can't be traced back to a user.

Both the standalone core binary (src/main.rs) and the Tauri shell already attach event.user inside the Sentry before_send hook, but they read it from app_state::peek_cached_current_user_identity(). That cache is only ever warmed by the frontend-driven app_state_snapshot RPC. Errors that fire from background loops — e.g. the periodic Composio sync tick in memory_sync/composio/periodic.rs, which calls report_composio_op_error — run before, or independent of, any snapshot RPC, so event.user resolves to None. The backend project carries user.id because it tags every event at the session boundary rather than lazily.

Solution

Mirror the backend's session-boundary tagging in the Rust core: set the Sentry scope user eagerly when the user becomes known, instead of relying on the lazily-warmed before_send cache.

  • store_session (the fresh-login and account-switch path) resolves the backend's mongo ObjectId into resolved_user_id; we call set_sentry_user_id(uid) right there. The scope mutation lands on the global hub, so all subsequent report_error / capture_message calls — including the background composio tick — attach it.
  • clear_session (logout) calls clear_sentry_user() so post-logout events aren't misattributed to the prior account.
  • Blank/whitespace ids are dropped so we never attach an empty user.id.
  • No-op under a missing-DSN no-op guard.

Submission Checklist

If a section does not apply to this change, mark the item as N/A with a one-line reason. Do not delete items.

  • Tests added or updated (happy path + at least one failure / edge case) per Testing Strategy
  • Diff coverage ≥ 80% — all new logic (set/clear/blank helpers, the warm_sentry_user_from_active_session boot warmer) is covered by added unit tests; the thin jsonrpc.rs boot wiring delegates to the tested helper. CI diff-cover gate is authoritative.
  • Coverage matrix updated — N/A: observability-only change, no user-facing feature row.
  • All affected feature IDs from the matrix are listed in the PR description under ## Related — N/A: no feature IDs.
  • No new external network dependencies introduced (mock backend used per Testing Strategy)
  • Manual smoke checklist updated if this touches release-cut surfaces — N/A: no release-cut surface touched.
  • Linked issue closed via Closes #NNN in the ## Related section

Impact

  • Restores user attribution on direct-mode core Sentry events in tauri-rust / core-rust, matching backend-mode coverage.
  • Desktop + CLI core; no frontend/Tauri-shell code changed.
  • Security/privacy: only the user id (already stored in the auth profile) is added to the Sentry scope; no email/name/IP, preserving send_default_pii: false.

Related


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

Linear Issue

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

Commit & Branch

Validation Run

  • N/A: pnpm --filter openhuman-app format:check — no frontend change.
  • N/A: pnpm typecheck — no frontend change.
  • Focused tests: cargo test --lib core::observability (156 passed) + cargo test --lib credentials::ops (39 passed) — incl. all new tests.
  • Rust fmt/check (if changed): cargo fmt --check clean; cargo check clean (pre-existing warnings in unrelated slack_backfill bin only).
  • N/A: Tauri fmt/check — Tauri shell not changed.

Validation Blocked

  • command: cargo clippy --lib -- -D warnings
  • error: 215 pre-existing clippy errors on a clean main checkout (local toolchain 1.93.0 newer than repo target); none in the touched files.
  • impact: clippy gate not actionable locally; CI uses the pinned rust-toolchain.toml.

Behavior Changes

  • Intended behavior change: Rust-core Sentry events now carry user.id from login through logout.
  • User-visible effect: none in-app; Sentry dashboard gains user attribution on core events.

Parity Contract

  • Legacy behavior preserved: the before_send cache-based attribution is untouched; this adds an eager scope-level set that takes effect even when the cache is cold.
  • Guard/fallback/dispatch parity checks: blank-id guard + no-op-guard safety verified by tests.

Duplicate / Superseded PR Handling

  • Duplicate PR(s): none
  • Canonical PR: this
  • Resolution: N/A

Summary by CodeRabbit

Release Notes

  • New Features

    • Enhanced error reporting infrastructure by implementing automatic user context tracking across the full authentication lifecycle, including login, logout, background session restoration, and app startup, ensuring all crashes, errors, and diagnostic logs are accurately attributed to the correct user.
  • Tests

    • Added test coverage for user context management, validating proper handling during authentication events and session restoration scenarios.

@oxoxDev oxoxDev requested a review from a team June 1, 2026 09:09
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Jun 1, 2026

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

This PR establishes Sentry scope user-tracking for core-mode errors by introducing helper functions to set and clear the global Sentry user scope at session boundaries, integrating those helpers into session login/logout/boot flows, and adding comprehensive tests to validate the behavior.

Changes

Sentry user-scoping for direct-mode event attribution

Layer / File(s) Summary
Sentry user-scoping helpers
src/core/observability.rs
set_sentry_user_id(user_id: &str) trims input and sets the global Sentry scope user to a sentry::User with only the user.id field populated, skipping on blank input; clear_sentry_user() clears the scope user via set_user(None) and logs a debug breadcrumb.
Helper validation tests
src/core/observability_tests.rs
set_sentry_user_id_attaches_id_to_reported_events verifies user id is attached to reported events and clear removes it; set_sentry_user_id_ignores_blank_input confirms empty and whitespace inputs are no-ops.
Session lifecycle integration and boot warmer
src/openhuman/credentials/ops.rs
store_session() now calls set_sentry_user_id() after successful credential persistence; clear_session() calls clear_sentry_user() before stopping services; new warm_sentry_user_from_active_session(root_dir) reads the active user id from disk and restores it into the Sentry scope.
Boot-warm tests
src/openhuman/credentials/ops_tests.rs
Tests validate that warm_sentry_user_from_active_session returns Some(id) when active user exists on disk and None when absent.
Background service boot detection
src/core/jsonrpc.rs
Startup logic now uses warm_sentry_user_from_active_session(&root) to determine already_logged_in and warm the Sentry scope for restored sessions before background services run.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~12 minutes

Suggested labels

sentry-traced-bug

Suggested reviewers

  • graycyrus
  • senamakel

Poem

🐇 A Sentry scope so keen and bright,
Now tracks each user left and right!
From login's dawn to logout's end,
No error shall again offend—
user.id now rides the wire,
Tracing tales the logs require! 🌟

🚥 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 and concisely describes the main change: adding user ID attachment to Sentry events in the Rust core, which is the primary objective of this PR.
Linked Issues check ✅ Passed All coding requirements from #3135 are met: helpers set/clear scope user on login/logout, ignore blank IDs, only transmit user.id, include boot-restore warmer, and are properly integrated into session boundaries.
Out of Scope Changes check ✅ Passed All changes directly support the objective of attaching user context to Sentry events; no unrelated modifications detected beyond the stated scope.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

✏️ 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.

@coderabbitai coderabbitai Bot added the rust-core Core Rust runtime in src/: CLI, core_server, shared infrastructure. label Jun 1, 2026
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: 2

🧹 Nitpick comments (1)
src/core/observability.rs (1)

1772-1793: ⚡ Quick win

Add debug breadcrumbs for Sentry user-scope mutations (PII-safe).

The new flow mutates global Sentry scope but emits no debug/trace signal. Add grep-friendly debug logs while keeping the id redacted.

♻️ Proposed patch
 pub fn set_sentry_user_id(user_id: &str) {
     let trimmed = user_id.trim();
     if trimmed.is_empty() {
         return;
     }
     let owned = trimmed.to_string();
     sentry::configure_scope(|scope| {
         scope.set_user(Some(sentry::User {
             id: Some(owned.clone()),
             ..Default::default()
         }));
     });
+    tracing::debug!(
+        user_id_len = trimmed.len(),
+        "[observability] sentry user scope set"
+    );
 }

 pub fn clear_sentry_user() {
     sentry::configure_scope(|scope| scope.set_user(None));
+    tracing::debug!("[observability] sentry user scope cleared");
 }

As per coding guidelines, "In Rust, default to verbose diagnostics on new/changed flows using log/tracing at debug/trace levels with stable grep-friendly prefixes and correlation fields" and "Never log secrets or full PII — redact sensitive information in logs".

🤖 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/core/observability.rs` around lines 1772 - 1793, Add verbose,
grep-friendly debug breadcrumbs when mutating the Sentry user scope: in
set_sentry_user_id, after trimming and before/after calling
sentry::configure_scope, emit a tracing::debug (or log::debug) with a stable
prefix like "SENTRY_USER: set id=" and include a redacted form of the id (e.g.,
first 6 chars + "...") rather than the full PII; also call
sentry::add_breadcrumb (category "user", level Debug) recording the same
redacted id and action. In clear_sentry_user, emit a tracing::debug with prefix
"SENTRY_USER: cleared" and add a corresponding sentry::add_breadcrumb noting the
clear action. Ensure no full user_id is ever logged or sent in breadcrumbs and
reuse the existing set_sentry_user_id and clear_sentry_user symbols for
placement.
🤖 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/core/observability_tests.rs`:
- Around line 3603-3609: The test set_sentry_user_id_ignores_blank_input
currently has no assertion; after calling set_sentry_user_id("") and
set_sentry_user_id("   "), capture the emitted Sentry event (using the test
transport / test helper used elsewhere in tests) and assert that the event has
no user attached (e.g., event.user is None or event.user.id is None) to ensure
blank/whitespace ids are dropped; update the test to retrieve the last emitted
event and perform this assertion referencing
set_sentry_user_id_ignores_blank_input and set_sentry_user_id.

In `@src/openhuman/credentials/ops.rs`:
- Around line 174-181: The Sentry binding is set too early: move the
crate::core::observability::set_sentry_user_id(resolved_user_id) call so it runs
only after session persistence succeeds (i.e., after store_provider_token and
any related session/store operations complete without error); specifically,
remove or skip the current early call guarded by resolved_user_id and re-invoke
set_sentry_user_id only in the success path following the successful return from
store_provider_token (and any subsequent session persistence steps) to avoid
binding Sentry on failed persistence.

---

Nitpick comments:
In `@src/core/observability.rs`:
- Around line 1772-1793: Add verbose, grep-friendly debug breadcrumbs when
mutating the Sentry user scope: in set_sentry_user_id, after trimming and
before/after calling sentry::configure_scope, emit a tracing::debug (or
log::debug) with a stable prefix like "SENTRY_USER: set id=" and include a
redacted form of the id (e.g., first 6 chars + "...") rather than the full PII;
also call sentry::add_breadcrumb (category "user", level Debug) recording the
same redacted id and action. In clear_sentry_user, emit a tracing::debug with
prefix "SENTRY_USER: cleared" and add a corresponding sentry::add_breadcrumb
noting the clear action. Ensure no full user_id is ever logged or sent in
breadcrumbs and reuse the existing set_sentry_user_id and clear_sentry_user
symbols for placement.
🪄 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: 8319a2e0-d137-47c8-b589-3434f2872677

📥 Commits

Reviewing files that changed from the base of the PR and between 4b26267 and 090135f.

📒 Files selected for processing (3)
  • src/core/observability.rs
  • src/core/observability_tests.rs
  • src/openhuman/credentials/ops.rs

Comment thread src/core/observability_tests.rs
Comment thread src/openhuman/credentials/ops.rs Outdated
@oxoxDev
Copy link
Copy Markdown
Contributor Author

oxoxDev commented Jun 1, 2026

Pushed 2 follow-up commits closing the boot-restore gap: store_session only binds the Sentry user on fresh login/switch, so a restored session (app reopened while signed in) started the periodic Composio sync tick with a cold scope → errors reported user = None. New warm_sentry_user_from_active_session() reads active_user.toml and binds the scope on the existing-session boot path (jsonrpc.rs), with unit coverage for present/absent. cargo check + cargo fmt --check clean; credentials lib tests green.

@coderabbitai coderabbitai Bot added feature Net-new user-facing capability or product behavior. working A PR that is being worked on by the team. labels Jun 1, 2026
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.

♻️ Duplicate comments (1)
src/openhuman/credentials/ops.rs (1)

174-181: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Move Sentry user binding to after session persistence succeeds.

The Sentry scope is set before store_provider_token (Line 279) can fail. If persistence fails, the global Sentry scope remains bound and can misattribute subsequent events.

🐛 Proposed fix
-    // Bind the signed-in user's id to the Sentry scope as early as possible
-    // (on fresh login and on account switch, both of which route through here)
-    // so events reported by background loops carry `user.id` even before the
-    // frontend `app_state_snapshot` RPC has warmed the identity cache. Only
-    // the id is sent — never email/name/IP. See `set_sentry_user_id`.
-    if let Some(ref uid) = resolved_user_id {
-        crate::core::observability::set_sentry_user_id(uid);
-    }
-
     // If we know the user_id, activate the user-scoped directory BEFORE storing
@@
     let profile = auth
         .store_provider_token(
             APP_SESSION_PROVIDER,
             DEFAULT_AUTH_PROFILE_NAME,
             trimmed_token,
             metadata,
             true,
         )
         .map_err(|e| e.to_string())?;
+
+    // Bind the signed-in user's id to the Sentry scope after persistence
+    // succeeds so events reported by background loops carry `user.id`.
+    if let Some(ref uid) = resolved_user_id {
+        crate::core::observability::set_sentry_user_id(uid);
+    }
🤖 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/credentials/ops.rs` around lines 174 - 181, The Sentry user
binding (crate::core::observability::set_sentry_user_id) is currently called as
soon as resolved_user_id exists, before persisting the session with
store_provider_token; move the call so it only runs after store_provider_token
returns success to avoid binding Sentry to a user when persistence fails.
Specifically, locate the resolved_user_id check and the set_sentry_user_id call
and relocate or duplicate the call to execute after the successful return/Ok
path of store_provider_token (or inside the success branch handling function
that confirms persistence), ensuring any error path does not call
set_sentry_user_id.
🤖 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.

Duplicate comments:
In `@src/openhuman/credentials/ops.rs`:
- Around line 174-181: The Sentry user binding
(crate::core::observability::set_sentry_user_id) is currently called as soon as
resolved_user_id exists, before persisting the session with
store_provider_token; move the call so it only runs after store_provider_token
returns success to avoid binding Sentry to a user when persistence fails.
Specifically, locate the resolved_user_id check and the set_sentry_user_id call
and relocate or duplicate the call to execute after the successful return/Ok
path of store_provider_token (or inside the success branch handling function
that confirms persistence), ensuring any error path does not call
set_sentry_user_id.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 798dd78f-15d3-46ee-bede-12e4f602a04d

📥 Commits

Reviewing files that changed from the base of the PR and between 090135f and 3cf3a66.

📒 Files selected for processing (3)
  • src/core/jsonrpc.rs
  • src/openhuman/credentials/ops.rs
  • src/openhuman/credentials/ops_tests.rs

coderabbitai[bot]
coderabbitai Bot previously approved these changes Jun 1, 2026
Copy link
Copy Markdown
Contributor

@graycyrus graycyrus left a comment

Choose a reason for hiding this comment

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

@oxoxDev the code looks good to me — the session-boundary scope binding is the right fix, the blank-id guard is correct, and the boot-restore warmer covers the restored-session gap that the previous lazy cache approach missed.

the two CodeRabbit findings (bind after persistence succeeds, blank-id test assertion) are both addressed in 19a7cf7, and the test coverage is solid: isolated TestTransport hubs, PII-minimal assertions, clear/re-bind lifecycle verified.

one thing i want to call out: warm_sentry_user_from_active_session does double duty — it returns the active user id (which jsonrpc.rs uses to set already_logged_in) and also fires the Sentry scope binding as a side effect. the function name makes the side effect clear, so this is fine for the boot path where both are needed together. just worth documenting if this function ever gets called from somewhere that only wants the id.

the only thing holding this back is CI — Rust Core Coverage and E2E Playwright lanes 1 and 2 are failing. the frontend/playwright failures look unrelated to this Rust-only change, but the Rust Core Coverage failure is blocking the diff-cover gate, so i cannot confirm >= 80% on the diff. once those are green i'll come back and approve.

@oxoxDev
Copy link
Copy Markdown
Contributor Author

oxoxDev commented Jun 1, 2026

@graycyrus thanks for the review — all addressed:

CI reds (the blocker): root-caused the two red Rust lanes. They were not from this change — #3125 switched AuthProfilesStore::load() to gracefully drop OAuth profiles with a missing access_token (log + skip) but left config_auth_app_state_connectivity_e2e still asserting .expect_err on the old error path. That test went red on main when #3125 merged and this branch inherited it; both Rust Core Coverage and Rust E2E (mock backend) failed on that single panic, which is what was stalling the diff-cover gate. Fixed in 83649ec — the assertion now checks the profile is dropped from the recovered store. Ran the test locally: green.

Double-duty callout: documented in 6f6945f — the rustdoc now spells out that warm_sentry_user_from_active_session returns the id and binds the Sentry scope as a side effect, and points a future id-only caller at read_active_user_id.

Frontend Coverage + Playwright lane 1/4: unrelated to this Rust-only change — Frontend Coverage fails on memoryGraphLayout.test.ts (graph layout, untouched here) and Playwright lanes 2–4 are green so lane 1 is flaky. A rerun should clear both.

Fresh CI is running now — should come back green on the Rust lanes. Re-requesting your review once it settles.

coderabbitai[bot]
coderabbitai Bot previously approved these changes Jun 1, 2026
Copy link
Copy Markdown
Contributor

@sanil-23 sanil-23 left a comment

Choose a reason for hiding this comment

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

@oxoxDev the code itself looks solid — setting the Sentry scope user at the session boundary (login/account-switch + boot-restore warmer) instead of leaning on the lazily-warmed before_send cache is the right fix, and it lines up with the backend pattern you referenced. The blank/whitespace guard, the after-persist ordering so a failed store_provider_token can't leave a stale global scope, and the PII-minimal id-only payload (no email/name/IP) are all handled well. Tests cover the set/clear/blank branches and both warmer paths.

The one thing holding this back is CI, which is red right now. Worth noting the failing checks look unrelated to this change:

  • Rust Core Coverage fails on tests/inference_provider_admin_round22_raw_coverage_e2e.rs:254 ("nested provider failure" assertion) — that file isn't touched by this PR, so it reads as a pre-existing failure on main, same flavor as the missing-access-token e2e you already realigned in this branch.
  • Frontend Coverage (Vitest) and the web E2E lane are frontend lanes, and this is a Rust-only diff.

Can you rebase on latest main (and pick up any fix for the round22 test) so CI goes green? Once the required checks are passing i'll come back and approve — the code is ready. Let me know if you want a hand tracking down the unrelated failures.

@oxoxDev oxoxDev requested a review from graycyrus June 1, 2026 13:19
@oxoxDev
Copy link
Copy Markdown
Contributor Author

oxoxDev commented Jun 1, 2026

@graycyrus re-requesting review. State of the CI you flagged:

  • Rust E2E (mock backend) → green. The red was a stale assertion from fix(auth): gracefully drop OAuth profiles with missing access_token #3125 (graceful-drop) — fixed in 83649ec.
  • Playwright E2E Gate → green. lane 1/4's individual red is the chat-harness-subagent cascade that regressed on main after feat(subagent): persist sub-agent runs and let orchestrator relay user messages #3055 — identical failure on main's own runs, not this PR.
  • Rust Core Coverage → still red, but not this PR. Once Rust E2E passed it unmasked a parallel test-isolation bug in inference_provider_admin_round22_raw_coverage_e2e (3 tests stomp process-global OPENHUMAN_OLLAMA_BASE_URL/OPENHUMAN_WORKSPACE; --test-threads=1 → 5 pass, parallel → 1 fail). Broken on main, just masked there by the config_auth failure running first.
  • Frontend Coverage → memoryGraphLayout.test.ts, also stale-on-main.

The three stale-main failures (memoryGraphLayout, chat-harness-subagent quarantine, + the round22 isolation fix) are being consolidated into #3149 ("align stale main tests"). Once #3149 lands I'll rebase this and every lane goes green. This PR's own diff is Rust-only and self-contained; the binding/test changes you reviewed are unchanged since your pass. The double-duty doc note you asked for is in 6f6945f.

@oxoxDev
Copy link
Copy Markdown
Contributor Author

oxoxDev commented Jun 1, 2026

@sanil-23 thanks — appreciate the close read on the after-persist ordering + PII-minimal payload.

You're right that round22 is pre-existing/unrelated, with one refinement: it's not a stale assertion (the nested provider failure string is still correct) — it's a parallel test-isolation bug. Three tests in that file set process-global OPENHUMAN_OLLAMA_BASE_URL/OPENHUMAN_WORKSPACE via EnvVarGuard and stomp each other when run concurrently: --test-threads=1 → 5 pass, default parallel → the object-error lookup resolves against a sibling's mock base and the assert fails. It was masked on main by the config_auth failure running first; realigning that one unmasked it.

Picking up the round22 fix (serialize the env-mutating tests) plus the two frontend stale-main lanes now, then rebasing on latest main. Will ping when green.

oxoxDev added 6 commits June 1, 2026 19:14
…inyhumansai#3135)

store_session only binds the Sentry user on fresh login/account-switch. A
restored session (app reopened while signed in) never routes through it, so
background-loop errors (e.g. the periodic Composio sync tick) reported with
user = None. Add warm_sentry_user_from_active_session() reading active_user.toml
+ binding the scope, with unit coverage for the present/absent branches.
…ansai#3135)

Wire warm_sentry_user_from_active_session() into the existing-session boot
branch so the scope is set before login-gated background services (incl. the
periodic Composio sync) start.
…t blank-input, add breadcrumbs (tinyhumansai#3135)

- store_session: move set_sentry_user_id to after store_provider_token
  succeeds, so a failed persist no longer leaves the global scope bound.
- set_sentry_user_id_ignores_blank_input: assert the captured event carries
  no user (test previously had no assertion).
- set/clear: add PII-safe debug breadcrumbs (id length only, never the id).
@oxoxDev oxoxDev force-pushed the feat/3135-sentry-user-context branch from 6f6945f to 1d5e3b1 Compare June 1, 2026 13:45
@oxoxDev oxoxDev requested a review from sanil-23 June 1, 2026 14:30
@oxoxDev
Copy link
Copy Markdown
Contributor Author

oxoxDev commented Jun 1, 2026

Status — this is ready for approval. Rebased on latest main (picked up #3147's stale-test fixes; dropped my now-redundant config_auth realignment).

The PR is MERGEABLE; the only remaining red checks are pre-existing on main itself and don't gate merge:

Net: CI on this branch is at parity with main — every failure reproduces on main and PRs (#3125, #3147) merged over them. The Sentry diff you both reviewed is unchanged. Re-requesting approval — happy to open a separate tracking issue for the systemic *_raw_coverage_e2e isolation flakes if useful.

Copy link
Copy Markdown
Contributor

@sanil-23 sanil-23 left a comment

Choose a reason for hiding this comment

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

@oxoxDev re-reviewed at f3addf5. The observability work itself is in good shape — session-boundary scope binding plus the boot-restore warmer is exactly the right fix for the userCount=0 gap, the post-persist ordering (so a failed store_provider_token can't leave the global scope bound) is correct, and the test coverage is solid: set/clear, blank-input asserting no scope user, and both warmer branches. Privacy posture is good too — only the id reaches the scope, never email/name/IP, consistent with send_default_pii: false, and the debug logs only carry id length.

The boot-path change in jsonrpc.rs preserves the prior .is_some() semantics, so no behavioral drift there. New exports are only consumed by credentials/ops and the boot path — no broken callers.

I can't approve yet because CI isn't green:

  • Rust Core Coverage fails on inference_voice_http_round23_raw_coverage_e2e.rs:124 (reasoning-v1 model-listing assertion) — unrelated to this diff, looks pre-existing.
  • E2E web lane 1 fails on auth-access-control.spec.ts (auth callback not reaching /home) — a timing-sensitive auth e2e, not something a Sentry scope mutation can cause; reads as flaky.
  • The chat-harness-subagent quarantine in the last commit is a reasonable call for the ECONNREFUSED cascade, though it's a bundled concern from #3055.

If you rebase on latest main to pick up any fixes for those coverage/e2e tests (or confirm they're flaky and re-run), I'll come back and approve once required checks are green. Nice work.

@oxoxDev oxoxDev force-pushed the feat/3135-sentry-user-context branch from f3addf5 to 1d5e3b1 Compare June 1, 2026 15:25
@oxoxDev
Copy link
Copy Markdown
Contributor Author

oxoxDev commented Jun 1, 2026

Dropped the bundled chat-harness-subagent quarantine commit (the #3055 concern @sanil-23 flagged) — this branch is now a clean Sentry-only diff rebased on latest main.

CI is now at parity with the other open PRs on the same base (e.g. #3150, #3153): the only reds are the pre-existing Rust Core Coverage (inference_voice_http_round23 isolation flake) and the flaky Playwright web lane 1 (auth-access-control) — both reproduce identically on those PRs and on main's own run of the #3147 merge commit. Required Playwright E2E Gate is green. No extra failures introduced by this PR.

@coderabbitai coderabbitai Bot added the sentry-traced-bug Bug identified via Sentry triage label Jun 1, 2026
Copy link
Copy Markdown
Contributor

@sanil-23 sanil-23 left a comment

Choose a reason for hiding this comment

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

@oxoxDev re-reviewed at 1d5e3b1. The code is in good shape — binding the Sentry scope user at the session boundary (login/account-switch) plus the boot-restore warmer is the right fix for the userCount=0 gap, and it's a cleaner approach than leaning on the lazily-warmed before_send cache. The post-persist ordering in store_session is correct (a failed store_provider_token can no longer leave the global scope pointing at a half-completed login), the blank/whitespace guard is right, and the logout clear prevents misattribution. PII handling is solid: only the id reaches the scope, never email/name/IP, and the debug logs carry id length only — consistent with send_default_pii: false. Test coverage is strong: set, clear, blank-input (asserting no user), and both boot-warmer branches are all exercised.

The two earlier review points (bind after persistence succeeds, and the missing assertion in the blank-id test) are both resolved.

The only thing holding this back is CI — the Playwright E2E lanes and the coverage jobs are still pending/red on this commit. Once the required checks are green i'll come back and approve. If any of the failing lanes look like pre-existing flakes unrelated to this Rust-only diff, just flag it and i'll take a look.

Copy link
Copy Markdown
Contributor

@sanil-23 sanil-23 left a comment

Choose a reason for hiding this comment

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

@oxoxDev re-reviewed at 732c444 (the upstream/main merge). No observability code changed since the last pass, and it still reads clean — session-boundary scope binding plus the boot-restore warmer, post-persist ordering, blank-input guard, logout clear, and id-only PII posture are all correct, with solid test coverage. Both earlier review points stay resolved and CodeRabbit is approving.

Still can't approve yet — CI is red, but on checks unrelated to this Rust-only diff:

  • Rust Core Coverage fails in tools_approval_channels_raw_coverage_e2e.rs:1689 (orchestrator_tool_synthesis_covers_agent_and_integration_delegation_edges) — orchestrator tooling, nothing a Sentry scope mutation touches.
  • E2E web lane 1/4 fails on chat-harness-subagent.spec.ts:136, which then cascades ECONNREFUSED into the following specs. This is the #3055 flake; the clean rebase dropped the earlier quarantine, so it resurfaced.

Both look like pre-existing flakes rather than anything from this change. A re-run (or rebasing past whatever lands the fixes on main) should clear them. Once the required checks are green i'll come back and approve — the code side is ready.

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

Labels

feature Net-new user-facing capability or product behavior. rust-core Core Rust runtime in src/: CLI, core_server, shared infrastructure. sentry-traced-bug Bug identified via Sentry triage working A PR that is being worked on by the team.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

observability(sentry): attach user id to Rust-core events for direct-mode traceability

4 participants