@runfusion/fusion 0.77.0-beta.1 → 0.77.0-beta.2

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 (137) hide show
  1. package/dist/bin.js +298 -62
  2. package/dist/child-process-worker.js +287 -57
  3. package/dist/client/.vite/manifest.json +262 -262
  4. package/dist/client/assets/{AgentDetailView-Crk9OdF-.js → AgentDetailView-Dew3af1O.js} +1 -1
  5. package/dist/client/assets/{AgentPermissionPolicyEditor-BHQ9RPKE.js → AgentPermissionPolicyEditor-CxPQffiY.js} +1 -1
  6. package/dist/client/assets/{AgentsView-BWOl5J6F.js → AgentsView-Cr4LDGdt.js} +3 -3
  7. package/dist/client/assets/{ChatView-BP8-Are_.js → ChatView-cNYtYx05.js} +1 -1
  8. package/dist/client/assets/{CommandCenter-C1adapJC.js → CommandCenter-DCC90LOP.js} +1 -1
  9. package/dist/client/assets/{DevServerView-B2wexHCU.js → DevServerView-B5b520w6.js} +1 -1
  10. package/dist/client/assets/{DirectoryPicker-C7rzLVyD.js → DirectoryPicker-1JvRd5mC.js} +1 -1
  11. package/dist/client/assets/{DocumentsView-hOjlyCfz.js → DocumentsView-ksK9f7xt.js} +1 -1
  12. package/dist/client/assets/{EvalsView-CdAB_4D_.js → EvalsView-a3LhgRSo.js} +1 -1
  13. package/dist/client/assets/{ExperimentalAgentOnboardingModal-GEypAOXx.js → ExperimentalAgentOnboardingModal-vdVa8h7F.js} +1 -1
  14. package/dist/client/assets/{GoalsView-DW1qRxeP.js → GoalsView-Dz2o91fd.js} +1 -1
  15. package/dist/client/assets/{InsightsView-B5roc0nI.js → InsightsView-De3R_46d.js} +1 -1
  16. package/dist/client/assets/{MemoryView-B0mSfU8f.js → MemoryView-Cagk2wZG.js} +1 -1
  17. package/dist/client/assets/{PiExtensionsManager-ps7tZzok.js → PiExtensionsManager-C9JABO_4.js} +1 -1
  18. package/dist/client/assets/{PluginManager-BrsdP_Zs.js → PluginManager-D2csmdQa.js} +1 -1
  19. package/dist/client/assets/{PullRequestView-CkLoEm3J.js → PullRequestView-DT8vTeVi.js} +1 -1
  20. package/dist/client/assets/{ReportModal-BKdSSVii.js → ReportModal-YMP6V4vu.js} +1 -1
  21. package/dist/client/assets/{ResearchView-OZW77xd4.js → ResearchView-cNsjYu8F.js} +1 -1
  22. package/dist/client/assets/{SecretsView-CawXNPvr.js → SecretsView-BAmf7KyS.js} +1 -1
  23. package/dist/client/assets/{SessionTerminal-C26iOW6f.js → SessionTerminal-BjGcxq22.js} +1 -1
  24. package/dist/client/assets/{SettingsModal-BNWlULmc.js → SettingsModal-C_9RiEI7.js} +1 -1
  25. package/dist/client/assets/{SettingsModal-CfXTMT99.js → SettingsModal-DLfy2e_5.js} +4 -4
  26. package/dist/client/assets/{SettingsTextareaRow-DrXj-rTG.js → SettingsTextareaRow-D8RHSj8b.js} +1 -1
  27. package/dist/client/assets/{SetupWizardModal-Dk4cldse.js → SetupWizardModal-DRJ96pHQ.js} +2 -2
  28. package/dist/client/assets/{SkillsView-NBEe0Qjt.js → SkillsView-DyeCwYit.js} +1 -1
  29. package/dist/client/assets/{WorkflowNodeEditor-CZX7Mlum.js → WorkflowNodeEditor-C5Jc9w_A.js} +3 -3
  30. package/dist/client/assets/{agent-import-generation-TK-wCZBK.js → agent-import-generation-SCPNeF25.js} +1 -1
  31. package/dist/client/assets/{agentsOrgChartLayout-BFC39-tw.js → agentsOrgChartLayout-CqKtNCjy.js} +1 -1
  32. package/dist/client/assets/{architectureDiagram-3BPJPVTR-CYHuEL_5.js → architectureDiagram-3BPJPVTR-Cz2HYn0b.js} +1 -1
  33. package/dist/client/assets/{blockDiagram-GPEHLZMM-BhFoNt44.js → blockDiagram-GPEHLZMM-IimJVscG.js} +1 -1
  34. package/dist/client/assets/{c4Diagram-AAUBKEIU-CQorfIRn.js → c4Diagram-AAUBKEIU-C-xii7zn.js} +1 -1
  35. package/dist/client/assets/channel-D-UHgx8G.js +1 -0
  36. package/dist/client/assets/{chunk-2J33WTMH-DLyODy_m.js → chunk-2J33WTMH-DQ-9Xccu.js} +1 -1
  37. package/dist/client/assets/{chunk-4BX2VUAB-C4ZfqjSh.js → chunk-4BX2VUAB-DmdAX9Bx.js} +1 -1
  38. package/dist/client/assets/{chunk-55IACEB6-CvGg-ubc.js → chunk-55IACEB6-IF2bk5IL.js} +1 -1
  39. package/dist/client/assets/{chunk-727SXJPM-jCzUYl5C.js → chunk-727SXJPM-AeYpIzVu.js} +1 -1
  40. package/dist/client/assets/{chunk-AQP2D5EJ-DYLT-tu8.js → chunk-AQP2D5EJ-DV942DD9.js} +1 -1
  41. package/dist/client/assets/{chunk-FMBD7UC4-CCgBBj1Z.js → chunk-FMBD7UC4-Bt_riVlZ.js} +1 -1
  42. package/dist/client/assets/{chunk-ND2GUHAM-BFdFrIja.js → chunk-ND2GUHAM-CbyjNbXR.js} +1 -1
  43. package/dist/client/assets/{chunk-QZHKN3VN-BUTaQ50G.js → chunk-QZHKN3VN-DqiRniYQ.js} +1 -1
  44. package/dist/client/assets/classDiagram-4FO5ZUOK-Do-8MdAc.js +1 -0
  45. package/dist/client/assets/classDiagram-v2-Q7XG4LA2-Do-8MdAc.js +1 -0
  46. package/dist/client/assets/{cose-bilkent-S5V4N54A-DUbNlc_8.js → cose-bilkent-S5V4N54A-UCvthviL.js} +1 -1
  47. package/dist/client/assets/{dagre-BM42HDAG-Db_33UO6.js → dagre-BM42HDAG-DnMlT1Oi.js} +1 -1
  48. package/dist/client/assets/{dashboard-view-C3PU63FX.js → dashboard-view-C9D0aoRW.js} +1 -1
  49. package/dist/client/assets/dashboard-view-Cz0sp2Wl.js +1 -0
  50. package/dist/client/assets/{dashboard-view-CblIAx4p.js → dashboard-view-JysoyDRX.js} +1 -1
  51. package/dist/client/assets/{dashboard-view-ZGZ80pF3.js → dashboard-view-myjM9tr8.js} +1 -1
  52. package/dist/client/assets/{diagram-2AECGRRQ-BCTLM-Gj.js → diagram-2AECGRRQ-BYFiqXGw.js} +1 -1
  53. package/dist/client/assets/{diagram-5GNKFQAL-CkYPWqJ0.js → diagram-5GNKFQAL-DZU3pSYq.js} +1 -1
  54. package/dist/client/assets/{diagram-KO2AKTUF-7_DJ9TOL.js → diagram-KO2AKTUF-0ybdKuMm.js} +1 -1
  55. package/dist/client/assets/{diagram-LMA3HP47-BAkMEapl.js → diagram-LMA3HP47-Rxbgnbsp.js} +1 -1
  56. package/dist/client/assets/{diagram-OG6HWLK6-DbsQHZ0F.js → diagram-OG6HWLK6-BAU_hTeV.js} +1 -1
  57. package/dist/client/assets/{erDiagram-TEJ5UH35-Bz1kISgV.js → erDiagram-TEJ5UH35-zr8HrwTt.js} +1 -1
  58. package/dist/client/assets/{flowDiagram-I6XJVG4X-fkBLx_JW.js → flowDiagram-I6XJVG4X-CrTw0iFw.js} +1 -1
  59. package/dist/client/assets/{folder-open-l8Z1o786.js → folder-open-DnAlsXXn.js} +1 -1
  60. package/dist/client/assets/{ganttDiagram-6RSMTGT7-BEDb0Uyw.js → ganttDiagram-6RSMTGT7-CCORsxxz.js} +1 -1
  61. package/dist/client/assets/{gitGraphDiagram-PVQCEYII-Cen5lgL7.js → gitGraphDiagram-PVQCEYII-C07wWSjj.js} +1 -1
  62. package/dist/client/assets/index-B_z7OKl4.js +2818 -0
  63. package/dist/client/assets/index-DPo-IAb2.css +1 -0
  64. package/dist/client/assets/{infoDiagram-5YYISTIA-BHqjYX5-.js → infoDiagram-5YYISTIA-hAeccD_4.js} +1 -1
  65. package/dist/client/assets/{ishikawaDiagram-YF4QCWOH-6JaCT-0H.js → ishikawaDiagram-YF4QCWOH-WLCzv-NH.js} +1 -1
  66. package/dist/client/assets/{journeyDiagram-JHISSGLW-C_sUwyWW.js → journeyDiagram-JHISSGLW-ClnasNhy.js} +1 -1
  67. package/dist/client/assets/{kanban-definition-UN3LZRKU-__o8oDEP.js → kanban-definition-UN3LZRKU-CAuARhUz.js} +1 -1
  68. package/dist/client/assets/{mermaid.core-BwF2BF3U.js → mermaid.core-Bh02RvfX.js} +4 -4
  69. package/dist/client/assets/{mindmap-definition-RKZ34NQL-DMhvCrGK.js → mindmap-definition-RKZ34NQL-KNTlbW4V.js} +1 -1
  70. package/dist/client/assets/{pieDiagram-4H26LBE5-Dk0KKcwv.js → pieDiagram-4H26LBE5-BouAfBEw.js} +1 -1
  71. package/dist/client/assets/{puzzle-DcLi6jmv.js → puzzle-CC8Ur3Cy.js} +1 -1
  72. package/dist/client/assets/{quadrantDiagram-W4KKPZXB-DXcBYGil.js → quadrantDiagram-W4KKPZXB-wTdkCUJU.js} +1 -1
  73. package/dist/client/assets/{requirementDiagram-4Y6WPE33--GXgKHm2.js → requirementDiagram-4Y6WPE33-BnjY4Tc_.js} +1 -1
  74. package/dist/client/assets/{sankeyDiagram-5OEKKPKP-fX5WAFdr.js → sankeyDiagram-5OEKKPKP-C-QsS08u.js} +1 -1
  75. package/dist/client/assets/{sequenceDiagram-3UESZ5HK-VQOx2wc2.js → sequenceDiagram-3UESZ5HK-DD36MILE.js} +1 -1
  76. package/dist/client/assets/{shield-alert-DLW2jAvI.js → shield-alert-DcXc56Fy.js} +1 -1
  77. package/dist/client/assets/{standing-instructions-template-RbXYE69q.js → standing-instructions-template-BF5y1BLz.js} +1 -1
  78. package/dist/client/assets/{stateDiagram-AJRCARHV-COZ_NGxp.js → stateDiagram-AJRCARHV-WTq1wQr1.js} +1 -1
  79. package/dist/client/assets/{stateDiagram-v2-BHNVJYJU-CmgPEZwe.js → stateDiagram-v2-BHNVJYJU-eILJdjII.js} +1 -1
  80. package/dist/client/assets/{timeline-definition-PNZ67QCA-eJYKjHOn.js → timeline-definition-PNZ67QCA-PRNwI0NR.js} +1 -1
  81. package/dist/client/assets/{upload-DwRVPojw.js → upload-qH_64O-B.js} +1 -1
  82. package/dist/client/assets/{useDiscoveredSkillsCache-DhScYUmX.js → useDiscoveredSkillsCache-C-GPCNaO.js} +1 -1
  83. package/dist/client/assets/{users-BrTlKeXP.js → users-D2UesRE4.js} +1 -1
  84. package/dist/client/assets/{vennDiagram-CIIHVFJN-Ddt59BdZ.js → vennDiagram-CIIHVFJN-CG2Z4T4O.js} +1 -1
  85. package/dist/client/assets/{wardley-L42UT6IY-C4R0wOO2.js → wardley-L42UT6IY-gO_fGvF9.js} +1 -1
  86. package/dist/client/assets/{wardleyDiagram-YWT4CUSO-BJDm4S7I.js → wardleyDiagram-YWT4CUSO-BkyNpjUb.js} +1 -1
  87. package/dist/client/assets/{xychartDiagram-2RQKCTM6-DlypQnRH.js → xychartDiagram-2RQKCTM6-CMlZsiPq.js} +1 -1
  88. package/dist/client/index.html +2 -2
  89. package/dist/client/version.json +1 -1
  90. package/dist/droid-cli/package.json +1 -1
  91. package/dist/extension.js +290 -59
  92. package/dist/pi-claude-cli/package.json +1 -1
  93. package/dist/plugin-sdk/index.js +29 -2
  94. package/dist/plugins/fusion-plugin-acp-runtime/bundled.js +269 -16
  95. package/dist/plugins/fusion-plugin-acp-runtime/mcp-schema-server.cjs +171 -0
  96. package/dist/plugins/fusion-plugin-acp-runtime/package.json +1 -1
  97. package/dist/plugins/fusion-plugin-claude-runtime/bundled.js +29 -2
  98. package/dist/plugins/fusion-plugin-claude-runtime/package.json +1 -1
  99. package/dist/plugins/fusion-plugin-cli-printing-press/bundled.js +29 -2
  100. package/dist/plugins/fusion-plugin-cli-printing-press/package.json +1 -1
  101. package/dist/plugins/fusion-plugin-compound-engineering/bundled.js +29 -2
  102. package/dist/plugins/fusion-plugin-compound-engineering/package.json +1 -1
  103. package/dist/plugins/fusion-plugin-cursor-runtime/bundled.js +29 -2
  104. package/dist/plugins/fusion-plugin-cursor-runtime/package.json +1 -1
  105. package/dist/plugins/fusion-plugin-dependency-graph/bundled.js +29 -2
  106. package/dist/plugins/fusion-plugin-dependency-graph/package.json +1 -1
  107. package/dist/plugins/fusion-plugin-droid-runtime/bundled.js +29 -2
  108. package/dist/plugins/fusion-plugin-droid-runtime/package.json +1 -1
  109. package/dist/plugins/fusion-plugin-grok-runtime/bundled.js +29 -2
  110. package/dist/plugins/fusion-plugin-grok-runtime/package.json +1 -1
  111. package/dist/plugins/fusion-plugin-hermes-runtime/bundled.js +29 -2
  112. package/dist/plugins/fusion-plugin-hermes-runtime/package.json +1 -1
  113. package/dist/plugins/fusion-plugin-linear-import/bundled.js +29 -2
  114. package/dist/plugins/fusion-plugin-linear-import/package.json +1 -1
  115. package/dist/plugins/fusion-plugin-omp-runtime/bundled.js +29 -2
  116. package/dist/plugins/fusion-plugin-omp-runtime/package.json +1 -1
  117. package/dist/plugins/fusion-plugin-openclaw-runtime/bundled.js +29 -2
  118. package/dist/plugins/fusion-plugin-openclaw-runtime/package.json +1 -1
  119. package/dist/plugins/fusion-plugin-paperclip-runtime/bundled.js +29 -2
  120. package/dist/plugins/fusion-plugin-paperclip-runtime/package.json +1 -1
  121. package/dist/plugins/fusion-plugin-quality/bundled.js +29 -2
  122. package/dist/plugins/fusion-plugin-quality/package.json +1 -1
  123. package/dist/plugins/fusion-plugin-reports/bundled.js +29 -2
  124. package/dist/plugins/fusion-plugin-reports/package.json +1 -1
  125. package/dist/plugins/fusion-plugin-roadmap/bundled.js +29 -2
  126. package/dist/plugins/fusion-plugin-roadmap/package.json +1 -1
  127. package/dist/plugins/fusion-plugin-todos/bundled.js +29 -2
  128. package/dist/plugins/fusion-plugin-todos/package.json +1 -1
  129. package/dist/plugins/fusion-plugin-whatsapp-chat/bundled.js +29 -2
  130. package/dist/plugins/fusion-plugin-whatsapp-chat/package.json +1 -1
  131. package/package.json +1 -1
  132. package/dist/client/assets/channel-DgImfyB5.js +0 -1
  133. package/dist/client/assets/classDiagram-4FO5ZUOK-C3pUNxZS.js +0 -1
  134. package/dist/client/assets/classDiagram-v2-Q7XG4LA2-C3pUNxZS.js +0 -1
  135. package/dist/client/assets/dashboard-view-c3FuWBJb.js +0 -1
  136. package/dist/client/assets/index-BnZB0GkD.js +0 -2818
  137. package/dist/client/assets/index-CPGSlKrd.css +0 -1
@@ -16843,8 +16843,34 @@ function didFallBackToDefault(ir2) {
16843
16843
  async function resolveWorkflowIrForTaskWithProvenance(store, taskId, irCache, selectionCache) {
16844
16844
  let workflowId;
16845
16845
  try {
16846
- const selection = selectionCache?.has(taskId) ? selectionCache.get(taskId) : store.getTaskWorkflowSelectionAsync ? await store.getTaskWorkflowSelectionAsync(taskId) : store.getTaskWorkflowSelection(taskId);
16847
- if (!selectionCache?.has(taskId)) selectionCache?.set(taskId, selection);
16846
+ const isSelectionCached = selectionCache?.has(taskId) ?? false;
16847
+ let selection = isSelectionCached && selectionCache ? selectionCache.get(taskId) : void 0;
16848
+ if (!isSelectionCached) {
16849
+ let inflight;
16850
+ let coalesced = false;
16851
+ if (selectionCache) {
16852
+ inflight = inflightSelectionReads.get(selectionCache);
16853
+ if (!inflight) {
16854
+ inflight = /* @__PURE__ */ new Map();
16855
+ inflightSelectionReads.set(selectionCache, inflight);
16856
+ }
16857
+ const pending = inflight.get(taskId);
16858
+ if (pending) {
16859
+ selection = await pending;
16860
+ coalesced = true;
16861
+ }
16862
+ }
16863
+ if (!coalesced) {
16864
+ const readPromise = store.getTaskWorkflowSelectionAsync ? store.getTaskWorkflowSelectionAsync(taskId) : Promise.resolve(store.getTaskWorkflowSelection(taskId));
16865
+ if (inflight) inflight.set(taskId, readPromise);
16866
+ try {
16867
+ selection = await readPromise;
16868
+ } finally {
16869
+ inflight?.delete(taskId);
16870
+ }
16871
+ }
16872
+ selectionCache?.set(taskId, selection);
16873
+ }
16848
16874
  workflowId = selection?.workflowId;
16849
16875
  } catch {
16850
16876
  return { ir: defaultCodingWorkflowIr(), source: "default" };
@@ -16868,13 +16894,14 @@ async function isTaskTerminalNodeIdAsync(store, taskId, nodeId) {
16868
16894
  }
16869
16895
  return nodeId === "end";
16870
16896
  }
16871
- var FELL_BACK_TO_DEFAULT;
16897
+ var inflightSelectionReads, FELL_BACK_TO_DEFAULT;
16872
16898
  var init_workflow_ir_resolver = __esm({
16873
16899
  "../core/src/workflows/workflow-ir-resolver.ts"() {
16874
16900
  "use strict";
16875
16901
  init_builtin_workflows();
16876
16902
  init_workflow_ir();
16877
16903
  init_workflow_prompt_overrides();
16904
+ inflightSelectionReads = /* @__PURE__ */ new WeakMap();
16878
16905
  FELL_BACK_TO_DEFAULT = /* @__PURE__ */ Symbol.for("fusion.workflowIr.fellBackToDefault");
16879
16906
  }
16880
16907
  });
@@ -36495,9 +36522,12 @@ async function markApprovalRequestCompleted(layer, requestId, input) {
36495
36522
  });
36496
36523
  }
36497
36524
  async function getApprovalAuditHistory(handle2, requestId, projectId2) {
36498
- const rows = await handle2.select().from(project_exports.approvalRequestAuditEvents).where(and(eq(project_exports.approvalRequestAuditEvents.requestId, requestId), projectScopeFor(project_exports.approvalRequestAuditEvents.projectId, projectId2))).orderBy(
36499
- sql`${project_exports.approvalRequestAuditEvents.createdAt} ASC, ${project_exports.approvalRequestAuditEvents.id} ASC`
36500
- );
36525
+ const eventType = project_exports.approvalRequestAuditEvents.eventType;
36526
+ const lifecycleRank = sql`CASE ${eventType} ${sql.join(
36527
+ APPROVAL_REQUEST_AUDIT_EVENT_TYPES.map((type, rank) => sql`WHEN ${type} THEN ${rank}`),
36528
+ sql` `
36529
+ )} ELSE ${APPROVAL_REQUEST_AUDIT_EVENT_TYPES.length} END`;
36530
+ const rows = await handle2.select().from(project_exports.approvalRequestAuditEvents).where(and(eq(project_exports.approvalRequestAuditEvents.requestId, requestId), projectScopeFor(project_exports.approvalRequestAuditEvents.projectId, projectId2))).orderBy(sql`${project_exports.approvalRequestAuditEvents.createdAt} ASC, ${lifecycleRank} ASC, ${project_exports.approvalRequestAuditEvents.id} ASC`);
36501
36531
  return rows.map((row) => rowToAuditEvent(row));
36502
36532
  }
