Skip to content

fix(sessions): add non-destructive offline state.db recovery - #71586

Closed
helix4u wants to merge 1 commit into
NousResearch:mainfrom
helix4u:fix/state-db-offline-recovery
Closed

helix4u wants to merge 1 commit into
NousResearch:mainfrom
helix4u:fix/state-db-offline-recovery

Conversation

@helix4u

@helix4u helix4u commented Jul 25, 2026

Copy link
Copy Markdown
Collaborator

What does this PR do?

Adds a non-destructive offline recovery path for state databases that remain unusable after the existing in-place repair ladder.

The new hermes sessions recover command never opens the supplied source through SQLite. It copies the database and any WAL, SHM, or rollback-journal sidecars into a disposable working directory, reads canonical data from that copy, and rebuilds it into a separate current-schema database. Derived FTS indexes and migration metadata are recreated instead of copied.

Recovery refuses existing outputs and source aliases, fingerprints the source bundle before and after copying, and never installs the recovered database automatically. A successful result requires integrity, foreign-key, schema-version, canonical row-count, and FTS verification.

Before copying, the command checks available disk space. It reserves room for the complete source bundle, a full-size output allowance, and the larger of 256 MiB or 5 percent additional headroom. Work and output filesystems are checked independently when they differ.

Related Issue

No GitHub issue. Reported in Discord: https://discord.com/channels/1053877538025386074/1530627291120537771

This complements the in-place repair work merged in #43149 and #68653. It covers the remaining failure mode where canonical sessions and messages are readable from a disposable writable-schema connection, but repair_state_db_schema cannot restore the original database to a cleanly openable state.

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

  • Add hermes sessions recover with explicit source, output, inspection-only, work-directory, chunk-size, and JSON-report options.
  • Add hermes_cli/session_recovery.py for source isolation, sidecar-aware snapshots, disk-space preflight, bounded canonical-table copying, derived-index rebuilding, and final verification.
  • Add disposable-database tests covering malformed FTS schema recovery, source immutability, overwrite guards, insufficient-space refusal, required-table failure, and the real CLI/report path.

How to Test

  1. Run scripts/run_tests.sh -j 4 tests/hermes_cli/test_session_recovery.py tests/test_state_db_malformed_repair.py -q.
  2. Against a disposable damaged database, run python -m hermes_cli.main sessions recover --source damaged.db --output recovered.db --work-dir PATH.
  3. Confirm the command leaves damaged.db unchanged, creates recovered.db plus recovered.db.recovery.json, and reports complete only when integrity, counts, schema, and FTS verification pass.

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: Windows

Documentation & Housekeeping

  • I've updated relevant documentation (README, docs/, docstrings), or N/A: CLI help and module docstrings cover the new command
  • I've updated cli-config.yaml.example if I added/changed config keys, or N/A: no config changes
  • I've updated CONTRIBUTING.md or AGENTS.md if I changed architecture or workflows, or N/A: no workflow changes
  • 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: no model-tool changes

Screenshots / Logs

Focused local validation on Windows:

  • 25 tests passed, 0 failed, using the project parallel runner with four workers.
  • Ruff lint passed for all changed Python files.
  • Ruff formatting passed for the two new files.
  • Python compilation passed.
  • git diff --check passed.
  • Windows footgun scan passed for the origin/main diff.
  • Manual disposable-database recovery preserved the source hash and recovered all 8 sessions and 2,000 messages from the reproduced failure case.

Full-suite coverage is left to GitHub CI.

@helix4u
helix4u marked this pull request as ready for review July 25, 2026 21:11
@alt-glitch alt-glitch added type/feature New feature or request comp/cli CLI entry point, hermes_cli/, setup wizard area/sessions Session lifecycle, resume, persistence, history P2 Medium — degraded but workaround exists sweeper:risk-session-state Sweeper risk: may lose/corrupt/mis-associate session or context state labels Jul 25, 2026
@teknium1

Copy link
Copy Markdown
Collaborator

Merged via #71629 — your commit was cherry-picked onto current main with your authorship preserved in git log (a9b8128bcb).

Thanks for this one. It closes a real dead end: hermes sessions repair would exhaust its in-place ladder, print "keep state.db and the backup", and stop — leaving a user whose sessions had vanished with two files and nothing to run. The design decisions that made it mergeable as-is were the right ones: copying the sidecars before SQLite opens anything, rebuilding derived FTS indexes instead of copying them, fingerprinting the source bundle before and after, and never installing the result automatically.

Verified end to end against a genuinely unopenable database (25 sessions / 2500 messages, FTS shadow schema rewritten to reference a missing table, WAL mode):

  • source bundle sha256 byte-identical after recovery, every sidecar
  • 25/25 sessions and 2500/2500 messages recovered, integrity_check: ok, schema v23, zero FK violations
  • recovered database usable by the real SessionDB, FTS search returning hits
  • derived fts_rebuild_high_water correctly absent; real meta preserved
  • overwrite and source-alias guards both raise
  • real CLI recover and --inspect-only both rc 0

One follow-up commit on top (ec2a0f8c1e): the failed-repair branch now names your command — --inspect-only first, then --output — pre-filled with the backup path it just preserved. The recovery path existed but the affected user had no way to discover it. Covered by a CLI-surface regression test that drives the real sessions repair subprocess against an unrepairable database, sabotage-verified so it can't pass vacuously.

This is going straight to use on a reported incident: the next step there is an inspection-only pass against the user's preserved state.db.malformed-backup-*.

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

Labels

area/sessions Session lifecycle, resume, persistence, history comp/cli CLI entry point, hermes_cli/, setup wizard P2 Medium — degraded but workaround exists sweeper:risk-session-state Sweeper risk: may lose/corrupt/mis-associate session or context state type/feature New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants