Skip to content

fix(bot-mode): the canonical Bot Chat is found by name — session-id pins removed - #92129

Merged
teknium1 merged 3 commits into
mainfrom
hermes/hermes-058cc730
Aug 22, 2026
Merged

teknium1 merged 3 commits into
mainfrom
hermes/hermes-058cc730

Conversation

@teknium1

@teknium1 teknium1 commented Aug 22, 2026

Copy link
Copy Markdown
Collaborator

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

  • plugin.js — resolution inverted to the registry: openBotCanonicalChat(name) is one lookup — session.list {title: 'Bot Chat', include_hidden} → open the row (lineage tip via resolved_id); no row → createCanonicalChat (adopt-before-mint retained inside creation). Deleted: pin verification via preferred_session_ids, drifted-pin keep branches, last_session grandfathering, dead-pin recovery re-anchoring, newerVisibleBotChat, all saveBotMeta({chat}) writes. mergeServerMeta drops legacy chat keys on sight.
  • gateway (methods_profiles.py): profiles.list now reports canonical_session per profile row — the registry row resolved server-side by title (hidden rows resolve; tool/kanban sources and archived rows do not; compression lineages resolve to the live tip). Replaces the preferred_session_ids request contract.
  • roster/preview/guards: row preview, activity signals (age, pulse, unread watermark), and the /new/compact guard all read canonical_session — preview identity and click identity are the same row by construction.
  • AGENTS.md: integrates docs: record the Bot Mode canonical-chat invariant in AGENTS.md #92121's Bot Mode section, corrected from the pin-first contract it documented (fix(bot-mode): a bot row opens the bot's canonical Bot Chat #92042 era) to the name-identity contract this PR ships — no session-id pin at any tier, registry lookup on every open, updated reviewer corollaries and test references.
  • tests: four pin-contract suites replaced by 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 as test_profiles_list_canonical_session.py; creation/hide/preview/activity suites modernized.

Validation

Result
Plugin suite 361/361 pass (node --test tests/*.test.mjs)
Gateway suite tests/tui_gateway/ 536 pass, 1 skip (1 unrelated isolation-artifact failure, passes in isolation with and without this diff)
Ruff clean
E2E (real SessionDB + real profiles.list/session.list RPCs, temp HERMES_HOME) corrupt cron pin ignored, hidden forever-chat found by title and opened; no registry row → mint; deny-listed "Bot Chat" row → None

Infographic

The chat is the name — session-id pins removed

…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.
@github-actions

github-actions Bot commented Aug 22, 2026

Copy link
Copy Markdown
Contributor

૮ >ﻌ< ა ci review

ran on b9f7ec7 — docs(agents-md): Bot Mode canonical-chat invariant is name-i

⚠️ Warnings

OSV vulnerability scan · View job

7 known vulnerabilities found in pinned dependencies.

How to fix:

Review the findings in the Security tab. Update the affected dependencies if a patched version is available.


debug info

CI timings

CI timings · View report · View job

Wall time 2m49s vs 2m18s (+22.5%). 9 job(s) slower, 4 unchanged.

  • Python tests / Run tests: +33.0s
  • JS & TS checks / JS & TS checks: +6.0s
  • OS-specific tests / Windows-only tests: +4.0s
  • Python tests / e2e: +3.0s
  • Python lints / Windows footguns (blocking): +2.0s

… 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.
@alt-glitch alt-glitch added type/bug Something isn't working P2 Medium — degraded but workaround exists comp/desktop Electron desktop app (apps/desktop/*) comp/tui Terminal UI (ui-tui/ + tui_gateway/) area/sessions Session lifecycle, resume, persistence, history sweeper:risk-session-state Sweeper risk: may lose/corrupt/mis-associate session or context state labels Aug 22, 2026
@teknium1
teknium1 merged commit 14c59f0 into main Aug 22, 2026
38 checks passed
@teknium1
teknium1 deleted the hermes/hermes-058cc730 branch August 22, 2026 08:23
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).
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area/sessions Session lifecycle, resume, persistence, history comp/desktop Electron desktop app (apps/desktop/*) comp/tui Terminal UI (ui-tui/ + tui_gateway/) P2 Medium — degraded but workaround exists sweeper:risk-session-state Sweeper risk: may lose/corrupt/mis-associate session or context state type/bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants