mirror of
https://github.com/garrytan/gstack.git
synced 2026-10-03 09:56:57 +02:00
test(qa-observer): fix mode treats atomic temps of authorized src/test writes as transient
CI webhook-fix failed with 'Could not watch test/worker.regression-1.test.ts.tmp...': Claude Code's Write renamed its temp before the per-file watch was added. The functional eval now tells the observer its mode, and a temp whose target that mode may write is observed through its directory watch. Report-only mode and undeclared paths keep failing closed.
This commit is contained in:
1 parent
b541f28ddb
commit
00dacee8bd
3 files changed
+16
-5
No files matched your search
@@ -103,7 +103,7 @@ export async function runQAFunctionalCase(entry: { id: string; family: QAFamily;
|
|||||||
fixtureGit(fixture.root, ['commit', '-m', 'Bind current QA instructions to fixture']);
|
fixtureGit(fixture.root, ['commit', '-m', 'Bind current QA instructions to fixture']);
|
||||||
fixture.revision = fixtureGit(fixture.root, ['rev-parse', 'HEAD']);
|
fixture.revision = fixtureGit(fixture.root, ['rev-parse', 'HEAD']);
|
||||||
if (fixtureGit(fixture.root, ['status', '--porcelain'])) throw new Error('QA fixture must start clean');
|
if (fixtureGit(fixture.root, ['status', '--porcelain'])) throw new Error('QA fixture must start clean');
|
||||||
observer = await observeQAWrites(fixture.root, { evidenceProducer: true });
|
observer = await observeQAWrites(fixture.root, { evidenceProducer: true, atomicWriteMode: entry.mode });
|
||||||
await runRecordedOfficeHoursAttempt({
|
await runRecordedOfficeHoursAttempt({
|
||||||
collector, name: entry.id, suite: 'Functional QA native E2E',
|
collector, name: entry.id, suite: 'Functional QA native E2E',
|
||||||
model: process.env.EVALS_MODEL ?? resolveEvalModel('capture'),
|
model: process.env.EVALS_MODEL ?? resolveEvalModel('capture'),
|
||||||
|
|||||||
@@ -72,7 +72,7 @@ export interface QAWriteObservation {
|
|||||||
limits: string[];
|
limits: string[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function observeQAWrites(root: string, options: { reportDirectory?: string; evidenceProducer?: boolean; atomicTargets?: string[] } = {}) {
|
export async function observeQAWrites(root: string, options: { reportDirectory?: string; evidenceProducer?: boolean; atomicTargets?: string[]; atomicWriteMode?: QAMode } = {}) {
|
||||||
if (process.platform !== 'linux') throw new Error('QA write observer unavailable: Linux inotify required');
|
if (process.platform !== 'linux') throw new Error('QA write observer unavailable: Linux inotify required');
|
||||||
if (fs.realpathSync(root) !== root) throw new Error('Observer root must be canonical');
|
if (fs.realpathSync(root) !== root) throw new Error('Observer root must be canonical');
|
||||||
let reportDirectory: string | undefined;
|
let reportDirectory: string | undefined;
|
||||||
@@ -83,7 +83,9 @@ export async function observeQAWrites(root: string, options: { reportDirectory?:
|
|||||||
}
|
}
|
||||||
const transientFile = (relative: string) => qaWriteAllowed(relative, 'qa-only')
|
const transientFile = (relative: string) => qaWriteAllowed(relative, 'qa-only')
|
||||||
|| (reportDirectory !== undefined && relative.startsWith(reportDirectory + path.sep))
|
|| (reportDirectory !== undefined && relative.startsWith(reportDirectory + path.sep))
|
||||||
|| (options.atomicTargets ?? []).some(target => relative.startsWith(target + '.tmp.') && /^\.tmp\.[1-9]\d*\.[0-9a-f]{12}$/.test(relative.slice(target.length)));
|
|| (options.atomicTargets ?? []).some(target => relative.startsWith(target + '.tmp.') && /^\.tmp\.[1-9]\d*\.[0-9a-f]{12}$/.test(relative.slice(target.length)))
|
||||||
|
|| (options.atomicWriteMode !== undefined && /\.tmp\.[1-9]\d*\.[0-9a-f]{12}$/.test(relative)
|
||||||
|
&& qaWriteAllowed(relative.replace(/\.tmp\.[1-9]\d*\.[0-9a-f]{12}$/, ''), options.atomicWriteMode));
|
||||||
const before = qaTreeSnapshot(root);
|
const before = qaTreeSnapshot(root);
|
||||||
const { dlopen, FFIType, ptr } = await import('bun:ffi');
|
const { dlopen, FFIType, ptr } = await import('bun:ffi');
|
||||||
const libc = dlopen('libc.so.6', {
|
const libc = dlopen('libc.so.6', {
|
||||||
|
|||||||
@@ -14,11 +14,11 @@ function fixture() {
|
|||||||
return root;
|
return root;
|
||||||
}
|
}
|
||||||
|
|
||||||
async function atomicPublication(directory: string, declared?: string, atomicTargets?: string[]) {
|
async function atomicPublication(directory: string, declared?: string, atomicTargets?: string[], atomicWriteMode?: 'qa' | 'qa-only') {
|
||||||
const root = fixture();
|
const root = fixture();
|
||||||
const temporary = path.join(root, directory, 'exploration-004.json.tmp.2644.340bb6ad0afe');
|
const temporary = path.join(root, directory, 'exploration-004.json.tmp.2644.340bb6ad0afe');
|
||||||
const target = path.join(root, directory, 'exploration-004.json');
|
const target = path.join(root, directory, 'exploration-004.json');
|
||||||
const observer = await observeQAWrites(root, { reportDirectory: declared, atomicTargets });
|
const observer = await observeQAWrites(root, { reportDirectory: declared, atomicTargets, atomicWriteMode });
|
||||||
const stat = fs.lstatSync;
|
const stat = fs.lstatSync;
|
||||||
let renamedAtWatch = false;
|
let renamedAtWatch = false;
|
||||||
let stopped = false;
|
let stopped = false;
|
||||||
@@ -83,6 +83,15 @@ async function atomicPublication(directory: string, declared?: string, atomicTar
|
|||||||
expect(result.observation.events).toContainEqual(expect.objectContaining({ path: result.target, mask: 0x80 }));
|
expect(result.observation.events).toContainEqual(expect.objectContaining({ path: result.target, mask: 0x80 }));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('fix mode treats an atomic temp for an authorized source/test write as transient; report-only mode stays strict', async () => {
|
||||||
|
const fix = await atomicPublication('src', undefined, undefined, 'qa');
|
||||||
|
expect(fix.renamedAtWatch).toBe(false);
|
||||||
|
expect(fix.observation.failures).toEqual([]);
|
||||||
|
const reportOnly = await atomicPublication('src', undefined, undefined, 'qa-only');
|
||||||
|
expect(reportOnly.renamedAtWatch).toBe(true);
|
||||||
|
expect(reportOnly.observation.complete).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
test('supports an explicitly selected nested report directory, not an implicit reports name', async () => {
|
test('supports an explicitly selected nested report directory, not an implicit reports name', async () => {
|
||||||
const result = await atomicPublication('foreign/reports', 'foreign/reports');
|
const result = await atomicPublication('foreign/reports', 'foreign/reports');
|
||||||
expect(result.observation.complete).toBe(true);
|
expect(result.observation.complete).toBe(true);
|
||||||
|
|||||||
Reference in new issue
Block a user