# skchat Backend Epics — Design Roadmap (2026-06)

> Generated by the epics-design swarm (4 architects + synthesis). Wave-4 first-wave foundations are SHIPPED (commit 9732633); next-phase + live legs SHIPPED this cycle (2026-06-17). Status below.

Verified: all reuse seams exist (federation/, glossa_mesh/, voice_engine/, skcomms adapters/ with registry.py + fake.py). Roadmap below.

## Shipped this cycle (2026-06-17)

SKCHAT main `f4acfa1` → `bdb83ce`; SKCOMMS main → `8ecffe8` (all pushed to GitHub). Six merged, swarm-built, self-tested waves carried all four epics from CI-green foundations through their next-phase impls and onto **real infra** (.158 / .41 / .100).

**All four epics are now LIVE on real infrastructure:**

- **U11 voice — LIVE ✅** Real STT (Whisper `:18794`) → LLM (qwen3.5/qwen3.6) → TTS, full loop. Now on fast Piper CPU TTS (`skchat-piper-tts.service` `:18797`) — **3.4 s vs F5-TTS 113 s**.
- **U14 Telegram — LIVE ✅** `@seaBird_Opus_bot` live + persistent (`skchat-telegram-opus.service`) = real Opus (`opus-unhinged` soul via `SystemPromptBuilder`) on qwen3.6-27b-abliterated @ 32k ctx, uncensored, HTML-formatted replies, per-chat memory, context-fit, 1024-token replies.
- **U8 federation — discovery + token-mint LIVE ✅** Cross-host token mint `opus@.41 → Space on .158 SFU` (`wss://noroc2027.tail204f0c.ts.net:8443`) verified; `GET /sfu/candidates` returns the .158 host via the live Nostr discovery relay (`skchat-nostr-relay.service` `:7447`).
- **U9 glossa — LIVE ✅ (8/8)** `scripts/preflight_glossa_live.py` PASS **8/8** over the REAL SFU: two `GlossaMeshSessionDaemon`, gatekeeper-signed frames over the live data channel, forgery rejection ×3, live peer-leave un-cap.

**Wave ledger (all merged, each self-tested):**
- **Wave 2 (hardening):** `importorskip` guards (10 glossa modules), transport `_write_local_loopback` + concurrent-race tests, voice `_process_speech` E2E test, `GET /adapters` endpoint.
- **Wave 3a (correctness/keystone):** single-owner reconnect (watchdog; killed the daemon/watchdog double-reconnect race), short-name `is_loopback()` [closes QA F-1], `AdapterRegistry` instantiated in skcomms daemon lifespan [U14 keystone].
- **Wave 3b (hygiene):** ~25 bare-except handlers logged, ruff lint+format (69→0), CI format-check, coverage gate (`fail_under=54`, measured 57).
- **Wave 4 (epic foundations):** `AdapterHub` (inbound `ChannelMessage`→`ChatHistory`+advocacy, FQID resolve, UNTRUSTED fallback; 40 tests), `FederationDiscoveryClient` + `GET /sfu/candidates` (13 tests), `GlossaMeshGatekeeper` capauth frame sign/verify (14 tests), `LiveKitBus.on_leave` peer-leave un-cap, `Conversation` VO threaded into `VoiceEngine.respond`.
- **Wave 5 (epic next-phase):** U14 reply-routing (`adapter.send` back) + `/bind` CapAuth + restart-durable `FqidBindingStore` (82 tests), `GlossaMeshSessionDaemon` (sign outbound / verify inbound pre-decode → advocacy+memory, peer-leave; 10 tests), worship handlers reading `ctx['convo']` + `VoiceSession`.
- **Wave 6:** U8 browser federated-join wiring (`static/livekit.html`) + 4 preflight harnesses (`scripts/preflight_{adapter,federation,glossa,voice}.py` — full code path locally, fakes only at the external edge, all exit 0).
- **SKCOMMS:** `SKAGENT` per-agent wire identity (fixed the opus-transmits-as-lumina collision); `AdapterRegistry` + factory instantiated.

