Skip to content

feat(config): deprecate list.task-timeout-ms - #3615

Merged
max-sixty merged 1 commit into
mainfrom
remove-list-task-timeout
Jul 26, 2026
Merged

feat(config): deprecate list.task-timeout-ms#3615
max-sixty merged 1 commit into
mainfrom
remove-list-task-timeout

Conversation

@max-sixty

Copy link
Copy Markdown
Owner

Removes the [list] task-timeout-ms per-task command timeout and the thread-local machinery behind it. The key stops having any effect immediately; a config that still carries it loads, warns, and is stripped by wt config update.

The timeout killed any git command that outlived its budget, on every collect worker, through a thread-local that Cmd::run consulted on each invocation and clamped an explicit .timeout() against. Progressive rendering removed the reason for it: wt list and the picker paint from local data and stream results in behind the frame, so no single git command can hold up the first paint. What it bounded instead was completion, which [list] timeout-ms already bounds directly, and the drain falls back to a hardcoded 120s DRAIN_TIMEOUT whenever that is unset (collect/mod.rs drain_deadline), so removing this cannot introduce an unbounded wait. Both keys default to unset, so the default path never had a per-task timeout at all.

[list] timeout-ms, the wall-clock budget for the whole collect phase, stays. It is the surviving knob and the more direct expression of the same goal.

With the thread-local gone, the two-source min() in Cmd::run collapses to the command's own self.timeout, so every explicit .timeout() caller keeps its bound unclamped: PROBE_TIMEOUT in git/reap.rs, the fsmonitor stop/lsof bounds in git/remove.rs, the version check in config/show.rs, and REMOTE_DETECTION_TIMEOUT.

Deprecation

A Structural row in DEPRECATION_RULES strips the key from [list] in both the section and inline forms, top-level and per-project, following the [switch.picker] timeout-ms precedent (also a strip with no equivalent key to migrate into):

▲ User config: list.task-timeout-ms is no longer used — list.timeout-ms bounds the collect phase

The env overlay (WORKTRUNK__LIST__TASK_TIMEOUT_MS) and --config-set route through the same rule and migrate silently, since neither layer has a file for wt config update to materialize. Neither errors.

Testing

New unit tests cover detection and migration for the section, inline, and per-project forms plus the warning text, and two cases join the test_warning_fires_iff_update_changes battery that pins the warn-iff-update-changes invariant. The three integration tests that exercised the feature are gone; Cmd::timeout keeps its own coverage in shell_exec.rs, so dropping the four thread-local unit tests loses nothing for the surviving path. Verified end to end that a config carrying the key loads, warns, and has it stripped by wt config update with sibling keys intact.

This was written by Claude Code on behalf of max

The per-task timeout killed any git command that outlived the budget, on
every collect worker, through a thread-local that `Cmd::run` consulted on
each invocation and clamped an explicit `.timeout()` against. Progressive
rendering removed the reason for it: `wt list` and the picker paint from
local data and stream results in behind the frame, so no single git
command can hold up the first paint. What it bounded instead was
completion, which `[list] timeout-ms` already bounds directly, and the
drain falls back to a hardcoded 120s when that is unset.

A config carrying the key loads and warns; `wt config update` strips it.
Sibling keys, including `timeout-ms`, are untouched. The env and
`--config-set` layers migrate through the same rule, silently, as they
have no file to materialize.

With the thread-local gone, `Cmd::run` reads only the command's own
`self.timeout`, so every explicit `.timeout()` caller (remote detection,
fsmonitor teardown, the version check) keeps its bound unclamped.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@max-sixty
max-sixty merged commit 6307170 into main Jul 26, 2026
39 checks passed
@max-sixty
max-sixty deleted the remove-list-task-timeout branch July 26, 2026 20:36
@max-sixty max-sixty mentioned this pull request Jul 29, 2026
max-sixty added a commit that referenced this pull request Jul 29, 2026
Cuts 0.70.0 — version bump plus the changelog for the 45 commits since
v0.69.2.

**Minor bump, not patch.** `cargo semver-checks` reports 6 breaking
library changes (`set_command_timeout` and `ListConfig::task_timeout_ms`
removed, `WorkingTree::stage` arity changed, three enum variants added
to exhaustive enums). Worktrunk ships breaking library changes freely,
but semver still puts a break at minor while pre-1.0.

## Release validation

- Local gate green on the release commit: 4631 tests, lints, doctests.
- `nightly` dispatched on the cut-from tip (`a27cbd42`) for the full
cross-platform suite — `full-tests` green on linux, macOS, and Windows,
plus minimal-versions, nix-flake, crate-build, and the release targets.

## Data-loss surface review

The cumulative diff was audited against the deletion surface. One
deliberate widening, signed off for this release with follow-ups to
file:

- **#3602** removes the content check from `wt config shell install`'s
legacy cleanup, so `conf.d/{cmd}.fish` and the stranded nushell
`{cmd}.nu` are now deleted by path, unread. Only that exact filename is
touched and each removal is reported, but the deletion is absent from
both `--dry-run` and the confirmation prompt, and the already-configured
path skips the prompt entirely.

Also noted, none blocking:

- The `!path.exists()` check precedes the lock guard on the
`Path`/`Current` removal arm, so a locked worktree whose directory is
absent loses its branch. This already governed the branch-targeted route
in v0.69.2; #3533 unified the other arms onto it. The FAQ's "Neither
`git worktree remove` nor `wt remove` (even with `--force`) will delete
them" is absolute where the behavior isn't.
- On the default background removal path, `ensure_clean` and
`stop_fsmonitor_daemon` swapped order, so the safety gate is now
answered by the live fsmonitor daemon rather than a full re-stat.
Bounded by trash staging with 24-hour retention, and the foreground and
picker paths were already daemon-served.

Net *improvements* to the same surface: shared-branch retention across
remove/prune/merge (#3533), outcome-accurate removal reporting (#3633,
#3637), and the removal of the thread-local command timeout that could
kill in-flight git commands on worker threads (#3615).

## Changelog accuracy

Entries were verified against the actual diffs rather than commit
messages, which corrected several drafts: the prune figures were one PR
stale (~2.9 s → the real ~0.6 s), `wt merge` takes no worktree argument
so it only gained the retention half of #3533, the Azure DevOps report
is behind `--full`, #3608 never touched `nightly.yaml`, and #3601
inverted what the FAQ change actually said. Two omissions were added —
the `install-statusline` foreign-statusline fix (#3595) and the shipped
`/wt-switch-create` skill change (#3636).

> _This was written by Claude Code on behalf of Maximilian_
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants