Files
multi-agent-mux/.agents/reports/reviewer-grok-01/report-5ab36507.md
T

54 lines
9.0 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.
# Review 5ab36507 — Issue #60, second pass
Reviewed only the snapshot `/var/folders/q_/68sxgw3j073dgjg8wl9s08vw0000gn/T/mam-review/loop-20261018T064723Z-79133f/1` (`loop-20261018T064723Z-79133f`). Head `cb5f439cce41a2741a6b9974a308a765e50f00ad`, tree `cfec95403cbde10a6340b9007e1246c83be461cd`, base `74333f87d0c02888c2b5b281e912d97e74348c01` is an ancestor. Four commits, 14 files, +689/−300. `git diff --check` is clean. Snapshot porcelain is empty and the snapshot has no `.mam`. Control-plane main is `74333f87d0c02888c2b5b281e912d97e74348c01`, porcelain empty. No product file was edited.
The new commit on top of the first-pass head `6b4b96f` is `cb5f439` (`fix(opencode): a purge takes its child sessions' 1.x rows, the background service alone holds no session, XDG_DATA_HOME is followed`). The checks below are against the whole base..head range.
Host tools: `/usr/bin/python3` 3.9.6, the control-plane venv the same 3.9.6, `/bin/bash` 3.2.57. `bash -n` accepts `create_session.sh`, `resume_session.sh`, `update_yaml_resumed.sh`, `reconcile.sh`, and `status.sh`. `opencode --version` in a temporary home, with `XDG_DATA_HOME`, `XDG_STATE_HOME`, `XDG_CONFIG_HOME`, and `XDG_CACHE_HOME` unset, printed `opencode v2.0.32`.
The user's background service stayed up for the whole review. Its pid is 86524, the command is `opencode serve --service`, and both service-file mtimes stayed 1792246838. The real database mtime stayed 1792247140, and a read-only count stayed at 4 `session` rows and 4 `session_v2` rows. `lsof` on that database shows only pid 86524, and that command line contains `serve --service`. Nothing in this review called `session delete`, `service stop`, or an API against that database.
## What cb5f439 changes
`purge_artifacts` records descendants before `opencode session delete` (`opencode.py:162`). `_descendants` walks `parent_id` in `session_v2` and, when that column exists, in the 1.x `session` table, at every depth, through a read-only URI (`opencode.py:195-217`). The SQL that follows deletes `message`, `part`, `todo`, `session_share`, and `session` (and the plural names) for the parent and those descendants (`opencode.py:178-190`). `todo` and `session_share` are keyed by `session_id` on this machine's migrated database; the purge deletes those rows and the report path is what remains. A fresh 2.x database has none of those tables, and the real contract still expects the purged list to be only the session-delete line.
`residual_artifacts` asks `db_rows_naming` for the parent and for each descendant still on record, and suffixes the descendant lines with `[child session <id>]` (`opencode.py:229-237`). `test_purge_takes_the_1x_rows_of_the_child_sessions_too` seeds a parent, a child, a grandchild, and an unrelated session, and expects the unrelated session to keep six entries. `test_a_failed_delete_reports_the_child_sessions_left` expects the child `session_v2` row, with that suffix, when the binary is missing.
`conversation_in_use` still treats a command line that names the id as in use. A process that holds the shared database counts as well, except a command line that contains `serve --service` (`opencode.py:240-262`). On this Mac that is the only holder of the real database, so a purge of a session in that file is not refused solely because the user's service is running. Any other holder still returns true, and the purge CLI still exits 4 before it deletes (`agents/__main__.py:146-148`). `lsof` or `ps` raising is treated as in use. When `lsof` is absent the database-holder check returns false; the command-line check still runs. This host has `lsof`.
`_data_home` returns the caller's `XDG_DATA_HOME` when that variable is set and `ctx.home_dir` is the caller's own home (realpath), and `<home>/.local/share` for any other home (`opencode.py:87-94`). `_run` puts that value in the child environment (`opencode.py:103-106`). `test_the_data_dir_follows_the_callers_xdg_data_home_only_for_its_own_home` covers both sides and the unset case.
`resident_children` is `('opencode',)` (`opencode.py:70-73`). `background_children` records a process's children before it skips a resident name (`stall_watch.py:126-132`), so the `opencode serve --stdio` child is idle and a tool under that child still counts. `test_the_standalone_server_is_a_resident_child_but_its_tools_are_work` passed here. The Linux `_proc_table` reads `/proc`; the name check also uses `argv[0]`, so a process whose comm is the binary and whose argv0 is `opencode` still matches. This host took the `ps` path.
The real-binary test binds `_bin` to the absolute path it already resolved (`test_issue60_opencode_v2.py` `test_real_opencode_2x_contract`), so the later calls do not depend on `PATH` under the temporary home. Drift C in `reconcile.sh:966-968` keeps `parent_id IS NULL`. `test_comp_opencode_create_and_reconcile_and_stop` inserts a newer child in the same directory and requires the pinned id to be the parent. That test passed.
## Tests that catch a revert
On the snapshot, 50 tests passed in 65.40 s: `tests/test_issue60_opencode_v2.py` (including the real-binary contract), `tests/test_a4_adapter_contract.py`, `tests/test_issue31_purge_residual.py::test_opencode_purge_leaves_no_rows`, and `tests/test_tier2_component.py::test_comp_opencode_create_and_reconcile_and_stop`. No skip and no error. Pytest used the control-plane venv, `PYTHONDONTWRITEBYTECODE=1`, `-p no:cacheprovider`, and `--basetemp` `/tmp/mam5ab3-5ab36507-pybase`. Inherited `HERDR_*`, `MAM_*`, `MQTT_*`, `SKS_*`, `WORKSPACE_ROOT`, `DELEGATE_JOB_LOGS_DIR`, `HOME_DIR`, `CLAUDE_PROJECT_DIR`, `CLAUDE_CONFIG_DIR`, `CLAUDE_CODE_TMPDIR`, `CODEX_HOME`, `PYTHONPATH`, and the four `XDG_*` variables were dropped.
A separate clone, discarded afterward, failed three probes:
- SQL limited to the parent id: `test_purge_takes_the_1x_rows_of_the_child_sessions_too` failed in 0.66 s. The purged list was only the session-delete line.
- Every database holder treated as in use: `test_the_background_service_alone_does_not_hold_a_session` failed in 0.96 s (`True` where the service-shaped holder must leave the session free).
- Drift C without `parent_id IS NULL`: `test_comp_opencode_create_and_reconcile_and_stop` failed in 17.08 s. `opencode_session_id_own` stayed unset, so the parent was not pinned.
Snapshot porcelain stayed empty.
The real contract ran under a pytest temporary home and tmp, with the four `XDG_*` variables unset, and called `--help`, `api --standalone`, and `session delete --standalone`. `--help` contains `--standalone` and `--session`. Discover returned the parent. `verify_artifact` on the child was true. Purge cleared parent and child residuals. A second delete exited 1 with `Session not found`. No model prompt was sent. After that run the real database still had 4 and 4 rows.
## Boundaries
`status.sh` `resume_on_disk` queries `session_v2` once it has opened a database (`status.sh:127` and the second copy at `:337`). The path it opens is still `$home/.local/share/opencode` (then `storage.db`, then a `project/*.db` walk). Drift C asks the adapter, which follows `XDG_DATA_HOME` for the caller's own home. On this host `XDG_DATA_HOME` is unset, so both look at `~/.local/share/opencode/opencode.db`. A caller who sets `XDG_DATA_HOME` would see drift C and `resume_on_disk` use different files.
The append of `sqlite rows for session` happens when one of the 1.x tables exists (`opencode.py:188-190`), including when the delete matched no row. `_run` copies the rest of the environment and sets `HOME`, `HOME_DIR`, and `XDG_DATA_HOME`. The contract test clears `XDG_STATE_HOME`, `XDG_CONFIG_HOME`, and `XDG_CACHE_HOME` before it starts the binary.
This review did not start a herdr pane, so it did not drive create, a first TUI message, drift C, stop, and resume as one live chain against a real TUI. The tier-2 test drives create, drift C, and stop against the mock herdr and the fake CLI, in a sandbox home. Stop's graceful `/exit` then kill-session then SIGKILL path is unchanged. `--standalone` is what makes the server a child of that TUI. The id charset on this database matches `[A-Za-z0-9_]`, so the unquoted `$UUID` in `resume_session.sh` is safe for the ids stored here.
Live skill files and `docs/` have no `opencode db path` and no `--agent build`. `VERSIONS.md`, `bug-report/`, and `.agents/reports/` still describe the 1.x command; the brief leaves those historical notes in place. `create_session.sh` still treats `auth.json` or a provider key as enough, and otherwise accepts `opencode --version`. That check was left as it was, and `--version` exits 0 on this install.
The new code uses `HOME`, `XDG_DATA_HOME`, sqlite, `subprocess`, `lsof`, and `ps`. `stall_watch` reads `/proc` when that directory exists. Nothing in the new adapter code is macOS-only. This host is macOS, so Linux was not executed here.
After the report, snapshot porcelain is still empty, the snapshot still has no `.mam`, and control-plane porcelain is still empty at `74333f87`. The herdr config mtime is still 1784456394. The opencode database mtime is still 1792247140 and service pid 86524 is still `opencode serve --service`.
[VERDICT: PASS]