On this page

Which catalog a command reads

Without --catalog, the authority is <workspace>/stado/catalog/products.yml. --workspace DIR, else WISENT_WORKSPACE, selects the workspace, default ~/Documents/CodingProjects/Wisent; the host process's product sync uses ~/.stado/products/workspace. When that file is absent, a command reads the catalog compiled into Stado.

Readers read the committed catalog

A catalog file that a checkout tracks is read at that checkout's committed head, or at the checkout's origin/main when the head is behind it. It is also read at origin/main when the head has parted from it with commits of its own that leave the catalog as it was at the fork while origin/main changed it since (a main that kept a session's unpushed commits): the published catalog then holds every line the head's does and every one published after they parted, and the installation agrees with the catalog service ensure renders the unit from. When the head's own commits changed the catalog, it is read at the head. Stado's own catalog/products.yml is such a file. Every command that reads the catalog works this way: stado product catalog, status, remove, policy set, dependencies, advisories, build-trees, documentation, registry check-claims, and Stado's change and quality hooks. Several sessions share one checkout. An edit one of them has not committed yet, half written or older than a field the catalog now requires, therefore never refuses or steers another session's read.

The working tree's edit is reported on stderr instead of read:

  • an edit that does not parse: the working tree's catalog <file> has an uncommitted edit that does not parse: <error>; it is not read, <checkout>:<path> at <commit> was
  • an edit that does not validate: the working tree's catalog <file> has an uncommitted edit that does not validate: <error>; it is not read, <checkout>:<path> at <commit> was
  • a valid edit: the working tree's catalog <file> has an uncommitted edit; readers read it once it is committed, and <checkout>:<path> at <commit> was read
  • a working tree whose catalog cannot be read: the working-tree catalog <file> cannot be read (<error>); <checkout>:<path> at <commit> was read

Nothing is said when the working tree holds the committed document. stado product catalog --json names what it read under catalog_read: its source, the revision, and why that revision was chosen (committed-head or published-main). catalog_read is null for a file no checkout tracks and for the compiled-in catalog.

The submit queue (stado change submit and stado change run) reads the catalog at its checkout's origin/main, fetched first, never at the committed head: every landed change was reviewed and gated against that catalog, and a local head another session committed and did not land, which the installed Stado may not even validate, steered or refused every submit on the machine. A head that changes the catalog is named on stderr instead: stado: the committed head of <checkout> changes <path> and has not landed on origin/main; the submit queue reads origin/main's.

A file no checkout tracks is read as it stands and validated. That covers a catalog outside every checkout, or one untracked or ignored inside one, such as an operator's or a test's --catalog file. A catalog whose directory git cannot read for any reason other than "not a git repository" is refused with git's answer: git could not say which checkout holds the catalog <file> (<status>): <git's stderr>.

Editing the catalog

stado product registry add|set|rm edit the working tree's file. For a tracked catalog, the answer's uncommitted names the checkout, the path, that readers see the edit once it is committed, and the command that commits it (git -C <checkout> commit -m <message> -- <path>). uncommitted is null for an untracked file, which readers read as written. stado product create reads back the file it registered a product in, before that edit is committed.

A catalog kept as parts, and its one-file copy

Stado's own catalog is kept as parts, catalog/products/ (one <id>.yml per product, continuations <id>.<name>.yml, authority.yml for the catalog's own keys), which a Stado built from main compiles in and reads. A Stado built before the split knows no parts and reads catalog/products.yml, so that file stays beside the parts as their copy. The parts are the catalog; the copy is derived from them:

  • every stado product registry edit of the parts writes the copy again from what the parts join into, opened by # Derived from the parts in products/, for a Stado that reads no parts: every registry edit writes it again. Edit the parts, never this file. A directory without a copy beside it gets none. Where the copy said otherwise than the parts did before the edit, the edit says so on stderr before it overwrites it: stado product registry: the copy <copy> said otherwise than the parts in <n> place(s) (<id>: <field>; …); it is written again from the parts, so carry into the parts any of those edits made only in the copy (git log -p -- <copy> keeps them).
  • stado quality check refuses a revision whose copy says otherwise than its joined parts, before it is pushed (stado.product.catalog.parts.copy-differs): checking that the copy <copy> says what the parts of the catalog <parts> say: the copy, which a Stado that reads no parts reads, says otherwise than the parts joined in <n> place(s), so an edit made in one of them never reached the Stado that reads the other: <id>: <field>, <field>; <id>: only the copy holds it; the catalog's <key>. The copies are compared as documents, record by record and field by field, so layout, quoting and comments are no difference. Fix it by carrying every edit made only in the copy into the parts (stado product --catalog <parts> registry set <id> …), then letting any registry edit write the copy again, or removing the copy when no installed Stado reads it.

tests/product_catalog/parts.rs::a_registry_edit_writes_the_copy_beside_the_parts_again edits the parts of a directory with a stale copy beside it and reads the copy back as the parts joined, and a directory without one gets none; tests/quality/push/catalog_copy.rs pushes a commit whose copy says status: retired where the part says preview, refused naming probe: status, and one whose copy agrees, which lands.

Installations

The recipe an installation executes, after_install included, is read from a committed catalog, never from the checkout's working tree. An installation of Stado from --source-commit X reads catalog/products.yml at X, so a verb being renamed in the checkout cannot hand the installed binary a step it does not have. Every other installation, and product sync, reads the committed head as readers do. A checkout nobody brought up to date therefore cannot hand an installation a recipe the published catalog replaced; otherwise its Stado dependency would never match the receipt written from the published catalog and would be rebuilt on every installation. A machine without the checkout reads the catalog compiled into Stado. The report names what was read under catalog: its source, the revision, and why it was chosen (installed-revision, committed-head, published-main or embedded).

A tracked catalog that its commit does not hold is refused before anything is read or built, attributed to the declared state (config). This happens when the file was added to the index but never committed, or when the origin/main that a lagging head is read at does not hold it. The refusal reads the catalog <file> is not committed in <checkout> at <commit>; installations and readers take the committed catalog, never the file on disk, so nothing was read or built. Commit it and run the command again, or name a committed catalog with --catalog.

Repositories no record claims

stado product catalog --unclaimed ORG lists the organization's repositories that are not archived and that no record claims, either as owner_repository or as a surface's repository. When an unclaimed repository's default branch holds a .wisent-release.json, its row carries the product the manifest declares (declares_product) and a finding that says what is missing:

  • The id has a record that does not claim the repository: declares product <id>, whose catalog record does not claim <owner/name>; add it as a surface of <id> (stado product registry set <id> --add-surface KIND=<owner/name>) or correct the manifest's product.
  • The id is already the name of a binary that another product installs: declares product <id> without a catalog record, and <id> is already the name of a binary product <owner> installs; register it under an id no binary takes and correct the manifest, or rename the binary, before stado product registry add.
  • The id has no record at all: declares product <id> without a catalog record; stado product registry add --id <id> --owner-repository <owner/name> registers it, then stado product registry set <id> --submit '{…}' declares how its changes are reviewed.

stado change submit refuses every change of such a repository until a record claims it, naming stado product registry set <product> --add-surface <kind>=<owner/name>, stado product registry add and this listing (the submit queue). A surface Stado installs from a release published under another name than the product id also names that name on its installation, which --add-installation writes as a stado-release recipe field:

stado product registry set wisent-app --add-surface service=wisent-ai/wisent-backend \
  --add-installation 'service=stado-release:manifest=.wisent-release.json;release_product=wisent-backend'

Two refusals apply to the manifest itself:

  • A manifest that GitHub cannot read is refused with GitHub's answer: `gh api repos/<owner/name>/contents/.wisent-release.json` could not read <owner/name>'s .wisent-release.json (<status>): <answer>.
  • A manifest that names no product is refused with <owner/name>'s .wisent-release.json names no product.

Releases no recipe installs

A record that is preview may declare no installation recipe; one that is active may not. A repository whose .wisent-release.json declares "releases": true for a product whose record declares no recipe publishes releases that stado product install has nothing to install. stado quality check refuses such a commit before it is pushed:

stado quality check: <revision> of <checkout>: .wisent-release.json declares releases of <product>, whose catalog record (<status>) declares no installation recipe, so no release of it can be installed; `stado product registry set <product> --add-installation '{"surface":"cli","kind":"stado-release","repository":"<owner>/<repository>","manifest":".wisent-release.json"}'` adds one for each surface it releases, or set "releases": false until it has one

stado product catalog --uninstalled <tree> reads the same for any tree (a checkout or an exported revision) and prints {"tree", "uninstalled": [<reason>]}, with an empty list when the release can be installed. It is not refused, and lists nothing, for a tree with no manifest, with "releases": false, or whose product has no record (--unclaimed lists those); a manifest that is not JSON is refused with <tree>/.wisent-release.json is not JSON. Stado Desktop offers it as Say why the release a product tree declares cannot be installed.

With --revision <rev> it reads the manifest that commit of the checkout records (git show <rev>:.wisent-release.json) instead of its files, so a sweep reads a published revision of a checkout another session is editing; a commit the checkout lacks is refused with <checkout> holds no commit <rev>: <git's answer>, and a tree git cannot list with the tree of <commit> in <checkout> cannot be listed: <git's answer>.

Test

tests/product/catalog/committed.mjs builds a workspace below the checkout's .build whose stado checkout commits a valid catalog, and runs the Stado under test (STADO_BIN) against it:

  • An uncommitted edit that drops a product's owner_repository leaves catalog --json and registry check-claims reading the committed record, with catalog_read.revision equal to the head and the refusal on stderr.
  • A valid edit is read only once it is committed.
  • registry set names the commit command.
  • An untracked --catalog file is read as written.

tests/quality/uninstalled_release.rs (quality-uninstalled-release) commits a catalog of one preview product without a recipe and a product checkout declaring its releases: stado quality check refuses it and --uninstalled lists the same reason; "releases": false lists nothing; a manifest that is not JSON is refused; once the catalog commit adds the recipe, the check no longer refuses it and the list is empty.

Source: this website