Skip to content

v2.6.2

Choose a tag to compare

@github-actions github-actions released this 21 Aug 08:56
· 173 commits to main since this release
108a68a

The front door. No new capability - a feature freeze runs to the end of 2026
(docs/roadmap-2027.md) - and this release is the first instalment of what
replaces it: making the parts a stranger meets first actually good.

Drop-in. Swap the binary and restart; no config changes, no data migration.

--help no longer opens with a fleet orchestrator

Twenty-four subcommands, ordered by when each was added, with worker and control - a writer pool
and a control-plane API - ranking fifth and sixth, above sql. A solo dev reading top to bottom
met cursor leases before they met querying.

They are grouped now. CORE is init, add, dev, sql, mcp - the whole happy path and the
AI hook, and nothing else. Then OPERATING, SCALED, and the compliance pack. Nothing was removed and
no capability changed; the enterprise breadth is still there, one screen further down, which is
where RFC-0015 always said it should be.

init auto-detects all seven chains, not three

chains::lookup() knew seven chains. chains::all() - the list init walks when you omit
--chain - returned three. So a contract on Optimism, Polygon, BSC or Gnosis could be indexed by
name and never found by auto-detect, while the README stated in bold that omitting --chain probes
all seven.

Two lists of the same set, one of them silent when it fell behind. The existing test could not see
it: it asserted every chain in all() resolves via lookup, which is trivially true of any
subset. One direction of a two-list invariant, and the missing direction is always the one that
rots. The new test derives its expectation from the alias table that already enumerates all seven,
so there are two lists in that file rather than three.

Fixing it immediately failed a second test, which is the more interesting part: BSC ships one
endpoint where RFC-0030 §4 requires two
, and that rule had been passing only because the chain
breaking it was not in the list the rule iterates. Three candidate replacements were probed with
nuthatch doctor on 2026-08-21 and none is usable - two refuse the getLogs probe outright, the
third is tip-following only. So the exception is recorded in the test with its evidence, and written
to fail the day BSC gains a second endpoint, so the carve-out gets deleted rather than left behind.

Polygon can actually backfill

Polygon shipped in 2.6.0 with polygon-bor-rpc.publicnode.com listed first. By the next day that
endpoint had no archive depth and its getLogs probe failed outright, so a from-deployment backfill
could not use it at all, and log_window was 2,000 against an endpoint that serves 40.

The archive endpoint leads now, log_window is 40, and Polygon joins the live-endpoints probe so
this is measured on a schedule rather than once.

Worth stating plainly, because it is the more useful lesson: chains.rs recorded "Measured
2026-08-19: gives a 5,120-block window"
. That was true when written and false one day later. A
recorded measurement is a snapshot presented as a property.
Run nuthatch doctor --rpc <url>
before trusting a long backfill to any endpoint, ours included.

doctor had its advice the wrong way round

doctor --rpc <url> with no --address probes a range nothing matches, which measures the
provider's raw block-range ceiling and says nothing about what a real nest sustains. The number it
printed was being read as a ceiling. It is a floor: every measurement on record has
address-filtered limits coming in under the raw range, so the range-only figure is a conservative
lower bound, not a cap.

Also: doctor --dir <nest> now derives --address from the nest's first declared contract, so the
probe reflects the filter the nest will actually issue rather than an empty one.

A correct nest no longer starts with 38 warnings

Rename a contract's alias in nuthatch.toml and every table under the old alias became its own
semantic.toml describes table X, which the registry has no decoder for line. On a real nest that
was 38 warnings for one edit, which is the number at which people stop reading warnings.

Whole-alias orphans - where the alias prefix is gone from the registry entirely, meaning a rename -
collapse into one warning per alias, carrying the real table count and naming the edit that
clears it. Genuine per-table drift still reports per table, because that is a different fault.

Smaller

  • init's output is clean. The ABI resolver's name moved out of a raw tracing::info! line -
    ISO timestamp, log level, ANSI codes, straight through the middle of the / prose - and into
    the tick where it belonged.
  • Four security tests stopped being red on macOS. The admin-exposure suite bound 127.0.0.2,
    which Linux aliases on loopback and macOS does not, so they failed at the socket before any
    nuthatch code ran. They bind a real ::1 now. A local suite that is red by default is a local
    suite people stop reading, and these are the ones you least want scrolled past.

Under the hood, for contributors

Two CI gates that exist because of faults nobody would have found by reading:

  • A sprint whose issues are all closed must be reachable from main. Six PRs of good work once
    merged into a sprint branch, closed their issues, showed 6/6 on the dashboard, and shipped in
    nothing.
  • The RFC index is now gated against each RFC's own Status header. It found thirteen rows that
    disagreed with the documents they indexed.

Known: the second gate's stranded-PR rule reports "Nothing" when its query fails rather than "not
judged" (#699). Contributors only.