test: validate WP5 task lineage for WP4-07 gate
Dada P0-A isolated Windows CI / validate-and-package (push) Failing after 3m23s

This commit is contained in:
suyx
2026-08-03 19:26:39 +08:00
parent 40ecf0414a
commit dc83408ec2
4 changed files with 90 additions and 9 deletions
+49 -6
View File
@@ -4,9 +4,10 @@ import { existsSync, readFileSync } from "node:fs";
import { WP4_07_RED_RESOURCE_VERSION, WP4_07_SOURCE_HASHES, assertWp407Fixture } from "../../tests/visual-performance/wp4-07-fixture.mjs"; import { WP4_07_RED_RESOURCE_VERSION, WP4_07_SOURCE_HASHES, assertWp407Fixture } from "../../tests/visual-performance/wp4-07-fixture.mjs";
export const WP4_07_REQUIRED_WP5_BRANCHES = Object.freeze( export const WP4_07_REQUIRED_WP5_TASKS = Object.freeze(
Array.from({ length: 7 }, (_, index) => `codex/wp5-0${index + 1}`), Array.from({ length: 7 }, (_, index) => `TASK-WP5-0${index + 1}`),
); );
export const WP4_07_FINAL_WP5_BRANCH = "codex/wp5-07";
export function validateWp407FrozenInputs() { export function validateWp407FrozenInputs() {
const mismatches = []; const mismatches = [];
@@ -26,6 +27,28 @@ export function validateWp407FrozenInputs() {
return assertWp407Fixture(); return assertWp407Fixture();
} }
export function inspectWp5TaskLineage(heads, commits) {
const candidate_branch = [...WP4_07_REQUIRED_WP5_TASKS]
.reverse()
.map((taskId) => taskId.replace("TASK-WP5-", "codex/wp5-"))
.find((branch) => /^[0-9a-f]{40}$/.test(heads[branch] ?? "")) ?? null;
const task_shas = Object.fromEntries(WP4_07_REQUIRED_WP5_TASKS.flatMap((taskId) => {
const commit = commits.find((entry) => entry.subject.includes(taskId));
return commit && /^[0-9a-f]{40}$/.test(commit.sha) ? [[taskId, commit.sha]] : [];
}));
const missing_tasks = WP4_07_REQUIRED_WP5_TASKS.filter((taskId) => !task_shas[taskId]);
return {
candidate_baseline_branch: candidate_branch,
candidate_baseline_sha: candidate_branch ? heads[candidate_branch] : null,
complete: candidate_branch === WP4_07_FINAL_WP5_BRANCH && missing_tasks.length === 0,
final_branch_sha: heads[WP4_07_FINAL_WP5_BRANCH] ?? null,
missing_tasks,
required_final_branch: WP4_07_FINAL_WP5_BRANCH,
required_tasks: WP4_07_REQUIRED_WP5_TASKS,
task_shas,
};
}
export function readWp5RemoteGate() { export function readWp5RemoteGate() {
const result = spawnSync("git", ["ls-remote", "--heads", "origin", "codex/wp5-*"], { encoding: "utf8", timeout: 30_000 }); const result = spawnSync("git", ["ls-remote", "--heads", "origin", "codex/wp5-*"], { encoding: "utf8", timeout: 30_000 });
if ((result.status ?? 1) !== 0) { if ((result.status ?? 1) !== 0) {
@@ -37,12 +60,32 @@ export function readWp5RemoteGate() {
const [sha, reference] = line.split(/\s+/); const [sha, reference] = line.split(/\s+/);
return [reference.replace("refs/heads/", ""), sha]; return [reference.replace("refs/heads/", ""), sha];
})); }));
const missing_branches = WP4_07_REQUIRED_WP5_BRANCHES.filter((branch) => !/^[0-9a-f]{40}$/.test(heads[branch] ?? "")); const candidateBranch = [...WP4_07_REQUIRED_WP5_TASKS]
.reverse()
.map((taskId) => taskId.replace("TASK-WP5-", "codex/wp5-"))
.find((branch) => /^[0-9a-f]{40}$/.test(heads[branch] ?? ""));
let commits = [];
if (candidateBranch) {
const fetch = spawnSync("git", ["fetch", "--quiet", "--no-tags", "origin", `refs/heads/${candidateBranch}`], { encoding: "utf8", timeout: 60_000 });
if ((fetch.status ?? 1) !== 0) {
const error = new Error("WP4_07_GITEA_BASELINE_FETCH_FAILED");
error.details = { branch: candidateBranch, exit_code: fetch.status ?? 1 };
throw error;
}
const log = spawnSync("git", ["log", "--format=%H%x09%s", heads[candidateBranch]], { encoding: "utf8", timeout: 30_000 });
if ((log.status ?? 1) !== 0) {
const error = new Error("WP4_07_GITEA_BASELINE_HISTORY_UNREADABLE");
error.details = { branch: candidateBranch, exit_code: log.status ?? 1 };
throw error;
}
commits = log.stdout.trim().split(/\r?\n/).filter(Boolean).map((line) => {
const separator = line.indexOf("\t");
return { sha: line.slice(0, separator), subject: line.slice(separator + 1) };
});
}
return { return {
complete: missing_branches.length === 0,
heads, heads,
missing_branches, ...inspectWp5TaskLineage(heads, commits),
required_branches: WP4_07_REQUIRED_WP5_BRANCHES,
}; };
} }
+5 -1
View File
@@ -26,8 +26,12 @@ try {
code: "WP4_07_WP5_GATE_INCOMPLETE", code: "WP4_07_WP5_GATE_INCOMPLETE",
fixture_sha256: fixture.fixture_sha256, fixture_sha256: fixture.fixture_sha256,
layer, layer,
missing_remote_branches: gate.missing_branches, candidate_baseline_branch: gate.candidate_baseline_branch,
candidate_baseline_sha: gate.candidate_baseline_sha,
missing_remote_tasks: gate.missing_tasks,
observed_remote_heads: gate.heads, observed_remote_heads: gate.heads,
observed_task_shas: gate.task_shas,
required_final_branch: gate.required_final_branch,
status: "red", status: "red",
}, null, 2)); }, null, 2));
process.exit(1); process.exit(1);
+5 -2
View File
@@ -68,7 +68,7 @@ function runDirect(name, executable, args, expected) {
const commands = phase === "red" const commands = phase === "red"
? [ ? [
run("fixture-contract", "node --test tests/visual-performance/wp4-07-fixture.test.mjs", "zero"), run("fixture-contract", "node --test tests/visual-performance/wp4-07-fixture.test.mjs tests/visual-performance/wp4-07-gate.test.mjs", "zero"),
run("build-browser-dependencies", "pnpm --filter @dada/shared-contracts build && pnpm --filter @dada/static-sticker-catalog build && pnpm --filter @dada/template-registry build && pnpm --filter @dada/asset-renderer build", "zero"), run("build-browser-dependencies", "pnpm --filter @dada/shared-contracts build && pnpm --filter @dada/static-sticker-catalog build && pnpm --filter @dada/template-registry build && pnpm --filter @dada/asset-renderer build", "zero"),
run("browser-harness", "pnpm exec playwright test --config playwright.wp4-07.config.ts", "zero"), run("browser-harness", "pnpm exec playwright test --config playwright.wp4-07.config.ts", "zero"),
runDirect("visual-diff", process.execPath, ["scripts/compare-wp4-07-screenshots.mjs", "--evidence", visualDirectory, "--phase", "red"], "zero"), runDirect("visual-diff", process.execPath, ["scripts/compare-wp4-07-screenshots.mjs", "--evidence", visualDirectory, "--phase", "red"], "zero"),
@@ -110,8 +110,11 @@ const observation = {
eligible_for_green: false, eligible_for_green: false,
expected_failure: "Final WP-5 task SHAs, immutable release inputs, real fonts, and the final renderer are unavailable, so Chrome/Edge visual and performance results cannot become Green.", expected_failure: "Final WP-5 task SHAs, immutable release inputs, real fonts, and the final renderer are unavailable, so Chrome/Edge visual and performance results cannot become Green.",
fixture_sha256: fixture.fixture_sha256, fixture_sha256: fixture.fixture_sha256,
missing_remote_branches: remoteGate.missing_branches, candidate_baseline_branch: remoteGate.candidate_baseline_branch,
candidate_baseline_sha: remoteGate.candidate_baseline_sha,
missing_remote_tasks: remoteGate.missing_tasks,
observed_remote_heads: remoteGate.heads, observed_remote_heads: remoteGate.heads,
observed_task_shas: remoteGate.task_shas,
placeholder_policy: "red_contract resources are harness smoke inputs only and are rejected by the Green gate", placeholder_policy: "red_contract resources are harness smoke inputs only and are rejected by the Green gate",
status: redConfirmed ? "red_confirmed" : "failed", status: redConfirmed ? "red_confirmed" : "failed",
}; };
@@ -0,0 +1,31 @@
import assert from "node:assert/strict";
import test from "node:test";
import { inspectWp5TaskLineage } from "../../scripts/lib/wp4-07-gate.mjs";
const sha = (digit) => digit.repeat(40);
const commit = (index) => ({ sha: sha(String(index)), subject: `feat: complete TASK-WP5-0${index}` });
test("WP5 lineage recognizes tasks already merged into later remote heads", () => {
const gate = inspectWp5TaskLineage({
"codex/wp5-03": sha("3"),
"codex/wp5-04": sha("4"),
}, [commit(4), commit(3), commit(2), commit(1)]);
assert.equal(gate.complete, false);
assert.equal(gate.candidate_baseline_branch, "codex/wp5-04");
assert.deepEqual(gate.missing_tasks, ["TASK-WP5-05", "TASK-WP5-06", "TASK-WP5-07"]);
assert.equal(gate.task_shas["TASK-WP5-01"], sha("1"));
assert.equal(gate.task_shas["TASK-WP5-02"], sha("2"));
});
test("WP5 lineage becomes complete only at a wp5-07 head containing every task", () => {
const heads = { "codex/wp5-07": sha("7") };
const complete = inspectWp5TaskLineage(heads, Array.from({ length: 7 }, (_, index) => commit(7 - index)));
const incomplete = inspectWp5TaskLineage(heads, [commit(7), commit(6), commit(5), commit(4), commit(3), commit(2)]);
assert.equal(complete.complete, true);
assert.deepEqual(complete.missing_tasks, []);
assert.equal(incomplete.complete, false);
assert.deepEqual(incomplete.missing_tasks, ["TASK-WP5-01"]);
});