Skip to content

fix(sessions): read stale profile schemas without migration - #79200

Closed
cipher416 wants to merge 1 commit into
NousResearch:mainfrom
cipher416:fix/79029-stale-profile-session-schema
Closed

fix(sessions): read stale profile schemas without migration#79200
cipher416 wants to merge 1 commit into
NousResearch:mainfrom
cipher416:fix/79029-stale-profile-session-schema

Conversation

@cipher416

@cipher416 cipher416 commented Aug 5, 2026

Copy link
Copy Markdown

What does this PR do?

Fixes cross-profile session listing for dormant profiles whose databases predate newer sessions columns.

The Desktop sidebar reads other profiles through read-only SessionDB connections. Those connections intentionally cannot migrate a stale schema, but the listing queries unconditionally selected and ordered by newer columns such as last_activity_at and profile_name. The resulting per-profile query error was swallowed by the aggregation layer, leaving that profile's history absent until another code path opened the database writable and migrated it.

This change keeps read-only access non-mutating. It discovers the columns that actually exist, falls back to started_at when activity metadata is unavailable, and limits compact projections to live columns. Writable connections retain their existing migration behavior.

Related Issue

Fixes #79029

Type of Change

  • 🐛 Bug fix (non-breaking change that fixes an issue)
  • ✨ New feature (non-breaking change that adds functionality)
  • 🔒 Security fix
  • 📝 Documentation update
  • ✅ Tests (adding or improving test coverage)
  • ♻️ Refactor (no behavior change)
  • 🎯 New skill (bundled or hub)

Changes Made

  • hermes_state.py: cache the live sessions schema for read-only connections and route listing queries through schema-aware recency handling.
  • hermes_state_common.py: allow recency SQL helpers to fall back when last_activity_at is unavailable.
  • hermes_state_portability.py: build schema-aware list and compact-projection queries.
  • tests/test_hermes_state.py: add a regression test covering rich, sidebar, gateway, search, and Telegram-style listing paths against a stale schema, while asserting the database is not migrated.

How to Test

  1. Create a sessions database, then remove last_activity_at and profile_name to simulate a dormant profile created by an older Hermes version.
  2. Open it through SessionDB(read_only=True) and exercise the supported session-listing paths.
  3. Verify the stored session is returned and the schema remains unchanged.
  4. Run scripts/run_tests.sh tests/test_hermes_state.py -q.

Checklist

Code

  • I've read the Contributing Guide
  • My commit messages follow Conventional Commits (fix(scope):, feat(scope):, etc.)
  • I searched for existing PRs to make sure this isn't a duplicate
  • My PR contains only changes related to this fix/feature (no unrelated commits)
  • I've run pytest tests/ -q and all tests pass
  • I've added tests for my changes (required for bug fixes, strongly encouraged for features)
  • I've tested on my platform: Linux container, Python 3.11

The focused state suite passes: 179 passed. The full locked-dependency suite has six reproducible environment-sensitive failures outside the changed session-state code (macOS launcher harness, Termux/container detection, Camofox port assumptions, GNU sort probing, and Vercel doctor environment detection), so the full-suite checkbox is intentionally left unchecked.

Documentation & Housekeeping

  • I've updated relevant documentation (README, docs/, docstrings) — or N/A
  • I've updated cli-config.yaml.example if I added/changed config keys — or N/A
  • I've updated CONTRIBUTING.md or AGENTS.md if I changed architecture or workflows — or N/A
  • I've considered cross-platform impact (Windows, macOS) per the compatibility guide — or N/A
  • I've updated tool descriptions/schemas if I changed tool behavior — or N/A

Screenshots / Logs

scripts/run_tests.sh tests/test_hermes_state.py -q
179 passed

@cipher416
cipher416 marked this pull request as ready for review August 5, 2026 08:19
@alt-glitch alt-glitch added type/bug Something isn't working P2 Medium — degraded but workaround exists comp/desktop Electron desktop app (apps/desktop/*) sweeper:risk-session-state Sweeper risk: may lose/corrupt/mis-associate session or context state labels Aug 5, 2026
@spfcraze

spfcraze commented Aug 5, 2026

Copy link
Copy Markdown
Contributor

This was generated by AI during triage.

A note on the description:

The summary says the recency fallback is updated_at, but the sessions table has no such column — _sql_session_last_active falls back to started_at.

The sessions schema declares started_at REAL NOT NULL and no updated_at; the helper's docstring states "then fall back to started_at". As a wording that matches the code: "falls back to started_at when activity metadata is unavailable".


Checked against de15545 — the tip of fix/79029-stale-profile-session-schema when this was written — and 1be70d6, main at the same moment.

@cipher416

Copy link
Copy Markdown
Author

Thanks — good catch. I updated the PR description to say started_at; the implementation and regression test already use that fallback.

@egilewski

Copy link
Copy Markdown
Contributor

looks mergeable

Read-only profile databases with older sessions schemas now remain usable: session listing, gateway listing, search, and Telegram listing avoid selecting columns that are absent until that profile is opened writable and migrated.

Security evidence:

  • trust boundary: read-only cross-profile state is queried without granting migration or write authority.
  • source/sink/invariant: live sessions columns are captured once and constrain both projected columns and recency SQL expressions.
  • current-main reproduction: an unmigrated database failed on all four affected list/search surfaces with missing-column errors.
  • PR-head or patch-replay validation: the exact head passed the legacy-schema regression and the complete Hermes state test file.
  • positive/negative cases: migrated and unmigrated schemas preserve the same session and message results without mutating the read-only database.
  • residual bypass search: all changed recency and compact-column call sites were checked for missing-column references.
  • reviewer validation: the state, portability, and common SQL helpers were reviewed; no cross-profile data or write-authority bypass was identified.

Signed: GPT-5.6-luna-max in Codex

@alt-glitch alt-glitch added comp/gateway Gateway runner, session dispatch, delivery area/sessions Session lifecycle, resume, persistence, history area/profiles Multi-profile isolation, HERMES_HOME scoping sweeper:risk-compatibility Sweeper risk: may break existing users, config, migrations, defaults, or upgrades labels Aug 6, 2026
@jquesnelle

Copy link
Copy Markdown
Collaborator

Appreciate the contribution! Closed by #80797

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area/profiles Multi-profile isolation, HERMES_HOME scoping area/sessions Session lifecycle, resume, persistence, history comp/desktop Electron desktop app (apps/desktop/*) comp/gateway Gateway runner, session dispatch, delivery P2 Medium — degraded but workaround exists sweeper:risk-compatibility Sweeper risk: may break existing users, config, migrations, defaults, or upgrades sweeper:risk-session-state Sweeper risk: may lose/corrupt/mis-associate session or context state type/bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Desktop session sidebar shows empty after hermes update until a new message triggers a session event

5 participants