7d69a1f320
Loosen AntiDrift's session controller into Keel's general collect→brain→act
loop. A new internal/harness runs at most one mode.Mode at a time, fanning
async completions out to the web SSE and status-bar surfaces.
- internal/mode: the Mode contract (Kind/Command/View/Active) plus optional
EvidenceConsumer and Expirer ports, and the surfacing Envelope.
- internal/mode/focus: the former session/domain/statemachine packages moved
under the mode, now satisfying the harness contracts unchanged.
- internal/mode/offscreen: a one-shot away-from-desk mode built on the new
ai.Proposer, which turns a life-domain brief into one off-screen action.
- cmd/keeld replaces cmd/antidriftd; daemon wires focus + offscreen factories
with per-mode persistence under ~/.keel/modes/<kind>.
- Finish the rename: KEEL_* env refs in the README, /keeld build artifact
ignored, stale antidriftd binary removed.
Include the design + implementation plan this refactor was built from under
docs/superpowers/{specs,plans}/2026-06-04-controller-refactor*.md.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
108 lines
5.4 KiB
Markdown
108 lines
5.4 KiB
Markdown
# Keel
|
|
|
|
A **human-harness** that helps Felix hold course toward his higher goals: it
|
|
collects his real state from the tools he already uses, hands it to a swappable
|
|
brain (`claude` / `codex` / Hermes), and acts on the reply through gated
|
|
effectors — surfaced on a web UI and the WM status bar. The brain, the storage
|
|
(ActivityWatch), and the data sources are reused; the harness is what we build.
|
|
|
|
> **Architecture:** [`docs/keel-architecture.md`](docs/keel-architecture.md) is the
|
|
> source of truth. **Agents:** read [`AGENTS.md`](AGENTS.md) first.
|
|
|
|
## Modes
|
|
|
|
Keel's controller is now a generic **harness** that hosts one *mode* at a time,
|
|
and the daemon ships two:
|
|
|
|
- **Focus (AntiDrift)** — Keel's first mode: a personal focus operating system
|
|
that treats each work session as an explicit commitment (next action, success
|
|
condition, timebox) and makes drift visible. The milestone history below is
|
|
this mode's record.
|
|
- **Off-screen** — an away-from-the-desk mode: it reads today's Marvin tasks plus
|
|
the `~/owc` goals and life-domain `bug-*.md` notes, asks the brain for one
|
|
worthwhile off-screen action, and on confirm files it as a Marvin task.
|
|
|
|
When no mode is active the web UI shows a **launcher** to start either one.
|
|
Further modes (body, review, capture) are aspirational. The milestone history
|
|
below records the focus mode's development.
|
|
|
|
> **Naming:** the rename is complete — directory, repo, Go module, binary,
|
|
> runtime, and env are all `keel` (`keeld` binary, `~/.keel/` runtime,
|
|
> `KEEL_*` env). If you have a live ledger under the old `~/.antidrift/`
|
|
> directory, move it manually to `~/.keel/`.
|
|
|
|
## Run
|
|
|
|
```bash
|
|
go run ./cmd/keeld
|
|
```
|
|
|
|
The daemon serves a local web UI at http://localhost:7777 and opens your
|
|
browser. Per-mode state is persisted under `~/.keel/modes/<kind>/` (e.g.
|
|
`~/.keel/modes/focus/state.json`).
|
|
|
|
## Test
|
|
|
|
```bash
|
|
go test ./...
|
|
```
|
|
|
|
## Status
|
|
|
|
**M8 (Tier A) — Enforcement (window-minimize).** Drift finally costs something.
|
|
A planning-screen "Enforce focus" toggle arms the new `enforce.Guard` port: when
|
|
the drift judge confirms the active window is off-task, the daemon minimizes that
|
|
window (native X11, no `xdotool`). It is unprivileged, per-session (the chosen
|
|
enforcement level rides the snapshot), and degrades to today's advisory behavior
|
|
when off, unwired, or on a platform without the X11 adapter.
|
|
|
|
M7 (reflection): when a session ends, a fourth AI role — the reviewer —
|
|
reflects on it, read against your recent sessions, and produces two short
|
|
lines: a recap shown on the Review screen, and a carry-forward takeaway that
|
|
grounds the coach the next time you plan. It runs once asynchronously on
|
|
entering Review, never blocks the End button, and degrades gracefully — with
|
|
no backend (or a slow/failed call) Review and Planning behave exactly as
|
|
before. The carry-forward is snapshot-persisted (latest-wins) and composes
|
|
into the coach's grounding; the reflection lines are short and cross the wire
|
|
by design, while the knowledge profile still does not.
|
|
|
|
M6 (knowledge port): the planning coach now sharpens intents against who you
|
|
actually are. A single profile file (`~/.keel/knowledge.md`, overridable
|
|
via `KEEL_KNOWLEDGE_FILE`) holds your standing context — priorities,
|
|
values, what counts as good work — and the daemon loads it asynchronously on
|
|
entering planning, mirroring the tasks fetch. The cached text grounds the AI
|
|
coach prompt only; the drift judge and nudge are untouched, and the profile text
|
|
never crosses the wire. A subtle planning-screen indicator shows whether the
|
|
profile loaded and from where, with a "change" affordance to repoint at another
|
|
file. It degrades gracefully — a missing, blank, or unreadable file just leaves
|
|
the coach ungrounded, and planning still works.
|
|
|
|
M3.5 (semantic nudge): the drift interceptor catches the *wrong app*, but not
|
|
the *wrong work inside a right app*. M3.5 adds a third, ambient AI role that —
|
|
only while you are on-task in an allowed app — periodically reads your recent
|
|
window titles and, if the trajectory has wandered from the commitment, shows a
|
|
soft, dismissible "Heads up" line (no interrupt, no buttons to fight). It is
|
|
debounced to roughly one check every five minutes, reuses the same CLI backend
|
|
as the coach and drift judge, and degrades gracefully — without it, everything
|
|
else still works.
|
|
|
|
M3 (drift interceptor): while a commitment is Active, the daemon watches the
|
|
focused window. A cheap local match against the session's allowed window classes
|
|
is authoritative for on-task; only unmatched windows are sent to the LLM drift
|
|
judge (debounced and cached per class, run asynchronously). When it judges you
|
|
off-task, the active view shows a dismissible interrupt: "Back to task", "This is
|
|
on task" (which adds the app to the session's allowed list), or "End session".
|
|
The drift judge degrades gracefully — without it, local matching still runs.
|
|
|
|
M2 (AI planning coach): in the Planning view, a rough intent is "sharpened"
|
|
into a structured commitment (next action, success condition, timebox) by an
|
|
LLM CLI backend (claude or codex, selectable via `KEEL_AI_BACKEND`). The
|
|
coach runs asynchronously and degrades gracefully — manual planning always
|
|
works.
|
|
|
|
M1 (evidence & audit): active-window tracking, two-tier evidence store
|
|
(disposable per-session raw log + permanent hash-chained session summaries),
|
|
and live SSE updates. Live drift judgment and the ambient nudge arrived in later
|
|
milestones (M3 and M3.5 above; the original roadmap spec has since been removed —
|
|
code and git history are the record).
|