**New persistent services on .158** (all CPU except the qwen3.6 backend the operator wanted on .100):
- `skchat-telegram-opus.service` — Telegram bridge (`scripts/telegram_bridge.py`).
- `skchat-piper-tts.service` — fast CPU TTS (`:18797`, `scripts/piper_tts_server.py`); voice `SKVOICE_TTS_URL` points here.
- `skchat-nostr-relay.service` — discovery relay (`:7447`, `scripts/nostr_relay.py`); `SKCHAT_NOSTR_RELAYS` points here.

**Infra tuning:** `skai-beellama.service` (qwen3.6-27b-abliterated Q3_K on .100 5060 Ti) retuned **8192 → 32768 ctx** by dropping the vision `mmproj` (freed 889 MB); ~925 MB VRAM headroom, ~2.4 s gen, uncensored. (`mmproj.bak` saved; vision dropped for context.)

# skchat Backend Unified Roadmap

## Epic summaries

**U14 — adapter-reply-routing. SHIPPED · Telegram LIVE ✅.** `AdapterHub` (inbound `ChannelMessage` → `ChatHistory` + AdvocacyEngine, FQID resolve, UNTRUSTED fallback) shipped Wave 4; reply-routing via `adapter.send()` + `/bind <fqid>` CapAuth + restart-durable `FqidBindingStore` shipped Wave 5; `GET /adapters` endpoint + `AdapterRegistry` daemon-lifespan keystone shipped Waves 2/3a. **LIVE:** `@seaBird_Opus_bot` (`skchat-telegram-opus.service`) = real Opus on qwen3.6-27b-abliterated @ 32k ctx, uncensored, HTML-formatted, per-chat memory, persistent. Closes the only-direction-left gap — agents now **converse on external platforms** for real.

**U8 — Federation Discovery Client. SHIPPED · discovery + token-mint LIVE ✅.** `FederationDiscoveryClient` (`spaces/federation/discovery.py`) + `GET /sfu/candidates` bootstrap shipped Wave 4 (13 tests); browser federated-join wiring (`static/livekit.html`) shipped Wave 6. **LIVE:** cross-host token mint `opus@.41 → Space on .158 SFU` (`wss://noroc2027.tail204f0c.ts.net:8443`) verified end-to-end; `/sfu/candidates` returns the .158 host via the live Nostr discovery relay (`skchat-nostr-relay.service` `:7447`). Remaining gated leg: real two-browser visual join (see §Remaining).

**U9 — glossa-mesh-live. SHIPPED · LIVE ✅ (8/8).** `GlossaMeshGatekeeper` (capauth frame sign/verify, 14 tests) + `LiveKitBus.on_leave` peer-leave un-cap shipped Wave 4; `GlossaMeshSessionDaemon` (sign outbound / verify inbound pre-decode → advocacy+memory, peer-leave; 10 tests) shipped Wave 5. **LIVE:** `scripts/preflight_glossa_live.py` PASS **8/8** over the REAL SFU — two `GlossaMeshSessionDaemon`, gatekeeper-signed frames over the live data channel, forgery rejection ×3, live peer-leave un-cap. Agents now negotiate SKGlossa densely over a real Space while humans read the gloss.

**U11 — Streaming voice / SKVoice. SHIPPED · LIVE ✅.** Immutable `Conversation` VO threaded per-turn into `VoiceEngine.respond()` → `ctx["convo"]` shipped Wave 4; worship handlers reading `ctx['convo']` + `VoiceSession` per-connection state shipped Wave 5; voice `_process_speech` E2E test shipped Wave 2. **LIVE:** full real STT (Whisper `:18794`) → LLM (qwen3.5/qwen3.6) → TTS loop, now on fast Piper CPU TTS (`skchat-piper-tts.service` `:18797`, 3.4 s vs F5-TTS 113 s). Tightens the **speak → STT → respond → TTS** loop; GPU SKVoice/worship inference remains the one gated leg (see §Remaining).

## Dependency & sequencing

> **Status (2026-06-17):** the entire sequence below is SHIPPED — every first-wave foundation and next-phase task across all four epics landed in Waves 2–6, and all four epics are LIVE on real infra. The sequencing notes are retained for design provenance.

