Repository navigation
Fixed
-
The Homebrew formula installs the man page and the shell completions. Its
def installwasbin.install "proef"and nothing else, so from 0.16.0 —
the release that started shippingproef.1and fivecompletions/files in
every archive — until 0.18.0,brew install proefgave noman proefand no
tab completion, while binstall and a direct download gave both. The formula
is a heredoc insiderelease.ymland the archive is staged in a different
job, so nothing tied the two together and no gate could see the gap; the
render step now fails if the archive lacks a file the formula installs, and
the formula's owntest doasserts the man page and completion landed.
Takes effect on the next tag: a tag runs the workflow from its own commit. -
--dry-runrefuses afile,…;asset that is not there. A suite whose
asset had been deleted reporteddry-run OK, and the failure arrived later
from a different command, against a live backend — from the one gate CI runs
before standing an environment up. Whether an asset resolves is statically
knowable, so it is answered there now. The checker is staging's own
(assets::resolve_assets, split out ofstage_assets) rather than a second
walk over the same artifacts, so validation and the run cannot disagree; the
message and the diagnostic code are the ones a run already gave. -
file,inside a JSON or assertion body is no longer mistaken for a file
asset. The emitter found the files an artifact reads by scanning its text
for the literalfile,and a closing;, so a request body containing that
substring —{"note": "see file,notes.txt; for details"}— produced a
phantom asset, and staging then failed the run over a file the request never
reads. The claiming engine now reads its own AST, where a body reference and
six characters of prose are different things.
Breaking
-
proef_core::emit::emittakes the registered step kinds, and
StepKindSpecgains anassetshook. Asset recognition was hurl's body
grammar living inproef-core:emit::file_refs_inscanned for the literal
"file,", which ADR-0002's amendment forbids and — worse — which the guard
pinning that amendment could not see.engine_grammar_kindclassifies
fences,HTTP,[Section]headers, method lines andkey: valueoptions; a
body constructor is none of those, so the literal was never sanctioned and
never reported missing. The ADR's own measurement said thirteen literals; it
was fourteen.The scan moves behind the seam as
StepKindSpec::assets, the fourth
engine-contributed hook besidevalidate,fragmentsandoptions, and the
guard gains abodyarm so the shape is classifiable whether or not anything
currently uses it.emit()takes&[StepKindSpec]to reach it;FrontEnd
carrieskindsbeside thekind_to_enginetable it is built with, which
registryalready documents as a pair that must not be re-derived
separately.emit::file_refs_inis gone.
Internal
-
A fragment's assets stage from where its file was read, not from where its
name points.AssetRoots::source_dirrebuilt a fragment's directory by
splittingfile.hurl#nameand joining the file half onto the project root —
the naming boundary run backwards, without the canonicalize fallback that
boundary carries precisely because a lexical-only version already shipped a
bug (a suite reached through a symlink silently failed to match, R11-9). The
two agreed only because both were seeded fromconfig.root()and discovery
walked from that same root, so only the lexical case was ever exercised, and
nothing made them stay inverses. The corpus reader now records the directory
it read each file from (front::CorpusDirs, carried onFrontEndbeside
kinds), and staging looks it up — the fragment-side twin of what
LoadedFeature::read_fromalready does for features, so both halves of the
naming boundary are one-way in the same way.AssetRootsloses itsproject
field andbuild_specsitsproject_rootargument: with nothing to
recompute, the project root is no longer staging's business. -
--rerunreads its base record once. It calledrecord::read_eventsfor
the JUnit overlay and thenrecord::rerun_candidates, which read and
deserialized the sameevents.jsonla second time — two full passes bounded
only by the 256 MiB record ceiling, over a file another process may still be
writing.rerun_candidatesnow takes the&[Event]its caller already
holds, which is the ruleread_record's own documentation had already
stated for exactly this case. The read error is handled once as well: the
first call swallowed it with.ok()and the second rediscovered it a line
later. -
The one doc check that reads only files now runs in the half that reads
files.no_current_behaviour_doc_spells_a_format_as_an_output_pathlived
intests/docs.rs, whose stated charter is the checks needing a built
binary to ask clap — this one only scans markdown, so it never ran in the
fast doc-only CI step. It is nowxtask docs-check's
check_output_path_spelling, reusingliving_docs()instead of carrying a
second directory walk. Its allowlist-shrink guard got stricter on the way:
it counted ADRs into the same total, so a renamed entry could be masked by
docs/adrbeing larger than the shortfall — which is the one failure that
guard exists to catch. All three paths were checked by mutation: a stale
spelling planted in an allowlisted doc, one planted in an ADR, and an
allowlisted doc renamed away.
Documentation
-
The worklist stops contradicting what shipped. Three entries in
OPEN-FINDINGSstill called CTRF declined or its trigger unfired — the
2026-08-31 external re-test, the RF audit's deferred list, and R3-5 under
"deferred, with the trigger named" — for the eight days after--ctrf
actually shipped (#160). R3-9, four bullets below R3-5 in that same list, was
annotated the moment it shipped — the convention the three missed. Two more claims
had outlived their facts: the shipped-changelog duplicate headers (no release
carries one now, andcheck_changelog_kindsfails if one returns) and the
machine-side note about Homebrew's Rust shadowing rustup. Filed at the same
time:a_second_interrupt_hard_exits_with_130failed once on Linux CI and
passed on a re-run of the same commit, so the evidence, the mechanism and the
fix shape are written down instead of left to the next re-run. And the stance
that a scenario-level@retryis deliberately absent — retry-until-green
hides a one-in-four defect 99.6% of the time — is stated in
TESTING-STRATEGY§5, which the worklist asked for and nobody had written. -
The runbook records that the registry skips three versions. 0.15.0–0.17.0
were tagged and GitHub-released but never published, so crates.io moves
0.14.0 → 0.18.0. Noted inRELEASING.mdso the gap does not read as a failed
upload. The long-standinghomepagequestion inOPEN-FINDINGSis also
resolved: the field reached the registry with 0.18.0, exactly as that entry
predicted;documentationremains unset and still open. -
The release history records every release again.
RELEASING.md's History
section carried no entry forv0.16.0orv0.17.0and filedv0.15.0
betweenv0.13.0andv0.14.0; the order is repaired and all three versions
are present,v0.18.0included. The corpus also stops calling the 0.18 series
unreleased, and anIMPROVEMENT-PLANpointer into CHANGELOG[Unreleased]
now names the releases that actually carried the work —[Unreleased]has
been cut several times since that sentence was written.