Commit graph

3 commits

Author SHA1 Message Date
Randall Stillwell
af10b162b7 docs(invites): mark Task-workspace-invites-and-roles done; capture design decisions
Status -> done. Acceptance criteria all checked off except the operator
smoke test (full invite -> accept across two browsers), which requires
a live dev stack. Added a 'design decisions captured here' section
covering (a) why caller role is derived from workspace_members not
from the resolve query, (b) the new tRPC errorFormatter that exposes
error.cause, and (c) why the invite dialog doesn't offer 'owner' role
even though the schema accepts it.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-02 10:35:42 -05:00
Randall Stillwell
820dae6510 docs(plans): split workspace-invites convoy into identity + invites + autocomplete
User pushed back on "strict email match in v1" — the right architectural
answer is multi-email identity (one users row owning multiple verified
emails), not a stopgap. Scaling the convoy accordingly:

1. Task-multi-email-identity (NEW, P1, foundation)
   - user_email_identities table (user_id, email lowercased, verified_at,
     source: primary | oauth:<provider> | manual)
   - Refactor ensureUserIdByEmail -> ensureUserIdByVerifiedEmail against
     the new table.
   - OAuth callback writes a source='oauth:<provider>' identity when the
     provider returns email_verified=true. Cross-user conflict rejects.
   - Profile UI: "Linked emails" section, read-only in v1.
   - Exports userOwnsEmail(userId, emailLower) for invite accept to call.

2. Task-workspace-invites-and-roles (existing, narrowed)
   - All the original spec.
   - Accept procedure calls userOwnsEmail() instead of comparing
     users.email directly. Mismatch renders an explainer page, not a
     silent accept.

3. Task-invite-recipient-autocomplete (NEW, P1, polish)
   - invites.suggestRecipient returns typed suggestions across four
     kinds: member / pending_invite / known_user / new_email.
   - Tenancy fence on known_user is the security-relevant assertion;
     test for it explicitly.
   - Combobox UI renders each kind with its own affordance.

Three follow-ups filed explicitly to keep this convoy PR-sized:
- Task-manual-email-verification (add an email outside OAuth)
- Task-disconnect-linked-email (destructive, needs last-verified guard)
- Task-account-merge (handle the legacy duplicate-users case)

Epic file refreshed with the new task table, follow-up table, and a
phase ordering note. Identity lands first because it touches the
sign-in path; invites and autocomplete can ship in their own PRs.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-02 10:02:38 -05:00
Randall Stillwell
778fe1d321 plans: scaffold daily-driver-finish, saas-hardening, agent-coordination
Three new plan trees that fill in the gaps surfaced during repo review.
Together they map out what remains between the current scaffold-with-stubs
state and a daily-usable, multitenant, agent-coordinated app.

* Plan-daily-driver-finish (P0): turn stubs into real data. Five tasks
  covering the lint/shared-types breakage, hardcoded dashboard mocks,
  AI-page setTimeout placeholder, post-signin landing decision, and a
  cross-browser collab smoke test against the deployed Hocuspocus
  instance.

* Plan-multitenant-saas-hardening (P1): everything multitenant needs
  beyond what Plan-multitenant-cursor-sync already covers. Invites and
  role management, soft-delete + append-only audit log, rate limits on
  the auth + mutation hot paths, and a Vitest + GitHub Actions test
  foundation so PRs can't ship red.

* Plan-agent-coordination (P2): the layer that makes a Task-*.md
  runnable, not just readable. Adds workflow_prompt with task -> epic
  -> plan inheritance, an agent_runs table for auditable sessions, and
  two new MCP tools (claim_task / complete_task) that replace the
  freeform update_object composition agents do today. Includes an
  intentionally-deferred Epic-optional-orchestrator that captures the
  Symphony-shaped runner as a decision point rather than an immediate
  build.

Each task is bead-scale (one focused Cursor session) with explicit
in-scope, out-of-scope, and anti-goal sections so a future agent can
pick up a single Task-*.md and start without scrollback context.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-01 23:52:22 -05:00