From 5fb1a7a92116fb8469a53faa0d254c19b62a3a4c Mon Sep 17 00:00:00 2001 From: Libby Date: Fri, 13 Nov 2026 22:08:05 +0000 Subject: [PATCH 1/2] ci: shard the portability job into three so each fits the forge's 30-minute cap Runs 902, 904 and 905 were cancelled at 30:00 with one job holding every step. The steps now split by owner: reactive semantics and leases, cache owners (TypeScript, Python, Rust and C# caches, packed packages) and mesh authority and content parity. Every step still runs; each shard repeats the setup. Co-Authored-By: Claude Sonnet 5.5 --- .github/workflows/cultmesh-portability.yml | 31 ++++++++++++++++++---- 1 file changed, 26 insertions(+), 5 deletions(-) diff --git a/.github/workflows/cultmesh-portability.yml b/.github/workflows/cultmesh-portability.yml index bafe70fc..ca7b91bd 100644 --- a/.github/workflows/cultmesh-portability.yml +++ b/.github/workflows/cultmesh-portability.yml @@ -23,14 +23,19 @@ concurrency: jobs: reactive-and-resource-lifetime: - name: ${{ matrix.os }} reactive semantics and leases + name: ${{ matrix.os }} ${{ matrix.shard }} runs-on: ${{ matrix.os }} - # The job dies at the lower of this value and the forge runner's own cap, which Eureka is raising from 30 to 60 minutes - # (run 1378 carried 60 and was still cancelled at 30:02); the two must both say 60 before a full run can finish. - timeout-minutes: 60 + # The forge runner cancels a job at 30:00 and one undivided job ran 22 to 30 minutes, so the steps are sharded across + # three jobs (matrix.shard) that each fit well under it; the forge runs them one after another. Every shard repeats + # the checkout and toolchain setup (about 4 minutes) so that no shard depends on another's output. + timeout-minutes: 30 strategy: fail-fast: false matrix: + shard: + - reactive semantics and leases + - cache owners + - mesh authority and content parity # The forge runner (label `ubuntu-latest`) is Linux-only and no Windows runner exists: the Windows leg runs on workflow_dispatch only (GitHub or a Windows runner), the ubuntu-latest leg gates push and PR. Windows push/PR triggers return in the PR that registers a Windows runner. os: ${{ fromJSON(github.event_name == 'workflow_dispatch' && '["windows-latest", "ubuntu-latest"]' || '["ubuntu-latest"]') }} @@ -101,7 +106,7 @@ jobs: # The forge runner images carry no pwsh; GitHub's Windows and hosted images do. - name: Install PowerShell - if: runner.os == 'Linux' + if: runner.os == 'Linux' && matrix.shard == 'reactive semantics and leases' shell: bash run: | set -euo pipefail @@ -117,14 +122,17 @@ jobs: DOTNET_SYSTEM_GLOBALIZATION_INVARIANT=1 pwsh -Version - name: Verify explicit reactive scheduling across runtimes + if: matrix.shard == 'reactive semantics and leases' shell: pwsh run: ./scripts/verify-reactive-document-scaling.ps1 - name: Measure representative reactive workload + if: matrix.shard == 'reactive semantics and leases' shell: pwsh run: ./scripts/measure-reactive-document-performance.ps1 -Quick -SkipRuntimeBuild - name: Churn 100,000 distinct document leases + if: matrix.shard == 'reactive semantics and leases' run: >- dotnet test tests/GameCult.Mesh.Tests/GameCult.Mesh.Tests.csproj --filter FullyQualifiedName~Document_OneHundredThousandDistinctLeaseCyclesLeaveNoResourcesOrCallbacks @@ -143,20 +151,24 @@ jobs: # and unrun. The wiring check fails when any TypeScript test file is run by no script, and the cache's own suite # runs here beside the packages built on it. - name: Check that every TypeScript test file is run by its package + if: matrix.shard == 'cache owners' run: node scripts/check-typescript-tests-wired.mjs - name: Verify the CultCache document owner with the TypeScript cache + if: matrix.shard == 'cache owners' run: npm run test --workspace packages/cultcache-ts # The Python cache's unit suite ran only in cultnet-interop.yml (windows-latest, dispatch-only), so on Linux # it ran nowhere. It sits after the TypeScript cache's test so the built cultcache-ts store holder exists for the # cross-runtime store-lock test, which skips without it. - name: Verify the CultCache document owner with the Python cache + if: matrix.shard == 'cache owners' run: python -m unittest discover -s packages/cultcache-py/tests # cargo test for packages/cultcache-rs ran in no workflow: the Rust cache's own unit and doc tests were unwatched # (only the interop example built, inside the C# store tests). The target directory is under RUNNER_TEMP, away from the checkout. - name: Verify the CultCache document owner with the Rust cache + if: matrix.shard == 'cache owners' shell: bash run: CARGO_TARGET_DIR="$RUNNER_TEMP/cultcache-rs-target" cargo test --locked --manifest-path packages/cultcache-rs/Cargo.toml @@ -166,14 +178,17 @@ jobs: # culture and throws CultureNotFoundException; it is excluded here and still runs wherever ICU is present # (variants:follow_up:culture-test-invariant-image). - name: Verify the CultCache document owner with the C# reference + if: matrix.shard == 'cache owners' run: dotnet test tests/GameCult.Caching.Tests/GameCult.Caching.Tests.csproj --filter "FullyQualifiedName!~ADeterministicIdIsTheSameUnderEveryCulture" --logger "console;verbosity=minimal" # Packs the four TypeScript packages and installs them into a clean consumer, so a package that builds but does # not install or load fails here. - name: Verify the packed TypeScript packages install and load from a clean consumer + if: matrix.shard == 'cache owners' run: npm run test:pack:ts - name: Verify the CultMesh authority rules with the TypeScript verifier + if: matrix.shard == 'mesh authority and content parity' run: npm run test --workspace packages/cultnet-ts # cultmesh-browser's test compiles test/interop, which imports cultmesh-ts/content, so cultmesh-ts builds first, @@ -186,9 +201,11 @@ jobs: # in CI on any OS until these two steps existed, so a codec that agreed # only with itself would have gone green. - name: Verify the realtime frame codec and the content provider with the TypeScript runtime + if: matrix.shard == 'mesh authority and content parity' run: npm run test --workspace packages/cultmesh-ts - name: Verify the browser client against the shared TypeScript verifier + if: matrix.shard == 'mesh authority and content parity' run: npm run test --workspace packages/cultmesh-browser # Both directions of the CultMesh authority vectors: the C#-written @@ -198,6 +215,7 @@ jobs: # in the step above. CULTMESH_WRITE_VECTORS is never set in CI: the # committed files are the contract. - name: Verify the CultMesh authority vectors with the C# reference + if: matrix.shard == 'mesh authority and content parity' run: >- dotnet test tests/GameCult.Mesh.Tests/GameCult.Mesh.Tests.csproj --filter FullyQualifiedName~AuthorityProof @@ -207,6 +225,7 @@ jobs: # re-encodes the vector file the TypeScript codec wrote. A fixture # authored on one side alone would only agree with itself. - name: Verify the realtime frame vectors with the C# reference + if: matrix.shard == 'mesh authority and content parity' run: >- dotnet test tests/GameCult.Mesh.Tests/GameCult.Mesh.Tests.csproj --filter FullyQualifiedName~RealtimeWireProtocol @@ -218,6 +237,7 @@ jobs: # content parity, which no CI step ran before this one. CULTNET_WRITE_VECTORS # is never set in CI: the committed files are the contract. - name: Verify the content vectors with the C# reference + if: matrix.shard == 'mesh authority and content parity' run: >- dotnet test tests/GameCult.Mesh.Tests/GameCult.Mesh.Tests.csproj --filter FullyQualifiedName~CultMeshContentVectorTests @@ -227,4 +247,5 @@ jobs: # TypeScript reader fetch from the TypeScript provider in its own process. Both refuse bytes tampered in flight, # and the provider refuses a corrupt store. - name: Verify HTTPS content parity across C#, the TypeScript provider and the TypeScript reader + if: matrix.shard == 'mesh authority and content parity' run: npm run test:interop --workspace packages/cultmesh-browser -- 2.47.3 From 60285f46750bc87ecffe301789db87902e7af57c Mon Sep 17 00:00:00 2001 From: Libby Date: Fri, 13 Nov 2026 23:01:15 +0000 Subject: [PATCH 2/2] ci: the mesh shard builds the TypeScript workspaces it imports Run 912's mesh shard failed after 6m56s: cultnet-ts and cultmesh-ts import cultcache-ts's built output, which the reactive shard's script used to produce earlier in the single job. Co-Authored-By: Claude Sonnet 5.5 --- .github/workflows/cultmesh-portability.yml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/.github/workflows/cultmesh-portability.yml b/.github/workflows/cultmesh-portability.yml index ca7b91bd..466802eb 100644 --- a/.github/workflows/cultmesh-portability.yml +++ b/.github/workflows/cultmesh-portability.yml @@ -187,6 +187,12 @@ jobs: if: matrix.shard == 'cache owners' run: npm run test:pack:ts + # The reactive shard's script builds cultcache-ts and cultnet-ts, and the packages below import their built output, + # so this shard builds the workspaces itself instead of relying on a shard that may not have run first. + - name: Build the TypeScript workspaces + if: matrix.shard == 'mesh authority and content parity' + run: npm run build:ts + - name: Verify the CultMesh authority rules with the TypeScript verifier if: matrix.shard == 'mesh authority and content parity' run: npm run test --workspace packages/cultnet-ts -- 2.47.3