Skip to content

diffr: read revisions with gix's specs, so X^! compares the commit - #1020

Closed
sidkmenon wants to merge 27 commits into
sm/cc-modfrom
sm/diffr-rev-specs
Closed

sidkmenon wants to merge 27 commits into
sm/cc-modfrom
sm/diffr-rev-specs

Conversation

@sidkmenon

Copy link
Copy Markdown
Contributor

diffr X^! showed X^ against the working tree. rev-parse accepts X^!, so diffr took it for one revision and compared it with the working tree, a different and usually much larger comparison, with no error. /diffr HEAD^! in the Claude Code pane did the same.

diffr now builds the comparison from the spec gix parses, the way git reads it, instead of splitting .. and ... by hand:

  • X^!: X against its parent; a root commit against the empty tree; a merge is refused, naming its parents.
  • X^- and X^-n: X against its first or nth parent (gix reads these as ranges).
  • a..b and a...b: as before, now from gix's range and merge specs.
  • X^@ and ^X: refused, since neither names one comparison.
  • --merge-base and --cached keep their rules, checked against the parsed spec.

Tests use git as the oracle: HEAD^!, HEAD^- and HEAD^-1 with a dirty working tree, so the old reading would show; HEAD^@; and a merge's ^! against HEAD^2 HEAD. diffr's full test suite passes.

sidkmenon and others added 27 commits October 7, 2026 19:19
Reverse the temporary rollback and restore the exact tree from 6c2c5a2. The following commit reapplies the Whiteboard 0.2.1 version fields.

Agent-Session: fb96350b-c19e-48f0-b364-31e9ad2b72f4
Agent-Session: a7af3b88-3bb7-48a2-ae20-7626a83628a1
Agent-Session: ab10ae26-42f2-4ecd-86d0-79f5a9edecf0
Carry forward the production release version in product.json and the two package manifests after restoring the original diffr import snapshot.

Agent-Session: fb96350b-c19e-48f0-b364-31e9ad2b72f4
Agent-Session: a7af3b88-3bb7-48a2-ae20-7626a83628a1
Agent-Session: ab10ae26-42f2-4ecd-86d0-79f5a9edecf0
Benchmark query compilation, full core/shape processing, and isolated context shaping with Criterion and bundled PR998 inputs. Document timing boundaries and plugin rebuild requirements.

Imported from devdotfast/diffr#103.

AI-assisted: OpenAI Codex.
Agent-Session: fb96350b-c19e-48f0-b364-31e9ad2b72f4
Agent-Session: 01a1196d-bad4-7f61-bafc-f63d12217676
Agent-Session: 7d7ac281-cc4b-4437-9e1e-3872651e8bcf
Agent-Session: a7af3b88-3bb7-48a2-ae20-7626a83628a1
A leaf's new `pair` field names the leaf on the other side with the
same `alignment_id`, and is absent for an added or deleted leaf.
Projection sets it as it numbers leaves, and a cut names each new
tail's partner. diffr-ts and the TUI's wire schema accept the field.

AI-assisted: developed with Claude Code.
Agent-Session: fb96350b-c19e-48f0-b364-31e9ad2b72f4
Agent-Session: 01a1196d-bad4-7f61-bafc-f63d12217676
Agent-Session: 7d7ac281-cc4b-4437-9e1e-3872651e8bcf
Agent-Session: a7af3b88-3bb7-48a2-ae20-7626a83628a1
`has_changes`, `is_one_sided` and change coverage asked whether a leaf
is paired by collecting every alignment id on the other side, once per
call. `paired_leaf` searched the other side for the matching leaf. They
now read the leaf's `pair`. Test fixtures name pairs from their
alignment ids.

