From f1f9a1cf8f4dc5b8db533696ce7e4591085c124e Mon Sep 17 00:00:00 2001 From: john Date: Sun, 26 Jul 2026 15:20:14 +0800 Subject: [PATCH] fix: ignore generated gate artifacts in cleanliness check --- release-gate/runner.mjs | 21 +++++++++++++++++++-- release-gate/runner.test.mjs | 16 +++++++++++++++- 2 files changed, 34 insertions(+), 3 deletions(-) diff --git a/release-gate/runner.mjs b/release-gate/runner.mjs index 130a72c..355c69b 100644 --- a/release-gate/runner.mjs +++ b/release-gate/runner.mjs @@ -96,6 +96,18 @@ export async function runSuitesWithConcurrency(items, concurrency, worker) { return results; } +export function filterGeneratedWorktreeStatus(status) { + return status + .split('\n') + .filter((line) => { + if (!line) return false; + const path = line.slice(3).replace(/^"|"$/g, ''); + return !path.startsWith('.release-gate/') + && !path.startsWith('.runtime/portal/public/plaza-covers/'); + }) + .join('\n'); +} + export async function runCommand(command, args, { cwd = ROOT, timeoutMs = 15 * 60 * 1000, @@ -162,6 +174,7 @@ async function applyRepositoryChecks(results, artifactPath) { const byId = new Map(results.map((result) => [result.id, result])); const branch = await git('branch', '--show-current'); const status = await git('status', '--porcelain=v1', '--untracked-files=all'); + const relevantStatus = filterGeneratedWorktreeStatus(status); const originMain = await runCommand( 'git', ['rev-parse', '--verify', 'origin/main'], @@ -170,9 +183,13 @@ async function applyRepositoryChecks(results, artifactPath) { const remoteSha = originMain.code === 0 ? originMain.stdout.trim() : null; const headSha = await git('rev-parse', 'HEAD'); const rel01 = byId.get('REL-01'); - rel01.status = branch === 'main' && status === '' ? 'passed' : 'failed'; + rel01.status = branch === 'main' && relevantStatus === '' ? 'passed' : 'failed'; rel01.reason = rel01.status === 'passed' ? null : 'candidate_must_be_clean_main'; - rel01.evidence.push(`branch=${branch || 'detached'}`, `worktree_clean=${status === ''}`); + rel01.evidence.push( + `branch=${branch || 'detached'}`, + `worktree_clean=${relevantStatus === ''}`, + `ignored_generated_paths=${status !== relevantStatus}`, + ); const ciStatus = String(process.env.MEMIND_RELEASE_CI_STATUS ?? '').trim().toLowerCase(); const rel02 = byId.get('REL-02'); diff --git a/release-gate/runner.test.mjs b/release-gate/runner.test.mjs index 50bdbe3..6bd5009 100644 --- a/release-gate/runner.test.mjs +++ b/release-gate/runner.test.mjs @@ -1,7 +1,12 @@ import assert from 'node:assert/strict'; import test from 'node:test'; -import { parseRunnerArgs, runSuitesWithConcurrency, runWithConcurrency } from './runner.mjs'; +import { + filterGeneratedWorktreeStatus, + parseRunnerArgs, + runSuitesWithConcurrency, + runWithConcurrency, +} from './runner.mjs'; test('runner accepts a bounded suite concurrency and rejects unsafe values', () => { assert.equal( @@ -52,3 +57,12 @@ test('exclusive suites finish before parallel suites start', async () => { assert.ok(events.indexOf('end:mutating') < events.indexOf('start:parallel-a')); assert.ok(events.indexOf('end:mutating') < events.indexOf('start:parallel-b')); }); + +test('worktree cleanliness ignores only release-gate and generated plaza cover paths', () => { + const status = [ + '?? .release-gate/ea07f6f/report.json', + ' D .runtime/portal/public/plaza-covers/cover.jpg', + ' M release-gate/runner.mjs', + ].join('\n'); + assert.equal(filterGeneratedWorktreeStatus(status), ' M release-gate/runner.mjs'); +});