On this page

Failure catalogues: every way a functionality fails

A command that fails tells you what failed. Does it also tell you why, what it saw and what fixes it, and has anyone ever made it fail that way on purpose? A failure catalogue is the answer written down once, in code, beside the functionality that raises it: every way it can fail, including the ways nobody has met yet.

The unit is the functionality, not the command. One command runs several functionalities — enrolling a machine reads the registry, probes the machine over SSH and writes the registry back — and the same registry read runs under many commands. So each functionality declares its own causes once, names the entries that start it (commands, Stado Desktop screens) and the functionalities it uses, and a command meets every cause of what it starts and of everything that uses in turn.

What a cause declares

field what it says
id the cause, lowercase words joined by -, stable within its functionality
failed the step that failed, as you would name it
cause why it failed
observed the state the refusal shows: the values that do exist, the path read, what the layer below answered
remedy what fixes it: the command to run, the declaration to change
code, exit the failure code and the exit status the command leaves
test the real test that provokes it, tests/<area>/<file>::<test name>

A functionality declares its id (its failure point, stado.fleet.enroll), a summary, its documentation page, its entries, the functionalities it uses and the causes of its own steps. One made only of what it uses declares no cause of its own; one only other functionalities run names no entry.

The same declaration produces the refusal you read (the Error: line, a fix: line, and a cause: line), the cause, functionality and remedy of the --json envelope, the stado errors show listing and the causes on the command's page in the CLI reference. The cause: line names the cause by the functionality that declares it, because an id alone names no one cause (the store's missing and the vault's are two), and the catalogue to read for the rest: the one the command starts when it has one, otherwise the declaring functionality's own.

cause: stado.registry.read.registry-missing; `stado errors show stado.registry.read` lists every way it fails

A command page that starts no declared functionality says so first: `stado <command>` starts no functionality that declares how it fails yet, so the ways it fails at run time are not listed here; `stado errors list` names every such command, and the sentences below are read from its help text.

The layers every command reads through

Most commands meet the same few layers: the object store, the vault, this machine's configuration and files, the fleet registry, the host channel, and the layers a family of commands shares, such as how every stado service verb finds what it acts on. Each declares its own causes once, raised by the one conversion every command's failure passes through, whichever command met it. The layers every command reads through lists each layer with its causes.

Which functionalities declare one, and which commands start none

stado errors list [--json | --markdown]

list prints one line per functionality that declares a catalogue (its id, how many causes of its own, its entries or run by the functionalities that use it, and its page), then reads the CLI's own command tree and says how many of the commands that run something, a command with no subcommand of its own, hidden ones included, a functionality names among its entries:

<named> of <commands> commands start a functionality that declares how it fails; <rest> start none:
  stado <command>
  …

With --json the answer is {"operations": [...], "commands": <n>, "undeclared": ["stado <command>", ...]}, each functionality carrying its operation (the id), entries, uses, summary, docs and causes. --markdown prints each catalogue's documentation section.

stado errors show <functionality> [--json | --markdown]

show prints the functionality, how it is started, what it uses, and every cause it can meet — its own and those of every functionality it uses — each with declared by <functionality>, what failed, why, what was observed, the fix, its code, exit status and test. A functionality that declares no catalogue is refused with the usage status (cause stado.errors.show.functionality-unknown): showing the failure catalogue of '<id>': no functionality of this Stado declares a catalogue under that id; observed: the functionalities that do: <ids>, fix: name one of them as stado errors list prints it.

Check the catalogues against the CLI and the repository

stado errors check --source <directory>... --repository <checkout> [--baseline <file>] [--tests-baseline <file>] [--json]

