On this page

Product catalog commands

Commands

Every former wisent-products <args> is stado product <args> with the same arguments, JSON and exit status. The global --catalog PATH selects another authority file: stado product --catalog PATH <command>. The global --workspace DIR selects another workspace of canonical checkouts: stado product --workspace DIR <command>; one process works in one workspace, and a second different one is refused with this process already works in <dir>; <dir> cannot also be its workspace.

stado product catalog --json
stado product status jeden --surface cli --json
stado product install weles --surface cli --json
stado product signing inspect ~/.stado/bin/jeden --json
stado product kompilator --manifest-path ../stado/stado-rs/Cargo.toml --json stage --bin stado
stado product swift --package-path ../oko-desktop --json index --jobs 4
stado product documentation cli-pages --origin https://stado.wisent.com
  • catalog reads the authority, derives the CLI or service lists (--cli-products, --output PATH, --check PATH), compares it with the copy compiled into this Stado (--check-package), and reports records without approval provenance (--unapproved) or organization repositories no record claims (--unclaimed).
  • registry add, registry set and registry rm change one record under a writer lock. Invalid identities, dangling integrations, a unit one product runs while another retires it, and invalid installation declarations are refused before the file is written, and the neighbouring records and comments are kept byte for byte. A registry set that changes only top-level scalar fields of the record (--status, --visibility, a value on one plain line) rewrites just those key: value lines, so every other line of the record stays as its author wrote it, quoting included; any other change renders the record again. registry add gives a new product its first display name; registry set does not change names.
  • Whether a product's sites link its source is its record's visibility alone. stado product registry set PRODUCT --visibility public|private changes it, and registry add and registry set read the owner repository's visibility from GitHub whenever --visibility or --owner-repository is written: a record that would say public for a repository GitHub keeps private (every site would link a 404), or private for a public one, is refused with <id>'s visibility would be <stated>, but GitHub reports <repo> <reported> … nothing was written: give --visibility <reported>, and a repository GitHub does not answer is refused with GitHub's words. The sites follow with one command over the workspace, landing site source sync --workspace <workspace> --catalog <the file stado product catalog --json prints>, which finds each generated site's product from its git origin, writes the source link the record now gives it and refuses, naming the link and the product, any link into a repository the catalog keeps private (the source link contract landing-cli keeps). --check writes nothing and lists every site that would change. documentation repository-visibility says whether each record's visibility is what GitHub reports.
  • name list|show|check|set|forget|adopt decides what products are called: each change records who decided it and why, keeps every former name findable, and carries a new id through the catalog and the installation receipts: see Deciding what products are called.
  • create provisions private repositories through Wisent Integrations and keeps each request for --status and --resume; create --plan makes every check create makes with reads only and prints what it would create, and surface add provisions one more repository for an existing product: see Creating a product and adding surfaces.
  • install, update, status, rollback and remove act on one product surface (--surface cli|desktop|service, --host for a service). --release-version with --source-commit installs an exact published release of a stado-release recipe on the cli or service surface: the archive that version's signed manifest names for this host's platform is fetched and verified, and a different source, digest or signing identity is refused, not rebuilt. A service installed this way runs on the named host like any other service operation and its unit is ensured as for a source build, so a host can take a product that is not under release control — the vault host's own Skarbiec — without building or signing anything. install and update of a cli or desktop surface given --host are refused before anything is built, because those surfaces install on the machine that runs the command and the build would land there while the caller meant the named host: --host names the host a service surface's unit runs on; the <surface> surface installs on the machine that runs this command, so --host <host> would be ignored and the build would land here. To put it on <host>, run the installation on <host>, or deliver a release there (`stado release prepare` then `stado release promote`); to install here, drop --host. Nothing was built or installed; sync --surface cli|desktop --host <host> is refused the same way. A release coordinate on any other recipe or surface is refused with exact release coordinates require a stado-release recipe on the cli or service surface. --source-commit alone installs a source build of that exact canonical commit: the checkout exports that commit's tree, never its working tree, once origin/main or the canonical checkout's own main carries it. A commit on main that is not pushed yet installs — a repair to the push gate can only be pushed once it is installed — and the installation says <commit> is on <checkout>'s main but origin/main does not carry it yet; installing it as canonical source ahead of origin, the plan's install_decision carries source_on_origin: false and the build's prepared.json the same; a commit on neither is refused as neither origin/main nor <checkout>'s main carries <commit>; only canonical source is installed. Merge it into main (and push it to origin/main) and install again, or name a commit main carries with --source-commit, attributed to the request (refused, which retrying the same commit cannot change), and a repository with no canonical checkout in the workspace is attributed to the declared state (config). A workspace directory that does not exist holds no checkout, never an I/O error: an installation creates it and clones into it, and a command that only reads checkouts answers checkout-missing (no canonical checkout answers for <owner>/<name> in <workspace>; no checkout was created) and leaves it uncreated, as stado quality hooks status does with the row no-checkout (tests/product/absent_workspace.mjs, STADO_BIN). The real journey tests/install/source_commit_ahead.mjs (STADO_BIN, STADO_PRODUCT_TEST_PRODUCT, STADO_PRODUCT_TEST_SOURCE) installs the head of the checkout's main into an isolated home and has a commit on no branch refused with the receipt unchanged. Without it a source build is of the checkout's head, advanced to origin/main.
  • A newer install or update of a surface supersedes an older one still preparing (building, nothing placed): the older process and every process under it (its build script and the compiler under that) are told to stop and the newer one takes the surface, printed as superseding the installation held by pid <pid> …: it is preparing, not placing files, and the wait that follows says told to stop with the <n> process(es) under it, or names each pid that could not be told. One already placing files is waited for, printed as waiting for <lock>: … (it is placing files), and so is one asking for the same installation (the same arguments after the program path, with --json, --wait and --purpose <id> aside, since they change what is printed, whether it waits and why it was asked, not what is installed), printed as waiting for <lock>: … (the same installation, already under way), because stopping it would only restart the same work. --wait keeps the older one whatever it is doing and runs after it. sync, and every installation a sync pass makes, always waits this way: an unattended pass never stops an installation someone started. A service operation (install, update, status, rollback, remove with --surface service) whose --host is another registry host runs on that host: Stado sends the same arguments, with --host in its canonical name, to the host's own installed Stado over its host channel and prints that Stado's output. The source build, the placed files, the restarted unit and the receipt are all on the named host; the machine you typed on builds nothing. The host needs the toolchain its recipe builds with. --catalog is refused for another host, because it names a file on this machine: --catalog names a file on this machine, and a service install for <host> runs on <host> with that host's own catalog. A failure on the host is reported as stado product … on <host> failed (exit <code>): <its output>. A stado-release service built from source is placed where its catalog program runs, $HOME/.stado/services/<service>/current/<platform>, each staged member at its staged path and with no link on PATH; only a cli surface places its bin/ members into ~/.stado/bin. A shared bin/ would let one product's launcher collide with another's (~/.stado/bin/start belongs to another recorded product) and never write the program the unit runs. A release that stages no file named as the catalog program is refused the release stages no <launcher>, the program the catalog runs the service as (<program>); nothing was installed. A service whose catalog program is its product's own CLI program in ~/.stado/bin (Probierz, Singularity, Skarbiec, Stado, the Wisent backend: $HOME/.stado/bin/skarbiec) runs the file the cli surface installs, so it is placed as that surface is, from the same build; a cli surface installed at another revision still owns the file and the installation is refused installation would change <path>, which <product>/cli still owns; finish, install or remove that surface first. A build that stages no such program is refused the release stages no <path>, the program the catalog runs the service as (<program>), among the files its CLI surface places; nothing was installed. Any other catalog program outside ~/.stado/services/<service>/current/<platform> is refused naming it.
  • Every placement, removal and rollback records which surface owns which path under one host-wide lock, ~/.stado/products/ownership.lock, held only for that bookkeeping. An installation of one product that reaches it while another product's installation holds it waits for that holder, and the wait names it, instead of refusing after its build and rolling back.
  • An installation (every lifecycle verb: install, update, finish, sync, remove, rollback, status, cancel) stopped by SIGTERM, SIGINT or SIGHUP prints, before it ends as that signal ends it, stado: this installation received SIGTERM from pid <pid> (uid <uid>); ps -o command= -p <pid> names that process while it runs, and stops as it was told. A newer installation that supersedes it is that pid; any other pid is a stop nothing in Stado asked for (a person, a janitor, another program), so a build stopped half an hour in still names who stopped it.
  • Once every file is placed, the product's own command in ~/.stado/bin (~/.stado/bin/<product>) is started once with --help, which every Wisent command answers without acting. A command the kernel kills at start (on macOS SIGKILL (Code Signature Invalid), although codesign --verify passes) is placed again through a fresh file and started again, printing <path>: killed (signal 9) when started as placed; placing it again through a fresh file; killed again, the installation is refused: the placed <path> is killed (signal 9) at every start, also after it was placed again through a fresh file; …, naming the crash reports under ~/Library/Logs/DiagnosticReports and codesign --verify --strict --verbose=4 <path>. A command ending on any other signal refuses the installation as a broken build.
  • install and update build from the canonical checkout in the workspace (--workspace DIR, else $WISENT_WORKSPACE, default ~/Documents/CodingProjects/Wisent). When none holds the product's repository, or a repository a Cargo or Swift git dependency resolves to, they clone its GitHub origin at <workspace>/<name> and print <owner>/<name>: no canonical checkout was in <workspace>; cloned https://github.com/<owner>/<name>.git into <path>. A checkout works on the default branch its origin names: the one the clone recorded (origin/HEAD), else the one the origin advertises (git ls-remote --symref origin HEAD); no organization or repository is told apart by name. An origin that cannot be asked is refused with <path> records no default branch of its origin, and its origin could not be asked. A checkout on another branch is refused as <path> is not on <branch>; no branch was switched. A directory already at that path that is not the checkout is left alone and refused (… <path> exists and does not identify <owner>/<name>), and a failed clone is reported with git's own error (… cloning <origin> into <path> failed (<status>): <stderr>). status never creates a checkout; sync creates one the same way only with --clone-missing.
  • An installation that names no --source-commit (a dependency install installs first, install, update and sync without one) builds the product's published revision, not the canonical checkout's head, which stands wherever work in that checkout left it: its origin/main, fetched with GIT_TERMINAL_PROMPT=0, else, when the origin cannot be fetched, the revision the delivery routine last qualified green (~/.stado/ci-main.json). When that revision is an ancestor of the installed source revision, the installed revision is built again instead, so an installation never replaces a revision with an older one. Standard error says <product>/<surface>: no source coordinate was named, so it builds <revision>: <why>, and --plan reads the same revision. With neither, the installation refuses: <product> <surface> names no source coordinate and has no published revision to build: its origin could not be fetched (…) and …; name one with --source-commit.
  • What an installation named --source-commit <commit> builds depends on its recipe. A kind: stado-release, kind: kompilator or kind: npm recipe exports <commit> from the canonical checkout's object store and builds that, so the shared checkout may stand on another commit and hold other sessions' uncommitted edits: neither is read, moved or changed, and the receipt's source_revision is <commit>. A commit the checkout does not hold is refused, <checkout> holds no commit <commit> to build; fetch it there (git -C <checkout> fetch origin). Every other recipe (local-build, pipx, a desktop surface) builds the checkout's working tree, so it is refused unless the checkout stands on <commit>: a cli <kind> installation builds its checkout's own working tree, and <checkout> stands on <head>, not <commit> …; the installation advances a clean checkout that is behind to origin/main itself.
  • A source installation runs no gate before its build, so a release manifest's stage key that a gate writes on a release (a test's evidence, such as Jeden's release-tests) is not in the installation's build output. The installation passes over such a key and says so on standard error, stage key <key> is not in the build output <dir>: an installation runs no gate before its build, and a gate writes this key on a release; it is neither signed nor placed. A program the installation places that the build did not produce is refused where it is placed (CLI stage member must be a regular file under bin/ …), and a release still refuses every stage key its output lacks (`the build did not produce stage key inside WISENT_OUTPUT_DIR`).
  • Every dependency is soft. A product's catalogue record lists the products it depends on, and an installation installs, rebuilds or repairs none of them: every product runs without every other one, and integration with another product is used where that product is installed. A dependency installed on this machine is used as it is installed, <product>: uses <dependency>/<surface> as it is installed; an installation installs, rebuilds or repairs no other product; one that an installation in progress above this one places is met by it; one that is not installed is gone without, <product>: installed without <dependency>/<surface>, which is not installed here: <what it does without>, recorded in the installation's dependencies with "without": true. What it does without is the catalogue's alternative, or, for a dependency declared bound_because, the catalogue binds it by design, so what needs it does not work: <reason>, or the catalogue declares no alternative, so what needs it does not work. --without brama,skarbiec names dependencies to go without even where they are installed, printed as <product>: installed without <dependency>/<surface>: <what it does without>; the choice is kept, so update and sync go without them too, and a later --without replaces it. A product a dependant needs is installed by its own stado product install. Which dependencies each product declares, and which of the products its code reaches still have no alternative, is read with stado product catalog --json and stado product dependencies below; this page does not keep a copy of that list.
  • dependencies [PRODUCT] [--json] reads, from the canonical checkout of every repository a product's record names (owner_repository and each surface), every line of tracked product code that reaches another catalogued product, and prints, per product, the repositories it read and each product reached with its standing and how many lines reach it through each channel: program (a spawn call naming one of the other product's declared binaries, or its id when none is declared; a quoted path ending in it, unless the path lies under tests/ (a manifest's test target), is a URL (https://github.com/<owner>/<program>, a page) or is a module specifier after from, import, import( or require( (a source file the language loads); a shell line starting with it), resolver (forwards/<product>… or <product>.local), http (<product>.wisent.com/…, a /docs link excepted), files (.<product>/), environment (<PRODUCT>_…). What the product never runs is not product code: comment lines, Markdown, tests/, a git submodule (another repository's commit, which git tracks as a directory), the forge's automation (any .github/ directory), git's metadata (.gitignore, .gitattributes, .gitmodules), an example to copy (*.example, such as .env.example; the variable it shows is read by code that is scanned on its own), package lockfiles (Cargo.lock, package-lock.json, npm-shrinkwrap.json, yarn.lock, pnpm-lock.yaml, bun.lock, Package.resolved, Gemfile.lock, poetry.lock, uv.lock), the release tooling in a top-level release/ directory (any case) and the release manifest each catalogue installation of that repository declares: how the product is built, signed and published is its installation recipe's, not what the product runs. --json adds every line as {repository, file, line, channel}. A product reached is independent only when every catalogue dependency on it declares an alternative and an alternative_source (<repository>/<path>[:<lines>], the code that implements the alternative) that is a file in the workspace; otherwise it is undeclared, bound (a dependency with no alternative; with because when the catalogue declares bound_because, the reason the product does not run without the other by design — a surface of it and not a separate product, or work that is the other's by definition), unproven (an alternative with no alternative_source) or source-missing. Each is refused on stderr as <product> cannot be shown to run without <other>: <reason>; first reached at <repository>/<file>:<line> — a bound need with a reason as the catalog declares that <product> needs the <other> <surface> by design: <because>, one without as … with no alternative and no bound_because saying why — an absent checkout as <product>: checkout <path> of <repository> is absent, so what it calls is unknown, and the exit status is a failure when any examined product is not independent. The catalogue refuses an alternative_source without an alternative, an empty alternative, alternative_source or bound_because, a bound_because beside an alternative (a need is bound by design or has a way around it, not both), a field outside product, surface, for_surface, alternative, alternative_source, bound_because, and a dependency naming an unknown product.
  • A declared need is written with stado product registry set PRODUCT --add-dependency '{"product":"brama","surface":"service","for_surface":"cli","alternative":"<how PRODUCT runs without it>","alternative_source":"<checkout>/<path>"}', which adds it or replaces the need with the same product, surface and for_surface, and --remove-dependency OTHER removes every declared need of OTHER. A key outside product, surface, for_surface, alternative, alternative_source, bound_because is refused as --add-dependency: <key> is not a field; the fields are product, surface, for_surface, alternative, alternative_source, bound_because, a value that is not one JSON object as --add-dependency takes one JSON object with product, surface, for_surface, alternative, alternative_source, bound_because, removing a product no need names as no dependency on <product> is declared; nothing was changed, and the catalogue's own checks above run on every write, so nothing is written when one refuses. A record whose last need is removed carries no dependencies list. Stado Desktop → Products → Update a catalog product carries the same two fields, beside the rival, roadmap and benchmark fields of the same command.
  • How the submit queue reviews a product's changes is its record's submit, written with stado product registry set PRODUCT --submit '{"criticality":"critical"}' or --submit '{"criticality":"standard","review_paths":["hooks/"]}'; each write replaces the whole declaration. A product with no submit has every change refused at stado change submit naming the declaration it needs. A key outside criticality, review_paths is refused as --submit: <key> is not a field; the fields are criticality, review_paths (the line limits earlier declarations carried, max_changed_lines and low_risk_max_changed_lines, among them: no number of lines decides how a change is reviewed), and the catalog validation every write runs refuses, leaving the file as it was, a review path outside the repository and a criticality other than critical or standard as <id>: submit is not a submit declaration: unknown variant `<word>`, expected `critical` or `standard` . Stado Desktop → Products → Update a catalog product carries the same field. Real journey tests/product/registry_submit.mjs in Stado.
  • sync reinstalls every catalogued surface whose canonical checkout moved past its installed revision; --dry-run decides without fetching. A product whose repository the workspace holds no checkout of is held as no-checkout, unless --clone-missing clones it from its GitHub origin first.
  • The catalog dependencies judges against is a declaration, not a call: when that file lies in a checkout being read (Stado's own catalog/products.yml, which names every product's environment and programs), it is left out of that checkout's product code like a release manifest, so Stado is not reported as reaching every product its catalog describes. A catalog given with --catalog outside every checkout is read only as the catalog. Real journey: tests/dependencies/independence.mjs in Stado.
  • The same judgement refuses a revision before it lands. stado quality check, which every product's pre-push, change hand-off, release submission and delivery gate run, reads only the lines the revision adds against its published origin/main (every line when it has no published base), leaves out the files dependencies leaves out (tests, prose, documentation, the forge's automation, release tooling and manifests, lockfiles, examples, git metadata, comments), and judges each catalog product those lines reach by the catalog the check reads, read as every catalog reader reads it: a tracked catalog at its checkout's origin/main when the committed head is behind it, else at the committed head; every verdict names it (judged against the catalog <checkout>:catalog/products.yml at <commit> (its origin/main, which the checkout's head is behind | its committed head), or <path> as it stands for a catalog no checkout tracks). A product reached undeclared, bound, with no alternative_source, or with one the workspace does not hold refuses the revision, one line per product: <product> cannot be shown to run without <other>: <why>; added at <file>:<line>, …. Reach <other> through \stado capabilities` when a family offers it, or declare what does without it: `stado product registry set --add-dependency '…'`. A line already on origin/mainis never a later push's refusal;dependenciesstill reports it. Two products are not judged, and the check says why on its progress stream,the lines adds are not judged for product independence: : one the catalog does not record ( names no product , so it declares nothing the lines can be judged against), and the catalog's authority (authority.owner, Stado), which installs and manages every product. Real journey: tests/quality/independence.mjs` in Stado.
  • signing inspects, signs, reports and reconciles native code identities; see Native macOS signatures.
  • paths reports executable ownership: collisions, the names in ~/.local/bin that another directory on PATH answers differently, and unowned, every executable in ~/.stado/bin and ~/.local/bin that no installed product's receipt owns (its installed_paths, and the backups of an interrupted installation), each as {name, path, root, resolves_to}. An unowned program is a launcher placed by hand or one a retired surface left behind: no release delivers or updates it, and a product that runs it runs bytes no repository holds. The command reads and changes nothing; a root or entry that cannot be read refuses naming it. Real journey tests/product/paths/unowned.mjs.
  • swift builds canonical main checkouts with an isolated multi-root workspace. swift index writes .bsp/wisent-native.json, whose editor command is stado product editor serve. The editor server reads the canonical package's published settings; it does not compile or hold the janitor lock. editor status reports held workspace ownership, parent and connection age; see Editor connections for lifetime, duplicate-instance refusals and Desktop inspection.
  • kompilator stage is a release manifest's build step: kompilator builds the release's binaries and each is placed at $WISENT_OUTPUT_DIR/<name> (--revision <commit> builds that commit through kompilator's own --revision, so another session's uncommitted edit never enters it). A Rust build or test is kompilator build or kompilator test, a committed revision of it kompilator build --revision <commit>, and a workspace's packages kompilator metadata: see kompilator. An installation needs no flag for this: install and update already build the checkout's committed HEAD, never its uncommitted edits; --source-commit installs a commit origin/main or the canonical checkout's own main carries.
  • cargo --all results reads the receipts an earlier Stado's Cargo runs left in the workspace's checkouts and runs nothing: --json prints workspace, checkouts (each with how many receipts and unread entries it holds), results (each path, state, operation, sources, evidence, output_directory, failedOperation, error), unread and skipped (a directory of the receipt area that is no receipt, such as the installation's own cache). No Stado writes a receipt any more; the command stays because installed programs still ask for the retained ones (Oko's scorecard runs stado product cargo --all --json results), and an installation refuses a Stado that does not answer a command an installed product was seen running. Without --all it is refused as stado.product.arguments.option-missing. Real journey tests/product_cargo/receipts.rs in Stado.
  • documentation cli-pages verifies each catalogued CLI's published command pages; documentation markdown-policy reports repository Markdown outside the root README; documentation star-repositories asks GitHub whether each record's star_repository is public and not archived, and lists every product whose star request a stranger could not follow (<id> names no star_repository, so its GitHub star request links nowhere, GitHub answers <repo> as private, so a stranger opening the star request gets a 404, … as archived, so it cannot be starred, or GitHub's own error), exiting 1 when any does.
  • documentation repository-visibility asks GitHub whether each record's owner_repository has the visibility the record states, since every product site links its source by that visibility alone (landing site source sync). A public record of a private repository is the catalog gives <id> the visibility public, but GitHub reports <repo> private, so every site of <id> links a source visitors get GitHub's 404 for; a private record of a public repository says its sites hide a source anyone can open; an owner_repository that is not OWNER/REPO is refused, and GitHub's own errors are rows. Each row carries product, owner_repository, catalog and github; the command exits 1 on any violation. stado product registry set <id> --visibility <what GitHub reports> brings the record back, then landing site source sync --site <site> --catalog <catalog> on each of its sites.
  • documentation cut-sentences [--root DIR] [--revision REV] lists every heading of a site's src/content/docs that cuts the sentence above it, in any repository: it exports the rule stado-landing publishes (src/cli/docs-corpus/sentences.mjs at its origin/main, never the checkout sessions edit) into the checkout's run area and runs it on the pages under --root (the current directory when omitted), or with --revision on the pages that commit holds; the rule's own reads ignore a GIT_DIR a probe sets. It answers {ok, root, pages, revision, rule: {repository, commit, file}, cuts} and exits 0 however many it lists; a root without src/content/docs answers an empty cuts and said. It refuses, under stado.product.documentation.cut-sentences, a workspace without a stado-landing checkout (rule-checkout-absent), a checkout whose origin/main names no commit (rule-unpublished), a commit without the rule (rule-missing), an export it cannot write (rule-unexported), no node on PATH (node-absent), the rule refusing the pages (rule-refused, with its words), an answer that is not text (rule-not-text), a revision the root's repository does not hold (revision-unknown), and an unreadable current directory with no --root (root-unread). Real journey tests/product_documentation/sentences.rs.
  • A record names star_repository: the public OWNER/REPO the product's in-app GitHub star request links to. A product whose own repository is public names that repository; one whose repository is private names another public one (Wisent's records name wisent-ai/ster), since a private repository answers a stranger with a 404. Nothing an installation executes reads it, so a record without one is still read by every catalog reader with the rest; only that product's star request has nowhere to link, which documentation star-repositories names. One a record declares that is not OWNER/REPO is refused naming <id>.star_repository. registry add and registry set take --star-repository. The star request itself — five successful actions, then one request, dismissable — is the shared onboarding runtime's star-request journey (Echo onboarding), which reads this field. The real qualification is node tests/product/catalog/star_unnamed.mjs in the Stado checkout with STADO_BIN naming the Stado under test.

