fix(daemon): tolerate coarse artifact mtimes

This commit is contained in:
Denis Redozubov 2026-05-28 22:00:11 +04:00
parent 755d8173df
commit 67396350c8
3 changed files with 13 additions and 3 deletions

View file

@ -32,10 +32,16 @@ const FORBIDDEN_SEGMENT = /^$|^\.\.?$/;
const RESERVED_PROJECT_FILE_SEGMENTS = new Set(['.live-artifacts']);
const DESIGN_HANDOFF_FILENAME = 'DESIGN-HANDOFF.md';
const DESIGN_MANIFEST_FILENAME = 'DESIGN-MANIFEST.json';
export const RUN_ARTIFACT_RECONCILE_MTIME_GRACE_MS = 1000;
export const projectFileRenameTestHooks = {
beforeCommit: null as null | ((paths: { source: string; target: string }) => Promise<void> | void),
};
export function isRunTouchedProjectFile(fileMtimeMs, runStartTimeMs) {
if (!Number.isFinite(fileMtimeMs) || !Number.isFinite(runStartTimeMs)) return false;
return fileMtimeMs + RUN_ARTIFACT_RECONCILE_MTIME_GRACE_MS >= runStartTimeMs;
}
export function projectDir(projectsRoot, projectId) {
if (!isSafeId(projectId)) throw new Error('invalid project id');
return path.join(projectsRoot, projectId);

View file

@ -353,6 +353,7 @@ import {
assertSandboxProjectRootAvailable,
detectEntryFile,
ensureProject,
isRunTouchedProjectFile,
isSafeId,
listFiles,
mimeFor,
@ -12751,7 +12752,7 @@ export async function startServer({
try {
const filePath = path.join(dir, f.name);
const st = await fs.promises.stat(filePath);
if (st.mtimeMs < runStartTimeMs) continue;
if (!isRunTouchedProjectFile(st.mtimeMs, runStartTimeMs)) continue;
await reconcileHtmlArtifactManifest(
PROJECTS_DIR,
run.projectId,

View file

@ -9,7 +9,7 @@ import fs from 'node:fs';
import os from 'node:os';
import path from 'node:path';
import { closeDatabase, insertProject, openDatabase } from '../src/db.js';
import { reconcileHtmlArtifactManifest, writeProjectFile } from '../src/projects.js';
import { isRunTouchedProjectFile, reconcileHtmlArtifactManifest, writeProjectFile } from '../src/projects.js';
const PROJECT_ID = 'reconcile-test';
let tempDir = null;
@ -146,6 +146,9 @@ describe('run-end artifact manifest reconciliation (#2893)', () => {
// File written during the run
await writeProjectFile(projectsRoot, PROJECT_ID, 'new-output.html', '<p>new</p>');
const newPath = path.join(projectsRoot, PROJECT_ID, 'new-output.html');
const coarseFsTime = new Date(runStartTimeMs - 500);
fs.utimesSync(newPath, coarseFsTime, coarseFsTime);
// Simulate the close-handler reconciliation with mtime filter
const dir = path.join(projectsRoot, PROJECT_ID);
@ -154,7 +157,7 @@ describe('run-end artifact manifest reconciliation (#2893)', () => {
const ext = path.extname(name).toLowerCase();
if (ext !== '.html' && ext !== '.htm') continue;
const st = fs.statSync(path.join(dir, name));
if (st.mtimeMs < runStartTimeMs) continue;
if (!isRunTouchedProjectFile(st.mtimeMs, runStartTimeMs)) continue;
await reconcileHtmlArtifactManifest(projectsRoot, PROJECT_ID, name);
}