Skip to content

Releases

v0.17.0

Choose a tag to compare

@github-actions github-actions released this 21 Oct 05:42
639d1ca

Fixed

  • The header named every PR an "Azure DevOps PR": a GitHub PR is a "GitHub PR".

Added

  • Since your review: the report of a PR you explained before at another head says what changed since. A valid
    report records its head in .explain-change/reviews/; the next head's page compares the PR with itself (an
    interdiff): each file and component is new, changed again or the same, and the files the PR left are listed. A
    file whose own added and removed lines are the same counts as the same, so a rebase or a merge of the target adds
    no noise (the page says when the merge base moved). A switch in the header, on by default, rings what changed on
    the map and marks it in the changes, dimming the rest there; the map's boxes are never dimmed, as the story lights
    them. local, pr and render print the counts (since in --json). Checked on 3 example PRs against their own
    commits after an earlier one: maybe (linear) flags its 4 files changed again and 1 new, and 1 file a commit
    changed and the next restored stays the same; bitwarden (a rename, then a merge of main) flags only the renamed
    migration, new, with its old path gone; chatwoot (4 merges of develop) flags only the 2 files its fixes changed.
  • What changed since, in words: when a PR you reviewed at another head changed since, the digest gets
    ## Since your review, with how each file's patch changed (its interdiff: > a line it has now, < one it
    had), and the draft gets since: a summary and 1 line per component that is new or changed again. The validator
    checks it against the review the draft names (stale for another); the page shows it in the header and under each
    component, only for that review. A haiku agent on chatwoot's PR, reviewed 4 merges of develop and 2 fixes
    earlier, wrote it in 1 render from the interdiff ("The sync now writes only when its attempt is newer than the
    stored check…"). Without a review, the digest, the draft and the page are as before.
  • Steps you read: the story marks a step read (✓) when you go through it, and keeps it on the page across
    visits (localStorage, per PR and head). At the next head of a PR, a step stays read only when nothing it shows
    changed since your review; the others say "changed since your review". The count in the story's header clears
    them. Read replaces the old green mark on the steps before the current one, which only said where you were.
  • A code reference in a GitHub PR's report opens the PR at its lines, not at the top of the file: a story step, a
    decision's evidence, an entity and a symbol in the inspector link to #diff-<sha>R12-R20 (L on the base side,
    for a removed file or symbol). A line outside every hunk of the PR has no anchor on GitHub's page, so its link
    opens the file as before; a range is cut to the hunk that holds its first line. The page carries the hunks of each
    changed file (hunkSpans) for this, as it ships only the lines of its excerpts. In the 11 real examples, the 379
    line links all fall inside the hunks GitHub serves for their PR.
  • On Azure DevOps, whose PR files view takes no line, a code reference opens the file at the commit of its side
    (the head, or the merge base for a removed line) with its lines selected: ?path=…&version=GC<sha>&line=12&lineEnd=21,
    the file link Zed, GitLens and pr-brief build. It leaves the PR; a reference without a line still opens the PR's
    files view.

v0.16.1

Choose a tag to compare

@github-actions github-actions released this 20 Oct 13:14
ba04d1d

Fixed

  • explain-change update could reinstall the version it had: npm keeps a package's versions cached for minutes, so
    right after a release explain-change@latest was still the one before, and update said 0.15.1 → 0.15.1. It now
    asks the registry itself which version to install (npm view --prefer-online) and installs exactly that one. It
    says when the copy is already up to date, and fails, naming the path, when the copy it runs is not the one npm
    installed.
  • A Rails constant no longer goes to a file that does not define it. With ActiveAdmin, app/admin/like.rb
    (ActiveAdmin.register Like) took Like from app/models/like.rb, because the first path in alphabetical order
    won: every reference to Like drew an arrow to the admin panel, and only a constants list in the config hid it.
    The Zeitwerk index now leaves out what Rails does not autoload: app/assets, app/javascript, app/views, and
    app/admin when config/initializers/active_admin.rb exists. A file under [ignore] takes a constant only when
    no other file defines it. A constants list written as that workaround can go. ANALYSIS_VERSION 16.

