The measurements that justified path resolution are now reproducible, and they moved
Every default in resolve-path.ts was argued from a table nobody could re-run. The script that regenerates it is now committed — and it turns out the original count treated image embeds as navigational links, which shifted every percentage that used the corpus as a denominator. All six conclusions survive; only the digits moved.
The measurements that justified path resolution are now reproducible, and they moved
src/utils/resolve-path.ts has fifteen config options and, deliberately, no
baked-in policy. The few things it does decide by default — that there is an
index at all, that basename is in the cascade, that a collision refuses to
guess, that matching is case- and separator-insensitive — were each argued from
a measurement rather than from taste.
That was the right instinct and it was undermined by the execution: the tables lived in a published package’s README, and the scripts that produced them were throwaway and never committed. Nobody could check them. A number nobody can re-derive is not a measurement, it is a claim with a table around it.
What changed
scripts/measure-vault.mjs regenerates every quoted figure, takes about a
second, and accepts --json. It also measures the README’s four-route
resolve-rate, which previously could not be verified at all because the route
config behind the claim had never been written down.
node scripts/measure-vault.mjs # ../content by default
node scripts/measure-vault.mjs --json # machine-readable
The numbers moved, and one of them was an error
The original sweep counted ![[embeds]] as wikilinks. The resolver is never
asked to route an embed — it is an image, not navigation — so including them
inflated the corpus and every percentage computed against it.
| claimed 2026-08-23 | re-derived 2026-09-08 | |
|---|---|---|
| Navigational wikilinks | 13,846 | 13,556 |
| Wikilinks with no folder | 3,839 — 28% | 3,624 — 26.7% |
| Files with a unique basename | 4,622 — 98.4% | 4,563 — 97.0% |
| Colliding basenames | 73 (153 files) | 66 (139 files) |
| Bare links hitting a collision | 25 — 0.7% | 17 — 0.5% |
| Exact hits among pathed links | 8,956 — 89.8% | 9,108 — 91.7% |
| Resolution cost | 5.09 µs/link | 7.65 µs/link |
Every conclusion survives. Better than a quarter of the corpus is still written without a folder, so the index is still load-bearing and prefix rules alone would still miss it. Basenames are still unique far more often than not, so the tier stays. Collisions are still rare enough that refusing to guess costs almost nothing. The headline reproduces almost exactly — 79.2% against the documented 79%.
The one number worth restating plainly: resolution is 7.65 µs per link, not 5.09. That is 50% slower than claimed and still ~104 ms to resolve the entire vault, so the design doc’s performance argument is unchanged. It is recorded because a shared library should not round its own cost down.
The old entry was corrected, not rewritten
changelog/2026-08-23_01.md keeps its original table with a correction note
pointing here. A ship log records what was shipped, including the parts that
were wrong at the time. Rewriting it would erase the more useful fact — that a
published package carried unverifiable statistics for two weeks.
Why this was blocking
Version 0.6.0 has been sitting unreleased on wip/wikilink-path-resolution
since 2026-08-23, and this overclaim was the stated reason. With the script
committed and the tables re-derived, that objection is resolved.
The other caveat from the parking commit still stands: the 806 lines of
resolve-path.ts have not had a human read them.
Related
context-v/Maintain-Path-Resolution-for-Wikilinks.md— the design recordchangelog/2026-08-23_01.md— the original release entry, with its correctiontest/resolve-path.test.mjs— 39 assertions pinning the two invariants