Skip to content

Build(deps): bump serde from 1.0.228 to 1.0.229 - #440

Merged
github-actions[bot] merged 1 commit into
mainfrom
dependabot/cargo/serde-1.0.229
Jul 27, 2026
Merged

github-actions[bot] merged 1 commit into
mainfrom
dependabot/cargo/serde-1.0.229

Conversation

@dependabot

@dependabot dependabot Bot commented on behalf of github Jul 27, 2026 •

Copy link
Copy Markdown
Contributor

Bumps serde from 1.0.228 to 1.0.229.

Release notes

Sourced from serde's releases.

v1.0.229

  • Update to syn 3
Commits
  • 7fc3b4c Release 1.0.229
  • 6d6e9a1 Merge pull request #3085 from dtolnay/syn3
  • 6dec3b7 Update to syn 3
  • cfe6692 Resolve mut_mut pedantic clippy lint
  • 1023d07 Update actions/upload-artifact@v6 -> v7
  • dd682c2 Update actions/checkout@v6 -> v7
  • 5f0f18b Update ui test suite to nightly-2026-06-01
  • 63a1498 Regenerate stderr with trybuild normalization fixes
  • fa7da4a Fix unused_features warning
  • 6b1a178 Unpin CI miri toolchain
  • Additional commits viewable in compare view

Dependabot compatibility score

Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting @dependabot rebase.


Dependabot commands and options

You can trigger Dependabot actions by commenting on this PR:

  • @dependabot rebase will rebase this PR
  • @dependabot recreate will recreate this PR, overwriting any edits that have been made to it
  • @dependabot show <dependency name> ignore conditions will show all of the ignore conditions of the specified dependency
  • @dependabot ignore this major version will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself)
  • @dependabot ignore this minor version will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself)
  • @dependabot ignore this dependency will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)

