Skip to content

ref(web): Use the User id for web Actors - #2630

Merged
dcramer merged 2 commits into
mainfrom
ref/web-actor-user-id
Nov 4, 2026
Merged

dcramer merged 2 commits into
mainfrom
ref/web-actor-user-id

Conversation

@sentry-junior

@sentry-junior sentry-junior Bot commented Nov 3, 2026

Copy link
Copy Markdown
Contributor

Web Actors now use the internal User id. Before this change they used dashboard: plus the first 24 hex characters of sha256(email). webActorFromEmail(email) is now webActorFromUser(user).

ID format

The bare user.id, with no prefix. On web, Junior is the provider, so the User id is the provider-scoped id. Actors already carry platform. Keys that span platforms already add it (provenance, agent invocation, memory web:${userId}), so a web: prefix would give web:web:<id>. User ids are UUIDs, so they cannot collide with Slack U…/W… ids or local ids in flat keyspaces.

Prompt keys (user_id, actor_id, author_id) do not change. Only their value changes.

Call sites

api/acp/conversations.ts, api/connections/routes.ts, api/conversations/{create,fork}.ts, chat/conversations/fork.ts (now takes viewer: User), and chat/review-items/messages.ts. Evals fixtures (history.ts, agent.ts, recorded.ts) follow. The recorded sanitizer replaces web User ids with web-userN.

Legacy ids

chat/conversations/legacy-web-actor-id.ts holds the old hash. Each caller has a TODO that says when to delete it.

  • Viewer checks: stop.ts, pending-messages.ts, and steer-pending-messages.ts accept the User id or the legacy id for the same viewer. Pending messages queued before the deploy still match.
  • Event authors: sql/event-actor.ts keeps the SQL reverse lookup as a read-only fallback for dashboard: authors.
  • OAuth tokens: withLegacyWebTokens (user token store and sandbox egress) and the MCP credential read move tokens to the User id on a miss. They also delete the legacy key, so a later disconnect cannot bring it back. The lookup reads the database only for UUID-shaped ids.

TERMINOLOGY.md: a web Actor id is the User id.

Rolling-deploy risk

While old and new instances both run:

  • Old instances write and check only the legacy id. If a stop or steer request for a message written by a new instance reaches an old instance, the request fails until the rollout ends. A retry after the rollout works.
  • After a new instance moves a connection's tokens, an old instance sees no connection, so the person may see it as disconnected until the rollout ends. If they reconnect on an old instance, the tokens go to the legacy key. New instances read the User id key first.
  • Steering does not join a Turn that started before the deploy with the legacy id. Those messages start the next Turn.

No data migration. Nothing is deleted except moved legacy token keys.

Tests

  • Extended integration tests: stop, steer-pending-messages, connections (legacy token move and disconnect), list, oauth-identity-link, agent-invocation-user-approval, memory-plugin-storage, and conversation-work. The conversation harness now resolves a real viewer User.
  • junior-evals unit test: recorded.test.ts.
  • Passed: pnpm typecheck, pnpm lint, prettier, oxlint, file-length:check, and the focused, MCP, and api/conversations suites.
  • The MCP token move has no direct test.
  • In the full suite, durable-queue, annotation-cards, agent-dispatch-work, event-wake-delay, bot-image-hydration, and attachment-s3 also fail on main or need S3. watched-message-behavior is flaky and passes alone.
  • Evals were not run. Run the behavioral and integration suites before merge.

via @dcramer.

--

View Session [Sentry]

Web Actors now use the internal User id instead of
dashboard:<sha256(email)[:24]>. Junior is the web provider, so the User id
is the provider-scoped Actor id.

Input, web authorizations, and OAuth credentials saved under the legacy id
before the deploy keep working: viewer checks accept both ids, and token and
MCP credential reads move legacy keys to the User id on a miss.

Co-Authored-By: David Cramer <23610+dcramer@users.noreply.github.com>
@vercel

vercel Bot commented Nov 3, 2026 •

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated
junior-docs Ready Ready Preview Nov 4, 2026 12:10am UTC

Request Review

@github-actions

github-actions Bot commented Nov 3, 2026 •

Copy link
Copy Markdown
Contributor

1 screenshot change — 1 changed · 0 added · 0 removed

Review screenshots in Frameshift

Settings Mcp · Desktop
Settings Mcp · Desktop
Changed

@dcramer
dcramer marked this pull request as ready for review November 3, 2026 23:57
@github-actions github-actions Bot added the risk: high PR risk score: high label Nov 3, 2026

