Skip to content

Commit 8059d43

Browse files
committed
chore: terminal banner
1 parent 34738d2 commit 8059d43

3 files changed

Lines changed: 561 additions & 152 deletions

File tree

CHANGELOG.md

Lines changed: 119 additions & 110 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,22 @@
22

33
## [0.9.0] — 08-12-2026
44

5+
diffmind was built as a gate: run it, get a verdict, pass or fail. This release
6+
adds the other half — a place to *sit* while deciding what to say about someone
7+
else's branch. Both surfaces share one engine; neither replaces the other.
8+
9+
### Breaking
10+
11+
- **`--format json`: `stats.chunks``stats.units`**, plus `units_cached` and
12+
`units_unparseable`. The unit of review is now a region of a file rather than
13+
an arbitrary slice of lines, and the field names say so. Exit codes, SARIF and
14+
Markdown output are unchanged, so CI gates are unaffected.
15+
- **Daemon protocol changed.** A running 0.8 daemon is ignored rather than
16+
misused — the version gate already handled this — but you will want
17+
`diffmind serve --stop` after upgrading.
18+
- **`chunk_diff` removed** from the `core-engine` public API, superseded by
19+
`build_units`.
20+
521
### Security
622

723
- **Model weights are now pinned and verified.** Downloads used HuggingFace's
@@ -22,6 +38,64 @@
2238
Existing downloads are verified against the new pins on next use; the current
2339
files match, so no re-download is expected.
2440

41+
### Added
42+
43+
- **Code graph.** A tree-sitter symbol graph for 13 languages — Rust,
44+
TypeScript, TSX, JavaScript, Python, Go, Java, C#, Ruby, PHP, C, C++ and
45+
Scala — stored in `.diffmind/graph.db` and updated incrementally
46+
by mtime. Replaces the regex symbol index, which could only see `pub`/`export`
47+
declarations and had no concept of a reference at all.
48+
Review context now includes **the callers of every changed symbol** — a
49+
changed signature is judged against the code that depends on it — plus the
50+
enclosing definition, referenced definitions, and the file's tests. Everything
51+
stays inside a byte budget, so context does not grow with the repository.
52+
Bodies are read from the working tree rather than stored, so a snippet can
53+
never disagree with the file being reviewed. The graph refreshes itself
54+
incrementally before every review (~0.1s on 647 unchanged files) and builds on
55+
first use, so it can never fall behind the code; `--no-index` opts out. Adding a language is one entry in
56+
a table; contributions welcome.
57+
58+
- **Cross-file review units.** When a symbol and code that calls it both change
59+
in one diff, they are reviewed together as a single unit instead of separately.
60+
Reviewed apart, the model judges an interaction while seeing only one side of
61+
it as background. One call replaces two.
62+
- **Reviewer's cockpit** (`diffmind --tui`). Analyses on launch. Each finding
63+
shows the actual hunk the model reviewed and the context it was given.
64+
`a` accepts (and copies a review comment via OSC 52, which works over SSH),
65+
`d` dismisses, `w` marks wrong. Verdicts are written through immediately.
66+
- **Review standards as markdown**`.diffmind/rules/*.md`, scoped by path
67+
glob, committed to the repo. `diffmind rules init` / `rules list`. A finding
68+
the model attributes to a rule set gets the ID `rulebook.<id>` and suppresses
69+
like any other; an attribution naming a rule set that does not govern that
70+
file is discarded.
71+
- **Reportable pre-filter.** Lockfiles, `linguist-generated` paths, `@generated`
72+
banners, minified bundles, assets, snapshots and formatting-only hunks are
73+
dropped before the model sees them — and the run says what it skipped:
74+
`312 hunks → 74 reviewable (238 filtered: lockfiles, generated, formatting)`.
75+
Whitespace inside a string literal counts as content; indentation is never
76+
dismissed in Python or YAML.
77+
- **`diffmind stats`** — findings, cost and the accept-to-wrong ratio over
78+
recorded runs. Every review is filed to `.diffmind/runs/<sha>/`.
79+
- **Cost reporting** — wall-clock and token counts in the footer, in JSON, and
80+
in the run record. Estimated counts are marked `~` rather than passed off as
81+
exact.
82+
- **Revision ranges**`diffmind v1.2.0..HEAD`, or `--range`. Paths after a
83+
range narrow it.
84+
- **`review.ignore`** globs in `.diffmind/config.toml`.
85+
- diffmind writes its own `.diffmind/.gitignore`, keeping generated state out of
86+
git while leaving `rules/`, `rules.toml`, `config.toml` and `baseline.json`
87+
committable. Your repository's `.gitignore` is not touched.
88+
89+
### Changed
90+
91+
- `Graph::definitions_of` is replaced by `definitions_of_names` (batched) and
92+
`declarations_overlapping` (span-based). The "prefer a definition in this
93+
file" rule moved to `rag`, where the file being reviewed is known.
94+
- `ReviewAnalyzer::analyze`'s progress callback takes the unit being reviewed:
95+
`Fn(usize, usize)``Fn(usize, usize, &ReviewUnit)`. Callers that need to
96+
show a reader the hunk behind a finding should record it here rather than
97+
predicting the unit list with `plan_units`.
98+
2599
### Fixed
26100

