Skip to content

Make worktree maintenance visible and responsive - #104

Merged
wesm merged 36 commits into
mainfrom
better-ergonomics
Aug 22, 2026
Merged

Make worktree maintenance visible and responsive#104
wesm merged 36 commits into
mainfrom
better-ergonomics

Conversation

@wesm

@wesm wesm commented Aug 22, 2026

Copy link
Copy Markdown
Member

This makes long-running worktree maintenance visible and keeps the TUI usable while inventory updates and deletions run.

  • doctor and prune now show bounded progress, the current phase, and an ETA without mixing status text into structured output.
  • prune --merged proves that a pull request was merged before dirtiness matters, then asks before removing a dirty merged worktree. Unattended runs still fail closed.
  • The TUI refreshes the active project first, uses one Git status command per worktree with bounded concurrency, and allows safe navigation from cached rows.
  • Worktree removals run through a FIFO background queue, so the rest of the dashboard remains responsive.

A lightweight global refresh keeps the shared cache current without repeating fleet-wide Git status work. Repository scope, session liveness, and removal authority remain fail-closed.

wesm and others added 22 commits August 22, 2026 08:54
Long maintenance commands currently provide no useful feedback, and the TUI
blocks safe navigation behind an expensive fleet-wide refresh. The approved
design narrows interactive refresh to the active project while keeping the
daemon-owned global cache authoritative.

Merged pruning will prove pull-request relevance before reporting dirty state.
This lets users confirm removal of a dirty, confirmed-merged worktree without
weakening the hard stop for a branch that advanced after its pull request.

The design also keeps removal responsive through a single background queue and
preserves fail-closed generation, endpoint, and ownership checks.

Generated with Codex
Co-authored-by: Codex <noreply@openai.com>
Dry runs, declined prompts, and unattended commands need distinct exit
semantics. Assign each case a stable reason and increase the prune report
schema version so machine consumers can interpret the result without knowing
the invocation mode.

Treat ignored files as deletion-sensitive content. Refresh status before a
prompt and make the prompt authorize the complete worktree directory.

Also separate inventory currency from status collection. A background global
refresh must update daemon cache and effective configuration without paying for
fleet-wide Git status.

Generated with Codex
Co-authored-by: Codex <noreply@openai.com>
Break the approved ergonomics design into three independently testable
delivery sequences: maintenance progress and merged pruning, scoped TUI
inventory, and queued worktree removal.

Capture the reviewed failure semantics, freshness boundaries, lock ownership,
and test-first checkpoints so implementation decisions are not left implicit.

Generated with Codex
Co-authored-by: Codex <noreply@openai.com>
Long maintenance operations need visible feedback without turning redirected
output into an unbounded event log. Use one transient terminal line and four
milestones for non-terminal streams.

Keep progress on stderr so structured stdout remains safe for automation.

Generated with Codex
Co-authored-by: Codex <noreply@openai.com>
Dry runs, declined prompts, and unattended runs need different machine-readable
results even when they observe the same dirty merged worktree. Give each path a
stable reason with its own summary and exit behavior.

Increase the schema version so consumers can recognize the new contract.

Generated with Codex
Co-authored-by: Codex <noreply@openai.com>
Dirty state alone does not identify a merged worktree. Resolve the pull request
and verify its exact head first so unrelated dirty worktrees remain normal
non-candidates and advanced branches retain their hard stop.

The command can now make the final cleanliness decision with current evidence.

Generated with Codex
Co-authored-by: Codex <noreply@openai.com>
A merged pull request is strong removal evidence, but local files still need a
narrow user decision. Recheck dirtiness at prompt time and force only the one
worktree that the user accepts.

Keep unattended runs fail-closed, make dry runs informative, and group repeated
human diagnostics so large fleets remain readable.

Generated with Codex
Co-authored-by: Codex <noreply@openai.com>
Doctor and prune can spend many seconds inspecting a fleet without visible
feedback. Report their current phase and bounded candidate counts on stderr so
people can tell that work is advancing.

Separate expired-candidate validation from removal so each phase reflects the
operation that is actually running.

Generated with Codex
Co-authored-by: Codex <noreply@openai.com>
Users and automation need to know where progress is written and how dirty
merged worktrees affect reports and exit status. Document the stderr contract,
narrow interactive approval, and schema version 2 reasons.

Generated with Codex
Co-authored-by: Codex <noreply@openai.com>
Large inventories paid for several Git processes per worktree before the dashboard could show current state. Read file state, divergence, and branch metadata from one stable porcelain response so each row has a much smaller fixed cost.

Keep per-file untracked counts with -uall and preserve the existing no-fetch divergence behavior.

Generated with Codex
Co-authored-by: Codex <codex@openai.com>
One goroutine per worktree makes large inventories compete for processes and file descriptors. Use a small worker pool so refresh cost stays predictable and one unavailable repository degrades only its own row.

Calculate activity from the worktree root, HEAD, and changed paths. Clean checkouts stay recent without scanning every tracked file.

Generated with Codex
Co-authored-by: Codex <noreply@openai.com>
A current daemon snapshot does not always need expensive Git status collection. Give callers an explicit scope and status choice so the TUI can refresh structural catalog data independently.

Run daemon, status, tmux, and fleet I/O outside the backend configuration lock so long refreshes do not freeze unrelated backend reads.

Generated with Codex
Co-authored-by: Codex <noreply@openai.com>
The lifecycle repository view can fall back to the global catalog when its anchor is missing or is not a repository. Reject empty, unrelated, and main-less results so one scoped refresh cannot mark unrelated cached rows current.

Merge accepted rows over the dashboard snapshot and retain catalog-only fields such as the canonical repository URL.

Generated with Codex
Co-authored-by: Codex <noreply@openai.com>
A cached global catalog is useful for first paint, but it must not force every project through Git status before the active view is usable. Refresh the selected repository first and update the daemon-owned global catalog once in the background without status work.

Track currency by scope so mutations wait for the evidence they need. Preserve cached rows and newer project statuses when a background refresh fails or returns structural updates.

Generated with Codex
Co-authored-by: Codex <noreply@openai.com>
Cached rows are enough to enter an existing directory or reconnect to a session that is still live. Let those actions proceed during refresh while keeping every session-creation path behind current inventory.

Re-resolve cached tmux state at attach time and fail closed unless exactly one endpoint is live. This prevents stale cache hints from creating or repairing workspaces.

Generated with Codex
Co-authored-by: Codex <noreply@openai.com>
Dashboard users need to know why cached rows appear before current status and which actions remain safe during that window. Describe the project-first refresh, the background global pass, and the live-only navigation rules.

Also document the cheaper activity signal so row ordering no longer implies a scan of every tracked file.

Generated with Codex
Co-authored-by: Codex <noreply@openai.com>
A confirmed deletion can take long enough that an unchanged row looks stuck and invites duplicate actions. Give active and queued removals an explicit state that takes precedence over Git status.

Generated with Codex
Co-authored-by: Codex <noreply@openai.com>
Deletion should be exceptional slow work, not a modal pause for the whole dashboard. Queue confirmed worktrees in order, keep other rows usable, and reconcile each outcome against its repository.

Capture removal authority under the backend lock, then release it before daemon, fleet, and tmux I/O. Inventory configuration and navigation can continue while one removal runs.

Generated with Codex
Co-authored-by: Codex <noreply@openai.com>
Users can keep working while deletion runs, but they need a clear contract for queue order and failure recovery. Explain the removing state and which interactions remain available.

Generated with Codex
Co-authored-by: Codex <noreply@openai.com>
Keep scoped refresh fail-closed by separating dashboard launch context from repository requests, validating the anchor, and refusing to carry status across a changed checkout.

Avoid hidden global work in the fleet overlay, retain removal and warning state across merges, preserve failed fleet observations, and account for nested deletions in activity. Dry-run merged pruning now checks every hard removal condition before reporting confirmation as the only remaining requirement.

Generated with Codex
Co-authored-by: Codex <noreply@openai.com>
A terminal error must not share a line with an active spinner, and completed counts must describe finished candidate work. Pause progress before diagnostics and advance merged-prune counts only after each prior candidate has reached an outcome.

Generated with Codex
Co-authored-by: Codex <noreply@openai.com>
The responsive inventory refactor left obsolete wrappers and an unused test assignment that the repository lint gate rejects. Remove that dead code and apply the required Markdown formatting so the branch passes its publication checks.

Generated with Codex
Co-authored-by: Codex <noreply@openai.com>
@roborev-ci

roborev-ci Bot commented Aug 22, 2026

Copy link
Copy Markdown

roborev: Combined Review (a10a1fe)

Verdict: Three medium-severity correctness issues should be addressed before merging.

Medium

  • Stale freshness state permits unsafe worktree mutationsinternal/tui/model.go:1112
    selectedScopeCurrent falls back to the shared inventoryCurrent flag when no project-specific freshness record exists. A status-free background dashboard refresh can set this flag, allowing delete, create, sync, and kill actions on stale worktree data from projects that were never status-refreshed.
    Fix: Require project-specific freshness for worktree mutations. Mark a project current only after a scoped refresh or a status-bearing global refresh.

  • Out-of-order inventory responses can restore deleted rows or enable actions during removalinternal/tui/model.go:346, internal/tui/model.go:382
    Inventory requests lack sequence guards. A late global response started before a removal can overwrite a newer scoped refresh and reintroduce the deleted row. Inventory merging also fails to reapply queued Removing state, temporarily enabling actions on a row being removed.
    Fix: Add request generations and discard superseded responses, preserve newer project rows when merging older global results, and reapply creation/removal state after every merge.

  • Valid Git worktree states are incorrectly classified as cleaninternal/status/status_collector.go:311
    The porcelain-v2 parser recognizes only M, A, D, and U. Unstaged rename (R), copy (C), and type-change (T) states produce zero change counts, misreport status, and prevent the TUI’s dirty-worktree force-removal flow.
    Fix: Count R, C, and T as modifications and add behavioral tests for .R, .C, and .T records.


Reviewers: 2 done | Synthesis: codex, 13s | Total: 15m5s

A status-free cache update must not authorize destructive work, and a slow inventory response must not undo newer removal reconciliation. Track freshness per project, reject superseded inventory results, and retain in-flight removal state across every merge.

Treat all porcelain v2 worktree changes as dirty so renames, copies, and type changes cannot bypass removal checks. Preserve the finished architecture in maintained docs instead of implementation plans.

Generated with Codex
Co-authored-by: Codex <noreply@openai.com>
@roborev-ci

roborev-ci Bot commented Aug 22, 2026

Copy link
Copy Markdown

roborev: Combined Review (eba3664)

Review verdict: Five medium-severity issues require attention before merge.

Medium

  • Terminal-control injection can forge destructive prune confirmationinternal/cmd/prune_merged.go:558
    candidate.Policy.Path is written directly to the terminal. A Git ref containing C0/C1 controls, such as U+009B, can produce a path that erases or rewrites the prompt, potentially misleading users before deletion of local files. Escape or visibly quote all terminal controls and line breaks in the displayed path while retaining the raw path for removal. Add a behavioral test covering U+009B.

  • Removal can be undone by an in-flight fleet mergeinternal/tui/model.go:1863
    A pre-removal fleetRowsMsg can retain the same loadSeq, arrive after the scoped refresh, and restore the deleted row indefinitely. Cancel the active fleet merge and advance its generation whenever removal changes rows or replacement inventory begins.

  • Global refresh leaves stale projects mutableinternal/tui/model.go:930
    An all-project refresh clears only globalFresh.Current, while mutation guards use projectFresh. Previously current projects can therefore remain mutable while replacement inventory is loading. Mark every project stale when starting a status-bearing dashboard refresh, or have mutation checks account for the active global refresh.

  • Fleet reads no longer publish fresh local stateinternal/cmd/tui_backend.go:1248
    Ordinary commits and dirty-state changes are not sent to the hub unless another mutation or explicit sync command triggers publication, leaving remote dashboards stale. Restore best-effort publication before fleet reads or add equivalent background publication using freshly collected inventory.

  • Status silently succeeds with incomplete resultsinternal/cmd/status.go:236
    CollectAll converts per-worktree failures into diagnostics with a nil error, but kwt status discards those diagnostics. The command can exit successfully and emit incomplete JSON or CSV containing unknown statuses. Surface the diagnostics and return a nonzero status, or aggregate them into the command error.


