Append-only change log. Newest first. Updating this on every code/dep/config/structure change is mandatory — see
CLAUDE.md→ "Codemap & audit rules".Type legend:
scaffold·feat·fix·refactor·remove·deps·docs·chore
| Date | Phase | Type | Change | Components / files | Why |
|---|---|---|---|---|---|
| 2026-07-29 | 4 | docs | README sweep post-Phase-4: root README (badge → Phase 4, status quote, 8-entry menu incl. quick actions / mode / sessions, native-list + picker paragraph, roadmap Phase 4 done w/ HW-verify-pending note, serve auto-creates session in getting-started), daemon/README.md (driver/renderer/controller bullets gain sessions + list payload + pickers; launch optional), glasses-client/README.md (two rendering paths: native list + in-place text upgrade, why) |
README.md, daemon/README.md, glasses-client/README.md |
READMEs were one phase stale after the Phase 4 commit |
| 2026-07-29 | 4 | feat | Native OS-list menu/pickers (HW: scroll jump survived BOTH the upgrade-render fix and the invisible capture-overlay — the OS scroll-animates the page for any capturing text container; no SDK flag disables it): contract gains ADDITIVE frame.list {items,selected} (menu/picker screens; old clients ignore it and keep the text rendering) and gesture.item (int, OS-reported selected index). Client: list page = title text container + ListContainerProperty (isItemSelectBorderEn:1 — the OS moves a selection border, nothing slides) vs text page (single capturing text container; overlay split REVERTED — didn't help); rebuild only when items/page-mode change, title-only changes via textContainerUpgrade, listEvent relayed with item; startup-create retry + space-not-empty content kept (first-open crash). Daemon: renderer.list_payload(), server.current_frame() (was current_lines; returns lines+payload, poll diffs both), Controller.on_gesture(name,item=None) + _sync_selection (host owns the highlight: scroll-with-item = sync only, tap executes the OS-selected item), menu/picker index resets to 0 on entry (OS border opens at 0; mode picker no longer opens on current mode — * still marks it). Repacked (31541 B), needs re-upload/reinstall; list geometry (48px title + 240px list, itemWidth 560) is a HW-tune guess. 173 tests green |
daemon/src/eventermidex/{protocol,renderer,server,controller}.py, daemon/tests/{test_protocol,test_server,test_controller}.py, glasses-client/src/main.ts, glasses-client/eventermidex.ehpk, docs/protocol.md |
Wearer: scroll animation still there after both text-container attempts; native list is the platform's intended menu widget |
| 2026-07-29 | 4 | fix | Display/input container split (HW: scroll jump persisted after the upgrade fix; diagnostic showed jump on 5-row AND 9-row screens → the OS scroll-animates ANY event-capturing text container regardless of content height): visible container 1 main now isEventCapture: 0; new EMPTY overlay container 2 touch (same bounds, isEventCapture: 1) takes the gestures — the OS animates the empty container, nothing visible moves. Both containers in startup + rebuild (containerTotalNum: 2). Repacked (31175 B), needs re-upload/reinstall + HW verify (overlay must still receive gestures and not occlude text). Fallback if this fails: native ListContainerProperty menu (OS-drawn selection border, List_ItemEvent index) — contract addition, bigger change |
glasses-client/src/main.ts, glasses-client/eventermidex.ehpk |
Text containers are inherently scrollable surfaces; separate what's seen from what's touched |
| 2026-07-29 | 4 | fix | Client renders via in-place text upgrade, not page rebuild: render() now sends textContainerUpgrade (containerID 1, splice contentOffset=0/contentLength=len(shown)) and only falls back to rebuildPageContainer when the upgrade is refused/throws or after FOREGROUND_ENTER (forceRebuild — the runtime may have dropped the page, an upgrade would go nowhere). Rebuild-per-frame re-laid-out the whole page and visibly jumped on every scroll frame. Rebuilt + repacked eventermidex.ehpk (31148 B) — needs re-upload/reinstall; upgrade splice semantics need HW verify (fallback covers refusal) |
glasses-client/src/main.ts, glasses-client/eventermidex.ehpk |
Wearer: "screen jumps on scroll — hard to see"; HUD text must update calmly in-vision |
| 2026-07-29 | 4 | fix | Done-news debounced (HW finding, first Phase-4 wearer session): the spinner disappears for a tick or two mid-turn (TUI redraws, tool hand-offs) and the raw falling edge flashed false * done - tap to read. note_pane(pane) (prev param dropped; server call updated) now tracks running+last_active (monotonic) and fires news only after the spinner stays gone ≥ NEWS_DEBOUNCE (1.0s) with a message present; new activity still retires news instantly |
daemon/src/eventermidex/{controller,server}.py, daemon/tests/test_controller.py |
Wearer: "it flashes done when it is not done" |
| 2026-07-29 | 4 | feat | Kill sessions from the HUD (wearer request): session picker grows a - kill session entry (only when non-active sessions exist) → opens a kill picker kind listing every session EXCEPT the active one (killing the watched pane would wedge the HUD); tap kills via new TmuxDriver.kill_session(name) (kill-session -t =name), picker stays open refreshed for bulk cleanup, falls back to the session picker when nothing killable remains. Menu untouched (8-entry cap respected — entry lives inside the picker) |
daemon/src/eventermidex/{controller,renderer,tmux_driver}.py, daemon/tests/{conftest,test_controller,test_renderer,test_tmux_driver}.py |
Wearer: "I should be able to delete sessions"; test-session cleanup from the glasses |
| 2026-07-29 | 4 | feat | Phase 4: sessions, mode picker, quick-actions — one generic picker overlay (dictation pattern: state on ControllerState, _render_picker sliding 8-row window w/ > highlight + * active, effective_view priority dictation > picker > prompt-fallback), three kinds from new MENU entries (MENU now 8 = hard cap, title+items=TAIL_LINES): sessions = fresh list_sessions() + + new session (auto-named claude-N, created in daemon cwd, revert on TmuxError); switching mutates shared driver.session (poll+controller retarget atomically); mode = picker + verify: user policy mode_steps (modular 4-ring distance; None on unknown/bypass = safe noop) + _set_mode executor (one BTab per fresh capture, bounded MODE_ATTEMPTS=6, refuses w/ prompt open); "cycle plan/auto" blind-BTab entry retired; quick-actions (/clear, /compact, continue, fix tests — hardcoded, config is Phase 5) send_text+Enter behind the _approve-style prompt guard, land on comm. New route_gesture picker branch (scroll=move/tap=select/2tap=close, BEFORE universal 2tap rule). Driver: list_sessions(), new_session(cwd=). serve auto-creates the session at boot (zero onboarding). Disconnect closes picker. ws contract untouched. 163 tests green (was 126) |
daemon/src/eventermidex/{controller,renderer,tmux_driver,__main__}.py, daemon/tests/{conftest,test_controller,test_renderer,test_tmux_driver}.py, CODEMAP.md, docs/architecture.md |
Phase 4 plan (delightful-gathering-deer): session picker + reliable modes + canned prompts; user wants prompts sendable with just the computer on |
| 2026-07-29 | 3 | docs | Doc sweep post-Phase-3: README (status badge phases 0–3 done, ambient-cockpit intro, "On the glasses" section w/ menu + gesture model, roadmap, [voice] install + --stt-model + mic-permission/force-quit notes), docs/architecture.md (goal/data-path incl. stt, hub-and-spoke + voice + TUI-parsing decision records, phases marked done), sub-READMEs: daemon/ (real CLI + modules), phone/ (Tailscale reality, ssh-tunnel retired), glasses-client/ (thin production client, pack flow, exit-event caveat); docs/protocol.md exit row marked currently-unsent |
README.md, docs/{architecture,protocol}.md, {daemon,phone,glasses-client}/README.md |
All narrative docs were 1–2 phases stale after the on-HW redesigns |
| 2026-07-29 | 3 | fix | Live-spinner detection + prompt context (reproduced on a throwaway claude session): (1) this Claude build's spinner has NO "esc to interrupt" — live line is ✻ Deciphering… (2s · ↓ 81 tokens · thinking); running = glyph + parenthesized (Ns (finished ✻ Baked for 20s has no parens) → done-notification can fire now; (2) PendingPrompt.context = nearest ⏺ Tool(args) above the option run (stop at first ⏺ so stale tools can't masquerade) — shown as [Write(probe.txt)] in both the comm notification and the prompt view, answering "what needs approval". Verified against real pane captures. Mic recovery confirmed: force-quit Even app re-grants audioControl. 126 tests green |
daemon/src/eventermidex/renderer.py, daemon/tests/test_renderer.py |
Wearer couldn't see completion or approval subject — both were parser blind spots |
| 2026-07-29 | 3 | remove | "exit app" menu entry dropped (menu now 6 entries) — Even's UI has hold-to-exit built in, the entry was redundant. Protocol exit message + client handling kept (contract stays; nothing sends it today). Note: mic failing again this session (mic-open-failed despite manifest permission) — investigating; suspected permission re-grant needed after reinstall |
daemon/src/eventermidex/controller.py, daemon/tests/test_controller.py |
Wearer feedback; don't duplicate OS affordances |
| 2026-07-29 | 3 | fix | Four HW-feedback fixes: (1) activity parser rewritten — running = live esc to interrupt line ONLY; finished spinners (✻ Baked for 20s) linger in the transcript forever and had made activity permanently truthy → done-notification never fired; (2) menu title now menu - mode: X (live) — "cycle plan/auto" had no visible feedback; (3) status screen dropped (redundant with mode-in-menu), message→"output", tail→"terminal" labels; (4) comm needs-input notification now includes the prompt question (2 reflowed rows). Client: ABNORMAL/SYSTEM_EXIT no longer unsubscribes (Even's "End this feature?" dialog fires them even on cancel → zombied app), FOREGROUND_ENTER repaints lastLines; rebuilt+repacked (needs re-upload/reinstall). 125 tests green |
daemon/src/eventermidex/{renderer,controller}.py, daemon/tests/*, glasses-client/src/main.ts, glasses-client/eventermidex.ehpk |
First real wearer session: feedback gaps + OS-dialog zombie |
| 2026-07-28 | 3 | refactor | Hub-and-spoke navigation (user redesign #2, on-HW): menu = home + only router (8 flat entries: comm screen, dictate, plan/auto, Esc, status, message, tail, exit; starts on menu at every client connect); scrolling NEVER changes screens; double-tap on any screen = back to menu (universal, never touches Claude); comm screen = blank while executing (busy * dropped), top notifications only: ! needs input - tap to view (prompt) > * done - tap to read (news); tap notification → full prompt view (tap=approve, 2tap=back). Prompt hijack + dismissed + next_view + cycle-view retired; HudState.view is now a view-name string; effective_view(pane,selected,ctl) keeps only dictation overlay + answered-prompt fallback. Tests reworked; 128 green |
daemon/src/eventermidex/{renderer,controller,server}.py, daemon/tests/* |
Wearer model: ambient notifications + explicit menu nav, not a scroll carousel |
| 2026-07-28 | 3 | feat | Calm screen + menu escape (user redesign mid-HW-session): new default VIEWS[0] calm — blank when idle, * while Claude busy, * done - tap to read on turn finish (tap → message view); note_pane() detects the activity falling edge (new activity retires stale news); review-send lands on calm; menu double-tap → calm (wrap+noop had left the menu with NO exit — checklist finding); new actions goto-view/show-news; poll() logs outgoing frames (debug aid for HUD-repaint investigation). Server/renderer/controller tests reworked for calm default; 128 green. HW also confirmed this session: glasses mic PCM = 16kHz/16-bit mono (10.0s computed ≈ 10s wall), dictate→review→send injected real prompts, mid-recording cancel, transcribe ~1-2s on tiny |
daemon/src/eventermidex/{renderer,controller,server}.py, daemon/tests/{test_renderer,test_controller,test_server}.py, CODEMAP.md |
Wearer wants an ambient HUD (Dynamic-Island-like), not a live terminal; menu was a gesture trap |
| 2026-07-28 | 3 | fix | HW finding: mic needs a manifest permission — first on-G2 dictate attempts failed with mic-open-failed (audioControl(true) rejected); cause: permissions: []. Added g2-microphone (+desc) to app.json, repacked .ehpk. Permission names dug out of the minified evenhub-cli schema: g2-microphone, phone-microphone, album, location, network, camera. Rest of the on-HW checklist so far: menu nav+wrap, mode/esc/exit exec, menu 2tap noop, error-state gestures, clean exit w/o reconnect — all pass |
glasses-client/app.json, glasses-client/eventermidex.ehpk, CODEMAP.md |
Glasses mic is the whole voice path; undocumented requirement |
| 2026-07-28 | 3 | feat | Phase 3 policies implemented + Stage-2 e2e PASS (no glasses) — 3 stubs filled per locked decisions: route_gesture (menu wraps; 2tap=Esc only on prompt overlay; dictation per-state), finish_review (strip+send_text+Enter; empty=discard), effective_view (dictation outranks prompt). 119/119 green. Live probe vs real claude tmux (p3test, loopback daemon, tiny model): menu nav, tap→BTab flipped the real mode line (manual→accept-edits; cycle is 4 states on this build: +auto), dictation with say-PCM → whisper review frame "Hello world, please run the tests." → discard clean; daemon spike log 68950 bytes ~= 2.2s matches wall clock for 16kHz/16-bit mono |
daemon/src/eventermidex/{controller,renderer}.py (bodies only), scratchpad probe (throwaway) |
TDD to green; prove the whole control pipeline before hardware |
| 2026-07-28 | 3 | chore | Policy TDD contract + STT proven locally — user decisions locked (menu wraps; double-tap=Esc only on prompt overlay; empty-transcript send=discard; dictation outranks prompt) and encoded as 35 red table tests for the 3 stubs (route_gesture/finish_review/effective_view); new test_stt.py green: say --data-format=LEI16@16000 → stdlib wave → real faster-whisper tiny transcribes correctly (no ffmpeg dep), silence→"" pinned; [voice] extra installed in venv. 81 green / 38 stub-red |
daemon/tests/{test_controller,test_renderer,test_stt}.py, daemon/.venv |
TDD handoff: tests are the contract, user implements to green; validate STT path pre-hardware |
| 2026-07-28 | 3 | scaffold | Phase 3 start: control pipeline built, 3 learning stubs red — new controller.py (events→actions: menu exec mode/BTab · esc · exit · dictate, dictation machine idle→recording→transcribing→review with gen stale-guard, on_voice PCM accumulation, on_disconnect reset; user stubs route_gesture + finish_review) + new stt.py (faster-whisper wrapper, optional [voice] extra, 16kHz/16-bit assumption logged for the HW spike). Protocol additive: out mic{open}/exit, in voice{chunk|error} (replaces reserved text shape). renderer.py: VIEWS+menu, ControlView snapshot, menu/dictation renders, effective_view gains ctl param (stub 3: dictation-vs-prompt priority). server.py thinned to transport (gestures/voice delegate to Controller). Client: double-tap exit removed, mic/exit handling, PCM→base64 relay. --stt-model flag; doctor voice line. Tests: conftest.py FakeDriver, test_controller.py new; 77 green + 3 stub-red (route_gesture) |
daemon/src/eventermidex/{controller,stt,renderer,server,protocol,__main__}.py, daemon/pyproject.toml, daemon/tests/*, glasses-client/src/main.ts, docs/protocol.md, CODEMAP.md |
Phase 3 plan; learning-mode handoff of gesture map + review flow + overlay priority |
| 2026-07-28 | 2 | fix | Tail view: rule lines clip to one "-"*36 row instead of reflowing — a 220-wide Claude TUI rule wrapped into 6+ dash rows and flooded the whole HUD (found by the user on hardware right after verification; Phase 1's hard clip had masked it) |
daemon/src/eventermidex/renderer.py, daemon/tests/test_renderer.py |
Reflow is for content; decoration must be clipped |
| 2026-07-28 | 2 | feat | Phase 2 VERIFIED on G2 hardware — full checklist green: status/message/tail cycling with instant wake-event repaints; prompt overlay hijacked HUD on a real Do you want to create hw-verify.txt? menu, tap approved it (file written), promptless taps ignored before AND after; mode tracking live incl. newly-discovered auto mode on status string (added to _MODES + test, 54 green); daemon restart → client auto-reconnect; session kill → placeholder → relaunch; degraded plain-shell TUI kept gestures alive. Also: positive tap path now logged. HW findings: double-tap now raises Even Hub "End this feature?" dialog (not insta-close) but STILL zombies the client via the spike handler's unsubscribe — remove in Phase 3 client rebuild |
daemon/src/eventermidex/{renderer,server}.py, daemon/tests/test_renderer.py, CODEMAP.md |
Phase 2 exit gate; real TUI strings only hardware could reveal |
| 2026-07-28 | 2 | feat | Phase 2 wired: renderer → server — user-authored policies implemented: effective_view() = rising-edge prompt hijack with unlocked navigation (server marks the overlaid prompt dismissed on scroll; new/mutated prompt ≠ dismissed re-asserts by equality) + extract_message() = bottom-up boundary scan (⏺ Name(args) tool noise discarded, other ⏺ blocks = prose). server.py rewired: HudState, poll() sleeps on wait_for(wake, interval) for instant gesture repaints, scrolls cycle VIEWS, tap re-captures fresh + sends Enter only when a prompt is detected; --tap-approves flag removed (__main__.py). test_server.py reworked (FakeDriver.capture, prompt-tap on/off, wake immediacy, overlay dismissal); 53 tests green |
daemon/src/eventermidex/{renderer,server,__main__}.py, daemon/tests/{test_renderer,test_server}.py, CODEMAP.md |
Phase 2 plan steps 2–3; prompt-aware tap replaces the blunt smoke flag |
| 2026-07-28 | 2 | scaffold | Phase 2 start: renderer.py skeleton — pure pane→HUD module: parse_pane()→PaneState (mode/activity/prompt heuristics off the real pane sample; never raises), PendingPrompt numbered-menu detection (❯-marker guard vs prose lists, whole-pane scan), reflow() word-wrap replacing Phase 1's hard clip, render() for status/message/tail views + prompt overlay (≤9×≤36 ASCII invariant enforced in one place), next_view(). Two learning stubs left for the user: effective_view() (prompt-hijack policy) + extract_message() (last prose block; 3 TDD tests pre-written, red). test_renderer.py: 5 pane fixtures, 45 green + 3 stub-red. Server untouched — Phase 1 daemon still works |
daemon/src/eventermidex/renderer.py, daemon/tests/test_renderer.py, CODEMAP.md |
Phase 2 plan step 1; learning-mode handoff of view policy + message heuristic |
| 2026-07-28 | 1 | refactor | Renamed project evenTermux → evenTermidex ("Termux" was vestigial after Tailscale replaced the ssh tunnel; new name fits the multi-session/multi-device direction). Python package eventermux→eventermidex (dir git mv), console script + logger + all docs/README/manifest swept; app.json package_id → com.aplaino.eventermidex (needs repack + re-add on dashboard when client is next touched); reinstalled editable, 22 tests green. Local dir path + GitHub repo renamed separately |
repo-wide (excl. .claude/settings.local.json path) |
Name no longer described the project |
| 2026-07-28 | 1 | fix | Security: fail closed on bind. default_host() now falls back to 127.0.0.1, not 0.0.0.0, when Tailscale gives no IP — the daemon injects keystrokes into Claude and must never land on all interfaces by accident (--host 0.0.0.0 to opt in). Flagged by commit + push security review |
daemon/src/eventermidex/__main__.py |
Auto security review; also matches the user's tailnet-only intent |
| 2026-07-28 | 1 | docs | README refreshed for reality: SSH→Tailscale throughout (tagline, arch diagram, layout), Phase 1 checked off + status→Phase 2, getting-started shows launch/serve + Tailscale probe instead of the old ssh/echo-server flow |
README.md |
Public README was stale on transport and phase after Phases 0–1 |
| 2026-07-28 | 1 | feat | First real cockpit action: tap→approve verified on G2 — tapped a live Claude permission prompt from the HUD, daemon sent Enter, file written. Two fixes it required: (a) tap injects Enter not y (--tap-sends-y→--tap-approves) — Claude prompts are numbered menus, y is ignored; (b) _to_ascii() overflow guard: unmapped wide glyphs (╌, ●) overflowed the container → Even Hub's native scroll ate all gestures; now all non-ASCII drops to ?. Also learned: no ws keepalive → daemon trusts a frozen/backgrounded WebView as "connected" (gestures silently stop); force-quit+reopen recovers. Resilience item for Phase 5 |
daemon/src/eventermidex/{server,__main__}.py, daemon/tests/test_server.py, CODEMAP.md |
Prove the control pipeline end-to-end; harden HUD rendering |
| 2026-07-28 | 1 | feat | Phase 1 VERIFIED on G2 hardware — HUD shows live claude tmux tail (fits after sanitizer), tap/scroll-up/scroll-down all arrive at daemon; client re-sideloaded as packed eventermidex.ehpk (dashboard) after dev-entry loss; handler now swallows ConnectionClosed (phone sleep is routine). Known trap: spike client's double-tap exit leaves a zombie WebView (unsubscribed but connected) — revisit with the Phase 3 gesture map |
daemon/src/eventermidex/server.py, glasses-client/eventermidex.ehpk |
Close out Phase 1 end-to-end gate |
| 2026-07-28 | 1 | fix | Wide-glyph sanitizer in current_lines(): Claude's box borders/❯/⏸/arrows translate to narrow ASCII, blank runs collapse, clamps → 9×36 — char-count clipping alone can't stop proportional-font wrap |
daemon/src/eventermidex/server.py, CODEMAP.md |
42-char clamp still wrapped on hardware: UI rows are dominated by wide glyphs |
| 2026-07-28 | 1 | fix | HUD grid measured on G2 via ruler frames: 576×288 container ≈ 10 rows × ~50 digit-widths, font is proportional, over-wide lines wrap (eat rows) not clip → clamps now TAIL_LINES=9/TAIL_COLS=42 |
daemon/src/eventermidex/server.py, CODEMAP.md |
Spike client rendered unusably (wrapping) with the guessed 8×44; Phase 2 renderer owns real reflow |
| 2026-07-28 | 1 | feat | Phase 1 daemon skeleton complete — user decisions implemented: hello() = hello-only versioning ({type:hello,v:1}, frames stay lean over BLE) + poll() = clipped-line diffing (broadcast only when visible HUD lines change); new tmux_driver.py (capture/tail/send_text/send_key, exact-match has-session); __main__.py → argparse CLI doctor/launch/serve with default_host() = tailscale ip -4 fallback 0.0.0.0; server.make_handler extracted so tests exercise the real handler; 22 tests green; local e2e verified (probe → tap → --tap-sends-y injects y → change frame returns) |
daemon/src/eventermidex/{protocol,server,tmux_driver,__main__}.py, daemon/tests/**, docs/protocol.md, CODEMAP.md |
Phase 1: working daemon core before hardware verification |
| 2026-07-28 | 1 | fix | Probe client speaks v1: skip hello (ignore-unknown rule), expect frames only on change, print change-triggered frames for 2s after tap |
spike/host/probe_client.py |
Old probe assumed periodic frames and hung against the send-on-change daemon |
| 2026-07-28 | 1 | scaffold | Stub protocol.py (v1 contract: frame/parse_event done, hello() = user TODO) + server.py (handler/run done, poll() = user TODO) + docs/protocol.md skeleton |
daemon/src/eventermidex/protocol.py, daemon/src/eventermidex/server.py, docs/protocol.md, CODEMAP.md |
Phase 1 start; learning-mode handoff of the versioning core + poll/diff strategy |
| 2026-07-28 | 0 | feat | Phase 0 COMPLETE — full round-trip on real G2: frames render on HUD, tap→tap/double-tap→double-tap relay to host over Tailscale. Instrumented raw events to decode HW shape, then removed debug |
glasses-client/src/main.ts |
Verify the architecture end-to-end on hardware; confirm gesture decoding (tap omits eventType) |
| 2026-07-28 | 0 | feat | Glasses client scaffolded from Even Hub minimal template; spike app wires ws↔HUD↔gestures; typecheck + vite build pass |
glasses-client/** |
Phase 0 step 4: build the confirmed Even Hub client (WebSocket works on sideload) |
| 2026-07-28 | 0 | feat | Phone↔host ws round-trip proven over Tailscale (both directions); adopt Tailscale as transport | docs/architecture.md, CODEMAP.md |
Phase 0 step 3a: pick a reliable off-LAN transport, drop firewall/port-forward/ssh -L friction |
| 2026-07-28 | — | docs | Professional public README + MIT LICENSE; publish as public GitHub repo | README.md, LICENSE |
Make the project shareable/open-source with clear framing + disclaimer |
| 2026-07-28 | 0 | feat | Host echo server + local probe; round-trip (frames out / event in) verified on the Mac | spike/host/echo_server.py, spike/host/probe_client.py |
Phase 0 step 1-2: prove the host anchor both relay paths connect to |
| 2026-07-28 | — | docs | Add codemap + audit sheet + project maintenance rules | CODEMAP.md, AUDIT.md, CLAUDE.md, README.md |
Persistent project understanding; save context; enforce keeping docs in sync |
| 2026-07-28 | 0 | scaffold | Scaffold polyglot monorepo; Python daemon on py3.12 venv; eventermidex env self-check; git init |
README.md, .gitignore, daemon/**, glasses-client/README.md, phone/README.md, spike/README.md, docs/architecture.md |
Establish the build environment + workspace |
| 2026-07-28 | 0 | chore | Install tmux (brew); confirm Node 24 / Python 3.12 available |
host toolchain | tmux is how the daemon reads/injects the claude session |
| 2026-07-27 | plan | docs | Research (G2 protocol, Termux/BLE, SDKs) + wrote implementation plan | ~/.claude/plans/twinkling-brewing-allen.md |
Decide architecture before building |