From b59e2a1753af888a3f797d95aab27e1153e3a3a5 Mon Sep 17 00:00:00 2001 From: Luke Curley Date: Mon, 2 Nov 2026 21:35:21 -0800 Subject: [PATCH 1/3] quest: plan the 2026-11-02 spawn's follow-ups Plan the follow-ups the spawn's 37 agents suggested that evidence showed are real: a missing srt seccomp helper that lets opencode jobs reach the Nix daemon, a Claude Code boundary test, rechecking head trust when a job queues, reviewer usage-limit exits, drafts in quest waiting, unambiguous decision records, releasing session checkouts, an upstream claude rm report awaiting approval, and a release with the worktree hooks. Co-Authored-By: Claude Opus 5.5 --- quest/m0/README.md | 5 +++ quest/m0/claude-rm-upstream.md | 34 +++++++++++++++++ quest/m0/decision-records.md | 20 ++++++++++ quest/m0/kix/sandbox/README.md | 3 ++ quest/m0/kix/sandbox/claude-boundary.md | 40 ++++++++++++++++++++ quest/m0/kix/sandbox/seccomp.md | 50 +++++++++++++++++++++++++ quest/m0/kix/sandbox/trust-on-queue.md | 23 ++++++++++++ quest/m0/review-quota.md | 24 ++++++++++++ quest/m0/session-checkouts.md | 33 ++++++++++++++++ quest/m0/waiting-drafts.md | 24 ++++++++++++ quest/m1/README.md | 1 + quest/m1/release.md | 35 +++++++++++++++++ 12 files changed, 292 insertions(+) create mode 100644 quest/m0/claude-rm-upstream.md create mode 100644 quest/m0/decision-records.md create mode 100644 quest/m0/kix/sandbox/claude-boundary.md create mode 100644 quest/m0/kix/sandbox/seccomp.md create mode 100644 quest/m0/kix/sandbox/trust-on-queue.md create mode 100644 quest/m0/review-quota.md create mode 100644 quest/m0/session-checkouts.md create mode 100644 quest/m0/waiting-drafts.md create mode 100644 quest/m1/release.md diff --git a/quest/m0/README.md b/quest/m0/README.md index eaf5ced7..3faf6bdd 100644 --- a/quest/m0/README.md +++ b/quest/m0/README.md @@ -88,8 +88,11 @@ ownership-manifest design: - [Checks are judged by their latest run](/quest/m0/latest-check-runs.md) - `quest status`, `quest wait`, and the gate count each check's latest run on the head, as GitHub does, so a superseded cancelled or failed run stops failing them or refusing release-plz PRs GitHub would merge - [Current landing status](/quest/m0/status-landing.md) - `quest status` clears a stale `landing-stopped` reason once the PR is queued again - [Agents don't stall](/quest/m0/agent-stalls.md) - long waits block in the same turn, pre-existing failures don't stop review, and reviewer outages become waits +- [Reviewer usage limits](/quest/m0/review-quota.md) - `quest review` exits distinctly on a reviewer's quota and prints the reset time +- [Drafts in quest waiting](/quest/m0/waiting-drafts.md) - `quest waiting` lists open draft PRs' `## Waiting` items beside the tree's - [Follow-ups as agents report](/quest/m0/follow-ups-as-reports.md) - coordinators plan follow-ups as reports arrive, fewer of them, with standing milestone rules - [Context before decision prompts](/quest/m0/prompt-context.md) - every prompt explains the work first, and maintainer steps say why an agent can't take them +- [Rejected options in decision records](/quest/m0/decision-records.md) - grill and plan label rejected options so no one reads them as qualifiers - [Coordinator control](/quest/m0/coordinator-control.md) - finish up, pause, and status are defined, and dead agents are resumed - [Spawn keeps finding work](/quest/m0/spawn-refill.md) - recompute ready quests per report, resume leftover local branches, and check open PRs before claiming - [Kix decision UI](/quest/m0/kix/README.md) - planning and implementation through prompts and summaries, run on the maintainer's own runners @@ -103,6 +106,8 @@ ownership-manifest design: - [Merge driver upkeep](/quest/m0/merge-driver-upkeep.md) - local merges drop links to finished quests, old drivers upgrade, and `quest-pinned` stays current - [Worktree hooks here](/quest/m0/worktree-hooks.md) - `quest` reaches Claude Code's hooks and the merge driver in this repository, then `quest init` installs the hooks - [Other worktree routes](/quest/m0/worktree-routes.md) - live-check PR worktrees, agent view, remote-control, and workflow agents against the hooks +- [Session checkouts released](/quest/m0/session-checkouts.md) - a Claude session's own checkout is released when the session ends and reclaimed on resume +- [claude rm upstream report](/quest/m0/claude-rm-upstream.md) - waiting on approval: ask Claude Code to run `WorktreeRemove` instead of advising an unlock - [Spotlight off in macOS CI](/quest/m0/ci-spotlight.md) - this repository's macOS jobs turn off Spotlight indexing first - [Review through a gateway](/quest/m0/review-gateway.md) - `quest review` keeps gateway, cloud-provider, and Codex connection variables - [opencode 2.x](/quest/m0/opencode-sdk.md) - the runner drives the opencode 2.x binary through `@opencode/client`, removing the interim 1.19.0 pin diff --git a/quest/m0/claude-rm-upstream.md b/quest/m0/claude-rm-upstream.md new file mode 100644 index 00000000..49d6ee26 --- /dev/null +++ b/quest/m0/claude-rm-upstream.md @@ -0,0 +1,34 @@ +# [XS] Report claude rm's unlock advice upstream + +## Goal + +anthropics/claude-code has an issue asking `claude rm` to dispatch the +`WorktreeRemove` hook for a checkout a `WorktreeCreate` hook made, as remote +control already does, instead of telling the user to run +`git worktree unlock`, which breaks the hook's lock. Delete this quest once +the issue is filed, or abandon it if the maintainer declines. + +## Plan + +Decided 2026-11-02 (coordinator, /quest-spawn follow-ups, after the user +asked the coordinator to decide follow-ups from evidence): plan it, [XS], +m0, waiting on the maintainer's approval to post. + +- [moq-dev/quest#425](https://github.com/moq-dev/quest/pull/425)'s probe + with Claude Code 2.1.316: `claude rm` on an agent-view session's + hook-made checkout reports it "locked … by something other than Claude + Code" and suggests `git worktree unlock`. The README now tells users to + run `quest worktree remove` first. +- No upstream issue covers it (searched 2026-11-02). Related but different: + [anthropics/claude-code#79872](https://github.com/anthropics/claude-code/issues/79872) + (`claude agents` delete skips plugin `WorktreeRemove` hooks), + [#93676](https://github.com/anthropics/claude-code/issues/93676), + [#101283](https://github.com/anthropics/claude-code/issues/101283), and + [#99270](https://github.com/anthropics/claude-code/issues/99270). Link + them in the report. +- Include the Claude Code version, the minimal reproduction from #425's + scratch repository, and the expected behaviour. + +## Waiting + +- Maintainer: approve posting on anthropics/claude-code, which is outside the kixelated and moq-dev orgs where agents may post without asking; check by a ✅ recorded in this quest's Plan diff --git a/quest/m0/decision-records.md b/quest/m0/decision-records.md new file mode 100644 index 00000000..95cd859a --- /dev/null +++ b/quest/m0/decision-records.md @@ -0,0 +1,20 @@ +# [XS] Recorded decisions mark the rejected options + +## Goal + +The grill and plan skills record each decision with the chosen option +marked and the rejected options labelled as rejected, such as +`✅ Yes, as stated (rejected: without the overlap check; drop it)`, so no +reader takes a rejected option for a qualifier of the chosen one. + +## Plan + +Decided 2026-11-02 (user, /quest-spawn follow-ups): ✅ plan as stated +(rejected: drop it); ✅ m0 (rejected: m1, m2). + +- Raised by [moq-dev/quest#416](https://github.com/moq-dev/quest/pull/416): + Codex read "✅ Yes, as stated; without the overlap check; drop it" as + dropping the overlap check, and the agent spent a review round refuting + it. +- Change the recording instruction in `assets/skills/grill.md` and + `assets/skills/plan.md`. Existing records stay as written. diff --git a/quest/m0/kix/sandbox/README.md b/quest/m0/kix/sandbox/README.md index 712d87b1..e06dedc2 100644 --- a/quest/m0/kix/sandbox/README.md +++ b/quest/m0/kix/sandbox/README.md @@ -77,5 +77,8 @@ Shipped in [moq-dev/quest#212](https://github.com/moq-dev/quest/pull/212) ## Required +- [No Unix sockets in opencode jobs](/quest/m0/kix/sandbox/seccomp.md) - srt's seccomp helper is packaged, so commands can't reach the Nix daemon, and the runner refuses to start without it - [Head approval](/quest/m0/kix/sandbox/head-approval.md) - a maintainer approves an exact head, such as one kix pushed, for host runners - [Boundary test in CI](/quest/m0/kix/sandbox/live-test.md) - the opencode sandbox's hostile-repository test runs on Linux CI with a scripted model +- [Trust rechecked on queue](/quest/m0/kix/sandbox/trust-on-queue.md) - a job's head trust is reread whenever it queues, so the approval decision shows without a free runner +- [Claude Code boundary test](/quest/m0/kix/sandbox/claude-boundary.md) - the same hostile-repository test for Claude Code jobs, with a fake model behind `ANTHROPIC_BASE_URL` diff --git a/quest/m0/kix/sandbox/claude-boundary.md b/quest/m0/kix/sandbox/claude-boundary.md new file mode 100644 index 00000000..e4ab1839 --- /dev/null +++ b/quest/m0/kix/sandbox/claude-boundary.md @@ -0,0 +1,40 @@ +# [M] The Claude Code sandbox's boundary test runs in CI + +## Goal + +Claude Code jobs get the same checked-in hostile-repository boundary test +that opencode jobs got in +[moq-dev/quest#404](https://github.com/moq-dev/quest/pull/404): a scripted +fake model behind `ANTHROPIC_BASE_URL` drives the pinned Claude Code through +the runner's launch path on Linux CI, and every escape attempt is refused. +A Claude Code bump that weakens the boundary fails CI before it lands. + +## Plan + +Decided 2026-11-02 (coordinator, /quest-spawn follow-ups, after the user +asked the coordinator to decide follow-ups from evidence): plan it, [M], +in this epic after the seccomp fix. + +Evidence, gathered 2026-11-02: + +- The only Claude Code coverage is unit tests of its settings and + environment (`test/runner.test.ts`). The boundary was last checked by hand + ("verified live on Linux with Claude Code 2.1.291 and 2.1.305"), and + [moq-dev/quest#242](https://github.com/moq-dev/quest/pull/242) bumped the + pin without a rerun although 2.1.305 added `allowNestedUserNamespaces`. +- The opencode test found a real hole (the missing seccomp helper, see + [Unix sockets](/quest/m0/kix/sandbox/seccomp.md)), so these tests pay off. + +Work: + +- Reuse #404's attempts and layout: denied reads by path and symlink, + writes outside the checkout and job temp directory, planted project + settings and hooks, the network directly and through the proxy, and the + Unix-socket probe from the seccomp quest. +- Assert that `TMPDIR` inside a command is the job's private directory and + is writable. Reading Claude Code 2.1.305 says its strict policy exports + `TMPDIR=/claude-`, which the + runner's `TMPDIR` (`src/runner/claude.ts`) puts inside `allowWrite`; the + test confirms it instead of trusting the read. +- Like #404, each shell attempt first succeeds outside the sandbox, and the + test fails rather than skips on Linux when the sandbox can't start. diff --git a/quest/m0/kix/sandbox/seccomp.md b/quest/m0/kix/sandbox/seccomp.md new file mode 100644 index 00000000..7105d14c --- /dev/null +++ b/quest/m0/kix/sandbox/seccomp.md @@ -0,0 +1,50 @@ +# [S] opencode jobs' commands can't reach Unix sockets + +## Goal + +The sandbox-runtime (srt) that runs opencode jobs' commands applies its +seccomp filter, so a sandboxed command cannot connect to any Unix socket, +the Nix daemon's included. The runner refuses to start an opencode job when +srt reports Unix-socket blocking disabled, and the boundary test probes the +daemon socket, so a package bump that drops the filter fails CI. srt's +always-writable `/tmp/claude` is denied too. + +## Plan + +Decided 2026-11-02 (coordinator, /quest-spawn follow-ups, after the user +asked the coordinator to decide follow-ups from evidence): plan it, [S], +first in this epic, since it is a hole in a boundary the README says holds. + +Evidence, gathered 2026-11-02: + +- nixpkgs' `sandbox-runtime-0.0.81`, which `flake.nix` uses as is, ships + `vendor/seccomp/build.ts` but no compiled `apply-seccomp` helper. srt then + logs `apply-seccomp binary not available - unix socket blocking disabled` + (seen in [moq-dev/quest#435](https://github.com/moq-dev/quest/pull/435)'s + session on 2026-11-03T03:55Z). +- Run with the runner's `runtimeSettings` shape (reads of `/home` and `/tmp` + denied), `nix store ping --store daemon` inside srt answered + `Version: 2.35.2`. `nix config show trusted-users` is + `root kixelated github-runner`, so a job's command on this host could pass + the daemon root-level options. This contradicts the epic's "Commands have + no Nix daemon". No runner was deployed on the host yet. +- [moq-dev/quest#404](https://github.com/moq-dev/quest/pull/404)'s boundary + test passed because it probes no Unix socket. +- srt 0.0.81 lists `/tmp/claude` in `SANDBOX_OWN_WRITE_PATHS`, which it never + drops (`dist/sandbox/sandbox-utils.js`). The directory doesn't exist on + this host and a sandboxed `mkdir` lands on srt's private tmpfs, so it + matters only if something on the host creates it. Deny it anyway while + editing the same settings. + +Work: + +- Build `apply-seccomp` for the flake's `sandbox-runtime` (an override that + compiles it per `build.ts` against static libseccomp), or package the npm + tarball's prebuilt helper and pass `seccomp.applyPath`. Prefer the build + from source, matching how the flake pins the rest. +- The runner refuses an opencode account when srt reports Unix-socket + blocking disabled, with an error that names the missing helper. +- Add to the boundary test: connecting to the Nix daemon socket, and to a + socket the test opens outside the sandbox, are both refused. +- Add `/tmp/claude` to `denyWrite` if srt honours it over its own write + paths without leaving a placeholder behind; otherwise record why not. diff --git a/quest/m0/kix/sandbox/trust-on-queue.md b/quest/m0/kix/sandbox/trust-on-queue.md new file mode 100644 index 00000000..2f12520b --- /dev/null +++ b/quest/m0/kix/sandbox/trust-on-queue.md @@ -0,0 +1,23 @@ +# [S] A job's head trust is rechecked when it queues + +## Goal + +kix rechecks a PR job's head trust whenever the job becomes queued, after an +answer or when a runner's lease expires, not only when a runner has a free +slot. A job whose head is no longer trusted shows the "Head to approve" +decision at once instead of sitting as "Queued" until a slot frees. + +## Plan + +Decided 2026-11-02 (user, /quest-spawn follow-ups): ✅ plan as stated +(rejected: drop it); ✅ m0 (rejected: m1, m2). + +- Raised by [moq-dev/quest#402](https://github.com/moq-dev/quest/pull/402), + which added per-head approval and rechecks trust at job creation and at + assignment. Existing behaviour: a queued job's trust is reread only when a + runner slot frees, so a job resumed after kix pushed new commits looks + idle while it needs a maintainer. +- Reuse #402's trust classification; call it from every transition into + `queued`. Keep the recheck at assignment as the authority. +- Test a resume after kix pushed, and a lease expiry, each surfacing the + decision without a free runner slot. diff --git a/quest/m0/review-quota.md b/quest/m0/review-quota.md new file mode 100644 index 00000000..29267ecc --- /dev/null +++ b/quest/m0/review-quota.md @@ -0,0 +1,24 @@ +# [S] quest review reports a reviewer's usage limit + +## Goal + +When the reviewer CLI fails because its account hit a usage limit or quota, +`quest review` exits with a distinct status and prints the reset time, so an +agent records the outage as a `## Waiting` item without parsing the +reviewer's error text. + +## Plan + +Decided 2026-11-02 (user, /quest-spawn follow-ups): ✅ plan as stated +(rejected: drop it); ✅ m0 (rejected: m1, m2). + +- Raised by [moq-dev/quest#411](https://github.com/moq-dev/quest/pull/411), + whose guide rule turns a reviewer's quota or login failure into a + `## Waiting` item naming when it resets. About 29 sessions between + 2026-10-30 and 11-01 ended on a reviewer's weekly quota. +- Reuse the matchers that exist: Claude Code's usage-limit `LIMIT` in + `src/runner/claude.ts` and `quest agent`'s `EXHAUSTED` in + `src/cli/agent.ts`. Move them where both and `quest review` import them, + rather than adding a third copy. +- Document the exit status in `quest review --help` and the README, and + point #411's rule at it. diff --git a/quest/m0/session-checkouts.md b/quest/m0/session-checkouts.md new file mode 100644 index 00000000..4598b82c --- /dev/null +++ b/quest/m0/session-checkouts.md @@ -0,0 +1,33 @@ +# [M] A Claude session's checkout is released when the session ends + +## Goal + +A checkout `quest worktree hook` made for a Claude Code session itself (not +a subagent), through `--worktree`, `--bg`, or agent view, is released when +the session ends and claimed again when it resumes, so session checkouts no +longer pile up the way Claude Code's own do. + +## Plan + +Decided 2026-11-02 (coordinator, /quest-spawn follow-ups, after the user +asked the coordinator to decide follow-ups from evidence): plan it, [M], m0. + +Evidence, gathered 2026-11-02: + +- [moq-dev/quest#425](https://github.com/moq-dev/quest/pull/425)'s live + probes found session checkouts never released by any route. +- Claude Code's own session worktrees (not `agent-*`) already accumulate: + 32 registered in moq.pro, 21 in moq, and 3 in quest (2026-10-14 to + 10-27), beside 449, 124, and 26 `agent-*` worktrees. Once the hooks own + these checkouts the same backlog would build in the registry. + +Work: + +- Install `SessionEnd` and `SessionStart` hook entries through + `quest init`, recognised by `init` and `uninstall` like the others. +- `SessionEnd` releases (never removes) the session's checkout; + `SessionStart` claims it again on resume. `/clear` also fires + `SessionEnd`, so release is idempotent and the next `SessionStart` + reclaims it. An idle background session never ends; leave it held. +- Verify each route live as #425 did, from a session that is not itself + worktree-isolated. diff --git a/quest/m0/waiting-drafts.md b/quest/m0/waiting-drafts.md new file mode 100644 index 00000000..0081d077 --- /dev/null +++ b/quest/m0/waiting-drafts.md @@ -0,0 +1,24 @@ +# [S] quest waiting lists draft PRs' Waiting items + +## Goal + +`quest waiting` lists each open draft PR's `## Waiting` items beside the +quest tree's, ranked the same way with the maintainer's first, so one +command shows everything that waits on someone. `/quest-unblock` reads it +instead of gathering draft bodies with `gh` itself. + +## Plan + +Decided 2026-11-02 (user, /quest-spawn follow-ups): ✅ plan as stated +(rejected: drop it); ✅ m0 (rejected: m1, m2). + +- Raised by [moq-dev/quest#411](https://github.com/moq-dev/quest/pull/411): + a reviewer outage lives in the draft's `## Waiting`, which `quest waiting` + never reads. Drafts handed back for decisions carry theirs there too. +- Read drafts of the repository's own PRs, parsing `## Waiting` (and the + older `## Decisions needed` until + [its retirement](/quest/m1/decisions-needed.md)) with the claim code that + already detects the heading. Each item names its PR's URL. +- Keep `quest waiting` usable offline: without GitHub access it prints the + tree's items and says the drafts were skipped. +- Update `assets/skills/unblock.md` to read the command's output. diff --git a/quest/m1/README.md b/quest/m1/README.md index 1c238be9..0adc6b33 100644 --- a/quest/m1/README.md +++ b/quest/m1/README.md @@ -22,6 +22,7 @@ earlier "Later work" milestone became m2. ## Required - [Open kix.quest](/quest/m1/open.md) - the Worker moves to kix.quest and any owner may enroll +- [Release with worktree hooks](/quest/m1/release.md) - tag a release that has `quest worktree hook`, so SETUP.md's release routes work - [Kix for any repository](/quest/m1/kix/README.md) - onboarding, read-only CI checks, contributor sandboxing, and the maintainer guide - [Quest log theme](/quest/m1/theme/README.md) - an MMO-style identity, copy, board, landing page, and CLI output for the demo and launch - [Launch material](/quest/m1/launch.md) - README, quickstart, demo, comparison, and launch-post drafts diff --git a/quest/m1/release.md b/quest/m1/release.md new file mode 100644 index 00000000..4fb089a3 --- /dev/null +++ b/quest/m1/release.md @@ -0,0 +1,35 @@ +# [S] Cut a release with the worktree hooks + +## Goal + +A tagged quest release includes `quest worktree hook`, so SETUP.md's release, +mise, and installer routes give adopters a `quest` whose Claude Code hooks +work, and the release checklist says when the next one is due. + +## Plan + +Decided 2026-11-02 (coordinator, /quest-spawn follow-ups, after the user +asked the coordinator to decide follow-ups from evidence): plan it, [S], m1, +since every moq-dev repository pins quest through a nix flake and only +third-party adopters take the release routes. + +Evidence, gathered 2026-11-02: + +- The only release, v0.1.0 (`4fbbb0c0`, 2026-10-07), is 323 commits behind + `main` and has no `worktree` command; the hook arrived in + [moq-dev/quest#350](https://github.com/moq-dev/quest/pull/350) on + 2026-11-01. `SETUP.md` still offers those routes, and `quest init` + installs hook entries that call it. +- No quest plans a release; m0's README only notes "v0.1.0 shipped". + +Work: + +- Tag the next version from `main` once the open worktree-hook work + ([moq-dev/quest#423](https://github.com/moq-dev/quest/pull/423), + [#425](https://github.com/moq-dev/quest/pull/425)) has landed or been + dropped; `.github/workflows/release.yml` publishes on the tag push. Check + that the release has the Windows archives SETUP.md expects. +- Until a release has the hook, SETUP.md says the release routes lack it + and points at the flake route. +- Decide in the PR how later releases are cut, such as a step in the release + checklist or a recurring quest through `/quest-recurring`. From 158df7b1089b0549080df313b2d86a2f67a62372 Mon Sep 17 00:00:00 2001 From: Luke Curley Date: Tue, 3 Nov 2026 07:01:59 -0800 Subject: [PATCH 2/3] quest: answer CodeRabbit's findings on the spawn follow-ups Point the Claude boundary test's fake model through the account's user settings, scope the seccomp goal to socket creation with an inherited-fd check, define review's quota exit contract, bound and recover a SessionEnd release, keep unblock's per-draft hold check, and order the release's SETUP.md caveat and package.json bump. Co-Authored-By: Claude Opus 5.5 --- quest/m0/kix/sandbox/claude-boundary.md | 14 ++++++++++++-- quest/m0/kix/sandbox/seccomp.md | 9 +++++++-- quest/m0/review-quota.md | 11 +++++++++-- quest/m0/session-checkouts.md | 8 ++++++++ quest/m0/waiting-drafts.md | 4 +++- quest/m1/release.md | 14 +++++++++----- 6 files changed, 48 insertions(+), 12 deletions(-) diff --git a/quest/m0/kix/sandbox/claude-boundary.md b/quest/m0/kix/sandbox/claude-boundary.md index e4ab1839..7f6904b8 100644 --- a/quest/m0/kix/sandbox/claude-boundary.md +++ b/quest/m0/kix/sandbox/claude-boundary.md @@ -5,8 +5,9 @@ Claude Code jobs get the same checked-in hostile-repository boundary test that opencode jobs got in [moq-dev/quest#404](https://github.com/moq-dev/quest/pull/404): a scripted -fake model behind `ANTHROPIC_BASE_URL` drives the pinned Claude Code through -the runner's launch path on Linux CI, and every escape attempt is refused. +fake model, set as the account's endpoint in its home, drives the pinned +Claude Code through the runner's launch path on Linux CI, and every escape +attempt is refused. A Claude Code bump that weakens the boundary fails CI before it lands. ## Plan @@ -36,5 +37,14 @@ Work: `TMPDIR=/claude-`, which the runner's `TMPDIR` (`src/runner/claude.ts`) puts inside `allowWrite`; the test confirms it instead of trusting the read. +- Point the account at the fake model in its home, as #404's `signIn` + configures opencode's provider: `ANTHROPIC_BASE_URL` in the `env` of + `$CLAUDE_CONFIG_DIR/settings.json`, which `--setting-sources user` loads. + An ambient `ANTHROPIC_BASE_URL` never reaches Claude Code, since + `jobEnvironment` (`src/runner/claude.ts`) keeps only `keptEnvironment`'s + allowlist (`src/runner/sandbox.ts`); leave that allowlist as it is. If + settings' `env` also reaches the commands, the fake key there is no + secret, so the unreadable-secret attempt reads a planted credential file + in the home instead. - Like #404, each shell attempt first succeeds outside the sandbox, and the test fails rather than skips on Linux when the sandbox can't start. diff --git a/quest/m0/kix/sandbox/seccomp.md b/quest/m0/kix/sandbox/seccomp.md index 7105d14c..bc740cf9 100644 --- a/quest/m0/kix/sandbox/seccomp.md +++ b/quest/m0/kix/sandbox/seccomp.md @@ -3,8 +3,8 @@ ## Goal The sandbox-runtime (srt) that runs opencode jobs' commands applies its -seccomp filter, so a sandboxed command cannot connect to any Unix socket, -the Nix daemon's included. The runner refuses to start an opencode job when +seccomp filter, so a sandboxed command cannot create a Unix socket and so +cannot connect to one, the Nix daemon's included. The runner refuses to start an opencode job when srt reports Unix-socket blocking disabled, and the boundary test probes the daemon socket, so a package bump that drops the filter fails CI. srt's always-writable `/tmp/claude` is denied too. @@ -46,5 +46,10 @@ Work: blocking disabled, with an error that names the missing helper. - Add to the boundary test: connecting to the Nix daemon socket, and to a socket the test opens outside the sandbox, are both refused. +- The filter blocks creating a Unix socket, not using one a command + inherits or receives over `SCM_RIGHTS`. Have the boundary test list the + command's open descriptors (`/proc/self/fd`) and fail on any Unix socket + other than its stdio's, so nothing the runner or opencode passes down + reaches a host socket. - Add `/tmp/claude` to `denyWrite` if srt honours it over its own write paths without leaving a placeholder behind; otherwise record why not. diff --git a/quest/m0/review-quota.md b/quest/m0/review-quota.md index 29267ecc..804d95a8 100644 --- a/quest/m0/review-quota.md +++ b/quest/m0/review-quota.md @@ -20,5 +20,12 @@ Decided 2026-11-02 (user, /quest-spawn follow-ups): ✅ plan as stated `src/runner/claude.ts` and `quest agent`'s `EXHAUSTED` in `src/cli/agent.ts`. Move them where both and `quest review` import them, rather than adding a third copy. -- Document the exit status in `quest review --help` and the README, and - point #411's rule at it. +- The contract: `quest review` exits 3, which it uses for nothing else + (0 is a round, 1 a failure, 2 a usage error), and prints one stderr line, + `error: hit its usage limit; resets