Reviewers: 2 done | Synthesis: codex, 12s | Total: 17m21s

Destructive prompts and mutations need current, unambiguous state. Quote merged-prune paths, reject fleet results from before a removal or replacement inventory, and make project freshness stale while a global status refresh runs.

Fail the status command when any worktree could not be inspected so structured output cannot look complete when it is not. Keep TUI fleet reads project-scoped; whole-host publication remains on explicit mutations and sync.

Generated with Codex
Co-authored-by: Codex <noreply@openai.com>
@roborev-ci

roborev-ci Bot commented Aug 22, 2026

Copy link
Copy Markdown

roborev: Combined Review (13add38)

The changes have three medium-severity issues affecting repository refresh reliability and project freshness tracking.

Medium

  • internal/tui/model.go:377 — Every nonempty project perspective triggers repository inventory, but directory workspaces and remote-only fleet projects have no local Entry. This produces an empty working directory, causing refresh failure that may supersede the background global refresh and leave actions blocked until “All projects” is selected.

    • Fix: Use the current dashboard inventory/global freshness for perspectives without a local repository entry.
  • internal/tui/model.go:392 — Project freshness keys preserve exact identity casing, while repository validation and row merging use case-folded forge identities. When refreshed rows differ only in GitHub/GitLab casing, freshness lookup fails, statuses can be discarded during structural merging, and mutations remain disabled.

    • Fix: Normalize all projectFresh keys through the repository-identity folding helper on both reads and writes.
  • internal/status/status_collector.go:355 — The new git show used for activity ordering has no timeout. Because TUI callers use a background context, a blocked Git process can occupy workers indefinitely and prevent inventory refresh from completing.

    • Fix: Run the lookup with a bounded child context or obtain the commit timestamp without an additional unbounded Git command.

Reviewers: 2 done | Synthesis: codex, 10s | Total: 17m21s

Project views without a local repository must use dashboard freshness. This keeps directory workspaces and remote-only fleet rows usable without sending an invalid repository request.

Treat forge identity casing consistently when tracking freshness, and bound the activity lookup so one stalled Git process cannot block an inventory worker.

Generated with Codex
Co-authored-by: Codex <noreply@openai.com>
@roborev-ci

roborev-ci Bot commented Aug 22, 2026

Copy link
Copy Markdown

roborev: Combined Review (49e3c82)

The PR has one medium-severity correctness issue that should be addressed before merging.

Medium

  • Status freshness is not invalidated for newly discovered worktreesinternal/tui/model.go:466

    A status-free dashboard refresh can add a worktree to an already-fresh project without marking that project stale. selectedScopeCurrent may then allow mutations and session creation for the new row even though its status was never collected.

    Recommended fix: Mark the project stale whenever a structural refresh returns a worktree without a matching fresh prior row, and add a regression test covering externally added worktrees.


Reviewers: 2 done | Synthesis: codex, 6s | Total: 16m26s

A structural dashboard refresh can discover a worktree whose status was never collected. Mark its project stale so mutations and session creation remain blocked until a scoped status refresh completes.

Generated with Codex
Co-authored-by: Codex <noreply@openai.com>
@roborev-ci

roborev-ci Bot commented Aug 22, 2026

Copy link
Copy Markdown

roborev: Combined Review (288db88)

Reviewed the combined feedback: one medium-severity issue requires attention.

Medium

  • internal/tui/model.go:353 — If the initial repository-scoped refresh fails, the model returns without starting the one-time global refresh. A deleted or unreadable cached launch repository can leave the dashboard stuck indefinitely on stale cached rows.
    • Suggested fix: After recording the repository refresh failure, start the status-free current-dashboard refresh when backgroundGlobalStarted is false.

Reviewers: 2 done | Synthesis: codex, 7s | Total: 18m1s

A missing or unreadable launch repository must not strand the TUI on cached rows. Continue with the status-free global refresh after recording the scoped refresh error.

Generated with Codex
Co-authored-by: Codex <noreply@openai.com>
@roborev-ci

roborev-ci Bot commented Aug 22, 2026

Copy link
Copy Markdown

roborev: Combined Review (c11425e)

Two medium-severity correctness risks remain in the TUI’s removal and confirmation flows.

Medium

  • internal/tui/model.go:1912 — Removed rows can be restored by stale inventory responses. A successful queued removal invalidates fleet results but not in-flight inventory results. An older repository/global response can restore the deleted row, potentially as current and actionable, especially if the later refresh fails. Immediately supersede inventory requests or retain a removal tombstone until a post-removal refresh confirms the row is absent.

  • internal/tui/model.go:1465 — Confirmations can act on stale session data. Confirmations retain their original row while background inventory updates continue. A later confirmation can dispatch stale data; killing uses the old endpoint without generation/context matching and could terminate a replacement session with the same name. Re-resolve and validate the row’s freshness, generation, and endpoint when confirming, or cancel the confirmation when inventory changes it. Use context-matched session termination.


Reviewers: 2 done | Synthesis: codex, 10s | Total: 17m13s

A completed queued removal must take precedence over inventory that started before it. Supersede those responses immediately so they cannot restore a deleted row while another removal runs.

Confirm actions against the current row and use context-matched tmux termination. This prevents a delayed confirmation from acting on a changed workspace or replacement session.

Generated with Codex
Co-authored-by: Codex <noreply@openai.com>
@roborev-ci

roborev-ci Bot commented Aug 22, 2026

Copy link
Copy Markdown

roborev: Combined Review (13cd50a)

Verdict: Two medium-severity issues require attention before merge.

Medium

  • internal/tui/model.go:1163 — Escaping from a project perspective widens the view without starting the status-bearing global refresh. Other projects remain stale, and their mutations can be blocked indefinitely with “inventory is refreshing” even though no refresh is running.
    Fix: Call startFetch when clearing projectPerspective, matching the project picker’s “all” behavior.

  • internal/tui/model.go:1490 — Removal confirmation revalidation checks only path, generation, and session state. Because branch or HEAD changes do not alter the worktree generation, an external checkout after a dirty-delete prompt could cause forced removal of a different checkout than the one confirmed.
    Fix: Include the expected branch/HEAD in the removal request, verify them atomically before forced removal, and reject refreshed rows when sameCheckout fails.


Reviewers: 2 done | Synthesis: codex, 8s | Total: 16m51s

wesm and others added 2 commits August 22, 2026 17:49
Leaving a project view exposes projects whose status may still be stale. Start the global status refresh immediately so those rows do not remain blocked without active work.

A dirty-delete confirmation must authorize one exact checkout. Carry its branch and HEAD into the mutation-locked removal preflight so an external checkout or commit change preserves the worktree.

Generated with Codex
Co-authored-by: Codex <noreply@openai.com>
Forced TUI removal now requires the checkout identity supplied by real inventory rows. Include that identity in the existing forced-removal fixtures so they continue to exercise generation rejection and dirty removal.

Generated with Codex
Co-authored-by: Codex <noreply@openai.com>
@roborev-ci

