@overdeck/core 0.49.0 → 0.49.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (60) hide show
  1. package/dist/{build-info-Ck2TzOSQ.js → build-info-Bts-0E0B.js} +3 -3
  2. package/dist/{build-info-Ck2TzOSQ.js.map → build-info-Bts-0E0B.js.map} +1 -1
  3. package/dist/build-info-CD0O1MvT.js +2 -0
  4. package/dist/cli/index.js +4 -4
  5. package/dist/dashboard/{build-info-DXfOjbg3.js → build-info-DTX8JwVP.js} +3 -3
  6. package/dist/dashboard/{build-info-DXfOjbg3.js.map → build-info-DTX8JwVP.js.map} +1 -1
  7. package/dist/dashboard/build-info-PQlh_Rhb.js +2 -0
  8. package/dist/dashboard/{deacon-CeqVPw0o.js → deacon-BEH-IY0v.js} +2 -2
  9. package/dist/dashboard/{deacon-CQxv6Qwv.js → deacon-TPYssFYt.js} +8 -8
  10. package/dist/dashboard/{deacon-CQxv6Qwv.js.map → deacon-TPYssFYt.js.map} +1 -1
  11. package/dist/dashboard/{deacon-merge-ugZ7jekt.js → deacon-merge-BOGFEV8_.js} +5 -5
  12. package/dist/dashboard/{deacon-merge-ugZ7jekt.js.map → deacon-merge-BOGFEV8_.js.map} +1 -1
  13. package/dist/dashboard/{deacon-pending-lifecycle-Cxu6Rezs.js → deacon-pending-lifecycle-DVHjOLje.js} +2 -2
  14. package/dist/dashboard/{deacon-pending-lifecycle-Cxu6Rezs.js.map → deacon-pending-lifecycle-DVHjOLje.js.map} +1 -1
  15. package/dist/dashboard/deacon.js +2 -2
  16. package/dist/dashboard/{deploy-patrol-DxnpZcnl.js → deploy-patrol-BXjlvoV3.js} +2 -2
  17. package/dist/dashboard/{deploy-patrol-DxnpZcnl.js.map → deploy-patrol-BXjlvoV3.js.map} +1 -1
  18. package/dist/dashboard/{identity-BnxKo9-z.js → identity-Bwh6VGeL.js} +2 -2
  19. package/dist/dashboard/{identity-BnxKo9-z.js.map → identity-Bwh6VGeL.js.map} +1 -1
  20. package/dist/dashboard/{lifecycle-ozA4GZNs.js → lifecycle-B0rL38D3.js} +2 -2
  21. package/dist/dashboard/{lifecycle-ozA4GZNs.js.map → lifecycle-B0rL38D3.js.map} +1 -1
  22. package/dist/dashboard/{memory-governor-W8994ASc.js → memory-governor-A4O6vI9d.js} +3 -3
  23. package/dist/dashboard/{memory-governor-W8994ASc.js.map → memory-governor-A4O6vI9d.js.map} +1 -1
  24. package/dist/dashboard/{memory-pressure-patrol-D_T_QLFC.js → memory-pressure-patrol-BWlWywmA.js} +2 -2
  25. package/dist/dashboard/{memory-pressure-patrol-D_T_QLFC.js.map → memory-pressure-patrol-BWlWywmA.js.map} +1 -1
  26. package/dist/dashboard/{merge-agent-B8YsWRDu.js → merge-agent-BRSz26Oh.js} +1 -1
  27. package/dist/dashboard/{merge-agent-DxecsTre.js → merge-agent-GWhmnxFk.js} +2 -2
  28. package/dist/dashboard/{merge-agent-DxecsTre.js.map → merge-agent-GWhmnxFk.js.map} +1 -1
  29. package/dist/dashboard/merge-ops-DtHx17Vd.js +2 -0
  30. package/dist/dashboard/{post-merge-lifecycle-worker-DC-i1kqz.js → post-merge-lifecycle-worker-KK1RTKRE.js} +2 -2
  31. package/dist/dashboard/{post-merge-lifecycle-worker-DC-i1kqz.js.map → post-merge-lifecycle-worker-KK1RTKRE.js.map} +1 -1
  32. package/dist/dashboard/{resolver-BdHzKhNL.js → resolver-C8rooxoz.js} +2 -2
  33. package/dist/dashboard/{resolver-BdHzKhNL.js.map → resolver-C8rooxoz.js.map} +1 -1
  34. package/dist/dashboard/server.js +28 -28
  35. package/dist/dashboard/{service-0hbSVeHz.js → service-BLb_CyPL.js} +6 -6
  36. package/dist/dashboard/{service-0hbSVeHz.js.map → service-BLb_CyPL.js.map} +1 -1
  37. package/dist/dashboard/{specialists-C2ZVszIW.js → specialists-DocAK-7A.js} +5 -5
  38. package/dist/dashboard/{specialists-C2ZVszIW.js.map → specialists-DocAK-7A.js.map} +1 -1
  39. package/dist/dashboard/{specialists-DhLEuxVX.js → specialists-xXcJHAyg.js} +1 -1
  40. package/dist/dashboard/{stall-sweeper-o4-q0vqY.js → stall-sweeper-DOyIfeLR.js} +2 -2
  41. package/dist/dashboard/{stall-sweeper-o4-q0vqY.js.map → stall-sweeper-DOyIfeLR.js.map} +1 -1
  42. package/dist/dashboard/{uat-train-fK0FbCx7.js → uat-train-COSy-vEX.js} +1 -1
  43. package/dist/dashboard/{uat-train-CSOval9z.js → uat-train-DVUNW03P.js} +0 -0
  44. package/dist/dashboard/{uat-train-CSOval9z.js.map → uat-train-DVUNW03P.js.map} +1 -1
  45. package/dist/dashboard/{workflows-BnNc1QlT.js → workflows-BYwR2aIN.js} +2 -2
  46. package/dist/dashboard/{workflows-BnNc1QlT.js.map → workflows-BYwR2aIN.js.map} +1 -1
  47. package/dist/dashboard/workflows-Cf4LAnhg.js +2 -0
  48. package/dist/dashboard/{workspaces-CqeaoIK6.js → workspaces-BlpbOPZ7.js} +11 -11
  49. package/dist/dashboard/{workspaces-CqeaoIK6.js.map → workspaces-BlpbOPZ7.js.map} +1 -1
  50. package/dist/{lifecycle-Bhu9mEsV.js → lifecycle-BhAciIXh.js} +1 -1
  51. package/dist/{lifecycle-CMRdm3mR.js → lifecycle-D8m3w850.js} +2 -2
  52. package/dist/{lifecycle-CMRdm3mR.js.map → lifecycle-D8m3w850.js.map} +1 -1
  53. package/dist/{merge-agent-DQ7DOXE2.js → merge-agent-CUnIrr8R.js} +2 -2
  54. package/dist/{merge-agent-DQ7DOXE2.js.map → merge-agent-CUnIrr8R.js.map} +1 -1
  55. package/dist/{merge-agent-D33x_y_T.js → merge-agent-DwfVId_E.js} +1 -1
  56. package/package.json +1 -1
  57. package/dist/build-info-B9bbKbXz.js +0 -2
  58. package/dist/dashboard/build-info-CrBYH-L2.js +0 -2
  59. package/dist/dashboard/merge-ops-C9wpFz1G.js +0 -2
  60. package/dist/dashboard/workflows-C6MkcBR_.js +0 -2
@@ -1 +1 @@
1
- {"version":3,"file":"lifecycle-CMRdm3mR.js","names":["execAsync","promisify","mkdir","cp","rename","readdir","readlink","execAsync","promisify","readFile","rm","join","execFileAsync","promisify","defaultDeps","execFileAsync","execFile","resolve","promisify","promisify","copyFile","readFile","writeFile"],"sources":["../src/lib/prd-locations.ts","../src/lib/lifecycle/archive-planning.ts","../src/lib/process-cwd.ts","../src/lib/lifecycle/teardown-workspace.ts","../src/lib/cloister/agent-gc.ts","../src/lib/cloister/deacon-canonical-state.ts","../src/lib/cloister/ship-status.ts","../src/lib/lifecycle/dod.ts","../src/lib/lifecycle/dod-gate.ts","../src/lib/lifecycle/residue.ts","../src/lib/lifecycle/workflows.ts"],"sourcesContent":["/**\n * PRD location resolution — single source of truth.\n *\n * PRDs have historically been written in four formats due to a casing bug:\n * 1. docs/prds/<status>/<id-lower>/ — canonical subdirectory (new)\n * 2. docs/prds/<status>/<ID-UPPER>/ — buggy uppercase variant\n * 3. docs/prds/<status>/<id-lower>-plan.md — legacy flat file\n * 4. docs/prds/<status>/<ID-UPPER>-plan.md — buggy uppercase flat file\n *\n * All readers MUST go through findPrdAtStatus / findPrdAnywhere so they tolerate\n * every variant. All writers MUST use canonicalPrdSubdir so new artifacts only\n * land in the canonical lowercase subdirectory format.\n */\n\nimport { existsSync } from 'fs';\nimport { access, readFile, readdir } from 'node:fs/promises';\nimport { join } from 'path';\nimport { Effect } from 'effect';\nimport {\n PROJECT_DOCS_SUBDIR,\n PROJECT_PRDS_SUBDIR,\n PROJECT_PRDS_ACTIVE_SUBDIR,\n PROJECT_PRDS_PLANNED_SUBDIR,\n PROJECT_PRDS_COMPLETED_SUBDIR,\n} from './paths.js';\nimport { getDraftPath, getIssueDraftPath } from './pan-dir/index.js';\n\nexport type PrdStatus = 'active' | 'planned' | 'completed' | 'draft';\nexport type PrdFormat = 'subdir' | 'flat' | 'pan-draft';\n\nexport interface PrdLocation {\n /** Absolute path to either the per-issue subdirectory or the flat .md file. */\n path: string;\n format: PrdFormat;\n status: PrdStatus;\n}\n\n/** Read Markdown from any location returned by this module's PRD resolvers. */\nexport async function readPrdContent(loc: PrdLocation): Promise<string | null> {\n if (loc.format === 'flat' || loc.format === 'pan-draft') {\n return readFile(loc.path, 'utf8').catch(() => null);\n }\n\n const files = (await readdir(loc.path).catch(() => [] as string[]))\n .filter((file) => file.endsWith('.md'))\n .sort((a, b) => {\n if (a === 'prd.md') return -1;\n if (b === 'prd.md') return 1;\n return a.localeCompare(b);\n });\n const firstMarkdown = files[0];\n if (!firstMarkdown) return null;\n return readFile(join(loc.path, firstMarkdown), 'utf8').catch(() => null);\n}\n\nconst STATUS_DIRS: Record<Exclude<PrdStatus, 'draft'>, string> = {\n active: PROJECT_PRDS_ACTIVE_SUBDIR,\n planned: PROJECT_PRDS_PLANNED_SUBDIR,\n completed: PROJECT_PRDS_COMPLETED_SUBDIR,\n};\n\nfunction statusRoot(projectPath: string, status: Exclude<PrdStatus, 'draft'>): string {\n return join(projectPath, PROJECT_DOCS_SUBDIR, PROJECT_PRDS_SUBDIR, STATUS_DIRS[status]);\n}\n\n/**\n * Canonical lowercase subdirectory path. Always use this for NEW writes.\n * Does not check existence — callers create the directory as needed.\n */\nexport function canonicalPrdSubdirSync(\n projectPath: string,\n issueId: string,\n status: Exclude<PrdStatus, 'draft'>,\n): string {\n return join(statusRoot(projectPath, status), issueId.toLowerCase());\n}\n\n/**\n * Find an existing PRD for an issue under a single lifecycle status.\n * Checks all four legacy/buggy formats, preferring canonical.\n */\nexport function findPrdAtStatusSync(\n projectPath: string,\n issueId: string,\n status: Exclude<PrdStatus, 'draft'>,\n): PrdLocation | null {\n const root = statusRoot(projectPath, status);\n const lower = issueId.toLowerCase();\n const upper = issueId.toUpperCase();\n\n const candidates: PrdLocation[] = [\n { path: join(root, lower), format: 'subdir', status },\n { path: join(root, upper), format: 'subdir', status },\n { path: join(root, `${lower}-plan.md`), format: 'flat', status },\n { path: join(root, `${upper}-plan.md`), format: 'flat', status },\n ];\n\n for (const c of candidates) {\n if (existsSync(c.path)) return c;\n }\n return null;\n}\n\nfunction draftPrdCandidates(projectPath: string, issueId: string): PrdLocation[] {\n // Canonical drafts exist in both filename cases on disk (the door writes\n // UPPER.md; humans and conversations historically wrote lower.md) — accept\n // either, matching checkPrdGateSync.\n return [\n { path: getIssueDraftPath(projectPath, issueId), format: 'pan-draft', status: 'draft' },\n { path: getDraftPath(projectPath, `${issueId.toLowerCase()}.md`), format: 'pan-draft', status: 'draft' },\n ];\n}\n\nexport function findDraftPrdSync(projectPath: string, issueId: string): PrdLocation | null {\n for (const candidate of draftPrdCandidates(projectPath, issueId)) {\n if (existsSync(candidate.path)) return candidate;\n }\n return null;\n}\n\nexport async function findDraftPrdAsync(projectPath: string, issueId: string): Promise<PrdLocation | null> {\n for (const candidate of draftPrdCandidates(projectPath, issueId)) {\n try {\n await access(candidate.path);\n return candidate;\n } catch {\n // Try the historical filename case before reporting no draft.\n }\n }\n return null;\n}\n\n/**\n * Find a PRD across all lifecycle statuses, in priority order:\n * active → completed → planned → draft. Returns the first match or null.\n */\nexport function findPrdAnywhereSync(\n projectPath: string,\n issueId: string,\n): PrdLocation | null {\n for (const status of ['active', 'completed', 'planned'] as const) {\n const loc = findPrdAtStatusSync(projectPath, issueId, status);\n if (loc) return loc;\n }\n return findDraftPrdSync(projectPath, issueId)\n}\n\n// ─── Effect variants (PAN-1249) ───────────────────────────────────────────────\n//\n// Path-only helpers stay synchronous. Draft discovery uses the promise-based\n// filesystem door so resource refreshes never block the dashboard event loop.\n\n/** Effect variant of {@link canonicalPrdSubdirSync}. */\nexport const canonicalPrdSubdir = (\n projectPath: string,\n issueId: string,\n status: Exclude<PrdStatus, 'draft'>,\n): Effect.Effect<string, never> =>\n Effect.sync(() => canonicalPrdSubdirSync(projectPath, issueId, status));\n\n/** Effect variant of {@link findPrdAtStatusSync}. */\nexport const findPrdAtStatus = (\n projectPath: string,\n issueId: string,\n status: Exclude<PrdStatus, 'draft'>,\n): Effect.Effect<PrdLocation | null, never> =>\n Effect.sync(() => findPrdAtStatusSync(projectPath, issueId, status));\n\n/** Async Effect variant of {@link findDraftPrdAsync}. */\nexport const findDraftPrd = (\n projectPath: string,\n issueId: string,\n): Effect.Effect<PrdLocation | null, never> =>\n Effect.promise(() => findDraftPrdAsync(projectPath, issueId));\n\n/** Effect variant of {@link findPrdAnywhereSync}. */\nexport const findPrdAnywhere = (\n projectPath: string,\n issueId: string,\n): Effect.Effect<PrdLocation | null, never> =>\n Effect.sync(() => findPrdAnywhereSync(projectPath, issueId));\n","/**\n * archive-planning — PRD active→completed + workspace .pan/ preservation.\n *\n * Consolidates PRD moving from close-out.ts, merge-agent.ts, and the\n * approve endpoint into a single idempotent operation.\n *\n * Steps:\n * 1. Move PRD from docs/prds/active/ → docs/prds/completed/ (git mv, fallback to copy)\n * 2. Archive workspace .pan/ artifacts to ~/.overdeck/archives/<issue>/\n * 3. Rotate previous archives to prevent overwrite\n */\n\nimport { existsSync, readdirSync } from 'fs';\nimport { mkdir, cp, rename } from 'fs/promises';\nimport { join, dirname } from 'path';\nimport { exec } from 'child_process';\nimport { promisify } from 'util';\nimport { Effect } from 'effect';\nimport {\n ARCHIVES_DIR,\n PROJECT_DOCS_SUBDIR,\n PROJECT_PRDS_SUBDIR,\n PROJECT_PRDS_ACTIVE_SUBDIR,\n PROJECT_PRDS_COMPLETED_SUBDIR,\n} from '../paths.js';\nimport {\n PAN_CONTINUE_FILENAME,\n PAN_CONTEXT_FILENAME,\n PAN_DIRNAME,\n PAN_FEEDBACK_DIRNAME,\n PAN_SESSIONS_FILENAME,\n PAN_SPEC_FILENAME,\n} from '../pan-dir/index.js';\nimport { findPrdAtStatusSync, canonicalPrdSubdirSync } from '../prd-locations.js';\nimport type { LifecycleContext, StepResult, ArchiveOptions } from './types.js';\nimport { stepOk, stepSkipped, stepFailed } from './types.js';\n\nconst execAsync = promisify(exec);\n\n/**\n * Find ALL workspace paths matching an issue across canonical and legacy\n * naming conventions. Returns every candidate that exists on disk, so callers\n * (especially teardown) can clean up drift where the same issue ended up with\n * both `feature-pan-XXXX` and `feature-XXXX` directories. Order is canonical\n * → legacy, so first-match callers still prefer the canonical form.\n */\nexport function findAllWorkspacePaths(projectPath: string, issueLower: string): string[] {\n // e.g. \"pan-488\" → \"488\" for legacy workspaces named feature-488\n const numericSuffix = issueLower.replace(/^[a-z]+-/, '');\n const workspaceRoot = join(projectPath, 'workspaces');\n const slotWorkspaces = existsSync(workspaceRoot)\n ? readdirSync(workspaceRoot).filter(name => name.startsWith(`feature-${issueLower}-slot-`)).map(name => join(workspaceRoot, name))\n : [];\n const candidates = [\n join(projectPath, 'workspaces', `feature-${issueLower}`),\n // Strike worktrees (`pan strike`) use the `-strike` suffix (PAN-1721)\n join(projectPath, 'workspaces', `feature-${issueLower}-strike`),\n join(projectPath, 'workspaces', `feature-${numericSuffix}`),\n join(projectPath, 'workspaces', issueLower),\n join(projectPath, '.worktrees', issueLower),\n join(dirname(projectPath), `feature-${issueLower}`),\n ...slotWorkspaces,\n ];\n return candidates.filter((p) => existsSync(p));\n}\n\n/**\n * Find the workspace path for an issue.\n * Checks multiple conventional locations, including legacy numeric-suffix naming.\n * Prefer findAllWorkspacePaths when cleaning up — this only returns the first match.\n */\nexport function findWorkspacePath(projectPath: string, issueLower: string): string | null {\n return findAllWorkspacePaths(projectPath, issueLower)[0] ?? null;\n}\n\n/**\n * Infer the git branch for an issue from its resolved workspace path.\n * Strike workspaces (path ends in `-strike`) use `strike/<id>`; every other\n * workspace uses `feature/<id>`. PAN-2270: review-dispatch callers must derive\n * the branch this way instead of hardcoding `feature/${issueLower}`.\n */\nexport function inferBranchFromWorkspace(workspacePath: string, issueLower: string): string {\n if (workspacePath.endsWith('-strike')) return `strike/${issueLower}`;\n return `feature/${issueLower}`;\n}\n\n/**\n * Move PRD from active/ to completed/ directory.\n * Uses git mv with fallback to plain copy. Idempotent — skips if already completed.\n */\nexport function movePrd(\n ctx: LifecycleContext,\n opts: ArchiveOptions = {},\n): Effect.Effect<StepResult> {\n return Effect.tryPromise({\n try: () => movePrdImpl(ctx, opts),\n catch: (err) => err,\n }).pipe(\n Effect.catch((err) =>\n Effect.succeed(stepFailed('archive-planning:move-prd', `Failed to preserve PRD: ${(err as Error).message}`)),\n ),\n );\n}\n\nasync function movePrdImpl(\n ctx: LifecycleContext,\n opts: ArchiveOptions,\n): Promise<StepResult> {\n const { pushToRemote = true } = opts;\n const step = 'archive-planning:move-prd';\n\n // Idempotent skip: if any completed PRD already exists in any format, we're done.\n if (findPrdAtStatusSync(ctx.projectPath, ctx.issueId, 'completed')) {\n return stepSkipped(step, ['PRD already in completed/']);\n }\n\n // Find source PRD in active/ in any format/case (subdir lower/upper, flat lower/upper).\n const source = findPrdAtStatusSync(ctx.projectPath, ctx.issueId, 'active');\n if (!source) {\n return stepSkipped(step, ['No PRD found in active/ (may not have had one)']);\n }\n\n // Destination is always the canonical lowercase form, mirroring source format.\n const issueLower = ctx.issueId.toLowerCase();\n const completedSubdir = canonicalPrdSubdirSync(ctx.projectPath, ctx.issueId, 'completed');\n const completedFlat = join(\n ctx.projectPath, PROJECT_DOCS_SUBDIR, PROJECT_PRDS_SUBDIR,\n PROJECT_PRDS_COMPLETED_SUBDIR, `${issueLower}-plan.md`,\n );\n const dest = source.format === 'subdir' ? completedSubdir : completedFlat;\n const destParent = dirname(dest);\n if (!existsSync(destParent)) {\n await mkdir(destParent, { recursive: true });\n }\n\n const formatLabel = source.format === 'subdir' ? 'PRD subdirectory' : 'PRD';\n\n // For the legacy `flat` format the PRD is a single `.md` file, but the xBRIEF\n // JSON sidecar (`<id>-plan.vbrief.json`) lives next to it and was historically\n // left behind in active/ after merge (PAN-487). Detect it here so we can move\n // it alongside the `.md`. The `subdir` format already moves both files because\n // they share the directory.\n const flatActive = source.format === 'flat'\n ? join(ctx.projectPath, PROJECT_DOCS_SUBDIR, PROJECT_PRDS_SUBDIR, PROJECT_PRDS_ACTIVE_SUBDIR)\n : null;\n const flatCompleted = source.format === 'flat'\n ? join(ctx.projectPath, PROJECT_DOCS_SUBDIR, PROJECT_PRDS_SUBDIR, PROJECT_PRDS_COMPLETED_SUBDIR)\n : null;\n const sidecarLower = flatActive ? join(flatActive, `${issueLower}-plan.vbrief.json`) : null;\n const sidecarUpper = flatActive ? join(flatActive, `${ctx.issueId.toUpperCase()}-plan.vbrief.json`) : null;\n // Always land sidecar at canonical lowercase in completed/.\n const sidecarDest = flatCompleted ? join(flatCompleted, `${issueLower}-plan.vbrief.json`) : null;\n const resolvedSidecarSource = sidecarLower && existsSync(sidecarLower)\n ? sidecarLower\n : (sidecarUpper && existsSync(sidecarUpper) ? sidecarUpper : null);\n\n try {\n await execAsync(`git mv \"${source.path}\" \"${dest}\"`, { cwd: ctx.projectPath });\n if (resolvedSidecarSource && sidecarDest) {\n try {\n await execAsync(`git mv \"${resolvedSidecarSource}\" \"${sidecarDest}\"`, { cwd: ctx.projectPath });\n } catch {\n // sidecar may not be tracked — fall back to plain copy\n try { await cp(resolvedSidecarSource, sidecarDest); } catch { /* non-fatal */ }\n }\n }\n // The commit subject must be commitlint-compliant (type prefix required) —\n // a bare \"Move X PRD to completed\" is rejected by commit-msg, and before the\n // guard below that left the rename staged with the close-out wedged (PAN-3211).\n await execAsync(`git commit -m \"chore: move ${ctx.issueId} PRD to completed\"`, { cwd: ctx.projectPath });\n if (pushToRemote) {\n await execAsync('git push', { cwd: ctx.projectPath });\n }\n const sidecarNote = resolvedSidecarSource ? ' (with xBRIEF sidecar)' : '';\n return stepOk(step, [`Moved ${formatLabel} from active/ to completed/ via git mv${sidecarNote}`]);\n } catch {\n // git mv failed — fall back to plain copy. cp handles both file and directory.\n // Guard: if git mv already moved the source and a LATER step (commit/push)\n // failed, the destination holds the PRD and the rename sits staged. cp would\n // then ENOENT on the vanished source and bury the real failure (PAN-3211).\n if (!existsSync(source.path)) {\n if (existsSync(dest)) {\n return stepFailed(step, 'PRD moved to completed/ but the follow-up git commit failed; the rename is staged — commit it and re-run close-out');\n }\n return stepFailed(step, `PRD source ${source.path} is missing — nothing to preserve`);\n }\n try {\n await cp(source.path, dest, { recursive: true });\n if (!existsSync(dest)) {\n return stepFailed(step, 'PRD copy appeared to succeed but destination not found');\n }\n if (resolvedSidecarSource && sidecarDest) {\n try { await cp(resolvedSidecarSource, sidecarDest); } catch { /* non-fatal */ }\n }\n return stepOk(step, [`Copied ${formatLabel} to completed/ (git mv failed, plain copy succeeded)`]);\n } catch (err) {\n return stepFailed(step, `Failed to preserve PRD: ${(err as Error).message}`);\n }\n }\n}\n\n/**\n * Archive workspace .pan/ artifacts to ~/.overdeck/archives/<issue>/.\n * Rotates previous archives to prevent overwrite.\n * Returns a hard failure if archiving fails — callers must NOT proceed with\n * workspace deletion after an archive failure.\n */\nexport function archiveWorkspaceArtifacts(\n ctx: LifecycleContext,\n): Effect.Effect<StepResult> {\n return Effect.tryPromise({\n try: () => archiveWorkspaceArtifactsImpl(ctx),\n catch: (err) => err,\n }).pipe(\n Effect.catch((err) =>\n Effect.succeed(stepFailed('archive-planning:archive-artifacts', `Failed to archive: ${(err as Error).message}`)),\n ),\n );\n}\n\nasync function archiveWorkspaceArtifactsImpl(\n ctx: LifecycleContext,\n): Promise<StepResult> {\n const issueLower = ctx.issueId.toLowerCase();\n const step = 'archive-planning:archive-artifacts';\n\n const workspacePath = findWorkspacePath(ctx.projectPath, issueLower);\n if (!workspacePath || !existsSync(workspacePath)) {\n return stepSkipped(step, ['No workspace found to archive']);\n }\n\n let archiveDir = join(ARCHIVES_DIR, issueLower);\n\n // Rotate previous archive if it exists\n if (existsSync(archiveDir)) {\n let version = 1;\n while (existsSync(`${archiveDir}.${version}`)) {\n version++;\n }\n const rotatedDir = `${archiveDir}.${version}`;\n // Rename is O(1) metadata vs. copy+delete which is O(archive size). Both\n // paths live under ARCHIVES_DIR so they're on the same filesystem.\n await rename(archiveDir, rotatedDir);\n }\n\n await mkdir(archiveDir, { recursive: true });\n const details: string[] = [];\n\n const panDir = join(workspacePath, PAN_DIRNAME)\n\n const feedbackDir = join(panDir, PAN_FEEDBACK_DIRNAME)\n if (existsSync(feedbackDir)) {\n await cp(feedbackDir, join(archiveDir, 'feedback'), { recursive: true })\n details.push('Archived feedback/')\n }\n\n const continueFile = join(panDir, PAN_CONTINUE_FILENAME)\n if (existsSync(continueFile)) {\n await cp(continueFile, join(archiveDir, PAN_CONTINUE_FILENAME))\n details.push(`Archived ${PAN_CONTINUE_FILENAME}`)\n }\n\n const specFile = join(panDir, PAN_SPEC_FILENAME)\n if (existsSync(specFile)) {\n await cp(specFile, join(archiveDir, PAN_SPEC_FILENAME))\n details.push(`Archived ${PAN_SPEC_FILENAME}`)\n }\n\n const sessionsFile = join(panDir, PAN_SESSIONS_FILENAME)\n if (existsSync(sessionsFile)) {\n await cp(sessionsFile, join(archiveDir, PAN_SESSIONS_FILENAME))\n details.push(`Archived ${PAN_SESSIONS_FILENAME}`)\n }\n\n const contextFile = join(panDir, PAN_CONTEXT_FILENAME)\n if (existsSync(contextFile)) {\n await cp(contextFile, join(archiveDir, PAN_CONTEXT_FILENAME))\n details.push(`Archived ${PAN_CONTEXT_FILENAME}`)\n }\n\n const prdFile = join(panDir, 'prd.md')\n if (existsSync(prdFile)) {\n await cp(prdFile, join(archiveDir, 'prd.md'))\n details.push('Archived workspace prd.md')\n }\n\n details.push(`Archived to ${archiveDir}`);\n return stepOk(step, details);\n}\n\n/**\n * Full archive-planning operation: move PRD + archive workspace artifacts.\n * Archive failure is a hard fail — do not proceed with workspace deletion.\n */\nexport function archivePlanning(\n ctx: LifecycleContext,\n opts: ArchiveOptions = {},\n): Effect.Effect<StepResult[]> {\n return Effect.gen(function* () {\n const results: StepResult[] = [];\n const prdResult = yield* movePrd(ctx, opts);\n results.push(prdResult);\n const archiveResult = yield* archiveWorkspaceArtifacts(ctx);\n results.push(archiveResult);\n return results;\n });\n}\n","/**\n * Enumerate host PIDs whose current working directory is inside a given\n * directory tree, via /proc/<pid>/cwd.\n *\n * This is the safe selector for \"orphaned workspace processes\" (leftover vite\n * watchers, npm/vitest runners spawned in a workspace). It deliberately does\n * NOT use `lsof +D <dir>`: Bun installs node_modules as hardlinks from a\n * global cache, so long-lived platform processes (the dashboard server, every\n * conversation's PTY supervisor) mmap native addons (e.g. pty.node) whose\n * inode also lives inside every workspace's node_modules. `lsof +D` reports\n * them as holding files \"in\" the workspace, and killing that list takes down\n * the dashboard and all live conversations (2026-07-16 incident: `pan close\n * PAN-2532` killed the dashboard and an active operator conversation).\n */\nimport { readdir, readlink } from 'fs/promises';\n\nexport async function pidsWithCwdUnder(dir: string): Promise<string[]> {\n const prefix = dir.endsWith('/') ? dir : `${dir}/`;\n const pids: string[] = [];\n let entries: string[];\n try {\n entries = await readdir('/proc');\n } catch {\n return [];\n }\n for (const entry of entries) {\n if (!/^\\d+$/.test(entry)) continue;\n try {\n const cwd = await readlink(`/proc/${entry}/cwd`);\n if (cwd === dir || cwd.startsWith(prefix)) pids.push(entry);\n } catch {\n // Process exited, or belongs to another user — not killable by us anyway.\n }\n }\n return pids;\n}\n","/**\n * teardown-workspace — Full workspace cleanup.\n *\n * Consolidates workspace teardown from close-out.ts and workspace-manager.ts.\n * Handles: tmux sessions, TLDR daemon, Docker containers, git worktrees,\n * agent state directories, and (optionally) git branches.\n *\n * The workspace-manager's removeWorkspace() handles additional project-specific\n * cleanup (DNS, tunnels, Hume, ports) that this module does not cover.\n * In Phase 2, removeWorkspace() will delegate to this module for the common steps.\n */\n\nimport { existsSync } from 'fs';\nimport { readFile, rm } from 'fs/promises';\nimport { join, basename, dirname } from 'path';\nimport { homedir } from 'os';\nimport { exec } from 'child_process';\nimport { promisify } from 'util';\nimport { Effect } from 'effect';\nimport { AGENTS_DIR } from '../paths.js';\nimport { pidsWithCwdUnder } from '../process-cwd.js';\nimport { killSession, sessionExists, listSessionNames } from '../tmux.js';\nimport type { LifecycleContext, StepResult, TeardownOptions } from './types.js';\nimport { stepOk, stepSkipped, stepFailed } from './types.js';\nimport { findAllWorkspacePaths, findWorkspacePath } from './archive-planning.js';\nimport { getContainersReferencingWorkspacePath } from '../workspace-manager.js';\nimport { DEVCONTAINER_DIRNAME } from '../workspace/devcontainer-renderer.js';\nimport { removeAgentStateDir } from '../agents/state-dir-removal.js';\n\nconst execAsync = promisify(exec);\n\n/**\n * Kill tmux sessions associated with an issue.\n */\nfunction killTmuxSessions(issueLower: string): Effect.Effect<StepResult> {\n return Effect.tryPromise({\n try: () => killTmuxSessionsImpl(issueLower),\n catch: (err) => err,\n }).pipe(\n Effect.catch((err) =>\n Effect.succeed(stepFailed('teardown:tmux-sessions', `Failed: ${(err as Error).message}`)),\n ),\n );\n}\n\nasync function killTmuxSessionsImpl(issueLower: string): Promise<StepResult> {\n const step = 'teardown:tmux-sessions';\n let killed = 0;\n\n // Exact-match sessions (agent, test, merge, planning, strike).\n const exactPatterns = [\n `agent-${issueLower}`,\n `test-${issueLower}`,\n `merge-${issueLower}`,\n `planning-${issueLower}`,\n `strike-${issueLower}`,\n ];\n for (const session of exactPatterns) {\n if (await Effect.runPromise(sessionExists(session))) {\n try {\n await Effect.runPromise(killSession(session));\n killed++;\n } catch {\n // session may have died between check and kill\n }\n }\n }\n\n // Pattern-match sessions for review coordinators, review specialists, and\n // canonical specialists. Today's naming uses the UPPER issue ID (PAN-1024)\n // not the lower form, and includes session families that the prior\n // single-regex didn't cover. PAN-1024 close-out left\n // `review-coordinator-PAN-1024-...` and\n // `specialist-overdeck-PAN-1024-test-agent` alive (2026-05-09).\n //\n // Patterns we now match (case-insensitive on the issue ID):\n // - review-coordinator-<ISSUE>-<timestamp>\n // - review-<ISSUE>-<timestamp>-<role> (legacy)\n // - specialist-<projectKey>-<ISSUE>-<role> (canonical PAN-830/915)\n try {\n const allSessions = await Effect.runPromise(listSessionNames());\n const escapedLower = issueLower.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&');\n const escapedUpper = issueLower.toUpperCase().replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&');\n const issuePart = `(${escapedLower}|${escapedUpper})`;\n const patterns: RegExp[] = [\n new RegExp(`^review-coordinator-${issuePart}-\\\\d+`),\n new RegExp(`^review-${issuePart}-\\\\d+`),\n new RegExp(`^specialist-[^-]+(?:-[^-]+)*?-${issuePart}-`),\n ];\n const matchedSessions = allSessions.filter(s => patterns.some(p => p.test(s)));\n for (const session of matchedSessions) {\n try {\n await Effect.runPromise(killSession(session));\n killed++;\n } catch {\n // session may have died between check and kill\n }\n }\n } catch {\n // Session listing may fail if tmux server is not running\n }\n\n // NOTE: Per-project ephemeral specialists (specialist-{project}-{type}) are NOT killed here.\n // They belong to the project, not the issue, and accumulate context across issues via --resume.\n // Their grace period / idle timeout handles cleanup when no new work arrives.\n\n if (killed > 0) {\n return stepOk(step, [`Killed ${killed} tmux session(s)`]);\n }\n return stepSkipped(step, ['No tmux sessions found']);\n}\n\n/**\n * Stop TLDR daemon if workspace has a .venv.\n */\nfunction stopTldrDaemon(workspacePath: string): Effect.Effect<StepResult> {\n return Effect.tryPromise({\n try: () => stopTldrDaemonImpl(workspacePath),\n catch: (err) => err,\n }).pipe(\n Effect.catch(() =>\n Effect.succeed(stepSkipped('teardown:tldr-daemon', ['TLDR daemon not running or failed to stop (non-fatal)'])),\n ),\n );\n}\n\nasync function stopTldrDaemonImpl(workspacePath: string): Promise<StepResult> {\n const step = 'teardown:tldr-daemon';\n const venvPath = join(workspacePath, '.venv');\n if (!existsSync(venvPath)) {\n return stepSkipped(step, ['No .venv found']);\n }\n try {\n const { getTldrDaemonServiceSync } = await import('../tldr-daemon.js');\n const tldrService = getTldrDaemonServiceSync(workspacePath, venvPath);\n await tldrService.stop();\n return stepOk(step, ['Stopped TLDR daemon']);\n } catch {\n return stepSkipped(step, ['TLDR daemon not running or failed to stop (non-fatal)']);\n }\n}\n\n/**\n * Stop Docker containers for the workspace.\n */\nfunction stopDocker(\n workspacePath: string,\n issueLower: string,\n): Effect.Effect<StepResult> {\n return Effect.tryPromise({\n try: () => stopDockerImpl(workspacePath, issueLower),\n catch: (err) => err,\n }).pipe(\n Effect.catch(() =>\n Effect.succeed(stepSkipped('teardown:docker', ['Docker cleanup skipped (not running or failed)'])),\n ),\n );\n}\n\nasync function stopDockerImpl(\n workspacePath: string,\n issueLower: string,\n): Promise<StepResult> {\n const step = 'teardown:docker';\n try {\n const { stopWorkspaceDocker } = await import('../workspace-manager.js');\n await Effect.runPromise(stopWorkspaceDocker(workspacePath, issueLower));\n return stepOk(step, ['Stopped Docker containers']);\n } catch {\n return stepSkipped(step, ['Docker cleanup skipped (not running or failed)']);\n }\n}\n\n/**\n * Detect whether a PID belongs to a process running inside a Docker container.\n * Container processes appear in host lsof output when paths are bind-mounted.\n */\nasync function isDockerContainerProcess(pid: string): Promise<boolean> {\n try {\n const cgroup = await readFile(`/proc/${pid}/cgroup`, 'utf-8');\n return cgroup.includes('/docker-') || cgroup.includes('/docker/');\n } catch {\n return false;\n }\n}\n\n/**\n * Kill orphaned host processes for a workspace.\n */\nfunction killOrphanedProcesses(workspacePath: string): Effect.Effect<StepResult> {\n return Effect.tryPromise({\n try: () => killOrphanedProcessesImpl(workspacePath),\n catch: (err) => err,\n }).pipe(\n Effect.catch(() =>\n Effect.succeed(stepSkipped('teardown:orphaned-processes', ['Orphaned process cleanup failed (non-fatal)'])),\n ),\n );\n}\n\nasync function killOrphanedProcessesImpl(workspacePath: string): Promise<StepResult> {\n const step = 'teardown:orphaned-processes';\n try {\n // Find PIDs with cwd inside the workspace. NEVER select by open files\n // (`lsof +D`): Bun's hardlinked node_modules make the dashboard and every\n // conversation's PTY supervisor appear to hold files \"in\" any workspace\n // via mmap'd native addons — see src/lib/process-cwd.ts.\n const pids = await pidsWithCwdUnder(workspacePath);\n\n if (pids.length === 0) {\n return stepSkipped(step, ['No orphaned processes found']);\n }\n\n // Don't kill our own process or the dashboard\n const myPid = String(process.pid);\n const safePids = pids.filter(p => p !== myPid);\n\n if (safePids.length === 0) {\n return stepSkipped(step, ['No orphaned processes to kill']);\n }\n\n // Filter out Docker container processes — they appear in host lsof due to bind mounts\n const hostPids: string[] = [];\n for (const pid of safePids) {\n if (!(await isDockerContainerProcess(pid))) {\n hostPids.push(pid);\n }\n }\n\n if (hostPids.length === 0) {\n return stepSkipped(step, ['No orphaned host processes to kill (all were container processes)']);\n }\n\n await execAsync(`kill ${hostPids.join(' ')} 2>/dev/null || true`, { encoding: 'utf-8', timeout: 5000 });\n return stepOk(step, [`Killed ${hostPids.length} orphaned process(es)`]);\n } catch {\n return stepSkipped(step, ['Orphaned process cleanup failed (non-fatal)']);\n }\n}\n\n/**\n * Remove git worktree for the workspace.\n */\nfunction removeWorktree(\n projectPath: string,\n workspacePath: string,\n): Effect.Effect<StepResult> {\n return Effect.tryPromise({\n try: () => removeWorktreeImpl(projectPath, workspacePath),\n catch: (err) => err,\n }).pipe(\n Effect.catch((err) =>\n Effect.succeed(stepFailed('teardown:worktree', `Failed to remove workspace: ${(err as Error).message}`)),\n ),\n );\n}\n\nasync function removeWorktreeImpl(\n projectPath: string,\n workspacePath: string,\n): Promise<StepResult> {\n const step = 'teardown:worktree';\n if (!existsSync(workspacePath)) {\n return stepSkipped(step, ['Workspace directory does not exist']);\n }\n\n // Guard: never delete workspace (and its `.devcontainer/`) while containers\n // still reference compose paths inside it.\n const orphanedContainers = await Effect.runPromise(getContainersReferencingWorkspacePath(workspacePath));\n if (orphanedContainers.length > 0) {\n return stepFailed(\n step,\n `Cannot remove workspace: ${orphanedContainers.length} Docker container(s) still reference compose paths in ${DEVCONTAINER_DIRNAME}/. ` +\n `Run workspace Docker cleanup first or stop the containers manually.`,\n );\n }\n\n try {\n await execAsync(`git worktree remove \"${workspacePath}\" --force`, { cwd: projectPath });\n return stepOk(step, ['Removed git worktree']);\n } catch {\n // worktree remove failed — try direct removal\n try {\n await rm(workspacePath, { recursive: true, force: true });\n return stepOk(step, ['Removed workspace directory after worktree removal failed']);\n } catch (err) {\n return stepFailed(step, `Failed to remove workspace: ${(err as Error).message}`);\n }\n }\n}\n\n/**\n * Remove every agent state directory tied to an issue.\n */\nfunction removeAgentState(issueLower: string): Effect.Effect<StepResult> {\n return Effect.tryPromise({\n try: () => removeAgentStateImpl(issueLower),\n catch: (err) => err,\n }).pipe(\n Effect.catch((err) =>\n Effect.succeed(stepFailed('teardown:agent-state', `Failed: ${(err as Error).message}`)),\n ),\n );\n}\n\nasync function removeAgentStateImpl(issueLower: string): Promise<StepResult> {\n const step = 'teardown:agent-state';\n const { readdir } = await import('fs/promises');\n\n let entries: string[];\n try {\n entries = await readdir(AGENTS_DIR);\n } catch {\n return stepSkipped(step, ['Agents directory not present']);\n }\n\n const work = `agent-${issueLower}`;\n const planner = `planning-${issueLower}`;\n const strike = `strike-${issueLower}`;\n const specialistPrefix = `agent-${issueLower}-`;\n const targets = entries.filter(name =>\n name === work || name === planner || name === strike || name.startsWith(specialistPrefix),\n );\n\n let removed = 0;\n let preservedTranscripts = 0;\n for (const name of targets) {\n try {\n const result = await removeAgentStateDir(join(AGENTS_DIR, name));\n if (result.removedDir || result.preservedTranscripts > 0) removed++;\n preservedTranscripts += result.preservedTranscripts;\n } catch { /* non-fatal */ }\n }\n\n if (removed > 0) {\n return stepOk(step, [\n `Cleaned ${removed} agent state director${removed === 1 ? 'y' : 'ies'} (${preservedTranscripts} transcript files preserved)`,\n ]);\n }\n return stepSkipped(step, ['No agent state directories found']);\n}\n\n/**\n * Delete feature branches (local + remote).\n */\nfunction deleteBranches(\n projectPath: string,\n issueLower: string,\n): Effect.Effect<StepResult> {\n return Effect.tryPromise({\n try: () => deleteBranchesImpl(projectPath, issueLower),\n catch: (err) => err,\n }).pipe(\n Effect.catch((err) =>\n Effect.succeed(stepFailed('teardown:branches', `Failed: ${(err as Error).message}`)),\n ),\n );\n}\n\nasync function deleteBranchesImpl(\n projectPath: string,\n issueLower: string,\n): Promise<StepResult> {\n const step = 'teardown:branches';\n const branchName = `feature/${issueLower}`;\n const details: string[] = [];\n const localSlots = await execAsync(`git for-each-ref --format=\"%(refname:short)\" \"refs/heads/feature/${issueLower}-slot-*\"`, { cwd: projectPath, encoding: 'utf-8' })\n .then(({ stdout }) => stdout.trim().split('\\n').filter(Boolean)).catch(() => [] as string[]);\n const remoteSlots = await execAsync(`git ls-remote --heads origin \"feature/${issueLower}-slot-*\"`, { cwd: projectPath, encoding: 'utf-8' })\n .then(({ stdout }) => stdout.trim().split('\\n').map(line => line.split('refs/heads/')[1]).filter((name): name is string => Boolean(name))).catch(() => [] as string[]);\n\n // Delete local branch\n try {\n await execAsync(`git branch -D \"${branchName}\"`, { cwd: projectPath, encoding: 'utf-8' });\n details.push(`Deleted local branch ${branchName}`);\n } catch {\n details.push(`Local branch ${branchName} not found (already deleted)`);\n }\n\n for (const slotBranch of localSlots) {\n try {\n await execAsync(`git branch -D ${JSON.stringify(slotBranch)}`, { cwd: projectPath, encoding: 'utf-8' });\n details.push(`Deleted local branch ${slotBranch}`);\n } catch { details.push(`Failed to delete local branch ${slotBranch}`); }\n }\n\n // Delete remote branch\n try {\n await execAsync(`git push origin --delete \"${branchName}\"`, { cwd: projectPath, encoding: 'utf-8' });\n details.push(`Deleted remote branch ${branchName}`);\n } catch {\n details.push(`Remote branch ${branchName} not found (already deleted)`);\n }\n\n for (const slotBranch of remoteSlots) {\n try {\n await execAsync(`git push origin --delete ${JSON.stringify(slotBranch)}`, { cwd: projectPath, encoding: 'utf-8' });\n details.push(`Deleted remote branch ${slotBranch}`);\n } catch { details.push(`Failed to delete remote branch ${slotBranch}`); }\n }\n\n return stepOk(step, details);\n}\n\n/**\n * Whether a local branch exists and is fully merged into main.\n */\nasync function localBranchMergedState(\n projectPath: string,\n branchName: string,\n): Promise<{ exists: boolean; merged: boolean }> {\n try {\n await execAsync(`git show-ref --verify --quiet \"refs/heads/${branchName}\"`, { cwd: projectPath, encoding: 'utf-8' });\n } catch {\n return { exists: false, merged: false };\n }\n try {\n await execAsync(`git merge-base --is-ancestor \"${branchName}\" main`, { cwd: projectPath, encoding: 'utf-8' });\n return { exists: true, merged: true };\n } catch {\n return { exists: true, merged: false };\n }\n}\n\n/**\n * Delete the strike branch (local + remote) once it is merged into main.\n *\n * Strike work (`pan strike`) lands on main directly, so a merged\n * `strike/<issue>` branch is pure residue — it is deleted on every teardown,\n * not just when `deleteBranches` is requested (PAN-1721). An UNMERGED strike\n * branch may hold the only copy of an aborted strike's work, so it is\n * preserved and reported instead.\n */\nfunction deleteStrikeBranch(\n projectPath: string,\n issueLower: string,\n): Effect.Effect<StepResult> {\n return Effect.tryPromise({\n try: () => deleteStrikeBranchImpl(projectPath, issueLower),\n catch: (err) => err,\n }).pipe(\n Effect.catch((err) =>\n Effect.succeed(stepFailed('teardown:strike-branch', `Failed: ${(err as Error).message}`)),\n ),\n );\n}\n\nasync function deleteStrikeBranchImpl(\n projectPath: string,\n issueLower: string,\n): Promise<StepResult> {\n const step = 'teardown:strike-branch';\n const branchName = `strike/${issueLower}`;\n const details: string[] = [];\n\n // Local branch\n const local = await localBranchMergedState(projectPath, branchName);\n if (local.exists) {\n if (local.merged) {\n try {\n await execAsync(`git branch -D \"${branchName}\"`, { cwd: projectPath, encoding: 'utf-8' });\n details.push(`Deleted local branch ${branchName}`);\n } catch (err) {\n details.push(`Failed to delete local branch ${branchName}: ${(err as Error).message}`);\n }\n } else {\n details.push(`Preserved local branch ${branchName} — not merged to main`);\n }\n }\n\n // Remote branch\n try {\n const { stdout: remoteRef } = await execAsync(\n `git ls-remote --heads origin \"${branchName}\" 2>/dev/null || true`,\n { cwd: projectPath, encoding: 'utf-8' },\n );\n if (remoteRef.trim()) {\n let remoteMerged = false;\n try {\n await execAsync(`git fetch origin \"${branchName}\"`, { cwd: projectPath, encoding: 'utf-8' });\n await execAsync('git merge-base --is-ancestor FETCH_HEAD main', { cwd: projectPath, encoding: 'utf-8' });\n remoteMerged = true;\n } catch {\n remoteMerged = false;\n }\n if (remoteMerged) {\n await execAsync(`git push origin --delete \"${branchName}\"`, { cwd: projectPath, encoding: 'utf-8' });\n details.push(`Deleted remote branch ${branchName}`);\n } else {\n details.push(`Preserved remote branch ${branchName} — not merged to main`);\n }\n }\n } catch {\n // remote check/delete failed — non-fatal\n }\n\n if (details.length === 0) {\n return stepSkipped(step, [`No strike branch ${branchName} found`]);\n }\n return details.some(d => d.startsWith('Failed'))\n ? stepFailed(step, details.join('; '))\n : stepOk(step, details);\n}\n\n/**\n * Remove leftover strike worktrees (`workspaces/feature-<issue>-strike`).\n *\n * Runs when the regular worktree removal is gated off (remove_workspace=false\n * at close-out): a strike worktree has no post-close purpose once the strike\n * branch is merged or gone, and leaving it keeps the closed issue alive in\n * resource discovery (PAN-1721). Worktrees whose strike branch has unmerged\n * commits are preserved alongside the branch.\n */\nfunction removeStrikeWorktrees(\n projectPath: string,\n issueLower: string,\n): Effect.Effect<StepResult> {\n return Effect.tryPromise({\n try: () => removeStrikeWorktreesImpl(projectPath, issueLower),\n catch: (err) => err,\n }).pipe(\n Effect.catch((err) =>\n Effect.succeed(stepFailed('teardown:strike-worktree', `Failed: ${(err as Error).message}`)),\n ),\n );\n}\n\nasync function removeStrikeWorktreesImpl(\n projectPath: string,\n issueLower: string,\n): Promise<StepResult> {\n const step = 'teardown:strike-worktree';\n const strikePaths = findAllWorkspacePaths(projectPath, issueLower).filter(p => p.endsWith('-strike'));\n if (strikePaths.length === 0) {\n return stepSkipped(step, ['No strike worktree found']);\n }\n\n const branch = await localBranchMergedState(projectPath, `strike/${issueLower}`);\n if (branch.exists && !branch.merged) {\n return stepSkipped(step, [`Preserved strike worktree — strike/${issueLower} not merged to main`]);\n }\n\n const details: string[] = [];\n for (const p of strikePaths) {\n const result = await removeWorktreeImpl(projectPath, p);\n details.push(...(result.details ?? [result.error ?? result.step]));\n if (!result.success && !result.skipped) {\n return stepFailed(step, `Failed to remove strike worktree ${p}: ${result.error ?? 'unknown error'}`);\n }\n }\n return stepOk(step, details);\n}\n\n/**\n * Clear shadow state for an issue.\n */\nfunction clearShadowState(issueId: string): Effect.Effect<StepResult> {\n return Effect.tryPromise({\n try: () => clearShadowStateImpl(issueId),\n catch: (err) => err,\n }).pipe(\n Effect.catch(() =>\n Effect.succeed(stepSkipped('teardown:shadow-state', ['Shadow state cleanup skipped (non-fatal)'])),\n ),\n );\n}\n\nasync function clearShadowStateImpl(issueId: string): Promise<StepResult> {\n const step = 'teardown:shadow-state';\n try {\n const { removeShadowState } = await import('../shadow-state.js');\n const result = removeShadowState(issueId);\n if (result.success) {\n return stepOk(step, [`Cleared shadow state for ${issueId}`]);\n }\n return stepSkipped(step, ['No shadow state found']);\n } catch {\n return stepSkipped(step, ['Shadow state cleanup skipped (non-fatal)']);\n }\n}\n\n/**\n * Remove legacy .planning/<issue>/ directory from project root.\n */\nfunction clearLegacyPlanningDir(\n projectPath: string,\n issueLower: string,\n): Effect.Effect<StepResult> {\n return Effect.tryPromise({\n try: async () => {\n const step = 'teardown:legacy-planning-dir';\n const legacyDir = join(projectPath, '.planning', issueLower);\n if (!existsSync(legacyDir)) {\n return stepSkipped(step, ['No legacy planning directory found']);\n }\n await rm(legacyDir, { recursive: true, force: true });\n return stepOk(step, [`Deleted legacy planning dir: ${legacyDir}`]);\n },\n catch: (err) => err,\n }).pipe(\n Effect.catch((err) =>\n Effect.succeed(stepFailed('teardown:legacy-planning-dir', `Failed to delete legacy planning dir: ${(err as Error).message}`)),\n ),\n );\n}\n\n/**\n * Build template placeholders for project-specific cleanup (tunnel, Hume).\n */\nfunction buildPlaceholders(\n ctx: LifecycleContext,\n opts: TeardownOptions,\n workspacePath: string,\n) {\n const issueLower = ctx.issueId.toLowerCase();\n const featureFolder = `feature-${issueLower}`;\n const projName = opts.projectName || ctx.projectName || basename(ctx.projectPath);\n const domain = opts.workspaceConfig?.dns?.domain || 'localhost';\n return {\n FEATURE_NAME: issueLower,\n FEATURE_FOLDER: featureFolder,\n BRANCH_NAME: `feature/${issueLower}`,\n COMPOSE_PROJECT: `${projName}-${featureFolder}`,\n DOMAIN: domain,\n PROJECT_NAME: projName,\n PROJECT_PATH: ctx.projectPath,\n WORKSPACE_PATH: workspacePath,\n };\n}\n\n/**\n * Remove Cloudflare tunnel ingress for workspace.\n */\nfunction removeTunnelConfig(\n tunnelConfig: any,\n placeholders: Record<string, string>,\n): Effect.Effect<StepResult> {\n return Effect.tryPromise({\n try: async () => {\n const { removeTunnelIngress } = await import('../tunnel.js');\n const result = await Effect.runPromise(removeTunnelIngress(tunnelConfig, placeholders as any));\n return stepOk('teardown:tunnel', result.steps || ['Removed tunnel ingress']);\n },\n catch: (err) => err,\n }).pipe(\n Effect.catch((err) =>\n Effect.succeed(stepSkipped('teardown:tunnel', [`Tunnel cleanup warning: ${(err as Error).message}`])),\n ),\n );\n}\n\n/**\n * Remove Hume EVI config for workspace.\n */\nfunction removeHumeEviConfig(\n humeConfig: any,\n placeholders: Record<string, string>,\n): Effect.Effect<StepResult> {\n return Effect.tryPromise({\n try: async () => {\n const { deleteHumeConfig } = await import('../hume.js');\n const result = await Effect.runPromise(deleteHumeConfig(humeConfig, placeholders as any));\n return stepOk('teardown:hume', result.steps || ['Removed Hume EVI config']);\n },\n catch: (err) => err,\n }).pipe(\n Effect.catch((err) =>\n Effect.succeed(stepSkipped('teardown:hume', [`Hume cleanup warning: ${(err as Error).message}`])),\n ),\n );\n}\n\n/**\n * Full workspace teardown.\n */\nexport function teardownWorkspace(\n ctx: LifecycleContext,\n opts: TeardownOptions = {},\n): Effect.Effect<StepResult[]> {\n return Effect.gen(function* () {\n const issueLower = ctx.issueId.toLowerCase();\n const workspacePath = findWorkspacePath(ctx.projectPath, issueLower);\n const shouldDeleteWorkspace = opts.deleteWorkspace !== false; // default true\n const results: StepResult[] = [];\n\n // 1. Kill tmux sessions\n results.push(yield* killTmuxSessions(issueLower));\n\n // 2. Clear shadow state (always runs)\n results.push(yield* clearShadowState(ctx.issueId));\n\n // 3. Clear legacy planning directory (always runs)\n results.push(yield* clearLegacyPlanningDir(ctx.projectPath, issueLower));\n\n // 4-9: Workspace-specific cleanup\n if (workspacePath && existsSync(workspacePath)) {\n // 4. Stop TLDR daemon (only if deleting workspace)\n if (shouldDeleteWorkspace) {\n results.push(yield* stopTldrDaemon(workspacePath));\n }\n\n // 5. Stop Docker containers (only if deleting workspace)\n if (shouldDeleteWorkspace && !opts.skipDocker) {\n results.push(yield* stopDocker(workspacePath, issueLower));\n }\n\n // 5b. Kill orphaned host processes (Vite, node) that survive Docker teardown\n if (shouldDeleteWorkspace) {\n results.push(yield* killOrphanedProcesses(workspacePath));\n }\n\n // 7-8: Project-specific cleanup (tunnel, Hume)\n if (shouldDeleteWorkspace && (opts.workspaceConfig?.tunnel || opts.workspaceConfig?.hume)) {\n const placeholders = buildPlaceholders(ctx, opts, workspacePath);\n\n if (opts.workspaceConfig.tunnel) {\n results.push(yield* removeTunnelConfig(opts.workspaceConfig.tunnel, placeholders));\n }\n if (opts.workspaceConfig.hume) {\n results.push(yield* removeHumeEviConfig(opts.workspaceConfig.hume, placeholders));\n }\n }\n\n // 9. Remove worktree + workspace directory (only if deleting workspace).\n if (shouldDeleteWorkspace) {\n const allPaths = findAllWorkspacePaths(ctx.projectPath, issueLower);\n for (const p of allPaths) {\n results.push(yield* removeWorktree(ctx.projectPath, p));\n }\n }\n } else {\n results.push(stepSkipped('teardown:workspace', ['No workspace found to clean up']));\n }\n\n // 9b. Strike worktrees are residue once the strike branch is merged —\n // remove them even when deleteWorkspace is gated off (PAN-1721). When\n // shouldDeleteWorkspace is true the loop above already removed them via\n // findAllWorkspacePaths.\n if (!shouldDeleteWorkspace) {\n results.push(yield* removeStrikeWorktrees(ctx.projectPath, issueLower));\n }\n\n // 10. Remove agent state\n results.push(yield* removeAgentState(issueLower));\n\n // 11. Delete branches. Feature branches only if explicitly requested;\n // the strike branch is always a candidate once merged to main (PAN-1721).\n results.push(yield* deleteStrikeBranch(ctx.projectPath, issueLower));\n if (opts.deleteBranches) {\n results.push(yield* deleteBranches(ctx.projectPath, issueLower));\n }\n\n // 12. Prune checkpoint refs for this issue's agents.\n results.push(yield* pruneCheckpointRefs(ctx.projectPath, issueLower));\n\n // 13. Prune specialist registry entries for this issue.\n results.push(yield* pruneSpecialistRegistry(ctx.issueId));\n\n return results;\n });\n}\n\nfunction pruneSpecialistRegistry(issueId: string): Effect.Effect<StepResult> {\n return Effect.tryPromise({\n try: async () => {\n const step = 'teardown:specialist-registry';\n const { pruneSpecialistRegistryEntriesForIssue } = await import('../cloister/specialists.js');\n const removed = pruneSpecialistRegistryEntriesForIssue(issueId);\n return removed > 0\n ? stepOk(step, [`Pruned ${removed} specialist registry entr${removed === 1 ? 'y' : 'ies'} for ${issueId}`])\n : stepSkipped(step, [`No specialist registry entries for ${issueId}`]);\n },\n catch: (err) => err,\n }).pipe(\n Effect.catch((err) =>\n Effect.succeed(stepSkipped('teardown:specialist-registry', [`Specialist registry prune failed (non-fatal): ${(err as Error).message}`])),\n ),\n );\n}\n\nfunction pruneCheckpointRefs(projectPath: string, issueLower: string): Effect.Effect<StepResult> {\n return Effect.tryPromise({\n try: async () => {\n const step = 'teardown:checkpoint-refs';\n const { pruneCheckpointRefsForAgents } = await import('../checkpoint/checkpoint-manager.js');\n const agentIds = [`agent-${issueLower}`, `planning-${issueLower}`, `strike-${issueLower}`];\n const pruned = await Effect.runPromise(pruneCheckpointRefsForAgents(projectPath, agentIds));\n return stepOk(step, [`Pruned ${pruned} checkpoint ref(s) for ${agentIds.join(', ')}`]);\n },\n catch: (err) => err,\n }).pipe(\n Effect.catch((err) =>\n Effect.succeed(stepFailed('teardown:checkpoint-refs', `Checkpoint prune failed: ${(err as Error).message}`)),\n ),\n );\n}\n","import { join } from 'node:path';\n\nimport { Effect } from 'effect';\n\nimport {\n listAllAgentsSync,\n removeAgentRecordSync,\n RETAINED_TRANSCRIPTS_PHASE,\n tombstoneAgentRecordSync,\n} from '../overdeck/agents.js';\nimport {\n appendAgentPlaneLifecycle,\n flushAgentPlaneWrites,\n type AgentPlaneLifecycleEntry,\n type AgentPlaneTombstonePredicate,\n} from '../pan-dir/agents.js';\nimport { readIssueRecordForWorkspaceSync } from '../pan-dir/record.js';\nimport { getOverdeckHome } from '../paths.js';\nimport { emitActivityEntrySync } from '../activity-logger.js';\nimport { getAgentStateSync } from '../agents/agent-state.js';\nimport {\n hasRetainedTranscriptsMarker,\n listAgentStateFilesForRemoval,\n markRetainedTranscripts,\n removeAgentStateDir,\n type RemoveAgentStateDirResult,\n} from '../agents/state-dir-removal.js';\nimport { readLiveTrackerIssueState, type LiveTrackerIssueState } from './issue-closed.js';\nimport { sessionExists } from '../tmux.js';\nimport { getProjectSync, resolveProjectFromIssueSync } from '../projects.js';\nimport { resolveProjectReposForIssueSync } from '../project-repos.js';\nimport { listOpenPullRequestsSnapshot } from '../pipeline-membership-gather.js';\nimport { listOpenGitLabMergeRequests } from '../gitlab-merge-requests.js';\nimport { getReviewStatusSync } from '../review-status.js';\n\nexport interface AgentGcResult { removed: string[]; preserved: string[] }\nexport interface AgentGcRow {\n id: string;\n issueId: string;\n status: string;\n workspace?: string | null;\n phase?: string | null;\n role?: string | null;\n branch?: string | null;\n}\n\nexport interface AgentGcTerminalityDeps {\n hasClosedOutFlag: (agent: AgentGcRow) => boolean;\n readTrackerState: (issueId: string) => Promise<LiveTrackerIssueState>;\n hasLiveTmuxSession: (agentId: string) => Promise<boolean>;\n hasOpenChangeRequest: (agent: AgentGcRow) => Promise<boolean>;\n hasInFlightReviewOrTest: (agent: AgentGcRow) => boolean | Promise<boolean>;\n log: (message: string) => void;\n}\n\nexport type AgentGcTerminalityResult = boolean | AgentPlaneTombstonePredicate | null;\n\nexport interface AgentGcDeps {\n agentsDir: string;\n cleanStateDir: (dirPath: string, agentsDir: string) => Promise<RemoveAgentStateDirResult>;\n listFilesToRemove: (dirPath: string, agentsDir: string) => Promise<string[]>;\n hasRetainedMarker: (dirPath: string) => Promise<boolean>;\n markRetained: (dirPath: string) => Promise<void>;\n writeTombstone: (agent: AgentGcRow, entry: AgentPlaneLifecycleEntry) => Promise<void>;\n emitPruneEvent: (agent: AgentGcRow, entry: AgentPlaneLifecycleEntry) => void;\n removeRecord: (id: string) => void;\n tombstoneRecord: (id: string) => void;\n isTerminalAgent: (agent: AgentGcRow) => AgentGcTerminalityResult | Promise<AgentGcTerminalityResult>;\n log?: (message: string) => void;\n}\n\nfunction branchCandidates(agent: AgentGcRow, configuredBranch: string): Set<string> {\n return new Set([\n agent.branch,\n configuredBranch,\n `feature/${agent.issueId.toLowerCase()}`,\n `strike/${agent.issueId.toLowerCase()}`,\n ].filter((branch): branch is string => Boolean(branch)));\n}\n\nasync function hasOpenChangeRequest(agent: AgentGcRow): Promise<boolean> {\n const resolved = resolveProjectFromIssueSync(agent.issueId);\n if (!resolved) throw new Error(`No configured project resolves ${agent.issueId}`);\n const project = getProjectSync(resolved.projectKey);\n if (!project) throw new Error(`Project ${resolved.projectKey} is not configured`);\n const repos = resolveProjectReposForIssueSync(agent.issueId);\n if (!repos?.length) throw new Error(`No configured repositories resolve ${agent.issueId}`);\n\n const githubRepos = repos.filter((repo) => repo.forge === 'github');\n if (githubRepos.length > 0) {\n const [owner, repo] = project.github_repo?.split('/') ?? [];\n if (githubRepos.length !== 1 || !owner || !repo) {\n throw new Error(`Cannot safely resolve GitHub PR repository for ${agent.issueId}`);\n }\n const openPullRequests = await listOpenPullRequestsSnapshot(owner, repo);\n const candidates = branchCandidates(agent, githubRepos[0].sourceBranch);\n if (openPullRequests.some((pullRequest) => candidates.has(pullRequest.headRefName))) return true;\n }\n\n for (const repo of repos.filter((candidate) => candidate.forge === 'gitlab')) {\n const candidates = branchCandidates(agent, repo.sourceBranch);\n const openMergeRequests = await listOpenGitLabMergeRequests(repo.repoPath);\n if (openMergeRequests.some((mergeRequest) => candidates.has(mergeRequest.source_branch))) return true;\n }\n\n return false;\n}\n\nfunction hasInFlightReviewOrTest(agent: AgentGcRow): boolean {\n const status = getReviewStatusSync(agent.issueId);\n if (\n status?.reviewStatus === 'reviewing'\n || status?.testStatus === 'testing'\n || status?.verificationStatus === 'running'\n || (status?.reviewStatus === 'pending' && Boolean(status.reviewRequestedAt))\n ) return true;\n\n return listAllAgentsSync().some((candidate) =>\n candidate.issueId.toUpperCase() === agent.issueId.toUpperCase()\n && (candidate.role === 'review' || candidate.role === 'test')\n && ['starting', 'running', 'waiting', 'idle'].includes(candidate.status));\n}\n\nfunction defaultTerminalityDeps(): AgentGcTerminalityDeps {\n return {\n hasClosedOutFlag: (agent) => Boolean(agent.workspace\n && readIssueRecordForWorkspaceSync(agent.workspace, agent.issueId)?.pipeline?.closedOut === true),\n readTrackerState: readLiveTrackerIssueState,\n hasLiveTmuxSession: (agentId) => Effect.runPromise(sessionExists(agentId)),\n hasOpenChangeRequest,\n hasInFlightReviewOrTest,\n log: (message) => console.log(message),\n };\n}\n\n/**\n * Confirm every live fact required before periodic GC may remove agent state.\n * Any dependency failure is allowed to reject; the sweep catches it and\n * preserves the agent because uncertainty cannot authorize deletion.\n */\nexport async function resolveLiveAgentTerminalityEvidence(\n agent: AgentGcRow,\n deps: AgentGcTerminalityDeps = defaultTerminalityDeps(),\n): Promise<AgentPlaneTombstonePredicate | null> {\n if (agent.status !== 'stopped' || !agent.workspace) return null;\n const closedOutFlag = deps.hasClosedOutFlag(agent);\n if (!closedOutFlag) {\n deps.log(`[agent-gc] preserving ${agent.id}: issue record is not closed out`);\n return null;\n }\n\n const trackerState = await deps.readTrackerState(agent.issueId);\n if (trackerState !== 'closed') {\n deps.log(`[agent-gc] preserving ${agent.id}: live tracker state is ${trackerState}`);\n return null;\n }\n const liveTmux = await deps.hasLiveTmuxSession(agent.id);\n if (liveTmux) {\n deps.log(`[agent-gc] preserving ${agent.id}: tmux session is live`);\n return null;\n }\n const openChangeRequest = await deps.hasOpenChangeRequest(agent);\n if (openChangeRequest) {\n deps.log(`[agent-gc] preserving ${agent.id}: an open PR or MR still exists`);\n return null;\n }\n const inFlightReviewOrTest = await deps.hasInFlightReviewOrTest(agent);\n if (inFlightReviewOrTest) {\n deps.log(`[agent-gc] preserving ${agent.id}: review or test work is still in flight`);\n return null;\n }\n\n return {\n closedOutFlag,\n trackerState,\n liveTmux,\n openChangeRequest,\n inFlightReviewOrTest,\n };\n}\n\nexport async function confirmLiveAgentTerminality(\n agent: AgentGcRow,\n deps: AgentGcTerminalityDeps = defaultTerminalityDeps(),\n): Promise<boolean> {\n return (await resolveLiveAgentTerminalityEvidence(agent, deps)) !== null;\n}\n\nasync function persistAgentGcTombstone(\n agent: AgentGcRow,\n entry: AgentPlaneLifecycleEntry,\n): Promise<void> {\n const state = getAgentStateSync(agent.id);\n if (!state) {\n throw new Error(`local agent state is unavailable for durable tombstone ${agent.id}`);\n }\n const written = await appendAgentPlaneLifecycle(state, entry);\n if (!written) {\n throw new Error(`durable agent plane is unavailable for tombstone ${agent.id}`);\n }\n const flush = await flushAgentPlaneWrites(agent.issueId, agent.id);\n if (!flush || flush.errored || flush.pushed !== true) {\n throw new Error(\n `durable tombstone push failed for ${agent.id}: ${flush?.reason ?? 'no confirmed state-branch push'}`,\n );\n }\n}\n\nfunction emitAgentGcPruneEvent(\n agent: AgentGcRow,\n entry: AgentPlaneLifecycleEntry,\n): void {\n emitActivityEntrySync({\n source: 'cloister',\n level: 'info',\n status: 'completed',\n issueId: agent.issueId,\n message: `${agent.id} tombstoned before local agent-state cleanup`,\n details: JSON.stringify({\n predicate: entry.predicate,\n filesRemoved: entry.filesRemoved,\n }),\n });\n}\n\nfunction defaultAgentGcDeps(): AgentGcDeps {\n return {\n agentsDir: join(getOverdeckHome(), 'agents'),\n cleanStateDir: removeAgentStateDir,\n listFilesToRemove: listAgentStateFilesForRemoval,\n hasRetainedMarker: hasRetainedTranscriptsMarker,\n markRetained: markRetainedTranscripts,\n writeTombstone: persistAgentGcTombstone,\n emitPruneEvent: emitAgentGcPruneEvent,\n removeRecord: removeAgentRecordSync,\n tombstoneRecord: tombstoneAgentRecordSync,\n isTerminalAgent: (agent) => resolveLiveAgentTerminalityEvidence(agent),\n log: (message) => console.warn(message),\n };\n}\n\nfunction preverifiedCloseOutPredicate(): AgentPlaneTombstonePredicate {\n return {\n closedOutFlag: true,\n trackerState: 'closed-preverified-by-close-out',\n liveTmux: null,\n openChangeRequest: null,\n inFlightReviewOrTest: null,\n };\n}\n\nexport async function pruneAgentRowsAfterTranscriptCleanup(\n agents: readonly AgentGcRow[],\n deps: AgentGcDeps = defaultAgentGcDeps(),\n evidenceByAgent: ReadonlyMap<string, AgentPlaneTombstonePredicate> = new Map(),\n): Promise<AgentGcResult> {\n const removed: string[] = [];\n const preserved: string[] = [];\n for (const agent of agents) {\n try {\n const agentDir = join(deps.agentsDir, agent.id);\n if (await deps.hasRetainedMarker(agentDir)) {\n preserved.push(agent.id);\n continue;\n }\n const entry: AgentPlaneLifecycleEntry = {\n at: new Date().toISOString(),\n event: 'tombstoned',\n predicate: evidenceByAgent.get(agent.id) ?? preverifiedCloseOutPredicate(),\n filesRemoved: await deps.listFilesToRemove(agentDir, deps.agentsDir),\n };\n await deps.writeTombstone(agent, entry);\n deps.emitPruneEvent(agent, entry);\n const result = await deps.cleanStateDir(agentDir, deps.agentsDir);\n if (!result.removedDir) {\n await deps.markRetained(agentDir);\n deps.tombstoneRecord(agent.id);\n preserved.push(agent.id);\n continue;\n }\n deps.removeRecord(agent.id);\n removed.push(agent.id);\n } catch (error) {\n deps.log?.(\n `[agent-gc] preserving ${agent.id}: tombstone or cleanup failed: `\n + `${error instanceof Error ? error.message : String(error)}`,\n );\n preserved.push(agent.id);\n }\n }\n return { removed, preserved };\n}\n\n/**\n * Close-out-only pruning path. The lifecycle workflow has already positively\n * verified tracker terminality before it reaches this handoff, so it does not\n * repeat the periodic sweep's remote checks.\n */\nexport async function pruneStoppedAgentsForIssue(\n issueId: string,\n agents: AgentGcRow[] = listAllAgentsSync(),\n deps: AgentGcDeps = defaultAgentGcDeps(),\n): Promise<AgentGcResult> {\n const issue = issueId.toUpperCase();\n const scoped = agents.filter(agent => agent.issueId.toUpperCase() === issue);\n const terminal = scoped.filter(agent => agent.status === 'stopped');\n const live = scoped.filter(agent => agent.status !== 'stopped').map(agent => agent.id);\n const result = await pruneAgentRowsAfterTranscriptCleanup(terminal, deps);\n return { removed: result.removed, preserved: [...live, ...result.preserved] };\n}\n\nexport async function pruneTerminalStoppedAgents(\n agents: AgentGcRow[] = listAllAgentsSync(),\n deps: AgentGcDeps = defaultAgentGcDeps(),\n options: { dryRun?: boolean } = {},\n): Promise<AgentGcResult> {\n const candidates = agents.filter((agent) =>\n agent.phase !== RETAINED_TRANSCRIPTS_PHASE\n && agent.status === 'stopped'\n && Boolean(agent.workspace));\n const terminal: AgentGcRow[] = [];\n const evidenceByAgent = new Map<string, AgentPlaneTombstonePredicate>();\n const preserved: string[] = [];\n\n for (const agent of candidates) {\n try {\n const terminality = await deps.isTerminalAgent(agent);\n if (terminality) {\n terminal.push(agent);\n if (typeof terminality === 'object') {\n evidenceByAgent.set(agent.id, terminality);\n }\n } else preserved.push(agent.id);\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n deps.log?.(`[agent-gc] preserving ${agent.id}: live terminality check failed: ${message}`);\n preserved.push(agent.id);\n }\n }\n\n if (options.dryRun) {\n return { removed: terminal.map((agent) => agent.id), preserved };\n }\n\n const result = await pruneAgentRowsAfterTranscriptCleanup(\n terminal,\n deps,\n evidenceByAgent,\n );\n return { removed: result.removed, preserved: [...preserved, ...result.preserved] };\n}\n","import { execFile } from 'child_process';\nimport { promisify } from 'util';\nimport { mapGitHubStateToCanonical } from '../../core/state-mapping.js';\nimport { resolveGitHubIssueSync } from '../tracker-utils.js';\n\nconst execFileAsync = promisify(execFile);\n\n// Cache for auto-close-out canonical state queries to avoid N+1 shell execs on patrol\nconst autoCloseOutCache = new Map<string, { state: string | null; timestamp: number }>();\nconst AUTO_CLOSE_OUT_CACHE_TTL_MS = 10 * 60 * 1000; // 10 minutes\n\nexport function sweepAutoCloseOutCache(): void {\n const now = Date.now();\n for (const [issueId, entry] of autoCloseOutCache.entries()) {\n if (now - entry.timestamp > AUTO_CLOSE_OUT_CACHE_TTL_MS) {\n autoCloseOutCache.delete(issueId);\n }\n }\n}\n\nexport async function getAutoCloseOutCanonicalState(issueId: string): Promise<string | null> {\n const cached = autoCloseOutCache.get(issueId);\n if (cached && Date.now() - cached.timestamp < AUTO_CLOSE_OUT_CACHE_TTL_MS) {\n return cached.state;\n }\n\n const ghResolved = resolveGitHubIssueSync(issueId);\n if (!ghResolved.isGitHub) {\n autoCloseOutCache.set(issueId, { state: null, timestamp: Date.now() });\n return null;\n }\n\n try {\n const { stdout } = await execFileAsync('gh', [\n 'issue',\n 'view',\n String(ghResolved.number),\n '--repo',\n `${ghResolved.owner}/${ghResolved.repo}`,\n '--json',\n 'state,labels',\n ], { encoding: 'utf-8' });\n const parsed = JSON.parse(stdout) as { state?: string; labels?: Array<string | { name?: string }> };\n const labels = (parsed.labels ?? [])\n .map(label => typeof label === 'string' ? label : label.name)\n .filter((label): label is string => typeof label === 'string');\n const result = mapGitHubStateToCanonical(parsed.state ?? 'open', labels);\n autoCloseOutCache.set(issueId, { state: result, timestamp: Date.now() });\n return result;\n } catch {\n autoCloseOutCache.set(issueId, { state: null, timestamp: Date.now() });\n return null;\n }\n}\n","import type { ProjectConfig } from '../projects.js';\nimport { readIssueRecord, type PanIssueShipRecord } from '../pan-dir/record.js';\nimport type { UatGeneration } from '../overdeck/merge-sync.js';\n\nconst SHIP_RECORD_READ_CONCURRENCY = 4;\n\ninterface ShipStatusDeps {\n readRecord: typeof readIssueRecord;\n}\n\nconst defaultDeps: ShipStatusDeps = {\n readRecord: readIssueRecord,\n};\n\nasync function mapBounded<T>(\n items: readonly T[],\n concurrency: number,\n worker: (item: T) => Promise<void>,\n): Promise<void> {\n let next = 0;\n const workers = Array.from({ length: Math.min(concurrency, items.length) }, async () => {\n while (next < items.length) {\n const item = items[next++]!;\n await worker(item);\n }\n });\n await Promise.all(workers);\n}\n\n/** Load each unique issue record once with bounded async filesystem concurrency. */\nexport async function loadShipRecords(\n project: ProjectConfig,\n generations: readonly UatGeneration[],\n deps: ShipStatusDeps = defaultDeps,\n): Promise<Map<string, PanIssueShipRecord | null>> {\n const issueIds = [...new Set(generations.flatMap(generation =>\n generation.members.map(member => member.issueId.toUpperCase())))] ;\n const records = new Map<string, PanIssueShipRecord | null>();\n await mapBounded(issueIds, SHIP_RECORD_READ_CONCURRENCY, async issueId => {\n records.set(issueId, (await deps.readRecord(project, issueId))?.pipeline.ship ?? null);\n });\n return records;\n}\n\nfunction latest(records: readonly PanIssueShipRecord[]): PanIssueShipRecord {\n return [...records].sort((left, right) => right.at.localeCompare(left.at))[0]!;\n}\n\n/**\n * Fold every member's durable record into one conservative generation verdict.\n * A generation cannot report passed while any member is missing, pending,\n * partial, failed, or records a different version.\n */\nexport function aggregateGenerationShipStatus(\n generation: UatGeneration,\n recordsByIssue: ReadonlyMap<string, PanIssueShipRecord | null>,\n): PanIssueShipRecord | null {\n const records = generation.members.map(member => {\n const ship = recordsByIssue.get(member.issueId.toUpperCase());\n return ship?.batch === generation.name ? ship : null;\n });\n const matching = records.filter((record): record is PanIssueShipRecord => record !== null);\n if (matching.length === 0) {\n return generation.status === 'promoted'\n ? {\n status: 'pending',\n batch: generation.name,\n reason: 'no durable ship settlement was recorded for this promoted batch',\n at: generation.updatedAt,\n }\n : null;\n }\n if (matching.length !== records.length) {\n return {\n status: 'pending',\n batch: generation.name,\n reason: `${records.length - matching.length} member(s) have no durable ship settlement`,\n at: latest(matching).at,\n };\n }\n\n const failed = matching.filter(record => record.status === 'failed');\n if (failed.length > 0) return latest(failed);\n\n const partial = matching.filter(record => record.status === 'partial');\n if (partial.length > 0) {\n const newest = latest(partial);\n const paths = [...new Map(partial\n .flatMap(record => record.paths ?? [])\n .map(path => [path.path, path])).values()];\n return { ...newest, paths };\n }\n\n const pending = matching.filter(record => record.status === 'pending');\n if (pending.length > 0) return latest(pending);\n\n const versions = new Set(matching.map(record => record.version ?? ''));\n if (versions.size > 1) {\n return {\n status: 'partial',\n batch: generation.name,\n version: latest(matching).version,\n paths: [],\n reason: 'members record different shipped versions',\n at: latest(matching).at,\n };\n }\n return latest(matching);\n}\n\n/** Public aggregate omits operational error and reason strings. */\nexport function publicShipStatus(\n ship: PanIssueShipRecord | null,\n): Omit<PanIssueShipRecord, 'error' | 'reason'> | null {\n if (!ship) return null;\n const { error: _error, reason: _reason, ...safe } = ship;\n return safe;\n}\n","/** Shared Definition-of-Done row definitions and gate result types. */\n\nexport type DodRowId =\n | 'review'\n | 'tests'\n | 'verification'\n | 'merged'\n | 'post-merge'\n | 'main-verify'\n | 'ship'\n | 'deploy'\n | 'teardown';\n\nexport interface DodRowDef {\n id: DodRowId;\n num: 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9;\n title: string;\n expected: string;\n overridable: boolean;\n}\n\nexport const DOD_ROWS: readonly DodRowDef[] = [\n {\n id: 'review',\n num: 1,\n title: 'Review passed',\n expected: 'reviewStatus: passed, or tracker-closed with landed work and no negative review verdict',\n overridable: true,\n },\n {\n id: 'tests',\n num: 2,\n title: 'Tests passed',\n expected: 'testStatus: passed, or tracker-closed with landed work (negative verdict requires main verification pass)',\n overridable: true,\n },\n {\n id: 'verification',\n num: 3,\n title: 'Verification green on the branch',\n expected: 'verificationStatus: passed, or tracker-closed with landed work (negative verdict requires main verification pass)',\n overridable: true,\n },\n { id: 'merged', num: 4, title: 'Merged to main', expected: 'PR merged on the forge (feature or strike head), or branch work contained in main (non-PR landing)', overridable: true },\n {\n id: 'post-merge',\n num: 5,\n title: 'Post-merge lifecycle ran',\n expected: 'agents paused, workspace stack stopped, and verifying-on-main label applied',\n overridable: true,\n },\n {\n id: 'main-verify',\n num: 6,\n title: 'Verified on main',\n expected: 'merged commit verified on main',\n overridable: true,\n },\n {\n id: 'ship',\n num: 7,\n title: 'Version strings propagated',\n expected: 'every declared version_sync.expect path reports the batch version',\n overridable: true,\n },\n {\n id: 'deploy',\n num: 8,\n title: 'Deployed',\n expected: 'live server build includes the merged commit',\n overridable: true,\n },\n {\n id: 'teardown',\n num: 9,\n title: 'Close-out teardown verified',\n expected: 'workspace and configured branches removed, planning archived, issue closed, and Docker network removed',\n overridable: false,\n },\n];\n\nexport function acceptFlagFor(row: DodRowDef): string {\n return `--accept-${row.id}`;\n}\n\nexport function canAcceptDodMisses(actor: string): boolean {\n return !actor.startsWith('flywheel-');\n}\n\nexport const BRANCH_ABSENT_MERGE_ERROR = 'Feature branch is absent; positive merge evidence is required';\n\nexport type DodRowStatus = 'pass' | 'miss' | 'skip';\n\nexport interface DodRowResult {\n id: DodRowId;\n num: DodRowDef['num'];\n title: string;\n expected: string;\n observed: string;\n status: DodRowStatus;\n acceptedBy?: {\n flag: string;\n by: string;\n at: string;\n };\n}\n\nexport interface DodGateResult {\n rows: DodRowResult[];\n passed: boolean;\n misses: DodRowId[];\n accepted: DodRowId[];\n /** PAN-3211: set only for abandoned/residue dispositions, persisted beside the gate rows. */\n disposition?: { reason: string; by: string; kind?: 'abandon' | 'residue' };\n}\n\n/**\n * PAN-3211: the gate result for an abandoned disposition — every row skipped\n * with the operator's note, so the durable audit shows the gate was\n * deliberately not evaluated rather than silently green.\n */\nexport function buildAbandonedDodGate(reason: string, by: string): DodGateResult {\n return {\n rows: DOD_ROWS.map(row => ({\n ...row,\n status: 'skip' as const,\n observed: `gate not evaluated — abandoned disposition recorded by ${by}: ${reason}`,\n })),\n misses: [],\n accepted: [],\n passed: true,\n disposition: { reason, by, kind: 'abandon' },\n };\n}\n\n/**\n * PAN-3396: the gate result for a residue disposition — every row skipped\n * with verified evidence of tracker closure and stale PR cleanup, so the\n * durable audit shows the gate was deliberately not evaluated for stale\n * pre-record-era issues with lingering convention PRs/MRs.\n */\nexport function buildResidueDodGate(reason: string, by: string, evidence: string[]): DodGateResult {\n const evidenceStr = evidence.join('; ');\n return {\n rows: DOD_ROWS.map(row => ({\n ...row,\n status: 'skip' as const,\n observed: `gate not evaluated — residue disposition recorded by ${by}: ${reason}. Verified: ${evidenceStr}`,\n })),\n misses: [],\n accepted: [],\n passed: true,\n disposition: { reason, by, kind: 'residue' },\n };\n}\n","/** Mechanical Definition-of-Done row checks. */\n\nimport { execFile } from 'node:child_process';\nimport { userInfo } from 'node:os';\nimport { resolve } from 'node:path';\nimport { promisify } from 'node:util';\nimport type { CanonicalState } from '../../core/state-mapping.js';\nimport { Effect } from 'effect';\nimport { listRunningAgents, type AgentState } from '../agents.js';\nimport { getDashboardApiUrlSync } from '../config.js';\nimport { getReviewStatus, type ReviewStatus } from '../review-status.js';\nimport type { StrikeLandingStatus } from '../strike-landing.js';\nimport {\n getProjectConfigFromWorkspacePath,\n readIssueRecord,\n resolveProjectForIssue,\n type PanIssuePipelineRecord,\n type PanIssueShipRecord,\n} from '../pan-dir/record.js';\nimport type { ProjectConfig } from '../projects.js';\nimport { getAutoCloseOutCanonicalState } from '../cloister/deacon-canonical-state.js';\nimport { aggregateGenerationShipStatus, loadShipRecords } from '../cloister/ship-status.js';\nimport { isTrackerIssueClosed } from '../cloister/issue-closed.js';\nimport {\n fetchCommitCheckRuns,\n fetchIssuePullRequest,\n fetchRequiredStatusChecks,\n type CommitCheckRuns,\n} from '../overdeck/pull-requests.js';\nimport { listUatGenerationsSync, type UatGeneration } from '../overdeck/merge-sync.js';\nimport { getForgeAdapter } from '../forge.js';\nimport { resolveProjectReposForIssueSync } from '../project-repos.js';\nimport {\n gatherIssueBranchContainment,\n type IssueBranchContainment,\n} from '../pipeline-membership-gather.js';\nimport {\n acceptFlagFor,\n BRANCH_ABSENT_MERGE_ERROR,\n canAcceptDodMisses,\n DOD_ROWS,\n type DodGateResult,\n type DodRowId,\n type DodRowResult,\n} from './dod.js';\nimport type { LifecycleContext, StepResult } from './types.js';\n\nconst execFileAsync = promisify(execFile);\n\ntype StatusSource =\n | { source: 'live'; status: ReviewStatus }\n | { source: 'journal'; status: PanIssuePipelineRecord }\n | null;\ntype Awaitable<T> = T | Promise<T>;\n\nexport interface DodStatusRowDeps {\n getReviewStatus: (issueId: string) => Awaitable<ReviewStatus | null>;\n getJournalStatus: (issueId: string) => Awaitable<PanIssuePipelineRecord | null>;\n}\n\nexport interface MergedDodRowResult extends DodRowResult {\n mergedAt?: string;\n mergeCommit?: string;\n evidence?: 'branch-containment';\n}\n\ninterface MergedForgeArtifact {\n forge: string;\n url?: string;\n id?: string;\n}\n\ninterface MergedRowDeps {\n verifyMerged: (ctx: LifecycleContext) => Promise<StepResult>;\n readPullRequest: (ctx: LifecycleContext, branchName: string) => Promise<{\n number?: number;\n state?: string;\n mergedAt?: string;\n mergeCommit?: { oid?: string } | string | null;\n }>;\n readDurableMerges?: (ctx: LifecycleContext) => Promise<string[]>;\n readMergedForgeArtifacts?: (ctx: LifecycleContext) => Promise<MergedForgeArtifact[]>;\n readBranchContainment?: (ctx: LifecycleContext) => Promise<IssueBranchContainment>;\n}\n\nconst defaultMergedRowDeps: MergedRowDeps = {\n verifyMerged: async () => ({\n step: 'close-out:verify-merged',\n success: false,\n skipped: false,\n error: 'merge verifier was not supplied',\n }),\n readPullRequest: async (ctx, branchName) => {\n if (!ctx.github) return {};\n void branchName;\n const response = await fetchIssuePullRequest(ctx.issueId);\n if (response.error) throw new Error(response.error);\n return response.pr ?? {};\n },\n readDurableMerges: async ctx => {\n const project = resolveProjectForIssue(ctx.issueId) ?? getProjectConfigFromWorkspacePath(ctx.projectPath);\n return (await readIssueRecord(project, ctx.issueId))?.closeOut.merges ?? [];\n },\n readMergedForgeArtifacts: async ctx => {\n const repos = resolveProjectReposForIssueSync(ctx.issueId)?.filter(repo => repo.required) ?? [];\n const artifacts = await Promise.all(repos.map(repo => getForgeAdapter(repo.forge).findMergedArtifact({\n sourceBranch: repo.sourceBranch,\n targetBranch: repo.targetBranch,\n cwd: repo.repoPath,\n })));\n return artifacts.filter((artifact): artifact is NonNullable<typeof artifact> => artifact !== null);\n },\n readBranchContainment: async ctx => {\n const project = resolveProjectForIssue(ctx.issueId) ?? getProjectConfigFromWorkspacePath(ctx.projectPath);\n if (!project) return { unmergedRefs: [], mergedWorkRefs: [], pointerRefs: [] };\n return gatherIssueBranchContainment(project, ctx.issueId);\n },\n};\n\ninterface PostMergeRowDeps {\n readCanonicalState: (ctx: LifecycleContext) => Promise<CanonicalState | null>;\n readMergeStatus: (issueId: string) => Awaitable<string | undefined>;\n listAgents: () => Awaitable<Array<Pick<AgentState, 'id' | 'issueId' | 'role' | 'status'>>>;\n readStrikeLanded?: (issueId: string) => Awaitable<boolean>;\n}\n\nconst defaultPostMergeRowDeps: PostMergeRowDeps = {\n readStrikeLanded: async issueId => strikeLanded((await loadStatus(issueId, defaultDeps))?.status),\n readCanonicalState: async ctx => {\n return getAutoCloseOutCanonicalState(ctx.issueId) as Promise<CanonicalState | null>;\n },\n // PAN-3025: same live→journal fallback as the verdict rows — after close-out\n // clears live status, the durable record is the only place mergeStatus survives.\n readMergeStatus: async issueId => (await loadStatus(issueId, defaultDeps))?.status.mergeStatus,\n listAgents: async () => Effect.runPromise(listRunningAgents()),\n};\n\nexport const DEFAULT_MAIN_VERIFY_REQUIRED_CHECKS = ['test', 'lint', 'build (22)', 'guard'];\n\ninterface MainVerifyRowDeps {\n readCheckRuns: (ctx: LifecycleContext, commit: string) => Promise<CommitCheckRuns>;\n readRequiredChecks?: (ctx: LifecycleContext) => Promise<string[]>;\n /**\n * PAN-3202: default-branch commits that contain `mergeCommit`, newest first.\n * These are the candidate heads for the later-green-run evidence form.\n */\n readContainingDefaultBranchCommits?: (ctx: LifecycleContext, mergeCommit: string) => Promise<string[]>;\n}\n\nfunction configuredMainVerifyChecks(ctx: LifecycleContext): string[] {\n const project = resolveProjectForIssue(ctx.issueId) ?? getProjectConfigFromWorkspacePath(ctx.projectPath);\n return project?.main_verify_required_checks?.length\n ? project.main_verify_required_checks\n : DEFAULT_MAIN_VERIFY_REQUIRED_CHECKS;\n}\n\nconst defaultMainVerifyRowDeps: MainVerifyRowDeps = {\n readCheckRuns: async (ctx, commit) => {\n if (!ctx.github) return { total: 0, names: [], successful: [], failed: [], pending: [] };\n return fetchCommitCheckRuns(ctx.github.owner, ctx.github.repo, commit);\n },\n readRequiredChecks: async ctx => {\n if (!ctx.github) return configuredMainVerifyChecks(ctx);\n return fetchRequiredStatusChecks(ctx.github.owner, ctx.github.repo, 'main', configuredMainVerifyChecks(ctx));\n },\n readContainingDefaultBranchCommits: async (ctx, mergeCommit) => {\n const { stdout } = await execFileAsync(\n 'git',\n ['rev-list', '--ancestry-path', '--first-parent', `${mergeCommit}..origin/main`],\n { cwd: ctx.projectPath, encoding: 'utf-8', timeout: 10000, maxBuffer: 8 * 1024 * 1024 },\n );\n return stdout.split('\\n').map(line => line.trim()).filter(Boolean);\n },\n};\n\ninterface ShipRowDeps {\n readProject: (ctx: LifecycleContext) => ProjectConfig | null;\n readPipeline: (ctx: LifecycleContext) => Promise<PanIssuePipelineRecord | null>;\n findPromotedBatch: (ctx: LifecycleContext) => UatGeneration | null;\n readBatchShip: (project: ProjectConfig, generation: UatGeneration) => Promise<PanIssueShipRecord | null>;\n}\n\nconst defaultShipRowDeps: ShipRowDeps = {\n readProject: ctx => resolveProjectForIssue(ctx.issueId) ?? getProjectConfigFromWorkspacePath(ctx.projectPath),\n readPipeline: async ctx => {\n const project = resolveProjectForIssue(ctx.issueId) ?? getProjectConfigFromWorkspacePath(ctx.projectPath);\n return (await readIssueRecord(project, ctx.issueId))?.pipeline ?? null;\n },\n findPromotedBatch: ctx => {\n const project = resolveProjectForIssue(ctx.issueId) ?? getProjectConfigFromWorkspacePath(ctx.projectPath);\n return listUatGenerationsSync({ projectRoot: resolve(project.path), statuses: ['promoted'] })\n .find(generation => generation.members.some(member => member.issueId.toUpperCase() === ctx.issueId.toUpperCase())) ?? null;\n },\n readBatchShip: async (project, generation) => aggregateGenerationShipStatus(\n generation,\n await loadShipRecords(project, [generation]),\n ),\n};\n\ninterface DeployRowDeps {\n dashboardUrl: () => string;\n readJson: (url: string) => Promise<Record<string, unknown>>;\n commitContains: (repoRoot: string, mergeCommit: string, buildCommit: string) => Promise<boolean>;\n}\n\nexport interface TerminalVerdictSettlement {\n trackerClosed: boolean;\n landedWork: boolean;\n mainVerifyStatus: DodRowResult['status'];\n}\n\nexport interface EvaluateDodGateDeps {\n review: (issueId: string, settlement?: TerminalVerdictSettlement) => DodRowResult | Promise<DodRowResult>;\n tests: (issueId: string, settlement?: TerminalVerdictSettlement) => DodRowResult | Promise<DodRowResult>;\n verification: (issueId: string, settlement?: TerminalVerdictSettlement) => DodRowResult | Promise<DodRowResult>;\n merged: (ctx: LifecycleContext) => MergedDodRowResult | Promise<MergedDodRowResult>;\n postMerge: (ctx: LifecycleContext, merged?: MergedDodRowResult) => DodRowResult | Promise<DodRowResult>;\n mainVerify: (ctx: LifecycleContext, mergeCommit?: string) => DodRowResult | Promise<DodRowResult>;\n ship: (ctx: LifecycleContext) => DodRowResult | Promise<DodRowResult>;\n deploy: (ctx: LifecycleContext, merge: {\n mergedAt?: string;\n mergeCommit?: string;\n mergedRowStatus?: DodRowResult['status'];\n mainVerifyRowStatus?: DodRowResult['status'];\n }) => DodRowResult | Promise<DodRowResult>;\n trackerClosed?: (issueId: string) => Awaitable<boolean>;\n now: () => string;\n}\n\nconst defaultEvaluateDodGateDeps: EvaluateDodGateDeps = {\n review: (issueId, settlement) => checkReviewRow(issueId, defaultDeps, settlement),\n tests: (issueId, settlement) => checkTestsRow(issueId, defaultDeps, settlement),\n verification: (issueId, settlement) => checkVerificationRow(issueId, defaultDeps, settlement),\n merged: checkMergedRow,\n postMerge: checkPostMergeRow,\n mainVerify: checkMainVerifyRow,\n ship: checkShipRow,\n deploy: checkDeployRow,\n trackerClosed: isTrackerIssueClosed,\n now: () => new Date().toISOString(),\n};\n\nconst defaultDeployRowDeps: DeployRowDeps = {\n dashboardUrl: getDashboardApiUrlSync,\n readJson: async url => {\n const response = await fetch(url, { signal: AbortSignal.timeout(3000) });\n if (!response.ok) throw new Error(`${response.status} ${response.statusText}`);\n return response.json() as Promise<Record<string, unknown>>;\n },\n commitContains: async (repoRoot, mergeCommit, buildCommit) => {\n try {\n await execFileAsync('git', ['merge-base', '--is-ancestor', mergeCommit, buildCommit], {\n cwd: repoRoot,\n encoding: 'utf-8',\n timeout: 10000,\n });\n return true;\n } catch {\n return false;\n }\n },\n};\n\nconst defaultDeps: DodStatusRowDeps = {\n getReviewStatus: issueId => Effect.runPromise(getReviewStatus(issueId)),\n getJournalStatus: async issueId => {\n const project = resolveProjectForIssue(issueId) ?? getProjectConfigFromWorkspacePath(process.cwd());\n return (await readIssueRecord(project, issueId))?.pipeline ?? null;\n },\n};\n\nfunction rowDefinition(id: DodRowId) {\n const row = DOD_ROWS.find(candidate => candidate.id === id);\n if (!row) throw new Error(`Unknown DoD row: ${id}`);\n return row;\n}\n\nfunction result(id: DodRowId, status: DodRowResult['status'], observed: string): DodRowResult {\n const row = rowDefinition(id);\n return { id, num: row.num, title: row.title, expected: row.expected, observed, status };\n}\n\nasync function loadStatus(issueId: string, deps: DodStatusRowDeps): Promise<StatusSource> {\n try {\n const live = await deps.getReviewStatus(issueId);\n if (live) return { source: 'live', status: live };\n const journal = await deps.getJournalStatus(issueId);\n return journal ? { source: 'journal', status: journal } : null;\n } catch {\n return null;\n }\n}\n\n/**\n * PAN-3180: `landed` is the strike path's own durable statement that this issue's\n * work reached main through `strike/<id>`. It is written by the server merge door\n * (`mergeCompletionStatus` in `merge-strike.ts`) and mirrored into the per-issue\n * record's `pipeline` block, so it outlives review-status clearing. Every earlier\n * landing state (`ready`/`landing`/`recovering`/`needs_you`) is a strike still in\n * flight and earns no waiver.\n */\nfunction strikeLanded(status: StrikeLandingStatus | null | undefined): boolean {\n return status?.strikeLandingState === 'landed';\n}\n\n/**\n * PAN-3180: a strike dispatches neither a review nor a test specialist — that\n * bypass is the whole point of the path. So for a strike-landed issue these rows\n * are `skip` (deliberately not run), never `pass` (ran and succeeded) and never\n * `miss` (outstanding). A reader of the close-out record still sees the real\n * verdict and the reason it was never produced.\n */\nconst STRIKE_BYPASS_NOTE: Record<'review' | 'tests', string> = {\n review: 'no review specialist is dispatched for a strike',\n tests: 'no test specialist is dispatched for a strike',\n};\n\n/**\n * A specialist that ran and returned a negative verdict is a different fact from\n * one that was never dispatched, so the strike waiver never covers it — those\n * still block until an operator records an explicit `--accept-<row>` override.\n */\nconst NEGATIVE_VERDICTS = new Set(['failed', 'blocked', 'dispatch_failed']);\n\n/**\n * PAN-3187: a tracker-closed issue with landed work cannot produce a verdict it\n * never produced while active. Negative test/verification verdicts settle only\n * when main verification proves the landed state green; review negatives remain.\n */\nfunction terminalVerdictSettlement(\n id: 'review' | 'tests' | 'verification',\n value: string | undefined,\n observed: string,\n settlement?: TerminalVerdictSettlement,\n): DodRowResult | null {\n if (!settlement?.trackerClosed || !settlement.landedWork) return null;\n if (!NEGATIVE_VERDICTS.has(value ?? '')) {\n return result(\n id,\n 'skip',\n `${observed}; settled because the tracker issue is closed and merged work is landed`,\n );\n }\n if (id !== 'review' && settlement.mainVerifyStatus === 'pass') {\n return result(\n id,\n 'skip',\n `${observed}; superseded because the tracker issue is closed, merged work is landed, and main verification passed`,\n );\n }\n return null;\n}\n\nasync function checkVerdict(\n issueId: string,\n id: 'review' | 'tests',\n field: 'reviewStatus' | 'testStatus',\n deps: DodStatusRowDeps,\n settlement?: TerminalVerdictSettlement,\n): Promise<DodRowResult> {\n const loaded = await loadStatus(issueId, deps);\n if (!loaded) return result(id, 'miss', 'no review status or journal record found');\n\n const value = loaded.status[field];\n const source = loaded.source === 'journal' ? ' from pipeline journal' : '';\n const policy = value === 'skipped' ? ' (skipped per issue policy)' : '';\n const observed = `${field}: ${value ?? 'missing'}${source}${policy}`;\n if (value === 'passed' || value === 'skipped') return result(id, 'pass', observed);\n if (strikeLanded(loaded.status) && !NEGATIVE_VERDICTS.has(value ?? '')) {\n return result(\n id,\n 'skip',\n `${observed}; skipped by the strike path (strikeLandingState: landed) — ${STRIKE_BYPASS_NOTE[id]}`,\n );\n }\n return terminalVerdictSettlement(id, value, observed, settlement) ?? result(id, 'miss', observed);\n}\n\nexport function checkReviewRow(\n issueId: string,\n deps: DodStatusRowDeps = defaultDeps,\n settlement?: TerminalVerdictSettlement,\n): Promise<DodRowResult> {\n return checkVerdict(issueId, 'review', 'reviewStatus', deps, settlement);\n}\n\nexport function checkTestsRow(\n issueId: string,\n deps: DodStatusRowDeps = defaultDeps,\n settlement?: TerminalVerdictSettlement,\n): Promise<DodRowResult> {\n return checkVerdict(issueId, 'tests', 'testStatus', deps, settlement);\n}\n\nexport async function checkVerificationRow(\n issueId: string,\n deps: DodStatusRowDeps = defaultDeps,\n settlement?: TerminalVerdictSettlement,\n): Promise<DodRowResult> {\n const loaded = await loadStatus(issueId, deps);\n if (!loaded) return result('verification', 'miss', 'no review status or journal record found');\n\n // PAN-3067: the verdict alone decides this row, exactly like rows 1 and 2.\n // `lastVerifiedCommit` is a best-effort optimization anchor — verification-runner.ts\n // snapshots it inside a try/catch and spreads the field conditionally, and a\n // `skipped` verdict never has one at all — so its absence says nothing about\n // whether verification ran. Requiring it made every issue whose anchor was\n // never written un-closable. The observed string always names the anchor's\n // presence or absence so a reader can never mistake it for a hidden condition.\n const value = loaded.status.verificationStatus;\n const commit = loaded.status.lastVerifiedCommit;\n const accepted = value === 'passed' || value === 'skipped';\n const notes: string[] = [];\n if (loaded.source === 'journal') notes.push('from pipeline journal');\n if (value === 'skipped') notes.push('skipped per issue policy');\n if (accepted && !commit) notes.push('no lastVerifiedCommit recorded');\n const observed = `verificationStatus: ${value ?? 'missing'}${commit ? ` at ${commit}` : ''}${\n notes.length > 0 ? ` (${notes.join('; ')})` : ''\n }`;\n if (accepted) return result('verification', 'pass', observed);\n // An out-of-band merge never enters merge-ops, so the CI-green skip cannot\n // record its normal verification verdict. Once rows 4 and 6 prove the landed\n // work and main CI green, that evidence satisfies row 3 without an override.\n if (value === undefined && settlement?.landedWork && settlement.mainVerifyStatus === 'pass') {\n return result('verification', 'pass', `${observed}; verification satisfied by green main CI after landing`);\n }\n return terminalVerdictSettlement('verification', value, observed, settlement) ??\n result('verification', 'miss', observed);\n}\n\nexport async function checkMergedRow(\n ctx: LifecycleContext,\n deps: MergedRowDeps = defaultMergedRowDeps,\n): Promise<MergedDodRowResult> {\n const verified: StepResult = await deps.verifyMerged(ctx).catch(error => ({\n step: 'close-out:verify-merged',\n success: false,\n skipped: false,\n error: error instanceof Error ? error.message : String(error),\n }));\n const detail = verified.details?.join('; ');\n const observed = detail || verified.error || (verified.skipped ? 'issue already closed on forge' : 'merge not verified');\n const merged = result('merged', verified.success || verified.skipped ? 'pass' : 'miss', observed) as MergedDodRowResult;\n const branchAbsent = verified.error === BRANCH_ABSENT_MERGE_ERROR;\n let durableMerges: string[] = [];\n let forgeArtifacts: MergedForgeArtifact[] = [];\n\n if (branchAbsent) {\n try {\n durableMerges = await (deps.readDurableMerges ?? defaultMergedRowDeps.readDurableMerges!)(ctx);\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n merged.observed = `${merged.observed}; durable merge evidence unavailable: ${message}`;\n }\n if (!ctx.github && deps.readMergedForgeArtifacts) {\n try {\n forgeArtifacts = await deps.readMergedForgeArtifacts(ctx);\n const labels = forgeArtifacts.map(artifact => {\n const label = artifact.forge === 'gitlab'\n ? (artifact.id ? `MR !${artifact.id}` : 'GitLab MR')\n : (artifact.id ? `PR #${artifact.id}` : 'GitHub PR');\n return `${label} merged${artifact.url ? ` (${artifact.url})` : ''}`;\n });\n if (labels.length > 0) merged.observed = `${merged.observed}; ${labels.join('; ')}`;\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n merged.observed = `${merged.observed}; forge artifact evidence unavailable: ${message}`;\n }\n }\n }\n\n let pullRequestState: string | undefined;\n if (ctx.github) {\n try {\n const branchName = `feature/${ctx.issueId.toLowerCase()}`;\n const pullRequest = await deps.readPullRequest(ctx, branchName);\n pullRequestState = pullRequest.state;\n merged.mergedAt = pullRequest.mergedAt;\n merged.mergeCommit = typeof pullRequest.mergeCommit === 'string'\n ? pullRequest.mergeCommit\n : pullRequest.mergeCommit?.oid;\n const number = pullRequest.number ? `PR #${pullRequest.number}` : 'PR';\n const state = pullRequest.state ?? 'state unknown';\n const at = pullRequest.mergedAt ? ` at ${pullRequest.mergedAt}` : '';\n merged.observed = `${merged.observed}; ${number} ${state}${at}`;\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n merged.observed = `${merged.observed}; forge metadata unavailable: ${message}`;\n }\n }\n\n if (branchAbsent) {\n const forgeMerged = pullRequestState?.toUpperCase() === 'MERGED';\n if (durableMerges.length > 0 || forgeMerged || forgeArtifacts.length > 0) {\n merged.status = 'pass';\n if (durableMerges.length > 0) {\n merged.observed = `${merged.observed}; durable close-out record contains ${durableMerges.length} merge artifact(s)`;\n }\n } else {\n merged.observed = `${merged.observed}; no merged forge artifact or durable close-out merge record found`;\n }\n }\n\n if (merged.status === 'miss') {\n try {\n const containment = await (deps.readBranchContainment ?? defaultMergedRowDeps.readBranchContainment!)(ctx);\n if (containment.mergedWorkRefs.length > 0 && containment.unmergedRefs.length === 0) {\n merged.status = 'pass';\n merged.evidence = 'branch-containment';\n merged.observed = `${merged.observed}; branch work contained in default branch with no merged PR — non-PR landing (membership L2-work lens): ${containment.mergedWorkRefs.join(', ')}`;\n }\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n merged.observed = `${merged.observed}; branch containment evidence unavailable: ${message}`;\n }\n }\n\n return merged;\n}\n\nexport async function checkPostMergeRow(\n ctx: LifecycleContext,\n merged?: MergedDodRowResult,\n deps: PostMergeRowDeps = defaultPostMergeRowDeps,\n): Promise<DodRowResult> {\n try {\n const canonicalState = await deps.readCanonicalState(ctx);\n const mergeStatus = await deps.readMergeStatus(ctx.issueId);\n const issueId = ctx.issueId.toUpperCase();\n const runningAgents = (await deps.listAgents()).filter(agent =>\n agent.issueId.toUpperCase() === issueId &&\n (agent.role === 'work' || agent.role === 'plan') &&\n (agent.status === 'starting' || agent.status === 'running'),\n );\n const stateObserved = canonicalState\n ? `canonical state: ${canonicalState}`\n : `canonical state unavailable; mergeStatus: ${mergeStatus ?? 'missing'}`;\n const agentsObserved = runningAgents.length > 0\n ? `running agents: ${runningAgents.map(agent => agent.id).join(', ')}`\n : 'no running work/planning agents';\n // PAN-3188 (row 5): terminal canonical states settle this row. 'done'\n // proves the post-merge lifecycle already ran to close-out; 'canceled'\n // makes it moot. Requiring the transient verifying_on_main marker here\n // wedges every re-evaluated terminal issue (the 11 closed issues that\n // blocked close-out sweeps on 'canonical state: done').\n if (canonicalState === 'done') {\n return result(\n 'post-merge',\n runningAgents.length === 0 ? 'pass' : 'miss',\n `terminal canonical state: done — the post-merge lifecycle already ran to close-out; ${agentsObserved}`,\n );\n }\n if (canonicalState === 'canceled') {\n return result(\n 'post-merge',\n runningAgents.length === 0 ? 'skip' : 'miss',\n `terminal canonical state: canceled — post-merge lifecycle not applicable; ${agentsObserved}`,\n );\n }\n const lifecycleObserved = canonicalState === 'verifying_on_main' || mergeStatus === 'merged';\n // PAN-3180: `postMergeLifecycle()` is the work-agent handoff — it pauses the\n // work/planning agents, stops the workspace stack, and applies\n // `verifying-on-main`. A strike has no work agent to pause and its landing is\n // owned by the Deacon's merge door, so the marker this row looks for is never\n // written and its absence proves nothing. What a strike does still owe is\n // quiescence, so a live work/planning agent remains a real miss.\n if (!lifecycleObserved && await (deps.readStrikeLanded ?? defaultPostMergeRowDeps.readStrikeLanded!)(ctx.issueId)) {\n return result(\n 'post-merge',\n runningAgents.length === 0 ? 'skip' : 'miss',\n `strike landing (strikeLandingState: landed) — the work-agent post-merge handoff is not the strike path's lifecycle; ${stateObserved}; ${agentsObserved}`,\n );\n }\n if (!lifecycleObserved && merged?.evidence === 'branch-containment') {\n return result(\n 'post-merge',\n runningAgents.length === 0 ? 'pass' : 'miss',\n `non-PR landing (branch-containment evidence) — post-merge lifecycle not applicable; ${agentsObserved}`,\n );\n }\n return result(\n 'post-merge',\n lifecycleObserved && runningAgents.length === 0 ? 'pass' : 'miss',\n `${stateObserved}; ${agentsObserved}`,\n );\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n return result('post-merge', 'miss', `post-merge evidence unavailable: ${message}`);\n }\n}\n\n/**\n * PAN-3202: how many default-branch heads above the merge commit are probed for\n * the later-green-run evidence form. The tip of main is often mid-CI, so a single\n * candidate would usually read as pending; five reaches back far enough to find\n * the newest concluded green head without turning one row into a check-run storm.\n */\nconst LATER_GREEN_CANDIDATE_LIMIT = 5;\n\n/**\n * PAN-3202: the fallback evidence form for DoD row 6. When a merge lands inside a\n * red-main window, the merge commit's own check-runs can never turn green, so the\n * row was permanently unsatisfiable even after main went green hundreds of times\n * with the commit included. A later default-branch head whose check-runs all\n * concluded green, and which contains the merge commit, is strictly stronger\n * evidence than the original run: it proves main is healthy *with* the merge in it,\n * which is exactly what \"verified on main\" means. Row 7 (deploy) already reasons\n * this way through the same containment relation.\n */\nfunction requiredChecksOutcome(checks: CommitCheckRuns, required: string[]): {\n missing: string[];\n unsuccessful: string[];\n} {\n return {\n missing: required.filter(check => !checks.names.includes(check)),\n unsuccessful: required.filter(check => checks.names.includes(check) && !checks.successful.includes(check)),\n };\n}\n\nfunction hasRequiredChecksGreen(checks: CommitCheckRuns, required: string[]): boolean {\n const outcome = requiredChecksOutcome(checks, required);\n return outcome.missing.length === 0 && outcome.unsuccessful.length === 0;\n}\n\nasync function findLaterGreenDefaultBranchRun(\n ctx: LifecycleContext,\n mergeCommit: string,\n required: string[],\n deps: MainVerifyRowDeps,\n): Promise<{ run: { sha: string; total: number } | null; note: string }> {\n const readCandidates =\n deps.readContainingDefaultBranchCommits ?? defaultMainVerifyRowDeps.readContainingDefaultBranchCommits!;\n let candidates: string[];\n try {\n candidates = await readCandidates(ctx, mergeCommit);\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n return { run: null, note: `later-run evidence unavailable: ${message}` };\n }\n\n const probed = candidates.slice(0, LATER_GREEN_CANDIDATE_LIMIT);\n for (const sha of probed) {\n try {\n const checks = await deps.readCheckRuns(ctx, sha);\n if (hasRequiredChecksGreen(checks, required)) {\n return { run: { sha, total: checks.total }, note: '' };\n }\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n return { run: null, note: `later-run evidence unavailable: ${message}` };\n }\n }\n return {\n run: null,\n note: probed.length === 0\n ? 'no later default-branch commit contains the merge'\n : `no green CI run among the ${probed.length} newest default-branch commit(s) containing the merge`,\n };\n}\n\nexport async function checkMainVerifyRow(\n ctx: LifecycleContext,\n mergeCommit?: string,\n deps: MainVerifyRowDeps = defaultMainVerifyRowDeps,\n): Promise<DodRowResult> {\n if (!ctx.github || !mergeCommit) {\n return result(\n 'main-verify',\n 'skip',\n 'no merge commit resolvable — verified-on-main has no durable marker (see DoD row 6)',\n );\n }\n\n try {\n const requiredChecks = await (deps.readRequiredChecks ?? defaultMainVerifyRowDeps.readRequiredChecks!)(ctx);\n const checks = await deps.readCheckRuns(ctx, mergeCommit);\n const outcome = requiredChecksOutcome(checks, requiredChecks);\n if (hasRequiredChecksGreen(checks, requiredChecks)) {\n return result('main-verify', 'pass', `required checks concluded successfully on ${mergeCommit}: ${requiredChecks.join(', ')}`);\n }\n\n // The merge commit's own run stays the primary evidence, so its outcome is\n // always recorded first; the later-green-run form only appends to it.\n const parts = [\n ...(outcome.missing.length > 0 ? [`missing required checks on ${mergeCommit}: ${outcome.missing.join(', ')}`] : []),\n ...(outcome.unsuccessful.length > 0 ? [`required checks not successful: ${outcome.unsuccessful.join(', ')}`] : []),\n ];\n const primary = parts.join('; ');\n\n const later = await findLaterGreenDefaultBranchRun(ctx, mergeCommit, requiredChecks, deps);\n if (later.run) {\n return result(\n 'main-verify',\n 'pass',\n `${primary}; verified on main by later green CI run ${later.run.sha} containing the merge (required checks concluded successfully: ${requiredChecks.join(', ')})`,\n );\n }\n return result('main-verify', 'miss', `${primary}; ${later.note}`);\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n return result('main-verify', 'miss', `could not read merge-commit check-runs: ${message}`);\n }\n}\n\nexport async function checkShipRow(\n ctx: LifecycleContext,\n deps: ShipRowDeps = defaultShipRowDeps,\n): Promise<DodRowResult> {\n const project = deps.readProject(ctx);\n if (!project?.version_sync) {\n return result('ship', 'skip', 'project declares no version_sync; ship step not applicable');\n }\n\n const promotedBatch = deps.findPromotedBatch(ctx);\n const ship = promotedBatch\n ? await deps.readBatchShip(project, promotedBatch)\n : (await deps.readPipeline(ctx))?.ship;\n if (!ship) {\n if (promotedBatch) {\n return result(\n 'ship',\n 'miss',\n `batch ${promotedBatch.name} includes this issue but no durable ship settlement was recorded`,\n );\n }\n return result('ship', 'skip', 'merged outside a batch; ship is batch-scoped');\n }\n if (ship.status === 'passed') {\n return result(\n 'ship',\n 'pass',\n `version ${ship.version ?? 'unknown'} shipped for batch ${ship.batch}; ${ship.paths?.length ?? 0} path(s) verified`,\n );\n }\n if (ship.status === 'pending') {\n return result(\n 'ship',\n 'miss',\n `batch ${ship.batch} merged but no version was shipped — use the Ship version action on the batch card for ${ship.batch}`,\n );\n }\n if (ship.status === 'partial') {\n const failing = ship.paths?.filter(path => !path.ok).map(path => path.path) ?? [];\n return result(\n 'ship',\n 'miss',\n `batch ${ship.batch} partially propagated version ${ship.version ?? 'unknown'}; failing paths: ${failing.join(', ') || 'unknown'}`,\n );\n }\n return result(\n 'ship',\n 'miss',\n `batch ${ship.batch} version ship failed (${ship.errorCode ?? 'unknown-failure'}): ${ship.error ?? ship.reason ?? 'inspect the local dashboard log'}`,\n );\n}\n\nexport async function checkDeployRow(\n ctx: LifecycleContext,\n merge: {\n mergedAt?: string;\n mergeCommit?: string;\n mergedRowStatus?: DodRowResult['status'];\n mainVerifyRowStatus?: DodRowResult['status'];\n },\n deps: DeployRowDeps = defaultDeployRowDeps,\n): Promise<DodRowResult> {\n if (!merge.mergeCommit) {\n if (merge.mergedRowStatus === 'miss') {\n return result(\n 'deploy',\n 'skip',\n 'no merge commit resolved because the merged row missed — deploy ancestry depends on row 4; build ancestry unchecked',\n );\n }\n // PAN-3188: row 6 (main-verify) skips whenever no merge commit is\n // resolvable — the no-durable-anchor landing class (e.g. GitLab-backed\n // landings whose row 4 evidence is the merge specialist's confirmation).\n // Row 7 must agree: same missing anchor, same skip. Only when main-verify\n // did NOT skip is the absent commit an integrity problem worth a miss.\n if (merge.mainVerifyRowStatus === 'skip') {\n return result(\n 'deploy',\n 'skip',\n 'verified-on-main skipped — no merge commit resolvable, so deploy build-ancestry has no durable anchor either (same landing class as DoD row 6 skip)',\n );\n }\n return result('deploy', 'miss', 'merged row passed without a resolvable merge commit; build ancestry cannot be checked');\n }\n\n const baseUrl = deps.dashboardUrl().replace(/\\/$/, '');\n let health: Record<string, unknown>;\n try {\n health = await deps.readJson(`${baseUrl}/api/health`);\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n return result('deploy', 'miss', `dashboard not reachable at ${baseUrl} — a merged fix is not live if no server is serving it: ${message}`);\n }\n\n const repoRoot = typeof health.repoRoot === 'string' ? health.repoRoot : '';\n if (!repoRoot) return result('deploy', 'miss', `dashboard at ${baseUrl} did not report repoRoot`);\n if (resolve(repoRoot) !== resolve(ctx.projectPath)) {\n return result('deploy', 'skip', `live dashboard serves ${repoRoot}, not this project — deploy semantics undefined for ${ctx.projectPath}`);\n }\n\n const buildCommit = typeof health.buildCommit === 'string' && health.buildCommit.trim()\n ? health.buildCommit\n : undefined;\n if (!buildCommit) {\n return result('deploy', 'miss', `dashboard at ${baseUrl} did not report buildCommit; live deployment cannot be proven`);\n }\n try {\n if (health.buildDirty === true) {\n return result(\n 'deploy',\n 'miss',\n `live build ${buildCommit.slice(0, 8)} was built from a dirty working tree — uncommitted changes may be serving; redeploy canonically with \\`pan reload\\``,\n );\n }\n\n const canonical = await deps.commitContains(repoRoot, buildCommit, 'origin/main');\n if (!canonical) {\n return result(\n 'deploy',\n 'miss',\n `live build commit ${buildCommit.slice(0, 8)} is not an ancestor of origin/main — the server is running a build of local-only commits; redeploy with \\`pan reload\\``,\n );\n }\n\n const contains = await deps.commitContains(repoRoot, merge.mergeCommit, buildCommit);\n const observed = `build commit ${buildCommit.slice(0, 8)} ${contains ? 'contains' : 'does not contain'} merge ${merge.mergeCommit.slice(0, 8)}`;\n return result('deploy', contains ? 'pass' : 'miss', observed);\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n return result('deploy', 'miss', `deploy evidence unavailable: ${message}`);\n }\n}\n\nexport async function evaluateDodGate(\n ctx: LifecycleContext,\n opts: {\n acceptedRows?: DodRowId[];\n acceptedBy?: string;\n verifyMerged?: MergedRowDeps['verifyMerged'];\n } = {},\n deps: EvaluateDodGateDeps = defaultEvaluateDodGateDeps,\n): Promise<DodGateResult> {\n const acceptedRows = new Set(opts.acceptedRows ?? []);\n const overridable = new Set(DOD_ROWS.filter(row => row.overridable).map(row => row.id));\n for (const id of acceptedRows) {\n if (!overridable.has(id)) throw new TypeError(`DoD row \"${id}\" cannot be accepted; valid rows: ${[...overridable].join(', ')}`);\n }\n const by = opts.acceptedBy ?? process.env.OVERDECK_AGENT_ID ?? userInfo().username;\n if (acceptedRows.size > 0 && !canAcceptDodMisses(by)) {\n throw new TypeError('The flywheel orchestrator cannot accept missed Definition-of-Done rows; an operator must apply --accept-<row> overrides.');\n }\n\n const merged = opts.verifyMerged\n ? await checkMergedRow(ctx, { ...defaultMergedRowDeps, verifyMerged: opts.verifyMerged })\n : await deps.merged(ctx);\n // Main-verify computes before deploy because deploy's no-merge-commit\n // branch keys on main-verify's outcome (PAN-3188: row 7 skips when row 6\n // skips — both mean \"no durable anchor\" for this landing class). The verdict\n // rows also need this landed-state evidence before they can settle terminal issues.\n const mainVerify = await deps.mainVerify(ctx, merged.mergeCommit);\n const trackerClosed = await (deps.trackerClosed ?? defaultEvaluateDodGateDeps.trackerClosed!)(ctx.issueId);\n const settlement: TerminalVerdictSettlement = {\n trackerClosed,\n landedWork: merged.status === 'pass',\n mainVerifyStatus: mainVerify.status,\n };\n const [review, tests, verification, postMerge, ship, deploy] = await Promise.all([\n deps.review(ctx.issueId, settlement),\n deps.tests(ctx.issueId, settlement),\n deps.verification(ctx.issueId, settlement),\n deps.postMerge(ctx, merged),\n deps.ship(ctx),\n deps.deploy(ctx, {\n mergedAt: merged.mergedAt,\n mergeCommit: merged.mergeCommit,\n mergedRowStatus: merged.status,\n mainVerifyRowStatus: mainVerify.status,\n }),\n ]);\n const rows = [review, tests, verification, merged, postMerge, mainVerify, ship, deploy];\n for (const row of rows) {\n if (row.status === 'miss' && acceptedRows.has(row.id)) {\n row.acceptedBy = { flag: acceptFlagFor(rowDefinition(row.id)), by, at: deps.now() };\n }\n }\n const misses = rows.filter(row => row.status === 'miss').map(row => row.id);\n const accepted = rows.filter(row => row.acceptedBy).map(row => row.id);\n return {\n rows,\n misses,\n accepted,\n passed: rows.every(row => row.status !== 'miss' || Boolean(row.acceptedBy)),\n };\n}\n\n/**\n * PAN-3025: completion witness for the close-out ceremony. Returns closedOutAt\n * only when the journal is terminal AND the live row is gone (the ceremony's\n * final mutating step). closedOut with live status present means a prior run\n * aborted mid-ceremony and must fall through to complete it.\n */\nexport async function readCompletedCloseOut(issueId: string, projectPath: string): Promise<string | null> {\n try {\n const project = resolveProjectForIssue(issueId) ?? getProjectConfigFromWorkspacePath(projectPath);\n const record = await readIssueRecord(project, issueId.toUpperCase());\n if (!record?.pipeline.closedOut) return null;\n // Fail closed: a read error means we cannot confirm absence, so return null (incomplete)\n const live = await Effect.runPromise(getReviewStatus(issueId)).catch(() => 'unknown');\n if (live === 'unknown') return null; // Reject the idempotent path on any read failure\n return live ? null : (record.pipeline.closedOutAt ?? 'unknown');\n } catch {\n // On any error (record read, etc.), fail closed\n return null;\n }\n}\n","/**\n * Residue disposition: close stale convention PRs/MRs for tracker-closed\n * pre-record-era issues with no landing evidence.\n */\n\nimport { execFile } from 'child_process';\nimport { promisify } from 'util';\nimport type { StepResult } from './types.js';\n\nconst execFileAsync = promisify(execFile);\n\n/**\n * Extract GitHub owner/repo from a repository checkout path.\n * Reads the git remote URL to resolve forge coordinates.\n */\nexport async function extractGitHubCoordinates(repoPath: string): Promise<string | null> {\n try {\n const { stdout } = await execFileAsync('git', [\n '-C', repoPath,\n 'remote', 'get-url', 'origin',\n ], { encoding: 'utf-8', timeout: 5_000 });\n\n const remoteUrl = stdout.trim();\n const match = remoteUrl.match(/github\\.com[:/]([^/]+)\\/(.+?)(\\.git)?$/);\n if (match) {\n const owner = match[1];\n const repo = match[2];\n return `${owner}/${repo}`;\n }\n return null;\n } catch {\n return null;\n }\n}\n\n/**\n * Extract GitLab project path from a repository checkout path.\n * Reads the git remote URL to resolve forge coordinates.\n */\nexport async function extractGitLabProject(repoPath: string): Promise<string | null> {\n try {\n const { stdout } = await execFileAsync('git', [\n '-C', repoPath,\n 'remote', 'get-url', 'origin',\n ], { encoding: 'utf-8', timeout: 5_000 });\n\n const remoteUrl = stdout.trim();\n const match = remoteUrl.match(/gitlab\\.com[:/](.+?)(\\.git)?$/);\n if (match) {\n return match[1];\n }\n return null;\n } catch {\n return null;\n }\n}\n\nexport interface CloseResidueConventionPrsContext {\n issueId: string;\n projectPath: string;\n github?: { repos: string[] }; // owner/repo paths for GitHub\n gitlab?: { projects: string[] }; // GitLab project paths\n}\n\nexport async function closeResidueConventionPrs(\n ctx: CloseResidueConventionPrsContext,\n): Promise<StepResult> {\n const evidence: string[] = [];\n const fatalErrors: string[] = [];\n const issueIdLower = ctx.issueId.toLowerCase();\n const possibleHeads = [`feature/${issueIdLower}`, `strike/${issueIdLower}`];\n\n // GitHub PR close — handle all configured repositories\n if (ctx.github?.repos) {\n for (const githubRepo of ctx.github.repos) {\n for (const head of possibleHeads) {\n try {\n const { stdout } = await execFileAsync('gh', [\n 'pr', 'list',\n '--repo', githubRepo,\n '--head', head,\n '--state', 'open',\n '--json', 'number',\n ], { encoding: 'utf-8', timeout: 15_000 });\n\n const prs = JSON.parse(stdout) as Array<{ number: number }>;\n if (prs.length === 0) {\n evidence.push(`No open GitHub PRs found on ${githubRepo}/${head}`);\n }\n for (const pr of prs) {\n try {\n await execFileAsync('gh', [\n 'pr', 'close',\n `${pr.number}`,\n '--repo', githubRepo,\n '--comment', 'Closing stale residue PR with no merge claim — issue closed out without merge landing.',\n ], { encoding: 'utf-8', timeout: 15_000 });\n evidence.push(`Closed GitHub PR #${pr.number} on ${githubRepo}/${head}`);\n } catch (err) {\n fatalErrors.push(`GitHub PR #${pr.number} close failed on ${githubRepo}: ${err instanceof Error ? err.message : String(err)}`);\n }\n }\n } catch (err) {\n fatalErrors.push(`GitHub PR lookup for ${githubRepo}/${head} failed: ${err instanceof Error ? err.message : String(err)}`);\n }\n }\n }\n }\n\n // GitLab MR close — write note BEFORE closing to ensure auditability on retry\n if (ctx.gitlab?.projects) {\n for (const glProject of ctx.gitlab.projects) {\n for (const head of possibleHeads) {\n try {\n const { stdout } = await execFileAsync('glab', [\n 'mr', 'list',\n '--repo', glProject,\n '--source-branch', head,\n '--state', 'opened',\n '--output', 'json',\n ], { encoding: 'utf-8', timeout: 15_000 });\n\n const mrs = JSON.parse(stdout) as Array<{ iid: number }>;\n if (mrs.length === 0) {\n evidence.push(`No open GitLab MRs found on ${glProject}/${head}`);\n }\n for (const mr of mrs) {\n try {\n // Write the audit note BEFORE closing to ensure it's recorded\n await execFileAsync('glab', [\n 'mr', 'note',\n '--repo', glProject,\n String(mr.iid),\n '--message', 'Closing stale residue MR with no merge claim — issue closed out without merge landing.',\n ], { encoding: 'utf-8', timeout: 15_000 });\n\n // Now close the MR\n await execFileAsync('glab', [\n 'mr', 'close',\n '--repo', glProject,\n String(mr.iid),\n ], { encoding: 'utf-8', timeout: 15_000 });\n\n evidence.push(`Closed GitLab MR !${mr.iid} on ${glProject}/${head}`);\n } catch (err) {\n fatalErrors.push(`GitLab MR !${mr.iid} operation failed on ${glProject}: ${err instanceof Error ? err.message : String(err)}`);\n }\n }\n } catch (err) {\n fatalErrors.push(`GitLab MR lookup for ${glProject}/${head} failed: ${err instanceof Error ? err.message : String(err)}`);\n }\n }\n }\n }\n\n // Fail on any repository/operation errors — even if some PRs/MRs closed\n if (fatalErrors.length > 0) {\n return {\n step: 'Close stale convention PRs/MRs',\n success: false,\n skipped: false,\n error: `Failed to list/close convention PRs/MRs: ${fatalErrors.join('; ')}`,\n details: evidence.length > 0 ? [`Partial progress before failure: ${evidence.join('; ')}`] : undefined,\n };\n }\n\n if (evidence.length === 0) {\n return {\n step: 'Close stale convention PRs/MRs',\n success: true,\n skipped: true,\n details: ['No open convention PRs/MRs found on any configured repository'],\n };\n }\n\n return {\n step: 'Close stale convention PRs/MRs',\n success: true,\n skipped: false,\n details: evidence,\n };\n}\n","/**\n * Lifecycle workflows — Compose atomic operations into complete workflows.\n *\n * approve() — Post-merge: archive + close + teardown\n * close() — Simple close: close-issue + teardown\n * closeOut() — Full ceremony: verify-merged + archive + teardown + close + label + clear-status\n * deepWipe() — Destructive: teardown(deleteBranches) + delete agent state + reset issue\n */\n\nimport { existsSync } from 'fs';\nimport { copyFile, readFile, writeFile } from 'fs/promises';\nimport { join } from 'path';\nimport { exec } from 'child_process';\nimport { promisify } from 'util';\nimport { Effect } from 'effect';\nimport { OVERDECK_HOME } from '../paths.js';\nimport type {\n LifecycleContext,\n WorkflowResult,\n StepResult,\n ApproveOptions,\n DeepWipeOptions,\n ArchiveOptions,\n CloseOutOptions,\n} from './types.js';\nimport { stepOk, stepSkipped, stepFailed, getLinearApiKey } from './types.js';\nimport { archivePlanning, findWorkspacePath } from './archive-planning.js';\nimport { closeIssue, type CloseIssueOptions } from './close-issue.js';\nimport { teardownWorkspace } from './teardown-workspace.js';\nimport { loadCloisterConfig } from '../cloister/config.js';\nimport { extractNumberSync, extractPrefixSync } from '../issue-id.js';\nimport { recordFeatureRegistryLifecycle } from '../registry/feature-registry-population.js';\nimport { getForgeAdapter } from '../forge.js';\nimport { resolveProjectReposForIssueSync } from '../project-repos.js';\nimport {\n getProjectConfigFromWorkspacePath,\n markRecordPipelineClosedOutSync,\n markRecordPipelineResidueClosedOutSync,\n writeCloseOutDodGate,\n} from '../pan-dir/record.js';\nimport { pruneStoppedAgentsForIssue } from '../cloister/agent-gc.js';\nimport { isTrackerIssueClosed } from '../cloister/issue-closed.js';\nimport { evaluateDodGate, readCompletedCloseOut } from './dod-gate.js';\nimport { closeResidueConventionPrs, extractGitHubCoordinates, extractGitLabProject } from './residue.js';\nimport {\n capturePipelineStage,\n resolvePipelineTelemetryContext,\n type PipelineTelemetryContext,\n} from '../telemetry/pipeline.js';\nimport { acceptFlagFor, BRANCH_ABSENT_MERGE_ERROR, buildAbandonedDodGate, buildResidueDodGate, DOD_ROWS, type DodGateResult, type DodRowId } from './dod.js';\n\nconst execAsync = promisify(exec);\n\nfunction trackerName(ctx: LifecycleContext, fallback: string): string {\n const name = ctx.tracker?.name ?? fallback;\n return name.charAt(0).toUpperCase() + name.slice(1);\n}\n\n/**\n * Build a WorkflowResult from collected steps.\n */\nfunction buildResult(\n workflow: WorkflowResult['workflow'],\n issueId: string,\n steps: StepResult[],\n startTime: number,\n dodGate?: DodGateResult,\n): WorkflowResult {\n return {\n workflow,\n issueId,\n success: steps.every(s => s.success),\n steps,\n duration: Date.now() - startTime,\n ...(dodGate ? { dodGate } : {}),\n };\n}\n\nfunction hasBlockingFailure(steps: StepResult[]): boolean {\n return steps.some(s => !s.success && !s.skipped);\n}\n\n/**\n * approve() — Post-merge lifecycle.\n */\nexport function approve(\n ctx: LifecycleContext,\n opts: ApproveOptions & CloseIssueOptions & ArchiveOptions = {},\n): Effect.Effect<WorkflowResult> {\n return Effect.gen(function* () {\n const start = Date.now();\n const allSteps: StepResult[] = [];\n\n // 1. Archive planning\n const archiveSteps = yield* archivePlanning(ctx, opts);\n allSteps.push(...archiveSteps);\n\n // If archive failed, stop — don't destroy unarchived artifacts\n const archiveFailed = archiveSteps.some(s => !s.success && !s.skipped);\n if (archiveFailed) {\n allSteps.push(stepFailed('approve:abort', 'Stopped — archiving failed, workspace preserved'));\n return buildResult('approve', ctx.issueId, allSteps, start);\n }\n\n // 2. Close issue\n const closeSteps = yield* closeIssue(ctx, {\n tracker: opts.tracker,\n comment: 'Merged to main via Overdeck lifecycle',\n applyLabel: true,\n });\n allSteps.push(...closeSteps);\n\n // 3. Teardown workspace (delete branches — merge is complete)\n const teardownSteps = yield* teardownWorkspace(ctx, { deleteBranches: true });\n allSteps.push(...teardownSteps);\n\n // 5. Clear review status\n const clearResult = yield* clearReviewStatusStep(ctx.issueId);\n allSteps.push(clearResult);\n\n return buildResult('approve', ctx.issueId, allSteps, start);\n });\n}\n\n/**\n * close() — Simple issue close with teardown.\n */\nexport function close(\n ctx: LifecycleContext,\n opts: CloseIssueOptions = {},\n): Effect.Effect<WorkflowResult> {\n return Effect.gen(function* () {\n const start = Date.now();\n const allSteps: StepResult[] = [];\n\n // 1. Close issue\n const closeSteps = yield* closeIssue(ctx, {\n tracker: opts.tracker,\n reason: opts.reason,\n applyLabel: false,\n });\n allSteps.push(...closeSteps);\n\n // 2. Teardown workspace\n const teardownSteps = yield* teardownWorkspace(ctx);\n allSteps.push(...teardownSteps);\n\n // 3. Clear review status\n const clearResult = yield* clearReviewStatusStep(ctx.issueId);\n allSteps.push(clearResult);\n\n return buildResult('close', ctx.issueId, allSteps, start);\n });\n}\n\n/**\n * closeOut() — Full close-out ceremony.\n *\n * This is the human-gated verification and cleanup workflow.\n * Replaces the monolithic executeCloseOut() function.\n *\n * 1. Verify branch merged (hard fail if not — must pass before any cleanup)\n * 2. Move PRD + archive workspace artifacts (hard fail if archiving fails)\n * 3. Mark xBRIEF completed\n * 4. Clean up workspace (tmux, TLDR, Docker, worktree)\n * 5. Clean up agent state\n * 6. Close issue on tracker\n * 7. Apply closed-out label\n * 8. Clear review status\n */\nexport function closeOut(\n ctx: LifecycleContext,\n opts: CloseIssueOptions & ArchiveOptions & CloseOutOptions = {},\n): Effect.Effect<WorkflowResult> {\n return Effect.gen(function* () {\n const start = Date.now();\n const allSteps: StepResult[] = [];\n\n // PAN-3025: idempotent short-circuit for already-completed close-out ceremony.\n // Fail closed: on any error, proceed to the gate (null = not-confirmed-complete).\n const closedOutAt = yield* Effect.promise(() =>\n readCompletedCloseOut(ctx.issueId, ctx.projectPath).catch(() => null)\n );\n if (closedOutAt) {\n allSteps.push(stepSkipped('close-out:idempotent', [\n `Issue already closed out at ${closedOutAt} — skipping the Definition-of-Done gate and ceremony`,\n ]));\n return buildResult('close-out', ctx.issueId, allSteps, start);\n }\n\n // Recover UAT-promotion evidence before the gate reads the verification verdict.\n const uatEvidenceStep = yield* Effect.promise(async () => {\n try {\n const { healUatPromotionVerification } = await import('../cloister/uat-promote-verification.js');\n const evidence = await healUatPromotionVerification(ctx.issueId);\n if (!evidence) {\n return stepSkipped('dod:uat-promotion-evidence', ['No missing UAT-promotion verification evidence found']);\n }\n return stepOk('dod:uat-promotion-evidence', [\n `Recorded verification from ${evidence.generation}`,\n ...(evidence.mergeSha ? [`Promoted to main at ${evidence.mergeSha.slice(0, 9)}`] : []),\n ]);\n } catch (err) {\n return stepSkipped('dod:uat-promotion-evidence', [\n `Evidence recovery unavailable: ${err instanceof Error ? err.message : String(err)}`,\n ]);\n }\n });\n allSteps.push(uatEvidenceStep);\n\n // 1. Collect residue evidence BEFORE building the gate (if residue disposition)\n const abandon = opts.abandonDisposition;\n const residue = opts.residueDisposition;\n let residueEvidence: string[] = [];\n let trackerClosedEvidence: string[] = [];\n\n if (residue) {\n // Pre-verify tracker is closed for residue disposition\n try {\n const isClosed = yield* Effect.promise(() => isTrackerIssueClosed(ctx.issueId));\n if (!isClosed) {\n allSteps.push(stepFailed('close-out:residue-precondition', 'Residue disposition requires the tracker issue to be already closed'));\n return buildResult('close-out', ctx.issueId, allSteps, start);\n }\n trackerClosedEvidence.push('Tracker issue verified closed');\n } catch (err) {\n allSteps.push(stepFailed('close-out:residue-precondition', `Could not verify tracker closure: ${err instanceof Error ? err.message : String(err)}`));\n return buildResult('close-out', ctx.issueId, allSteps, start);\n }\n\n // Resolve forge coordinates and close stale PRs/MRs\n const prRepos = resolveProjectReposForIssueSync(ctx.issueId);\n if (!prRepos || prRepos.length === 0) {\n allSteps.push(stepFailed('close-out:residue-precondition', 'Could not resolve any configured repositories for residue cleanup'));\n return buildResult('close-out', ctx.issueId, allSteps, start);\n }\n\n // Resolve all GitHub and GitLab coordinates\n const githubPaths = prRepos.filter(r => r.forge === 'github').map(r => r.repoPath);\n const gitlabPaths = prRepos.filter(r => r.forge === 'gitlab').map(r => r.repoPath);\n\n const resolveCoords = yield* Effect.promise(async () => {\n const githubRepos: string[] = [];\n const gitlabRepos: string[] = [];\n const errors: string[] = [];\n\n for (const repoPath of githubPaths) {\n const coords = await extractGitHubCoordinates(repoPath);\n if (coords) {\n githubRepos.push(coords);\n } else {\n errors.push(`GitHub coordinate extraction failed for ${repoPath}`);\n }\n }\n\n for (const repoPath of gitlabPaths) {\n const proj = await extractGitLabProject(repoPath);\n if (proj) {\n gitlabRepos.push(proj);\n } else {\n errors.push(`GitLab project extraction failed for ${repoPath}`);\n }\n }\n\n return { githubRepos, gitlabRepos, errors };\n });\n\n // Fail if any configured repository could not be resolved\n if (resolveCoords.errors.length > 0) {\n allSteps.push(stepFailed('close-out:residue-precondition', `Could not resolve all forge coordinates: ${resolveCoords.errors.join('; ')}`));\n return buildResult('close-out', ctx.issueId, allSteps, start);\n }\n\n // Fail if no repositories were successfully resolved\n if (resolveCoords.githubRepos.length === 0 && resolveCoords.gitlabRepos.length === 0) {\n allSteps.push(stepFailed('close-out:residue-precondition', 'No forge coordinates were resolved for residue cleanup'));\n return buildResult('close-out', ctx.issueId, allSteps, start);\n }\n\n // Execute residue cleanup\n const residueStep = yield* Effect.promise(() => closeResidueConventionPrs({\n issueId: ctx.issueId,\n projectPath: ctx.projectPath,\n github: resolveCoords.githubRepos.length > 0 ? { repos: resolveCoords.githubRepos } : undefined,\n gitlab: resolveCoords.gitlabRepos.length > 0 ? { projects: resolveCoords.gitlabRepos } : undefined,\n }));\n allSteps.push(residueStep);\n if (!residueStep.success && !residueStep.skipped) {\n allSteps.push(stepFailed('close-out:abort', 'Stopped — residue PR/MR close failed'));\n return buildResult('close-out', ctx.issueId, allSteps, start);\n }\n residueEvidence = residueStep.details ?? [];\n }\n\n // 2. Build the Definition-of-Done gate with collected evidence\n let dodGate: DodGateResult = abandon\n ? buildAbandonedDodGate(abandon.reason, abandon.by)\n : residue\n ? buildResidueDodGate(residue.reason, residue.by, [\n ...trackerClosedEvidence,\n ...residueEvidence,\n ])\n : yield* Effect.promise(() => evaluateDodGate(ctx, {\n acceptedRows: opts.dodAcceptedRows,\n acceptedBy: opts.dodAcceptedBy,\n verifyMerged: verifyBranchMergedImpl,\n }));\n\n for (const row of dodGate.rows) {\n const details = [`expected: ${row.expected}`, `observed: ${row.observed}`];\n if (row.acceptedBy) {\n allSteps.push(stepSkipped(`dod:${row.id}`, [\n ...details,\n `MISS accepted via ${row.acceptedBy.flag} by ${row.acceptedBy.by} at ${row.acceptedBy.at}`,\n ]));\n } else if (row.status === 'skip') {\n allSteps.push(stepSkipped(`dod:${row.id}`, details));\n } else if (row.status === 'pass') {\n allSteps.push(stepOk(`dod:${row.id}`, details));\n } else {\n allSteps.push(stepFailed(`dod:${row.id}`, row.observed, details));\n }\n }\n if (!dodGate.passed) {\n const flags = dodGate.rows\n .filter(row => row.status === 'miss' && !row.acceptedBy)\n .map(row => `${row.id} (${acceptFlagFor(DOD_ROWS.find(def => def.id === row.id)!)})`);\n allSteps.push(stepFailed(\n 'close-out:dod-gate',\n `Definition-of-Done gate blocked close-out: ${flags.join(', ')}. Re-run with the named --accept-<row> flag to record an explicit override.`,\n ));\n return buildResult('close-out', ctx.issueId, allSteps, start, dodGate);\n }\n\n // 3. Move PRD + archive workspace artifacts\n // (Note: step numbering adjusted due to upfront residue cleanup)\n\n // 2. Move PRD + archive workspace artifacts\n const archiveSteps = yield* archivePlanning(ctx, opts);\n allSteps.push(...archiveSteps);\n\n // Hard fail on archive failure — don't destroy unarchived artifacts\n const archiveFailed = archiveSteps.some(s => !s.success && !s.skipped);\n if (archiveFailed) {\n allSteps.push(stepFailed('close-out:abort', 'Stopped — archiving failed, workspace preserved'));\n return buildResult('close-out', ctx.issueId, allSteps, start, dodGate);\n }\n\n // 3. Mark the xBRIEF completed on main before teardown removes local state.\n const xbriefStep = yield* Effect.promise(() => completeXBriefStep(ctx));\n allSteps.push(xbriefStep);\n if (!xbriefStep.success && !xbriefStep.skipped) {\n allSteps.push(stepFailed('close-out:abort', 'Stopped — xBRIEF completion failed, workspace preserved'));\n return buildResult('close-out', ctx.issueId, allSteps, start, dodGate);\n }\n\n // 5+6. Teardown workspace + agent state\n const telemetryContext: PipelineTelemetryContext | null = yield* Effect.promise(async () => {\n try {\n return await resolvePipelineTelemetryContext(ctx.issueId);\n } catch {\n // Membership and agent attribution are best-effort and must not abort close-out.\n return null;\n }\n });\n const closeOutConfig = (yield* Effect.promise(() => Effect.runPromise(loadCloisterConfig()))).close_out;\n const teardownSteps = yield* teardownWorkspace(ctx, {\n deleteWorkspace: closeOutConfig?.remove_workspace ?? false,\n deleteBranches: closeOutConfig?.delete_feature_branch ?? false,\n });\n allSteps.push(...teardownSteps);\n if (hasBlockingFailure(teardownSteps)) {\n allSteps.push(stepFailed('close-out:abort', 'Stopped — teardown failed, tracker issue and review status preserved'));\n return buildResult('close-out', ctx.issueId, allSteps, start, dodGate);\n }\n\n const teardownDef = DOD_ROWS.find(row => row.id === 'teardown')!;\n dodGate.rows.push({\n ...teardownDef,\n status: 'pass',\n observed: teardownSteps.flatMap(step => step.details ?? []).join('; ') || 'close-out teardown completed',\n });\n\n // 6+7. Close issue + apply label\n const closeSteps = yield* closeIssue(ctx, {\n tracker: opts.tracker,\n comment: abandon\n ? `Closed without landing evidence — disposition recorded: ${abandon.reason}`\n : ctx.auto ? 'Closed via automatic close-out ceremony' : 'Closed via close-out ceremony',\n applyLabel: true,\n });\n allSteps.push(...closeSteps);\n if (hasBlockingFailure(closeSteps)) {\n allSteps.push(stepFailed('close-out:abort', 'Stopped — issue close failed, review status preserved'));\n return buildResult('close-out', ctx.issueId, allSteps, start, dodGate);\n }\n\n // 8. Mark durable pipeline terminal before clearing the DB cache.\n const markTerminal = yield* markPipelineClosedOutStep(ctx, residue);\n allSteps.push(markTerminal);\n\n // Update gate with verified residue evidence before recording if a residue row exists\n if (residue && residueEvidence.length > 0) {\n const residueRow = dodGate.rows.find(row => (row.id as string) === 'residue');\n if (residueRow) {\n residueRow.observed = residueEvidence.join('; ');\n }\n }\n\n const recordDodGate = yield* recordDodGateStep(ctx, dodGate, abandon, residue);\n allSteps.push(recordDodGate);\n if (!recordDodGate.success) {\n allSteps.push(stepFailed('close-out:abort', 'Stopped — Definition-of-Done audit could not be persisted; review status preserved'));\n return buildResult('close-out', ctx.issueId, allSteps, start, dodGate);\n }\n if (markTerminal.success) {\n const pruned = yield* Effect.promise(() => pruneStoppedAgentsForIssue(ctx.issueId));\n allSteps.push(pruned.preserved.length > 0\n ? stepSkipped('close-out:prune-agent-rows', [`Preserved live agents or terminal rows with retained transcripts: ${pruned.preserved.join(', ')}`])\n : stepOk('close-out:prune-agent-rows', [`Pruned ${pruned.removed.length} stopped agent row(s)`]));\n }\n\n // 9. Clear review status\n const clearResult = yield* clearReviewStatusStep(ctx.issueId);\n allSteps.push(clearResult);\n\n yield* Effect.promise(() => resetPostMergeStateForIssue(ctx.issueId));\n yield* Effect.promise(() => recordFeatureRegistryLifecycle({ issueId: ctx.issueId, status: 'archived' }));\n\n const result = buildResult('close-out', ctx.issueId, allSteps, start, dodGate);\n if (result.success && !markTerminal.skipped) capturePipelineStage('closed_out', telemetryContext);\n return result;\n });\n}\n\nfunction markPipelineClosedOutStep(ctx: LifecycleContext, residue?: { reason: string; by: string }): Effect.Effect<StepResult> {\n const step = 'close-out:mark-pipeline-terminal';\n return Effect.try({\n try: () => {\n const project = getProjectConfigFromWorkspacePath(ctx.projectPath);\n if (residue) {\n markRecordPipelineResidueClosedOutSync(project, ctx.issueId.toUpperCase());\n } else {\n markRecordPipelineClosedOutSync(project, ctx.issueId.toUpperCase());\n }\n return stepOk(step, ['Marked durable pipeline journal closed-out']);\n },\n catch: (err) => err,\n }).pipe(\n Effect.catch((err) =>\n Effect.succeed(stepSkipped(step, [`Pipeline terminal marker failed (non-fatal): ${(err as Error).message ?? String(err)}`])),\n ),\n );\n}\n\nfunction recordDodGateStep(ctx: LifecycleContext, dodGate: DodGateResult, abandonDisposition?: { reason: string; by: string }, residueDisposition?: { reason: string; by: string }): Effect.Effect<StepResult> {\n const step = 'close-out:record-dod-gate';\n return Effect.tryPromise({\n try: async () => {\n const project = getProjectConfigFromWorkspacePath(ctx.projectPath);\n await writeCloseOutDodGate(project, ctx.issueId.toUpperCase(), {\n evaluatedAt: new Date().toISOString(),\n rows: dodGate.rows,\n accepted: dodGate.accepted,\n ...(abandonDisposition ? { disposition: abandonDisposition } : {}),\n ...(residueDisposition ? { disposition: residueDisposition } : {}),\n });\n return stepOk(step, ['Recorded Definition-of-Done gate with 8 rows']);\n },\n catch: (err) => err,\n }).pipe(\n Effect.catch((err) =>\n Effect.succeed(stepFailed(step, `Definition-of-Done gate record failed: ${(err as Error).message ?? String(err)}`)),\n ),\n );\n}\n\n/**\n * deepWipe() — Destructive cleanup for abandoned workspaces.\n */\nfunction destructiveResetWorkflow(\n workflow: 'deep-wipe' | 'reset' | 'cancel',\n ctx: LifecycleContext,\n opts: DeepWipeOptions,\n resetStep: (ctx: LifecycleContext) => Effect.Effect<StepResult>,\n progressLabel: string,\n progressSuccessDetail: string,\n): Effect.Effect<WorkflowResult> {\n return Effect.gen(function* () {\n const start = Date.now();\n const allSteps: StepResult[] = [];\n const { deleteWorkspace = true, deleteBranches = true, resetIssue = true, onProgress } = opts;\n const resetContext = opts.tracker ? { ...ctx, tracker: opts.tracker } : ctx;\n\n const TOTAL_STEPS = 3 + (resetIssue ? 1 : 0);\n let stepNum = 0;\n\n const progress = (label: string, detail: string, status: 'active' | 'complete' | 'error' = 'active') => {\n onProgress?.({ step: stepNum, total: TOTAL_STEPS, label, detail, status });\n };\n\n stepNum = 1;\n\n // Preserve PRD before workspace teardown so it survives reset/cancel.\n const issueLower = ctx.issueId.toLowerCase();\n const workspacePath = findWorkspacePath(ctx.projectPath, issueLower);\n if (workspacePath && existsSync(workspacePath)) {\n const prdPath = join(workspacePath, '.pan', 'prd.md');\n if (existsSync(prdPath)) {\n yield* Effect.tryPromise({\n try: async () => {\n const activeDir = join(ctx.projectPath, 'docs', 'prds', 'active', issueLower);\n const { mkdir } = await import('fs/promises');\n await mkdir(activeDir, { recursive: true });\n await copyFile(prdPath, join(activeDir, 'prd.md'));\n },\n catch: () => null,\n }).pipe(Effect.catch(() => Effect.void));\n }\n }\n\n progress('Tearing down workspace', 'Killing agents, stopping services, removing files');\n const teardownSteps = yield* teardownWorkspace(ctx, {\n deleteWorkspace,\n deleteBranches,\n workspaceConfig: opts.workspaceConfig,\n projectName: opts.projectName,\n });\n allSteps.push(...teardownSteps);\n const teardownFailed = teardownSteps.some(s => !s.success && !s.skipped);\n progress('Tearing down workspace', teardownFailed ? 'Some steps failed' : 'Workspace torn down', teardownFailed ? 'error' : 'complete');\n\n stepNum = 2;\n progress('Deleting git branches', `feature/${ctx.issueId.toLowerCase()}`);\n progress('Deleting git branches', deleteBranches ? 'Branches removed' : 'Skipped', 'complete');\n\n if (resetIssue) {\n stepNum = 3;\n progress(progressLabel, `${ctx.issueId}`);\n const resetResult = yield* resetStep(resetContext);\n allSteps.push(resetResult);\n progress(progressLabel, resetResult.success ? progressSuccessDetail : (resetResult.error || 'Failed'), resetResult.success ? 'complete' : 'error');\n }\n\n stepNum = resetIssue ? 4 : 3;\n progress('Clearing review status', 'Removing specialist state');\n const clearResult = yield* clearReviewStatusStep(ctx.issueId);\n allSteps.push(clearResult);\n progress('Clearing review status', 'Review status cleared', 'complete');\n\n return buildResult(workflow, ctx.issueId, allSteps, start);\n });\n}\n\nexport function deepWipe(\n ctx: LifecycleContext,\n opts: DeepWipeOptions = {},\n): Effect.Effect<WorkflowResult> {\n return destructiveResetWorkflow(\n 'deep-wipe',\n ctx,\n opts,\n resetIssueToTodo,\n 'Resetting issue status',\n 'Issue reset to Todo',\n );\n}\n\nexport function resetToTodo(\n ctx: LifecycleContext,\n opts: DeepWipeOptions = {},\n): Effect.Effect<WorkflowResult> {\n return destructiveResetWorkflow(\n 'reset',\n ctx,\n opts,\n resetIssueToTodo,\n 'Resetting issue status',\n 'Issue reset to Todo',\n );\n}\n\nexport function cancelIssueWorkflow(\n ctx: LifecycleContext,\n opts: DeepWipeOptions = {},\n): Effect.Effect<WorkflowResult> {\n return destructiveResetWorkflow(\n 'cancel',\n ctx,\n opts,\n resetIssueToCanceled,\n 'Canceling issue',\n 'Issue moved to Canceled',\n );\n}\n\n// --- Internal helpers ---\n\nasync function completeXBriefStep(ctx: LifecycleContext): Promise<StepResult> {\n const step = 'close-out:vbrief-completed';\n try {\n const { transitionXBriefOnMain } = await import('../xbrief/lifecycle-io.js');\n const result = await Effect.runPromise(transitionXBriefOnMain(\n ctx.projectPath,\n ctx.issueId,\n 'completed',\n 'completed',\n `scope: complete ${ctx.issueId.toUpperCase()} xBRIEF`,\n ));\n const details = [\n result.moved ? 'Updated xBRIEF lifecycle to completed' : 'xBRIEF lifecycle already completed',\n result.statusUpdated ? 'Updated plan.status to completed' : 'plan.status already completed',\n ];\n if (result.committed) details.push('Committed xBRIEF completion on main');\n return stepOk(step, details);\n } catch (err) {\n const cause = (err as { cause?: unknown }).cause ?? err;\n const message = cause instanceof Error ? cause.message : String(cause);\n if (message.includes('No xBRIEF found')) {\n return stepSkipped(step, [`No xBRIEF found for ${ctx.issueId}`]);\n }\n return stepFailed(step, `xBRIEF completion failed: ${message}`);\n }\n}\n\n/**\n * Verify feature branch is merged into main.\n */\nfunction verifyBranchMerged(ctx: LifecycleContext): Effect.Effect<StepResult> {\n return Effect.tryPromise({\n try: () => verifyBranchMergedImpl(ctx),\n catch: (err) => err,\n }).pipe(\n Effect.catch((err) =>\n Effect.succeed(stepFailed('close-out:verify-merged', `Could not verify merge: ${(err as Error).message}`)),\n ),\n );\n}\n\nexport interface MergeVerificationRoot { repoKey: string; dir: string; sourceBranch: string; targetBranch: string; forge: 'github' | 'gitlab' }\n\nasync function verifyRootConventionBranches(ctx: LifecycleContext, root: MergeVerificationRoot, issueLower: string): Promise<StepResult | null> {\n const featureResult = await verifyConventionBranchMerged(ctx, root);\n if (featureResult?.success) return featureResult;\n\n const strikeResult = await verifyConventionBranchMerged(ctx, { ...root, sourceBranch: `strike/${issueLower}` });\n if (strikeResult?.success && !strikeResult.skipped) {\n return stepOk('close-out:verify-merged', [\n ...(strikeResult.details ?? []),\n `Superseded ${root.sourceBranch} residual: ${featureResult?.error ?? BRANCH_ABSENT_MERGE_ERROR}`,\n ]);\n }\n return featureResult;\n}\n\nexport async function verifyBranchMergedImpl(ctx: LifecycleContext): Promise<StepResult> {\n const step = 'close-out:verify-merged';\n const issueLower = ctx.issueId.toLowerCase();\n const defaultRoot: MergeVerificationRoot = { repoKey: issueLower, dir: ctx.projectPath, sourceBranch: `feature/${issueLower}`, targetBranch: 'main', forge: 'github' };\n\n try {\n // Check review-status first — the merge specialist validates before marking merged\n try {\n const { loadReviewStatuses } = await import('../review-status.js');\n const statuses = loadReviewStatuses();\n const issueKey = ctx.issueId.toUpperCase();\n if (statuses[issueKey]?.mergeStatus === 'merged') {\n return stepOk(step, ['Merge specialist confirmed merge completed']);\n }\n } catch {\n // review-status.json may not exist, continue with git checks\n }\n\n const resolvedRoots = resolveProjectReposForIssueSync(ctx.issueId)\n ?.filter((repo) => repo.required)\n .map((repo): MergeVerificationRoot => ({\n repoKey: repo.repoKey,\n dir: repo.repoPath,\n sourceBranch: repo.sourceBranch,\n targetBranch: repo.targetBranch,\n forge: repo.forge,\n }));\n const roots = resolvedRoots?.length ? resolvedRoots : [defaultRoot];\n if (roots.length === 1) {\n return await verifyRootConventionBranches(ctx, roots[0], issueLower)\n ?? stepFailed(step, BRANCH_ABSENT_MERGE_ERROR);\n }\n\n const results = await Promise.all(roots.map(async (root) => ({\n root,\n result: await verifyRootConventionBranches(ctx, root, issueLower),\n })));\n const failures = results.filter(({ result }) => result && !result.success);\n if (failures.length > 0) {\n return stepFailed(step, failures.map(({ root, result }) => `${root.repoKey}: ${result?.error}`).join('; '));\n }\n const successes = results.filter(({ result }) => result?.success);\n if (successes.length > 0) {\n return stepOk(step, successes.flatMap(({ root, result }) =>\n (result?.details ?? []).map((detail) => `${root.repoKey}: ${detail}`)));\n }\n return stepFailed(step, BRANCH_ABSENT_MERGE_ERROR);\n } catch (err) {\n return stepFailed(step, `Could not verify merge: ${(err as Error).message}`);\n }\n}\n\ninterface GitLabMergeLookupCache {\n attempted: boolean;\n headSha?: string;\n artifact?: { id?: string; url?: string } | null;\n}\n\ninterface GitLabMergeMatch {\n result: StepResult;\n reused: boolean;\n}\n\nexport async function verifyConventionBranchMerged(ctx: LifecycleContext, root: MergeVerificationRoot): Promise<StepResult | null> {\n const step = 'close-out:verify-merged';\n const gitlabLookup: GitLabMergeLookupCache = { attempted: false };\n const { stdout: branchExists } = await execAsync(\n `git branch --list \"${root.sourceBranch}\" 2>/dev/null || true`,\n { cwd: root.dir, encoding: 'utf-8' },\n );\n\n if (branchExists.trim()) {\n // Use merge-base --is-ancestor: checks if the branch tip is reachable from main\n try {\n await execAsync(\n `git merge-base --is-ancestor ${root.sourceBranch} ${root.targetBranch}`,\n { cwd: root.dir, encoding: 'utf-8' },\n );\n const remoteCheck = await verifyRemoteBranchIfPresent(ctx, root, gitlabLookup);\n if (remoteCheck && !remoteCheck.success) return remoteCheck;\n return stepOk(step, ['All commits merged to main', ...(remoteCheck?.details ?? [])]);\n } catch {\n // --is-ancestor fails for squash merges where the branch still exists.\n const gitlabMerged = await verifySquashMergedMrByBranch(root, root.sourceBranch, gitlabLookup);\n if (gitlabMerged) {\n const remoteCheck = await verifyRemoteBranchIfPresent(ctx, root, gitlabLookup);\n if (remoteCheck && !remoteCheck.success) return remoteCheck;\n return stepOk(step, [...(gitlabMerged.result.details ?? []), ...(remoteCheck?.details ?? [])]);\n }\n\n try {\n const { stdout: codeDiff } = await execAsync(\n `git diff ${root.targetBranch}...${root.sourceBranch} -- ':!.planning' ':!docs/prds' ':!.overdeck/prompts' 2>/dev/null || true`,\n { cwd: root.dir, encoding: 'utf-8' },\n );\n if (!codeDiff.trim()) {\n const remoteCheck = await verifyRemoteBranchIfPresent(ctx, root, gitlabLookup);\n if (remoteCheck && !remoteCheck.success) return remoteCheck;\n return stepOk(step, [\n 'Code changes squash-merged to main (only planning artifacts remain on branch)',\n ...(remoteCheck?.details ?? []),\n ]);\n }\n } catch {\n // diff failed — fall through to unmerged report\n }\n\n const githubMerged = root.forge === 'github'\n ? await verifySquashMergedPrByBranch(ctx, root, root.sourceBranch)\n : null;\n if (githubMerged?.success) {\n const remoteCheck = await verifyRemoteBranchIfPresent(ctx, root, gitlabLookup);\n if (remoteCheck && !remoteCheck.success) return remoteCheck;\n return stepOk(step, [\n ...(githubMerged.details ?? []),\n ...(remoteCheck?.details ?? []),\n ]);\n }\n if (githubMerged) return githubMerged;\n\n const { stdout: unmerged } = await execAsync(\n `git log ${root.targetBranch}..${root.sourceBranch} --oneline 2>/dev/null || true`,\n { cwd: root.dir, encoding: 'utf-8' },\n );\n const count = unmerged.trim() ? unmerged.trim().split('\\n').length : 0;\n\n if (ctx.github && root.forge === 'github') {\n try {\n const { stdout: issueState } = await execAsync(\n `gh issue view ${ctx.github.number} --repo ${ctx.github.owner}/${ctx.github.repo} --json state --jq '.state'`,\n { cwd: root.dir, encoding: 'utf-8' },\n );\n if (issueState.trim().toUpperCase() === 'CLOSED') {\n return stepSkipped(step, [`Issue already closed on GitHub; ${count} unmerged commit(s) remain on ${root.sourceBranch}`]);\n }\n } catch {\n // gh check failed — fall through to hard fail\n }\n }\n\n return stepFailed(step, `${count} unmerged commit(s) on ${root.sourceBranch}. Merge before closing out.`);\n }\n }\n\n // Check remote\n const { stdout: remoteBranch } = await execAsync(\n `git ls-remote --heads origin \"${root.sourceBranch}\" 2>/dev/null || true`,\n { cwd: root.dir, encoding: 'utf-8' },\n );\n\n if (remoteBranch.trim()) {\n await execAsync(`git fetch origin ${root.sourceBranch}`, { cwd: root.dir }).catch(() => {});\n const remoteCheck = await verifyRemoteBranchIfPresent(ctx, root, gitlabLookup);\n if (remoteCheck) return remoteCheck;\n }\n\n return null;\n}\n\nasync function verifySquashMergedMrByBranch(\n root: MergeVerificationRoot,\n branchRef: string,\n lookup: GitLabMergeLookupCache,\n): Promise<GitLabMergeMatch | null> {\n if (root.forge !== 'gitlab') return null;\n try {\n const { stdout } = await execAsync(`git rev-parse ${branchRef} 2>/dev/null`, { cwd: root.dir, encoding: 'utf-8' });\n const headSha = stdout.trim();\n if (!headSha) return null;\n const reused = lookup.attempted;\n if (!reused) {\n lookup.attempted = true;\n lookup.headSha = headSha;\n lookup.artifact = await getForgeAdapter('gitlab').findMergedArtifact({\n sourceBranch: root.sourceBranch,\n targetBranch: root.targetBranch,\n headSha,\n cwd: root.dir,\n });\n }\n if (!lookup.artifact || lookup.headSha !== headSha) return null;\n const label = lookup.artifact.id ? `MR !${lookup.artifact.id}` : 'GitLab MR';\n const url = lookup.artifact.url ? ` (${lookup.artifact.url})` : '';\n return {\n result: stepOk('close-out:verify-merged', [`${label} is merged and ${branchRef} matches the merged MR head${url}`]),\n reused,\n };\n } catch {\n return null;\n }\n}\n\ntype GitHubMergedPr = {\n number?: number;\n mergedAt?: string | null;\n headRefOid?: string | null;\n url?: string | null;\n};\n\nasync function verifyRemoteBranchIfPresent(\n ctx: LifecycleContext,\n root: MergeVerificationRoot,\n gitlabLookup: GitLabMergeLookupCache,\n): Promise<StepResult | null> {\n const step = 'close-out:verify-merged';\n const remoteRef = `origin/${root.sourceBranch}`;\n\n const { stdout: remoteBranch } = await execAsync(\n `git ls-remote --heads origin \"${root.sourceBranch}\" 2>/dev/null || true`,\n { cwd: root.dir, encoding: 'utf-8' },\n );\n if (!remoteBranch.trim()) return null;\n\n await execAsync(`git fetch origin ${root.sourceBranch}`, { cwd: root.dir }).catch(() => {});\n\n try {\n await execAsync(\n `git merge-base --is-ancestor ${remoteRef} ${root.targetBranch}`,\n { cwd: root.dir, encoding: 'utf-8' },\n );\n return stepOk(step, ['Remote branch fully merged']);\n } catch {\n // Squash-merge detection for remote branch\n try {\n const { stdout: codeDiff } = await execAsync(\n `git diff ${root.targetBranch}...${remoteRef} -- ':!.planning' ':!docs/prds' ':!.overdeck/prompts' 2>/dev/null || true`,\n { cwd: root.dir, encoding: 'utf-8' },\n );\n if (!codeDiff.trim()) {\n return stepOk(step, ['Remote code changes squash-merged to main (only planning artifacts remain on branch)']);\n }\n } catch {\n // diff failed — fall through\n }\n\n const gitlabMerged = await verifySquashMergedMrByBranch(root, remoteRef, gitlabLookup);\n if (gitlabMerged) {\n return gitlabMerged.reused\n ? stepOk(step, [`Remote ${remoteRef} matches the merged MR head`])\n : gitlabMerged.result;\n }\n\n const githubMerged = root.forge === 'github'\n ? await verifySquashMergedPrByBranch(ctx, root, remoteRef)\n : null;\n if (githubMerged) return githubMerged;\n\n const { stdout: remoteUnmerged } = await execAsync(\n `git log ${root.targetBranch}..${remoteRef} --oneline 2>/dev/null || true`,\n { cwd: root.dir, encoding: 'utf-8' },\n );\n const count = remoteUnmerged.trim() ? remoteUnmerged.trim().split('\\n').length : 0;\n\n if (ctx.github && root.forge === 'github') {\n try {\n const { stdout: issueState } = await execAsync(\n `gh issue view ${ctx.github.number} --repo ${ctx.github.owner}/${ctx.github.repo} --json state --jq '.state'`,\n { cwd: root.dir, encoding: 'utf-8' },\n );\n if (issueState.trim().toUpperCase() === 'CLOSED') {\n return stepSkipped(step, [`Issue already closed on GitHub; ${count} unmerged commit(s) remain on remote ${root.sourceBranch}`]);\n }\n } catch {\n // gh check failed — fall through to hard fail\n }\n }\n\n return stepFailed(step, `${count} unmerged commit(s) on remote ${root.sourceBranch}.`);\n }\n}\n\nasync function verifySquashMergedPrByBranch(\n ctx: LifecycleContext,\n root: MergeVerificationRoot,\n branchRef: string,\n): Promise<StepResult | null> {\n if (!ctx.github) return null;\n\n const step = 'close-out:verify-merged';\n const { owner, repo } = ctx.github;\n const remoteTargetRef = `origin/${root.targetBranch}`;\n\n try {\n const { stdout: prJson } = await execAsync(\n `gh pr list --repo ${owner}/${repo} --state merged --head ${JSON.stringify(root.sourceBranch)} --json number,mergedAt,headRefOid,url`,\n { cwd: root.dir, encoding: 'utf-8' },\n );\n const prs = JSON.parse(prJson) as GitHubMergedPr[];\n const mergedPr = prs\n .filter((pr) => (\n typeof pr.mergedAt === 'string'\n && pr.mergedAt.length > 0\n && typeof pr.headRefOid === 'string'\n && pr.headRefOid.length > 0\n ))\n .sort((a, b) => Date.parse(String(b.mergedAt)) - Date.parse(String(a.mergedAt)))[0];\n if (!mergedPr?.headRefOid) return null;\n\n const { stdout: tipShaRaw } = await execAsync(\n `git rev-parse ${branchRef} 2>/dev/null`,\n { cwd: root.dir, encoding: 'utf-8' },\n );\n const tipSha = tipShaRaw.trim();\n if (!tipSha) return null;\n\n if (tipSha === mergedPr.headRefOid) {\n const prLabel = typeof mergedPr.number === 'number' ? `PR #${mergedPr.number}` : 'GitHub PR';\n return stepOk(step, [`${prLabel} is squash-merged and ${branchRef} matches the merged PR head`]);\n }\n\n // A workspace may merge a newer target branch after its PR head was pushed.\n // Ignore those merge commits and allow close-out when every post-PR commit\n // is already on the configured remote target; only branch-unique work is unsafe.\n let commitsNotOnTarget: string[] | null = null;\n try {\n const { stdout: commitsRaw } = await execAsync(\n `git log --no-merges --format=%H ${mergedPr.headRefOid}..${tipSha}`,\n { cwd: root.dir, encoding: 'utf-8' },\n );\n const commits = commitsRaw.split('\\n').map((sha) => sha.trim()).filter(Boolean);\n const unmerged: string[] = [];\n for (const commit of commits) {\n try {\n await execAsync(\n `git merge-base --is-ancestor ${commit} ${remoteTargetRef}`,\n { cwd: root.dir, encoding: 'utf-8' },\n );\n } catch {\n unmerged.push(commit);\n }\n }\n if (unmerged.length === 0) {\n const prLabel = typeof mergedPr.number === 'number' ? `PR #${mergedPr.number}` : 'GitHub PR';\n return stepOk(step, [\n `${prLabel} is squash-merged; all ${commits.length} post-PR non-merge commit(s) on ${branchRef} are already on ${remoteTargetRef}`,\n ]);\n }\n\n commitsNotOnTarget = unmerged;\n } catch { /* containment check failure falls through to the state-plane policy */ }\n\n // PAN-2406 / state-plane policy rule 3: commits after the merged head that\n // touch ONLY legacy state-plane paths under .pan/ are pipeline exhaust —\n // e.g. 'chore: record merge status' — and must not block close-out.\n try {\n const { stdout: deltaRaw } = await execAsync(\n `git diff --name-only ${mergedPr.headRefOid}..${tipSha}`,\n { cwd: root.dir, encoding: 'utf-8' },\n );\n let deltaFiles = deltaRaw.split('\\n').map((f) => f.trim()).filter(Boolean);\n let statePlaneOnly = deltaFiles.length > 0 && deltaFiles.every((f) =>\n f.startsWith('.pan/'));\n if (!statePlaneOnly) {\n // Branch may have merged its target INTO itself after the PR merged —\n // the two-dot delta then contains target-branch files. Judge only changes\n // UNIQUE to the branch (three-dot vs the remote target): if those are\n // state-plane-only, everything real is already on the target.\n const { stdout: uniqueRaw } = await execAsync(\n `git diff --name-only ${remoteTargetRef}...${tipSha}`,\n { cwd: root.dir, encoding: 'utf-8' },\n );\n deltaFiles = uniqueRaw.split('\\n').map((f) => f.trim()).filter(Boolean);\n statePlaneOnly = deltaFiles.every((f) =>\n f.startsWith('.pan/'));\n }\n if (statePlaneOnly) {\n const prLabel = typeof mergedPr.number === 'number' ? `PR #${mergedPr.number}` : 'GitHub PR';\n return stepOk(step, [\n `${prLabel} is squash-merged; ${branchRef} is ahead only by state-plane commits (${deltaFiles.length} file(s): .pan) — accepted per state-plane policy`,\n ]);\n }\n } catch { /* diff failure falls through to the strict rejection below */ }\n\n const prLabel = typeof mergedPr.number === 'number' ? `PR #${mergedPr.number}` : 'merged GitHub PR';\n if (commitsNotOnTarget) {\n return stepFailed(step, `${branchRef} has ${commitsNotOnTarget.length} commit(s) after merged ${prLabel} that are not on ${remoteTargetRef}: ${commitsNotOnTarget.join(', ')}`);\n }\n return stepFailed(step, `${branchRef} does not match the head commit of merged ${prLabel}; inspect before closing out.`);\n } catch {\n return null;\n }\n}\n\n/**\n * Reset issue back to open/backlog state (for destructive reset).\n */\nfunction resetIssueToTodo(ctx: LifecycleContext): Effect.Effect<StepResult> {\n return Effect.tryPromise({\n try: () => resetIssueToTodoImpl(ctx),\n catch: (err) => err,\n }).pipe(\n Effect.catch((err) =>\n Effect.succeed(stepFailed('reset:reset-issue', `Failed to reset issue: ${(err as Error).message}`)),\n ),\n );\n}\n\nasync function resetIssueToTodoImpl(ctx: LifecycleContext): Promise<StepResult> {\n const step = 'reset:reset-issue';\n try {\n if (ctx.github) {\n const { owner, repo, number } = ctx.github;\n // Reopen the issue\n await execAsync(\n `gh issue reopen ${number} --repo ${owner}/${repo}`,\n { encoding: 'utf-8' },\n ).catch(() => {}); // May already be open\n // Remove lifecycle labels\n const labelsToRemove = ['in-review', 'in-progress', 'planned', 'planning', 'Review: Approved', 'Review: Failed', 'ready-for-merge'];\n for (const label of labelsToRemove) {\n await execAsync(\n `gh issue edit ${number} --repo ${owner}/${repo} --remove-label \"${label}\"`,\n { encoding: 'utf-8' },\n ).catch(() => {}); // Label may not exist\n }\n return stepOk(step, [`Reset GitHub issue #${number}: reopened and cleared labels`]);\n }\n\n // Linear: reopen to Todo\n const linearApiKey = await getLinearApiKey();\n if (linearApiKey) {\n const { LinearClient } = await import('@linear/sdk');\n const client = new LinearClient({ apiKey: linearApiKey });\n const issueNum = extractNumberSync(ctx.issueId);\n const teamKey = extractPrefixSync(ctx.issueId);\n if (issueNum === null || teamKey === null) {\n return stepFailed(step, `Could not parse issue ID: ${ctx.issueId}`);\n }\n const results = await client.issues({\n filter: {\n number: { eq: issueNum },\n team: { key: { eq: teamKey } },\n },\n first: 1,\n });\n if (results.nodes.length > 0) {\n const issue = results.nodes[0];\n const team = await issue.team;\n if (team) {\n const states = await team.states();\n const todoState = states.nodes.find(s => s.type === 'unstarted' && s.name === 'Todo') ||\n states.nodes.find(s => s.type === 'unstarted');\n if (todoState) {\n await issue.update({ stateId: todoState.id });\n }\n }\n }\n return stepOk(step, [`Reset ${trackerName(ctx, 'linear')} issue ${ctx.issueId} to Todo`]);\n }\n\n return stepSkipped(step, ['No tracker available to reset issue']);\n } catch (err) {\n return stepFailed(step, `Failed to reset issue: ${(err as Error).message}`);\n }\n}\n\n/**\n * Clear review status for an issue.\n */\nasync function resetPostMergeStateForIssue(issueId: string): Promise<void> {\n try {\n const { resetPostMergeState } = await import('../cloister/merge-agent.js');\n resetPostMergeState(issueId);\n resetPostMergeState(issueId.toUpperCase());\n } catch {\n return;\n }\n}\n\nfunction clearReviewStatusStep(issueId: string): Effect.Effect<StepResult> {\n return Effect.tryPromise({\n try: () => clearReviewStatusStepImpl(issueId),\n catch: (err) => err,\n }).pipe(\n Effect.catch((err) =>\n Effect.succeed(stepSkipped('clear-review-status', [`Failed to clear review status (non-fatal): ${(err as Error).message}`])),\n ),\n );\n}\n\nasync function clearReviewStatusStepImpl(issueId: string): Promise<StepResult> {\n const step = 'clear-review-status';\n try {\n const { clearReviewStatus } = await import('../review-status.js');\n clearReviewStatus(issueId.toUpperCase());\n return stepOk(step, ['Review status cleared']);\n } catch {\n // Fallback: direct file manipulation\n try {\n const statusFile = join(OVERDECK_HOME, 'review-status.json');\n if (existsSync(statusFile)) {\n const data = JSON.parse(await readFile(statusFile, 'utf-8'));\n const upperKey = issueId.toUpperCase();\n if (data[upperKey]) {\n delete data[upperKey];\n await writeFile(statusFile, JSON.stringify(data, null, 2));\n }\n }\n return stepOk(step, ['Review status cleared (direct)']);\n } catch (innerErr) {\n return stepSkipped(step, [`Failed to clear review status (non-fatal): ${(innerErr as Error).message}`]);\n }\n }\n}\n\nexport const __testInternals = {\n completeXBriefStep,\n verifyBranchMerged,\n};\n\nfunction resetIssueToCanceled(ctx: LifecycleContext): Effect.Effect<StepResult> {\n return Effect.tryPromise({\n try: () => resetIssueToCanceledImpl(ctx),\n catch: (err) => err,\n }).pipe(\n Effect.catch((err) =>\n Effect.succeed(stepFailed('cancel:reset-issue', `Failed to cancel issue: ${(err as Error).message}`)),\n ),\n );\n}\n\nasync function resetIssueToCanceledImpl(ctx: LifecycleContext): Promise<StepResult> {\n const step = 'cancel:reset-issue';\n try {\n if (ctx.github) {\n const { owner, repo, number } = ctx.github;\n await execAsync(\n `gh issue edit ${number} --repo ${owner}/${repo} --add-label \"wontfix\"`,\n { encoding: 'utf-8' },\n ).catch(() => {});\n return stepOk(step, [`Marked GitHub issue #${number} as canceled/wontfix`]);\n }\n\n const linearApiKey = await getLinearApiKey();\n if (linearApiKey) {\n const { LinearClient } = await import('@linear/sdk');\n const client = new LinearClient({ apiKey: linearApiKey });\n const issueNum = extractNumberSync(ctx.issueId);\n const teamKey = extractPrefixSync(ctx.issueId);\n if (issueNum === null || teamKey === null) {\n return stepFailed(step, `Could not parse issue ID: ${ctx.issueId}`);\n }\n const results = await client.issues({\n filter: {\n number: { eq: issueNum },\n team: { key: { eq: teamKey } },\n },\n first: 1,\n });\n if (results.nodes.length > 0) {\n const issue = results.nodes[0];\n const team = await issue.team;\n if (team) {\n const states = await team.states();\n const canceledState = states.nodes.find(s => s.type === 'canceled') ||\n states.nodes.find(s => s.name.toLowerCase() === 'canceled');\n if (canceledState) {\n await issue.update({ stateId: canceledState.id });\n }\n }\n }\n return stepOk(step, [`Reset ${trackerName(ctx, 'linear')} issue ${ctx.issueId} to Canceled`]);\n }\n\n return stepSkipped(step, ['No tracker available to cancel issue']);\n } catch (err) {\n return stepFailed(step, `Failed to cancel issue: ${(err as Error).message}`);\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"lifecycle-D8m3w850.js","names":["execAsync","promisify","mkdir","cp","rename","readdir","readlink","execAsync","promisify","readFile","rm","join","execFileAsync","promisify","defaultDeps","execFileAsync","execFile","resolve","promisify","promisify","copyFile","readFile","writeFile"],"sources":["../src/lib/prd-locations.ts","../src/lib/lifecycle/archive-planning.ts","../src/lib/process-cwd.ts","../src/lib/lifecycle/teardown-workspace.ts","../src/lib/cloister/agent-gc.ts","../src/lib/cloister/deacon-canonical-state.ts","../src/lib/cloister/ship-status.ts","../src/lib/lifecycle/dod.ts","../src/lib/lifecycle/dod-gate.ts","../src/lib/lifecycle/residue.ts","../src/lib/lifecycle/workflows.ts"],"sourcesContent":["/**\n * PRD location resolution — single source of truth.\n *\n * PRDs have historically been written in four formats due to a casing bug:\n * 1. docs/prds/<status>/<id-lower>/ — canonical subdirectory (new)\n * 2. docs/prds/<status>/<ID-UPPER>/ — buggy uppercase variant\n * 3. docs/prds/<status>/<id-lower>-plan.md — legacy flat file\n * 4. docs/prds/<status>/<ID-UPPER>-plan.md — buggy uppercase flat file\n *\n * All readers MUST go through findPrdAtStatus / findPrdAnywhere so they tolerate\n * every variant. All writers MUST use canonicalPrdSubdir so new artifacts only\n * land in the canonical lowercase subdirectory format.\n */\n\nimport { existsSync } from 'fs';\nimport { access, readFile, readdir } from 'node:fs/promises';\nimport { join } from 'path';\nimport { Effect } from 'effect';\nimport {\n PROJECT_DOCS_SUBDIR,\n PROJECT_PRDS_SUBDIR,\n PROJECT_PRDS_ACTIVE_SUBDIR,\n PROJECT_PRDS_PLANNED_SUBDIR,\n PROJECT_PRDS_COMPLETED_SUBDIR,\n} from './paths.js';\nimport { getDraftPath, getIssueDraftPath } from './pan-dir/index.js';\n\nexport type PrdStatus = 'active' | 'planned' | 'completed' | 'draft';\nexport type PrdFormat = 'subdir' | 'flat' | 'pan-draft';\n\nexport interface PrdLocation {\n /** Absolute path to either the per-issue subdirectory or the flat .md file. */\n path: string;\n format: PrdFormat;\n status: PrdStatus;\n}\n\n/** Read Markdown from any location returned by this module's PRD resolvers. */\nexport async function readPrdContent(loc: PrdLocation): Promise<string | null> {\n if (loc.format === 'flat' || loc.format === 'pan-draft') {\n return readFile(loc.path, 'utf8').catch(() => null);\n }\n\n const files = (await readdir(loc.path).catch(() => [] as string[]))\n .filter((file) => file.endsWith('.md'))\n .sort((a, b) => {\n if (a === 'prd.md') return -1;\n if (b === 'prd.md') return 1;\n return a.localeCompare(b);\n });\n const firstMarkdown = files[0];\n if (!firstMarkdown) return null;\n return readFile(join(loc.path, firstMarkdown), 'utf8').catch(() => null);\n}\n\nconst STATUS_DIRS: Record<Exclude<PrdStatus, 'draft'>, string> = {\n active: PROJECT_PRDS_ACTIVE_SUBDIR,\n planned: PROJECT_PRDS_PLANNED_SUBDIR,\n completed: PROJECT_PRDS_COMPLETED_SUBDIR,\n};\n\nfunction statusRoot(projectPath: string, status: Exclude<PrdStatus, 'draft'>): string {\n return join(projectPath, PROJECT_DOCS_SUBDIR, PROJECT_PRDS_SUBDIR, STATUS_DIRS[status]);\n}\n\n/**\n * Canonical lowercase subdirectory path. Always use this for NEW writes.\n * Does not check existence — callers create the directory as needed.\n */\nexport function canonicalPrdSubdirSync(\n projectPath: string,\n issueId: string,\n status: Exclude<PrdStatus, 'draft'>,\n): string {\n return join(statusRoot(projectPath, status), issueId.toLowerCase());\n}\n\n/**\n * Find an existing PRD for an issue under a single lifecycle status.\n * Checks all four legacy/buggy formats, preferring canonical.\n */\nexport function findPrdAtStatusSync(\n projectPath: string,\n issueId: string,\n status: Exclude<PrdStatus, 'draft'>,\n): PrdLocation | null {\n const root = statusRoot(projectPath, status);\n const lower = issueId.toLowerCase();\n const upper = issueId.toUpperCase();\n\n const candidates: PrdLocation[] = [\n { path: join(root, lower), format: 'subdir', status },\n { path: join(root, upper), format: 'subdir', status },\n { path: join(root, `${lower}-plan.md`), format: 'flat', status },\n { path: join(root, `${upper}-plan.md`), format: 'flat', status },\n ];\n\n for (const c of candidates) {\n if (existsSync(c.path)) return c;\n }\n return null;\n}\n\nfunction draftPrdCandidates(projectPath: string, issueId: string): PrdLocation[] {\n // Canonical drafts exist in both filename cases on disk (the door writes\n // UPPER.md; humans and conversations historically wrote lower.md) — accept\n // either, matching checkPrdGateSync.\n return [\n { path: getIssueDraftPath(projectPath, issueId), format: 'pan-draft', status: 'draft' },\n { path: getDraftPath(projectPath, `${issueId.toLowerCase()}.md`), format: 'pan-draft', status: 'draft' },\n ];\n}\n\nexport function findDraftPrdSync(projectPath: string, issueId: string): PrdLocation | null {\n for (const candidate of draftPrdCandidates(projectPath, issueId)) {\n if (existsSync(candidate.path)) return candidate;\n }\n return null;\n}\n\nexport async function findDraftPrdAsync(projectPath: string, issueId: string): Promise<PrdLocation | null> {\n for (const candidate of draftPrdCandidates(projectPath, issueId)) {\n try {\n await access(candidate.path);\n return candidate;\n } catch {\n // Try the historical filename case before reporting no draft.\n }\n }\n return null;\n}\n\n/**\n * Find a PRD across all lifecycle statuses, in priority order:\n * active → completed → planned → draft. Returns the first match or null.\n */\nexport function findPrdAnywhereSync(\n projectPath: string,\n issueId: string,\n): PrdLocation | null {\n for (const status of ['active', 'completed', 'planned'] as const) {\n const loc = findPrdAtStatusSync(projectPath, issueId, status);\n if (loc) return loc;\n }\n return findDraftPrdSync(projectPath, issueId)\n}\n\n// ─── Effect variants (PAN-1249) ───────────────────────────────────────────────\n//\n// Path-only helpers stay synchronous. Draft discovery uses the promise-based\n// filesystem door so resource refreshes never block the dashboard event loop.\n\n/** Effect variant of {@link canonicalPrdSubdirSync}. */\nexport const canonicalPrdSubdir = (\n projectPath: string,\n issueId: string,\n status: Exclude<PrdStatus, 'draft'>,\n): Effect.Effect<string, never> =>\n Effect.sync(() => canonicalPrdSubdirSync(projectPath, issueId, status));\n\n/** Effect variant of {@link findPrdAtStatusSync}. */\nexport const findPrdAtStatus = (\n projectPath: string,\n issueId: string,\n status: Exclude<PrdStatus, 'draft'>,\n): Effect.Effect<PrdLocation | null, never> =>\n Effect.sync(() => findPrdAtStatusSync(projectPath, issueId, status));\n\n/** Async Effect variant of {@link findDraftPrdAsync}. */\nexport const findDraftPrd = (\n projectPath: string,\n issueId: string,\n): Effect.Effect<PrdLocation | null, never> =>\n Effect.promise(() => findDraftPrdAsync(projectPath, issueId));\n\n/** Effect variant of {@link findPrdAnywhereSync}. */\nexport const findPrdAnywhere = (\n projectPath: string,\n issueId: string,\n): Effect.Effect<PrdLocation | null, never> =>\n Effect.sync(() => findPrdAnywhereSync(projectPath, issueId));\n","/**\n * archive-planning — PRD active→completed + workspace .pan/ preservation.\n *\n * Consolidates PRD moving from close-out.ts, merge-agent.ts, and the\n * approve endpoint into a single idempotent operation.\n *\n * Steps:\n * 1. Move PRD from docs/prds/active/ → docs/prds/completed/ (git mv, fallback to copy)\n * 2. Archive workspace .pan/ artifacts to ~/.overdeck/archives/<issue>/\n * 3. Rotate previous archives to prevent overwrite\n */\n\nimport { existsSync, readdirSync } from 'fs';\nimport { mkdir, cp, rename } from 'fs/promises';\nimport { join, dirname } from 'path';\nimport { exec } from 'child_process';\nimport { promisify } from 'util';\nimport { Effect } from 'effect';\nimport {\n ARCHIVES_DIR,\n PROJECT_DOCS_SUBDIR,\n PROJECT_PRDS_SUBDIR,\n PROJECT_PRDS_ACTIVE_SUBDIR,\n PROJECT_PRDS_COMPLETED_SUBDIR,\n} from '../paths.js';\nimport {\n PAN_CONTINUE_FILENAME,\n PAN_CONTEXT_FILENAME,\n PAN_DIRNAME,\n PAN_FEEDBACK_DIRNAME,\n PAN_SESSIONS_FILENAME,\n PAN_SPEC_FILENAME,\n} from '../pan-dir/index.js';\nimport { findPrdAtStatusSync, canonicalPrdSubdirSync } from '../prd-locations.js';\nimport type { LifecycleContext, StepResult, ArchiveOptions } from './types.js';\nimport { stepOk, stepSkipped, stepFailed } from './types.js';\n\nconst execAsync = promisify(exec);\n\n/**\n * Find ALL workspace paths matching an issue across canonical and legacy\n * naming conventions. Returns every candidate that exists on disk, so callers\n * (especially teardown) can clean up drift where the same issue ended up with\n * both `feature-pan-XXXX` and `feature-XXXX` directories. Order is canonical\n * → legacy, so first-match callers still prefer the canonical form.\n */\nexport function findAllWorkspacePaths(projectPath: string, issueLower: string): string[] {\n // e.g. \"pan-488\" → \"488\" for legacy workspaces named feature-488\n const numericSuffix = issueLower.replace(/^[a-z]+-/, '');\n const workspaceRoot = join(projectPath, 'workspaces');\n const slotWorkspaces = existsSync(workspaceRoot)\n ? readdirSync(workspaceRoot).filter(name => name.startsWith(`feature-${issueLower}-slot-`)).map(name => join(workspaceRoot, name))\n : [];\n const candidates = [\n join(projectPath, 'workspaces', `feature-${issueLower}`),\n // Strike worktrees (`pan strike`) use the `-strike` suffix (PAN-1721)\n join(projectPath, 'workspaces', `feature-${issueLower}-strike`),\n join(projectPath, 'workspaces', `feature-${numericSuffix}`),\n join(projectPath, 'workspaces', issueLower),\n join(projectPath, '.worktrees', issueLower),\n join(dirname(projectPath), `feature-${issueLower}`),\n ...slotWorkspaces,\n ];\n return candidates.filter((p) => existsSync(p));\n}\n\n/**\n * Find the workspace path for an issue.\n * Checks multiple conventional locations, including legacy numeric-suffix naming.\n * Prefer findAllWorkspacePaths when cleaning up — this only returns the first match.\n */\nexport function findWorkspacePath(projectPath: string, issueLower: string): string | null {\n return findAllWorkspacePaths(projectPath, issueLower)[0] ?? null;\n}\n\n/**\n * Infer the git branch for an issue from its resolved workspace path.\n * Strike workspaces (path ends in `-strike`) use `strike/<id>`; every other\n * workspace uses `feature/<id>`. PAN-2270: review-dispatch callers must derive\n * the branch this way instead of hardcoding `feature/${issueLower}`.\n */\nexport function inferBranchFromWorkspace(workspacePath: string, issueLower: string): string {\n if (workspacePath.endsWith('-strike')) return `strike/${issueLower}`;\n return `feature/${issueLower}`;\n}\n\n/**\n * Move PRD from active/ to completed/ directory.\n * Uses git mv with fallback to plain copy. Idempotent — skips if already completed.\n */\nexport function movePrd(\n ctx: LifecycleContext,\n opts: ArchiveOptions = {},\n): Effect.Effect<StepResult> {\n return Effect.tryPromise({\n try: () => movePrdImpl(ctx, opts),\n catch: (err) => err,\n }).pipe(\n Effect.catch((err) =>\n Effect.succeed(stepFailed('archive-planning:move-prd', `Failed to preserve PRD: ${(err as Error).message}`)),\n ),\n );\n}\n\nasync function movePrdImpl(\n ctx: LifecycleContext,\n opts: ArchiveOptions,\n): Promise<StepResult> {\n const { pushToRemote = true } = opts;\n const step = 'archive-planning:move-prd';\n\n // Idempotent skip: if any completed PRD already exists in any format, we're done.\n if (findPrdAtStatusSync(ctx.projectPath, ctx.issueId, 'completed')) {\n return stepSkipped(step, ['PRD already in completed/']);\n }\n\n // Find source PRD in active/ in any format/case (subdir lower/upper, flat lower/upper).\n const source = findPrdAtStatusSync(ctx.projectPath, ctx.issueId, 'active');\n if (!source) {\n return stepSkipped(step, ['No PRD found in active/ (may not have had one)']);\n }\n\n // Destination is always the canonical lowercase form, mirroring source format.\n const issueLower = ctx.issueId.toLowerCase();\n const completedSubdir = canonicalPrdSubdirSync(ctx.projectPath, ctx.issueId, 'completed');\n const completedFlat = join(\n ctx.projectPath, PROJECT_DOCS_SUBDIR, PROJECT_PRDS_SUBDIR,\n PROJECT_PRDS_COMPLETED_SUBDIR, `${issueLower}-plan.md`,\n );\n const dest = source.format === 'subdir' ? completedSubdir : completedFlat;\n const destParent = dirname(dest);\n if (!existsSync(destParent)) {\n await mkdir(destParent, { recursive: true });\n }\n\n const formatLabel = source.format === 'subdir' ? 'PRD subdirectory' : 'PRD';\n\n // For the legacy `flat` format the PRD is a single `.md` file, but the xBRIEF\n // JSON sidecar (`<id>-plan.vbrief.json`) lives next to it and was historically\n // left behind in active/ after merge (PAN-487). Detect it here so we can move\n // it alongside the `.md`. The `subdir` format already moves both files because\n // they share the directory.\n const flatActive = source.format === 'flat'\n ? join(ctx.projectPath, PROJECT_DOCS_SUBDIR, PROJECT_PRDS_SUBDIR, PROJECT_PRDS_ACTIVE_SUBDIR)\n : null;\n const flatCompleted = source.format === 'flat'\n ? join(ctx.projectPath, PROJECT_DOCS_SUBDIR, PROJECT_PRDS_SUBDIR, PROJECT_PRDS_COMPLETED_SUBDIR)\n : null;\n const sidecarLower = flatActive ? join(flatActive, `${issueLower}-plan.vbrief.json`) : null;\n const sidecarUpper = flatActive ? join(flatActive, `${ctx.issueId.toUpperCase()}-plan.vbrief.json`) : null;\n // Always land sidecar at canonical lowercase in completed/.\n const sidecarDest = flatCompleted ? join(flatCompleted, `${issueLower}-plan.vbrief.json`) : null;\n const resolvedSidecarSource = sidecarLower && existsSync(sidecarLower)\n ? sidecarLower\n : (sidecarUpper && existsSync(sidecarUpper) ? sidecarUpper : null);\n\n try {\n await execAsync(`git mv \"${source.path}\" \"${dest}\"`, { cwd: ctx.projectPath });\n if (resolvedSidecarSource && sidecarDest) {\n try {\n await execAsync(`git mv \"${resolvedSidecarSource}\" \"${sidecarDest}\"`, { cwd: ctx.projectPath });\n } catch {\n // sidecar may not be tracked — fall back to plain copy\n try { await cp(resolvedSidecarSource, sidecarDest); } catch { /* non-fatal */ }\n }\n }\n // The commit subject must be commitlint-compliant (type prefix required) —\n // a bare \"Move X PRD to completed\" is rejected by commit-msg, and before the\n // guard below that left the rename staged with the close-out wedged (PAN-3211).\n await execAsync(`git commit -m \"chore: move ${ctx.issueId} PRD to completed\"`, { cwd: ctx.projectPath });\n if (pushToRemote) {\n await execAsync('git push', { cwd: ctx.projectPath });\n }\n const sidecarNote = resolvedSidecarSource ? ' (with xBRIEF sidecar)' : '';\n return stepOk(step, [`Moved ${formatLabel} from active/ to completed/ via git mv${sidecarNote}`]);\n } catch {\n // git mv failed — fall back to plain copy. cp handles both file and directory.\n // Guard: if git mv already moved the source and a LATER step (commit/push)\n // failed, the destination holds the PRD and the rename sits staged. cp would\n // then ENOENT on the vanished source and bury the real failure (PAN-3211).\n if (!existsSync(source.path)) {\n if (existsSync(dest)) {\n return stepFailed(step, 'PRD moved to completed/ but the follow-up git commit failed; the rename is staged — commit it and re-run close-out');\n }\n return stepFailed(step, `PRD source ${source.path} is missing — nothing to preserve`);\n }\n try {\n await cp(source.path, dest, { recursive: true });\n if (!existsSync(dest)) {\n return stepFailed(step, 'PRD copy appeared to succeed but destination not found');\n }\n if (resolvedSidecarSource && sidecarDest) {\n try { await cp(resolvedSidecarSource, sidecarDest); } catch { /* non-fatal */ }\n }\n return stepOk(step, [`Copied ${formatLabel} to completed/ (git mv failed, plain copy succeeded)`]);\n } catch (err) {\n return stepFailed(step, `Failed to preserve PRD: ${(err as Error).message}`);\n }\n }\n}\n\n/**\n * Archive workspace .pan/ artifacts to ~/.overdeck/archives/<issue>/.\n * Rotates previous archives to prevent overwrite.\n * Returns a hard failure if archiving fails — callers must NOT proceed with\n * workspace deletion after an archive failure.\n */\nexport function archiveWorkspaceArtifacts(\n ctx: LifecycleContext,\n): Effect.Effect<StepResult> {\n return Effect.tryPromise({\n try: () => archiveWorkspaceArtifactsImpl(ctx),\n catch: (err) => err,\n }).pipe(\n Effect.catch((err) =>\n Effect.succeed(stepFailed('archive-planning:archive-artifacts', `Failed to archive: ${(err as Error).message}`)),\n ),\n );\n}\n\nasync function archiveWorkspaceArtifactsImpl(\n ctx: LifecycleContext,\n): Promise<StepResult> {\n const issueLower = ctx.issueId.toLowerCase();\n const step = 'archive-planning:archive-artifacts';\n\n const workspacePath = findWorkspacePath(ctx.projectPath, issueLower);\n if (!workspacePath || !existsSync(workspacePath)) {\n return stepSkipped(step, ['No workspace found to archive']);\n }\n\n let archiveDir = join(ARCHIVES_DIR, issueLower);\n\n // Rotate previous archive if it exists\n if (existsSync(archiveDir)) {\n let version = 1;\n while (existsSync(`${archiveDir}.${version}`)) {\n version++;\n }\n const rotatedDir = `${archiveDir}.${version}`;\n // Rename is O(1) metadata vs. copy+delete which is O(archive size). Both\n // paths live under ARCHIVES_DIR so they're on the same filesystem.\n await rename(archiveDir, rotatedDir);\n }\n\n await mkdir(archiveDir, { recursive: true });\n const details: string[] = [];\n\n const panDir = join(workspacePath, PAN_DIRNAME)\n\n const feedbackDir = join(panDir, PAN_FEEDBACK_DIRNAME)\n if (existsSync(feedbackDir)) {\n await cp(feedbackDir, join(archiveDir, 'feedback'), { recursive: true })\n details.push('Archived feedback/')\n }\n\n const continueFile = join(panDir, PAN_CONTINUE_FILENAME)\n if (existsSync(continueFile)) {\n await cp(continueFile, join(archiveDir, PAN_CONTINUE_FILENAME))\n details.push(`Archived ${PAN_CONTINUE_FILENAME}`)\n }\n\n const specFile = join(panDir, PAN_SPEC_FILENAME)\n if (existsSync(specFile)) {\n await cp(specFile, join(archiveDir, PAN_SPEC_FILENAME))\n details.push(`Archived ${PAN_SPEC_FILENAME}`)\n }\n\n const sessionsFile = join(panDir, PAN_SESSIONS_FILENAME)\n if (existsSync(sessionsFile)) {\n await cp(sessionsFile, join(archiveDir, PAN_SESSIONS_FILENAME))\n details.push(`Archived ${PAN_SESSIONS_FILENAME}`)\n }\n\n const contextFile = join(panDir, PAN_CONTEXT_FILENAME)\n if (existsSync(contextFile)) {\n await cp(contextFile, join(archiveDir, PAN_CONTEXT_FILENAME))\n details.push(`Archived ${PAN_CONTEXT_FILENAME}`)\n }\n\n const prdFile = join(panDir, 'prd.md')\n if (existsSync(prdFile)) {\n await cp(prdFile, join(archiveDir, 'prd.md'))\n details.push('Archived workspace prd.md')\n }\n\n details.push(`Archived to ${archiveDir}`);\n return stepOk(step, details);\n}\n\n/**\n * Full archive-planning operation: move PRD + archive workspace artifacts.\n * Archive failure is a hard fail — do not proceed with workspace deletion.\n */\nexport function archivePlanning(\n ctx: LifecycleContext,\n opts: ArchiveOptions = {},\n): Effect.Effect<StepResult[]> {\n return Effect.gen(function* () {\n const results: StepResult[] = [];\n const prdResult = yield* movePrd(ctx, opts);\n results.push(prdResult);\n const archiveResult = yield* archiveWorkspaceArtifacts(ctx);\n results.push(archiveResult);\n return results;\n });\n}\n","/**\n * Enumerate host PIDs whose current working directory is inside a given\n * directory tree, via /proc/<pid>/cwd.\n *\n * This is the safe selector for \"orphaned workspace processes\" (leftover vite\n * watchers, npm/vitest runners spawned in a workspace). It deliberately does\n * NOT use `lsof +D <dir>`: Bun installs node_modules as hardlinks from a\n * global cache, so long-lived platform processes (the dashboard server, every\n * conversation's PTY supervisor) mmap native addons (e.g. pty.node) whose\n * inode also lives inside every workspace's node_modules. `lsof +D` reports\n * them as holding files \"in\" the workspace, and killing that list takes down\n * the dashboard and all live conversations (2026-07-16 incident: `pan close\n * PAN-2532` killed the dashboard and an active operator conversation).\n */\nimport { readdir, readlink } from 'fs/promises';\n\nexport async function pidsWithCwdUnder(dir: string): Promise<string[]> {\n const prefix = dir.endsWith('/') ? dir : `${dir}/`;\n const pids: string[] = [];\n let entries: string[];\n try {\n entries = await readdir('/proc');\n } catch {\n return [];\n }\n for (const entry of entries) {\n if (!/^\\d+$/.test(entry)) continue;\n try {\n const cwd = await readlink(`/proc/${entry}/cwd`);\n if (cwd === dir || cwd.startsWith(prefix)) pids.push(entry);\n } catch {\n // Process exited, or belongs to another user — not killable by us anyway.\n }\n }\n return pids;\n}\n","/**\n * teardown-workspace — Full workspace cleanup.\n *\n * Consolidates workspace teardown from close-out.ts and workspace-manager.ts.\n * Handles: tmux sessions, TLDR daemon, Docker containers, git worktrees,\n * agent state directories, and (optionally) git branches.\n *\n * The workspace-manager's removeWorkspace() handles additional project-specific\n * cleanup (DNS, tunnels, Hume, ports) that this module does not cover.\n * In Phase 2, removeWorkspace() will delegate to this module for the common steps.\n */\n\nimport { existsSync } from 'fs';\nimport { readFile, rm } from 'fs/promises';\nimport { join, basename, dirname } from 'path';\nimport { homedir } from 'os';\nimport { exec } from 'child_process';\nimport { promisify } from 'util';\nimport { Effect } from 'effect';\nimport { AGENTS_DIR } from '../paths.js';\nimport { pidsWithCwdUnder } from '../process-cwd.js';\nimport { killSession, sessionExists, listSessionNames } from '../tmux.js';\nimport type { LifecycleContext, StepResult, TeardownOptions } from './types.js';\nimport { stepOk, stepSkipped, stepFailed } from './types.js';\nimport { findAllWorkspacePaths, findWorkspacePath } from './archive-planning.js';\nimport { getContainersReferencingWorkspacePath } from '../workspace-manager.js';\nimport { DEVCONTAINER_DIRNAME } from '../workspace/devcontainer-renderer.js';\nimport { removeAgentStateDir } from '../agents/state-dir-removal.js';\n\nconst execAsync = promisify(exec);\n\n/**\n * Kill tmux sessions associated with an issue.\n */\nfunction killTmuxSessions(issueLower: string): Effect.Effect<StepResult> {\n return Effect.tryPromise({\n try: () => killTmuxSessionsImpl(issueLower),\n catch: (err) => err,\n }).pipe(\n Effect.catch((err) =>\n Effect.succeed(stepFailed('teardown:tmux-sessions', `Failed: ${(err as Error).message}`)),\n ),\n );\n}\n\nasync function killTmuxSessionsImpl(issueLower: string): Promise<StepResult> {\n const step = 'teardown:tmux-sessions';\n let killed = 0;\n\n // Exact-match sessions (agent, test, merge, planning, strike).\n const exactPatterns = [\n `agent-${issueLower}`,\n `test-${issueLower}`,\n `merge-${issueLower}`,\n `planning-${issueLower}`,\n `strike-${issueLower}`,\n ];\n for (const session of exactPatterns) {\n if (await Effect.runPromise(sessionExists(session))) {\n try {\n await Effect.runPromise(killSession(session));\n killed++;\n } catch {\n // session may have died between check and kill\n }\n }\n }\n\n // Pattern-match sessions for review coordinators, review specialists, and\n // canonical specialists. Today's naming uses the UPPER issue ID (PAN-1024)\n // not the lower form, and includes session families that the prior\n // single-regex didn't cover. PAN-1024 close-out left\n // `review-coordinator-PAN-1024-...` and\n // `specialist-overdeck-PAN-1024-test-agent` alive (2026-05-09).\n //\n // Patterns we now match (case-insensitive on the issue ID):\n // - review-coordinator-<ISSUE>-<timestamp>\n // - review-<ISSUE>-<timestamp>-<role> (legacy)\n // - specialist-<projectKey>-<ISSUE>-<role> (canonical PAN-830/915)\n try {\n const allSessions = await Effect.runPromise(listSessionNames());\n const escapedLower = issueLower.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&');\n const escapedUpper = issueLower.toUpperCase().replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&');\n const issuePart = `(${escapedLower}|${escapedUpper})`;\n const patterns: RegExp[] = [\n new RegExp(`^review-coordinator-${issuePart}-\\\\d+`),\n new RegExp(`^review-${issuePart}-\\\\d+`),\n new RegExp(`^specialist-[^-]+(?:-[^-]+)*?-${issuePart}-`),\n ];\n const matchedSessions = allSessions.filter(s => patterns.some(p => p.test(s)));\n for (const session of matchedSessions) {\n try {\n await Effect.runPromise(killSession(session));\n killed++;\n } catch {\n // session may have died between check and kill\n }\n }\n } catch {\n // Session listing may fail if tmux server is not running\n }\n\n // NOTE: Per-project ephemeral specialists (specialist-{project}-{type}) are NOT killed here.\n // They belong to the project, not the issue, and accumulate context across issues via --resume.\n // Their grace period / idle timeout handles cleanup when no new work arrives.\n\n if (killed > 0) {\n return stepOk(step, [`Killed ${killed} tmux session(s)`]);\n }\n return stepSkipped(step, ['No tmux sessions found']);\n}\n\n/**\n * Stop TLDR daemon if workspace has a .venv.\n */\nfunction stopTldrDaemon(workspacePath: string): Effect.Effect<StepResult> {\n return Effect.tryPromise({\n try: () => stopTldrDaemonImpl(workspacePath),\n catch: (err) => err,\n }).pipe(\n Effect.catch(() =>\n Effect.succeed(stepSkipped('teardown:tldr-daemon', ['TLDR daemon not running or failed to stop (non-fatal)'])),\n ),\n );\n}\n\nasync function stopTldrDaemonImpl(workspacePath: string): Promise<StepResult> {\n const step = 'teardown:tldr-daemon';\n const venvPath = join(workspacePath, '.venv');\n if (!existsSync(venvPath)) {\n return stepSkipped(step, ['No .venv found']);\n }\n try {\n const { getTldrDaemonServiceSync } = await import('../tldr-daemon.js');\n const tldrService = getTldrDaemonServiceSync(workspacePath, venvPath);\n await tldrService.stop();\n return stepOk(step, ['Stopped TLDR daemon']);\n } catch {\n return stepSkipped(step, ['TLDR daemon not running or failed to stop (non-fatal)']);\n }\n}\n\n/**\n * Stop Docker containers for the workspace.\n */\nfunction stopDocker(\n workspacePath: string,\n issueLower: string,\n): Effect.Effect<StepResult> {\n return Effect.tryPromise({\n try: () => stopDockerImpl(workspacePath, issueLower),\n catch: (err) => err,\n }).pipe(\n Effect.catch(() =>\n Effect.succeed(stepSkipped('teardown:docker', ['Docker cleanup skipped (not running or failed)'])),\n ),\n );\n}\n\nasync function stopDockerImpl(\n workspacePath: string,\n issueLower: string,\n): Promise<StepResult> {\n const step = 'teardown:docker';\n try {\n const { stopWorkspaceDocker } = await import('../workspace-manager.js');\n await Effect.runPromise(stopWorkspaceDocker(workspacePath, issueLower));\n return stepOk(step, ['Stopped Docker containers']);\n } catch {\n return stepSkipped(step, ['Docker cleanup skipped (not running or failed)']);\n }\n}\n\n/**\n * Detect whether a PID belongs to a process running inside a Docker container.\n * Container processes appear in host lsof output when paths are bind-mounted.\n */\nasync function isDockerContainerProcess(pid: string): Promise<boolean> {\n try {\n const cgroup = await readFile(`/proc/${pid}/cgroup`, 'utf-8');\n return cgroup.includes('/docker-') || cgroup.includes('/docker/');\n } catch {\n return false;\n }\n}\n\n/**\n * Kill orphaned host processes for a workspace.\n */\nfunction killOrphanedProcesses(workspacePath: string): Effect.Effect<StepResult> {\n return Effect.tryPromise({\n try: () => killOrphanedProcessesImpl(workspacePath),\n catch: (err) => err,\n }).pipe(\n Effect.catch(() =>\n Effect.succeed(stepSkipped('teardown:orphaned-processes', ['Orphaned process cleanup failed (non-fatal)'])),\n ),\n );\n}\n\nasync function killOrphanedProcessesImpl(workspacePath: string): Promise<StepResult> {\n const step = 'teardown:orphaned-processes';\n try {\n // Find PIDs with cwd inside the workspace. NEVER select by open files\n // (`lsof +D`): Bun's hardlinked node_modules make the dashboard and every\n // conversation's PTY supervisor appear to hold files \"in\" any workspace\n // via mmap'd native addons — see src/lib/process-cwd.ts.\n const pids = await pidsWithCwdUnder(workspacePath);\n\n if (pids.length === 0) {\n return stepSkipped(step, ['No orphaned processes found']);\n }\n\n // Don't kill our own process or the dashboard\n const myPid = String(process.pid);\n const safePids = pids.filter(p => p !== myPid);\n\n if (safePids.length === 0) {\n return stepSkipped(step, ['No orphaned processes to kill']);\n }\n\n // Filter out Docker container processes — they appear in host lsof due to bind mounts\n const hostPids: string[] = [];\n for (const pid of safePids) {\n if (!(await isDockerContainerProcess(pid))) {\n hostPids.push(pid);\n }\n }\n\n if (hostPids.length === 0) {\n return stepSkipped(step, ['No orphaned host processes to kill (all were container processes)']);\n }\n\n await execAsync(`kill ${hostPids.join(' ')} 2>/dev/null || true`, { encoding: 'utf-8', timeout: 5000 });\n return stepOk(step, [`Killed ${hostPids.length} orphaned process(es)`]);\n } catch {\n return stepSkipped(step, ['Orphaned process cleanup failed (non-fatal)']);\n }\n}\n\n/**\n * Remove git worktree for the workspace.\n */\nfunction removeWorktree(\n projectPath: string,\n workspacePath: string,\n): Effect.Effect<StepResult> {\n return Effect.tryPromise({\n try: () => removeWorktreeImpl(projectPath, workspacePath),\n catch: (err) => err,\n }).pipe(\n Effect.catch((err) =>\n Effect.succeed(stepFailed('teardown:worktree', `Failed to remove workspace: ${(err as Error).message}`)),\n ),\n );\n}\n\nasync function removeWorktreeImpl(\n projectPath: string,\n workspacePath: string,\n): Promise<StepResult> {\n const step = 'teardown:worktree';\n if (!existsSync(workspacePath)) {\n return stepSkipped(step, ['Workspace directory does not exist']);\n }\n\n // Guard: never delete workspace (and its `.devcontainer/`) while containers\n // still reference compose paths inside it.\n const orphanedContainers = await Effect.runPromise(getContainersReferencingWorkspacePath(workspacePath));\n if (orphanedContainers.length > 0) {\n return stepFailed(\n step,\n `Cannot remove workspace: ${orphanedContainers.length} Docker container(s) still reference compose paths in ${DEVCONTAINER_DIRNAME}/. ` +\n `Run workspace Docker cleanup first or stop the containers manually.`,\n );\n }\n\n try {\n await execAsync(`git worktree remove \"${workspacePath}\" --force`, { cwd: projectPath });\n return stepOk(step, ['Removed git worktree']);\n } catch {\n // worktree remove failed — try direct removal\n try {\n await rm(workspacePath, { recursive: true, force: true });\n return stepOk(step, ['Removed workspace directory after worktree removal failed']);\n } catch (err) {\n return stepFailed(step, `Failed to remove workspace: ${(err as Error).message}`);\n }\n }\n}\n\n/**\n * Remove every agent state directory tied to an issue.\n */\nfunction removeAgentState(issueLower: string): Effect.Effect<StepResult> {\n return Effect.tryPromise({\n try: () => removeAgentStateImpl(issueLower),\n catch: (err) => err,\n }).pipe(\n Effect.catch((err) =>\n Effect.succeed(stepFailed('teardown:agent-state', `Failed: ${(err as Error).message}`)),\n ),\n );\n}\n\nasync function removeAgentStateImpl(issueLower: string): Promise<StepResult> {\n const step = 'teardown:agent-state';\n const { readdir } = await import('fs/promises');\n\n let entries: string[];\n try {\n entries = await readdir(AGENTS_DIR);\n } catch {\n return stepSkipped(step, ['Agents directory not present']);\n }\n\n const work = `agent-${issueLower}`;\n const planner = `planning-${issueLower}`;\n const strike = `strike-${issueLower}`;\n const specialistPrefix = `agent-${issueLower}-`;\n const targets = entries.filter(name =>\n name === work || name === planner || name === strike || name.startsWith(specialistPrefix),\n );\n\n let removed = 0;\n let preservedTranscripts = 0;\n for (const name of targets) {\n try {\n const result = await removeAgentStateDir(join(AGENTS_DIR, name));\n if (result.removedDir || result.preservedTranscripts > 0) removed++;\n preservedTranscripts += result.preservedTranscripts;\n } catch { /* non-fatal */ }\n }\n\n if (removed > 0) {\n return stepOk(step, [\n `Cleaned ${removed} agent state director${removed === 1 ? 'y' : 'ies'} (${preservedTranscripts} transcript files preserved)`,\n ]);\n }\n return stepSkipped(step, ['No agent state directories found']);\n}\n\n/**\n * Delete feature branches (local + remote).\n */\nfunction deleteBranches(\n projectPath: string,\n issueLower: string,\n): Effect.Effect<StepResult> {\n return Effect.tryPromise({\n try: () => deleteBranchesImpl(projectPath, issueLower),\n catch: (err) => err,\n }).pipe(\n Effect.catch((err) =>\n Effect.succeed(stepFailed('teardown:branches', `Failed: ${(err as Error).message}`)),\n ),\n );\n}\n\nasync function deleteBranchesImpl(\n projectPath: string,\n issueLower: string,\n): Promise<StepResult> {\n const step = 'teardown:branches';\n const branchName = `feature/${issueLower}`;\n const details: string[] = [];\n const localSlots = await execAsync(`git for-each-ref --format=\"%(refname:short)\" \"refs/heads/feature/${issueLower}-slot-*\"`, { cwd: projectPath, encoding: 'utf-8' })\n .then(({ stdout }) => stdout.trim().split('\\n').filter(Boolean)).catch(() => [] as string[]);\n const remoteSlots = await execAsync(`git ls-remote --heads origin \"feature/${issueLower}-slot-*\"`, { cwd: projectPath, encoding: 'utf-8' })\n .then(({ stdout }) => stdout.trim().split('\\n').map(line => line.split('refs/heads/')[1]).filter((name): name is string => Boolean(name))).catch(() => [] as string[]);\n\n // Delete local branch\n try {\n await execAsync(`git branch -D \"${branchName}\"`, { cwd: projectPath, encoding: 'utf-8' });\n details.push(`Deleted local branch ${branchName}`);\n } catch {\n details.push(`Local branch ${branchName} not found (already deleted)`);\n }\n\n for (const slotBranch of localSlots) {\n try {\n await execAsync(`git branch -D ${JSON.stringify(slotBranch)}`, { cwd: projectPath, encoding: 'utf-8' });\n details.push(`Deleted local branch ${slotBranch}`);\n } catch { details.push(`Failed to delete local branch ${slotBranch}`); }\n }\n\n // Delete remote branch\n try {\n await execAsync(`git push origin --delete \"${branchName}\"`, { cwd: projectPath, encoding: 'utf-8' });\n details.push(`Deleted remote branch ${branchName}`);\n } catch {\n details.push(`Remote branch ${branchName} not found (already deleted)`);\n }\n\n for (const slotBranch of remoteSlots) {\n try {\n await execAsync(`git push origin --delete ${JSON.stringify(slotBranch)}`, { cwd: projectPath, encoding: 'utf-8' });\n details.push(`Deleted remote branch ${slotBranch}`);\n } catch { details.push(`Failed to delete remote branch ${slotBranch}`); }\n }\n\n return stepOk(step, details);\n}\n\n/**\n * Whether a local branch exists and is fully merged into main.\n */\nasync function localBranchMergedState(\n projectPath: string,\n branchName: string,\n): Promise<{ exists: boolean; merged: boolean }> {\n try {\n await execAsync(`git show-ref --verify --quiet \"refs/heads/${branchName}\"`, { cwd: projectPath, encoding: 'utf-8' });\n } catch {\n return { exists: false, merged: false };\n }\n try {\n await execAsync(`git merge-base --is-ancestor \"${branchName}\" main`, { cwd: projectPath, encoding: 'utf-8' });\n return { exists: true, merged: true };\n } catch {\n return { exists: true, merged: false };\n }\n}\n\n/**\n * Delete the strike branch (local + remote) once it is merged into main.\n *\n * Strike work (`pan strike`) lands on main directly, so a merged\n * `strike/<issue>` branch is pure residue — it is deleted on every teardown,\n * not just when `deleteBranches` is requested (PAN-1721). An UNMERGED strike\n * branch may hold the only copy of an aborted strike's work, so it is\n * preserved and reported instead.\n */\nfunction deleteStrikeBranch(\n projectPath: string,\n issueLower: string,\n): Effect.Effect<StepResult> {\n return Effect.tryPromise({\n try: () => deleteStrikeBranchImpl(projectPath, issueLower),\n catch: (err) => err,\n }).pipe(\n Effect.catch((err) =>\n Effect.succeed(stepFailed('teardown:strike-branch', `Failed: ${(err as Error).message}`)),\n ),\n );\n}\n\nasync function deleteStrikeBranchImpl(\n projectPath: string,\n issueLower: string,\n): Promise<StepResult> {\n const step = 'teardown:strike-branch';\n const branchName = `strike/${issueLower}`;\n const details: string[] = [];\n\n // Local branch\n const local = await localBranchMergedState(projectPath, branchName);\n if (local.exists) {\n if (local.merged) {\n try {\n await execAsync(`git branch -D \"${branchName}\"`, { cwd: projectPath, encoding: 'utf-8' });\n details.push(`Deleted local branch ${branchName}`);\n } catch (err) {\n details.push(`Failed to delete local branch ${branchName}: ${(err as Error).message}`);\n }\n } else {\n details.push(`Preserved local branch ${branchName} — not merged to main`);\n }\n }\n\n // Remote branch\n try {\n const { stdout: remoteRef } = await execAsync(\n `git ls-remote --heads origin \"${branchName}\" 2>/dev/null || true`,\n { cwd: projectPath, encoding: 'utf-8' },\n );\n if (remoteRef.trim()) {\n let remoteMerged = false;\n try {\n await execAsync(`git fetch origin \"${branchName}\"`, { cwd: projectPath, encoding: 'utf-8' });\n await execAsync('git merge-base --is-ancestor FETCH_HEAD main', { cwd: projectPath, encoding: 'utf-8' });\n remoteMerged = true;\n } catch {\n remoteMerged = false;\n }\n if (remoteMerged) {\n await execAsync(`git push origin --delete \"${branchName}\"`, { cwd: projectPath, encoding: 'utf-8' });\n details.push(`Deleted remote branch ${branchName}`);\n } else {\n details.push(`Preserved remote branch ${branchName} — not merged to main`);\n }\n }\n } catch {\n // remote check/delete failed — non-fatal\n }\n\n if (details.length === 0) {\n return stepSkipped(step, [`No strike branch ${branchName} found`]);\n }\n return details.some(d => d.startsWith('Failed'))\n ? stepFailed(step, details.join('; '))\n : stepOk(step, details);\n}\n\n/**\n * Remove leftover strike worktrees (`workspaces/feature-<issue>-strike`).\n *\n * Runs when the regular worktree removal is gated off (remove_workspace=false\n * at close-out): a strike worktree has no post-close purpose once the strike\n * branch is merged or gone, and leaving it keeps the closed issue alive in\n * resource discovery (PAN-1721). Worktrees whose strike branch has unmerged\n * commits are preserved alongside the branch.\n */\nfunction removeStrikeWorktrees(\n projectPath: string,\n issueLower: string,\n): Effect.Effect<StepResult> {\n return Effect.tryPromise({\n try: () => removeStrikeWorktreesImpl(projectPath, issueLower),\n catch: (err) => err,\n }).pipe(\n Effect.catch((err) =>\n Effect.succeed(stepFailed('teardown:strike-worktree', `Failed: ${(err as Error).message}`)),\n ),\n );\n}\n\nasync function removeStrikeWorktreesImpl(\n projectPath: string,\n issueLower: string,\n): Promise<StepResult> {\n const step = 'teardown:strike-worktree';\n const strikePaths = findAllWorkspacePaths(projectPath, issueLower).filter(p => p.endsWith('-strike'));\n if (strikePaths.length === 0) {\n return stepSkipped(step, ['No strike worktree found']);\n }\n\n const branch = await localBranchMergedState(projectPath, `strike/${issueLower}`);\n if (branch.exists && !branch.merged) {\n return stepSkipped(step, [`Preserved strike worktree — strike/${issueLower} not merged to main`]);\n }\n\n const details: string[] = [];\n for (const p of strikePaths) {\n const result = await removeWorktreeImpl(projectPath, p);\n details.push(...(result.details ?? [result.error ?? result.step]));\n if (!result.success && !result.skipped) {\n return stepFailed(step, `Failed to remove strike worktree ${p}: ${result.error ?? 'unknown error'}`);\n }\n }\n return stepOk(step, details);\n}\n\n/**\n * Clear shadow state for an issue.\n */\nfunction clearShadowState(issueId: string): Effect.Effect<StepResult> {\n return Effect.tryPromise({\n try: () => clearShadowStateImpl(issueId),\n catch: (err) => err,\n }).pipe(\n Effect.catch(() =>\n Effect.succeed(stepSkipped('teardown:shadow-state', ['Shadow state cleanup skipped (non-fatal)'])),\n ),\n );\n}\n\nasync function clearShadowStateImpl(issueId: string): Promise<StepResult> {\n const step = 'teardown:shadow-state';\n try {\n const { removeShadowState } = await import('../shadow-state.js');\n const result = removeShadowState(issueId);\n if (result.success) {\n return stepOk(step, [`Cleared shadow state for ${issueId}`]);\n }\n return stepSkipped(step, ['No shadow state found']);\n } catch {\n return stepSkipped(step, ['Shadow state cleanup skipped (non-fatal)']);\n }\n}\n\n/**\n * Remove legacy .planning/<issue>/ directory from project root.\n */\nfunction clearLegacyPlanningDir(\n projectPath: string,\n issueLower: string,\n): Effect.Effect<StepResult> {\n return Effect.tryPromise({\n try: async () => {\n const step = 'teardown:legacy-planning-dir';\n const legacyDir = join(projectPath, '.planning', issueLower);\n if (!existsSync(legacyDir)) {\n return stepSkipped(step, ['No legacy planning directory found']);\n }\n await rm(legacyDir, { recursive: true, force: true });\n return stepOk(step, [`Deleted legacy planning dir: ${legacyDir}`]);\n },\n catch: (err) => err,\n }).pipe(\n Effect.catch((err) =>\n Effect.succeed(stepFailed('teardown:legacy-planning-dir', `Failed to delete legacy planning dir: ${(err as Error).message}`)),\n ),\n );\n}\n\n/**\n * Build template placeholders for project-specific cleanup (tunnel, Hume).\n */\nfunction buildPlaceholders(\n ctx: LifecycleContext,\n opts: TeardownOptions,\n workspacePath: string,\n) {\n const issueLower = ctx.issueId.toLowerCase();\n const featureFolder = `feature-${issueLower}`;\n const projName = opts.projectName || ctx.projectName || basename(ctx.projectPath);\n const domain = opts.workspaceConfig?.dns?.domain || 'localhost';\n return {\n FEATURE_NAME: issueLower,\n FEATURE_FOLDER: featureFolder,\n BRANCH_NAME: `feature/${issueLower}`,\n COMPOSE_PROJECT: `${projName}-${featureFolder}`,\n DOMAIN: domain,\n PROJECT_NAME: projName,\n PROJECT_PATH: ctx.projectPath,\n WORKSPACE_PATH: workspacePath,\n };\n}\n\n/**\n * Remove Cloudflare tunnel ingress for workspace.\n */\nfunction removeTunnelConfig(\n tunnelConfig: any,\n placeholders: Record<string, string>,\n): Effect.Effect<StepResult> {\n return Effect.tryPromise({\n try: async () => {\n const { removeTunnelIngress } = await import('../tunnel.js');\n const result = await Effect.runPromise(removeTunnelIngress(tunnelConfig, placeholders as any));\n return stepOk('teardown:tunnel', result.steps || ['Removed tunnel ingress']);\n },\n catch: (err) => err,\n }).pipe(\n Effect.catch((err) =>\n Effect.succeed(stepSkipped('teardown:tunnel', [`Tunnel cleanup warning: ${(err as Error).message}`])),\n ),\n );\n}\n\n/**\n * Remove Hume EVI config for workspace.\n */\nfunction removeHumeEviConfig(\n humeConfig: any,\n placeholders: Record<string, string>,\n): Effect.Effect<StepResult> {\n return Effect.tryPromise({\n try: async () => {\n const { deleteHumeConfig } = await import('../hume.js');\n const result = await Effect.runPromise(deleteHumeConfig(humeConfig, placeholders as any));\n return stepOk('teardown:hume', result.steps || ['Removed Hume EVI config']);\n },\n catch: (err) => err,\n }).pipe(\n Effect.catch((err) =>\n Effect.succeed(stepSkipped('teardown:hume', [`Hume cleanup warning: ${(err as Error).message}`])),\n ),\n );\n}\n\n/**\n * Full workspace teardown.\n */\nexport function teardownWorkspace(\n ctx: LifecycleContext,\n opts: TeardownOptions = {},\n): Effect.Effect<StepResult[]> {\n return Effect.gen(function* () {\n const issueLower = ctx.issueId.toLowerCase();\n const workspacePath = findWorkspacePath(ctx.projectPath, issueLower);\n const shouldDeleteWorkspace = opts.deleteWorkspace !== false; // default true\n const results: StepResult[] = [];\n\n // 1. Kill tmux sessions\n results.push(yield* killTmuxSessions(issueLower));\n\n // 2. Clear shadow state (always runs)\n results.push(yield* clearShadowState(ctx.issueId));\n\n // 3. Clear legacy planning directory (always runs)\n results.push(yield* clearLegacyPlanningDir(ctx.projectPath, issueLower));\n\n // 4-9: Workspace-specific cleanup\n if (workspacePath && existsSync(workspacePath)) {\n // 4. Stop TLDR daemon (only if deleting workspace)\n if (shouldDeleteWorkspace) {\n results.push(yield* stopTldrDaemon(workspacePath));\n }\n\n // 5. Stop Docker containers (only if deleting workspace)\n if (shouldDeleteWorkspace && !opts.skipDocker) {\n results.push(yield* stopDocker(workspacePath, issueLower));\n }\n\n // 5b. Kill orphaned host processes (Vite, node) that survive Docker teardown\n if (shouldDeleteWorkspace) {\n results.push(yield* killOrphanedProcesses(workspacePath));\n }\n\n // 7-8: Project-specific cleanup (tunnel, Hume)\n if (shouldDeleteWorkspace && (opts.workspaceConfig?.tunnel || opts.workspaceConfig?.hume)) {\n const placeholders = buildPlaceholders(ctx, opts, workspacePath);\n\n if (opts.workspaceConfig.tunnel) {\n results.push(yield* removeTunnelConfig(opts.workspaceConfig.tunnel, placeholders));\n }\n if (opts.workspaceConfig.hume) {\n results.push(yield* removeHumeEviConfig(opts.workspaceConfig.hume, placeholders));\n }\n }\n\n // 9. Remove worktree + workspace directory (only if deleting workspace).\n if (shouldDeleteWorkspace) {\n const allPaths = findAllWorkspacePaths(ctx.projectPath, issueLower);\n for (const p of allPaths) {\n results.push(yield* removeWorktree(ctx.projectPath, p));\n }\n }\n } else {\n results.push(stepSkipped('teardown:workspace', ['No workspace found to clean up']));\n }\n\n // 9b. Strike worktrees are residue once the strike branch is merged —\n // remove them even when deleteWorkspace is gated off (PAN-1721). When\n // shouldDeleteWorkspace is true the loop above already removed them via\n // findAllWorkspacePaths.\n if (!shouldDeleteWorkspace) {\n results.push(yield* removeStrikeWorktrees(ctx.projectPath, issueLower));\n }\n\n // 10. Remove agent state\n results.push(yield* removeAgentState(issueLower));\n\n // 11. Delete branches. Feature branches only if explicitly requested;\n // the strike branch is always a candidate once merged to main (PAN-1721).\n results.push(yield* deleteStrikeBranch(ctx.projectPath, issueLower));\n if (opts.deleteBranches) {\n results.push(yield* deleteBranches(ctx.projectPath, issueLower));\n }\n\n // 12. Prune checkpoint refs for this issue's agents.\n results.push(yield* pruneCheckpointRefs(ctx.projectPath, issueLower));\n\n // 13. Prune specialist registry entries for this issue.\n results.push(yield* pruneSpecialistRegistry(ctx.issueId));\n\n return results;\n });\n}\n\nfunction pruneSpecialistRegistry(issueId: string): Effect.Effect<StepResult> {\n return Effect.tryPromise({\n try: async () => {\n const step = 'teardown:specialist-registry';\n const { pruneSpecialistRegistryEntriesForIssue } = await import('../cloister/specialists.js');\n const removed = pruneSpecialistRegistryEntriesForIssue(issueId);\n return removed > 0\n ? stepOk(step, [`Pruned ${removed} specialist registry entr${removed === 1 ? 'y' : 'ies'} for ${issueId}`])\n : stepSkipped(step, [`No specialist registry entries for ${issueId}`]);\n },\n catch: (err) => err,\n }).pipe(\n Effect.catch((err) =>\n Effect.succeed(stepSkipped('teardown:specialist-registry', [`Specialist registry prune failed (non-fatal): ${(err as Error).message}`])),\n ),\n );\n}\n\nfunction pruneCheckpointRefs(projectPath: string, issueLower: string): Effect.Effect<StepResult> {\n return Effect.tryPromise({\n try: async () => {\n const step = 'teardown:checkpoint-refs';\n const { pruneCheckpointRefsForAgents } = await import('../checkpoint/checkpoint-manager.js');\n const agentIds = [`agent-${issueLower}`, `planning-${issueLower}`, `strike-${issueLower}`];\n const pruned = await Effect.runPromise(pruneCheckpointRefsForAgents(projectPath, agentIds));\n return stepOk(step, [`Pruned ${pruned} checkpoint ref(s) for ${agentIds.join(', ')}`]);\n },\n catch: (err) => err,\n }).pipe(\n Effect.catch((err) =>\n Effect.succeed(stepFailed('teardown:checkpoint-refs', `Checkpoint prune failed: ${(err as Error).message}`)),\n ),\n );\n}\n","import { join } from 'node:path';\n\nimport { Effect } from 'effect';\n\nimport {\n listAllAgentsSync,\n removeAgentRecordSync,\n RETAINED_TRANSCRIPTS_PHASE,\n tombstoneAgentRecordSync,\n} from '../overdeck/agents.js';\nimport {\n appendAgentPlaneLifecycle,\n flushAgentPlaneWrites,\n type AgentPlaneLifecycleEntry,\n type AgentPlaneTombstonePredicate,\n} from '../pan-dir/agents.js';\nimport { readIssueRecordForWorkspaceSync } from '../pan-dir/record.js';\nimport { getOverdeckHome } from '../paths.js';\nimport { emitActivityEntrySync } from '../activity-logger.js';\nimport { getAgentStateSync } from '../agents/agent-state.js';\nimport {\n hasRetainedTranscriptsMarker,\n listAgentStateFilesForRemoval,\n markRetainedTranscripts,\n removeAgentStateDir,\n type RemoveAgentStateDirResult,\n} from '../agents/state-dir-removal.js';\nimport { readLiveTrackerIssueState, type LiveTrackerIssueState } from './issue-closed.js';\nimport { sessionExists } from '../tmux.js';\nimport { getProjectSync, resolveProjectFromIssueSync } from '../projects.js';\nimport { resolveProjectReposForIssueSync } from '../project-repos.js';\nimport { listOpenPullRequestsSnapshot } from '../pipeline-membership-gather.js';\nimport { listOpenGitLabMergeRequests } from '../gitlab-merge-requests.js';\nimport { getReviewStatusSync } from '../review-status.js';\n\nexport interface AgentGcResult { removed: string[]; preserved: string[] }\nexport interface AgentGcRow {\n id: string;\n issueId: string;\n status: string;\n workspace?: string | null;\n phase?: string | null;\n role?: string | null;\n branch?: string | null;\n}\n\nexport interface AgentGcTerminalityDeps {\n hasClosedOutFlag: (agent: AgentGcRow) => boolean;\n readTrackerState: (issueId: string) => Promise<LiveTrackerIssueState>;\n hasLiveTmuxSession: (agentId: string) => Promise<boolean>;\n hasOpenChangeRequest: (agent: AgentGcRow) => Promise<boolean>;\n hasInFlightReviewOrTest: (agent: AgentGcRow) => boolean | Promise<boolean>;\n log: (message: string) => void;\n}\n\nexport type AgentGcTerminalityResult = boolean | AgentPlaneTombstonePredicate | null;\n\nexport interface AgentGcDeps {\n agentsDir: string;\n cleanStateDir: (dirPath: string, agentsDir: string) => Promise<RemoveAgentStateDirResult>;\n listFilesToRemove: (dirPath: string, agentsDir: string) => Promise<string[]>;\n hasRetainedMarker: (dirPath: string) => Promise<boolean>;\n markRetained: (dirPath: string) => Promise<void>;\n writeTombstone: (agent: AgentGcRow, entry: AgentPlaneLifecycleEntry) => Promise<void>;\n emitPruneEvent: (agent: AgentGcRow, entry: AgentPlaneLifecycleEntry) => void;\n removeRecord: (id: string) => void;\n tombstoneRecord: (id: string) => void;\n isTerminalAgent: (agent: AgentGcRow) => AgentGcTerminalityResult | Promise<AgentGcTerminalityResult>;\n log?: (message: string) => void;\n}\n\nfunction branchCandidates(agent: AgentGcRow, configuredBranch: string): Set<string> {\n return new Set([\n agent.branch,\n configuredBranch,\n `feature/${agent.issueId.toLowerCase()}`,\n `strike/${agent.issueId.toLowerCase()}`,\n ].filter((branch): branch is string => Boolean(branch)));\n}\n\nasync function hasOpenChangeRequest(agent: AgentGcRow): Promise<boolean> {\n const resolved = resolveProjectFromIssueSync(agent.issueId);\n if (!resolved) throw new Error(`No configured project resolves ${agent.issueId}`);\n const project = getProjectSync(resolved.projectKey);\n if (!project) throw new Error(`Project ${resolved.projectKey} is not configured`);\n const repos = resolveProjectReposForIssueSync(agent.issueId);\n if (!repos?.length) throw new Error(`No configured repositories resolve ${agent.issueId}`);\n\n const githubRepos = repos.filter((repo) => repo.forge === 'github');\n if (githubRepos.length > 0) {\n const [owner, repo] = project.github_repo?.split('/') ?? [];\n if (githubRepos.length !== 1 || !owner || !repo) {\n throw new Error(`Cannot safely resolve GitHub PR repository for ${agent.issueId}`);\n }\n const openPullRequests = await listOpenPullRequestsSnapshot(owner, repo);\n const candidates = branchCandidates(agent, githubRepos[0].sourceBranch);\n if (openPullRequests.some((pullRequest) => candidates.has(pullRequest.headRefName))) return true;\n }\n\n for (const repo of repos.filter((candidate) => candidate.forge === 'gitlab')) {\n const candidates = branchCandidates(agent, repo.sourceBranch);\n const openMergeRequests = await listOpenGitLabMergeRequests(repo.repoPath);\n if (openMergeRequests.some((mergeRequest) => candidates.has(mergeRequest.source_branch))) return true;\n }\n\n return false;\n}\n\nfunction hasInFlightReviewOrTest(agent: AgentGcRow): boolean {\n const status = getReviewStatusSync(agent.issueId);\n if (\n status?.reviewStatus === 'reviewing'\n || status?.testStatus === 'testing'\n || status?.verificationStatus === 'running'\n || (status?.reviewStatus === 'pending' && Boolean(status.reviewRequestedAt))\n ) return true;\n\n return listAllAgentsSync().some((candidate) =>\n candidate.issueId.toUpperCase() === agent.issueId.toUpperCase()\n && (candidate.role === 'review' || candidate.role === 'test')\n && ['starting', 'running', 'waiting', 'idle'].includes(candidate.status));\n}\n\nfunction defaultTerminalityDeps(): AgentGcTerminalityDeps {\n return {\n hasClosedOutFlag: (agent) => Boolean(agent.workspace\n && readIssueRecordForWorkspaceSync(agent.workspace, agent.issueId)?.pipeline?.closedOut === true),\n readTrackerState: readLiveTrackerIssueState,\n hasLiveTmuxSession: (agentId) => Effect.runPromise(sessionExists(agentId)),\n hasOpenChangeRequest,\n hasInFlightReviewOrTest,\n log: (message) => console.log(message),\n };\n}\n\n/**\n * Confirm every live fact required before periodic GC may remove agent state.\n * Any dependency failure is allowed to reject; the sweep catches it and\n * preserves the agent because uncertainty cannot authorize deletion.\n */\nexport async function resolveLiveAgentTerminalityEvidence(\n agent: AgentGcRow,\n deps: AgentGcTerminalityDeps = defaultTerminalityDeps(),\n): Promise<AgentPlaneTombstonePredicate | null> {\n if (agent.status !== 'stopped' || !agent.workspace) return null;\n const closedOutFlag = deps.hasClosedOutFlag(agent);\n if (!closedOutFlag) {\n deps.log(`[agent-gc] preserving ${agent.id}: issue record is not closed out`);\n return null;\n }\n\n const trackerState = await deps.readTrackerState(agent.issueId);\n if (trackerState !== 'closed') {\n deps.log(`[agent-gc] preserving ${agent.id}: live tracker state is ${trackerState}`);\n return null;\n }\n const liveTmux = await deps.hasLiveTmuxSession(agent.id);\n if (liveTmux) {\n deps.log(`[agent-gc] preserving ${agent.id}: tmux session is live`);\n return null;\n }\n const openChangeRequest = await deps.hasOpenChangeRequest(agent);\n if (openChangeRequest) {\n deps.log(`[agent-gc] preserving ${agent.id}: an open PR or MR still exists`);\n return null;\n }\n const inFlightReviewOrTest = await deps.hasInFlightReviewOrTest(agent);\n if (inFlightReviewOrTest) {\n deps.log(`[agent-gc] preserving ${agent.id}: review or test work is still in flight`);\n return null;\n }\n\n return {\n closedOutFlag,\n trackerState,\n liveTmux,\n openChangeRequest,\n inFlightReviewOrTest,\n };\n}\n\nexport async function confirmLiveAgentTerminality(\n agent: AgentGcRow,\n deps: AgentGcTerminalityDeps = defaultTerminalityDeps(),\n): Promise<boolean> {\n return (await resolveLiveAgentTerminalityEvidence(agent, deps)) !== null;\n}\n\nasync function persistAgentGcTombstone(\n agent: AgentGcRow,\n entry: AgentPlaneLifecycleEntry,\n): Promise<void> {\n const state = getAgentStateSync(agent.id);\n if (!state) {\n throw new Error(`local agent state is unavailable for durable tombstone ${agent.id}`);\n }\n const written = await appendAgentPlaneLifecycle(state, entry);\n if (!written) {\n throw new Error(`durable agent plane is unavailable for tombstone ${agent.id}`);\n }\n const flush = await flushAgentPlaneWrites(agent.issueId, agent.id);\n if (!flush || flush.errored || flush.pushed !== true) {\n throw new Error(\n `durable tombstone push failed for ${agent.id}: ${flush?.reason ?? 'no confirmed state-branch push'}`,\n );\n }\n}\n\nfunction emitAgentGcPruneEvent(\n agent: AgentGcRow,\n entry: AgentPlaneLifecycleEntry,\n): void {\n emitActivityEntrySync({\n source: 'cloister',\n level: 'info',\n status: 'completed',\n issueId: agent.issueId,\n message: `${agent.id} tombstoned before local agent-state cleanup`,\n details: JSON.stringify({\n predicate: entry.predicate,\n filesRemoved: entry.filesRemoved,\n }),\n });\n}\n\nfunction defaultAgentGcDeps(): AgentGcDeps {\n return {\n agentsDir: join(getOverdeckHome(), 'agents'),\n cleanStateDir: removeAgentStateDir,\n listFilesToRemove: listAgentStateFilesForRemoval,\n hasRetainedMarker: hasRetainedTranscriptsMarker,\n markRetained: markRetainedTranscripts,\n writeTombstone: persistAgentGcTombstone,\n emitPruneEvent: emitAgentGcPruneEvent,\n removeRecord: removeAgentRecordSync,\n tombstoneRecord: tombstoneAgentRecordSync,\n isTerminalAgent: (agent) => resolveLiveAgentTerminalityEvidence(agent),\n log: (message) => console.warn(message),\n };\n}\n\nfunction preverifiedCloseOutPredicate(): AgentPlaneTombstonePredicate {\n return {\n closedOutFlag: true,\n trackerState: 'closed-preverified-by-close-out',\n liveTmux: null,\n openChangeRequest: null,\n inFlightReviewOrTest: null,\n };\n}\n\nexport async function pruneAgentRowsAfterTranscriptCleanup(\n agents: readonly AgentGcRow[],\n deps: AgentGcDeps = defaultAgentGcDeps(),\n evidenceByAgent: ReadonlyMap<string, AgentPlaneTombstonePredicate> = new Map(),\n): Promise<AgentGcResult> {\n const removed: string[] = [];\n const preserved: string[] = [];\n for (const agent of agents) {\n try {\n const agentDir = join(deps.agentsDir, agent.id);\n if (await deps.hasRetainedMarker(agentDir)) {\n preserved.push(agent.id);\n continue;\n }\n const entry: AgentPlaneLifecycleEntry = {\n at: new Date().toISOString(),\n event: 'tombstoned',\n predicate: evidenceByAgent.get(agent.id) ?? preverifiedCloseOutPredicate(),\n filesRemoved: await deps.listFilesToRemove(agentDir, deps.agentsDir),\n };\n await deps.writeTombstone(agent, entry);\n deps.emitPruneEvent(agent, entry);\n const result = await deps.cleanStateDir(agentDir, deps.agentsDir);\n if (!result.removedDir) {\n await deps.markRetained(agentDir);\n deps.tombstoneRecord(agent.id);\n preserved.push(agent.id);\n continue;\n }\n deps.removeRecord(agent.id);\n removed.push(agent.id);\n } catch (error) {\n deps.log?.(\n `[agent-gc] preserving ${agent.id}: tombstone or cleanup failed: `\n + `${error instanceof Error ? error.message : String(error)}`,\n );\n preserved.push(agent.id);\n }\n }\n return { removed, preserved };\n}\n\n/**\n * Close-out-only pruning path. The lifecycle workflow has already positively\n * verified tracker terminality before it reaches this handoff, so it does not\n * repeat the periodic sweep's remote checks.\n */\nexport async function pruneStoppedAgentsForIssue(\n issueId: string,\n agents: AgentGcRow[] = listAllAgentsSync(),\n deps: AgentGcDeps = defaultAgentGcDeps(),\n): Promise<AgentGcResult> {\n const issue = issueId.toUpperCase();\n const scoped = agents.filter(agent => agent.issueId.toUpperCase() === issue);\n const terminal = scoped.filter(agent => agent.status === 'stopped');\n const live = scoped.filter(agent => agent.status !== 'stopped').map(agent => agent.id);\n const result = await pruneAgentRowsAfterTranscriptCleanup(terminal, deps);\n return { removed: result.removed, preserved: [...live, ...result.preserved] };\n}\n\nexport async function pruneTerminalStoppedAgents(\n agents: AgentGcRow[] = listAllAgentsSync(),\n deps: AgentGcDeps = defaultAgentGcDeps(),\n options: { dryRun?: boolean } = {},\n): Promise<AgentGcResult> {\n const candidates = agents.filter((agent) =>\n agent.phase !== RETAINED_TRANSCRIPTS_PHASE\n && agent.status === 'stopped'\n && Boolean(agent.workspace));\n const terminal: AgentGcRow[] = [];\n const evidenceByAgent = new Map<string, AgentPlaneTombstonePredicate>();\n const preserved: string[] = [];\n\n for (const agent of candidates) {\n try {\n const terminality = await deps.isTerminalAgent(agent);\n if (terminality) {\n terminal.push(agent);\n if (typeof terminality === 'object') {\n evidenceByAgent.set(agent.id, terminality);\n }\n } else preserved.push(agent.id);\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n deps.log?.(`[agent-gc] preserving ${agent.id}: live terminality check failed: ${message}`);\n preserved.push(agent.id);\n }\n }\n\n if (options.dryRun) {\n return { removed: terminal.map((agent) => agent.id), preserved };\n }\n\n const result = await pruneAgentRowsAfterTranscriptCleanup(\n terminal,\n deps,\n evidenceByAgent,\n );\n return { removed: result.removed, preserved: [...preserved, ...result.preserved] };\n}\n","import { execFile } from 'child_process';\nimport { promisify } from 'util';\nimport { mapGitHubStateToCanonical } from '../../core/state-mapping.js';\nimport { resolveGitHubIssueSync } from '../tracker-utils.js';\n\nconst execFileAsync = promisify(execFile);\n\n// Cache for auto-close-out canonical state queries to avoid N+1 shell execs on patrol\nconst autoCloseOutCache = new Map<string, { state: string | null; timestamp: number }>();\nconst AUTO_CLOSE_OUT_CACHE_TTL_MS = 10 * 60 * 1000; // 10 minutes\n\nexport function sweepAutoCloseOutCache(): void {\n const now = Date.now();\n for (const [issueId, entry] of autoCloseOutCache.entries()) {\n if (now - entry.timestamp > AUTO_CLOSE_OUT_CACHE_TTL_MS) {\n autoCloseOutCache.delete(issueId);\n }\n }\n}\n\nexport async function getAutoCloseOutCanonicalState(issueId: string): Promise<string | null> {\n const cached = autoCloseOutCache.get(issueId);\n if (cached && Date.now() - cached.timestamp < AUTO_CLOSE_OUT_CACHE_TTL_MS) {\n return cached.state;\n }\n\n const ghResolved = resolveGitHubIssueSync(issueId);\n if (!ghResolved.isGitHub) {\n autoCloseOutCache.set(issueId, { state: null, timestamp: Date.now() });\n return null;\n }\n\n try {\n const { stdout } = await execFileAsync('gh', [\n 'issue',\n 'view',\n String(ghResolved.number),\n '--repo',\n `${ghResolved.owner}/${ghResolved.repo}`,\n '--json',\n 'state,labels',\n ], { encoding: 'utf-8' });\n const parsed = JSON.parse(stdout) as { state?: string; labels?: Array<string | { name?: string }> };\n const labels = (parsed.labels ?? [])\n .map(label => typeof label === 'string' ? label : label.name)\n .filter((label): label is string => typeof label === 'string');\n const result = mapGitHubStateToCanonical(parsed.state ?? 'open', labels);\n autoCloseOutCache.set(issueId, { state: result, timestamp: Date.now() });\n return result;\n } catch {\n autoCloseOutCache.set(issueId, { state: null, timestamp: Date.now() });\n return null;\n }\n}\n","import type { ProjectConfig } from '../projects.js';\nimport { readIssueRecord, type PanIssueShipRecord } from '../pan-dir/record.js';\nimport type { UatGeneration } from '../overdeck/merge-sync.js';\n\nconst SHIP_RECORD_READ_CONCURRENCY = 4;\n\ninterface ShipStatusDeps {\n readRecord: typeof readIssueRecord;\n}\n\nconst defaultDeps: ShipStatusDeps = {\n readRecord: readIssueRecord,\n};\n\nasync function mapBounded<T>(\n items: readonly T[],\n concurrency: number,\n worker: (item: T) => Promise<void>,\n): Promise<void> {\n let next = 0;\n const workers = Array.from({ length: Math.min(concurrency, items.length) }, async () => {\n while (next < items.length) {\n const item = items[next++]!;\n await worker(item);\n }\n });\n await Promise.all(workers);\n}\n\n/** Load each unique issue record once with bounded async filesystem concurrency. */\nexport async function loadShipRecords(\n project: ProjectConfig,\n generations: readonly UatGeneration[],\n deps: ShipStatusDeps = defaultDeps,\n): Promise<Map<string, PanIssueShipRecord | null>> {\n const issueIds = [...new Set(generations.flatMap(generation =>\n generation.members.map(member => member.issueId.toUpperCase())))] ;\n const records = new Map<string, PanIssueShipRecord | null>();\n await mapBounded(issueIds, SHIP_RECORD_READ_CONCURRENCY, async issueId => {\n records.set(issueId, (await deps.readRecord(project, issueId))?.pipeline.ship ?? null);\n });\n return records;\n}\n\nfunction latest(records: readonly PanIssueShipRecord[]): PanIssueShipRecord {\n return [...records].sort((left, right) => right.at.localeCompare(left.at))[0]!;\n}\n\n/**\n * Fold every member's durable record into one conservative generation verdict.\n * A generation cannot report passed while any member is missing, pending,\n * partial, failed, or records a different version.\n */\nexport function aggregateGenerationShipStatus(\n generation: UatGeneration,\n recordsByIssue: ReadonlyMap<string, PanIssueShipRecord | null>,\n): PanIssueShipRecord | null {\n const records = generation.members.map(member => {\n const ship = recordsByIssue.get(member.issueId.toUpperCase());\n return ship?.batch === generation.name ? ship : null;\n });\n const matching = records.filter((record): record is PanIssueShipRecord => record !== null);\n if (matching.length === 0) {\n return generation.status === 'promoted'\n ? {\n status: 'pending',\n batch: generation.name,\n reason: 'no durable ship settlement was recorded for this promoted batch',\n at: generation.updatedAt,\n }\n : null;\n }\n if (matching.length !== records.length) {\n return {\n status: 'pending',\n batch: generation.name,\n reason: `${records.length - matching.length} member(s) have no durable ship settlement`,\n at: latest(matching).at,\n };\n }\n\n const failed = matching.filter(record => record.status === 'failed');\n if (failed.length > 0) return latest(failed);\n\n const partial = matching.filter(record => record.status === 'partial');\n if (partial.length > 0) {\n const newest = latest(partial);\n const paths = [...new Map(partial\n .flatMap(record => record.paths ?? [])\n .map(path => [path.path, path])).values()];\n return { ...newest, paths };\n }\n\n const pending = matching.filter(record => record.status === 'pending');\n if (pending.length > 0) return latest(pending);\n\n const versions = new Set(matching.map(record => record.version ?? ''));\n if (versions.size > 1) {\n return {\n status: 'partial',\n batch: generation.name,\n version: latest(matching).version,\n paths: [],\n reason: 'members record different shipped versions',\n at: latest(matching).at,\n };\n }\n return latest(matching);\n}\n\n/** Public aggregate omits operational error and reason strings. */\nexport function publicShipStatus(\n ship: PanIssueShipRecord | null,\n): Omit<PanIssueShipRecord, 'error' | 'reason'> | null {\n if (!ship) return null;\n const { error: _error, reason: _reason, ...safe } = ship;\n return safe;\n}\n","/** Shared Definition-of-Done row definitions and gate result types. */\n\nexport type DodRowId =\n | 'review'\n | 'tests'\n | 'verification'\n | 'merged'\n | 'post-merge'\n | 'main-verify'\n | 'ship'\n | 'deploy'\n | 'teardown';\n\nexport interface DodRowDef {\n id: DodRowId;\n num: 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9;\n title: string;\n expected: string;\n overridable: boolean;\n}\n\nexport const DOD_ROWS: readonly DodRowDef[] = [\n {\n id: 'review',\n num: 1,\n title: 'Review passed',\n expected: 'reviewStatus: passed, or tracker-closed with landed work and no negative review verdict',\n overridable: true,\n },\n {\n id: 'tests',\n num: 2,\n title: 'Tests passed',\n expected: 'testStatus: passed, or tracker-closed with landed work (negative verdict requires main verification pass)',\n overridable: true,\n },\n {\n id: 'verification',\n num: 3,\n title: 'Verification green on the branch',\n expected: 'verificationStatus: passed, or tracker-closed with landed work (negative verdict requires main verification pass)',\n overridable: true,\n },\n { id: 'merged', num: 4, title: 'Merged to main', expected: 'PR merged on the forge (feature or strike head), or branch work contained in main (non-PR landing)', overridable: true },\n {\n id: 'post-merge',\n num: 5,\n title: 'Post-merge lifecycle ran',\n expected: 'agents paused, workspace stack stopped, and verifying-on-main label applied',\n overridable: true,\n },\n {\n id: 'main-verify',\n num: 6,\n title: 'Verified on main',\n expected: 'merged commit verified on main',\n overridable: true,\n },\n {\n id: 'ship',\n num: 7,\n title: 'Version strings propagated',\n expected: 'every declared version_sync.expect path reports the batch version',\n overridable: true,\n },\n {\n id: 'deploy',\n num: 8,\n title: 'Deployed',\n expected: 'live server build includes the merged commit',\n overridable: true,\n },\n {\n id: 'teardown',\n num: 9,\n title: 'Close-out teardown verified',\n expected: 'workspace and configured branches removed, planning archived, issue closed, and Docker network removed',\n overridable: false,\n },\n];\n\nexport function acceptFlagFor(row: DodRowDef): string {\n return `--accept-${row.id}`;\n}\n\nexport function canAcceptDodMisses(actor: string): boolean {\n return !actor.startsWith('flywheel-');\n}\n\nexport const BRANCH_ABSENT_MERGE_ERROR = 'Feature branch is absent; positive merge evidence is required';\n\nexport type DodRowStatus = 'pass' | 'miss' | 'skip';\n\nexport interface DodRowResult {\n id: DodRowId;\n num: DodRowDef['num'];\n title: string;\n expected: string;\n observed: string;\n status: DodRowStatus;\n acceptedBy?: {\n flag: string;\n by: string;\n at: string;\n };\n}\n\nexport interface DodGateResult {\n rows: DodRowResult[];\n passed: boolean;\n misses: DodRowId[];\n accepted: DodRowId[];\n /** PAN-3211: set only for abandoned/residue dispositions, persisted beside the gate rows. */\n disposition?: { reason: string; by: string; kind?: 'abandon' | 'residue' };\n}\n\n/**\n * PAN-3211: the gate result for an abandoned disposition — every row skipped\n * with the operator's note, so the durable audit shows the gate was\n * deliberately not evaluated rather than silently green.\n */\nexport function buildAbandonedDodGate(reason: string, by: string): DodGateResult {\n return {\n rows: DOD_ROWS.map(row => ({\n ...row,\n status: 'skip' as const,\n observed: `gate not evaluated — abandoned disposition recorded by ${by}: ${reason}`,\n })),\n misses: [],\n accepted: [],\n passed: true,\n disposition: { reason, by, kind: 'abandon' },\n };\n}\n\n/**\n * PAN-3396: the gate result for a residue disposition — every row skipped\n * with verified evidence of tracker closure and stale PR cleanup, so the\n * durable audit shows the gate was deliberately not evaluated for stale\n * pre-record-era issues with lingering convention PRs/MRs.\n */\nexport function buildResidueDodGate(reason: string, by: string, evidence: string[]): DodGateResult {\n const evidenceStr = evidence.join('; ');\n return {\n rows: DOD_ROWS.map(row => ({\n ...row,\n status: 'skip' as const,\n observed: `gate not evaluated — residue disposition recorded by ${by}: ${reason}. Verified: ${evidenceStr}`,\n })),\n misses: [],\n accepted: [],\n passed: true,\n disposition: { reason, by, kind: 'residue' },\n };\n}\n","/** Mechanical Definition-of-Done row checks. */\n\nimport { execFile } from 'node:child_process';\nimport { userInfo } from 'node:os';\nimport { resolve } from 'node:path';\nimport { promisify } from 'node:util';\nimport type { CanonicalState } from '../../core/state-mapping.js';\nimport { Effect } from 'effect';\nimport { listRunningAgents, type AgentState } from '../agents.js';\nimport { getDashboardApiUrlSync } from '../config.js';\nimport { getReviewStatus, type ReviewStatus } from '../review-status.js';\nimport type { StrikeLandingStatus } from '../strike-landing.js';\nimport {\n getProjectConfigFromWorkspacePath,\n readIssueRecord,\n resolveProjectForIssue,\n type PanIssuePipelineRecord,\n type PanIssueShipRecord,\n} from '../pan-dir/record.js';\nimport type { ProjectConfig } from '../projects.js';\nimport { getAutoCloseOutCanonicalState } from '../cloister/deacon-canonical-state.js';\nimport { aggregateGenerationShipStatus, loadShipRecords } from '../cloister/ship-status.js';\nimport { isTrackerIssueClosed } from '../cloister/issue-closed.js';\nimport {\n fetchCommitCheckRuns,\n fetchIssuePullRequest,\n fetchRequiredStatusChecks,\n type CommitCheckRuns,\n} from '../overdeck/pull-requests.js';\nimport { listUatGenerationsSync, type UatGeneration } from '../overdeck/merge-sync.js';\nimport { getForgeAdapter } from '../forge.js';\nimport { resolveProjectReposForIssueSync } from '../project-repos.js';\nimport {\n gatherIssueBranchContainment,\n type IssueBranchContainment,\n} from '../pipeline-membership-gather.js';\nimport {\n acceptFlagFor,\n BRANCH_ABSENT_MERGE_ERROR,\n canAcceptDodMisses,\n DOD_ROWS,\n type DodGateResult,\n type DodRowId,\n type DodRowResult,\n} from './dod.js';\nimport type { LifecycleContext, StepResult } from './types.js';\n\nconst execFileAsync = promisify(execFile);\n\ntype StatusSource =\n | { source: 'live'; status: ReviewStatus }\n | { source: 'journal'; status: PanIssuePipelineRecord }\n | null;\ntype Awaitable<T> = T | Promise<T>;\n\nexport interface DodStatusRowDeps {\n getReviewStatus: (issueId: string) => Awaitable<ReviewStatus | null>;\n getJournalStatus: (issueId: string) => Awaitable<PanIssuePipelineRecord | null>;\n}\n\nexport interface MergedDodRowResult extends DodRowResult {\n mergedAt?: string;\n mergeCommit?: string;\n evidence?: 'branch-containment';\n}\n\ninterface MergedForgeArtifact {\n forge: string;\n url?: string;\n id?: string;\n}\n\ninterface MergedRowDeps {\n verifyMerged: (ctx: LifecycleContext) => Promise<StepResult>;\n readPullRequest: (ctx: LifecycleContext, branchName: string) => Promise<{\n number?: number;\n state?: string;\n mergedAt?: string;\n mergeCommit?: { oid?: string } | string | null;\n }>;\n readDurableMerges?: (ctx: LifecycleContext) => Promise<string[]>;\n readMergedForgeArtifacts?: (ctx: LifecycleContext) => Promise<MergedForgeArtifact[]>;\n readBranchContainment?: (ctx: LifecycleContext) => Promise<IssueBranchContainment>;\n}\n\nconst defaultMergedRowDeps: MergedRowDeps = {\n verifyMerged: async () => ({\n step: 'close-out:verify-merged',\n success: false,\n skipped: false,\n error: 'merge verifier was not supplied',\n }),\n readPullRequest: async (ctx, branchName) => {\n if (!ctx.github) return {};\n void branchName;\n const response = await fetchIssuePullRequest(ctx.issueId);\n if (response.error) throw new Error(response.error);\n return response.pr ?? {};\n },\n readDurableMerges: async ctx => {\n const project = resolveProjectForIssue(ctx.issueId) ?? getProjectConfigFromWorkspacePath(ctx.projectPath);\n return (await readIssueRecord(project, ctx.issueId))?.closeOut.merges ?? [];\n },\n readMergedForgeArtifacts: async ctx => {\n const repos = resolveProjectReposForIssueSync(ctx.issueId)?.filter(repo => repo.required) ?? [];\n const artifacts = await Promise.all(repos.map(repo => getForgeAdapter(repo.forge).findMergedArtifact({\n sourceBranch: repo.sourceBranch,\n targetBranch: repo.targetBranch,\n cwd: repo.repoPath,\n })));\n return artifacts.filter((artifact): artifact is NonNullable<typeof artifact> => artifact !== null);\n },\n readBranchContainment: async ctx => {\n const project = resolveProjectForIssue(ctx.issueId) ?? getProjectConfigFromWorkspacePath(ctx.projectPath);\n if (!project) return { unmergedRefs: [], mergedWorkRefs: [], pointerRefs: [] };\n return gatherIssueBranchContainment(project, ctx.issueId);\n },\n};\n\ninterface PostMergeRowDeps {\n readCanonicalState: (ctx: LifecycleContext) => Promise<CanonicalState | null>;\n readMergeStatus: (issueId: string) => Awaitable<string | undefined>;\n listAgents: () => Awaitable<Array<Pick<AgentState, 'id' | 'issueId' | 'role' | 'status'>>>;\n readStrikeLanded?: (issueId: string) => Awaitable<boolean>;\n}\n\nconst defaultPostMergeRowDeps: PostMergeRowDeps = {\n readStrikeLanded: async issueId => strikeLanded((await loadStatus(issueId, defaultDeps))?.status),\n readCanonicalState: async ctx => {\n return getAutoCloseOutCanonicalState(ctx.issueId) as Promise<CanonicalState | null>;\n },\n // PAN-3025: same live→journal fallback as the verdict rows — after close-out\n // clears live status, the durable record is the only place mergeStatus survives.\n readMergeStatus: async issueId => (await loadStatus(issueId, defaultDeps))?.status.mergeStatus,\n listAgents: async () => Effect.runPromise(listRunningAgents()),\n};\n\nexport const DEFAULT_MAIN_VERIFY_REQUIRED_CHECKS = ['test', 'lint', 'build (22)', 'guard'];\n\ninterface MainVerifyRowDeps {\n readCheckRuns: (ctx: LifecycleContext, commit: string) => Promise<CommitCheckRuns>;\n readRequiredChecks?: (ctx: LifecycleContext) => Promise<string[]>;\n /**\n * PAN-3202: default-branch commits that contain `mergeCommit`, newest first.\n * These are the candidate heads for the later-green-run evidence form.\n */\n readContainingDefaultBranchCommits?: (ctx: LifecycleContext, mergeCommit: string) => Promise<string[]>;\n}\n\nfunction configuredMainVerifyChecks(ctx: LifecycleContext): string[] {\n const project = resolveProjectForIssue(ctx.issueId) ?? getProjectConfigFromWorkspacePath(ctx.projectPath);\n return project?.main_verify_required_checks?.length\n ? project.main_verify_required_checks\n : DEFAULT_MAIN_VERIFY_REQUIRED_CHECKS;\n}\n\nconst defaultMainVerifyRowDeps: MainVerifyRowDeps = {\n readCheckRuns: async (ctx, commit) => {\n if (!ctx.github) return { total: 0, names: [], successful: [], failed: [], pending: [] };\n return fetchCommitCheckRuns(ctx.github.owner, ctx.github.repo, commit);\n },\n readRequiredChecks: async ctx => {\n if (!ctx.github) return configuredMainVerifyChecks(ctx);\n return fetchRequiredStatusChecks(ctx.github.owner, ctx.github.repo, 'main', configuredMainVerifyChecks(ctx));\n },\n readContainingDefaultBranchCommits: async (ctx, mergeCommit) => {\n const { stdout } = await execFileAsync(\n 'git',\n ['rev-list', '--ancestry-path', '--first-parent', `${mergeCommit}..origin/main`],\n { cwd: ctx.projectPath, encoding: 'utf-8', timeout: 10000, maxBuffer: 8 * 1024 * 1024 },\n );\n return stdout.split('\\n').map(line => line.trim()).filter(Boolean);\n },\n};\n\ninterface ShipRowDeps {\n readProject: (ctx: LifecycleContext) => ProjectConfig | null;\n readPipeline: (ctx: LifecycleContext) => Promise<PanIssuePipelineRecord | null>;\n findPromotedBatch: (ctx: LifecycleContext) => UatGeneration | null;\n readBatchShip: (project: ProjectConfig, generation: UatGeneration) => Promise<PanIssueShipRecord | null>;\n}\n\nconst defaultShipRowDeps: ShipRowDeps = {\n readProject: ctx => resolveProjectForIssue(ctx.issueId) ?? getProjectConfigFromWorkspacePath(ctx.projectPath),\n readPipeline: async ctx => {\n const project = resolveProjectForIssue(ctx.issueId) ?? getProjectConfigFromWorkspacePath(ctx.projectPath);\n return (await readIssueRecord(project, ctx.issueId))?.pipeline ?? null;\n },\n findPromotedBatch: ctx => {\n const project = resolveProjectForIssue(ctx.issueId) ?? getProjectConfigFromWorkspacePath(ctx.projectPath);\n return listUatGenerationsSync({ projectRoot: resolve(project.path), statuses: ['promoted'] })\n .find(generation => generation.members.some(member => member.issueId.toUpperCase() === ctx.issueId.toUpperCase())) ?? null;\n },\n readBatchShip: async (project, generation) => aggregateGenerationShipStatus(\n generation,\n await loadShipRecords(project, [generation]),\n ),\n};\n\ninterface DeployRowDeps {\n dashboardUrl: () => string;\n readJson: (url: string) => Promise<Record<string, unknown>>;\n commitContains: (repoRoot: string, mergeCommit: string, buildCommit: string) => Promise<boolean>;\n}\n\nexport interface TerminalVerdictSettlement {\n trackerClosed: boolean;\n landedWork: boolean;\n mainVerifyStatus: DodRowResult['status'];\n}\n\nexport interface EvaluateDodGateDeps {\n review: (issueId: string, settlement?: TerminalVerdictSettlement) => DodRowResult | Promise<DodRowResult>;\n tests: (issueId: string, settlement?: TerminalVerdictSettlement) => DodRowResult | Promise<DodRowResult>;\n verification: (issueId: string, settlement?: TerminalVerdictSettlement) => DodRowResult | Promise<DodRowResult>;\n merged: (ctx: LifecycleContext) => MergedDodRowResult | Promise<MergedDodRowResult>;\n postMerge: (ctx: LifecycleContext, merged?: MergedDodRowResult) => DodRowResult | Promise<DodRowResult>;\n mainVerify: (ctx: LifecycleContext, mergeCommit?: string) => DodRowResult | Promise<DodRowResult>;\n ship: (ctx: LifecycleContext) => DodRowResult | Promise<DodRowResult>;\n deploy: (ctx: LifecycleContext, merge: {\n mergedAt?: string;\n mergeCommit?: string;\n mergedRowStatus?: DodRowResult['status'];\n mainVerifyRowStatus?: DodRowResult['status'];\n }) => DodRowResult | Promise<DodRowResult>;\n trackerClosed?: (issueId: string) => Awaitable<boolean>;\n now: () => string;\n}\n\nconst defaultEvaluateDodGateDeps: EvaluateDodGateDeps = {\n review: (issueId, settlement) => checkReviewRow(issueId, defaultDeps, settlement),\n tests: (issueId, settlement) => checkTestsRow(issueId, defaultDeps, settlement),\n verification: (issueId, settlement) => checkVerificationRow(issueId, defaultDeps, settlement),\n merged: checkMergedRow,\n postMerge: checkPostMergeRow,\n mainVerify: checkMainVerifyRow,\n ship: checkShipRow,\n deploy: checkDeployRow,\n trackerClosed: isTrackerIssueClosed,\n now: () => new Date().toISOString(),\n};\n\nconst defaultDeployRowDeps: DeployRowDeps = {\n dashboardUrl: getDashboardApiUrlSync,\n readJson: async url => {\n const response = await fetch(url, { signal: AbortSignal.timeout(3000) });\n if (!response.ok) throw new Error(`${response.status} ${response.statusText}`);\n return response.json() as Promise<Record<string, unknown>>;\n },\n commitContains: async (repoRoot, mergeCommit, buildCommit) => {\n try {\n await execFileAsync('git', ['merge-base', '--is-ancestor', mergeCommit, buildCommit], {\n cwd: repoRoot,\n encoding: 'utf-8',\n timeout: 10000,\n });\n return true;\n } catch {\n return false;\n }\n },\n};\n\nconst defaultDeps: DodStatusRowDeps = {\n getReviewStatus: issueId => Effect.runPromise(getReviewStatus(issueId)),\n getJournalStatus: async issueId => {\n const project = resolveProjectForIssue(issueId) ?? getProjectConfigFromWorkspacePath(process.cwd());\n return (await readIssueRecord(project, issueId))?.pipeline ?? null;\n },\n};\n\nfunction rowDefinition(id: DodRowId) {\n const row = DOD_ROWS.find(candidate => candidate.id === id);\n if (!row) throw new Error(`Unknown DoD row: ${id}`);\n return row;\n}\n\nfunction result(id: DodRowId, status: DodRowResult['status'], observed: string): DodRowResult {\n const row = rowDefinition(id);\n return { id, num: row.num, title: row.title, expected: row.expected, observed, status };\n}\n\nasync function loadStatus(issueId: string, deps: DodStatusRowDeps): Promise<StatusSource> {\n try {\n const live = await deps.getReviewStatus(issueId);\n if (live) return { source: 'live', status: live };\n const journal = await deps.getJournalStatus(issueId);\n return journal ? { source: 'journal', status: journal } : null;\n } catch {\n return null;\n }\n}\n\n/**\n * PAN-3180: `landed` is the strike path's own durable statement that this issue's\n * work reached main through `strike/<id>`. It is written by the server merge door\n * (`mergeCompletionStatus` in `merge-strike.ts`) and mirrored into the per-issue\n * record's `pipeline` block, so it outlives review-status clearing. Every earlier\n * landing state (`ready`/`landing`/`recovering`/`needs_you`) is a strike still in\n * flight and earns no waiver.\n */\nfunction strikeLanded(status: StrikeLandingStatus | null | undefined): boolean {\n return status?.strikeLandingState === 'landed';\n}\n\n/**\n * PAN-3180: a strike dispatches neither a review nor a test specialist — that\n * bypass is the whole point of the path. So for a strike-landed issue these rows\n * are `skip` (deliberately not run), never `pass` (ran and succeeded) and never\n * `miss` (outstanding). A reader of the close-out record still sees the real\n * verdict and the reason it was never produced.\n */\nconst STRIKE_BYPASS_NOTE: Record<'review' | 'tests', string> = {\n review: 'no review specialist is dispatched for a strike',\n tests: 'no test specialist is dispatched for a strike',\n};\n\n/**\n * A specialist that ran and returned a negative verdict is a different fact from\n * one that was never dispatched, so the strike waiver never covers it — those\n * still block until an operator records an explicit `--accept-<row>` override.\n */\nconst NEGATIVE_VERDICTS = new Set(['failed', 'blocked', 'dispatch_failed']);\n\n/**\n * PAN-3187: a tracker-closed issue with landed work cannot produce a verdict it\n * never produced while active. Negative test/verification verdicts settle only\n * when main verification proves the landed state green; review negatives remain.\n */\nfunction terminalVerdictSettlement(\n id: 'review' | 'tests' | 'verification',\n value: string | undefined,\n observed: string,\n settlement?: TerminalVerdictSettlement,\n): DodRowResult | null {\n if (!settlement?.trackerClosed || !settlement.landedWork) return null;\n if (!NEGATIVE_VERDICTS.has(value ?? '')) {\n return result(\n id,\n 'skip',\n `${observed}; settled because the tracker issue is closed and merged work is landed`,\n );\n }\n if (id !== 'review' && settlement.mainVerifyStatus === 'pass') {\n return result(\n id,\n 'skip',\n `${observed}; superseded because the tracker issue is closed, merged work is landed, and main verification passed`,\n );\n }\n return null;\n}\n\nasync function checkVerdict(\n issueId: string,\n id: 'review' | 'tests',\n field: 'reviewStatus' | 'testStatus',\n deps: DodStatusRowDeps,\n settlement?: TerminalVerdictSettlement,\n): Promise<DodRowResult> {\n const loaded = await loadStatus(issueId, deps);\n if (!loaded) return result(id, 'miss', 'no review status or journal record found');\n\n const value = loaded.status[field];\n const source = loaded.source === 'journal' ? ' from pipeline journal' : '';\n const policy = value === 'skipped' ? ' (skipped per issue policy)' : '';\n const observed = `${field}: ${value ?? 'missing'}${source}${policy}`;\n if (value === 'passed' || value === 'skipped') return result(id, 'pass', observed);\n if (strikeLanded(loaded.status) && !NEGATIVE_VERDICTS.has(value ?? '')) {\n return result(\n id,\n 'skip',\n `${observed}; skipped by the strike path (strikeLandingState: landed) — ${STRIKE_BYPASS_NOTE[id]}`,\n );\n }\n return terminalVerdictSettlement(id, value, observed, settlement) ?? result(id, 'miss', observed);\n}\n\nexport function checkReviewRow(\n issueId: string,\n deps: DodStatusRowDeps = defaultDeps,\n settlement?: TerminalVerdictSettlement,\n): Promise<DodRowResult> {\n return checkVerdict(issueId, 'review', 'reviewStatus', deps, settlement);\n}\n\nexport function checkTestsRow(\n issueId: string,\n deps: DodStatusRowDeps = defaultDeps,\n settlement?: TerminalVerdictSettlement,\n): Promise<DodRowResult> {\n return checkVerdict(issueId, 'tests', 'testStatus', deps, settlement);\n}\n\nexport async function checkVerificationRow(\n issueId: string,\n deps: DodStatusRowDeps = defaultDeps,\n settlement?: TerminalVerdictSettlement,\n): Promise<DodRowResult> {\n const loaded = await loadStatus(issueId, deps);\n if (!loaded) return result('verification', 'miss', 'no review status or journal record found');\n\n // PAN-3067: the verdict alone decides this row, exactly like rows 1 and 2.\n // `lastVerifiedCommit` is a best-effort optimization anchor — verification-runner.ts\n // snapshots it inside a try/catch and spreads the field conditionally, and a\n // `skipped` verdict never has one at all — so its absence says nothing about\n // whether verification ran. Requiring it made every issue whose anchor was\n // never written un-closable. The observed string always names the anchor's\n // presence or absence so a reader can never mistake it for a hidden condition.\n const value = loaded.status.verificationStatus;\n const commit = loaded.status.lastVerifiedCommit;\n const accepted = value === 'passed' || value === 'skipped';\n const notes: string[] = [];\n if (loaded.source === 'journal') notes.push('from pipeline journal');\n if (value === 'skipped') notes.push('skipped per issue policy');\n if (accepted && !commit) notes.push('no lastVerifiedCommit recorded');\n const observed = `verificationStatus: ${value ?? 'missing'}${commit ? ` at ${commit}` : ''}${\n notes.length > 0 ? ` (${notes.join('; ')})` : ''\n }`;\n if (accepted) return result('verification', 'pass', observed);\n // An out-of-band merge never enters merge-ops, so the CI-green skip cannot\n // record its normal verification verdict. Once rows 4 and 6 prove the landed\n // work and main CI green, that evidence satisfies row 3 without an override.\n if (value === undefined && settlement?.landedWork && settlement.mainVerifyStatus === 'pass') {\n return result('verification', 'pass', `${observed}; verification satisfied by green main CI after landing`);\n }\n return terminalVerdictSettlement('verification', value, observed, settlement) ??\n result('verification', 'miss', observed);\n}\n\nexport async function checkMergedRow(\n ctx: LifecycleContext,\n deps: MergedRowDeps = defaultMergedRowDeps,\n): Promise<MergedDodRowResult> {\n const verified: StepResult = await deps.verifyMerged(ctx).catch(error => ({\n step: 'close-out:verify-merged',\n success: false,\n skipped: false,\n error: error instanceof Error ? error.message : String(error),\n }));\n const detail = verified.details?.join('; ');\n const observed = detail || verified.error || (verified.skipped ? 'issue already closed on forge' : 'merge not verified');\n const merged = result('merged', verified.success || verified.skipped ? 'pass' : 'miss', observed) as MergedDodRowResult;\n const branchAbsent = verified.error === BRANCH_ABSENT_MERGE_ERROR;\n let durableMerges: string[] = [];\n let forgeArtifacts: MergedForgeArtifact[] = [];\n\n if (branchAbsent) {\n try {\n durableMerges = await (deps.readDurableMerges ?? defaultMergedRowDeps.readDurableMerges!)(ctx);\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n merged.observed = `${merged.observed}; durable merge evidence unavailable: ${message}`;\n }\n if (!ctx.github && deps.readMergedForgeArtifacts) {\n try {\n forgeArtifacts = await deps.readMergedForgeArtifacts(ctx);\n const labels = forgeArtifacts.map(artifact => {\n const label = artifact.forge === 'gitlab'\n ? (artifact.id ? `MR !${artifact.id}` : 'GitLab MR')\n : (artifact.id ? `PR #${artifact.id}` : 'GitHub PR');\n return `${label} merged${artifact.url ? ` (${artifact.url})` : ''}`;\n });\n if (labels.length > 0) merged.observed = `${merged.observed}; ${labels.join('; ')}`;\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n merged.observed = `${merged.observed}; forge artifact evidence unavailable: ${message}`;\n }\n }\n }\n\n let pullRequestState: string | undefined;\n if (ctx.github) {\n try {\n const branchName = `feature/${ctx.issueId.toLowerCase()}`;\n const pullRequest = await deps.readPullRequest(ctx, branchName);\n pullRequestState = pullRequest.state;\n merged.mergedAt = pullRequest.mergedAt;\n merged.mergeCommit = typeof pullRequest.mergeCommit === 'string'\n ? pullRequest.mergeCommit\n : pullRequest.mergeCommit?.oid;\n const number = pullRequest.number ? `PR #${pullRequest.number}` : 'PR';\n const state = pullRequest.state ?? 'state unknown';\n const at = pullRequest.mergedAt ? ` at ${pullRequest.mergedAt}` : '';\n merged.observed = `${merged.observed}; ${number} ${state}${at}`;\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n merged.observed = `${merged.observed}; forge metadata unavailable: ${message}`;\n }\n }\n\n if (branchAbsent) {\n const forgeMerged = pullRequestState?.toUpperCase() === 'MERGED';\n if (durableMerges.length > 0 || forgeMerged || forgeArtifacts.length > 0) {\n merged.status = 'pass';\n if (durableMerges.length > 0) {\n merged.observed = `${merged.observed}; durable close-out record contains ${durableMerges.length} merge artifact(s)`;\n }\n } else {\n merged.observed = `${merged.observed}; no merged forge artifact or durable close-out merge record found`;\n }\n }\n\n if (merged.status === 'miss') {\n try {\n const containment = await (deps.readBranchContainment ?? defaultMergedRowDeps.readBranchContainment!)(ctx);\n if (containment.mergedWorkRefs.length > 0 && containment.unmergedRefs.length === 0) {\n merged.status = 'pass';\n merged.evidence = 'branch-containment';\n merged.observed = `${merged.observed}; branch work contained in default branch with no merged PR — non-PR landing (membership L2-work lens): ${containment.mergedWorkRefs.join(', ')}`;\n }\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n merged.observed = `${merged.observed}; branch containment evidence unavailable: ${message}`;\n }\n }\n\n return merged;\n}\n\nexport async function checkPostMergeRow(\n ctx: LifecycleContext,\n merged?: MergedDodRowResult,\n deps: PostMergeRowDeps = defaultPostMergeRowDeps,\n): Promise<DodRowResult> {\n try {\n const canonicalState = await deps.readCanonicalState(ctx);\n const mergeStatus = await deps.readMergeStatus(ctx.issueId);\n const issueId = ctx.issueId.toUpperCase();\n const runningAgents = (await deps.listAgents()).filter(agent =>\n agent.issueId.toUpperCase() === issueId &&\n (agent.role === 'work' || agent.role === 'plan') &&\n (agent.status === 'starting' || agent.status === 'running'),\n );\n const stateObserved = canonicalState\n ? `canonical state: ${canonicalState}`\n : `canonical state unavailable; mergeStatus: ${mergeStatus ?? 'missing'}`;\n const agentsObserved = runningAgents.length > 0\n ? `running agents: ${runningAgents.map(agent => agent.id).join(', ')}`\n : 'no running work/planning agents';\n // PAN-3188 (row 5): terminal canonical states settle this row. 'done'\n // proves the post-merge lifecycle already ran to close-out; 'canceled'\n // makes it moot. Requiring the transient verifying_on_main marker here\n // wedges every re-evaluated terminal issue (the 11 closed issues that\n // blocked close-out sweeps on 'canonical state: done').\n if (canonicalState === 'done') {\n return result(\n 'post-merge',\n runningAgents.length === 0 ? 'pass' : 'miss',\n `terminal canonical state: done — the post-merge lifecycle already ran to close-out; ${agentsObserved}`,\n );\n }\n if (canonicalState === 'canceled') {\n return result(\n 'post-merge',\n runningAgents.length === 0 ? 'skip' : 'miss',\n `terminal canonical state: canceled — post-merge lifecycle not applicable; ${agentsObserved}`,\n );\n }\n const lifecycleObserved = canonicalState === 'verifying_on_main' || mergeStatus === 'merged';\n // PAN-3180: `postMergeLifecycle()` is the work-agent handoff — it pauses the\n // work/planning agents, stops the workspace stack, and applies\n // `verifying-on-main`. A strike has no work agent to pause and its landing is\n // owned by the Deacon's merge door, so the marker this row looks for is never\n // written and its absence proves nothing. What a strike does still owe is\n // quiescence, so a live work/planning agent remains a real miss.\n if (!lifecycleObserved && await (deps.readStrikeLanded ?? defaultPostMergeRowDeps.readStrikeLanded!)(ctx.issueId)) {\n return result(\n 'post-merge',\n runningAgents.length === 0 ? 'skip' : 'miss',\n `strike landing (strikeLandingState: landed) — the work-agent post-merge handoff is not the strike path's lifecycle; ${stateObserved}; ${agentsObserved}`,\n );\n }\n if (!lifecycleObserved && merged?.evidence === 'branch-containment') {\n return result(\n 'post-merge',\n runningAgents.length === 0 ? 'pass' : 'miss',\n `non-PR landing (branch-containment evidence) — post-merge lifecycle not applicable; ${agentsObserved}`,\n );\n }\n return result(\n 'post-merge',\n lifecycleObserved && runningAgents.length === 0 ? 'pass' : 'miss',\n `${stateObserved}; ${agentsObserved}`,\n );\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n return result('post-merge', 'miss', `post-merge evidence unavailable: ${message}`);\n }\n}\n\n/**\n * PAN-3202: how many default-branch heads above the merge commit are probed for\n * the later-green-run evidence form. The tip of main is often mid-CI, so a single\n * candidate would usually read as pending; five reaches back far enough to find\n * the newest concluded green head without turning one row into a check-run storm.\n */\nconst LATER_GREEN_CANDIDATE_LIMIT = 5;\n\n/**\n * PAN-3202: the fallback evidence form for DoD row 6. When a merge lands inside a\n * red-main window, the merge commit's own check-runs can never turn green, so the\n * row was permanently unsatisfiable even after main went green hundreds of times\n * with the commit included. A later default-branch head whose check-runs all\n * concluded green, and which contains the merge commit, is strictly stronger\n * evidence than the original run: it proves main is healthy *with* the merge in it,\n * which is exactly what \"verified on main\" means. Row 7 (deploy) already reasons\n * this way through the same containment relation.\n */\nfunction requiredChecksOutcome(checks: CommitCheckRuns, required: string[]): {\n missing: string[];\n unsuccessful: string[];\n} {\n return {\n missing: required.filter(check => !checks.names.includes(check)),\n unsuccessful: required.filter(check => checks.names.includes(check) && !checks.successful.includes(check)),\n };\n}\n\nfunction hasRequiredChecksGreen(checks: CommitCheckRuns, required: string[]): boolean {\n const outcome = requiredChecksOutcome(checks, required);\n return outcome.missing.length === 0 && outcome.unsuccessful.length === 0;\n}\n\nasync function findLaterGreenDefaultBranchRun(\n ctx: LifecycleContext,\n mergeCommit: string,\n required: string[],\n deps: MainVerifyRowDeps,\n): Promise<{ run: { sha: string; total: number } | null; note: string }> {\n const readCandidates =\n deps.readContainingDefaultBranchCommits ?? defaultMainVerifyRowDeps.readContainingDefaultBranchCommits!;\n let candidates: string[];\n try {\n candidates = await readCandidates(ctx, mergeCommit);\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n return { run: null, note: `later-run evidence unavailable: ${message}` };\n }\n\n const probed = candidates.slice(0, LATER_GREEN_CANDIDATE_LIMIT);\n for (const sha of probed) {\n try {\n const checks = await deps.readCheckRuns(ctx, sha);\n if (hasRequiredChecksGreen(checks, required)) {\n return { run: { sha, total: checks.total }, note: '' };\n }\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n return { run: null, note: `later-run evidence unavailable: ${message}` };\n }\n }\n return {\n run: null,\n note: probed.length === 0\n ? 'no later default-branch commit contains the merge'\n : `no green CI run among the ${probed.length} newest default-branch commit(s) containing the merge`,\n };\n}\n\nexport async function checkMainVerifyRow(\n ctx: LifecycleContext,\n mergeCommit?: string,\n deps: MainVerifyRowDeps = defaultMainVerifyRowDeps,\n): Promise<DodRowResult> {\n if (!ctx.github || !mergeCommit) {\n return result(\n 'main-verify',\n 'skip',\n 'no merge commit resolvable — verified-on-main has no durable marker (see DoD row 6)',\n );\n }\n\n try {\n const requiredChecks = await (deps.readRequiredChecks ?? defaultMainVerifyRowDeps.readRequiredChecks!)(ctx);\n const checks = await deps.readCheckRuns(ctx, mergeCommit);\n const outcome = requiredChecksOutcome(checks, requiredChecks);\n if (hasRequiredChecksGreen(checks, requiredChecks)) {\n return result('main-verify', 'pass', `required checks concluded successfully on ${mergeCommit}: ${requiredChecks.join(', ')}`);\n }\n\n // The merge commit's own run stays the primary evidence, so its outcome is\n // always recorded first; the later-green-run form only appends to it.\n const parts = [\n ...(outcome.missing.length > 0 ? [`missing required checks on ${mergeCommit}: ${outcome.missing.join(', ')}`] : []),\n ...(outcome.unsuccessful.length > 0 ? [`required checks not successful: ${outcome.unsuccessful.join(', ')}`] : []),\n ];\n const primary = parts.join('; ');\n\n const later = await findLaterGreenDefaultBranchRun(ctx, mergeCommit, requiredChecks, deps);\n if (later.run) {\n return result(\n 'main-verify',\n 'pass',\n `${primary}; verified on main by later green CI run ${later.run.sha} containing the merge (required checks concluded successfully: ${requiredChecks.join(', ')})`,\n );\n }\n return result('main-verify', 'miss', `${primary}; ${later.note}`);\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n return result('main-verify', 'miss', `could not read merge-commit check-runs: ${message}`);\n }\n}\n\nexport async function checkShipRow(\n ctx: LifecycleContext,\n deps: ShipRowDeps = defaultShipRowDeps,\n): Promise<DodRowResult> {\n const project = deps.readProject(ctx);\n if (!project?.version_sync) {\n return result('ship', 'skip', 'project declares no version_sync; ship step not applicable');\n }\n\n const promotedBatch = deps.findPromotedBatch(ctx);\n const ship = promotedBatch\n ? await deps.readBatchShip(project, promotedBatch)\n : (await deps.readPipeline(ctx))?.ship;\n if (!ship) {\n if (promotedBatch) {\n return result(\n 'ship',\n 'miss',\n `batch ${promotedBatch.name} includes this issue but no durable ship settlement was recorded`,\n );\n }\n return result('ship', 'skip', 'merged outside a batch; ship is batch-scoped');\n }\n if (ship.status === 'passed') {\n return result(\n 'ship',\n 'pass',\n `version ${ship.version ?? 'unknown'} shipped for batch ${ship.batch}; ${ship.paths?.length ?? 0} path(s) verified`,\n );\n }\n if (ship.status === 'pending') {\n return result(\n 'ship',\n 'miss',\n `batch ${ship.batch} merged but no version was shipped — use the Ship version action on the batch card for ${ship.batch}`,\n );\n }\n if (ship.status === 'partial') {\n const failing = ship.paths?.filter(path => !path.ok).map(path => path.path) ?? [];\n return result(\n 'ship',\n 'miss',\n `batch ${ship.batch} partially propagated version ${ship.version ?? 'unknown'}; failing paths: ${failing.join(', ') || 'unknown'}`,\n );\n }\n return result(\n 'ship',\n 'miss',\n `batch ${ship.batch} version ship failed (${ship.errorCode ?? 'unknown-failure'}): ${ship.error ?? ship.reason ?? 'inspect the local dashboard log'}`,\n );\n}\n\nexport async function checkDeployRow(\n ctx: LifecycleContext,\n merge: {\n mergedAt?: string;\n mergeCommit?: string;\n mergedRowStatus?: DodRowResult['status'];\n mainVerifyRowStatus?: DodRowResult['status'];\n },\n deps: DeployRowDeps = defaultDeployRowDeps,\n): Promise<DodRowResult> {\n if (!merge.mergeCommit) {\n if (merge.mergedRowStatus === 'miss') {\n return result(\n 'deploy',\n 'skip',\n 'no merge commit resolved because the merged row missed — deploy ancestry depends on row 4; build ancestry unchecked',\n );\n }\n // PAN-3188: row 6 (main-verify) skips whenever no merge commit is\n // resolvable — the no-durable-anchor landing class (e.g. GitLab-backed\n // landings whose row 4 evidence is the merge specialist's confirmation).\n // Row 7 must agree: same missing anchor, same skip. Only when main-verify\n // did NOT skip is the absent commit an integrity problem worth a miss.\n if (merge.mainVerifyRowStatus === 'skip') {\n return result(\n 'deploy',\n 'skip',\n 'verified-on-main skipped — no merge commit resolvable, so deploy build-ancestry has no durable anchor either (same landing class as DoD row 6 skip)',\n );\n }\n return result('deploy', 'miss', 'merged row passed without a resolvable merge commit; build ancestry cannot be checked');\n }\n\n const baseUrl = deps.dashboardUrl().replace(/\\/$/, '');\n let health: Record<string, unknown>;\n try {\n health = await deps.readJson(`${baseUrl}/api/health`);\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n return result('deploy', 'miss', `dashboard not reachable at ${baseUrl} — a merged fix is not live if no server is serving it: ${message}`);\n }\n\n const repoRoot = typeof health.repoRoot === 'string' ? health.repoRoot : '';\n if (!repoRoot) return result('deploy', 'miss', `dashboard at ${baseUrl} did not report repoRoot`);\n if (resolve(repoRoot) !== resolve(ctx.projectPath)) {\n return result('deploy', 'skip', `live dashboard serves ${repoRoot}, not this project — deploy semantics undefined for ${ctx.projectPath}`);\n }\n\n const buildCommit = typeof health.buildCommit === 'string' && health.buildCommit.trim()\n ? health.buildCommit\n : undefined;\n if (!buildCommit) {\n return result('deploy', 'miss', `dashboard at ${baseUrl} did not report buildCommit; live deployment cannot be proven`);\n }\n try {\n if (health.buildDirty === true) {\n return result(\n 'deploy',\n 'miss',\n `live build ${buildCommit.slice(0, 8)} was built from a dirty working tree — uncommitted changes may be serving; redeploy canonically with \\`pan reload\\``,\n );\n }\n\n const canonical = await deps.commitContains(repoRoot, buildCommit, 'origin/main');\n if (!canonical) {\n return result(\n 'deploy',\n 'miss',\n `live build commit ${buildCommit.slice(0, 8)} is not an ancestor of origin/main — the server is running a build of local-only commits; redeploy with \\`pan reload\\``,\n );\n }\n\n const contains = await deps.commitContains(repoRoot, merge.mergeCommit, buildCommit);\n const observed = `build commit ${buildCommit.slice(0, 8)} ${contains ? 'contains' : 'does not contain'} merge ${merge.mergeCommit.slice(0, 8)}`;\n return result('deploy', contains ? 'pass' : 'miss', observed);\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n return result('deploy', 'miss', `deploy evidence unavailable: ${message}`);\n }\n}\n\nexport async function evaluateDodGate(\n ctx: LifecycleContext,\n opts: {\n acceptedRows?: DodRowId[];\n acceptedBy?: string;\n verifyMerged?: MergedRowDeps['verifyMerged'];\n } = {},\n deps: EvaluateDodGateDeps = defaultEvaluateDodGateDeps,\n): Promise<DodGateResult> {\n const acceptedRows = new Set(opts.acceptedRows ?? []);\n const overridable = new Set(DOD_ROWS.filter(row => row.overridable).map(row => row.id));\n for (const id of acceptedRows) {\n if (!overridable.has(id)) throw new TypeError(`DoD row \"${id}\" cannot be accepted; valid rows: ${[...overridable].join(', ')}`);\n }\n const by = opts.acceptedBy ?? process.env.OVERDECK_AGENT_ID ?? userInfo().username;\n if (acceptedRows.size > 0 && !canAcceptDodMisses(by)) {\n throw new TypeError('The flywheel orchestrator cannot accept missed Definition-of-Done rows; an operator must apply --accept-<row> overrides.');\n }\n\n const merged = opts.verifyMerged\n ? await checkMergedRow(ctx, { ...defaultMergedRowDeps, verifyMerged: opts.verifyMerged })\n : await deps.merged(ctx);\n // Main-verify computes before deploy because deploy's no-merge-commit\n // branch keys on main-verify's outcome (PAN-3188: row 7 skips when row 6\n // skips — both mean \"no durable anchor\" for this landing class). The verdict\n // rows also need this landed-state evidence before they can settle terminal issues.\n const mainVerify = await deps.mainVerify(ctx, merged.mergeCommit);\n const trackerClosed = await (deps.trackerClosed ?? defaultEvaluateDodGateDeps.trackerClosed!)(ctx.issueId);\n const settlement: TerminalVerdictSettlement = {\n trackerClosed,\n landedWork: merged.status === 'pass',\n mainVerifyStatus: mainVerify.status,\n };\n const [review, tests, verification, postMerge, ship, deploy] = await Promise.all([\n deps.review(ctx.issueId, settlement),\n deps.tests(ctx.issueId, settlement),\n deps.verification(ctx.issueId, settlement),\n deps.postMerge(ctx, merged),\n deps.ship(ctx),\n deps.deploy(ctx, {\n mergedAt: merged.mergedAt,\n mergeCommit: merged.mergeCommit,\n mergedRowStatus: merged.status,\n mainVerifyRowStatus: mainVerify.status,\n }),\n ]);\n const rows = [review, tests, verification, merged, postMerge, mainVerify, ship, deploy];\n for (const row of rows) {\n if (row.status === 'miss' && acceptedRows.has(row.id)) {\n row.acceptedBy = { flag: acceptFlagFor(rowDefinition(row.id)), by, at: deps.now() };\n }\n }\n const misses = rows.filter(row => row.status === 'miss').map(row => row.id);\n const accepted = rows.filter(row => row.acceptedBy).map(row => row.id);\n return {\n rows,\n misses,\n accepted,\n passed: rows.every(row => row.status !== 'miss' || Boolean(row.acceptedBy)),\n };\n}\n\n/**\n * PAN-3025: completion witness for the close-out ceremony. Returns closedOutAt\n * only when the journal is terminal AND the live row is gone (the ceremony's\n * final mutating step). closedOut with live status present means a prior run\n * aborted mid-ceremony and must fall through to complete it.\n */\nexport async function readCompletedCloseOut(issueId: string, projectPath: string): Promise<string | null> {\n try {\n const project = resolveProjectForIssue(issueId) ?? getProjectConfigFromWorkspacePath(projectPath);\n const record = await readIssueRecord(project, issueId.toUpperCase());\n if (!record?.pipeline.closedOut) return null;\n // Fail closed: a read error means we cannot confirm absence, so return null (incomplete)\n const live = await Effect.runPromise(getReviewStatus(issueId)).catch(() => 'unknown');\n if (live === 'unknown') return null; // Reject the idempotent path on any read failure\n return live ? null : (record.pipeline.closedOutAt ?? 'unknown');\n } catch {\n // On any error (record read, etc.), fail closed\n return null;\n }\n}\n","/**\n * Residue disposition: close stale convention PRs/MRs for tracker-closed\n * pre-record-era issues with no landing evidence.\n */\n\nimport { execFile } from 'child_process';\nimport { promisify } from 'util';\nimport type { StepResult } from './types.js';\n\nconst execFileAsync = promisify(execFile);\n\n/**\n * Extract GitHub owner/repo from a repository checkout path.\n * Reads the git remote URL to resolve forge coordinates.\n */\nexport async function extractGitHubCoordinates(repoPath: string): Promise<string | null> {\n try {\n const { stdout } = await execFileAsync('git', [\n '-C', repoPath,\n 'remote', 'get-url', 'origin',\n ], { encoding: 'utf-8', timeout: 5_000 });\n\n const remoteUrl = stdout.trim();\n const match = remoteUrl.match(/github\\.com[:/]([^/]+)\\/(.+?)(\\.git)?$/);\n if (match) {\n const owner = match[1];\n const repo = match[2];\n return `${owner}/${repo}`;\n }\n return null;\n } catch {\n return null;\n }\n}\n\n/**\n * Extract GitLab project path from a repository checkout path.\n * Reads the git remote URL to resolve forge coordinates.\n */\nexport async function extractGitLabProject(repoPath: string): Promise<string | null> {\n try {\n const { stdout } = await execFileAsync('git', [\n '-C', repoPath,\n 'remote', 'get-url', 'origin',\n ], { encoding: 'utf-8', timeout: 5_000 });\n\n const remoteUrl = stdout.trim();\n const match = remoteUrl.match(/gitlab\\.com[:/](.+?)(\\.git)?$/);\n if (match) {\n return match[1];\n }\n return null;\n } catch {\n return null;\n }\n}\n\nexport interface CloseResidueConventionPrsContext {\n issueId: string;\n projectPath: string;\n github?: { repos: string[] }; // owner/repo paths for GitHub\n gitlab?: { projects: string[] }; // GitLab project paths\n}\n\nexport async function closeResidueConventionPrs(\n ctx: CloseResidueConventionPrsContext,\n): Promise<StepResult> {\n const evidence: string[] = [];\n const fatalErrors: string[] = [];\n const issueIdLower = ctx.issueId.toLowerCase();\n const possibleHeads = [`feature/${issueIdLower}`, `strike/${issueIdLower}`];\n\n // GitHub PR close — handle all configured repositories\n if (ctx.github?.repos) {\n for (const githubRepo of ctx.github.repos) {\n for (const head of possibleHeads) {\n try {\n const { stdout } = await execFileAsync('gh', [\n 'pr', 'list',\n '--repo', githubRepo,\n '--head', head,\n '--state', 'open',\n '--json', 'number',\n ], { encoding: 'utf-8', timeout: 15_000 });\n\n const prs = JSON.parse(stdout) as Array<{ number: number }>;\n if (prs.length === 0) {\n evidence.push(`No open GitHub PRs found on ${githubRepo}/${head}`);\n }\n for (const pr of prs) {\n try {\n await execFileAsync('gh', [\n 'pr', 'close',\n `${pr.number}`,\n '--repo', githubRepo,\n '--comment', 'Closing stale residue PR with no merge claim — issue closed out without merge landing.',\n ], { encoding: 'utf-8', timeout: 15_000 });\n evidence.push(`Closed GitHub PR #${pr.number} on ${githubRepo}/${head}`);\n } catch (err) {\n fatalErrors.push(`GitHub PR #${pr.number} close failed on ${githubRepo}: ${err instanceof Error ? err.message : String(err)}`);\n }\n }\n } catch (err) {\n fatalErrors.push(`GitHub PR lookup for ${githubRepo}/${head} failed: ${err instanceof Error ? err.message : String(err)}`);\n }\n }\n }\n }\n\n // GitLab MR close — write note BEFORE closing to ensure auditability on retry\n if (ctx.gitlab?.projects) {\n for (const glProject of ctx.gitlab.projects) {\n for (const head of possibleHeads) {\n try {\n const { stdout } = await execFileAsync('glab', [\n 'mr', 'list',\n '--repo', glProject,\n '--source-branch', head,\n '--state', 'opened',\n '--output', 'json',\n ], { encoding: 'utf-8', timeout: 15_000 });\n\n const mrs = JSON.parse(stdout) as Array<{ iid: number }>;\n if (mrs.length === 0) {\n evidence.push(`No open GitLab MRs found on ${glProject}/${head}`);\n }\n for (const mr of mrs) {\n try {\n // Write the audit note BEFORE closing to ensure it's recorded\n await execFileAsync('glab', [\n 'mr', 'note',\n '--repo', glProject,\n String(mr.iid),\n '--message', 'Closing stale residue MR with no merge claim — issue closed out without merge landing.',\n ], { encoding: 'utf-8', timeout: 15_000 });\n\n // Now close the MR\n await execFileAsync('glab', [\n 'mr', 'close',\n '--repo', glProject,\n String(mr.iid),\n ], { encoding: 'utf-8', timeout: 15_000 });\n\n evidence.push(`Closed GitLab MR !${mr.iid} on ${glProject}/${head}`);\n } catch (err) {\n fatalErrors.push(`GitLab MR !${mr.iid} operation failed on ${glProject}: ${err instanceof Error ? err.message : String(err)}`);\n }\n }\n } catch (err) {\n fatalErrors.push(`GitLab MR lookup for ${glProject}/${head} failed: ${err instanceof Error ? err.message : String(err)}`);\n }\n }\n }\n }\n\n // Fail on any repository/operation errors — even if some PRs/MRs closed\n if (fatalErrors.length > 0) {\n return {\n step: 'Close stale convention PRs/MRs',\n success: false,\n skipped: false,\n error: `Failed to list/close convention PRs/MRs: ${fatalErrors.join('; ')}`,\n details: evidence.length > 0 ? [`Partial progress before failure: ${evidence.join('; ')}`] : undefined,\n };\n }\n\n if (evidence.length === 0) {\n return {\n step: 'Close stale convention PRs/MRs',\n success: true,\n skipped: true,\n details: ['No open convention PRs/MRs found on any configured repository'],\n };\n }\n\n return {\n step: 'Close stale convention PRs/MRs',\n success: true,\n skipped: false,\n details: evidence,\n };\n}\n","/**\n * Lifecycle workflows — Compose atomic operations into complete workflows.\n *\n * approve() — Post-merge: archive + close + teardown\n * close() — Simple close: close-issue + teardown\n * closeOut() — Full ceremony: verify-merged + archive + teardown + close + label + clear-status\n * deepWipe() — Destructive: teardown(deleteBranches) + delete agent state + reset issue\n */\n\nimport { existsSync } from 'fs';\nimport { copyFile, readFile, writeFile } from 'fs/promises';\nimport { join } from 'path';\nimport { exec } from 'child_process';\nimport { promisify } from 'util';\nimport { Effect } from 'effect';\nimport { OVERDECK_HOME } from '../paths.js';\nimport type {\n LifecycleContext,\n WorkflowResult,\n StepResult,\n ApproveOptions,\n DeepWipeOptions,\n ArchiveOptions,\n CloseOutOptions,\n} from './types.js';\nimport { stepOk, stepSkipped, stepFailed, getLinearApiKey } from './types.js';\nimport { archivePlanning, findWorkspacePath } from './archive-planning.js';\nimport { closeIssue, type CloseIssueOptions } from './close-issue.js';\nimport { teardownWorkspace } from './teardown-workspace.js';\nimport { loadCloisterConfig } from '../cloister/config.js';\nimport { extractNumberSync, extractPrefixSync } from '../issue-id.js';\nimport { recordFeatureRegistryLifecycle } from '../registry/feature-registry-population.js';\nimport { getForgeAdapter } from '../forge.js';\nimport { resolveProjectReposForIssueSync } from '../project-repos.js';\nimport {\n getProjectConfigFromWorkspacePath,\n markRecordPipelineClosedOutSync,\n markRecordPipelineResidueClosedOutSync,\n writeCloseOutDodGate,\n} from '../pan-dir/record.js';\nimport { pruneStoppedAgentsForIssue } from '../cloister/agent-gc.js';\nimport { isTrackerIssueClosed } from '../cloister/issue-closed.js';\nimport { evaluateDodGate, readCompletedCloseOut } from './dod-gate.js';\nimport { closeResidueConventionPrs, extractGitHubCoordinates, extractGitLabProject } from './residue.js';\nimport {\n capturePipelineStage,\n resolvePipelineTelemetryContext,\n type PipelineTelemetryContext,\n} from '../telemetry/pipeline.js';\nimport { acceptFlagFor, BRANCH_ABSENT_MERGE_ERROR, buildAbandonedDodGate, buildResidueDodGate, DOD_ROWS, type DodGateResult, type DodRowId } from './dod.js';\n\nconst execAsync = promisify(exec);\n\nfunction trackerName(ctx: LifecycleContext, fallback: string): string {\n const name = ctx.tracker?.name ?? fallback;\n return name.charAt(0).toUpperCase() + name.slice(1);\n}\n\n/**\n * Build a WorkflowResult from collected steps.\n */\nfunction buildResult(\n workflow: WorkflowResult['workflow'],\n issueId: string,\n steps: StepResult[],\n startTime: number,\n dodGate?: DodGateResult,\n): WorkflowResult {\n return {\n workflow,\n issueId,\n success: steps.every(s => s.success),\n steps,\n duration: Date.now() - startTime,\n ...(dodGate ? { dodGate } : {}),\n };\n}\n\nfunction hasBlockingFailure(steps: StepResult[]): boolean {\n return steps.some(s => !s.success && !s.skipped);\n}\n\n/**\n * approve() — Post-merge lifecycle.\n */\nexport function approve(\n ctx: LifecycleContext,\n opts: ApproveOptions & CloseIssueOptions & ArchiveOptions = {},\n): Effect.Effect<WorkflowResult> {\n return Effect.gen(function* () {\n const start = Date.now();\n const allSteps: StepResult[] = [];\n\n // 1. Archive planning\n const archiveSteps = yield* archivePlanning(ctx, opts);\n allSteps.push(...archiveSteps);\n\n // If archive failed, stop — don't destroy unarchived artifacts\n const archiveFailed = archiveSteps.some(s => !s.success && !s.skipped);\n if (archiveFailed) {\n allSteps.push(stepFailed('approve:abort', 'Stopped — archiving failed, workspace preserved'));\n return buildResult('approve', ctx.issueId, allSteps, start);\n }\n\n // 2. Close issue\n const closeSteps = yield* closeIssue(ctx, {\n tracker: opts.tracker,\n comment: 'Merged to main via Overdeck lifecycle',\n applyLabel: true,\n });\n allSteps.push(...closeSteps);\n\n // 3. Teardown workspace (delete branches — merge is complete)\n const teardownSteps = yield* teardownWorkspace(ctx, { deleteBranches: true });\n allSteps.push(...teardownSteps);\n\n // 5. Clear review status\n const clearResult = yield* clearReviewStatusStep(ctx.issueId);\n allSteps.push(clearResult);\n\n return buildResult('approve', ctx.issueId, allSteps, start);\n });\n}\n\n/**\n * close() — Simple issue close with teardown.\n */\nexport function close(\n ctx: LifecycleContext,\n opts: CloseIssueOptions = {},\n): Effect.Effect<WorkflowResult> {\n return Effect.gen(function* () {\n const start = Date.now();\n const allSteps: StepResult[] = [];\n\n // 1. Close issue\n const closeSteps = yield* closeIssue(ctx, {\n tracker: opts.tracker,\n reason: opts.reason,\n applyLabel: false,\n });\n allSteps.push(...closeSteps);\n\n // 2. Teardown workspace\n const teardownSteps = yield* teardownWorkspace(ctx);\n allSteps.push(...teardownSteps);\n\n // 3. Clear review status\n const clearResult = yield* clearReviewStatusStep(ctx.issueId);\n allSteps.push(clearResult);\n\n return buildResult('close', ctx.issueId, allSteps, start);\n });\n}\n\n/**\n * closeOut() — Full close-out ceremony.\n *\n * This is the human-gated verification and cleanup workflow.\n * Replaces the monolithic executeCloseOut() function.\n *\n * 1. Verify branch merged (hard fail if not — must pass before any cleanup)\n * 2. Move PRD + archive workspace artifacts (hard fail if archiving fails)\n * 3. Mark xBRIEF completed\n * 4. Clean up workspace (tmux, TLDR, Docker, worktree)\n * 5. Clean up agent state\n * 6. Close issue on tracker\n * 7. Apply closed-out label\n * 8. Clear review status\n */\nexport function closeOut(\n ctx: LifecycleContext,\n opts: CloseIssueOptions & ArchiveOptions & CloseOutOptions = {},\n): Effect.Effect<WorkflowResult> {\n return Effect.gen(function* () {\n const start = Date.now();\n const allSteps: StepResult[] = [];\n\n // PAN-3025: idempotent short-circuit for already-completed close-out ceremony.\n // Fail closed: on any error, proceed to the gate (null = not-confirmed-complete).\n const closedOutAt = yield* Effect.promise(() =>\n readCompletedCloseOut(ctx.issueId, ctx.projectPath).catch(() => null)\n );\n if (closedOutAt) {\n allSteps.push(stepSkipped('close-out:idempotent', [\n `Issue already closed out at ${closedOutAt} — skipping the Definition-of-Done gate and ceremony`,\n ]));\n return buildResult('close-out', ctx.issueId, allSteps, start);\n }\n\n // Recover UAT-promotion evidence before the gate reads the verification verdict.\n const uatEvidenceStep = yield* Effect.promise(async () => {\n try {\n const { healUatPromotionVerification } = await import('../cloister/uat-promote-verification.js');\n const evidence = await healUatPromotionVerification(ctx.issueId);\n if (!evidence) {\n return stepSkipped('dod:uat-promotion-evidence', ['No missing UAT-promotion verification evidence found']);\n }\n return stepOk('dod:uat-promotion-evidence', [\n `Recorded verification from ${evidence.generation}`,\n ...(evidence.mergeSha ? [`Promoted to main at ${evidence.mergeSha.slice(0, 9)}`] : []),\n ]);\n } catch (err) {\n return stepSkipped('dod:uat-promotion-evidence', [\n `Evidence recovery unavailable: ${err instanceof Error ? err.message : String(err)}`,\n ]);\n }\n });\n allSteps.push(uatEvidenceStep);\n\n // 1. Collect residue evidence BEFORE building the gate (if residue disposition)\n const abandon = opts.abandonDisposition;\n const residue = opts.residueDisposition;\n let residueEvidence: string[] = [];\n let trackerClosedEvidence: string[] = [];\n\n if (residue) {\n // Pre-verify tracker is closed for residue disposition\n try {\n const isClosed = yield* Effect.promise(() => isTrackerIssueClosed(ctx.issueId));\n if (!isClosed) {\n allSteps.push(stepFailed('close-out:residue-precondition', 'Residue disposition requires the tracker issue to be already closed'));\n return buildResult('close-out', ctx.issueId, allSteps, start);\n }\n trackerClosedEvidence.push('Tracker issue verified closed');\n } catch (err) {\n allSteps.push(stepFailed('close-out:residue-precondition', `Could not verify tracker closure: ${err instanceof Error ? err.message : String(err)}`));\n return buildResult('close-out', ctx.issueId, allSteps, start);\n }\n\n // Resolve forge coordinates and close stale PRs/MRs\n const prRepos = resolveProjectReposForIssueSync(ctx.issueId);\n if (!prRepos || prRepos.length === 0) {\n allSteps.push(stepFailed('close-out:residue-precondition', 'Could not resolve any configured repositories for residue cleanup'));\n return buildResult('close-out', ctx.issueId, allSteps, start);\n }\n\n // Resolve all GitHub and GitLab coordinates\n const githubPaths = prRepos.filter(r => r.forge === 'github').map(r => r.repoPath);\n const gitlabPaths = prRepos.filter(r => r.forge === 'gitlab').map(r => r.repoPath);\n\n const resolveCoords = yield* Effect.promise(async () => {\n const githubRepos: string[] = [];\n const gitlabRepos: string[] = [];\n const errors: string[] = [];\n\n for (const repoPath of githubPaths) {\n const coords = await extractGitHubCoordinates(repoPath);\n if (coords) {\n githubRepos.push(coords);\n } else {\n errors.push(`GitHub coordinate extraction failed for ${repoPath}`);\n }\n }\n\n for (const repoPath of gitlabPaths) {\n const proj = await extractGitLabProject(repoPath);\n if (proj) {\n gitlabRepos.push(proj);\n } else {\n errors.push(`GitLab project extraction failed for ${repoPath}`);\n }\n }\n\n return { githubRepos, gitlabRepos, errors };\n });\n\n // Fail if any configured repository could not be resolved\n if (resolveCoords.errors.length > 0) {\n allSteps.push(stepFailed('close-out:residue-precondition', `Could not resolve all forge coordinates: ${resolveCoords.errors.join('; ')}`));\n return buildResult('close-out', ctx.issueId, allSteps, start);\n }\n\n // Fail if no repositories were successfully resolved\n if (resolveCoords.githubRepos.length === 0 && resolveCoords.gitlabRepos.length === 0) {\n allSteps.push(stepFailed('close-out:residue-precondition', 'No forge coordinates were resolved for residue cleanup'));\n return buildResult('close-out', ctx.issueId, allSteps, start);\n }\n\n // Execute residue cleanup\n const residueStep = yield* Effect.promise(() => closeResidueConventionPrs({\n issueId: ctx.issueId,\n projectPath: ctx.projectPath,\n github: resolveCoords.githubRepos.length > 0 ? { repos: resolveCoords.githubRepos } : undefined,\n gitlab: resolveCoords.gitlabRepos.length > 0 ? { projects: resolveCoords.gitlabRepos } : undefined,\n }));\n allSteps.push(residueStep);\n if (!residueStep.success && !residueStep.skipped) {\n allSteps.push(stepFailed('close-out:abort', 'Stopped — residue PR/MR close failed'));\n return buildResult('close-out', ctx.issueId, allSteps, start);\n }\n residueEvidence = residueStep.details ?? [];\n }\n\n // 2. Build the Definition-of-Done gate with collected evidence\n let dodGate: DodGateResult = abandon\n ? buildAbandonedDodGate(abandon.reason, abandon.by)\n : residue\n ? buildResidueDodGate(residue.reason, residue.by, [\n ...trackerClosedEvidence,\n ...residueEvidence,\n ])\n : yield* Effect.promise(() => evaluateDodGate(ctx, {\n acceptedRows: opts.dodAcceptedRows,\n acceptedBy: opts.dodAcceptedBy,\n verifyMerged: verifyBranchMergedImpl,\n }));\n\n for (const row of dodGate.rows) {\n const details = [`expected: ${row.expected}`, `observed: ${row.observed}`];\n if (row.acceptedBy) {\n allSteps.push(stepSkipped(`dod:${row.id}`, [\n ...details,\n `MISS accepted via ${row.acceptedBy.flag} by ${row.acceptedBy.by} at ${row.acceptedBy.at}`,\n ]));\n } else if (row.status === 'skip') {\n allSteps.push(stepSkipped(`dod:${row.id}`, details));\n } else if (row.status === 'pass') {\n allSteps.push(stepOk(`dod:${row.id}`, details));\n } else {\n allSteps.push(stepFailed(`dod:${row.id}`, row.observed, details));\n }\n }\n if (!dodGate.passed) {\n const flags = dodGate.rows\n .filter(row => row.status === 'miss' && !row.acceptedBy)\n .map(row => `${row.id} (${acceptFlagFor(DOD_ROWS.find(def => def.id === row.id)!)})`);\n allSteps.push(stepFailed(\n 'close-out:dod-gate',\n `Definition-of-Done gate blocked close-out: ${flags.join(', ')}. Re-run with the named --accept-<row> flag to record an explicit override.`,\n ));\n return buildResult('close-out', ctx.issueId, allSteps, start, dodGate);\n }\n\n // 3. Move PRD + archive workspace artifacts\n // (Note: step numbering adjusted due to upfront residue cleanup)\n\n // 2. Move PRD + archive workspace artifacts\n const archiveSteps = yield* archivePlanning(ctx, opts);\n allSteps.push(...archiveSteps);\n\n // Hard fail on archive failure — don't destroy unarchived artifacts\n const archiveFailed = archiveSteps.some(s => !s.success && !s.skipped);\n if (archiveFailed) {\n allSteps.push(stepFailed('close-out:abort', 'Stopped — archiving failed, workspace preserved'));\n return buildResult('close-out', ctx.issueId, allSteps, start, dodGate);\n }\n\n // 3. Mark the xBRIEF completed on main before teardown removes local state.\n const xbriefStep = yield* Effect.promise(() => completeXBriefStep(ctx));\n allSteps.push(xbriefStep);\n if (!xbriefStep.success && !xbriefStep.skipped) {\n allSteps.push(stepFailed('close-out:abort', 'Stopped — xBRIEF completion failed, workspace preserved'));\n return buildResult('close-out', ctx.issueId, allSteps, start, dodGate);\n }\n\n // 5+6. Teardown workspace + agent state\n const telemetryContext: PipelineTelemetryContext | null = yield* Effect.promise(async () => {\n try {\n return await resolvePipelineTelemetryContext(ctx.issueId);\n } catch {\n // Membership and agent attribution are best-effort and must not abort close-out.\n return null;\n }\n });\n const closeOutConfig = (yield* Effect.promise(() => Effect.runPromise(loadCloisterConfig()))).close_out;\n const teardownSteps = yield* teardownWorkspace(ctx, {\n deleteWorkspace: closeOutConfig?.remove_workspace ?? false,\n deleteBranches: closeOutConfig?.delete_feature_branch ?? false,\n });\n allSteps.push(...teardownSteps);\n if (hasBlockingFailure(teardownSteps)) {\n allSteps.push(stepFailed('close-out:abort', 'Stopped — teardown failed, tracker issue and review status preserved'));\n return buildResult('close-out', ctx.issueId, allSteps, start, dodGate);\n }\n\n const teardownDef = DOD_ROWS.find(row => row.id === 'teardown')!;\n dodGate.rows.push({\n ...teardownDef,\n status: 'pass',\n observed: teardownSteps.flatMap(step => step.details ?? []).join('; ') || 'close-out teardown completed',\n });\n\n // 6+7. Close issue + apply label\n const closeSteps = yield* closeIssue(ctx, {\n tracker: opts.tracker,\n comment: abandon\n ? `Closed without landing evidence — disposition recorded: ${abandon.reason}`\n : ctx.auto ? 'Closed via automatic close-out ceremony' : 'Closed via close-out ceremony',\n applyLabel: true,\n });\n allSteps.push(...closeSteps);\n if (hasBlockingFailure(closeSteps)) {\n allSteps.push(stepFailed('close-out:abort', 'Stopped — issue close failed, review status preserved'));\n return buildResult('close-out', ctx.issueId, allSteps, start, dodGate);\n }\n\n // 8. Mark durable pipeline terminal before clearing the DB cache.\n const markTerminal = yield* markPipelineClosedOutStep(ctx, residue);\n allSteps.push(markTerminal);\n\n // Update gate with verified residue evidence before recording if a residue row exists\n if (residue && residueEvidence.length > 0) {\n const residueRow = dodGate.rows.find(row => (row.id as string) === 'residue');\n if (residueRow) {\n residueRow.observed = residueEvidence.join('; ');\n }\n }\n\n const recordDodGate = yield* recordDodGateStep(ctx, dodGate, abandon, residue);\n allSteps.push(recordDodGate);\n if (!recordDodGate.success) {\n allSteps.push(stepFailed('close-out:abort', 'Stopped — Definition-of-Done audit could not be persisted; review status preserved'));\n return buildResult('close-out', ctx.issueId, allSteps, start, dodGate);\n }\n if (markTerminal.success) {\n const pruned = yield* Effect.promise(() => pruneStoppedAgentsForIssue(ctx.issueId));\n allSteps.push(pruned.preserved.length > 0\n ? stepSkipped('close-out:prune-agent-rows', [`Preserved live agents or terminal rows with retained transcripts: ${pruned.preserved.join(', ')}`])\n : stepOk('close-out:prune-agent-rows', [`Pruned ${pruned.removed.length} stopped agent row(s)`]));\n }\n\n // 9. Clear review status\n const clearResult = yield* clearReviewStatusStep(ctx.issueId);\n allSteps.push(clearResult);\n\n yield* Effect.promise(() => resetPostMergeStateForIssue(ctx.issueId));\n yield* Effect.promise(() => recordFeatureRegistryLifecycle({ issueId: ctx.issueId, status: 'archived' }));\n\n const result = buildResult('close-out', ctx.issueId, allSteps, start, dodGate);\n if (result.success && !markTerminal.skipped) capturePipelineStage('closed_out', telemetryContext);\n return result;\n });\n}\n\nfunction markPipelineClosedOutStep(ctx: LifecycleContext, residue?: { reason: string; by: string }): Effect.Effect<StepResult> {\n const step = 'close-out:mark-pipeline-terminal';\n return Effect.try({\n try: () => {\n const project = getProjectConfigFromWorkspacePath(ctx.projectPath);\n if (residue) {\n markRecordPipelineResidueClosedOutSync(project, ctx.issueId.toUpperCase());\n } else {\n markRecordPipelineClosedOutSync(project, ctx.issueId.toUpperCase());\n }\n return stepOk(step, ['Marked durable pipeline journal closed-out']);\n },\n catch: (err) => err,\n }).pipe(\n Effect.catch((err) =>\n Effect.succeed(stepSkipped(step, [`Pipeline terminal marker failed (non-fatal): ${(err as Error).message ?? String(err)}`])),\n ),\n );\n}\n\nfunction recordDodGateStep(ctx: LifecycleContext, dodGate: DodGateResult, abandonDisposition?: { reason: string; by: string }, residueDisposition?: { reason: string; by: string }): Effect.Effect<StepResult> {\n const step = 'close-out:record-dod-gate';\n return Effect.tryPromise({\n try: async () => {\n const project = getProjectConfigFromWorkspacePath(ctx.projectPath);\n await writeCloseOutDodGate(project, ctx.issueId.toUpperCase(), {\n evaluatedAt: new Date().toISOString(),\n rows: dodGate.rows,\n accepted: dodGate.accepted,\n ...(abandonDisposition ? { disposition: abandonDisposition } : {}),\n ...(residueDisposition ? { disposition: residueDisposition } : {}),\n });\n return stepOk(step, ['Recorded Definition-of-Done gate with 8 rows']);\n },\n catch: (err) => err,\n }).pipe(\n Effect.catch((err) =>\n Effect.succeed(stepFailed(step, `Definition-of-Done gate record failed: ${(err as Error).message ?? String(err)}`)),\n ),\n );\n}\n\n/**\n * deepWipe() — Destructive cleanup for abandoned workspaces.\n */\nfunction destructiveResetWorkflow(\n workflow: 'deep-wipe' | 'reset' | 'cancel',\n ctx: LifecycleContext,\n opts: DeepWipeOptions,\n resetStep: (ctx: LifecycleContext) => Effect.Effect<StepResult>,\n progressLabel: string,\n progressSuccessDetail: string,\n): Effect.Effect<WorkflowResult> {\n return Effect.gen(function* () {\n const start = Date.now();\n const allSteps: StepResult[] = [];\n const { deleteWorkspace = true, deleteBranches = true, resetIssue = true, onProgress } = opts;\n const resetContext = opts.tracker ? { ...ctx, tracker: opts.tracker } : ctx;\n\n const TOTAL_STEPS = 3 + (resetIssue ? 1 : 0);\n let stepNum = 0;\n\n const progress = (label: string, detail: string, status: 'active' | 'complete' | 'error' = 'active') => {\n onProgress?.({ step: stepNum, total: TOTAL_STEPS, label, detail, status });\n };\n\n stepNum = 1;\n\n // Preserve PRD before workspace teardown so it survives reset/cancel.\n const issueLower = ctx.issueId.toLowerCase();\n const workspacePath = findWorkspacePath(ctx.projectPath, issueLower);\n if (workspacePath && existsSync(workspacePath)) {\n const prdPath = join(workspacePath, '.pan', 'prd.md');\n if (existsSync(prdPath)) {\n yield* Effect.tryPromise({\n try: async () => {\n const activeDir = join(ctx.projectPath, 'docs', 'prds', 'active', issueLower);\n const { mkdir } = await import('fs/promises');\n await mkdir(activeDir, { recursive: true });\n await copyFile(prdPath, join(activeDir, 'prd.md'));\n },\n catch: () => null,\n }).pipe(Effect.catch(() => Effect.void));\n }\n }\n\n progress('Tearing down workspace', 'Killing agents, stopping services, removing files');\n const teardownSteps = yield* teardownWorkspace(ctx, {\n deleteWorkspace,\n deleteBranches,\n workspaceConfig: opts.workspaceConfig,\n projectName: opts.projectName,\n });\n allSteps.push(...teardownSteps);\n const teardownFailed = teardownSteps.some(s => !s.success && !s.skipped);\n progress('Tearing down workspace', teardownFailed ? 'Some steps failed' : 'Workspace torn down', teardownFailed ? 'error' : 'complete');\n\n stepNum = 2;\n progress('Deleting git branches', `feature/${ctx.issueId.toLowerCase()}`);\n progress('Deleting git branches', deleteBranches ? 'Branches removed' : 'Skipped', 'complete');\n\n if (resetIssue) {\n stepNum = 3;\n progress(progressLabel, `${ctx.issueId}`);\n const resetResult = yield* resetStep(resetContext);\n allSteps.push(resetResult);\n progress(progressLabel, resetResult.success ? progressSuccessDetail : (resetResult.error || 'Failed'), resetResult.success ? 'complete' : 'error');\n }\n\n stepNum = resetIssue ? 4 : 3;\n progress('Clearing review status', 'Removing specialist state');\n const clearResult = yield* clearReviewStatusStep(ctx.issueId);\n allSteps.push(clearResult);\n progress('Clearing review status', 'Review status cleared', 'complete');\n\n return buildResult(workflow, ctx.issueId, allSteps, start);\n });\n}\n\nexport function deepWipe(\n ctx: LifecycleContext,\n opts: DeepWipeOptions = {},\n): Effect.Effect<WorkflowResult> {\n return destructiveResetWorkflow(\n 'deep-wipe',\n ctx,\n opts,\n resetIssueToTodo,\n 'Resetting issue status',\n 'Issue reset to Todo',\n );\n}\n\nexport function resetToTodo(\n ctx: LifecycleContext,\n opts: DeepWipeOptions = {},\n): Effect.Effect<WorkflowResult> {\n return destructiveResetWorkflow(\n 'reset',\n ctx,\n opts,\n resetIssueToTodo,\n 'Resetting issue status',\n 'Issue reset to Todo',\n );\n}\n\nexport function cancelIssueWorkflow(\n ctx: LifecycleContext,\n opts: DeepWipeOptions = {},\n): Effect.Effect<WorkflowResult> {\n return destructiveResetWorkflow(\n 'cancel',\n ctx,\n opts,\n resetIssueToCanceled,\n 'Canceling issue',\n 'Issue moved to Canceled',\n );\n}\n\n// --- Internal helpers ---\n\nasync function completeXBriefStep(ctx: LifecycleContext): Promise<StepResult> {\n const step = 'close-out:vbrief-completed';\n try {\n const { transitionXBriefOnMain } = await import('../xbrief/lifecycle-io.js');\n const result = await Effect.runPromise(transitionXBriefOnMain(\n ctx.projectPath,\n ctx.issueId,\n 'completed',\n 'completed',\n `scope: complete ${ctx.issueId.toUpperCase()} xBRIEF`,\n ));\n const details = [\n result.moved ? 'Updated xBRIEF lifecycle to completed' : 'xBRIEF lifecycle already completed',\n result.statusUpdated ? 'Updated plan.status to completed' : 'plan.status already completed',\n ];\n if (result.committed) details.push('Committed xBRIEF completion on main');\n return stepOk(step, details);\n } catch (err) {\n const cause = (err as { cause?: unknown }).cause ?? err;\n const message = cause instanceof Error ? cause.message : String(cause);\n if (message.includes('No xBRIEF found')) {\n return stepSkipped(step, [`No xBRIEF found for ${ctx.issueId}`]);\n }\n return stepFailed(step, `xBRIEF completion failed: ${message}`);\n }\n}\n\n/**\n * Verify feature branch is merged into main.\n */\nfunction verifyBranchMerged(ctx: LifecycleContext): Effect.Effect<StepResult> {\n return Effect.tryPromise({\n try: () => verifyBranchMergedImpl(ctx),\n catch: (err) => err,\n }).pipe(\n Effect.catch((err) =>\n Effect.succeed(stepFailed('close-out:verify-merged', `Could not verify merge: ${(err as Error).message}`)),\n ),\n );\n}\n\nexport interface MergeVerificationRoot { repoKey: string; dir: string; sourceBranch: string; targetBranch: string; forge: 'github' | 'gitlab' }\n\nasync function verifyRootConventionBranches(ctx: LifecycleContext, root: MergeVerificationRoot, issueLower: string): Promise<StepResult | null> {\n const featureResult = await verifyConventionBranchMerged(ctx, root);\n if (featureResult?.success) return featureResult;\n\n const strikeResult = await verifyConventionBranchMerged(ctx, { ...root, sourceBranch: `strike/${issueLower}` });\n if (strikeResult?.success && !strikeResult.skipped) {\n return stepOk('close-out:verify-merged', [\n ...(strikeResult.details ?? []),\n `Superseded ${root.sourceBranch} residual: ${featureResult?.error ?? BRANCH_ABSENT_MERGE_ERROR}`,\n ]);\n }\n return featureResult;\n}\n\nexport async function verifyBranchMergedImpl(ctx: LifecycleContext): Promise<StepResult> {\n const step = 'close-out:verify-merged';\n const issueLower = ctx.issueId.toLowerCase();\n const defaultRoot: MergeVerificationRoot = { repoKey: issueLower, dir: ctx.projectPath, sourceBranch: `feature/${issueLower}`, targetBranch: 'main', forge: 'github' };\n\n try {\n // Check review-status first — the merge specialist validates before marking merged\n try {\n const { loadReviewStatuses } = await import('../review-status.js');\n const statuses = loadReviewStatuses();\n const issueKey = ctx.issueId.toUpperCase();\n if (statuses[issueKey]?.mergeStatus === 'merged') {\n return stepOk(step, ['Merge specialist confirmed merge completed']);\n }\n } catch {\n // review-status.json may not exist, continue with git checks\n }\n\n const resolvedRoots = resolveProjectReposForIssueSync(ctx.issueId)\n ?.filter((repo) => repo.required)\n .map((repo): MergeVerificationRoot => ({\n repoKey: repo.repoKey,\n dir: repo.repoPath,\n sourceBranch: repo.sourceBranch,\n targetBranch: repo.targetBranch,\n forge: repo.forge,\n }));\n const roots = resolvedRoots?.length ? resolvedRoots : [defaultRoot];\n if (roots.length === 1) {\n return await verifyRootConventionBranches(ctx, roots[0], issueLower)\n ?? stepFailed(step, BRANCH_ABSENT_MERGE_ERROR);\n }\n\n const results = await Promise.all(roots.map(async (root) => ({\n root,\n result: await verifyRootConventionBranches(ctx, root, issueLower),\n })));\n const failures = results.filter(({ result }) => result && !result.success);\n if (failures.length > 0) {\n return stepFailed(step, failures.map(({ root, result }) => `${root.repoKey}: ${result?.error}`).join('; '));\n }\n const successes = results.filter(({ result }) => result?.success);\n if (successes.length > 0) {\n return stepOk(step, successes.flatMap(({ root, result }) =>\n (result?.details ?? []).map((detail) => `${root.repoKey}: ${detail}`)));\n }\n return stepFailed(step, BRANCH_ABSENT_MERGE_ERROR);\n } catch (err) {\n return stepFailed(step, `Could not verify merge: ${(err as Error).message}`);\n }\n}\n\ninterface GitLabMergeLookupCache {\n attempted: boolean;\n headSha?: string;\n artifact?: { id?: string; url?: string } | null;\n}\n\ninterface GitLabMergeMatch {\n result: StepResult;\n reused: boolean;\n}\n\nexport async function verifyConventionBranchMerged(ctx: LifecycleContext, root: MergeVerificationRoot): Promise<StepResult | null> {\n const step = 'close-out:verify-merged';\n const gitlabLookup: GitLabMergeLookupCache = { attempted: false };\n const { stdout: branchExists } = await execAsync(\n `git branch --list \"${root.sourceBranch}\" 2>/dev/null || true`,\n { cwd: root.dir, encoding: 'utf-8' },\n );\n\n if (branchExists.trim()) {\n // Use merge-base --is-ancestor: checks if the branch tip is reachable from main\n try {\n await execAsync(\n `git merge-base --is-ancestor ${root.sourceBranch} ${root.targetBranch}`,\n { cwd: root.dir, encoding: 'utf-8' },\n );\n const remoteCheck = await verifyRemoteBranchIfPresent(ctx, root, gitlabLookup);\n if (remoteCheck && !remoteCheck.success) return remoteCheck;\n return stepOk(step, ['All commits merged to main', ...(remoteCheck?.details ?? [])]);\n } catch {\n // --is-ancestor fails for squash merges where the branch still exists.\n const gitlabMerged = await verifySquashMergedMrByBranch(root, root.sourceBranch, gitlabLookup);\n if (gitlabMerged) {\n const remoteCheck = await verifyRemoteBranchIfPresent(ctx, root, gitlabLookup);\n if (remoteCheck && !remoteCheck.success) return remoteCheck;\n return stepOk(step, [...(gitlabMerged.result.details ?? []), ...(remoteCheck?.details ?? [])]);\n }\n\n try {\n const { stdout: codeDiff } = await execAsync(\n `git diff ${root.targetBranch}...${root.sourceBranch} -- ':!.planning' ':!docs/prds' ':!.overdeck/prompts' 2>/dev/null || true`,\n { cwd: root.dir, encoding: 'utf-8' },\n );\n if (!codeDiff.trim()) {\n const remoteCheck = await verifyRemoteBranchIfPresent(ctx, root, gitlabLookup);\n if (remoteCheck && !remoteCheck.success) return remoteCheck;\n return stepOk(step, [\n 'Code changes squash-merged to main (only planning artifacts remain on branch)',\n ...(remoteCheck?.details ?? []),\n ]);\n }\n } catch {\n // diff failed — fall through to unmerged report\n }\n\n const githubMerged = root.forge === 'github'\n ? await verifySquashMergedPrByBranch(ctx, root, root.sourceBranch)\n : null;\n if (githubMerged?.success) {\n const remoteCheck = await verifyRemoteBranchIfPresent(ctx, root, gitlabLookup);\n if (remoteCheck && !remoteCheck.success) return remoteCheck;\n return stepOk(step, [\n ...(githubMerged.details ?? []),\n ...(remoteCheck?.details ?? []),\n ]);\n }\n if (githubMerged) return githubMerged;\n\n const { stdout: unmerged } = await execAsync(\n `git log ${root.targetBranch}..${root.sourceBranch} --oneline 2>/dev/null || true`,\n { cwd: root.dir, encoding: 'utf-8' },\n );\n const count = unmerged.trim() ? unmerged.trim().split('\\n').length : 0;\n\n if (ctx.github && root.forge === 'github') {\n try {\n const { stdout: issueState } = await execAsync(\n `gh issue view ${ctx.github.number} --repo ${ctx.github.owner}/${ctx.github.repo} --json state --jq '.state'`,\n { cwd: root.dir, encoding: 'utf-8' },\n );\n if (issueState.trim().toUpperCase() === 'CLOSED') {\n return stepSkipped(step, [`Issue already closed on GitHub; ${count} unmerged commit(s) remain on ${root.sourceBranch}`]);\n }\n } catch {\n // gh check failed — fall through to hard fail\n }\n }\n\n return stepFailed(step, `${count} unmerged commit(s) on ${root.sourceBranch}. Merge before closing out.`);\n }\n }\n\n // Check remote\n const { stdout: remoteBranch } = await execAsync(\n `git ls-remote --heads origin \"${root.sourceBranch}\" 2>/dev/null || true`,\n { cwd: root.dir, encoding: 'utf-8' },\n );\n\n if (remoteBranch.trim()) {\n await execAsync(`git fetch origin ${root.sourceBranch}`, { cwd: root.dir }).catch(() => {});\n const remoteCheck = await verifyRemoteBranchIfPresent(ctx, root, gitlabLookup);\n if (remoteCheck) return remoteCheck;\n }\n\n return null;\n}\n\nasync function verifySquashMergedMrByBranch(\n root: MergeVerificationRoot,\n branchRef: string,\n lookup: GitLabMergeLookupCache,\n): Promise<GitLabMergeMatch | null> {\n if (root.forge !== 'gitlab') return null;\n try {\n const { stdout } = await execAsync(`git rev-parse ${branchRef} 2>/dev/null`, { cwd: root.dir, encoding: 'utf-8' });\n const headSha = stdout.trim();\n if (!headSha) return null;\n const reused = lookup.attempted;\n if (!reused) {\n lookup.attempted = true;\n lookup.headSha = headSha;\n lookup.artifact = await getForgeAdapter('gitlab').findMergedArtifact({\n sourceBranch: root.sourceBranch,\n targetBranch: root.targetBranch,\n headSha,\n cwd: root.dir,\n });\n }\n if (!lookup.artifact || lookup.headSha !== headSha) return null;\n const label = lookup.artifact.id ? `MR !${lookup.artifact.id}` : 'GitLab MR';\n const url = lookup.artifact.url ? ` (${lookup.artifact.url})` : '';\n return {\n result: stepOk('close-out:verify-merged', [`${label} is merged and ${branchRef} matches the merged MR head${url}`]),\n reused,\n };\n } catch {\n return null;\n }\n}\n\ntype GitHubMergedPr = {\n number?: number;\n mergedAt?: string | null;\n headRefOid?: string | null;\n url?: string | null;\n};\n\nasync function verifyRemoteBranchIfPresent(\n ctx: LifecycleContext,\n root: MergeVerificationRoot,\n gitlabLookup: GitLabMergeLookupCache,\n): Promise<StepResult | null> {\n const step = 'close-out:verify-merged';\n const remoteRef = `origin/${root.sourceBranch}`;\n\n const { stdout: remoteBranch } = await execAsync(\n `git ls-remote --heads origin \"${root.sourceBranch}\" 2>/dev/null || true`,\n { cwd: root.dir, encoding: 'utf-8' },\n );\n if (!remoteBranch.trim()) return null;\n\n await execAsync(`git fetch origin ${root.sourceBranch}`, { cwd: root.dir }).catch(() => {});\n\n try {\n await execAsync(\n `git merge-base --is-ancestor ${remoteRef} ${root.targetBranch}`,\n { cwd: root.dir, encoding: 'utf-8' },\n );\n return stepOk(step, ['Remote branch fully merged']);\n } catch {\n // Squash-merge detection for remote branch\n try {\n const { stdout: codeDiff } = await execAsync(\n `git diff ${root.targetBranch}...${remoteRef} -- ':!.planning' ':!docs/prds' ':!.overdeck/prompts' 2>/dev/null || true`,\n { cwd: root.dir, encoding: 'utf-8' },\n );\n if (!codeDiff.trim()) {\n return stepOk(step, ['Remote code changes squash-merged to main (only planning artifacts remain on branch)']);\n }\n } catch {\n // diff failed — fall through\n }\n\n const gitlabMerged = await verifySquashMergedMrByBranch(root, remoteRef, gitlabLookup);\n if (gitlabMerged) {\n return gitlabMerged.reused\n ? stepOk(step, [`Remote ${remoteRef} matches the merged MR head`])\n : gitlabMerged.result;\n }\n\n const githubMerged = root.forge === 'github'\n ? await verifySquashMergedPrByBranch(ctx, root, remoteRef)\n : null;\n if (githubMerged) return githubMerged;\n\n const { stdout: remoteUnmerged } = await execAsync(\n `git log ${root.targetBranch}..${remoteRef} --oneline 2>/dev/null || true`,\n { cwd: root.dir, encoding: 'utf-8' },\n );\n const count = remoteUnmerged.trim() ? remoteUnmerged.trim().split('\\n').length : 0;\n\n if (ctx.github && root.forge === 'github') {\n try {\n const { stdout: issueState } = await execAsync(\n `gh issue view ${ctx.github.number} --repo ${ctx.github.owner}/${ctx.github.repo} --json state --jq '.state'`,\n { cwd: root.dir, encoding: 'utf-8' },\n );\n if (issueState.trim().toUpperCase() === 'CLOSED') {\n return stepSkipped(step, [`Issue already closed on GitHub; ${count} unmerged commit(s) remain on remote ${root.sourceBranch}`]);\n }\n } catch {\n // gh check failed — fall through to hard fail\n }\n }\n\n return stepFailed(step, `${count} unmerged commit(s) on remote ${root.sourceBranch}.`);\n }\n}\n\nasync function verifySquashMergedPrByBranch(\n ctx: LifecycleContext,\n root: MergeVerificationRoot,\n branchRef: string,\n): Promise<StepResult | null> {\n if (!ctx.github) return null;\n\n const step = 'close-out:verify-merged';\n const { owner, repo } = ctx.github;\n const remoteTargetRef = `origin/${root.targetBranch}`;\n\n try {\n const { stdout: prJson } = await execAsync(\n `gh pr list --repo ${owner}/${repo} --state merged --head ${JSON.stringify(root.sourceBranch)} --json number,mergedAt,headRefOid,url`,\n { cwd: root.dir, encoding: 'utf-8' },\n );\n const prs = JSON.parse(prJson) as GitHubMergedPr[];\n const mergedPr = prs\n .filter((pr) => (\n typeof pr.mergedAt === 'string'\n && pr.mergedAt.length > 0\n && typeof pr.headRefOid === 'string'\n && pr.headRefOid.length > 0\n ))\n .sort((a, b) => Date.parse(String(b.mergedAt)) - Date.parse(String(a.mergedAt)))[0];\n if (!mergedPr?.headRefOid) return null;\n\n const { stdout: tipShaRaw } = await execAsync(\n `git rev-parse ${branchRef} 2>/dev/null`,\n { cwd: root.dir, encoding: 'utf-8' },\n );\n const tipSha = tipShaRaw.trim();\n if (!tipSha) return null;\n\n if (tipSha === mergedPr.headRefOid) {\n const prLabel = typeof mergedPr.number === 'number' ? `PR #${mergedPr.number}` : 'GitHub PR';\n return stepOk(step, [`${prLabel} is squash-merged and ${branchRef} matches the merged PR head`]);\n }\n\n // A workspace may merge a newer target branch after its PR head was pushed.\n // Ignore those merge commits and allow close-out when every post-PR commit\n // is already on the configured remote target; only branch-unique work is unsafe.\n let commitsNotOnTarget: string[] | null = null;\n try {\n const { stdout: commitsRaw } = await execAsync(\n `git log --no-merges --format=%H ${mergedPr.headRefOid}..${tipSha}`,\n { cwd: root.dir, encoding: 'utf-8' },\n );\n const commits = commitsRaw.split('\\n').map((sha) => sha.trim()).filter(Boolean);\n const unmerged: string[] = [];\n for (const commit of commits) {\n try {\n await execAsync(\n `git merge-base --is-ancestor ${commit} ${remoteTargetRef}`,\n { cwd: root.dir, encoding: 'utf-8' },\n );\n } catch {\n unmerged.push(commit);\n }\n }\n if (unmerged.length === 0) {\n const prLabel = typeof mergedPr.number === 'number' ? `PR #${mergedPr.number}` : 'GitHub PR';\n return stepOk(step, [\n `${prLabel} is squash-merged; all ${commits.length} post-PR non-merge commit(s) on ${branchRef} are already on ${remoteTargetRef}`,\n ]);\n }\n\n commitsNotOnTarget = unmerged;\n } catch { /* containment check failure falls through to the state-plane policy */ }\n\n // PAN-2406 / state-plane policy rule 3: commits after the merged head that\n // touch ONLY legacy state-plane paths under .pan/ are pipeline exhaust —\n // e.g. 'chore: record merge status' — and must not block close-out.\n try {\n const { stdout: deltaRaw } = await execAsync(\n `git diff --name-only ${mergedPr.headRefOid}..${tipSha}`,\n { cwd: root.dir, encoding: 'utf-8' },\n );\n let deltaFiles = deltaRaw.split('\\n').map((f) => f.trim()).filter(Boolean);\n let statePlaneOnly = deltaFiles.length > 0 && deltaFiles.every((f) =>\n f.startsWith('.pan/'));\n if (!statePlaneOnly) {\n // Branch may have merged its target INTO itself after the PR merged —\n // the two-dot delta then contains target-branch files. Judge only changes\n // UNIQUE to the branch (three-dot vs the remote target): if those are\n // state-plane-only, everything real is already on the target.\n const { stdout: uniqueRaw } = await execAsync(\n `git diff --name-only ${remoteTargetRef}...${tipSha}`,\n { cwd: root.dir, encoding: 'utf-8' },\n );\n deltaFiles = uniqueRaw.split('\\n').map((f) => f.trim()).filter(Boolean);\n statePlaneOnly = deltaFiles.every((f) =>\n f.startsWith('.pan/'));\n }\n if (statePlaneOnly) {\n const prLabel = typeof mergedPr.number === 'number' ? `PR #${mergedPr.number}` : 'GitHub PR';\n return stepOk(step, [\n `${prLabel} is squash-merged; ${branchRef} is ahead only by state-plane commits (${deltaFiles.length} file(s): .pan) — accepted per state-plane policy`,\n ]);\n }\n } catch { /* diff failure falls through to the strict rejection below */ }\n\n const prLabel = typeof mergedPr.number === 'number' ? `PR #${mergedPr.number}` : 'merged GitHub PR';\n if (commitsNotOnTarget) {\n return stepFailed(step, `${branchRef} has ${commitsNotOnTarget.length} commit(s) after merged ${prLabel} that are not on ${remoteTargetRef}: ${commitsNotOnTarget.join(', ')}`);\n }\n return stepFailed(step, `${branchRef} does not match the head commit of merged ${prLabel}; inspect before closing out.`);\n } catch {\n return null;\n }\n}\n\n/**\n * Reset issue back to open/backlog state (for destructive reset).\n */\nfunction resetIssueToTodo(ctx: LifecycleContext): Effect.Effect<StepResult> {\n return Effect.tryPromise({\n try: () => resetIssueToTodoImpl(ctx),\n catch: (err) => err,\n }).pipe(\n Effect.catch((err) =>\n Effect.succeed(stepFailed('reset:reset-issue', `Failed to reset issue: ${(err as Error).message}`)),\n ),\n );\n}\n\nasync function resetIssueToTodoImpl(ctx: LifecycleContext): Promise<StepResult> {\n const step = 'reset:reset-issue';\n try {\n if (ctx.github) {\n const { owner, repo, number } = ctx.github;\n // Reopen the issue\n await execAsync(\n `gh issue reopen ${number} --repo ${owner}/${repo}`,\n { encoding: 'utf-8' },\n ).catch(() => {}); // May already be open\n // Remove lifecycle labels\n const labelsToRemove = ['in-review', 'in-progress', 'planned', 'planning', 'Review: Approved', 'Review: Failed', 'ready-for-merge'];\n for (const label of labelsToRemove) {\n await execAsync(\n `gh issue edit ${number} --repo ${owner}/${repo} --remove-label \"${label}\"`,\n { encoding: 'utf-8' },\n ).catch(() => {}); // Label may not exist\n }\n return stepOk(step, [`Reset GitHub issue #${number}: reopened and cleared labels`]);\n }\n\n // Linear: reopen to Todo\n const linearApiKey = await getLinearApiKey();\n if (linearApiKey) {\n const { LinearClient } = await import('@linear/sdk');\n const client = new LinearClient({ apiKey: linearApiKey });\n const issueNum = extractNumberSync(ctx.issueId);\n const teamKey = extractPrefixSync(ctx.issueId);\n if (issueNum === null || teamKey === null) {\n return stepFailed(step, `Could not parse issue ID: ${ctx.issueId}`);\n }\n const results = await client.issues({\n filter: {\n number: { eq: issueNum },\n team: { key: { eq: teamKey } },\n },\n first: 1,\n });\n if (results.nodes.length > 0) {\n const issue = results.nodes[0];\n const team = await issue.team;\n if (team) {\n const states = await team.states();\n const todoState = states.nodes.find(s => s.type === 'unstarted' && s.name === 'Todo') ||\n states.nodes.find(s => s.type === 'unstarted');\n if (todoState) {\n await issue.update({ stateId: todoState.id });\n }\n }\n }\n return stepOk(step, [`Reset ${trackerName(ctx, 'linear')} issue ${ctx.issueId} to Todo`]);\n }\n\n return stepSkipped(step, ['No tracker available to reset issue']);\n } catch (err) {\n return stepFailed(step, `Failed to reset issue: ${(err as Error).message}`);\n }\n}\n\n/**\n * Clear review status for an issue.\n */\nasync function resetPostMergeStateForIssue(issueId: string): Promise<void> {\n try {\n const { resetPostMergeState } = await import('../cloister/merge-agent.js');\n resetPostMergeState(issueId);\n resetPostMergeState(issueId.toUpperCase());\n } catch {\n return;\n }\n}\n\nfunction clearReviewStatusStep(issueId: string): Effect.Effect<StepResult> {\n return Effect.tryPromise({\n try: () => clearReviewStatusStepImpl(issueId),\n catch: (err) => err,\n }).pipe(\n Effect.catch((err) =>\n Effect.succeed(stepSkipped('clear-review-status', [`Failed to clear review status (non-fatal): ${(err as Error).message}`])),\n ),\n );\n}\n\nasync function clearReviewStatusStepImpl(issueId: string): Promise<StepResult> {\n const step = 'clear-review-status';\n try {\n const { clearReviewStatus } = await import('../review-status.js');\n clearReviewStatus(issueId.toUpperCase());\n return stepOk(step, ['Review status cleared']);\n } catch {\n // Fallback: direct file manipulation\n try {\n const statusFile = join(OVERDECK_HOME, 'review-status.json');\n if (existsSync(statusFile)) {\n const data = JSON.parse(await readFile(statusFile, 'utf-8'));\n const upperKey = issueId.toUpperCase();\n if (data[upperKey]) {\n delete data[upperKey];\n await writeFile(statusFile, JSON.stringify(data, null, 2));\n }\n }\n return stepOk(step, ['Review status cleared (direct)']);\n } catch (innerErr) {\n return stepSkipped(step, [`Failed to clear review status (non-fatal): ${(innerErr as Error).message}`]);\n }\n }\n}\n\nexport const __testInternals = {\n completeXBriefStep,\n verifyBranchMerged,\n};\n\nfunction resetIssueToCanceled(ctx: LifecycleContext): Effect.Effect<StepResult> {\n return Effect.tryPromise({\n try: () => resetIssueToCanceledImpl(ctx),\n catch: (err) => err,\n }).pipe(\n Effect.catch((err) =>\n Effect.succeed(stepFailed('cancel:reset-issue', `Failed to cancel issue: ${(err as Error).message}`)),\n ),\n );\n}\n\nasync function resetIssueToCanceledImpl(ctx: LifecycleContext): Promise<StepResult> {\n const step = 'cancel:reset-issue';\n try {\n if (ctx.github) {\n const { owner, repo, number } = ctx.github;\n await execAsync(\n `gh issue edit ${number} --repo ${owner}/${repo} --add-label \"wontfix\"`,\n { encoding: 'utf-8' },\n ).catch(() => {});\n return stepOk(step, [`Marked GitHub issue #${number} as canceled/wontfix`]);\n }\n\n const linearApiKey = await getLinearApiKey();\n if (linearApiKey) {\n const { LinearClient } = await import('@linear/sdk');\n const client = new LinearClient({ apiKey: linearApiKey });\n const issueNum = extractNumberSync(ctx.issueId);\n const teamKey = extractPrefixSync(ctx.issueId);\n if (issueNum === null || teamKey === null) {\n return stepFailed(step, `Could not parse issue ID: ${ctx.issueId}`);\n }\n const results = await client.issues({\n filter: {\n number: { eq: issueNum },\n team: { key: { eq: teamKey } },\n },\n first: 1,\n });\n if (results.nodes.length > 0) {\n const issue = results.nodes[0];\n const team = await issue.team;\n if (team) {\n const states = await team.states();\n const canceledState = states.nodes.find(s => s.type === 'canceled') ||\n states.nodes.find(s => s.name.toLowerCase() === 'canceled');\n if (canceledState) {\n await issue.update({ stateId: canceledState.id });\n }\n }\n }\n return stepOk(step, [`Reset ${trackerName(ctx, 'linear')} issue ${ctx.issueId} to Canceled`]);\n }\n\n return stepSkipped(step, ['No tracker available to cancel issue']);\n } catch (err) {\n return stepFailed(step, `Failed to cancel issue: ${(err as Error).message}`);\n }\n}\n"],"mappings":"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"}