27101
- **A non-ASCII path in a diff header could abort the process.** The
@@ -34,6 +108,21 @@
34108
which matters for binary files and mode changes, where no `+++ b/…` follows to
35109
correct it.
36110

111+
- **A large rule set could fail the whole review.** Two faults compounding.
112+
Splitting an oversized prompt halves the *diff*, but the context and rule
113+
sections were sized from the window alone — identical at every recursion
114+
level — so a prompt that overran because of its rules failed the same way four
115+
times and then gave up. And giving up returned an error that propagated out of
116+
`analyze`, exiting 2 and discarding the findings every *other* unit had
117+
produced, including deterministic ones that never needed a model.
118+
119+
Both sections now shrink with each split (dropping whole rule sets rather than
120+
truncating one mid-sentence), and are held to at most half of what the window
121+
has left, so the diff under review cannot be crowded out. A unit that still
122+
will not fit is counted in the new `units_too_large` stat and skipped, and the
123+
footer says so. Unchanged for an ordinary review: at depth 0 on the shipped
124+
32K window the budgets are exactly what they were.
125+
37126
- **A non-ASCII identifier could abort the detector.** `references_identifier`
38127
advanced by one *byte* past a match, so a name whose first character is
39128
multi-byte — `const élan = 1`, which `extract_declared_name` collects because
@@ -97,20 +186,24 @@
97186
Both are now per-request. The daemon also re-asserts that caching is off above
98187
temperature 0 rather than trusting the client to have done so.
99188

100-
### Internal
101-
102-
- **End-to-end coverage for `--stdin`.** `apps/tui-cli/tests/stdin_pipeline.rs`
103-
runs the real binary with a diff on stdin and asserts on stdout and the exit
104-
code, with inference supplied by a stub `openai-compatible` endpoint rather
105-
than the bundled model — so no 1.1 GB download is a test dependency, and the
106-
path exercised is a shipped one rather than a test-only seam.
107-
The stub answers each request about whichever file it can see in the prompt,
108-
and records the prompts, so the tests can assert what the model was *shown* and
109-
not merely what came back.
110-
111-
This is the coverage whose absence let one defect live in four places at once:
112-
each copy had unit tests, and none of them tested the four together. Reverting
113-
all four fixes now fails four of the six new tests.
189+
- **The result cache never worked across files.** Context was assembled once
190+
from the whole diff and folded into every chunk's cache key, so editing one
191+
file invalidated every other file's cached result — a re-review after a
192+
force-push re-inferred the entire diff. Context is now assembled per unit.
193+
- **Each chunk was given the wrong context.** The six enclosing functions were
194+
taken from whichever files sorted first, so most chunks paid for context about
195+
files they did not contain.
196+
- **Editing one region of a file re-reviewed the whole file.** Units are grouped
197+
by region, so a change in one function only invalidates that function.
198+
- **Glob matching could not express a directory prefix.** `src/api/**` matched
199+
nothing, and `**/gen/**` was a substring test that also matched
200+
`src/gen-legacy/`. Affects `rules.toml` `files` patterns as well.
201+
- **Conflicting input selectors were silently resolved by order.** Passing both
202+
`--staged` and `--last` reviewed whichever the code checked first; it now
203+
errors.
204+
- A diff dominated by a lockfile is no longer refused for size before the
205+
lockfile is filtered out.
206+
- The TUI now honours the pre-filter and files a run record, which it did not.
114207