roborev-ci Bot commented Aug 22, 2026

Copy link
Copy Markdown

roborev: Combined Review (9af3004)

The changes have two medium-severity correctness and responsiveness issues; no critical or high-severity findings or material security vulnerabilities were identified.

Medium

  • internal/tui/model.go:568 — Remote-only rows can remain indefinitely blocked after materialization.
    While scoped to a project, materialization triggers a status-free dashboard refresh because the local repository row does not yet exist. The new row is marked stale, so workspace creation remains blocked with a “refreshing” message even though no refresh is running.
    Suggested fix: After materialization, request a status-bearing dashboard refresh, or chain a current-repository refresh after discovering the new local row.

  • internal/cmd/tui_backend.go:1779 — Worktree removal holds the backend mutex during Git operations.
    RemoveWorktree retains the mutex while prepareRemoval invokes several Git commands through repositoryRootForRow. Slow or hung Git inspection can block inventory configuration and other backend operations, undermining responsive background removal.
    Suggested fix: Snapshot configuration and function dependencies under the mutex, release it, then perform repository-root resolution and validation using the operation context.


Reviewers: 2 done | Synthesis: codex, 9s | Total: 15m36s

wesm and others added 2 commits August 22, 2026 19:04
A remote-only worktree must receive current status before the TUI enables mutations. Refresh status after materialization so the new local row does not remain blocked.

Repository inspection can be slow. Keep it outside the backend configuration lock so inventory updates remain responsive while a removal is prepared.

Generated with Codex
Co-authored-by: Codex <noreply@openai.com>
Removal preparation now validates against a configuration snapshot. The old backend-bound wrapper has no caller and fails the repository lint gate.

Generated with Codex
Co-authored-by: Codex <noreply@openai.com>
@roborev-ci

roborev-ci Bot commented Aug 22, 2026

Copy link
Copy Markdown

roborev: Combined Review (80550ab)

Changes need revision due to three medium-severity correctness and compatibility issues.

Medium

  • Capability version does not cover guarded removal fieldsinternal/cmd/daemon_client.go:230, internal/daemon/types.go:18
    ExpectedBranch and ExpectedHead extend the guarded-removal contract, but the client still negotiates worktree.removal.v2 with an unchanged API schema. An older v2 daemon may ignore the new checkout guard during forced removal or reject compatible requests. Introduce and require a new removal capability/version, advertised only by daemons that enforce both fields.

  • HEAD changes are ignored at freshness and confirmation boundariesinternal/tui/model.go:522, internal/tui/model.go:1509
    sameCheckout ignores commit changes on named branches. This can preserve status from an old commit and allow an open deletion confirmation to rebind to a changed row, bypassing the intended changed-HEAD rejection. Compare branch, generation, and commit for every worktree at these boundaries; mark the project stale or reject confirmation when HEAD changes.

  • Branch creation can use stale or replaced inventory stateinternal/tui/model.go:1089, internal/tui/model.go:1242, internal/tui/model.go:1401
    Freshness is checked before entering new/existing-branch input modes, but inventory may change while the input remains open. Submission can then use a newly selected row or stale saved branchRow. Capture the original target when opening the input and, upon submission, verify that the same generation and project still exist and remain current.


Reviewers: 2 done | Synthesis: codex, 19s | Total: 17m5s

An older daemon must not accept forced removal without enforcing the confirmed branch and HEAD. Require the new checkout-removal capability while retaining the existing session-removal contract.

Inventory can change while a confirmation or branch prompt is open. Reject the action when the checkout identity, project, or freshness no longer matches what the user reviewed.

Generated with Codex
Co-authored-by: Codex <noreply@openai.com>
@roborev-ci

roborev-ci Bot commented Aug 22, 2026

Copy link
Copy Markdown

roborev: Combined Review (b2386d2)

The PR has one medium-severity issue; no high or critical findings were reported.

