On this page

Deciding what products are called

A product has an id, the word every program, receipt, release and route keys it by, and a display name. Both live in its record in catalog/products.yml, together with the decision that gave them and every name it had before. stado product name is the one place they change: stado product registry add gives a new product its first name, and registry set no longer takes --name.

stado product name list
stado product name show ALPHA
stado product name check omega --as-id --org wisent-ai
stado product name set alpha --id gamma --name Gamma --decided-by operator --reason "the name the operator chose"
stado product name set gamma --name "Gamma Prime" --decided-by operator --reason "a display name only"
stado product name forget gamma alpha
stado product name adopt
stado product name usages stado
stado product name sync --check
stado product name sync

Every verb takes --json.

What the catalog keeps

  • naming: {decided_by, decided_at, reason}, the decision that gave the product its current names. decided_at is an RFC 3339 time and is not in the future; decided_by and reason are not empty.
  • former_names: one row per earlier name, {id, name, until}, with the decided_by, decided_at and reason it stood under when one was recorded. A row is written by name set and removed only by name forget.
  • A name, current or former, belongs to one product. Names are compared without regard to case or surrounding spaces. Any write of the catalog (registry, name, create) refuses a catalog where two products share one: <product>: its <what> "<name>" is already the <held as> of <other>; a name, current or former, belongs to one product, so give <product> another name (stado product name check NAME says whether a name is free).
  • No product id names a directory Stado keeps for itself under ~/.stado/products, beside the receipt directories named for products: output, builds, placed, workspace, cargo-build, inputs (the pinned input archives a build keeps by their digest) and wisent-products. A product so named would keep its installation receipts in Stado's own output, build areas, staged bundles or kept archives, so every read of the catalog refuses it (stado.product.catalog.record.product-kept-directory): holding the catalog record of <id> to its declarations: product id <id> names a directory Stado keeps for itself under ~/.stado/products (one of output, builds, placed, workspace, cargo-build, inputs, wisent-products), so the product's installation receipts would share it; observed: id: <id>, fixed by giving the product another id. Stado never reads one of those directories as a product's receipts.

The verbs

  • list prints every product's id, display name, the decision behind them and each former name with when it stopped.
  • show NAME answers which product a name belongs to and how it holds it (id, display name, former id, former display name), with the product's naming record. A word no product holds or held is refused: no product is or was called "<name>" in <catalog>: stado product name list lists every name, current and former.
  • check NAME exits 0 when no product holds the name and 1 when one does, naming it. --as-id also refuses a name that cannot be an id (lowercase letters, digits and inner hyphens) and one that names a directory Stado keeps under ~/.stado/products, with the refusal above. --org ORG also reads the organization's repositories through gh repo list and calls the name taken by a repository named for it or for one of its surfaces (<name>-desktop), saying which product claims it.
  • set PRODUCT records one decision. It requires --decided-by and --reason, and --id, --name or both.
  • forget PRODUCT FORMER removes a former name recorded in error. A name the product really had stays, because it is how a reader of an older page finds the product.
  • adopt moves this machine's installation receipts kept under a former id to the product's current id. stado product sync does the same at the start of every pass (--dry-run only says what would move), so every machine follows a rename on its own.
  • sync writes wisent-names.json at the root of the workspace checkout of every repository a record claims and commits that file alone (git commit --only), so another session's staged or unstaged work in the same checkout is neither committed nor touched. The file holds own, the names of the product that claims the repository, and authority, the names of the product that owns the catalog (authority.owner), through which every other product is reached by capability; no checkout needs a third product's names. Each is {id, name, program, programs, state_directory, environment_prefix, site, address, unit_label, owner_repository, former_ids}, derived from the record alone: program is the first program the installations declare, else the id; state_directory (.<program>) and environment_prefix (<PROGRAM>_) follow the program; site is the cli surface's docs_origin, else https://<id>.wisent.com, and address is the site without its scheme; unit_label is service.unit, else com.wisent.<id>. Each repository answers one row: current, written and committed, rewritten and committed, written, not committed: <git's words>, absent: the workspace holds no checkout of it, or refused: claimed by <products>, so it has no one own product. --check writes nothing and exits 1 naming every file that is missing or stale, and every repository claimed by several products: a checkout's names file is missing, stale or has no one own product, so what it reads is not the name the catalog decided. A names file the system will not read is refused: the system refused to read it, so whether it says what the catalog says cannot be told.

What set changes

With --name alone, the display name changes, the decision is recorded, and the names it had become a former name.

With --id, also:

  • every other record's integrations and dependencies naming the product are carried to the new id;
  • the declarations other systems already hold under the former id are kept, written out where the id implied them: service.unit (com.wisent.<former>, the label hosts run), service.directory_service (the service directory key), release_product on each stado-release installation (the name its published releases and release manifest carry) and the cli surface's docs_origin (https://<former>.wisent.com). Catalog validation accepts a former id's unit label for this reason. A renamed product therefore keeps installing from its published releases, keeps its running service and its documentation; each moves to the new id when its owner moves it;
  • this machine's receipts move from ~/.stado/products/<former>/ to ~/.stado/products/<id>/ and are rewritten to name the new id;
  • sync runs, so every checkout's wisent-names.json carries the decision, and every page and program that reads the file follows it with no edit.

