Skip to content

fix(telegram): preserve explicit topic titles - #76454

Open
cloudfreed wants to merge 1 commit into
NousResearch:mainfrom
cloudfreed:fix/telegram-explicit-title-registry
Open

fix(telegram): preserve explicit topic titles#76454
cloudfreed wants to merge 1 commit into
NousResearch:mainfrom
cloudfreed:fix/telegram-explicit-title-registry

Conversation

@cloudfreed

Copy link
Copy Markdown

What does this PR do?

Preserves explicit Telegram DM topic titles as user-confirmed state instead of treating them like automatic session titles.

An explicit /title may now bypass disable_topic_auto_rename, but only with the exact trusted provenance user_confirmed. Telegram rename success is confirmed before the Verified-title Registry is written; no bot/client, no API call, a false API result, an exception, a stale generation, or a changed topic binding cannot create a verified record.

This also makes automatic and explicit title operations deterministic under concurrency: explicit user intent takes priority, per-topic operations are serialized, and automatic renames respect persisted verified titles after restart.

Related Issue

Related to #52734, which covers the basic /title → Telegram topic rename behavior. This PR is intentionally separate and references that overlap explicitly because it adds the fail-closed API-result, persistence, migration, binding, async-offload, and concurrency guarantees needed for the behavior to remain correct when automatic renaming is disabled or operations fail/race.

Type of Change

  • 🐛 Bug fix (non-breaking change that fixes an issue)
  • ✨ New feature (non-breaking change that adds functionality)
  • 🔒 Security fix
  • 📝 Documentation update
  • ✅ Tests (adding or improving test coverage)
  • ♻️ Refactor (no behavior change)
  • 🎯 New skill (bundled or hub)

Changes Made

  • gateway/slash_commands.py
    • mark explicit /title propagation with trusted user_confirmed provenance
  • gateway/run.py
    • allow only explicit user-confirmed renames to bypass automatic-rename disablement
    • serialize title changes per Telegram topic and enforce explicit-over-automatic ordering
    • require confirmed Telegram API success before Registry writes
    • fail closed for stale operations, changed bindings, Registry read errors, and post-restart verified titles
    • make explicit rename and Registry-write failures observable
  • hermes_state.py
    • add the v3 Verified Telegram topic-title Registry with lazy migration from v2
    • enforce provenance and expected binding at the storage boundary
    • add async offload wrappers for gateway DB access
  • plugins/platforms/telegram/adapter.py
    • return strict boolean rename results and report no-call/false-result paths accurately
  • tests/
    • add deterministic regressions for API false/no-call/exception paths, migration, binding changes, Registry failures, restart behavior, and explicit/automatic concurrency orderings

How to Test

  1. Run the related regression suites:
    HERMES_PYTHON=/path/to/python scripts/run_tests.sh \
      tests/gateway/test_telegram_topic_mode.py \
      tests/gateway/test_title_command.py \
      tests/gateway/test_async_session_db.py \
      tests/test_hermes_state.py -q
    Result on this branch: 190 passed, 0 failed.
  2. Run static and patch checks:
    python -m ruff check gateway/run.py gateway/slash_commands.py hermes_state.py \
      plugins/platforms/telegram/adapter.py tests/gateway/test_title_command.py \
      tests/gateway/test_telegram_topic_mode.py tests/gateway/test_async_session_db.py \
      tests/test_hermes_state.py
    python -m py_compile gateway/run.py gateway/slash_commands.py hermes_state.py \
      plugins/platforms/telegram/adapter.py tests/gateway/test_title_command.py \
      tests/gateway/test_telegram_topic_mode.py tests/gateway/test_async_session_db.py \
      tests/test_hermes_state.py
    git diff --check origin/main...HEAD
  3. The full canonical suite was also attempted. The available environment has unrelated baseline failures from missing optional ACP/Anthropic SDK dependencies and a WeCom ET issue; those same failures were reproduced on the clean base. No failure occurred in the changed title/Telegram topic/SessionDB paths.

Checklist

Code

Documentation & Housekeeping

  • Documentation update — N/A; no user-facing config key or command syntax changed
  • cli-config.yaml.example update — N/A
  • CONTRIBUTING.md / AGENTS.md update — N/A
  • Cross-platform impact considered; implementation uses existing asyncio, threading, SQLite, and adapter abstractions
  • Tool descriptions/schemas update — N/A

Screenshots / Logs

Not applicable. The change is covered by deterministic gateway, adapter, migration, and storage regressions.

@alt-glitch alt-glitch added type/bug Something isn't working P3 Low — cosmetic, nice to have comp/gateway Gateway runner, session dispatch, delivery comp/plugins Plugin system and bundled plugins platform/telegram Telegram bot adapter sweeper:risk-message-delivery Sweeper risk: may drop, duplicate, misroute, or suppress messages sweeper:risk-session-state Sweeper risk: may lose/corrupt/mis-associate session or context state sweeper:risk-compatibility Sweeper risk: may break existing users, config, migrations, defaults, or upgrades labels Aug 1, 2026
@alt-glitch

Copy link
Copy Markdown
Collaborator