Medium

  • internal/status/status_collector.go:225 — When FetchRemote is enabled, the status command does not explicitly pass --ahead-behind. If status.aheadBehind=false, Git reports unknown counts that are parsed as zero, silently hiding ahead/behind commits.
    • Suggested fix: Pass --ahead-behind whenever fetchRemote is true, and add a behavioral test covering status.aheadBehind=false.

Reviewers: 2 done | Synthesis: codex, 6s | Total: 17m41s

wesm and others added 2 commits August 22, 2026 21:12
kwt is unreleased, so its existing v2 removal contract has no installed compatibility boundary. Extend that contract with branch and HEAD guards instead of adding a permanent v3 layer.

Generated with Codex
Co-authored-by: Codex <noreply@openai.com>
Windows can spell one temporary directory through both short-name and
long-name paths. Verify that the diagnostic identifies the failed worktree
without requiring one operating-system-specific full path.

Generated with Codex
Co-authored-by: Codex <noreply@openai.com>
@roborev-ci

roborev-ci Bot commented Aug 22, 2026

Copy link
Copy Markdown

roborev: Combined Review (f77a8e3)

Two medium-severity correctness issues remain; no concrete security vulnerabilities were found.

Medium

  • internal/tui/model.go:397-399 — The scoped repository merge overwrites m.rows before preserving creation state. If a refresh omits an in-progress worktree, its optimistic row disappears despite remaining in m.creating, allowing duplicate creation attempts and violating the documented merge behavior.

    • Fix: Save the previous rows before mergeRepositoryRows, pass them as the second argument to mergeCreatingRows, and add a scoped-refresh test where the pending worktree is absent.
  • internal/tui/model.go:1976-1983 — Known stale-state removal failures do not trigger a refresh. If a clean worktree becomes dirty or changes branch/HEAD after confirmation, removal fails while the row remains marked current, causing repeated deletion attempts to use stale status.

    • Fix: Treat dirty and checkout-condition conflicts as refresh-required, or invalidate and refresh the affected project after these failures.

Reviewers: 2 done | Synthesis: codex, 7s | Total: 21m47s

An active creation must remain visible until its command finishes, even when
a scoped listing has not discovered it. A rejected removal can mean that the
checkout or status changed after confirmation, so refresh the affected project
before another action.

Generated with Codex
Co-authored-by: Codex <noreply@openai.com>
@roborev-ci

roborev-ci Bot commented Aug 22, 2026

Copy link
Copy Markdown

roborev: Combined Review (778a31a)

No Medium, High, or Critical findings; the reviews found no material issues at the requested severity threshold.


Reviewers: 2 done | Synthesis: codex, 5s | Total: 16m29s

@wesm
wesm merged commit 38f0ea1 into main Aug 22, 2026
6 checks passed
wesm pushed a commit that referenced this pull request Aug 23, 2026
This adds `kwt changes [path]`, a focused command for seeing exactly which files changed in one registered worktree. It reports staged and working-tree changes separately, including untracked files, renames, copies, deletions, and conflicts. Human output is intended for interactive use; `--json` provides deterministic ordering and a stable contract. The same inspection API is available to Go consumers.

Kwt checks the worktree’s durable generation before and after reading Git status. If the worktree is removed or replaced during inspection, kwt discards the result instead of returning changes for the wrong checkout.

The daemon supplies current inventory and configuration, while Git runs in the foreground client with bounded time and output. Kwt credentials and repository-routing variables are removed from Git’s environment while trusted Git configuration remains available. If an exact change list exceeds the output limit, kwt returns an actionable error instead of partial file records.

This does not change the status accounting or TUI presentation introduced in #104. The TUI-related code here only keeps credential filtering current during repository refreshes.

This PR also raises kwt’s source-build baseline to Go 1.27.0, matching the other Go projects. The resulting macOS support floor is version 13.

Out of scope: patches or file contents, fetching, watching, caching, sibling-worktree discovery, and daemon-owned status polling.


Co-authored-by: sarcasticbird <sarcasticbird@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

1 participant