felixm 13633ffabf Fix drift sync: per-window verdicts, forgiving class match, event-driven status bar
Three independent defects made the focus state feel flaky and let the OS
status bar disagree with the web UI:

- Status file lagged the web by up to a minute: it rendered only on a 60s
  ticker with no hook into state changes. notify() now fans out to multiple
  listeners (AddOnChange) and the writer has a coalesced Wake() so drift
  reaches the bar as promptly as the browser.

- A brief off-task visit could latch drift for the whole session. Sibling
  windows of one app (a browser's reading tab vs its chat tab) share a window
  class, but the judge verdict was cached by class alone, so one tab's verdict
  poisoned the rest and never re-judged. Cache is now keyed by class + scrubbed
  title (judgedWindows) so siblings are judged independently.

- Allowed-class matching was exact equality, so a short token ("brave") never
  matched the real WM_CLASS ("Brave-browser") and every window was routed to
  the LLM. Matching is now substring-based, and planning surfaces the live
  window class with a click-to-add chip so users pick a token that matches.

Also fix the planning checkbox alignment: the global full-width input rule was
stretching the "Enforce focus" checkbox; scope it out for checkboxes.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-04 09:25:30 -04:00
2026-06-04 09:06:14 -04:00
2022-08-13 09:19:19 -04:00
2026-06-04 09:06:14 -04:00

AntiDrift

A personal focus operating system: treat each work session as an explicit commitment (next action, success condition, timebox), and make drift visible.

See docs/superpowers/specs/ for the design.

Run

go run ./cmd/antidriftd

The daemon serves a local web UI at http://localhost:7777 and opens your browser. State is persisted to ~/.antidrift/state.json.

Test

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 (~/.antidrift/knowledge.md, overridable via ANTIDRIFT_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 ANTIDRIFT_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 arrive in later milestones (see the roadmap in docs/superpowers/specs/2026-05-31-go-focus-os-design.md).

S
Description
AntiDrift is an app that empowers you to utilize your computer mindfully.
Readme 1.7 MiB
Languages
Go 91.5%
JavaScript 6.4%
CSS 1.9%
HTML 0.2%