docs(infra): runner fails at different stages across runs — nondeterministic
CI and Deploy / test (push) Failing after 1m0s
CI and Deploy / deploy (push) Has been skipped

Two post-reboot runs: the first reproduced the failure identically (smoke 1s
pass, suite 3s fail), the second failed earlier at `dotnet restore` in 0s — a
step that succeeded in 3-4s on every previous run, same commit, same runner.

That rules out stuck state (reboot changed nothing) and rules out a
deterministic sandbox policy such as seccomp/W^X blocking runtime IL emission,
which was the leading remaining hypothesis. Combined with host telemetry
showing no disk/memory/PID pressure, confidence in any specific mechanism drops
to ~25%; confidence that application code is not the cause stays high.

Removes the pure-vs-Moq diagnostic scaffolding (it never executed). No test
skipped or weakened.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
cesnimda
2026-07-18 23:20:19 +02:00
parent c4c0cd4e42
commit 3b59152782
2 changed files with 33 additions and 27 deletions
+4 -24
View File
@@ -62,30 +62,10 @@ jobs:
- name: Test backend (host smoke)
run: dotnet test JobTrackerApi.Tests/JobTrackerApi.Tests.csproj --configuration Release --no-build --filter "FullyQualifiedName~CvBuilderTests.Every_catalog_theme_renders_valid_html"
# Runner-only failure, localised to the AiWorkspace tests by bisecting across steps (the job
# log is not readable via the Gitea API). Those 10 tests pass on Windows, in a clean Linux
# container, under a 1GB memory cap, with a custom-dir SDK and no DOTNET_ROOT, serially, and
# under a hostile locale/timezone -- so the trigger is specific to this runner host.
# See docs/infrastructure/runner-investigation.md. Collection parallelism stays off for determinism, the
# same reason the frontend runs --runInBand. Every test still runs; nothing is filtered.
# Post-reboot the failure is identical (smoke 1s pass, suite 3s fail), so it is deterministic,
# not an OOM/flake -- and host telemetry shows no disk/memory/PID pressure. The passing smoke is
# a PURE test (no EF, no Moq); every class known to fail uses EF InMemory + Moq, whose Castle
# DynamicProxy emits IL at runtime. A container seccomp/W^X policy that blocks runtime code
# generation would kill the host exactly this way. These ordered steps test that: pure classes
# first, then a Moq+EF class. Diagnostic scaffolding; no test is skipped or weakened.
- name: T pure JobPipeline
run: dotnet test JobTrackerApi.Tests/JobTrackerApi.Tests.csproj --configuration Release --no-build --filter "FullyQualifiedName~JobPipelineTests"
- name: T pure EmailStatusClassifier
run: dotnet test JobTrackerApi.Tests/JobTrackerApi.Tests.csproj --configuration Release --no-build --filter "FullyQualifiedName~EmailStatusClassifierTests"
- name: T pure JobCvMatchService
run: dotnet test JobTrackerApi.Tests/JobTrackerApi.Tests.csproj --configuration Release --no-build --filter "FullyQualifiedName~JobCvMatchServiceTests"
- name: T MoqEF CvBuilder
run: dotnet test JobTrackerApi.Tests/JobTrackerApi.Tests.csproj --configuration Release --no-build --filter "FullyQualifiedName~CvBuilderTests"
# This runner fails at a DIFFERENT stage on different runs with no readable diagnostics
# (test host at 3s; on another run `dotnet restore` at 0s). See
# docs/infrastructure/runner-investigation.md. Collection parallelism stays off for
# determinism, the same reason the frontend runs --runInBand. Every test runs; nothing skipped.
- name: Test backend
run: dotnet test JobTrackerApi.Tests/JobTrackerApi.Tests.csproj --configuration Release --no-build -- xUnit.parallelizeTestCollections=false xUnit.maxParallelThreads=1