mirror of
https://github.com/pbakaus/impeccable.git
synced 2026-09-22 02:56:52 +03:00
fix: third review round + unmask and fix the astro-vite7 e2e failure
cursor[bot]: - variant_mount_failed joins EVENT_TYPES_NEEDING_AGENT_REPLY so stream mode waits for the repair reply instead of moving on mid-lease. - The fake agent's mount-failure repair no longer forces sourceEventType generate; the server maps the done reply onto the pending failure event, which acknowledges it instead of leaving it to be redelivered on every poll. greptile-apps[bot]: - With every helper server stopped, repo-root resolution now prefers the app whose durable store holds a non-terminal session (the interrupted session the user is recovering) over the most recent boot. astro-vite7 (pre-existing CI failure, root-caused): Astro 7 auto-detects AI-agent environments and daemonizes `astro dev`; the detached server holds a lock, outlives the harness, squats dev ports across runs, and makes the parent exit 0, which the harness read as a crash. The fixture now sets ASTRO_DEV_BACKGROUND=1 (disables the agent detection) plus --ignore-lock, and the harness supports per-fixture runtime.env. The core cycle now passes for the first time; the missed-done recovery scenario fails identically at origin/main with the daemon bypassed, so it is marked as a per-scenario known limitation with that rationale. This work was produced with AI assistance (Claude Code). Co-Authored-By: Claude Code <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Code
parent
a6f965e8bf
commit
f27bea5bc0
@@ -28,7 +28,7 @@ const scriptCmd = (name) => `node "${path.join(SELF_DIR, name)}"`;
|
||||
export const PER_REQUEST_TIMEOUT_MS = 270_000;
|
||||
export const DEFAULT_EVENT_LEASE_MS = 600_000;
|
||||
|
||||
const EVENT_TYPES_NEEDING_AGENT_REPLY = new Set(['generate', 'steer', 'manual_edit_apply', 'carbonize_cleanup']);
|
||||
const EVENT_TYPES_NEEDING_AGENT_REPLY = new Set(['generate', 'steer', 'manual_edit_apply', 'carbonize_cleanup', 'variant_mount_failed']);
|
||||
|
||||
function readServerInfo() {
|
||||
const record = readLiveServerInfo(process.cwd());
|
||||
|
||||
@@ -297,6 +297,32 @@ function hasLiveServer(appRoot) {
|
||||
}
|
||||
}
|
||||
|
||||
const TERMINAL_SESSION_PHASES = new Set(['completed', 'discarded']);
|
||||
|
||||
/**
|
||||
* True when the app's durable session store holds a session that is not
|
||||
* terminal. With every helper server stopped, this is what distinguishes
|
||||
* "the app whose interrupted session the user is trying to recover" from an
|
||||
* app that merely booted more recently.
|
||||
*/
|
||||
function hasActiveDurableSession(appRoot) {
|
||||
const dir = path.join(appRoot, '.impeccable', 'live', 'sessions');
|
||||
let entries;
|
||||
try {
|
||||
entries = fs.readdirSync(dir);
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
for (const name of entries) {
|
||||
if (!name.endsWith('.snapshot.json')) continue;
|
||||
try {
|
||||
const snapshot = JSON.parse(fs.readFileSync(path.join(dir, name), 'utf-8'));
|
||||
if (snapshot?.phase && !TERMINAL_SESSION_PHASES.has(snapshot.phase)) return true;
|
||||
} catch { /* skip unreadable snapshots */ }
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
function readManifestAt(appRoot) {
|
||||
try {
|
||||
const raw = JSON.parse(fs.readFileSync(rootsFilePath(appRoot), 'utf-8'));
|
||||
@@ -331,15 +357,18 @@ export function resolveLiveRoots(cwd = process.cwd(), { targetPath = null } = {}
|
||||
|
||||
const gitRoot = findGitRoot(absCwd);
|
||||
if (gitRoot) {
|
||||
// Several apps in one repo may have booted live. Prefer the one whose
|
||||
// helper server is actually running; a stale pointer entry must not
|
||||
// redirect status/poll/accept onto the wrong app's session store.
|
||||
// Several apps in one repo may have booted live. Preference order:
|
||||
// a running helper server, then an app whose durable store still holds
|
||||
// a non-terminal session (the stopped session the user is recovering),
|
||||
// then the most recent boot. A stale pointer entry must never redirect
|
||||
// status/poll/accept onto the wrong app's session store.
|
||||
const candidates = readPointerEntries(gitRoot)
|
||||
.map((entry) => readManifestAt(entry.appRoot))
|
||||
.filter(Boolean);
|
||||
if (candidates.length > 0) {
|
||||
const live = candidates.find((manifest) => hasLiveServer(manifest.appRoot));
|
||||
return { manifest: live || candidates[0], source: 'pointer' };
|
||||
const recovering = live || candidates.find((manifest) => hasActiveDurableSession(manifest.appRoot));
|
||||
return { manifest: recovering || candidates[0], source: 'pointer' };
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user