Skip to content

Deal rooms and co-invest: join checks the deal, a Free investor can still leave, and co-invest is Institutional as the pricing page says (D614) #1307

Description

@guillaumelauzier

S1: Four gate fixes on deal rooms and co-invest. Decision D614. No migration. Found by #1165's audit (area 6).

What is true on main

  • Join. POST /api/deals/:id/dealroom/join (cloudflare-worker/src/routes/deals.ts) inserts a membership without checking that the deal exists, so a bad id still uses up a quota slot.
  • Leave. Every /api/deals/* route sits behind requireInvestorTier('professional') (cloudflare-worker/src/index.ts, INVESTOR_PRO_PREFIXES). An investor who drops to Free cannot call DELETE /api/deals/:id/dealroom/leave for the rooms they joined.
  • Co-invest. GET /api/funds/syndication (routes/funds.ts) checks only the role. The pricing page (frontend/src/pages/InvestorPricingPage.jsx) sells "Co-invest discovery" as Institutional only, yet any investor reads every listing.
  • Create. POST /api/deals takes any status. A status the table's CHECK does not allow becomes a 500.

The work

  1. Join answers 404 deal_not_found for a deal that does not exist, and writes nothing.
  2. Leave is reachable on any plan: exempt that one route from the Professional gate. The rest of /api/deals/* stays gated.
  3. Co-invest requires Institutional for investors. Use the same middleware and paywall shape (required: 'institutional'), so the client's paywall opens. Admins and partners pass, as today.
  4. Create refuses an unknown status with a 400 that lists the allowed ones, read from the same list the table allows.
  5. D614 records the four gates.

Files this task owns

  • cloudflare-worker/src/routes/deals.ts (join and create only)
  • cloudflare-worker/src/index.ts (the investor gate's mounts only)
  • cloudflare-worker/src/routes/funds.ts (the syndication route only)
  • new Worker tests
  • documentation/architecture/decisions/D614.md

Done when

  • Tests cover:
    • join refused for an unknown deal, using no quota;
    • a Free investor can leave a room but still cannot list deals;
    • co-invest answers 402 with required: 'institutional' for a Professional investor, and 200 for an Institutional investor, an admin and a partner;
    • create refuses a bad status with a 400.
  • Every new assertion is mutation-checked.

Generated by Claude Code

Activity

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    slot:S07Work queue of slot S07state:reviewDraft PR open; CI and review

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions