Files
iris_x_hermes/AGENTS.md
T
ARIA 863ab34915
CI / Gateway plugin tests (push) Failing after 6m35s
CI / Kotlin tests (android host + desktop) (push) Successful in 6m57s
fix(app): apply whole-review fixes (HIGH/MEDIUM/LOW) + dead code & stale comments
HIGH:
- ntfy listener: replace blocking exhausted() loop with SSE read + capped
  exponential-backoff reconnect; 60s read timeout
- GatewayClient.stop(): reset HTTP leg (http, httpCursor, sseFailures,
  usingLongPoll, lastAck)
- non-atomic shared state -> synchronized/@Volatile/AtomicLong/
  CopyOnWriteArrayList

MEDIUM:
- mediaId path-traversal guard (isValidMediaId) at network/app/fs boundaries
- loadFromCache: move ts=0 pending bubbles to end, keep stored order
- attachment placeholder tracked by identity, not filename
- optimistic ChannelStore updates on favorite/icon/automation/default
- secure-store caching (desktop map, android store)
- secret passed to keyring via stdin (macOS + Linux)
- SecureStore.clear() clears deviceId/syncCursor/fcmToken/ntfy*
- random ids for system messages; SSE EOF reconnect delay
- PowerShell $ escaping; dispose() cancels job before saving flows
- wire up "Forget pairing" in Settings
- move machine-specific org.gradle.java.home to user-level gradle.properties

LOW + dead code + stale comments:
- .aac->audio/aac; locale-fixed cost/size; 3-digit hex; hour+ latency
- Backdrop.DEFAULT defined once; notification id 24-bit; channel id cap
- remove dead FileSource, unused protocol/theme/media constants, empty
  onDispose, SDK_INT<O guard, hostFromUrl
- fix stale WS/SSE, M1/M5, and milestone KDoc comments

Verified: Kotlin desktop+android host tests, 100/100 Python gateway tests,
LSP clean, installed & running on device.
2026-08-23 12:57:35 +02:00

4.8 KiB

AGENTS.md

Hard rules

  • hermes-agent/ is a read-only research reference (git-ignored). Never commit, push, or modify it — a pre-commit hook (scripts/guard_hermes_agent.sh --staged) fails any commit that stages it. Never modify hermes core; we only install our plugin into the live hermes home.
  • Commit/push scope: when asked to "commit and push all changes," that means all changes in the working tree — it does NOT matter whether a change was made this session or earlier. Stage everything (git add .) and commit; do not cherry-pick or second-guess which files are "yours." The only exception is hermes-agent/ (git-ignored, never staged).
  • The plugin is installed by symlink: ~/.hermes/plugins/android → <repo>/gateway-plugin (already set up on this machine).

Layout

  • gateway-plugin/ — Python hermes platform plugin (android). No build step, zero new deps (stdlib + hermes-provided websockets/httpx). protocol.py is the frame source of truth, mirrored in app/shared/.../protocol/Protocol.kt and docs/protocol/frames.schema.json.
  • app/ — one Compose Multiplatform Gradle project: :shared (KMP, most of the code; jvmMain is shared by the android and desktop targets since both are JVM-based), :androidApp (thin shell, package dev.iris.app), :desktopApp (thin shell).
  • docs/ — numbered reference library; read docs/00-overview.md first. Locked decisions: docs/16-open-questions.md.

Commands

  • hermes is not on PATH: use hermes-agent/.venv/bin/hermes (venv from cd hermes-agent && uv sync).
  • Gateway: hermes gateway setup (one-time; generates IRIS_TOKEN in ~/.hermes/.env, prints the token only once) → hermes gateway (run) → hermes gateway status.
  • Android: check the device is connected first (adb devices → a5ca2a4b listed as device); then cd app && ./gradlew :androidApp:installDebug to install on the phone and live-verify changes (launch/screenshot: see ADB below).
  • Desktop: cd app && ./gradlew :desktopApp:run; packaging: :desktopApp:jpackage (app-image; -PjpackageType=deb for a .deb).
  • Python tests — never bare pytest: cd hermes-agent && scripts/run_tests.sh tests/gateway/test_android.py (no args = full suite).
  • Kotlin tests: cd app && ./gradlew :shared:testAndroidHostTest / :shared:desktopTest (host-side; jvmTest is the shared source set).
  • WS probe (gateway must be running): hermes-agent/.venv/bin/python gateway-plugin/tests/ws_probe.py --token <IRIS_TOKEN> --send "hello" — assertion flags documented in gateway-plugin/tests/README.md.
  • E2E driver (gateway must be running; it never starts/stops it): hermes-agent/.venv/bin/python gateway-plugin/tests/e2e.py.

Environment / pairing quirks

  • Pairing token: IRIS_TOKEN in ~/.hermes/.env. Pairing is manual URL + token entry; on Android there's also a QR-scan button (camera) that fills URL + token from the gateway's pairing QR. Desktop has no camera, so it's manual entry only.
  • WS default bind is 127.0.0.1; for a phone on the LAN set IRIS_WS_HOST to the gateway's LAN IP.
  • app/local.properties (sdk.dir) is git-ignored and required for Android builds.
  • google-services.json is optional: without it FCM is inert and ntfy is the push path. Public ntfy.sh SSE is flaky — self-host ntfy.
  • JDK 21 is required (the desktop Markdown renderer ships Java-21 bytecode); no system Gradle — always the wrapper (./gradlew). The JDK-21 home is machine-specific and set per machine (NOT committed): add org.gradle.java.home=/path/to/jdk21 to ~/.gradle/gradle.properties, or export JAVA_HOME=/path/to/jdk21 before running ./gradlew.
  • Desktop jpackage on Linux/JDK 21 prints a non-fatal pure virtual method called (JDK-8348560); the app works.

Testing quirks

  • hermes-agent/tests/gateway/test_android.py is a thin mirror that imports the live gateway-plugin/ package from this repo (override with IRIS_PLUGIN_DIR); HERMES_HOME is sandboxed per-test by the conftest. Tests must never touch the real ~/.hermes.
  • e2e scenarios 3 (reasoning) and 5 (commentary) are model-dependent → SKIP; 11 (push) and 12 (reconnect) are PARTIAL by design.
  • ADB: launch adb shell am start -n dev.iris.app/.MainActivity; reset pairing state adb shell pm clear dev.iris.app; screenshot adb exec-out screencap -p > /tmp/shot.png.
  • ADB UI taps: never guess tap coordinates from a screenshot — dump the hierarchy and tap the element's real bounds: adb shell uiautomator dump → adb pull /sdcard/window_dump.xml → find the node by text / content-desc / resource-id → adb shell input tap at the center of its bounds="[x1,y1][x2,y2]". Re-dump after every navigation; if a tap misses, the dump is stale — re-dump, don't nudge coordinates.
  • Gateway logs: ~/.hermes/logs/gateway.log or hermes logs --follow.