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
3 changes: 1 addition & 2 deletions quest/m0/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -104,8 +104,7 @@ ownership-manifest design:
- [Gate bench](/quest/m0/gate-bench.md) - measure headroom admission on an idle host
- [Gate load wording](/quest/m0/gate-load-wording.md) - stop naming Spotlight as the cause of a fresh macOS runner's load
- [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
- [Wrapped worktree hooks](/quest/m0/wrapped-hooks.md) - `quest init` accepts a hook that runs `… worktree hook` through a wrapper such as `nix run`
- [Agents get the dev shell from user settings](/quest/m0/user-dev-shell/README.md) - maintainers' own Claude and Codex settings load the dev shell; repositories drop dev-shell hooks, Quest worktree hooks, and `nix develop` instructions, and `quest init` writes no 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
Expand Down
16 changes: 14 additions & 2 deletions quest/m0/session-checkouts.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,12 +23,24 @@ Evidence, gathered 2026-11-02:

Work:

- Install `SessionEnd` and `SessionStart` hook entries through
`quest init`, recognised by `init` and `uninstall` like the others.
- Add `SessionEnd` and `SessionStart` entries to the opt-in user-level hook
setup the docs describe. Amended 2026-11-03 (maintainer, /quest-unblock):
`quest init` writes no hook entries anymore
([quest init writes no hooks](/quest/m0/user-dev-shell/init-hooks.md)),
so this quest no longer installs them through `init`.
Comment thread
coderabbitai[bot] marked this conversation as resolved.
Resolved
- `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.
- Remote control's `bridge-*` checkouts are session checkouts too, and the
release covers them if `SessionEnd` fires in a bridge-spawned session.
[Remote control's live check](/quest/m0/worktree-routes.md) confirms
whether it does. Release only prunes the declared scratch: it keeps every
other file and Git's worktree lock (`release` and `prune` in
`src/cli/worktree.ts`; only `remove` unlocks). The bridge still decides at
session end, as that quest describes: it removes a clean checkout through
`WorktreeRemove` and keeps one with changes. Verify that order in the same
live check.
- 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
Expand Down
77 changes: 77 additions & 0 deletions quest/m0/user-dev-shell/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
# Agents get the dev shell from user settings, not repository config

## Goal

Agents in moq-dev's repositories run tools without caring about Nix: each
maintainer's own Claude Code and Codex settings load the repository's dev
shell into every session's commands, so no repository forces direnv, Nix, or
`quest` on its contributors through checked-in hooks. No repository carries a
dev-shell `SessionStart` hook, a Quest worktree hook, or AGENTS.md
instructions to wrap commands in `nix develop`, and `quest init` writes no
hook entries.

## Plan

Decided 2026-11-03 (maintainer, /quest-unblock of
[moq-dev/quest#423](https://github.com/moq-dev/quest/pull/423)'s wait to
install `quest` on Claude's `PATH`):

- Install `quest` on the `PATH` Claude Code starts with? ✅ No (rejected:
`nix profile add github:moq-dev/quest` on every host).
- How should sessions reach a pinned `quest` and the rest of the dev shell?
✅ A dev-shell hook in the maintainer's own Claude settings (rejected: hook
entries that `nix run` the repository's pinned input, as moq-dev/moq does;
dropping the hooks with no replacement). Reason: "We don't want to force
direnv+nix for other users"; repository config applies to every
contributor.
- Repository-level direnv `SessionStart` hooks in quest, moq, moq.pro, rind,
and rust2? ✅ Remove them from all five once the user-level hook loads the
dev shell (rejected: keep them).
- Quest's worktree hooks? ✅ Drop them from repositories, and `quest init`
stops writing hook entries; the hooks become an opt-in user-level setup
(rejected: this repository and moq only; this repository only; moving them
into the maintainer's settings).
[moq-dev/quest#423](https://github.com/moq-dev/quest/pull/423) is closed.
- Codex? ✅ A user-level Codex `SessionStart` hook does the same capture, and
the repositories' `.codex/` dev-shell hooks go (rejected: Claude only).
- A fresh worktree needs `direnv allow`: direnv keys each approval by the
SHA-256 of the `.envrc`'s absolute path and contents
(`~/.local/share/direnv/allow/`, 1,706 entries on the maintainer's host on
2026-11-03), so nothing carries over from the main checkout. ✅ The
user-level hook allows a checkout itself when its `.envrc`, `flake.nix`,
and `flake.lock` match the remote's default branch (rejected: whitelisting
`~/work` and `~/worktrees` in `direnv.toml`, which would also evaluate a
fork PR's dev shell; keeping manual allows). A branch that changes them
still needs a manual allow. Amended 2026-11-03 (review of
[moq-dev/quest#458](https://github.com/moq-dev/quest/pull/458)): those
three files alone don't make a checkout reviewed. Any repository's fresh
clone matches its own default branch. A fork PR head can change another
file the dev shell runs, such as moq.pro's `dev/mbx-shim.sh`. Both were
auto-allowed in a reproduction against the merged hook.
Decided 2026-11-03 (maintainer, /quest-complete), after weighing the
risk as narrow: a session rooted at an outside contributor's fork head,
which the guide already says never to run, since agents check untrusted
heads out under `.tmp/` rather than as a session root, and an agent
session in any checkout runs its code anyway. ✅ One cheap guard: the
hook also requires `HEAD` to be on some `origin` branch
(`git for-each-ref --contains HEAD refs/remotes/origin/`), so a fork PR
head is never auto-allowed (rejected: an owner allowlist, a clean tree,
and a tracked `.envrc` on top; matching the whole tree to the merge base;
an owner allowlist alone; leaving it as is). Shipped in
[kixelated/dotfiles#35](https://github.com/kixelated/dotfiles/pull/35)
and live on `desktop` and `nixos2`, where a fresh worktree of moq-dev/moq
still loads and one on an unpushed commit is refused.
- AGENTS.md instructions to run tools through `nix develop`? ✅ Remove them in
all five repositories once the user-level hooks are on every host the
maintainer runs agents on (rejected: this repository only; keeping them as
a fallback).

The user-level hooks live in [kixelated/dotfiles](https://github.com/kixelated/dotfiles),
installed into each Claude profile's `settings.json` and Codex's user
config, and generalize this repository's `.claude/hooks/direnv.sh` and
moq-dev/moq's `.codex/hooks/direnv.sh`.

## Required

- [quest init writes no hooks](/quest/m0/user-dev-shell/init-hooks.md) - `quest init` stops writing hook entries and removes the ones it or a wrapper wrote; the docs describe the hooks as user-level opt-in
- [Repositories drop dev-shell config](/quest/m0/user-dev-shell/repo-config.md) - quest, moq, moq.pro, rind, and rust2 drop their dev-shell hooks, Quest worktree hooks, and `nix develop` instructions
42 changes: 42 additions & 0 deletions quest/m0/user-dev-shell/init-hooks.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
# [S] quest init writes no hook entries

## Goal

`quest init` no longer writes Claude Code hook entries into a repository's
`.claude/settings.json`, and removes the `quest worktree hook` entries an
earlier version wrote, including ones a repository wrapped, such as
moq-dev/moq's `nix run "${CLAUDE_PROJECT_DIR}#quest" -- worktree hook`.
`quest uninstall` removes both forms too. The README and SETUP.md describe the
worktree hooks as an opt-in a user adds to their own Claude settings, with
the entries to add.

## Plan

Decided 2026-11-03 (maintainer, /quest-unblock): repository config applies
to every contributor, so Quest's hooks become user-level opt-in; see the
[epic](/quest/m0/user-dev-shell/README.md).

- Replaces the wrapped-hooks quest (moq-dev/moq, 2026-11-03), which widened
`isQuestHook` (`src/core/init.ts`) so `init` would accept wrapped hooks.
For removal, a command hook is Quest's only when it runs Quest: the plain
`quest worktree hook`, or a wrapper whose command ends in `worktree hook`
and names the `quest` executable or package as a word or flake attribute,
such as moq's `nix run "${CLAUDE_PROJECT_DIR}#quest" -- worktree hook`.
Another tool's command that merely ends in `worktree hook` stays.
- `HOOK_EVENTS` and the hook plan in `src/core/init.ts` stop adding
entries; `init` reports each entry it removes. The enrollment setup PR
([moq-dev/quest#431](https://github.com/moq-dev/quest/pull/431)) uses the
same plan, so it stops proposing hooks too.
- The `quest worktree hook` command stays, for users who opt in.
- Removing moq's four wrapped entries, whether `init` here or
[repo-config](/quest/m0/user-dev-shell/repo-config.md) does it first, ends
Quest's reclaim of Claude Code checkouts in moq. Nothing replaces it: the
maintainer's user-level setup (kixelated/dotfiles) loads only the dev
shell, and the epic's decision rejected moving Quest's hooks into the
maintainer's settings. So this quest doesn't wait on that setup, and the
same end state results in either order. A user who wants reclaim adds the
opt-in entries the docs describe. Any removal through the enrollment setup
PR shows in that PR's diff.
Comment thread
coderabbitai[bot] marked this conversation as resolved.
Resolved
- Test `init` and `uninstall` on a repository with plain and wrapped
entries, and with other hooks beside them, which stay, including another
tool's command that ends in `worktree hook`.
31 changes: 31 additions & 0 deletions quest/m0/user-dev-shell/repo-config.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
# [S] Repositories drop their dev-shell config

## Goal

moq-dev/quest, moq, moq.pro, rind, and rust2 carry no dev-shell hooks and no
instructions to wrap commands in Nix: each drops its direnv `SessionStart`
hook from `.claude/settings.json` and `.codex/hooks.json` with the scripts
and tests behind them, moq drops its four Quest worktree hook entries, and
each AGENTS.md drops its `nix develop` instructions. Agents there still run
the pinned tools, through the maintainer's user-level hooks. One PR per
repository.

## Plan

Decided 2026-11-03 (maintainer, /quest-unblock); see the
[epic](/quest/m0/user-dev-shell/README.md).

- Keep every other hook: moq.pro's `submodule.sh`, `worktree.sh` (its own
submodule-aware worktrees, not Quest's), and `tofu-secrets.py`.
- CONTRIBUTING.md's human setup (`nix develop` to match CI) stays; only the
agent-facing instructions go. In this repository, AGENTS.md's "`quest`
means `bun run --silent quest`" note stays, since it names which `quest`
to run, not how to load it.
- Check each repository from a fresh worktree on a host with the user-level
hooks: a new Claude Code session and a new Codex session both resolve the
dev shell's tools (`which just`, `quest --version` where pinned) with no
`nix develop` and no manual `direnv allow`.

## Waiting

- Maintainer: pull kixelated/dotfiles and run `bin/install-dev-shell.py` (or the full `install`, with `codex` on `PATH`) on `nixos` (`kixtop` has neither direnv nor Nix, so the hook has nothing to load there; it got only the effort defaults on 2026-11-03); it changes the live agent config on each host, which an agent here doesn't touch unasked. The repositories' hooks must not go before the user-level ones arrive. [kixelated/dotfiles#33](https://github.com/kixelated/dotfiles/pull/33) is merged. On 2026-11-03, `desktop` and `nixos2` had the hook in `~/.claude-personal/settings.json`, but `nixos` and `kixtop` didn't. Check on each host with direnv and Nix (`desktop`, `nixos2`, `nixos`) that `~/.claude-personal/settings.json` has the dev-shell `SessionStart` hook and that a fresh worktree's session resolves `just`; on `kixtop` the hook is an expected no-op
29 changes: 0 additions & 29 deletions quest/m0/worktree-hooks.md

This file was deleted.

4 changes: 3 additions & 1 deletion quest/m0/worktree-routes.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,9 @@ agent can't do, so its README description comes from Claude Code's own code:
- The bridge never runs `git worktree unlock` on a hook-created checkout,
since it records no Git root for one, so quest's lock stays in place.

Check it from a scratch repository set up with `quest init`, with
Check it from a scratch repository with the worktree hooks configured (the
opt-in user-level setup once
[quest init writes no hooks](/quest/m0/user-dev-shell/init-hooks.md) lands), with
`QUEST_WORKTREE_HOME` isolated: start `claude remote-control --spawn worktree`,
spawn a session from claude.ai, end it once without changes and once with an
untracked file, and watch `quest worktree status`. Fix the README if it
Expand Down
40 changes: 0 additions & 40 deletions quest/m0/wrapped-hooks.md

This file was deleted.