Trim verification snapshot scope

This commit is contained in:
ejclaw
2026-04-08 03:49:54 +09:00
parent 6907a71483
commit c0f7f2c12d
4 changed files with 114 additions and 8 deletions

View File

@@ -2,6 +2,10 @@ export declare const VERIFICATION_SNAPSHOT_EXCLUDE_NAMES: ReadonlySet<string>;
export declare function isVerificationSnapshotExcludedName( export declare function isVerificationSnapshotExcludedName(
name: string, name: string,
): boolean; ): boolean;
export declare function isVerificationSnapshotExcludedPath(
repoDir: string,
currentPath: string,
): boolean;
export declare function computeVerificationSnapshotId(repoDir: string): string; export declare function computeVerificationSnapshotId(repoDir: string): string;
export declare function resolveVerificationResponsesDir( export declare function resolveVerificationResponsesDir(
hostIpcDir: string, hostIpcDir: string,

View File

@@ -3,15 +3,46 @@ import fs from 'fs';
import path from 'path'; import path from 'path';
export const VERIFICATION_SNAPSHOT_EXCLUDE_NAMES = new Set([ export const VERIFICATION_SNAPSHOT_EXCLUDE_NAMES = new Set([
'.git',
'node_modules', 'node_modules',
'.env',
]); ]);
const VERIFICATION_SNAPSHOT_ROOT_EXCLUDE_NAMES = new Set([
'.git',
'.env',
'data',
'logs',
'cache',
'.ejclaw-reviewer-runtime',
]);
const VERIFICATION_SNAPSHOT_ROOT_EXCLUDE_PREFIXES = [
'store.local-backup-',
];
export function isVerificationSnapshotExcludedName(name) { export function isVerificationSnapshotExcludedName(name) {
return VERIFICATION_SNAPSHOT_EXCLUDE_NAMES.has(name); return VERIFICATION_SNAPSHOT_EXCLUDE_NAMES.has(name);
} }
export function isVerificationSnapshotExcludedPath(repoDir, currentPath) {
const name = path.basename(currentPath);
if (isVerificationSnapshotExcludedName(name)) {
return true;
}
const relPath = path.relative(repoDir, currentPath);
const isRepoRootEntry = relPath !== '' && !relPath.includes(path.sep);
if (!isRepoRootEntry) {
return false;
}
return (
VERIFICATION_SNAPSHOT_ROOT_EXCLUDE_NAMES.has(name) ||
VERIFICATION_SNAPSHOT_ROOT_EXCLUDE_PREFIXES.some((prefix) =>
name.startsWith(prefix),
)
);
}
function updateVerificationSnapshotHash(hash, repoDir, currentPath) { function updateVerificationSnapshotHash(hash, repoDir, currentPath) {
const relPath = path.relative(repoDir, currentPath) || '.'; const relPath = path.relative(repoDir, currentPath) || '.';
const stat = fs.lstatSync(currentPath); const stat = fs.lstatSync(currentPath);
@@ -21,8 +52,9 @@ function updateVerificationSnapshotHash(hash, repoDir, currentPath) {
hash.update(`dir\0${relPath}\0`); hash.update(`dir\0${relPath}\0`);
} }
for (const entry of fs.readdirSync(currentPath).sort()) { for (const entry of fs.readdirSync(currentPath).sort()) {
if (isVerificationSnapshotExcludedName(entry)) continue; const nextPath = path.join(currentPath, entry);
updateVerificationSnapshotHash(hash, repoDir, path.join(currentPath, entry)); if (isVerificationSnapshotExcludedPath(repoDir, nextPath)) continue;
updateVerificationSnapshotHash(hash, repoDir, nextPath);
} }
return; return;
} }

View File

@@ -83,6 +83,76 @@ describe('verification helpers', () => {
expect(third).not.toBe(first); expect(third).not.toBe(first);
}); });
it('ignores runtime state directories and local backup folders', () => {
const repoDir = fs.mkdtempSync(path.join(os.tmpdir(), 'ejclaw-snapshot-'));
fs.mkdirSync(path.join(repoDir, 'src'), { recursive: true });
fs.mkdirSync(path.join(repoDir, 'data'), { recursive: true });
fs.mkdirSync(path.join(repoDir, 'logs'), { recursive: true });
fs.mkdirSync(path.join(repoDir, 'cache'), { recursive: true });
fs.mkdirSync(path.join(repoDir, '.ejclaw-reviewer-runtime'), {
recursive: true,
});
fs.mkdirSync(path.join(repoDir, 'store.local-backup-20260408_024500'), {
recursive: true,
});
fs.writeFileSync(
path.join(repoDir, 'src', 'index.ts'),
'export const x = 1;\n',
);
fs.writeFileSync(path.join(repoDir, 'data', 'state.json'), '{"x":1}\n');
fs.writeFileSync(path.join(repoDir, 'logs', 'service.log'), 'line-1\n');
fs.writeFileSync(path.join(repoDir, 'cache', 'tmp.txt'), 'cache-a\n');
fs.writeFileSync(
path.join(repoDir, '.ejclaw-reviewer-runtime', 'runtime.json'),
'{"ok":true}\n',
);
fs.writeFileSync(
path.join(repoDir, 'store.local-backup-20260408_024500', 'db.sqlite'),
'backup-a\n',
);
const first = computeVerificationSnapshot(repoDir).snapshotId;
fs.writeFileSync(path.join(repoDir, 'data', 'state.json'), '{"x":2}\n');
fs.writeFileSync(path.join(repoDir, 'logs', 'service.log'), 'line-2\n');
fs.writeFileSync(path.join(repoDir, 'cache', 'tmp.txt'), 'cache-b\n');
fs.writeFileSync(
path.join(repoDir, '.ejclaw-reviewer-runtime', 'runtime.json'),
'{"ok":false}\n',
);
fs.writeFileSync(
path.join(repoDir, 'store.local-backup-20260408_024500', 'db.sqlite'),
'backup-b\n',
);
const second = computeVerificationSnapshot(repoDir).snapshotId;
expect(second).toBe(first);
});
it('still includes nested source directories named like excluded roots', () => {
const repoDir = fs.mkdtempSync(path.join(os.tmpdir(), 'ejclaw-snapshot-'));
fs.mkdirSync(path.join(repoDir, 'src', 'fixtures', 'data'), {
recursive: true,
});
fs.writeFileSync(
path.join(repoDir, 'src', 'fixtures', 'data', 'sample.json'),
'{"v":1}\n',
);
const first = computeVerificationSnapshot(repoDir).snapshotId;
fs.writeFileSync(
path.join(repoDir, 'src', 'fixtures', 'data', 'sample.json'),
'{"v":2}\n',
);
const second = computeVerificationSnapshot(repoDir).snapshotId;
expect(second).not.toBe(first);
});
it('backfills legacy node_modules state before verification', async () => { it('backfills legacy node_modules state before verification', async () => {
const repoDir = fs.mkdtempSync( const repoDir = fs.mkdtempSync(
path.join(os.tmpdir(), 'ejclaw-verification-legacy-'), path.join(os.tmpdir(), 'ejclaw-verification-legacy-'),

View File

@@ -20,7 +20,7 @@ import {
} from './workspace-package-manager.js'; } from './workspace-package-manager.js';
import { import {
computeVerificationSnapshotId, computeVerificationSnapshotId,
isVerificationSnapshotExcludedName, isVerificationSnapshotExcludedPath,
resolveVerificationResponsesDir, resolveVerificationResponsesDir,
} from '../shared/verification-snapshot.js'; } from '../shared/verification-snapshot.js';
@@ -95,8 +95,8 @@ export function buildVerificationCommand(
}; };
} }
function shouldExcludePath(name: string): boolean { function shouldExcludePath(repoDir: string, source: string): boolean {
return isVerificationSnapshotExcludedName(name); return isVerificationSnapshotExcludedPath(repoDir, source);
} }
export function computeVerificationSnapshot( export function computeVerificationSnapshot(
@@ -130,7 +130,7 @@ function copyWorkspaceToScratch(repoDir: string, scratchDir: string): void {
recursive: true, recursive: true,
filter: (source) => { filter: (source) => {
if (source === repoDir) return true; if (source === repoDir) return true;
return !shouldExcludePath(path.basename(source)); return !shouldExcludePath(repoDir, source);
}, },
}); });
} }