feat(sessions): contract session list to GSI-only once migration completes (issue #175 Phase 3)#678
Merged
Conversation
…letes (issue #175 Phase 3) Final phase of the static-sort-key migration. list_user_sessions now reads the SessionRecencyIndex GSI alone once the Phase 2 backfill has set the migration- complete marker (PK=MIGRATION#session-sk, SK=STATE, complete=true) — the legacy S#ACTIVE# union branch is only queried until then. - The marker check is memoised per-process (the marker only ever goes unset->set, never back), so migrated deployments pay no extra read after the first observation; a container that started pre-backfill picks up the flip on a later call. - Fails open: any error reading the marker keeps dual-read. Downstream/forked deployments that haven't run the backfill stay in dual-read, so removing the legacy branch here can never blank an un-migrated sidebar. - Safety net: even with the marker set, if the GSI query itself errors (transient ValidationException/ResourceNotFound) the legacy branch is still queried, so a flaky index never returns an empty list. The legacy code path is retained behind the marker rather than deleted, per the downstream-safety design; a later release can drop it once all deployments report the marker set. Tests: dual-read when marker absent, GSI-only (legacy row excluded) when set, marker memoisation, and GSI-failure-falls-back-to-legacy-even-with-marker. Full sessions+routes+backfill+architecture suites: 136 passed. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What & why
Final phase of the session-metadata static-sort-key migration (issue #175).
list_user_sessionsnow contracts from the dual-scheme union down to a GSI-only read once the Phase 2 backfill has set the migration-complete marker (PK=MIGRATION#session-sk,SK=STATE,complete=true). The legacyS#ACTIVE#union branch is only queried until then.Completes the sequence: #666 (GSI) → #667 (union read) → #669 (fallback fix) → #673 (write path) → #677 (backfill). The backfill has been applied to dev and prod, so the marker is already set in both — this deploy flips them to GSI-only on the first list.
Behaviour & safety
ValidationException/ResourceNotFound, the legacy branch is still queried, so a flaky index never returns an empty list.The legacy path is kept behind the marker per the downstream-safety design; a later release can delete it outright once all deployments report the marker set.
Verification
TestListContractOnMarker(4): dual-read when marker absent, GSI-only (legacy row excluded) when set, marker memoisation, and GSI-failure-falls-back-to-legacy-even-with-marker.test_sessions_metadata+test_sessions(routes) +test_backfill_session_static_sk+ architecture import-boundaries.Impact
Pure read-path optimization — since prod is 100% migrated, the dual-read was already returning everything from the GSI branch; this just stops issuing the now-dead legacy query and drops the marker-read once memoised. No user-facing change. Ships via
backend.yml.Closes out issue #175
Root cause (
lastMessageAtin the sort key → per-turn row rotation → ghost rows /Failed to parse session item) is eliminated: static SK, sparse recency GSI, all rows migrated, ghosts deleted, dual-scheme scaffolding retired behind the marker.🤖 Generated with Claude Code