AI-assisted: developed with Claude Code.
Agent-Session: fb96350b-c19e-48f0-b364-31e9ad2b72f4
Agent-Session: 01a1196d-bad4-7f61-bafc-f63d12217676
Agent-Session: 7d7ac281-cc4b-4437-9e1e-3872651e8bcf
Agent-Session: a7af3b88-3bb7-48a2-ae20-7626a83628a1
Every lookup by id searched both trees. The cursor now keeps each
region's side, parent and path from its side's root, so a lookup is a
probe and a descent. `cut` and `join` rewrite the entries of the
regions they move. The sides become private so nothing else can move a
region; the plugin host takes them back with `into_sides`.

No behavior change.

AI-assisted: developed with Claude Code.
Agent-Session: fb96350b-c19e-48f0-b364-31e9ad2b72f4
Agent-Session: 01a1196d-bad4-7f61-bafc-f63d12217676
Agent-Session: 7d7ac281-cc4b-4437-9e1e-3872651e8bcf
Agent-Session: a7af3b88-3bb7-48a2-ae20-7626a83628a1
`linked_regions`, `set_collapsed` and `link` walked both trees to find
the regions in a fold state. The cursor now keeps the regions of each
fold state, and `cut`, `join` and `link` update it.

AI-assisted: developed with Claude Code.
Agent-Session: fb96350b-c19e-48f0-b364-31e9ad2b72f4
Agent-Session: 01a1196d-bad4-7f61-bafc-f63d12217676
Agent-Session: 7d7ac281-cc4b-4437-9e1e-3872651e8bcf
Agent-Session: a7af3b88-3bb7-48a2-ae20-7626a83628a1
`startup/one_file` compiles the default queries, then diffs and projects
one fixture: what a run pays before its first result.

AI-assisted: developed with Claude Code.
Agent-Session: fb96350b-c19e-48f0-b364-31e9ad2b72f4
Agent-Session: 01a1196d-bad4-7f61-bafc-f63d12217676
Agent-Session: 7d7ac281-cc4b-4437-9e1e-3872651e8bcf
Agent-Session: a7af3b88-3bb7-48a2-ae20-7626a83628a1
Startup compiled the fold and context queries of every language before
diffing the first file. `Params` now keeps each language's assembled
sources and compiles them the first time that language is diffed.

A query that does not compile no longer fails the run at startup. It
fails each file in its language with a `query_error`; files in other
languages diff as usual. Reading query files and resolving their
imports still happens at startup.

AI-assisted: developed with Claude Code.
Agent-Session: fb96350b-c19e-48f0-b364-31e9ad2b72f4
Agent-Session: 7d7ac281-cc4b-4437-9e1e-3872651e8bcf
Agent-Session: a7af3b88-3bb7-48a2-ae20-7626a83628a1
diffr pprint renders saved NDJSON for people and agents. The script only
reproduced the TUI's rows as text, nothing ran it, and it reached into the
TUI's internals. mock_gemini.py served only its wrapper.

Agent-Session: 01a118c6-60a6-7373-bad2-7f763da2c3c2
Agent-Session: fb96350b-c19e-48f0-b364-31e9ad2b72f4
Agent-Session: a7af3b88-3bb7-48a2-ae20-7626a83628a1
Agent-Session: ab10ae26-42f2-4ecd-86d0-79f5a9edecf0
Agent-Session: 7d7ac281-cc4b-4437-9e1e-3872651e8bcf
palette.ts holds the palette, Helix theme mapping and theme config with no
Bun.TOML or node:fs, and events.ts parses NDJSON text with stream.ts left to
decode bytes. The model layer can then be bundled outside Bun. The bundled
dark and light palettes move to theme.ts with the rest of theme loading.

Agent-Session: 01a118c6-60a6-7373-bad2-7f763da2c3c2
Agent-Session: fb96350b-c19e-48f0-b364-31e9ad2b72f4
Agent-Session: a7af3b88-3bb7-48a2-ae20-7626a83628a1
Agent-Session: ab10ae26-42f2-4ecd-86d0-79f5a9edecf0
Agent-Session: 7d7ac281-cc4b-4437-9e1e-3872651e8bcf
…its rail (AI-assisted)

