diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index ebb836ed..f794f174 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -1551,7 +1551,9 @@ jobs: if-no-files-found: error # The split lane itself: the three-arm shape ARCHITECTURE.md ยง13.2 asks for verbatim, plus the - # first real run of the five MG_Test/Wire suites, plus P5's two exit-gate negative controls. + # first real run of the five MG_Test/Wire suites. P5's two exit-gate negative controls are in-test + # now (RemoteWaitBoundaryControl.AnUnwaitedPublishIsSeenAsNoWait and + # PersistentCoherentMapScenario.ThePushIsTheOnlyRouteAWriteTakesToTheServer). integration-split: runs-on: ubuntu-26.04 timeout-minutes: 120 @@ -1778,6 +1780,29 @@ jobs: print(f"the two arms agree on all {len(a)} entries, name and status") PY + # The DirectGLES.Split. entries and their log-ownership check, once, in a job whose 'Split + # lanes' step never ran because an earlier step failed: without this, a red unit lane or + # flatc build would hide every split integration result of the run. When the lane step ran + # it left split-baseline.xml, and this step does nothing. + - name: Split lane fallback (lane step skipped) + if: ${{ !cancelled() }} + working-directory: ${{ env.SPLIT_BUILD_DIR }} + env: + MOBILEGL_ITEST_REQUIRE_GPU: "1" + run: | + if [ -f "${RUNNER_TEMP}/split-baseline.xml" ]; then + echo "the lane step ran and left split-baseline.xml; nothing to fall back to" + exit 0 + fi + echo "::notice::the lane step left no split-baseline.xml, so the DirectGLES.Split. entries run here" + python3 "${GITHUB_WORKSPACE}/MobileGL/MG_IntegrationTest/Harness/split_log_paths.py" check ctest "$PWD" + # A directory of its own: the job uploads RUNNER_TEMP/*.xml for the skip census, which + # has no row for this run. + mkdir -p "${RUNNER_TEMP}/split-fallback" + ctest --output-on-failure -L integration-split -R '^DirectGLES\.Split\.' -j 4 --no-tests=error \ + --output-junit "${RUNNER_TEMP}/split-fallback/isplit.xml" + python3 "${GITHUB_WORKSPACE}/scripts/ci/junit_tally.py" "${RUNNER_TEMP}/split-fallback/isplit.xml" --require-split-ran + # build-linux ran the server link-closure ratchet on the build these lanes took, with # continue-on-error so that a red there does not skip build-linux's own consumers. - name: Server link-closure ratchet of the reused build diff --git a/docs/ci/ci-cleanup-log.md b/docs/ci/ci-cleanup-log.md index 212aaf36..7475457e 100644 --- a/docs/ci/ci-cleanup-log.md +++ b/docs/ci/ci-cleanup-log.md @@ -54,7 +54,7 @@ them wrong. | 2026-10-17 | `Magma split lane (strict, dual-block)` on the disaggregation branches when the dual-block step proved its defaults | Test / `integration-split-strict` | the dual-block census run covers the `integration-magma-split` label in the same environment | the dual-block census run | | 2026-10-17 | the 75 retrace-split inproc rows other than OpenRA's, when `build-linux-split` reuses `build-linux` | Test / `retrace-split` | the retrace job's inproc-acceptance rows replay the same cases on the same library, strict and with the dual block and run-ahead on; no one-sided pair since 10-05 | the inproc-acceptance rows, which now prove per leg from the client log that they run with strict=1, role-split-state=1 and run-ahead=1 (`MGTRACE_EXPECT_IPC`) and keep a refusal census; every remaining retrace-split leg proves its defaults (strict=0, role-split-state=1, run-ahead=1) and a changed default reds every leg with the rows to restore; `trace_cases.py --self-test-split-matrix` (disaggregation branches) proves every dropped row is an acceptance row; OpenRA's inproc rows stay for the wire evidence | | 2026-10-17 | `Transport resolution check`'s grep of one entry's log for the inproc resolution sentence | Test / `integration-split` | it checked one entry (`DirectGLES.Split.PersistentMapArm.`) after the fact | in-test: the split fixture (`Harness/ScenarioFixture.h`) now fails every armed Split/Spawn/Tcp entry whose process did not resolve the transport its registration claims, or whose server is not where the claim says (a thread here for inproc, a process of its own for spawn and tcp), from the running process (`SplitRuntimePeek`, `ClientSession::PeerServerPid()`); `SplitLaneHarness.TransportClaimRule` (unit) proves the rule red on canned inputs; the step keeps the IPC default proof as `Default-knob resolution check` | -| 2026-10-18 | `Negative controls (barrier, map push)` (`scripts/ci/split_negative_controls.sh`; skip-census lane `junit-integration-split/control`) | Test / `integration-split` | it reran the split entries with `MOBILEGL_IPC_VERB_BARRIER=0` and `MOBILEGL_IPC_PERSISTENT_BLOCK_KB=0`, two product knobs that exist only for it; what was left without them (the ownership check and the baseline tally) repeats `SplitLogPaths.PrivateAndDistinct` and the lane step's own `junit_tally.py --require-split-ran` | E1: `RemoteWaitBoundaryControl.AnUnwaitedPublishIsSeenAsNoWait` (unit, always on); E3(a): `PersistentCoherentMapScenario.ThePushIsTheOnlyRouteAWriteTakesToTheServer`, always on in the split-arm entries (DirectGLES inproc, spawn, tcp and small ring; Magma's in the informational tier), within one frame and across a Present. Narrower than the control: it takes the push away with `PersistentMapTracker::Forget`, which also drops the membership (only the push reads it today), and draws its own frames, so it no longer shows that the bodies of `TwoWrites...` and `AWriteAfter...` make no announcing call | +| 2026-10-18 | `Negative controls (barrier, map push)` (`scripts/ci/split_negative_controls.sh`; skip-census lane `junit-integration-split/control`) | Test / `integration-split` | it reran the split entries with `MOBILEGL_IPC_VERB_BARRIER=0` and `MOBILEGL_IPC_PERSISTENT_BLOCK_KB=0`, two product knobs that exist only for it; of what was left without them, the ownership check and the baseline tally repeat `SplitLogPaths.PrivateAndDistinct` and the lane step's own `junit_tally.py --require-split-ran`, and the `DirectGLES.Split.` rerun for a job whose lane step never ran stays as the step `Split lane fallback (lane step skipped)` | E1: `RemoteWaitBoundaryControl.AnUnwaitedPublishIsSeenAsNoWait` (unit, always on); E3(a): `PersistentCoherentMapScenario.ThePushIsTheOnlyRouteAWriteTakesToTheServer`, always on in the split-arm entries (DirectGLES inproc, spawn, tcp and small ring; Magma's in the informational tier), within one frame and across a Present. Narrower than the control: it takes the push away with `PersistentMapTracker::Forget`, which also drops the membership (only the push reads it today), and draws its own frames, so it no longer shows that the bodies of `TwoWrites...` and `AWriteAfter...` make no announcing call | | 2026-10-18 | `scripts/ci/wait_boundary_negative_control.py` | Test / `integration-split` | E1's body inside the step above: the three boundary cases rerun with the barrier off | `RemoteWaitBoundaryControl.AnUnwaitedPublishIsSeenAsNoWait`: the same ApplierReset published with no wait must read parked=0, the observation the owed waits read as parked=1 | | 2026-10-18 | `scripts/ci/control_smoke_test.sh`, `scripts/ci/testdata/stub_ctest.sh`, `scripts/ci/testdata/split_private_log_smoke.sh`, and the `reset` / `results` / `assertion` / `evidence` modes of `split_log_paths.py` | Test / pipe-gates (R-16) | smoke cases and helpers of the step above only | none needed | | 2026-10-18 | unit `RemoteRunAhead.ClearingTheVerbBarrierDisarmsRunAheadEvenWithTheCapBit`, and the `MOBILEGL_IPC_VERB_BARRIER` knob with its arm in the shipped client (`ClientSession`: the free-running `EmitAndWaitTails` return, the run-ahead latch term, the `gPipeInputs` guard's early return, the `verb-barrier=` field of the `Config: IPC` line) | Test / unit (split); product code | `0` was never a product mode, only E1's negative control, retired above; the unit case pinned that the control also disarmed run-ahead | `RemoteWaitBoundaryControl.AnUnwaitedPublishIsSeenAsNoWait`; `MOBILEGL_IPC_RUN_AHEAD=0` stays the lockstep A/B arm | diff --git a/scripts/ci/junit_tally.py b/scripts/ci/junit_tally.py index 12e89b27..9a7484b3 100755 --- a/scripts/ci/junit_tally.py +++ b/scripts/ci/junit_tally.py @@ -9,8 +9,9 @@ """Tally a ctest --output-junit file as "passed failed skipped". -Split out of .github/workflows/test.yml's negative-control step so that the workflow, the local -gate and the control smoke test all count a run the same way. +Split out of .github/workflows/test.yml's split negative-control step (since retired) so that +every caller counts a run the same way: the split, spawn, TCP, verify and dual-block lane steps, +the split lane's fallback, and scripts/ci/dualblock_negative_control.sh. WHY THIS EXISTS AT ALL (ID-46 finding 8, second half). The counter this replaces lived inline in the workflow and counted a case as having "run" when it was merely not :