Skip to content

fix(gateway): restore the state.db periodic maintenance reverted by #64 - #81

Merged
pai-scaffolde merged 2 commits into
mainfrom
fix/restore-79-state-db-maintenance
Aug 20, 2026
Merged

fix(gateway): restore the state.db periodic maintenance reverted by #64#81
pai-scaffolde merged 2 commits into
mainfrom
fix/restore-79-state-db-maintenance

Conversation

@pai-scaffolde

Copy link
Copy Markdown
Collaborator

What happened

While landing the stacked LSP backlog, PR #64 was replayed onto main
with the wrong merge base — its parent branch's tip instead of the
true fork point. Files that main gained after that branch forked
therefore presented as deletions in the 3-way merge, so merging #64
silently reverted #79:

  • gateway/run.py — the _run_state_db_maintenance_once() refactor undone (158 lines)
  • hermes_cli/config_defaults.py — housekeeping comment reverted
  • tests/gateway/test_state_db_periodic_maintenance.pydeleted (266 lines)

This matters: #79 is what keeps state.db pruning on the housekeeping
cadence rather than only at process startup — the fix for the 2026-08-15
disk-pressure incident where state.db reached 3 GB.

What this does

Re-applies #79 verbatim onto current main. The restored gateway/run.py
and test file are byte-identical to their landed state; max_clients and
the rest of the #63/#64 LSP work are preserved.

Audit

Every merge in this batch was compared against its original PR diffstat:

PR original (add/del/files) actual merge verdict
#63 1121/0/6 1121/0/6 match
#64 461/20/2 504/406/5 mismatch — this bug
#65 433/296/5 433/296/5 match

#64 was the only bad replay. git diff --diff-filter=D dd73d9c37 origin/main
confirms exactly one file was deleted repo-wide.

🤖 Generated with Claude Code

pai-scaffolde and others added 2 commits August 19, 2026 20:34
PR #64 was replayed onto main with the wrong merge base (its parent
branch's TIP rather than the true fork point). Files main had gained
after that branch forked therefore looked like deletions, so merging #64
silently reverted #79:

  * gateway/run.py — _run_state_db_maintenance_once() refactor undone
  * hermes_cli/config_defaults.py — housekeeping comment reverted
  * tests/gateway/test_state_db_periodic_maintenance.py — deleted

This re-applies #79 verbatim onto current main. The LSP work from #63
and #64 (including sessions.max_clients) is untouched. Audited: #63 and
#65 match their original diffstats exactly; #64 was the only bad replay.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: c905ca0dcc

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread gateway/run.py
Comment on lines +26059 to +26060
finally:
_adb.close()

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Avoid checkpointing the live WAL on every maintenance tick

When either automatic archive or prune is enabled, this hourly path always calls _adb.close(), even when the maybe_* methods immediately skip because min_interval_hours has not elapsed. SessionDB.close() unconditionally executes PRAGMA wal_checkpoint(TRUNCATE) for writable connections (hermes_state.py:2716-2752), so a gateway with a large or busy WAL can contend with active transcript writers every hour despite this change's stated goal of avoiding disruptive mid-traffic database maintenance. The periodic connection needs a close path that does not request a TRUNCATE checkpoint; the mock-only test currently cannot expose this real SQLite side effect.

AGENTS.md reference: AGENTS.md:L84-L87

Useful? React with 👍 / 👎.

Comment thread gateway/run.py
Comment on lines +26050 to +26056
_adb.maybe_auto_prune_and_vacuum(
retention_days=int(_sess_cfg.get("retention_days", 90)),
min_interval_hours=int(_sess_cfg.get("min_interval_hours", 24)),
min_vacuum_interval_days=int(
_sess_cfg.get("min_vacuum_interval_days", 30)
),
vacuum=False,

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Preserve a pending VACUUM after live pruning

When this live pass actually deletes sessions, maybe_auto_prune_and_vacuum() records last_auto_prune but cannot VACUUM because vacuum=False. The only later VACUUM path is startup, yet that helper either skips while this timestamp is fresh or sees pruned == 0 after the live pass already removed the rows, and it only vacuums when the same invocation reports pruned > 0 (hermes_state.py:9080-9121). Consequently a long-lived gateway—or one restarted within the configured interval—can keep vacuum_after_prune enabled while never returning the deleted pages to disk, leaving the multi-gigabyte file this change is intended to address. Record pending reclamation separately and consume it during the next safe startup pass.

AGENTS.md reference: AGENTS.md:L54-L57

Useful? React with 👍 / 👎.

Comment on lines +2649 to +2652
# per (roughly) min_interval_hours at CLI/gateway/cron startup, and
# on the resident gateway's hourly housekeeping sweep (per served
# profile, without VACUUM) — so a gateway that stays up for weeks
# keeps pruning on cadence instead of only at its next restart.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Update the cleanup guide for the live pruning cadence

This changes the retention contract from startup-only pruning to pruning by an already-running gateway, but the user guide still says sessions are pruned only at CLI/gateway startup (website/docs/user-guide/sessions.md:716, with the same statement in the Chinese guide at line 581). An operator can therefore enable sessions.auto_prune while planning for deletion at the next controlled restart, only to have the resident gateway irreversibly delete eligible history on its next housekeeping pass. Update the user-facing cleanup documentation alongside this new cadence.

Useful? React with 👍 / 👎.

@pai-scaffolde
pai-scaffolde merged commit 5280611 into main Aug 20, 2026
37 checks passed
@pai-scaffolde
pai-scaffolde deleted the fix/restore-79-state-db-maintenance branch August 20, 2026 03:42
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