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
catalogreads 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 setandregistry rmchange 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. Aregistry setthat changes only top-level scalar fields of the record (--status,--visibility, a value on one plain line) rewrites just thosekey: valuelines, so every other line of the record stays as its author wrote it, quoting included; any other change renders the record again.registry addgives a new product its first display name;registry setdoes not change names.- Whether a product's sites link its source is its record's
visibilityalone.stado product registry set PRODUCT --visibility public|privatechanges it, andregistry addandregistry setread the owner repository's visibility from GitHub whenever--visibilityor--owner-repositoryis written: a record that would saypublicfor a repository GitHub keeps private (every site would link a 404), orprivatefor 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).--checkwrites nothing and lists every site that would change.documentation repository-visibilitysays whether each record's visibility is what GitHub reports. name list|show|check|set|forget|adoptdecides 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.createprovisions private repositories through Wisent Integrations and keeps each request for--statusand--resume;create --planmakes every check create makes with reads only and prints what it would create, andsurface addprovisions one more repository for an existing product: see Creating a product and adding surfaces.install,update,status,rollbackandremoveact on one product surface (--surface cli|desktop|service,--hostfor a service).--release-versionwith--source-commitinstalls an exact published release of astado-releaserecipe on thecliorservicesurface: 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.installandupdateof acliordesktopsurface given--hostare 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 withexact release coordinates require a stado-release recipe on the cli or service surface.--source-commitalone installs a source build of that exact canonical commit: the checkout exports that commit's tree, never its working tree, onceorigin/mainor the canonical checkout's ownmaincarries it. A commit onmainthat 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'sinstall_decisioncarriessource_on_origin: falseand the build'sprepared.jsonthe same; a commit on neither is refused asneither 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 answerscheckout-missing(no canonical checkout answers for <owner>/<name> in <workspace>; no checkout was created) and leaves it uncreated, asstado quality hooks statusdoes with the rowno-checkout(tests/product/absent_workspace.mjs,STADO_BIN). The real journeytests/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 toorigin/main.- A newer
installorupdateof 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 assuperseding the installation held by pid <pid> …: it is preparing, not placing files, and the wait that follows saystold 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 aswaiting for <lock>: … (it is placing files), and so is one asking for the same installation (the same arguments after the program path, with--json,--waitand--purpose <id>aside, since they change what is printed, whether it waits and why it was asked, not what is installed), printed aswaiting for <lock>: … (the same installation, already under way), because stopping it would only restart the same work.--waitkeeps 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,removewith--surface service) whose--hostis another registry host runs on that host: Stado sends the same arguments, with--hostin 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.--catalogis 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 asstado product … on <host> failed (exit <code>): <its output>. Astado-releaseservice 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 onPATH; only aclisurface places itsbin/members into~/.stado/bin. A sharedbin/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 refusedthe 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 theclisurface installs, so it is placed as that surface is, from the same build; aclisurface installed at another revision still owns the file and the installation is refusedinstallation would change <path>, which <product>/cli still owns; finish, install or remove that surface first. A build that stages no such program is refusedthe 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 macOSSIGKILL (Code Signature Invalid), althoughcodesign --verifypasses) 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/DiagnosticReportsandcodesign --verify --strict --verbose=4 <path>. A command ending on any other signal refuses the installation as a broken build. installandupdatebuild 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>).statusnever creates a checkout;synccreates one the same way only with--clone-missing.- An installation that names no
--source-commit(a dependencyinstallinstalls first,install,updateandsyncwithout one) builds the product's published revision, not the canonical checkout's head, which stands wherever work in that checkout left it: itsorigin/main, fetched withGIT_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--planreads 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. Akind: stado-release,kind: kompilatororkind: npmrecipe 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'ssource_revisionis<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 toorigin/mainitself. - 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 keyinside 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'sdependencieswith"without": true. What it does without is the catalogue'salternative, or, for a dependency declaredbound_because,the catalogue binds it by design, so what needs it does not work: <reason>, orthe catalogue declares no alternative, so what needs it does not work.--without brama,skarbiecnames dependencies to go without even where they are installed, printed as<product>: installed without <dependency>/<surface>: <what it does without>; the choice is kept, soupdateandsyncgo without them too, and a later--withoutreplaces it. A product a dependant needs is installed by its ownstado product install. Which dependencies each product declares, and which of the products its code reaches still have no alternative, is read withstado product catalog --jsonandstado product dependenciesbelow; 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_repositoryand 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 declaredbinaries, or its id when none is declared; a quoted path ending in it, unless the path lies undertests/(a manifest's test target), is a URL (https://github.com/<owner>/<program>, a page) or is a module specifier afterfrom,import,import(orrequire((a source file the language loads); a shell line starting with it),resolver(forwards/<product>…or<product>.local),http(<product>.wisent.com/…, a/docslink 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-levelrelease/directory (any case) and the releasemanifesteach catalogue installation of that repository declares: how the product is built, signed and published is its installation recipe's, not what the product runs.--jsonadds every line as{repository, file, line, channel}. A product reached isindependentonly when every catalogue dependency on it declares analternativeand analternative_source(<repository>/<path>[:<lines>], the code that implements the alternative) that is a file in the workspace; otherwise it isundeclared,bound(a dependency with no alternative; withbecausewhen the catalogue declaresbound_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 noalternative_source) orsource-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 asthe 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 analternative_sourcewithout analternative, an emptyalternative,alternative_sourceorbound_because, abound_becausebeside analternative(a need is bound by design or has a way around it, not both), a field outsideproduct, 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 sameproduct,surfaceandfor_surface, and--remove-dependency OTHERremoves every declared need ofOTHER. A key outsideproduct, surface, for_surface, alternative, alternative_source, bound_becauseis 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 asno 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 nodependencieslist. 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 withstado product registry set PRODUCT --submit '{"criticality":"critical"}'or--submit '{"criticality":"standard","review_paths":["hooks/"]}'; each write replaces the whole declaration. A product with nosubmithas every change refused atstado change submitnaming the declaration it needs. A key outsidecriticality, review_pathsis refused as--submit: <key> is not a field; the fields are criticality, review_paths(the line limits earlier declarations carried,max_changed_linesandlow_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 thancriticalorstandardas<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 journeytests/product/registry_submit.mjsin Stado. syncreinstalls every catalogued surface whose canonical checkout moved past its installed revision;--dry-rundecides without fetching. A product whose repository the workspace holds no checkout of is held asno-checkout, unless--clone-missingclones it from its GitHub origin first.- The catalog
dependenciesjudges against is a declaration, not a call: when that file lies in a checkout being read (Stado's owncatalog/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--catalogoutside every checkout is read only as the catalog. Real journey:tests/dependencies/independence.mjsin 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 publishedorigin/main(every line when it has no published base), leaves out the filesdependenciesleaves 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'sorigin/mainwhen 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 standsfor a catalog no checkout tracks). A product reached undeclared, bound, with noalternative_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 whatdoes without it: `stado product registry set --add-dependency '…'` . A line already onorigin/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 linesadds 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. signinginspects, signs, reports and reconciles native code identities; see Native macOS signatures.pathsreports executable ownership:collisions, the names in~/.local/binthat another directory onPATHanswers differently, andunowned, every executable in~/.stado/binand~/.local/binthat no installed product's receipt owns (itsinstalled_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 journeytests/product/paths/unowned.mjs.swiftbuilds canonicalmaincheckouts with an isolated multi-root workspace.swift indexwrites.bsp/wisent-native.json, whose editor command isstado product editor serve. The editor server reads the canonical package's published settings; it does not compile or hold the janitor lock.editor statusreports held workspace ownership, parent and connection age; see Editor connections for lifetime, duplicate-instance refusals and Desktop inspection.kompilator stageis 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 iskompilator buildorkompilator test, a committed revision of itkompilator build --revision <commit>, and a workspace's packageskompilator metadata: see kompilator. An installation needs no flag for this:installandupdatealready build the checkout's committedHEAD, never its uncommitted edits;--source-commitinstalls a commitorigin/mainor the canonical checkout's ownmaincarries.cargo --all resultsreads the receipts an earlier Stado's Cargo runs left in the workspace's checkouts and runs nothing:--jsonprintsworkspace,checkouts(each with how many receipts and unread entries it holds),results(eachpath,state,operation,sources,evidence,output_directory,failedOperation,error),unreadandskipped(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 runsstado product cargo --all --json results), and an installation refuses a Stado that does not answer a command an installed product was seen running. Without--allit is refused asstado.product.arguments.option-missing. Real journeytests/product_cargo/receipts.rsin Stado.documentation cli-pagesverifies each catalogued CLI's published command pages;documentation markdown-policyreports repository Markdown outside the root README;documentation star-repositoriesasks GitHub whether each record'sstar_repositoryis 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-visibilityasks GitHub whether each record'sowner_repositoryhas the visibility the record states, since every product site links its source by that visibility alone (landing site source sync). Apublicrecord of a private repository isthe 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; aprivaterecord of a public repository says its sites hide a source anyone can open; anowner_repositorythat is notOWNER/REPOis refused, and GitHub's own errors are rows. Each row carriesproduct,owner_repository,catalogandgithub; the command exits 1 on any violation.stado product registry set <id> --visibility <what GitHub reports>brings the record back, thenlanding 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'ssrc/content/docsthat cuts the sentence above it, in any repository: it exports the rule stado-landing publishes (src/cli/docs-corpus/sentences.mjsat itsorigin/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--revisionon the pages that commit holds; the rule's own reads ignore aGIT_DIRa probe sets. It answers{ok, root, pages, revision, rule: {repository, commit, file}, cuts}and exits 0 however many it lists; a root withoutsrc/content/docsanswers an emptycutsandsaid. It refuses, understado.product.documentation.cut-sentences, a workspace without a stado-landing checkout (rule-checkout-absent), a checkout whoseorigin/mainnames no commit (rule-unpublished), a commit without the rule (rule-missing), an export it cannot write (rule-unexported), nonodeonPATH(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 journeytests/product_documentation/sentences.rs.- A record names
star_repository: the publicOWNER/REPOthe 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 namewisent-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, whichdocumentation star-repositoriesnames. One a record declares that is notOWNER/REPOis refused naming<id>.star_repository.registry addandregistry settake--star-repository. The star request itself — five successful actions, then one request, dismissable — is the shared onboarding runtime'sstar-requestjourney (Echo onboarding), which reads this field. The real qualification isnode tests/product/catalog/star_unnamed.mjsin the Stado checkout withSTADO_BINnaming 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, elseWISENT_WORKSPACE, else~/Documents/CodingProjects/Wisent) and finds, outermost only, each tree that is rebuildable for one of three reasons, itsdeclared_by:cachedir_tag: the directory carries aCACHEDIR.TAGits owner wrote whose first line isSignature: 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/buildand.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.buildthat sits beside aPackage.swift. Such a.buildis never one tree, because a checkout keeps its own scratch (review stagings, journey runs, qualification areas) in it beside SwiftPM's output, andswift package cleanis 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.buildthat carries SwiftPM'sworkspace-state.json(an editor'sindex-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.buildwith noPackage.swiftbeside it (Stado's checkout, whose package isdesktop/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,
.gitis never walked, and a directory on another volume or owned by another account is never entered. A directory the walk cannot read is named inerrorsand the walk goes on. - A tree a build is writing is kept as
in_use, with the lock inlock: Stado's run marker.in-use, SwiftPM's.lockor Cargo's.cargo-lock, at the tree's top, in a directory below it or one level further, held by a process. Adesktop_build_treeis also kept while a process holds the lock at the top of the.buildit sits in, where SwiftPM keeps its own. listchanges nothing; every tree isreclaimableorin_use.removedeletes everyreclaimabletree and marks itremoved, orfailedwith the operating system's reason inerrors. A checkout whose origin cannot be read is not taken for a desktop checkout, and the reason is inerrors.- Each tree reports
checkout,path,declared_by,bytes(the lengths of its files),stateandlock. The report addscheckouts,bytes,in_use_bytes,removed_bytes, and the volume'sfree_bytes_beforeandfree_bytes_after. Without--jsonit 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 answersOperation 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 thanlistorremove, 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 --jsonthrough 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.buildonly the build directories SwiftPM's configuration links name are removed, then runsremovewith theRUN_MUTATIONconfirmation. 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