check exits 0 only when nothing below is found, and otherwise refuses with exit 1 (cause stado.errors.check.standard-not-met): holding the catalogues, the commands and the source to the error standard: <n> problem(s) stand; observed: <every finding, joined by "; ">:

  • a declaration that is not one: a cause without its fix or test, an id that is not lowercase words, a usage refusal declared retryable, the catch-all unknown, an empty entry, a functionality that uses itself or declares no cause and uses nothing (each cause <id>: … or operation <id>: …);
  • what only the whole catalogue shows: operation <id> is declared twice, operation <id>: it uses <id>, which the catalogue does not list; list every functionality the product runs, and operation <id>: nothing starts it: it names no entry and no functionality of the catalogue uses it;
  • an entry naming a command the CLI does not have: the functionality <id> names the entry , which this CLI does not have; name the command as a person types it;
  • a declared cause whose test the repository does not hold and the --tests-baseline list does not name: <functionality>.<cause> names the test <test>, which the repository does not hold: <why>; write that test, or name the one that provokes this cause, where <why> is <checkout>/<file> is absent, <checkout>/<file> cannot be read: <cause>, <checkout>/<file> does not parse as Rust: <parser's words> or <checkout>/<file> declares no #[test] fn <name>. A function counts when it carries a test attribute (#[test], #[tokio::test]) in the file or in a module written inline in it;
  • a test the --tests-baseline list (["tests/<area>/<file>::<name>", …]) names that is no longer absent, or that no cause names: the tests baseline allows <test> to be absent, but no declared cause names it as an absent test; remove it from the baseline. The list only shrinks: Stado keeps it in stado-rs/errors-tests-baseline.json, and its quality gate errors-tests-baseline refuses every candidate that adds a cause without its test or writes a test without taking it off the list;
  • every command no functionality names: stado <command> starts no functionality that declares how it fails; declare every functionality it runs as a wisent_errors::Operation naming it among its entries, each cause with the real test that provokes it, and list it in operations() of stado-rs/src/cli/errors/mod.rs;
  • an error message in the source under each --source directory holding a generic sentence ("did not succeed", "operation failed") beyond what --baseline ({"<path>": {"<sentence>": <count>}}) allows, and a baseline entry above what the file still holds (lower it to <count>).

It first says what it read:

<n> functionality(ies) declare <c> cause(s), <m> of which name a test the repository does not hold; <u> of <commands> commands start no declared functionality; <g> generic sentence(s) in the source; <p> problem(s)

With --json the same counts are operations, causes, missing_tests, commands, undeclared and generic_sentences, every absent test under missing (each {cause, test}), the problems about tests under test_problems, and every finding under problems, followed by the refusal's envelope.

Refusals that end the check without a verdict, each a declared cause of stado.errors.check with its fix: line (the repository is judged before anything else is read):

  • repository-not-directory, usage status 2: reading the declared causes' tests from <path>: it is not a directory, so no test file can be read from it; observed: --repository <path>;
  • no --repository or no --source: the parser names the missing option, usage status 2;
  • source-unreadable, usage status 2: scanning the source directory <path> for generic sentences: the directory, or an entry of it, cannot be listed; observed: <the system's answer>;
  • source-file-unreadable, exit 1: reading the source file <path> for generic sentences: the file cannot be read as UTF-8 text; observed: <the system's answer>;
  • baseline-unreadable, usage status 2: reading the baseline <path>: the file cannot be read; observed: <the system's answer>;
  • baseline-malformed, usage status 2: reading the baseline <path>: it is not {"<path>": {"<sentence>": <count>}}; observed: <the parser's answer>;
  • tests-baseline-unreadable, usage status 2: reading the tests baseline <path>: the file cannot be read; observed: <the system's answer>;
  • tests-baseline-malformed, usage status 2: reading the tests baseline <path>: it is not a JSON array of test names, each tests/<area>/<file>::<name>; observed: <the parser's answer>.

A product written in another language prints the same JSON from its own errors list --json; node ci/error-standard.mjs catalogue <file> --repository <checkout> --entries <entries.json> in wisent-errors applies the same rules to it, with the product's own list of entries.

In Stado Desktop

Releases → Available operations… carries List every functionality's failure catalogue and the commands that start none, Read every declared way one functionality fails and Check that every command starts functionalities declaring how they fail, each cause with its real test. Each runs the same stado errors command with --json on the selected Stado API host and shows its answer or refusal.

The real journey

kompilator test --manifest-path stado-rs/Cargo.toml --test errors-catalogues

It runs the checkout's own stado in an isolated home. list must split every command the binary runs into the ones a functionality names among its entries and the ones starting none, each answering --help. check runs against a fixture repository holding one cause's test as a #[test] function in an inline module (not refused), one cause's function without a test attribute and one file that does not parse (each refused with its sentence), then against an empty repository (every cause's file absent), and must refuse a --repository that is not a directory and a check that names none. Every command, its exit status and both streams are kept with the source revision under .build/errors-catalogues/.

kompilator test --manifest-path stado-rs/Cargo.toml --test errors-tests-baseline

The quality gate of the same name. It checks this checkout behind stado-rs/errors-tests-baseline.json and must find no test problem; a list naming one more test that exists must be refused naming exactly that test; an unreadable list and a list that is not an array must each be refused with its declared cause. Each run's command and answer are kept under .build/journeys/errors/.

Source: this website