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";
|
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,
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
@@ -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"]);
|
||||||
|
});
|
||||||
Reference in New Issue
Block a user