fix: race condition hardening across auth, counters, and data fetching #102

Merged
chihlasm merged 4 commits from fix/race-conditions-critical into main 2026-03-10 05:57:22 +00:00
chihlasm commented 2026-03-09 21:38:33 +00:00 (Migrated from github.com)

Summary

  • Atomic token operations: Refresh token rotation uses conditional UPDATE...WHERE revoked_at IS NULL to prevent concurrent replays. All single-use tokens (invite codes, password reset, email verification) use SELECT FOR UPDATE to prevent double-spend.
  • Atomic counters: Tree usage_count and tag usage_count increments/decrements use SQL-level expressions instead of Python read-modify-write, preventing lost updates under concurrent load.
  • Plan limit TOCTOU: Re-checks tree count after db.flush() to close the race window where two concurrent creates both pass the pre-check.
  • Double-submit guard: handleSaveDraft and handlePublish now check isSaving inside the handler, not just on the button disabled prop — prevents Ctrl+S from firing duplicate saves.
  • Stale response guards: SessionHistoryPage, TreeLibraryPage, and QuickStartPage discard out-of-order API responses when filters/search change rapidly.
  • Token refresh subscriber fix: Swap subscriber arrays before iterating so a throwing callback can't leave the queue in a dirty state.

Test plan

  • Register with an invite code — verify it works and can't be reused
  • Login/logout cycle — verify refresh token rotation works normally
  • Rapid filter switching on Session History and Flow Library — verify correct data displays
  • Ctrl+S spam in tree editor — verify only one save fires
  • Create trees up to plan limit — verify limit is enforced

🤖 Generated with Claude Code

## Summary - **Atomic token operations:** Refresh token rotation uses conditional `UPDATE...WHERE revoked_at IS NULL` to prevent concurrent replays. All single-use tokens (invite codes, password reset, email verification) use `SELECT FOR UPDATE` to prevent double-spend. - **Atomic counters:** Tree `usage_count` and tag `usage_count` increments/decrements use SQL-level expressions instead of Python read-modify-write, preventing lost updates under concurrent load. - **Plan limit TOCTOU:** Re-checks tree count after `db.flush()` to close the race window where two concurrent creates both pass the pre-check. - **Double-submit guard:** `handleSaveDraft` and `handlePublish` now check `isSaving` inside the handler, not just on the button `disabled` prop — prevents Ctrl+S from firing duplicate saves. - **Stale response guards:** SessionHistoryPage, TreeLibraryPage, and QuickStartPage discard out-of-order API responses when filters/search change rapidly. - **Token refresh subscriber fix:** Swap subscriber arrays before iterating so a throwing callback can't leave the queue in a dirty state. ## Test plan - [ ] Register with an invite code — verify it works and can't be reused - [ ] Login/logout cycle — verify refresh token rotation works normally - [ ] Rapid filter switching on Session History and Flow Library — verify correct data displays - [ ] Ctrl+S spam in tree editor — verify only one save fires - [ ] Create trees up to plan limit — verify limit is enforced 🤖 Generated with [Claude Code](https://claude.com/claude-code)
railway-app[bot] commented 2026-03-09 21:50:45 +00:00 (Migrated from github.com)

🚅 Deployed to the patherly-pr-102 environment in selfless-grace

Service Status Web Updated (UTC)
hopeful-liberation Success (View Logs) Web Mar 10, 2026 at 4:44 am
patherly Success (View Logs) Web Mar 10, 2026 at 4:43 am
<!-- railway-bot-comment-version=2 --> <!-- railway-project-id="22b9b58c-271b-42e5-a10e-6fdec8d00134" railway-project-name="selfless-grace" --> 🚅 Deployed to the [patherly-pr-102](https://railway.com/project/22b9b58c-271b-42e5-a10e-6fdec8d00134?environmentId=ee4e3fbc-1672-4d8d-8373-601c2e38aeed) environment in **[selfless-grace](https://railway.com/project/22b9b58c-271b-42e5-a10e-6fdec8d00134)** | **Service** | **Status** | **Web** | **Updated** (UTC) | | :--- | :--- | :--- | :--- | | hopeful-liberation | ✅ Success ([View Logs](https://railway.com/project/22b9b58c-271b-42e5-a10e-6fdec8d00134/service/e1db2ee3-d241-4f45-abe4-c9c5fdf483d5?id=855b6930-48ee-4aef-9710-51f09087889b&environmentId=ee4e3fbc-1672-4d8d-8373-601c2e38aeed)) | [Web](https://hopeful-liberation-patherly-pr-102.up.railway.app) | Mar 10, 2026 at 4:44 am | | patherly | ✅ Success ([View Logs](https://railway.com/project/22b9b58c-271b-42e5-a10e-6fdec8d00134/service/95f556ff-5264-4116-a0c2-618a2fc53ba4?id=d65c729e-cd63-4b00-bad5-ba08866437a3&environmentId=ee4e3fbc-1672-4d8d-8373-601c2e38aeed)) | [Web](https://patherly-patherly-pr-102.up.railway.app) | Mar 10, 2026 at 4:43 am |
Sign in to join this conversation.