test: validate WP5 task lineage for WP4-07 gate
Dada P0-A isolated Windows CI / validate-and-package (push) Failing after 3m23s
Dada P0-A isolated Windows CI / validate-and-package (push) Failing after 3m23s
This commit is contained in:
@@ -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";
|
||||
|
||||
export const WP4_07_REQUIRED_WP5_BRANCHES = Object.freeze(
|
||||
Array.from({ length: 7 }, (_, index) => `codex/wp5-0${index + 1}`),
|
||||
export const WP4_07_REQUIRED_WP5_TASKS = Object.freeze(
|
||||
Array.from({ length: 7 }, (_, index) => `TASK-WP5-0${index + 1}`),
|
||||
);
|
||||
export const WP4_07_FINAL_WP5_BRANCH = "codex/wp5-07";
|
||||
|
||||
export function validateWp407FrozenInputs() {
|
||||
const mismatches = [];
|
||||
@@ -26,6 +27,28 @@ export function validateWp407FrozenInputs() {
|
||||
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() {
|
||||
const result = spawnSync("git", ["ls-remote", "--heads", "origin", "codex/wp5-*"], { encoding: "utf8", timeout: 30_000 });
|
||||
if ((result.status ?? 1) !== 0) {
|
||||
@@ -37,12 +60,32 @@ export function readWp5RemoteGate() {
|
||||
const [sha, reference] = line.split(/\s+/);
|
||||
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 {
|
||||
complete: missing_branches.length === 0,
|
||||
heads,
|
||||
missing_branches,
|
||||
required_branches: WP4_07_REQUIRED_WP5_BRANCHES,
|
||||
...inspectWp5TaskLineage(heads, commits),
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -26,8 +26,12 @@ try {
|
||||
code: "WP4_07_WP5_GATE_INCOMPLETE",
|
||||
fixture_sha256: fixture.fixture_sha256,
|
||||
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_task_shas: gate.task_shas,
|
||||
required_final_branch: gate.required_final_branch,
|
||||
status: "red",
|
||||
}, null, 2));
|
||||
process.exit(1);
|
||||
|
||||
@@ -68,7 +68,7 @@ function runDirect(name, executable, args, expected) {
|
||||
|
||||
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("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"),
|
||||
@@ -110,8 +110,11 @@ const observation = {
|
||||
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.",
|
||||
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_task_shas: remoteGate.task_shas,
|
||||
placeholder_policy: "red_contract resources are harness smoke inputs only and are rejected by the Green gate",
|
||||
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"]);
|
||||
});
|
||||
Reference in New Issue
Block a user