mirror of
https://github.com/heygen-com/hyperframes.git
synced 2026-09-03 23:03:09 +00:00
ci(regression): compute the shard matrix from recorded fixture timings (#2815)
* ci(regression): compute the shard matrix from recorded fixture timings * ci(regression): refresh shard timings from a green post-PSNR run * fix(ci): close two silent-skip holes in the shard schedule contract * ci(regression): schedule the new static-volume-future-set fixture * test(producer): regenerate static-volume-future-set golden in the pinned container
This commit is contained in:
@@ -0,0 +1,191 @@
|
||||
// Computes the regression workflow's shard matrix instead of hand-maintaining
|
||||
// it in .github/workflows/regression.yml.
|
||||
//
|
||||
// Two problems this fixes:
|
||||
//
|
||||
// 1. Staleness. The hand-written matrix was bin-packed once against a specific
|
||||
// CI run and then drifted. By the time it was measured again, shard work
|
||||
// ranged from 17.2 to 36.8 minutes against a comment claiming "within ~40s
|
||||
// of the others" — and CI wall-clock is set by the worst shard.
|
||||
//
|
||||
// 2. Silent drift. A fixture only ran if someone remembered to paste its name
|
||||
// into the YAML. 25 fixtures that the harness can run were in no shard at
|
||||
// all, some for months, and 3 more were rejected at load time for invalid
|
||||
// meta.json with nothing louder than a console warning. The default
|
||||
// outcome for a new fixture was that it never ran.
|
||||
//
|
||||
// Fixtures are now discovered from disk. Every one must be either scheduled
|
||||
// (with a timing) or explicitly excluded with a reason, or this script fails.
|
||||
// Drift becomes a build error instead of silent absence.
|
||||
//
|
||||
// Usage:
|
||||
// node scripts/plan-regression-shards.mjs [--shards N] [--pretty]
|
||||
|
||||
import { readdirSync, readFileSync, existsSync } from "node:fs";
|
||||
import { dirname, join, resolve } from "node:path";
|
||||
import { fileURLToPath } from "node:url";
|
||||
|
||||
const PRODUCER_ROOT = resolve(dirname(fileURLToPath(import.meta.url)), "..");
|
||||
const TESTS_DIR = join(PRODUCER_ROOT, "tests");
|
||||
const SCHEDULE_FILE = join(TESTS_DIR, "shard-schedule.json");
|
||||
|
||||
export const DEFAULT_SHARD_COUNT = 8;
|
||||
|
||||
/**
|
||||
* A fixture with no recorded timing still has to land somewhere. Assume it is
|
||||
* on the expensive side so an unmeasured newcomer cannot quietly overload the
|
||||
* shard it lands in; the next timing refresh corrects it.
|
||||
*/
|
||||
export const UNKNOWN_FIXTURE_SECONDS = 300;
|
||||
|
||||
/**
|
||||
* Mirrors `discoverTestSuites()` in src/regression-harness.ts: a fixture is a
|
||||
* directory holding both `src/index.html` and `meta.json`, found either at
|
||||
* `tests/<name>/` or one level down under `tests/distributed/<name>/`.
|
||||
*
|
||||
* The two implementations are pinned together by a test
|
||||
* (regression-shard-plan.test.ts). If they ever diverge, this script's
|
||||
* unaccounted/stale checks fail the build rather than silently mis-scheduling.
|
||||
*/
|
||||
const isFixtureDir = (dir) =>
|
||||
existsSync(join(dir, "meta.json")) && existsSync(join(dir, "src", "index.html"));
|
||||
|
||||
const childDirNames = (dir) =>
|
||||
readdirSync(dir, { withFileTypes: true })
|
||||
.filter((entry) => entry.isDirectory() && entry.name !== "node_modules")
|
||||
.map((entry) => entry.name)
|
||||
.filter((name) => !name.startsWith("."));
|
||||
|
||||
export function discoverFixtures(testsDir = TESTS_DIR) {
|
||||
return childDirNames(testsDir)
|
||||
.flatMap((name) =>
|
||||
// tests/distributed/<name>/ fixtures are surfaced by bare name, same as
|
||||
// top-level ones, so the harness CLI can target them without a prefix.
|
||||
name === "distributed"
|
||||
? childDirNames(join(testsDir, name)).map((sub) => [sub, join(testsDir, name, sub)])
|
||||
: [[name, join(testsDir, name)]],
|
||||
)
|
||||
.filter(([, dir]) => isFixtureDir(dir))
|
||||
.map(([name]) => name)
|
||||
.sort();
|
||||
}
|
||||
|
||||
/**
|
||||
* Longest-processing-time-first bin packing. Optimal enough for tens of items
|
||||
* and, unlike the hand-packed list, it re-derives from current timings every
|
||||
* run. Returns shards ordered heaviest-first.
|
||||
*/
|
||||
export function packShards(fixtures, timings, shardCount) {
|
||||
const bins = Array.from({ length: shardCount }, () => ({ fixtures: [], seconds: 0 }));
|
||||
const weighted = fixtures
|
||||
.map((name) => ({ name, seconds: timings[name] ?? UNKNOWN_FIXTURE_SECONDS }))
|
||||
.sort((left, right) => right.seconds - left.seconds || left.name.localeCompare(right.name));
|
||||
|
||||
for (const { name, seconds } of weighted) {
|
||||
const lightest = bins.reduce((min, bin) => (bin.seconds < min.seconds ? bin : min), bins[0]);
|
||||
lightest.fixtures.push(name);
|
||||
lightest.seconds += seconds;
|
||||
}
|
||||
|
||||
return bins
|
||||
.filter((bin) => bin.fixtures.length > 0)
|
||||
.sort((left, right) => right.seconds - left.seconds);
|
||||
}
|
||||
|
||||
export function planShards({
|
||||
testsDir = TESTS_DIR,
|
||||
scheduleFile = SCHEDULE_FILE,
|
||||
shardCount,
|
||||
} = {}) {
|
||||
const schedule = JSON.parse(readFileSync(scheduleFile, "utf-8"));
|
||||
const timings = schedule.timings ?? {};
|
||||
const excluded = schedule.excluded ?? {};
|
||||
const resolvedShardCount = shardCount ?? schedule.shardCount ?? DEFAULT_SHARD_COUNT;
|
||||
|
||||
const onDisk = discoverFixtures(testsDir);
|
||||
const onDiskSet = new Set(onDisk);
|
||||
|
||||
// "Exactly one of the two maps" has to be enforced, not just "at least one".
|
||||
// `excluded` wins when a name is in both, so a fixture listed in both would
|
||||
// drop out of CI while every other check here still passed — the precise
|
||||
// failure mode this file exists to prevent.
|
||||
const inBoth = Object.keys(timings).filter((name) => name in excluded);
|
||||
if (inBoth.length > 0) {
|
||||
throw new Error(
|
||||
`Fixtures are both scheduled and excluded: ${inBoth.join(", ")}.\n` +
|
||||
`Remove each from one of "timings" or "excluded" in ${scheduleFile}.`,
|
||||
);
|
||||
}
|
||||
|
||||
// A fixture that is neither timed nor excluded is the drift this script
|
||||
// exists to catch. Fail loudly rather than silently skipping it.
|
||||
const unaccounted = onDisk.filter((name) => !(name in timings) && !(name in excluded));
|
||||
if (unaccounted.length > 0) {
|
||||
throw new Error(
|
||||
`Fixtures are neither scheduled nor excluded: ${unaccounted.join(", ")}.\n` +
|
||||
`Add each to "timings" in ${scheduleFile} to run it in CI, or to "excluded" with a reason.`,
|
||||
);
|
||||
}
|
||||
|
||||
// Catch the opposite drift: entries left behind after a fixture is deleted
|
||||
// or renamed, which would schedule a shard arg that matches nothing.
|
||||
const stale = [...Object.keys(timings), ...Object.keys(excluded)].filter(
|
||||
(name) => !onDiskSet.has(name),
|
||||
);
|
||||
if (stale.length > 0) {
|
||||
throw new Error(
|
||||
`Schedule references fixtures that no longer exist: ${stale.join(", ")}.\n` +
|
||||
`Remove them from ${scheduleFile}.`,
|
||||
);
|
||||
}
|
||||
|
||||
const scheduled = onDisk.filter((name) => !(name in excluded));
|
||||
const bins = packShards(scheduled, timings, resolvedShardCount);
|
||||
|
||||
return {
|
||||
include: bins.map((bin, index) => ({
|
||||
shard: `shard-${index + 1}`,
|
||||
args: bin.fixtures.join(" "),
|
||||
})),
|
||||
// Diagnostics for the workflow log — not consumed by the matrix.
|
||||
plan: bins.map((bin, index) => ({
|
||||
shard: `shard-${index + 1}`,
|
||||
fixtures: bin.fixtures.length,
|
||||
estimatedMinutes: Math.round((bin.seconds / 60) * 10) / 10,
|
||||
})),
|
||||
excludedCount: Object.keys(excluded).length,
|
||||
};
|
||||
}
|
||||
|
||||
function main() {
|
||||
const argv = process.argv.slice(2);
|
||||
const shardFlag = argv.indexOf("--shards");
|
||||
const shardCount = shardFlag === -1 ? undefined : Number(argv[shardFlag + 1]);
|
||||
if (shardCount !== undefined && (!Number.isInteger(shardCount) || shardCount < 1)) {
|
||||
throw new Error("--shards must be a positive integer");
|
||||
}
|
||||
|
||||
const { include, plan, excludedCount } = planShards({ shardCount });
|
||||
|
||||
if (argv.includes("--pretty")) {
|
||||
const worst = Math.max(...plan.map((row) => row.estimatedMinutes));
|
||||
const best = Math.min(...plan.map((row) => row.estimatedMinutes));
|
||||
for (const row of plan) {
|
||||
console.log(`${row.shard}\t${row.fixtures} fixtures\t~${row.estimatedMinutes}m`);
|
||||
}
|
||||
console.log(
|
||||
`\nworst shard ~${worst}m, lightest ~${best}m, spread ~${
|
||||
Math.round((worst - best) * 10) / 10
|
||||
}m`,
|
||||
);
|
||||
console.log(`${excludedCount} fixture(s) explicitly excluded`);
|
||||
return;
|
||||
}
|
||||
|
||||
// Single-line JSON for `echo "matrix=$(...)" >> $GITHUB_OUTPUT`.
|
||||
console.log(JSON.stringify({ include }));
|
||||
}
|
||||
|
||||
if (import.meta.url === `file://${process.argv[1]}`) {
|
||||
main();
|
||||
}
|
||||
@@ -430,7 +430,7 @@ function validateMetadata(meta: unknown): TestMetadata {
|
||||
return m as TestMetadata;
|
||||
}
|
||||
|
||||
function discoverTestSuites(
|
||||
export function discoverTestSuites(
|
||||
testsDir: string,
|
||||
filterNames: string[],
|
||||
excludeTags: string[] = [],
|
||||
|
||||
@@ -0,0 +1,155 @@
|
||||
// Guards the computed regression shard matrix.
|
||||
//
|
||||
// scripts/plan-regression-shards.mjs re-implements fixture discovery in plain
|
||||
// JS so the GitHub workflow can plan shards without building the TypeScript
|
||||
// producer package first. That duplication is the risk these tests exist to
|
||||
// contain: if the planner and the harness ever disagree about what a fixture
|
||||
// is, CI would schedule shard args the harness does not recognise, or quietly
|
||||
// stop running fixtures.
|
||||
|
||||
import { describe, expect, it } from "bun:test";
|
||||
import { mkdtempSync, readFileSync, writeFileSync } from "node:fs";
|
||||
import { tmpdir } from "node:os";
|
||||
import { join } from "node:path";
|
||||
import { discoverTestSuites } from "./regression-harness.js";
|
||||
import {
|
||||
discoverFixtures,
|
||||
packShards,
|
||||
planShards,
|
||||
UNKNOWN_FIXTURE_SECONDS,
|
||||
} from "../scripts/plan-regression-shards.mjs";
|
||||
|
||||
const TESTS_DIR = join(import.meta.dir, "..", "tests");
|
||||
|
||||
function readSchedule(): { timings?: Record<string, number>; excluded?: Record<string, string> } {
|
||||
return JSON.parse(readFileSync(join(TESTS_DIR, "shard-schedule.json"), "utf-8"));
|
||||
}
|
||||
|
||||
describe("shard planner fixture discovery", () => {
|
||||
it("sees every fixture the harness can actually run", () => {
|
||||
// The planner matches on directory layout only; the harness additionally
|
||||
// validates meta.json and drops invalid fixtures with a warning. So the
|
||||
// harness set is a subset. It must never contain something the planner
|
||||
// missed — that would be a fixture CI silently stops scheduling.
|
||||
const harnessIds = discoverTestSuites(TESTS_DIR, []).map((suite) => suite.id);
|
||||
const plannerIds = new Set(discoverFixtures(TESTS_DIR));
|
||||
const invisibleToPlanner = harnessIds.filter((id) => !plannerIds.has(id));
|
||||
expect(invisibleToPlanner).toEqual([]);
|
||||
});
|
||||
|
||||
it("schedules exactly the fixtures the harness can run, minus explicit exclusions", () => {
|
||||
// Subset alone is not enough. The matrix is built from planner discovery,
|
||||
// which only looks at directory layout, while the harness additionally
|
||||
// validates meta.json and drops what fails. So a scheduled fixture whose
|
||||
// meta.json later goes invalid would keep its slot in a shard, be skipped
|
||||
// at run time with a console warning, and leave the shard green — the
|
||||
// fixture stops running and nothing goes red. Pinning set equality is what
|
||||
// makes that show up as a failing test.
|
||||
const excluded = new Set(Object.keys(readSchedule().excluded ?? {}));
|
||||
const harnessRunnable = discoverTestSuites(TESTS_DIR, [])
|
||||
.map((suite) => suite.id)
|
||||
.filter((id) => !excluded.has(id))
|
||||
.sort();
|
||||
const scheduled = planShards()
|
||||
.include.flatMap((row) => row.args.split(" "))
|
||||
.sort();
|
||||
expect(scheduled).toEqual(harnessRunnable);
|
||||
});
|
||||
|
||||
it("rejects a fixture listed in both timings and excluded", () => {
|
||||
// `excluded` wins on conflict, so without this the fixture would quietly
|
||||
// stop running while every other invariant still passed.
|
||||
const schedule = readSchedule();
|
||||
const victim = Object.keys(schedule.timings ?? {})[0] as string;
|
||||
const tainted = join(mkdtempSync(join(tmpdir(), "hf-shard-schedule-")), "shard-schedule.json");
|
||||
writeFileSync(
|
||||
tainted,
|
||||
JSON.stringify({
|
||||
...schedule,
|
||||
excluded: { ...schedule.excluded, [victim]: "duplicate entry that must be rejected" },
|
||||
}),
|
||||
);
|
||||
expect(() => planShards({ scheduleFile: tainted })).toThrow(/both scheduled and excluded/);
|
||||
});
|
||||
|
||||
it("gives every excluded fixture a written reason", () => {
|
||||
// Exclusions are how a fixture legitimately stays out of CI, so the bar
|
||||
// is that someone had to type why. This is what stops the excluded list
|
||||
// from becoming the silent dumping ground the old YAML matrix was.
|
||||
const excluded = readSchedule().excluded ?? {};
|
||||
for (const [fixture, reason] of Object.entries(excluded)) {
|
||||
expect(typeof reason, `${fixture} needs a reason`).toBe("string");
|
||||
expect((reason as string).length, `${fixture} needs a real reason`).toBeGreaterThan(20);
|
||||
}
|
||||
});
|
||||
|
||||
it("finds fixtures nested under tests/distributed/", () => {
|
||||
const discovered = discoverFixtures(TESTS_DIR);
|
||||
// These live at tests/distributed/<name>/ rather than tests/<name>/, and
|
||||
// an earlier hand-written matrix scheduled them by bare name.
|
||||
for (const nested of ["mp4-h264-sdr", "webm-vp9", "png-sequence"]) {
|
||||
expect(discovered).toContain(nested);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
describe("packShards()", () => {
|
||||
it("spreads work so the heaviest shard is no worse than longest-item-plus-average", () => {
|
||||
const timings = { a: 600, b: 300, c: 300, d: 120, e: 120, f: 60 };
|
||||
const shards = packShards(Object.keys(timings), timings, 3);
|
||||
const totals = shards.map((shard) =>
|
||||
shard.fixtures.reduce((sum, name) => sum + timings[name], 0),
|
||||
);
|
||||
// LPT's bound: worst bin <= optimal * 4/3. Optimal here is 500s.
|
||||
expect(Math.max(...totals)).toBeLessThanOrEqual(Math.ceil(500 * (4 / 3)));
|
||||
expect(shards.flatMap((shard) => shard.fixtures).sort()).toEqual(Object.keys(timings).sort());
|
||||
});
|
||||
|
||||
it("keeps a single indivisible fixture as the floor", () => {
|
||||
// No amount of sharding beats the slowest single fixture. This is the
|
||||
// reason shard count alone cannot drive wall-clock below the long pole.
|
||||
const timings = { huge: 1500, small: 10 };
|
||||
const shards = packShards(Object.keys(timings), timings, 8);
|
||||
expect(Math.max(...shards.map((shard) => shard.seconds))).toBe(1500);
|
||||
});
|
||||
|
||||
it("assumes untimed fixtures are expensive rather than free", () => {
|
||||
const shards = packShards(["known", "brand-new"], { known: 10 }, 2);
|
||||
const newShard = shards.find((shard) => shard.fixtures.includes("brand-new"));
|
||||
expect(newShard?.seconds).toBe(UNKNOWN_FIXTURE_SECONDS);
|
||||
});
|
||||
|
||||
it("emits no empty shards when fixtures are fewer than the shard count", () => {
|
||||
const shards = packShards(["only"], { only: 5 }, 8);
|
||||
expect(shards).toHaveLength(1);
|
||||
});
|
||||
});
|
||||
|
||||
describe("planShards()", () => {
|
||||
it("schedules or explicitly excludes every fixture on disk", () => {
|
||||
// The real schedule file must stay exhaustive; this is the check that
|
||||
// turns "someone added a fixture and forgot the matrix" into a red build.
|
||||
expect(() => planShards()).not.toThrow();
|
||||
});
|
||||
|
||||
it("produces a matrix the workflow can consume", () => {
|
||||
const { include } = planShards();
|
||||
expect(include.length).toBeGreaterThan(0);
|
||||
for (const row of include) {
|
||||
expect(row.shard).toMatch(/^shard-\d+$/);
|
||||
expect(row.args.length).toBeGreaterThan(0);
|
||||
}
|
||||
// Every scheduled fixture appears exactly once across all shards.
|
||||
const scheduled = include.flatMap((row) => row.args.split(" "));
|
||||
expect(new Set(scheduled).size).toBe(scheduled.length);
|
||||
});
|
||||
|
||||
it("runs every fixture that is not explicitly excluded", () => {
|
||||
const { include } = planShards();
|
||||
const scheduled = new Set(include.flatMap((row) => row.args.split(" ")));
|
||||
const excluded = new Set(Object.keys(readSchedule().excluded ?? {}));
|
||||
for (const fixture of discoverFixtures(TESTS_DIR)) {
|
||||
expect(scheduled.has(fixture) || excluded.has(fixture)).toBe(true);
|
||||
}
|
||||
});
|
||||
});
|
||||
@@ -1,3 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:29abd7d2f00d654654348b10cdef4c9c6f91e52451c1a8f007605bef7ab12017
|
||||
size 61986
|
||||
oid sha256:84699f00ac13eca17781789e1f114bd549ce631d43502aa8ce279ca77a63886b
|
||||
size 60235
|
||||
|
||||
@@ -0,0 +1,92 @@
|
||||
{
|
||||
"$comment": [
|
||||
"Drives the regression workflow's shard matrix via scripts/plan-regression-shards.mjs.",
|
||||
"Every fixture on disk must appear in exactly one of 'timings' or 'excluded', or CI fails.",
|
||||
"timings: per-fixture wall-clock seconds, used only for bin-packing \u2014 approximate is fine.",
|
||||
" Measured on CI run 30223148821 (green, post single-pass PSNR). A newly added fixture can",
|
||||
" carry a conservative estimate until the next refresh; over-estimating only costs balance.",
|
||||
" Refresh from a green main run when shards drift apart; no test asserts these values.",
|
||||
"excluded: fixtures deliberately not run, each mapped to the reason why."
|
||||
],
|
||||
"shardCount": 8,
|
||||
"timings": {
|
||||
"animejs-adapter": 17,
|
||||
"audio-mux-parity": 32,
|
||||
"chat": 54,
|
||||
"css-spinner-render-compat": 15,
|
||||
"font-variant-numeric": 9,
|
||||
"gsap-letters-render-compat": 13,
|
||||
"hdr-hlg-regression": 158,
|
||||
"hdr-regression": 279,
|
||||
"iframe-render-compat": 24,
|
||||
"many-cuts": 23,
|
||||
"missing-host-comp-id": 8,
|
||||
"mov-prores": 5,
|
||||
"mp4-h264-sdr": 4,
|
||||
"mp4-h265-sdr": 4,
|
||||
"overlay-montage-prod": 512,
|
||||
"parallel-capture-regression": 86,
|
||||
"png-sequence": 3,
|
||||
"portrait-edge-bleed": 25,
|
||||
"raf-ball-render-compat": 22,
|
||||
"render-symlinked-assets": 6,
|
||||
"static-volume-future-set": 120,
|
||||
"style-1-prod": 156,
|
||||
"style-10-prod": 232,
|
||||
"style-11-prod": 324,
|
||||
"style-12-prod": 247,
|
||||
"style-13-prod": 898,
|
||||
"style-15-prod": 1413,
|
||||
"style-16-prod": 401,
|
||||
"style-17-prod": 224,
|
||||
"style-18-prod": 394,
|
||||
"style-2-prod": 190,
|
||||
"style-3-prod": 191,
|
||||
"style-4-prod": 203,
|
||||
"style-5-prod": 247,
|
||||
"style-6-prod": 301,
|
||||
"style-7-prod": 325,
|
||||
"style-8-prod": 178,
|
||||
"style-9-prod": 216,
|
||||
"sub-comp-id-selector": 10,
|
||||
"sub-comp-t0": 18,
|
||||
"sub-composition-video": 1346,
|
||||
"typegpu-adapter": 15,
|
||||
"variables-prod": 8,
|
||||
"vfr-screen-recording": 7,
|
||||
"vignelli-stacking": 96,
|
||||
"webm-transparency": 60,
|
||||
"webm-vp9": 4
|
||||
},
|
||||
"excluded": {
|
||||
"chrome-screenshot-bottom-edge": "Was absent from the hand-written shard matrix, so it has never run in CI and its cost and pass state are unknown. Triage and either schedule it or record a real reason here.",
|
||||
"css-import-scoping": "Rejected by the harness at load time (meta.json: maxAudioLagWindows must be >= 1), so it has never run despite looking scheduled-able. Fix the metadata, then schedule it.",
|
||||
"css-var-fonts": "Rejected by the harness at load time (meta.json: minAudioCorrelation must be between 0 and 1), so it has never run despite looking scheduled-able. Fix the metadata, then schedule it.",
|
||||
"d3-adapter": "Was absent from the hand-written shard matrix, so it has never run in CI and its cost and pass state are unknown. Triage and either schedule it or record a real reason here.",
|
||||
"dogs-captions": "Was absent from the hand-written shard matrix, so it has never run in CI and its cost and pass state are unknown. Triage and either schedule it or record a real reason here.",
|
||||
"escape-hatch-fatal-fallback": "Was absent from the hand-written shard matrix, so it has never run in CI and its cost and pass state are unknown. Triage and either schedule it or record a real reason here.",
|
||||
"fast-capture-gsap": "Was absent from the hand-written shard matrix, so it has never run in CI and its cost and pass state are unknown. Triage and either schedule it or record a real reason here.",
|
||||
"google-maps-adapter": "Was absent from the hand-written shard matrix, so it has never run in CI and its cost and pass state are unknown. Triage and either schedule it or record a real reason here.",
|
||||
"gsap-call-render-seek": "Was absent from the hand-written shard matrix, so it has never run in CI and its cost and pass state are unknown. Triage and either schedule it or record a real reason here.",
|
||||
"heygen-promo-preview-assets": "Was absent from the hand-written shard matrix, so it has never run in CI and its cost and pass state are unknown. Triage and either schedule it or record a real reason here.",
|
||||
"hf2550-video-subcomposition-ghost": "Was absent from the hand-written shard matrix, so it has never run in CI and its cost and pass state are unknown. Triage and either schedule it or record a real reason here.",
|
||||
"leaflet-adapter": "Was absent from the hand-written shard matrix, so it has never run in CI and its cost and pass state are unknown. Triage and either schedule it or record a real reason here.",
|
||||
"mapbox-adapter": "Was absent from the hand-written shard matrix, so it has never run in CI and its cost and pass state are unknown. Triage and either schedule it or record a real reason here.",
|
||||
"maplibre-adapter": "Was absent from the hand-written shard matrix, so it has never run in CI and its cost and pass state are unknown. Triage and either schedule it or record a real reason here.",
|
||||
"nested-subcomp-depth-3": "Was absent from the hand-written shard matrix, so it has never run in CI and its cost and pass state are unknown. Triage and either schedule it or record a real reason here.",
|
||||
"page-side-shader-compositor-render-compat": "Was absent from the hand-written shard matrix, so it has never run in CI and its cost and pass state are unknown. Triage and either schedule it or record a real reason here.",
|
||||
"pip-video-late-host": "Was absent from the hand-written shard matrix, so it has never run in CI and its cost and pass state are unknown. Triage and either schedule it or record a real reason here.",
|
||||
"render-video-overlay-stretch": "Was absent from the hand-written shard matrix, so it has never run in CI and its cost and pass state are unknown. Triage and either schedule it or record a real reason here.",
|
||||
"software-beginframe-yoyo-compositor": "Was absent from the hand-written shard matrix, so it has never run in CI and its cost and pass state are unknown. Triage and either schedule it or record a real reason here.",
|
||||
"spanish-empire-cdn-inline": "Was absent from the hand-written shard matrix, so it has never run in CI and its cost and pass state are unknown. Triage and either schedule it or record a real reason here.",
|
||||
"sub-comp-class-selector": "Was absent from the hand-written shard matrix, so it has never run in CI and its cost and pass state are unknown. Triage and either schedule it or record a real reason here.",
|
||||
"sub-comp-height-percent": "Was absent from the hand-written shard matrix, so it has never run in CI and its cost and pass state are unknown. Triage and either schedule it or record a real reason here.",
|
||||
"three-boundary": "Was absent from the hand-written shard matrix, so it has never run in CI and its cost and pass state are unknown. Triage and either schedule it or record a real reason here.",
|
||||
"three-boundary-deferred": "Was absent from the hand-written shard matrix, so it has never run in CI and its cost and pass state are unknown. Triage and either schedule it or record a real reason here.",
|
||||
"timed-descendant-visibility": "Was absent from the hand-written shard matrix, so it has never run in CI and its cost and pass state are unknown. Triage and either schedule it or record a real reason here.",
|
||||
"transparency-regression": "Not a harness fixture by design \u2014 its meta.json says it is exercised by `tsx src/transparency-test.ts`, which asserts a real alpha channel rather than comparing against a golden MP4.",
|
||||
"video-hfid-no-id": "Was absent from the hand-written shard matrix, so it has never run in CI and its cost and pass state are unknown. Triage and either schedule it or record a real reason here.",
|
||||
"webgl-video-texture-render-compat": "Was absent from the hand-written shard matrix, so it has never run in CI and its cost and pass state are unknown. Triage and either schedule it or record a real reason here.",
|
||||
"wysiwyg-subcomp-css": "Rejected by the harness at load time (meta.json: maxAudioLagWindows must be >= 1), so it has never run despite looking scheduled-able. Fix the metadata, then schedule it."
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user