A collapsed fold's chevron and label arm it, so its chevron takes the accent
as an open scope's does. A syntax body folded behind a multi-line summary
keeps its chevron on the opener, stands the summary beside the scope's own
rail, and boxes its brackets while armed, as the Whiteboard client does.
Label lines name their fold with labelOf, so clicking the summary opens it.

Agent-Session: 01a118c6-60a6-7373-bad2-7f763da2c3c2
Agent-Session: fb96350b-c19e-48f0-b364-31e9ad2b72f4
Agent-Session: a7af3b88-3bb7-48a2-ae20-7626a83628a1
Agent-Session: ab10ae26-42f2-4ecd-86d0-79f5a9edecf0
Agent-Session: 7d7ac281-cc4b-4437-9e1e-3872651e8bcf
The TUI moves from tui/ to packages/tui, with Hunk's packages/hunk/src
flattened to src. xtask, the release archives and their smoke test, NOTICE
and the diffr-ts fixture path follow. Release archives keep mirroring the
repository's license paths, as NOTICE cites them. No code changes.

Agent-Session: fb96350b-c19e-48f0-b364-31e9ad2b72f4
Agent-Session: a7af3b88-3bb7-48a2-ae20-7626a83628a1
Agent-Session: ab10ae26-42f2-4ecd-86d0-79f5a9edecf0
Agent-Session: 7d7ac281-cc4b-4437-9e1e-3872651e8bcf
Theme parsing then runs anywhere, not only under Bun, so the code can be
shared with frontends that do not run on Bun.

Agent-Session: fb96350b-c19e-48f0-b364-31e9ad2b72f4
Agent-Session: a7af3b88-3bb7-48a2-ae20-7626a83628a1
Agent-Session: ab10ae26-42f2-4ecd-86d0-79f5a9edecf0
Agent-Session: 7d7ac281-cc4b-4437-9e1e-3872651e8bcf
packages/viewer holds what does not depend on OpenTUI, one folder per
domain: protocol (diffr's wire format, events and store), document
(regions, rows, the file tree and selection), viewport (row geometry),
terminal (text measurement and sanitizing) and theme (the palette, Helix
theme loading and the themes). It type-checks against ES2023 plus the few
web APIs every host has, so nothing Bun-, Node- or DOM-only creeps in.

packages/ becomes a Bun workspace with one lockfile, and xtask installs at
its root. Code moves unchanged apart from imports; the row and span types
join rows.ts, and the regions tests' file builders join fixture.ts.

Agent-Session: fb96350b-c19e-48f0-b364-31e9ad2b72f4
Agent-Session: a7af3b88-3bb7-48a2-ae20-7626a83628a1
Agent-Session: ab10ae26-42f2-4ecd-86d0-79f5a9edecf0
Agent-Session: 01a1196d-bad4-7f61-bafc-f63d12217676
Agent-Session: 7d7ac281-cc4b-4437-9e1e-3872651e8bcf
planCell lays one side of a code row out as coloured runs, with the cells a
click folds or opens and the scope the pointer focuses. CodeRowView draws
the runs and routes OpenTUI's mouse through those targets. No behaviour
change.

Agent-Session: fb96350b-c19e-48f0-b364-31e9ad2b72f4
Agent-Session: a7af3b88-3bb7-48a2-ae20-7626a83628a1
Agent-Session: ab10ae26-42f2-4ecd-86d0-79f5a9edecf0
Agent-Session: 01a1196d-bad4-7f61-bafc-f63d12217676
Agent-Session: 7d7ac281-cc4b-4437-9e1e-3872651e8bcf
…ted)

On a collapsed fold's label line, the pointer over an enclosing scope's rail
arms that scope, but a click opened the label's fold instead. A click now
picks the same target as the pointer.

