Skip to content

feat(runner): keep each opencode job's saved output its own - #536

Merged
moq-bot[bot] merged 10 commits into
mainfrom
quest/m0/kix/sandbox/opencode-data
Nov 7, 2026
Merged

moq-bot[bot] merged 10 commits into
mainfrom
quest/m0/kix/sandbox/opencode-data

Conversation

@kixelated

@kixelated kixelated commented Nov 7, 2026 •

Copy link
Copy Markdown
Collaborator

Each opencode job now saves its tool output and shell captures in its own directory, so no job can read output another job saved, whether on another repository or the same one. Sessions and the account's credential stay in the account's shared database.

Changes

  • Workspace.data (<checkout>.data) sits beside the checkout, in the runner's state directory, which commands cannot read. Workspace.remove deletes it along with the checkout. The job passes it to the adapter as Launch.data.
  • The opencode launch sets XDG_DATA_HOME to that directory. savedOutput, the external_directory allow rules, and the plugin's reads now name only this job's opencode/tool-output and opencode/shell.
  • OPENCODE_DB points at the account's opencode.db by absolute path for sign-in, status, and jobs alike (accountDatabase). A relative name would resolve inside the job's directory. Without the variable, opencode picks a database name per release channel.
  • RunnerHome resolves its directory to an absolute path, so a relative QUEST_RUNNER_HOME cannot turn OPENCODE_DB or the sandbox's paths relative.
  • Pre-existing hole, fixed here: moq-dev/quest#502 moved the pinned opencode from 2.1.1 to 2.2.1. When execute is denied, 2.2.x offers Code Mode's tools directly (packages/core/src/tool.ts, "expose them as direct tools instead"). Jobs were therefore offered opencode_session_move, opencode_session_rename, opencode_worktree_create/list/remove, opencode_models, and the MCP resource tools, all running in the unconfined server. They could move or rename any session in the shared database and create or remove worktrees anywhere. The quest's decision to share the database relied on those tools being unreachable. The runner's plugin now removes every tool in opencode's namespace through ctx.tool.transform. A deny rule could not catch all of them, because worktree_remove checks its permission as edit. On main, the live test already fails its offered-tools check on 2.2.1.
  • test/live/runner-sandbox.test.ts now runs a second job in another checkout of the same repository, on the same account. It tries to read the first job's real shell capture, plus a tool-output file the test puts where opencode would, through both the read tool and cat. opencode saves tool-output only for tools that bound none of their own output, such as kix's MCP tools, and the scripted job cannot call those. That is why the test plants the tool-output file instead of having job 1 produce one.

Public API and wire impact

None outside the runner. Launch gains a required data field. On-disk state moves (no migration; nothing is in production).

Decisions

Choices made without asking

  • The directory is named <checkout>.data, by its role, following <checkout>.review.
  • OPENCODE_DB is set for sign-in and status too, not only for jobs, so all three use one database whatever channel opencode was built for.
  • I fixed the 2.2.1 tool exposure in this PR rather than splitting it out: the quest's shared-database decision depends on it, and the live test this PR extends fails on it.

Validation

  • just check and just test pass (1100 passed, 6 skipped).
  • just runner-sandbox cannot run on this host. The host's AppArmor bwrap-userns-restrict profile makes srt's bwrap fail with bwrap: loopback: Failed RTM_NEWADDR: Operation not permitted. It fails in unixSocketRefusal, before any code this PR changes, and fails the same way on main. CI runs it nightly.
  • To cover what I could, I ran the live suite against the pinned opencode 2.2.1 with a pass-through srt (a stand-in that confines nothing), in a throwaway copy of the test:
    • The offered tools match the reviewed list again.
    • Job 1's shell captures land in checkouts/job.data/opencode/shell/<project>/. The account directory holds only opencode.db, log, and repos.
    • Job 2 ran on the shared database's credential.
    • The plugin refused job 2's read of both files of job 1.
    • Job 2's cat reads got through, but only because the stand-in confines nothing. The real srt denies the runner's state directory, which holds the data directory.
  • On main, the same run fails the offered-tools check with the eight opencode_* tools listed.

Caused by

  • moq-dev/quest#480 - on opencode 2.x, every job on an account shared its data/opencode, and the plugin let jobs read all saved output in it.
  • moq-dev/quest#502 - moved the pinned opencode to 2.2.1, which offers its own tools directly once execute is denied, without a run of the live sandbox test.

Suggested follow-ups

  • Make the live sandbox test gate opencode bumps: run just runner-sandbox on PRs that change flake.lock or @opencode/client, not only nightly. Recommendation: plan it. moq-dev/quest#502 merged an unreviewed opencode release, and that exposed unconfined tools.

