Skip to content

feat(admin): founding member audit trail#4016

Closed
bokelley wants to merge 1 commit into
mainfrom
claude/issue-4014-founding-member-audit-trail
Closed

feat(admin): founding member audit trail#4016
bokelley wants to merge 1 commit into
mainfrom
claude/issue-4014-founding-member-audit-trail

Conversation

@bokelley

@bokelley bokelley commented May 3, 2026

Copy link
Copy Markdown
Contributor

Closes #4014

Adds an audit trail for member_profiles.is_founding_member manual overrides so future admins can distinguish auto-grants (migration-driven) from manual grandfathering decisions.

What changed

Migration 465 adds four nullable columns to member_profiles:

Column Type Purpose
founding_member_source TEXT CHECK IN ('auto_pre_cutoff', 'manual_grandfather') Distinguishes auto-grant vs manual
founding_member_granted_at TIMESTAMPTZ When the status was set
founding_member_granted_reason TEXT Free-text rationale
founding_member_granted_by TEXT WorkOS user ID of granting admin (server-side only)

Backfills on migration run:

Both UPDATE statements are guarded with AND founding_member_source IS NULL for idempotency.

Admin PUT route (server/src/routes/member-profiles.ts:1851):

  • founding_member_granted_by and founding_member_granted_at stripped from request body (prevent spoofing)
  • When is_founding_member: true is set: defaults source to 'manual_grandfather' (backward compat for existing callers), blocks 'auto_pre_cutoff' via API (migrations only), captures granted_by = req.user!.id and granted_at = new Date() server-side

Non-breaking justification: All four new columns are nullable with no DEFAULT; existing INSERT/UPDATE paths are unaffected. The route-level validation is backward-compatible (missing source defaults to 'manual_grandfather').

Notes (from pre-PR review)

  • founding_member_granted_by stores a WorkOS user ID, consistent with the set_by_user_id pattern used elsewhere in this codebase (e.g. type_reclassification_log). If cross-referencing by email is needed, that's a follow-up.
  • Revocation tracking (setting is_founding_member: false) is not in scope for this issue — the issue asks only about grant provenance. Follow-up if needed.
  • Admin UI surfacing of founding_member_source is deferred per the issue ("optional, but cheap once the data is there").
  • Post-migration verification: SELECT id, founding_member_source FROM member_profiles WHERE is_founding_member = TRUE AND founding_member_source IS NULL should return 0 rows after migration 465 runs.

Pre-PR review

  • code-reviewer: approved — migration idempotency guard added, null-safety fixed, type consistency aligned (new Date() not .toISOString())
  • internal-tools-strategist: approved — Option A correctly scoped; revocation tracking and email denormalization noted as follow-ups, not blockers for this issue's stated scope

Triage-managed PR. This bot does not currently iterate on
review comments or PR conversation threads (only on the source
issue). To unblock:

  • Push fixup commits directly: gh pr checkout <num>
    fix → push.
  • Or re-trigger: comment /triage execute on the source
    issue.

See #3121
for context.

Session: https://claude.ai/code/session_01Gd19avtPzkW362ahfkpfhe


Generated by Claude Code

Adds four nullable columns to member_profiles to record who granted
founding member status, when, and why. Migration 465 backfills all
auto-granted rows (source=auto_pre_cutoff) and the one known manual
grandfather. Admin PUT route now captures granted_by/granted_at
server-side and blocks auto_pre_cutoff from being written via the API.

https://claude.ai/code/session_01Gd19avtPzkW362ahfkpfhe
@bokelley bokelley added the claude-triaged Issue has been triaged by the Claude Code triage routine. Remove to re-triage. label May 3, 2026
@bokelley

bokelley commented May 3, 2026

Copy link
Copy Markdown
Contributor Author

Superseded by #4018, which closed #4014 and is already merged on main (commit 7abeadb). Triage routine fired before #4018 landed; closing this duplicate.

@bokelley bokelley closed this May 3, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

claude-triaged Issue has been triaged by the Claude Code triage routine. Remove to re-triage.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Audit trail for member_profiles flag overrides (founding_member, etc.)

2 participants