Skip to content
7 changes: 7 additions & 0 deletions .claude/skills/release/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -145,11 +145,18 @@ awk '/^## /{if (f) exit; f=1} f' CHANGELOG.md \

**Calibrate against the ceiling, not against the last release.** Length ratchets: each release is drafted beside the previous section, and an abstract rule loses to a concrete neighbouring exemplar every time. Entries grew from 49 to 101 words on average across five releases while this skill said "be brief" throughout. Read the previous section for what it drifted to, then ignore it and write to the ceiling.

**Entries describe.** An entry records what the old behavior was and what it is now. Give imperative guidance only for a material issue that affects a sizable share of users, which is rare; otherwise remediation belongs in the PR description.

**No editorial framing.** Describe what changed, not what was wrong with the previous decision in subjective terms. Avoid words like "sledgehammer", "ugly", "noisy", "wrong" applied to past code. State the prior behavior neutrally and the new behavior plainly.

**Good:** "Removed `.pi/` from the default excludes list; users who need it can add it via `[step.copy-ignored]`."
**Bad:** "Removed `.pi/` — a sledgehammer fix from an unrelated debugging session that has no place as a project-agnostic default."

**Name the conditions a bug needed, so the entry carries its magnitude.** A correctness or data-loss entry reads as though it fired for everyone unless it says what it took. These are usually narrow — a non-default git config, a command run from a particular place, a change of a particular shape — and naming each condition lets a reader decide in one pass whether it reached them. Join the conditions with "and": "X set *and* run from Y" is a corner case, while "X or Y" reads as two common triggers. Establish each one by reproducing against the previous release's binary rather than reading it off the diff — a condition assumed is a condition the entry overstates, and overstating a data-loss bug is its own kind of inaccuracy.

**Good:** "`diff.relative` set *and* the command run from a subdirectory the branch's changes sat entirely outside"
**Bad:** "`diff.relative` could hide a branch's changes from the integration check"

### Credit External Contributors

For any changelog entry where an external contributor (not the repo owner) authored the commit, add credit with their GitHub username:
Expand Down
2 changes: 1 addition & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@

### Fixed

- **Diff display config no longer changes what Worktrunk reads**: `color.ui = always` and `diff.external` reached the commit, squash, and summary prompts. Two settings could also hide a branch's changes from the integration check, so `wt remove` deleted the branch without `-D`: `diff.relative` when run from a subdirectory the changes sat outside, and `submodule.<name>.ignore` on a submodule bump. Every diff Worktrunk parses now runs plumbing, which ignores that configuration, and upgrading discards the cached answers. ([#4146](https://github.com/max-sixty/worktrunk/pull/4146), [#4147](https://github.com/max-sixty/worktrunk/pull/4147), [#4181](https://github.com/max-sixty/worktrunk/pull/4181))
- **Diff display config no longer changes what Worktrunk reads**: `color.ui = always` and `diff.external` reached the commit, squash, and summary prompts. Two corner cases could also hide a branch's changes from the integration check, so `wt remove` deleted the branch without `-D`: `diff.relative` set *and* the command run from a subdirectory the branch's changes sat entirely outside, or `submodule.<name>.ignore` set (git config or `.gitmodules`) *and* a submodule bump as the branch's only change. Every diff Worktrunk parses now runs plumbing, which ignores that configuration. ([#4146](https://github.com/max-sixty/worktrunk/pull/4146), [#4147](https://github.com/max-sixty/worktrunk/pull/4147))

- **`wt config update --output <path>` asks before overwriting**: it replaced whatever was there, so `--output ~/.config/worktrunk/config.toml` from a repo with a deprecated project config wiped the user config and reported success. It now prompts, takes `--yes`, and refuses the config being migrated. (Breaking: `--output` naming the config being migrated used to succeed unless the migration dropped `approved-commands`.) ([#4153](https://github.com/max-sixty/worktrunk/pull/4153))

Expand Down
2 changes: 1 addition & 1 deletion CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -243,7 +243,7 @@ No `get_*` — bare nouns follow Rust stdlib convention.

`Repository` caches read-only values via `Arc<RepoCache>` (cloning shares it). What is and isn't cached, the `list_worktrees()` post-mutation invariant, the two storage patterns, and the in-memory-`RepoCache`-vs-persistent-`sha_cache` decision (cheap-and-hot → in-memory get-or-create; expensive → disk; both → in-memory front over disk back): the `# Caching` section in `src/git/repository/mod.rs`.

**A change to what a cached value means bumps `CACHE_EPOCH`** (`src/cache.rs`). The persistent entries are keyed on SHA pairs with no TTL, so the key cannot see the generator's output: an entry written by an older `wt` keeps answering until its key recurs, and on a finished branch that is never. Moving the diffs onto plumbing is the worked example — it left `has-added-changes` entries saying an unmerged branch had no added changes, which is `wt remove` deleting it. So the question belongs to any change that alters what a cached read reports, not just to code inside `cache.rs`; ask it whenever a generator behind one of `sha_cache`'s kinds changes.
**A change to what a cached value means leaves the old answers in place.** The persistent entries are keyed on SHA pairs with no TTL, so an entry written by an older `wt` keeps answering until its key recurs, and on a finished branch that is never. Moving the diffs onto plumbing is the worked example — it left `has-added-changes` entries saying an unmerged branch had no added changes. Nothing invalidates these on an upgrade, deliberately: a cache version is permanent machinery for a transition that has come up once. So when a generator behind one of `sha_cache`'s kinds changes, the PR description says what the old answers get wrong, which is where that trade gets decided.

## Releases

Expand Down
3 changes: 1 addition & 2 deletions docs/src/content/docs/faq.md
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,7 @@ Worktrunk stores repository state, caches, and logs under `.git/`:
| Location | Purpose | Created by |
|----------|---------|------------|
| `git config worktrunk.*` | Cached default branch, switch history, branch markers, custom variables | Various commands |
| `.git/wt/cache/{kind}/*.json` | Cached CI status, the largest PR/MR number seen (sizes the `wt list` CI column), and git command results (merge-tree, integration probes, diff stats, ancestry checks, ahead/behind counts, merge bases), beside a stamp of what those results mean — a worktrunk that disagrees with it discards the tree | `wt list`, `wt merge`, `wt remove` |
| `.git/wt/cache/{kind}/*.json` | Cached CI status, the largest PR/MR number seen (sizes the `wt list` CI column), and git command results (merge-tree, integration probes, diff stats, ancestry checks, ahead/behind counts, merge bases) | `wt list`, `wt merge`, `wt remove` |
| `.git/wt/cache/summary/{branch}/{hash}.json` | Cached LLM branch summaries, content-addressed by diff hash | `wt list --full`, `wt switch` (when `[list] summary = true`) |
| `.git/wt/cache/picker-preview/*.json` | Rendered preview panes for the interactive picker | `wt switch` |
| `.git/wt/logs/{branch}/**/*.log` | Background hook output (nested per branch) | Hooks, background `wt remove` |
Expand Down Expand Up @@ -213,7 +213,6 @@ A branch checked out in a second worktree is retained regardless, `-D` included.

- `wt merge` / `wt step push` — the target branch's checked-out worktree is updated to the merged commits, so a file those commits delete disappears from it, and an ignored file at a path they track is overwritten — the same result a `git merge` run in that worktree would produce. Uncommitted changes at paths the merge doesn't touch stay in place, staged or not; one at a path it does touch refuses the merge upfront, naming the file
- `wt remove` — also stops the removed worktree's `git fsmonitor--daemon` (under `core.fsmonitor=true`), and sweeps `.git/wt/trash/` entries older than 24 hours along with fsmonitor daemons whose worktree no longer exists
- any command reading a cache — discards the cached git-command results under `.git/wt/cache/` when they were written by a worktrunk that computed them differently, so a stale answer can't outlive the code that produced it. Cached CI status and LLM summaries are keyed on what they describe, so they are left alone
- `wt config state clear` — removes all worktrunk data from `.git/` (config keys, caches, markers, hints, variables, logs, stale trash)
- `wt config shell install` — replaces an existing fish or Nushell wrapper file whole, and removes one an older version installed at a previous location (fish `conf.d/wt.fish`, Nushell `<config-dir>/vendor/autoload/wt.nu`), printing each removal; [Files created](/shell-integration/#files-created) names the file each shell gets
- `wt config shell uninstall` — removes the lines that run `wt config shell init` from bash/zsh/PowerShell rc files, and deletes wrapper and completion files carrying worktrunk's content markers (fish `functions/`, `conf.d/`, and `completions/`; nushell `vendor/autoload`). Every removed line is printed
Expand Down
3 changes: 1 addition & 2 deletions plugins/worktrunk/skills/worktrunk/reference/faq.md

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 1 addition & 2 deletions skills/worktrunk/reference/faq.md

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading
Loading