Source handoff and later batch qualification

How a commit reaches a release after its author pushes it.

Writing code does not start a release. The author commits, pushes to origin/main, then records the Oko work item (a task, task- and 16 hex digits, or a registered defect, defect- and 8 hex digits) and the exact full commit:

stado release changes submit --source /path/to/product --commit FULL_SHA --task <task-id> --session AUTHOR_SESSION --json
stado release changes list --task <task-id> --json
stado release changes list --id CHANGE_ID --id CHANGE_ID --json

--id (repeatable) reads only the named tickets: a caller that follows a few handoffs, such as Oko refreshing its unsettled qualification receipts, does not wait on every ticket the fleet ever queued.

Admission checks the committed product manifest and the remote branch's actual ancestry. A checkout that is not on main (the refusal names the checkout and its branch), a missing work id or session, a commit that is not on origin's main (the refusal names the repository and origin's head), an unreadable remote and releases:false are stated refusals. Repeating the same handoff returns its identity; it neither creates another ticket nor resets a failed qualification.

Admission also runs the product's own lock and formatting gates over the handed-off commit, the check stado quality check runs: each committed Cargo.lock must resolve its manifest, and every fmt gate the platform manifest declares must pass on that commit, exported beside the checkout and not written to it. A commit that fails one is refused before any ticket exists: <commit> is not handed off: stado quality check: gate "fmt" of <product> refuses <commit> of <checkout>: …; stado quality format writes what it reads. The gate's own output (the formatter's diff) goes to stderr, so --json keeps one ticket on stdout. Commit and push the formatting, then hand off that commit; an unformatted commit otherwise reaches the batch build and fails every other session's work in that batch.

The check runs cargo, and each gate's program, from the paths the fleet declares for that program — the table stado host exec probes, for Cargo ~/.cargo/bin/cargo, /Users/Shared/.cargo/bin/cargo, /opt/homebrew/bin/cargo and /usr/local/bin/cargo in that order — and never from the calling shell's PATH, so a shell started outside a login hands off exactly as a login shell does. A program installed at none of them is refused with every path tried: cannot run cargo: cargo is installed at none of its declared paths: /Users/<you>/.cargo/bin/cargo, …. A program the table does not declare runs as the manifest names it.

Before the gates run, the same check confirms every release input the manifest pins (inputs.<name>.uri) is in its store, and stages the inputs a selected gate declares it reads as the release worker does: a gate names them under its own inputs ({"name": "web-quality", "kind": "format", "argv": [...], "inputs": ["deep-analytics-bundle", "onboarding-web"]}), and for each the check fetches the object, checks its bytes against the pinned sha256, extracts or mounts it beside the exported tree as extract declares, and hands the gates the same WISENT_INPUT_<NAME>_DIR variables (the name upper-cased, every other character an underscore). A gate that reads an input, such as stado web quality --git-input, therefore gives the verdict here that it gives on the builder. An input no selected gate declares is confirmed stored and left there (release input <name> is stored at <uri>; no selected gate declares reading it, so it stays in the store): a formatter reads the source alone, so a push never fetches inputs, such as a desktop's whole SwiftPM resolution, that no selected gate reads. A release build stages every input whatever the gates declare. A gate that names an input the manifest does not pin is refused when the manifest is read: <platform>: quality gate "<gate>" reads release input "<name>", which the manifest does not pin under inputs. An input whose object is not there is refused: release input <name> of <product> pins <uri>, which <store> (<backend>) answers is absent: store the object the pin names, or pin one that is stored; one whose bytes differ: release input <name> of <product> at <uri> hashes to <digest>, but the manifest pins <sha256>. This is the shape of a pin written before its archive was stored, or of a Cargo.lock that moved past the archive exported for it; store the exported archive with stado storage put <uri> <file>, or repin the manifest. A store that refused, could not answer now or could not be reached is reported as that state, the same five-state answer stado storage stat gives, and never as absent. A passing check ends with <product> resolves its locks, passes its quality gates and finds its release inputs stored at <commit> of <checkout>.

stado web quality --git-input INPUT=OWNER/REPOSITORY.git answers a private Git dependency from an input that is a Git bundle mounted as a file ("extract": false): both spellings GitHub is reached by are rewritten to the bundle's path, which Git clones and lists as a repository.

stado quality format [--root DIR] writes with the declared gate's own formatter (its argv without --check) and then runs the gate's check. One rustfmt pass is not always enough: a match arm whose body is a literal longer than the line becomes a block only on the next pass. So format writes again while the check refuses, printing stado quality format: gate "fmt" still refuses after that pass; writing again, and ends when the check passes. A pass that leaves the check's report exactly as before is refused with gate "fmt" of <product> still refuses <dir> after left its report unchanged, so another pass would not satisfy it either; the gate reports: followed by that report, because no further pass would change it.

The coordinator later runs stado build submit --source DIR --commit FULL_SHA --version V --json for the accumulated source. Before jobs start, Stado freezes the waiting changes whose commits are ancestors of that build. The binding is stored at runs/build/<build id>/changes.json, beside the build record, and is never rewritten. Unrelated branches and already-qualified changes are not attributed to that build. A change submitted after the freeze whose commit the same build contains — a build is identified by its commit, so building that commit again reaches the same build — is bound in an additional batch runs/build/<build id>/changes-<digest>.json the next time the build is submitted, and from then on reads that build's verdict instead of staying queued. No release or signing grant is needed to read its qualification; release submit --source uses the same build admission. There is no per-commit build poller, and handoff spends no build budget.

Each platform manifest can declare tests, an array of named argv steps. They execute after a successful build and before packaging and publication. Receipts retain the exact source, job, platform, commands and exit statuses; test entries in quality use the test: prefix. A test step belongs in a manifest only once the operator has approved its path with tama tests approve. Under required-behavioral-tests-v1, a passing build without declared tests on any required platform remains awaiting_tests, not qualified. Each required platform must retain exactly one passing receipt entry per declared test, with matching argv and successful exit status, alongside a passing build. Missing, duplicated or mismatched test entries cannot qualify the change. Older workers that did not execute the declared steps cannot provide passing qualification evidence. The policy is returned as qualification_policy; observations cached under a former build-only policy are not reused as behavioral proof.

An awaiting_tests observation names its platform and the first unmet proof obligation: no tests declared, no receipt for a named test, duplicate entries for that test, recorded argv differing from the manifest, or an unsuccessful build/test status or exit code. Command mismatches retain both declared and observed argv; execution failures retain the actual status and optional exit code rather than inventing an exit. The worker's own failed-receipt cause remains authoritative. Read evidence for the complete retained receipts.

Such a platform is named before anything is built, not only after: stado release catalog enroll <checkout>, stado build submit --source DIR --version V, and stado release submit --source DIR --version V refuse a manifest whose required platform declares no tests. The refusal names the product, platform and awaiting_tests consequence, and asks for real product journeys in that platform's tests array in .wisent-release.json. It is returned before Stado publishes the source object, creates a build or claims a version. stado release catalog audit lists every catalogued deficient manifest and exits nonzero. These commands use the same declaration rule; a build or qualification observation recorded before this admission rule remains awaiting_tests, not passed.

Step names must be unique across both quality and tests in one platform. Reusing a name refuses admission before a handoff or build is created; the diagnostic names the platform and the duplicate step. Correct the manifest, commit and push it, then submit the new full commit. Renaming a step does not remove or skip its command.

changes list reports queued, building, awaiting_tests, passed or failed, with the build id in run_id, observed failure and worker evidence. stado build status BUILD_ID --json reads that build. Qualification reads terminal jobs itself; it does not require publication or a prior status query. A failed required platform, including a placement refusal before a job exists, fails its covered changes. Optional platform failures stay in the evidence and do not override required-platform success. A failed read is an error, not an empty queue. Oko's task control refreshes these records. An accepted handoff records source ownership; it does not finish a task while qualification is pending or failed. Passing fleet evidence cannot override an unmet acceptance check, a recorded blocker, a later operator correction or an applicable independent not-done/blocked judgment. A batch failure identifies the covered set; it does not claim to have isolated which commit caused the failure.

Stado Desktop exposes the same operations in Releases: Hand pushed work to a later build and Read waiting changes and batch test verdicts. The read form accepts the optional task identity and exact handoff identities, one per line. Supplying both restricts the result to those handoffs belonging to that task, just as combining CLI --task with repeated --id does. The result retains the same platform-specific qualification failure cause. Oko Desktop exposes source handoff and verdict refresh in each Tasks row.

Source: this website