2525 type : string
2626 default : all
2727 test_selection :
28- description : ' PR test selection (smart CI): the artifact the select-tests job uploaded; the test-unix and test-windows legs run only its tests. Empty = every test (dry run, release, and any PR the selection does not narrow)'
28+ description : ' PR test selection (smart CI): the artifact the select-tests job uploaded; the Linux x86_64 test-unix leg runs only its tests, the arm64, macOS and Windows legs run every test . Empty = every test (dry run, release, and any PR the selection does not narrow)'
2929 type : string
3030 default : ' '
3131
@@ -222,7 +222,11 @@ jobs:
222222 ccache-test-${{ matrix.os }}-${{ matrix.cc }}-
223223
224224 - name : Fetch the test selection
225- if : ${{ inputs.test_selection != '' }}
225+ # Only the Linux x86_64 leg narrows (user decision 2026-10-16): the
226+ # coverage that justifies an omission comes from the Linux x86_64
227+ # producer, so arm64, macOS and Windows code under #ifdef has static
228+ # evidence only and those legs run every test.
229+ if : ${{ inputs.test_selection != '' && runner.os == 'Linux' && runner.arch == 'X64' }}
226230 uses : actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
227231 with :
228232 name : ${{ inputs.test_selection }}
@@ -234,7 +238,7 @@ jobs:
234238 CBM_TEST_SHARD : ${{ matrix.shard }}
235239 CBM_TEST_LEG : ${{ matrix.os }}-${{ matrix.cc }}
236240 CBM_SKIP_PERF : ${{ inputs.skip_perf && '1' || '' }}
237- CBM_TEST_SELECTION_DIR : ${{ inputs.test_selection != '' && format('{0}/test-selection', runner.temp) || '' }}
241+ CBM_TEST_SELECTION_DIR : ${{ inputs.test_selection != '' && runner.os == 'Linux' && runner.arch == 'X64' && format('{0}/test-selection', runner.temp) || '' }}
238242 CCACHE_DIR : ${{ github.workspace }}/.ccache
239243 CCACHE_MAXSIZE : 1500M
240244
@@ -256,6 +260,23 @@ jobs:
256260 # shard-completeness job still gates the cross-shard union.
257261 if-no-files-found : warn
258262
263+ # The job log carries a red suite's SUMMARY; these are the complete logs
264+ # behind it, so a failure can be read instead of re-run. The harness
265+ # copies the log of every suite that did not end green, plus results.txt,
266+ # into test-logs/failed/ (scripts/suite-failure-report.sh) — typically a
267+ # few kilobytes for a red suite, not the ~25 MB of a whole run's logs.
268+ # Runs only on a failed job. warn, not error: a job that failed OUTSIDE
269+ # the suite wave (contract step, build, prod-binary guards) has no such
270+ # directory, and a second error would only bury the first.
271+ - name : Failing suite logs
272+ if : failure()
273+ uses : actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
274+ with :
275+ name : suite-logs-${{ matrix.os }}-${{ matrix.cc }}-${{ strategy.job-index }}
276+ path : build/c/test-logs/failed/
277+ retention-days : 7
278+ if-no-files-found : warn
279+
259280 - name : Compiler cache (save)
260281 if : always() && (matrix.shard == null || startsWith(matrix.shard, '1/'))
261282 uses : actions/cache/save@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
@@ -301,6 +322,28 @@ jobs:
301322 shell : bash
302323 run : scripts/ci/test-package-wrappers.sh
303324
325+ # CodeAtlasWeb (graph-ui) leg: the frontend's own vitest suite (2000+
326+ # tests; the render tests run under jsdom), its hardcoded-string and
327+ # promise scans, its portable frozen acceptance checks, and a production
328+ # build with the same npm ci the product build runs. One canonical script
329+ # (scripts/ci/test-ui.sh) is the whole leg, here and locally. No browser
330+ # is downloaded: the browser proofs (graph-ui/tools/smoke-*.mjs) need a
331+ # running server and Playwright browsers, and they stay a local, manually
332+ # dispatched venue documented in graph-ui/README.md rather than a hosted
333+ # gate.
334+ test-ui :
335+ runs-on : ubuntu-latest
336+ timeout-minutes : 20
337+ steps :
338+ - uses : actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
339+
340+ - uses : actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
341+ with :
342+ node-version : " 22"
343+
344+ - name : CodeAtlasWeb leg (install, unit, style, promises, acceptance, build)
345+ run : scripts/ci/test-ui.sh
346+
304347 # ThreadSanitizer data-race gate. The threaded production surfaces (watcher,
305348 # subprocess, httpd, pipeline, mcp, worker pool, diagnostics, the runnable
306349 # daemon-coordination paths) plus allocator concurrency all run under TSan
@@ -353,6 +396,17 @@ jobs:
353396 scripts/test.sh CC=clang-21 CXX=clang++-21 \
354397 SANITIZE="-fsanitize=address,undefined -fno-omit-frame-pointer -fno-optimize-sibling-calls"
355398
399+ # Same step as in test-unix: the complete logs of the suites that did
400+ # not end green, only when the job failed.
401+ - name : Failing suite logs
402+ if : failure()
403+ uses : actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
404+ with :
405+ name : suite-logs-diag
406+ path : build/c/test-logs/failed/
407+ retention-days : 7
408+ if-no-files-found : warn
409+
356410 - name : Compiler cache (save)
357411 if : always()
358412 uses : actions/cache/save@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
@@ -370,16 +424,19 @@ jobs:
370424 # artifact — the local ladder cannot answer that, having no faithful x86-64
371425 # emulation. It has now answered it, and the answer SPLIT the list:
372426 #
373- # - the five deep-recursion suites overflow their thread stacks on x86-64
374- # too, so that limit is NOT architectural;
427+ # - five suites aborted with "stack-overflow ... nested bug" on both
428+ # architectures. That was not deep recursion: it was MSan crashing while
429+ # it reported real uninitialized reads (reporting with the slow unwinder
430+ # exhausted the main thread's stack). scripts/msan.sh now unwinds with
431+ # frame pointers, the
432+ # reports print, the reads are fixed, and the five suites run again;
375433 # - `cli` does not overflow here at all — it fails in the install path for
376434 # an unrelated, still-undiagnosed reason;
377435 # - `incremental` was a shadow-memory RSS artifact, now fixed in the test
378436 # itself rather than skipped, so it stays in the lane.
379437 #
380438 # Per-cause detail lives in scripts/msan.sh, which is the single authoritative
381- # list both venues now share. Zero use-of-uninitialized-value findings were
382- # reported across the lane on either architecture.
439+ # list both venues now share.
383440 test-msan :
384441 if : ${{ inputs.lanes == 'all' || contains(fromJSON(inputs.lanes), 'msan') }}
385442 runs-on : ubuntu-latest
@@ -437,6 +494,17 @@ jobs:
437494 LLVM_PREFIX="$(brew --prefix llvm@22)"
438495 scripts/test.sh CC="$LLVM_PREFIX/bin/clang" CXX="$LLVM_PREFIX/bin/clang++"
439496
497+ # Same step as in test-unix: the complete logs of the suites that did
498+ # not end green, only when the job failed.
499+ - name : Failing suite logs
500+ if : failure()
501+ uses : actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
502+ with :
503+ name : suite-logs-lsan-macos
504+ path : build/c/test-logs/failed/
505+ retention-days : 7
506+ if-no-files-found : warn
507+
440508 - name : Compiler cache (save)
441509 if : always()
442510 uses : actions/cache/save@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
@@ -553,13 +621,6 @@ jobs:
553621 ccache-test-${{ matrix.os }}-${{ matrix.msystem }}-${{ github.ref }}-
554622 ccache-test-${{ matrix.os }}-${{ matrix.msystem }}-
555623
556- - name : Fetch the test selection
557- if : ${{ inputs.test_selection != '' }}
558- uses : actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
559- with :
560- name : ${{ inputs.test_selection }}
561- path : ${{ runner.temp }}/test-selection
562-
563624 - name : Test
564625 shell : msys2 {0}
565626 # AddressSanitizer is unavailable on native ARM64 Windows (LLVM ships no
@@ -587,7 +648,8 @@ jobs:
587648 CBM_TEST_SHARD : ${{ matrix.shard }}
588649 CBM_TEST_LEG : ${{ matrix.os }}-${{ matrix.msystem }}
589650 CBM_SKIP_PERF : ${{ inputs.skip_perf && '1' || '' }}
590- CBM_TEST_SELECTION_DIR : ${{ inputs.test_selection != '' && format('{0}/test-selection', runner.temp) || '' }}
651+ # Windows legs run every test: see the Linux x86_64-only fetch in test-unix.
652+ CBM_TEST_SELECTION_DIR : ' '
591653 CCACHE_DIR : ${{ github.workspace }}/.ccache
592654 CCACHE_MAXSIZE : 1500M
593655
@@ -611,6 +673,17 @@ jobs:
611673 # shard-completeness job still gates the cross-shard union.
612674 if-no-files-found : warn
613675
676+ # Same step as in test-unix: the complete logs of the suites that did
677+ # not end green, only when the job failed.
678+ - name : Failing suite logs
679+ if : failure()
680+ uses : actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
681+ with :
682+ name : suite-logs-${{ matrix.os }}-${{ matrix.msystem }}-${{ strategy.job-index }}
683+ path : build/c/test-logs/failed/
684+ retention-days : 7
685+ if-no-files-found : warn
686+
614687 # Shard 1 (or the unsharded job) saves the compiler cache; every shard
615688 # builds the identical runner, so one cache carries the full object set.
616689 - name : Compiler cache (save)
0 commit comments