Changelog

An entry arrives when a tag is cut. A tag is the only condition that fills this page, and not a date and not a milestone. There is no cadence, and this page says so rather than promise one.

v0.5.0

The fifth release, cut on 2026-09-29, closes the milestone called An adopter publishes their corpus as a site. The aim of that milestone is that an adopter can take a governed corpus to a website, and that the engine tells them when the built site no longer agrees with the corpus.

Publish a corpus as a site. The new verb headwater site <site-dir> holds a directory that a site generator wrote against the corpus at --root. It reports four classes of finding: site.page.missing, a page that the navigation names and the site does not hold; site.page.stale, a page under a shelf that answers to no document; site.link.dead, an in-site link to no file; and site.fragment.dead, a fragment that names no id on its page. It reads the navigation from the projection plan, as headwater generate does, and not from the committed navigation file. The new guide Publish your corpus as a site (docs/how-to/publish-your-corpus-as-a-site.md) takes a corpus to a MkDocs site: declare site_nav in the overlay, generate, copy the configuration it gives, build with mkdocs build --strict, and run headwater site as the last step of each build. A graph_export projection can declare committed: false, so an export that headwater export builds at publish time need not be in the repository.

New rules an adopter meets. relation.target.is_source reports a relation entry whose target is the document that declares it, and a code_path anchor that names only the declaring document's own file. link.identifier.mismatch reports a link whose text names one document and whose path reaches another. harvest.pin.unread reports a pinned export that does not read, and names the pin. A digest pin that the vendored taxonomy package no longer hashes to now fails headwater check and headwater taxonomy validate. relation.target.suspect reports, at info, an edge that reaches only a named pipe, a socket or a device. headwater taxonomy validate compares identifier schemes across namespaces, because one namespace and a literal can spell another. The headwater/standard taxonomy is at 4.12.0.

Changes to the verbs. A solution corpus can resolve an anchor into another repository through the committed export that it pins under harvests in .headwater/taxonomy.yml. A bundle can add into a bundle that it names in requires, and the resolver refuses it, with both names, when that bundle is not selected. headwater check --fix stamps verified_revision only on a document that the change re-verified, and no longer on each document that was reviewed that day. headwater check never opens a named pipe, a socket or a device, so it no longer hangs on one, and headwater show and explain refuse such a path. headwater new, when it refuses a kind that has no identifier scheme, prints the two lines to paste into .headwater/overlay.yml. The MCP related and governing_docs_for_path tools read ./x, a/../x and an absolute path as explain does. explain and show, and the MCP explain, related and governing_docs_for_path tools, refuse a path outside the repository with one sentence, and a path through a dangling symlink out of the root is outside. explain finds the root in an absolute path typed through a symlink, such as a macOS temporary directory. A probe plan that is refused prints its reason and no cost that it did not compute.

New integrations. A JetBrains plugin, under integrations/jetbrains/, shows the documents that govern a task, an open file and a saved file, in IntelliJ IDEA, PyCharm, GoLand and the other JetBrains IDEs. It is a client of headwater mcp. The site deploys again after each release, so https://headwater.tools/apt/ serves the new version as soon as the release exists.

v0.4.1

A patch release, cut on 2026-09-28. It is the first release whose assets carry signed APT metadata. The release has the Debian package headwater_0.4.1_amd64.deb, and it also has Packages, Release, InRelease and Release.gpg. The site serves these files from https://headwater.tools/apt/. A subkey signs them, and the public keyring is https://headwater.tools/apt/headwater-archive-keyring.asc. The package holds the engine binary and no taxonomy.

Changes to rules an adopter meets. The new rule lifecycle.state.not_set_by_edge reports a decision that a successor supersedes while it still says current. headwater check --fix corrects the state and dates it from the successor. surface.command.undeclared reads here-document end words, arithmetic and substitutions in a shell block as the shell does. The sentence splitter keeps U.S., U.K., a.m., p.m. and Ph.D. as abbreviations before a name, and it keeps a statute citation before a section number as one sentence. A period after a code span ends the sentence, whatever letters the span ends on. A page that an edge sets to a state takes its date only from the documents that set that state.

Changes to the verbs. headwater show prints the bytes of one document, and it finds the document as explain does. explain, show and the MCP explain tool find a document by a path that starts with ./ or holds .., and by an absolute path. The MCP route and governing_docs_for_path tools answer with structuredContent, and structuredContent.pointers is the field for a client to parse. The MCP route tool drops a path that git ignores, as headwater route does. route reads the bytes of a governed file only when its answer needs them, so it is faster. explain --json states governed_entries: how many distinct tree entries the governs edges of a document reach, counted once across all its edges. headwater init --git writes merge=union for the two append-only stores, so two branches that each ran headwater new merge without a conflict. The graph export carries the member patterns of each list anchor, and the engine no longer records corpus.checks. A fetch of a taxonomy over plain http to a loopback address does not go through a proxy.

Changes to probes. A probe workspace no longer holds the answer keys, and a probe with one expected value accepts only that value. A paired probe campaign can be recorded and compared. A probe transcript stays gradable when the lock changes in a part that its probes do not read.

v0.4.0

The fourth release, cut on 2026-09-27, closes the milestone called The language rules read everything a reader meets. The aim of that milestone is that the controlled-language rules read the prose a newcomer meets first, and not only the typed documents under the corpus root. Most of that work was already in the v0.3.0 binary, and the v0.3.0 entry did not name it. This entry names it, and it marks which parts are new since v0.3.0.

What was already in the binary, and is named here for the first time. A language regime can list paths outside the corpus root under outside_root, as patterns relative to the repository root, such as README.md and .github/CONTRIBUTING.md. Three rules read each listed file under the regime that lists it: language.controlled.not_met, language.retired_term.used and language.source_form.not_met. No other rule reads it, it owes no front matter, and it never becomes a census row. headwater check --fix reads a corrected file back under the regime of the path that it wrote. A listed pattern that reads nothing is an error of the new rule language.outside_root.refused, so check --strict fails on it. That covers a pattern that matches a path under the root, a path that two regimes reach, a symlink and a pattern that matches no file. headwater taxonomy resolve refuses a pattern with .., an absolute pattern and one literal path that two regimes list, so no lock carries one.

voice.forbidden_construction reads the scent-role facet in the same way as the other two rules that read it. When the population it reads is empty, it reports that it skipped, with the reason, and not a silent pass. relation.reciprocity.missing passes a pair with one half while the writer of that half is at an initial state such as draft. headwater new therefore writes no reciprocal half into a live document while the new document is a draft. It names the owed half in its report, and headwater check --fix writes it once the draft is promoted.

New since v0.3.0. surface.command.undeclared reads a quote that stays open across a line break, a $'...' string and a # comment as a shell does. Before this change, a command after a quote that a line break carried was not read, so an instruction could run a command that the rule did not see. A command that a block never finishes is still read, so the rule fails closed. The install text in README.md and the tutorial now pins the headwater/standard taxonomy at 4.9.1, with its digest.

v0.3.0

The third release, cut on 2026-09-26, closes the milestone called Governance reach: a document can say which code it governs, and the engine now tells you when that claim goes out of date, what share of the code it reaches, and when an edit lands on code that nothing governs. Several parts of that work were already in the v0.2.0 and v0.2.1 binaries, and two of them had an entry here: a governs edge that goes suspect when the bytes it reaches change, and a governs list with a directory member. This entry names the rest, and it marks which parts are new since v0.2.1.

What was already in the binary, and is named here for the first time. A governs anchor is a pattern over the tree, so one edge reaches a set of files. A taxonomy can declare the governed scope of an anchor kind under anchors.<kind>.scope, headwater taxonomy validate refuses a scope pattern that matches no entry, and the engine reports what share of that scope the edges reach. An edge that a comment in a source file asserts binds only on the identifier of the document that asserts it, and not on any other identifier the file mentions. headwater check prints a verification block that gives each verification one state: declared, observed at <commit>, or suspect since <commit>. headwater json takes an array index as a path step. A relation can declare lifecycle_sensitive, and the core of a taxonomy reads it. A generated page whose state no edge sets is dated by the newest document it read.

New since v0.2.1, on the governance side. headwater route names each path in the task that is in the governed scope and that nothing governs, with the front-matter lines that would declare the edge, and its JSON carries an ungoverned list. Each anchored pointer that route reports now carries a suspect list, and route uses the same comparison as headwater check, so the two never disagree about which edge is suspect. An observation snapshot that does not read, or a verification entry written without its kind, is reported as unknown and never as a pass. The base package now declares who writes each relation: governs and traces_to are written by an agent that proposes the line, and discharges and cites_evidence by the author.

Two new advisory rules. lifecycle.dependency.on_initial reports a live document that declares a relation to a document still at an initial state such as draft. lifecycle.state.set_twice reports a generated document that two relations give two different states, and it names every relation, the document that wrote it and the state it asked for.

Other changes an adopter meets. headwater new writes a document onto a shelf whose path fixes the file name: a single-file shelf, or a fixed name under a glob, where the new --directory flag names the directory. explain --json writes each related edge's targets as an array, and target stays as it was. headwater check exits 1 with one sentence, and does not panic, when it cannot write a stream, and it says so on standard error when it cannot write its cache. headwater taxonomy validate names a root package.yml that does not read. headwater init --git says when git did not answer, exits 1, and writes no line that an unread nested .gitattributes could already hold. A GIT_DISCOVERY_ACROSS_FILESYSTEM value that is not UTF-8 now crosses the filesystem boundary, as git reads it. headwater probe stale colors its report under a terminal, and its closing line counts a transcript it cannot judge apart from a stale one. surface.local_path.instructed counts a bare local root such as site only in a shell block and a code span.

v0.2.1

A patch release, cut on 2026-09-26. It carries three changes that reached the default branch after v0.2.0 was tagged, and the first of them is the reason for the tag: the tutorial describes it, and v0.2.0 does not do it.

headwater taxonomy vendor --expect now writes the pin it verified. When the artifact matches the digest you passed, and .headwater/taxonomy.yml declares no taxonomy.digest, the verb writes digest: <the digest> into the taxonomy: block, so the next clone installs the same artifact with no flag. It replaces the commented # digest: line that headwater init writes, or adds the line after version:. It reads the file back, and if the pin does not read, it restores the original bytes and says to write the line by hand. It never replaces a declared pin: an --expect that differs from the declared value still installs, prints both values and leaves the file as it was. A refused run writes nothing. In v0.2.0 the install worked once, and the digest was not recorded.

A governs entry written as a list with a member that names a directory is now reported at Info. The source tree takes no digest of a directory, so that edge never went suspect when what it governs changed, and headwater check said nothing. The finding names each directory member and gives the remedy: write <directory>/** in the list instead.

headwater taxonomy validate run at the root of an authored package source no longer refuses every governed-scope pattern. The directories that the root's package.yml names under contents are the taxonomy's own and do not count as a governed tree, in the same way as a dot-directory, a directory git ignores and a resolution source already did not. Where nothing else at the root counts as a tree, the run now prints the one notice line and refuses no pattern. An examples/ directory still counts as a tree unless contents names it.

v0.2.0

The second release, cut on 2026-09-26, is the first version that an adopter can take into a repository of their own with nothing but the headwater binary. It covers everything since v0.1.0, and that includes the two patch tags v0.1.1 and v0.1.2 of 2026-09-09, which moved the crates onto crates.io so that cargo install works and which never had an entry here.

A package now arrives without a clone. headwater taxonomy vendor takes the https:// location of a published artifact as well as a directory, fetches it, and installs it under .headwater/packages/ only when it matches the digest you pinned: the value of --expect, or by default taxonomy.digest in .headwater/taxonomy.yml. It fetches over https://, and over plain http:// only from this machine's own loopback host. A fetch that began on https:// stays on it at every redirect. It follows a bounded number of redirects, and it bounds what an archive may unpack to. The fetch lives in a crate that only the binary links, so no part of the checking loop opens a socket. The vendored root moved from packages/ to .headwater/packages/, and the old root is named in the refusal that meets it.

The checks go into CI with one file. A composite GitHub Action under integrations/headwater-check/ downloads a released binary, checks its checksum, and runs headwater check against your corpus, with no Rust toolchain on the runner. The guide gives the workflow. The pages written for an adopter are now a declared list, generated as one page, and headwater check holds each of them to it: none of them sends an adopter to a script that only this repository has.

The generated files survive a concurrent merge. headwater derived reports which files each producer writes and which of them state a count or a digest over the whole corpus. The graph export and the generated pages no longer store such a count. headwater init --git commits a -merge line in .gitattributes for each file that still does, and in an adopter's tree that is the taxonomy lock. On that file git stops the merge, keeps your side and records a conflict, in every clone and with no configuration. The same step prints the two git config lines that make headwater merge-driver run in a clone, and --git-config writes them. The driver then names the verb that writes the file again.

A forge does not read that attribute. GitHub showed a pull request that moved a -merge file as mergeable, and its test merge held the edits of both branches. The cover for a merge made on the forge is the CI check of the merged tree, and it covers only when the check is required and the branch must be up to date before it merges. The guide names both settings.

Four smaller things an adopter meets. headwater change writes the change manifest that headwater check --change reads, with no git plumbing in the caller. An identifier that resolves to nothing is refused in the words of an identifier rather than of a path. headwater taxonomy graph prints the resolved taxonomy as a Mermaid flowchart. And headwater check reports a governs edge as suspect when the bytes it reaches change.

Two pieces of this work are still open, and this release does not claim them: an install with no toolchain on more hosts, and the retirement of the bootstrap script that fetched a package before vendor could.

v0.1.0

The first release is the first version of Headwater that a reader can name, obtain and build. Everything before it was a branch, and a reader who wanted a version had only the tip of the default branch and no name for what they had taken.

It carries the engine and the verbs the specification states in the present tense: the checker that reads a corpus against its taxonomy and the gate that refuses a commit on what it finds, the scaffolder that writes a new document from the kind it declares, the router that answers a prompt with the documents that govern it, the generator that projects an index and a site navigation out of the graph, the exporter that writes the graph itself, and the publisher and the consumer of a taxonomy package.

It carries headwater/standard beside the engine — the base taxonomy this repository publishes and then consumes, with the bundles that model a documentation tradition on top of it. The release states one digest: the release.digest field of that package's release record, and not a checksum of the file that carries it. A reader who runs headwater taxonomy vendor --expect then checks the artifact against a number the release published and not against one read out of the artifact itself.

The tag names one commit, and the tutorial builds from a clone of the default branch instead. The two answer different questions. The tutorial teaches the model and wants the tree it is read beside; the tag is for a reader who wants a version that stays where they left it. The project's README names the tag and the one command that builds it.