The plugin is named 'iris' (IrisAdapter, IRIS_HOME_CHANNEL, label Iris), but several docs still referred to it as the android platform/plugin and to the product as 'the Android app'. Rename name-mentions to iris/IRIS and product-mentions to 'Iris app'; keep legitimate OS references (androidApp, Android SDK, Android 10, androidx, test_android.py, ...). Also includes pi-lens markdown-lint autofixes (table spacing, trailing newlines) in the touched files.
78 lines
5.2 KiB
Markdown
78 lines
5.2 KiB
Markdown
# 16 — Decisions & Open Questions
|
||
|
||
## Locked decisions (from planning, 2026-08-19)
|
||
|
||
| # | Decision | Choice | Rationale |
|
||
| --- | --- | --- | --- |
|
||
| 1 | Desktop app tech | **Compose Multiplatform** | Desktop = "the Iris app, tweaked"; share protocol/state/UI. |
|
||
| 2 | Push backend | **Both — ntfy default, FCM optional** (issue #10: FCM metadata — title, device token — is routed via Google's servers, contradicting the privacy claim; ntfy keeps it on your own infrastructure) | `IRIS_PUSH_BACKEND`. |
|
||
| 3 | Media transport | **Over the WebSocket** | One transport, zero new Python deps; chunked binary frames. |
|
||
| 4 | Phone default layout | **User-toggleable, single-pane default** | App-like on phones; auto two-pane on large screens; desktop defaults two-pane. |
|
||
|
||
## Additional decisions made during planning
|
||
|
||
| Decision | Choice | Note |
|
||
| --- | --- | --- |
|
||
| Connection point | **Messaging gateway** (platform plugin `iris`), not `tui_gateway` | Makes cron/`send_message`/slash/coexistence native. |
|
||
| Plugin style | **Community plugin** (`register(ctx)`) | Zero hermes-core changes. |
|
||
| Python deps | **None new** (`websockets` + `httpx` are core) | Respects hermes pinning policy. |
|
||
| Tool events | **Structured frames; app controls verbosity** | Gateway sends full data; app = everything/truncated/nothing. |
|
||
| Reasoning | **Adapter splits the `show_reasoning` prefix** | Clean `reasoning` field → collapsible block above message. |
|
||
| Channels/threads | **Map onto `chat_id`/`thread_id`** | Existing gateway primitives; cron targets them. |
|
||
| Offline | **SQLite outbox + sync cursor** | Catch-up on reconnect; push on disconnect. |
|
||
| Default chat id | `default` | Home channel + cron default. |
|
||
| WS port | `8790` (default) | Configurable. |
|
||
| minSdk | 26 (test device API 29) | Broad coverage. |
|
||
| Frame routing | **Broadcast to all connected devices** (no per-chat subscribe) | Single-user model; simpler. |
|
||
| Initial channel load | **`history` frame** (paginated) | `sync` only replays outbox; `history` loads full messages. |
|
||
| Slash menu | **`commands.catalog` + `commands.complete`** frames | Gateway serves catalog; app renders bottom sheet + autocomplete. |
|
||
| Agent lifecycle | **`agent.busy`/`agent.idle`** events + **`agent.stop`/`agent.steer`** requests | App shows thinking indicator; user can abort or steer mid-turn. |
|
||
| Local DB (KMP) | **SQLDelight** (not Room) | Room is Android-only; SQLDelight works in commonMain for both platforms. |
|
||
| Voice input | **Record → upload as audio media** (no client-side STT) | Agent's STT (if configured) handles transcription. |
|
||
| QR encoder | **Pure-stdlib** (`gateway-plugin/qr.py`) | No `qrcode`/`segno`/Pillow; byte mode, EC M→L, v1–10, ISO penalty masking. Keeps the plugin's zero-new-dep rule. |
|
||
| QR scanner | **ML Kit barcode** (not zxing-android-embedded) | On-device, no Google Play services; better accuracy/latency, Google-maintained. Android-only (desktop has no camera). |
|
||
|
||
## Open questions (resolve during implementation)
|
||
|
||
These are **implementation-time** details, not blockers. Each has a default we
|
||
will proceed with unless you say otherwise.
|
||
|
||
1. **Tool-progress vs commentary classification (M2).** The exact signal that
|
||
distinguishes a tool-progress `send()` from a regular `send()`/commentary in
|
||
the legacy path. *Default:* per-chat turn-state machine + line-format
|
||
heuristic, **verified empirically** with the `ws_probe.py` harness against the
|
||
live gateway. If a clean metadata marker exists, prefer it.
|
||
2. **Reasoning prefix format stability (M2).** We split on the `code`-style
|
||
`💭 **Reasoning:**\n```\n…\n```\n\n` prefix. *Default:* set
|
||
`reasoning_style: code` for iris and split on that; fallback = no
|
||
reasoning field (full text) if the prefix isn't found. Verify in M2.
|
||
3. **Per-device tokens vs shared token (M1/M5).** *Default (v1):* shared
|
||
`IRIS_TOKEN` + optional `IRIS_ALLOWED_USERS` device allowlist.
|
||
Per-device revocable tokens are a stretch.
|
||
4. **Desktop video backend (M6).** *Default:* `libmpv`/`mpv`-backed Compose
|
||
surface, WebView fallback. Confirm `mpv` availability on target OSes during
|
||
M6.
|
||
5. **Remote access default (M1).** *Default:* document Tailscale as the
|
||
recommended remote path; WSS + reverse proxy as alternatives. No public bind
|
||
by default.
|
||
6. **Streaming cadence (M2).** If live updates look chunky, tune
|
||
`display.platforms.iris.streaming` / consumer edit interval. *Default:*
|
||
follow global streaming config.
|
||
7. **App package name / branding.** *Default:* applicationId `dev.iris.app`,
|
||
app name "Iris". Confirm final product name + package + icon.
|
||
8. **ntfy in-app listener (M5).** *Default:* a foreground service maintaining the
|
||
ntfy subscription (no extra native lib) — or a lightweight ntfy client lib if
|
||
one is acceptable. Decide in M5.
|
||
9. **Auto-update for desktop (M7).** *Default:* out of scope (manual download).
|
||
Revisit post-v1.
|
||
10. **iOS port.** Out of scope for v1 (protocol is transport-agnostic, so it's a
|
||
future port). No action now.
|
||
|
||
## Explicit non-goals (v1)
|
||
|
||
- Multi-user / group chat (personal 1-user agent).
|
||
- End-to-end encryption (transport WSS only).
|
||
- Standalone-cron delivery while the gateway process is fully down (best-effort
|
||
push only).
|
||
- iOS.
|