stado product signatures was removed in 0.22.0. Use stado product signing report PRODUCT --surface SURFACE to read and stado product signing reconcile PRODUCT --surface SURFACE to repair.

Skarbiec's source installation recipes for CLI, service and desktop declare signing_keychain: $HOME/.stado/signing/fleet-identity.keychain-db and signing_role: macos-development-signing; each stado product install skarbiec --surface cli --json keeps that role's identity in the keychain (importing it when missing or renewed) and signs with it, so the installed identity stays the same on every Mac, and reports signing.kept_identity, the observed keychain and each native signature in the installed receipt. A role that cannot be read signs from the identity kept earlier; with none kept the installation refuses naming the role before building. stado product status skarbiec --surface cli --json reads that result; Products in Stado Desktop shows the declaration, the kept role, the signature and the same refusal. An exact --release-version and --source-commit uses already signed bytes instead. See signing.

Reusing an installation build

A source installation reports each product and surface before it prepares it: build for an explicit source request or a dependency that needs replacing, reuse for a dependency whose completed receipt names its installed revision and whose installed files and signatures verify, whatever revision its checkout head has moved to, and release for a verified published archive. What a product needs of a dependency is a verb its contract requires (below), not the dependency's head: rebuilding every dependency to its head would make products that depend on each other a cycle. A changed recipe or destination, unfinished receipt, missing file or changed bytes does not qualify for dependency reuse. Products may depend on each other: a dependency that is an installation already in progress above this one (Tama depending back on the Oko being installed) is met by that installation, printed as <product>: <dependency> is met: the installation in progress that this one is a dependency of places it and recorded with "enclosing": true, not reported as a cycle. Dependencies are checked before the requested product is built, including the dependencies of a reused product. The JSON receipt keeps install_decision (action, reason, source_revision); each checked dependencies entry keeps its own decision. Repeating an explicit source install still runs its recipe.

A kind: kompilator source installation builds with kompilator, never Cargo: it runs kompilator build --revision <commit> --release --bin … with no cargo check before it and places each binary from the path kompilator printed. Stado carries kompilator's sources and compiles kompilator once per source digest with the machine's rustc. kompilator keeps the checkout's compiled units in its build area, ~/.stado/products/builds/<the checkout's path below the home>, and names a workspace package's files by their path inside it, so every later export of the same sources reuses them and a revision that changes one package compiles that package again; it reads every file a package's record names, so a change outside a package's directory that the package reads compiles it again too. No compiler cache is installed or wrapped around the compiler. The output and unit store carry CACHEDIR.TAG, so the janitor reclaims them under disk pressure, never one whose .lock a build holds.

kompilator in the receipt reports the program, its digest, the unit store and each path kompilator answered with its SHA-256. build-steps.json in the retained installation evidence records each successful build command, its exit status, stdout and stderr; a failed command names its retained command evidence. Stado Desktop's Products screen presents the same build/reuse reason for the product and its dependencies and the receipt's kompilator record. These values come from the API's structured receipt, not parsed logs.

An unfinished installation belongs to its receipt

A different --host cannot reuse an installation still marked installing. The refusal names its product id, surface, recorded host, started_at (the receipt's installed_at while installing), full receipt path and the requested host. A missing host is printed as None, not guessed from the request. The CLI classifies this transition as refused, not unknown; Desktop's Products operation displays the same refusal and recovery command.

The command in the refusal acts on the machine that holds the receipt, with its exact HOME, workspace and catalog. It rolls back the incomplete installation to its retained predecessor. For a service with no predecessor it instead names product remove, because rollback cannot establish that service's absence. Both operations change owned files and may change the service on the receipt's recorded host. They retain the normal ownership and backup-integrity checks. They are not run automatically. Do not add --host to this recovery command: that would forward it to another machine, leaving this receipt unfinished. After recovery succeeds, repeat the installation originally requested.

This also covers a receipt left on the wrong machine by an older installation: finishing an installation on its recorded remote host does not clear the local receipt. Do not delete or rewrite the receipt to evade its ownership checks.

Build trees in the checkouts

The janitor cannot read a checkout in ~/Documents unless macOS lets its process read the folder, so a tree built there before the build area existed, or by a build Stado does not run — a Cargo target/, an old install run, a desktop .build — stays through any disk pressure. stado product build-trees reads and removes those trees from the process that runs it, which is the one macOS asks about: run it from a terminal that may read the folder.

stado product build-trees list
stado product build-trees remove --json
stado product --workspace ~/Documents/CodingProjects/Wisent build-trees list --json
  • It reads every git checkout directly in the workspace (--workspace, else WISENT_WORKSPACE, else ~/Documents/CodingProjects/Wisent) and finds, outermost only, each tree that is rebuildable for one of three reasons, its declared_by:
    • cachedir_tag: the directory carries a CACHEDIR.TAG its owner wrote whose first line is Signature: 8a477f597d28d172789f06886806bc55, as Cargo writes into every target;
    • stado_run_area: one of the areas older Stado versions wrote into the checkout, .wisent-output/install, .wisent-output/local-install, .wisent-output/cargo, .wisent-output/cargo-install, .wisent-output/commands, .wisent-output/install-failures, .wisent-output/native, .wisent-output/quality, .build/local-install, .build/package-install, .build/wisent-source, .build/wisent-surface, .build/wisent-native/build and .build/stado-build. A quality check an older Stado is still running holds no lock there and is not kept;
    • desktop_build_tree: in a checkout whose origin is a repository the catalog builds a desktop surface from, a build directory SwiftPM itself names in a .build that sits beside a Package.swift. Such a .build is never one tree, because a checkout keeps its own scratch (review stagings, journey runs, qualification areas) in it beside SwiftPM's output, and swift package clean is not used, because it deletes every member except SwiftPM's dependency clones and state file. Each configuration link at the top of the .build (debug -> arm64-apple-macosx/debug) names, as its first component, the per-triple build directory beside it, and that directory is the tree; so in each member of the .build that carries SwiftPM's workspace-state.json (an editor's index-build). Dependency clones, the state file and every member no link names stay, and the walk goes on through the rest of the .build, where the other two reasons still apply. A .build with no Package.swift beside it (Stado's checkout, whose package is desktop/StadoDesktop, keeps bootstrap builds and test runs in its root one) is no package's and is judged by the other two reasons only.
  • Symbolic links are never followed, .git is never walked, and a directory on another volume or owned by another account is never entered. A directory the walk cannot read is named in errors and the walk goes on.
  • A tree a build is writing is kept as in_use, with the lock in lock: Stado's run marker .in-use, SwiftPM's .lock or Cargo's .cargo-lock, at the tree's top, in a directory below it or one level further, held by a process. A desktop_build_tree is also kept while a process holds the lock at the top of the .build it sits in, where SwiftPM keeps its own.
  • list changes nothing; every tree is reclaimable or in_use. remove deletes every reclaimable tree and marks it removed, or failed with the operating system's reason in errors. A checkout whose origin cannot be read is not taken for a desktop checkout, and the reason is in errors.
  • Each tree reports checkout, path, declared_by, bytes (the lengths of its files), state and lock. The report adds checkouts, bytes, in_use_bytes, removed_bytes, and the volume's free_bytes_before and free_bytes_after. Without --json it prints one line per tree and a summary in GiB.
  • Refusals: a workspace that cannot be read, reading the workspace <dir>: <operating-system error> (a folder macOS withholds from this process answers Operation not permitted); a tree that could not be removed, after the report is printed, build-tree removal incomplete: <n> tree(s) under <workspace> could not be removed; each is named with the operating system's reason in errors; an operation other than list or remove, or none, is a usage error (exit 2).
  • Settings in Stado Desktop has Rebuildable trees in the API host's checkouts: List rebuildable trees runs stado product build-trees list --json through the selected Stado API, shows each tree's state, size and reason and the summary, and Remove rebuildable trees… asks first, saying that of a desktop .build only the build directories SwiftPM's configuration links name are removed, then runs remove with the RUN_MUTATION confirmation. The API's process reaches the checkouts only where macOS lets it read their folder; its refusal and the complete receipt are shown as the command wrote them.

Source: this website