ci: add a manual Linux watch-soak workflow for the cli_watch flake family (#129, #318, #320) - #375
ci: add a manual Linux watch-soak workflow for the cli_watch flake family (#129, #318, #320)#375dean0x wants to merge 1 commit into
Conversation
A `workflow_dispatch`-only ubuntu-latest instrument that runs
`cargo test -p mds-cli --test cli_watch` N times (1-200) across two legs,
`default` and `startup-race-probe`, and tallies a pass/fail RATE rather
than aborting on the first red.
It is NOT a gate: dispatch-only means zero check-runs on any PR head, so
it cannot enter branch protection, is not a required context, is not a
release-surface path, and is invisible to scripts/verify-pr-checks.mjs
and all 212 gate specs.
Pins mirror ci.yml/release.yml byte-for-byte (PF-040); per-leg rust-cache
key because the legs build different feature sets (PF-041); no `${{ }}`
inside any `run:` block -- every value crosses via `env:` (PF-045); the
artifact uses `if-no-files-found: error` against an always-written
summary.txt (PF-016).
CI rerun: pre-existing flaky test, not caused by this PRRun: 34404318888 on head Attempt 1 failed only in This PR touches only Per plan, exactly one Attempt 2 (this rerun) is still RED: No further reruns issued. Fix belongs to PR-B of the C2 workstream (atomic writes for the test harness). |
Summary
This adds an instrument, not a fix. Every
cli_watchfailure in the last 40ci.ymlruns is one of exactly two mechanisms, both onubuntu-latest: a fixed-window debounce split atcrates/mds-cli/tests/cli_watch.rs:1222, and truncate-then-write tearing at--debounce 0atcrates/mds-cli/tests/cli_watch.rs:4370(most recently run 34366009518 onmain@2b91850). macOS FSEvents cannot reproduce this bug class at all, so a green macOS leg proves nothing.The problem with fixing it blind is that
ci.ymlruns the suite once per push with no retries. That produces a bit, not a rate — and "failed at iteration 3" cannot distinguish 1-in-20 from 20-in-20. A before/after control (PF-027 resolution 6) needs both numbers.watch-soak.ymlrunscargo test -p mds-cli --test cli_watchN times (1-200, default 20) onubuntu-latest, across two legs —defaultandstartup-race-probe(the #317 probe widens the publish→arm window to 200 ms) — and tallies pass/fail into a step summary plus an artifact holding only the failing iterations' logs. It deliberately usescargo test, not nextest: nextest's process-per-test model is a different execution environment, and these failures are environment-sensitive.Sequencing: this PR lands first so the baseline rate is measured against unmodified
main. PR-B (the actual fixes) follows and is dispatched on its own branch for the after-rate.What this is NOT
workflow_dispatchis the only trigger.scripts/verify-pr-checks.mjs's tally (neither Tier A nor Tier B).RELEASE_SURFACE. It touches none of the six release-surface paths and does not modifyrelease.yml.npm run test:gates) — confirmed green below.cancel-in-progress: falseis deliberate: a soak is a measurement, and cancelling one halfway leaves a partial tally indistinguishable from a clean run with fewer iterations.github.run_idis in the concurrency group key so two deliberate dispatches on the same ref never evict each other.set -uo pipefailwithout-ein the loop is deliberate: a non-zerocargo testis the DATA this step collects, not an error that should abort it. The step still exits 1 at the end iffail > 0.Pins
All four mirror the live files byte-for-byte; nothing new was introduced.
actions/checkout@v7ci.yml:25actions/*→ tag pin (PF-040)dtolnay/rust-toolchain@4360b52568e2003a75bf9bc1d59f33a8e3fc893c # stableci.yml:26diffSwatinem/rust-cache@v2ci.yml:34(and:62/:80/:177/:239/:280/:332/:410)ci.ymlpins this by tag, not SHA, at all 8 call sites — mirroring the repo's existing convention was chosen over unilaterally introducing a SHA pin in a non-gate workflow. Changing the convention belongs in a dedicated sweep across all 8+ sites, not here.actions/upload-artifact@v7release.yml:450/490/910actions/*→ tag pin (PF-040)Other pitfalls honoured:
rust-cachekeywatch-soak-${{ matrix.label }}: the two legs build different feature sets, so one shared blob would be a contamination channel.${{ }}token appears anywhere inside anyrun:block, not even in a bash comment. Every value crosses the boundary viaenv:. Scan output below.if-no-files-found: error(notignore) against asoak/summary.txtthat is written on every path, so an empty upload means the glob is wrong rather than that the soak was clean.panicked at/... FAILED/test result: FAILEDinline, because the panic's file:line is the diagnosis, not the test's name.Verification
Source-hygiene gate:
Gate spec suite:
PF-045 expression scan — every hit is at a YAML key level; the three
run: |blocks span lines 79-90, 97-103, and 113-183, and no hit falls inside any of them:Pin byte-comparison against
ci.yml:YAML parse (actionlint is not installed on this machine and was deliberately not installed):
Dispatch probe (D-U5 premise check), run once after the push and before this PR:
Confirmed as expected: a
workflow_dispatchworkflow is not dispatchable until its file exists on the default branch. This is precisely why the work is split into two PRs — this one lands the instrument so it becomes dispatchable, and PR-B carries the fixes.Working-tree scope:
(An unrelated local edit to
.gitignorewas present in the tree throughout and is deliberately not part of this PR.)Release process note
No
gh workflow run release.ymldispatch for this PR: it changes no build input (nocrates/**, no bindings, no release-surface path). The user may override at approval.Related Issues
Refs #129 #318 #320