Skip to content

MEMBERS-ROSTER-001 — Add officer-only member roster view and safe CSV export #116

Description

@daliu

Officer impact: Authorized officers can view the current membership roster on the website and download a Google-Sheets-compatible CSV without reading raw Firestore collections, handling secrets, or requesting a developer export.

Officer documentation: Update OFFICER_START_HERE.md, docs/officers/EVENTS_SHOP_MEMBERS.md, docs/officers/ACCESS_CONTINUITY.md, docs/officers/SYSTEM_MAPS.md and its text alternative, and the glossary. The procedure must state approved uses, minimum columns, safe storage/sharing, deletion, stop conditions, proof, and escalation.

Deployment evidence: None at issue creation. Closure requires tests, merged source, website publication and runmprc.com verification, Firebase deployment evidence, approved field/access configuration, a synthetic export inspection, and a backup-officer walkthrough. Until then roster export is NOT AVAILABLE YET.

Product decision

The complete membership system of record lives on the MPRC website/backend. Officers need an easy roster view and downloadable spreadsheet. The first implementation is an on-demand, Google-Sheets-compatible CSV generated from authoritative membership records—not a second, continuously synchronized spreadsheet database.

Named officer accounts are preferred for attribution. The club account may receive access only as a verified Firebase identity deliberately granted the same scoped capability, MFA, recent-authentication, and audit requirements. Email-string equality never grants access.

Problem

There is currently no member roster export. The Admin Members page directly reads all members/{uid} documents and shows account roles, not annual paid membership. Existing registration export code includes high-risk event/payment fields and lacks purpose-specific columns, recent authentication, MFA/capability checks, bounds, and durable export audit; it is not a safe template for a membership roster.

Atomic outcome

Add a server-generated, purpose-specific current-membership roster projection with:

  • a simple officer-only website view;
  • filters for approved term/status/plan fields;
  • an as-of timestamp and result count; and
  • an on-demand, Google-Sheets/Excel-compatible CSV download using only the approved minimum columns.

Security and privacy invariants

  • The server queries canonical membership terms/links and applies the current-term rule; the browser does not download a broad collection and filter it locally.
  • A scoped membership_roster_view/membership_export capability is required. Export additionally requires approved MFA/recent authentication and a stated purpose/reason.
  • Each export creates one append-oriented server-only audit event with actor UID/capability, purpose category, approved filter/profile, as-of time, row count, correlation ID, and outcome—never the exported rows.
  • The server selects one owner-approved column profile from DATA-001B — Inventory personal data and approve a minimization/retention matrix #110. Unknown/custom columns are rejected.
  • Default output excludes Auth UIDs, internal document IDs, custom claims, DOB, emergency contacts, addresses, waiver text/evidence, Stripe/provider IDs, payment credentials/details, OAuth tokens, arbitrary notes, invite links, and discount codes.
  • CSV formula prefixes are neutralized; Unicode/newlines/quotes are handled; rows/bytes/time are bounded or streamed safely; filenames contain no PII.
  • No roster is written to public/, static hosting, logs, analytics, issues, build artifacts, or a broadly readable Firestore document.
  • Downloads use a direct authenticated response or short-lived one-use capability with restrictive cache/referrer behavior; permanent public URLs are prohibited.

Acceptance criteria

  • An authorized officer can view current members and download the approved CSV from the website without terminal access.
  • Anonymous, member, unverified, wrong-capability, stale-auth, and non-MFA users receive generic denials and no account/roster enumeration.
  • The explicitly approved club account works only when it has a verified Firebase identity and scoped capability; removing that capability immediately removes access after safe token refresh/revocation.
  • Current, upcoming, expired, lapsed, suspended, unlinked, household, non-resident, and corrected records follow MEMBERS-IDENTITY-001 — Define authoritative membership and external-account state #81/MEMBERS-DUES-001 — Build server-authoritative annual membership checkout and renewal #114's approved inclusion rules and are labeled truthfully.
  • Exact column snapshots prove only DATA-001B — Inventory personal data and approve a minimization/retention matrix #110-approved data appears; unapproved/sensitive fields are absent even if added to source documents later.
  • Empty, filtered, maximum-size, Unicode, comma/quote/newline, formula-injection, duplicate, and mid-export change cases are deterministic and safe.
  • One successful or failed export attempt creates one minimal audit event; retries cannot multiply audit/side effects unexpectedly.
  • Logs, monitoring, analytics, errors, filenames, and audit contain no exported roster data.
  • The CSV opens correctly in Google Sheets and a common spreadsheet program using synthetic data.
  • A backup officer completes the documented path, stores/shares/deletes the synthetic file according to the approved procedure, and identifies when to stop/escalate.

Dependencies and coordination

Explicitly out of scope

  • A continuously synchronized Google Sheet, Google Drive upload, scheduled email attachment, or public/shareable roster URL. Any requested Drive delivery is a separate provider/configuration issue after this download flow is proven.
  • Bulk import, account linking, membership activation, dues checkout, role grants, or payment reconciliation.
  • Event check-in/emergency, finance, fulfillment, waiver, or registration exports owned by ADMIN-001D and related domain tickets.
  • Real-member data in tests, issues, screenshots, AI tools, or repository fixtures.

Claim protocol

Creation is not a claim. Do not edit implementation files until dependencies are complete, the issue is assigned, and a timestamped CLAIMED by <canonical agent> at <UTC>; branch <branch> comment is posted and re-read. Earliest valid claim wins. Owner approval of roster fields and access is required separately from an engineering claim.

Metadata

Metadata

Assignees

No one assigned

    Labels

    area:adminAdministrative workflowsarea:firebaseFirebase services and dataarea:membershipMembership lifecycle and reconciliationarea:privacyPersonal data, consent, minimization, retention, and privacy operationsarea:webWeb application and hostingpriority:P1High-priority follow-upsize:MMedium multi-file issuestatus:proposedDesigned but dependencies or decisions remaintype:featureProduct capabilitytype:operationsOperational setup or runbooktype:securitySecurity or privacy boundary

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions