Skip to content

Multiplexed gateway housekeeping runs per profile scope; parked MCP servers stop flooding the error log (#115713, #116700, salvage #115775) - #116744

Merged
teknium1 merged 3 commits into
mainfrom
salvage/115775-mcp-reprobe-scope
Sep 20, 2026
Merged

teknium1 merged 3 commits into
mainfrom
salvage/115775-mcp-reprobe-scope

Conversation

@teknium1

Copy link
Copy Markdown
Collaborator

On a multiplexed gateway the hourly curator / skills-sync / org-sync housekeeping ticks now run once per served profile under that profile's runtime scope, and a parked MCP server's identical re-park lines no longer flood the error log.

Closes #115713
Supersedes #115775
Closes #116700

Changes

  • Housekeeping profile scope (gateway/run_profile_reconcile.py::_for_each_served_profile, profile_scoped_chore; gateway/run.py::_start_gateway_housekeeping): the MCP reconciler's per-served-profile iteration is lifted into a helper and Curator tick, Sync pull tick and Org sync pull tick ride it. Each served profile's tick reads ITS OWN home, config and Nous credentials; single-profile gateways run each chore once against the process home (a standalone gateway flipped into multi-profile hosting binds the launch profile's own scope, as turns do — [Bug]: Hosted-room activation breaks subsequent standalone gateway model resolution #112878).
  • MCP re-park log dedupe (@liuhao1024, cherry-picked from fix(mcp): demote re-park lines to DEBUG while a server never revived #115775 with authorship): park lines route through MCPServerTask._log_park; first park = WARNING (with the hermes mcp login <name> remedy, state still visible in hermes mcp list), identical re-park while never revived = DEBUG, healthy session clears the latch so the next outage warns again.
  • Fix-up on the cherry-pick: the dedupe keys on the park LINE, not on "still parked" — a park for a different reason (auth error after connection refused) warns again. Two unit checks folded into one contract test.

Root cause (scope): _start_gateway_housekeeping runs on a bare thread with no turn on the stack; tools/skills_sync_client.py::resolve_identity → hermes_cli.auth.resolve_nous_runtime_credentials reads through the fail-closed multiplex reader and logs nous: NOUS_INFERENCE_BASE_URL unreadable — no profile secret scope on a multiplexed call (plus the portal-URL twin) every tick. Live journal on one two-profile gateway: 150 lines / 24h in 29 bursts, 23 of them exactly 6 lines at the top of the hour — the 60-tick cadence, not the MCP re-park timer they were first attributed to (probe: MCP parked-retry runs under the connect-site scope, 0 warnings).

Validation

Probe: two temp homes A (launch = multiplex default) and B (served), different NOUS_INFERENCE_BASE_URL in each .env, set_multiplex_active(True), 60 ticks driven through _start_gateway_housekeeping.

Case base (origin/main) head
per-chore (home, override seen), multiplex [(A, None)] ×3 chores [(A, a-url), (B, b-url)] ×3 chores
no profile secret scope warnings, multiplex 3 (4+4 per tick unstubbed) 0
ambient home after ticks A A
control: single profile (multiplex off) [(A, a-url)], 0 warnings unchanged
MCP reconciler chore per-profile scoped unchanged (same iteration)
park log: first / identical repeat / new reason / its repeat WARN / WARN / WARN / WARN WARN / DEBUG / WARN / DEBUG

Tests: tests/gateway/test_housekeeping_profile_scope.py (2 invariants, red on base: [('.hermes', None)] != [('.hermes', a-url), ('b', b-url)]), tests/tools/test_mcp_park_log_dedupe.py (3). Suite: tests/tools tests/gateway tests/hermes_cli — 32253 passed; reds are the known env-red files plus test_dashboard_auth_gate (port 9119 held by the host backend — red on pristine origin/main too) and one test_cmd_update uv-not-found flake that passes on re-run.

Sibling: b9-auth-credentials-mcp-connect-scope (fb87caa, d51429e) covers the MCP connect scope; it does not touch the housekeeping sync chores. No overlap.

Not covered: Auto-archive tick (launch state.db only) and Checkpoint prune tick (launch checkpoints/ only) stay launch-scoped — whether the gateway should archive/prune served profiles' stores is a separate decision, listed in #116700's sweep table.

Infographic

infographic

liuhao1024 and others added 3 commits September 19, 2026 22:01
A parked server's timed self-probe wakes the run task and, when the
dependency is still down, re-parks — re-emitting the same WARNING as the
first park on every interval with no bound. On a long-lived gateway this
flooded gateway.error.log with identical lines (10,548 for one server in
a month) and drowned the genuinely-new errors.

Route all park lines through _log_park: the first park (a real state
transition) still warns; re-parks while the server never revived are
DEBUG-only (hermes mcp list already surfaces the parked state). A session
that proves healthy clears _was_parked, so the next outage warns again.
… again

`_log_park` demoted every park after the first to DEBUG for as long as
`_was_parked` held. That also hid a park for a DIFFERENT reason (a server
parked on connection-refused that re-parks on a revoked token), which is
new information an operator needs at WARNING. Remember the last park line
per server and demote only an identical repeat; fold the two unit checks
into one contract test (first warns, identical repeat DEBUG, new reason
warns, its repeat DEBUG).
…erved profile's scope

The housekeeping thread has no turn on the stack, so nothing bound a profile
for the 60-tick chores. Under gateway.multiplex_profiles the skills-sync
pull (`tools/skills_sync_client.py::resolve_identity` ->
`resolve_nous_runtime_credentials`) and its org-sync sibling read Nous
credentials through the fail-closed reader and logged
`nous: NOUS_INFERENCE_BASE_URL unreadable - no profile secret scope on a
multiplexed call` (and the portal-URL twin) every hourly tick: 150 lines in
24h on one two-profile gateway, in bursts of six. The curator tick read the
LAUNCH profile's skills tree and curator state for every served profile.

Lift the MCP reconciler's per-served-profile iteration into
`_for_each_served_profile` and run the curator, sync pull and org sync pull
through it, so each served profile's tick reads ITS OWN home, config and
credentials (A reads A's NOUS_INFERENCE_BASE_URL, B reads B's, B never sees
A's). Single-profile gateways still run each chore once against the process
home; a standalone gateway that a hosted room flipped into multi-profile
hosting binds the launch profile's own scope, as turns do.

Probe (two temp homes, multiplex active, 60 housekeeping ticks):
  base: per-chore (home, override) = [(A, None)] x3, scope warnings 3
  head: [(A, a-url), (B, b-url)] per chore, scope warnings 0; single-profile
        control unchanged ([(A, a-url)] per chore, 0 warnings).
@github-actions

github-actions Bot commented Sep 20, 2026 •

Copy link
Copy Markdown
Contributor

૮ >ﻌ< ა ci review

ran on d506461 — fix(gateway): run curator/skills-sync housekeeping ticks und

debug info

CI timings

CI timings · View report · View job

Wall time 4m52s vs 6m29s (-24.9%). 4 job(s) slower, 7 faster, 1 unchanged.

  • OS-specific tests / Windows-only tests: -59.0s
  • Check no case-colliding filenames / check-case-collisions: -34.0s
  • OS-specific tests / macOS-only tests: -14.0s
  • Python tests / Run tests: +13.0s
  • Python lints / Windows footguns (blocking): +10.0s

@alt-glitch alt-glitch added type/bug Something isn't working P2 Medium — degraded but workaround exists comp/gateway Gateway runner, session dispatch, delivery tool/mcp MCP client and OAuth tool/skills Skills system (list, view, manage) area/auth Authentication, OAuth, credential pools area/profiles Multi-profile isolation, HERMES_HOME scoping sweeper:risk-message-delivery Sweeper risk: may drop, duplicate, misroute, or suppress messages sweeper:risk-security-boundary Sweeper risk: may affect sandboxing, auth, credentials, or sensitive data labels Sep 20, 2026
@teknium1
teknium1 merged commit b5e8bb7 into main Sep 20, 2026
37 checks passed
@teknium1
teknium1 deleted the salvage/115775-mcp-reprobe-scope branch September 20, 2026 05:34
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area/auth Authentication, OAuth, credential pools area/profiles Multi-profile isolation, HERMES_HOME scoping comp/gateway Gateway runner, session dispatch, delivery P2 Medium — degraded but workaround exists sweeper:risk-message-delivery Sweeper risk: may drop, duplicate, misroute, or suppress messages sweeper:risk-security-boundary Sweeper risk: may affect sandboxing, auth, credentials, or sensitive data tool/mcp MCP client and OAuth tool/skills Skills system (list, view, manage) type/bug Something isn't working

Projects

None yet

3 participants