On this page
Owner-local builds
Owner-local source selection and qualification, and canonical Swift sources and editor indexing, for native builds.
Owner-local source selection and qualification
stado product builds owner-local CLI and Desktop installations from the
existing canonical checkout on main. It matches the catalogue's repository
against Git's GitHub origin, not the directory name. For example, a checkout
of wisent-ai/stado named wisent-compute needs no second checkout or alias.
WISENT_WORKSPACE selects the workspace parent. Missing or ambiguous origins,
symlinks, linked worktrees and non-main build inputs are refused.
stado product status stado --surface cli --json
stado product install stado --surface cli --json
Status includes source_directory and compares the installed revision against
that checkout's origin/main without fetching. An unknown source is not
ready. Every surface records the actual checkout revision, including local
changes as -dirty; Desktop builds create no sibling source export.
Automatic updates hold a dirty, divergent or non-main checkout rather than
changing its contents.
A local Stado release recipe runs each platform quality command before its
build, signing and installation. A refusal names the product, gate and command
with both output streams. Failed qualification leaves the installed executable
and receipt unchanged. A concurrent change to a signing target also refuses
replacement. These owner-local installations do not publish an immutable
release or replace the queued release qualification and delivery contract.
stado product supplies the full base Git commit as WISENT_SOURCE_COMMIT.
The retained .wisent-output/install/<run>/source.json still names local
changes with -dirty, alongside a complete working.patch, without changing
the operator's Git index. Stado checks the base commit against its own checkout
and keeps the measured dirty state in the executable's revision. An immutable
archive does not borrow a parent checkout's Git state; malformed commits and
conflicting explicit revisions still refuse the build.
Each quality and build step is recorded under
~/.stado/products/output/commands/<run>/: command.json names the program,
arguments, directory, source revision, the running Stado's SHA-256, process
id, state, exit status and start and finish times, beside the step's complete
stdout.log and stderr.log. The first recorded command of each Stado
process prunes the directory once: a succeeded command's record goes, and of
a failed command's records only the newest of that same program, arguments
and directory stays, since its error names it; a record its process still
holds is never removed.
Stado's Clippy recipes keep --all-targets --keep-going -- -D warnings:
independent target failures are collected together and still fail the gate.
A failed step names its record as evidence <path> with both output streams;
a failed quality gate adds <product> quality <name> failed; build evidence: <run>, and a failed build <product> build failed; evidence: <run>. These
records expose what failed; they do not by themselves prove that an
installation succeeded.
Each install exports the committed source and builds it under install/<run>/
of the checkout's build area (~/.stado/products/builds/<the checkout's path below the home>), which for a large product is over a gigabyte. Before an
install creates its run, it removes all but the newest earlier runs of that
checkout. The same retention holds for every other per-run directory a Stado
command writes, all of them under the build area or Stado's home and none in
the checkout: a local build's local-install/<run>/ and
local-install-home/<run>/, a cargo install's cargo-install/<run>/ target
directory, stado product cargo's cargo/<invocation>/ and stado product swift's native/<run>/ evidence, and the recorded commands under
~/.stado/products/output/commands/<run>/. A release archive's package and a
builder job keep theirs inside the job's tree, which the janitor sweeps with
the job. A run whose command is still working holds <run>/.in-use
exclusively and is never removed, whatever its age. Every Stado writing
under one parent prunes it, so two of them reach the same superseded record,
build run or shed tree; a run or tree another Stado removed first, wholly or
part way, counts as removed, and any other removal error refuses naming the
run or tree (removing the earlier tree <path>: <cause>). The real journey
concurrent-shed (cargo test --manifest-path stado-rs/product/Cargo.toml --test concurrent-shed) sheds one run from as many threads as the machine
runs at once and requires every one to succeed, then requires a tree the
run's permissions keep to be refused by name. An install that fails
before its build step passed removes its run's source/ export and build
output and its inputs/, keeping the run's logs and receipt; one that fails
after the build passed keeps the run whole (below).
Before a build run is created, the free space under its parent is compared
with what the newest earlier run there holds; with less free, nothing is
written and the command refuses with <free> GiB are free under <parent> and the previous run there, <run>, took <size> GiB; a build now would fill the volume. Free space on this host first (stado space reclaim ), then run it again. A parent with no earlier run has no measure yet and is not refused.
A quality gate is a function of the committed tree and its toolchain, so a
failed gate is recorded under install-failures/ of the checkout's build area (~/.stado/products/builds/<the checkout's path below the home>, never inside the checkout; records an earlier Stado left in .wisent-output/install-failures/ are reclaimed by stado product build-trees remove) for that
product, platform, revision and catalog recipe, and installing the same
revision again is refused before anything is exported, with
<product> <revision> already failed quality <gate> at <time>: <error>; build evidence: <path>. The same revision fails the same gate; commit a repair and install that revision. A failed build step is not recorded: a build can fail
for a reason outside the tree, and the next install tries it again. Neither is
a gate the host could not run: a gate program that did not start, or a gate
script whose shell exited 126 or 127 because it could not find or run a
program it calls, says nothing about the revision. Each install step runs with
~/.stado/bin, ~/.local/bin, ~/.cargo/bin, /opt/homebrew/bin and
/usr/local/bin ahead of the inherited PATH, so a gate script finds the
cargo the step itself would. A record written
before Stado told these apart ("kind": "gate-verdict") no longer refuses;
current records read "kind": "tree-verdict".
A gate stopped by a product it depends on says nothing about the revision
either. Each gate runs with WISENT_DEPENDENCY_REFUSALS naming a file in the
install's evidence (dependency-refusals.jsonl), cleared before the gate
starts; a product notes there, one JSON line {"product", "refused"} each,
every refusal of a product it depends on, such as a gateway that refused or
did not answer, or a vault that served no field. A gate that fails with such
a note passes when the revision's commit attestation stands, read word for
word by Tama (tama attestation check --commit <revision> --repository <checkout> --json, the reading the quality check uses when no model judges a
command tree), and standard error says the gate stood on the attestation and
names the dependency's refusal. Without a standing attestation the install is
refused naming both the dependency's refusal and what the attestation lacks,
and no install-failures/ record is written: the tree was never judged.
A build that passed its quality gates and its build step is a function of
the committed tree, the catalog recipe and the platform, while signing,
staging, placement and activation after it can fail for a reason outside the
tree: a vault that cannot be reached, a keychain without the identity, a unit
that does not start. So an install that fails after its build keeps the run
whole — <run>/.kept beside the evidence, and install-built/<key>.json in
the checkout's build area naming the run, the product, the revision and the
time it was built, keyed like the quality failures by product, platform,
revision and recipe. The next install of the same revision and recipe does
not export, check or compile again: it holds that run in use, resumes at the
step after the build, and says so — <product>: resumed at signing from the build kept at <run> — with resumed_from in the run's prepared.json and
in the installation's receipt and report (prepared.resumed_from, absent when
the install built). The kept run is released when an installation of that
revision completes: settling the installation sheds the run's directories as
for any build, which removes .kept, and the record that then names a shed
tree is removed the next time it is read. Until then the kept run is also a
cache: it carries CACHEDIR.TAG, so stado space reclaim <host> (its
tagged_build_caches stage) and the janitor's build-cache cleaner take it
under disk pressure, after which the next install builds from the export
again. A kept run is never shed or superseded by a later install's fresh run,
and a kept-build record that cannot be read or names no run refuses the
install naming the record.
STADO_BIN=<built stado> STADO_PRODUCT_TEST_PRODUCT=<product with a stado-release service recipe> STADO_PRODUCT_TEST_SOURCE=<its checkout> node tests/product/resume/kept_build.mjs runs that journey on a Mac in an
isolated home under .build/product-resume-kept-build-*: it signs with an
empty keychain it creates, so the first install builds and fails at signing
and must leave one kept, cache-tagged run and its record, and the second
install must say it resumed from that run and export nothing new; the
commands, exit statuses and refusals are kept in report.json. On a host
that is not a Mac it ends blocked, never passed.
An installation of the Stado that runs it does not sign, stage, place or
activate with itself: those steps are code too, and the installed Stado is the
old code, so a repair to any of them would never reach the host it repairs.
Once the build of the running product passed and is kept (or a kept build was
found), the installation lets go of the surface's writer lock and runs the
program that build made — the stage entry bin/<the running program's name>
in the build output — as <built stado> product finish stado --surface <surface> --source-commit <revision>, carrying --signing-keychain and
--signing-identity when they were named. stado product finish finishes a
kept build with the binary that runs it: it resumes from the kept run, and a
revision with no kept build is refused with <product>: no build of <revision> is retained here, so stado product finish has nothing to finish; stado product update <product> --source-commit <revision> builds it. The receipt and the
report of the installation name the program that finished it under
finished_by (binary and verb). A finish that fails leaves the build
kept, so repeating the update resumes at the step that failed:
<built stado> product finish … exited <status>; the build stays retained, so repeating the installation resumes at the step that failed: <its output>.
A Stado installed before this hand-off existed still signs with its own code. To install a repaired Stado over it, run the repair's own build: build the pushed commit in the checkout and let that binary update Stado, so the build, the hand-off and every step after it are the new code:
cd ~/Documents/CodingProjects/Wisent/stado
cargo build --locked --release --manifest-path stado-rs/Cargo.toml --bin stado
stado-rs/target/release/stado product update stado --surface cli \
--source-commit "$(git rev-parse origin/main)" \
--signing-keychain ~/Library/Keychains/login.keychain-db \
--signing-identity '<identity name or SHA-1>'
Canonical Swift sources and editor indexing
stado product swift also builds Swift applications against the existing
canonical dependency checkouts:
stado product swift --package-path ../oko-desktop build --build-tests
stado product swift --package-path ../oko-desktop run oko-desktop-bundle --install
stado product swift --package-path ../oko-desktop --json index --jobs 4
Public dependency tags do not select a second source tree for these owner-local
commands. Stado resolves each repository's canonical main checkout, creates
a multi-root workspace under .build/wisent-native/, and passes that workspace
to SwiftPM. Nested bundle builders use WISENT_SWIFT_WORKSPACE as their
--multiroot-data-file. Missing, ambiguous, non-main and unsupported inputs are
refused before building; Stado never creates or switches a checkout.
test requires an explicit --test-product NAME, so it cannot accidentally run
every dependency's tests. Swift calls multi-root data unsupported; a toolchain
that no longer accepts it fails rather than falling back to dependency clones.
Source resolution disables Git transports and SwiftPM Keychain credential
lookup. The swift build, test or run that follows passes --skip-update,
so it updates no dependency, and leaves Git transports as the caller has them:
a test or program that uses Git itself (a fixture repository over file://, a
remote over https) runs as it would outside Stado.
Each run retains revisions, working patches, actual commands, exit statuses and
output in .wisent-output/native/<run>/. Patches include nonignored untracked
source. A private index preserves staged work, and Git writes the patch bytes
directly. Compiler scratch and temporary files stay in the ignored build tree.
A successful build is not a signing, installation or editor verdict.
index compiles the canonical graph and its test sources without running tests.
It writes the actual compiler settings and index store under
.build/wisent-native/index/, plus .bsp/wisent-native.json and
.sourcekit-lsp/config.json. Editors launch stado product editor serve
to read those settings; the provider never resolves dependencies.
Unreadable or damaged settings report the failed path, cause and regeneration
command. The persistent BSP declaration prevents a missing compiler cache from
silently selecting SwiftPM and creating dependency copies.
--editor-workspace DIR puts those declarations in a separate editor workspace.
Unrelated SourceKit options survive; another build server or symlinked
configuration is refused. Keep the declarations with the workspace. Existing
editor sessions keep their loaded build system; index does not restart them.
Source: this website