From a8ab6de82f47af107ec49273774cacd246a9fb01 Mon Sep 17 00:00:00 2001 From: suyx Date: Tue, 4 Aug 2026 17:53:01 +0800 Subject: [PATCH] test: establish WP5 continuous lineage gate --- package.json | 3 +- scripts/run-wp5-lineage-validation.mjs | 59 +++++++++++++++++++ tests/package/wp5-lineage-validation.test.mjs | 10 ++++ 3 files changed, 71 insertions(+), 1 deletion(-) create mode 100644 scripts/run-wp5-lineage-validation.mjs create mode 100644 tests/package/wp5-lineage-validation.test.mjs diff --git a/package.json b/package.json index cc20eff..95067ca 100644 --- a/package.json +++ b/package.json @@ -96,7 +96,8 @@ "test:wp5-04": "node scripts/run-wp5-04-validation.mjs", "test:wp5-04:red": "node scripts/run-wp5-04-validation.mjs --phase red", "test:wp5-05": "node scripts/run-wp5-05-validation.mjs", - "test:wp5-05:red": "node scripts/run-wp5-05-validation.mjs --phase red" + "test:wp5-05:red": "node scripts/run-wp5-05-validation.mjs --phase red", + "test:wp5-lineage": "node scripts/run-wp5-lineage-validation.mjs" }, "devDependencies": { "@playwright/test": "1.62.0", diff --git a/scripts/run-wp5-lineage-validation.mjs b/scripts/run-wp5-lineage-validation.mjs new file mode 100644 index 0000000..3a429ca --- /dev/null +++ b/scripts/run-wp5-lineage-validation.mjs @@ -0,0 +1,59 @@ +import { execFileSync } from 'node:child_process'; +import fs from 'node:fs'; +import path from 'node:path'; + +const tasks = [ + ['TASK-WP5-03', 'origin/codex/wp5-03'], + ['TASK-WP5-04', 'origin/codex/wp5-04'], + ['TASK-WP5-05', 'origin/codex/wp5-05'], + ['TASK-WP5-06', 'origin/codex/wp5-06'], + ['TASK-WP5-07', 'origin/codex/wp5-07'], +]; + +function sha(ref) { + return execFileSync('git', ['rev-parse', ref], { encoding: 'utf8' }).trim(); +} + +function isAncestor(ref) { + try { + execFileSync('git', ['merge-base', '--is-ancestor', ref, 'HEAD'], { stdio: 'ignore' }); + return true; + } catch { + return false; + } +} + +function run(command, args) { + try { + const executable = process.platform === 'win32' && command === 'pnpm' ? 'pnpm.cmd' : command; + execFileSync(executable, args, { stdio: 'pipe', encoding: 'utf8', shell: process.platform === 'win32' }); + return { status: 'passed' }; + } catch (error) { + return { status: 'failed', exit_code: error.status ?? 1 }; + } +} + +const checks = tasks.map(([task, ref]) => ({ task, ref, sha: sha(ref), ancestor: isAncestor(ref) })); +const regression = { + 'TASK-WP5-06': run('pnpm', ['exec', 'vitest', 'run', 'tests/integration/wp5-06-sticker-cleanup.test.ts']), + 'TASK-WP5-07': run('pnpm', ['exec', 'vitest', 'run', 'tests/api/wp5-07-preview-grants.test.ts']), +}; +const status = checks.every((check) => check.ancestor) + && Object.values(regression).every((check) => check.status === 'passed') + ? 'ready_for_wp4_07' + : 'externally_blocked'; + +const output = { + schema_version: '1.0', + task: 'TASK-WP5-LINEAGE', + status, + checks, + regression, + note: 'This gate proves ancestry and targeted regressions only; it does not replace per-task UI/resource evidence.', +}; +const runId = `wp5-lineage-${new Date().toISOString().replace(/[^0-9]/g, '')}`; +const outputDirectory = path.join('artifacts', 'tdd', runId); +fs.mkdirSync(outputDirectory, { recursive: true }); +fs.writeFileSync(path.join(outputDirectory, 'lineage.json'), `${JSON.stringify(output, null, 2)}\n`, 'utf8'); +console.log(JSON.stringify(output)); +process.exitCode = status === 'ready_for_wp4_07' ? 0 : 3; diff --git a/tests/package/wp5-lineage-validation.test.mjs b/tests/package/wp5-lineage-validation.test.mjs new file mode 100644 index 0000000..22886a3 --- /dev/null +++ b/tests/package/wp5-lineage-validation.test.mjs @@ -0,0 +1,10 @@ +import test from 'node:test'; +import assert from 'node:assert/strict'; +import { execFileSync } from 'node:child_process'; + +test('WP5 lineage branch contains every required remote task tip', () => { + for (const task of ['wp5-03', 'wp5-04', 'wp5-05', 'wp5-06', 'wp5-07']) { + execFileSync('git', ['merge-base', '--is-ancestor', `origin/codex/${task}`, 'HEAD']); + } + assert.ok(true); +});