36503
36533
  var init_async_approval_request_store = __esm({
@@ -143585,9 +143615,9 @@ function mergeParkedColumns(base, lanes) {
143585
143615
  terminal: /* @__PURE__ */ new Set([...base.terminal, ...lanes.terminal ?? [], complete, archived])
143586
143616
  };
143587
143617
  }
143588
- async function resolveTaskParkedColumns(store, taskId) {
143618
+ async function resolveTaskParkedColumns(store, taskId, selectionCache) {
143589
143619
  try {
143590
- const ir2 = await resolveWorkflowIrForTask(store, taskId);
143620
+ const ir2 = await resolveWorkflowIrForTask(store, taskId, void 0, selectionCache);
143591
143621
  const l = resolveLifecycleColumns(ir2);
143592
143622
  const complete = l?.complete ?? LEGACY_PARKED_COLUMNS2.complete;
143593
143623
  const archived = l?.archived ?? LEGACY_PARKED_COLUMNS2.archived;
@@ -143832,6 +143862,7 @@ var init_scheduler = __esm({
143832
143862
  }
143833
143863
  });
143834
143864
  this.store.on("task:moved", async ({ task, from, to: to2, source, lanes }) => {
143865
+ const movedSelectionCache = /* @__PURE__ */ new Map();
143835
143866
  this.lastAutoClaimFingerprint.set(task.id, computeAutoClaimFingerprint(task));
143836
143867
  const parked = mergeParkedColumns(LEGACY_PARKED_COLUMNS2, lanes);
143837
143868
  if (from === parked.hold || to2 === parked.hold) {
@@ -143887,7 +143918,7 @@ var init_scheduler = __esm({
143887
143918
  });
143888
143919
  }
143889
143920
  }
143890
- const resolvedParked = mergeParkedColumns(await resolveTaskParkedColumns(this.store, task.id), lanes);
143921
+ const resolvedParked = mergeParkedColumns(await resolveTaskParkedColumns(this.store, task.id, movedSelectionCache), lanes);
143891
143922
  if (resolvedParked.terminal.has(to2)) {
143892
143923
  try {
143893
143924
  const settings = await this.store.getSettings();
@@ -143953,6 +143984,7 @@ var init_scheduler = __esm({
143953
143984
  }
143954
143985
  });
143955
143986
  this.store.on("task:updated", (task, meta2) => {
143987
+ const updatedSelectionCache = /* @__PURE__ */ new Map();
143956
143988
  const nextFingerprint = computeAutoClaimFingerprint(task);
143957
143989
  const previousFingerprint = this.lastAutoClaimFingerprint.get(task.id);
143958
143990
  if (!previousFingerprint || previousFingerprint !== nextFingerprint) {
@@ -143983,7 +144015,7 @@ var init_scheduler = __esm({
143983
144015
  });
143984
144016
  }
143985
144017
  void (async () => {
143986
- const unpausedParked = await resolveTaskParkedColumns(this.store, task.id);
144018
+ const unpausedParked = await resolveTaskParkedColumns(this.store, task.id, updatedSelectionCache);
143987
144019
  if (this.running && unpausedParked.wake.has(task.column)) {
143988
144020
  schedulerLog.log(`Task ${task.id} unpaused \u2014 triggering scheduling`);
143989
144021
  void this.schedule();
@@ -143995,7 +144027,7 @@ var init_scheduler = __esm({
143995
144027
  } else if (this.planningTaskIds.has(task.id)) {
143996
144028
  this.planningTaskIds.delete(task.id);
143997
144029
  void (async () => {
143998
- const planningParked = await resolveTaskParkedColumns(this.store, task.id);
144030
+ const planningParked = await resolveTaskParkedColumns(this.store, task.id, updatedSelectionCache);
143999
144031
  if (this.running && !task.status && !task.paused && !task.userPaused && planningParked.wake.has(task.column)) {
144000
144032
  schedulerLog.log(`Task ${task.id} finished planning \u2014 triggering scheduling`);
144001
144033
  void this.schedule();
@@ -144008,7 +144040,7 @@ var init_scheduler = __esm({
144008
144040
  } else if (!task.status && !task.paused && !task.userPaused && (this.approvalHeldTaskIds.delete(task.id) || (Boolean(task.approvedPlanFingerprint) || task.workflowStepResults?.some(isPlanReviewSatisfied) === true) && !this.approvalReleasedTaskIds.has(task.id))) {
144009
144041
  this.approvalReleasedTaskIds.add(task.id);
144010
144042
  void (async () => {
144011
- const approvalParked = await resolveTaskParkedColumns(this.store, task.id);
144043
+ const approvalParked = await resolveTaskParkedColumns(this.store, task.id, updatedSelectionCache);
144012
144044
  if (this.running && !task.status && !task.paused && !task.userPaused && approvalParked.wake.has(task.column)) {
144013
144045
  schedulerLog.log(`Task ${task.id} plan approval cleared \u2014 triggering scheduling`);
144014
144046
  void this.schedule();
@@ -144047,7 +144079,8 @@ var init_scheduler = __esm({
144047
144079
  if (settings.globalPause || settings.enginePaused) {
144048
144080
  return;
144049
144081
  }
144050
- const deletedParked = await resolveTaskParkedColumns(this.store, task.id);
144082
+ const deletedSelectionCache = /* @__PURE__ */ new Map();
144083
+ const deletedParked = await resolveTaskParkedColumns(this.store, task.id, deletedSelectionCache);
144051
144084
  const deletedWipColumns = await resolveProjectColumnsForRoles(this.store, ["countsTowardWip"]);
144052
144085
  const todoTasks = await this.store.listTasks({ column: deletedParked.hold, slim: true });
144053
144086
  const inProgressById = /* @__PURE__ */ new Map();
@@ -144256,7 +144289,16 @@ var init_scheduler = __esm({
144256
144289
  }
144257
144290
  }
144258
144291
  async transitionQueuedEpisode(task, input) {
144259
- return (await this.store.transitionQueuedEpisode(task.id, input)).appended;
144292
+ if (typeof this.store.transitionQueuedEpisode === "function") {
144293
+ return (await this.store.transitionQueuedEpisode(task.id, input)).appended;
144294
+ }
144295
+ await this.store.updateTask(task.id, {
144296
+ status: "queued",
144297
+ blockedBy: input.blockedBy ?? null,
144298
+ overlapBlockedBy: input.overlapBlockedBy ?? null
144299
+ });
144300
+ await this.logDispatchQueuedReason(task.id, input.action);
144301
+ return true;
144260
144302
  }
144261
144303
  async logDispatchQueuedReason(taskId, reason, memoKey) {
144262
144304
  const key = `${taskId}:${memoKey ?? reason}`;
@@ -144308,13 +144350,14 @@ var init_scheduler = __esm({
144308
144350
  }
144309
144351
  async emitHighOverlapFanoutWarnings(tasks2) {
144310
144352
  const escalationIrCache = /* @__PURE__ */ new Map();
144353
+ const escalationSelectionCache = /* @__PURE__ */ new Map();
144311
144354
  const escalationByTaskId = /* @__PURE__ */ new Map();
144312
144355
  const escalationColumns = /* @__PURE__ */ new Set();
144313
144356
  const holdByTaskId = /* @__PURE__ */ new Map();
144314
144357
  const terminalByTaskId = /* @__PURE__ */ new Map();
144315
144358
  const blockerReviewColumns = /* @__PURE__ */ new Set();
144316
144359
  for (const task of tasks2) {
144317
- const ir2 = await resolveWorkflowIrForTask(this.store, task.id, escalationIrCache).catch(() => void 0);
144360
+ const ir2 = await resolveWorkflowIrForTask(this.store, task.id, escalationIrCache, escalationSelectionCache).catch(() => void 0);
144318
144361
  if (!ir2) continue;
144319
144362
  for (const id of columnsWithFlag(ir2, "countsTowardWip")) escalationColumns.add(id);
144320
144363
  for (const id of columnsWithFlag(ir2, "mergeOrchestration")) escalationColumns.add(id);
@@ -144384,9 +144427,10 @@ var init_scheduler = __esm({
144384
144427
  if (!agentStore) return;
144385
144428
  const runningAgents = await agentStore.listAgents({ state: "running", includeEphemeral: false });
144386
144429
  const linkedAgents = runningAgents.filter((agent) => agent.taskId === taskId);
144430
+ const rollbackSelectionCache = /* @__PURE__ */ new Map();
144387
144431
  for (const agent of linkedAgents) {
144388
144432
  const activeRun = await agentStore.getActiveHeartbeatRun?.(agent.id);
144389
- const rollbackParked = await resolveTaskParkedColumns(this.store, taskId);
144433
+ const rollbackParked = await resolveTaskParkedColumns(this.store, taskId, rollbackSelectionCache);
144390
144434
  const proof = evaluateParkedAgentTaskLink({
144391
144435
  agent,
144392
144436
  linkedTask: { column: rollbackParked.hold },
@@ -144434,11 +144478,12 @@ var init_scheduler = __esm({
144434
144478
  const repo = getCurrentRepo(this.store.getRootDir());
144435
144479
  if (!repo) return;
144436
144480
  const hydrationIrCache = /* @__PURE__ */ new Map();
144481
+ const hydrationSelectionCache = /* @__PURE__ */ new Map();
144437
144482
  for (const task of tasks2) {
144438
144483
  if (!task.prInfo) continue;
144439
144484
  let flags;
144440
144485
  try {
144441
- const ir2 = await resolveWorkflowIrForTask(this.store, task.id, hydrationIrCache);
144486
+ const ir2 = await resolveWorkflowIrForTask(this.store, task.id, hydrationIrCache, hydrationSelectionCache);
144442
144487
  const column = ir2.columns?.find((candidate) => candidate.id === task.column);
144443
144488
  if (column) flags = resolveColumnFlags(column);
144444
144489
  } catch {
@@ -145367,19 +145412,20 @@ var init_scheduler = __esm({
145367
145412
  return;
145368
145413
  }
145369
145414
  const sliceIdBeforeUpdate = feature.sliceId;
145370
- const featureSlice = await missionStore.getSlice(feature.sliceId);
145371
- const milestone = featureSlice ? await missionStore.getMilestone(featureSlice.milestoneId) : void 0;
145372
- const missionId = task.missionId ?? milestone?.missionId;
145373
- if (!missionId) {
145374
- schedulerLog.warn(`Task ${taskId} feature ${feature.id} has no resolvable mission; skipping reconciliation`);
145375
- return;
145376
- }
145377
- if (missionId !== task.missionId) {
145378
- try {
145415
+ let missionId = task.missionId;
145416
+ try {
145417
+ if (!missionId && typeof missionStore.getSlice === "function") {
145418
+ const featureSlice = await missionStore.getSlice(feature.sliceId);
145419
+ const milestone = featureSlice && typeof missionStore.getMilestone === "function" ? await missionStore.getMilestone(featureSlice.milestoneId) : void 0;
145420
+ missionId = milestone?.missionId;
145421
+ }
145422
+ if (!missionId) {
145423
+ schedulerLog.warn(`Task ${taskId} feature ${feature.id} has no resolvable mission; skipping reconciliation`);
145424
+ } else if (missionId !== task.missionId) {
145379
145425
  await reconcileMissionState({ taskStore: this.store, missionStore }, { missionId, source: "task-move" });
145380
- } catch (error2) {
145381
- schedulerLog.warn(`Mission reconciliation failed after resolving task ${taskId}; continuing mission completion handling:`, error2);
145382
145426
  }
145427
+ } catch (error2) {
145428
+ schedulerLog.warn(`Task ${taskId} feature ${feature.id} mission reconcile skipped (${String(error2)}); continuing to completion check`);
145383
145429
  }
145384
145430
  let completionFlags;
145385
145431
  try {
@@ -148355,17 +148401,40 @@ var init_auth_storage = __esm({
148355
148401
  async getApiKey(provider, instance) {
148356
148402
  return resolveStoredCredentialApiKey(provider, instance ? this.getInstance(instance) : this.get(provider));
148357
148403
  }
148404
+ /*
148405
+ FNXC:ProviderAuth 2026-08-18-04:40:
148406
+ MODIFY MUST BE ABLE TO CREATE. This is the seam pi persists a COMPLETED LOGIN through
148407
+ (`Models.login` -> `credentials.modify(provider.id, ...)` in pi-ai's models.js), not just the seam
148408
+ it refreshes an existing token through.
148409
+
148410
+ It used to resolve its write target with `creating: false` and then bail —
148411
+ `if (!target || !this.credential(target, current)) return { changed: false }` — whenever the
148412
+ provider had no credential row yet. The callback was never invoked, nothing was written, and pi's
148413
+ login resolved as a SUCCESS. So a first-ever login for a provider could not be saved: the browser
148414
+ flow completed, the token exchange succeeded, the lock file was taken and released, `auth.json`
148415
+ stayed `{}`, and the dashboard's poll saw `authenticated: false` and reported the useless "Login
148416
+ did not complete. Please try again."
148417
+
148418
+ It looked provider-specific and environment-specific for the worst possible reason: it only
148419
+ reproduces on a store with NO existing row, so every developer and every long-lived install — where
148420
+ a row already exists and this path is a plain refresh — works flawlessly, while every fresh install
148421
+ (a new container, a new machine, a wiped `~/.fusion`) can never complete its first login.
148422
+
148423
+ Create when absent, update when present. A callback returning undefined still writes nothing, so
148424
+ pi's refresh-bails-out behaviour is unchanged.
148425
+ */
148358
148426
  async modify(provider, fn2) {
148359
148427
  this.assertRefFromKey(provider);
148360
148428
  return this.withLock(async (current) => {
148361
- const target = this.resolveWriteTarget(provider, current, false);
148362
- if (!target || !this.credential(target, current)) return { result: void 0, changed: false };
148363
- const next = await fn2(this.credential(target, current));
148429
+ const target = this.resolveWriteTarget(provider, current, true);
148430
+ if (!target) return { result: void 0, changed: false };
148431
+ const existing = this.credential(target, current);
148432
+ const next = await fn2(existing);
148364
148433
  if (next !== void 0) {
148365
148434
  current[formatProviderInstanceKey(target)] = next;
148366
148435
  return { result: next, changed: true };
148367
148436
  }
148368
- return { result: this.credential(target, current), changed: false };
148437
+ return { result: existing, changed: false };
148369
148438
  });
148370
148439
  }
148371
148440
  getOAuthProviders() {
@@ -148378,11 +148447,24 @@ var init_auth_storage = __esm({
148378
148447
  if (!this.modelRuntime) throw new Error("OAuth login requires a ModelRuntime-backed Fusion auth storage");
148379
148448
  const executionProvider = toExecutionModelProviderId(provider);
148380
148449
  const legacy = callbacks;
148381
- const interaction = { signal: legacy.signal, prompt: async (prompt) => legacy.onPrompt?.({ message: prompt.message, placeholder: "placeholder" in prompt ? prompt.placeholder : void 0 }) ?? "", notify: (event) => {
148382
- if (event.type === "auth_url") legacy.onAuth?.({ url: event.url, instructions: event.instructions });
148383
- else if (event.type === "device_code") legacy.onDeviceCode?.(event);
148384
- else if (event.type === "progress") legacy.onProgress?.(event.message);
148385
- } };
148450
+ const interaction = {
148451
+ signal: legacy.signal,
148452
+ prompt: async (prompt) => {
148453
+ if (prompt.type === "select") {
148454
+ const chosen = await legacy.onSelect?.({ message: prompt.message, options: prompt.options });
148455
+ return chosen ?? prompt.options[0]?.id ?? "";
148456
+ }
148457
+ if (prompt.type === "manual_code" && legacy.onManualCodeInput) {
148458
+ return await legacy.onManualCodeInput();
148459
+ }
148460
+ return await legacy.onPrompt?.({ message: prompt.message, placeholder: "placeholder" in prompt ? prompt.placeholder : void 0 }) ?? "";
148461
+ },
148462
+ notify: (event) => {
148463
+ if (event.type === "auth_url") legacy.onAuth?.({ url: event.url, instructions: event.instructions });
148464
+ else if (event.type === "device_code") legacy.onDeviceCode?.(event);
148465
+ else if (event.type === "progress") legacy.onProgress?.(event.message);
148466
+ }
148467
+ };
148386
148468
  await this.modelRuntime.login(executionProvider, "oauth", interaction);
148387
148469
  this.reload();
148388
148470
  }
@@ -186396,7 +186478,6 @@ var init_notifier = __esm({
186396
186478
  }
186397
186479
  notifyGridlock(event) {
186398
186480
  if (event === null) {
186399
- this.lastGridlockNotificationAt = null;
186400
186481
  return;
186401
186482
  }
186402
186483
  if (!this.config.enabled || !this.config.topic || !this.isEventEnabled("gridlock")) return;
@@ -187533,6 +187614,63 @@ var init_agent_user_comments = __esm({
187533
187614
  }
187534
187615
  });
187535
187616
 
187617
+ // ../engine/src/git-identity.ts
187618
+ function slugifyAgentEmailLocalPart(value) {
187619
+ return value.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 64);
187620
+ }
187621
+ function resolveCommitIdentity(input = {}) {
187622
+ const { agent, settings } = input;
187623
+ if (settings?.commitAuthorEnabled === false) {
187624
+ return void 0;
187625
+ }
187626
+ const configuredName = settings?.commitAuthorName?.trim();
187627
+ const configuredEmail = settings?.commitAuthorEmail?.trim();
187628
+ if (configuredName && configuredEmail) {
187629
+ return { name: configuredName, email: configuredEmail };
187630
+ }
187631
+ const agentName = agent?.name?.trim() || agent?.id?.trim() || "";
187632
+ if (agentName) {
187633
+ const localPart = slugifyAgentEmailLocalPart(agentName);
187634
+ if (localPart) {
187635
+ return {
187636
+ name: configuredName || `${agentName} (Fusion)`,
187637
+ email: configuredEmail || `${localPart}@agents.fusion.local`
187638
+ };
187639
+ }
187640
+ }
187641
+ return {
187642
+ name: configuredName || FUSION_FALLBACK_IDENTITY.name,
187643
+ email: configuredEmail || FUSION_FALLBACK_IDENTITY.email
187644
+ };
187645
+ }
187646
+ function commitIdentityEnv(identity) {
187647
+ if (!identity) {
187648
+ return {};
187649
+ }
187650
+ return {
187651
+ GIT_AUTHOR_NAME: identity.name,
187652
+ GIT_AUTHOR_EMAIL: identity.email,
187653
+ GIT_COMMITTER_NAME: identity.name,
187654
+ GIT_COMMITTER_EMAIL: identity.email
187655
+ };
187656
+ }
187657
+ function commitIdentityArgs(identity) {
187658
+ if (!identity) {
187659
+ return [];
187660
+ }
187661
+ return ["-c", `user.name=${identity.name}`, "-c", `user.email=${identity.email}`];
187662
+ }
187663
+ var FUSION_FALLBACK_IDENTITY;
187664
+ var init_git_identity = __esm({
187665
+ "../engine/src/git-identity.ts"() {
187666
+ "use strict";
187667
+ FUSION_FALLBACK_IDENTITY = {
187668
+ name: "Fusion",
187669
+ email: "noreply@runfusion.ai"
187670
+ };
187671
+ }
187672
+ });
187673
+
187536
187674
  // ../engine/src/sandbox/bubblewrap-detect.ts
187537
187675
  import { exec as exec20 } from "node:child_process";
187538
187676
  import { promisify as promisify30 } from "node:util";
@@ -192765,8 +192903,12 @@ import { createHash as createHash29 } from "node:crypto";
192765
192903
  import { join as join72, resolve as resolve44 } from "node:path";
192766
192904
  import { hostname as hostname3 } from "node:os";
192767
192905
  import { Type as Type4 } from "typebox";
192768
- function mergerCommitEnv() {
192769
- return { ...process.env, [IDENTITY_GUARD_BYPASS_ENV]: "1" };
192906
+ function mergerCommitEnv(identity) {
192907
+ return {
192908
+ ...process.env,
192909
+ [IDENTITY_GUARD_BYPASS_ENV]: "1",
192910
+ ...commitIdentityEnv(identity ?? resolveCommitIdentity())
192911
+ };
192770
192912
  }
192771
192913
  async function resolveMergerLifecycleColumn(store, taskId, which) {
192772
192914
  const fallback = which === "complete" ? "done" : which === "merge" ? "in-review" : "todo";
@@ -200742,6 +200884,7 @@ var init_merger = __esm({
200742
200884
  init_worktree_hooks();
200743
200885
  init_effective_settings();
200744
200886
  init_agent_user_comments();
200887
+ init_git_identity();
200745
200888
  init_verification_utils();
200746
200889
  init_sandbox();
200747
200890
  init_verification_utils();
@@ -211629,7 +211772,9 @@ function isLegacyCompleteColumnForWorkspaceTeardown(column) {
211629
211772
  }
211630
211773
  var worktreeMetadataReconcileLog = createLogger("worktree-metadata-reconcile");
211631
211774
  var yieldEventLoop2 = () => new Promise((resolve48) => setImmediateCb2(resolve48));
211775
+ var DONE_METADATA_REPAIR_CAP = 25;
211632
211776
  var DONE_TASK_INTEGRITY_SWEEP_LIMIT = 50;
211777
+ var GIT_WORKTREE_CHURN_INTERVAL_MS = 60 * 60 * 1e3;
211633
211778
  var BOARD_STALL_NOTIFICATION_COOLDOWN_MS = 60 * 6e4;
211634
211779
  var DB_CORRUPTION_NOTIFICATION_COOLDOWN_MS = 60 * 60 * 1e3;
211635
211780
  var PHANTOM_EXECUTOR_BINDING_AGE_MULTIPLIER = 3;
@@ -211733,7 +211878,10 @@ var SelfHealingManager = class _SelfHealingManager extends SelfHealingGitEvidenc
211733
211878
  lastUnpauseAt = 0;
211734
211879
  // ── Maintenance timer ───────────────────────────────────────────────
211735
211880
  maintenanceInterval = null;
211881
+ maintenanceStarting = false;
211736
211882
  maintenanceRunning = false;
211883
+ /** Last wall-clock a batch-1 git-churn pass ran on this manager (coarse-cadence gate). */
211884
+ lastGitWorktreeChurnAt = 0;
211737
211885
  /** In-process belt only; the durable apply token remains the cross-process fence. */
211738
211886
  autoRecoverTerminalFailuresInFlight = false;
211739
211887
  // ── Event listener cleanup ──────────────────────────────────────────
@@ -212631,6 +212779,7 @@ var SelfHealingManager = class _SelfHealingManager extends SelfHealingGitEvidenc
212631
212779
  }
212632
212780
  // ── Auto-unpause ───────────────────────────────────────────────────
212633
212781
  onSettingsUpdated(settings, previous) {
212782
+ this.syncMaintenanceOnRefresh(settings);
212634
212783
  if (!previous.globalPause && settings.globalPause) {
212635
212784
  if (!settings.autoUnpauseEnabled) {
212636
212785
  log34.log("Global pause activated \u2014 auto-unpause disabled, requires manual intervention");
@@ -212814,6 +212963,7 @@ var SelfHealingManager = class _SelfHealingManager extends SelfHealingGitEvidenc
212814
212963
  try {
212815
212964
  await this.store.moveTask(taskId, await resolveReboundTargetForTask(this.store, taskId), {
212816
212965
  preserveProgress: true,
212966
+ preserveWorktree: true,
212817
212967
  preserveStatus: true,
212818
212968
  // #1411: backward recovery — skip order-derived adjacency.
212819
212969
  moveSource: "engine",
@@ -212894,7 +213044,23 @@ var SelfHealingManager = class _SelfHealingManager extends SelfHealingGitEvidenc
212894
213044
  }
212895
213045
  // ── Periodic maintenance ──────────────────────────────────────────
212896
213046
  async startMaintenance() {
213047
+ if (this.maintenanceInterval) return;
213048
+ if (this.maintenanceStarting) return;
213049
+ this.maintenanceStarting = true;
213050
+ try {
213051
+ await this.armMaintenanceInternal();
213052
+ } finally {
213053
+ this.maintenanceStarting = false;
213054
+ }
213055
+ }
213056
+ async armMaintenanceInternal() {
213057
+ if (this.maintenanceInterval) return;
212897
213058
  const settings = await this.store.getSettings();
213059
+ if (this.maintenanceInterval) return;
213060
+ if (settings.globalPause || settings.enginePaused) {
213061
+ log34.debug("Periodic maintenance not armed \u2014 engine/global pause is active");
213062
+ return;
213063
+ }
212898
213064
  const intervalMs = settings.maintenanceIntervalMs ?? 9e5;
212899
213065
  if (intervalMs <= 0) {
212900
213066
  log34.debug("Periodic maintenance disabled (maintenanceIntervalMs <= 0)");
@@ -212905,6 +213071,38 @@ var SelfHealingManager = class _SelfHealingManager extends SelfHealingGitEvidenc
212905
213071
  void this.runMaintenance();
212906
213072
  }, intervalMs);
212907
213073
  }
213074
+ /*
213075
+ * FNXC:PauseGatedMaintenance 2026-08-13-03:08 (RUFU-076):
213076
+ * Keep the periodic maintenance timer in sync with the engine/global pause state. `onSettingsUpdated`
213077
+ * calls this on every settings:updated event; pausing clears the armed timer (stopping the git churn),
213078
+ * unpausing (re)arms it idempotently via `startMaintenance`'s guard. This is the only writer of the
213079
+ * pause->stop-maintenance transition besides `stop()` teardown.
213080
+ */
213081
+ /*
213082
+ * FNXC:GitWorktreeChurnCadenceCoarsen 2026-08-13-04:05 (RUFU-076):
213083
+ * Coarse-cadence gate for batch-1 git-churn steps. Returns true only when a full coarse window
213084
+ * (hourly) has elapsed since the last batch-1 git-worktree pass, so an idle/healthy active project
213085
+ * does not re-run `git worktree prune` / `git worktree list` / `git branch` on every maintenance
213086
+ * interval. Note the shared timestamp also bounds cleanup-orphaned-branches and enforce-worktree-cap
213087
+ * even though they live further down batch-1.
213088
+ */
213089
+ isGitWorktreeChurnDue() {
213090
+ return Date.now() - this.lastGitWorktreeChurnAt >= GIT_WORKTREE_CHURN_INTERVAL_MS;
213091
+ }
213092
+ syncMaintenanceOnRefresh(settings) {
213093
+ const paused = settings.globalPause || settings.enginePaused;
213094
+ if (paused) {
213095
+ if (this.maintenanceInterval) {
213096
+ clearInterval(this.maintenanceInterval);
213097
+ this.maintenanceInterval = null;
213098
+ log34.debug("Periodic maintenance paused \u2014 cleared maintenance timer");
213099
+ }
213100
+ return;
213101
+ }
213102
+ if (!this.maintenanceInterval) {
213103
+ void this.startMaintenance();
213104
+ }
213105
+ }
212908
213106
  /*
212909
213107
  FNXC:CrossProcessDeleteObservation 2026-08-01-11:39:
212910
213108
  Periodic self-healing owns outbox retention. Each already-open PostgreSQL project is gated to
@@ -213241,17 +213439,26 @@ var SelfHealingManager = class _SelfHealingManager extends SelfHealingGitEvidenc
213241
213439
  log34.debug("Maintenance cycle starting");
213242
213440
  try {
213243
213441
  const settings = await this.store.getSettings();
213442
+ const maintenancePaused = settings.globalPause || settings.enginePaused;
213443
+ const gitWorktreeChurnDue = this.isGitWorktreeChurnDue();
213444
+ if (!maintenancePaused && gitWorktreeChurnDue) {
213445
+ this.lastGitWorktreeChurnAt = Date.now();
213446
+ }
213244
213447
  const batch1Fns = [
213245
- { name: "prune-worktrees", fn: () => this.pruneWorktrees() },
213448
+ {
213449
+ name: "prune-worktrees",
213450
+ fn: () => maintenancePaused || !gitWorktreeChurnDue ? Promise.resolve(0) : this.pruneWorktrees()
213451
+ },
213246
213452
  {
213247
213453
  name: "prune-task-lifecycle-events",
213248
213454
  fn: async () => this.pruneTaskLifecycleEventsForMaintenance()
213249
213455
  },
213250
213456
  { name: "prune-github-check-states", fn: async () => this.pruneGitHubCheckStatesForMaintenance() },
213251
- { name: "cleanup-orphans", fn: () => this.cleanupOrphans() },
213457
+ { name: "cleanup-orphans", fn: () => maintenancePaused || !gitWorktreeChurnDue ? Promise.resolve(0) : this.cleanupOrphans() },
213252
213458
  {
213253
213459
  name: "cleanup-stale-temp-merge-worktrees",
213254
213460
  fn: async () => {
213461
+ if (maintenancePaused || !gitWorktreeChurnDue) return 0;
213255
213462
  const cleaned = await this.cleanupStaleTempMergeWorktrees();
213256
213463
  if (cleaned > 0) {
213257
213464
  log34.debug(`Cleaned ${cleaned} stale AI merge temp worktree(s)`);
@@ -213259,7 +213466,10 @@ var SelfHealingManager = class _SelfHealingManager extends SelfHealingGitEvidenc
213259
213466
  return cleaned;
213260
213467
  }
213261
213468
  },
213262
- { name: "cleanup-orphaned-branches", fn: () => this.cleanupOrphanedBranches() },
213469
+ {
213470
+ name: "cleanup-orphaned-branches",
213471
+ fn: () => maintenancePaused || !gitWorktreeChurnDue ? Promise.resolve(0) : this.cleanupOrphanedBranches()
213472
+ },
213263
213473
  {
213264
213474
  name: "reconcile-orphaned-task-dirs",
213265
213475
  fn: async () => {
@@ -213367,7 +213577,7 @@ var SelfHealingManager = class _SelfHealingManager extends SelfHealingGitEvidenc
213367
213577
  },
213368
213578
  { name: "fts-maintenance", fn: () => this.maintainTaskFts() },
213369
213579
  { name: "checkpoint-wal", fn: () => Promise.resolve(this.checkpointWal()) },
213370
- { name: "enforce-worktree-cap", fn: () => this.enforceWorktreeCap() }
213580
+ { name: "enforce-worktree-cap", fn: () => maintenancePaused || !gitWorktreeChurnDue ? Promise.resolve(0) : this.enforceWorktreeCap() }
213371
213581
  ];
213372
213582
  for (const fn2 of batch1Fns) {
213373
213583
  try {
@@ -214649,6 +214859,7 @@ var SelfHealingManager = class _SelfHealingManager extends SelfHealingGitEvidenc
214649
214859
  moveSource: "engine",
214650
214860
  // #1411: backward recovery — skip order-derived adjacency.
214651
214861
  recoveryRehome: true,
214862
+ // worktree-discard-intended: reclaim proved the branch already merged / has zero unique commits; the checkout holds nothing worth keeping and worktree was explicitly nulled above.
214652
214863
  preserveProgress: true,
214653
214864
  preserveResumeState: true
214654
214865
  });
@@ -214739,6 +214950,7 @@ var SelfHealingManager = class _SelfHealingManager extends SelfHealingGitEvidenc
214739
214950
  moveSource: "engine",
214740
214951
  // #1411: backward recovery — skip order-derived adjacency.
214741
214952
  recoveryRehome: true,
214953
+ // worktree-discard-intended: reclaim proved the branch already merged / has zero unique commits; the checkout holds nothing worth keeping and worktree was explicitly nulled above.
214742
214954
  preserveProgress: true,
214743
214955
  preserveResumeState: true
214744
214956
  });
@@ -215535,7 +215747,18 @@ var SelfHealingManager = class _SelfHealingManager extends SelfHealingGitEvidenc
215535
215747
  const withUniqueWork = existingCandidates.filter((candidate) => candidate.aheadCount > 0);
215536
215748
  if (withUniqueWork.length === 1) {
215537
215749
  const selected = withUniqueWork[0];
215538
- const patch = { branch: selected.branch, worktree: null };
215750
+ let preservedWorktree = false;
215751
+ if (task.worktree && existsSync58(task.worktree)) {
215752
+ try {
215753
+ const { stdout } = await execAsync18("git rev-parse --abbrev-ref HEAD", {
215754
+ cwd: task.worktree,
215755
+ timeout: 3e4
215756
+ });
215757
+ preservedWorktree = stdout.trim() === selected.branch;
215758
+ } catch {
215759
+ }
215760
+ }
215761
+ const patch = preservedWorktree ? { branch: selected.branch } : { branch: selected.branch, worktree: null };
215539
215762
  if (!task.baseCommitSha) {
215540
215763
  const derivedBaseCommit = (await execAsync18(
215541
215764
  `git merge-base ${shellQuote2(integrationBase)} ${shellQuote2(selected.branch)}`,
@@ -215574,7 +215797,8 @@ var SelfHealingManager = class _SelfHealingManager extends SelfHealingGitEvidenc
215574
215797
  aheadCount: selected.aheadCount,
215575
215798
  integrationBase,
215576
215799
  source: "auto-rebind-in-review",
215577
- previousBranch: task.branch ?? null
215800
+ previousBranch: task.branch ?? null,
215801
+ preservedWorktree
215578
215802
  }
215579
215803
  });
215580
215804
  result.repaired++;
@@ -215584,7 +215808,8 @@ var SelfHealingManager = class _SelfHealingManager extends SelfHealingGitEvidenc
215584
215808
  branch: selected.branch,
215585
215809
  aheadCount: selected.aheadCount,
215586
215810
  integrationBase,
215587
- previousBranch: task.branch ?? null
215811
+ previousBranch: task.branch ?? null,
215812
+ preservedWorktree
215588
215813
  });
215589
215814
  continue;
215590
215815
  }
@@ -217116,7 +217341,8 @@ var SelfHealingManager = class _SelfHealingManager extends SelfHealingGitEvidenc
217116
217341
  */
217117
217342
  recoveryRehome: true,
217118
217343
  bypassGuards: true,
217119
- preserveProgress: true
217344
+ preserveProgress: true,
217345
+ preserveWorktree: true
217120
217346
  });
217121
217347
  rehomed += 1;
217122
217348
  await createRunAuditor(this.store, {
@@ -217754,7 +217980,7 @@ var SelfHealingManager = class _SelfHealingManager extends SelfHealingGitEvidenc
217754
217980
  await this.emitBackwardMoveNoAction(task, "finalize-no-op-review", "task:finalize-no-op-review-no-action", proof);
217755
217981
  continue;
217756
217982
  }
217757
- await this.store.moveTask(task.id, await resolveReboundTargetForTask(this.store, task.id), { preserveProgress: true, moveSource: "engine", recoveryRehome: true });
217983
+ await this.store.moveTask(task.id, await resolveReboundTargetForTask(this.store, task.id), { preserveProgress: true, preserveWorktree: true, moveSource: "engine", recoveryRehome: true });
217758
217984
  continue;
217759
217985
  }
217760
217986
  const mergedAt = (/* @__PURE__ */ new Date()).toISOString();
@@ -217804,7 +218030,7 @@ var SelfHealingManager = class _SelfHealingManager extends SelfHealingGitEvidenc
217804
218030
  baseRef: classification.baseRef,
217805
218031
  lane: "self-healing-finalize-no-op-review"
217806
218032
  });
217807
- await this.store.moveTask(task.id, await resolveReboundTargetForTask(this.store, task.id), { preserveProgress: true, moveSource: "engine", recoveryRehome: true });
218033
+ await this.store.moveTask(task.id, await resolveReboundTargetForTask(this.store, task.id), { preserveProgress: true, preserveWorktree: true, moveSource: "engine", recoveryRehome: true });
217808
218034
  recovered++;
217809
218035
  continue;
217810
218036
  }
@@ -217823,7 +218049,7 @@ var SelfHealingManager = class _SelfHealingManager extends SelfHealingGitEvidenc
217823
218049
  classification: "proven-no-op",
217824
218050
  baseRef: classification.baseRef
217825
218051
  });
217826
- await this.store.moveTask(task.id, await resolveReboundTargetForTask(this.store, task.id), { preserveProgress: true, moveSource: "engine", recoveryRehome: true });
218052
+ await this.store.moveTask(task.id, await resolveReboundTargetForTask(this.store, task.id), { preserveProgress: true, preserveWorktree: true, moveSource: "engine", recoveryRehome: true });
217827
218053
  recovered++;
217828
218054
  continue;
217829
218055
  }
@@ -218366,7 +218592,7 @@ var SelfHealingManager = class _SelfHealingManager extends SelfHealingGitEvidenc
218366
218592
  task.id,
218367
218593
  "Auto-recovered: in-review task still had incomplete steps \u2014 moved back to todo for retry"
218368
218594
  );
218369
- await this.store.moveTask(task.id, await resolveReboundTargetForTask(this.store, task.id), { preserveProgress: true, moveSource: "engine", recoveryRehome: true });
218595
+ await this.store.moveTask(task.id, await resolveReboundTargetForTask(this.store, task.id), { preserveProgress: true, preserveWorktree: true, moveSource: "engine", recoveryRehome: true });
218370
218596
  log34.log(`Recovered stale incomplete review task ${task.id}: moved back to todo`);
218371
218597
  recovered++;
218372
218598
  } catch (err) {
@@ -218760,7 +218986,7 @@ var SelfHealingManager = class _SelfHealingManager extends SelfHealingGitEvidenc
218760
218986
  task.id,
218761
218987
  "Auto-recovered: in-review task idle past stuck-task timeout \u2014 kicked back to todo"
218762
218988
  );
218763
- await this.store.moveTask(task.id, await resolveReboundTargetForTask(this.store, task.id), { preserveProgress: true, moveSource: "engine", recoveryRehome: true });
218989
+ await this.store.moveTask(task.id, await resolveReboundTargetForTask(this.store, task.id), { preserveProgress: true, preserveWorktree: true, moveSource: "engine", recoveryRehome: true });
218764
218990
  log34.log(`Kicked ghost review task ${task.id} back to todo`);
218765
218991
  recovered++;
218766
218992
  } catch (err) {
@@ -219684,8 +219910,9 @@ var SelfHealingManager = class _SelfHealingManager extends SelfHealingGitEvidenc
219684
219910
  return Boolean(task.baseCommitSha);
219685
219911
  });
219686
219912
  if (candidates.length === 0) return 0;
219913
+ const batch = candidates.slice(0, DONE_METADATA_REPAIR_CAP);
219687
219914
  let repaired = 0;
219688
- for (const task of candidates) {
219915
+ for (const task of batch) {
219689
219916
  if (isWorkspaceTask(task)) continue;
219690
219917
  if (task.mergeDetails?.landedFilesAttributionRestricted || task.mergeDetails?.noOpVerifiedShortCircuit) {
219691
219918
  log34.debug(`recoverDoneTaskMergeMetadata: skipped ${task.id} \u2014 attribution-restricted`);
@@ -221342,7 +221569,7 @@ var SelfHealingManager = class _SelfHealingManager extends SelfHealingGitEvidenc
221342
221569
  nextRecoveryAt: new Date(Date.now() + delayMs).toISOString()
221343
221570
  },
221344
221571
  targetColumn: await resolveReboundTargetForTask(this.store, task.id),
221345
- moveOptions: { preserveProgress: true, moveSource: "engine" }
221572
+ moveOptions: { preserveProgress: true, preserveWorktree: true, moveSource: "engine" }
221346
221573
  });
221347
221574
  if (applied.outcome === "applied") {
221348
221575
  await this.store.logEntry(task.id, `Auto-recovered generic terminal failure (attempt ${claim.attempt}/${MAX_TERMINAL_FAILURE_AUTO_RETRIES})`);
@@ -221484,6 +221711,7 @@ var SelfHealingManager = class _SelfHealingManager extends SelfHealingGitEvidenc
221484
221711
  if (route.kind === "node-requeue" && fresh.column !== requeueTarget) {
221485
221712
  await this.store.moveTask(task.id, requeueTarget, {
221486
221713
  preserveProgress: true,
221714
+ preserveWorktree: true,
221487
221715
  moveSource: "engine",
221488
221716
  recoveryRehome: true
221489
221717
  });
@@ -221717,6 +221945,7 @@ var SelfHealingManager = class _SelfHealingManager extends SelfHealingGitEvidenc
221717
221945
  const leaseRecovered = await this.options.leaseManager.recoverAbandonedLease(
221718
221946
  task.id,
221719
221947
  `in-progress limbo: ${describeWorktreeState(task)} + null branch`,
221948
+ // worktree-discard-intended: limbo recovery — the worktree is already missing or its metadata cleared; there is no live checkout to hold.
221720
221949
  { preserveProgress: true }
221721
221950
  );
221722
221951
  if (!leaseRecovered) {
@@ -222902,7 +223131,7 @@ var SelfHealingManager = class _SelfHealingManager extends SelfHealingGitEvidenc
222902
223131
  task.id,
222903
223132
  `Auto-retry ${nextCount}/${MAX_TASK_DONE_RETRIES}: agent finished without fn_task_done \u2014 requeuing to todo to resume partial work`
222904
223133
  );
222905
- await this.store.moveTask(task.id, await resolveReboundTargetForTask(this.store, task.id), { preserveProgress: true, moveSource: "engine", recoveryRehome: true });
223134
+ await this.store.moveTask(task.id, await resolveReboundTargetForTask(this.store, task.id), { preserveProgress: true, preserveWorktree: true, moveSource: "engine", recoveryRehome: true });
222906
223135
  recovered++;
222907
223136
  } catch (err) {
222908
223137
  const errorMessage = err instanceof Error ? err.message : String(err);
@@ -265940,6 +266169,7 @@ init_auth_storage();
265940
266169
  init_merger();
265941
266170
 
265942
266171
  // ../engine/src/merge/merger-ai.ts
266172
+ init_git_identity();
265943
266173
  init_src2();
265944
266174
  init_agent_user_comments();
265945
266175
  init_worktree_names();
@@ -266938,7 +267168,7 @@ async function ensureCommitTaskMetadata(mergeRoot, taskId, includeTaskId, traile
266938
267168
  const needsPrefix = includeTaskId && !subject.toLowerCase().startsWith(taskId.toLowerCase());
266939
267169
  const missingTrailers = trailers.filter((t) => !fullMessage.includes(t));
266940
267170
  if (!needsPrefix && missingTrailers.length === 0) return;
266941
- const args = ["-c", "trailer.ifExists=addIfDifferent", "commit", "--amend"];
267171
+ const args = [...commitIdentityArgs(resolveCommitIdentity()), "-c", "trailer.ifExists=addIfDifferent", "commit", "--amend"];
266942
267172
  if (needsPrefix) {
266943
267173
  args.push("-m", `${taskId}: ${subject}`);
266944
267174
  if (body.trim()) args.push("-m", body);