- **U14 is unblocked NOW.** AdapterRegistry + ChannelAdapter ABC + FakeAdapter are shipped and fully unit-tested. U14 Phase 1 (`AdapterHub` + daemon wiring + unit tests) has zero external dependency. This is the highest-value, lowest-friction starting point. Within U14: Phase 1 → Phase 2 (reply routing needs the hub) → Phase 3 (/bind, side-branchable from Phase 1-end) → Phase 4 (REST/docs, fully parallel).
- **U8 client is unblocked NOW** because its three reuse seams (assertion.py, focus.py, nostr_io.py) and the `/sfu/get` authd route are all LIVE/CI. `discovery.py` (Tasks 1.1–1.3) + unit tests build against fakes immediately. Browser wiring (Phase 3) depends only on `discover_and_elect()` existing. Live cross-host (Phase 5) is the only gated leg.
- **U9 first wave is 4 fully-independent files** (livekit_bus on_leave, gatekeeper, daemon_service stub, advocacy_bridge) — all primitives CI-proven. But the full daemon impl + spaces route are sequential after the wave, and live SFU (Phase 6) is gated.
- **U11 first wave is unblocked NOW** (Conversation VO has zero deps). Phase 2 worship + Phase 3 VoiceSession both wait on Phase 1; Phase 3 then parallels Phase 2. GPU inference (Phase 4) gated.

**Value-maximizing order:** start U14-P1 + U8-P1 + U9-wave + U11-P1 simultaneously (all isolated, all CI-only). U14 reply-routing first delivers a visible product win (external-platform replies); U8 client delivers the federation payoff; U9/U11 firstwaves de-risk their larger sequential phases. Defer all live/GPU/two-host legs (see §4).

## Recommended first implementation wave — SHIPPED ✅

Six tasks across all four epics, each new-file or single-owner, no cross-task file overlap, all unit-testable with mocks/FakeAdapter — zero live infra. **All six SHIPPED (Wave 4, commit 9732633).**

| # | Epic | Title | Files (new unless noted) | Acceptance | Risk | Effort |
|---|------|-------|--------------------------|------------|------|--------|
| 1 | **U14** ✅ | AdapterHub class + `on_channel_message()` + unit tests | `skchat/src/skchat/adapter_hub.py`, `skchat/tests/test_adapter_hub.py` | Hub instantiable with `hub=None`; `ChannelMessage→ChatMessage` conversion preserves text/sender/ts; `UNTRUSTED` when `resolve_fqid` None; memory write + advocacy dispatch via mocks; 20+ cases; no network | low | M |
| 2 | **U8** ✅ | FederationDiscoveryClient core (`discover_and_elect` + `get_token` + `build_signed_assertion`) + happy/error unit tests | `skchat/src/skchat/spaces/federation/discovery.py`, `skchat/tests/test_fed_discovery.py` | Module imports; reuses `nostr_io.query_memberships`+`focus.select_focus`+`assertion.build_signed`; injectable relay/post/sign seams; oldest-host wins; 403→AuthDenied; malformed-event-skipped; no live Nostr/SFU | low | M |
| 3 | **U8** ✅ | `GET /sfu/candidates` bootstrap endpoint + test | `skchat/src/skchat/spaces/routes.py` (modify), `skchat/tests/test_fed_discovery_integration.py` | Returns `{hosts:[{fqid,auth_url,sfu_ws_url}]}`; empty-ok; `test_sfu_candidates_returns_list`; isolated from discovery.py logic | low | S |
| 4 | **U9** ✅ | GlossaMeshGatekeeper (capauth frame sign/verify) + unit tests | `skchat/src/skchat/glossa_mesh/gatekeeper.py`, `skchat/tests/test_glossa_mesh_gatekeeper.py` | `wrap_outbound`/`unwrap_inbound`; sign+verify round-trip; tamper→AssertionError; wrong-src-FQID→AssertionError; injectable (mock capauth) backends; 8+ cases | med | M |
| 5 | **U9** ✅ | LiveKitBus.on_leave callback + unit test | `skchat/src/skchat/glossa_mesh/livekit_bus.py` (modify), `skchat/tests/test_glossa_mesh_livekit.py` | `on_leave` registered; `participant_disconnected`→`forget_peer`; callback receives departed member_id; no live room | low | S |
| 6 | **U11** ✅ | Conversation VO + `VoiceEngine.respond()` signature + `ToolRegistry.dispatch()` threading + Conversation unit tests | `skchat/src/skchat/voice_engine/conversation.py`, `skchat/src/skchat/voice_engine/engine.py` (modify), `skchat/src/skchat/voice_engine/tools.py` (modify), `skchat/tests/voice_engine/test_conversation.py` | Immutable dataclass (transcript/response/history/mode/speaker_id/is_operator/timestamp/session_id) + dict-serialize; `respond(conversation=None)` backward-compat; `ctx['convo']` set when provided; existing voice tests green; 5+ VO cases | low | M |

