D304 - The repair searched for an index it could compute

decided · 2026-08-26 · audit --repair ran for five hours on thirty-three names

Growing a vocabulary renumbers every derivation index built from it (D213), which is ordinary and expected - the loop invalidating its own earlier records is the price of the loop working. audit --repair re-derives them in one sweep, and after a morning's vocabulary work it was given thirty-three names and did not finish in five hours.

Not a hard problem. Pattern::name_at is a mixed-radix decode: each part contributes a digit, least-significant last, and the index is read off by repeated division. Its inverse is the same arithmetic backwards. But solve::derive does not invert it - it searches:

for pattern in patterns {
    for index in 0..pattern.len() {
        if pattern.write_at(index, &mut buffer) && buffer == wanted {}

A linear scan of a space this project measures at 1.52 trillion candidates, thirty-three times over, for something a division answers.

So Pattern::index_of computes it: split the name against the pattern's own vocabularies - backtracking where a slot has several words that could start the remainder - then combine the chosen positions as the mixed-radix number name_at would have decoded. Bounded by vocabulary size times pattern depth, not by their product.

The search stays for the case it was written for. derive is also what answers "could this repository have produced this name at all?" for a name with no record - and there the pattern is unknown, so there is nothing to invert against and walking is the only honest answer. That is --deep, it is documented as slow, and it is a different question from "this name was generated by that pattern and the vocabulary moved underneath it".

Worth stating because the failure was invisible from the outside: a repair that has not finished looks exactly like a repair that is being thorough, and the gate stays red either way with nothing saying which.