Skip to content

fix: scoped-lock guard dead code in buzz/irc/line adapters (tuple truthiness) - #74978

Open
spector-in-london wants to merge 1 commit into
NousResearch:mainfrom
spector-in-london:fix/scoped-lock-tuple-truthiness
Open

spector-in-london wants to merge 1 commit into
NousResearch:mainfrom
spector-in-london:fix/scoped-lock-tuple-truthiness

Conversation

@spector-in-london

Copy link
Copy Markdown

Bug

gateway/status.py:acquire_scoped_lock() returns tuple[bool, Optional[dict]].

Three platform adapters test the return value for truthiness instead of unpacking it:

  • plugins/platforms/buzz/adapter.py:475if not acquire_scoped_lock("buzz", lock_key):
  • plugins/platforms/irc/adapter.py:170if not acquire_scoped_lock("irc", lock_key):
  • plugins/platforms/line/adapter.py:790if not acquire_scoped_lock("line", tok_hash):

A non-empty 2-tuple is always truthy, so not ret is always False and the lock_conflict refuse-branch is unreachable dead code. The scoped lock silently never prevents anything on these three platforms.

Correct shape (already in tree)

The correct pattern already exists in the same codebase:

  • gateway/platforms/base.py:3213acquired, existing = acquire_scoped_lock(...)
  • plugins/platforms/feishu/adapter.py:1751 — same

Fix

One-line change per call site — unpack the tuple and test the bool:

acquired, existing = acquire_scoped_lock("buzz", lock_key)
if not acquired:
    ...

Also enriches the log lines with the holding PID (from the existing dict) so the operator learns which process holds the lock, matching base.py.

Tests

  • Fixed the existing test_connect_fails_when_identity_lock_held buzz test: its mock returned a bare False, which would crash on tuple unpacking after the fix. Now returns the real (False, {...}) shape.
  • Added TestIRCScopedLockRegression — two tests: conflict path fires, acquired path does not fail with lock_conflict.
  • Added TestLineScopedLockRegression — conflict path fires, acquired path exercises the lock guard.

All 75 tests in the three adapter test files pass.

Impact

For Buzz this is the control that prevents two Hermes profiles from driving one Nostr identity on one relay — Nostr events are immutable and there is no rotation story for a misattributed signed event. The same class of bug affects IRC (duplicate nick on the same server) and LINE (duplicate channel access token).

…thiness)

acquire_scoped_lock() returns tuple[bool, Optional[dict]]. Three platform
adapters tested the return for truthiness instead of unpacking it:

  - buzz/adapter.py:475  — if not acquire_scoped_lock(buzz, lock_key):
  - irc/adapter.py:170   — if not acquire_scoped_lock(irc, lock_key):
  - line/adapter.py:790  — if not acquire_scoped_lock(line, tok_hash):

A non-empty 2-tuple is always truthy, so 'not ret' was always False and
the lock_conflict refuse-branch was unreachable dead code. The scoped lock
silently never prevented anything on these three platforms.

The correct shape already exists in gateway/platforms/base.py:3213 and
plugins/platforms/feishu/adapter.py:1751 — unpack the tuple and test the
bool. This fixes all three call sites to match.

Also enriches the log lines with the holding PID (from the existing dict)
so the operator learns which process holds the lock, matching base.py.

Regression tests added for all three adapters asserting the guard fires
on conflict. The existing buzz test mock (returning bare False) is fixed
to return the real tuple shape — without this it would crash on unpack.
@alt-glitch alt-glitch added type/bug Something isn't working comp/gateway Gateway runner, session dispatch, delivery comp/plugins Plugin system and bundled plugins P3 Low — cosmetic, nice to have labels Jul 30, 2026
@teknium1

Copy link
Copy Markdown
Collaborator

Thanks for tracking this down. The production premise is verified on current main: gateway/status.py:1362 returns a (bool, record) tuple, while plugins/platforms/buzz/adapter.py:475, plugins/platforms/irc/adapter.py:170, and plugins/platforms/line/adapter.py:790 still truth-test that tuple. The proposed unpacking matches gateway/platforms/base.py:3213.

Problems

  • The new acquired-path LINE test constructs ad but never calls ad.connect(); it calls the monkeypatched gateway_status.acquire_scoped_lock() directly. That cannot detect a regression in LineAdapter.connect()'s guard or _lock_key assignment.
  • website/i18n/zh-Hans/docusaurus-plugin-content-docs/current/developer-guide/adding-platform-adapters.md:672 still teaches the same broken truthiness form. The English counterpart already uses unpacking at website/docs/developer-guide/adding-platform-adapters.md:676-677.

Suggested changes

  • Drive the LINE acquired case through await ad.connect() with downstream networking/server setup stubbed, and assert it passes the guard.
  • Correct the localized Token Locks snippet to unpack the tuple.

The patch otherwise targets all current production instances found by the scoped-lock search. GitHub main c9de69c6 and the PR share parent 07447bd5; main's sole post-base change is desktop-only, so salvage should be mechanical. This is an automated hermes-sweeper review.

@teknium1 teknium1 added sweeper:risk-message-delivery Sweeper risk: may drop, duplicate, misroute, or suppress messages sweeper:risk-security-boundary Sweeper risk: may affect sandboxing, auth, credentials, or sensitive data sweeper:blast-moderate Sweeper blast radius: moderate — a subsystem or single platform labels Jul 30, 2026
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 sweeper:blast-moderate Sweeper blast radius: moderate — a subsystem or single platform sweeper:risk-message-delivery Sweeper risk: may drop, duplicate, misroute, or suppress messages sweeper:risk-security-boundary Sweeper risk: may affect sandboxing, auth, credentials, or sensitive data type/bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants