Rendered from docs/interfaces/headwater-check.md in the Headwater corpus. Every document on this half of the site is typed by the taxonomy the descriptor names: corpus.json.

headwater check

Synopsis

headwater check [--strict] [--fix] [--no-cache] [--now <date>]
                [--change <manifest>]
                [--read-set <path>] [--register <path>]
                [--format text|json|sarif|markdown | --json]
                [--root <path>] [--no-color] [--no-banner]

The verb takes no operand. A word after check is refused, and the message names check as a verb the binary does not carry.

Description

headwater check runs every check that the taxonomy in .headwater/taxonomy.lock generates over the corpus that .headwater/taxonomy.yml declares. It reports the census, the graph, every finding, the coverage account, the obligation register and the state of each verification. The report is one artifact in one of four vocabularies. --format picks the vocabulary, and --json is a second spelling of --format json.

The text format renders the palette HW-DR-0045 rules on, when standard output is a terminal. A severity word, a path, an obligation identifier and a fix: label each carry their own color, and the six block headings do too. The other three formats never color, whatever stream they reach. A machine reads them, and an escape sequence in JSON or SARIF is a defect rather than a courtesy. --no-color forces the same plain text every format already wrote before this decision.

The run is advisory unless --strict is passed. A finding of any severity leaves the exit status at 0, which spec 6 fixes as the default. A tool that blocks on first contact is a tool somebody removes, and a removed tool catches nothing.

Two streams carry two different facts, and a caller that merges them reads a correct report as a broken one. The report goes to standard output. The cache accounting goes to standard error, because it is a fact about the disk of one machine rather than about the corpus. --fix puts its account of what it wrote on standard error for the same reason. A cached run and a --no-cache run write the same bytes to standard output. A line about the cache on that stream is the one thing that would make the two differ.

The verb takes every declaration from the lock and never from the package sources. An edit to a declaration under .headwater/packages/ or to .headwater/overlay.yml reaches no check until headwater taxonomy resolve writes the lock again. The one reading of the package directory is the digest in the next paragraph, and it takes no declaration. This is the trap that makes a test of a rule pass over a declaration that moved.

The verb also hashes the vendored package against the digest pin, and it takes no declaration from it. Where .headwater/taxonomy.yml declares taxonomy.digest, each run computes the digest over the files in the package directory. It is the same number that headwater taxonomy publish prints. Where the two digests differ, taxonomy.pin.diverged reports one error on .headwater/taxonomy.yml. The error names the pin, the digest that the run computed, and each file that differs from the release record where a record is there. A pin that was edited and a vendored file that was edited both give this error. Where the verb finds no package directory, the error says that it computed no digest. Where nothing is pinned, the rule is silent. The run computes this on every run and never caches it. The pin file and each vendored file join --read-set's output, so headwater gate on a later tree sees an edit to either. The error says that the bytes are not the bytes that the pin names. It says nothing about who wrote them (HW-OBL-0115).

A page for an adopter is held against the consumer surface the taxonomy declares. HW-DR-0077 rules that no page for an adopter instructs a file that only this repository holds. The surface block of the taxonomy lists those pages in adopter_documents and those roots in local_roots. surface.local_path.instructed reports an error for each path under a local root that such a page puts in a code span or in a code block. It reports the same error when such a page puts the path in a front-matter value. A path in a sentence of prose is not reported. A passage that states how this repository does a thing passes under a scope=block directive with reason=accepted_deviation. This is the passage "that says so" of the record. A taxonomy with no surface block generates no instance. The rule reads typed documents under the corpus root, and adopter_documents lists only such pages. README.md at the root is a page for an adopter, and it is untyped. The three language rules read it through the outside_root list of a language regime (HW-DR-0084), but this rule does not. No check of this repository holds it against the local roots, and that is a limit of this rule. The shipped package text is not a document either, so no check reads it. A token counts when it opens with a local root, with ./, or with a shell variable such as $ROOT/ or ${ROOT}/. A token that is a local root with no trailing slash also counts, such as the bare directory name that a git config core.hooksPath line sets. A bare token does not count when it is the name of a program that the surface declares. The programs are commands, prerequisites and the depends_on of each integration point. So mkdocs in mkdocs build is the program of the site generator and not the directory. A word of prose that is the same as a root is not reported, because the rule does not read prose. $HOME/ and ${HOME}/ do not count, because a path under the home directory of the user is not a path of this repository. A variable in quotes before the slash, as in "$ROOT"/tools/x.sh, is not reported, and that is a limit of this rule. Code inside a block quote counts. Raw HTML does not count. The rule instantiates only on a typed document that adopter_documents lists. A page that is not on the list has no instance of the rule. archive, integration_points, prerequisites, companions and commands are declared, and headwater generate renders each of them on the consumer surface page. That page is a reader and not a check. surface.command.undeclared reads commands. surface.local_path.instructed reads commands, prerequisites and integration_points only to know which bare names are programs. No check reads archive or companions.

The program exclusion applies only to a whole bare token, so mkdocs/build.sh counts. A bare token counts only in a shell block, as the paragraph on surface.command.undeclared below defines it, and in a code span. In a fenced block with a different info string, and in a front-matter value, a bare word is a value of the adopter's own configuration. So site in the yaml line site_dir: site is not reported. A token that opens with a local root and its slash, such as site/docs, counts in every block. A code span such as `site_dir: site` in a sentence is still reported, and that is a limit of this rule.

A page for an adopter runs only the programs that the surface declares. commands in the surface block lists each program that such a page may run. It holds headwater, each platform prerequisite that HW-DR-0077 declares by name, and the dependencies of a declared integration point. surface.command.undeclared reports an error for each program that a shell block of a page on adopter_documents runs and commands does not list. The engine holds no list of programs of its own. A shell block is a fenced code block whose info string starts with one of four words: sh, shell, bash or console. The rule compares the whole first word, in lower case. So SH and shell-session are not shell blocks. A command is one line of the block, together with each line that a trailing \ joins to it. Nothing else joins two lines, and a \ joins only when no quote or span is open and it is not in a comment. An escaped backslash, \\, at the end of a line joins nothing. A \ before a CR joins nothing, because the shell reads it as an escape of the CR. A line can end inside a quote or a span. The quotes are a single quote, a double quote and a $'…' quote. The spans are a pair of backticks, a $(, a $((, a <(, a >( and a ${. Then the rule reads the programs of that line and reports an error for the rest of the block, because it cannot read it. These spans nest, and each one closes only with its own closer. A $(( closes with )). A ) inside an open ${ is text, and a } inside an open $( is text, as the shell reads them. A block that needs a quote across two lines passes under a scope=block directive with reason=accepted_deviation. In a $'…' quote, a backslash escapes the next character, so \' does not close the quote. A quote inside backticks opens nothing. A # starts a comment only when no quote or span is open. It must also be at the start of a line or after a blank, ;, & or |. The comment continues to the end of the line. After ), < or >, a # is part of a word. Inside a $( or a ${, a # is part of a word. A quote, a \ or a << in a comment has no effect. A << inside quotes or inside $(( )) opens no here-document. The rule still reads a command that a last trailing \ does not finish. The rule reads the first word of each command. It also reads the first word after each |, ||, &&, & and ; that is not in quotes. It also reads the first word after each $(, <(, >( and opening backtick that is not in single quotes. The command around such a span reads on after its closer, and the span is part of the word that holds it. So FOO=$(pwd) npm ci runs pwd and npm, and echo $(date) x runs echo and date. It skips a blank line, a line that starts with #, a $ prompt, and an assignment such as NAME=value before the program. It also skips a reserved word of the shell, such as if or then, and the body of a here-document. In a console block, a line with no $ prompt is output, and the rule does not read it. The rule compares the last segment of a path, so ./headwater is headwater. A step that runs an undeclared program on purpose passes under a scope=block directive with reason=accepted_deviation. A taxonomy that declares no commands generates no instance, and the rule instantiates only on a typed document that adopter_documents lists. These are the limits of this rule. A fenced block with no info string or with a different info string is not read. An indented code block is not read. So an author tags each block of commands sh. A program that a different program runs, such as sed in xargs sed, is not read. The rule finds a here-document at the first << outside quotes, comments and $(( )). The end word is the word after the << without its quotes and backslashes. It stops at a blank, an operator, < or >. So <<\EOF, <<$'EOF' and <<EOF>out.yml each end at EOF. The rule skips the body to the first line whose text, without blanks at the ends, is the end word. It finds only the first here-document of a line, so it reads the body of a second one as commands. README.md at the root is untyped, so this rule does not read it either. Only the three language rules read it, through outside_root.

--fix records a verification only where the change states one. A verified_revision stamp on a source-tree edge says that a person read the document against the file it governs. So --fix offers the stamp when three conditions hold. The run carries --change. The change states that a person re-read the document that declares the edge. The relation declares verified_revision. A change states the re-reading in one of three ways. It adds the document. It moves the document's last_verified. Or it names the document in a verified line. --fix leaves every other suspect edge for a person, and it stamps no edge in a run without --change. The date does not decide, so one tree gets the same answer on every day. The target of the edge does not decide either. A change that edits a governed file makes every edge onto it suspect, and nobody re-read those documents. To record a stamp, re-read the document and set its last_verified. Then run headwater change <base> <dir> and headwater check --fix --change <dir>/manifest. A second change on one day cannot move last_verified, because it already shows that day. In that change, pass --verified <document> to headwater change, which writes the verified line. An edit to the document does not state a re-reading by itself. Otherwise a small correction a month later would stamp edges that nobody re-read.

Nothing here reaches a network. No crate that this verb reaches depends on an HTTP client, and the checks read the tree in front of them. The one crate with a client is headwater-fetch. It serves taxonomy vendor, and only the binary links it (HW-DR-0075).

Preconditions

.headwater/taxonomy.lock is there. headwater taxonomy resolve writes it, and it is written only when the taxonomy validates, so a lock is a validated taxonomy. Without one the verb prints the path it looked at, names the verb that writes it, and exits 1. Everything downstream of the load reads the lock, so this is the precondition every other one sits behind.

.headwater/taxonomy.yml reads, and it names the corpus root and the exclusions. A corpus root that no directory answers is an empty census rather than a refusal.

A language regime can list paths outside the corpus root, and the census reports them on their own line. The line reads outside the corpus root: 4 paths, ste_house 4, with the number of paths that each regime binds. Such a path is not a row of the census. So it does not change the count of files under the root or the coverage account. The three language rules read it and no other rule does (HW-DR-0084). The report names under that line each pattern that matches no file and each pattern that the record refuses. Each of these is also an error of language.outside_root.refused, so --strict fails on it. A symlink is never followed: a listed path that is a link, or that a wildcard matches as a link, is refused and reads nothing. A corpus that lists no such path prints no line.

The host states a date, or --now states one. The clock is read once, in this verb, before any check runs. Spec 12 makes the date an injected value rather than a call inside a check. One corpus, one lock and one date therefore write one set of bytes. A host with no readable clock and no --now is refused rather than guessed at.

With --change, the manifest reads. A manifest this engine cannot open ends the run, because a line that was dropped reads as a document that did not move. A path inside the manifest that reaches no row of the census is counted and named in the report. So a mistyped path is visible rather than absorbed.

Options

Option What it does
--strict Exit non-zero when a finding is an error. Without it the run is advisory.
--fix Write the patch that rides with a finding, in this working tree, before the report is composed. A finding carries a patch only where the remedy is mechanical and total, and a suppressed finding carries none. A verified_revision stamp also needs --change, as the Description states. The report is the state after the write, so a patch that produced a document the checks reject is reported on the same run.
--no-cache Read and write no cache, and evaluate every instance. Standard output is the same either way, and a difference is a defect in the cache rather than a result.
--now <date> The date to evaluate against, as YYYY-MM-DD. It defaults to the system clock.
--change <manifest> The manifest of a change, which the rules that read a transition use. The flag does not narrow the documents that the run checks. A run with a manifest that names one file reports the findings on every other document too. The first line is headwater change 1. A file that opens with anything else is refused rather than read. Each line after it is added<TAB><path>, prior<TAB><path><TAB><file> or verified<TAB><path>. The second form names a file holding the bytes that stood before the change. The third form states that a person re-read the document at <path>, and it names no version of that document. A document the manifest omits did not move. Without the flag, every rule that reads a transition reports each of its instances as skipped rather than as passed. headwater change is a producer of this file, and .githooks/change-manifest is the one this repository's own hooks call.
--read-set <path> Write the read set of this run to a file as well as into the report. headwater gate is the reader.
--register <path> Write the obligation and control register of this run to a file as well as into the report. The content is the content already in the report. The report lays its copy out at the width of the run. This file is written at no width, because nothing reads it back.
--format text\|json\|sarif\|markdown The vocabulary the report is written in. text is the default and the one a person reads. sarif is what a forge ingests, markdown is a job summary or a review comment, and json is the finding shape that spec 4 declares. The JSON report, member by member names each member of the json report, and Raising the version gives each version of its shape. sarif writes its own loss set into the artifact. markdown declares one in the source and not in the artifact, because nothing it writes is machine-readable. text declares one drop there too, the routing of each skip, and carries the census and the graph that no other format holds. json declares one there as well, the per-document account of coverage, and writes no loss set of its own. The flag moves no verdict and no exit status.
--json The same artifact --format json writes, byte for byte, on both streams and with the same exit status. A command line that states both is refused, because two names for one target is a question answered twice.
--root <path> The repository to read. It defaults to the working directory.
--no-color Force plain text on both streams: bold and dim weight plus glyphs, no escape sequence. The default already senses whether each stream is a terminal, and renders color only there.
--no-banner Suppress the masthead: the line naming this binary and its version, that the root help screen alone prints. It is accepted here and does nothing, since only the root screen prints one.
--wide How wide the help and the report of this verb are laid out. COLUMNS states the width, and this binary holds the reading to the range 80 to 120. A reading that is absent or is not a number gives 80, which is the width a run with no flag gives. headwater check --wide, headwater check --wide --format text and headwater check --wide --help are each laid out at that width. The flag is refused beside --format json, --format sarif, --format markdown and --json, because nothing lays a machine format out. --json is named here as well as --format json, because the two spellings reach one target. A refusal that read one of them would accept the other. The read-set block of the report is never laid out, for the reason the Files section gives.

Every flag above belongs to this verb, and a flag that belongs to another verb is refused here. headwater check --level L0 exits 1 and writes no report, because --level is a flag of headwater conformance. The table above is the set of flags that reach this verb. Two flags are answered before the verb is reached. --help and --version, on both spellings, each exit 0, write to standard output alone, and produce no report. --wide reaches the verb and states the width of its report, and it exits 1 beside a machine format alone, in either spelling of one. HW-DR-0033 is the ruling that a flag belongs to the verb that reads it. engine/crates/cli/tests/wiring.rs holds this paragraph over a corpus where the verb otherwise succeeds, and engine/crates/cli/tests/width.rs holds the --wide sentence in it at both ends.

Exit status

0 where the run completed and no reason below applied. A finding of any severity, including an error, leaves the status at 0 unless --strict was passed.

1 for each of the twelve reasons below. There is no third status, so a caller reads the message to tell them apart.

The reason Where it is decided
A flag that names a value has none after it. Or --now is not YYYY-MM-DD. Or the command line holds a word this verb does not read. Or it names one target twice, as --json beside --format the parse, before the verb is entered
--format names a target that is not one of the four check, before the corpus is walked
The host has no readable clock and no --now was passed check, before the corpus is walked
--change names a manifest that did not read check, before the corpus is walked
--fix composed a patch and the write did not land fix, before the report
The lock is absent, or a declaration under it did not read. Or the imports or harvests block of .headwater/taxonomy.yml did not read, or two of its entries name one resolver. Or the block or one of its entries is not a mapping. Or the at path of an entry is outside the repository root, through .., an absolute path or a symlink load, called by check
Standard output or standard error could not be written wherever the write is
The report lost a finding that no declared loss reason covers the adapter census, after the report is written
--read-set names a file that could not be written after the report is written
--register names a file that could not be written after the report is written
--fix was passed and a file refused its patch after the report is written
--strict was passed and at least one finding is an error the last reading of the verb

Two of those are worth separating. A refused patch is not a finding, so no absence of --strict softens it. The verb was asked to write and did not, and a caller who read a 0 would believe a corpus was fixed. And the report is written before the last five rows are decided. A run that exits 1 for one of those five still put a complete report on standard output. That holds in text, json, sarif and markdown alike.

The first six rows are refusals, and a refusal writes nothing to standard output. The account of a refusal is one English sentence on standard error, under --json and --format json alike. So the property is that a refusal writes no document, and not that a non-zero exit writes none. HW-DR-0043 rules it, and a_refusal_writes_no_document_and_accounts_for_itself_on_the_other_stream in engine/crates/cli/tests/json.rs holds both halves.

A stream that cannot be written is the seventh row, and the verb does not panic on it. A full disk and a closed pipe are the usual causes. Where standard output fails, the report is not complete. The verb then writes one sentence on standard error that names standard output and the error of the host. Where standard error fails, the report on standard output stays complete, and the verb says nothing more. In both cases the status is 1 and never 101, so a caller can tell a full disk from a defect in the engine. the_report_survives_a_standard_error_that_cannot_be_written and a_report_that_cannot_reach_standard_output_exits_1_and_says_so in engine/crates/cli/tests/json.rs hold the two halves.

Environment

No environment variable reaches this verb. The date is --now, the corpus is --root, and the taxonomy is the lock. Six variables are read by test targets alone and reach no shipped code path. HEADWATER_BLESS re-records a fixture. HEADWATER_STOCK_VALIDATOR, HEADWATER_SARIF_VALIDATOR, HEADWATER_SHA256_ORACLE, HEADWATER_JSON_ORACLE and HEADWATER_SHELL_ORACLE each turn a reading taken by a tool outside this repository from a note into a requirement. The last of the five is bash, which is the only reader that can say a completion script loads. The continuous-integration job sets all five, so a lost dependency fails the job rather than going quiet.

One variable reaches this binary, and a run of this verb reads it under --wide alone. COLUMNS says how wide the help and the report of this verb are laid out. engine/crates/cli/src/paint.rs reads it, and only where the raw command line carries --wide. That call is the one std::env::var under engine/crates/ outside a test target. The reading is held to the range 80 to 120, and a reading that is absent or is not a number gives 80. So a run of this verb that carries no --wide is a function of the command line, the tree and the lock. Nothing a shell exported reaches it.

A measurement build writes more to standard error, and no shipped build is one. The phase-times feature of headwater-cli is off by default. A binary built with it writes one phase <name> <microseconds> line for each stage of this verb, and a measurement script of this repository reads those lines. release.yml builds the default features, so no released binary carries it, and no variable or flag turns it on.

A reader who met HEADWATER_NOW in a continuous-integration job is reading a shell variable of that job, which the job passes to --now.

Files

Path How this verb treats it
.headwater/taxonomy.lock read. The taxonomy every check is generated from, and the digest that keys the cache. A lock that is not a regular file stops the run with exit 1 before the run opens it. A named pipe is one example. The message names the path. A link to a regular file is read.
.headwater/taxonomy.yml read. The corpus root, the exclusions, the package the repository consumes and the digest pin. A declaration that is not a regular file stops the run as the lock's row states.
.headwater/packages/<name>/ read where .headwater/taxonomy.yml pins a digest. Each file is hashed against the pin and nothing in it is read as a declaration. The pin file and each file here join --read-set's output.
the corpus read. Every file under the declared root that no exclusion removes.
.headwater/cache/checks read and written, unless --no-cache. A file that is absent, unreadable or written by another engine reads as an empty cache. That costs one full run and is not an error. A cache that cannot be written is one line on standard error, headwater: cache not written: <path>: <error>. That is not an error either, and it changes no exit status and no byte of standard output.
.headwater/cache/.gitignore written, unless --no-cache. The pattern excludes .headwater/cache/checks and keeps itself, so a repository that adds this directory never needs a line of its own for the cache.
.headwater/imports/ read where .headwater/taxonomy.yml declares an import, for the anchors an imported snapshot supplies. An imports.<name>.at path outside the repository root stops the run with exit 1. A snapshot binds no anchor when its pin has no digest, when it is not the pinned artifact, or when it does not read. Then import.pin.unread reports one error on .headwater/taxonomy.yml for each such import that names a resolver, whether or not an anchor names that resolver. The error names the pin and its path, and it says why the snapshot binds nothing. The run reads each snapshot on every run and never caches the result. Each file of the snapshot joins --read-set's output, so headwater gate on a later tree sees a snapshot change. An absent release record or payload joins it with no digest.
the path each harvests.<name>.at names read where .headwater/taxonomy.yml declares a pinned corpus export, for the anchors that export supplies. The run compares the digest of the file with harvests.<name>.digest before it reads a document. A pin with no digest binds no anchor. An absent file binds no anchor, and neither does a file that fails the pinned digest or is not an export. Each anchor into such a pin is then a relation.target.unresolved finding that names the pin. Also, harvest.pin.unread reports one error on .headwater/taxonomy.yml for each such pin, whether or not an anchor names the pin. The error names the pin and its path, and it says why the pin binds nothing. The run reads each pin on every run and never caches the result. Each path joins --read-set's output, so headwater gate on a later tree sees an export change. A path outside the repository root stops the run with exit 1 before the run reads it.
.headwater/ids/ read, and written under --fix alone. The identifier claim store, which two corpus-scoped rules take as one input with a digest over its whole listing. A write here creates a file and never modifies one, so a claim this verb met is a claim it left. The verb never opens an entry that is a named pipe, a socket or a device. identifier.claim.stale reports such an entry at its own path in its own sentence, and the remediation tells the reader to delete it.
.headwater/observations.yml read. One entry per line item, either kind read by the same parser. A kind: control entry, or one naming no kind at all (the shape every snapshot before #937 wrote), names a control that discharges its obligation through a mechanism outside the engine. It carries the commit it ran at, on the same absent-reads-empty terms as .headwater/ids/. A kind: verification entry names the commit a verification was observed at. It also names the digest of the acceptance criterion it proves, taken when the snapshot was written. relation.target.verification.suspect compares that digest against the criterion's digest today, and reports the pair once they disagree. This verb never writes either shape. A control entry feeds the obligation register, a taxonomy-scoped projection that the engine computes on every run and never caches. A verification entry feeds two readers. The first is an edge-scoped instance of that rule, on the same terms as any other Graph-origin rule. The second is the verification block of the report, which names each verification of the corpus with one state: declared, observed at <commit> or suspect since <commit>. The block also names each criterion that changed. Where the file or the entry for a verification did not read, the state is unknown. That is because the run does not know whether an entry names it. The rule then reports no suspect finding for that verification: it skips the instance with the reason, and the skip shows in the coverage line. An entry keyed by a verification that names no kind reads as a control. So the verification is unknown and not declared, and the rule skips it. The block also names each entry that names no verification of the corpus. Where such an entry named kind: verification and failed its own shape, the line also gives the reason that it did not read. An entry of another kind, or of none, stays out of the block, and control.observation.invalid reports it. The engine matches kind exactly, so Verification is neither kind. The rule and the block use one comparison, so a finding and a line never disagree. The block states once that each observed or suspect state is transcribed from this file, and it gives the digest of the file. The block is a projection that the engine computes on every run and never caches, as the register is. Present, the file joins the cache key of each instance of relation.target.verification.suspect. It also joins --read-set's output, as the claim store does. So headwater gate on a later tree sees an edit or a deletion of it. Absent, it joins no cache key and no read set, on the terms .headwater/ids/'s own row states.
the path --read-set names written. The report carries the same bytes, indented two spaces and laid out at no width. headwater gate reads that file as a grammar, so a line break inside it would refuse the file rather than widen it.
the path --register names written. The report carries the same content, laid out at the width of the run. Nothing reads this file back, so the layout costs no consumer anything.
the path --change names read.
a document of the corpus written, and only under --fix.

Without --fix, this verb writes no byte of the corpus. The cache is outside the corpus root, so a run that wrote one changes nothing a check reads. The claim store is outside it too, and a claim --fix made is read by the next run rather than checked as a document.

The JSON report, member by member

headwater check --format json writes one JSON object to standard output. This table names each member of the object at the top level, and each member of taxonomy, change and an entry of rules. A test holds the table to what the engine writes. A path uses . between a key and its parent, and [] for each element of an array. Spec 4 declares the members of an entry of findings.

Member When it is present What it means
version Always The version of this shape. Raising the version gives each version and what it added.
tool Always The name of the tool that wrote the report, headwater.
taxonomy Always The taxonomy that the run took from the lock.
taxonomy.package Always The name of the package that the corpus uses.
taxonomy.version Always The version of that package.
taxonomy.lock Always The digest of .headwater/taxonomy.lock.
clock Always The date that the run evaluated against, from --now or from the clock of the host.
change Only on a run with --change The account of the manifest. A run without --change writes no change, so its absence says that the run read the whole corpus.
change.documents With change The number of documents that the manifest names.
change.added With change The number of those documents that an added line names.
change.carried With change The number of those documents that a row of the census holds and whose prior version the run read.
change.unreadable With change The number of those documents whose prior version does not read.
change.verified With change The number of documents that a verified line names and that a row of the census holds. This count overlaps the counts above.
change.verified_alone With change The number of documents that only a verified line names. added, carried, unreadable, verified_alone and the length of unmatched sum to documents.
change.unmatched With change Each path of the manifest that no row of the census holds, so that a mistyped path is visible.
change.promotions With change The number of warrants that the change moves from asserted to accepted.
coverage Always The census counts, the instances and the account of each skipped instance.
rules Always One entry for each rule that the run served.
rules[].rule Always The identifier of the rule.
rules[].scope Always The grain of each instance of the rule, such as document, edge, corpus or taxonomy.
rules[].version Always The version of the check, as a number.
rules[].obligations Always The identifiers of the obligations that the rule serves. The array is empty when the rule names none.
rules[].compared Only on the entries for link.identifier.mismatch and link.fragment.unresolved The number of links that the rule compared, whether they pass or not. "compared": 0 says that the rule examined nothing. It is not written on the entry of any other rule.
findings Always One entry for each finding of the report, in the shape that spec 4 declares.
read_set Always Each input that the run read, with the digest of its content. An input with no digest carries an empty array in place of the digest.

Raising the version

The current version is 1.5. The version is a constant of the shape, and it is not the version of the engine. Two engines that write one shape write one version, so a reader does not read the shape again. The SARIF report carries change and coverage in its property bag, so this version also applies to those two members of that report.

Each version added a member, and each addition moved the minor. So an absent member says a different thing at each version. At 1.1 a report with no change says that the run read the whole corpus. At 1.0 it says only that the engine had no such member to write.

  • 1.0 was the first shape, when --format json arrived. It wrote version, tool, taxonomy, clock, coverage, rules, findings and read_set. An entry of rules held rule, scope, version and obligations.
  • 1.1 added change, for a run with --change.
  • 1.2 added the account of skipped instances to coverage. So "skipped": 0 says that each instance of the run reached a verdict (#233).
  • 1.3 added documents and unrouted to each entry of coverage.skips. documents names the census rows that the skips of that class fell on, with the rules that skipped each row. unrouted counts the skips of that class that fell on no document (#654).
  • 1.4 added verified and verified_alone to change. Before 1.4, a document that only a verified line named was in documents and in no other count, so the counts did not sum (#1398).
  • 1.5 added compared to the entries of rules for link.identifier.mismatch and link.fragment.unresolved. A rule that reports nothing is silent over links that all pass. It is also silent over a corpus with none of its links, and compared tells the two apart (#1347).

See also

headwater taxonomy resolve writes the lock this verb reads, and a check that reports a rule nobody declared is a lock that was not written again.

headwater gate reads the file that --read-set writes, and answers whether the verdicts of this run carry to another tree.

headwater change writes the manifest that --change reads, from a base revision and the working tree in front of it.

headwater sweep reports what no check can see. Its findings reach no rule and no exit status of this verb.

The command surface lists every verb this binary dispatches, and it marks the ones that no contract describes. headwater generate writes it.

Spec 12 declares the check layer: the scopes, the cache, the read set, and the fixability bar that decides which findings --fix acts on. Spec 4 declares the finding shape and the register. Spec 6 declares the exit-code convention this verb follows.

The commit gate of this repository is .githooks/pre-commit, and it runs headwater check --strict after .githooks/change-manifest writes the manifest that --change reads.