Skip to content

fix: two flaky required checks — the Visual Studio mask and e2e 845 - #806

Merged
Sunrisepeak merged 2 commits into
mcpp-community:mainfrom
ZheFeng7110:fix/flaky-845-and-vs-mask
Oct 13, 2026
Merged

Sunrisepeak merged 2 commits into
mcpp-community:mainfrom
ZheFeng7110:fix/flaky-845-and-vs-mask

Conversation

@ZheFeng7110

Copy link
Copy Markdown
Member

Summary

Two required checks failed intermittently on run 38406939721 (PR #804), on files that run never touched. The analysis is in #804 (comment); this PR makes both deterministic.

e2e 845 (test: commit). 845_play_game_restores_the_terminal.sh sent its arrow keys and Ctrl-C at a fixed t0 + 1.5s, while the build reaches Phase::Building only after its build program has compiled and run (and after the toolchain was resolved) — 1.24s of that budget on the runner that failed. The signal therefore landed in the Programs phase and G1's Building .* · snake \d+ had no row to match, although the rows that reached the terminal did carry the score; phase_status() prints Running for Phase::Programs and Building only for Phase::Building. The wait is now the row itself: the arrows and the Ctrl-C go out when the frame the assertion names first appears, with a 60s cap so a build that never draws it fails rather than hangs. The assertion is unchanged, and it now also proves the kill lands while the prepare action (the 4s sleep) runs.

The Visual Studio mask (fix: commit). The mask renamed ...\Microsoft Visual Studio\*\*\VC once and treated "Access to the path is denied" as final. That refusal is host state, not image state: on windows-2025-vs2026 (image 20260925.250) the same image committed the same rename at 09:57 and 14:32 UTC and was refused at 13:59. Because the postcondition is what keeps an incomplete mask from reading as a green "bare Windows" row, the refusal became a red required check. The rename is now retried, and a directory that stays locked is answered one level deeper — a handle into VC does not block renaming VC\Tools, and VC\Tools\MSVC is where vswhere's resolution, the environment strategy and the well-known-path scan all end. The postcondition is unchanged. The block had three copies (ci-windows.yml, ci-fresh-install.yml, measure-windows-tool-crt.yml), which is how it could drift; it now lives in .github/actions/mask-visual-studio and all three sites name it.

Closes #805

Test plan

  • Documentation-only: not applicable
  • mcpp test (unit/integration): not applicable, no C++ changed
  • e2e 845's two legs, old vs new play.py, against a local mcpp: the script's own heredocs extracted unmodified, project built from them. Unloaded, both pass. With the Programs phase held past 1.5s (a sleep in the build program — the CI runner's shape), the old play.py fails exactly as CI did (AssertionError: G1: no status row carries the score, all frames Running) and the new one passes, its frames reaching Building … · app: app:install 0:00 · snake 0 before the keys go out.
  • bash -n on the script; the extracted play.py compiled and executed by that run
  • bash tests/e2e/232_workflow_syntax.sh (24 workflows, plus its .github/actions scan); .github/tools/check_workflow_assertions.py reports 0 problems; tools/lint-ci-assertions.sh reports the same 5 warnings as before this change; git diff --check clean
  • action.yml, ci-windows.yml, ci-fresh-install.yml and measure-windows-tool-crt.yml all parse as YAML (PyYAML), and the new action reads as runs.using: composite with its one step
  • The PowerShell itself: no pwsh on this host, so it is unrun here — the windows / bare Windows job in this PR's CI is where it runs, and this PR touches .github/**, so the full matrix runs it

`845_play_game_restores_the_terminal.sh` sent its arrow keys and its Ctrl-C at
a fixed 1.5s after the fork, while the build reaches Phase::Building only
after its build program has compiled and run (and after the toolchain was
resolved). On the runner where run 38406939721's macos leg failed, that work
was 1.24s of the 1.5s budget, so the signal landed in the Programs phase and
G1's `Building .* · snake \d+` found no row to match -- while the rows that
did reach the terminal carried the score, and only the phase word differed
(phase_status() prints `Running` for Phase::Programs).

The wait is now the row itself: the arrows and the Ctrl-C go out when the
frame the assertion names first appears, which is also the frame that proves
the prepare action (the 4s sleep) is running. A 60s cap keeps a build that
never draws the row from hanging the leg. The score assertion is unchanged.
The "Mask Visual Studio" step renamed `...\Microsoft Visual Studio\*\*\VC` to
`VC.masked` in one attempt and treated "Access to the path is denied" as final.
That refusal is a state of the host, not of the image: on
windows-2025-vs2026 (image 20260925.250) run 38406939721 was refused at
13:59 UTC where the same image committed the same rename at 09:57 and 14:32.
Since the step's postcondition is what keeps an incomplete mask from reading
as a green "bare Windows" row, the refusal turned into a red required check.

The rename is now retried (5 attempts, 2s apart), and a directory that stays
locked is answered one level deeper: a handle into VC does not block renaming
VC\Tools, and VC\Tools\MSVC is where vswhere's resolution, the environment
strategy and the well-known-path scan all end. The postcondition, the env
variables it clears and the failure it prints are unchanged.

The block had three copies — ci-windows.yml, ci-fresh-install.yml and
measure-windows-tool-crt.yml — which is why they could drift; it now lives in
.github/actions/mask-visual-studio and the three call sites name it.
@Sunrisepeak
Sunrisepeak merged commit b5630e2 into mcpp-community:main Oct 13, 2026
63 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

fix: two flaky required checks — the Visual Studio mask's rename and e2e 845's phase deadline

2 participants