115208
### Performance
116209

@@ -138,101 +231,17 @@ every review — including one that turned out to be fully cached.
138231
collision would hand one unit another's context, and confidently wrong context
139232
is the failure the whole module exists to prevent.
140233

141-
### Changed
142-
143-
- `Graph::definitions_of` is replaced by `definitions_of_names` (batched) and
144-
`declarations_overlapping` (span-based). The "prefer a definition in this
145-
file" rule moved to `rag`, where the file being reviewed is known.
146-
- `ReviewAnalyzer::analyze`'s progress callback takes the unit being reviewed:
147-
`Fn(usize, usize)``Fn(usize, usize, &ReviewUnit)`. Callers that need to
148-
show a reader the hunk behind a finding should record it here rather than
149-
predicting the unit list with `plan_units`.
150-
- **Daemon protocol changed**`ReviewRequest` gains `temperature` and `seed`
151-
and drops the unused `languages` field. The version gate already refuses a
152-
daemon from another build; run `diffmind serve --stop` after upgrading.
153-
154-
155-
diffmind was built as a gate: run it, get a verdict, pass or fail. This release
156-
adds the other half — a place to *sit* while deciding what to say about someone
157-
else's branch. Both surfaces share one engine; neither replaces the other.
158-
159-
### Breaking
160-
161-
- **`--format json`: `stats.chunks``stats.units`**, plus `units_cached` and
162-
`units_unparseable`. The unit of review is now a region of a file rather than
163-
an arbitrary slice of lines, and the field names say so. Exit codes, SARIF and
164-
Markdown output are unchanged, so CI gates are unaffected.
165-
- **Daemon protocol changed.** A running 0.8 daemon is ignored rather than
166-
misused — the version gate already handled this — but you will want
167-
`diffmind serve --stop` after upgrading.
168-
- **`chunk_diff` removed** from the `core-engine` public API, superseded by
169-
`build_units`.
170-
171-
### Added
172-
173-
- **Code graph.** A tree-sitter symbol graph for 13 languages — Rust,
174-
TypeScript, TSX, JavaScript, Python, Go, Java, C#, Ruby, PHP, C, C++ and
175-
Scala — stored in `.diffmind/graph.db` and updated incrementally
176-
by mtime. Replaces the regex symbol index, which could only see `pub`/`export`
177-
declarations and had no concept of a reference at all.
178-
Review context now includes **the callers of every changed symbol** — a
179-
changed signature is judged against the code that depends on it — plus the
180-
enclosing definition, referenced definitions, and the file's tests. Everything
181-
stays inside a byte budget, so context does not grow with the repository.
182-
Bodies are read from the working tree rather than stored, so a snippet can
183-
never disagree with the file being reviewed. The graph refreshes itself
184-
incrementally before every review (~0.1s on 647 unchanged files) and builds on
185-
first use, so it can never fall behind the code; `--no-index` opts out. Adding a language is one entry in
186-
a table; contributions welcome.
187-
188-
- **Cross-file review units.** When a symbol and code that calls it both change
189-
in one diff, they are reviewed together as a single unit instead of separately.
190-
Reviewed apart, the model judges an interaction while seeing only one side of
191-
it as background. One call replaces two.
192-
- **Reviewer's cockpit** (`diffmind --tui`). Analyses on launch. Each finding
193-
shows the actual hunk the model reviewed and the context it was given.
194-
`a` accepts (and copies a review comment via OSC 52, which works over SSH),
195-
`d` dismisses, `w` marks wrong. Verdicts are written through immediately.
196-
- **Review standards as markdown**`.diffmind/rules/*.md`, scoped by path
197-
glob, committed to the repo. `diffmind rules init` / `rules list`. A finding
198-
the model attributes to a rule set gets the ID `rulebook.<id>` and suppresses
199-
like any other; an attribution naming a rule set that does not govern that
200-
file is discarded.
201-
- **Reportable pre-filter.** Lockfiles, `linguist-generated` paths, `@generated`
202-
banners, minified bundles, assets, snapshots and formatting-only hunks are
203-
dropped before the model sees them — and the run says what it skipped:
204-
`312 hunks → 74 reviewable (238 filtered: lockfiles, generated, formatting)`.
205-
Whitespace inside a string literal counts as content; indentation is never
206-
dismissed in Python or YAML.
207-
- **`diffmind stats`** — findings, cost and the accept-to-wrong ratio over
208-
recorded runs. Every review is filed to `.diffmind/runs/<sha>/`.
209-
- **Cost reporting** — wall-clock and token counts in the footer, in JSON, and
210-
in the run record. Estimated counts are marked `~` rather than passed off as
211-
exact.
212-
- **Revision ranges**`diffmind v1.2.0..HEAD`, or `--range`. Paths after a
213-
range narrow it.
214-
- **`review.ignore`** globs in `.diffmind/config.toml`.
215-
- diffmind writes its own `.diffmind/.gitignore`, keeping generated state out of
216-
git while leaving `rules/`, `rules.toml`, `config.toml` and `baseline.json`
217-
committable. Your repository's `.gitignore` is not touched.
234+
### Internal
218235

