fix(opencode): the Linux check fixes for opencode 2.x (Issue #60) #62

Merged
Godopu merged 10 commits from fix/issue-60-linux into main 2026-10-19 03:16:21 +00:00
Owner

Closes #60

This fixes the defects that the Linux check of v4.9.6 found. That check ran on Ubuntu 26.04 with Python 3.14.7, opencode 2.0.32 and herdr 0.9.3. Every Linux-specific behaviour already matched the Mac: paths, XDG, the --standalone child's lifetime, the rejected flags, /exit and status. The defects below are not specific to one OS. No version bump; the version is set at merge.

The 2.x home placeholder is not typed input (14d7a46, 6a4e385)

  • On the 2.x home screen the input box shows Ask anything… "<example>". The extractor read that line as typed text, so stall_watch.input_empty() never saw the box as empty.
  • Adapters gain input_placeholder_pattern, a regex the box's first line must match in full. opencode's pattern also takes the example cut short in a narrow box.
  • Other adapters keep the exact input_placeholder match.
  • New fixtures:
    • opencode_idle_2x_home.txt: a real 140x40 capture with the cwd line masked;
    • opencode_idle_2x_home_narrow.txt: the example cut short.
  • Typed text is still read, including text that looks like the placeholder.

opencode queries no longer write beside the agent's database (ac5f115, 7803d47)

  • Before: verify_artifact, _has_session and discover opened opencode.db read-write. On Linux, closing the last connection checkpoints the WAL and deletes -wal/-shm.
  • A plain mode=ro is not enough. On a WAL database with no sidecars it creates both files and leaves them (sqlite 3.53.4). The Linux check left two such files in a real home.
  • _ro_connect now picks the open by which sidecars exist:
    • both sidecars (a live writer keeps them): mode=ro, which reads through them and creates nothing;
    • no -wal: nothing to apply, so mode=ro&immutable=1;
    • a -wal left without its -shm (review): it still holds committed rows. They are read from a private temporary copy, loaded into memory. This state is rare, and the copy costs about 0.14 s for a 195 MB database.
  • db_rows_naming takes an optional connect. Only opencode passes one, so other agents are unchanged. agy (#28), codex and hermes still open their databases read-write and can adopt the same helper later.
  • The purge's intended writes are unchanged: opencode session delete and the cleanup of old rows.

debug paths runs once per binary and home (614ab14)

  • Each opencode debug paths writes a cli starting line to opencode's own log.
  • _resolve_db now caches the printed path per (binary, HOME, XDG_DATA_HOME) within a process. Before, one stop ran it twice.

The auth check is the binary (d17cd80)

  • 2.x has no auth.json. A fresh HOME runs a free default model, and local providers have no key.
  • opencode's auth_ok is therefore true when the binary exists. Keys and auth.json no longer count.
  • create_session.sh no longer keeps its own auth.json and key check. It asks the adapter through a new python -m lib_py.agents auth-ok <agent>. This also drops the opencode --version call that wrote to the real opencode log.
  • Other agents' checks are unchanged.

stop's wording matches what happened (463817e)

  • /exit ends opencode, but send_keys_safe then waits for working and returns rc 4.
  • stop used to print "falling back to kill chain" even when nothing was killed.
  • It now prints "delivery of '/exit' unconfirmed … waiting for the session to exit". The exit decision and the kill chain are unchanged.

A test no longer depends on PATH's python3 (4d49abb)

  • test_u1e_a_dry_run_record_is_not_an_open_job runs the wrapper from tmp_path, which has no .venv. The wrapper then took PATH's python3, which on this host is linuxbrew's, with no paho.
  • The test now passes DELEGATE_JOB_PYTHON=sys.executable.

Verification

  • Regression tests: tests/test_issue60_linux_fixes.py (45 tests) plus test_u1e, 46 in all.
    • On base 1d7b7da, with only the new tests and fixtures added: 23 of the 46 fail.
    • At head: all pass.
  • Review loop: --plan --all-reviewer.
    • Round 1: claude and agy PASS. grok and codex NOT PASS: item 3 was not yet committed, and a -wal without its -shm was ignored.
    • Round 2: claude, agy, grok and codex all PASS.
  • Full suite on Linux at 6a4e385: 2018 passed, 20 skipped, 12 failed (54 min).
    • The 12 failures are the known tests/test_issue10_13_purge.py agy failures (#28). test_u1e now passes.
    • The skips are the nats-docker submodule (18), the nested-pytest half of S6 (1), and .claude/skills in a worktree (1).
  • Real home: the real opencode data, state and config directories and the opencode log were unchanged before and after the suite.

Not in this PR

  • The 2.x chat-screen placeholder. Sending a message would call a model, so there is no capture of that screen.
  • The /tmp traces the suite leaves despite --basetemp. They are unrelated to #60.

🤖 Generated with Claude Code

Closes #60 This fixes the defects that the Linux check of v4.9.6 found. That check ran on Ubuntu 26.04 with Python 3.14.7, opencode 2.0.32 and herdr 0.9.3. Every Linux-specific behaviour already matched the Mac: paths, XDG, the `--standalone` child's lifetime, the rejected flags, `/exit` and status. The defects below are not specific to one OS. No version bump; the version is set at merge. ## The 2.x home placeholder is not typed input (14d7a46, 6a4e385) - On the 2.x home screen the input box shows `Ask anything… "<example>"`. The extractor read that line as typed text, so `stall_watch.input_empty()` never saw the box as empty. - Adapters gain `input_placeholder_pattern`, a regex the box's first line must match in full. opencode's pattern also takes the example cut short in a narrow box. - Other adapters keep the exact `input_placeholder` match. - New fixtures: - `opencode_idle_2x_home.txt`: a real 140x40 capture with the cwd line masked; - `opencode_idle_2x_home_narrow.txt`: the example cut short. - Typed text is still read, including text that looks like the placeholder. ## opencode queries no longer write beside the agent's database (ac5f115, 7803d47) - Before: `verify_artifact`, `_has_session` and `discover` opened `opencode.db` read-write. On Linux, closing the last connection checkpoints the WAL and deletes `-wal`/`-shm`. - A plain `mode=ro` is not enough. On a WAL database with no sidecars it creates both files and leaves them (sqlite 3.53.4). The Linux check left two such files in a real home. - `_ro_connect` now picks the open by which sidecars exist: - both sidecars (a live writer keeps them): `mode=ro`, which reads through them and creates nothing; - no `-wal`: nothing to apply, so `mode=ro&immutable=1`; - a `-wal` left without its `-shm` (review): it still holds committed rows. They are read from a private temporary copy, loaded into memory. This state is rare, and the copy costs about 0.14 s for a 195 MB database. - `db_rows_naming` takes an optional `connect`. Only opencode passes one, so other agents are unchanged. agy (#28), codex and hermes still open their databases read-write and can adopt the same helper later. - The purge's intended writes are unchanged: `opencode session delete` and the cleanup of old rows. ## `debug paths` runs once per binary and home (614ab14) - Each `opencode debug paths` writes a `cli starting` line to opencode's own log. - `_resolve_db` now caches the printed path per (binary, HOME, XDG_DATA_HOME) within a process. Before, one stop ran it twice. ## The auth check is the binary (d17cd80) - 2.x has no `auth.json`. A fresh HOME runs a free default model, and local providers have no key. - opencode's `auth_ok` is therefore true when the binary exists. Keys and `auth.json` no longer count. - `create_session.sh` no longer keeps its own `auth.json` and key check. It asks the adapter through a new `python -m lib_py.agents auth-ok <agent>`. This also drops the `opencode --version` call that wrote to the real opencode log. - Other agents' checks are unchanged. ## stop's wording matches what happened (463817e) - `/exit` ends opencode, but `send_keys_safe` then waits for `working` and returns rc 4. - stop used to print "falling back to kill chain" even when nothing was killed. - It now prints "delivery of '/exit' unconfirmed … waiting for the session to exit". The exit decision and the kill chain are unchanged. ## A test no longer depends on PATH's python3 (4d49abb) - `test_u1e_a_dry_run_record_is_not_an_open_job` runs the wrapper from `tmp_path`, which has no `.venv`. The wrapper then took PATH's `python3`, which on this host is linuxbrew's, with no paho. - The test now passes `DELEGATE_JOB_PYTHON=sys.executable`. ## Verification - **Regression tests:** `tests/test_issue60_linux_fixes.py` (45 tests) plus `test_u1e`, 46 in all. - On base `1d7b7da`, with only the new tests and fixtures added: 23 of the 46 fail. - At head: all pass. - **Review loop:** `--plan --all-reviewer`. - Round 1: claude and agy PASS. grok and codex NOT PASS: item 3 was not yet committed, and a `-wal` without its `-shm` was ignored. - Round 2: claude, agy, grok and codex all PASS. - **Full suite on Linux at `6a4e385`:** 2018 passed, 20 skipped, 12 failed (54 min). - The 12 failures are the known `tests/test_issue10_13_purge.py` agy failures (#28). `test_u1e` now passes. - The skips are the nats-docker submodule (18), the nested-pytest half of S6 (1), and `.claude/skills` in a worktree (1). - **Real home:** the real opencode data, state and config directories and the opencode log were unchanged before and after the suite. ## Not in this PR - The 2.x chat-screen placeholder. Sending a message would call a model, so there is no capture of that screen. - The `/tmp` traces the suite leaves despite `--basetemp`. They are unrelated to #60. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
Godopu added 8 commits 2026-10-18 23:43:44 +00:00
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
An adapter can now name its placeholder as a pattern the box's first line
matches in full (input_placeholder_pattern); input_placeholder stays the
literal lib.sh greps for. opencode: `Ask anything… "<example>"`, the closing
quote optional because a box under ~65 columns cuts the example. Fixtures:
the 140-column herdr capture and a 60-column one with the cut example.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A read-write open checkpointed the WAL and deleted -wal/-shm on close; a
plain mode=ro open created missing ones and left them. _ro_connect uses the
sidecars only when both exist (a live writer keeps them) and reads the file
as immutable otherwise. verify_artifact, _has_session, discover,
_descendants and residual_artifacts (db_rows_naming takes a connect) use it;
the purge's own writes are unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Every run appends a line to opencode's log, and one discover resolved the
database once per candidate. A successful answer is kept per (binary, HOME,
XDG_DATA_HOME) - the values the run sees; a failed run is not remembered.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
An agent that exits on /exit never turns `working`, so send_keys_safe
returns 4 although the stop then ends cleanly. The line now says the
delivery is unconfirmed and that the stop waits for the session to exit;
the kill steps keep their own lines, printed only when they run. Exit
judgement and the kill chain are unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
_ro_connect read every database without both sidecars as immutable, which
skips a -wal's committed rows: a recovered DB+WAL with no -shm showed no
sessions (planner-reviewer-codex-01). That state - no writer runs without
a -shm - is now read from a private DB+WAL copy, where SQLite may rebuild
the index, loaded into memory; nothing beside the source is touched. The
query matrix gains the walonly state.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
opencode 2.x needs no login: a fresh HOME runs a free default model and a
local provider has no key, while 2.x keeps no auth.json. auth_ok is now
"opencode on PATH or under <HOME>/.opencode/bin". `python -m lib_py.agents
auth-ok <agent>` exits with an adapter's verdict, and create's opencode
branch uses it (same 7 lines) instead of its own auth.json/key test and
`opencode --version`, which wrote a line to the real opencode log.
test_a4's opencode block now pins the binary rule (decision D1, required
by the brief's item 3 and by both reviewers). Other agents are unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Godopu added 1 commit 2026-10-19 01:57:41 +00:00
- tests: macOS SQLite keeps -wal/-shm after the last close (persistent WAL);
  the `clean` state removes them after the checkpointing close
- the 2.x placeholder pattern requires the quoted example, so a typed
  `Ask anything…` is read as a draft
- auth_ok takes ~/.opencode/bin/opencode only as an executable file
- status.sh and reconcile.sh (drift C) open opencode.db with _ro_connect

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VCgu9Fa9iT7TbJUSzdq4Ff
Godopu added 1 commit 2026-10-19 03:16:20 +00:00
Godopu merged commit 3ce5d5fdc1 into main 2026-10-19 03:16:21 +00:00
Sign in to join this conversation.
No Reviewers
No labels
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: tmpl/multi-agent-mux#62