This was generated by AI during triage.

Related: #52734 wires the basic explicit /title and /new rename paths. This patch adds distinct verified-title persistence, failure handling, binding, and concurrency guarantees, so it is not a duplicate.

@teknium1 teknium1 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for separating the explicit-title behavior from the related basic /title work. The premise is confirmed on current main: /title schedules the rename in gateway/slash_commands.py:4282-4293, while gateway/run.py:19167 drops it whenever disable_topic_auto_rename is enabled.

Problems

  • The user-facing documentation needs to change with this behavior. website/docs/user-guide/messaging/telegram.md:801 currently says the flag means Hermes “never edits the Telegram topic name,” and :836 says it turns off auto-rename for all topics. The new explicit /title exception makes those statements inaccurate.

Suggested changes

  • Document that disable_topic_auto_rename suppresses automatic renames only; an explicit /title remains a user-confirmed rename. Update the configuration explanation and the “Under the hood” bullet at website/docs/user-guide/messaging/telegram.md:791-801,835-836.

Automated hermes-sweeper review.

Comment thread gateway/slash_commands.py
source,
session_id,
sanitized,
registry_provenance="user_confirmed",

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This adds the documented disable_topic_auto_rename exception for explicit /title. Please update website/docs/user-guide/messaging/telegram.md:791-801 and :835-836, which currently say the flag means Hermes never edits topic names and disables renames for all topics.

@teknium1 teknium1 added the sweeper:blast-moderate Sweeper blast radius: moderate — a subsystem or single platform label Aug 1, 2026

@GottZ GottZ left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This was generated by AI during triage.

Summary

Sixteen PRs address or reference this two-issue cluster. Most implement the already-landed session-title-to-topic rename path or its manual /title variant; the remaining distinct gaps concern /new <title>, runtime-discovered/client-created topics, and #76454's persistent user-confirmed-title and concurrency semantics, while none of the diffs directly fixes voice-transcription timing from #57341.

Related pull requests

  • #9921 [closed] partial — (+302/-1) — close as already implemented on main: the diff adds auto-title and manual /title callbacks plus Telegram topic renaming, but current main provides the guarded equivalent at gateway/run.py:19015, gateway/run.py:13720, gateway/run.py:13612, and gateway/slash_commands.py:3487, with tests at tests/gateway/test_telegram_topic_mode.py:889 and tests/gateway/test_title_command.py:173, consistent with the automated close verdict.
  • #14462 [closed] fixes — (+892/-49) — close as superseded by #49245 for /title: its broad platform hook, topic cache, callback composition, and auto-title refactor exceed the reported manual-title cause; contributor review records that the core /title gap was salvaged through #49172 into #49245, while any wider behavior requires a focused follow-up.
  • #16408 [closed] best fix — (+413/-3) — close as already implemented on main: this is the same auto/manual title-sync bridge as #9921, while current main ships the guarded callback, scheduler, and manual-title path in commit 38f1a923af6e77cad16a4a270c74f79847311c2b, with coverage at tests/gateway/test_telegram_topic_mode.py:889 and tests/gateway/test_title_command.py:173, matching the automated close verdict.
  • #20681 [closed] partial — (+28/-0) — close as duplicate of #49172, salvaged into #49245: the diff only calls the existing Telegram rename scheduler after successful /title persistence and adds one regression test, which is the same narrow cause and mechanism.
  • #21111 [closed] partial — (+459/-11) — close as duplicate of #49172, salvaged into #49245 for the /title cause: its additional /new, manual-ownership metadata, and debounce changes are broader than that gap and were not the salvaged implementation.
  • #30559 [closed] fixes — (+557/-32) — keep closed as an abandoned broader abstraction: the diff introduces cross-platform update_topic_title plumbing and refactors established Telegram guards, rather than isolating the title-sync defect, and the author explicitly closed the generated branch.
  • #31395 [closed] fixes — (+889/-114) — keep closed as superseded by cleaner focused work: although it combines #14462 and #21111, the diff mixes title-generation rewrites, CLI branding, debounce changes, ownership metadata, and Telegram synchronization, and the author reported that the branch could not be cleanly extracted.
  • #35400 [closed] partial — (+9/-0) — close as duplicate of #14462/#49172, with the latter salvaged into #49245: the diff only invokes the existing topic-rename helper after a successful /title write.
  • #35678 [closed] partial — (+7/-0) — close as duplicate of #49172, salvaged into #49245: despite the APPROVED review on #35678, its seven-line diff performs the same /title scheduler call, and contributor review explicitly dispositioned it through #49172#49245.
  • #46644 [closed] partial — (+127/-3) — close the /title portion as duplicate of #49172, salvaged into #49245; keep its group-forum predicate only as possible focused follow-up material, because that broader scope is not implemented by the manual-title fix.
  • #49172 [closed] partial — (+52/-0) — close as superseded by merged #49245: this is the source implementation cherry-picked with authorship preserved, adding the successful /title scheduler call and tests for both set-title and show-title paths.
  • #49245 [merged] partial — (+52/-0) — merged reference implementation: it salvages #49172 and supplies the landed /title_schedule_telegram_topic_title_rename wiring plus no-rename-on-show coverage, addressing the manual-title portion of the cluster.
  • #52734 partial — (+83/-0) — keep open with a salvage path for /new <title> only, in line with its keep_open review: commit 38f1a923a already covers /title at gateway/slash_commands.py:3499-3516, while the remaining change should be rebased into gateway/slash_commands.py:272-304, gated on successful title persistence, with a current-main regression test.
  • #58430 best fix — (+71/-13) — keep open with a salvage path, in line with its keep_open verdict: preserve the _dm_topics_config membership distinction and runtime-cache regression, but apply it after current main's source-aware self._adapter_for_source(source) at gateway/run.py:13906 rather than restoring the historical adapter lookup. This removes one rename blocker relevant to #57341 but does not itself change voice/STT timing.
  • #64021 partial — (+39/-3) — keep open with a salvage path, in line with the contributor keep_open review: the narrower DM-thread predicate addresses client-created topics without Hermes topic mode, but the updated patch must add a regression through auto-title callback installation and scheduling, not only call the rename helper directly.
  • #76454 partial — (+1014/-35) — keep open with a salvage path, consistent with the contributor keep_open review: the diff uniquely adds user-confirmed provenance, success-gated persistent title records, binding checks, restart protection, and serialized explicit-over-automatic operations. It must first update website/docs/user-guide/messaging/telegram.md:791-801,835-836 so disable_topic_auto_rename is documented as suppressing automatic renames while explicit /title remains allowed.

