fix: allow notify reactions when auto is false, clean up reserved IDs on spawn failure

- Reactions with action: "notify" bypass the auto: false gate so
  merge-ready notifications fire for approved-and-green config
- All spawn failure paths now delete the reserved session metadata file

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Prateek 2026-02-13 21:00:58 +05:30
parent 65787f08e3
commit 07b3a553f7
2 changed files with 43 additions and 23 deletions

View File

@ -418,11 +418,14 @@ export function createLifecycleManager(deps: LifecycleManagerDeps): LifecycleMan
? { ...globalReaction, ...projectReaction }
: globalReaction;
if (reactionConfig && reactionConfig.auto !== false && reactionConfig.action) {
if (reactionConfig && reactionConfig.action) {
// auto: false skips automated agent actions but still allows notifications
if (reactionConfig.auto !== false || reactionConfig.action === "notify") {
await executeReaction(event, reactionKey, reactionConfig as ReactionConfig);
}
}
}
}
} else {
// No transition but track current state
states.set(session.id, newStatus);
@ -467,11 +470,13 @@ export function createLifecycleManager(deps: LifecycleManagerDeps): LifecycleMan
const reactionKey = eventToReactionKey("summary.all_complete");
if (reactionKey) {
const reactionConfig = config.reactions[reactionKey];
if (reactionConfig && reactionConfig.auto !== false && reactionConfig.action) {
if (reactionConfig && reactionConfig.action) {
if (reactionConfig.auto !== false || reactionConfig.action === "notify") {
await executeReaction(event, reactionKey, reactionConfig as ReactionConfig);
}
}
}
}
} catch {
// Poll cycle failed — will retry next interval
} finally {

View File

@ -192,6 +192,7 @@ export function createSessionManager(deps: SessionManagerDeps): SessionManager {
// Create workspace (if workspace plugin is available)
let workspacePath = project.path;
if (plugins.workspace) {
try {
const wsInfo = await plugins.workspace.create({
projectId: spawnConfig.projectId,
project,
@ -200,7 +201,7 @@ export function createSessionManager(deps: SessionManagerDeps): SessionManager {
});
workspacePath = wsInfo.path;
// Run post-create hooks — clean up workspace on failure (skip if project root)
// Run post-create hooks — clean up workspace on failure
if (plugins.workspace.postCreate) {
try {
await plugins.workspace.postCreate(wsInfo, project);
@ -215,6 +216,15 @@ export function createSessionManager(deps: SessionManagerDeps): SessionManager {
throw err;
}
}
} catch (err) {
// Clean up reserved session ID on workspace failure
try {
deleteMetadata(config.dataDir, sessionId, false);
} catch {
/* best effort */
}
throw err;
}
}
// Get agent launch config and create runtime — clean up workspace on failure
@ -242,7 +252,7 @@ export function createSessionManager(deps: SessionManagerDeps): SessionManager {
},
});
} catch (err) {
// Clean up workspace if agent config or runtime creation failed
// Clean up workspace and reserved ID if agent config or runtime creation failed
if (plugins.workspace && workspacePath !== project.path) {
try {
await plugins.workspace.destroy(workspacePath);
@ -250,6 +260,11 @@ export function createSessionManager(deps: SessionManagerDeps): SessionManager {
/* best effort */
}
}
try {
deleteMetadata(config.dataDir, sessionId, false);
} catch {
/* best effort */
}
throw err;
}