_is_other_profile only allowed empty/current, so a ?profile=default
save skipped the session.info broadcast on the process whose config
it just wrote. Compare the resolved target to the process HERMES_HOME.
The desktop settings page saves approvals.mode through REST PUT /api/config
(and the raw editor through PUT /api/config/raw). Enforcement follows the
file immediately, because the approval gate re-reads config per command, but
every live session's YOLO/approval indicator repaints only on a session.info
event, and the REST save emitted nothing. The indicator showed bypass OFF
while approvals.mode=off silently auto-approved every dangerous command, and
switching sessions repainted the stale cached per-session state, making the
toggle look like it flipped itself back. The gateway /approvals slash
command had the same gap.
The config.set RPC handler already re-emits session.info to all live
sessions after a mode flip; give the other writers the same contract:
- tui_gateway/server.py: add broadcast_session_info(), which snapshots
_sessions under _sessions_lock and re-emits via
_emit_session_info_for_session. Also call it from the /approvals slash
mirror when a mode argument was persisted (bare /approvals is read-only).
- hermes_cli/web_server.py: after a REST save that actually changed the
normalized approvals.mode, call the broadcast through a sys.modules guard
(no gateway imported means no sessions to notify). The comparison runs on
the in-memory documents (existing vs merged, parsed vs raw): the settings
page PUTs the defaulted GET record while disk holds sparse YAML, so a
block-level compare would broadcast on every autosave, and re-reading
through the config cache after the save could serve the pre-save document
on an (mtime_ns, size) key collision. Own-profile saves only: a
profile-scoped save targets a different HERMES_HOME than this process's
gateway sessions.
No broadcast on saves that leave the effective mode unchanged, so settings
autosave churn (skin, font, TTS) can't spam session.info.
Scope: reaches sessions of the in-process gateway (hermes serve / hermes
dashboard, the topologies the desktop app talks to). A spawned
tui_gateway.entry child gateway has its own process and _sessions; its TUI
statusbar reconciles each turn via the existing session.info emissions.