(
workspaceId: string
)
| 10286 | } |
| 10287 | |
| 10288 | private async canCleanupReportedTask( |
| 10289 | workspaceId: string |
| 10290 | ): Promise<{ ok: true; parentWorkspaceId: string } | { ok: false; reason: string }> { |
| 10291 | assert(workspaceId.length > 0, "canCleanupReportedTask: workspaceId must be non-empty"); |
| 10292 | |
| 10293 | const config = this.config.loadConfigOrDefault(); |
| 10294 | const entry = findWorkspaceEntry(config, workspaceId); |
| 10295 | if (!entry) { |
| 10296 | return { ok: false, reason: "workspace_not_found" }; |
| 10297 | } |
| 10298 | |
| 10299 | const parentWorkspaceId = entry.workspace.parentWorkspaceId; |
| 10300 | if (!parentWorkspaceId) { |
| 10301 | return { ok: false, reason: "missing_parent_workspace" }; |
| 10302 | } |
| 10303 | |
| 10304 | if (!hasCompletedAgentReport(entry.workspace)) { |
| 10305 | return { ok: false, reason: "task_not_reported" }; |
| 10306 | } |
| 10307 | |
| 10308 | if (entry.workspace.bestOf?.total != null && entry.workspace.bestOf.total > 1) { |
| 10309 | if ( |
| 10310 | await this.shouldDeferBestOfFallback({ |
| 10311 | parentWorkspaceId, |
| 10312 | groupId: entry.workspace.bestOf.groupId, |
| 10313 | total: entry.workspace.bestOf.total, |
| 10314 | }) |
| 10315 | ) { |
| 10316 | return { ok: false, reason: "best_of_parent_partial_pending" }; |
| 10317 | } |
| 10318 | } |
| 10319 | |
| 10320 | if (this.aiService.isStreaming(workspaceId)) { |
| 10321 | log.debug("cleanupReportedLeafTask: deferring auto-delete; stream still active", { |
| 10322 | workspaceId, |
| 10323 | parentWorkspaceId, |
| 10324 | }); |
| 10325 | return { ok: false, reason: "still_streaming" }; |
| 10326 | } |
| 10327 | |
| 10328 | // Topology gate: a completed task can only be cleaned up when it is a structural leaf |
| 10329 | // (has no child agent tasks in config). This stays status-agnostic so ancestor deletion |
| 10330 | // never orphans descendants that have not been pruned yet. |
| 10331 | const index = this.buildAgentTaskIndex(config); |
| 10332 | const isWorkflowOwnedTask = this.isWorkflowOwnedTaskUsingIndex(index, workspaceId); |
| 10333 | if (this.hasChildAgentTasks(index, workspaceId)) { |
| 10334 | return { ok: false, reason: "has_child_tasks" }; |
| 10335 | } |
| 10336 | |
| 10337 | const parentSessionDir = this.config.getSessionDir(parentWorkspaceId); |
| 10338 | const patchArtifact = await readSubagentGitPatchArtifact(parentSessionDir, workspaceId); |
| 10339 | if (patchArtifact?.status === "pending") { |
| 10340 | log.debug("cleanupReportedLeafTask: deferring auto-delete; patch artifact pending", { |
| 10341 | workspaceId, |
| 10342 | parentWorkspaceId, |
| 10343 | }); |
| 10344 | return { ok: false, reason: "patch_pending" }; |
| 10345 | } |
no test coverage detected