219-
### Fixed
236+
- **End-to-end coverage for `--stdin`.** `apps/tui-cli/tests/stdin_pipeline.rs`
237+
runs the real binary with a diff on stdin and asserts on stdout and the exit
238+
code, with inference supplied by a stub `openai-compatible` endpoint rather
239+
than the bundled model — so no 1.1 GB download is a test dependency, and the
240+
path exercised is a shipped one rather than a test-only seam.
241+
The stub answers each request about whichever file it can see in the prompt,
242+
and records the prompts, so the tests can assert what the model was *shown* and
243+
not merely what came back.
220244

221-
- **The result cache never worked across files.** Context was assembled once
222-
from the whole diff and folded into every chunk's cache key, so editing one
223-
file invalidated every other file's cached result — a re-review after a
224-
force-push re-inferred the entire diff. Context is now assembled per unit.
225-
- **Each chunk was given the wrong context.** The six enclosing functions were
226-
taken from whichever files sorted first, so most chunks paid for context about
227-
files they did not contain.
228-
- **Editing one region of a file re-reviewed the whole file.** Units are grouped
229-
by region, so a change in one function only invalidates that function.
230-
- **Glob matching could not express a directory prefix.** `src/api/**` matched
231-
nothing, and `**/gen/**` was a substring test that also matched
232-
`src/gen-legacy/`. Affects `rules.toml` `files` patterns as well.
233-
- **Conflicting input selectors were silently resolved by order.** Passing both
234-
`--staged` and `--last` reviewed whichever the code checked first; it now
235-
errors.
236-
- A diff dominated by a lockfile is no longer refused for size before the
237-
lockfile is filtered out.
238-
- The TUI now honours the pre-filter and files a run record, which it did not.
245+
This is the coverage whose absence let one defect live in four places at once:
246+
each copy had unit tests, and none of them tested the four together. Reverting
247+
all four fixes now fails four of the six new tests.

0 commit comments

Comments
 (0)