Skip to content

refactor(org-db): DRY the tier-column SELECT + row type (#2826)#2922

Merged
bokelley merged 1 commit into
mainfrom
bokelley/tier-columns-dry
Apr 23, 2026
Merged

refactor(org-db): DRY the tier-column SELECT + row type (#2826)#2922
bokelley merged 1 commit into
mainfrom
bokelley/tier-columns-dry

Conversation

@bokelley

Copy link
Copy Markdown
Contributor

Summary

Closes #2826. Follow-up to #2822/#2830 — the inner-tx membership-tier read was duplicating the SQL column list and row type that `resolveMembershipTier` already documented via its function signature. A future column that the resolver's inference needed to consider would have silently missed one of the two inline SELECTs.

What changed

  • `organization-db.ts` now exports:
    • `MembershipTierRow` — the resolver's input type, named so other modules can reuse it.
    • `MEMBERSHIP_TIER_COLUMNS` — tuple of column names, single source of truth.
    • `readMembershipTierFromClient(client, orgId, { forUpdate? })` — runs the SELECT with the shared column list against a caller-held pg client, parses the row, returns `MembershipTier | null`. Optional `forUpdate` when the caller needs to lock the org row too (the seat-check path uses this).
  • Two inline SELECT+resolve pairs replaced:
    • `organization-db.ts:380` (seat-check inside a tx) — uses `forUpdate: true`.
    • `member-profiles.ts:727` (`applyAgentVisibility` inner-tx tier re-read) — uses the default.

Test plan

  • `npm run typecheck` — clean
  • `npm run test:server-unit` — 1920 pass
  • `npm run test:unit` — 631 pass

No behavior change, pure DRY refactor.

🤖 Generated with Claude Code

`applyAgentVisibility` (inner-tx tier re-read from #2822) and the
seat-check path in organization-db duplicated the same SQL and row
shape against a pg client. A future column that `resolveMembershipTier`
needs to consider had to land in three places (the resolver's input
type, both SELECTs) or silently miss one.

- Export `MembershipTierRow` type + `MEMBERSHIP_TIER_COLUMNS` tuple
  as the single source of truth for the resolver's input shape.
  `resolveMembershipTier` now takes the named type.
- Export `readMembershipTierFromClient(client, orgId, { forUpdate? })`
  that runs the SELECT with the shared column list, parses the row,
  and returns `MembershipTier | null`. Optional `forUpdate` for
  callers that need to lock the `organizations` row too.
- Both inline call sites (`organization-db.ts` seat check,
  `member-profiles.ts` applyAgentVisibility) use the helper.

No behavior change. 1920 server + 631 root unit tests pass.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@bokelley bokelley merged commit cc4d067 into main Apr 23, 2026
12 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Inner-tx tier read duplicates resolveMembershipTier's column list

1 participant