Models

  • Size: M
  • Implementer: Claude Code, claude-opus-5-5, medium effort

Review

mechanical fix by codex, gpt-6.1-sol, medium effort: Merged base and re·solved the modify/delete conflict by retaining the m0 sandbox quest deletion; its m1 replacement already preserves base's updated wording; ran just check and just test (passed). (head 4fae41b)

Rewritten by quest review after each round.

  • Reviewer: Codex, gpt-6.1-sol, medium effort
  • Session: 01a1b820-bae3-7643-a7a4-ca9e3f92e4d5, round 1
  • Head: 3d5a993
  • Base: origin/main at 2108b1d
  • Score: 9/10, No actionable regressions were found; the output isolation, shared account database, cleanup, and PR workflow changes appear consistent, though builds and tests were not run as instructed.
  • Findings: none

@pullfrog

pullfrog Bot commented Nov 7, 2026

Copy link
Copy Markdown

Pullfrog  | Review this ➔

Each opencode job now saves its tool output and shell captures in a data
directory beside its checkout, removed with it, while sessions and the
credential stay in the account's database through an absolute OPENCODE_DB.
The runner's plugin also removes opencode's own tools, which opencode 2.2
offers directly once Code Mode's execute is denied.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@kixelated kixelated changed the title Per-job opencode saved output feat(runner): keep each opencode job's saved output its own Nov 7, 2026
@kixelated
kixelated marked this pull request as ready for review November 7, 2026 19:12
@coderabbitai

coderabbitai Bot commented Nov 7, 2026 •

Copy link
Copy Markdown

Important

  • Trigger review

This repository does not receive automatic reviews because it has fewer than 10 stars.

Run configuration

  • Source mode: Auto
  • Repository: Unavailable
  • Branch: Unavailable
  • File: No configuration file from this repository applied
  • Commit: Unavailable
  • Layers: Organization UI
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: 0a235fdd-5726-459f-b643-19ebd94e226e
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

Share

@kixelated kixelated left a comment

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Automated review by review (OpenAI)

Reviewed head d10d2d9 against base 1c3cf6a.

No actionable introduced findings in the inspected paths. Per-job data is propagated from Workspace through Launch, retained across attempts, and removed with the workspace. The saved-output permission roots now use that data directory, outside the checkout and within the runner state excluded from shell reads. Login, status and jobs share the explicit account database path, with RunnerHome normalized to an absolute path. The plugin removes tools in opencode's namespace rather than relying on their individual permission names, while retaining ordinary file tools and kix tools.

The added coverage checks configuration, plugin filtering and cleanup, and extends the live boundary test to a second job on the same account/repository reading the first job's saved files through both file and shell tools.

Direction: retain the full live sandbox test as a deployment validation requirement on a host where the real sandbox works. The reported pass-through-srt experiment is useful for integration, but does not demonstrate shell isolation; the PR correctly discloses that limitation.

Verification limits: GitHub-only static inspection of changed code, surrounding sandbox/plugin paths and tests. I did not execute tests or independently validate pinned opencode's runtime tool-transform behavior or operating-system isolation. No host configuration was changed.

(Written by review (OpenAI))

@kixelated
kixelated added this pull request to the merge queue Nov 7, 2026
@github-merge-queue
github-merge-queue Bot removed this pull request from the merge queue due to a conflict with the base branch Nov 7, 2026
@moq-bot

moq-bot Bot commented Nov 7, 2026

Copy link
Copy Markdown
Contributor

quest land watch: the merge queue removed this pull request at d10d2d9 because it conflicts with main (merge_conflict).

The land fixer works on it in the background, and the watcher re-enqueues it at the back of the queue once it is fixed or judged flaky, or hands it back here with what it needs.

(written by quest land watch)

@moq-bot
moq-bot Bot enabled auto-merge November 7, 2026 20:21
@moq-bot

moq-bot Bot commented Nov 7, 2026

Copy link
Copy Markdown
Contributor

quest land watch: armed auto-merge on this pull request at 0081eea, so it joins the back of the merge queue once its required checks pass, with main at 4b3a874 merged in by the quest merge driver.

(written by quest land watch)

@moq-bot
moq-bot Bot added this pull request to the merge queue Nov 7, 2026
@github-merge-queue
github-merge-queue Bot removed this pull request from the merge queue due to failed status checks Nov 7, 2026
@moq-bot

moq-bot Bot commented Nov 7, 2026

Copy link
Copy Markdown
Contributor

quest land watch: the merge queue removed this pull request at 0081eea because its merge_group build failed (failed_checks).

