Multiplexed housekeeping: one profile's failure no longer skips the rest or leaks its home (salvage #110405) - #120268
Merged
Conversation
Contributor
૮ >ﻌ< ა ci reviewran on 8511560 — fix(gateway): one served profile's failing chore no longer s debug infoCI timingsCI timings · View report · View jobWall time 5m54s vs 6m42s (-11.9%). 4 job(s) slower, 8 faster,
|
…rest _for_each_served_profile ran a housekeeping body once per served profile with no boundary between them, and _housekeeping_chore only catches at the tick level. One profile's unreadable store or broken .env therefore ended the loop, and every profile after it lost its state.db archive/prune, curator, skill-sync and MCP reconcile pass on every tick. That state is reachable: _init_session_db tolerates a failed launch store and keeps running, and hermes serve defers each served profile's auto-archive to this loop (#117746), so a satellite behind a broken launch store had no sweeper at all. Each profile now gets its own try/except, logged at debug like _housekeeping_chore. Co-authored-by: Baris Sencan <b.sencan@equalsmoney.com>
austinpickett
force-pushed
the
austin/fix/statedb-housekeeping-isolation
branch
from
September 23, 2026 14:12
132afcb to
8511560
Compare
Closed
1 task
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.
One gap left in the multiplexed gateway's per-profile housekeeping after #117746 and #118006 moved every served profile's state.db sweep onto that loop.
What changed
gateway/run_profile_reconcile.py::_for_each_served_profilegives each served profile its owntry/except. Before this, one profile's unreadable store or broken.envended the loop, and_housekeeping_choreonly catches at the tick level. Every profile after the failing one lost its state.db archive/prune, curator, skill-sync and MCP-reconcile pass on every tick. This can happen in practice:_init_session_dbtolerates a failed launch store and keeps running, andhermes servehands every served profile's auto-archive to this loop (state.db residuals: serve stands down under a live gateway, recovery survives the first resume, import refuses a held deleted database #117746 C1). A satellite sitting behind a broken launch store had nothing sweeping it. Failures log at debug, like_housekeeping_chore._profile_runtime_scopeunwinding fix (a failed secret hydration leaving the profile's home installed) landed onmainindependently as 6aaa1c4 while this was open, so it was dropped on rebase. The regression test for it is kept, since this loop makes that path reachable on every tick.Tests
Two cases added to
tests/gateway/test_housekeeping_profile_scope.py. Both use real stores and the real housekeeping loop:test_a_failing_profile_does_not_strand_the_profiles_after_it: the launch store raises inacquire(), and the served profile B must still be swept.test_profile_scope_setup_failure_restores_the_callers_home: secret hydration raises, and the caller's home and secret scope must be restored.Red on
origin/mainbefore the scope fix landed, with both production files reverted:The second line comes from a different test in the same worker. The leaked override carried profile B's home into it. With only the isolation fix applied, the scope test still fails with
profiles/b == .hermes.tests/gateway/test_housekeeping_profile_scope.pytests/gateway+tests/tui_gatewayfile that references_profile_runtime_scope/_for_each_served_profile/ housekeeping (66 files)ruff,check-windows-footguns.py,check_no_tmp_literals.py,check_compat_pointers.py,git diff --checkNot changed
GATEWAY_HEALTH_URLrung. state.db residuals: serve stands down under a live gateway, recovery survives the first resume, import refuses a held deleted database #117746 already makes serve stand down when_check_gateway_runningsays a gateway owns the store. Under one-backend-per-host, serve is a legitimateSessionDBwriter anyway, since Desktop chat turns write through it. The close hazard that made a stray writable open dangerous is handled by the WAL lock guard (state.db WAL survives sibling closes and stray lock cancellation on every runtime (Desktop chat dies after update; salvage #109758) #110544 / state.db WAL lock guard follows the handle lifecycle: reopen, checkpoint-vs-close, fd reuse (review of #110544) #110872), not by this gate. Merged onto current main, the fix(dashboard): fail closed on unknown gateway ownership, isolate per-profile maintenance #110405 head also fails two existing main tests (test_web_server_auto_archive_gateway_lock.py,test_web_server_auto_archive_profile_config.py).try/except. The boundary sits in the shared loop, so every profile-scoped chore gets it.Refs #100896
Supersedes #110405. Thanks @isair: the broken-launch-store isolation case comes from your review rounds on that PR (you're co-author on the commit). Your scope-unwinding fix is also on
mainnow, via 6aaa1c4.Rebased onto main 2026-09-23:
test_housekeeping_profile_scope.py7/7 pass; the isolation test fails on main'srun_profile_reconcile.pyand passes with the fix.