Skip to content

feat(sessions): contract session list to GSI-only once migration completes (issue #175 Phase 3)#678

Merged
philmerrell merged 1 commit into
developfrom
feature/session-metadata-static-sk-phase3
Jul 18, 2026
Merged

feat(sessions): contract session list to GSI-only once migration completes (issue #175 Phase 3)#678
philmerrell merged 1 commit into
developfrom
feature/session-metadata-static-sk-phase3

Conversation

@philmerrell

Copy link
Copy Markdown
Contributor

What & why

Final phase of the session-metadata static-sort-key migration (issue #175). list_user_sessions now 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 legacy S#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

  • Memoised per-process. The marker only ever goes unset→set (never back), so a migrated deployment pays no extra read after the first observation. A container that started pre-backfill picks up the flip on a later call.
  • Fails open + forker-safe. Any error reading the marker keeps dual-read, and the legacy branch is retained behind the marker, not deleted. Downstream/forked deployments that haven't run the backfill stay in dual-read — so contracting the read here can never blank an un-migrated sidebar.
  • GSI-error safety net. Even with the marker set, if the GSI query itself throws a transient 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

  • New 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.
  • 136 passed across 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 (lastMessageAt in 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

…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>
@philmerrell
philmerrell merged commit 3e32a78 into develop Jul 18, 2026
4 checks passed
@philmerrell
philmerrell deleted the feature/session-metadata-static-sk-phase3 branch July 18, 2026 00:58
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.

1 participant