Clean up lint/LSP across gateway, Android, and desktop (alpha -> stable)

Gateway (gateway-plugin/):
- Fix interactive_setup broken imports: print helpers were imported from the
  wrong hermes module (hermes_cli.config instead of hermes_cli.cli_output) plus
  a non-existent print_code; the try/except swallowed the ImportError so
  `hermes gateway setup` for android always bailed out early.
- Fix release_scoped_lock type error (str | None passed where str required).
- Rewrite empty `except: pass` blocks as contextlib.suppress with rationale.
- Restructure two ambiguous ws_server try blocks (hello-auth, frame loop).
- Ruff cleanup: type annotations, import sorting, line wrapping, magic values
  -> named constants, `raise ... from e`, complexity. Add gateway-plugin/ruff.toml.
- Add pyrightconfig.json so the Python LSP resolves hermes-runtime imports.
- Suppress verified false positives inline (parameterized SQL, column-name
  "secrets", hermes-generated media path).

Android (app/androidApp + app/shared):
- Consolidate launcher icons into a single mipmap-anydpi (minSdk 29 >= 26) with
  the monochrome layer; clears ObsoleteSdkInt + MonochromeLauncherIcon.
- Bump core-splashscreen 1.0.1 -> 1.2.0; pin targetSdk 34 (deliberate).
- Suppress verified findings inline (LAN ws:// default, correct GCM IV usage).

Desktop (app/desktopApp):
- Move the desktop to a Java 21 runtime (org.gradle.java.home) and set the
  desktop jvmTarget to 21 (Android stays JVM 17 / minSdk 29). Fixes the startup
  UnsupportedClassVersionError and restores Markdown renderer 0.44.0.

Tooling/config:
- .pi-lens.json: disable verified-noisy heuristics (documented in docs).
- .gitleaks.toml: allowlist git-ignored false-positive paths.
- docs/18-code-review.md: full findings + verification.

Verified: ruff clean, pyright 0 errors, 64/64 gateway tests, all Kotlin tests,
Android lint 0 issues, Android installed+launched on device, desktop launches
on JDK 21.
This commit is contained in:
ARIA committed 2026-08-21 18:47:03 +02:00
1 parent 9f3f9842c8
commit 678c0344c8
27 files changed
+928 -454

No files matched your search

+26 -35
View File
@@ -15,13 +15,14 @@ Storage: ``get_hermes_home()/"android"/outbox.db``.
Milestone M3 (built), extended in M5 (push integration).
"""
import contextlib
import json
import logging
import sqlite3
import threading
import time
from pathlib import Path
from typing import Any, Dict, List, Optional
from typing import Any
logger = logging.getLogger(__name__)
@@ -69,9 +70,7 @@ class Outbox:
)
"""
)
self._conn.execute(
"CREATE INDEX IF NOT EXISTS idx_outbox_created ON outbox (created)"
)
self._conn.execute("CREATE INDEX IF NOT EXISTS idx_outbox_created ON outbox (created)")
self._conn.execute(
"""
CREATE TABLE IF NOT EXISTS counters (
@@ -84,7 +83,7 @@ class Outbox:
# ── append / cursor ───────────────────────────────────────────────────
def append(self, chat_id: Optional[str], frame_json: str) -> int:
def append(self, chat_id: str | None, frame_json: str) -> int:
"""Append a frame; returns the (monotonic) cursor assigned to it."""
now = time.time()
with self._lock:
@@ -92,13 +91,10 @@ class Outbox:
"INSERT INTO counters (name, value) VALUES ('cursor', 1) "
"ON CONFLICT(name) DO UPDATE SET value = value + 1"
)
row = self._conn.execute(
"SELECT value FROM counters WHERE name = 'cursor'"
).fetchone()
row = self._conn.execute("SELECT value FROM counters WHERE name = 'cursor'").fetchone()
cursor = int(row["value"]) if row else 1
self._conn.execute(
"INSERT INTO outbox (cursor, chat_id, frame, created) "
"VALUES (?, ?, ?, ?)",
"INSERT INTO outbox (cursor, chat_id, frame, created) VALUES (?, ?, ?, ?)",
(cursor, chat_id, frame_json, now),
)
self._enforce_row_cap()
@@ -135,14 +131,12 @@ class Outbox:
def latest_cursor(self) -> int:
"""The high-water cursor (0 when nothing has been appended)."""
with self._lock:
row = self._conn.execute(
"SELECT value FROM counters WHERE name = 'cursor'"
).fetchone()
row = self._conn.execute("SELECT value FROM counters WHERE name = 'cursor'").fetchone()
return int(row["value"]) if row else 0
# ── replay ────────────────────────────────────────────────────────────
def replay(self, cursor: int, limit: int = _REPLAY_LIMIT) -> List[Dict[str, Any]]:
def replay(self, cursor: int, limit: int = _REPLAY_LIMIT) -> list[dict[str, Any]]:
"""Frames with ``cursor > `cursor```, oldest first.
Each entry: ``{cursor, chat_id, frame}`` where ``frame`` is the parsed
@@ -156,7 +150,7 @@ class Outbox:
"WHERE cursor > ? ORDER BY cursor ASC LIMIT ?",
(cursor, limit),
).fetchall()
out: List[Dict[str, Any]] = []
out: list[dict[str, Any]] = []
for r in rows:
try:
frame = json.loads(r["frame"])
@@ -164,9 +158,7 @@ class Outbox:
continue
if not isinstance(frame, dict):
continue
out.append(
{"cursor": int(r["cursor"]), "chat_id": r["chat_id"], "frame": frame}
)
out.append({"cursor": int(r["cursor"]), "chat_id": r["chat_id"], "frame": frame})
return out
# ── history (full message history for a chat/thread) ──────────────────
@@ -174,10 +166,10 @@ class Outbox:
def history(
self,
chat_id: str,
thread_id: Optional[str] = None,
before_message_id: Optional[str] = None,
thread_id: str | None = None,
before_message_id: str | None = None,
limit: int = 50,
) -> Dict[str, Any]:
) -> dict[str, Any]:
"""Final messages for a chat/thread, for the ``history`` frame.
Reconstructs the message list from the outbox log: a final message is
@@ -194,11 +186,10 @@ class Outbox:
limit = max(1, min(int(limit or 50), 200))
with self._lock:
rows = self._conn.execute(
"SELECT cursor, frame FROM outbox WHERE chat_id = ? "
"ORDER BY cursor ASC",
"SELECT cursor, frame FROM outbox WHERE chat_id = ? ORDER BY cursor ASC",
(chat_id,),
).fetchall()
final: List[Dict[str, Any]] = []
final: list[dict[str, Any]] = []
for r in rows:
try:
frame = json.loads(r["frame"])
@@ -244,7 +235,7 @@ class Outbox:
}
)
# Deduplicate by message_id (keep the latest occurrence), keep order.
by_id: Dict[str, Dict[str, Any]] = {}
by_id: dict[str, dict[str, Any]] = {}
for m in final:
mid = m.get("message_id")
if mid:
@@ -283,7 +274,7 @@ class Outbox:
self,
chat_id: str,
message_id: str,
thread_id: Optional[str] = None,
thread_id: str | None = None,
) -> int:
"""Remove every outbox frame belonging to *message_id* in *chat_id*.
@@ -304,7 +295,7 @@ class Outbox:
rows = self._conn.execute(
"SELECT cursor, frame FROM outbox WHERE chat_id = ?", (chat_id,)
).fetchall()
cursors: List[int] = []
cursors: list[int] = []
for r in rows:
try:
frame = json.loads(r["frame"])
@@ -320,9 +311,11 @@ class Outbox:
if not cursors:
return 0
placeholders = ",".join("?" * len(cursors))
self._conn.execute(
f"DELETE FROM outbox WHERE cursor IN ({placeholders})", cursors
)
sql = f"DELETE FROM outbox WHERE cursor IN ({placeholders})"
# Safe: ``placeholders`` is only ``?`` markers; every cursor value is
# bound as a parameter (no user data in the SQL text).
# pi-lens-ignore: python-sql-injection
self._conn.execute(sql, cursors)
self._conn.commit()
return len(cursors)
@@ -347,8 +340,6 @@ class Outbox:
self._maybe_prune()
def close(self) -> None:
with self._lock:
try:
self._conn.close()
except Exception:
pass
with self._lock, contextlib.suppress(Exception):
# Best-effort: a close failure on shutdown is not actionable.
self._conn.close()