Lock guest claim workspace routing contract in tests
Summary
Locks the guest-claim workspace routing contract for Done-when 2 ("adopt saved work only after verified authentication into the intended workspace") with a new fixture suite, lib/context/__tests__/guestWorkspaceRouting.test.ts. Unlike the existing guest tests it runs the real authorizeContextOwner against a mocked membership check, so it proves: a claim without organization_id adopts with p_owner === p_actor === accountId; a verified organization claim keeps the signed-in account as actor and the organization as owner; denied or malformed membership never reaches storage or dispatch; Guest session expired / Guest work already claimed propagate without dispatch and never restart extraction; repeated claims stay idempotent; and through guestContextHandler a personal or organization claim passes verified identity into adopt_context_guest without reissuing a cookie, while a claim retry on an expired or foreign-claimed cookie answers 409 with no replacement cookie. The Context README gains one paragraph stating the routing contract and that campaign attribution is not captured yet. No production code changes.
Refs recoupable/app#2145 (child of epic recoupable/app#2116).
Merge sequencing
database → api. Sibling PR for this ticket: recoupable/database branch claude/ctx-2145-guest-adoption-tests (SQL transaction tests for adoption, expiry, repeat claims and purge). Both are tests-only and independently mergeable; neither depends on an OPEN PR. Builds on merged api#915 (guest funnel) and database#91.
Verification
- Ran:
pnpm exec vitest run lib/context/__tests__/guestWorkspaceRouting.test.ts lib/context/__tests__/guest.test.ts lib/context/__tests__/guestTransport.test.ts→ 23 passed (8 new + 6 + 9 unchanged) - Ran:
pnpm exec vitest run lib/context→ 68 files passed, 8 skipped; 334 tests passed, 8 skipped - Ran:
pnpm exec eslint lib/context/__tests__/guestWorkspaceRouting.test.ts→ clean;pnpm exec prettier --check lib/context/__tests__/guestWorkspaceRouting.test.ts lib/context/README.md→ clean - Ran:
pnpm exec tsc --noEmit→ no errors inlib/context/**; the remaining errors are pre-existing in test files this branch does not touch (lib/trigger/__tests__,lib/chats/__tests__,lib/credits/__tests__, …). - Red → green: mutation run (not committed) changed
p_owner: ownerIdtop_owner: accountIdinprocessGuestContext.ts; the two organization-routing cases failed (2 failed | 6 passed). Reverted → 8 passed. - Not verified: hosted deployment, live providers, production database, real Privy authentication, Chat UI (unchanged), hosted CONTEXT_GUEST_ENABLED rollout.
Out of scope / known gaps
- Campaign attribution capture on guest start and on the adopted request (separate field; later slice with migration + API + docs).
- Chat organization destination picker; the UI still claims into the personal account only.
- Live browser run of SCENARIOS S17/S32.
- No edits to
guest.test.ts/guestTransport.test.tsor any shared dispatcher, to stay conflict-free with sibling tickets.
🤖 Generated with Claude Code
Summary by cubic
Adds a fixture suite that locks the guest-claim workspace routing contract for recoupable/app#2145, exercising the real authorizeContextOwner with a mocked membership check. Verifies personal versus verified-organization adoption, idempotent re-claims, and that denied, malformed, expired, or foreign-claimed cases never reach dispatch or restart extraction. The Context README now documents the routing contract and that campaign attribution is deferred. No production code changes.