fix(buzz): latch DM classification on p-tag when channel metadata says DM - #77901
Open
solovision24 wants to merge 20 commits into
Open
fix(buzz): latch DM classification on p-tag when channel metadata says DM#77901solovision24 wants to merge 20 commits into
solovision24 wants to merge 20 commits into
Conversation
…026.7.20 upgrade - agent/conversation_loop.py: prioritise structured reasoning fields over inline-think content for tool_progress_callback relay - ui-tui appLayout.tsx: drop stale inputHeight box sizing (auto-size from rendered content instead) - ui-tui textInput.tsx: cancel pending fast-echo parent update on submit
Merged after independent verification: 54 focused lifecycle/tool/core tests passed, compileall and git diff --check passed, and the PR is limited to the upstream-compatible Hermes baseline.
* fix(kanban): allow requeued review workers past PR guard * fix(kanban): preserve review routing after crash requeue * fix(kanban): preserve native review lane on crash * fix(kanban): apply retry guards to native reviews * fix(kanban): guard native review respawns during cooldown --------- Co-authored-by: SoLo <solo@SoLoBot>
Co-authored-by: SoLo <solo@SoLoBot>
* fix(kanban): restore native GitHub PR ingest * fix(kanban): restore GitHub PR lifecycle safeguards --------- Co-authored-by: SoLo <solo@SoLoBot>
* feat(kanban): enforce native review lifecycle * fix(kanban): authorize review changes from active review runs --------- Co-authored-by: SoLo <solo@SoLoBot>
* feat(kanban): enforce native review lifecycle * fix(kanban): authorize review changes from active review runs * ci: scope contributor checks to the PR base --------- Co-authored-by: SoLo <solo@SoLoBot>
* fix(kanban): replay native review convergence on current base * fix(kanban): restore same-card review remediation lifecycle * fix(kanban): replay webhook review convergence on approved base --------- Co-authored-by: SoLo <solo@SoLoBot>
* fix(kanban): finalize claimed webhook review runs * fix(kanban): correct native review handoff comment --------- Co-authored-by: SoLo <solo@SoLoBot>
* fix(kanban): hard-gate changes-required review completion * fix(kanban): harden review remediation gates * fix(kanban): bind review approval to immutable head * fix(kanban): expose ready review remediation handoff * fix(kanban): authorize remediation dependency exception * fix(kanban): secure remediation handoff identity * fix(kanban): harden remediation key contract * fix(kanban): reject pre-seeded remediation keys --------- Co-authored-by: SoLo <solo@SoLoBot>
* fix(kanban): repair same-card review branch base * fix(kanban): ignore archived prior review remediation keys --------- Co-authored-by: SoLo <solo@SoLoBot>
…tch probe contract Incident 2026-08-03: corrupt messages b-tree / FTS trees made kanban workers fail their first canonical transcript write (session_persistence_ failed -> 'session storage could not be written') and drain the fleet via the two-failure circuit breaker. - New regression tests (tests/state/test_state_db_corruption_worker_drain.py): 9 pass today, reproducing the write failure against a real corrupted messages b-tree, proving the corruption is silent to plain reads, pinning the session_persistence_failed message contract, and showing FTS classes self-heal while the messages b-tree class does not. 1 fails on purpose: test_pre_dispatch_state_db_probe_exists_in_kanban_dispatch pins the contract for the pre-dispatch state.db health probe to implement in hermes_cli/kanban_db.py (delegate to hermes_state._db_opens_cleanly). - docs/design/state-db-corruption-worker-drain.md: exact write-path trace, corruption classes + current self-heal behavior, detector/repair machinery, pre-dispatch probe contract, recovery/restart paths. No production behavior changed; no PR to official Hermes.
Alert when Todo/Blocked work exists but no runnable worker remains because every owner profile is gated — quarantined/unhealthy session store or tripped two-failure circuit breaker. This is the 2026-08-03 "silent fleet drain" signal: after the breaker trips every task goes blocked and the board looks idle even though it is drained. - New hermes_cli/kanban_health.py: read-only check_queue_drain() + QueueDrainReport + quarantine provider seam (task-1 event stream scan by default, register_quarantine_provider for richer providers). - Gateway dispatcher watcher runs the check every tick, rate-limited to one ERROR alert per board per 5 min. - `hermes kanban health` CLI subcommand (exit 1 when alerting) for on-demand checks / cron watchdogs; mirrored into the --force daemon. - 14 tests: all-gated (quarantine / breaker / mixed) fires, runnable worker suppresses, event-stream quarantine detection, CLI exit codes. Local-only change; no external PR.
Never spawn a worker against an unhealthy profile store. A worker pointed at a corrupt state.db opens fine, fails its first canonical transcript write (session_persistence_failed), and drains the fleet through the failure circuit breaker (incident 2026-08-03). - Add hermes_cli.kanban_db.pre_dispatch_state_db_probe(profile_name) -> Optional[str], resolving the profile's HERMES_HOME state.db and delegating to hermes_state._db_opens_cleanly (integrity_check, FTS read probes, rolled-back canonical write probe). Non-destructive; missing state.db is healthy. - dispatch_once consults it per assignee (memoized per profile per tick) before _default_spawn: on failure, ready tasks are blocked (kind=capability) with the high-signal diagnostic 'profile <name> store unhealthy: <error>; worker blocked'; review tasks are skipped without claiming and a 'quarantined' event records the reason. Store is never replaced/deleted; timestamped backups are the recovery path. - detect_crashed_workers probes the assignee's store for the low-signal 'pid N not alive' class and records the store diagnostic instead when the store is unhealthy. - Tests: tests/hermes_cli/test_kanban_store_quarantine.py (probe, quarantine gate, healthy-profile dispatch, dry-run, review lane, crash diagnostic); task-0 regression tests now pass.
…OVERY.md End-to-end verification of the 2026-08-03 state.db hardening epic exposed an integration gap: task 1's quarantine gate blocked tasks via `blocked` events, but task 2's queue-drain alert (kanban_health) only scans `profile_quarantined` / `profile_store_healthy` events — and nothing registered the richer provider seam — so in production the alert could never fire from a real quarantine. Fix: the pre-dispatch gate now emits the contract events itself, once per profile per tick (deduped via a per-tick set, separate from the probe memo): - `profile_quarantined` when a task is blocked / review-skipped (payload carries profile, reason=store_unhealthy, blocker detail, db_path, sqlite error) - `profile_store_healthy` when a previously-quarantined profile's store heals and the probe passes on a later tick (uses kanban_health's get_quarantine_state as the single source of truth, lazy import to avoid a cycle) Verified with a full incident simulation on real corrupted stores (all checks pass): healthy profiles dispatch; corrupt default/quill/orion are quarantined; no worker spawns; crash diagnostic is store-unhealthy not pid-not-alive; queue-drain alert fires when the whole fleet is gated and clears when a store heals; no state.db is ever replaced/deleted. Also adds RECOVERY.md (safe offline recovery guidance: timestamped backup first, integrity/foreign_key checks on the backup, FTS rebuild / REINDEX on a copy not the live DB, unreadable rows preserved in a separate backup, restore path, post-restore unblock + probe verification) and documents the event wiring in docs/design/state-db-corruption-worker-drain.md. Tests: 47 relevant tests pass (quarantine 17, alert 14, state-db drain 10, malformed repair + FTS rebuild 6); full hermes_cli -k kanban suite 248 pass. Four new event-contract tests in tests/hermes_cli/test_kanban_store_quarantine.py. Local-only commit: no PR to official Hermes opened — per task policy no official Hermes PR without SoLo's explicit approval, and no GitHub credential exists on this host for push (same blocker as parent tasks).
…s DM
A DM whose prose names the agent ("Hey Orion, ...") was excluded from
DM detection because it visibly mentions the agent, so the first message
dispatched as chat_type=group and a later un-mentioned message latched
chat_type=dm — splitting one DM into two sessions with no shared history
(session keys embed chat_type).
When channel metadata already marks the conversation as a DM (name ==
"DM", empty description), the structural p-tag alone latches it. The
visible-mention exclusion still protects metadata-less conversations and
real community channels that p-tag us on typed mentions.
Adds regression tests reproducing the split (t_bc8d998b).
Collaborator
This branch includes extensive unrelated agent, Kanban, auth, CLI, and TUI changes alongside the Buzz DM fix. Please rebase and submit only the focused Buzz change for review. |
19 tasks
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
Fixes a DM session-split in the Buzz platform adapter: a direct message whose prose names the agent (e.g. "Hey Orion, do you know why this job is progressing?") was excluded from DM detection because it visibly mentions the agent, so the first message dispatched as chat_type=group. A later un-mentioned message in the same DM latched chat_type=dm — splitting one conversation into two sessions (session keys embed chat_type) with no shared history.
Root cause
_is_direct_message_eventrequired the content to NOT visibly mention the agent. In a real DM, users routinely name the agent in prose without a typed @mention. The p-tag is structural recipient addressing; the visible-mention exclusion exists only to protect real community channels (which p-tag us solely on typed mentions) from reclassification.Fix
When channel metadata already marks the conversation as a DM (
name == "DM"and empty description), the structural p-tag alone latches chat_type=dm. The visible-mention exclusion still applies to metadata-less conversations and real named channels, preserving the original protection.Tests
Adds
plugins/platforms/buzz/test_dm_classification.pywith 4 regression tests:Verified old code fails the first test; new code passes all 4.
Kanban Task
https://mc.solobot.cloud/tasks?task=t_bc8d998b