Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 11 additions & 4 deletions .github/lint-baseline.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,12 @@
"targets": [
"src"
],
"scannedFileCount": 120,
"scannedFileCount": 127,
"scannedFilesByExtension": {
".js": 12,
".jsx": 35,
".ts": 43,
".tsx": 30
".ts": 46,
".tsx": 34
},
"errorCount": 113,
"warningCount": 6,
Expand All @@ -31,7 +31,7 @@
".ts": 0,
".tsx": 6
},
"fingerprint": "sha256:b62532df877e1a1599273d0bdbf9876e75dae8be6e60ecadce601f854c283b6a",
"fingerprint": "sha256:67db6e465743580f63e5f1bdd359b60bceaa2217c4e754ea8902e84a85cf74ba",
"scannedFiles": [
"src/App.jsx",
"src/App.test.jsx",
Expand Down Expand Up @@ -65,6 +65,10 @@
"src/pages/account/Account.tsx",
"src/pages/account/MemberDirectoryProfile.test.tsx",
"src/pages/account/MemberDirectoryProfile.tsx",
"src/pages/account/RunnerCard.tsx",
"src/pages/account/RunnerConnections.test.tsx",
"src/pages/account/RunnerConnections.tsx",
"src/pages/account/RunnerProfileForm.tsx",
"src/pages/account/StravaCallback.tsx",
"src/pages/account/StravaSection.tsx",
"src/pages/activities/Activities.jsx",
Expand Down Expand Up @@ -118,6 +122,9 @@
"src/services/account/memberDirectorySearchService.ts",
"src/services/account/memberDirectoryService.test.ts",
"src/services/account/memberDirectoryService.ts",
"src/services/account/runnerConnectionContract.ts",
"src/services/account/runnerConnectionService.test.ts",
"src/services/account/runnerConnectionService.ts",
"src/services/accountEmail/accountEmailSender.test.ts",
"src/services/accountEmail/accountEmailSender.ts",
"src/services/analytics/analytics.test.ts",
Expand Down
18 changes: 18 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -190,3 +190,21 @@ jobs:
- run: npm ci --legacy-peer-deps --ignore-scripts
- name: Run Firestore rules tests against the emulator
run: npm run test:rules
- name: Install Functions dependencies for runner profile tests
run: npm --prefix functions ci --ignore-scripts
- name: Run runner profile persistence tests against the emulator
env:
REQUIRE_RUNNER_CONNECTION_PROFILES_EMULATOR: '1'
REQUIRE_RUNNER_CONNECTION_SERVICE_EMULATOR: '1'
REQUIRE_RUNNER_CONNECTION_RECOMMENDATIONS_EMULATOR: '1'
run: >-
npx --no-install firebase emulators:exec
--project demo-functions-test
--only firestore,auth
"npm --prefix functions run test:run --
--runInBand runnerConnectionProfiles.emulator.test.js
runnerConnectionService.emulator.test.js
runnerConnectionRecommendations.emulator.test.js"
- name: Run runner client HTTP transport tests
timeout-minutes: 5
run: node scripts/run-runner-transport-tests.cjs
21 changes: 21 additions & 0 deletions GITHUB_ISSUES.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,27 @@ The 2026-09-27 production-dependency audit remains nonzero: root 5 findings (1 l

## Backlog conventions

September 27 addition: [COMMUNITY-001 / #689](https://github.com/Run-MPRC/Run-MPRC.github.io/issues/689)
tracks owner-authorized opt-in runner connections. See [the implementation
record](./docs/RUNNER_CONNECTIONS.md) for current source and remaining integrated
acceptance cases. The draft source adds a tested private storage primitive and
member-confirmed 18+ with no DOB. A disabled callable factory now checks current
Auth and canonical membership before saves and recommendations, with bounded
daily windows, directed hide/block and fresh delivery checks. Approved membership
population, retention/account-deletion handling, persistent privacy-choice
management and integrated staging remain incomplete. The member interface now
has a tested form, exact preview, consent, withdrawal, recommendations and
this-visit privacy undo; its source gate remains literally false. This is
not a live feature or permission to
deploy additional Functions through the two-profile-Function release.
The latest source also adds mandatory actual-client HTTP tests against isolated
Auth/Firestore/Functions emulators. These do not verify token signatures or a
hosted browser pilot; local emulator success does not close #689.
A separate explicit local browser mode now connects the actual workspace/client
to those emulators and verifies save/reload, suggestions, block/undo, withdrawal
and nonmember denial with disposable fixtures. Normal app sign-in and hosted
acceptance remain unproven; production source gates are unchanged.

### Labels

- Priority: `priority:P0`, `priority:P1`, `priority:P2`
Expand Down
14 changes: 14 additions & 0 deletions IMPLEMENTATION_PLAN.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,20 @@ RUNTIME-001 [#691](https://github.com/Run-MPRC/Run-MPRC.github.io/issues/691) pr

## 1. Program outcome

Owner-authorized runner connections are tracked in [#689](https://github.com/Run-MPRC/Run-MPRC.github.io/issues/689).
The [implementation record](./docs/RUNNER_CONNECTIONS.md) distinguishes the initial
synthetic domain/persistence and disabled profile/recommendation-service tests
(including bounded windows, hide/block and delivery rechecks), and the disabled
member form/preview/recommendation UI, from outstanding canonical membership
population, retention/account-deletion approval, persistent privacy-choice
management, integrated staging and live-release work. Explicit 18+ self-attestation
is selected; no birth date is collected. The existing small profile deployment
scope is unchanged.
Actual client-to-local-HTTP transport now has an isolated synthetic rehearsal;
the actual member workspace/client also has a connected local browser rehearsal.
Normal whole-app sign-in, hosted integration and token/App Check verification
remain separate required checks because the Functions emulator bypasses signatures.

MPRC can open a race or merchandise item for sale only when the platform can:

- Prove one client request creates at most one active Checkout Session and business record.
Expand Down
35 changes: 35 additions & 0 deletions OPERATIONS_RUNBOOK.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,41 @@

This runbook explains how to operate the website, Firebase services, and Stripe integration safely. Unless a procedure is explicitly marked **available now**, it describes the **target** operating model and must not be represented as implemented or deployed. The implementation source of truth is [GITHUB_ISSUES.md](./GITHUB_ISSUES.md); any procedure whose owning issue is not `done` is unavailable for production use.

Runner connections (#689) are **NOT AVAILABLE YET**. The draft matching/storage
code is exercised only with synthetic local data and exports no new Cloud
Function. Do not collect profiles, run backfills or deploy these paths through
the existing two-profile-Function release. Read the
[officer review guide](./docs/officers/RUNNER_CONNECTIONS.md) for the selected
18+ self-attestation, evidence boundary and stop conditions. The disabled profile
service's current-account/membership checks pass local tests; approved membership
population, hosted Auth/App Check proof and a protected pilot remain
outstanding. The disabled recommendation service has local synthetic tests for
bounded daily windows, hide/block and fresh delivery checks. It adds no usable
officer tool; retention/account-deletion and privacy-choice recovery still need
approved procedures before a pilot. The member form, exact-card preview,
recommendations, withdrawal and this-visit privacy undo now pass synthetic UI
tests; the route and account link remain disabled by a literal source gate.
The browser demo used an in-memory fake client, not Firebase. It proves layout
and interface transitions, not hosted access or an integrated release.
The separate real-client HTTP rehearsal now uses the local Auth, Firestore and
Functions emulators. Engineers run `node scripts/run-runner-transport-tests.cjs`
with Node 20/Java 21 and the committed root/Functions installs; officers request
its evidence through the guide. The script supplies an isolated demo project,
empty CLI configuration and synthetic credentials and cleans up its own fixtures.
Never use the production Functions entry point for this test. The emulator
bypasses token signatures, so this does not replace hosted Auth/App Check proof.
The explicit `--browser` option now provides a connected local member-workspace
rehearsal with made-up accounts, not the app's normal sign-in flow. The printed
loopback URL is the only supported host. Its **End rehearsal and clean up** action
must report zero users/root collections and stop the services. See the
implementation record for the fixed scope and tested save/reload, privacy and
nonmember-denial behavior. Production source gates remain disabled.

The #689 frontend increment expands the lint inventory to 127 files: 12 JS,
35 JSX, 46 TS and 34 TSX. All seven new TypeScript files are finding-free;
the exact existing 113 errors and six warnings are unchanged. Only the inventory
and fingerprint are updated. This is not a clean-lint claim.

Club officers and backup maintainers should start with [OFFICER_START_HERE.md](./OFFICER_START_HERE.md). It converts this technical runbook into short request, approval, verification, access, and emergency steps without terminal commands.

### Node 22 profile release prerequisite — hosted behavior NOT AVAILABLE YET
Expand Down
32 changes: 32 additions & 0 deletions SECURITY.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,38 @@ This is both the repository security policy and the current engineering risk reg

The public informational website can continue operating with normal care. **Live race or merchandise payments should remain disabled until every P0 launch blocker below is closed and verified in a staging dress rehearsal.** The existing payment implementation is a useful prototype, but several correctness defects could mark unpaid transactions paid, lose payment callbacks, oversell race capacity, fail to reconcile late-payment links, or allow overly broad administrative access.

Runner connections [#689](https://github.com/Run-MPRC/Run-MPRC.github.io/issues/689)
remain source-only and unavailable. Their separate private storage paths deny
all browser access, including owner/admin access. Transactional saves require
versioned discovery consent and explicit 18+ self-attestation; withdrawal clears
both the card and affirmation. This is not age/identity verification and never
accepts a DOB. The disabled profile-service factory checks current verified
Auth, revocation and a bounded canonical membership read, with native App Check
and separate request limits; its callback tests do not prove hosted enforcement.
Source now bounds daily recommendations to five references, with fresh candidate,
consent and directed-exclusion checks before delivery; all locator, window and
exclusion records deny browser access. These point-in-time reads cannot recall
data already sent. The source-gated browser interface uses closed projections,
unchecked initial consent/age confirmation, exact previews, account/app-bound
state and identical-command recovery after uncertain saves. It clears suggestions
on blur, expiry or failed requests; that is not a substitute for server checks.
Approved membership population, retention/account-deletion approval, persistent
privacy-choice management and protected integration/release proof remain required; see the
[implementation record](./docs/RUNNER_CONNECTIONS.md).

The runner transport harness uses a fixed demo project, loopback-only client and
service network guard, disposable CLI settings and ephemeral synthetic Admin
credentials. It does not import the production Functions index or enable a
production capability. Local HTTP checks now use the actual browser client
modules, but the Functions emulator bypasses token-signature verification:
passing local missing-token/account/membership tests does not prove hosted Auth
or App Check enforcement, attestation, IAM or browser CORS behavior.
The explicit `--browser` rehearsal additionally exercises real browser-to-loopback
calls with the actual member workspace/client, not the normal app login route.
Its fixed local assets use exact Host/Origin checks, restrictive CSP and no-store
headers; only randomized synthetic fixtures are seeded and removed. This proves
the exercised local CORS behavior, not hosted CORS or cryptographic enforcement.

### Runtime prerequisite — source only, 2026-09-27

RUNTIME-001 [#691](https://github.com/Run-MPRC/Run-MPRC.github.io/issues/691) targets Node.js 22 without changing first-generation triggers, permissions, profile schema, deployment scope, or resource limits. [Google schedules Node 20 decommissioning for October 30, 2026](https://docs.cloud.google.com/functions/docs/runtime-support). Both protected-release CI reads now require the separate Node 22 backend/profile-persistence job, and provider verification rejects either profile Function unless its runtime is exactly `nodejs22`. The original Node 20 baseline checks remain required.
Expand Down
25 changes: 24 additions & 1 deletion SYSTEM_DESIGN.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,29 @@ RUNTIME-001 [#691](https://github.com/Run-MPRC/Run-MPRC.github.io/issues/691) pr

### Goals

The September 27 owner direction adds in-house, opt-in runner connections under
[#689](https://github.com/Run-MPRC/Run-MPRC.github.io/issues/689). The current
[implementation record](./docs/RUNNER_CONNECTIONS.md) contains a synthetic-tested
validator/ranker, private transactional storage and a disabled callable
factory with current Auth/canonical-membership reads, bounded daily suggestions
and directed hide/block controls. The member form, exact-card preview and
recommendation/privacy interface now have synthetic tests, but remain disabled
behind a literal source gate. No service is deployed. Approved membership
population remains a release
dependency; roles and officer-directory consent never replace it.
The selected 18+ policy is explicit member self-attestation,
with no DOB or verified-age claim; current membership remains independently
server-authoritative. Member-discovery consent is
separate from officer lookup. Payment processing, demographic matching and live
release remain separate decisions; no existing member is automatically enrolled.
The actual client also passes a separate Node/Web SDK-to-local-HTTP rehearsal
against Auth/Firestore/Functions emulators. It is not a rendered-browser pilot
or token-signature/App Check proof; hosted staging remains required.
A separate test-only browser entry now joins the actual member workspace and
client to those local emulators, with in-memory Auth and disposable fixtures.
Save/reload, suggestions, block/undo, withdrawal/reload and nonmember denial
worked; the production route and normal whole-app sign-in remain unexercised.

- Publish public club content, events, and merchandise.
- Support anonymous and signed-in race registration.
- Apply member pricing only when the server verifies a current member role.
Expand Down Expand Up @@ -799,7 +822,7 @@ The CommonJS module creates an account-independent revision-1 snapshot, records

This contract does not verify a person, payment, plan, evidence item, refund, dispute, or policy decision. It does not choose calendar-year versus anniversary terms, grace, prices, plan eligibility, retention, or legacy disposition. Its identifier grammar is not a semantic privacy classifier; a future trusted server must mint opaque values and establish every referenced fact. The last-command marker prevents only an immediate changed retry; command IDs are not a durable global replay registry. #451 changes only the accepted command ordering within schema version 1: existing link-first snapshots remain valid, and because no snapshot is persisted or runtime-adopted, this correction needs no migration or backfill. Durable cross-record UID uniqueness, full command replay history, append-only audit, Firestore schema/Rules, custom claims, token refresh/revocation, runtime authorization, any future migration, and deployment are later children behind #110, #113, #114, AUTH-003/ADMIN, and the protected release work.

The module is imported by no runtime or Functions index. It reads no clock or environment, calls no Firebase/Stripe/provider service, stores nothing, logs nothing, changes no current profile/role/claim, and cannot make #81, annual renewal, discounts, roster export, or officer membership tools available. Source tests and a merge are not Firebase deployment or live behavior proof.
The module itself reads no clock or environment, calls no Firebase/Stripe/provider service, stores nothing, logs nothing, changes no current profile/role/claim, and is absent from the Functions index. The disabled runner-profile service factory in #689 now composes it after a bounded transactional read of `memberships/{membershipId}`; local synthetic emulator tests prove only that consumer. Approved membership population, unique association and officer operations remain unavailable under #81/#114/#115. This does not make annual renewal, discounts, roster export or officer membership tools available. Source tests and a merge are not Firebase deployment or live behavior proof.

### 8.0b Officer manual off-platform dues-evidence command — SOURCE ONLY, UNUSED

Expand Down
Loading
Loading