diff --git a/docs/superpowers/specs/2026-05-31-go-focus-os-design.md b/docs/superpowers/specs/2026-05-31-go-focus-os-design.md index 9127e36..3693135 100644 --- a/docs/superpowers/specs/2026-05-31-go-focus-os-design.md +++ b/docs/superpowers/specs/2026-05-31-go-focus-os-design.md @@ -93,6 +93,54 @@ on the log, at a fraction of the code. State-machine *correctness* is still enforced — by the pure transition functions at the point of transition, tested directly — just not re-litigated on every startup. +## Architecture: Ports Around a Decision Core + +AntiDrift is a **focus brain**: a decision core surrounded by pluggable +interfaces (ports) to the outside world. This is a ports-and-adapters +(hexagonal) architecture, and it is the organizing principle the whole system +grows along. New capability is almost always "a new port + adapter," not a +change to the core. + +The core is layered, and the layering is load-bearing: + +- **Skeleton — deterministic, no I/O** (`domain` + `statemachine`). The rails. + Owns what moves are *legal*. The original spec's safety property, "no + unchosen transitions," lives here: the system can only ever be in a legal + state, reached by a legal move. +- **Nervous system — the orchestrator** (`session.Controller`). The single hub. + Holds the in-memory state-of-truth, routes signals between ports and the + skeleton, persists snapshots, appends to the audit log, and broadcasts. + Everything connects through here. +- **Cortex — the advisor** (the LLM, via the `ai` port). Powerful *judgment* at + the decision points the state machine exposes — sharpen this commitment, is + this window drift, nudge me. It informs and proposes; **it can never force an + illegal transition.** The LLM is the most powerful adapter, not the kernel. + +"The brain" is all three together. Critically, the state machine — not the LLM +— owns transitions; the LLM acts only within the rails the skeleton enforces. + +### Ports + +Each port is a small Go interface with one real adapter (and a fake for tests). + +| Port | Interface | "Answers" | Adapter(s) | Milestone | +| ---- | --------- | --------- | ---------- | --------- | +| Activity | `evidence.Source` | What am I doing right now? | X11 / xgbutil (was xdotool) | M1 | +| Advisor | `ai.Assistant` (`Coach`/`JudgeDrift`/`Nudge`) | What's the smart call here? | `claude`/`codex` CLI | M2–M3 | +| Tasks | `tasks.Provider` | What *should* I be doing? | Amazing Marvin (existing `ampy` + marvin MCP) | deferred (M5) | +| Knowledge | `knowledge.Source` | Who am I; what are my priorities? | PKM / files | deferred (M6) | +| Enforcement | `enforce.Guard` | Make drift cost something | window-minimize now (legacy `minimize_other`); nftables/guardian later | deferred (M7) | +| UI | `web` | Show me; take my input | Gin + browser over SSE | M0, ongoing | + +Persistence (`store`) is infrastructure shared by the orchestrator, not a port. + +The `tasks`, `knowledge`, and `enforce` ports are **named now but built later** +— defining them keeps the architecture coherent without expanding near-term +scope. We resist designing their interfaces in detail until the milestone that +builds them, to avoid speculative abstraction (YAGNI). M1 ships the first real +port end-to-end (`evidence.Source` + X11 adapter + fake), establishing the +pattern every later port copies. + ## Package Layout | Package | Ports from | Size | Purpose | @@ -104,6 +152,9 @@ directly — just not re-litigated on every startup. | `evidence` | `window/*` + `context.rs`| small | Active-window snapshot (xdotool/X11), evidence health, allowed-context matching | | `ai` | new | small | `Coach` / `JudgeDrift` / `Nudge` behind one interface; CLI backend | | `web` | new (replaces TUI) | medium | Gin routes, SSE stream, static browser UI | +| `tasks` | new (deferred, M5) | small | `Provider` port over current to-do items; Amazing Marvin adapter | +| `knowledge` | new (deferred, M6) | small | `Source` port over personal priorities / about-me context | +| `enforce` | `window/*` (minimize) | small | `Guard` port; make drift cost something (window-minimize → nftables/guardian) | Design constraint: every package stays small and single-purpose so an AI edit loads one focused file, not a monolith. This is the concrete mechanism for the @@ -136,23 +187,36 @@ type Assistant interface { in v1. The three live roles ship first: planning **coach**, live **drift -interceptor**, ambient **nudge**. The reviewer is deferred. +interceptor**, ambient **nudge**. The reviewer is deferred. The advisor sits at +the **cortex** layer of the decision core (see "Architecture: Ports Around a +Decision Core"): it proposes and judges at the decision points the state +machine exposes, but never owns a transition. ## Roadmap Each milestone is independently shippable and gets its own spec → plan → build -cycle. +cycle. M0–M4 build the core plus the first two ports (activity, advisor) and the +UI; M5–M7 add the remaining ports, each a small interface + adapter following +the pattern M1 establishes. - **M0 — Walking skeleton.** Daemon + Gin + minimal browser UI; port `domain` + `statemachine`; snapshot persistence; manual commitment → timebox → end. Proves the full stack end-to-end. No AI, no window tracking. -- **M1 — Evidence & audit.** xdotool active-window tracking, evidence health, - per-window time stats, append-only hash-chained audit log, live SSE updates. -- **M2 — AI planning coach.** `ai` package + CLI backend; "sharpen this +- **M1 — Evidence & audit.** X11 (xgbutil) active-window tracking via the + `evidence.Source` port, evidence health, per-window time stats, append-only + hash-chained audit log, live SSE updates. Establishes the port pattern. +- **M2 — AI planning coach.** `ai` port + CLI backend; "sharpen this commitment" in the Planning view. - **M3 — Drift interceptor + ambient nudge.** Allowed-context matching + live AI drift judgment (debounced/cached) + violation friction UI. - **M4 — Look good.** A real design pass on the web UI. +- **M5 — Tasks port.** `tasks.Provider` over current to-do items; Amazing Marvin + adapter. Pull the day's commitments from real tasks. +- **M6 — Knowledge port.** `knowledge.Source` over personal priorities and + about-me context, feeding the advisor richer grounding. +- **M7 — Enforcement port.** `enforce.Guard`: make drift cost something, starting + with window-minimize (porting legacy `minimize_other`) and later the + privileged guardian / nftables path. The first sub-project to brainstorm and spec in detail is **M0**. @@ -164,11 +228,19 @@ The first sub-project to brainstorm and spec in detail is **M0**. ## Out of Scope (v1) -- Privileged enforcement: guardian process, root-owned Unix socket IPC, - nftables/DNS domain blocking, delayed admin, break-glass. +"v1" here means the first shippable arc, **M0–M4** (core + activity/advisor +ports + UI). The items below are deferred past it; some are now named ports with +their own later milestones (see Roadmap), others remain fully out of scope. + +- **Tasks, knowledge, and enforcement ports** — named in the architecture and + slotted as M5–M7, but not built in v1. The `enforce.Guard` port starts with + window-minimize; its **privileged** adapters (guardian process, root-owned + Unix socket IPC, nftables/DNS domain blocking, delayed admin, break-glass) + remain out of scope until that milestone and keep the original Stage 2 threat + boundary. - AI reviewer / session-end reflection. - Wayland compositor adapters beyond the existing degraded reporting. -- Planner/project/task model and outcome writeback. +- Planner/project model and outcome writeback (beyond the M5 tasks port). - Presence sensing. These remain governed by `commitment-os-design.md` and may return as later