Changed

  • The configure skill says that a layout in AGENTS.md or the README describes the lanes, never the components, and
    that "a typical PR lights 3–8 boxes" is how to judge the model, not a condition to meet before splitting
    Models/Services/Queries by context.

v0.16.0

Choose a tag to compare

@github-actions github-actions released this 20 Oct 12:17
7d7ee5c

Removed

  • explain-change share: neither Azure DevOps nor GitHub PRs take an HTML attachment a reviewer can open, so the
    command, the Azure DevOps upload and the share field of the --json output are gone. A report is one
    self-contained HTML file: send it the way your team shares files.
  • A design decision no longer has an alternative. An alternative was mostly the agent's guess at an option nobody
    weighed, and naming the options not taken is a reviewer's work. A decision says what was chosen, why, and where the
    why comes from. An explanation.json that still has one renders as before, without it.

Fixed

  • The map no longer cuts its labels with "…". Component and lane names wrap to 2 or 3 lines and their boxes grow;
    a flow call too long for its lifelines takes 2 lines, broken after a code separator (Folders. /
    DeleteMany()), never inside a name, and the lifelines move apart only when 2 lines do not fit either; a step's
    data fits between its lifelines too; entity boxes take the width of their longest name or field. Only a label
    longer than its room even then ends in "…", with the whole text as its tooltip.

v0.15.1

Choose a tag to compare

@github-actions github-actions released this 19 Oct 15:05
d2b8270

Fixed

  • The npm package of 0.15.0 carried a stale build: explain-change --version printed 0.14.0, and the skills were
    marked 0.14.0. npm publish now rebuilds the bundle first (prepublishOnly), so the package always carries the
    version it is published as.
  • The README links its images and pages by absolute URL on main, so the npm page shows them as GitHub does,
    and an npm badge shows the latest published version. A test keeps the links absolute.

v0.15.0

Choose a tag to compare

@github-actions github-actions released this 19 Oct 08:29
67f02cc

Changed

  • npm is the way to install: npm install -g explain-change, then explain-change install for the skills.
  • explain-change update runs npm install -g explain-change@latest (or the --version tag), then refreshes the
    skills. A copy that is not a global npm install says how to update it.
  • The npm package no longer ships skills/: the CLI carries the skills and explain-change install writes them.

Removed

  • The standalone binaries and the install scripts (install.sh, install.ps1): releases no longer carry binaries.
    A binary from an earlier release cannot update itself any more: delete it, then npm install -g explain-change.

Added

  • explain-change uninstall removes the skill folders that install wrote (it keeps any other folder), then says
    how to remove the CLI itself.

v0.14.0

Choose a tag to compare

@github-actions github-actions released this 18 Oct 21:13
d8dfe8b

Fixed

  • A detected flow no longer takes a class that inherits for a call: class FamilyExport < ApplicationRecord gave
    maybe's data export a step family → shared-kernel: FamilyExport, the class the line declares, on a dependency
    that is only its parent class. A line that declares a class, module, interface, record or struct is no step of a
    path. ANALYSIS_VERSION 15.

Added

  • render rejects an entity whose class the change declares in another component (wrong-component): the
    entity belongs to the component of its model, not of its migration. The draft fills it from the models the change
    edits, when they all live in 1 component, instead of the migration's folder.
  • bun run eval -- --calibrate --judge none: the meta-eval for free, on the structure and mentions graders.
  • --baseline compares PR by PR when 5 or more PRs are in common: the mean of the per-PR differences against a 95%
    interval of it from Student's t (at least 0.05), with the PRs that went up and down and a sign test. A rise above the
    noise is IMPROVED; the score of each split is listed, and a drop on one fails the eval. Unpaired runs keep the 0.1
    floor. The gate of the improvement loop runs on all 11 PRs (docs/EVALS.md).

Measured (haiku agent, sonnet judge, all 11 PRs, 2 runs each) against 0.13.0, paired: score 0.76 → 0.76 (+0.01
±0.05, 7 PRs up, 4 down), no measure regressed or improved; on test, score 0.75 → 0.78 (+0.03 ±0.07). With 2
standard errors instead of t, test's prose (+0.13 on 3 of 5 PRs, a field this change does not touch) passed as
improved; the t interval (±0.16) reads it as noise. The paired noise of the
score is ±0.05, against ±0.1 unpaired on the same runs. The step family → shared-kernel: FamilyExport reached 1
of 2 maybe runs before, 0 now. Free calibration (--judge none, 11 PRs): 51 of 53 measurable mutants caught.

v0.13.0

Choose a tag to compare

@github-actions github-actions released this 18 Oct 17:35
3bbf6af

Changed

  • The draft's story lights at least 80% of the code the change rewrites when its budget lets it: the largest
    component left out (50+ lines of code) gets a step, ranked right after the first one. Before, 4 ranked components
    filled the story and a component with no public symbol stayed out however large (bitwarden's stored procedures,
    189 of 372 lines; maybe's views, 374 of 961). Drafts that pass story-coverage: 7/11 → 11/11. Measured (haiku
    agent, sonnet judge) against 0.12.0: train score 0.76 → 0.82, A/B 3–2; test (2 runs) score 0.74 → 0.76,
    structure 0.88 → 0.98, renders 1.3 → 1.0, A/B 4–3; grounded 0.80 → 0.67 on test (its noise is ±0.34).
    ANALYSIS_VERSION 14.
  • The 11 example explanations come from these eval runs (the best valid run of each PR).

v0.12.0

Choose a tag to compare

@github-actions github-actions released this 18 Oct 11:36
3c557fe

Changed

  • The draft's tldr, goal, story body and decision why carry a present-tense example and "no would/could" next to
    the field: the modal that still cost a round of fixes in 2 of 6 train runs no longer shows up. Measured (haiku
    agent, sonnet judge) against 0.11.0: train score 0.77 → 0.76, renders 1.33 → 1.0; test (2 runs) score 0.74 → 0.74;
    no would in 34 runs; on the 4 train PRs, 12 runs each without a judge, score 0.89 → 0.93, tokens in −5%.
  • 5 example explanations (bitwarden bulk folder delete and staged invites, chatwoot branded layouts and WhatsApp
    number health, saleor webhook payloads) come from a current agent run: their flows no longer have a step from a
    component to itself.

v0.11.0

Choose a tag to compare

@github-actions github-actions released this 18 Oct 10:31
ee0ab65

Changed

  • render moves a location up to 20 lines outside a hunk (any line of an added file) onto the nearest hunk,
    writes it back to explanation.json and lists the fix (explanation.fixed): no round of fixes for it.
  • A field too long is checked for prose in the same pass, and its issue says how much to cut and shows the words
    past the limit: 1 round of fixes is enough.
  • The eval keeps the messages of the first render's issues (firstMessages).
  • The draft gives limits in words first (≤20 words (140 chars)) and a present-tense example for a decision's why
    and alternative; the prose rules in the digest show how to rewrite a modal ("it would fail" → "it fails").

Fixed

  • A detected flow's id cut at 40 characters could end with a hyphen, so the draft failed its own validator
    (ANALYSIS_VERSION 13).

Measured with a haiku agent and a sonnet judge against 0.10.0 graded again by sonnet: train score 0.79 → 0.83,
renders 1.5 → 1.17, tokens in −16%; test (2 runs) score 0.77 → 0.76, renders 1.6 → 1.4. With the prose context:
test renders 1.4 → 1.1, tokens in 153k → 147k, score 0.76 → 0.74 (inside the noise); on the 4 train PRs that
rendered most, 12 runs each, renders 1.58 → 1.0 and turns 7.6 → 6.

v0.10.0

Choose a tag to compare

@github-actions github-actions released this 18 Oct 07:23
a238f2e

Added

  • The eval grades each run with pass/fail graders, after claude plugin eval and skill-creator, and score is the
    share that pass: structure (deterministic, against the facts: the story lights at least 80% of the changed
    code, the key dependency is one the change touches, decision evidence points into the change, flow steps exist in
    the code, no field repeats another), content (each example's assertions.json), prose (a rubric of
    PASS/FAIL conditions) and grounded (each decision's why against its source). The summary lists the failing
    graders first, with their evidence: what to fix.
  • examples/real/<name>/assertions.json: what any good explanation of that PR says, written once by a model from
    the PR's title, description and diff, never from the tool's digest or explanation (bun scripts/eval-assertions.ts).
  • Train and test splits (real.json eval, --split, train by default): an improvement loop reads the failures
    of train only and keeps a change when test does not drop.
  • --calibrate, the meta-eval: each committed explanation and its mutants (a dropped story step, a key dependency
    the change does not touch, evidence outside the change, generic prose, a TL;DR that lists files, a goal that
    repeats it, decisions that restate the change, a made-up why) graded; it reports which defects the graders catch.
  • --compare results.json: a blind A/B of 2 evals' explanations, run by run, asked in both orders (a verdict that
    changes with the order is a tie), with a sign test.
  • --max-cost USD stops starting runs past the cost; --baseline also lists the graders an iteration fixed or
    broke.
  • The summary lists the issues of each run's first render, most often first: what makes the agent render again.
  • Each eval run records its tool calls in order (tools, with the size of each input and result), the prompt
    tokens read from and written to the cache, and the refused words behind a first render's issues: where the turns
    and the tokens go.
  • docs/EVALS.md: the eval's graders, splits, improvement loop, options and the judge's calibration against sonnet.

Changed

  • local and pr no longer write the draft to explanation.json: it ends digest.md (and is in draft.json),
    and the agent writes explanation.json in 1 call. An agent no longer spends a turn reading a file it already has,
    since an existing file must be read before it is overwritten. The explanation is missing until then.
  • The digest says how to read a source file at the head of a PR (git show <head>:<path>): the checkout can be
    another commit, and a failed read cost a turn.
  • Counts in words (two … twelve) are no longer refused: the page writes them as digits. The refusal made most
    eval runs render twice.
  • Each placeholder states its limit in words too (≤160 chars, ~22 words): a story body or heading over its limit
    was the commonest cause of a second render.
  • The skill asks for the digest and the fixes in as few messages as possible.
  • Together they cut the agent's prompt tokens by 23% and its turns by a third, at the same score: on the train
    split (haiku, 1 run) 231k → 177k tokens per run, 10.8 → 8 turns, score 0.88 → 0.88; on the test split (2 runs)
    210k → 161k tokens, 11.6 → 7.6 turns, score 0.82 → 0.83, cost per run −14%.
  • The judge's calls are lean (no tools, MCP servers, skills or settings; a 1-line system prompt): about 600 tokens
    of overhead per call instead of 20k+. Haiku stays the default judge.
  • The agent under test runs with no MCP server and, unless --discover, no other skill: each run is isolated from
    the machine's setup. Tokens do not compare with earlier evals.
  • The agent under test gets only the tools it may use (--tools): the others' definitions cost ~5.5k tokens on
    every model call.
  • sourceReads also counts read-only shell commands (git show, sed, grep), which run without a permission.
  • The grounded judge reads the whole hunk around a code evidence (at most 80 lines), not the line ±3.
  • fidelity, draft and lift stay as a regression check of the structure, not a measure of quality: they compare
    with the committed explanation, which the same tool wrote.

Removed

  • faithful: it checked the prose against the digest, which is the tool's own selection, and sat near 0.9 in
    every eval. The content assertions, written from the diff, replace it.