You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
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:
functions/membershipAssociation.js therefore requires an active, dues-confirmed membership before it authorizes association.
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:
exactly one explicit UID association; and
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.
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
functions/membershipAuthority.js
functions/membershipAuthority.test.js
Only necessary composition assertions in existing membership contract tests; do not edit their emitters or runtime modules.
Only the named provider-neutral membership-authority section/diagram in SYSTEM_DESIGN.md.
Only the matching future-state section/diagram in docs/officers/EVENTS_SHOP_MEMBERS.md.
This source-only correction is independent of owner-blocked retention, legacy-source, provider-console, deployment, and production decisions.
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.
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 remainSOURCE ONLY, UNUSEDandNOT 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:
functions/membershipAssociation.jstherefore requires an active, dues-confirmed membership before it authorizes association.The shipped pure authority reducer currently makes that safe sequence impossible.
record_term_decisionrejects every unlinked record, andassociate_accountrejects 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:
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_decisionremains valid and byte-compatible.create_membership -> record_term_decision -> associate_accountbecomes valid.Rejected transitions and failure cases
Compatibility and migration
membershipAuthoritySchemaVersion === 1, all command/record/result fields, enums, fixed errors, and entitlement outputs unchanged.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:
not_entitled, then becomes current only after the exact UID is associated;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
functions/membershipAuthority.jsfunctions/membershipAuthority.test.jsSYSTEM_DESIGN.md.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, postCLAIMED by <canonical agent> at <UTC>; branch <branch>; exact base <SHA>, re-read live comments/labels, and movestatus:readytostatus:in-progress. Earliest valid claim wins.