Files
multi-agent-mux/.agents/reports/reviewer-agy-01/report-ba63f0b5.md
T

94 lines
8.5 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# PR #62 Code Review Report — Issue #60 Linux Follow-up (`1d7b7da..6a4e385`)
- **Job ID**: `ba63f0b5`
- **Reviewer**: `agy` (`herdr:reviewer-agy-01`)
- **Branch**: `fix/issue-60-linux`
- **Base .. Head**: `1d7b7dab58a10fe86fd747b7290d47674ca93230` .. `6a4e385a78c8a561d5cf3644336de6a352ea9271` (8 commits)
- **Verification Worktree**: `/var/folders/q_/68sxgw3j073dgjg8wl9s08vw0000gn/T/mam-review/loop-20261019T000805Z-975c4c/1`
---
## 1. Executive Summary
All production code changes in PR #62 (`_ro_connect`, `_DB_PATHS` cache, `input_placeholder_pattern`, `auth_ok` / `auth-ok`, `stop_session.sh` log text, and `test_u1e`) are well-designed and behave as intended on macOS (Python 3.9.6, Apple SQLite 3.51.0, opencode 2.0.32).
However, **5 test cases in `tests/test_issue60_linux_fixes.py` fail on macOS** (`test_a_query_reads_right_and_leaves_the_sidecars_alone[clean-listing0-*]`) due to a macOS vs. Linux SQLite difference in `wal_home`'s `make("clean")` fixture helper:
- On Linux (upstream SQLite default `SQLITE_FCNTL_PERSIST_WAL = 0`), closing the last connection to a WAL database (`_wal_writer(db, ws).close()`) checkpoints the WAL into `opencode.db` and unlinks `opencode.db-wal` and `opencode.db-shm`.
- On macOS (`/usr/bin/python3` linked against Apple's `/usr/lib/libsqlite3.dylib`, which sets `SQLITE_FCNTL_PERSIST_WAL = 1` by default), `_wal_writer(db, ws).close()` checkpoints the WAL into `opencode.db` and truncates `opencode.db-wal` to 0 bytes, **but leaves `opencode.db-wal` and `opencode.db-shm` on disk**.
- Consequently, `assert make(state) == listing` at `tests/test_issue60_linux_fixes.py:181` fails with `AssertionError: assert ['opencode.db', 'opencode.db-shm', 'opencode.db-wal'] == ['opencode.db']` before `_ro_connect` is even called.
Unlinking `opencode.db-wal` and `opencode.db-shm` after `_wal_writer(db, ws).close()` in `make("clean")` makes all 20 `(query × state)` cases pass on macOS as well.
---
## 2. Findings
### `[P1]` Blocking — `wal_home` fixture `make("clean")` fails on macOS due to Apple SQLite `SQLITE_FCNTL_PERSIST_WAL=1`
- **Location**: `tests/test_issue60_linux_fixes.py:157-159, 181`
- **Symptom**:
Running `pytest tests/test_issue60_linux_fixes.py` on macOS (`/usr/bin/python3` 3.9.6, `sqlite3.sqlite_version == '3.51.0'`) fails 5 tests:
- `FAILED tests/test_issue60_linux_fixes.py::test_a_query_reads_right_and_leaves_the_sidecars_alone[clean-listing0-_descendants]`
- `FAILED tests/test_issue60_linux_fixes.py::test_a_query_reads_right_and_leaves_the_sidecars_alone[clean-listing0-_has_session]`
- `FAILED tests/test_issue60_linux_fixes.py::test_a_query_reads_right_and_leaves_the_sidecars_alone[clean-listing0-discover]`
- `FAILED tests/test_issue60_linux_fixes.py::test_a_query_reads_right_and_leaves_the_sidecars_alone[clean-listing0-residual_artifacts]`
- `FAILED tests/test_issue60_linux_fixes.py::test_a_query_reads_right_and_leaves_the_sidecars_alone[clean-listing0-verify_artifact]`
- **Root Cause**:
Apple's system `libsqlite3.dylib` enables `SQLITE_FCNTL_PERSIST_WAL` by default. When `_wal_writer(db, ws).close()` runs on macOS, SQLite checkpoints the committed WAL frames into `opencode.db` (`8192` bytes) and truncates `opencode.db-wal` to `0` bytes, but keeps `opencode.db-wal` and `opencode.db-shm` on disk instead of unlinking them.
- **Verification & Fix**:
In `make(state)` (`tests/test_issue60_linux_fixes.py:157-159`), removing any remaining sidecars after `_wal_writer(db, ws).close()` checkpoints the DB:
```python
def make(state):
if state == "clean":
_wal_writer(db, ws).close()
for suffix in ("-wal", "-shm"):
if os.path.exists(str(db) + suffix):
os.unlink(str(db) + suffix)
```
leaves `opencode.db` with its WAL header and checkpointed rows and no sidecar files (`["opencode.db"]`). Verified on macOS that all 20 `(query × state)` combinations pass with this change, and that `_ro_connect(db)`'s `mode=ro&immutable=1` branch is crucial on macOS (where a plain `mode=ro` open on a sidecar-free WAL-mode DB raises `sqlite3.OperationalError: unable to open database file`, and a plain read-write `sqlite3.connect(db)` creates and leaves `-wal` and `-shm`).
---
### `[P2]` Non-blocking — `status.sh` and `reconcile.sh` still open `opencode.db` with read-write `sqlite3.connect(odb)`
- **Locations**:
- `.agents/skills/multi-agent-mux-status/scripts/status.sh:116, 316`
- `.agents/skills/multi-agent-mux-monitor/scripts/reconcile.sh:953`
- **Observation**:
While `OpenCodeAgentAdapter` methods (`verify_artifact`, `discover`, `_has_session`, `_descendants`, `residual_artifacts`) now use `_ro_connect(db)`, `status.sh` (both `--json` and text mode) and `reconcile.sh` (drift C, which `status.sh` also runs via `--dry-run`) still call `sqlite3.connect(odb)` directly:
- On macOS, calling `sqlite3.connect(odb)` on a sidecar-free WAL-mode `opencode.db` creates `opencode.db-wal` and `opencode.db-shm` and leaves them on disk after `.close()`.
- On Linux, calling `sqlite3.connect(odb)` on a `leftover` WAL database checkpoints the WAL and deletes `opencode.db-wal` and `opencode.db-shm` on `.close()`.
- **Suggestion**:
In `status.sh:108-120, 308-320`, replace the raw `sqlite3.connect(odb)` block with `get_adapter('opencode')._has_session(odb, u)` (which already uses `_ro_connect`), and in `reconcile.sh:953`, use `_ro_connect(odb)` from `lib_py.agents.adapters.opencode`.
---
## 3. Detailed Review by Checklist Item
1. **`_ro_connect` on macOS (`.agents/skills/lib_py/agents/adapters/opencode.py:13-36`)**:
- **Both `-wal` and `-shm` present (`leftover` / `writer`)**: `sqlite3.connect(f"file:{quote(path)}?mode=ro", uri=True)` reads uncommitted-to-main-file (WAL-committed) rows through the existing WAL index without creating or removing sidecars.
- **No `-wal` (`clean`)**: `sqlite3.connect(f"file:{quote(path)}?mode=ro&immutable=1", uri=True)` reads `opencode.db` directly without creating `-wal`/`-shm` (and avoids `sqlite3.OperationalError: unable to open database file` that plain `mode=ro` raises on macOS when opening a WAL-header DB without sidecars). Because every query opens, fetches, and immediately closes `conn`, any race with a writer starting right after `os.path.exists(path + "-wal")` only sees the pre-WAL snapshot for that single short read and never holds an immutable handle across writes.
- **`-wal` without `-shm` (`walonly`)**: Copies `opencode.db` and `opencode.db-wal` into a `tempfile.TemporaryDirectory(prefix="mam-opencode-")`, opens the private copy so SQLite can rebuild `-shm` and recover the committed WAL frames, backs it up to `:memory:`, and cleans up the temp dir. Verified on macOS that committed rows in `-wal` are returned and the source directory remains `['opencode.db', 'opencode.db-wal']`.
2. **`_DB_PATHS` process-local cache (`opencode.py:40, 144-160`)**:
- Keyed by `(bin_path, ctx.home_dir, self._data_home(ctx))` (or `(bin_path, HOME, XDG_DATA_HOME)` when `ctx.home_dir` is unset).
- Different `HOME`, `XDG_DATA_HOME`, or binary paths produce distinct cache keys; failed `debug paths` runs (`returncode != 0` or no `db` line) are not cached.
3. **`auth_ok` & `create_session.sh` (`opencode.py:317-321`, `__main__.py:120-127`, `create_session.sh:146-151`)**:
- Checks binary existence on `PATH` or `<home>/.opencode/bin/opencode` without spawning `opencode --version` (preventing log writes to the real opencode data dir). Other agents' branches in `create_session.sh` are untouched.
4. **`input_placeholder_pattern` (`opencode.py:75-79`, `base.py:212-217`, `input_region.py:80-81`)**:
- `re.fullmatch(r'Ask anything(?:…|\.\.\.)(?: "[^"]*"?)?', head.strip())` strips both full and narrow-truncated 2.x home screen placeholders while preserving user input starting with `Ask anything…` (such as `'Ask anything… "x" and fix it'`). Other adapters return `None`.
5. **Python 3.9 & macOS compatibility**:
- All syntax, type annotations, and shell scripts are compatible with Python 3.9.6 and macOS bash 3.2 (`set -euo pipefail`). `git diff --check` is clean.
---
## 4. Test Execution Summary
Executed in `/var/folders/q_/68sxgw3j073dgjg8wl9s08vw0000gn/T/mam-review/loop-20261019T000805Z-975c4c/1` with isolated `HOME`, external `--basetemp`, `PYTHONDONTWRITEBYTECODE=1`, and `-p no:cacheprovider`:
- `tests/test_issue60_opencode_v2.py`: 15 passed
- `tests/test_a4_adapter_contract.py`: 33 passed
- `tests/test_issue50_dispatch.py`: 49 passed
- `tests/test_issue55_agent_homes.py`: 11 passed
- `tests/test_issue60_linux_fixes.py`: 40 passed, **5 failed** (`test_a_query_reads_right_and_leaves_the_sidecars_alone[clean-listing0-*]`)
[VERDICT: NOT PASS]