Skip to content

Commit 656d584

Browse files
authored
Fix the UiRenderSmoke golden probe and run the full CI matrix on every PR (#92)
The 'P red' probe of UiRenderSmoke.GoldenUiScene read pixel (20, 20), which the smoke scene's button (16, 16, 20 x 14, green uv, declared after the panel, drawn on top by the monotone-z contract) covers: the correct color there is the button's green, so the probe failed on all four Linux lanes of the merge run (run 38163032436) while the full-frame CPU-reference comparison reported zero mismatches (the reference inherits the quad UVs and cannot catch the intent error). The probe now reads (12, 12), inside the panel and outside the button. The PR lane (.github/workflows/ci-pull.yml) now runs the same eleven jobs as the merge lane, unconditionally: the four Linux trees, Windows, both macOS lanes, and the four tooling jobs. The ci:windows / ci:macos label selector is removed from both workflows: no job is gated on a label and no test is ever skipped. The PR-lane sanitizer jobs upload no artifacts (the pull_request token is read-only, including fork PRs); their tee'd ctest output keeps every sanitizer report in the job log, and the merge lane still archives artifacts. AGENTS.md section 5 gains the local Linux test gate: before any push to GitHub, all four Linux CI trees (g++, clang++, ASan+UBSan, TSan) must be built and their full unfiltered ctest suites must pass locally, in parallel when the machine allows.
1 parent a8b1c98 commit 656d584

4 files changed

Lines changed: 236 additions & 83 deletions

File tree

‎.github/workflows/ci-pull.yml‎

Lines changed: 187 additions & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -1,44 +1,39 @@
11
# ============================================================================
2-
# Laige CI — P0 platform matrix on pull request (M0-CI-01)
2+
# Laige CI — full P0 platform matrix on pull request (M0-CI-01,
3+
# re-baselined 2026-10-11)
34
#
45
# Roadmap: roadmap/M0-foundations.md, step M0-CI-01
56
# PRD refs: §6 (P0 platforms, AC-6.1), §14 (cadence), §8.1 (build budget)
67
#
7-
# Cadence: the PR lane covers only the P0 OSes that cannot be verified
8-
# locally — Windows and macOS. Linux is verified on the developer
9-
# machine with the canonical trees from
10-
# docs/getting-started/building.md (including the LAIGE_ASAN=ON and
11-
# LAIGE_TSAN=ON instrumented trees), so this workflow carries no Linux
12-
# jobs. Merges to master run the FULL matrix — every P0 OS/variant job,
13-
# both sanitizer lanes, and all tooling jobs — via ci.yml, which is
14-
# the "all per merge" half of the PRD §14 cadence.
8+
# Cadence (PRD §14, as amended 2026-10-11 — "test everything, all the
9+
# time"): this workflow runs on EVERY pull request, and ALL eleven jobs
10+
# run — unconditionally:
1511
#
16-
# Label selector (apply at most one per PR; if both are present
17-
# ci:macos wins, so exactly one P0 OS always runs):
12+
# * the five P0 OS jobs (linux-gcc, linux-clang, windows-msvc,
13+
# macos-arm64, macos-intel);
14+
# * the two sanitizer lanes (linux-asan, linux-tsan; M0-CI-02);
15+
# * the four platform-independent tooling jobs (include-lint,
16+
# determinism-lint, api-manifest, detcheck).
1817
#
19-
# ci:macos → macos-arm64 + macos-intel jobs
20-
# ci:windows → windows-msvc job
21-
# (no label) → no P0 OS build; the platform-independent tooling jobs
22-
# (include-lint, determinism-lint, api-manifest,
23-
# detcheck) still run on every PR.
24-
#
25-
# NOTE: the labels must be created once in the repository settings
26-
# (GitHub does not create labels from workflows). A PR with no ci:*
27-
# label simply runs the tooling jobs. The former ci:linux label is no
28-
# longer a selector (Linux is verified locally); a PR carrying it gets
29-
# the no-label treatment.
18+
# The former ci:windows / ci:macos label selector is REMOVED: no job is
19+
# gated on a label and no test is ever skipped. Linux is no longer
20+
# "verified locally only" — it runs in the PR lane exactly as in the
21+
# merge lane (ci.yml), so a PR that breaks a Linux tree fails in the
22+
# PR lane before it can reach master. Local verification of the Linux
23+
# trees before pushing remains a developer obligation (AGENTS.md,
24+
# section 5: the local Linux test gate).
3025
#
3126
# Jobs, runner images, steps, and the build-budget timeouts are the
3227
# same as in ci.yml — including the macOS exception (the two macOS
3328
# jobs carry timeout-minutes: 15, the shared-runners tail latency),
3429
# documented in ci.yml's header (the full matrix documentation and
35-
# the Windows multi-config note).
36-
#
37-
# Sanitizer lanes (M0-CI-02): the linux-asan (LAIGE_ASAN=ON) and
38-
# linux-tsan (LAIGE_TSAN=ON) lanes no longer run on PRs (no Linux jobs
39-
# in the PR lane); they run on every merge via ci.yml. See the ci.yml
40-
# header for the lane semantics (fatal sanitizer reports, report
41-
# archiving, toolchain choice).
30+
# the Windows multi-config note). The one difference: the sanitizer
31+
# jobs upload no artifacts here. The pull_request GITHUB_TOKEN is
32+
# read-only (contents:read for same-repo PRs, read-only for fork PRs —
33+
# Actions cannot grant actions:write on this event), so the report
34+
# files are not archived; the tee'd ctest output keeps every sanitizer
35+
# report in the job log, which is enough to diagnose a red PR. The
36+
# merge lane (ci.yml) still archives them as artifacts.
4237
#
4338
# Fuzz lane (PRD §14 "every commit (bounded)"; M0-TEST-01): no separate
4439
# fuzz job — the `fuzz_json_parse` ctest entry (1000 deterministic runs
@@ -47,11 +42,10 @@
4742
# conventions: docs/testing.md.
4843
#
4944
# Include-graph lint (M0-CI-03; NFR-8.11, NFR-8.13): the `include-lint`
50-
# job runs on EVERY pull request, independent of the ci:* label selector
51-
# — it is a platform-independent repository check (Python 3 stdlib only),
52-
# not a P0 OS build, so it does not interact with the "one P0 OS per PR"
53-
# cadence. It enforces the PRD §10.1 include rules over src/** (laige-core
54-
# depends on nothing internal; arrows only downward in the module stack;
45+
# job runs on EVERY pull request — it is a platform-independent
46+
# repository check (Python 3 stdlib only), not a P0 OS build. It
47+
# enforces the PRD §10.1 include rules over src/** (laige-core depends
48+
# on nothing internal; arrows only downward in the module stack;
5549
# vendored deps only included from their deps.lock `owner`) and reports
5650
# the vendored-dependency count, which must stay ≤ 10 (PRD §11).
5751
#
@@ -73,11 +67,138 @@ concurrency:
7367
cancel-in-progress: true
7468

7569
jobs:
70+
linux-gcc:
71+
name: Linux x64 (g++)
72+
runs-on: ubuntu-24.04
73+
timeout-minutes: 10
74+
steps:
75+
- uses: actions/checkout@v4
76+
- name: Install GLFW X11 headers (laige-render)
77+
# GLFW's X11 backend (GLFW_BUILD_WAYLAND=OFF in src/laige-render/
78+
# CMakeLists.txt) needs only the X11 *headers* at configure time
79+
# (GLFW 3.5 dlopens the X11/GL libraries at runtime); the
80+
# ubuntu-24.04 runner image ships none of them. The libegl/libgl
81+
# packages are the RUNTIME GL stack for the headless GL smoke
82+
# (Mesa surfaceless EGL + llvmpipe software GL; the engine and
83+
# GLAD load them by dlopen at runtime) (M2-GL-01).
84+
run: |
85+
sudo apt-get update -qq
86+
sudo apt-get install -y -qq \
87+
libx11-dev libxcursor-dev libxrandr-dev \
88+
libxinerama-dev libxi-dev libxext-dev \
89+
libegl1 libgl1 libegl-mesa0 libglx-mesa0 libgl1-mesa-dri
90+
- name: Configure
91+
run: cmake -S . -B build -DCMAKE_BUILD_TYPE=Debug -DCMAKE_CXX_COMPILER=g++
92+
- name: Build
93+
run: cmake --build build -j
94+
- name: Test (unit)
95+
run: ctest --test-dir build --output-on-failure
96+
97+
linux-clang:
98+
name: Linux x64 (clang++)
99+
runs-on: ubuntu-24.04
100+
timeout-minutes: 10
101+
steps:
102+
- uses: actions/checkout@v4
103+
- name: Install GLFW X11 headers (laige-render)
104+
# GLFW's X11 backend (GLFW_BUILD_WAYLAND=OFF in src/laige-render/
105+
# CMakeLists.txt) needs only the X11 *headers* at configure time
106+
# (GLFW 3.5 dlopens the X11/GL libraries at runtime); the
107+
# ubuntu-24.04 runner image ships none of them. The libegl/libgl
108+
# packages are the RUNTIME GL stack for the headless GL smoke
109+
# (Mesa surfaceless EGL + llvmpipe software GL; the engine and
110+
# GLAD load them by dlopen at runtime) (M2-GL-01).
111+
run: |
112+
sudo apt-get update -qq
113+
sudo apt-get install -y -qq \
114+
libx11-dev libxcursor-dev libxrandr-dev \
115+
libxinerama-dev libxi-dev libxext-dev \
116+
libegl1 libgl1 libegl-mesa0 libglx-mesa0 libgl1-mesa-dri
117+
- name: Configure
118+
run: cmake -S . -B build -DCMAKE_BUILD_TYPE=Debug -DCMAKE_CXX_COMPILER=clang++
119+
- name: Build
120+
run: cmake --build build -j
121+
- name: Test (unit)
122+
run: ctest --test-dir build --output-on-failure
123+
124+
linux-asan:
125+
name: Linux x64 ASan+UBSan (clang++)
126+
runs-on: ubuntu-24.04
127+
timeout-minutes: 10
128+
# No artifact step here: the pull_request token is read-only (see
129+
# the header). The tee keeps every report in the job log.
130+
steps:
131+
- uses: actions/checkout@v4
132+
- name: Install GLFW X11 headers (laige-render)
133+
# GLFW's X11 backend (GLFW_BUILD_WAYLAND=OFF in src/laige-render/
134+
# CMakeLists.txt) needs only the X11 *headers* at configure time
135+
# (GLFW 3.5 dlopens the X11/GL libraries at runtime); the
136+
# ubuntu-24.04 runner image ships none of them. The libegl/libgl
137+
# packages are the RUNTIME GL stack for the headless GL smoke
138+
# (Mesa surfaceless EGL + llvmpipe software GL; the engine and
139+
# GLAD load them by dlopen at runtime) (M2-GL-01).
140+
run: |
141+
sudo apt-get update -qq
142+
sudo apt-get install -y -qq \
143+
libx11-dev libxcursor-dev libxrandr-dev \
144+
libxinerama-dev libxi-dev libxext-dev \
145+
libegl1 libgl1 libegl-mesa0 libglx-mesa0 libgl1-mesa-dri
146+
- name: Configure (ASan+UBSan)
147+
run: cmake -S . -B build-asan -DCMAKE_BUILD_TYPE=Debug -DCMAKE_CXX_COMPILER=clang++ -DLAIGE_ASAN=ON
148+
- name: Build (ASan+UBSan)
149+
run: cmake --build build-asan -j
150+
- name: Test (unit, ASan+UBSan)
151+
# Every sanitizer report is fatal to the test process: ASan via
152+
# abort_on_error=1:halt_on_error=1 here, UBSan via
153+
# -fno-sanitize-recover=all (wired by CMake). detect_leaks=1 (the
154+
# Linux default) is stated explicitly.
155+
env:
156+
ASAN_OPTIONS: "abort_on_error=1:halt_on_error=1:detect_leaks=1"
157+
run: |
158+
set -o pipefail
159+
ctest --test-dir build-asan --output-on-failure 2>&1 | tee asan-ctest-output.txt
160+
161+
linux-tsan:
162+
name: Linux x64 TSan (clang++)
163+
# 30 min (not 10): TSan is the slowest lane by design (~5-15x
164+
# instrumentation overhead), and the suite has grown past the old
165+
# margin (the documented rationale, with the evidence runs, is in
166+
# the linux-tsan job of ci.yml).
167+
# No artifact step here: the pull_request token is read-only (see
168+
# the header). The tee keeps the race report in the job log.
169+
runs-on: ubuntu-24.04
170+
timeout-minutes: 30
171+
steps:
172+
- uses: actions/checkout@v4
173+
- name: Install GLFW X11 headers (laige-render)
174+
# GLFW's X11 backend (GLFW_BUILD_WAYLAND=OFF in src/laige-render/
175+
# CMakeLists.txt) needs only the X11 *headers* at configure time
176+
# (GLFW 3.5 dlopens the X11/GL libraries at runtime); the
177+
# ubuntu-24.04 runner image ships none of them. The libegl/libgl
178+
# packages are the RUNTIME GL stack for the headless GL smoke
179+
# (Mesa surfaceless EGL + llvmpipe software GL; the engine and
180+
# GLAD load them by dlopen at runtime) (M2-GL-01).
181+
run: |
182+
sudo apt-get update -qq
183+
sudo apt-get install -y -qq \
184+
libx11-dev libxcursor-dev libxrandr-dev \
185+
libxinerama-dev libxi-dev libxext-dev \
186+
libegl1 libgl1 libegl-mesa0 libglx-mesa0 libgl1-mesa-dri
187+
- name: Configure (TSan)
188+
run: cmake -S . -B build-tsan -DCMAKE_BUILD_TYPE=Debug -DCMAKE_CXX_COMPILER=clang++ -DLAIGE_TSAN=ON
189+
- name: Build (TSan)
190+
run: cmake --build build-tsan -j
191+
- name: Test (unit, TSan)
192+
# TSAN_OPTIONS=halt_on_error=1 is applied per test by tests/
193+
# (M0-BUILD-01), so the first data race report kills the test
194+
# process and ctest fails. The race report is printed to stderr
195+
# and captured by the tee below.
196+
run: |
197+
set -o pipefail
198+
ctest --test-dir build-tsan --output-on-failure 2>&1 | tee tsan-ctest-output.txt
199+
76200
windows-msvc:
77201
name: Windows x64 (MSVC 2022)
78-
if: >-
79-
contains(github.event.pull_request.labels.*.name, 'ci:windows') &&
80-
!contains(github.event.pull_request.labels.*.name, 'ci:macos')
81202
runs-on: windows-2022
82203
timeout-minutes: 10
83204
steps:
@@ -116,7 +237,6 @@ jobs:
116237
117238
macos-arm64:
118239
name: macOS arm64 (AppleClang)
119-
if: contains(github.event.pull_request.labels.*.name, 'ci:macos')
120240
runs-on: macos-15
121241
# The shared macOS runners' tail latency exceeds the 10-min build
122242
# budget (the macOS exception, documented in ci.yml's header).
@@ -132,7 +252,6 @@ jobs:
132252

133253
macos-intel:
134254
name: macOS Intel (AppleClang)
135-
if: contains(github.event.pull_request.labels.*.name, 'ci:macos')
136255
runs-on: macos-14
137256
# The shared macOS runners' tail latency exceeds the 10-min build
138257
# budget (the macOS exception, documented in ci.yml's header).
@@ -147,12 +266,12 @@ jobs:
147266
run: ctest --test-dir build --output-on-failure
148267

149268
include-lint:
150-
# M0-CI-03: runs on every PR (no ci:* condition). Platform-independent:
151-
# the lint is Python 3 stdlib only (no setup step needed) and checks
152-
# the repository layout, not a compiler-specific build. The CTest
269+
# M0-CI-03: runs on every PR. Platform-independent: the lint is
270+
# Python 3 stdlib only (no setup step needed) and checks the
271+
# repository layout, not a compiler-specific build. The CTest
153272
# suite runs it against the real tree in every P0 job as well
154273
# (tests/tools, test `include-lint-real-tree`), so a broken include
155-
# fails even a PR that selects another P0 OS.
274+
# fails every job, not just this one.
156275
name: Include-graph lint + dependency count
157276
runs-on: ubuntu-24.04
158277
timeout-minutes: 5
@@ -162,16 +281,15 @@ jobs:
162281
run: python3 tools/laige-include-lint
163282

164283
determinism-lint:
165-
# M1-DET-01: runs on every PR (no ci:* condition). The sim-source
166-
# determinism scan (the second half of the G-R8 guarantee; the
167-
# first is the compile-time trait checked by the
168-
# trait_compile_* CTest fixtures): forbids raw float/double and
169-
# unordered containers in src/laige-sim/**, with per-line
170-
# LAIGE-DETERM-EXCEPTION markers as the documented false-positive
171-
# policy (docs/concepts/determinism.md). Platform-independent:
172-
# Python 3 stdlib only, no setup step. The CTest suite runs it
173-
# against fixture trees and the real tree in every P0 job as well
174-
# (tests/tools, tests `determinism-lint-*`).
284+
# M1-DET-01: runs on every PR. The sim-source determinism scan
285+
# (the second half of the G-R8 guarantee; the first is the
286+
# compile-time trait checked by the trait_compile_* CTest
287+
# fixtures): forbids raw float/double and unordered containers in
288+
# src/laige-sim/**, with per-line LAIGE-DETERM-EXCEPTION markers as
289+
# the documented false-positive policy (docs/concepts/determinism.md).
290+
# Platform-independent: Python 3 stdlib only, no setup step. The
291+
# CTest suite runs it against fixture trees and the real tree in
292+
# every P0 job as well (tests/tools, tests `determinism-lint-*`).
175293
name: Determinism source scan (sim module)
176294
runs-on: ubuntu-24.04
177295
timeout-minutes: 5
@@ -181,13 +299,13 @@ jobs:
181299
run: python3 tools/laige-determinism-lint
182300

183301
api-manifest:
184-
# M0-TOOL-01: runs on every PR (no ci:* condition). The checked-in
185-
# public API manifest (laige-api.json, PRD §9.4, NFR-13.1) must stay
186-
# in sync with the public headers: this job regenerates it from the
187-
# current headers with laige-api-scanner and fails on any drift, so
188-
# adding a public symbol without regenerating the manifest fails CI.
189-
# The CTest suite runs the same check against the real tree in every
190-
# P0 job as well (tests/api, test `api-real-tree`).
302+
# M0-TOOL-01: runs on every PR. The checked-in public API manifest
303+
# (laige-api.json, PRD §9.4, NFR-13.1) must stay in sync with the
304+
# public headers: this job regenerates it from the current headers
305+
# with laige-api-scanner and fails on any drift, so adding a public
306+
# symbol without regenerating the manifest fails CI. The CTest
307+
# suite runs the same check against the real tree in every P0 job
308+
# as well (tests/api, test `api-real-tree`).
191309
name: Public API manifest drift
192310
runs-on: ubuntu-24.04
193311
timeout-minutes: 10
@@ -215,19 +333,15 @@ jobs:
215333
run: ./build/bin/laige-api-scanner --root . --check laige-api.json
216334

217335
detcheck:
218-
# M1-DET-04: runs on every PR (no ci:* condition). The determinism
219-
# checker (FR-11.5): the built-in synthetic scenario self-check,
220-
# then the M1-SAMPLE-01 hello scenario asserted against the
221-
# committed per-tick hash baselines (samples/hello/baselines/) on
222-
# BOTH SimMath backends (fixed_point_16_16, float_pinned_32 — ADR
223-
# 0002) with `hello --expect` (the laige-replay --expect contract:
224-
# 0 match, 1 first divergence, 2 baseline read/contract error).
225-
# The PR's P0 OS job's ctest repeats the same baseline assertion on
226-
# its own platform (tests/sample: hello_baseline_fpx /
227-
# hello_baseline_fp32); this tooling job guarantees the check runs
228-
# on every PR, including ones labelled for non-Linux P0 OSes. The
229-
# full two-configuration matrix (g++ vs clang++, Debug+ASan vs
230-
# Release) runs on merges in the detcheck job of ci.yml.
336+
# M1-DET-04: runs on every PR. The determinism checker (FR-11.5):
337+
# the built-in synthetic scenario self-check, then the M1-SAMPLE-01
338+
# hello scenario asserted against the committed per-tick hash
339+
# baselines (samples/hello/baselines/) on BOTH SimMath backends
340+
# (fixed_point_16_16, float_pinned_32 — ADR 0002) with `hello
341+
# --expect` (the laige-replay --expect contract: 0 match, 1 first
342+
# divergence, 2 baseline read/contract error). The full
343+
# two-configuration matrix (g++ vs clang++, Debug+ASan vs Release)
344+
# runs on merges in the detcheck job of ci.yml.
231345
name: Determinism check
232346
runs-on: ubuntu-24.04
233347
timeout-minutes: 10

‎.github/workflows/ci.yml‎

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,8 @@
44
# Roadmap: roadmap/M0-foundations.md, step M0-CI-01
55
# PRD refs: §6 (P0 platforms, AC-6.1), §14 (cadence), §8.1 (build budget)
66
#
7-
# Cadence (PRD §14: "one per PR, all per merge"):
7+
# Cadence (PRD §14, as amended 2026-10-11 — "test everything, all the
8+
# time"):
89
# * this workflow runs on every push to the default branch (master) —
910
# i.e. on merge — and on manual workflow_dispatch:
1011
# ALL eleven jobs run (the seven P0 OS/variant jobs — the five P0 OS
@@ -19,11 +20,13 @@
1920
# deterministic runs of laige-fuzz on the json_parse target) runs inside
2021
# every build job's ctest above, instrumented in the ASan tree. Seed and
2122
# nightly-long-run conventions: docs/testing.md.
22-
# * Pull requests run only the P0 OSes that cannot be verified
23-
# locally — Windows and/or macOS, selected by the PR's ci:windows /
24-
# ci:macos label (no label: no P0 OS build; Linux is verified
25-
# locally) — in the companion workflow
26-
# .github/workflows/ci-pull.yml.
23+
# * Pull requests run the SAME eleven jobs, unconditionally, in the
24+
# companion workflow .github/workflows/ci-pull.yml. The former
25+
# ci:windows / ci:macos label selector is removed: no job is gated
26+
# on a label and no test is ever skipped (Linux included). Local
27+
# verification of the Linux trees before pushing remains a
28+
# developer obligation (AGENTS.md, section 5: the local Linux test
29+
# gate).
2730
#
2831
# One job per P0 OS/variant (PRD §6). Each job runs the canonical flow from
2932
# docs/getting-started/building.md — configure → build → ctest (unit tests
@@ -472,9 +475,9 @@ jobs:
472475
# stated per ARCH-010).
473476
#
474477
# Like include-lint and api-manifest, the job runs on every PR and
475-
# merge, independent of the ci:* label selector — it is a tooling
476-
# check, not an additional P0 OS build (the PRD §14 cadence is
477-
# unchanged).
478+
# merge unconditionally (no label selector — see the header) — it is
479+
# a tooling check, not an additional P0 OS build (the PRD §14
480+
# cadence is unchanged).
478481
name: Determinism check
479482
runs-on: ubuntu-24.04
480483
timeout-minutes: 20

0 commit comments

Comments
 (0)