Install parity contracts

This page holds the two install parity contracts, the toolchain and the platforms tables each gate holds in lockstep.

The install-toolchain parity contract

Two <!-- toolchain:begin --> tables hold the probe roster, the adopter’s rows in docs/requirements.md and the contributor’s in CONTRIBUTING.md: check-install-toolchain asserts whole-element parity between their union and native/src/toolfloor.rs’s PROBE_SET array both directions. It also holds placement: a contributors row sits only in CONTRIBUTING.md, every other row only on the requirements page. Its positional form takes the two pages, then a roster override. Each row renders its roster element verbatim, so all four axes are held: name, version floor, implementation token and audience:

| Tool | Version | Needed | Why |
|---|---|---|---|
| `<name>` | <version> | <audience> | <purpose> |

A row is a block line opening with | whose first cell is one backticked name. No cell carries |. The Version cell is ≥ <floor>, the implementation token, both joined by , , or any for neither. The Needed cell renders the audience: required for none; contributors for contributor; optional: if you register a gate that runs it for registered; a kit list as optional: if your profile includes <kits>, the names joined by , and the last two by the word or. A derived audience is resolved over the kit roots and compared as that kit list; where the kit roots were read and none ships a surface, the list is empty and the cell is the predicate’s two anchor arms, optional: if your docs mark a fence runnable or you register a shell gate, text the crate holds once beside the predicate. The Why cell is hand prose and unread, so a roster edit reds the tables without an emitter handshake. The gate reads the crate’s own constant by default — and, where an element’s audience is derived (context-kit/SPEC.md §bin/env-probe), resolves it by walking the kit roots before comparing. That half is why this contract is not satisfiable by editing both sides: one of them is a measurement of the tree, so a kit shipping an undeclared surface reds the page rather than joining it silently. A derived element whose walk read no kit root beside the floor-holder exits 2 rather than comparing the page against a derivation that did not run. A hermetic fixture may steer it onto a roster file instead, and that file is parsed and never sourced, because a fixture path is untrusted input and the reader that lints the array must not be made to execute the file it reads.

The install-platforms parity contract

docs/requirements.md carries the <!-- platforms:begin --> marker block, and this section is where a reader looks for what holds it, as the section above is for toolchain. It is hand-authored like its neighbour, and a table like it, one row per supported platform:

| System | Minimum | Binary | Status |
|---|---|---|---|
| <gloss> | <floor> | `<triple>` | joined |
| <gloss> | <floor> | `<triple>` | held: <precondition> |

A row is a block line opening with | that carries a backticked run, so the header and delimiter rows are not rows. No cell carries |. The triple is the row’s first backticked run, the Minimum its second cell and the join state its last cell, trimmed. There are two states and no third. joined means the triple is a live line in native/targets.list and a line of the roster at the pinned release’s tag, at that tag’s Minimum. held: <precondition> means the platform is documented as supported but is not in the roster, and it carries the named run that would join it. A hold with no stated cause is how a pile grows silently, so the precondition is mandatory rather than conventional. The Minimum cell is the platform’s OS floor: glibc <X.Y> for a *-unknown-linux-gnu triple, Linux <X.Y> for a *-unknown-linux-musl triple, the kernel floor, macOS <X[.Y]> for a *-apple-darwin one. An empty cell reds, since a support claim with no floor is half a claim. A platform the page does not state as supported is absent rather than held: a held entry is still a support claim, so absence is the state for a platform nothing has been promised about. Three readers: .github/workflows/gates.yml’s native-artifacts roster step, which derives the producer’s build matrix from every declared triple regardless of state, since a platform that is never built is a platform that can never stop being held; check-install-platforms, which holds the table and the roster in lockstep in both directions and is what mechanizes gate-sdk/SPEC.md §Consumer payload’s first bound; and the shared build body, scripts/ci-build-artifact.sh, which reads its target’s Minimum cell and refuses an artifact whose measured floor differs from it or, for a musl triple, refuses an artifact that is not statically linked. That gate has landed, a born-native repo-root member at precommit tier, so the lockstep is machine-held rather than discipline. A joined declaration with no roster line reds; so does a roster line no bullet declares joined; a hold reds on an empty precondition or on a roster line it should not have. Arm G holds joined to the pinned release, the tag docs/install.sh’s pin names: each joined triple must be a line of the roster at that tag. The roster is read at the gate’s own roster path, never through GATE_SDK_NATIVE_TARGETS_FILE, whose value a smoke narrows. Its Minimum must equal the tagged page’s for that triple. The Minimum half is dormant while the tagged block does not parse as this table, and the whole arm is dormant where the tag does not resolve; the clean line says which. A finding is admitted as pending while this iteration will release and reds while its disposition withholds one, on the terms and through the reader of installer/SPEC.md §The front door’s verbs. Its positional form takes arm G’s four inputs as files, so the fixture pair holds still as the tags move. The table is also bound to the two host detectors: target_of_host with its fallback map fallback_of_target in installer/bin/checkwright.sh and Get-HostTarget with Get-FallbackTarget in installer/bin/checkwright.ps1, whose extraction shapes installer/SPEC.md §The gate binary pins. Each detector’s emitted triple set is held equal to the block’s declared set. It is equality rather than containment because each direction closes a distinct failure. A triple a detector emits that the block does not declare is the attested case: detected by the installer while on no roster, with nothing going red. A triple the block declares that no detector emits is a support claim the installer can never honour, and it has no live instance, which is exactly why it is asserted rather than assumed. Bash readers and a compiled one of one grammar is the shape scripts/gates.list already ships, not a duplication to collapse. The state word now decides a binding posture on both sides of the artifact hand-off, and that is a wider consequence than “which platforms get built”. The roster step publishes what it derives twice, as the producer matrix and as an object keyed by triple. That second output is read at job level by the Intel install-smoke-sh-macos-intel leg, whose runs-on and continue-on-error both resolve from it. So flipping a row from held: to joined makes that triple’s producer leg binding and its consumer leg binding, in one edit, with no workflow change and nobody remembering to make it so. Read the state word as a support commitment taking effect rather than as documentation: the legs are green at the moment of a flip, so nothing reds at the landing. The cost arrives the first time that platform breaks. The mechanism has worked instances now, and the second is what makes it a mechanism rather than one file’s arrangement. x86_64-apple-darwin’s flip to joined made that triple’s producer leg and its Intel install-smoke-sh-macos-intel consumer leg binding on master at the landing commit, and the diff that did it edits .github/workflows/gates.yml nowhere; a roster line and a state word are the whole of it. x86_64-pc-windows-msvc then took the same route, and install-smoke-pwsh-windows reads its matrix entry’s runner and continue-on-error off the roster step’s Windows output, derived from the same declaration, so no platform leg’s posture is hard-coded any more.