@dcramer dcramer left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

generally should be good. we should be identifying users at the edge, and then translating that to internal IDs etc (tho in the web the session cookie should already identify their user ID)

need to get the tests passing or disqualify the failure

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 1cd2e23. Configure here.

@sentry-junior

sentry-junior Bot commented Nov 4, 2026

Copy link
Copy Markdown
Contributor Author

The integration / shard 2/2 failure is not from this PR.

  • Rerun (attempt 2): subagents.eval.ts > stop it without a result turn now passes. Only coding/handoff.eval.ts > when the turn hands off, do the new request fails (58/59 pass).
  • That handoff case also fails in each of the last 11 failed integration-eval runs on unrelated branches, for example feat/review-item-answer-messages (41734424241), fix/evals-drop-session-affinity (41733630767), ref/tool-result-keys (41728176957), fix/event-people-render-once (41679382783), and compaction-background-job (41644896266).
  • Both cases run Slack conversations (actor U0TEST). This PR changes only web Actor ids and the web eval fixtures.

A read moves legacy dashboard credentials only when the User id has
none, so a disconnect could leave the legacy key and a later read
brought the connection back. Delete both keys for OAuth tokens and MCP
credentials.
@dcramer
dcramer merged commit 485dc14 into main Nov 4, 2026
50 of 51 checks passed
@dcramer
dcramer deleted the ref/web-actor-user-id branch November 4, 2026 00:28
sentry-junior Bot added a commit that referenced this pull request Nov 4, 2026
…ests

event-wake-delay still asserted the old Event prose. It timed out in the
sandbox before it reached the assertions.

web-batch-messages still called webActorFromEmail, which #2630 removed.
thomasjiangcy pushed a commit that referenced this pull request Nov 4, 2026
Main CI on 38c8693 (#2625) fails `check / types`, `test / junior
unit`, and `test / dashboard e2e`.

**Types and unit.** #2625 added `web-batch-messages.test.ts` with
`webActorFromEmail`. #2630 removed it. The test now uses
`webActorFromUser(testViewer(...))`, the same fix as c1e36af in #2631.

**Dashboard e2e.** `conversations-mobile.spec.ts` "keeps the message in
view while earlier history and late content load" is flaky. It is not a
regression from #2622 or #2625:

- Main CI: failed on f23838b (#2622, which only changes
`ReviewItemContext.tsx`) and 38c8693. Passed only on retry on
38597fb. Each of these failed at line 752, the last step (spacer above
the reader).
- #2634 added `scrollIntoViewIfNeeded()` right before that step. The
scroll event fires on the next frame. When the test appends the spacer
first, the transcript's scroll handler records the reader's place from
the layout that already has the spacer. The resize correction then sees
no shift. Local logs showed this order in every failure and the opposite
order in every pass.
- The test now waits one frame after each scroll so the scroll event
runs first. Assertions are unchanged. Locally the last step went from 6
failures in 16 runs to 0 in 160.

**Still open.** Under 4 parallel workers, 7 of the 160 local runs failed
one step earlier (after the earlier page loads). I did not measure that
step on main without this change. In the one failure I logged, the
correction ran twice: `scrollTop` moved by exactly 2× the shift. That
points to a product race in `transcriptBottomPinning.ts`, not a test
race. This PR does not fix it. I have not seen it fail in CI.

<!-- junior-request-attribution:start -->
via @dcramer.
<!-- junior-request-attribution:end -->

<!-- junior-session-footer:start -->
<!-- junior-conversation-id:agent%3A586024895340456299c9cfbe04e88e91 -->

--

[View
Session](https://junior-prod.sentry.dev/conversations/agent%3A586024895340456299c9cfbe04e88e91)
[[Sentry]](https://sentry.sentry.io/explore/conversations/agent%3A586024895340456299c9cfbe04e88e91/?project=4510944073809921)

<!-- junior-session-footer:end -->

---------

Co-authored-by: sentry-junior[bot] <264270552+sentry-junior[bot]@users.noreply.github.com>
Co-authored-by: David Cramer <23610+dcramer@users.noreply.github.com>
sentry-junior Bot added a commit that referenced this pull request Nov 4, 2026
…ests

event-wake-delay still asserted the old Event prose. It timed out in the
sandbox before it reached the assertions.

web-batch-messages still called webActorFromEmail, which #2630 removed.

This branch was successfully deployed

1 active deployment
Preview – junior-docs — e599ad02 Deployed Nov 4, 2026 by vercel[bot]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

risk: high PR risk score: high

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant