Repository navigation
Stabilize mature interfaces and remove retired config - #3949
Conversation
worktrunk-bot
left a comment
There was a problem hiding this comment.
The stabilization sweep looks complete on the generated pages, but the hand-edited llm-commits.md was missed — it's the primary for two of the six interfaces this PR promotes, and nothing in the sync test catches a badge in prose.
docs/src/content/docs/llm-commits.md still marks both features experimental. Under ## Branch summaries and under ## Appending to the prompt there's a <span class="badge-experimental"></span> on the line after each heading. Per docs/CLAUDE.md's sync taxonomy that file is primary, with the [experimental] copies in skills/worktrunk/reference/llm-commits.md and plugins/worktrunk/skills/worktrunk/reference/llm-commits.md generated from it — so the fix is to drop both spans and regenerate. As it stands, list.md describes LLM summaries as stable while the dedicated LLM page still badges them, and the same split applies to template-append, which the PR de-badges in config.md, dev/config.example.toml, dev/wt.example.toml, and the CommitGenerationConfig doc comment. Happy to push that commit if you want it.
The retired template keys land on a message that doesn't say what happened. A user with commit.generation.template-file now sees User config has unknown field commit.generation.template-file (will be ignored) and their commit prompt silently falls back to DEFAULT_TEMPLATE — the warning reads as a typo report rather than "the key you configured is gone and here's where the contents go". ListTaskTimeout kept a DeprecationKind for exactly this shape (list.task-timeout-ms is no longer used — list.timeout-ms bounds the collect phase), and an UpdateOnly row would warn with that guidance while keeping the test_warning_fires_iff_update_changes invariant intact. I can see the argument for the current choice — leaving the key in place preserves the path so the user can still find their template file — so this is a judgment call rather than a defect; flagging it because the troubleshooting page is the only place the migration path is written down and nothing in the warning points there.
Not approving: the diff edits the wt step prune declaration in src/cli/step.rs, promoting a bulk worktree-and-branch removal command out of experimental. Per .claude/skills/running-tend/references/review-pr.md → Data-Loss Surface: Hold for Human Review, changes on that surface aren't mine to sign off, and a stability promise on a bulk-delete command is the kind of signal that changes how readily users reach for it. Everything else here reads correct to me: exclusive_sibling and the mutual-exclusivity validation go together cleanly, ProjectConfig::ci_platform had no callers left outside its own test, and the retired switch.picker.timeout-ms rule is consistently gone from the rules table, the warning formatter, the combined snapshot, and both deprecation test batteries.
Cuts 0.76.0. `cargo semver-checks` fails ten lints, so the bump is minor. ## Changelog filing fix Five entries had been appended to the already-tagged `## 0.75.0` section after v0.75.0 shipped (#3977/#2860, #3949 ×2, #3959, #3945). They describe changes that ship *here*, and GitHub's 0.75.0 release notes — built from the tag — never showed them. They are moved into `## 0.76.0` and rewritten to the length ceiling; `## 0.75.0` is restored byte-identical to `git show v0.75.0:CHANGELOG.md`. The mechanism is systemic: a PR appends under whatever heading is currently top, so every post-release PR lands in the shipped section until the next release opens a new one. ## `--execute` template guidance `#3977` switched `--execute` expansion to `ShellEscapeMode::Literal`, which is correct for an argv model. But the migration that v0.53.0–v0.75.0's deprecation warning *printed* — `-x sh -- -c '<old body>'` — splices a template variable into text `sh` re-parses, so a value with spaces word-splits where the old POSIX-escaped model kept it intact. Worst case is `rm -rf` against unintended paths. `--execute`'s help now documents passing the variable as a separate argument and referencing it positionally. `extending.md` already modelled that form, so no recipe changed. ## Validation - `nightly` on the cut-from tip (`baf161bf6`): all 14 jobs green — [run 33538866753](https://github.com/max-sixty/worktrunk/actions/runs/33538866753) - `wt hook pre-merge --yes`: 4713 passed - Data-loss surface reviewed across the 45-commit diff by four independent finders; adjudication in the release thread. No new destructive path — `#3977` removes one, deleting the EXEC directive file from all five shell wrappers. > _This was written by Claude Code on behalf of max-sixty_
worktrunk 0.76.0 Created-by: HarmonybrewBot Commit-by: HarmonybrewBot Merged-by: HarmonybrewBot Description: Created by `brew bump` --- Created with `brew bump-formula-pr`.<details> <summary>release notes</summary> <pre>## Release Notes ### Improved - **`wt switch --execute` takes a program, not a shell string**: `-x` names one program, with everything after `--` passed as literal argv. Worktrunk spawns it as a child rather than running it in your interactive shell, so shell functions and its `cd` no longer reach you. `-x sh -- -c '…'` recovers shell syntax, not functions. (Breaking: existing `-x` strings, plus `WORKTRUNK_DIRECTIVE_EXEC_FILE` and `WORKTRUNK_SHELL`.) ([#3977](max-sixty/worktrunk#3977), closes [#2860](max-sixty/worktrunk#2860), fixes [#3944](max-sixty/worktrunk#3944), thanks @omgreenfield for testing the migration path) - **Worktrunk decides tracking for the branches it creates**: a new branch gets an upstream only when its name matches the remote branch it starts from, whatever `branch.autoSetupMerge` says: `--create release --base origin/release` tracks, `--create feature --base origin/release` does not. Under `autoSetupMerge = false` this previously exited 128 after creating the worktree. ([#3913](max-sixty/worktrunk#3913), [#3950](max-sixty/worktrunk#3950), fixes [#3937](max-sixty/worktrunk#3937), thanks @mjakl for reporting and diagnosing) - **Retired config keys are no longer read**: `commit.generation.template-file` and `squash-template-file` are gone — put the file's contents in `template` or `squash-template`. There is no migration: the key stays, warns as unknown on every load, and the prompt reverts to the built-in default. Setting both `template` and `template-file` used to fail the load; it now loads `template`. `switch.picker.timeout-ms` likewise warns rather than being stripped. (Breaking.) ([#3949](max-sixty/worktrunk#3949)) - **Project aliases and hooks can use `wt switch --execute`**: their bodies previously refused it. Worktrunk now starts the program itself instead of handing shell text to your shell, and the command-approval gate remains the control on project-defined commands. ([#3977](max-sixty/worktrunk#3977)) - **Established automation and LLM customization interfaces are now stable**: `wt step eval`, `wt step for-each`, `wt step prune`, LLM branch summaries, `wt config state vars`, and `commit.generation.template-append` are no longer marked experimental. ([#3949](max-sixty/worktrunk#3949)) - **`wt step prune` stages removals concurrently again**: only the `git worktree remove` teardowns serialize, so dirty checks, fsmonitor shutdown, and the trash rename overlap. The `prune_e2e/live` benchmark median went from 377 ms to 222 ms. ([#3954](max-sixty/worktrunk#3954)) - **`wt config approvals add` reads as a review rather than a warning**: it opens with a cyan `Review 1 command for repo:` instead of a yellow `▲ … needs approval to execute`. The execution-time gate is unchanged. ([#3953](max-sixty/worktrunk#3953)) ### Fixed - **The `wt switch` picker survives a preview longer than 65,535 lines**: skim keeps the pane's line count in a `u16`, so a larger diff aborted `wt` with exit 101 seconds after it painted — on screen the picker looked like it closed by itself. Panes now cap at 60,000 lines and say so. ([#3959](max-sixty/worktrunk#3959), fixes [#3958](max-sixty/worktrunk#3958), thanks @sandertammesoo for reporting and diagnosing) - **The nushell wrapper propagates exit codes without a POSIX shell**: it reported a failing `wt` by spawning `sh`, so on Windows every failing command printed a ``Command `sh` not found`` trace and exit 1 whatever the real code was. Nushell's wrapper is a static file, so rerun `wt config shell install` to pick this up. ([#3945](max-sixty/worktrunk#3945)) - **The picker no longer shows one detached worktree's diff for another**: previews were keyed by branch name, so every detached row shared the key `(detached)`. Rows now key by canonical identity, which also lets `wt remove feature ~/repo.feature` plan that worktree once instead of failing on the second. ([#3926](max-sixty/worktrunk#3926)) - **`wt switch pr:<n>` gives `pre-switch` hooks the real branch**: `branch` and `target` held the literal `pr:3933`, and `target_worktree_path` was unset even when that branch had a worktree. `pr_number` and `pr_url` are now set for same-repo PRs, not only forks. ([#3941](max-sixty/worktrunk#3941), fixes [#3934](max-sixty/worktrunk#3934), thanks @robsonpeixoto for reporting) - **`wt config shell install` no longer replaces an rc file created while it was checking**: the missing-file path refuses to clobber and fails with a rerun hint. A dangling rc symlink is preserved and rejected rather than overwritten. ([#3929](max-sixty/worktrunk#3929)) - **`wt switch --create X --base pr:<n>` works when the PR branch has no local branch**: the fetch writes only the remote-tracking ref, and git won't expand a bare name to one, so the command failed with `No branch, tag, or commit named …`. The base is now named under its remote. ([#3951](max-sixty/worktrunk#3951)) - **`wt switch --create` streams `git worktree add` instead of stalling**: where a sandbox denied the timed wait's pipe or `sigaction`, the command waited out git's entire run and then reported `Failed to wait for command` for work that had already succeeded. ([#3881](max-sixty/worktrunk#3881)) - **Claude Code activity markers stay on the session's launch worktree**: the plugin's marker hooks pass `-C "$CLAUDE_PROJECT_DIR"`, so a `cd` mid-session no longer marks another repository. A session launched outside a repository gets no marker. ([#3956](max-sixty/worktrunk#3956)) ### Documentation - **`--execute` documents how to get a variable into a shell body**: each position is substituted verbatim, so a variable spliced into `sh -c` text is re-parsed by that shell and splits on spaces. The help now shows passing it as a separate argument and referencing it positionally. ([#3977](max-sixty/worktrunk#3977)) - **The clone-local default-branch override is documented**: `wt config state default-branch set` writes `worktrunk.default-branch` to the clone's local git config — machine-local, never committed, and shared by every linked worktree. `set` warns when the branch isn't checked out locally, and `wt list` skips its comparisons until it is. ([#3947](max-sixty/worktrunk#3947), [#3948](max-sixty/worktrunk#3948), fixes [#3946](max-sixty/worktrunk#3946), thanks @danielo515 for the request) - **The docs site renders code with the CLI's own colors**: terminal examples reproduce each snapshot's exact ANSI roles, while command references and the homepage command comparison get clap and shell syntax color from paired light/dark themes. File excerpts gain a path tab, and code wraps instead of scrolling on mobile. ([#3936](max-sixty/worktrunk#3936), [#3938](max-sixty/worktrunk#3938), [#3939](max-sixty/worktrunk#3939), [#3942](max-sixty/worktrunk#3942)) - **The `pre-start` upstream example uses `--base`**: it showed `wt switch --create feature origin/feature`, which `wt switch` rejects. ([#3955](max-sixty/worktrunk#3955)) ### Internal - **Library API rework** (Breaking library API): `cargo-semver-checks` fails ten lints — `RemoteRefProvider` and its provider structs give way to `ForgeKind` dispatch, the `--execute` rework retires the fish and PowerShell `ShellEscapeMode` escapes and `WORKTRUNK_SHELL_ENV_VAR`, `BranchRef` swaps `full_ref`/`worktree_path` for `id`, and accessors including `Branch::unset_upstream` and `ProjectConfig::ci_platform` are gone. ([#3913](max-sixty/worktrunk#3913), [#3926](max-sixty/worktrunk#3926), [#3949](max-sixty/worktrunk#3949), [#3963](max-sixty/worktrunk#3963), [#3977](max-sixty/worktrunk#3977)) - **Hook pipelines and `wt list` collection lost two indirection layers**: the background pipeline carries `PreparedStep` directly, and 15 marker task types collapse into one exhaustive dispatch. The synthesized-`--force` submodule path keeps its dirty re-check and destructive command under one lock, so prune's narrower locking doesn't widen that window. ([#3964](max-sixty/worktrunk#3964), [#3954](max-sixty/worktrunk#3954)) ## Install worktrunk 0.76.0 ### Install prebuilt binaries via shell script ```sh curl --proto '=https' --tlsv1.2 -LsSf https://github.com/max-sixty/worktrunk/releases/download/v0.76.0/worktrunk-installer.sh | sh && wt config shell install ``` ### Install prebuilt binaries via powershell script ```sh powershell -ExecutionPolicy Bypass -c "irm https://github.com/max-sixty/worktrunk/releases/download/v0.76.0/worktrunk-installer.ps1 | iex"; git-wt config shell install ``` ### Install prebuilt binaries via Homebrew ```sh brew install worktrunk && wt config shell install ``` ## Download worktrunk 0.76.0 | File | Platform | Checksum | |--------|----------|----------| | [worktrunk-aarch64-apple-darwin.tar.xz](https://github.com/max-sixty/worktrunk/releases/download/v0.76.0/worktrunk-aarch64-apple-darwin.tar.xz) | Apple Silicon macOS | [checksum](https://github.com/max-sixty/worktrunk/releases/download/v0.76.0/worktrunk-aarch64-apple-darwin.tar.xz.sha256) | | [worktrunk-x86_64-ap See merge request: Harmonybrew/homebrew-core!18482
Marks the mature
wt step eval,wt step for-each,wt step prune, LLM summary, config-state vars, and commit-template append interfaces as stable.Removes compatibility for the long-deprecated
template-fileandsquash-template-filesettings, and retires the special migration for the already-ignoredswitch.picker.timeout-ms. These keys now follow the ordinary unknown-field path; the troubleshooting guide explains how to move file contents into inline templates. The change also removes the redundant project CI-platform accessor.Validation:
cargo run -- hook pre-merge --yes(4,710 tests passed; one skipped), plus a production documentation build.