Only the renamed record and the records whose references changed are rewritten; every other record and every comment in the file is kept byte for byte. The answer lists references, kept, receipts, outside and names (one row per repository, as sync answers), and, like registry, uncommitted: for a catalog a checkout tracks, readers read the committed catalog, so the answer names the checkout, the path and the git commit that makes the decision what every reader sees; it is null for a file no checkout tracks. forget answers the same field.

Where pages and programs read the names

A product's documentation pages write {{own.name}}, {{own.program}}, {{own.address}}, {{own.site}}, {{own.state_directory}}, {{own.environment_prefix}}, {{own.unit_label}} and {{own.id}}, and the same fields of authority, instead of the words; a page that shows a placeholder itself writes a backslash before it (\{{own.name}}). The same three commands of landing-cli serve every product's site. landing docs names check --corpus <pages> --names wisent-names.json refuses a line that writes a name by hand and a placeholder the file does not answer, each by page and line, so a new page cannot bring a name back. landing docs names publish … --out <directory> fills the placeholders into a directory of their own, which the site's build reads; a missing names file is refused with stado product name sync as what writes it. landing docs names adopt moves pages written by hand onto the file: each spelling of the product's names becomes its placeholder, and a name joined into another name (stado-rs, a repository's owner/name, a page slug) is left, because it names that thing and moves only when that thing is renamed. The site's own titles, metadata and sidebar read the same file through src/lib/site/names.ts.

What set names but does not change

outside lists every place that still carries the former id, each with what, state and next, the command that moves it. Nothing outside the catalog file, this machine's receipts and each checkout's wisent-names.json is changed, because each place has its own owner:

  • a repository the record claims that is named for the former id or <former>-<surface>: gh repo rename <new> --repo <owner>/<old>, then stado product registry set <id> with the new repository;
  • a release manifest in the workspace checkout of any repository the record claims whose product is the former id, read once per manifest: the catalog keeps release_product on the installations that read it, so change the manifest's product and remove release_product in one change when releases move to the new id; a manifest the workspace does not hold is listed as not read;
  • the documentation site at https://<former>.wisent.com: publish the site at the new address, then drop docs_origin;
  • with --org ORG, a repository of the organization named for the former id that no record claims;
  • the lines of the workspace's checkouts that still write the former id, counted as usages counts them: <lines> lines in <n> of the <total> checkouts still write <former>, with stado product name usages <former> as what names each one.

Refusals

Each leaves the catalog as it was.

Refusal Cause
name set <product> names nothing to change: give --id NEW-ID, --name "New Name" or both; nothing was changed neither --id nor --name
--decided-by and --reason must say who decided the name and why; nothing was changed one of them is blank; clap refuses a missing one with exit 2
--id "<id>" cannot be a product id; nothing was changed the id is not lowercase letters, digits and inner hyphens
<product> is already called <product> (<name>); nothing was changed the decision changes nothing
--id|--name "<word>" is already the <held as> of <other>: a name, current or former, belongs to one product, so choose another (...); nothing was changed another product holds the name, now or before
<word> is the <held as> of <product>, not a product id: name the product as <product>; nothing was changed PRODUCT is a former name or a display name
no product <word> in <catalog>: stado product name list lists every product; nothing was changed unknown product
<catalog> does not exist: a name is decided in the catalog file, so run this where the workspace holds stado/catalog/products.yml (--workspace), or name the file with --catalog; nothing was changed only the catalog compiled into Stado is available
another writer owns <catalog>.lock; held by pid ... another catalog write is running
catalog changed during the operation; nothing was overwritten the file changed between reading and writing
"<word>" is <product>'s current name, not a former one: a current name changes with stado product name set; nothing was changed forget of a current name
<product> has no former name "<word>"; its former names are <list>; nothing was changed forget of a name never recorded

set and adopt refuse to move receipts when the new id already has a receipt directory (<from> and <to> both hold receipts: ...) or an installation of the product is running (the <surface> installation of <former> is running (...); its receipts move to <id> once it ends: run stado product name adopt then). set reports this after the catalog is written; adopt lists such a directory as refused with the cause, moves the rest and exits 1.

Name register

stado product catalog --names adds a Former names table to ~/NAMES.md listing each former id and display name with the product that carries it now and when it stopped, so an older name still leads to its product.

Desktop

Stado Desktop's Products screen runs every verb through the selected Stado API host: Read what every product is called, Read which product a name belongs to, Check whether a name is free for a product, Rename a product: record the decision and carry the new name through the catalog (asks for confirmation), Remove a former name recorded in error, Move installation receipts kept under a former product id to the current one and Read every line of the workspace's checkouts that writes a product's name by hand: what renaming it has to change. Update a catalog product no longer offers a display name field. Answers and refusals are shown as the CLI prints them.

Real journey

STADO_BIN=<stado> node tests/product/name_decisions.mjs in Stado runs every verb against an isolated catalog and home, reads the catalog file and receipts back after each, and asserts each refusal sentence above. STADO_BIN=<stado> node tests/product/name_usages.mjs builds an isolated workspace of two git checkouts that write a fictional product's name in every kind, reads usages back line by line, renames the product and reads the former id's lines in set's outside, and asserts the unknown-name, unlistable-workspace and unreadable-checkout refusals.

Source: this website