Skip to content

(changes): list the worktrees of a session's subagents in its Changes panel (#303) - #398

Merged
devsuitup merged 5 commits into
mainfrom
feat/303-subagent-worktree-changes
Oct 2, 2026
Merged

devsuitup merged 5 commits into
mainfrom
feat/303-subagent-worktree-changes

Conversation

@devsuitup

@devsuitup devsuitup commented Oct 1, 2026 •

Copy link
Copy Markdown
Owner

Closes #303.

What changed

A session's Changes panel now also lists the changes in the worktrees its subagents work in. For each subagent whose sidecar (agent-<id>.meta.json) records a worktreePath distinct from the session's own directory, the list shows a header (the agent's description, else its type, else its id, then its branch) followed by that worktree's changed files. Rows open as read-only diffs through the existing git-changes-diff path.

Main side:

  • resolveGitChangesTarget accepts sub:<parent>:<agent> only with {allowSubagent: true}; git-changes-status and git-changes-diff opt in, so edit, save, locate, watch, the panel shell and the terminal path links keep refusing it. Both id parts are validated before any dependency runs.
  • The path is read from the sidecar, not the transcript (it exists when transcript saving is off). It must be absolute, free of control characters and .. segments, not start with two separators (UNC); an extended-length drive path is normalised, and exist. A removed worktree is reason: 'worktree-removed'.
  • A missing or unparsable sidecar is reason: 'no-worktree-recorded'; the parent's rows are never shown as the agent's. A sidecar that records no worktreePath means the agent shares the parent's directory.
  • listSubagentWorktrees (async, local parents only): sidecars read asynchronously, 8 at a time, and cached per path; only worktrees that exist, differ from the parent's cwd (case-insensitive on Windows) and are linked worktrees of the parent's repository (their .git file, read every pass, names a directory under the parent's <common dir>/worktrees/) are kept; answers are cached with lifetimes (5 min for a recorded worktree, 30 s for removed ones and the parent's common dir, 10 s for an unreadable sidecar); the 24 newest are scanned and the rest counted. collectSubagentChanges runs status() 3 at a time and keeps the groups with files: the cap of 8 applies to those, and the rest are counted (+N more subagent worktrees not shown). The result is subagents and subagentsOmitted on the status reply, computed on the refresh the panel already does; no new watcher. git-changes-status and git-changes-diff apply the same repository test to a sub: target, so git never runs in a worktree of another repository, and every git run in a subagent worktree uses -c core.fsmonitor=false.

Renderer (public/file-panel.js): group header and rows; a subagent row's click skips the editable content pair and diffs through the sub: id; selection and untracked counts match on (subSessionId, path). Each group is capped at 100 rows.

Divergence from the issue: the issue prefers a selectable subagent with its own panel over a grouped parent panel and says not to do both. The grouped parent panel was chosen on review; the sub: resolution is shared by both shapes. Subagents with a removed worktree are omitted from the list rather than shown with an error. The parent's own cwd keeps accepting a UNC path (not changed here).

Tests

  • test/git-changes-target.test.js: shape validation, refusal before any disk access, worktree resolution, shared cwd, removed worktree, unsafe and UNC paths, missing sidecar reason, listing (labels, filtering, bound), collecting.
  • test/git-changes-file.test.js: only status and diff opt in; status handler wiring.
  • test/dom-file-panel-changes.test.js (jsdom): no group when none, group header and rows apart from parent rows, summary with an empty own directory, read-only diff through the sub id with no editor or watch, selection and counts not leaking onto the parent's same-path row, diff error shown.
  • Red first throughout. Mutations turned red then restored: shape opt-in, dot check, absolute check, UNC check, removed-worktree check, sidecar reason, parent-equal filter, empty-group filter, status wiring (twice), row selection on path alone, editor opened for a subagent row, untracked counts on the parent record.

CI

The failure of test (20, ubuntu-latest) on 1f6222d (run 36907042297) is npm error HTTPError: Response code 500 during dependency install, before any test ran; no test change addresses it.

Not verified

No live run against a real worktree-isolated subagent or in the running app; behaviour is covered with injected dependencies and jsdom. The recorded worktreePath is trusted like the session's own recorded cwd.

Refs #303. A subagent working in a worktree of its own has its path in its
meta.json sidecar. The target resolver now accepts sub:<parent>:<agent> for
the read-only status and diff IPCs only, validates both parts before any disk
access, reports a removed worktree, and shares the parent's target when the
agent records none. The panel is not wired to it yet.
@devsuitup devsuitup added the no-changelog The PR changes nothing a user sees; the CHANGELOG.md check is waived label Oct 1, 2026
@devsuitup

Copy link
Copy Markdown
Owner Author

Reviewing 1f6222d (adversarial review in progress).

@devsuitup

Copy link
Copy Markdown
Owner Author

Adversarial review at 1f6222d: no blocker. The sidecar worktreePath is validated more strictly than the parent's recorded cwd (absolute, no .., no control chars, exists), ids are checked before any disk access, remote folders are refused, and only git-changes-status/git-changes-diff opt in. Held back for: no consumer yet (the renderer slice is being added to this PR so it closes #303), UNC paths still pass path.isAbsolute, and an unreadable sidecar silently falls back to the parent's rows.

… panel

Closes #303. The parent session's panel now shows, after its own rows, one
group per subagent working in a worktree of its own, headed by the agent's
name and branch. Rows open as read-only diffs through the sub: id; edit,
save, locate and watch still refuse it. A UNC worktree path in a sidecar is
refused, and a missing or unparsable sidecar is a distinct reason instead of
the parent's rows.
@devsuitup devsuitup removed the no-changelog The PR changes nothing a user sees; the CHANGELOG.md check is waived label Oct 1, 2026
@devsuitup devsuitup changed the title (changes): resolve a subagent's worktree for the Changes status and diff (#303) (changes): list the worktrees of a session's subagents in its Changes panel (#303) Oct 1, 2026
@devsuitup

Copy link
Copy Markdown
Owner Author

Reviewing 6f1c7b7 (adversarial review in progress).

@devsuitup

Copy link
Copy Markdown
Owner Author

Re-review at 6f1c7b7: the three earlier points are addressed (renderer consumes the listing, UNC rejected, missing sidecar → no-worktree-recorded); read-only holds in main and in the renderer; selection keyed on (subSessionId, path). Being fixed: (1) the 8-group cap is applied before git status, so clean or failed worktrees take slots and dirty ones past the 8th vanish silently; (2) every refresh reads every subagent sidecar synchronously on the main process and runs up to 8 git status in parallel; (3) git status in a sidecar-named worktree runs that repo's fsmonitor — will be restricted to the parent's repo or run with fsmonitor off; (4) the UNC check also rejects local \?\C:\ paths. The PR shows appended read-only groups, not the selectable-subagent shape the issue preferred — stated in the body.

Refs #303. The cap of 8 now counts groups that have changes, and the panel
says how many it left out. Sidecars are read asynchronously, a few at a
time, and cached; git status runs three at a time over the 24 newest
worktrees. A subagent worktree must share the session's git common dir
before git reads it (listing, status and diff). An extended-length drive
path is normalised instead of refused as UNC.
@devsuitup

Copy link
Copy Markdown
Owner Author

Reviewing af8cd7f (adversarial review in progress).

@devsuitup

Copy link
Copy Markdown
Owner Author

Re-review at af8cd7f: the cap, the async bounded reads, the long-path normalisation and the case-insensitive compare are fixed. Still open: the common-dir cache never expires (a worktree recreated at the same path as another repo would pass the ownership check), and the check should not rest on git rev-parse alone — the worktree's .git file will be required to point under the parent's worktrees/, and status/diff there will run with core.fsmonitor=false. Scan cap and negative caching being tightened too.

…re the caches

Refs #303. A subagent worktree is listed and read only when its .git file
names a directory under the session repository's worktrees directory, read
on every pass; the parent's common dir, removed worktrees and sidecar answers
are cached with a lifetime instead of for good. Git runs in a subagent
worktree with core.fsmonitor off. The scan bound counts scanned candidates,
the ones not scanned are included in the omitted note, and 'newest' is by
parsed time.
@devsuitup

Copy link
Copy Markdown
Owner Author

Re-review at 23ab9bd: closed. Ownership no longer rests on a cached git rev-parse: worktreeBelongsTo reads <wt>/.git on every pass (no git spawn), requires it to be a file whose gitdir: target resolves under <parentCommon>/worktrees/ (lexical containment after resolve, so .. cannot escape) — the git dir and its config.worktree are then the parent's own; every git call for subagent targets and status groups runs with -c core.fsmonitor=false. Caches are TTL-based (removed/no-worktree 30 s, unreadable 10 s, recorded 5 min), at most 24 candidates scanned with the rest counted in the omitted note, "newest" sorted by Date.parse. Ready to merge once CI is green on 23ab9bd.

# Conflicts:
#	CHANGELOG.md
#	main.js
@devsuitup
devsuitup merged commit d8132c6 into main Oct 2, 2026
10 checks passed
@devsuitup
devsuitup deleted the feat/303-subagent-worktree-changes branch October 2, 2026 10:00
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.

(changes): a session's panel ignores the worktrees its subagents are working in

1 participant