Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions quest/m0/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,9 @@ ownership-manifest design:
- [Land through the merge queue with an async fixer](/quest/m0/queue-watcher/README.md) - GitHub's merge queue lands one PR at a time, and a host watcher fixes and re-enqueues each PR it drops
- [Merge through a host gate](/quest/m0/host-land/README.md) - the host's fixer containers, and the frozen batched gate for the repositories not yet switched
- [The land fixer resolves every mechanical failure](/quest/m0/land-fixer/README.md) - workflow pushes and the PR's own CI as context
- [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
- [Rejected options in decision records](/quest/m0/decision-records.md) - grill and plan label rejected options so no one reads them as qualifiers
- [Kix decision UI](/quest/m0/kix/README.md) - planning and implementation through prompts and summaries, run on the maintainer's own runners
- [Codex cloud off](/quest/m0/codex-cloud-off.md) - automatic Codex cloud reviews stop once no open PR relies on them
- [Bun EFAULT report](/quest/m0/bun-efault.md) - report concurrent `rmSync` `EFAULT` on Windows, and drop the workaround once Bun fixes it
Expand All @@ -93,4 +96,6 @@ ownership-manifest design:
- [Agent workflow helpers](/quest/m0/helpers/README.md) - CLI commands for CI waits, PR status, claims, quest moves, and serialized merges
- [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
- [Remote control worktrees](/quest/m0/worktree-routes.md) - live-check `claude remote-control --spawn worktree` against the hooks, from a session spawned on claude.ai
- [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
- [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
34 changes: 34 additions & 0 deletions quest/m0/claude-rm-upstream.md
Original file line number Diff line number Diff line change
@@ -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
20 changes: 20 additions & 0 deletions quest/m0/decision-records.md
Original file line number Diff line number Diff line change
@@ -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.
3 changes: 3 additions & 0 deletions quest/m0/kix/sandbox/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -77,4 +77,7 @@ 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
- [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 set in the account's settings
55 changes: 55 additions & 0 deletions quest/m0/kix/sandbox/claude-boundary.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
# [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, 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

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_CODE_TMPDIR or os.tmpdir()>/claude-<uid>`, 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.

## Required

- [No Unix sockets in opencode jobs](/quest/m0/kix/sandbox/seccomp.md) - the Unix-socket probe this test reuses
- [Boundary test in CI](/quest/m0/kix/sandbox/live-test.md) - #404's attempts and layout this test reuses
56 changes: 56 additions & 0 deletions quest/m0/kix/sandbox/seccomp.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
# [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 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.
Comment thread
coderabbitai[bot] marked this conversation as resolved.
Resolved

## 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 [#404](https://github.com/moq-dev/quest/pull/404)'s boundary test,
in that PR if it is still open: 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.
23 changes: 23 additions & 0 deletions quest/m0/kix/sandbox/trust-on-queue.md
Original file line number Diff line number Diff line change
@@ -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.
31 changes: 31 additions & 0 deletions quest/m0/review-quota.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
# [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
Comment thread
coderabbitai[bot] marked this conversation as resolved.
Resolved
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.
- 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: <harness> hit its usage limit; resets <time>`, where `<time>` is
the reset as an ISO 8601 UTC timestamp, or `unknown` when the CLI's
output names none. Claude Code reports the reset as `resetsAt` epoch
seconds (`src/runner/claude.ts`); take Codex's from its error if it gives
one.
- Document the contract in `quest review --help` and the README, and point
#411's rule at it, so all three say the same.
41 changes: 41 additions & 0 deletions quest/m0/session-checkouts.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
# [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.
Comment thread
kixelated marked this conversation as resolved.
Resolved
- Claude Code bounds `SessionEnd` hooks by a short budget
(`CLAUDE_CODE_SESSIONEND_HOOKS_TIMEOUT_MS`) and does not wait past it.
Measure a release with scratch to delete, and set the entry's timeout to
cover it. A release that fails or is cut short leaves the checkout
reserved with its failure recorded, as `settle` does
(`src/cli/worktree.ts`), and the next `SessionStart` for that session, or
`quest worktree release` by hand, finishes it. Verify a killed release
this way too.
- Verify each route live as #425 did, from a session that is not itself
worktree-isolated.
26 changes: 26 additions & 0 deletions quest/m0/waiting-drafts.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# [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, keeping
its per-draft `quest status <pr>` hold check, so it still skips a draft
another session holds.
1 change: 1 addition & 0 deletions quest/m1/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
39 changes: 39 additions & 0 deletions quest/m1/release.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
# [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".

Comment thread
kixelated marked this conversation as resolved.
Resolved
Work:

- First, before any release: `SETUP.md` does not yet say v0.1.0 lacks the
hook, so add that the release routes lack it and point at the flake
route until a release has it.
- Once the remaining worktree-hook work
([moq-dev/quest#423](https://github.com/moq-dev/quest/pull/423); its
sibling [#425](https://github.com/moq-dev/quest/pull/425) landed on
2026-11-03) has landed or been dropped, bump `package.json`'s `version` in a PR to `main`, then tag that
version from `main`; `.github/workflows/release.yml` publishes on the tag
push and fails when the tag and `package.json` differ. Check that the
release has the Windows archives SETUP.md expects, then drop the
`SETUP.md` caveat.
- Decide in the PR how later releases are cut, such as a step in the release
checklist or a recurring quest through `/quest-recurring`.