28c974fe1d
Shipped in Spark Control v0.21.0: status badge + Update/Restart/Stop-Start/Logs
tile. All three exit criteria confirmed. matrix-bridge needed no code change.
- AGENTS.md: Current state + ROADMAP Phase 3 -> DONE; Deploy switched scp -> git
pull (Update button); D10 stamped; new Infra fact for the Spark->Gitea path and
the load-bearing IdentitiesOnly ssh-config pin the Update button depends on.
- spark-control-integration.md: trimmed from dev spec to live contract (dropped
sudo -iu fallback and dev-side scaffolding; folded in direct-as-modelo, the
Gitea key gotcha, restart cadence, and the LAN-only HTTP API).
- README: dropped stale "pre-Phase 0" status; Setup reframed for a fresh install.
Deferred follow-up: badge reflects container liveness only, not Matrix
connectivity; HEALTHCHECK + {{.State.Health.Status}} is the matrix-bridge-side fix.
75 lines
5.0 KiB
Markdown
75 lines
5.0 KiB
Markdown
# ROADMAP — matrix-bridge
|
||
|
||
Phased build plan. Near-term status lives in `AGENTS.md` → `## Current state`; this file is
|
||
the longer arc. Substance threshold is **N = 3** real uses per phase — exits are falsifiable
|
||
(it worked 3 real times), never checkboxes.
|
||
|
||
Phase 0 (the current first milestone) lives in `AGENTS.md` `## Current state`; it writes no
|
||
bot code — foundation + proving the manual chain by hand. The phases below are what comes
|
||
after it.
|
||
|
||
---
|
||
|
||
## Phase 1 — Single-room bot
|
||
|
||
- matrix-nio bot in a container on the Spark, logged in as a bot Matrix user.
|
||
- One hardcoded room → one repo. Any message in it spawns a session via the Mac wrapper.
|
||
- Carry over from Phase 0's proven launch chain (`ssh mac-bridge → gui-launch.sh → launch-claude.sh`):
|
||
- **Bake the SSH key + `mac-bridge` config into the container** (modelo's `~/.ssh` won't exist there).
|
||
- **Named sessions for the phone app.** Pass `claude -n "<repo> — <topic>"` so the Remote Control
|
||
conversation index is readable (project + topic). Bot derives `<topic>` from the message; confirm
|
||
whether the app labels off `-n` or `--remote-control <name>`. Plumb a name arg through the wrappers.
|
||
- **Quote-safe message passing.** Bot builds the SSH command with `shlex.quote`; `gui-launch.sh`
|
||
already isolates the osascript/shell layers via a `%q` temp script — stress-test with hostile text.
|
||
- **Fail loud, not silent.** Detect a stalled launch (untrusted-repo trust gate, or a reset Terminal
|
||
Automation grant) and report it back into the room instead of hanging.
|
||
- **Exit (falsifiable):** 3 consecutive real messages each correctly launch a drivable
|
||
session on the phone.
|
||
|
||
## Phase 2 — Multi-room routing — DONE (2026-06-15)
|
||
|
||
- Room → repo mapping table; the bot routes by `room_id` (config over code).
|
||
- **Exit (falsifiable):** 3 real uses across ≥2 rooms, correct repo every time, zero
|
||
wrong-directory launches. *Met — owner-confirmed N=3 pass.*
|
||
|
||
## Phase 3 — Spark Control integration — DONE (2026-06-16)
|
||
|
||
- Bot container status surfaced on the Spark Control dashboard.
|
||
- One-click update (pull + restart) wired the same way Spark Control drives the Sparks today
|
||
(SSH/commands behind a button).
|
||
- **Exit (falsifiable):** bot status is visible and the bot can be updated/restarted from the
|
||
panel. *Met — shipped in Spark Control v0.21.0; all three controls confirmed working.*
|
||
- **Shipped:** matrix-bridge tile (status badge + Update / Restart / Stop-Start / Logs) running
|
||
the spec's SSH commands; the Spark's `~/matrix-bridge` is now a Gitea clone tracking `master`.
|
||
matrix-bridge needed no code change. Deviation: Spark Control connects directly as `modelo` (no
|
||
`sudo` wrap — no passwordless sudo on this Spark). Live command contract + the Gitea key pin the
|
||
Update button depends on: `docs/spark-control-integration.md`.
|
||
- **Deferred follow-up:** badge reflects container liveness only, not Matrix connectivity — a
|
||
Docker `HEALTHCHECK` (bot-side liveness signal) would let the tile read `{{.State.Health.Status}}`.
|
||
matrix-bridge-side change; do it if/when "running but silent" bites.
|
||
|
||
## Phase 4+ — Future direction (documented, not yet scoped to build)
|
||
|
||
- **Intent-routing brain (D8).** Qwen3 via Spark Control as a smart dispatcher: given
|
||
knowledge of all repos/contexts, parse a freeform message and decide *which* repo/context
|
||
applies and *what* context to inject — not a task-vs-session classifier. MUST run on a local
|
||
model. Depends on the deterministic core (Phases 1–2) working first; the architecture must
|
||
not foreclose it.
|
||
- **Thread-based session continuity.** A Matrix thread = a distinct session/sub-context within
|
||
a repo. The first natural extension after multi-room routing.
|
||
- **Nextcloud / CalDAV output integration.** Routing Claude/bot *outputs* into Nextcloud
|
||
(Matrix ↔ Claude ↔ Nextcloud). Real interest, unscoped — not until Nextcloud Tasks/CalDAV
|
||
is actually in use.
|
||
- **E2EE (D9).** Add matrix-nio end-to-end encryption (libolm) if the bot ever handles
|
||
sensitive content over untrusted transport. Low priority while everything is WireGuard-local.
|
||
- **Headless "ask" mode — SHIPPED 2026-06-16.** A `?`-prefixed message runs `claude -p "<rest>"`
|
||
one-shot in the room's repo and posts the **full** answer back into the room — Matrix as a
|
||
request/response interface, not just a trigger. Built via `scripts/ask-claude.sh` (login-shell
|
||
wrapper) + the bot's `?`-dispatch (`run_ask`/`ask`). Resolved design choices: selector = `?` prefix
|
||
(per-message; the room still picks the repo); output posted in full, chunked under Matrix's event
|
||
cap (no truncation — chosen explicitly); auth = the long-lived `claude setup-token`
|
||
(`CLAUDE_CODE_OAUTH_TOKEN`, Approach A / D12) because a non-GUI SSH session can't reach the
|
||
Keychain; sovereignty unchanged (`claude -p` uses the subscription, no frontier API on payloads).
|
||
*Remaining open Qs:* very-long-output handling beyond chunking (thread / attach file); the
|
||
first-run folder-trust gate for a repo `claude` has never been opened in.
|