The land fixer works on it in the background, and the watcher re-enqueues it at the back of the queue once it is fixed or judged flaky, or hands it back here with what it needs.

Queue / Queue build / Build (job) failure at step Check that the PR leaves its quest accurate:

…store/lg46w7hrjx7kylsh6645c9l975i8h43b-git-2.55.0/bin/git config --local --get-all includeif.gitdir:/github/workspace/.git.path
/github/runner_temp/git-credentials-7d6dead1-0c75-4435-8713-45157be7a983.config
[command]/nix/store/lg46w7hrjx7kylsh6645c9l975i8h43b-git-2.55.0/bin/git config --local --unset includeif.gitdir:/github/workspace/.git.path \/github\/runner_temp\/git\-credentials\-7d6dead1\-0c75\-4435\-8713\-45157be7a983\.config
[command]/nix/store/lg46w7hrjx7kylsh6645c9l975i8h43b-git-2.55.0/bin/git config --local --get-all includeif.gitdir:/github/workspace/.git/worktrees/*.path
/github/runner_temp/git-credentials-7d6dead1-0c75-4435-8713-45157be7a983.config
[command]/nix/store/lg46w7hrjx7kylsh6645c9l975i8h43b-git-2.55.0/bin/git config --local --unset includeif.gitdir:/github/workspace/.git/worktrees/*.path \/github\/runner_temp\/git\-credentials\-7d6dead1\-0c75\-4435\-8713\-45157be7a983\.config
[command]/nix/store/lg46w7hrjx7kylsh6645c9l975i8h43b-git-2.55.0/bin/git submodule foreach --recursive git config --local --show-origin --name-only --get-regexp remote.origin.url
Removing credentials config '/var/cache/github-runner-work/_temp/git-credentials-7d6dead1-0c75-4435-8713-45157be7a983.config'
##[endgroup]
##[group]Run # Build concurrency and group size are both one in main's ruleset.
�[36;1m# Build concurrency and group size are both one in main's ruleset.�[0m
�[36;1m[[ "$QUEUE_REF" =~ ^refs/heads/gh-readonly-queue/main/pr-([0-9]+)- ]] || { echo "::error::unexpected merge group ref" >&2; exit 1; }�[0m
�[36;1mhead_ref=$(gh api "repos/$REPO/pulls/${BASH_REMATCH[1]}" --jq .head.ref)�[0m
�[36;1m./quest-base/bin/quest --root "$GITHUB_WORKSPACE" check --base "$BASE" --branch "$head_ref"�[0m
shell: /nix/store/1mv3qz005gkbalxfghk4y3s8ayv4d2dl-bash-interactive-5.3p15/bin/bash -e {0}
env:
  BASE: 455ebea4fa32580baaa2e2fd85d04612ab0cbc70
  GH_TOKEN: ***
  REPO: moq-dev/quest
  QUEUE_REF: refs/heads/gh-readonly-queue/main/pr-536-455ebea4fa32580baaa2e2fd85d04612ab0cbc70
##[endgroup]
quest: quest/m0/kix/sandbox/README.md: quest title must be '# [XS|S|M|L|XL] Title'
##[error]Process completed with exit code 1.
Post job cleanup.
[command]/nix/store/lg46w7hrjx7kylsh6645c9l975i8h43b-git-2.55.0/bin/git version
git version 2.55.0
Temporarily overriding HOME='/var/cache/github-runner-work/_temp/0782254c-e3a4-458e-89ad-609d4bca3154' before making global git config changes
Adding repository directory to the temporary git global config as a safe directory
[command]/nix/store/lg46w7hrjx7kylsh6645c9l975i8h43b-git-2.55.0/bin/git config --global --add safe.directory /var/cache/github-runner-work/quest/quest
Removing SSH command configuration
[command]/nix/store/lg46w7hrjx7kylsh6645c9l975i8h43b-git-2.55.0/bin/git config --local --name-only --get-regexp core\.sshCommand
[command]/nix/store/lg46w7hrjx7kylsh6645c9l975i8h43b-git-2.55.0/bin/git submodule foreach --recursive sh -c "git config --local --name-only --get-regexp 'core\.sshCommand' && git config --local --unset-all 'core.sshCommand' || :"
Removing HTTP extra header
[command]/nix/store/lg46w7hrjx7kylsh6645c9l975i8h43b-git-2.55.0/bin/git config --local --name-only --get-regexp http\.https\:\/\/github\.com\/\.extraheader
[command]/nix/store/lg46w7hrjx7kylsh6645c9l975i8h43b-git-2.55.0/bin/git submodule foreach --recursive sh -c "git config --local --name-only --get-regexp 'http\.https\:\/\/github\.com\/\.extraheader' && git config --local --unset-all 'http.https://github.com/.extraheader' || :"
Removing includeIf entries pointing to credentials config files
[command]/nix/store/lg46w7hrjx7kylsh6645c9l975i8h43b-git-2.55.0/bin/git config --local --name-only --get-regexp ^includeIf\.gitdir:
[command]/nix/store/lg46w7hrjx7kylsh6645c9l975i8h43b-git-2.55.0/bin/git submodule foreach --recursive git config --local --show-origin --name-only --get-regexp remote.origin.url
Post job cleanup.
[command]/nix/store/lg46w7hrjx7kylsh6645c9l975i8h43b-git-2.55.0/bin/git version
git version 2.55.0
Temporarily overriding HOME='/var/cache/github-runner-work/_temp/106a3c64-7054-4844-8eec-0a61235cb314' before making global git config changes
Adding repository directory to the temporary git global config as a safe directory
[command]/nix/store/lg46w7hrjx7kylsh6645c9l975i8h43b-git-2.55.0/bin/git config --global --add safe.directory /var/cache/github-runner-work/quest/quest
Removing SSH command configuration
[command]/nix/store/lg46w7hrjx7kylsh6645c9l975i8h43b-git-2.55.0/bin/git config --local --name-only --get-regexp core\.sshCommand
[command]/nix/store/lg46w7hrjx7kylsh6645c9l975i8h43b-git-2.55.0/bin/git submodule foreach --recursive sh -c "git config --local --name-only --get-regexp 'core\.sshCommand' && git config --local --unset-all 'core.sshCommand' || :"
Removing HTTP extra header
[command]/nix/store/lg46w7hrjx7kylsh6645c9l975i8h43b-git-2.55.0/bin/git config --local --name-only --get-regexp http\.https\:\/\/github\.com\/\.extraheader
[command]/nix/store/lg46w7hrjx7kylsh6645c9l975i8h43b-git-2.55.0/bin/git submodule foreach --recursive sh -c "git config --local --name-only --get-regexp 'http\.https\:\/\/github\.com\/\.extraheader' && git config --local --unset-all 'http.https://github.com/.extraheader' || :"
Removing includeIf entries pointing to credentials config files
[command]/nix/store/lg46w7hrjx7kylsh6645c9l975i8h43b-git-2.55.0/bin/git config --local --name-only --get-regexp ^includeIf\.gitdir:
[command]/nix/store/lg46w7hrjx7kylsh6645c9l975i8h43b-git-2.55.0/bin/git submodule foreach --recursive git config --local --show-origin --name-only --get-regexp remote.origin.url
A job completed hook has been configured by the self-hosted runner administrator
##[group]Run '/nix/store/gx2v6q84b0w43bafm0whkdx93qyxbpps-ci-cache-gc.sh/bin/ci-cache-gc.sh'
shell: /nix/store/1mv3qz005gkbalxfghk4y3s8ayv4d2dl-bash-interactive-5.3p15/bin/bash --noprofile --norc -e -o pipefail {0}
##[endgroup]
evicted 0 objects and 0 action results (0 B logical); 50.0 GiB logical remain
Cleaning up orphan processes

Queue / Queue (job) failure at step Require the build to pass:

Current runner version: '2.338.2'
##[group]Runner Image Provisioner
Hosted Compute Agent
Version: 20261028.624
Commit: 54898b74c23fde5556c9b3c3a2290865feef0dfe
Build Date: 2026-10-28T21:51:42Z
Worker ID: {cf4626c1-37a1-49fa-8cf5-4fd65b513c7e}
Region: westus2
Cloud: Azure
##[endgroup]
##[group]Operating System
Ubuntu
24.04.5
LTS
##[endgroup]
##[group]Runner Image
Image: ubuntu-24.04
Version: 20261101.360.1
Included Software: https://github.com/actions/runner-images/blob/ubuntu24/20261101.360/images/ubuntu/Ubuntu2404-Readme.md
Image Release: https://github.com/actions/runner-images/releases/tag/ubuntu24%2F20261101.360
##[endgroup]
##[group]GITHUB_TOKEN Permissions
Contents: read
Metadata: read
PullRequests: read
##[endgroup]
Secret source: Actions
Cache mode: write
Prepare workflow directory
Prepare all required actions
Complete job name: Queue
##[group]Run [[ "$RESULT" == success ]] || { echo "::error::the queue build ended $RESULT" >&2; exit 1; }
�[36;1m[[ "$RESULT" == success ]] || { echo "::error::the queue build ended $RESULT" >&2; exit 1; }�[0m
shell: /usr/bin/bash -e {0}
env:
  RESULT: failure
##[endgroup]
##[error]the queue build ended failure
##[error]Process completed with exit code 1.
Cleaning up orphan processes

(written by quest land watch)

@moq-bot

moq-bot Bot commented Nov 7, 2026

Copy link
Copy Markdown
Contributor

codex, gpt-6.1-sol, medium effort land fixer note:

The queue log says:

quest: quest/m0/kix/sandbox/README.md: quest title must be '# [XS|S|M|L|XL] Title'
Process completed with exit code 1.

Base passes quest validation (75 documents). I reproduced this failure using the base CLI against a scratch merge of this PR with base. This PR deletes sandbox/opencode-data.md, the sandbox epic's last child. The validator consequently treats the remaining README as a standalone quest, which requires a size. The README has no existing size to preserve; choosing one affects future reviewer selection, so it requires a planning decision. Please size the remaining sandbox work and add its title marker, then rerun the repository checks. The PR branch is unchanged.

(written by codex, gpt-6.1-sol, medium effort)

@moq-bot

moq-bot Bot commented Nov 7, 2026

Copy link
Copy Markdown
Contributor

codex, gpt-6.1-sol, medium effort land fixer: Base passes bun run --silent quest check (75 documents); on a scratch merge, the base CLI's check --base base --branch quest/m0/kix/sandbox/opencode-data fails: quest/m0/kix/sandbox/README.md: quest t. Recommendation: Decide the sandbox README's remaining work and size, add the corresponding [XS|S|M|L|XL] title marker, then run the base quest completion check, just check, just test, and nix flake check.

quest: quest/m0/kix/sandbox/README.md: quest title must be '# [XS|S|M|L|XL] Title'
##[error]Process completed with exit code 1.

(written by codex, gpt-6.1-sol, medium effort)

kixelated and others added 6 commits November 7, 2026 14:21
# Conflicts:
#	quest/m0/kix/sandbox/README.md
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Merges #560's branch so the epic's deletion holds whichever lands first.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@kixelated
kixelated added this pull request to the merge queue Nov 7, 2026

@kixelated kixelated left a comment

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Automated review by review (OpenAI)

Incremental review of 3d5a993 against base 2108b1d, comparing previously reviewed d10d2d9 with base changes accounted for.

No new actionable findings. The per-job isolation implementation is unchanged. The new Claude live-test launch supplies the required data directory; the lockfile's empty trustedDependencies agrees with package.json. The incorporated PR-trigger workflow is the same optional, read-only workflow reviewed in #560. Removing the completed m0 epic preserves its design decisions in m1, updates dependencies, and correctly distinguishes the existing OS sandbox from future untrusted-devShell VM isolation.

Direction: retain real sandbox boundary testing as a rollout requirement. Including the optional PR-trigger workflow does not itself prove OS isolation or guarantee that landing waits for it.

Verification limits: GitHub-only static, base-aware patch comparison and integration inspection. No build, tests, live Actions run or sandbox execution. The prior pass-through-srt experiment remains an integration check, not evidence of shell confinement. COMMENT only, not approval.

(Written by review (OpenAI))

@github-merge-queue
github-merge-queue Bot removed this pull request from the merge queue due to a conflict with the base branch Nov 7, 2026
@moq-bot

moq-bot Bot commented Nov 7, 2026

Copy link
Copy Markdown
Contributor

quest land watch: the merge queue removed this pull request at 3d5a993 because it conflicts with main (merge_conflict).

The land fixer works on it in the background, and the watcher re-enqueues it at the back of the queue once it is fixed or judged flaky, or hands it back here with what it needs.

(written by quest land watch)

@moq-bot
moq-bot Bot enabled auto-merge November 7, 2026 23:23
@moq-bot

moq-bot Bot commented Nov 7, 2026

Copy link
Copy Markdown
Contributor

quest land watch: armed auto-merge on this pull request at 4fae41b, so it joins the back of the merge queue once its required checks pass, with the land fixer's fix (codex, gpt-6.1-sol, medium effort): Merged base and re·solved the modify/delete conflict by retaining the m0 sandbox quest deletion; its m1 replacement already preserves base's updated wording; ran just check and just test (passed)..

(written by quest land watch)

@moq-bot
moq-bot Bot added this pull request to the merge queue Nov 7, 2026
Merged via the queue into main with commit 55070e1 Nov 7, 2026
18 checks passed
@moq-bot
moq-bot Bot deleted the quest/m0/kix/sandbox/opencode-data branch November 7, 2026 23:31
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.

1 participant