Skip to content

Add repository-declared diff file categories - #155

Merged
0101 merged 29 commits into
mainfrom
diff-categories
Jul 29, 2026
Merged

Add repository-declared diff file categories#155
0101 merged 29 commits into
mainfrom
diff-categories

Conversation

@0101

@0101 0101 commented Jul 29, 2026

Copy link
Copy Markdown
Owner

Problem

A large branch diff opens in the worktree diff viewer as one flat accordion of files. There is no way for a repository to say which files matter most, so a reviewer scrolls a mixed list of production code, tests, docs, and instruction files with no architectural shape and no cue about where the substance of a change lives.

Changes

Adds an optional, repository-declared categorization of changed files. Everything is additive — a repository without configuration keeps today's flat list.

Configuration (src/Server/DiffCategories.fs, src/Server/TreemonConfig.fs)

  • New optional diffCategories array in the repo-root .treemon.json, shared by every linked worktree alongside baseBranch/upstreamRemote.
  • A node has a name plus exactly one of patterns (leaf) or children (branch). Validation enforces sibling-name uniqueness, max depth 4, at most 200 nodes, 50 patterns per leaf, and name/pattern length bounds, and reserves the top-level name Other.
  • Configuration resolves to Missing, Invalid, or Configured. Invalid renders the flat list plus a bounded, non-blocking warning that never leaks a path, raw JSON, or exception text.
  • Re-read and validated on every summary request, so Refresh picks up an edited file immediately.

Matching

  • A bounded glob subset — literals, ?, * within a segment, ** across segments — matched ordinally against the whole repo-relative path. Repository text is never compiled as a regex.
  • Matching simulates reachable pattern positions instead of backtracking, so a star-crowded pattern such as a***b or src/**/** costs pattern length times input length. Literal and fixed-depth patterns short-circuit to an ordinal comparison.
  • Depth-first, first-matching-leaf-wins precedence; a rename matches its new path first and falls back to its old path. Unmatched files land in a trailing Other group.

Presentation (src/Server/Assets/diff/viewer.js, viewer.css)

  • Nested category headers with aria-expanded, a subtree file count, and aggregated +X/−Y line stats rendered like a file row's own stats.
  • A run of single-child categories collapses into one row labelled Parent > Child, at any depth.
  • Per-summary disclosure defaults favour an architectural overview: small leaves open, large ones close, and a branch whose subtree is large forces only its direct children collapsed.
  • Explicit expand/collapse choices survive Refresh and layer-filter changes, keyed by a collision-free path serialization. Collapsing a category holding the open file collapses it and aborts its in-flight patch request. Keyboard navigation moves only between visible rows.

Configure action (src/Server/CanvasDocServer.fs, src/Server/WorktreeDiffApi.fs)

  • An icon-only toolbar action in the embedded viewer sends the fixed configure-diff-categories message through the SystemView canvas transport to the worktree's agent session, reusing existing routing, waiting, and delivery-error banners. Not rendered in a standalone diff tab or without a transport.
  • The request text is fixed in the viewer script and derives nothing from repository content, so generated diff.html stays byte-identical across repositories. A unit test pins its stated depth bound and reserved name to DiffCategories.maxDepth and DiffCategories.reservedTopLevelName so prose and validator cannot drift.
  • The wait resolves by polling the repository's categorization revision — not the agent's turn — then reloads the summary automatically.

CLI (src/Cli/Program.fs)

  • tm categories [path] reports every declared leaf as its full path with the number of tracked files that classify into it, flags leaves matching zero files, and reports the unmatched count. Missing and Invalid exit non-zero so it works as a check; category names are stripped of control characters before printing.

Refactors carried along

  • DiffTemplate.html shrinks from ~1,400 lines to a shell by extracting its CSS and JS into self-hosted assets served through DiffAssets.
  • Diff summary requests now pair comparison context and categorization in a single DiffSummaryTarget, so a context and categorization from two repositories are not representable.
  • Category test fixtures and the viewer harness are extracted out of DiffEndpointTests.fs and DiffViewerTests.fs into shared helper modules.

Docs

  • New docs/spec/diff-file-categories.md; updates to worktree-diff-viewer.md, worktree-monitor.md, future/code-improvements.md, and README.md.

Tests

dotnet test src/Tests/Tests.fsproj --filter "Category=Fast"1808 passed, 0 failed (3m31s).

New coverage: DiffCategoriesTests.fs (schema, bounds, glob matcher), DiffCategoryTests.fs (viewer rendering, disclosure, flattening, line stats), DiffCategoryEndToEndTests.fs, DiffEndpointCategorizationTests.fs, and tm categories cases in CliTests.fs.

0101 and others added 28 commits July 28, 2026 23:26
Add the bounded category model and validation, an owned TreemonConfig reader, and focused schema tests.
…tion

Implemented bounded glob matching, depth-first classification, stable grouping, and focused tests.
…y responses

Carry category paths and categorization state through diff summary responses, with endpoint coverage.
…mary request

Resolve repository configuration from each diff request and add coverage for linked worktrees, configuration edits, and unknown worktrees.
Render configured category hierarchies with counts, fallback states, warnings, and DiffViewer coverage.
…egory toggles

Compute category disclosure defaults, preserve page-instance toggles, and add end-to-end coverage.
Add the embedded diff configure action, shared invalid-warning action, routing request, and E2E coverage.
…ction

Integrate nested category disclosure with file selection, patch cancellation, and visible-row keyboard navigation, with E2E coverage and spec updates.
… handling in DiffCategories

Collapse repeated stars and add literal and fixed-depth matcher fast paths with regression coverage and spec documentation.
…ategories validation

Reject oversized sibling arrays before duplicate-name validation, enforce trimmed category names at 100 characters, and document and test both bounds.
…e validator's constants

Expose the schema constants internally and add a viewer template contract test plus the spec note.
Add DiffSummaryTarget to keep summary comparison context and categorization paired through the request handler.
…on resumes a session

Clarify that configure routing starts or queues a session when needed and align the viewer comment with startup terminology.
…lf-hosted asset files

Move the diff viewer stylesheet and script into embedded self-hosted assets, serve them with revalidation policies, and update documentation and tests.
Remove the completed DiffTemplate asset extraction from the remaining WorktreeDiff refactoring idea.
…iffEndpointTests.fs

Extract shared diff-endpoint scaffolding and categorization test modules into dedicated files, registering them before DiffEndpointTests.fs.
…erTests.fs

Extract shared viewer harness code and category-specific E2E tests into focused test files.
…ndpointTests

Simplify fixture default insertion and document the locally owned JsonNode mutation.
…lper in DiffViewerTests

Add DiffViewerHarness.RouteSummaries, replace duplicated route callbacks, and document verification coverage.
…tion

Drop the unnecessary summary request counter and wait from the late patch response category test.
Exercise the real path end to end: a temp Git repo, a repository-root
.treemon.json, WorktreeDiffApi.liveService with real git subprocesses, a
Kestrel canvas host on a free loopback port, and a real Chromium page.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: a5c0840d-68c2-4631-8efa-3747509be31a
The action posted a fire-and-forget canvas message: no feedback, re-clickable,
and the new grouping only appeared if the user pressed Refresh.

The control now disables itself and shows a spinner while the request is
outstanding, shared by its toolbar and warning copies. Completion is resolved by
watching the repository's categorization rather than the agent, since an agent
can end its turn without writing anything: diff-categorization serves the
categorization alone, reading and validating .treemon.json with no Git call, so
polling costs a file read instead of a diff. A revision derived from the
validated outline detects a rewrite that leaves the status configured, which a
status alone cannot, while ignoring reformatting. One serializer produces the
block for both the summary and the new route.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: a5c0840d-68c2-4631-8efa-3747509be31a
…the CLI

Add the tm categories command, diff-category report API, coverage helpers, tests, and documentation.
Clarify that existing diff behavior is unchanged and document tracked-file enumeration.
…r and self-verification

Clarify category display ordering, require tm categories verification, update the spec decision, and pin the prompt behavior in tests.
Flatten category chains before rendering, preserve collapse state, and document and test the behavior.
Aggregate changed-line totals across category subtrees, render them on category headers, and cover the behavior with focused tests.
Copilot AI review requested due to automatic review settings July 29, 2026 10:17
@0101
0101 enabled auto-merge (squash) July 29, 2026 10:20
@0101
0101 disabled auto-merge July 29, 2026 10:21

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds repository-defined categories to organize worktree diffs while preserving flat-list behavior when unconfigured.

Changes:

  • Adds validated nested category configuration, glob matching, reporting API, and CLI command.
  • Adds categorized viewer rendering, disclosure state, and agent-assisted configuration.
  • Extracts viewer assets and adds broad unit/E2E coverage and documentation.
