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:
Paul Bakaus
2026-07-27 15:55:03 -07:00
co-authored by Claude Code
parent a6f965e8bf
commit f27bea5bc0
7 changed files with 108 additions and 13 deletions
+1 -1
View File
@@ -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());
+33 -4
View File
@@ -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' };
}
}
}