fix(bot-mode): the canonical Bot Chat is found by name — session-id pins removed - #92129
Merged
Merged
Conversation
…ins removed
A bot's forever-chat now has exactly one identity: the session titled
"Bot Chat" on that bot's profile. Core UNIQUE(title) makes (profile,
'Bot Chat') an exact registry, and every open consults it directly via
session.list {title, include_hidden}. The stored-id pin
(ui_meta['hermes-bots'].chat) and its entire verification apparatus —
preferred_session_ids resolution, drifted-pin keep branches, last_session
grandfathering, dead-pin recovery re-anchoring, newerVisibleBotChat — are
removed, not deprecated. Legacy ui_meta.chat keys are ignored and dropped
from merges on sight.
Every lost-canonical-chat incident (#88146, #88200, #90524, #90705, and
five hardening waves) traced to that pointer dangling or being stolen,
then later guards welding the wrong session in. A name cannot dangle:
corrupt pins self-heal on first click because the pointer is simply never
read.
Gateway: profiles.list now reports canonical_session per profile row
(registry row resolved server-side by title — hidden rows resolve,
deny-listed sources and archived rows do not, compression lineages
resolve to the live tip), replacing the preferred_session_ids request
contract. The roster preview, activity signals, and the /new→/compact
guard all read canonical_session, so preview identity and click identity
are the same row by construction.
No migration shims: this IS the system.
Contributor
૮ >ﻌ< ა ci reviewran on b9f7ec7 — docs(agents-md): Bot Mode canonical-chat invariant is name-i
|
… corrections folded in The cherry-picked #92121 text documented the pin-first contract (#92042 era). Corrected to the registry contract this branch ships: identity is (profile, 'Bot Chat') via exact-title lookup; there is no session-id pin at any tier; reviewer corollaries and regression-test references updated to the surviving suites.
This was referenced Aug 22, 2026
teknium1
added a commit
that referenced
this pull request
Sep 14, 2026
…classes Two weeks of closed issues/merged PRs show the same areas regenerating: each salvage pinned its instance while the class invariant had no test. These suites pin the invariants themselves: - tests/conformance/test_profile_write_tripwire.py — no writes to the default profile tree while a profile is active (#88532 #92662 #89190 #89625 #92156); reusable tripwire fixture, 4 surfaces - tests/hermes_cli/test_env_deprecation_truthtable.py — 18-row truth table for the Deprecated-.env warning (#88829 #89016 #89389 #90299) - tests/cron/test_cron_memory_contract.py — cron<->memory contract that flipped twice in Aug (#91269 -> #91384 -> #91447) - tests/agent/test_injected_param_strip_retry_registry.py — every strippable injected param x real 400 shapes must strip-and-retry; unknown params must still fail (#90257 #89897 #91164 #89503) - tests/agent/test_transcript_decoration_idempotence.py — f(f(x))==f(x) law + 4-breakpoint budget for apply_anthropic_cache_control (#90971) - tests/state/test_state_db_maintenance_conformance.py — registry- enumerated maintenance ops refuse/degrade under a live writer; copies of corrupt DBs are refused or flagged (#91839 #90806 #90613 #88235) - tests/tools/test_bot_mode_canonical_chat_resolution.py — canonical Bot Chat resolution is idempotent, never mints, unique per profile, race-safe (#92040 #90705 #92692 #90005 #90732, PR #92129) - tests/hermes_cli/test_update_receipt_truthfulness.py — receipts: crash never claims success; success requires full fleet accounting; refusal != failure (#91283 #91439 #92902 #92780) 117 tests, all sabotage-verified (each suite proven to FAIL when its bug class is reintroduced).
teknium1
added a commit
that referenced
this pull request
Sep 14, 2026
…classes Two weeks of closed issues/merged PRs show the same areas regenerating: each salvage pinned its instance while the class invariant had no test. These suites pin the invariants themselves: - tests/conformance/test_profile_write_tripwire.py — no writes to the default profile tree while a profile is active (#88532 #92662 #89190 #89625 #92156); reusable tripwire fixture, 4 surfaces - tests/hermes_cli/test_env_deprecation_truthtable.py — 18-row truth table for the Deprecated-.env warning (#88829 #89016 #89389 #90299) - tests/cron/test_cron_memory_contract.py — cron<->memory contract that flipped twice in Aug (#91269 -> #91384 -> #91447) - tests/agent/test_injected_param_strip_retry_registry.py — every strippable injected param x real 400 shapes must strip-and-retry; unknown params must still fail (#90257 #89897 #91164 #89503) - tests/agent/test_transcript_decoration_idempotence.py — f(f(x))==f(x) law + 4-breakpoint budget for apply_anthropic_cache_control (#90971) - tests/state/test_state_db_maintenance_conformance.py — registry- enumerated maintenance ops refuse/degrade under a live writer; copies of corrupt DBs are refused or flagged (#91839 #90806 #90613 #88235) - tests/tools/test_bot_mode_canonical_chat_resolution.py — canonical Bot Chat resolution is idempotent, never mints, unique per profile, race-safe (#92040 #90705 #92692 #90005 #90732, PR #92129) - tests/hermes_cli/test_update_receipt_truthfulness.py — receipts: crash never claims success; success requires full fleet accounting; refusal != failure (#91283 #91439 #92902 #92780) 117 tests, all sabotage-verified (each suite proven to FAIL when its bug class is reintroduced).
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.
Summary
A bot's canonical Bot Chat is now found by NAME, never by pointer: the one identity is (profile, session titled "Bot Chat"), resolved fresh on every open through the core UNIQUE(title) registry. The stored session-id pin (
ui_meta['hermes-bots'].chat) and its entire verification apparatus are removed — not deprecated, removed.Root cause of the whole class: every lost-canonical-chat incident (#88146, #88200, #90524, #90705, plus five hardening waves ending in #92042) traced to that pointer dangling or being stolen, after which "history-bearing pins are sacred" guards welded the wrong session in permanently. A live example: a pin re-anchored onto a cron report session passes every guard on current main and freezes the wrong transcript in forever. A name cannot dangle — under this change that same corrupt state self-heals on first click because the pointer is simply never read.
No migration shims, no compat tiers, no grandfathering: this IS the system.
Changes
openBotCanonicalChat(name)is one lookup —session.list {title: 'Bot Chat', include_hidden}→ open the row (lineage tip viaresolved_id); no row →createCanonicalChat(adopt-before-mint retained inside creation). Deleted: pin verification viapreferred_session_ids, drifted-pin keep branches,last_sessiongrandfathering, dead-pin recovery re-anchoring,newerVisibleBotChat, allsaveBotMeta({chat})writes.mergeServerMetadrops legacychatkeys on sight.methods_profiles.py):profiles.listnow reportscanonical_sessionper profile row — the registry row resolved server-side by title (hidden rows resolve;tool/kanbansources and archived rows do not; compression lineages resolve to the live tip). Replaces thepreferred_session_idsrequest contract./new→/compactguard all readcanonical_session— preview identity and click identity are the same row by construction.canonical-chat-registry.test.mjs(7 behavior tests pinning the registry contract, including "the open path never reads or writes a stored pointer"); gateway suite rewritten astest_profiles_list_canonical_session.py; creation/hide/preview/activity suites modernized.Validation
node --test tests/*.test.mjs)tests/tui_gateway/536 pass, 1 skip (1 unrelated isolation-artifact failure, passes in isolation with and without this diff)profiles.list/session.listRPCs, temp HERMES_HOME)Infographic