Skip to content

🎯 Decouple invitations from organizations (referral-ready platform invitations) #3808

@PierreBrisorgueil

Description

@PierreBrisorgueil

Epic β€” Decouple invitations from organizations (referral-ready)

Two invitation systems were colliding: the platform signup-gate (#3715, beta invite-only) and org membership email-invite. We fully decouple them.

Model: "invite a contact to the platform" (token + invitedBy + beta-gate eligibility) becomes a standalone optional invitations module depending only on auth. org becomes roster-of-existing-accounts only (add/roles/remove + join-requests). invitations βŠ₯ org β€” they communicate via userId. Referral (#5) is schema-ready (invitedBy + invitation.accepted event), credit-grant logic deferred.

Spec: infra/docs/superpowers/specs/2026-06-09-invitations-org-decouple-design.md
Plan: infra/docs/superpowers/plans/2026-06-09-invitations-org-decouple.md (audited on 4 axes + a Codex second-opinion pass folded in)

⚠️ Ordering constraint: Phase 1 (land #3715 byte-identical to Trawl's deployed state) MUST merge before Phase 2 (module extraction), otherwise the next Trawl /update-stack produces a 3-way conflict on auth.controller.js.

Locked decisions: no auto-verify of invite-created accounts Β· lowercase + case-insensitive unique index on users.email Β· cap TOCTOU overshoot accepted + documented Β· add-to-org β†’ PENDING with source:'owner_add' (E15 β€” never confused with a join-request) + persistent pending-list in Account + transient snackbar.

Phases (each = its own PR via /feature β†’ /verify β†’ /dev:verify-qa β†’ /dev:pull-request-finalize):

  • P1 β€” Land feat(auth): invite-only / capped signup gateΒ #3715 signup-gate on master (byte-identical) Β· Node
  • P2 β€” Extract invitations module + eligibility-hook inversion (E13 query token) Β· Node
  • P3 β€” Edge-case hardening (E2 two-phase claim, E3 CI email index, E6 no auto-verify, E8 soft-revoke, E9) Β· Node
  • P4 β€” Remove org email-invite (Node) + migration (org-owned) Β· Node
  • P5a β€” org.addMember(userId) + source discriminator + consent Β· Node
  • P5b β€” Org add-member UI + persistent pending-invitations list + snackbar Β· Vue
  • P6 β€” invitations Vue module + account/admin tabs + gap-fixes Β· Vue
  • P7 β€” Remove org email-invite (Vue) Β· Vue
  • P8a β€” Referral hook backend (referredBy schema + invitation.accepted) Β· Node
  • P8b β€” Referrals account view scaffold Β· Vue
  • P9 β€” Propagate downstream + run prod migrations (Trawl) Β· downstream

Sub-issues are linked below and tracked by the Sub-issues progress bar.


πŸ›‘ Fable review corrections (2026-06-10, code-verified vs origin/master)

  • πŸ›‘ feat(auth): invite-only / capped signup gateΒ #3715 is ALREADY MERGED on origin/master (the spec/plan were authored from a local checkout 115 commits behind). f0e7a83c is an ancestor; all modules/auth/**/*invitation* + auth.signupCapacity.js + gate hunks are present; trawl_node master auth.controller.js is hash-identical. β†’ P1 (πŸ”§ Land #3715 signup-gate on master (byte-identical)Β #3809) collapses to a verify-no-op. The "Node backend not on master / front-ahead-of-back" premise is FALSE.
  • πŸ”΄ Casing bug across P5a: membership status is stored lowercase (PENDING:'pending'); any === 'PENDING' check (incl. the prescribed pre('validate') consent guard) is INERT β€” use the MEMBERSHIP_STATUSES.PENDING constant everywhere.
  • πŸ”΄ P2 must ship a /api/auth/invitations deprecation alias kept until P6 repoints the Vue store (else the live admin tab breaks for ~4 PRs).
  • Before executing ANY phase: git fetch && git checkout origin/master and re-derive file/line refs (local tree is stale; org signup-join flow was reworked β€” domain-match auto-join removed β†’ suggestedJoin).

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions