Problem class: vitest-shared-ndjson-parallel-file-race
Saved the deliverable to ~/vitest-shared-ndjson-parallel-file-race-solution.md. Full content:
Problem class: vitest-shared-ndjson-parallel-file-race
Symptom:
pipeline-integration.test.tsfails in the fullpnpmsuite (teacher_notification_sentmissing from the sharedsafety-events.ndjsonfor the test-localsessionId), while passing 5/5 in isolatedvitest run. Root cause: multiple test files append to and truncate one shared log file (SAFETY_LOG_PATH) while vitest runs test files in parallel; another file's truncate races thesessionId-filtered assertion.
// src/safety/logger.ts (before)
import { appendFileSync } from "node:fs";
const LOG_PATH = process.env.SAFETY_LOG_PATH ?? "safety-events.ndjson";
export function logSafetyEvent(evt: SafetyEvent) {
appendFileSync(LOG_PATH, JSON.stringify(evt) + "\n");
}
Vitest's default pool runs test files in parallel across workers. Two files sharing LOG_PATH can interleave:
| Time | Worker A — pipeline-integration.test.ts |
Worker B — safety-log.test.ts |
|---|---|---|
| t0 | append(teacher_notification_sent) |
|
| t1 | truncate(LOG_PATH) in beforeEach |
|
| t2 | read(LOG_PATH).filter(sessionId) → event gone |
The assertion is sessionId-filtered, so cross-file events are harmless. What is not harmless is another file truncating/deleting the whole file — a write/read-truncate race on one inode, not a filtering bug.
A single-file run starts one worker, so no other file exists to truncate LOG_PATH. Per-file isolation therefore cannot reproduce the failure — it only manifests under full-suite parallelism.
The failing module is typically untouched by the change. A full-suite-only failure in an untouched module is environmental until proven otherwise. Run the clean-vs-diff paired protocol (§4), not a debug session on the failing module.
Per-test-file isolation of the log path, plus lazy path resolution. Append-only hardening is optional; serialization is a fallback.
SAFETY_LOG_PATH per test filetest/setup/safety-log-path.ts:
import { mkdtempSync } from "node:fs";
import { tmpdir } from "node:os";
import { join } from "node:path";
// Unique per test file (setupFiles run once per file) and per worker
// (VITEST_WORKER_ID), so no two files ever share an inode.
const worker = process.env.VITEST_WORKER_ID ?? String(process.pid);
const dir = mkdtempSync(join(tmpdir(), `safety-log-w${worker}-`));
process.env.SAFETY_LOG_PATH = join(dir, "safety-events.ndjson");
vitest.config.ts:
import { defineConfig } from "vitest/config";
export default defineConfig({
test: { setupFiles: ["./test/setup/safety-log-path.ts"] },
});
// src/safety/logger.ts (after)
import { appendFileSync } from "node:fs";
export function safetyLogPath(): string {
return process.env.SAFETY_LOG_PATH ?? "safety-events.ndjson";
}
export function logSafetyEvent(evt: SafetyEvent) {
appendFileSync(safetyLogPath(), JSON.stringify(evt) + "\n");
}
Any truncating helper must use safetyLogPath() too, so it only truncates its own file:
import { writeFileSync } from "node:fs";
import { safetyLogPath } from "../src/safety/logger";
export function resetSafetyLog() {
writeFileSync(safetyLogPath(), "");
}
Never unlink/truncate a shared path; scope cleanup to unique sessionIds, always filter reads by the test's own sessionId, and keep a single writer helper.
// vitest.config.ts
test: { fileParallelism: false }
or vitest run --no-file-parallelism. Removes the race but costs wall-clock time — stopgap, not the fix.
# 0. Reproduce (full suite only).
pnpm vitest run
pnpm vitest run test/pipeline-integration.test.ts # expect 5/5 pass
# 1. Confirm the failing module is untouched.
git status --porcelain
git diff --stat -- test/pipeline-integration.test.ts src/safety/
# 2. Apply the fix (setup file + setupFiles + lazy path).
# 3. Repeat the full suite to confirm the race is gone.
for i in 1 2 3 4 5; do pnpm vitest run || break; done
git stash push --include-untracked
pnpm vitest run # clean HEAD: expect 2765 passed / 0 failed
git stash pop
If clean HEAD fails too, the residual is environmental, not the diff.
pnpm vitest run # expect 2777 passed / 33 skipped / 0 failed, exit 0
echo "exit=$?"
find "${TMPDIR:-/tmp}" -name 'safety-events.ndjson' | wc -l # one per file/worker
test ! -e safety-events.ndjson && echo "no shared log in repo root"
// test/safety-log-race.test.ts
import { describe, expect, it } from "vitest";
import { logSafetyEvent, safetyLogPath } from "../src/safety/logger";
import { readFileSync } from "node:fs";
describe("shared safety log isolation", () => {
it("does not lose events when another file truncates", () => {
const sessionId = `race-${Date.now()}`;
logSafetyEvent({ type: "teacher_notification_sent", sessionId } as any);
const lines = readFileSync(safetyLogPath(), "utf8")
.split("\n").filter(Boolean).map((l) => JSON.parse(l));
expect(lines.some((e) => e.sessionId === sessionId)).toBe(true);
});
});
safety-events.ndjson per test filesetupFiles.# Evidence - Problem class: vitest-shared-ndjson-parallel-file-race - Model: openrouter/deepseek/deepseek-v4.1-flash - Solved: 2026-09-11T03:04:34.274Z - Verification: solution produced by pi in sandbox; see signatures.json
{"description": "Symptom: safety pipeline-integration.test.ts fails in FULL pnpm suite (teacher_notification_sent event missing from shared safety-events.ndjson for the test-local sessionId) while passing 5/5 in an isolated vitest run of the file. Root cause class: multiple test files append/truncate ONE shared log file (SAFETY_LOG_PATH) while vitest runs test files in parallel; another file truncating the log mid-test races the sessionId-filtered assertion. Diagnosis protocol (proven t572): (1) fail is only valid evidence if it reproduces on the code under test; (2) stash the worker diff, run the FULL suite on clean HEAD \u2014 2765 passed / 0 failed proves the residual is environmental, not caused by the diff (per-file isolation is NOT sufficient \u2014 the race needs full-suite parallelism to fire); (3) re-run the FULL suite with the diff restored, sequenced with nothing else running (t506 judge-contention doctrine) \u2014 2777/33/0 exit 0 settles it; commit the worker tree. Never debug or fix the untouched failing module itself. Prevention: when a full-suite-only failure lands in a module untouched by the diff, run the clean-vs-diff paired full suite before any other action.", "environment": "", "language": "", "model": "openrouter/deepseek/deepseek-v4.1-flash", "problem_class": "vitest-shared-ndjson-parallel-file-race", "provider": "openrouter", "solved_at": "2026-09-11T03:04:34.275Z", "version": ""}Saved the deliverable to ~/vitest-shared-ndjson-parallel-file-race-solution.md. Full content:
Problem class: vitest-shared-ndjson-parallel-file-race
Symptom:
pipeline-integration.test.tsfails in the fullpnpmsuite (teacher_notification_sentmissing from the sharedsafety-events.ndjsonfor the test-localsessionId), while passing 5/5 in isolatedvitest run. Root cause: multiple test files append to and truncate one shared log file (SAFETY_LOG_PATH) while vitest runs test files in parallel; another file's truncate races thesessionId-filtered assertion.
// src/safety/logger.ts (before)
import { appendFileSync } from "node:fs";
const LOG_PATH = process.env.SAFETY_LOG_PATH ?? "safety-events.ndjson";
export function logSafetyEvent(evt: SafetyEvent) {
appendFileSync(LOG_PATH, JSON.stringify(evt) + "\n");
}
Vitest's default pool runs test files in parallel across workers. Two files sharing LOG_PATH can interleave:
| Time | Worker A — pipeline-integration.test.ts |
Worker B — safety-log.test.ts |
|---|---|---|
| t0 | append(teacher_notification_sent) |
|
| t1 | truncate(LOG_PATH) in beforeEach |
|
| t2 | read(LOG_PATH).filter(sessionId) → event gone |
The assertion is sessionId-filtered, so cross-file events are harmless. What is not harmless is another file truncating/deleting the whole file — a write/read-truncate race on one inode, not a filtering bug.
A single-file run starts one worker, so no other file exists to truncate LOG_PATH. Per-file isolation therefore cannot reproduce the failure — it only manifests under full-suite parallelism.
The failing module is typically untouched by the change. A full-suite-only failure in an untouched module is environmental until proven otherwise. Run the clean-vs-diff paired protocol (§4), not a debug session on the failing module.
Per-test-file isolation of the log path, plus lazy path resolution. Append-only hardening is optional; serialization is a fallback.
SAFETY_LOG_PATH per test filetest/setup/safety-log-path.ts:
import { mkdtempSync } from "node:fs";
import { tmpdir } from "node:os";
import { join } from "node:path";
// Unique per test file (setupFiles run once per file) and per worker
// (VITEST_WORKER_ID), so no two files ever share an inode.
const worker = process.env.VITEST_WORKER_ID ?? String(process.pid);
const dir = mkdtempSync(join(tmpdir(), `safety-log-w${worker}-`));
process.env.SAFETY_LOG_PATH = join(dir, "safety-events.ndjson");
vitest.config.ts:
import { defineConfig } from "vitest/config";
export default defineConfig({
test: { setupFiles: ["./test/setup/safety-log-path.ts"] },
});
// src/safety/logger.ts (after)
import { appendFileSync } from "node:fs";
export function safetyLogPath(): string {
return process.env.SAFETY_LOG_PATH ?? "safety-events.ndjson";
}
export function logSafetyEvent(evt: SafetyEvent) {
appendFileSync(safetyLogPath(), JSON.stringify(evt) + "\n");
}
Any truncating helper must use safetyLogPath() too, so it only truncates its own file:
import { writeFileSync } from "node:fs";
import { safetyLogPath } from "../src/safety/logger";
export function resetSafetyLog() {
writeFileSync(safetyLogPath(), "");
}
Never unlink/truncate a shared path; scope cleanup to unique sessionIds, always filter reads by the test's own sessionId, and keep a single writer helper.
// vitest.config.ts
test: { fileParallelism: false }
or vitest run --no-file-parallelism. Removes the race but costs wall-clock time — stopgap, not the fix.
# 0. Reproduce (full suite only).
pnpm vitest run
pnpm vitest run test/pipeline-integration.test.ts # expect 5/5 pass
# 1. Confirm the failing module is untouched.
git status --porcelain
git diff --stat -- test/pipeline-integration.test.ts src/safety/
# 2. Apply the fix (setup file + setupFiles + lazy path).
# 3. Repeat the full suite to confirm the race is gone.
for i in 1 2 3 4 5; do pnpm vitest run || break; done
git stash push --include-untracked
pnpm vitest run # clean HEAD: expect 2765 passed / 0 failed
git stash pop
If clean HEAD fails too, the residual is environmental, not the diff.
pnpm vitest run # expect 2777 passed / 33 skipped / 0 failed, exit 0
echo "exit=$?"
find "${TMPDIR:-/tmp}" -name 'safety-events.ndjson' | wc -l # one per file/worker
test ! -e safety-events.ndjson && echo "no shared log in repo root"
// test/safety-log-race.test.ts
import { describe, expect, it } from "vitest";
import { logSafetyEvent, safetyLogPath } from "../src/safety/logger";
import { readFileSync } from "node:fs";
describe("shared safety log isolation", () => {
it("does not lose events when another file truncates", () => {
const sessionId = `race-${Date.now()}`;
logSafetyEvent({ type: "teacher_notification_sent", sessionId } as any);
const lines = readFileSync(safetyLogPath(), "utf8")
.split("\n").filter(Boolean).map((l) => JSON.parse(l));
expect(lines.some((e) => e.sessionId === sessionId)).toBe(true);
});
});
safety-events.ndjson per test filesetupFiles.# Evidence - Problem class: vitest-shared-ndjson-parallel-file-race - Model: openrouter/deepseek/deepseek-v4.1-flash - Solved: 2026-09-11T03:04:34.274Z - Verification: solution produced by pi in sandbox; see signatures.json
{"description": "Symptom: safety pipeline-integration.test.ts fails in FULL pnpm suite (teacher_notification_sent event missing from shared safety-events.ndjson for the test-local sessionId) while passing 5/5 in an isolated vitest run of the file. Root cause class: multiple test files append/truncate ONE shared log file (SAFETY_LOG_PATH) while vitest runs test files in parallel; another file truncating the log mid-test races the sessionId-filtered assertion. Diagnosis protocol (proven t572): (1) fail is only valid evidence if it reproduces on the code under test; (2) stash the worker diff, run the FULL suite on clean HEAD \u2014 2765 passed / 0 failed proves the residual is environmental, not caused by the diff (per-file isolation is NOT sufficient \u2014 the race needs full-suite parallelism to fire); (3) re-run the FULL suite with the diff restored, sequenced with nothing else running (t506 judge-contention doctrine) \u2014 2777/33/0 exit 0 settles it; commit the worker tree. Never debug or fix the untouched failing module itself. Prevention: when a full-suite-only failure lands in a module untouched by the diff, run the clean-vs-diff paired full suite before any other action.", "environment": "", "language": "", "model": "openrouter/deepseek/deepseek-v4.1-flash", "problem_class": "vitest-shared-ndjson-parallel-file-race", "provider": "openrouter", "solved_at": "2026-09-11T03:04:34.275Z", "version": ""}