Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
8.4 KiB
AntiDrift Go Reimagining — Design
Date: 2026-05-31
Purpose
AntiDrift is being reimagined from its current Rust implementation (~7,500 lines) into Go, to become the user's focus operating system on the computer: fast, good-looking, and deeply integrated with AI from the start.
This is not a faithful 1:1 port. The existing domain model and the
commitment-os-design.md spec remain the north star. The Rust code is a
reference, not a thing to replicate line-for-line. The move to Go is an
opportunity to shed incidental complexity — most notably the token-heavy
event-log replay/revalidation design — while preserving what is genuinely
valuable.
Why Go, why now
- Token efficiency for AI-assisted development. The current pain is not
runtime cost; it is that any AI edit to the core must load
session.rs(3,475 lines, ~80% replay-validation logic and its tests). Go's smaller idioms plus a redesigned, smaller core directly reduce the context any change requires. - Predictable LLM codegen. Go's rigid syntax produces functional code on the first pass with fewer correction loops.
- Runtime fit. Concurrency and local HTTP serving are first-class, which suits a long-running focus daemon that also talks to AI.
What Carries Over vs. What Changes
Preserved (high value, ports cleanly):
- The domain model:
Commitment,PolicySnapshot,RuntimeState,CommitmentState,AllowedContext,EnforcementLevel. - The pure runtime/commitment state machines (
state_machine.rs) — a near 1:1 port. - The
commitment-os-design.mdspec as the conceptual foundation, including "no unchosen transitions" and the staged threat model. - Hash-chained tamper evidence — but relocated to the audit log only.
Reimagined:
- Persistence. Replace replay-everything-and-revalidate-on-startup with an
in-memory state-of-truth, a persisted snapshot, and an append-only audit
log. This removes roughly 3,000 lines (the bulk of
session.rs). - UI. Replace the ratatui TUI with a local web app (Gin backend + browser). This is the surface that must "look good."
- AI. AI is a first-class participant from the start, not a later add-on.
Deferred for v1:
- The AI reviewer role (session-end reflection). The three live roles ship first.
- Privileged enforcement (guardian, IPC, nftables, delayed admin) — same Stage 2 boundary as the original spec.
Process Model
A single Go binary, antidriftd, runs as a local daemon and owns all
state. The browser is its face.
┌─────────────────────────────────────────┐
│ antidriftd (one Go process) │
│ │
│ web (Gin) ──HTTP + SSE──▶ browser UI │
│ │ │
│ session ── statemachine ── domain │
│ │ │
│ store (snapshot + audit log) │
│ evidence (xdotool/X11) │
│ ai (CLI backend, async workers) │
└─────────────────────────────────────────┘
- The daemon holds live state in memory as the single source of truth.
- It persists a snapshot on every state change (crash/restart recovery).
- It appends every significant event to an append-only audit log (the tamper-evident, hash-chained trail — for audit and later review, not for state reconstruction).
- The browser is stateless: it renders what the daemon pushes over Server-Sent Events (SSE) and POSTs user actions back. No business logic in the browser.
Why snapshot instead of replay
The original Rust design reconstructs all state by replaying the entire event log on startup and re-validating every transition, with a dedicated test per illegal sequence. That is correct and tamper-aware, but it is the single largest source of code and token weight. A snapshot of current state plus an append-only audit trail gives the same recoverability and keeps tamper evidence 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.
Package Layout
| Package | Ports from | Size | Purpose |
|---|---|---|---|
domain |
domain.rs |
small | Commitment, PolicySnapshot, runtime/commitment states, AllowedContext, EnforcementLevel, validation |
statemachine |
state_machine.rs |
small | Pure transition functions (1:1 port) |
session |
reimagined session.rs |
medium | In-memory controller; drives transitions, snapshots, audit appends; no replay validation |
store |
event_log.rs |
small | Snapshot file (current state) + append-only hash-chained audit JSONL |
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 |
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 token-efficiency goal.
AI Integration
AI is reached through one narrow interface with a single CLI backend to start:
type Assistant interface {
// Planning: turn a vague intent into a concrete commitment.
Coach(ctx context.Context, intent string) (domain.Commitment, error)
// Live: is the current window on-task for this commitment?
JudgeDrift(ctx context.Context, c domain.Commitment, w evidence.WindowSnapshot) (Verdict, error)
// Ambient: periodic check-in based on recent activity.
Nudge(ctx context.Context, c domain.Commitment, recent []evidence.WindowSnapshot) (string, error)
}
- Backend (v1): shell out to
claude/codexwith a strict prompt that demands JSON output. Reuses existing CLI auth; no API key plumbing. - Latency containment (the CLI is slow, ~seconds, and AI is in the live hot path): all AI calls run in background goroutines; the UI never blocks. Drift judgments are debounced (no faster than ~10s) and cached per (commitment, window-class) so the same window is not re-judged. The UI shows a pending state and updates via SSE when a verdict lands.
- Swap path: the interface boundary lets an Anthropic API backend (faster, structured, prompt-cached) drop in later without touching callers. Not built in v1.
The three live roles ship first: planning coach, live drift interceptor, ambient nudge. The reviewer is deferred.
Roadmap
Each milestone is independently shippable and gets its own spec → plan → build cycle.
- 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.
aipackage + 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.
The first sub-project to brainstorm and spec in detail is M0.
Repo Strategy
- New Go module at the repository root.
- Move the existing Rust into a
legacy/directory (or arustbranch) so it remains available as reference while the Go code becomes the front door.
Out of Scope (v1)
- Privileged enforcement: guardian process, root-owned Unix socket IPC, nftables/DNS domain blocking, delayed admin, break-glass.
- AI reviewer / session-end reflection.
- Wayland compositor adapters beyond the existing degraded reporting.
- Planner/project/task model and outcome writeback.
- Presence sensing.
These remain governed by commitment-os-design.md and may return as later
milestones.