Skip to content

fix(crud): preserve active session peer joined_at on re-add - #986

Closed
WizisCool wants to merge 3 commits into
plastic-labs:mainfrom
WizisCool:fix/preserve-active-peer-joined-at
Closed

WizisCool wants to merge 3 commits into
plastic-labs:mainfrom
WizisCool:fix/preserve-active-peer-joined-at

Conversation

@WizisCool

@WizisCool WizisCool commented Aug 6, 2026 •

Copy link
Copy Markdown
Contributor

Fixes #940.

Problem

When get_or_create_session re-adds a peer whose membership is still active (left_at IS NULL), the session-peer upsert currently resets joined_at to now(). peer_perspective search filters messages using message.created_at >= joined_at, so clients that re-add peers on startup can lose visibility of messages created before the latest restart, without an error.

Change

Preserve joined_at for active peers. Only reset it when a peer has actually left and rejoins (left_at IS NOT NULL). Existing observer-count logic, configuration handling, and left_at clearing are unchanged.

Tests

  • Active re-add preserves joined_at and the stored configuration.
  • A departed peer rejoining receives a new joined_at and incoming configuration.
  • peer_perspective search retains messages after an active re-add and starts a new window after a genuine rejoin.
  • The search regression fails on the pre-fix code.

Local validation against current main (including #955):

  • pytest tests/test_search.py tests/crud/test_session.py tests/crud/test_document.py: 39 passed
  • ruff check: clean

This PR supersedes #981, which encountered an unrelated full-suite CI cancellation in tests/test_session_allowlist.py after 1501 tests had passed. The new PR starts from the current main and uses a single clean commit.

Summary by CodeRabbit

  • Bug Fixes

    • Re-adding an active session peer now preserves existing membership history and message visibility.
    • Rejoining after leaving starts a new membership period, applies updated settings, and prevents access to messages from the earlier period.
    • Improved peer-perspective search behavior for active re-adds and leave-and-rejoin scenarios.
  • Tests

    • Added regression coverage for session membership updates and search visibility.

Fixes plastic-labs#940.

Preserve joined_at for active session peers during idempotent re-adds.
Only a peer that has left and rejoins starts a new membership window.
This keeps peer_perspective search visibility across client restarts.

The change includes regression coverage for membership timestamps,
configuration behavior, and peer_perspective search visibility.
@coderabbitai

coderabbitai Bot commented Aug 6, 2026 •

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 99ba75b1-31ad-480f-8a72-69dd5dba7704

📥 Commits

Reviewing files that changed from the base of the PR and between 715c755 and 9d2a47e.

📒 Files selected for processing (1)
  • tests/crud/test_session.py
🚧 Files skipped from review as they are similar to previous changes (1)
  • tests/crud/test_session.py

Walkthrough

The session-peer update preserves joined_at and configuration for active memberships. It refreshes both only when a departed peer rejoins. CRUD and perspective-search tests cover both membership paths.

Changes

Session peer re-add behavior

Layer / File(s) Summary
Conditional membership timestamp updates
src/crud/session.py, tests/crud/test_session.py
Active peers retain their original joined_at and configuration. Departed peers receive a new joined_at, cleared left_at, and updated configuration when they rejoin.
Perspective search regression coverage
tests/test_search.py
The test confirms that active re-adds preserve existing message visibility. A leave followed by rejoin starts a new visibility window.

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

Possibly related PRs

Poem

A rabbit keeps the joining date,
When active peers return to the gate.
Departed peers begin anew,
With fresh settings and timestamps too.
Old messages remain in view—
Hop, hop, the fix is true!

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely describes preserving active session peer joined_at during re-add operations.
Linked Issues check ✅ Passed The changes satisfy issue #940 by preserving active membership state, handling genuine rejoins, and testing perspective search boundaries.
Out of Scope Changes check ✅ Passed The implementation and regression tests directly address the linked issue objectives without unrelated code changes.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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
Contributor

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/crud/session.py (1)

1074-1076: 🎯 Functional Correctness | 🟠 Major | 🏗️ Heavy lift

Calculate the observer limit from the effective configuration.

The observer-limit query at Lines 1023-1046 excludes every peer in peer_names and counts the incoming configuration. This upsert now retains the stored configuration for active peers.

At a limit of one, re-add an active peer with stored observe_others=True and incoming observe_others=False, then add a new observer. The check permits one observer, but this upsert preserves the first observer and inserts the second observer.

Keep active peers in the persisted observer count. Count incoming configuration only for new or departed peers. Add this case to the observer-limit tests.

🤖 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/crud/session.py` around lines 1074 - 1076, Update the observer-limit
query and the SessionPeer upsert around the configuration case to calculate
counts from each peer’s effective configuration. Preserve stored configuration
for active peers when counting, while using incoming configuration only for new
or departed peers; ensure the limit check matches the persisted result. Add a
regression test covering limit one, re-adding an active observer with
observe_others disabled, then adding another observer.
🤖 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/crud/session.py`:
- Around line 1074-1076: Update the observer-limit query and the SessionPeer
upsert around the configuration case to calculate counts from each peer’s
effective configuration. Preserve stored configuration for active peers when
counting, while using incoming configuration only for new or departed peers;
ensure the limit check matches the persisted result. Add a regression test
covering limit one, re-adding an active observer with observe_others disabled,
then adding another observer.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 0ad643b0-865e-4bf4-870c-b684e1775a51

📥 Commits

Reviewing files that changed from the base of the PR and between 0bbeb3b and 60c9cd1.

📒 Files selected for processing (3)
  • src/crud/session.py
  • tests/crud/test_session.py
  • tests/test_search.py

@WizisCool

Copy link
Copy Markdown
Contributor Author

Static Analysis passes on #986. The FastAPI test workflow still fails at the same unrelated test as #981:

tests/test_session_allowlist.py::TestMessageCrudAllowlistIntersection::test_grep_messages_intersects_allowlist → concurrent.futures.CancelledError

The run completed with 1501 passed, 25 skipped, 1 error. The PostgreSQL service log also shows the allowlist test racing with database initialization (relation "public.message_embeddings" does not exist). No joined_at, peer_perspective, or changed-test failure is reported.

@eisene Could you please help rerun or assess this workflow failure? The implementation-specific tests pass locally (39 passed without an API key), and Static Analysis is green.

@WizisCool

Copy link
Copy Markdown
Contributor Author

Hi @akattelu — I noticed you reviewed and merged #983 after the CI issue was resolved there. Could you please take a look at #986 as well?

Static Analysis passes. The FastAPI workflow reaches 1501 passed / 25 skipped, then errors in the unrelated tests/test_session_allowlist.py::TestMessageCrudAllowlistIntersection::test_grep_messages_intersects_allowlist test with CancelledError; the service log also reports message_embeddings missing during database initialization.

The changed tests and local keyless validation are clean (39 passed). Could you advise whether this CI failure should simply be rerun or whether the allowlist test/database fixture needs a separate fix? Thanks.

@WizisCool

Copy link
Copy Markdown
Contributor Author

Follow-up to #987: I verified the additional PUT /sessions/{id}/peers path is affected as well. set_peers_for_session previously soft-deleted all active peers before the upsert, so the left_at CASE in #986 could still refresh joined_at on every replacement.

I addressed this in 715c755 by excluding peers present in the incoming set from the soft-delete update. Added regression coverage for the set-peers path:

  • active peer re-set preserves joined_at, left_at, and the existing configuration;
  • removed peer re-added later receives a fresh joined_at and new configuration.

Validation: 40 passed across session/search/document tests; ruff clean. This keeps the fix in one PR and covers both session initialization and PUT peers entry points.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

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 `@tests/crud/test_session.py`:
- Around line 125-128: In the test assertion around session_peer_stmt, remove
the unused first_joined_at binding by renaming it to _first_joined_at or
omitting it while preserving the existing first_left_at and first_config
assertions.
🪄 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: d326d45f-7e93-449a-9be1-ad5a6c954072

📥 Commits

Reviewing files that changed from the base of the PR and between 60c9cd1 and 715c755.

📒 Files selected for processing (2)
  • src/crud/session.py
  • tests/crud/test_session.py
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/crud/session.py

Comment thread tests/crud/test_session.py
@VVoruganti

Copy link
Copy Markdown
Member

Superseded by #1059, which landed both the joined_at CASE guard and the set_peers soft-delete exclusion. Thanks for the fix.

@VVoruganti VVoruganti closed this Sep 8, 2026
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.

[Bug] Re-adding an active session peer resets joined_at and hides perspective search results

2 participants