Agent-Session: fb96350b-c19e-48f0-b364-31e9ad2b72f4
Agent-Session: a7af3b88-3bb7-48a2-ae20-7626a83628a1
Agent-Session: ab10ae26-42f2-4ecd-86d0-79f5a9edecf0
Agent-Session: 01a1196d-bad4-7f61-bafc-f63d12217676
Agent-Session: 7d7ac281-cc4b-4437-9e1e-3872651e8bcf
Viewer holds open files and folds, the reader's position, layout mode,
wrap, theme, pan, the scope under the pointer, and the fold and navigation
keys. It lays out the diff column at a given size and notifies on change,
so App reads it through useSyncExternalStore. App keeps its file tree,
menus, selection and breakdown. No behaviour change.

Agent-Session: fb96350b-c19e-48f0-b364-31e9ad2b72f4
Agent-Session: a7af3b88-3bb7-48a2-ae20-7626a83628a1
Agent-Session: ab10ae26-42f2-4ecd-86d0-79f5a9edecf0
Agent-Session: 01a1196d-bad4-7f61-bafc-f63d12217676
Agent-Session: 7d7ac281-cc4b-4437-9e1e-3872651e8bcf
The mod runs diffr and draws @diffr/viewer's rows, folds and keys in a
Claude Code pane, with its own file tree and title bar. Clicks, hovers,
drags and keys reach it through Client surface modules; each post resends
the inputs not yet acknowledged, since a later post replaces an undelivered
one. H and L pan by 16 columns, as Claude Code reports shifted letters.
build.ts bundles it into plugin/hooks.

Agent-Session: fb96350b-c19e-48f0-b364-31e9ad2b72f4
Agent-Session: a7af3b88-3bb7-48a2-ae20-7626a83628a1
Agent-Session: ab10ae26-42f2-4ecd-86d0-79f5a9edecf0
Agent-Session: 01a1196d-bad4-7f61-bafc-f63d12217676
Agent-Session: 7d7ac281-cc4b-4437-9e1e-3872651e8bcf
Generated by bun run build in diffr/packages/claude-code. Installs read the
plugin folder as is, so the bundle is committed. Its NOTICE points to diffr's
source and licenses for the code it bundles.

/plugin install diffr --marketplace devdotfast/whiteboard

Agent-Session: fb96350b-c19e-48f0-b364-31e9ad2b72f4
Agent-Session: a7af3b88-3bb7-48a2-ae20-7626a83628a1
Agent-Session: 01a1196d-bad4-7f61-bafc-f63d12217676
Agent-Session: 7d7ac281-cc4b-4437-9e1e-3872651e8bcf
A job type-checks and tests the viewer and the TUI, tests the mod, and
rebuilds the mod's bundle to check the committed one matches its source.
The build pins Bun 1.3.10, as the release workflow does, since another Bun
version emits a slightly different bundle.

Agent-Session: fb96350b-c19e-48f0-b364-31e9ad2b72f4
Agent-Session: a7af3b88-3bb7-48a2-ae20-7626a83628a1
Agent-Session: 01a1196d-bad4-7f61-bafc-f63d12217676
Agent-Session: 7d7ac281-cc4b-4437-9e1e-3872651e8bcf
It maps a fold's tint to a palette colour, as captureColor maps a syntax
capture, so the theme no longer depends on the document.

Agent-Session: fb96350b-c19e-48f0-b364-31e9ad2b72f4
Agent-Session: 01a1196d-bad4-7f61-bafc-f63d12217676
Agent-Session: 7d7ac281-cc4b-4437-9e1e-3872651e8bcf
Agent-Session: a7af3b88-3bb7-48a2-ae20-7626a83628a1
…ted)

They slice and wrap spans by their text and merge runs whose other fields
all match, so they no longer import the document's span type. That removes
the last import from a lower folder of @diffr/viewer to a higher one.

Agent-Session: fb96350b-c19e-48f0-b364-31e9ad2b72f4
Agent-Session: 01a1196d-bad4-7f61-bafc-f63d12217676
Agent-Session: 7d7ac281-cc4b-4437-9e1e-3872651e8bcf
Agent-Session: a7af3b88-3bb7-48a2-ae20-7626a83628a1
…g (AI-assisted)

