Skip to content

fix(opencode): work in the task's workspace, not CodeFrame's own (#1007) - #1022

Merged
frankbria merged 2 commits into
mainfrom
fix/1007-opencode-dir
Aug 1, 2026
Merged

fix(opencode): work in the task's workspace, not CodeFrame's own (#1007)#1022
frankbria merged 2 commits into
mainfrom
fix/1007-opencode-dir

Conversation

@frankbria

Copy link
Copy Markdown
Owner

Closes #1007.

The bug

Every SubprocessAdapter targets a task's workspace with cwd=workspace_path. opencode does not honour it — it resolves its project directory from the parent process, so a delegated task edits whatever directory CodeFRAME is running from. Under codeframe serve, that is the server's own checkout.

require_file_changes does not save us: the guard inspects the workspace, finds no changes and fails the run — correctly reporting failure while the edits have already landed somewhere else.

Verification

The issue's own investigation stalled here: two --dir attempts timed out at 240s and 280s, and a plain opencode run timed out right afterwards, so --dir could not be confirmed. opencode's backend is still returning UnknownError for every turn today.

That turned out not to matter. opencode emits its resolved project directory in a session_start event before contacting any model, so the check needs no successful turn:

BASELINE  cwd=<target>, no --dir   →  "cwd":"/tmp"                    ← the parent shell's dir
WITH      --dir <target>           →  "cwd":"/tmp/oc1007-dir-wogj..." ← the target

Both directions are now pinned by binary-gated tests, and both genuinely run here (not skipped):

test_the_adapter_resolves_the_workspace_not_the_parent_cwd PASSED
test_without_dir_it_takes_the_parent_cwd                   PASSED

The second pins the bug, so nobody drops --dir believing cwd= is enough — and it xfails loudly if opencode ever starts honouring cwd=.

A gap this exposed in #915's contract tier

Adding --dir to the tier's expectations for opencode failed: --dir appears only under opencode run --help, and the tier could only read top-level --help. So the flags most likely to drift — subcommand flags, which is where modern CLIs put everything — were invisible to the tier that exists to catch exactly that.

Engine gains help_subcommand, and the opencode entry now pins --dir. kilocode 7.x needs the same mechanism (#1015), where --dir also lives under run.

Had I not checked, this would have broken the required Engine CLI Contract check.

Also

  • Removes the pytest.xfail in test_opencode_smoke_913.py that tracked this issue.
  • Updates the two existing test_opencode.py tests that assert the exact command list.
  • opencode writes --help to stderr, which the tier's _cli_help already handled and my first draft of the new test did not.

Testing

tests/core/adapters/: 298 passed. ruff clean. Full gate re-running on the clean branch.

Every SubprocessAdapter targets a workspace with cwd=workspace_path. opencode
does not honour it — it resolves its project directory from the *parent*
process, so a delegated task edits whatever directory CodeFrame is running
from. Under `codeframe serve` that is the server's own checkout.

require_file_changes does not catch it: the guard inspects the workspace, finds
nothing and fails the run — correctly reporting failure while the edits have
already landed elsewhere.

build_command now passes --dir. Verified against opencode 1.17.13 by reading
the project directory out of opencode's own session_start event, which it emits
*before* contacting any model: with cwd= alone it reports the parent's
directory, with --dir it reports the workspace. That makes the check
independent of opencode's backend, which is returning UnknownError for every
turn today — the same instability that stalled the issue's own investigation.

Both directions are pinned: one test asserts the adapter's command resolves the
workspace when the parent cwd is a *different* directory, and another pins the
bug itself so nobody drops --dir believing cwd= is enough (it xfails, loudly,
if opencode ever starts honouring cwd=).

Also extends #915's contract tier with `help_subcommand`. It could only read
top-level --help, and --dir appears only under `opencode run --help` — so the
flags most likely to drift, the subcommand ones, were invisible to the tier
that exists to catch exactly that. Caught while wiring this fix: adding --dir
to the tier's expectations failed until the tier could see the subcommand.
kilocode 7.x needs the same mechanism (#1015).

Removes the xfail in test_opencode_smoke_913.py that tracked this.
@github-actions

github-actions Bot commented Aug 1, 2026

Copy link
Copy Markdown
Contributor

Claude finished @frankbria's task in 2m 29s —— View job


GLM precision review — complete ✅

✅ No defects found. Scoped to concrete defects only (logic, error paths, security, races, contract breakage, resource leaks). Style/architecture/test-coverage are out of scope (covered by the general review).

  • Gather diff + PR context, check for prior findings
  • Read opencode.py + SubprocessAdapter caller, verify the --dir fix
  • Read Engine contract tier, verify help_subcommand plumbing
  • Confirm/discard each suspicion against real code
  • Post summary

Verified:

  • build_command (opencode.py:180) — --dir <workspace> precedes the positional, survives the >128 KiB stdin path, composes with --auto; str(Path) is the correct argv form.
  • help_subcommand (test_engine_smoke_tier.py:105) — defaulted field, inserted safely; no existing Engine(...) breaks.
  • _cli_help — both call sites handle the optional arg; failed subcommand-help fails the contract loudly.
  • No other caller/test asserts a now-broken command shape; unchanged test_opencode.py assertions still hold.

One note (not a defect): --dir makes opencode load <workspace>/opencode.json as project config — the intended #1007 fix. The repo-config trust surface this opens is already documented in get_env and tracked under #903/#905; --auto is off by default.

Full summary posted as a PR comment.

@github-actions

github-actions Bot commented Aug 1, 2026

Copy link
Copy Markdown
Contributor

✅ GLM review: no defects found.

Verified against the checked-out code, not the diff alone:

  • build_command (codeframe/core/adapters/opencode.py:180) — --dir <workspace> precedes the positional (correct for opencode run [options] [message..]), survives the >128 KiB prompt→stdin path, and composes with --auto. str(Path) is the right form for a single argv entry; ordering is pinned by the new test_dir_precedes_the_positional_message.
  • help_subcommand (tests/core/adapters/test_engine_smoke_tier.py:105) — new defaulted (None) dataclass field inserted before the other defaulted field, so no existing Engine(...) construction breaks; only opencode passes it.
  • _cli_help (test_engine_smoke_tier.py:175) — both call sites handle the new optional subcommand arg; a non-zero/empty subcommand --help returns empty text and fails the contract assertion loudly rather than passing silently.
  • No other caller/test asserts a now-broken command shape: the registry/runtime tests do not inspect opencode argv, and the unchanged assertions in test_opencode.py (last-element-is-prompt, all-argv-under-128KiB, prompt-in-exactly-one-transport) still hold with --dir prepended.

Note (intended behavior, not a defect): with --dir, opencode now loads <workspace>/opencode.json as its project config — this is the fix for #1007. The repo-supplied-config trust surface it opens is already documented in get_env and tracked under #903/#905, and --auto (the only path using the adapter deny-list) is off by default.

View job run

@github-actions

github-actions Bot commented Aug 1, 2026

Copy link
Copy Markdown
Contributor

Claude finished @frankbria's task in 3m 41s —— View job


GLM precision review — complete ✅

✅ No defects found. Re-reviewed on the synced branch (02bfc87). Scoped to concrete defects only (logic, error paths, security, races, contract breakage, resource leaks). Style/architecture/test-coverage are out of scope.

  • Gather diff + PR context, check for prior findings
  • Read opencode.py + SubprocessAdapter caller, verify the --dir fix
  • Read Engine contract tier, verify help_subcommand plumbing
  • Confirm/discard each suspicion against real code
  • Post summary

Verified against the checked-out code, not the diff alone:

  • build_command (opencode.py:180) — --dir <workspace> precedes the positional (opencode run [options] [message..]), survives the >128 KiB stdin path (positional dropped, --dir kept, len(cmd)==4), composes with --auto; str(workspace_path) is safe — always a real Path at the call site (runtime.py:732, never None).
  • help_subcommand (test_engine_smoke_tier.py:105) — defaulted None field inserted between two defaulted fields; all 4 Engine(...) constructions use ≤4 positionals + keywords, so none break; _cli_help no-ops when None and fails the contract loudly on a missing flag.
  • The merge only pulled in d5ecc95 (fix(cli): exit non-zero when a resumed task does not complete (#1005 follow-up) #1021, CLI-only) — no overlap with this PR's files, no conflicts.
  • No other caller/test asserts a now-broken command shape; updated test_opencode.py assertions hold.

One note (intended behavior, not a defect): --dir makes opencode load <workspace>/opencode.json as project config — this is the #1007 fix. The repo-config trust surface it opens is already documented in get_env and tracked under #903/#905; --auto is off by default.

Full summary posted as a PR comment.

@github-actions

github-actions Bot commented Aug 1, 2026

Copy link
Copy Markdown
Contributor

GLM precision review (re-review on synced branch) — ✅ no defects found

Re-ran on the 02bfc87 sync (merge brought in d5ecc95 / #1021, a CLI-only change — no overlap with this PR files, no conflicts). Scoped to concrete defects only: logic, error paths, security, races, contract breakage, resource leaks. Style/architecture/test-coverage are out of scope.

Verified against the checked-out code, not the diff alone:

Area Verdict
build_command (codeframe/core/adapters/opencode.py:180) — --dir <workspace> precedes the positional ✅ correct for opencode run [options] [message..]; pinned by test_dir_precedes_the_positional_message
Oversized-prompt (>128 KiB) path — positional dropped, --dir kept len(cmd) == 4 and stdin carries the prompt; no double-send
--auto composition _cli_args is ["run"]/["run","--auto"]; --dir lands after both, before the positional
str(workspace_path) safety ✅ always a real Path at the call site — effective_repo_path = exec_ctx.workspace_path (runtime.py:732), never None
help_subcommand (test_engine_smoke_tier.py:105) — defaulted field insertion ✅ placed between two defaulted fields; all 4 Engine(...) constructions use ≤4 positionals + keywords — none break
_cli_help optional arg ✅ no-ops when None; failed subcommand---help returns empty and fails the contract assertion loudly
Other callers/tests asserting command shape ✅ registry/runtime do not inspect opencode argv; updated test_opencode.py assertions hold

Note (intended behavior, not a defect): with --dir, opencode loads <workspace>/opencode.json as project config — this is the #1007 fix. The repo-config trust surface it touches is already documented in get_env and tracked under #903/#905; --auto (the only path using the adapter deny-list) is off by default.

@frankbria
frankbria merged commit 401bde3 into main Aug 1, 2026
12 of 13 checks passed
@frankbria
frankbria deleted the fix/1007-opencode-dir branch August 1, 2026 10:02
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[P0.27] OpenCode adapter writes outside the workspace — opencode ignores the subprocess cwd

1 participant