Repository navigation
MEMBERS-IDENTITY-001 — Define authoritative membership and external-account state #81
Description
Officer impact: Officers will have one plain-language source for whether a person is a current member and what access should follow. No officer workflow becomes available from this design ticket alone.
Officer documentation: Update OFFICER_START_HERE.md, docs/officers/EVENTS_SHOP_MEMBERS.md, docs/officers/ACCESS_CONTINUITY.md, and docs/officers/SYSTEM_MAPS.md with the approved contract and text alternative when implementation lands.
Deployment evidence: None at issue update. Closure must distinguish contract/source changes, tests, merge, website publication, Firebase deployment, provider configuration, and production verification; no green workflow alone proves live membership behavior.
Problem
Website roles, dues/roster membership, Google access, WhatsApp access, and Strava connections can drift. Treating any external channel as a peer membership authority would create accidental access and discount grants.
Invariant
Firebase UID plus a server-verified current MPRC membership record is the only membership authority. Google, WhatsApp, and Strava are desired/observed projections and never grant membership, prices, or privileges.
Scope
- Define membership states and transitions for approval, renewal, expiry, suspension, reactivation, household/nonresident cases, and offboarding.
- Define an additive provider-link contract with non-secret metadata, restricted identifiers/tokens, consent, desired state, observed state, last reconciliation, error, and stable command ID.
- Define collision handling for duplicate email, changed email, Google credential already linked elsewhere, duplicate WhatsApp number, and duplicate Strava athlete.
- Define minimization, consent, retention, deletion, and access-revocation SLA.
- Define pure idempotent transition rules and migration from current role-only records.
Acceptance criteria
- No provider connection or matching email automatically grants club membership.
- Custom claims contain authorization only, not provider IDs, phone/profile data, or rosters.
- Duplicate, out-of-order, lapse, renewal, relink, disconnect, and partial-failure transitions are specified and tested.
- Other integration tickets consume this contract rather than inventing provider-specific membership logic.
- Migration is additive, dry-runnable, and preserves existing accounts.
Dependencies and sequencing
SEC-001 (#100) and the remaining AUTH-001 verified-email/claim protections are prerequisites for implementation. This issue defines the authoritative membership contract consumed by AUTH-002/AUTH-002A (#117), #114, and #115; those downstream issues are not prerequisites for defining or completing this contract. Align with AUTH-003 capability design before implementation.
Coordination
Parent: #79
Do not begin implementation until dependencies are complete, this issue is assigned, and a timestamped CLAIMED comment naming the agent and branch is posted. Creation of this ticket is not a claim. Preserve the parallel agent work tracked by stable IDs in GITHUB_ISSUES.md.
Activity
daliu commentedon Jul 12, 2026
PRODUCT REQUIREMENT (2026-07-12): membership/account state must be identity-provider-neutral. Members may create/sign into a Firebase account with email/password or Google through #109, but either method starts account-only/pending and never grants membership, discounts, member pricing, payment state, or admin authority. The #81 contract must model the same lifecycle and collision rules for both providers with plain-language states suitable for elderly/nontechnical members; provider metadata remains a minimal derived identity link, not membership evidence.
daliu commentedon Jul 12, 2026
Privacy dependency: #110 will inventory provider/member fields, purposes, access, vendor handling, retention-decision status, and request behavior. It must consume this issue's provider-neutral account/membership state and must not redefine membership authority. #110 is owner-blocked and unclaimed.
daliu commentedon Jul 12, 2026
Legacy-source dependency: #113 now owns the read-only inventory and disposition decision for the inaccessible Firebase project, the club-owned Firebase project, and active Google Forms/Sheets. This issue remains the authority for the target provider-neutral membership/account contract. Do not infer membership from email or build the roster import until #113 identifies the approved source categories and #81 defines their target state.
daliu commentedon Jul 13, 2026
PRODUCT DECISION (2026-07-12): the MPRC website/Firebase backend will hold the full authoritative membership record. Membership renews annually based on approved payment evidence. A stable membership record may exist without a website account; initial onboarding may be one-at-a-time as people create accounts, but association to a UID must be explicit, audited, and require authoritative verified Auth state—never a silent email-only role grant.
The #81 contract must now include annual term boundaries, plan/household/non-resident semantics, payment-evidence-to-term transitions, renewal/lapse/grace/reactivation, manual/off-platform confirmation authority, refund/dispute outcomes, account-independent member IDs, link collisions, and derived claim expiry/revocation. Do not invent unresolved prices/policy values.
Published children/consumers:
Historical import remains deferred behind #113; do not create BACKEND-002 yet.
daliu commentedon Jul 13, 2026
COORDINATION EDIT STARTED by Codex agent /root/membership-access at 2026-07-13T06:35:55Z. This is not an implementation claim. I am correcting only the circular dependency direction among #81, #115, and #117; product scope and status labels remain unchanged.
daliu commentedon Jul 13, 2026
COORDINATION EDIT COMPLETE at 2026-07-13T06:37:39Z. The circular edge was removed: #81 is now explicitly upstream of #114/#115/#117, while #100 and remaining AUTH-001 protections remain its implementation prerequisites. Status stays proposed; this is still unassigned and unclaimed for implementation.
daliu commentedon Jul 14, 2026
AUTH-001B / #196 source outcome merged through PR #198 as exact
a8801770e97cf21d81f3307f5893734115140d8f. Exact-head CI29297012945and post-merge CI29297116454passed all blocking jobs; the Rules emulator proof is 348/348 with a prior 30-case red proof. No protected release ran and Firebase Rules were not deployed, so the boundary is not live. This advances the Rules prerequisite only; Functions/Admin SDK authorization, mirror reconciliation, token refresh/revocation, deployment, and live readback remain open. Verification still grants no membership or role.daliu commentedon Jul 18, 2026
CLAIMED (scoped, source-only) at 2026-07-18T23:16:56Z by agent /root on branch
codex/issue-367-provider-link(child #367).Scope of this claim is only the provider-neutral external-account link + collision sub-contract as a pure, unused source module (
functions/membershipProviderLink.js) delivered as a draft PR for owner review — a design deliverable for #81, not its implementation. It grants no membership/role/authority, is imported by no runtime path, and changes no live behavior.This does not claim or complete #81 overall. #81's live implementation (Firestore wiring, custom-claim issuance, provider configuration, UID association) remains gated on #100 (closed) plus the remaining AUTH-001 Functions/Admin authorization protections noted at 2026-07-14, and stays with the owner. The membership state/term/entitlement side remains owned by
functions/membershipAuthority.jsand #114/#115; this slice only fills the provider-link + collision gap.Verification still grants no membership or role.
daliu commentedon Jul 18, 2026
CLAIMED (scoped, source-only) at 2026-07-18T23:41:29Z by agent /root on branch
codex/issue-370-consent-state(child #370).Scope of this claim is only the provider-neutral versioned consent-state derivation sub-contract as a pure, unused source module (
functions/membershipConsentState.js) delivered as a draft PR for owner review — a design deliverable for #81, not its implementation. It fills the consent-state gap that #367 (membershipProviderLink.js) explicitly deferred: given the latest recorded consent decision for a (provider, subject, scope) track and the policy version now in force, it derives the effective disposition (active/reaffirmation_required/withdrawn/not_consented). Every result carriesgrantsAuthority: false; policy versions are compared for equality only (no ordering/recency invented). It is imported by no runtime path and changes no live behavior.This does not claim or complete #81 overall, and does not cover consent capture (the append-only event ledger), withdrawal side effects, the retention/deletion/SLA matrix (#110), or provider-specific WhatsApp consent wiring (#87). #81's live implementation (Firestore wiring, custom-claim issuance, provider configuration, UID association) remains gated on #100 (closed) plus the remaining AUTH-001 Functions/Admin authorization protections noted at 2026-07-14, and stays with the owner.
Verification still grants no membership or role.
daliu commentedon Jul 18, 2026
CLAIMED (scoped, source-only) at 2026-07-18T23:56:47Z by agent /root on branch
codex/issue-373-claim-reconciliation(child #373).Scope of this claim is only the entitlement-to-authorization-claim reconciliation sub-contract as a pure, unused source module (
functions/membershipClaimReconciliation.js) delivered as a draft PR for owner review — a design deliverable for #81, not its implementation. It fills the gap thatmembershipAuthority.js(#208) defers ("custom claims, token refresh/revocation"): given an entitlement disposition + email-verification + the observed member/officer claims, it derivesaligned/grant_member/revoke_memberfail-closed, encoding #81's "custom claims contain authorization only" and the separation that membership never grants or revokes the officeradminrole. Every result carriesgrantsAuthority: falseandofficerRoleAffected: false. It writes no claim, mints/revokes no token, is imported by no runtime path, and changes no live behavior.This does not claim or complete #81 overall, and does not cover the live custom-claim write/refresh/revocation or token lifecycle (gated on the remaining AUTH-001/AUTH-003 Functions/Admin authorization protections), the officer-role grant path (#115,
setMemberRole), or the retention/deletion/SLA matrix (#110). #81's live implementation stays with the owner.Verification still grants no membership or role.
MEMBERS-IDENTITY-001C: provider-neutral external-account link + colli…
MEMBERS-IDENTITY-001D: provider-neutral versioned consent-state contr…
daliu commentedon Jul 22, 2026
Atomic P0 child #445 claimed on branch codex/issue-445-provider-link-lifecycle from exact base 3cbfb9e. Scope: pure, unused, versioned provider-link lifecycle/reconciliation state machine with optimistic revision, idempotent command handling, fail-closed relink/disconnect behavior, focused tests, and named source/future officer design notes. Excludes runtime exports, Firestore/rules, provider calls, migration, deployment, and production behavior. Parent #81 remains open.
daliu commentedon Jul 22, 2026
Dependency-clearing child #445 merged through PR #446 as exact main commit 3586733. Exact-main CI run 29913889737 passed all five jobs.
The child now supplies the pure unused provider-link lifecycle snapshot/reducer missing between #367 and future runtime work: canonical changed-command detection, optimistic revision and ordered reconciliation sequence, link/unlink/relink, collision evidence, definitive failure, outcome-unknown, unresolved-intent invalidation, and replacement only after confirmed unlink. Every record/verdict grants no authority, strict typed inputs contain no raw provider/member data, and no runtime imports it.
#81 remains open. No Firestore schema/Rules, Auth claim, provider call/configuration, persistence, cross-record uniqueness, migration, deployment, production data, or live behavior changed. The automatic PR preview was not a production publication; no protected release ran.
daliu commentedon Jul 22, 2026
Atomic P0 child #447 claimed by /root at 2026-07-22T11:08:25Z on branch codex/issue-447-consent-receipts from exact main 3586733. Scope is one pure, unused append-oriented consent-decision receipt/head contract that composes #370 and remains technical history only. It excludes policy/legal consent decisions, retention/deletion, actor/capture evidence, persistence, runtime, provider configuration, deployment, and live behavior. Parent #81 remains open.
daliu commentedon Jul 22, 2026
Dependency-clearing child #447 merged through PR #448 as exact main commit
35240fa84efcda935b88eacbb0685e32aeb8ef10. Exact-main CI run 29916594885 passed all five blocking jobs.The child supplies the missing pure, unused append-oriented technical consent-decision receipt/head seam between #370 and future persistence. It is bounded to one latest receipt, detects only current-head conflicts, composes #370 in a separate call, and grants no authority. It explicitly cannot authenticate a self-consistent rewritten head or prove informed/legal consent.
#81 remains open. No actor/capture evidence, policy/notice text, retention/deletion rule, persistence, durable all-history uniqueness, Firestore schema/Rules, Auth claim/token action, provider call/configuration, migration, deployment, production data, or live behavior changed. The automatic PR preview was not production publication; no protected release ran.
daliu commentedon Jul 22, 2026
Atomic P0 corrective child #451 claimed by /root at 2026-07-22T12:18:35Z on branch codex/issue-451-term-before-association from exact main 0b9960f. It corrects the pure unused authority reducer so term evidence and explicit UID association may occur in either safe order; unlinked entitlement remains fail-closed. Parent #81 remains open. No runtime, persistence, Rules, migration, provider, deployment, or live behavior is in scope.
daliu commentedon Jul 22, 2026
Dependency-clearing child #451 merged through PR #452 as exact main commit
937e336031ffe064aa120460327538aab0d45d30. Exact-main CI run 29920636668 passed all five jobs. The pure unused membership authority now supports term-first and link-first ordering while denying every unlinked record; a later one-time UID association preserves the latest term. Parent #81 remains open. No runtime/persistence, cross-record uniqueness, Auth claim, Rules, migration, deployment, provider, production-data, or live behavior changed.daliu commentedon Oct 15, 2026
October 15 direct owner decisions for the minimum member release:
First focused runtime source change is #737: canonical current-member admission for new free-event signup with expiry-safe cancellation and immutable retry. This does not complete #81/#115/#114 or activate real membership/payments.