275 Commits

Author SHA1 Message Date
felixm 6cb6adf7ef docs(settings): list the ambient env vars in the Settings comment
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-05 22:02:33 -04:00
felixm b36964a427 fix(keeld): log the effective ambient cadence, not the raw setting
A settings.json predating the ambient fields deserializes cadence to 0;
the sentinel clamps that to its 5m default, so log the clamped value
instead of a misleading cadence=0s.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-05 21:59:42 -04:00
felixm 6c81a0d4f8 feat(keeld): wire the ambient drift coach sentinel
Automated smoke test results:
- GET /: 200
- POST /ambient/snooze: 204
- GET /settings: includes ambient_mode and ambient_cadence_secs fields
- daemon log: "ambient: mode=notify cadence=0s" (cadence=0 because existing
  settings.json predates these fields; ambient.New defaults to 5 min when
  cadence is non-positive — correct degradation)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-05 21:57:14 -04:00
felixm aaed1ad265 feat(web): ambient banner, snooze/focus actions, settings controls 2026-06-05 21:51:49 -04:00
felixm fa178a1fd2 feat(web): ambient line in state envelope + /ambient/snooze
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-05 21:46:45 -04:00
felixm ef845ce463 feat(settings): ambient_mode and ambient_cadence_secs fields
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-05 21:43:45 -04:00
felixm 4b2cb4242f feat(statusfile): render the ambient drift line when idle
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-05 21:41:24 -04:00
felixm 7507cc6d28 fix(ambient): re-check active mode after the judge call (coexistence TOCTOU) 2026-06-05 21:39:46 -04:00
felixm c963895599 feat(ambient): evaluate loop, two-stage surfacing, snooze, config 2026-06-05 21:35:56 -04:00
felixm af3a0e2ac8 feat(ambient): sentinel core — evidence ring, Line, deps 2026-06-05 21:33:59 -04:00
felixm b37ee40dd2 feat(ai): AmbientDrift — frame-grounded drift judge 2026-06-05 21:30:37 -04:00
felixm 2690fdd513 refactor(offscreen): assemble frame via shared internal/frame 2026-06-05 21:28:21 -04:00
felixm 703218658b feat(frame): shared ~/owc frame assembler extracted for reuse
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-05 21:26:21 -04:00
felixm 607ad74def feat(notify): notify-send effector with nop fallback 2026-06-05 21:22:12 -04:00
felixm aac3173874 docs: implementation plan for the ambient drift coach
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-05 21:20:11 -04:00
felixm 9d7786c3e6 docs: design the ambient drift coach (always-on sentinel)
The first Control loop / scheduler component (architecture §3): an
always-on sentinel beside the one-mode harness that judges activity
against the ~/owc frame when no session is declared, and surfaces drift
to the status bar, a notify-send toast, and a web-UI banner.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-05 21:03:10 -04:00
felixm 682eb603fa fix(evidence): X11 sensor polls so tab/title changes are tracked
The X11 source only re-read the active window on _NET_ACTIVE_WINDOW
changes, so switching a browser tab — which changes the window title
but not the active window — was invisible. All the time on the new tab
was credited to the stale title (e.g. reading a Consume article showed
up as time on the Keel tab).

Mirror the Windows sensor: poll the active window every 750ms and emit
only when the window or its title changes. A shared, display-free
pollLoop carries the change-dedup and is unit-tested for the exact
regression (a title-only change must emit).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-05 20:32:24 -04:00
felixm 6d296bf743 docs: mark off-screen AW memory as shipped in architecture §5 2026-06-05 20:08:20 -04:00
felixm c855731c39 fix(web): preserve aw_url when a settings POST omits it
A UI save was silently blanking the configured AW URL because
handlePostSettings bound the whole Settings from the request and saved
it as-is. Now reads s.settings.AWURL under the existing settingsMu lock
and carries it forward when req.AWURL is empty, so fields not exposed in
the form are never erased.
2026-06-05 20:05:59 -04:00
felixm bd2b8a41fd feat(offscreen): read recent proposals into the brief; prompt follows up
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-05 19:56:59 -04:00
felixm 16b44c9e90 feat(offscreen): record proposal_made/action_taken/proposal_dismissed
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-05 19:51:18 -04:00
felixm 5bdb98c1a7 feat(keeld): construct AW memory store, nop fallback when AW is down 2026-06-05 19:45:24 -04:00
felixm 67f91442a4 feat(settings): KEEL_AW_URL with localhost default
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-05 19:39:49 -04:00
felixm 3a4f498f86 feat(memory): AW-backed Store with over-fetch kind filtering 2026-06-05 19:38:35 -04:00
felixm 7d4f9ca4a8 feat(memory): Store port with nop and fake adapters 2026-06-05 19:33:46 -04:00
felixm 582af0d68b feat(aw): Insert and Recent over the events endpoint 2026-06-05 19:32:43 -04:00
felixm b422b6deb6 feat(aw): AW REST client with idempotent EnsureBucket 2026-06-05 19:29:41 -04:00
felixm 3386405c7c docs: implementation plan for off-screen AW-backed memory
Nine TDD tasks: internal/aw REST client (EnsureBucket/Insert/Recent),
internal/memory port (Store + awStore over-fetch filter + nop + fake),
KEEL_AW_URL setting, harness.Services.Memory wiring with nop fallback,
off-screen write path (proposal_made/action_taken/proposal_dismissed) and
read path (## Recently proposed + follow-up prompt rule), plus full and
live verification. Derived from the 2026-06-05 design spec.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-05 19:16:47 -04:00
felixm 730ffe33ce docs: design off-screen AW-backed memory (first durable-memory slice)
Brainstormed design for Keel's first cross-run memory: a thin memory.Store
port backed by an internal/aw REST client writing to the keel.events bucket,
with off-screen as the single wired consumer (records proposal_made /
action_taken / proposal_dismissed and reads its recent history back into the
brief for fresher, follow-up-aware proposals). Brain-only, best-effort,
degrades to nop when AW is down. Realizes keel-architecture.md §5/§7 at the
smallest honest vertical.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-05 19:08:41 -04:00
felixm 7d69a1f320 Generalize the focus controller into a harness hosting swappable modes
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>
2026-06-05 18:00:49 -04:00
felixm 258de2c14b docs: make Keel the repo front door + add agent context
- README: lead with Keel (the human-harness), frame AntiDrift as the focus
  mode, keep the milestone history. Fix the "rename deferred" wording (the
  dir/repo is already keel; only the code identity stays antidrift) and drop
  the dangling 2026-05-31 spec reference (that spec was intentionally removed).
- keel-architecture.md: update the rename status in the header and §8 — the
  directory is keel; the Go module / antidriftd binary / ANTIDRIFT_* env /
  ~/.antidrift -> ~/.keel migration land with the controller refactor.
- add AGENTS.md (agent orientation) + CLAUDE.md symlink so claude / codex /
  Hermes auto-load Keel context when launched in this repo.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-04 13:20:51 -04:00
felixm f9e580ff7e docs: name the cockpit Keel and add its architecture
Move the higher-goals cockpit architecture into this repo: AntiDrift is
Keel's seed, with its focus harness becoming one mode of a general
collect -> brain -> act loop. Records the locked decisions: the product
is the harness around a swappable brain (claude/codex/Hermes); storage is
ActivityWatch; two surfaces (web UI + status bar); gated -> autonomous
effectors. Code/repo rename to keel deferred until the controller-refactor
decision.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-04 12:57:14 -04:00
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
felixm 9012d5ddc6 Purge leftover Rust implementation
Remove the legacy/ Rust tree (Cargo manifests, .rs sources, desktop
entry), drop Rust-specific .gitignore blocks and README note, and strip
now-dangling "ported from Rust" comments from the Go sources.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-04 09:06:14 -04:00
felixm 91a3fb29c2 Add Windows 11 support behind evidence.Source and enforce.Guard ports
Polling active-window sensor and window-minimize guard as //go:build windows
adapters over a shared internal/winapi Win32 binding. Pure logic (class
normalization, emit-on-change tracking) is TDD-tested on Linux; syscall code is
cross-compile verified. No consumer changes.
2026-06-02 12:41:25 -04:00
felixm 9546999acb Clarify null-HWND doc note in ForegroundWindow 2026-06-02 12:39:32 -04:00
felixm d45a01eca5 Verify Windows cross-compile and Linux regression for Windows support
- GOOS=windows GOARCH=amd64 go build ./...  (whole module links)
- GOOS=darwin  GOARCH=amd64 go build ./...  (!linux && !windows fallback intact)
- go build ./... && go test ./...           (Linux host green, incl. new winapi/evidence tests)
- go vet ./... on linux and windows         (clean)
- cmd/antidriftd cross-compiles to a real PE32+ x86-64 .exe
2026-06-02 12:37:24 -04:00
felixm b6c7933f16 Align Windows guard ctx param and doc with X11 guard 2026-06-02 12:36:12 -04:00
felixm f37e295166 Add Windows window-minimize guard 2026-06-02 12:34:14 -04:00
felixm d149736946 Share unavailable() so the Windows sensor logs unavailable transitions 2026-06-02 12:33:23 -04:00
felixm 0daf8c2849 Add Windows polling active-window sensor 2026-06-02 12:30:16 -04:00
felixm 0f47d2dbf0 Document title/path truncation behavior in Win32 binding 2026-06-02 12:29:07 -04:00
felixm 0ac1511aac Add Win32 binding for foreground window and minimize 2026-06-02 12:24:55 -04:00
felixm 7d62af0750 Document foregroundTracker unavailable-state contract
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-02 12:23:54 -04:00
felixm e1194be3c2 Add emit-on-change tracker for the Windows polling sensor 2026-06-02 12:21:58 -04:00
felixm f79c149039 Clarify .exe suffix handling in class normalization 2026-06-02 12:21:12 -04:00
felixm 4ea8aed04b Add pure process-path to class normalization for Windows 2026-06-02 12:18:59 -04:00
felixm 885b0f9224 Plan Windows 11 support implementation
Six TDD/cross-compile tasks: pure class normalization and emit-on-change
tracker (unit-tested on Linux), Win32 binding, the two windows-tagged port
adapters, build-tag narrowing, and a whole-module cross-compile gate.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-02 12:08:01 -04:00
felixm e0ea1eca83 Spec Windows 11 support via windows-tagged port adapters
Full-parity design: evidence.Source (polling) and enforce.Guard
(ShowWindow) behind the existing X11/no-op port boundaries, pure Go,
verified compile-only.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-02 11:35:39 -04:00
felixm 14ec2b6ea8 Remove shipped specs and plans; history and code are the record
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-01 22:00:52 -04:00