visibleRows used only the start and end of Hunk's window, whose zero-height
rows and spacer sizes never occur in diffr: every measured row is at least
one line tall. A binary search for the first row in view and a walk to the
bottom replace it.

Agent-Session: fb96350b-c19e-48f0-b364-31e9ad2b72f4
Agent-Session: 01a1196d-bad4-7f61-bafc-f63d12217676
Agent-Session: 7d7ac281-cc4b-4437-9e1e-3872651e8bcf
Agent-Session: a7af3b88-3bb7-48a2-ae20-7626a83628a1
…isted)

wrapText, wrapTextByWidth, cellRangeToCharRange, fitText, padText,
measureWrappedSpansLineCount, formatTerminalPath, RenderSpan.transformFg,
and the sanitizer's options for keeping newlines and ANSI colours: only
sanitizeTerminalLine's behaviour is used. Their tests go with them.
rows.ts also stops re-exporting sourceLines, which callers import from
regions.ts.

Agent-Session: fb96350b-c19e-48f0-b364-31e9ad2b72f4
Agent-Session: 01a1196d-bad4-7f61-bafc-f63d12217676
Agent-Session: 7d7ac281-cc4b-4437-9e1e-3872651e8bcf
Agent-Session: a7af3b88-3bb7-48a2-ae20-7626a83628a1
counts.ts redeclared the wire's snapshot union as its own Snapshot, which
also shadowed the store's unrelated Snapshot. The protocol now exports it as
ComparisonEnd, and the store and the comparison label use it.

Agent-Session: fb96350b-c19e-48f0-b364-31e9ad2b72f4
Agent-Session: 01a1196d-bad4-7f61-bafc-f63d12217676
Agent-Session: 7d7ac281-cc4b-4437-9e1e-3872651e8bcf
Agent-Session: a7af3b88-3bb7-48a2-ae20-7626a83628a1
… git compares (AI-assisted)

Agent-Session: fb96350b-c19e-48f0-b364-31e9ad2b72f4
Agent-Session: 7d7ac281-cc4b-4437-9e1e-3872651e8bcf
Agent-Session: a7af3b88-3bb7-48a2-ae20-7626a83628a1
@sidkmenon
sidkmenon force-pushed the sm/cc-mod branch 3 times, most recently from c6a55e9 to 8765d7b Compare October 9, 2026 02:11
@sidkmenon

Copy link
Copy Markdown
Contributor Author

Replaced by #1068, the same change on main alone.

@sidkmenon sidkmenon closed this Oct 9, 2026
sidkmenon added a commit that referenced this pull request Oct 9, 2026
Use gix to parse instaed of handrolled parse crap for input git
revisions to the `diffr` cli (e.g.` diffr HEAD^1..HEAD`).

Text from a clanker:
```text
`diffr X^!` showed `X^` against the working tree. rev-parse accepts `X^!`, so diffr took it for one revision and compared it with the working tree, a different and usually much larger comparison, with no error.

diffr now builds the comparison from the spec gix parses, the way git reads it, instead of splitting `..` and `...` by hand:

- `X^!`: X against its parent; a root commit against the empty tree; a merge is refused, naming its parents.
- `X^-` and `X^-n`: X against its first or nth parent (gix reads these as ranges).
- `a..b` and `a...b`: as before, now from gix's range and merge specs.
- `X^@` and `^X`: refused, since neither names one comparison.
- `--merge-base` and `--cached` keep their rules, checked against the parsed spec.

Tests use git as the oracle: `HEAD^!`, `HEAD^-` and `HEAD^-1` with a dirty working tree, so the old reading would show; `HEAD^@`; and a merge's `^!` against `HEAD^2 HEAD`.

Replaces #1020, which was stacked on the /diffr mod; this is the same change on `main` alone.

https://claude.ai/code/session_01HTHD79i4QdthzVCkDZ26gd
```
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