Repository navigation
D614: deal-room join checks the deal, a Free investor can leave, co-invest is Institutional, create refuses an unknown status (#1307) - #1325
Merged
Conversation
…nvest is Institutional, create refuses an unknown status (#1307) - POST /api/deals/:id/dealroom/join answers 404 deal_not_found for a deal that does not exist, and writes no membership, so no quota slot is used. - The Professional gate on /api/deals/* (and the other investor-pro prefixes) is now requireInvestorTierExcept('professional'), which skips INVESTOR_PRO_EXEMPT: DELETE /api/deals/:id/dealroom/leave only. - GET /api/funds/syndication requires Institutional for investors, with the paywall's own 402 body (required: 'institutional'); admins and partners pass. - POST /api/deals refuses a status outside DEAL_STATUSES with a 400 that lists the allowed ones, instead of a 500 from the CHECK. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Q6MrbLAt5ETsDnEUFNzVSQ
|
Preview: https://studioos-pr-1325.guillaumelauzier.workers.dev (built from d15c12c) The pull request's SPA build on a Worker with no bindings: pages and deep links work, |
guillaumelauzier
marked this pull request as ready for review
October 6, 2026 23:20
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Objective
This fixes four gates on deal rooms and co-invest, found by #1165's audit (area 6). Decision D614. No migration.
Closes #1307
Implementation
POST /api/deals/:id/dealroom/joinnow reads the deal first. If no deal has that id, it answers 404deal_not_foundand writes nothing. Before, a bad id inserted a membership row that counted againstinvestor_dealroom_max.index.ts(INVESTOR_PRO_PREFIXES) is nowrequireInvestorTierExcept('professional'). It skips the requests listed inINVESTOR_PRO_EXEMPT, which has one entry:DELETE /api/deals/:id/dealroom/leave.middleware/requireInvestorTier.ts, beside the gate it wraps. That file is not on the issue's owned list. Putting it there lets the test mount the gate exactly asindex.tsdoes.index.tsitself changes only the gate's mount lines.GET /api/funds/syndicationnow callsuserMeetsInvestorTier(user, 'institutional'), the same checkrequireInvestorTiermakes, and refuses withinvestorUpsell('institutional').error: 'investor_tier_required',required: 'institutional') is the onePaywallModalalready opens on.POST /api/dealsrefuses a status outsideDEAL_STATUSESwith a 400unknown_statusand anallowedlist.DEAL_STATUSESis the listPUT /:idalready validates against, and the test checks it equals the CHECK inschema_baseline.sql.applied.Left open, on purpose.
POST /api/dealsstill acceptsrejected, because the table allows it and the issue asked only to refuse unknown statuses./draftandPUT /:idboth send a pass throughPOST /:id/pass, which requires a reason (task #127). Whether create should do the same is a separate call. D614 records it as not changed.Files changed
cloudflare-worker/src/routes/deals.ts: the join deal check; the create status checkcloudflare-worker/src/index.ts: the investor gate's mounts userequireInvestorTierExceptcloudflare-worker/src/middleware/requireInvestorTier.ts:INVESTOR_PRO_EXEMPT,requireInvestorTierExceptcloudflare-worker/src/routes/funds.ts: the syndication tier checkcloudflare-worker/test/dealroom_coinvest_gates_d614.test.ts: newcloudflare-worker/test/lp_money_authz_d370.test.ts: its free-tier LP now expects the 402; the listings are read by the Institutional GPdocumentation/architecture/decisions/D614.mdTesting
npm run build: exit 0.npm run test:drift > drift.log 2>&1; echo EXIT=$?: EXIT=0.index.tsmounts it, and is still refused listing, joining and aPOSTto the leave path;required: 'institutional'for Free and Professional investors; 200 for Institutional, admin and partner; 403 for a founder;allowedequals the baseline CHECK, and an allowed or missing status still creates.deals.ts,funds.ts, the middleware andindex.ts.main, the new test fails.Risks
wrangler.tomlchange, no frontend change.requireAuth.Dependencies
Found by #1165. Touches D370's syndication route (its role gate is unchanged).
Agent
S07 · Claude Code
Review requested
guillaumelauzier (owner)
🤖 Generated with Claude Code
https://claude.ai/code/session_01Q6MrbLAt5ETsDnEUFNzVSQ
Generated by Claude Code