Skip to content

MEMBERS-IDENTITY-001H — Permit term evidence before account association #451

Description

@daliu

Officer impact: No live officer workflow changes. The future membership workflow will safely accept approved term evidence before a member creates a website account, while withholding website access until an officer explicitly associates the membership with the correct verified account.

Officer documentation: Update only the provider-neutral membership-authority future-state section and its Mermaid/text alternative in docs/officers/EVENTS_SHOP_MEMBERS.md. It must remain SOURCE ONLY, UNUSED and NOT AVAILABLE YET.

Deployment evidence: None at issue creation. Closure must report source, tests, merge, website publication, runmprc.com, Firebase, provider configuration, migration, production data, and live behavior separately. A green workflow or preview does not prove any external surface is live.

Parent: #81. Corrective dependency slice for #114 and #115.

Problem

The merged product contract intentionally separates a stable membership from its optional Firebase UID:

The shipped pure authority reducer currently makes that safe sequence impossible. record_term_decision rejects every unlinked record, and associate_account rejects every record whose term revision is nonzero. Existing composition tests hide the contradiction by linking first.

Atomic outcome

Correct the unused provider-neutral membership authority so account association and term decisions may occur in either safe order. An unlinked record remains valid but always receives not_entitled; later one-time explicit association preserves the latest term and allows the existing fixed entitlement derivation to evaluate it.

Invariant

Membership evidence and account identity are independent prerequisites. Their order never grants authority. Website membership requires both:

  1. exactly one explicit UID association; and
  2. an approved term whose explicit half-open bounds include the supplied time.

Missing association, wrong UID, pending/future/expired/suspended/ended term state, malformed input, stale revision, or conflicting command remains fail-closed.

Allowed transitions

  • create_membership -> associate_account -> record_term_decision remains valid and byte-compatible.
  • create_membership -> record_term_decision -> associate_account becomes valid.
  • Multiple monotonic term decisions may be recorded while unlinked; later association preserves the latest term.
  • Later term revisions may follow either ordering.
  • An exact retry of the latest association or term command remains a read-only identity result.

Rejected transitions and failure cases

  • stale, skipped, reordered, or changed command reuse;
  • a second/different UID association, including at the otherwise-correct record revision;
  • non-monotonic term revisions;
  • record revision overflow;
  • malformed/hostile records and commands;
  • any entitlement for an unlinked record or a nonmatching UID.

Compatibility and migration

  • Keep membershipAuthoritySchemaVersion === 1, all command/record/result fields, enums, fixed errors, and entitlement outputs unchanged.
  • Preserve every existing link-first record and sequence.
  • Change only the ordering grammar and the two ordering gates; retain the revision formula, exact-latest retry semantics, one-time UID rule, time bounds, hostile-input checks, and safe-integer protections.
  • No migration or backfill: this module and its sibling projectors are source-only, imported by no runtime path, and no authority snapshots are persisted.

RED proof and required tests

Before the source fix, add a focused test proving the current reducer throws when a valid revision-1 term command is applied to a fresh unlinked record.

Lock down:

  • both command orders produce equivalent canonical membership facts;
  • approved/current evidence while unlinked remains not_entitled, then becomes current only after the exact UID is associated;
  • pending, future, expired, suspended, and ended evidence never becomes current merely because an account is associated;
  • approved then suspended while unlinked preserves the latest suspended term through association;
  • exact latest retries remain read-only; changed reuse, stale/skipped/reordered commands, a second UID, and maximum-safe revisions fail closed;
  • the real manual-evidence, verified-payment, reversal, immutable-term-receipt, association, and account-status projection contracts compose with the corrected authority without inventing policy.

Use only synthetic identities and payment references. Run focused tests, full Functions lint/tests, Rules and commerce emulator suites, frontend tests/build, SPA tests, workflow/release/artifact/dependency checks, YAML parsing, and diff checks.

Exact owned paths

  1. functions/membershipAuthority.js
  2. functions/membershipAuthority.test.js
  3. Only necessary composition assertions in existing membership contract tests; do not edit their emitters or runtime modules.
  4. Only the named provider-neutral membership-authority section/diagram in SYSTEM_DESIGN.md.
  5. Only the matching future-state section/diagram in docs/officers/EVENTS_SHOP_MEMBERS.md.

Dependencies and coordination

Explicit non-goals

No runtime import/export, endpoint, Firestore schema/Rules, durable command journal, cross-record UID uniqueness, Auth claim write/token refresh, payment/provider verification, price/calendar/plan/refund policy, UI, migration, package/workflow, provider configuration, deployment, production data, or live behavior.

The reducer commands and term ledger still do not bind themselves to a persisted membership document. A future trusted transaction must bind the selected record, command, receipt, and audit; do not claim this source-only correction supplies that runtime boundary.

Claim protocol

Creation is not a claim. Before any repository edit, assign the issue, create and push a unique branch from exact current main, post CLAIMED by <canonical agent> at <UTC>; branch <branch>; exact base <SHA>, re-read live comments/labels, and move status:ready to status:in-progress. Earliest valid claim wins.

Metadata

Metadata

Assignees

Labels

area:authAuthentication and authorizationarea:membershipMembership lifecycle and reconciliationpriority:P0Launch blocker or urgent security risksize:SSmall focused issuetype:architectureArchitecture and contract designtype:reliabilityReliability and recoverytype:securitySecurity or privacy boundarytype:testingTest infrastructure and quality gates

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions