# CodexBar-ESP32 Agent Notes

This repo is for the CodexBar ESP32 display and its local companion server. It has a
few known sharp edges — read this before making changes, especially around
networking, launchd, or attention-state behavior.

## Working Agreement

- Do not commit changes unless James explicitly asks for a commit.
- Preserve user edits in the working tree. Check `git status --short` before and after changes.
- Prefer small, focused changes with tests alongside behavior changes.
- Run tests as you go and report what passed.
- When touching ESP32 display behavior, update the simulator/readme screenshot path when it helps confirm the visual state.

## Known Failure Points

- Do not treat companion server failure as WiFi failure. HTTP errors such as `-1` usually mean the Mac companion is unreachable at `PROXY_HOST:7842`; do not disconnect WiFi in response to that.
- Do not reintroduce ESP32 reboot loops for ordinary WiFi outages. WiFi-down should show a diagnostic no-data screen, dim in steps, and retry on the no-data cadence.
- Keep "WiFi down", "server not found", and "usage unavailable" as separate states with different remediation text.
- Do not make `companion/server.py` kill an existing process by default. It should only replace an existing listener when explicitly requested with `CODEXBAR_REPLACE_EXISTING=1`.
- Keep the companion HTTP server threaded. A slow `/usage` refresh after Mac wake must not block `/health`, `/status`, or attention hooks.
- Keep usage refresh single-flight. Concurrent `/usage` requests should not stampede provider APIs; return stale cached usage during an in-progress refresh when possible.
- launchd should use a `KeepAlive` dictionary with `SuccessfulExit=false`, not plain `KeepAlive=true`, to avoid log spam after clean "port already in use" exits.
- Include `WorkingDirectory` in launchd examples so relative paths and local execution assumptions stay stable.
- Attention state polling is local to the companion server. Do not add frequent polling to Claude, Codex, or other provider endpoints.

## Test Expectations

Run the relevant checks before reporting completion:

```bash
.venv/bin/python -m unittest discover -s tests
.venv/bin/python companion/smoke_test_hooks.py
.venv/bin/python -m py_compile companion/attention_hook.py companion/server.py companion/smoke_test_hooks.py simulate_display.py tests/test_attention_hook.py tests/test_companion_attention.py
```

If firmware changed, also do one of:

- Compile/upload in Arduino IDE, or
- Capture a real hardware serial log with `tools/serial_capture.py` after the user flashes, or
- Say clearly that firmware compile/hardware verification was not run and why.

If display rendering changed, generate a simulator image:

```bash
.venv/bin/python simulate_display.py --startup --out /tmp/codexbar_startup.png
.venv/bin/python simulate_display.py --no-data --out /tmp/codexbar_no_data.png
```

For real hardware troubleshooting:

```bash
.venv/bin/python tools/serial_capture.py --list
.venv/bin/python tools/serial_capture.py --port /dev/cu.wchusbserial110 --seconds 90 --expect-usage
```

## Manual Diagnostics

Useful local checks:

```bash
curl http://127.0.0.1:7842/health
curl http://127.0.0.1:7842/status
lsof -nP -iTCP:7842 -sTCP:LISTEN
launchctl print gui/$(id -u)/com.codexbar.companion
tail -n 100 /tmp/codexbar-companion.log
```

Arduino Serial Monitor should be set to `115200` baud. Common `WiFi.status()` values are documented in `README.md`.
