Skip to content

feat(strava): serve strava-browser and transition export connections - #469

Open
Kahtaf wants to merge 4 commits into
tnunamak/legacy-automatic-replacement-1022from
kahtaf/strava-browser-binding
Open

Kahtaf wants to merge 4 commits into
tnunamak/legacy-automatic-replacement-1022from
kahtaf/strava-browser-binding

Conversation

@Kahtaf

@Kahtaf Kahtaf commented Oct 23, 2026 •

Copy link
Copy Markdown
Member

Desktop can only collect Strava from an export file. This PR lets the Personal Server serve strava.activities from strava-browser records, and adds an owner-authorized transition so existing Desktop export connections can switch without losing data. Desktop side: unity-surfaces #1865.

Stacking

This PR targets tnunamak/legacy-automatic-replacement-1022 (#465, itself stacked on #459), not main. Rebased onto #465 at a0d50ef8 (the runtime Unity dev pins since #1866), so this head (af9ff00b) adds only the two Strava commits. Unity #1865 pins it. Merge #459 and #465 first, then retarget this PR to main.

Desktop runtime build: kahtaf/strava-desktop-runtime @ b0dc78fd (this PR's four commits cherry-picked onto 4245639b, Unity dev's pin, which adds #471; no conflicts; lint, eslint, format, build clean; 355 files, 6554 passed, 1 expected fail, 2 skipped). Unity #1865 pins it.

1. strava-browser binding (core)

  • Adds a strava-browser profile bound to signed 0.1.35, with profile and provenance vendored byte for byte. It reuses the export binding's identity projection and adds start_date_local.
  • strava.coverage_diagnostics stays bound to the export method only.
  • Untagged stored strava.activities rows keep the original binding, as Claude's do, so adding a second profile introduces no new 409. Rows tagged by two producers are still refused.
  • A strava connection and a strava-browser connection that both hold activities return 409 ambiguous_connection; the two producers are never mixed.

2. Export-to-browser transition (server)

POST /pdpp/instances/:instance/transition-strava-browser with {expected_generation} follows the Claude transition (#444). In one SQLite transaction it saves the readable strava.activities and strava.coverage_diagnostics bodies, archives the export rows and binds strava-browser at N+1 (schema v15, pdpp_strava_transition). Replays are no-ops, stale export writers are fenced, and deletion or reset removes the archive. The server exports STRAVA_BROWSER_TRANSITION_CONTRACT = "strava-browser-transition-v1".

Account-mismatch rule (default, open for review). The saved activities stay authoritative until a browser run with a completion receipt for the new generation holds every saved activity id; nothing is deleted before then. Strava browser records carry no athlete id, so a completed run that misses ids is treated as a different account: the export is kept, and GET /pdpp/instances/:instance/strava-browser-transition returns account_mismatch (other values: none, pending, covered). Desktop shows that to the owner. After the handover, strava.activities follows the normal legacy view.

coverage_diagnostics. After a transition, the saved body is served frozen.

Mobile

Phones write strava-browser output as a legacy {activities} body, not {records}, and have no canonical connection. This change projects nothing for them and serves nothing twice.

Verification

  • npm run lint, npm run lint:eslint, npm run format:check: clean.
  • npm run build && npm test: 355 files, 6547 passed, 1 expected fail, 2 skipped.
  • New tests:
    • legacy-projection/strava-browser.test.ts
    • Strava stored-row cases in storage/legacy-projection.test.ts
    • Strava authority cases in server/.../legacy-scope-view.test.ts
    • server/.../strava-method-transition.test.ts, which covers: the archive, fencing and replay; refusal for other methods; no takeover without a completed run; mismatch keeping the export; takeover past a published export file; frozen diagnostics; and owner-only routes and status.
  • Schema-version assertions are bumped from 14 to 15.

Not proven: a live Strava account.

https://claude.ai/code/session_01T1dsXRYchsAfFBL3s9Pasy

@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Oct 23, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-10-23T18:53:01.674985Z f063c00 PR opened
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@Kahtaf
Kahtaf force-pushed the kahtaf/strava-browser-binding branch from f063c00 to 8abbe38 Compare October 23, 2026 19:07
@Kahtaf Kahtaf changed the title feat(core): serve strava.activities from strava-browser records feat(strava): serve strava-browser and transition export connections Oct 23, 2026
@Kahtaf
Kahtaf changed the base branch from main to tnunamak/legacy-automatic-replacement-1022 October 23, 2026 19:07
Bind signed strava-browser 0.1.35 to strava.activities with the export
binding's identity projection, plus start_date_local.
strava.coverage_diagnostics stays bound to the export method only.

Untagged stored strava.activities rows keep the original binding, so
adding a second profile adds no 409 for existing stored bodies. Rows
tagged by two producers still refuse. A strava and a strava-browser
connection that both hold activities refuse with ambiguous_connection;
no method transition is added.

Claude-Session: https://claude.ai/code/session_01T1dsXRYchsAfFBL3s9Pasy
Add an owner-only, one-way transition modelled on the Claude transition.
It snapshots the readable Strava bodies, archives the export rows and
binds strava-browser at the next generation (schema v15).

The saved activities stay authoritative until a browser run with a
completion receipt holds every saved activity id. A completed run that
misses ids keeps the export and reports account_mismatch through a new
status route. Saved coverage diagnostics stay readable and frozen.

Claude-Session: https://claude.ai/code/session_01T1dsXRYchsAfFBL3s9Pasy
@Kahtaf
Kahtaf force-pushed the kahtaf/strava-browser-binding branch from 8abbe38 to af9ff00 Compare October 24, 2026 01:17
Refuse a transition whose saved activities body has no readable ids,
and never count such a body as covered.

Claude-Session: https://claude.ai/code/session_01T1dsXRYchsAfFBL3s9Pasy
Replay, reset archive cleanup, unreadable refusal and a write racing
the snapshot.

Claude-Session: https://claude.ai/code/session_01T1dsXRYchsAfFBL3s9Pasy
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant