Skip to content

feat(session): add accidental-leave grace period - #199

Merged
scotej merged 3 commits into
mainfrom
feat/session-leave-grace-190
Aug 5, 2026
Merged

feat(session): add accidental-leave grace period#199
scotej merged 3 commits into
mainfrom
feat/session-leave-grace-190

Conversation

@scotej

@scotej scotej commented Aug 5, 2026

Copy link
Copy Markdown
Owner

Summary

  • keep the remaining participant's room alive for the existing 20-second grace period after a signed Leave, not only after transport loss
  • let the person who accidentally left rejoin from the report while preserving accurate auto, peer, and eviction end reasons
  • add deterministic regression coverage and reconcile the onboarding copy and canonical session documentation

Validation

  • npm run test — 1,088 passed
  • npm run build
  • npm run lint
  • npm run check-tokens
  • npm run check-strings
  • npm run check-migrations
  • npm run check-stories
  • npm run check-contrast
  • npm run format:check
  • npm run build-storybook
  • npm run check-a11y — 318 passed
  • cargo test — 118 passed across unit/integration suites
  • cargo fmt --check
  • cargo clippy
  • cargo deny check
  • git diff --check

Manual validation

Not machine-walked across two desktop instances. The state transition is covered with a deterministic fake scheduler for signed Leave, expiry attribution, and rejoin-before-expiry.

Closes #190

Summary by CodeRabbit

  • New Features

    • Added a 20-second recovery window whenever a session becomes empty, including after deliberate departures.
    • Participants can rejoin eligible sessions before the deadline and resume with their existing role and session state.
    • Rejoin controls now automatically disable when the recovery window expires.
  • Bug Fixes

    • Improved session-end tracking for accidental disconnects, deliberate departures, and full-room eviction.
    • Clarified session guidance and rejoin eligibility.

Copilot AI lite review requested due to automatic review settings August 5, 2026 13:14

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@coderabbitai

coderabbitai Bot commented Aug 5, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@scotej, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 39 minutes

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 05713c21-b60d-464f-bea7-007a40f3f1a7

📥 Commits

Reviewing files that changed from the base of the PR and between b7ce486 and a1caad4.

📒 Files selected for processing (1)
  • src/routes/Home.tsx
📝 Walkthrough

Walkthrough

The PR adds a 20-second grace period for empty-room departures. It updates end-reason attribution, stores rejoin deadlines, preserves host roles during rejoin, and updates the report, tests, and documentation.

Changes

Session grace window and rejoin flow

Layer / File(s) Summary
Grace timer and end-reason attribution
src/features/session/lifecycle.ts
Empty rooms now receive a 20-second grace period. Expiry stages auto for unexplained absence and peer for fully explained departures. Eligible auto and user endings receive a rejoin deadline.
Rejoin state and role-preserving join flow
src/stores/sessionStore.ts, src/features/session/join.ts, src/features/session/index.ts
The store validates rejoin credentials before the deadline. rejoinSession restores the stored host role.
Deadline-aware report rejoin action
src/routes/Home.tsx, src/features/session/Report.tsx
The home route passes rejoin credentials and the deadline. The report disables rejoin after the deadline.
Grace and rejoin validation
tests/unit/session-grace.test.ts, tests/unit/session-end-reason.test.ts, tests/integration/session.test.ts
Tests cover signed-departure grace, deadline expiry, host-role preservation, and peer-cap enforcement.
Lifecycle documentation and copy
ARCHITECTURE.md, PLAN.md, ISSUES.md, src/strings.ts
Documentation describes the shared grace period, end reasons, local leave, and rejoin rules.

Estimated code review effort: 3 (Moderate) | ~25 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Peer
  participant wireSessionRoom
  participant sessionStore
  participant Home
  participant Report
  participant rejoinSession
  Peer->>wireSessionRoom: Leave or disconnect
  wireSessionRoom->>sessionStore: Store end reason and rejoin deadline
  Home->>sessionStore: Request valid rejoin credentials
  sessionStore-->>Home: Return credentials before deadline
  Home->>Report: Provide rejoin callback and deadline
  Home->>rejoinSession: Rejoin with stored role
  rejoinSession->>wireSessionRoom: Restore session lifecycle
Loading

Possibly related PRs

  • scotej/studyvis#80: Both PRs modify session lifecycle, grace handling, departure attribution, and rejoin behavior.
  • scotej/studyvis#75: Both PRs modify session re-entry and reporting paths.
🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Description check ⚠️ Warning The description explains the behavior and validation, but it omits the required compatibility surfaces, gates, and merge-style sections from the repository template. Add the template's required headings, tick applicable manual-test, compatibility, gate, and merge-style items, and state any skipped checks or non-applicable surfaces.
Docstring Coverage ⚠️ Warning Docstring coverage is 50.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly identifies the primary change: an accidental-leave grace period for sessions.
Linked Issues check ✅ Passed The implementation and tests satisfy issue #190 by adding offline rejoin grace after participant leave and preserving end-reason attribution.
Out of Scope Changes check ✅ Passed The code, tests, documentation, and copy changes directly support the session grace-period and rejoin objectives in issue #190.
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
✨ Simplify code
  • Create PR with simplified code
  • Commit simplified code in branch feat/session-leave-grace-190

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/routes/Home.tsx`:
- Around line 502-506: Make the Rejoin action expiry-aware in the Home flow:
track the end time or 20-second grace deadline for auto and user endings, pass
it into Report, and ensure late clicks are disabled or rejected before
joinSession is called. Update the onRejoin wiring around handleRejoin to enforce
this deadline, and add fake-timer coverage verifying late clicks after both auto
and user endings do not reconnect.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 916f69c2-e200-4f75-89c9-baf4a24a0e54

📥 Commits

Reviewing files that changed from the base of the PR and between 3b72f10 and 38c0ee0.

📒 Files selected for processing (8)
  • ARCHITECTURE.md
  • PLAN.md
  • src/features/session/Report.tsx
  • src/features/session/lifecycle.ts
  • src/routes/Home.tsx
  • src/stores/sessionStore.ts
  • src/strings.ts
  • tests/unit/session-grace.test.ts
📜 Review details
⏰ Context from checks skipped due to timeout. (8)
  • GitHub Check: Installer (macOS)
  • GitHub Check: Installer (Windows)
  • GitHub Check: Rust (Linux)
  • GitHub Check: Rust (macOS)
  • GitHub Check: Rust (Windows)
  • GitHub Check: Frontend
  • GitHub Check: Analyze (rust)
  • GitHub Check: Analyze (javascript-typescript)
🧰 Additional context used
📓 Path-based instructions (5)
src/**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

src/**/*.{ts,tsx}: All design-token values—colors, spacing, fonts, radii, shadows, motion, and z-indexes—must come from src/design/tokens.ts; do not use raw hex values, arbitrary px, or inline cubic-bezier values.
User-facing toast and notification copy must live in src/strings.ts; prefer centralized strings for JSX text and aria-label values.
Maintain WCAG AA contrast for every text/background pairing in both themes, do not convey information by color alone, and honor the global reduced-motion kill switch; new motion must be gated by default.
Treat peer wire formats and identity derivation as cross-version contracts; coordinate changes so older builds and existing stored data remain compatible.
Every component and feature component must have a Storybook story.

Files:

  • src/strings.ts
  • src/routes/Home.tsx
  • src/features/session/Report.tsx
  • src/stores/sessionStore.ts
  • src/features/session/lifecycle.ts
**/*.{ts,tsx,rs}

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.{ts,tsx,rs}: Never add telemetry; the application is local-only. Never instruct users to paste model files or BIP39 mnemonics into an AI chat service.
Add comments only when the reason is non-obvious; identifiers should carry the meaning and code should read top-to-bottom.
Maintain scope discipline: do not refactor adjacent code while implementing a feature, add abstractions for hypothetical needs, or expand a bug fix beyond the bug.

Files:

  • src/strings.ts
  • src/routes/Home.tsx
  • src/features/session/Report.tsx
  • tests/unit/session-grace.test.ts
  • src/stores/sessionStore.ts
  • src/features/session/lifecycle.ts
**/*

📄 CodeRabbit inference engine (CLAUDE.md)

Use one focused change per commit, with a Conventional Commit subject such as feat:, fix:, chore:, docs:, or ci:; PRs are squash-merged.

Files:

  • src/strings.ts
  • PLAN.md
  • src/routes/Home.tsx
  • ARCHITECTURE.md
  • src/features/session/Report.tsx
  • tests/unit/session-grace.test.ts
  • src/stores/sessionStore.ts
  • src/features/session/lifecycle.ts
**/*.md

📄 CodeRabbit inference engine (CLAUDE.md)

Do not create new documentation files unless explicitly requested; update canonical documentation, CHANGELOG.md, or ISSUES.md when justified.

Files:

  • PLAN.md
  • ARCHITECTURE.md
tests/**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

Use Vitest for unit and integration tests; component tests are not currently supported because the harness uses node-env without RTL/jsdom, so component behavior belongs in Storybook and axe-core checks.

Files:

  • tests/unit/session-grace.test.ts
🔇 Additional comments (8)
src/features/session/lifecycle.ts (1)

424-428: LGTM!

Also applies to: 451-465, 478-503, 507-513, 534-541, 560-564

src/stores/sessionStore.ts (1)

15-19: LGTM!

src/routes/Home.tsx (1)

275-277: LGTM!

src/features/session/Report.tsx (1)

88-93: LGTM!

src/strings.ts (1)

162-162: LGTM!

PLAN.md (1)

62-62: LGTM!

ARCHITECTURE.md (1)

752-776: LGTM!

tests/unit/session-grace.test.ts (1)

177-177: LGTM!

Also applies to: 193-224, 304-305

Comment thread src/routes/Home.tsx

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/routes/Home.tsx (1)

510-515: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Hide Rejoin when no recovery deadline exists.

At Line 510, a solo user end still receives handleRejoin. buildLeaveHandler() stores rejoinDeadline: null when no peer joined. Line 515 converts that value to undefined, and Report treats an undefined deadline as available. The enabled button then calls handleRejoin, but getRejoinRequest() returns null and the click has no effect.

Require a non-null rejoinDeadline before passing onRejoin.

Proposed fix
           onRejoin={
-            sessionEndedBy === 'auto' || sessionEndedBy === 'user'
+            rejoinDeadline !== null &&
+            (sessionEndedBy === 'auto' || sessionEndedBy === 'user')
               ? handleRejoin
               : undefined
           }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/routes/Home.tsx` around lines 510 - 515, Update the onRejoin condition in
the Report props to require rejoinDeadline to be non-null alongside
sessionEndedBy being 'auto' or 'user'; otherwise pass undefined, while
preserving the existing rejoinDeadline fallback and handleRejoin behavior when a
recovery deadline exists.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@src/routes/Home.tsx`:
- Around line 510-515: Update the onRejoin condition in the Report props to
require rejoinDeadline to be non-null alongside sessionEndedBy being 'auto' or
'user'; otherwise pass undefined, while preserving the existing rejoinDeadline
fallback and handleRejoin behavior when a recovery deadline exists.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 882522d4-9325-4e28-9c06-93b4aae3b8d6

📥 Commits

Reviewing files that changed from the base of the PR and between 38c0ee0 and b7ce486.

📒 Files selected for processing (10)
  • ISSUES.md
  • src/features/session/Report.tsx
  • src/features/session/index.ts
  • src/features/session/join.ts
  • src/features/session/lifecycle.ts
  • src/routes/Home.tsx
  • src/stores/sessionStore.ts
  • src/strings.ts
  • tests/integration/session.test.ts
  • tests/unit/session-end-reason.test.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/strings.ts
📜 Review details
⏰ Context from checks skipped due to timeout. (7)
  • GitHub Check: Installer (macOS)
  • GitHub Check: Installer (Windows)
  • GitHub Check: Rust (macOS)
  • GitHub Check: Rust (Windows)
  • GitHub Check: Rust (Linux)
  • GitHub Check: Frontend
  • GitHub Check: Analyze (rust)
🧰 Additional context used
📓 Path-based instructions (5)
src/**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

src/**/*.{ts,tsx}: All design-token values—colors, spacing, fonts, radii, shadows, motion, and z-indexes—must come from src/design/tokens.ts; do not use raw hex values, arbitrary px, or inline cubic-bezier values.
User-facing toast and notification copy must live in src/strings.ts; prefer centralized strings for JSX text and aria-label values.
Maintain WCAG AA contrast for every text/background pairing in both themes, do not convey information by color alone, and honor the global reduced-motion kill switch; new motion must be gated by default.
Treat peer wire formats and identity derivation as cross-version contracts; coordinate changes so older builds and existing stored data remain compatible.
Every component and feature component must have a Storybook story.

Files:

  • src/features/session/index.ts
  • src/routes/Home.tsx
  • src/features/session/Report.tsx
  • src/features/session/join.ts
  • src/stores/sessionStore.ts
  • src/features/session/lifecycle.ts
**/*.{ts,tsx,rs}

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.{ts,tsx,rs}: Never add telemetry; the application is local-only. Never instruct users to paste model files or BIP39 mnemonics into an AI chat service.
Add comments only when the reason is non-obvious; identifiers should carry the meaning and code should read top-to-bottom.
Maintain scope discipline: do not refactor adjacent code while implementing a feature, add abstractions for hypothetical needs, or expand a bug fix beyond the bug.

Files:

  • src/features/session/index.ts
  • tests/unit/session-end-reason.test.ts
  • src/routes/Home.tsx
  • tests/integration/session.test.ts
  • src/features/session/Report.tsx
  • src/features/session/join.ts
  • src/stores/sessionStore.ts
  • src/features/session/lifecycle.ts
**/*

📄 CodeRabbit inference engine (CLAUDE.md)

Use one focused change per commit, with a Conventional Commit subject such as feat:, fix:, chore:, docs:, or ci:; PRs are squash-merged.

Files:

  • src/features/session/index.ts
  • ISSUES.md
  • tests/unit/session-end-reason.test.ts
  • src/routes/Home.tsx
  • tests/integration/session.test.ts
  • src/features/session/Report.tsx
  • src/features/session/join.ts
  • src/stores/sessionStore.ts
  • src/features/session/lifecycle.ts
**/*.md

📄 CodeRabbit inference engine (CLAUDE.md)

Do not create new documentation files unless explicitly requested; update canonical documentation, CHANGELOG.md, or ISSUES.md when justified.

Files:

  • ISSUES.md
tests/**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

Use Vitest for unit and integration tests; component tests are not currently supported because the harness uses node-env without RTL/jsdom, so component behavior belongs in Storybook and axe-core checks.

Files:

  • tests/unit/session-end-reason.test.ts
  • tests/integration/session.test.ts
🔇 Additional comments (9)
src/features/session/lifecycle.ts (1)

277-281: LGTM!

Also applies to: 397-397, 439-439, 500-506, 517-517, 543-544, 569-569

src/stores/sessionStore.ts (1)

23-28: LGTM!

Also applies to: 78-81, 130-132, 148-149, 164-164, 184-184, 215-215, 326-344

src/features/session/join.ts (1)

13-36: LGTM!

Also applies to: 52-58

src/features/session/index.ts (1)

2-2: LGTM!

Also applies to: 12-12

src/features/session/Report.tsx (1)

88-97: LGTM!

Also applies to: 117-124, 156-156, 265-265, 279-279, 291-291, 321-334, 513-518

src/routes/Home.tsx (1)

54-54: LGTM!

Also applies to: 87-91, 278-305

tests/integration/session.test.ts (1)

198-198: LGTM!

Also applies to: 290-309

tests/unit/session-end-reason.test.ts (1)

6-7: LGTM!

Also applies to: 89-120

ISSUES.md (1)

439-439: LGTM!

@scotej
scotej merged commit be16631 into main Aug 5, 2026
19 checks passed
@scotej scotej mentioned this pull request Aug 10, 2026
5 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Better session longevity

2 participants