Show a summary per file
File Description
src/Server/DiffCategories.fs Validates, matches, orders, and reports categories.
src/Server/TreemonConfig.fs Reads category configuration.
src/Server/WorktreeDiffApi.fs Classifies summaries and exposes categorization.
src/Server/WorktreeDiff.fs Enumerates tracked files.
src/Server/WorktreeApi.fs Builds category coverage reports.
src/Server/RefreshScheduler.fs Resolves owning repositories.
src/Server/CanvasDocServer.fs Routes category-aware diff requests.
src/Server/DiffAssets.fs Serves extracted viewer assets.
src/Server/DiffTemplate.html Becomes the viewer shell.
src/Server/Assets/diff/viewer.js Implements category UI and configuration flow.
src/Server/Assets/diff/viewer.css Styles categories and warnings.
src/Server/Server.fsproj Registers sources and embedded assets.
src/Shared/Types.fs Adds category wire/report types.
src/Shared/WorktreeApi.fs Adds the report API operation.
src/Cli/Program.fs Adds tm categories.
src/Tests/Tests.fsproj Registers new test modules.
src/Tests/DiffCategoriesTests.fs Tests validation, matching, and coverage.
src/Tests/DiffCategoryTests.fs Tests category viewer behavior.
src/Tests/DiffCategoryEndToEndTests.fs Tests the live repository/viewer path.
src/Tests/DiffEndpointCategorizationTests.fs Tests category endpoint integration.
src/Tests/DiffEndpointTestHelpers.fs Shares endpoint fixtures.
src/Tests/DiffEndpointTests.fs Updates endpoint serialization coverage.
src/Tests/DiffViewerTestHarness.fs Shares viewer routing fixtures.
src/Tests/DiffViewerTests.fs Updates viewer and asset tests.
src/Tests/CliTests.fs Tests category report formatting.
src/Tests/CanvasDocServerTests.fs Tests owning-repository resolution.
src/Tests/CreateWorktreeTests.fs Updates API stubs.
README.md Documents the CLI command.
docs/spec/diff-file-categories.md Specifies category behavior and architecture.
docs/spec/worktree-diff-viewer.md Integrates categories into viewer behavior.
docs/spec/worktree-monitor.md Documents repository-level configuration.
docs/spec/future/code-improvements.md Updates the remaining refactoring item.

Review details

  • Files reviewed: 32/32 changed files
  • Comments generated: 4
  • Review effort level: Medium

Comment thread src/Server/DiffCategories.fs Outdated
Comment thread src/Server/WorktreeDiffApi.fs Outdated
Comment thread src/Server/DiffCategories.fs
Comment thread src/Server/Assets/diff/viewer.js
Bound the total pattern text a repository may declare, so a valid
configuration cannot make one summary's classification cost minutes:
10,000 patterns against the 1,000-file cap measured 405 s, and 5,000
pattern characters holds the same worst case to 1.2 s.

Length-prefix names and patterns in the categorization revision, so a
name or pattern containing the punctuation between them cannot make two
different outlines encode alike and hide a rewrite from the poller.

Require a viewer instance and an empty query on diff-categorization,
which the endpoint contract already stated for all three routes.

Capture the configure wait's baseline revision before dispatching the
request rather than on the first poll, so a configuration written within
the poll interval is still seen as a change.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 40e14c86-fbe3-41f9-836b-4bce07eb7c9a
@0101
0101 merged commit 7819ee1 into main Jul 29, 2026
1 check passed
@0101
0101 deleted the diff-categories branch July 29, 2026 11:27
0101 added a commit that referenced this pull request Jul 29, 2026
Treemon's mechanical sync conflicted and aborted; resolved by hand.

main added repository-declared diff categories (#155) and consolidated the
dashboard font sizes (#156) while this branch moved the scheduler state slice
into SchedulerState and reshaped worktreeApi's dependencies, so the two collided
on five files:

- WorktreeApi: kept both sides - main's diffCategoryReport helper and this
  branch's WorktreeApiDependencies record. The record already carries
  AutoSyncStore, which main's positional signature did not have.
- CanvasDocServer: took main's resolveDiffTarget (it replaces the older
  getDiffComparisonContext) and retyped it to SchedulerState.StateMsg, dropping
  the stale duplicate resolver body this branch still had.
- DiffEndpointTests: took main's deletion - those helpers now live in the new
  DiffEndpointTestHelpers.fs. This branch's only edits there were the module
  rename, which is reapplied below.
- CanvasDocServerTests: kept main's new repoId binding with this branch's
  SchedulerState.createAgent.
- worktree-monitor.md: kept this branch's GitWorktree/GitBranchSync rows and
  took main's TreemonConfig row, which now names the diffCategories read.

main's new files still referenced RefreshScheduler for the state members this
branch moved, so DiffEndpointTestHelpers.fs, DiffCategoryEndToEndTests.fs and
one WorktreeApi call site were repointed at SchedulerState.

Server, client and tests build clean; fast suite 1878 passed.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 80f775f5-e421-45b9-acac-d4f981b3f691
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