feat: redesign merge with rebase styles and PR-style hook gates#471
Merged
feat: redesign merge with rebase styles and PR-style hook gates#471
Conversation
Cross-worktree merge verb with full git flag parity, layered config, optional post-merge cleanup, and explicit handling of worktree-specific edge cases (ephemeral worktree for targets without one, promote-on-conflict, cross-worktree abort/continue). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Replace the YAML-file config layer (~/.config/daft/config.toml, .daft/daft.yml) with git config daft.merge.* keys, matching how DaftSettings already works (daft.autocd, daft.checkout.push, etc.). Key names switch to lowerCamelCase. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
17-slice TDD-driven plan: scaffolding, CWD merge, --into resolution, safety rails, octopus announcement, full git flag passthrough, finish commands, conflict handling, FF plumbing, ephemeral worktree + prompt, promote-on-conflict, -r/-rb cleanup, layered config, list --merging, completions, docs/man/SKILL, and final CI verification. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
After comparing daft merge against worktrunk's wt merge, two features are worth pulling into scope: - `-y` / `--yes`: auto-accept interactive prompts (currently just the ephemeral-worktree prompt; future-proofs new prompts). Implies --adopt-target. Logs the coercion so invocations stay self-describing. - `merge-pre` / `merge-post` hooks: daft-layer hooks that carry cross-worktree / octopus / ephemeral context (info git's native hooks can't see). merge-pre aborts on failure; merge-post warns only. Spec updated with goals, CLI surface, full Hooks section with env-var contracts. Plan adds Task 10.1.5/10.1.6 for -y and a new Slice 15 for the hook integration, renumbering downstream slices. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Generated by the pre-commit man:gen hook as a side effect of registering git-worktree-merge in xtask. Slice 17 regenerates and authors the full reference page at docs/cli/daft-merge.md; these stubs land early so the working tree stays clean for subsequent tasks.
Task 1.2 now lists src/lib.rs as a file to modify and adds a step for the DAFT_VERBS entry (blocker found during pilot — bare `daft merge` verb path fails without it). Slice 17 gets a new Task 17.0 to wire daft_verb_tip() and related_commands() in xtask so the auto-generated docs/cli/git-worktree-merge.md picks up the tip box and See Also section its peers have. Also renumbers Slice 18's tasks 17.1/17.2 to 18.1/18.2 (missed during the earlier hook-adoption renumber). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Introduce src/core/worktree/merge.rs with the minimal Slice-2 surface: StartParams (sources), StartOutcome (already_up_to_date, conflicted), and execute_start() which dispatches `git merge <sources...>` inside a target worktree via std::process::Command, mirroring the established rebase.rs pattern. Conflict detection is exit-status-based; already_up_to_date is stubbed to false and upgraded in later slices. Registers the module in src/core/worktree/mod.rs alphabetically. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Replace Task 1.1's skeleton with the Slice-2 wiring: clap Args carrying the positional SOURCE list and --verbose, a repo guard, and a dispatch to core::worktree::merge::execute_start using std::env::current_dir() as the target worktree. Maps StartOutcome to user-facing output: a success line on a clean merge, "Already up to date." for no-op, and a bail with the continue hint on conflict. Regenerated man pages (man/daft-merge.1, man/git-worktree-merge.1) and the CLI docs stub (docs/cli/git-worktree-merge.md) are included because the added positional argument changed the generated output; the pre-commit verify hook requires these to be in sync with source. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Add tests/manual/scenarios/merge/basic.yml verifying the Slice-2 happy path: clone the standard-remote fixture, check out feature/test-feature to materialise it as a worktree, then from the main worktree run `git-worktree-merge feature/test-feature` and assert exit 0 plus "Merge complete." in the output. A final `git log --oneline main` sanity-checks that main still has a history. Also register the git-worktree-merge dev symlink in mise-tasks/setup/rust. Slice 1 added the command to the xtask COMMANDS list (man pages, completions) but did not add the dev symlink the scenario runner needs to exec git-worktree-merge; without it the scenario fails with command-not-found. Deviation from the task's file-restriction is intentional and minimal. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Drop the incorrect rebase.rs:295 citation and explain the direct Command usage as a Slice-2 shortcut to be replaced by GitCommand::merge_in later. Rename StartOutcome.conflicted to failed to avoid conflating true conflicts with other non-zero exits; the field now documents the Slice 5+ refinement. Strengthen the basic merge scenario's post-condition to assert the feature commit is reachable from main, not just that git log succeeds. Document the caller invariant on execute_start and delete the redundant empty- sources bail (clap's num_args = 1.. enforces it) along with the dead module_exports_are_visible test. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Introduces the --into flag on the merge command and a matching target field on core StartParams. Target resolution and dispatch land in the next commit; for now the flag is wired end-to-end as None by default. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds ResolvedTarget and resolve_target to the merge core. Delegates path resolution to GitCommand::resolve_worktree_path for explicit targets and reads the target branch via `git -C <path> symbolic-ref --short HEAD`. When no target is supplied, uses get_current_worktree_path and symbolic_ref_short_head against the process CWD. Unit tests cover branch_at_path against a fresh git init tempdir; the happy-path multi-worktree resolution is left to the YAML scenario since GitCommand's CWD-based helpers are hostile to parallel in-process tests. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Refactors execute_start to take &GitCommand and project_root, routes the call through resolve_target, and dispatches `git merge` with the target worktree as CWD. Command layer now loads DaftSettings for the gitoxide backend choice and constructs GitCommand, mirroring carry.rs. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds a YAML scenario that exercises the end-to-end --into path: from the feature worktree, run `daft merge --into main feature/...`, then verify main contains the feature commits without ever changing CWD into the target worktree. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…de parity Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds validate_distinct() pre-flight check that bails with an actionable error when any source branch equals the resolved target branch. Called from execute_start before the merge invocation so users see a clear "cannot merge branch 'X' into the same branch" instead of git's noisy "Already up to date." for what is almost always an --into typo. Covered by a unit test for both the refuse and allow paths plus a YAML scenario that runs `git-worktree-merge main --into main` and asserts exit 1 with "same branch" in stderr.
Adds detect_in_progress() which inspects the target worktree's real git directory (following the gitdir pointer for linked worktrees) for the state files git writes when a merge/rebase/cherry-pick/bisect is paused: MERGE_HEAD, rebase-merge/, rebase-apply/, CHERRY_PICK_HEAD, BISECT_LOG. execute_start now bails with "target worktree 'X' is mid-rebase; finish or abort it first" (or equivalent) when such a state is detected, after the source==target check and before the merge invocation. Stacking two operations' conflicts on the user is always the wrong move. Unit tests cover each op variant, the clean case, the rebase-apply alternate path, and the linked-worktree gitdir-pointer codepath. A YAML scenario induces an in-progress rebase in main and verifies refusal.
Adds validate_clean_target() which delegates to the existing GitCommand::has_uncommitted_changes_in helper (src/git/stash.rs) so dirtiness is judged by the same `git status --porcelain` check the rest of daft uses — including treating untracked files as dirty. execute_start now refuses with "target worktree 'X' has uncommitted changes; commit, stash, or allow via `daft.merge.requireCleanTarget=false`" after the source==target and in-progress checks. The config toggle referenced in the hint lands in Slice 13; for now the behavior is hard-coded to always-refuse and a TODO in the docstring points to the follow-up slice. Unit tests cover both the clean and untracked-file-dirty paths using tempdir-backed real git repos with identity set via env vars (never global git config). A YAML scenario clones, checks out feature, dirties main with an untracked file, and verifies the merge is refused with "uncommitted changes" in stderr.
- validate_distinct now strips refs/heads/ prefix before comparing, so `daft merge refs/heads/main --into main` is refused. Doc-comment notes comparison remains nominal (origin/main, SHAs not normalized). - detect_in_progress uses is_dir() for rebase-merge/rebase-apply markers and now bails with context when the resolved git_dir is missing. - Malformed `.git` file pointer (missing `gitdir:` prefix) errors out instead of silently falling back to the worktree path. - validate_clean_target error message no longer references daft.merge.requireCleanTarget (the config lands in slice 13). - New unit tests: later-source match and refs/heads/ prefix cases.
Add a pure `announcement()` helper in `core::worktree::merge` and call it from `execute_start` before invoking `git merge`. When two or more sources are supplied, the helper returns "Merging N sources into <target> via octopus strategy"; `execute_start` emits that line to stderr so it stays out of stdout (reserved for the final "Merge complete." / "Already up to date." result) but remains visible even when git's octopus refuses a conflict. Covered by new unit tests and two YAML scenarios (`merge:octopus`, `merge:octopus-conflict`).
Add CLI args for all 14 git merge passthrough flag groups (message/editor, ff mode, squash, commit, signoff, strategy, strategy options, gpg signing, signature verification, allow-unrelated-histories, stat). Flags flow from clap Args into a new EffectiveFlags struct on StartParams, which is serialized back into argv between the merge keyword and the source list via render_flags. Clap conflicts_with/conflicts_with_all attributes enforce mutual exclusion of paired flags (--ff / --no-ff / --ff-only, etc.) at parse time. -S uses num_args = 0..=1 + default_missing_value = "" to distinguish bare -S (default key -> GpgSign::Default) from -S<KEYID> (GpgSign::KeyId). Unit tests (13 new) cover render_flags for every variant: empty, message + file pairing, each FfMode, multiple -X options as separate pairs, each GpgSign variant, edit/no-edit, allow-unrelated-histories toggle, and a full combination that locks the declared emission order. YAML scenarios (8 new) smoke-test that each flag threads through end to end: ff default, --ff-only refusal on non-FF, --no-ff merge commit, --squash staging, --signoff trailer, -s ours, -X theirs, and already-up-to-date no-op. No gpg.yml scenario; GPG requires a configured key and the serializer is covered by unit tests. Man pages and CLI docs regenerated to document the new flags. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…lp text Switch `execute_start` from `.status()` to `.output()` so the core layer can observe git's stdout and set `StartOutcome.already_up_to_date = true` when git emits "Already up to date.". The captured stdout/stderr are re-emitted verbatim to our own stdio via `write_all`, preserving what the user sees. The command layer no longer prints its own "Already up to date." line in that branch — git's passthrough already said it — avoiding the double-print contradicted by a trailing "Merge complete.". The `already-up-to-date.yml` scenario asserts "Merge complete." is absent from the second-merge output. Also drop the "(also `-n`)" tail from the `--no-stat` help text; clap's `-n, --no-stat` header already advertises the short alias. Regenerated the daft-merge and git-worktree-merge man pages and the CLI reference page for the help-text update. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…es via plumbing Commit c51789e0 switched `execute_start`'s git invocation from `.status()` to `.output()` to detect "Already up to date." in stdout. That broke the interactive merge-commit editor path: for a non-FF merge without `-m`, git launches `$EDITOR` for the commit message, which requires stdio to be inherited from the parent TTY. Capturing stdout/stderr makes that unpredictable. Replace the stdout scrape with a plumbing pre-flight before argv is built: `rev-parse` each source and the target branch, then ask `merge-base --is-ancestor` whether every source is already reachable from the target. If so, print "Already up to date." directly and return early without invoking `git merge`. For real merges, revert to `.status()` so stdio inherits the parent TTY and the editor works again. For octopus (multi-source) merges, the predicate is "all sources are ancestors" — if any source has new commits, we still run git merge. If `rev-parse` fails for an invalid ref, skip the short-circuit and let `git merge` surface its usual error. Run the pre-flight before the octopus announcement so an up-to-date multi-source invocation doesn't herald a strategy we won't actually run. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…ints - `--abort`/`--continue`/`--quit` now conflict with every start-only flag, so `daft merge --abort -m msg` errors at parse time instead of silently ignoring the start-mode flag. - Finish mode accepts `--into <target>` as a fallback when no positional is given; passing both is rejected. - `execute_finish` routes through `ensure_merge_in_progress` and resolves branch names for candidate worktrees so single-candidate retry hints show a concrete `daft merge --abort <branch>` command. - Adds `merge/abort-hint-elsewhere.yml` covering the "merges in progress elsewhere" hint path. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Delete ff.yml, ff-only.yml (FF-default and ff-only behaviors removed), no-ff.yml (default is now always-merge-commit, covered by new scenarios), and remove-source.yml (worktree-only removal without branch delete is no longer expressible via daft merge). Update all -rb → -r, --no-ff → --merge, daft.merge.ff → daft.merge.style. Add --no-edit to all default-style merge commands to avoid editor invocation in CI. Update assertions from "Fast-forwarded" to "Merged ... into ... (commit" wherever the default style now forces a merge commit. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
MergeHookRunner was using HooksConfig::default() instead of load_hooks_config(), causing git config keys like daft.hooks.preMerge.failMode to be silently ignored. This made the warn-mode override ineffective: a failing pre-merge hook would always abort even after setting failMode=warn via git config. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Covers all four merge styles (--merge, --squash, --rebase, --rebase-merge), both cleanup modes (keep, remove-branch), the --set-default config writer, and pre/post-merge hook behavior under preMerge.failMode=warn. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- daft-merge.md: new Key Options table (--merge/--squash/--rebase/ --rebase-merge/-r/--keep-branch/--set-default), rebase/rebase-merge examples, --set-default section, updated Configuration table with daft.merge.style + daft.merge.cleanup, migration table from v1.9 flags - hooks.md: document preMerge.failMode=warn git config override, update DAFT_MERGE_MODE to include rebase/rebase-merge, fix -r/-rb refs - SKILL.md: update command synopsis and Cross-worktree Merges section with new flag set, default behavior note, and --set-default guidance Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The new default MergeStyle::Merge always creates a merge commit, which triggers a TTY guard early in start-mode. Scenarios that relied on pre-flight checks (dirty-target, same-source, conflict, abort setups) hit the TTY guard before reaching those checks. Add --no-edit to the merge invocations in 16 scenarios so the TTY guard is bypassed and the actual behaviors under test are exercised. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
detect_in_progress_state hardcoded path.join(".git") as a directory,
breaking --continue/--abort in every linked worktree (where .git is a
gitdir-pointer file). Now follows the same gitdir resolution that
detect_in_progress uses.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…merge Spec requires these flags to conflict with rebase styles since rebase has no auto-commit toggle. Without the clap conflicts, daft silently accepted invalid combinations. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Previous fix in 1511b99 covered MergeHookRunner but missed two sites: the cleanup loop's HookExecutor (worktree-pre/post-remove hooks) and fire_worktree_post_create_hook (worktree-post-create on ephemeral promotion). Without this, those paths ignore daft.hooks.* git config (trust level, hook paths). Also removes now-unused HooksConfig import. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Spec requires post-merge to fire only when a commit landed. The squash_staged_only path stages changes but produces no commit; firing post-merge there would deliver an empty commit_sha to hook scripts and violate the "merge happened" trigger contract. Fixes both the regular target-path path (line 1631) and the ephemeral worktree path (line 2060). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The Slice 3 CLI cutover replaced --ff/--no-ff/--ff-only/--squash/ --no-squash/-r/--remove/-b/--and-branch with --merge/--squash/--rebase/ --rebase-merge/-r/--remove-branch/--keep-branch/--set-default but missed the hardcoded flag lists that bash, zsh, fish, and fig completions embed as string constants. CLAUDE.md flags this exact case: flag completions for git-worktree-* commands are auto-generated from clap Args, but the hardcoded subcommand strings in bash/zsh/fish/fig must be updated manually when flags change. Users will need to re-eval daft completions <shell> in active shells (or restart) to pick up the updated lists. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Field testing surfaced two confusing aspects of `daft merge`'s output:
the user couldn't tell whether daft had understood their flags (the only
signal was a transient spinner that often vanished too fast to read),
and the cleanup hook box left them unsure which worktree it was acting
on — alarming when merging from a sibling worktree, since at first
glance it looked like the current worktree might be removed.
Three changes address this without behavior changes:
1. Persistent intent line at the start: "Merging X → Y (style · cleanup
[· saving as default])". Lands once and stays in scrollback so the
rest of the output reads in context.
2. Per-source cleanup section heading right before the worktree-pre-remove
hook box: "Cleaning up X (worktree, local branch)". Labels the target
in the user's main output stream before the box appears.
3. Hook-box title gains an `on: <target>` segment for worktree-scoped
phases (worktree-pre-create / -post-create / -pre-remove / -post-remove);
project-scoped phases (pre-merge, post-merge, post-clone) keep the bare
title. The redundant `hook:` label is also dropped — the box border
itself signals "this is a hook".
The `--set-default` notice ("Updated repository defaults: …") is also
moved from mid-flow (between the merge and cleanup) to a trailing footnote
after the success line, so it reads as a side effect rather than
interleaved with operational steps. Defaults are still persisted before
cleanup, and the notice fires on cleanup-failure paths too — the values
were saved regardless of cleanup outcome.
Pinned scenarios updated:
- merge-pre-post-merge-hooks-render-rich.yml: title contract changed,
`output_not_contains` guards against the legacy `hook:` label.
- New hook-box-target-segment.yml: load-bearing assertion that
`worktree-pre-remove on: feature/test-feature` appears in cleanup.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
4 tasks
Two PR-check failures, both regressions from this branch's new merge command landing without updating hardcoded CI lists: - Integration tests (yaml suite): all merge scenarios failed with "git-worktree-merge: command not found". The integration-tests job in test.yml has a hardcoded symlink-creation list that didn't include the new command. Same omission in the man-page validation list, the CLI-docs validation list, and the homebrew-simulation install/verify lists. Adding it to all six keeps them consistent. - Unit tests: 5 merge.rs tests panicked in CI with "empty ident name" because production code (git merge / git rebase) inherits the test process env but not the GIT_AUTHOR_*/GIT_COMMITTER_* env vars that init_repo only set for the bootstrap commit. Fix by writing user.name/user.email to local git config inside init_repo, so any git invocation on the test repo (test code or production code under test) has an identity. Local-only — no global config touched. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
`finish_mode_continue_resumes_rebase` failed in CI with "Terminal is dumb, but EDITOR unset" — `git rebase --continue` after a conflict resolution invokes `git commit` which opens an editor to confirm the original message. CI runners are headless with no EDITOR/GIT_EDITOR set. Set core.editor=true in the test repo's local config so any implicit editor invocation is a successful no-op that preserves the existing commit message. Test repo only — global config untouched. Reproduced locally with `env -i HOME=/tmp/empty PATH=...` to mimic the CI environment, confirmed fix passes the failing test and all 130 merge module tests. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Merged
avihut
added a commit
that referenced
this pull request
May 6, 2026
Master shipped the cross-worktree merge feature (#471) before this branch's IA restructure landed, so the merge docs entered the old IA shape (one guide/hooks.md, one guide/configuration.md, one guide/worktree-workflow.md, table-of-contents merge entries in the legacy sidebar). The rebase brought the new CLI ref pages and the merge config additions through cleanly via git's rename detection on configuration.md, but the rest needed manual porting. Changes: - New page: Worktrees → Merging across worktrees. Substantively expanded from master's 25-line section in guide/worktree-workflow.md into a proper how-to: quick examples, merge styles, conflicts, cleanup, ephemeral targets, and hook gates. Sidebar position: between "Running commands" and "Shortcuts". - Hooks → Lifecycle: added pre-merge / post-merge rows to the hook-types table, env-var tables for both, and a Merge hooks section covering semantics, fail modes (including the daft.hooks.preMerge.failMode override), and the squash-abort case. - Hooks → Overview: flipped merge gates from Roadmap to Shipped in the boundaries table; updated the "Where to next" line. - Hooks → Roadmap: removed the merge-gates entry (shipped); added a "Recently shipped" pointer to the new merge content. - Reference → Configuration: reconciled the merge-keys table to the v1.10 schema (daft.merge.style + daft.merge.cleanup) per the authoritative daft-merge.md migration table. Master's preexisting tabular content documented the v1.9 keys that were removed in #471. - Reference → CLI sidebar: added daft-merge under Maintenance and worktree-merge under Git Commands → Maintenance. - Landing: updated Worktrees and Hooks pillar cards to reflect the merge feature and shipped merge-gates story (no new card — landing stays pillar-shaped). - _redirects: /guide/hooks now lands on /hooks/lifecycle so anchors like #merge-hooks survive the redirect (Cloudflare can't match on fragments). - CLI ref: fixed two relative ../guide/* links in daft-merge.md to absolute paths in the new IA. Build clean with strict link checking; biome and prettier pass.
avihut
added a commit
that referenced
this pull request
May 9, 2026
Master shipped the cross-worktree merge feature (#471) before this branch's IA restructure landed, so the merge docs entered the old IA shape (one guide/hooks.md, one guide/configuration.md, one guide/worktree-workflow.md, table-of-contents merge entries in the legacy sidebar). The rebase brought the new CLI ref pages and the merge config additions through cleanly via git's rename detection on configuration.md, but the rest needed manual porting. Changes: - New page: Worktrees → Merging across worktrees. Substantively expanded from master's 25-line section in guide/worktree-workflow.md into a proper how-to: quick examples, merge styles, conflicts, cleanup, ephemeral targets, and hook gates. Sidebar position: between "Running commands" and "Shortcuts". - Hooks → Lifecycle: added pre-merge / post-merge rows to the hook-types table, env-var tables for both, and a Merge hooks section covering semantics, fail modes (including the daft.hooks.preMerge.failMode override), and the squash-abort case. - Hooks → Overview: flipped merge gates from Roadmap to Shipped in the boundaries table; updated the "Where to next" line. - Hooks → Roadmap: removed the merge-gates entry (shipped); added a "Recently shipped" pointer to the new merge content. - Reference → Configuration: reconciled the merge-keys table to the v1.10 schema (daft.merge.style + daft.merge.cleanup) per the authoritative daft-merge.md migration table. Master's preexisting tabular content documented the v1.9 keys that were removed in #471. - Reference → CLI sidebar: added daft-merge under Maintenance and worktree-merge under Git Commands → Maintenance. - Landing: updated Worktrees and Hooks pillar cards to reflect the merge feature and shipped merge-gates story (no new card — landing stays pillar-shaped). - _redirects: /guide/hooks now lands on /hooks/lifecycle so anchors like #merge-hooks survive the redirect (Cloudflare can't match on fragments). - CLI ref: fixed two relative ../guide/* links in daft-merge.md to absolute paths in the new IA. Build clean with strict link checking; biome and prettier pass.
avihut
added a commit
that referenced
this pull request
May 9, 2026
Master shipped the cross-worktree merge feature (#471) before this branch's IA restructure landed, so the merge docs entered the old IA shape (one guide/hooks.md, one guide/configuration.md, one guide/worktree-workflow.md, table-of-contents merge entries in the legacy sidebar). The rebase brought the new CLI ref pages and the merge config additions through cleanly via git's rename detection on configuration.md, but the rest needed manual porting. Changes: - New page: Worktrees → Merging across worktrees. Substantively expanded from master's 25-line section in guide/worktree-workflow.md into a proper how-to: quick examples, merge styles, conflicts, cleanup, ephemeral targets, and hook gates. Sidebar position: between "Running commands" and "Shortcuts". - Hooks → Lifecycle: added pre-merge / post-merge rows to the hook-types table, env-var tables for both, and a Merge hooks section covering semantics, fail modes (including the daft.hooks.preMerge.failMode override), and the squash-abort case. - Hooks → Overview: flipped merge gates from Roadmap to Shipped in the boundaries table; updated the "Where to next" line. - Hooks → Roadmap: removed the merge-gates entry (shipped); added a "Recently shipped" pointer to the new merge content. - Reference → Configuration: reconciled the merge-keys table to the v1.10 schema (daft.merge.style + daft.merge.cleanup) per the authoritative daft-merge.md migration table. Master's preexisting tabular content documented the v1.9 keys that were removed in #471. - Reference → CLI sidebar: added daft-merge under Maintenance and worktree-merge under Git Commands → Maintenance. - Landing: updated Worktrees and Hooks pillar cards to reflect the merge feature and shipped merge-gates story (no new card — landing stays pillar-shaped). - _redirects: /guide/hooks now lands on /hooks/lifecycle so anchors like #merge-hooks survive the redirect (Cloudflare can't match on fragments). - CLI ref: fixed two relative ../guide/* links in daft-merge.md to absolute paths in the new IA. Build clean with strict link checking; biome and prettier pass.
avihut
added a commit
that referenced
this pull request
May 9, 2026
Master shipped the cross-worktree merge feature (#471) before this branch's IA restructure landed, so the merge docs entered the old IA shape (one guide/hooks.md, one guide/configuration.md, one guide/worktree-workflow.md, table-of-contents merge entries in the legacy sidebar). The rebase brought the new CLI ref pages and the merge config additions through cleanly via git's rename detection on configuration.md, but the rest needed manual porting. Changes: - New page: Worktrees → Merging across worktrees. Substantively expanded from master's 25-line section in guide/worktree-workflow.md into a proper how-to: quick examples, merge styles, conflicts, cleanup, ephemeral targets, and hook gates. Sidebar position: between "Running commands" and "Shortcuts". - Hooks → Lifecycle: added pre-merge / post-merge rows to the hook-types table, env-var tables for both, and a Merge hooks section covering semantics, fail modes (including the daft.hooks.preMerge.failMode override), and the squash-abort case. - Hooks → Overview: flipped merge gates from Roadmap to Shipped in the boundaries table; updated the "Where to next" line. - Hooks → Roadmap: removed the merge-gates entry (shipped); added a "Recently shipped" pointer to the new merge content. - Reference → Configuration: reconciled the merge-keys table to the v1.10 schema (daft.merge.style + daft.merge.cleanup) per the authoritative daft-merge.md migration table. Master's preexisting tabular content documented the v1.9 keys that were removed in #471. - Reference → CLI sidebar: added daft-merge under Maintenance and worktree-merge under Git Commands → Maintenance. - Landing: updated Worktrees and Hooks pillar cards to reflect the merge feature and shipped merge-gates story (no new card — landing stays pillar-shaped). - _redirects: /guide/hooks now lands on /hooks/lifecycle so anchors like #merge-hooks survive the redirect (Cloudflare can't match on fragments). - CLI ref: fixed two relative ../guide/* links in daft-merge.md to absolute paths in the new IA. Build clean with strict link checking; biome and prettier pass.
avihut
added a commit
that referenced
this pull request
May 9, 2026
* chore(docs): install Diátaxis skills for project-wide doc IA work
Adds two project-scoped agent skills under .claude/skills/:
- diataxis-organize-docs: audits an existing docs corpus, classifies
pages into the four Diátaxis quadrants (tutorial/how-to/reference/
explanation), and proposes a directory restructure. Used to drive
the upcoming docs IA overhaul.
- writing-documentation-with-diataxis: companion skill for writing
individual pages in the appropriate Diátaxis style, anchored on the
"Diataxis Compass" two-question diagnostic.
Skipping Rust fmt/clippy/test gates: this commit only adds static
markdown under .claude/skills/ plus skills-lock.json — no Rust touched.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* docs(spec): design for docs IA restructure with cookbook section
Captures the brainstorm output for daft-398, expanded from "add a
cookbook" to a full IA restructure that includes the cookbook.
Decisions:
- Pillar-driven IA (Worktrees / Hooks / Networking) with Diátaxis
quadrants honored inside each pillar (Overview = Explanation; peer
How-to and Reference sections)
- Cookbook elevated to top-level (not buried under About like mise),
taxonomized by tooling / language / scenario, recipes pillar-tagged
- Reference unified, not pillar-fragmented
- "Hooks-as-boundaries" thesis surfaced from parallel merge-feature
work: daft hooks as a local-parallel-to-GitHub-Actions surface,
with each code-evolution stage (worktree create / commit / merge /
worktree teardown) getting its own gate
Out of scope (follow-up issues):
- Full git hooks drop-in (lefthook-style)
- Merge hooks
- Networking pillar content
- Visual rebrand
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* docs(spec): unify follow-up issues with feature tickets
Per "docs and features must enter together," restructure the spec's
out-of-scope section to track docs alongside feature work:
- #330 (Merge Branch) absorbs merge-hooks docs (was tracked at #464)
- #357 (Repo Catalog) absorbs Networking pillar docs (was at #466)
- #468 (new feat — full git-hooks drop-in) absorbs lefthook drop-in
docs (was at #465)
#464, #465, #466 closed as superseded; their work is happening, just
under the unified feature tickets.
Adds a "Coordination with #330" section covering the rebase strategy:
land this branch first, #330 converts its old-IA docs to new-IA paths
during its rebase prep. Avoids holding this branch open while #330
finishes (stale rebases) and avoids rework.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* docs(plan): implementation plan for docs IA restructure
8-phase plan covering: vitepress config + redirects scaffolding,
Worktrees pillar pages, Hooks pillar (split + thesis Overview),
Reference moves, About pages (Why daft, glossary, FAQ,
troubleshooting, comparison, networking roadmap), Cookbook
structure with 3 anchor recipes (mise, direnv, monorepo) and 10
stubs, per-pillar Recipes filter, and final cleanup.
Each phase is independently committable. Build verification
(`mise run docs:site:build`) gates every task. CLI ref stays at
docs/cli/ on disk; vitepress rewrites surface them at /reference/
cli/*. Cloudflare _redirects keep legacy URLs alive.
Recipe authoring strategy: anchor recipes (mise, direnv, monorepo)
written in full inline; stubs ship with frontmatter + outline so
sidebar + search index are populated immediately, with full prose
following as additional commits on this branch.
Plan paired with the design spec at
docs/superpowers/specs/2026-05-03-docs-ia-restructure-design.md.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* docs: scaffold pillar directory placeholders
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* docs(vitepress): adopt pillar IA in sidebar + nav + URL rewrites
Replaces the legacy 'Getting Started / Guide / daft Commands / Git
Commands / Project' sidebar with the new pillar IA from #398:
Getting Started | Worktrees | Hooks | Cookbook | Reference | About
Top nav exposes the three product surfaces (Worktrees / Hooks /
Cookbook) plus the version + GitHub link.
Adds vitepress rewrites so docs/cli/*.md (still autogenerated by
xtask at the same path) surface at /reference/cli/* URLs without
needing to move generated files.
ignoreDeadLinks is temporarily true; will revert in the final
migration cleanup task.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* docs: add Cloudflare redirects for legacy URLs
Maps pre-restructure URLs (/guide/*, /cli/*, /contributing,
/changelog) to their new homes under the pillar IA. 301 permanent
redirects so search engine results flow to the new locations.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* docs(worktrees): write pillar Overview, retire guide/worktree-workflow
Establishes the Worktrees pillar's spine page. Content sourced from
the existing guide/worktree-workflow.md, plus a new 'adoption arc'
section that names the three stages (code -> env -> automation) and
links forward into the cookbook and the Hooks pillar.
Closes the worktree-workflow legacy URL via the redirect added in
Task 1.3.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* docs(worktrees): migrate layouts page to pillar
Move guide/layouts.md → worktrees/layouts.md and append a
"Where to next" footer linking to the daft layout CLI reference
and the monorepo cookbook recipe.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* docs(spec,plan): restore historical /guide/X references corrupted by 8100bdf9
Task 2.2's blanket grep+sed `/guide/layouts` → `/worktrees/layouts`
rewrote semantic mentions of the legacy URL inside the IA spec and
the IA plan, where those references are intentionally describing
the pre-migration state:
- Spec section "URL redirects": example legacy URLs got rewritten
- Plan _redirects table: redirect rule became circular
- Plan migration steps: "Delete docs/guide/layouts.md" became
"Delete docs/worktrees/layouts.md"
Restores both files to their state at b4331076 (the commit before
Task 2.2). Future migration tasks must scope grep+sed to exclude
docs/superpowers/ so spec/plan history is preserved.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* docs(worktrees): move adopting-existing-repos to pillar
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* docs(worktrees): rename running-commands-across-worktrees to running-commands
Shorter path under the pillar. Cross-links updated.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* docs(worktrees): move shortcuts to pillar
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* docs: restore _redirects circular-redirect lines from corruption
Tasks 2.3, 2.4, and 2.5 ran `grep -rln '/guide/X' docs/` for cross-
link updates, which matched the LHS of the corresponding redirect
rules in docs/public/_redirects. The blanket sed rewrote both LHS
and RHS (since the RHS was already the new path, only LHS matched),
producing three circular self-redirects like:
/worktrees/shortcuts /worktrees/shortcuts 301
Restores _redirects to its canonical state from commit 69235d44.
Future migration tasks must scope grep+sed to exclude:
- docs/superpowers/ (intentional legacy URL history)
- docs/public/ (the whole point of _redirects is mapping old→new)
- docs/node_modules/ (build artifact)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* docs(worktrees): move multi-remote to pillar with cross-links
Move guide/multi-remote.md → worktrees/multi-remote.md and append
a "Where to next" footer linking to the daft multi-remote CLI ref
and the fork workflow cookbook recipe.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* docs(hooks): write pillar Overview with boundaries thesis
Frames daft hooks as a local-parallel-to-GitHub-Actions surface,
with each code-evolution stage (worktree create / commit / merge /
worktree teardown) getting its own gate. Honestly marks which
stages are shipped vs roadmap (#330, #468).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* docs(hooks): extract lifecycle reference from guide/hooks.md
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* docs(hooks): extract daft.yml schema reference from guide/hooks.md
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* docs(hooks): write job-orchestration explanation page
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* docs(hooks): write trust-and-security explanation page
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* docs(hooks): write roadmap stub for #330 (merge) and #468 (commit drop-in)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* docs(hooks): retire guide/hooks.md (content migrated to pillar pages)
The legacy guide/hooks.md was a kitchen-sink page mixing
Explanation + Reference + How-to. Its content now lives in:
- hooks/index.md (Overview, with the boundaries thesis)
- hooks/lifecycle.md (Reference: hook types, env vars, exit codes)
- hooks/yaml-reference.md (Reference: full daft.yml schema)
- hooks/job-orchestration.md (Explanation: parallelism, deps, conditions)
- hooks/trust-and-security.md (Explanation: trust-on-first-use)
- hooks/roadmap.md (Stub for #330, #468)
Cross-links updated. Legacy URL /guide/hooks redirects to /hooks/
via the Cloudflare _redirects file (added in Task 1.3).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* docs(reference): move configuration page to reference/
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* docs(reference): move output-formats page to reference/
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* docs(reference): move agent skill page to reference/ with multi-agent name
The skill applies to any agent that supports the agent-skills standard,
not just Claude. Renames `claude-skill.md` to `agent-skill.md` and
updates cross-links accordingly.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* docs(reference): write reference hub overview
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* docs(about): move contributing and changelog under about/
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* docs(about): write the why-daft thesis page
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* docs(about): write glossary
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* docs(about): write FAQ
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* docs(about): write troubleshooting
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* docs(about): write comparison page (vs git worktree, lefthook, gitup, Actions)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* docs(about): write networking-roadmap stub linking #357
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* docs(about): write about hub overview
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* docs(cookbook): write cookbook hub with by-tooling/by-language/by-scenario taxonomy
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* docs(cookbook): write mise recipe (anchor)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* docs(cookbook): write direnv recipe (anchor)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* docs(cookbook): write monorepo recipe (anchor)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* docs(cookbook): stub remaining 9 recipes with outlines
Stub recipes get frontmatter + outline + cross-links to anchor
recipes. They render as valid pages so sidebar entries don't 404
and the search index includes the topics. Full recipe content
follows as additional commits on this branch.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* docs(cookbook): build-time recipe metadata loader
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* docs(cookbook): add per-pillar recipe filter via URL query param
The Worktrees and Hooks pillar sidebars now have a 'Recipes' entry
that deep-links to /cookbook/?pillar=<name>. The cookbook index
reads the query param and filters its recipe list accordingly. The
canonical home of all recipes remains /cookbook/ — the filter is
just a discoverability shortcut.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* docs: fix vestigial RecipeFilter tag + broken background-jobs anchor
Two cleanup items from earlier phases:
1. Phase 7 left a `<RecipeFilter />` tag in cookbook/index.md that
has no registered component. Removed.
2. Task 3.7's cross-link rewrite landed `/hooks/index.md#background-jobs`
in daft-hooks-jobs.md, but the anchor lives on yaml-reference.md.
Fixed to point at the right page.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* docs(getting-started): narrate the worktree adoption arc in Quick Start
Restructures Quick Start as a 3-stage tutorial mirroring the
gradient (code -> env -> automation). Stage 1 keeps the original
worktree walkthrough; Stages 2 and 3 link forward to cookbook
recipes and the Hooks pillar.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* docs: update landing hero copy + features for pillar IA
Aligns the landing page with the new pillar shape (Worktrees,
Hooks, Cookbook) and the parallel-dev thesis. Full marquee-features
revamp remains scoped to #386.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* docs: re-enable strict link checking after IA migration
All pages from docs/guide/ have been migrated to their new pillar
homes. Empty directory removed. ignoreDeadLinks reverted to false
so future PRs catch broken cross-links.
Fixed 21 dead links surfaced by strict checking — all were relative
paths in docs/cli/ files that broke because VitePress resolves them
against the rewritten reference/cli/ virtual path. Converted to
absolute paths, and fixed stale ./hooks.md, ./layouts.md,
./worktree-workflow.md, ./shortcuts.md references in reference/ and
worktrees/ that pointed at pages which no longer exist at those paths.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* docs: rename Cookbook pillar to Recipes; drop pillar-sidebar Recipes shortcuts
The "Cookbook" pillar was conceptually fine but caused two UX issues:
- The sidebar's pillar shortcuts ("Recipes" under Worktrees and Hooks) pointed
at /cookbook/?pillar=<name>, which VitePress's active-link matcher
(path-only, query ignored) rendered as both Recipes and Cookbook → Overview
highlighted simultaneously.
- The pillar-shortcut indirection implied two separate destinations when there
was only one — the cookbook hub with a runtime filter applied.
Fix:
- Rename the pillar surface "Cookbook" → "Recipes" everywhere (top nav,
sidebar group, page heading, frontmatter title, prose). Move the URL path
/cookbook/ → /recipes/ so the IA stays coherent: label, URL, and concept all
match.
- Drop the duplicate "Recipes" entries under the Worktrees and Hooks sidebars.
Inline cross-links from pillar pages still deep-link with ?pillar= filters,
but the sidebar no longer presents the same destination twice.
Build remains clean with ignoreDeadLinks: false; biome lint passes.
* docs: integrate cross-worktree merge feature into pillar IA
Master shipped the cross-worktree merge feature (#471) before this branch's
IA restructure landed, so the merge docs entered the old IA shape (one
guide/hooks.md, one guide/configuration.md, one guide/worktree-workflow.md,
table-of-contents merge entries in the legacy sidebar). The rebase brought
the new CLI ref pages and the merge config additions through cleanly via
git's rename detection on configuration.md, but the rest needed manual
porting.
Changes:
- New page: Worktrees → Merging across worktrees. Substantively expanded
from master's 25-line section in guide/worktree-workflow.md into a proper
how-to: quick examples, merge styles, conflicts, cleanup, ephemeral
targets, and hook gates. Sidebar position: between "Running commands" and
"Shortcuts".
- Hooks → Lifecycle: added pre-merge / post-merge rows to the hook-types
table, env-var tables for both, and a Merge hooks section covering
semantics, fail modes (including the daft.hooks.preMerge.failMode
override), and the squash-abort case.
- Hooks → Overview: flipped merge gates from Roadmap to Shipped in the
boundaries table; updated the "Where to next" line.
- Hooks → Roadmap: removed the merge-gates entry (shipped); added a
"Recently shipped" pointer to the new merge content.
- Reference → Configuration: reconciled the merge-keys table to the v1.10
schema (daft.merge.style + daft.merge.cleanup) per the authoritative
daft-merge.md migration table. Master's preexisting tabular content
documented the v1.9 keys that were removed in #471.
- Reference → CLI sidebar: added daft-merge under Maintenance and
worktree-merge under Git Commands → Maintenance.
- Landing: updated Worktrees and Hooks pillar cards to reflect the merge
feature and shipped merge-gates story (no new card — landing stays
pillar-shaped).
- _redirects: /guide/hooks now lands on /hooks/lifecycle so anchors like
#merge-hooks survive the redirect (Cloudflare can't match on fragments).
- CLI ref: fixed two relative ../guide/* links in daft-merge.md to absolute
paths in the new IA.
Build clean with strict link checking; biome and prettier pass.
* docs(about): fix changelog include path after Phase 5 file move
When the changelog page moved from docs/changelog.md to docs/about/changelog.md
in Phase 5, the relative include `../CHANGELOG.md` kept resolving to its
original target — but that target is now docs/CHANGELOG.md (which doesn't
exist) instead of the repo-root CHANGELOG.md. The include silently failed
and the page rendered only the boilerplate footer (`daft release-notes`
hint + GitHub Releases link).
Changing the include to `../../CHANGELOG.md` reaches the repo root from the
new location, so the full release history renders again as it did on master.
* docs(recipes): scaffold patterns + walkthroughs structure
Replaces the by-tooling/by-language/by-scenario taxonomy with a
patterns-and-walkthroughs structure. Adds 13 placeholder pages, updates
the sidebar to the new shape, and rewrites the recipe data loader to walk
the new layout while emitting a `kind` discriminator
(pattern/walkthrough/anti-pattern/reference).
Old by-* recipe files are left in place for now so existing cross-links
keep building; they get retired in a later commit after content is ported
into the new patterns and external links are swept.
This commit is the structural scaffold; content lands in subsequent
commits per pattern/walkthrough/reference.
Refs the recipes restructure plan at
~/.claude/plans/zany-tumbling-quasar.md.
* docs(recipes): write toolchain-bootstrap pattern
Pattern recipe for installing dependencies idempotently when a worktree is
created. Covers Node (pnpm/npm/yarn/bun), Python (uv/pip/poetry), Rust
(cargo fetch — distinguished from cargo build, which belongs in
background-warmup), and Go (go mod download). Lockfile-strict commands
table makes the idempotency story explicit. Cross-links to Background
warmup, Declarative envs, Cleanup on remove, Sharing caches, and the
shared-mutable-state anti-pattern.
* docs(recipes): write background-warmup pattern
Pattern recipe for detached prebuild work using background: true. Covers
Rust cargo build (per-worktree, multi-package scoping), Go build cache
priming (shared cache safety), Vite optimizer warmup with root:, Gradle
daemon + deps, and sccache/ccache prime as a cross-worktree win.
Cancellation semantics (SIGTERM on worktree remove) and the codegen
warning (codegen is correctness, not optimization) are explicit.
Cross-links to toolchain-bootstrap (the upstream install), job
orchestration (background/needs/parallel), sharing-caches, and
shared-mutable-state.
* docs(recipes): write env-vars-and-secrets pattern
Pattern recipe for per-worktree env vars and secrets. Absorbs the
existing by-tooling/direnv content (seed-from-template + direnv allow)
and broadens to vault lookups (1Password/Bitwarden/AWS Secrets Manager),
sops + age for committed encrypted secrets, mise [env] declarative
config, per-job env: for scoped values, and a deterministic
branch-name-to-port allocation. Idempotency section flags the
must-guard cp pattern. Anti-patterns link to the secrets-in-hooks
reference.
* docs(recipes): write declarative-envs pattern
Pattern recipe for mise/asdf/nvm/pyenv as the declarative alternative or
complement to imperative hooks. Absorbs the existing by-tooling/mise
content and broadens to asdf, nvm, pyenv, mise [env]/[tasks], and
devbox/nix-direnv. Division-of-labor table makes the
declarative-vs-imperative split explicit. Warning against putting
long-running setup in mise [tasks] (which doesn't fire on worktree
create). Cross-links to env-vars-and-secrets, toolchain-bootstrap, and
CI parity.
* docs(recipes): write services-with-ports pattern
Pattern recipe for booting docker-compose stacks per worktree without
collision. COMPOSE_PROJECT_NAME for naming, deterministic
branch-name-to-port-range hashing for ports, symmetric pre-remove
teardown with -v to leave nothing on disk. Variants for compose
profiles, podman, native processes, adopting an existing stack, and
multi-file compose. Idempotency note distinguishes safe `up -d` from
destructive `down -v`. Anti-patterns flag external volumes
(cross-link), hardcoded ports, and skipping pre-remove.
* docs(recipes): write cleanup-on-remove pattern
Pattern recipe for the symmetric pre-remove half of lifecycle
automation. Compose teardown with -v --remove-orphans, native
processes via PID file, fallback cleanup by port, external registry
deregistration, and per-removal-reason logic via
DAFT_REMOVAL_REASON. Idempotency table flags which cleanups need
|| true. Warning against putting cleanup in worktree-post-remove
(too late — worktree directory is gone). Move-hook gating via
DAFT_IS_MOVE so cleanup doesn't fire during daft rename.
* docs(recipes): write ci-parity pattern
Pattern recipe for running daft.yml in CI with the same hooks fired on
local worktree creation. GitHub Actions, GitLab CI, generic-shell
variants. DAFT_NONINTERACTIVE + git daft-hooks trust --all for trust in
non-interactive contexts. skip:/only: with env CI=true for splitting
local-only and CI-only jobs. CI-specific env vars and secrets via the
provider's secret store. Layer caching with a daft-aware base image and
DAFT_SKIP_TAGS to bypass already-installed work. Warning against
running pre-remove hooks in CI.
Replaces the by-scenario/ci-integration stub.
* docs(recipes): write rust-binary walkthrough
End-to-end walkthrough threading toolchain-bootstrap (cargo fetch) and
background-warmup (cargo build) for a Rust binary project. Steps build
incrementally: minimal fetch → add warmup → scope in workspace →
optional sccache for cross-worktree sharing → verify with daft hooks
log show. Final daft.yml is two jobs, four lines of logic. "What you
got" section frames the before/after value.
* docs(recipes): write node-monorepo-services walkthrough
End-to-end walkthrough threading toolchain-bootstrap (pnpm install with
shared store), env-vars (per-worktree port allocation),
services-with-ports (compose with COMPOSE_PROJECT_NAME and per-worktree
ports for postgres/redis/minio), migrations as a hook step, and
cleanup-on-remove (down -v --remove-orphans). Optional profiles +
only:/skip: for opt-in heavy services. Full final daft.yml at the end.
Replaces the by-scenario/monorepo.md anchor recipe (deleted) — the new
walkthrough subsumes its content and adds services + ports + cleanup.
* docs(recipes): write python-uv-secrets walkthrough
End-to-end walkthrough threading declarative-envs (mise pinning Python
+ ruff + uv versions + committed [env] defaults), toolchain-bootstrap
(uv sync --frozen), and env-vars-and-secrets (sops + age, decrypted
to .env, loaded via direnv dotenv). The interesting bit: layering
declarative tool config and committed env defaults with hook-fetched
secrets. Final daft.yml has 4 jobs with explicit needs:; plus
mise.toml, .sops.yaml, secrets.enc.env all committed.
* docs(recipes): retire by-* taxonomy, sweep cross-links, rewrite hub
Deletes the 11 remaining by-tooling/by-language/by-scenario recipe files
(content was either folded into patterns/walkthroughs in prior commits
or was a stub with little salvageable content). The fork-workflow stub
is dropped entirely — it was always a worktrees-pillar topic, and
/worktrees/multi-remote covers it.
External cross-links rewritten to new homes:
- /recipes/by-scenario/monorepo → /recipes/walkthroughs/node-monorepo-services
(about/faq.md, worktrees/layouts.md)
- /recipes/by-tooling/{mise,direnv} → /recipes/declarative-envs +
/recipes/env-vars-and-secrets (worktrees/index.md, getting-started/quick-start.md)
- /recipes/by-scenario/fork-workflow links removed; replaced with
/recipes/?pillar=worktrees (worktrees/multi-remote.md, worktrees/merging.md)
Cloudflare _redirects gain three targeted rewrites and three catch-alls
so existing /recipes/by-* URLs land on a useful new page.
Hub rewrite (docs/recipes/index.md): reorganized around walkthroughs +
patterns (grouped by lifecycle stage: setup / steady state / teardown)
+ references shelf. Vue script reads the new loader's `kind` field and
groups dynamically. ?pillar= filter still works.
* docs(recipes): write reference pages
Three reference pages supporting the patterns:
- sharing-caches.md: per-tool answers for what's safe to share between
worktrees (pnpm store, cargo registry, Go caches, ccache/sccache) vs
not (node_modules, target/, .venv/, build dirs).
- anti-patterns/shared-mutable-state.md: the unsafe-sharing flip side
with concrete failure modes (silent target/ corruption, mystery test
failures from shared node_modules, pgdata corruption from shared
volumes).
- anti-patterns/secrets-in-hooks.md: how secrets accidentally land in
committed hooks (hardcoded keys, echoed in logs, env: on backgrounded
jobs visible in ps, baked into images, .env decrypted output that's
not gitignored) and what to do instead.
* docs(recipes): escape \${{ }} in secrets-in-hooks anti-pattern
VitePress / Vue parses {{ }} as interpolation even inside inline
\`code\` spans. The GitHub Actions \${{ secrets.API_KEY }} mention in
the prose was triggering "Cannot read properties of undefined (reading
'API_KEY')" during SSR. Wrapping in <span v-pre> opts the inline code
out of Vue parsing.
Code-block occurrences (in ci-parity.md) are unaffected — fenced code
blocks aren't Vue-parsed, only inline code.
Builds clean against ignoreDeadLinks: false.
* docs(hooks): cross-link Recipes → CI parity from Hooks pillar
Adds the Hooks-pillar end of the CI-parity cross-reference (per the
locked decision: CI parity lives in Recipes, with a pointer from Hooks).
- hooks/index.md Where-to-next: new bullet linking to /recipes/ci-parity
- hooks/yaml-reference.md: new "Running these in CI" section pointing
at /recipes/ci-parity for the GitHub Actions / GitLab / generic
patterns and the local-only skip: { env: { CI: "true" } } tip
* docs(recipes): rewrite toolchain-bootstrap with motivation-driven vignette
Lead with a concrete starting state (Node monorepo with bin/setup.sh,
forgotten-after-git-pull pain) and frame the recipe as a state diff
against that baseline. Move the failMode tuning out of the intro into
a dedicated subsection. Replace the Composes-with / See-also / Anti-
patterns trio with a single Where-to-next of three prioritized links.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* docs(recipes): rewrite background-warmup with motivation-driven vignette
Open with the Rust workspace + 4-minute first-build pain that motivates
reaching for a warmup, then frame the recipe as the second job that gets
added to the post-create hook (needs: the install). Reconcile the
default (--workspace) with the rust-binary walkthrough's choice. Trim
cross-link density and consolidate cancellation / no-critical-work
guidance into Idempotency & safety.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* docs(recipes): rewrite declarative-envs with vignette + own mise [env]
Open with the polyglot version-drift vignette (.nvmrc + .python-version
+ FROM rust:1.78 in three places). Make this page the canonical home
for mise [env] (non-secret defaults) — env-vars-and-secrets shrinks its
section to a one-liner pointer in a follow-up commit. Replace the asdf
"precursor" history with usable plugin-install guidance. Drop the
devbox/nix-direnv one-liner (unfair to summarize in one line; users on
those projects need their own docs).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* docs(recipes): rewrite env-vars-and-secrets with vignette + scope reduction
Open with the Slack-DM'd .env / committed-DATABASE_URL vignette that
motivates the pattern. Cleanly cede mise [env] coverage to declarative-
envs (one-liner pointer instead of a full subsection). Variants now
cluster strictly by source (vault / sops / per-job env). Move per-
worktree derived values (branch-port hash) out of variants into its
own subsection — it's a derived-value pattern, not a source variant.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* docs(recipes): rewrite services-with-ports + fold port allocation into minimal
Open with the "5432 already in use" / two-parallel-features vignette
that motivates per-worktree compose stacks. Fold port allocation INTO
the Recipe section so the minimal config is self-contained — no more
forward references to undefined PORT_POSTGRES vars. Variants now
strictly by runtime (compose profiles, podman, native, multi-file,
adopt-existing). Defer the deeper teardown story to cleanup-on-remove
rather than re-deriving it inline.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* docs(recipes): rewrite cleanup-on-remove with vignette + dedupe warning
Open with the housekeeping vignette: 23 stale containers, 14 leaked
volumes, 12 GB consumed, a port held by a process from a deleted
branch. Consolidate the post-remove-vs-pre-remove warning to a single
canonical spot inside Idempotency & safety (was stated 3x previously).
Variants now cluster strictly by resource type (compose / PID file /
port / external registry / per-removal-reason). Move "run in parallel"
note out of variants — it's default behavior, not a variant.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* docs(recipes): rewrite ci-parity with vignette + skip-warmup decision rule
Open with the daft.yml/test.yml drift vignette (protoc added in one
place, two-day CI breakage). Variants now strictly by vendor (GHA /
GitLab / generic). Move skip-conditions, CI-specific env vars, and
layer caching out of variants into their own subsections. Add explicit
decision rule for warmups: skip when the cache is per-worktree, run
when it primes a shared cache the test step benefits from.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* docs(recipes): rewrite rust-binary walkthrough + reconcile with pattern
Open with the project-shape vignette (three-crate workspace, ~200 deps,
4-minute first build). Steps now defer to patterns rather than
re-derive them — toolchain-bootstrap (cargo fetch) and background-
warmup (cargo build --workspace) are linked, not re-explained.
Reconcile final daft.yml with background-warmup pattern: both show
--workspace + sccache, with the trade-off discussed in one place.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* docs(recipes): rewrite node-monorepo-services walkthrough + Step 2/3 fix
Open with the project-shape vignette + the team's current 5-step
README ritual that motivates daft adoption. Move the DATABASE_URL
warning out of Step 3 — DATABASE_URL is now written into .envrc by
Step 2 alongside the ports, with the deferred-expansion (\$PORT_POSTGRES)
explained inline. Drop the tacked-on Step 6 (compose profiles); link
out to the services-with-ports pattern instead. Final daft.yml is
internally consistent — no warning-as-correction.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* docs(recipes): rewrite python-uv-secrets walkthrough + extract team prereq
Open with the rotated-DB-password / Slack-DM vignette that motivates
sops adoption. Extract the team-onboarding sops/age key generation
into a Prerequisite section before any per-worktree daft step — sops
is one-time team setup, not per-worktree work, and mixing them
confused the previous flow. Per-worktree steps now focus strictly on
hook config: mise install, uv sync --frozen, sops decrypt, direnv
load.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* docs(recipes): drop irrelevant team-size and timing details from vignettes
The recipes don't change at "two months old" vs "a year old," at "three
engineers" vs "seventy," or at "last week" vs "last quarter." Those
details are creative-writing color, not motivation.
Strip them out across all 10 pages: drop repo age, team headcount,
specific weeks/months, and other outside details that don't shape the
setup. Keep what actually motivates the recipe — failure modes,
ritual shape, the recurrence (not the cadence) of pain.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* docs(recipes): catch line-wrapped "two engineers" the prior pass missed
The earlier replace_all matched on the single-line phrase. Prettier had
wrapped this instance across two lines, so it slipped through. Fix the
last remaining "Two engineers on parallel features just work" in the
node-monorepo-services walkthrough.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* docs: internal style guide for writing recipes
Captures the writing principles we worked out during the recipes IA
restructure and narrative rewrite: motivation-first vignettes (with
the three-property bar), single-axis variants, self-contained minimal
recipe, walkthroughs that cite patterns rather than re-derive them,
and the cap on cross-link density.
Lives at docs/WRITING-RECIPES.md and excluded from the VitePress build
via srcExclude (alongside the existing WEBSITE-BOOTSTRAP.md and
HISTORY.md). Confirmed absent from dist/.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* docs(recipes): drop transitive dep counts from Rust vignettes
Same principle as the team-size/age cleanup: the recipe doesn't change
at 50 vs 200 vs 500 transitive deps. What's load-bearing is "the build
is slow" with a concrete time cost (4 minutes). The dep count is the
cause, not a recipe constraint.
Now matches the WRITING-RECIPES.md guidance ("dep counts" listed as a
non-belonger in the vignette).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* docs(recipes): fix malformed ::: directive blocks across 9 pages
The closing ::: on a content line doesn't terminate the VitePress
container, so the next ## heading gets swallowed into the warning/tip
box. The user-visible symptoms were Step 4 missing from the TOC in
python-uv-secrets and Step 3 missing in rust-binary; the same bug
existed in 7 more pages where the swallowed content was a paragraph
or table, less obvious but still wrong.
Standardize all 9 directive blocks: title on its own line, content
separate, closing ::: alone on its line. Add a "VitePress directive
format" subsection to WRITING-RECIPES.md so this doesn't recur.
Files fixed: background-warmup, ci-parity, cleanup-on-remove,
declarative-envs, env-vars-and-secrets, services-with-ports,
walkthroughs/node-monorepo-services, walkthroughs/python-uv-secrets,
walkthroughs/rust-binary.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* docs(recipes): blank-line ::: directive format that survives prettier
Previous fix put the closing ::: on its own line, but prettier
(proseWrap: always) joined it back to the content line on commit, so
the user-reported heading-swallow bug came back. The fix that survives
both VitePress and prettier is blank-line separation:
::: warning Title
Content paragraph.
:::
Verified by running prettier --write on the result; no rejoining.
Update WRITING-RECIPES.md with the prettier interaction explained, so
the next person doesn't waste a debugging cycle on this.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* docs: convert WRITING-RECIPES doc into a project skill
Move the recipe-writing style guide from docs/WRITING-RECIPES.md to
.claude/skills/writing-recipes/SKILL.md, following the existing
convention (.claude/skills/writing-documentation-with-diataxis/ has
the same shape: full content with frontmatter, no parallel doc).
The frontmatter description is tuned to auto-activate when an agent
is creating or revising recipe pages. Humans still discover it via
.claude/skills/ — same as the diataxis skill.
Drop the docs/WRITING-RECIPES.md file and its srcExclude entry.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* docs(recipes): add adopting-from-direnv pattern
Bridges the most common adoption path: teams already using direnv reach
for daft to cover the slow rituals (install, services, codegen,
cleanup) that direnv was never meant to handle. Three Variants by what
direnv already manages — tool versions via use mise, secrets via
dotenv, and PATH_add bin — each naming what daft skips because direnv
already does it.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* docs(recipes): split services-with-ports Variants into starting-state and runtime axes
Promotes the buried 11-line 'Adopting an existing stack' sub-bullet to a
first-class adopt-existing variant alongside green-field, on a new 'by
starting state' axis. Adopt-existing now distinguishes two cases —
compose.yaml already uses env-var ports (drops into green-field Recipe
as-is) vs hardcodes ports (gets container/network isolation but host
ports still collide; worthwhile adoption with a roadmap to graduation).
The runtime variants (compose profiles, podman, native processes,
multi-file) move into their own 'by runtime' Variants section, leaving
each axis internally single-axis as the writing-recipes skill prescribes.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* docs(recipes): add migrating-from-setup-sh walkthrough
Threads toolchain-bootstrap, services-with-ports, background-warmup,
and cleanup-on-remove to dismantle the typical bin/setup.sh ritual one
section at a time. Final state: setup.sh deleted, README's getting-
started shrinks to two lines, daft.yml is the source of truth, and the
cleanup-on-remove pre-remove hook handles teardown the imperative
script never had.
Honestly handles the parallel-worktree case: COMPOSE_PROJECT_NAME
gives container/network/volume isolation, but if compose.yaml hardcodes
ports the host-side still collide. Points readers at services-with-
ports adopt-existing for the upgrade path rather than glossing over it.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* docs(recipes): fix migrating-from-setup-sh formatting nits
Two prettier-induced regressions in the just-landed walkthrough:
- The 'Patterns we'll thread' list used '+' as prose conjunction
between two backtick-quoted commands; prettier reflowed it onto an
indented next line, which markdown then renders as a sub-bullet.
Replaced with 'and' so prose stays prose.
- The README example used a 4-backtick fence around a markdown block
that itself contains a 3-backtick bash fence. Prettier added a stray
trailing 4-backtick that broke the close. Simplified to inline prose
describing the two-line README content rather than re-rendering it.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* docs(recipes): add github-actions walkthrough
Threads ci-parity for a real .github/workflows/ci.yml that consumes a
daft-managed project. Three structural steps (install daft, trust
hooks, run worktree-post-create); the cache/install/build/codegen work
disappears because daft hooks run does it. Covers the warmup-skip-in-
CI decision rule from ci-parity inline rather than re-deriving it.
Final workflow ~25 lines, replacing a 12-step duplicate that drifted
from daft.yml whenever someone added a dep to one and not the other.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* docs(recipes): add editor-integration pattern
Vignette: Python team's gitignored .vscode/settings.json forces every
fresh worktree through a 'Select Interpreter' ritual. Recipe seeds
.vscode/settings.json from worktree-post-create with an idempotent
'write only if absent' guard, so customized settings survive but
fresh worktrees get sane defaults. ${workspaceFolder} keeps the
seeded JSON portable across worktrees.
Variants by editor: VS Code other languages (Rust, Node), IntelliJ
(honest about why .idea/ XML isn't safe to seed from a hook — points
at one-time setup script instead), Helix/LSP-generic (committed
pyproject.toml [tool.pyright] block, no hook needed).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* docs(recipes): add symptom-keyed troubleshooting reference
Vite-shaped reference page: H2 per symptom (verbatim search wording),
1-3 paragraphs per entry naming the root cause and linking to the
canonical pattern. Six symptoms covering background-warmup,
trust-and-security, env-vars-and-secrets, cleanup-on-remove, ci-parity.
Cross-linked with /about/troubleshooting at the top of each page so a
reader landing on the wrong one finds the other. The about page covers
general daft (install, layout); the recipes page covers hook
configuration and recipe-specific symptoms.
Registered as a reference via REFERENCE_FILES (recipe-list.ts) so it
shows in the recipes hub's References section automatically and not
as a pattern.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* docs(about): add Recipe glossary entry
Distinguishes a cookbook recipe (a documented pattern in docs/recipes/)
from a daft.yml job entry, which some ecosystems (notably just) call a
'recipe' in their own config sense. Cheap insurance now that the
research surfaced the naming collision.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* docs: keep writing-recipes skill in sync with recipe structural changes
Two paired changes:
1. CLAUDE.md gains a guideline alongside the existing SKILL.md (agent-
facing) bullet: structural changes to how recipes are written must
land in .claude/skills/writing-recipes/SKILL.md. Divergence between
the skill and the recipes is the silent way the conventions rot.
2. The writing-recipes skill itself gains a 'Show files; don't describe
them' section. Verbal descriptions of structured file content
(.envrc, mise.toml, daft.yml) are hard to follow; the reader has to
reconstruct the file from prose. Show the file as a code block after
a sentence or two of intro. Trees describe layout; code blocks
describe content; use both.
The verification checklist gains a matching item.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* docs(recipes): show files instead of describing them in three vignettes
Audit pass following the new writing-recipes skill rule: file contents
go in code blocks, not prose.
- toolchain-bootstrap: bin/setup.sh content was a verbal one-liner;
now a labeled bash block showing the actual script.
- declarative-envs: three pinning files described in a bullet list;
now a single code block with file path and content per line, which
reads cleaner for one-line files than three separate blocks.
- ci-parity: daft.yml's six jobs and test.yml's five steps were
described verbally as 'six jobs' and 'five steps in slightly different
order'; now both files are shown abridged so the reader sees the
parallel structure (and the divergence to come) in one glance.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* docs(recipes): rewrite adopting-from-direnv direnv-pure and add adopting-from-mise
Two paired changes:
1. The original adopting-from-direnv vignette modeled direnv and mise
as a hybrid (.envrc using 'use mise', mise.toml pinning versions
activated by direnv). That misrepresents the real choice — they
are alternatives, not complements. Rewrite the vignette to a
direnv-pure setup: .envrc with dotenv + PATH_add, tool versions
managed externally. Drop the use-mise variant; add a layout-style
variant ('layout python', 'layout ruby') that's an actual
direnv-only pattern.
2. Add the missing companion adopting-from-mise. Same shape, mise-only
starting state. mise.toml shown as a code block instead of described
verbally. Variants by what mise is already managing: [tools], [env],
[tasks].
Both vignettes now follow the writing-recipes 'show files; don't
describe them' rule that landed in the previous commit.
Where-to-next on each page links to the other (sibling adoption
recipes for the alternative tool).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* docs(recipes): add migration walkthroughs between direnv and mise
Two paired walkthroughs covering both directions, since teams pick
one or the other and may want to switch.
migrating-from-direnv-to-mise (the more common direction): replace
scattered .nvmrc / .python-version + .envrc exports with one
mise.toml. Two options for secrets — keep direnv just for dotenv
(Option A) or use mise's _.file (Option B) — with a decision rule
for picking between them. daft.yml stays untouched.
migrating-from-mise-to-direnv (the rarer reverse direction):
replace mise.toml with .envrc + per-language version managers
(nvm, pyenv). Honest about being uncommon up front; the trade-off
section names the cost (more tools per dev) so the reader can
decide if it's worth it. daft.yml stays untouched here too.
Both walkthroughs link to each other in 'Where to next' and to the
respective Adopting-from-X steady-state recipe.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* docs(recipes): remove direnv↔mise migration walkthroughs
These migrations are between env tools, not to/from daft — outside
daft's responsibility. Mise's and direnv's own docs cover the
direct-tool-migration story better than daft's docs ever could.
Adoption recipes in the to/from-daft direction stay (adopting-from-
direnv, adopting-from-mise, migrating-from-setup-sh) and are about to
be regrouped under a dedicated Adoption section in the next commit.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* docs(recipes): group adoption recipes under a dedicated section
Adoption-shaped recipes lived in two places — adopting-from-direnv and
adopting-from-mise under Patterns: Setup, migrating-from-setup-sh under
Walkthroughs. They form a natural group: 'introducing daft into an
existing setup.' Promote them to their own Adoption section in the hub
and the sidebar, and exclude them from their original groupings so they
appear once each.
The hub intro is restructured to name the three kinds of recipe pages
(adoption, walkthrough, pattern) so readers see the taxonomy
immediately.
The new Adoption section lands first in both the hub and sidebar — it's
the right entry point for new daft users.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* docs(recipes): add layering-direnv and layering-mise patterns
The missing direction in the adoption family: a team running daft
alone decides to add direnv (or mise) on top. Symmetric inverses of
adopting-from-direnv and adopting-from-mise.
Each pattern shows three landing changes — config file at the root
(.envrc or mise.toml), per-dev shell-rc activation snippet, and
optional daft.yml updates. Variants by what the new tool covers
(just env loading vs env + PATH; just tools vs tools + env vs tools +
env + dotenv).
Both pages cross-link to each other ("the alternative if you'd
rather have X") and to the corresponding adopting-from-X steady-state
recipe.
Registered in the Adoption section in the hub and sidebar.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* docs(recipes): note daft's pair-with-direnv-or-mise recommendation in adoption guides
Add a tip block at the top of each adoption guide making explicit that daft
recommends pairing with direnv or mise rather than substituting for them, and
pointing to the corresponding layering guide for readers coming from a
daft-only starting state.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* docs(about): remove networking-roadmap stub page
Delete the networking-roadmap page along with its sidebar entry, the about
hub link, and the two glossary rows ("Networking" / "Repo catalog") that
pointed to it. Add a 301 redirect from the old URL to /about/why-daft, where
the networking pillar is still acknowledged in the thesis.
The page was a hard stub for a feature that has not been built — its content
lives in #357 and will return as docs alongside the implementation, per
"docs and features enter together."
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* docs(about): drop stale #330 reference from comparison
The "vs GitHub Actions PR checks" parenthetical said the comparison would
be fully realized "once #330 and #468 ship." #330 (merge feature) has
shipped — drop it from the gate, leaving only #468 (commit-stage hooks)
as the remaining surface that completes the picture.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* docs: update stale references downstream of pillar IA
The IA restructure retired /guide/* and /cli/* in favor of pillar pages
(/worktrees/, /hooks/) and a unified /reference/. Update the references
that pointed at the old structure:
- README.md: hooks-guide and Agent Skill links → canonical daft.avihu.dev
URLs at /hooks/ and /reference/agent-skill (the old links also used the
pre-subdomain-move host avihu.dev/daft/...).
- CLAUDE.md: "Documentation Site" path map updated to reflect pillar
layout (worktrees/, hooks/, recipes/, reference/, about/).
Man pages already verify clean (mise run man:verify) — they're generated
from clap Args structs and were untouched by this branch.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* fix(xtask): point CLI-doc generator at /reference/output-formats
The IA restructure moved docs/guide/output-formats.md to
docs/reference/output-formats.md and rewrote the cross-references in the
committed docs/cli/*.md files accordingly. The xtask source that
generates those files was missed, so xtask-test (which regenerates and
diffs) failed on PR #485.
Update the hardcoded link in render_structured_output_section() to the
canonical site path /reference/output-formats so regeneration is a no-op
against the committed tree.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* docs(recipes): replace hardcoded ADOPTION_LINKS with kind: adoption frontmatter
Address PR review feedback. The Adoption section in the recipes hub was
gated on a hardcoded ADOPTION_LINKS array in index.md, with no build-time
check that it stayed in sync with the actual files. Adding a new adoption
recipe required edits in three places (file, ADOPTION_LINKS, sidebar) and
silently mis-classified the recipe if any was missed.
Move the classification into per-recipe frontmatter:
- Add "adoption" to the RecipeKind union; let frontmatter kind override
the path-based default in toRecipe.
- Add `kind: adoption` to adopting-from-{direnv,mise}, layering-{direnv,
mise}, and walkthroughs/migrating-from-setup-sh.
- Replace `ADOPTION_LINKS.includes(r.link)` with `r.kind === 'adoption'`
in the hub's three computed properties.
- Have recipes.data.ts re-export Recipe directly so the public Data type
stays in lock-step with RecipeKind.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
daft mergeto a GitHub-PR-style flow: four merge styles (--merge,--squash,--rebase,--rebase-merge), two cleanup outcomes (--remove-branch,--keep-branch), and--set-defaultto persist the resolved choices togit config --local.failMode: warnoverride; post-merge fires only on successful merges (not on squash-staged-only, not on conflict).--ff/--no-ff/--ff-only,--squash(toggle),-r/-rb/--and-branch, anddaft.merge.{ff,squash,postMerge.removeSourceWorktree,postMerge.alsoRemoveSourceBranch}are all gone. Pre-1.0 surface, no compat shims (perfeedback_breaking_change_marker.md).Merging X → Y (style · cleanup [· saving as default])), per-source cleanup heading before eachworktree-pre-removebox,on: <target>segment in the hook-box title for worktree-scoped phases, and theUpdated repository defaultsnotice moved to a trailing footnote.tests/manual/scenarios/merge/), 1695 unit tests passing, clippy clean, man pages regenerated. Spec + plan committed underdocs/superpowers/.Fixes #330.
Test plan
CI / automated:
mise run fmt:check— cleanmise run clippy— zero warningsmise run test:unit— 1695 passingmise run test:manual -- --ci— all merge scenarios green (308 steps)mise run man:verify— man pages up to dateManual smoke (each on a fresh sandbox repo):
--mergeproduces a merge commit on FF-able input;--squash --no-editproduces single-parent squash;--rebaseyields linear history with no merge commit;--rebase-mergeyields linear ancestors plus a final merge commit. Verify withgit log --graph --oneline.--remove-branchremoves worktree + branch; each with--keep-branchpreserves both; each with no flag honorsdaft.merge.cleanup.--set-defaultround-trip: run with flags, confirmgit config --get daft.merge.styleanddaft.merge.cleanuppersisted; subsequent flagless invocation picks them up; defaults footnote prints after the success line.Merging X → Y …at top,Cleaning up X (worktree, local branch)before the box, hook-box title carriesworktree-pre-remove on: X. Multi-source octopus produces a separate box per source with the righton:target each time.failMode: warnoverride allows the merge to proceed and post-merge to fire.daft merge --continueafter resolving (note documented limitation:--continuedoes not run cleanup);daft merge --abort; cross-worktree merge via--into <other>.--remove-branchwithdaft.branch_delete.delete_remote=truealso removes the source remote-tracking branch.daft completions <shell>and confirm tab completion offers--merge,--squash,--rebase,--rebase-merge,--remove-branch,--keep-branch,--set-default(and no longer offers the removed legacy flags).🤖 Generated with Claude Code