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.
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.
/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.
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)
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>
- 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
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
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
--standalonechild's lifetime, the rejected flags,/exitand 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)Ask anything… "<example>". The extractor read that line as typed text, sostall_watch.input_empty()never saw the box as empty.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.input_placeholdermatch.opencode_idle_2x_home.txt: a real 140x40 capture with the cwd line masked;opencode_idle_2x_home_narrow.txt: the example cut short.opencode queries no longer write beside the agent's database (
ac5f115,7803d47)verify_artifact,_has_sessionanddiscoveropenedopencode.dbread-write. On Linux, closing the last connection checkpoints the WAL and deletes-wal/-shm.mode=rois 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_connectnow picks the open by which sidecars exist:mode=ro, which reads through them and creates nothing;-wal: nothing to apply, somode=ro&immutable=1;-walleft 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_namingtakes an optionalconnect. 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.opencode session deleteand the cleanup of old rows.debug pathsruns once per binary and home (614ab14)opencode debug pathswrites acli startingline to opencode's own log._resolve_dbnow 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)auth.json. A fresh HOME runs a free default model, and local providers have no key.auth_okis therefore true when the binary exists. Keys andauth.jsonno longer count.create_session.shno longer keeps its ownauth.jsonand key check. It asks the adapter through a newpython -m lib_py.agents auth-ok <agent>. This also drops theopencode --versioncall that wrote to the real opencode log.stop's wording matches what happened (
463817e)/exitends opencode, butsend_keys_safethen waits forworkingand returns rc 4.A test no longer depends on PATH's python3 (
4d49abb)test_u1e_a_dry_run_record_is_not_an_open_jobruns the wrapper fromtmp_path, which has no.venv. The wrapper then took PATH'spython3, which on this host is linuxbrew's, with no paho.DELEGATE_JOB_PYTHON=sys.executable.Verification
tests/test_issue60_linux_fixes.py(45 tests) plustest_u1e, 46 in all.1d7b7da, with only the new tests and fixtures added: 23 of the 46 fail.--plan --all-reviewer.-walwithout its-shmwas ignored.6a4e385: 2018 passed, 20 skipped, 12 failed (54 min).tests/test_issue10_13_purge.pyagy failures (#28).test_u1enow passes..claude/skillsin a worktree (1).Not in this PR
/tmptraces the suite leaves despite--basetemp. They are unrelated to #60.🤖 Generated with Claude Code
debug pathsruns once per binary and home in a process (Issue #60, Linux item 6) 614ab1420f