You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
ci(tests): stage the browser font set once (no apt in a shard), re-measure weights, 6 → 8 shards - #7102
On 2026-10-23, attempt 1 of three runs turned Consolidate test results red because shards hit the 20-minute job cap:
PR run 40011143666, shard 4.
PR run 40011313475, shards 2 and 4.
main run 40027189653, shard 4.
Nothing was hung. In every cut shard, all projects were still making progress when the cap hit.
Memex.Portal.Shared.Test part 3/3 entered a new class every few seconds right up to the cancel.
On green runs that part measures 315–343 s.
The PluginBundleClient.ReadIndex stacks come from ModuleReloadFaultedTest, which injects that fault on purpose.
Two causes stacked:
The browser step spent its whole 480 s budget on a stalled Ubuntu mirror. It ran playwright-core install --with-deps for its apt half, and on ubuntu-24.04 that half adds only nine font packages. Each cut shard spent exactly 8:00 there; healthy shards spend 20–70 s. The step then gave up on the fonts and carried on anyway. Triage node: rbuergi/Feedback/20261024-browser-step-spends-480s-on-optional-apt-fonts.
Stale weights. Shard 4 printed ~454 s of load but ran 875–894 s.
The fix
No apt in a shard
New job browser-fonts ("Stage the browser font set"). It runs in parallel with the build and needs nothing.
On a warm cache it does a lookup and nothing else.
On a miss it first checks .github/chromium-fonts.list against the pinned Playwright's own install-deps --dry-run. A font package that is neither listed nor already installed fails the job, so the set stays the one deploy/base-images/portal-ai/Dockerfile ships.
It then downloads the .debs with a private apt state (own lists and cache, locking off, as the runner user, so no system apt lock is involved). It extracts their font files with dpkg-deb -x and saves them as the chromium-fonts cache. It caches nothing if any part fails.
The logic is in .github/scripts/stage-chromium-fonts.sh; key mode gives both jobs one cache-key definition.
The shards only restore and unpack.
"Restore the browser font set" has timeout-minutes: 2. That is deliberately longer than its 1 min download abort, so a stalled download ends as a reported cache miss and never as the step cap failing the shard (review thread).
"Provision the headless browser" has timeout-minutes: 5. It downloads the browser from the CDN (150 s budget, 90 s per attempt; the 115 MiB download takes about 2 s), installs the font set (40 s bound) and runs the smoke print.
Gone from the shards: --with-deps, the apt lock config, wait-for-apt.sh and the orphaned-apt reap.
A missing cache is a ::warning:: and the smoke print still decides. That is the same contract a failed --with-deps had.
New: TheProvisionStepsInnerBoundsFitItsCap. The download budget, kill grace, font install and smoke print add up to 285 s, inside the step's 300 s. A stall therefore fails with its own message before the step cap fires.
New: TheFontRestoreMissesBeforeItsCap. It requires the restore step's cap to exceed the download abort by at least 30 s.
New: BrowserProvisioningGateGuard.TheTestShardsNeverReachAPackageMirror. It forbids --with-deps, install-deps and apt-get in the test job. A repo with no --with-deps provisioner is no longer a vacuous failure.
Weights and splits
Each weight is the max over three green full main runs (40014951907, 40021939021, 40027189653).
I chose part counts by replaying the trx per-class durations through the workflow's sorted every-Nth class split. That replay reproduces the measured parts exactly.
Project
Parts
Predicted part seconds
Memex.Portal.Shared.Test
3→4
240/179/243/254
MeshWeaver.Graph.Test
3→4
210/221/252/172
MeshWeaver.Messaging.Hub.Test
2→3
136/223/154 (part 2/2 was 409–416 s, 87% of the per-project cap)
Eight is what the guard allows. The matrix has 8 shards and 8 upload steps, and both SHARD_TOTAL settings read ${{ env.CI_SHARD_TOTAL }}. The job cap is unchanged.
Evidence
The staging and install, in ubuntu:24.04 as a non-root user with sudo.
stage fetched 9 packages (21 MB, 192 font files).
install took fontconfig from 13 to 47 faces (WenQuanYi Zen Hei, IPAGothic, FreeSerif, Loma, Unifont).
Playwright 1.62.1's dry run asks for exactly the nine listed packages.
Drift negative control: dropping fonts-wqy-zenhei from the list makes stage fail with "installs font package(s) fonts-wqy-zenhei on this release, which drifted.list does not stage".
Guard negative controls, run locally against a mutated workflow and then restored:
CI_SHARD_TOTAL: 7 → red: "Expected 586.43 to be less than or equal to 567 … 4105 s … at CI_SHARD_TOTAL=7".
(previous round) Provision step capped at 5 min with the earlier 345 s of inner bounds → red: "bounds … add up to 345 s, more than its 300 s".
Restore step timeout-minutes: 1 → red: "Expected 0 to be greater than or equal to 30".
install --with-deps put back into the shard → red, naming the line.
Local gates.
MeshWeaver.Documentation.Test builds with -c Release -warnaserror (0 warnings, 0 errors), and its full run passed 917 of 917.
actionlint (with shellcheck) is clean, shellcheck on the script is clean, check-workflow-shell.py reports 0 live findings, and check-workflow-timeouts.py reports 0 violations.
What this does NOT establish
The new job has not run on a GitHub runner yet. This PR's own run is the first: a cache miss, then the stage. I tested the same script only on arm64 ubuntu:24.04 against ports.ubuntu.com, not on the amd64 runner image.
Font fidelity is "same files", not "same install". No package postinst runs: no update-alternatives for the generic Japanese-gothic name and no mkfontdir. Chromium resolves faces through fontconfig, which indexes the files directly, but I did not compare a printed PDF against one from a real --with-deps install.
Shard times are estimates. The heaviest of the eight shards should run about 545 s, from today's class list. Adding a class shifts the every-Nth parity.
Cache-key edge case. The browser-fonts job checks out the event's ref. A shard tests the fresh merge with main. If main changes the font list mid-flight, the keys differ, and the shards warn and print without the staged fonts for that run.
…lled browser step no longer cuts a shard
Shards were cancelled at the 20-minute job cap on PR runs 40011143666 and
40011313475 and on main run 40027189653 (attempt 1 each), with every
project still progressing. Memex.Portal.Shared.Test was not hung: part 3/3
entered a new class every few seconds until the cut and measures 315-343 s
on green runs.
Two causes stacked:
- the browser step spent its whole 480 s budget on a stalled apt mirror
(normally 20-70 s);
- shard 4 owed ~880 s of tests against a printed ~454 s (stale weights),
and the re-measured suite is ~3,900 s, i.e. ~650 s per shard at six
even when balanced, while the cap leaves ~570 s after a stalled step.
Changes:
- weights re-measured over three green full main runs (2026-10-23);
- splits chosen from trx per-class durations through the workflow's
sorted every-Nth split (which reproduces the measured parts exactly):
Portal.Shared 3->4, Graph 3->4, Messaging.Hub 2->3 (part 2/2 was
409-416 s, 87% of the per-project cap, invisible to the total/N
guard), Hosting 3->2 (now 223/226);
- CI_SHARD_TOTAL 6->8, matrix and upload steps; SHARD_TOTAL now reads
CI_SHARD_TOTAL;
- new guard ShardWeightHeadroomGuard.TheIdealShardLoadFitsTheShardJobCap
reads the cap, the browser budget and the shard count from the workflow.
Compiler.Pipeline's line is left to #7087.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…wser step really do; 8 -> 10 shards
Review on #7102:
- the guard summed the weight table, so unlisted suites (scheduled at
DEFAULT_WEIGHT) counted nothing and stale rows counted; it now sums every
test/**/*.csproj shard-assign.sh discovers, at its table weight or the
default;
- it reserved BROWSER_STEP_BUDGET only, while the last attempt's
--kill-after grace, the orphaned-apt reap and the smoke print's timeout run
after that deadline; it now reads all of them (588 s end to end).
Measured that way the suite is 3,832 s against ~462 s of test room per
shard, so eight shards fail the guard (479 > 416). Ten pass (383; ~410 once
#7087 splits Compiler.Pipeline).
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
rbuergi
changed the title
ci(tests): re-measure shard weights, re-split, 6 → 8 shards — a stalled browser step no longer cuts a shard
ci(tests): re-measure shard weights, re-split, 6 → 10 shards — a stalled browser step no longer cuts a shard
Oct 24, 2026
The reason will be displayed to describe this comment to others. Learn more.
🔵 Needs a closer look
The new shard timings remain estimates, and documented peak loads can still exceed the available test budget, requiring human acceptance of the remaining timeout risk.
Results for commit dc3e3d0. ± Comparison against base commit b85e51b.
This pull request removes 44 and adds 23 tests. Note that renamed tests count towards both.
--- End of inner exception stack trace ---
--- End of inner exception stack trace ---)
--- End of inner exception stack trace ---, expected: True)
--- End of inner exception stack trace ---, isDenial: True)
---> (Inner Exception #1) MeshWeaver.Mesh.QueryProviderStalledException: Query provider(s) [pg] did not emit an Initial within the query fan-in's 16s bound for query 'nodeType:NodeType' (user 'system'). The merged Initial gates on EVERY provider, so this query has NO snapshot to answer with — it is reported as unavailable (retryable) rather than left hanging with no error. This is an availability failure, never a permission verdict: a consumer deciding access must fail CLOSED and say it could not establish the answer. Fix the stalled provider; never bump the consumer's timeout.<---
---> (Inner Exception #1) MeshWeaver.Messaging.Hub.Test.InfrastructureFaultTest+ProviderException (0x80004005): Failed to connect to 10.42.18.4:5432<---
---> (Inner Exception #1) System.ArgumentException: Value does not fall within the expected range.<---
---> (Inner Exception #1) System.InvalidOperationException: boom<---
---> (Inner Exception #1) System.InvalidOperationException: source B is misconfigured<---
…
Memex.Portal.Shared.Test.InstanceIdRulesMatchTheRegistryTest ‑ TheSetupHostAgreesWithTheRegistry(candidate: "521d9fa5-5198-4e57-a1ef-cbab7ca2aff6")
Memex.Portal.Shared.Test.PublishStorageFaultIsNotAConflictTest ‑ AStorageFault_IsA500_NamingTheError(fault: System.AggregateException: One or more errors occurred. (Access to the path '/data/modules/activation.d/MeshWeaver.Markdown.Export.json' is denied.)
---> System.UnauthorizedAccessException: Access to the path '/data/modules/activation.d/MeshWeaver.Markdown.Export.json' is denied.
--- End of inner exception stack trace ---)
Memex.Portal.Shared.Test.PublishStorageFaultIsNotAConflictTest ‑ AStorageFault_IsA500_NamingTheError(fault: System.InvalidOperationException: landing faulted
---> System.UnauthorizedAccessException: Access to the path '/data/modules/activation.d/MeshWeaver.Markdown.Export.json' is denied.
--- End of inner exception stack trace ---)
Memex.Portal.Shared.Test.SessionDenialIsAnAnswerTest ‑ OnlyAVerdictReadsAsADenial(shape: "the same verdict nested, as a late denial dispatch"···, failure: System.InvalidOperationException: write failed
---> System.UnauthorizedAccessException: Access denied
--- End of inner exception stack trace ---, isDenial: True)
MeshWeaver.Compiler.Pipeline.Test.SourcesWatcherStallBackoffTest ‑ The_production_classifier_recognises_a_stall_through_every_wrapping(because: "a stall as an aggregate's FIRST member", fault: System.AggregateException: One or more errors occurred. (Query provider(s) [pg] did not emit an Initial within the query fan-in's 16s bound for query 'nodeType:NodeType' (user 'system'). The merged Initial gates on EVERY provider, so this query has NO snapshot to answer with — it is reported as unavailable (retryable) rather than left hanging with no error. This is an availability failure, never a permission verdict: a consumer deciding access must fail CLOSED and say it could not establish the answer. Fix the stalled provider; never bump the consumer's timeout.)
---> MeshWeaver.Mesh.QueryProviderStalledException: Query provider(s) [pg] did not emit an Initial within the query fan-in's 16s bound for query 'nodeType:NodeType' (user 'system'). The merged Initial gates on EVERY provider, so this query has NO snapshot to answer with — it is reported as unavailable (retryable) rather than left hanging with no error. This is an availability failure, never a permission verdict: a consumer deciding access must fail CLOSED and say it could not establish the answer. Fix the stalled provider; never bump the consumer's timeout.
--- End of inner exception stack trace ---, expected: True)
MeshWeaver.Compiler.Pipeline.Test.SourcesWatcherStallBackoffTest ‑ The_production_classifier_recognises_a_stall_through_every_wrapping(because: "a stall as an aggregate's SECOND member", fault: System.AggregateException: One or more errors occurred. (The operation has timed out.) (Query provider(s) [pg] did not emit an Initial within the query fan-in's 16s bound for query 'nodeType:NodeType' (user 'system'). The merged Initial gates on EVERY provider, so this query has NO snapshot to answer with — it is reported as unavailable (retryable) rather than left hanging with no error. This is an availability failure, never a permission verdict: a consumer deciding access must fail CLOSED and say it could not establish the answer. Fix the stalled provider; never bump the consumer's timeout.)
---> System.TimeoutException: The operation has timed out.
--- End of inner exception stack trace ---
---> (Inner Exception #1) MeshWeaver.Mesh.QueryProviderStalledException: Query provider(s) [pg] did not emit an Initial within the query fan-in's 16s bound for query 'nodeType:NodeType' (user 'system'). The merged Initial gates on EVERY provider, so this query has NO snapshot to answer with — it is reported as unavailable (retryable) rather than left hanging with no error. This is an availability failure, never a permission verdict: a consumer deciding access must fail CLOSED and say it could not establish the answer. Fix the stalled provider; never bump the consumer's timeout.<---
, expected: True)
MeshWeaver.Compiler.Pipeline.Test.SourcesWatcherStallBackoffTest ‑ The_production_classifier_recognises_a_stall_through_every_wrapping(because: "a stall nested in an aggregate inside a wrapper", fault: System.InvalidOperationException: outer
---> System.AggregateException: One or more errors occurred. (Value does not fall within the expected range.) (Query provider(s) [pg] did not emit an Initial within the query fan-in's 16s bound for query 'nodeType:NodeType' (user 'system'). The merged Initial gates on EVERY provider, so this query has NO snapshot to answer with — it is reported as unavailable (retryable) rather than left hanging with no error. This is an availability failure, never a permission verdict: a consumer deciding access must fail CLOSED and say it could not establish the answer. Fix the stalled provider; never bump the consumer's timeout.)
---> System.ArgumentException: Value does not fall within the expected range.
--- End of inner exception stack trace ---
---> (Inner Exception #1) MeshWeaver.Mesh.QueryProviderStalledException: Query provider(s) [pg] did not emit an Initial within the query fan-in's 16s bound for query 'nodeType:NodeType' (user 'system'). The merged Initial gates on EVERY provider, so this query has NO snapshot to answer with — it is reported as unavailable (retryable) rather than left hanging with no error. This is an availability failure, never a permission verdict: a consumer deciding access must fail CLOSED and say it could not establish the answer. Fix the stalled provider; never bump the consumer's timeout.<---
--- End of inner exception stack trace ---, expected: True)
MeshWeaver.Compiler.Pipeline.Test.SourcesWatcherStallBackoffTest ‑ The_production_classifier_recognises_a_stall_through_every_wrapping(because: "a stall wrapped as InnerException", fault: System.InvalidOperationException: outer
---> MeshWeaver.Mesh.QueryProviderStalledException: Query provider(s) [pg] did not emit an Initial within the query fan-in's 16s bound for query 'nodeType:NodeType' (user 'system'). The merged Initial gates on EVERY provider, so this query has NO snapshot to answer with — it is reported as unavailable (retryable) rather than left hanging with no error. This is an availability failure, never a permission verdict: a consumer deciding access must fail CLOSED and say it could not establish the answer. Fix the stalled provider; never bump the consumer's timeout.
--- End of inner exception stack trace ---, expected: True)
MeshWeaver.Compiler.Pipeline.Test.SourcesWatcherStallBackoffTest ‑ The_production_classifier_recognises_a_stall_through_every_wrapping(because: "an aggregate of unrelated faults", fault: System.AggregateException: One or more errors occurred. (The operation has timed out.) (Value does not fall within the expected range.)
---> System.TimeoutException: The operation has timed out.
--- End of inner exception stack trace ---
---> (Inner Exception #1) System.ArgumentException: Value does not fall within the expected range.<---
, expected: False)
MeshWeaver.Documentation.Test.BrowserProvisioningGateGuard ‑ TheTestShardsNeverReachAPackageMirror
…
♻️ This comment has been updated with latest results.
…e; no apt in a shard; 10 -> 8 shards
The shards' browser step ran `playwright-core install --with-deps` for its
apt half, which on ubuntu-24.04 adds only nine font packages. A stalled
Ubuntu mirror spent the step's whole 480 s on them, and that cut shards on
2026-10-23 (PR runs 40011143666 and 40011313475, main run 40027189653).
- New `browser-fonts` job ("Stage the browser font set"), parallel with the
build. On a cache miss it checks .github/chromium-fonts.list against the
pinned Playwright's `install-deps --dry-run` and downloads the .debs with a
private apt state (no system lock, no root). It extracts their font files
without installing them and saves the `chromium-fonts` cache. On a hit it is
a lookup.
- Shards restore that cache (step cap 1 min) and unpack it. The provision
step (step cap 6 min) now only downloads the browser (180 s budget, 90 s per
attempt), installs the fonts and runs the smoke print. No apt, no lock, no
reap. A missing cache is a warning; the smoke print still decides.
- ShardWeightHeadroomGuard reads the browser's cost as the two steps'
`timeout-minutes` (420 s) and checks that the provision step's inner bounds
fit its cap. Eight shards pass it (513 <= 567 s); seven fail (586).
- BrowserProvisioningGateGuard: TheTestShardsNeverReachAPackageMirror forbids
`--with-deps` / `install-deps` / `apt-get` in the test job. A repo with no
`--with-deps` provisioner is no longer a vacuous failure.
- ToImmutableDictionary for the weight lookup (review on #7102).
Verified locally in ubuntu:24.04 as a non-root user: stage fetched 9 packages
(21 MB, 192 font files), install took fontconfig from 13 to 47 faces, and a
list missing fonts-wqy-zenhei made stage fail naming it.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
rbuergi
changed the title
ci(tests): re-measure shard weights, re-split, 6 → 10 shards — a stalled browser step no longer cuts a shard
ci(tests): stage the browser font set once (no apt in a shard), re-measure weights, 6 → 8 shards
Oct 24, 2026
…ad of swallowing it
check-workflow-shell flagged `asked=$(… 2>/dev/null | … || :)`: a failed or
stalled `install-deps --dry-run` became an empty list with no trace. The dry
run's output now goes to a file with stderr kept. Exit 0/1 is read (1 is how
playwright-core reports missing deps). 124/137 is a named stall, and anything
else is a named failure, each printing the dry run's output. An empty font
list still refuses the stage, again with that output.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
… restore step's cap
Review on #7102: the restore step's 1 min cap equalled its 1 min
SEGMENT_DOWNLOAD_TIMEOUT_MINS, whose clock starts after the cache lookup. So
the cap could fire first and fail the shard over an optional input, before
the browser step's warning and the tests. The restore cap is now 2 min, and
the new TheFontRestoreMissesBeforeItsCap guard requires at least 30 s of room
past the abort.
The provision step goes 6 -> 5 min (download budget 180 -> 150 s, font
install 70 -> 40 s; inner bounds 285 of 300 s), so the browser's end-to-end
bound stays 420 s and eight shards still pass the shard budget (513 <= 567 s).
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
The reason will be displayed to describe this comment to others. Learn more.
🔵 Needs a closer look
Competing cache-download and step deadlines can fail shards before the intended warning-and-smoke fallback runs.
No open issues 🎉
1 previously missed issue
In code that hasn't changed since last review
Leave timeout headroom for cache-miss fallback
.github/workflows/dotnet-test.yml:1071
The cache segment timeout and the entire restore step cap are both 60 seconds, but the segment timer starts after cache lookup and download setup. A stalled segment can therefore hit the step cap before the action returns a cache miss, failing the shard and skipping browser provisioning and tests instead of reaching the warning-and-smoke fallback. Give the internal timeout enough headroom inside the step cap for that fallback to run, and re-check the shard-budget accounting if the outer cap changes.
This new ToList() introduces a mutable collection despite the immutable-collections requirement in AGENTS.md (“Collections Policy”). The guard only enumerates and counts these lines, so use an immutable list without changing its behavior.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What was cut, and why
On 2026-10-23, attempt 1 of three runs turned
Consolidate test resultsred because shards hit the 20-minute job cap:Nothing was hung. In every cut shard, all projects were still making progress when the cap hit.
Memex.Portal.Shared.Testpart 3/3 entered a new class every few seconds right up to the cancel.PluginBundleClient.ReadIndexstacks come fromModuleReloadFaultedTest, which injects that fault on purpose.Two causes stacked:
playwright-core install --with-depsfor its apt half, and on ubuntu-24.04 that half adds only nine font packages. Each cut shard spent exactly 8:00 there; healthy shards spend 20–70 s. The step then gave up on the fonts and carried on anyway. Triage node:rbuergi/Feedback/20261024-browser-step-spends-480s-on-optional-apt-fonts.The fix
No apt in a shard
browser-fonts("Stage the browser font set"). It runs in parallel with the build and needs nothing..github/chromium-fonts.listagainst the pinned Playwright's owninstall-deps --dry-run. A font package that is neither listed nor already installed fails the job, so the set stays the onedeploy/base-images/portal-ai/Dockerfileships..debs with a private apt state (own lists and cache, locking off, as the runner user, so no system apt lock is involved). It extracts their font files withdpkg-deb -xand saves them as thechromium-fontscache. It caches nothing if any part fails..github/scripts/stage-chromium-fonts.sh;keymode gives both jobs one cache-key definition.timeout-minutes: 2. That is deliberately longer than its 1 min download abort, so a stalled download ends as a reported cache miss and never as the step cap failing the shard (review thread).timeout-minutes: 5. It downloads the browser from the CDN (150 s budget, 90 s per attempt; the 115 MiB download takes about 2 s), installs the font set (40 s bound) and runs the smoke print.--with-deps, the apt lock config,wait-for-apt.shand the orphaned-apt reap.::warning::and the smoke print still decides. That is the same contract a failed--with-depshad.Guards, re-modelled
ShardWeightHeadroomGuard.TheIdealShardLoadFitsTheShardJobCap.timeout-minutes, 420 s, which GitHub enforces end to end.DEFAULT_WEIGHT.TheProvisionStepsInnerBoundsFitItsCap. The download budget, kill grace, font install and smoke print add up to 285 s, inside the step's 300 s. A stall therefore fails with its own message before the step cap fires.TheFontRestoreMissesBeforeItsCap. It requires the restore step's cap to exceed the download abort by at least 30 s.BrowserProvisioningGateGuard.TheTestShardsNeverReachAPackageMirror. It forbids--with-deps,install-depsandapt-getin the test job. A repo with no--with-depsprovisioner is no longer a vacuous failure.Weights and splits
MeshWeaver.Compiler.Pipeline.Testkeeps #7087's split.CI_SHARD_TOTAL6→8Eight is what the guard allows. The matrix has 8 shards and 8 upload steps, and both
SHARD_TOTALsettings read${{ env.CI_SHARD_TOTAL }}. The job cap is unchanged.Evidence
ubuntu:24.04as a non-root user with sudo.stagefetched 9 packages (21 MB, 192 font files).installtook fontconfig from 13 to 47 faces (WenQuanYi Zen Hei, IPAGothic, FreeSerif, Loma, Unifont).fonts-wqy-zenheifrom the list makesstagefail with "installs font package(s) fonts-wqy-zenhei on this release, which drifted.list does not stage".CI_SHARD_TOTAL: 7→ red: "Expected 586.43 to be less than or equal to 567 … 4105 s … at CI_SHARD_TOTAL=7".timeout-minutes: 1→ red: "Expected 0 to be greater than or equal to 30".install --with-depsput back into the shard → red, naming the line.MeshWeaver.Documentation.Testbuilds with-c Release -warnaserror(0 warnings, 0 errors), and its full run passed 917 of 917.actionlint(with shellcheck) is clean,shellcheckon the script is clean,check-workflow-shell.pyreports 0 live findings, andcheck-workflow-timeouts.pyreports 0 violations.What this does NOT establish
ubuntu:24.04againstports.ubuntu.com, not on the amd64 runner image.update-alternativesfor the generic Japanese-gothic name and nomkfontdir. Chromium resolves faces through fontconfig, which indexes the files directly, but I did not compare a printed PDF against one from a real--with-depsinstall.browser-fontsjob checks out the event's ref. A shard tests the fresh merge with main. If main changes the font list mid-flight, the keys differ, and the shards warn and print without the staged fonts for that run.Refs #7087.
Pairs-with: none — CI configuration, CI scripts, test-tree guards and a doc page only; no public surface changes.
🤖 Generated with Claude Code