Duplicates

The core auto-title synchronization implementations are #9921 and #16408; the manual /title duplicates are #14462, #20681, #21111, #35400, #35678, #46644, and #49172, with #49172 salvaged into merged #49245. #31395 combines several of those paths but is not a clean canonical patch. #52734 overlaps #49245 on /title but retains distinct /new <title> work; #58430, #64021, and #76454 address separate guard, eligibility, and persistence/concurrency gaps.

Suggested consolidation

Keep #76454 open with a salvage path: retain its verified user-confirmed-title registry, fail-closed API-result handling, binding validation, restart persistence, and explicit-over-automatic serialization, but require the contributor-requested documentation update before disposition. Also keep #52734, #58430, and #64021 open only for their distinct, review-specified salvage scopes; close or leave closed the auto-title and /title duplicates through the explicit chains above, using merged #49245 and commit 38f1a923a as the manual-title reference. Treat #57341 as still open after those consolidations because the presented diffs remove topic-rename eligibility blockers but do not demonstrate that transcribed voice content reaches title generation at the correct time.

Complex graph

flowchart LR
    classDef open fill:#dbeafe,stroke:#1d4ed8,color:#1e3a8a
    classDef merged fill:#dcfce7,stroke:#15803d,color:#14532d
    classDef closed fill:#e5e7eb,stroke:#6b7280,color:#1f2937
    classDef unverified fill:#f3f4f6,stroke:#9ca3af,color:#374151
    classDef best stroke-width:3px,stroke:#b45309
    classDef target stroke-width:3px,stroke:#4338ca
    I16255(["issue #16255 (closed)"])
    P76454["PR #76454 (open)"]
    P76454 -.->|partial| I16255
    class I16255 closed
    class P76454 open
    class P76454 target
    click I16255 "https://github.com/NousResearch/hermes-agent/issues/16255"
    click P76454 "https://github.com/NousResearch/hermes-agent/pull/76454"
Loading

Graph: solid arrow = fixes / best fix, dashed arrow = partial or unverified (see edge label); boxed group = PRs duplicating each other; amber border = best fix; indigo border = target; gray node = closed (state tag in the node label).

Cross-PR triage: Reviewed 16 pull requests and 2 issues in this complex. Each diff was read against this issue; Assessment working set: 285 kB of PR diffs, 38 kB of issue/PR text, 18 kB of discussion (31 comments), 21 verify verdicts. verdicts reflect diff content, not PR titles. Part of an automated triage batch.

@andrexibiza

Copy link
Copy Markdown
Contributor

Composition heads-up from the adapter decomposition campaign (epic #78791): PR #78949 (slice A1) moves the DM-topic cluster — including rename_dm_topic, which this PR changes (None → bool return) — into plugins/platforms/telegram/telegram_dm_topics.py (class TelegramDmTopicMixin), byte-identical bodies. Verified: A1 keeps the pre-change -> None body; your title-preservation work is not duplicated, it stays yours.

Merge order so nothing is lost: merge #78949 first, then rebase this PR onto main — your rename_dm_topic hunks then apply to the mixin file cleanly. Credit for the explicit-title-title preservation is noted in #78949's body.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

comp/gateway Gateway runner, session dispatch, delivery comp/plugins Plugin system and bundled plugins P3 Low — cosmetic, nice to have platform/telegram Telegram bot adapter sweeper:blast-moderate Sweeper blast radius: moderate — a subsystem or single platform sweeper:risk-compatibility Sweeper risk: may break existing users, config, migrations, defaults, or upgrades sweeper:risk-message-delivery Sweeper risk: may drop, duplicate, misroute, or suppress messages 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.

5 participants