stax 0.50.0

Fast stacked Git branches and PRs
Documentation
# Lessons

- When changing the `stax co` UI, match the `stax ls` visual language (colors, tree/indentation) and confirm it visually. Do not ship a redesign without verifying the output looks like the `ls` tree and that selection emphasis is obvious.
- Interactive prompts rendered on `stderr` must style item text against `stderr` and gate interactivity on `stdin` + `stderr`; shell integration can capture stdout for `--shell-output` while dialoguer prompts still run on stderr.
- TUI changes must be checked at standard terminal widths (around 80 columns); keep one-line summaries and footers compact, and prefer a single contextual action over listing every shortcut.
- If interactive lists scroll the terminal on navigation, clear and position the cursor before invoking the dialog to avoid rendering into the lower viewport.
- Bare commands that default to a TUI/dashboard must gate on both `stdin` and `stdout` being terminals and otherwise fall back to help or a non-interactive view; never assume `st`/`st wt` is launched from a full TTY.
- TUI/dashboard launch checks must also preflight the terminal input backend before entering the alternate screen; `isatty` alone is insufficient under constrained environments such as low file-descriptor limits.
- TUI/dashboard startup must render from cheap repo data first and defer expensive per-item Git or tmux inspection until after the first paint; do not block initial draw on full worktree/branch status scans.
- TUI/dashboard actions that shell back into `stax` must exit the alternate screen and drop live app/repo state before spawning the nested command; running child `stax` commands from inside an active dashboard can hit `EMFILE` in large repos.
- When adding or changing CLI commands/flags, update both `README.md` and `docs/` command references in the same change and verify parity against `stax --help` before marking docs complete.
- When a feature has both command docs and workflow guides, keep one canonical docs page for the command surface and have workflow pages link back to it instead of duplicating subcommand semantics.
- Shell integration snippets that define common command names (`stax`, `st`, `sw`) must clear conflicting aliases first; zsh expands aliases while parsing function definitions and can abort shell startup otherwise.
- Shared bash/zsh shell snippets must use shell-specific command lookup (`whence -p` in zsh, `type -P` in bash) when they need the real binary path; bash-only flags in shared wrappers can break every `st`/`stax` invocation on zsh.
- Shared bash/zsh shell snippets must not use zsh-special parameter names like `path` for local scratch variables inside wrappers; localizing `path` in zsh also localizes `PATH` and breaks nested command lookup.
- `shell-setup --install` must replace legacy inline/eval shell integration blocks instead of appending a new source line, and later runs should refresh existing generated shell-setup files after upgrades; stale pasted wrappers or stale generated snippets both leave users with duplicate or broken shell behavior.
- Shell integration should special-case only commands that require shell-side directory changes (`wt go/create`, `lane`, and removing the current worktree); route all other commands straight to the binary without injecting `--shell-output`.
- Commands intended for first-run/setup flows (for example `shell-setup`) must bypass repo initialization in `src/cli.rs`; otherwise running them outside a configured repo can accidentally trigger `init` and break shell startup or onboarding.
- Agent-launching commands must not blindly inherit the global `ai.model` setting across different CLIs; only an explicit per-command `--model` should override the selected agent's own default.
- When sync reparents children off merged branches, never clear `parent_branch_revision`; preserve the old-base boundary (or merged parent tip) so restack can run `git rebase --onto <new> <old>` and avoid replaying already-integrated commits.
- Integration tests that shell out to `git`/`stax` must be hermetic: strip GitHub token env, user-specific `stax` env such as `STAX_SHELL_INTEGRATION`, and force `GIT_CONFIG_GLOBAL`/`GIT_CONFIG_SYSTEM` to null so contributor-specific git config and shell setup cannot change behavior or add large runtime overhead.
- Integration tests that shell out to the compiled `stax` binary must resolve a path that exists at runtime; prefer helper logic that falls back from `CARGO_BIN_EXE_stax` to the sibling `target/.../stax` binary so `nextest` and Docker runs stay stable.
- Tests that must run outside any Git repository must not use temp dirs rooted under `STAX_TEST_TMPDIR`/`TMPDIR` inside the workspace; `git discover` walks parent directories, so those fixtures can accidentally execute inside the repo during `make test-native`.
- For full-suite test runs, use `make test` or `just test` (never `cargo test`); on macOS the default should use Docker for performance and consistency.
- Integration-test shims for external CLIs must track the option shapes used in production code; when tmux launch commands add flags such as `-c` or `new-window` flows, update the fake `tmux` script in the same change.
- Amend-style commands must detect zero-ahead tracked branches and avoid rewriting inherited parent commits; when the user supplies a message, prefer creating the first branch-local commit instead of amending shared history.
- Commands that remove the current worktree must resolve any follow-up repo paths before deletion; once the active worktree directory is gone, later `git` subprocesses can fail with ENOENT if they still rely on the deleted cwd.
- Branch-cleanup paths that may encounter linked worktrees must verify refs with `git show-ref`, prefer `git branch -D` plus post-delete existence checks over libgit2-only deletion, and handle both sides of the handoff: the merged branch may still be checked out elsewhere, and the fallback branch you need to checkout may already be owned by another worktree.
- When a linked worktree blocks a destructive action, do not stop at a raw Git/libgit2 error or a generic "checked out elsewhere" note; print the owning worktree and concrete recovery commands that are actually runnable: use a unique `st wt rm <selector>` target (prefer the checked-out branch over a non-unique basename), and fall back to `git -C <path> switch --detach` whenever trunk is already checked out in another worktree.
- Branch-cleanup prompts and summaries must distinguish deleting branch refs from deleting live worktree directories; if `sync` will remove a safe linked worktree, say that up front, and if it keeps the lane, explain why and point to `st wt cleanup` / `st wt rm ... --delete-branch`.
- Dirty-worktree removal must use the same force/blocker policy across `wt rm`, `wt cleanup`, and `sync`: if the user confirmed "remove anyway" or passed `--force`, the eventual `git worktree remove` call must also be forced, and prompts must not promise a removal path that still falls through to a non-forced Git command.
- When routing to an existing worktree, preserve a unique selector such as the worktree path or checked-out branch; never round-trip through the display basename because different worktrees can share the same leaf directory name.
- Branch-switching commands in very large repos must drop live libgit2 repository handles before spawning `git checkout` or writing prev-branch refs; use path-based `git` subprocesses for the final handoff so checkout still works under file-descriptor pressure.
- When a command needs to reopen a repository after an FD-heavy libgit2 phase, reopen from a saved repo path (`git_dir`/worktree path) instead of rediscovering `.`; `Repository::discover(\".\")` can fail under `EMFILE` even when the repo path is already known.
- Stack/branch graph traversal in user-facing commands must be iterative and cycle-safe; do not recurse over metadata graphs that can be deep or corrupted by local refs.
- For stack-merge flows that delete merged branches, always rebase and retarget descendant branches/PR bases before cleanup; deleting a base branch first can auto-close descendant PRs on GitHub.
- Any descendant-rebase path (`merge`, `merge --when-ready`, `restack`, `upstack restack`, `sync --restack`) must preserve provenance boundaries (`parent_branch_revision` / old parent tip) and use provenance-aware rebase logic; plain `git rebase <trunk>` will replay already-integrated parent history after squash merges.
- Configure explicit connect/read/write timeouts for GitHub API clients (and other network clients); never rely on library defaults for long-running CLI flows where silent waits look like hangs.