diff --git a/.gitignore b/.gitignore index 564befb..5fdc487 100644 --- a/.gitignore +++ b/.gitignore @@ -42,5 +42,16 @@ migrate_working_dir/ CLAUDE.local.md .sync-agents-cache.json +# Worktrees Claude Code creates for parallel sessions and isolated subagents. +# Only this subdirectory: the rest of `.claude/` is tracked, because the rules +# under it are part of the repository. +# +# They sit inside the working tree rather than beside it. `git clean -xdf` leaves them +# alone: git's nested-repository guard resolves a gitfile too, so it reports "Skipping +# repository .claude/worktrees/" and the contents survive. `git clean -xdff` does +# not skip them, and wipes a live worktree while `.git/worktrees/` survives, +# leaving a registration that `git worktree prune` then has to clear. +.claude/worktrees/ + # Local sibling-package path wiring (dev only) pubspec_overrides.yaml diff --git a/.worktreeinclude b/.worktreeinclude new file mode 100644 index 0000000..2ffd897 --- /dev/null +++ b/.worktreeinclude @@ -0,0 +1,30 @@ +# Gitignored files copied into every worktree Claude Code creates, for `--worktree`, +# for `EnterWorktree`, and for a subagent with `isolation: worktree`. +# +# A worktree is a fresh checkout, so `pubspec_overrides.yaml` is absent from it, and +# its absence fails silently rather than loudly: the siblings resolve from pub.dev +# instead of the working trees beside this one, `flutter pub get` succeeds, and the +# suite then passes against the PUBLISHED packages while the diff under review is of +# the local ones. An unreleased sibling API is where that bites. +# +# `.gitignore` syntax, and only files that match AND are gitignored are copied, so a +# tracked file can never be duplicated by this list. +# +# The paths inside `pubspec_overrides.yaml` have to be ABSOLUTE for this to work. +# Worktrees live under `.claude/worktrees/`, so a relative `../magic` resolves +# to `.claude/worktrees/magic`, which does not exist, and version solving fails on +# the first path dependency. +# +# One thing this layout costs, which no file here can fix because the rule lives +# upstream in artisan: `sessionOwnershipError` treats a working directory INSIDE +# `projectRoot` as owning the session (`state_file.dart:262`, `_isWithin` at `:282`), +# and a worktree now sits under the main checkout. So a `dusk:*` call from a worktree +# that has no session of its own falls back to the main checkout's app, drives THAT, +# and reports success. The old sibling layout tripped the guard instead. Run +# `artisan start` in the worktree, or pass `--state=`, before any dusk command +# there. +# +# NOT processed when a WorktreeCreate hook replaces the default git logic; such a +# hook has to copy these itself. + +pubspec_overrides.yaml diff --git a/doc/commands/dusk-doctor.md b/doc/commands/dusk-doctor.md index 701fdb8..4619872 100644 --- a/doc/commands/dusk-doctor.md +++ b/doc/commands/dusk-doctor.md @@ -100,6 +100,8 @@ Compares `state.json`'s `projectRoot` against the directory the command is runni WARN, naming both paths. Skipped when no `projectRoot` is recorded. Paths are compared after resolving symlinks, so a worktree checkout does not read as a mismatch on the same directory. +That last property cuts the other way now that worktrees live at `/.claude/worktrees/` rather than beside the checkout. A worktree is INSIDE `projectRoot`, so the check reads it as the owner and stays silent, while a `dusk:*` call from a worktree with no session of its own drives the app started from the main checkout and succeeds. The layout that used to trip this check no longer exists, so the check no longer covers the case it was written for. Start a session in the worktree, or pass `--state=`. + ### 7. CDP session health Probes the `cdpPort` recorded in state for three failures. They matter together because they present as one symptom, a capture that never changes, and nothing else in the output distinguishes them: