Repository navigation
fix(list): keep [list] columns out of the --format json plan - #3812
Conversation
`wt list --format json` planned `all_columns` unioned with the selection's forced-on columns, so a user's `[list] columns = […, "ci"]` turned a machine-readable call into a forge fetch — `--full` is meant to be the switch for that, and it's the one a caller controls. JSON now plans `all_columns` alone; the table and the picker are unchanged. Reported in #3787 by a Neovim plugin shelling out to `wt`, where the user's display config decided whether the editor talked to GitHub.
worktrunk-bot
left a comment
There was a problem hiding this comment.
The planning change itself is tight — the new three-way fork is behaviour-identical to the old prune_to_selection boolean everywhere except the JSON-with-a-selection case it means to change, required_tasks_for_render returns a HashSet so the picker's extend still dedups, and the layout (which is still computed with selected: Some(...) while tasks is now the unnarrowed full_plan()) is only read behind progressive_table/progressive_handler guards, so JSON can't observe the mismatch. wt list statusline builds its own plan through CollectOptions::for_columns, so it's untouched.
One doc gap left in the table this PR is tightening, plus a note below.
Worth calling out for the release note: this also changes schema 1. A caller with [list] columns = [..., "ci"] and no --full used to get the ci object in schema-1 JSON and now won't, and schema 1 has no collected envelope to say so. The schema-1 ci row already documented `--full` only, so the docs get more accurate — but the observable output changes for anyone who was relying on the forcing path.
Both rows described a gate the JSON plan no longer honours in full. With `[list] columns` out of the JSON plan, `summary` resolves under `ColumnSource::Default`, so it needs `--full` *and* `[list] summary = true` (plus a `[commit.generation]` command) — the listed-column escape hatch that made `[list] summary = true` alone sufficient is what this PR removes. `checks` rides the same `CiStatus` task as `pr` and stated no gate at all.
| json` always emits every field, but a listed gated column (`ci`, `summary`) | ||
| still forces its data collection on, so the JSON carries the same data the | ||
| table shows. | ||
| The selection drives the table and the `wt switch` picker; `wt list --format |
There was a problem hiding this comment.
Cut it entirely in 51c8121 — the scope now lives in the columns definition at the top of the section: "columns selects and orders the columns the wt list table and the wt switch picker render; --format json ignores it and always emits every field."
Net against the PR base, this section is 4 lines shorter than it was before the PR.
| | `upstream` | Tracking branch: `{remote, branch, ahead, behind}`; absent when none is configured | | ||
| | `pr` | Open PR/MR: `{number, url, review, mergeable, repo}`; collected with `--full` or a listed `ci` column. `review` uses the schema 1 `ci.review_state` vocabulary; `mergeable` is false when the forge reports conflicts, null otherwise | | ||
| | `checks` | CI pipeline: `{status, source, stale}`; `status` is `passed`, `running`, or `failed` — null when a conflicts report masks it | | ||
| | `pr` | Open PR/MR: `{number, url, review, mergeable, repo}`; collected with `--full` (`[list] columns` doesn't reach JSON). `review` uses the schema 1 `ci.review_state` vocabulary; `mergeable` is false when the forge reports conflicts, null otherwise | |
There was a problem hiding this comment.
way way too verbose, all this section
There was a problem hiding this comment.
Trimmed in 51c8121. The [list] columns annotations are gone from every row — the rows now only name --full, which is the gate a JSON caller actually controls:
| row | now |
|---|---|
pr |
collected with --full |
checks |
collected with --full |
summary |
needs --full, [list] summary = true, and a [commit.generation] command |
pr is shorter than it was before the PR; summary gains the two gates it was missing (a caller who set only [list] summary = true got no field).
|
there is no need to list this everywhere, just say in the |
The JSON caveat was repeated across the `columns` prose, three item-field rows, and two code comments. Say it once where `columns` is defined — the selection drives the `wt list` table and the picker, `--format json` ignores it — and drop the per-row annotations, which now just name `--full` as the gate.
|
Done in 51c8121 — the caveat is stated once, in the The definition now opens with the scope:
Removed: the six-line paragraph at the end of the The code change is unchanged; CI on |
worktrunk-bot
left a comment
There was a problem hiding this comment.
One gap left, in the doc this PR is most about.
Collected's doc comment in src/commands/list/model/item.rs still names the gate this PR removes — "Fact families whose collection is gated (--full, [list] summary, a listed ci/summary column)". Collected is built only from tasks (collect/mod.rs), and the JSON plan is now full_plan() alone, which passes ColumnSource::Default — so listed is always false there and a listed column can no longer set collected.ci/collected.summary.
That comment isn't internal: schemars ships it verbatim into docs/static/schema/list-v2.json, which test_docs_are_in_sync regenerates from wt list --print-schema and which after_long_help points JSON consumers at ("A JSON Schema for the envelope is published at worktrunk.dev/schema/list-v2.json"). So the published machine-readable contract currently advertises the exact forcing path #3787 asked to remove — to the same audience the PR is for.
The fix is to drop the clause rather than explain it, which is also a line shorter. Pushing that now, with the schema mirror.
Everything else checks out: the three-way fork is behaviour-identical to the old prune_to_selection boolean in all three unchanged cases (empty selection still lands on full_plan(), since listed_plan() over an empty selection is the empty set), CiStatus and Summary really are the only gated columns (Url's gate is data-source, identical under either source), and JSON's display.columns comes from columns_map(custom_columns, …) rather than the layout, so the "always emits every field" claim holds for custom columns too.
`Collected`'s doc comment ships verbatim into docs/static/schema/list-v2.json, and it still named "a listed `ci`/`summary` column" as a gate on `collected`. The JSON plan is now `full_plan()` alone, which passes `ColumnSource::Default`, so a listed column can never set `collected.ci`/`collected.summary` — the published schema was advertising the exact forcing path this PR removes, to the machine consumers it removes it for. Schema regenerated by `test_docs_are_in_sync`.
worktrunk 0.75.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 - **The `wt switch` picker opens on one unified diff**: local rows combine committed, staged, unstaged, and untracked changes. Tab skips empty subsidiary views, while `Alt-1` through `Alt-8` retain direct access. [Docs](https://worktrunk.dev/switch/#interactive-picker) ([#3865](max-sixty/worktrunk#3865)) - **Git 2.43 is now the minimum supported version**: older Git exits with an upgrade message before Git-dependent commands run; `wt config shell` remains available so shell startup continues. (Breaking.) ([#3895](max-sixty/worktrunk#3895)) ### Fixed - **`wt list` no longer grows `.git/objects` during advisory conflict checks**: untracked content stays visible but is excluded from synthetic trees, and all probe-only objects use temporary storage. ([#3906](max-sixty/worktrunk#3906), fixes [#3883](max-sixty/worktrunk#3883), thanks @srobroek for reporting and the original fix) - **`HEAD±` counts untracked files without inflating moves**: `wt list`, the picker, and statusline include untracked lines. Tracked deletions paired with untracked destinations count as renames, so pure moves are line-neutral and edited moves show only their edits. `HEAD±` now always detects renames, regardless of `diff.renames`. ([#3925](max-sixty/worktrunk#3925)) - **Timed child processes no longer abort `wt` in restricted sandboxes**: `wt switch --create`, picker pagers, and other bounded commands survive denied signal-handler wakes. TERM→KILL cleanup also returns promptly once the process group is gone. ([#3857](max-sixty/worktrunk#3857), [#3887](max-sixty/worktrunk#3887), fixes [#3856](max-sixty/worktrunk#3856), thanks @tomascamargo for reporting) - **JSON list output ignores display-column gates**: `[list] columns` no longer makes `wt list --format json` contact a forge or generate summaries. CI requires `--full`; summaries also require `[list] summary = true` and a configured generator. (Breaking: schema 1 loses config-driven `ci` and `summary` fields.) ([#3812](max-sixty/worktrunk#3812), thanks @emeren for the request) - **Long Windows paths compare consistently**: paths beyond 260 characters could retain a `\\?\` prefix and appear to be on another drive. `copy-ignored` then refused them, while switch, remove, merge, and relocate landed at the worktree root instead of the original subdirectory. ([#3899](max-sixty/worktrunk#3899), fixes [#3898](max-sixty/worktrunk#3898), thanks @Persedes for reporting and verifying the fix) - **Shell configuration rechecks before it writes**: overlapping installs lock and reread rc files; Fish completion installs preserve files created after preview; uninstall applies only previewed rc removals and rejects changed Worktrunk-owned files. ([#3853](max-sixty/worktrunk#3853), [#3924](max-sixty/worktrunk#3924)) - **`-vv` profiles exclude their own collector commands**: command counts and cache summaries no longer include duplicate-looking work performed only to assemble the diagnostic report; raw traces still retain it. ([#3900](max-sixty/worktrunk#3900)) - **Fenced HTML comments survive picker Markdown rendering**: PR descriptions and comments now preserve fenced `<!-- … -->` lines; fenced `<!-- wt list … -->` markers also no longer affect the following block. ([#3908](max-sixty/worktrunk#3908)) ### Documentation - **Agent CLIs without a plugin can publish activity markers**: the integration guide now specifies the session-start, turn-end, and session-end calls, working-directory requirement, error guard, and cleanup contract. [Docs](https://worktrunk.dev/claude-code/#agent-clis-without-a-plugin) ([#3848](max-sixty/worktrunk#3848), thanks @AsafMah for requesting generic-agent guidance and @ortonomy for the related Pi use case) - **Agent guidance explains worktree selection**: commands that name a branch already select its worktree; `-C` changes repository context and is needed only for commands without a worktree selector or callers outside the repository. ([#3890](max-sixty/worktrunk#3890)) - **The `wt up` recipe safely updates dirty worktrees**: it fetches all remotes, fast-forwards dirty branches without autostash, rebases clean branches, and continues past an ordinary refusal or a failed remote. [Docs](https://worktrunk.dev/extending/#recipe-rebase-every-worktree-onto-its-upstream) ([#3882](max-sixty/worktrunk#3882)) - **The docs site has a new responsive design**: rebuilt on Astro and Starlight while preserving public routes, anchors, and crawler URLs; generated reference pages remain synchronized. ([#3866](max-sixty/worktrunk#3866)) ### Internal - **Library API rework** (Breaking library API): `BranchDiffSpec` gained `working_base`, while remote-URL, shell-path, branch-push, approval, temporary-index, and repository helpers were removed. ([#3833](max-sixty/worktrunk#3833), [#3853](max-sixty/worktrunk#3853), [#3865](max-sixty/worktrunk#3865), [#3866](max-sixty/worktrunk#3866), [#3875](max-sixty/worktrunk#3875)) - **Codex loads repository maintainer skills**: `.agents/skills` now exposes the canonical `.claude/skills` tree; checkouts without symlink support keep the existing limitation. ([#3903](max-sixty/worktrunk#3903)) ## Install worktrunk 0.75.0 ### Install prebuilt binaries via shell script ```sh curl --proto '=https' --tlsv1.2 -LsSf https://github.com/max-sixty/worktrunk/releases/download/v0.75.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.75.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.75.0 | File | Platform | Checksum | |--------|----------|----------| | [worktrunk-aarch64-apple-darwin.tar.xz](https://github.com/max-sixty/worktrunk/releases/download/v0.75.0/worktrunk-aarch64-apple-darwin.tar.xz) | Apple Silicon macOS | [checksum](https://github.com/max-sixty/worktrunk/releases/download/v0.75.0/worktrunk-aarch64-apple-darwin.tar.xz.sha256) | | [worktrunk-x86_64-apple-darwin.tar.xz](https://github.com/max-sixty/worktrunk/releases/download/v0.75.0/worktrunk-x86_64-apple-darwin.tar.xz) | Intel macOS | [checksum](https://github.com/max-sixty/worktrunk/releases/download/v0.75.0/worktrunk-x86_64-apple-darwin.tar.xz.sha256) | | [worktrunk-x86_64-pc-windows-msvc.zip](https://github.com/max-sixty/worktrunk/releases/download/v0.75.0/worktrunk-x86_64-pc-windows-msvc.zip) | x64 Windows | [checksum](https://github.com/max-sixty/worktrunk/releases/download/v0.75.0/worktrunk-x86_64-pc-windows-msvc.zip.sha256) | | [worktrunk-aarch64-unknown-linux-musl.tar.xz](https://github.com/max-sixty/worktrunk/releases/download/v0.75.0/worktrunk-aarch64-unknown-linux-musl.tar.xz) | ARM64 MUSL Linux | [checksum](https://github.com/max-sixty/worktrunk/releases/download/v0.75.0/worktrunk-aarch64-unknown-linux-musl.tar.xz.sha256) | | [worktrunk-x86_64-unknown-linux-musl.tar.xz](https://github.com/max-sixty/worktrunk/releases/download/v0.75.0/worktrunk-x86_64-unknown-linux-musl.tar.xz) | x64 MUSL Linux | [checksum](https://github.com/max-sixty/worktrunk/releases/download/v0.75.0/worktrunk-x86_64-unknown-linux-musl.tar.xz.sha256) | ### Install via Cargo ```sh cargo install worktrunk && wt config shell install ``` ### Install via Winget (Windows) ```sh winget install max-sixty.worktrunk && git-wt config shell install ``` ### Install via AUR (Arch Linux) ```sh paru worktrunk-bin && wt config shell install ``` </pre> <p>View the full release notes at <a href="https://github.com/max-sixty/worktrunk/releases/tag/v0.75.0">https://github.com/max-sixty/worktrunk/releases/tag/v0.75.0</a>.</p> </details> <hr> See merge request: Harmonybrew/homebrew-core!17938
Closes the
[list] columnshalf of #3787, per the call in this comment: JSON always emits the same shape, andlist.columnsonly affects the actual columns.Before,
--format jsonplannedall_columns(sourceDefault) unioned with the selection's forced-on columns, so the selection reached JSON in one direction only — it couldn't narrow the emitted fields, but a listedcidid force the forge fetch on without--full. That made a presentation setting decide whether a machine-readable call talks to GitHub, which is the thing the Neovim plugin in #3787 had to pin--config-set 'list.columns=[…]'against. Now the JSON branch plansall_columnsalone;--fullis the only switch for the gated data, and it's the one a caller controls.The table and the
wt switchpicker are untouched — a listedcistill renders the CI column without--full, and the picker still unions the selection in so its table matcheswt list's.Only
ciandsummaryare affected: every other column is ungated, sofull_plan()already covered them, and custom columns require no background task.For the release note — this changes schema 1 too. A caller with
[list] columns = […, "ci"]and no--fullused to get theciobject in schema-1 JSON and now won't; schema 1 has nocollectedenvelope to say why. The schema-1cirow already documented`--full` only, so the docs get more accurate, but the observable output changes for anyone who was relying on the forcing path. Schema 2 reports the same narrowing throughcollected.ci.Docs updated in
after_long_help(the[list] columnssection plus the schema-2pr,summary, andchecksrows —summarynow names--fullalongside[list] summary = true, andchecksnames the--fullgate it shares withpr), with the generated mirrors,dev/config.example.toml, and the--helpsnapshots regenerated. TheCLAUDE.mdnetwork inventory and thecollectplanning comment now record the exemption too.Test
test_list_json_columns_selection_does_not_force_ciintests/integration_tests/list_config.rsasserts schema 2'scollected.ciacross three configs: unset (false),columns = ["branch", "ci"]without--full(false — the regression this fixes), and the same with--full(true).collectedrecords what the plan requested rather than what a fetch returned, so the test needs no forge and noghon PATH. It sits next totest_list_json_ignores_columns_selection, which owns the narrowing direction, andtest_list_config_listed_column_overrides_full_gate, which owns the table's forcing behaviour and still passes unchanged.Ran locally: full
cargo test --test integrationandcargo test --lib --bins, pluscargo clippy --all-targetsandcargo fmt --check. One unrelated failure,test_copy_ignored_preserves_file_executable_permissions, is a umask artifact of this sandbox (expects0644, the runner'sumask 002produces0664); it touches no code in this diff.The docs-row follow-up in df5c238 re-ran
cargo test --test integration -- test_help test_docs_are_in_sync(48 passed) andcargo fmt --check.