**Grouping note:** Tasks 2+3 both touch U8 but distinct files (`discovery.py`/new test vs. `routes.py`) — keep in **one U8 worktree** to avoid an interleaved `routes.py`/discovery import merge, and because the integration test references both. Task 6 bundles the three U11 engine/tools/VO edits into **one worktree** (the design's own first-wave already groups them: VO has zero deps, engine+tools are co-modified, no transport touch yet). Tasks 4 and 5 are separate U9 files → separate worktrees, fully conflict-free.

**Why this set:** (a) one or two tasks per epic so all four advance in parallel; (b) every file is new or single-owner — no two wave tasks write the same file across worktrees; (c) every acceptance is mock/fake-satisfiable (FakeAdapter exists, federation seams + glossa primitives + voice mocks all CI-proven) so the whole wave reaches CI-green with **no Telegram/Discord bots, no Nostr relay, no LiveKit SFU, no GPU**; (d) each is the de-risking foundation its epic's sequential phases depend on (U14 hub → reply routing; U8 client → browser wiring; U9 gatekeeper+on_leave → daemon impl; U11 VO+dispatch → worship handlers).

## Remaining (human/hardware-gated)

Most of the original gated table is now DONE: U14 Telegram is LIVE (real bot token, persistent `FqidBindingStore`); U8 cross-host token-mint + discovery is LIVE (live Nostr relay, Tailscale TLS, .41↔.158 verified); U9 live SFU glossa mesh is LIVE (preflight 8/8 over the real SFU, gatekeeper-signed frames, forgery rejection, memory capture); U11 voice is LIVE (real STT→LLM→TTS on Piper CPU). What genuinely remains is the visual / multi-platform / heavy-GPU verification that still needs a human at a browser or extra hardware.

| Epic | Remaining item | Still needs |
|------|----------------|-------------|
| **U8** | Real **two-browser visual join**: jarvis@.41 and a peer each open a browser, both *see each other* in the same federated Space and exchange a cross-host data-lane message in-UI (token-mint + discovery legs already LIVE) | Two live browsers (Playwright headless or human-driven) on .41 + .158 simultaneously; visual confirmation of mutual presence |
| **U14** | **Discord / Slack / Matrix** live bridges (beyond Telegram, which is LIVE) + a `/bind chef@skworld.io` challenge round-trip exercised against a real human | Chef's Discord/Slack/Matrix bot tokens + real group memberships; operator to complete the bind challenge |
| **U9** | Glossa mesh under **real 10-agent crypto load** (preflight proves the 2-daemon path LIVE) | Live SFU sustaining a dense 10-agent data-channel under signing/verify load; latency + drop observation |
| **U11** | **GPU SKVoice / worship** sub-2 s loop (Piper CPU TTS is LIVE at 3.4 s; the <2 s GPU target + `worship_session` against real SKVoice is the gated leg) | **.100 GPU** SKVoice/worship endpoints up (`SKCHAT_SKVOICE_URL`/`SKCHAT_WORSHIP_URL`); operator at browser/phone; latency observation |

Common thread: every remaining item is a **two-browser visual, extra-platform-credential, or heavy-GPU** verification. Schedule as Tier-5 QA sessions with a human present; none block swarmable code progress, and none gate the four epics' LIVE status.