Bumps [serde](https://github.com/serde-rs/serde) from 1.0.228 to 1.0.229.
- [Release notes](https://github.com/serde-rs/serde/releases)
- [Commits](serde-rs/serde@v1.0.228...v1.0.229)

---
updated-dependencies:
- dependency-name: serde
  dependency-version: 1.0.229
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
@dependabot dependabot Bot added cargo Dependabot channel for Rust crate updates. dependencies Dependency updates, applied to every Dependabot pull request. labels Jul 27, 2026
codescene-access[bot]

This comment was marked as outdated.

@github-actions
github-actions Bot enabled auto-merge (squash) July 27, 2026 00:38

@codescene-access codescene-access Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No quality gates enabled for this code.

@github-actions
github-actions Bot merged commit 57c4cc1 into main Jul 27, 2026
22 checks passed
@github-actions
github-actions Bot deleted the dependabot/cargo/serde-1.0.229 branch July 27, 2026 00:56
leynos added a commit that referenced this pull request Sep 4, 2026
The Windows merge gate has been failing since the lanes moved to
`windows-latest`. `install-whitaker` chose its archive extractor by probing
what `tar` is, and on that image the answer is GNU tar, which cannot read the
`.zip` installer asset. The step's `shell: bash` is Git Bash, whose PATH puts
MSYS2's tar ahead of the Windows system directory, so the probe never saw the
bsdtar its own comment assumed. That held only on the Namespace image this
branch migrated away from.

leynos/shared-actions#448 fixes it upstream, choosing the extractor by the
resolved asset extension instead, and is now merged. Repin all 23 references
from c6125f19 to that merge commit. The new revision also carries #440 and
#445, so `setup-rust` now restores the cache service variables that
`mozilla-actions/sccache-action` overwrites; nothing here depends on that yet,
since every caller keeps `use-sccache: 'false'`.

Claude-Session: https://claude.ai/code/session_01QrjNTnTwM7FmWXe5KFPMPY
leynos added a commit that referenced this pull request Sep 4, 2026
…664)

* Optimize Namespace runner caches

Give every Namespace job exactly one cache owner and make its effect
observable, following the estate adoption recipe's Phase 8 guidance.

- Mount one `nscloud-cache-action` volume per job and record its
  `cache-hit` output in the step summary, so a warm run is distinguishable
  from a cold one.
- Prefer explicit durable paths over command-dependent cache modes. The
  `rust` mode mounts Cargo's `target` directory, which duplicates sccache's
  role and breaks `cargo clean`; the `uv` and `bun` modes probe tools that
  are not always present.
- Hand the shared setup, Whitaker, and coverage actions `cache-provider:
  external` so they stop mounting a second owner for the same paths, and
  drop the `actions/cache` steps the volume now covers.
- Run sccache as the compiler-cache measurement layer, zeroing its counters
  before the build and emitting JSON statistics afterwards.
- Bound compilation and nextest workers to the profile's four vCPUs.
- Install Kani's Cargo front-end and verifier bundle from pinned, checksum
  verified prebuilt archives, and keep their Cargo, verifier, and Rustup
  homes on the volume so warm jobs skip the downloads entirely.
- Document the cache lanes and their owners in the developers' guide, and
  add workflow-contract tests that fail if a job loses its cache owner,
  gains a duplicate, or reintroduces a source build.

This squashes the branch's four incremental commits so the change rebases
onto main as one coherent unit.

Claude-Session: https://claude.ai/code/session_01QrjNTnTwM7FmWXe5KFPMPY

* Address CodeRabbit findings on the cache rollout

Split the merge gate, harden the prebuilt-tool installs, and tighten the
contracts that guard them.

- Move `build-test-windows` and the pull-request Windows recipe smoke job
  into `.github/workflows/ci-windows.yml`, invoked as a reusable workflow.
  `ci.yml` had grown to 574 lines, well past the 400-line file limit
  AGENTS.md sets. GitHub does not expose the `env` context to a reusable
  workflow's inputs, so the caller repeats its version pins as literals and
  a contract test holds the two copies equal.
- Install `cargo-orthohelp` after `rust-build-release` in the packaging
  workflow. That action provides the checksum-verified `cargo binstall`, so
  the earlier placement failed on any runner without a preinstalled
  `cargo-binstall`. Nothing before the help-generation step needs the tool.
- Pass `--disable-strategies compile` to every `cargo binstall` call, so a
  missing prebuilt release fails the job instead of silently compiling.
- Restrict the packaging build job and its three callers to `contents:
  read`. The pinned action chain builds and uploads artefacts; it neither
  publishes packages nor exchanges an OIDC token.
- Give Kani's front-end and verifier bundle version-qualified directories on
  the cache volume, so raising the pin in `tools/kani/VERSION` cannot be
  satisfied by a stale binary an earlier run left behind.
- Fold the yamllint and actionlint `actions/cache` steps that arrived with
  main into the Namespace volume, and reuse the cached actionlint only when
  it reports the pinned version.
- Contract tests: reject any `cargo install` form that would compile
  `cargo-orthohelp`, whatever flags precede the crate name; require the Kani
  cache to be mounted before Kani is installed; bind each archive's
  `sha256sum --check` to that archive's own extraction; and require every
  cache summary to carry `if: always()` and report the volume's `cache-hit`.
- Correct the developers' guide punctuation and document the split, the
  version-qualified Kani layout, and the caller-owned Whitaker cache.

Claude-Session: https://claude.ai/code/session_01QrjNTnTwM7FmWXe5KFPMPY

* Clear the empty Whitaker mount before installing

The Linux merge gate failed with `git pull failed: fatal: not a git
repository` while installing Whitaker. Mounting the cache volume creates
`~/.local/share/whitaker` even on a cold run, and `whitaker-installer`
0.2.7 chooses between cloning and pulling on directory existence alone, so
it pulled against a directory that held no repository.

Add a `Prepare Whitaker cache directory` step to both Whitaker jobs that
deletes the data directory when it is not a Git repository, and record the
removal condition in the developers' guide. Correct the Windows cache path
at the same time: the installer clones into
`~/AppData/Roaming/github/whitaker` there, so the previous
`~/.local/share/whitaker` entry cached nothing.

Split the two workflow-contract modules that crossed the 400-line limit,
extracting the actionlint installer constants and the Kani cache contract
into their own modules, and factor the long helpers the Clippy and Pylint
gates flagged.

Claude-Session: https://claude.ai/code/session_01QrjNTnTwM7FmWXe5KFPMPY

* Mark the shared-actions pin as a placeholder

The pinned revision is the head of shared-actions pull request 422, which
is still open. Say so in the developers' guide and state the removal
condition, so the pin is not mistaken for a merged commit.

Claude-Session: https://claude.ai/code/session_01QrjNTnTwM7FmWXe5KFPMPY

* Hold the formatter installers to a prebuilt release

`cargo-binstall`'s default strategy list ends in `compile`, so a missing
prebuilt artefact would be compiled in CI without anyone noticing. Both
formatter jobs already pass `--disable-strategies compile`; require it in
the workflow contract, and reject `cargo install` in the same step.

Start the CI workflow from an empty token and let each job opt in, rather
than inheriting the repository default.

Move the formatter installer contract into its own module: adding the
assertion pushed `ci_lint_test.py` past the 400-line limit. `SETUP_RUST_JOBS`
moves to `workflow_loading.py` so both suites read it from one place.

Claude-Session: https://claude.ai/code/session_01QrjNTnTwM7FmWXe5KFPMPY

* Move Linux CI to Ubicloud and Windows to GitHub-hosted runners

Ubicloud offers Linux runners only, so the estate splits along one line: the
Linux jobs that block a developer run on `ubicloud-standard-2-ubuntu-2404`,
and everything else runs on a GitHub-hosted runner. Windows and macOS have no
Ubicloud image; the metadata, publication, and delayed-comment jobs are
API-bound, and Ubicloud has no single-vCPU shape to make them cheaper. The
Ubuntu 22.04 compatibility lane takes the `-ubuntu-2204` label so the older
glibc it exists to exercise is still the one it runs on.

Every Linux job starts at the two-vCPU shape. `ubicloud-standard-4` is the
ceiling, not the default, and no escalation evidence exists yet, so the worker
bounds now derive from one named lane constant per shape rather than the four
workers the retired Namespace profiles supplied.

Ubicloud destroys the runner VM after each job, so the cache-volume design has
no counterpart: warm state arrives only through archives. Each lane's cache
steps move into a composite action that renders its keys once and owns a
disjoint path set, so restore, save, and the observation summary cannot drift
apart and no path gains a second owner. Restores precede every install; saves
happen only on a push to `main` where that key missed, which is why `ci.yml`
gains a trunk trigger and the Kani job now runs on it. One job writes each key
family and the rest restore only.

Linux keeps sccache on the GitHub Actions backend and exports the two runner
variables itself, because disabling the shared action's sccache also disables
the step that normally publishes them. The local-directory fallback is wired
behind a single repository variable, so exactly one backend is ever active.
Windows keeps its compiler cache in an explicit workspace directory under
`actions/cache`, keyed by OS and architecture so a Linux archive can never
restore onto it.

The Namespace contract modules are replaced by runner-placement, cache
ownership, write-policy, and compiler-cache suites over the same shared pure
validators, with the Hypothesis properties retargeted onto duplicated cache
ownership, oversubscribed worker counts, and save conditions that do not name
a trunk push.

Claude-Session: https://claude.ai/code/session_01QrjNTnTwM7FmWXe5KFPMPY

* Make sccache the only owner of compiler output on every lane

Cargo's build tree is now archived nowhere. sccache holds every build shape
this repository produces, and the shapes coexist in one store because sccache
hashes the flags that separate them, so a `target` archive would be a second
owner of the same bytes and would be invalidated far more often than it
helped. `cache-provider: external` was already keeping the shared actions'
`target` caches switched off; the change is that the coverage build, the
Netsukefile compatibility build, and the packaging build now compile through
sccache too, rather than leaving whole shapes uncached. Sizing rises to 4 GB
because one store now holds two shapes.

One gap stays open and is recorded rather than papered over: the shared
`rust-build-release` action nests an older `setup-rust` revision that caches
`target/${BUILD_PROFILE}` unconditionally and exposes no passthrough, so the
packaging lane still archives a build tree. Closing it needs a shared-actions
change, so the packaging job sets the wrapper and installs no second sccache
beside the nested one.

Ubicloud's transparent cache intercepts `actions/cache` at v6.1.0, so one
action at one pin now serves every lane and the deprecated `ubicloud/cache`
fork is gone. That also retires the rule that the fork must never appear in a
job which can reach a GitHub-hosted label.

sccache keeps its GitHub Actions backend, whose objects land in Ubicloud's own
store. Reaching it needs the two Actions cache variables, and the runner hands
those to JavaScript actions rather than to shell steps, so every lane exports
them through a dedicated action immediately after checkout. Ordering is the
substance of that contract: `--zero-stats`, `--start-server`, and the first
wrapped `rustc` all start the server, and a server started without those
variables stays on local disk for the whole job and reports zero compile
requests, which this repository's pilot has already suffered once.

The export also runs on the Windows lanes, which the estate directive says do
not need it. It costs a second and removes the same silent failure mode, and
Windows moves to the same backend here, so the guard is worth more than the
step it saves.

Claude-Session: https://claude.ai/code/session_01QrjNTnTwM7FmWXe5KFPMPY

* Cite the tracking issue for the packaging lane's build-tree archive

The packaging lane is the one place that still archives a Cargo build tree,
because the shared build action nests a setup-rust revision that caches it
unconditionally. Both the call-site comment and the developers guide said only
that the fix belonged to shared-actions, which left a reader no way to find out
whether it had happened. Name leynos/shared-actions#426 and say what to do when
it lands, so the note retires itself rather than ageing into folklore.

Claude-Session: https://claude.ai/code/session_01QrjNTnTwM7FmWXe5KFPMPY

* Correct the Ubicloud App grant note

The guide claimed the App did not yet cover this repository. It does; the
account-wide installation was already in place. The claim came from reading the
Ubicloud repository listing as a grant inventory, but that listing names only
repositories which have already run a job, so any repository awaiting its first
Ubicloud run is absent from it either way. Say what the prerequisite is, say
why the listing cannot answer it, and point at the symptom that would justify
rechecking the console.

Claude-Session: https://claude.ai/code/session_01QrjNTnTwM7FmWXe5KFPMPY

* Measure coverage once per commit

On a push to `main` the merge gate and the trunk coverage job both ran an
instrumented build of the same commit, and both wrote the ratchet baseline, so
the estate paid twice for one measurement and gave a cached artefact two
owners. The gate now measures coverage only on a pull request, where the
changed-line check is the sole consumer of the report. The trunk job keeps the
upload and becomes the single baseline writer.

The other half of the deduplication rule does not apply here, and the
enumeration that shows why is now in the guide. The gate's suite is a strict
superset of the coverage run rather than a subset of it: the coverage action
invokes nextest with default features and default targets and does not deny
warnings, so folding the gate into it would stop exercising the
`legacy-digests` feature, stop compiling the bench targets, stop denying
warnings in the test tree, and drop doctests entirely. Keeping both on a pull
request buys real coverage; keeping both on the trunk bought nothing.

A contract now holds the gate to the full workspace, every target, every
feature, warnings denied, and the doctest pass, so a later edit cannot narrow
it into a duplicate of the coverage run and make the fold look safe in
retrospect.

Claude-Session: https://claude.ai/code/session_01QrjNTnTwM7FmWXe5KFPMPY

* Execute the Linux suite once, inside the coverage run

The merge gate compiled and ran the workspace twice per pull request: once
uninstrumented through `make test`, then again instrumented to measure it. The
instrumented run now does both, so the lane pays for one compile instead of
two, and the uninstrumented pass is gone.

That is only sound while the instrumented invocation is as broad as the pass it
replaced. The coverage action defaults to nextest with default features and
default targets, which would have quietly retired the `legacy-digests` tests
and stopped compiling the bench targets, so both coverage callers now ask for
every feature and every target. Warnings stay denied through setup-rust's
`rustflags` input rather than a job-level RUSTFLAGS, which the Polonius
contract forbids, and `cargo llvm-cov` appends its instrumentation to whatever
it finds. Doctests are the one thing an instrumented run cannot execute at all,
so a cheap uninstrumented pass follows it on every event, including the trunk.

The trunk coverage job gains the same flags, so the baseline it writes is
measured over the set the ratchet later checks against rather than a narrower
one.

`build-test` keeps its name, so the required context is unchanged; the fold
moved work into the job rather than replacing it. The worker bounds move with
the work: the instrumented run reads cargo's and nextest's own variables, so
the two Make variables the removed step consumed are replaced by the ones that
now take effect, rather than left behind as decoration.

The workflow is written against the `all-features`, `all-targets`, and
`doctests` inputs that leynos/shared-actions#428 adds. Until that merges the
pinned action ignores them with an "Unexpected input(s)" warning, so this is
not exercisable in CI before the repin.

Claude-Session: https://claude.ai/code/session_01QrjNTnTwM7FmWXe5KFPMPY

* Take the doctest pass from the coverage action

Repins `generate-coverage` to the merge of shared-actions#428, which supplies
the `all-features`, `all-targets`, and `doctests` inputs the single-execution
rule was written against. Every other shared action stays on the #422
placeholder, which is still unmerged; the two pins are independent actions with
no shared surface.

The merged action turns out to run the doctests itself, uninstrumented and
under the same feature selection as the instrumented run, for exactly the
reason this repository needed them: so one coverage job can be a lane's only
test execution. The bespoke `make doctest` step is therefore removed rather
than kept beside it. Two statements of the feature selection could drift; one
cannot, and widening the instrumented run in future now widens the doctest run
with it.

`make test` keeps composing both passes for local use, and a contract holds its
flags equal to what CI drives, so a contributor's run still matches the gate.

Checked before repinning rather than after: the merged action still accepts
`cache-provider`, so the caller remains the single cache owner, and its own
cargo cache no longer lists a `target` tree at all.

Claude-Session: https://claude.ai/code/session_01QrjNTnTwM7FmWXe5KFPMPY

* Pin every shared action to one merged revision

shared-actions#422 merged, and its merge commit is also the main tip carrying
#425, #427, and #428, so every reference in this repository now shares one SHA
instead of a placeholder and a second pin. A future bump moves them together.

That revision also closes the packaging lane's build-tree archive.
`rust-build-release` now forwards `cache-provider` to its nested `setup-rust`,
so the reusable packaging workflow passes `external` and no lane in this
repository archives a `target` tree any more. `use-sccache` stays enabled
there deliberately: the nested action is what installs sccache and exports the
backend's credentials, so disabling it would leave `RUSTC_WRAPPER` pointing at
a binary that was never installed.

The contract that held the shared actions to caller-owned caches now covers the
packaging step too, so the gap cannot reopen quietly the way it was opened: by
a nested pin nobody was watching.

Claude-Session: https://claude.ai/code/session_01QrjNTnTwM7FmWXe5KFPMPY

* Install the two tools that publish no usable binaries

The first genuinely cold run found what a warm cache volume had been hiding:
neither mdtablefix nor cargo-orthohelp can be installed from a prebuilt
release, and with the compile strategy correctly disabled both simply failed.
The policy was right; the artefacts were never there to test it against.

mdtablefix publishes perfectly good tarballs, but its binstall metadata sets
`bin-dir = "."`, so resolution fails before it reaches them
(leynos/mdtablefix#458). The Linux lane now takes the tarball directly and
verifies it against a digest pinned in the action and cross-checked against the
release's own sidecar, the way actionlint is already installed. No Windows
binary is published at all, so that lane compiles the tool once per cache
generation into a directory the tool cache owns and the product never shares.

cargo-orthohelp has no binaries on any platform (leynos/ortho-config#479), so
the packaging lane keeps binary-only strategies first and falls back to a
source build on a genuine miss, into a dedicated target directory cached under
a key carrying the tool version. That lane is the one place a save is not
restricted to a push on `main`: no trunk event reaches the release workflow, so
the run that builds the tool has to be the run that publishes it, and the key
is content-addressed by version so concurrent writers agree.

Both exceptions are recorded with the issue that retires them. The contract
counts `cargo install` occurrences rather than pattern-matching them, so a
second source build cannot shelter behind the first, and the packaging contract
admits the fallback only in its exact guarded form.

Claude-Session: https://claude.ai/code/session_01QrjNTnTwM7FmWXe5KFPMPY

* Resolve the orthohelp build directory in the shell, not in env

The macOS packaging job failed the moment it reached the source-build fallback,
for two reasons that only bite outside Linux. GitHub does not expand `~` in an
`env:` value, so the target directory was a literal tilde rather than a path
under the home directory; and `mkdir --parents` is a GNU long option that the
BSD coreutils on macOS reject outright, which is where the exit code 64 came
from. Resolving the path from `$HOME` inside the script and using `-p` fixes
both, and the installer action drops the same long option so it cannot repeat
the failure if a macOS lane is ever added.

Claude-Session: https://claude.ai/code/session_01QrjNTnTwM7FmWXe5KFPMPY

* Size the gate to its workload and stop fighting GitHub's cache limits

Two runner-shaped failures, two different answers.

The merge gate lost its VM 16 minutes into the instrumented build on the
two-vCPU shape: every later step reported a null conclusion and GitHub served
no log, which is a machine disappearing rather than a step failing. The
instrumented run compiles the whole workspace with every feature and every
target, so memory is the plausible cause, and the recipe's escalation path
allows `-4` on exactly that evidence. But the cause was inferred, not measured,
so the job now samples used memory every 15 seconds and prints the peak into
its summary. If the peak comes back well under 6 GB the shape goes back to
`-2`; the note saying so lives beside the label rather than in this message.

The Windows lanes were not short of anything. They were being rate-limited:
the gate recorded 643 failed cache writes out of 643 on sccache's GitHub
Actions backend, and the packaging build 68. Those lanes now keep the compiler
cache in a workspace directory that the cache action owns, under a rolling key
with a prefix restore-key, and set no backend flag at all. The Ubicloud and
macOS lanes keep the backend, where it works, so the credential export narrows
to the lanes that still need it.

The packaging lane also gains the sccache installer it never had. Setting
`RUSTC_WRAPPER` to a binary nobody installed is the whole of "sccache: error:
failed to spawn Command", and a contract now ties the wrapper to an installer
that precedes the build.

Claude-Session: https://claude.ai/code/session_01QrjNTnTwM7FmWXe5KFPMPY

* Escalate the trunk coverage job alongside the gate

The trunk coverage job runs the same instrumented workload that lost the
merge gate's runner, and it is the writer every warm run reads from, so a VM
lost there would leave the whole estate cold rather than just failing one
check. It moves to the same shape on the same evidence instead of waiting to
reproduce the failure on `main`.

The sampler moves into a composite action rather than being pasted into a
second workflow. It was already the kind of thing that drifts: two copies of a
background loop, a PID handed between steps, and a summary format that only
matters if both copies agree. One implementation also gives the contract one
thing to assert.

Both jobs now declare the same lane size and derive their worker bounds from
it, and a contract holds the two equal, so a future resize cannot move one and
leave the other with the failure this addressed. The contracts split along the
same seam: placement says which provider runs a job, shape says how large that
runner is and requires the measurement that lets the escalation be reversed.

Claude-Session: https://claude.ai/code/session_01QrjNTnTwM7FmWXe5KFPMPY

* Pin the shared actions to the Whitaker digest fix

The Windows gate was rejecting a correct Whitaker archive: GNU `sha256sum`
prefixes its output with a backslash when the path it was given contains one,
so the comparison failed on the digest rather than on the download. The shared
action now hashes the archive from standard input, where there is no path to
escape, and all twenty-three references move to that revision together.

Claude-Session: https://claude.ai/code/session_01QrjNTnTwM7FmWXe5KFPMPY

* Probe before installing orthohelp, and log the cache evidence

Two things the first green-ish run exposed.

The orthohelp step had no version probe, alone among the installers here. The
cache restores `~/.cargo/bin`, and `cargo install` refuses to overwrite a
binary that is already present, so a warm run walked into the source-build
branch and failed on a cache hit. Probing first is what every other installer
in this repository already did; this one should have from the start.

The cache observations and the memory peak were written only to the job
summary, which the jobs API does not expose, so the evidence the exit gate
asks for could be read in a browser and nowhere else. They are teed to the log
as well now, which is where anyone reconstructing a run actually looks.

Claude-Session: https://claude.ai/code/session_01QrjNTnTwM7FmWXe5KFPMPY

* Route sccache through Ubicloud's proxy, and stop caching Windows releases

Three findings from the run, and the contracts that should have caught two of
them.

The compiler cache was writing to GitHub the whole time. The export published
`ACTIONS_RESULTS_URL`, which is GitHub's v2 results service, and sccache 0.16
prefers v2 whenever the service switch is set, so every object resolved past
Ubicloud's local proxy: 5310 requests, zero hits, one write error per miss, and
92 objects in GitHub's store rather than Ubicloud's. The export now publishes
the proxy address the runner advertises and clears the v2 switch, so sccache
uses the endpoint the proxy actually serves.

The Windows packaging lane stops using a compiler cache at all. sccache
re-spawns rustc there with the aarch64 target's whole `--extern` and `-L` list
and exceeds the operating system's command-line limit, which nothing in this
repository can shorten. Release builds are infrequent, so that lane runs
uncached rather than unreliably; the Windows merge gate keeps its local
sccache.

Two contracts were quietly blind. The cache-step matcher looked for `/cache/`
and so missed the combined `actions/cache@` form entirely, meaning a second
owner declared that way would have passed every ownership check; and forbidden
paths were compared exactly, so `target` was rejected while `target/debug`
sailed through. A lane that lost its cache steps altogether also satisfied
every contract vacuously. All three now fail rather than pass in silence.

The Windows cache action takes its toolchain as a declared input instead of
reading a variable its caller happened to set, which would have silently
shared one key across toolchains the first time a new caller forgot it.

Claude-Session: https://claude.ai/code/session_01QrjNTnTwM7FmWXe5KFPMPY

* Pin the shared actions to the Windows tar-path fix

The Whitaker digest verified after the last repin, but extraction then failed:
GNU tar in Git Bash reads the colon in a Windows drive letter as a remote host
separator, so a staging directory of `D:\a\_temp/...` became an attempt to
connect to host `D`. The shared action now converts the path with `cygpath`
before unpacking, and all twenty-three references move to that revision
together.

Claude-Session: https://claude.ai/code/session_01QrjNTnTwM7FmWXe5KFPMPY

* Measure disk, discard the instrumented tree, and fix an inverted guard

The Windows packaging lane still compiled through sccache after being told not
to. GitHub's `&&`/`||` yields the last truthy operand and an empty string is
falsy, so `platform == 'windows' && '' || 'sccache'` set the wrapper on exactly
the platform it meant to exempt, and the build then failed looking for an
sccache nobody had installed. Both guards are negated now, with the non-empty
value on the `&&` side, and a contract pins that shape because the mistake is
invisible on reading.

The escalation story turns out to be about disk, not memory. The gate peaked at
3,640 MiB against 16 GB, so memory was never the constraint; a sibling
repository's silent death on the smaller shape was disk exhaustion from a
second target tree. The sampler now records disk used and free alongside
memory, both jobs delete the instrumented tree once the report exists with
`df -h` either side, and the guide says the return to the smaller shape turns
on the disk figures rather than the memory one.

Two contracts close gaps a sibling repository fell into: the compiler-cache
backend flag must accompany the wrapper on every Ubicloud lane, since the
shared setup action sets neither, and the credential export must precede both
the toolchain setup and any step that starts the server. On Ubicloud the runner
re-injects the v2 service variables into every action step, so a server started
inside the setup action binds GitHub's service whatever the export said.

Claude-Session: https://claude.ai/code/session_01QrjNTnTwM7FmWXe5KFPMPY

* Bind cache gates to the keys they publish

Action the outstanding review findings on the Ubicloud runner move, and
close three gaps the contracts did not previously reach.

Wiring:

- Bump every shared-actions reference from 1f303985 to c6125f19. The two
  revisions differ only in setup-rust's rustc-wrapper export, which every
  caller here disables, so this unifies the pin without changing behaviour.
- Give the Kani cache action an explicit `runner-image` input. It read
  NETSUKE_RUNNER_IMAGE under `set -u` while declaring no dependency on it,
  so a caller without the workflow-level variable would have aborted while
  rendering the key rather than failing on a missing input.
- Rename that action's hit input from `cache-hit` to `kani-hit`, so every
  composite save is gated on an input named for the key it publishes and the
  write policy can derive the expected name rather than pattern-match it.
- Fail the sccache credentials action when SCCACHE_GHA_ENABLED is true and
  either credential is empty. That combination is otherwise silent: the
  server falls back to local disk and the job still passes.
- Derive coverage-main's SCCACHE_GHA_ENABLED from the same repository
  variable the merge gate reads, and restore the gate's local store on the
  local-directory arm. Pinned true, the trunk baseline would have compiled
  cold on every run with nothing failing.
- Read coverage-main's cache-key toolchain from rust-toolchain.toml instead
  of restating the nightly date. The Setup Rust literal stays, because the
  Polonius contract checks it against that file; both are now anchored to
  one source and cannot drift apart.
- Carry the pinned rust-build-release revision in the cargo-orthohelp cache
  key. The entry owns ~/.cargo/bin, which also holds the cargo-binstall that
  action provisions, so a bump to the action must turn the entry over.
- Give the packaging job a `timeout-minutes`. It had none, and its runner is
  caller-selected, so release.build-linux could leave a Ubicloud VM running.

Contracts:

- Bind each cache save's hit gate to its own key, for composite and inline
  saves alike. Matching any `-hit` input let a tools save gate on the
  registry result, which is the mis-gate the write policy exists to prevent.
- Derive the inline-save parametrization from KEY_WRITERS.
- Reject a top-level disjunction in a trunk-only save condition.
- Require a listed read-only lane to declare a restore step rather than
  skipping when it has none.
- Assert the statistics reset precedes the first compile and the report
  follows the last, on every lane declaring both.
- Cover the caller-selected Linux package job's timeout.
- Enforce the Kani `runner-image` input at the action and at every caller.
- Scan composite action inputs, not only `run` scripts, for a second Linux
  suite execution.
- Promote the inline job inventories into cache_contract_data, so a lane
  added there cannot go unchecked.

Structure and documentation:

- Split the runner-placement mutations and the sccache compile-step
  recognizer into sibling data modules, keeping every file inside the
  400-line limit.
- Give the shared cache and invariant helpers full NumPy docstrings.
- Document the guarded cargo-orthohelp fallback as the three stages the lane
  actually runs, rather than the single binstall line.

Forwarding NEXTEST_TEST_JOBS to `dev-test` is tracked separately as #671; it
is a local-workflow change that no CI lane exercises.

Claude-Session: https://claude.ai/code/session_01QrjNTnTwM7FmWXe5KFPMPY

* Flatten the second-execution scan into helpers

CodeScene flagged the widened forbidden-command scan on three biomarkers at
once: cyclomatic complexity 9, two blocks of nested conditional logic, and a
nesting depth of 4. Scanning `with` values as well as `run` scripts added a
fourth loop inside the existing three.

Split the traversal into `_step_offenders`, `_is_scannable_job` and
`_workflow_offenders`. The contract is unchanged and the exemption comment
moves to the predicate that applies it.

Claude-Session: https://claude.ai/code/session_01QrjNTnTwM7FmWXe5KFPMPY

* Run the release packaging lanes without a compiler cache

The Windows lane already ran uncached, because sccache re-spawns rustc with
the aarch64 target's whole `--extern` and `-L` list and exceeds the operating
system's command-line limit. The other lanes were only nominally cached: their
server starts inside the nested `setup-rust`, whose `mozilla-actions/
sccache-action` re-exports `ACTIONS_CACHE_SERVICE_V2` and GitHub's own results
address to `GITHUB_ENV` as its last act. On a Ubicloud runner that sends every
write past the cache proxy to GitHub, where it is rate-limited and lands in no
store this repository reads. There is no credentials export in this workflow to
clobber, so the lane was paying sccache's overhead for nothing.

Reproducing the merge gate's export, install and run-step start sequence for a
workflow that runs on tag pushes and the dry run only would not pay back, so
the lane now runs uncached on every platform: no `RUSTC_WRAPPER`, no backend
flag, no installer, and `use-sccache: 'false'` into the nested action.

That also removes the negated `platform != 'windows'` expressions. Getting that
negation backwards cost a release build once; requiring the variables to be
absent outright removes the expression and the whole class of mistake with it.

Correct the credentials action's own explanation while here. It claimed the
runner withholds reserved variables from shell steps. Measurement on
shared-actions runs 33854048777 and 33854213968 shows composite `run` steps do
see them, and that the real failure is the clobber described above. The
developers guide carried the same superseded reading.

Claude-Session: https://claude.ai/code/session_01QrjNTnTwM7FmWXe5KFPMPY

* Reject a cache-save disjunction at any depth

Four findings from the second review round.

`is_trunk_only_save` rejected a top-level `||` but accepted the same
disjunction in parentheses, which means exactly the same thing while
presenting no top-level operator to find. It now rejects `||` anywhere
outside a quoted literal. Rejecting outright, rather than deciding which
disjunctions are safe, is deliberate: every cache-save condition here is a
conjunction of equality tests, so nothing legitimate needs one, and a
predicate that tried to tell safe from unsafe would be a second, subtler
thing to get wrong. The parenthesised form joins the mutation set, and the
named regression test becomes three cases: bare, parenthesised, and nested
inside a conjunction.

The mdtablefix `build-dir` check compared substrings, which was wrong in
both directions: it accepted the relative `target/tool-build` and its
Windows spelling, which are the product's own tree, while rejecting
`build/target-cache`, whose first component merely shares a prefix. It also
accepted `.`, the workspace root, despite the whole point being a dedicated
directory. `is_dedicated_build_dir` now compares path components under both
POSIX and Windows flavours, and eleven parametrized cases pin the ones the
substring test got wrong.

Three workflow sweeps globbed `*.yml` only. GitHub Actions accepts `.yaml`
just as readily, so a workflow using it would have slipped past the cache
ownership, Kani and runner placement contracts unexamined.

Spelling: this repository is en-GB-oxendict, which takes the `-ize` forms.
The docstrings added on this branch used `normalised`, `recognised` and
`unrecognised`.

Claude-Session: https://claude.ai/code/session_01QrjNTnTwM7FmWXe5KFPMPY

* Let a warm-run dispatch reach every cache

The runner migration's exit gate measures warm cache behaviour by dispatching
the gate workflows on `main`. Two lanes were unreachable that way, so two of
the five key families would have had no warm evidence short of merging
something.

`kani-smoke` excluded dispatches on the grounds that a manual run has no
verification work to gate. True, but it is also the only job that touches the
Kani cache, so excluding it left that key measurable only by a real push. It
now runs on every trigger.

`coverage-main.yml` had no dispatch trigger at all. It now has one.

Neither can publish a generation. Every save in this repository gates on
`github.event_name == 'push'` together with `github.ref == 'refs/heads/main'`,
and `coverage-upload` declares no save at all. A new contract holds that
directly rather than by inference: for every workflow that accepts a dispatch,
each composite writer flag and each inline save must name the push event.
Naming the ref alone would not be enough, because `github.ref` is
`refs/heads/main` on a dispatch against the trunk too. Dropping the push
predicate from the Kani action's writer flag makes it fail.

Claude-Session: https://claude.ai/code/session_01QrjNTnTwM7FmWXe5KFPMPY

* Pin shared-actions to the Windows extraction fix

The Windows merge gate has been failing since the lanes moved to
`windows-latest`. `install-whitaker` chose its archive extractor by probing
what `tar` is, and on that image the answer is GNU tar, which cannot read the
`.zip` installer asset. The step's `shell: bash` is Git Bash, whose PATH puts
MSYS2's tar ahead of the Windows system directory, so the probe never saw the
bsdtar its own comment assumed. That held only on the Namespace image this
branch migrated away from.

leynos/shared-actions#448 fixes it upstream, choosing the extractor by the
resolved asset extension instead, and is now merged. Repin all 23 references
from c6125f19 to that merge commit. The new revision also carries #440 and
#445, so `setup-rust` now restores the cache service variables that
`mozilla-actions/sccache-action` overwrites; nothing here depends on that yet,
since every caller keeps `use-sccache: 'false'`.

Claude-Session: https://claude.ai/code/session_01QrjNTnTwM7FmWXe5KFPMPY

* Split the dispatch contract into named steps

CodeScene flagged the new contract as a Complex Method, and fairly: it walked
the composite actions, the inline saves and the dispatchable workflows in one
body, with the assertion at the end.

Each walk becomes a helper that returns labelled conditions, so the test reads
as its three parts: something must accept a dispatch, saves must exist, and
every one of them must name the push event. The contract is unchanged, and
dropping the push predicate from the Kani action still fails it.

Claude-Session: https://claude.ai/code/session_01QrjNTnTwM7FmWXe5KFPMPY

* Give the isolated-build tests a Windows budget

With the Whitaker installer fixed, the Windows gate reaches its `Test` step,
and `harness_compiles_under_a_split_build_dir` terminated there at 300.081s
with the other 2,503 tests passing. The immediately preceding run finished the
same test inside the budget, which is what marginal looks like rather than
broken.

These two tests each spawn a complete isolated Cargo build. The Windows gate
now runs on a four-vCPU GitHub-hosted runner rather than the Namespace profile
it used before, and that shape's compile throughput does not fit the 300s
default. This is the genuine external-resource constraint the configuration's
own policy reserves a targeted override for, so it gets one with the rationale
written down, rather than a retry.

Scoped to Windows. On Ubicloud these finish in a fraction of the budget, and
widening the timeout there would blunt the hang detection the default exists
to provide.

Claude-Session: https://claude.ai/code/session_01QrjNTnTwM7FmWXe5KFPMPY

* Hold the worker bounds and the lane shapes under contract

Three gaps the review round found, none of them in the workflows themselves.

The Makefile forwards two separate worker bounds to `cargo nextest run`, and
nothing held either in place. `NEXTEST_BUILD_JOBS` limits the compile that
precedes the run and `NEXTEST_TEST_JOBS` limits the test processes, so a lane
on a small runner can bound each without oversubscribing the other. Dropping
either silently returns that half to nextest's default of one worker per core,
which is what exhausted a two-vCPU runner in the first place. The Makefile
contract now requires both, and requires the test bound to appear after
`nextest run` rather than merely somewhere in the recipe, where it would read
as configured while bounding nothing.

The developers guide still described one two-vCPU merge gate deriving every
worker bound from a single count. That has not been true since the instrumented
lanes were escalated. It now carries a table of the six lanes with their
runners and actual variables, and separates the two four-vCPU instrumented
lanes from the two-vCPU Kani, Netsukefile and packaging lanes. The escalation's
justification is recorded with the evidence rather than asserted: peak volume
usage of about 80.6 GiB across three runs, against `ubicloud-standard-2`'s
whole 72 GB volume, with memory never above 2,668 MiB of 16 GB.

Two of the guide's universal claims had acquired an exception. The release
packaging lane records no cache observations, because its entry is
content-addressed and reached only by tag pushes and the dry run, so there is
no warm-versus-cold trend to report; and it uses no compiler cache at all, so
it must stay free of `RUSTC_WRAPPER`, `SCCACHE_GHA_ENABLED` and `SCCACHE_DIR`
rather than merely setting them empty. Both exceptions are now stated where
the rule is.

Also rename the dispatch contract's first helper to say what it returns, and
give both helpers the docstrings their new status as a module interface earns.

Claude-Session: https://claude.ai/code/session_01QrjNTnTwM7FmWXe5KFPMPY

* Bind the worker bounds to the command they bound

Four review findings, one of which weakened a contract I had just added.

`target_recipe` returns the recipe's lines joined together, so checking that
`$(NEXTEST_TEST_JOBS)` appears after `nextest run` in that string would accept a
bound sitting in an unrelated later command, reading as configured while
bounding nothing. Both bounds are now asserted against the line that invokes
`nextest run`, which is the thing they have to be arguments to.

Composite actions were discovered with a single-level glob, so one nested a
directory deeper would have bypassed both the save-gating policy and the
source-build scan without any test noticing. Both now recurse.

Two `isinstance` checks become structural pattern matches, per the repository's
Python guidance. `_accepts_a_dispatch` keeps returning False for the scalar and
list spellings of `on`, neither of which can carry the trigger's inputs.

Claude-Session: https://claude.ai/code/session_01QrjNTnTwM7FmWXe5KFPMPY

* Extract the nextest worker-bound assertions

Adding the two bounds took
`behavioural_make_test_composes_the_nextest_and_doctest_passes` to exactly the
70-line threshold CodeScene enforces.

The bounds check is a self-contained question about one command, so it becomes
`ensure_worker_bounds_reach_nextest`, which carries the reasoning that was
previously two comment blocks in the middle of the test: why there are two
variables rather than one, and why the assertion is made against the invoking
line rather than the joined recipe. The test drops to 55 lines and reads as the
sequence of properties it checks.

Claude-Session: https://claude.ai/code/session_01QrjNTnTwM7FmWXe5KFPMPY
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

cargo Dependabot channel for Rust crate updates. dependencies Dependency updates, applied to every Dependabot pull request.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

0 participants