@runfusion/fusion 0.77.0-beta.12 → 0.77.0-beta.14

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 (147) hide show
  1. package/dist/bin.js +1648 -909
  2. package/dist/child-process-worker.js +1072 -471
  3. package/dist/client/.vite/manifest.json +260 -260
  4. package/dist/client/assets/{AgentDetailView-BZfDgV8f.js → AgentDetailView-C-fxrRM5.js} +1 -1
  5. package/dist/client/assets/AgentDetailView-DPoh46Ym.css +1 -0
  6. package/dist/client/assets/{AgentPermissionPolicyEditor-8yBqnwbx.js → AgentPermissionPolicyEditor-BvEdHYdU.js} +1 -1
  7. package/dist/client/assets/{AgentsView-BjMOtIMq.js → AgentsView-nLidYV1p.js} +3 -3
  8. package/dist/client/assets/CommandCenter-BshTzOtB.css +1 -0
  9. package/dist/client/assets/{CommandCenter-CDuO3HPd.js → CommandCenter-BxFBD-rU.js} +22 -22
  10. package/dist/client/assets/{DevServerView-C96RV0QJ.js → DevServerView-KLdA68W-.js} +1 -1
  11. package/dist/client/assets/{DirectoryPicker-iRielXf7.js → DirectoryPicker-BiclyrJo.js} +1 -1
  12. package/dist/client/assets/{DocumentsView-DhAUnUD_.js → DocumentsView-BU24Dd5I.js} +1 -1
  13. package/dist/client/assets/{EvalsView--n1p2Bt0.js → EvalsView-BMSTg0vH.js} +1 -1
  14. package/dist/client/assets/{ExperimentalAgentOnboardingModal-DraglCCV.js → ExperimentalAgentOnboardingModal-BtYOAtE2.js} +1 -1
  15. package/dist/client/assets/{GoalsView-BOWOua5t.js → GoalsView-Dlb12ETy.js} +1 -1
  16. package/dist/client/assets/{InsightsView-C5YcJZSc.js → InsightsView-NJuA0vFY.js} +1 -1
  17. package/dist/client/assets/{MemoryView-DsCuGVGx.js → MemoryView-te2vPbGq.js} +1 -1
  18. package/dist/client/assets/{PatchnodeView-CzgChkJV.js → PatchnodeView-F-TmysVw.js} +1 -1
  19. package/dist/client/assets/{PiExtensionsManager-qYbmaigE.js → PiExtensionsManager-CClfNfAm.js} +1 -1
  20. package/dist/client/assets/{PluginManager-Cklj4Smo.js → PluginManager-BdNdqcjr.js} +1 -1
  21. package/dist/client/assets/{PullRequestView-C6vozTTI.js → PullRequestView-Du4FqTkG.js} +1 -1
  22. package/dist/client/assets/{ReportModal-B0LU4E4L.js → ReportModal-BCUOLOBM.js} +1 -1
  23. package/dist/client/assets/{ResearchView-CpESx46e.js → ResearchView-DukV-C42.js} +1 -1
  24. package/dist/client/assets/{SecretsView-8ZdH9Yzs.js → SecretsView-B3ApHMyx.js} +1 -1
  25. package/dist/client/assets/SettingsModal-BEULP2a7.css +1 -0
  26. package/dist/client/assets/{SettingsModal-BJXu-dhz.js → SettingsModal-D-_HKpi5.js} +1 -1
  27. package/dist/client/assets/SettingsModal-JPt8VHZy.js +21 -0
  28. package/dist/client/assets/{SettingsTextareaRow-th-q0_2k.js → SettingsTextareaRow-CpTYQz1W.js} +1 -1
  29. package/dist/client/assets/{SetupWizardModal-CBElxrRA.js → SetupWizardModal-DcD2ERVR.js} +2 -2
  30. package/dist/client/assets/{SkillsView-BuoicLbJ.js → SkillsView-DCqRw00z.js} +1 -1
  31. package/dist/client/assets/{WorkflowNodeEditor-BDw0LpWM.js → WorkflowNodeEditor-CxpjK-k_.js} +1 -1
  32. package/dist/client/assets/{agent-import-generation-CNridJHO.js → agent-import-generation-COouuGNI.js} +1 -1
  33. package/dist/client/assets/{agentsOrgChartLayout-BopFGp0r.js → agentsOrgChartLayout-BkXeACZM.js} +1 -1
  34. package/dist/client/assets/{app-DKzqBaeH.js → app-BUPxz3_I.js} +1 -1
  35. package/dist/client/assets/{app-y8x9KYqa.js → app-Bsb-vSJ2.js} +3 -3
  36. package/dist/client/assets/{app-DkJZhePX.js → app-CDMzhkVk.js} +1 -1
  37. package/dist/client/assets/{app-BtBMuy2Z.js → app-Ca3_7roP.js} +3 -3
  38. package/dist/client/assets/{app-JnrgtY9I.js → app-CaiQxtA9.js} +3 -3
  39. package/dist/client/assets/{app-LrO9EOLE.js → app-D7N3gRgK.js} +3 -3
  40. package/dist/client/assets/{app-Cv-4fcPl.js → app-Du0s0Xs8.js} +1 -1
  41. package/dist/client/assets/{architectureDiagram-3BPJPVTR-2_yoUOD5.js → architectureDiagram-3BPJPVTR-CGeVlxee.js} +1 -1
  42. package/dist/client/assets/{blockDiagram-GPEHLZMM-ueBSwDJh.js → blockDiagram-GPEHLZMM-D3J6Tg96.js} +1 -1
  43. package/dist/client/assets/{c4Diagram-AAUBKEIU-D0cUU3pE.js → c4Diagram-AAUBKEIU-Ckh64Ujd.js} +1 -1
  44. package/dist/client/assets/channel-HtwB1uv6.js +1 -0
  45. package/dist/client/assets/{chunk-2J33WTMH-DKJnGdD6.js → chunk-2J33WTMH-D2nEfwjq.js} +1 -1
  46. package/dist/client/assets/{chunk-4BX2VUAB-BUrgUtvt.js → chunk-4BX2VUAB-C_LbWHxf.js} +1 -1
  47. package/dist/client/assets/{chunk-55IACEB6-DHy2hCYz.js → chunk-55IACEB6-DCe4vUlx.js} +1 -1
  48. package/dist/client/assets/{chunk-727SXJPM-Dgbhllem.js → chunk-727SXJPM-iwy0O4kD.js} +1 -1
  49. package/dist/client/assets/{chunk-AQP2D5EJ-tPbicvl-.js → chunk-AQP2D5EJ-3FRhkbWy.js} +1 -1
  50. package/dist/client/assets/{chunk-FMBD7UC4-BpHFGFmo.js → chunk-FMBD7UC4-DKwvI3J1.js} +1 -1
  51. package/dist/client/assets/{chunk-ND2GUHAM-DUTQ63ld.js → chunk-ND2GUHAM-BLCbQ396.js} +1 -1
  52. package/dist/client/assets/{chunk-QZHKN3VN-ChCf_usL.js → chunk-QZHKN3VN-B8_SVfXK.js} +1 -1
  53. package/dist/client/assets/classDiagram-4FO5ZUOK-DY01_o5q.js +1 -0
  54. package/dist/client/assets/classDiagram-v2-Q7XG4LA2-DY01_o5q.js +1 -0
  55. package/dist/client/assets/{cose-bilkent-S5V4N54A-DgWzHtF9.js → cose-bilkent-S5V4N54A-BI3P2QH1.js} +1 -1
  56. package/dist/client/assets/{dagre-BM42HDAG-gQ1C1xHJ.js → dagre-BM42HDAG-Cw7T4fR4.js} +1 -1
  57. package/dist/client/assets/{dashboard-view-BXtoQ9a4.js → dashboard-view-Bahw8YdV.js} +1 -1
  58. package/dist/client/assets/{dashboard-view-BLenRoXu.js → dashboard-view-GzD2Iawz.js} +1 -1
  59. package/dist/client/assets/{dashboard-view-AQmzkBNb.js → dashboard-view-_8Rh_v2d.js} +1 -1
  60. package/dist/client/assets/{dashboard-view-BtFepPos.js → dashboard-view-nmh7Oq3W.js} +1 -1
  61. package/dist/client/assets/{diagram-2AECGRRQ-Dv96I1MI.js → diagram-2AECGRRQ-D-HeBfdu.js} +1 -1
  62. package/dist/client/assets/{diagram-5GNKFQAL-BRyhjYes.js → diagram-5GNKFQAL-DdqdGn6R.js} +1 -1
  63. package/dist/client/assets/{diagram-KO2AKTUF-Bmj3ihjc.js → diagram-KO2AKTUF-BiqjUeas.js} +1 -1
  64. package/dist/client/assets/{diagram-LMA3HP47-BYHVCFK2.js → diagram-LMA3HP47-XAqAjX_Q.js} +1 -1
  65. package/dist/client/assets/{diagram-OG6HWLK6-0WZBLayt.js → diagram-OG6HWLK6-DFA7xW7A.js} +1 -1
  66. package/dist/client/assets/{erDiagram-TEJ5UH35-Cu-yj8hA.js → erDiagram-TEJ5UH35-ChXB-xPr.js} +1 -1
  67. package/dist/client/assets/{flowDiagram-I6XJVG4X-BSetu6oh.js → flowDiagram-I6XJVG4X-7EGMaA4S.js} +1 -1
  68. package/dist/client/assets/{folder-open-CkswT0lE.js → folder-open-CRG4IIvN.js} +1 -1
  69. package/dist/client/assets/{ganttDiagram-6RSMTGT7-B5qn0KFQ.js → ganttDiagram-6RSMTGT7-CFu_eMTW.js} +1 -1
  70. package/dist/client/assets/{gitGraphDiagram-PVQCEYII-BVB3wm46.js → gitGraphDiagram-PVQCEYII-B6D4IPay.js} +1 -1
  71. package/dist/client/assets/index-bjswUBcF.js +2937 -0
  72. package/dist/client/assets/index-lxhrCjd_.css +1 -0
  73. package/dist/client/assets/{infoDiagram-5YYISTIA-ZBNKOBSK.js → infoDiagram-5YYISTIA-WjP49ki8.js} +1 -1
  74. package/dist/client/assets/{ishikawaDiagram-YF4QCWOH-CbNKgenI.js → ishikawaDiagram-YF4QCWOH-D9dfXlEr.js} +1 -1
  75. package/dist/client/assets/{journeyDiagram-JHISSGLW-BLc2FHIF.js → journeyDiagram-JHISSGLW-B1qmhVqE.js} +1 -1
  76. package/dist/client/assets/{kanban-definition-UN3LZRKU-B08tij8f.js → kanban-definition-UN3LZRKU-DKJ9zHQh.js} +1 -1
  77. package/dist/client/assets/{mermaid.core-QX5z7w8Z.js → mermaid.core-L96x9i-s.js} +4 -4
  78. package/dist/client/assets/{mindmap-definition-RKZ34NQL-C1md8M3c.js → mindmap-definition-RKZ34NQL-Cd6f8xCu.js} +1 -1
  79. package/dist/client/assets/{pieDiagram-4H26LBE5-BoQbn3Xp.js → pieDiagram-4H26LBE5-DxV4qYtG.js} +1 -1
  80. package/dist/client/assets/{puzzle-DCqPnWPE.js → puzzle-B-xyPV3x.js} +1 -1
  81. package/dist/client/assets/{quadrantDiagram-W4KKPZXB-DPvNcVK-.js → quadrantDiagram-W4KKPZXB-CSfHWa-s.js} +1 -1
  82. package/dist/client/assets/{requirementDiagram-4Y6WPE33-KfdYf15t.js → requirementDiagram-4Y6WPE33-BVyQ-8E1.js} +1 -1
  83. package/dist/client/assets/{sankeyDiagram-5OEKKPKP-D1Eyoanf.js → sankeyDiagram-5OEKKPKP-bz2Lc_rV.js} +1 -1
  84. package/dist/client/assets/{sequenceDiagram-3UESZ5HK-CmODsbCf.js → sequenceDiagram-3UESZ5HK-ZvToVBro.js} +1 -1
  85. package/dist/client/assets/{standing-instructions-template-D_mPaGcG.js → standing-instructions-template-DVKRWk6d.js} +1 -1
  86. package/dist/client/assets/{stateDiagram-AJRCARHV-Blbw_21G.js → stateDiagram-AJRCARHV-CSCy0VcB.js} +1 -1
  87. package/dist/client/assets/{stateDiagram-v2-BHNVJYJU-DwMgvYO8.js → stateDiagram-v2-BHNVJYJU-ChRwUFh1.js} +1 -1
  88. package/dist/client/assets/{timeline-definition-PNZ67QCA-DDtndgLc.js → timeline-definition-PNZ67QCA-9MuQjnnW.js} +1 -1
  89. package/dist/client/assets/{upload-DGCjJkMH.js → upload-B1HSIk-m.js} +1 -1
  90. package/dist/client/assets/{users-CbWZvFZP.js → users-CHrRYBSi.js} +1 -1
  91. package/dist/client/assets/{vennDiagram-CIIHVFJN-B6Sx0OqW.js → vennDiagram-CIIHVFJN-D3XO2rfJ.js} +1 -1
  92. package/dist/client/assets/{wardley-L42UT6IY-_SQFYU4g.js → wardley-L42UT6IY-Dug4bLhv.js} +1 -1
  93. package/dist/client/assets/{wardleyDiagram-YWT4CUSO-Ce5igv6s.js → wardleyDiagram-YWT4CUSO-CtGA4P2g.js} +1 -1
  94. package/dist/client/assets/{xychartDiagram-2RQKCTM6-zI0zyvDP.js → xychartDiagram-2RQKCTM6-D0EMf4Z7.js} +1 -1
  95. package/dist/client/index.html +2 -2
  96. package/dist/client/version.json +1 -1
  97. package/dist/droid-cli/package.json +1 -1
  98. package/dist/extension.js +1487 -803
  99. package/dist/pi-claude-cli/package.json +1 -1
  100. package/dist/plugin-sdk/index.d.ts +1 -1
  101. package/dist/plugin-sdk/index.js +25 -14
  102. package/dist/plugins/fusion-plugin-acp-runtime/bundled.js +25 -14
  103. package/dist/plugins/fusion-plugin-acp-runtime/package.json +1 -1
  104. package/dist/plugins/fusion-plugin-claude-runtime/bundled.js +25 -14
  105. package/dist/plugins/fusion-plugin-claude-runtime/package.json +1 -1
  106. package/dist/plugins/fusion-plugin-cli-printing-press/bundled.js +25 -14
  107. package/dist/plugins/fusion-plugin-cli-printing-press/package.json +1 -1
  108. package/dist/plugins/fusion-plugin-compound-engineering/bundled.js +25 -14
  109. package/dist/plugins/fusion-plugin-compound-engineering/package.json +1 -1
  110. package/dist/plugins/fusion-plugin-cursor-runtime/bundled.js +25 -14
  111. package/dist/plugins/fusion-plugin-cursor-runtime/package.json +1 -1
  112. package/dist/plugins/fusion-plugin-dependency-graph/bundled.js +25 -14
  113. package/dist/plugins/fusion-plugin-dependency-graph/package.json +1 -1
  114. package/dist/plugins/fusion-plugin-droid-runtime/bundled.js +25 -14
  115. package/dist/plugins/fusion-plugin-droid-runtime/package.json +1 -1
  116. package/dist/plugins/fusion-plugin-grok-runtime/bundled.js +25 -14
  117. package/dist/plugins/fusion-plugin-grok-runtime/package.json +1 -1
  118. package/dist/plugins/fusion-plugin-hermes-runtime/bundled.js +25 -14
  119. package/dist/plugins/fusion-plugin-hermes-runtime/package.json +1 -1
  120. package/dist/plugins/fusion-plugin-linear-import/bundled.js +25 -14
  121. package/dist/plugins/fusion-plugin-linear-import/package.json +1 -1
  122. package/dist/plugins/fusion-plugin-omp-runtime/bundled.js +25 -14
  123. package/dist/plugins/fusion-plugin-omp-runtime/package.json +1 -1
  124. package/dist/plugins/fusion-plugin-openclaw-runtime/bundled.js +25 -14
  125. package/dist/plugins/fusion-plugin-openclaw-runtime/package.json +1 -1
  126. package/dist/plugins/fusion-plugin-paperclip-runtime/bundled.js +25 -14
  127. package/dist/plugins/fusion-plugin-paperclip-runtime/package.json +1 -1
  128. package/dist/plugins/fusion-plugin-quality/bundled.js +25 -14
  129. package/dist/plugins/fusion-plugin-quality/package.json +1 -1
  130. package/dist/plugins/fusion-plugin-reports/bundled.js +25 -14
  131. package/dist/plugins/fusion-plugin-reports/package.json +1 -1
  132. package/dist/plugins/fusion-plugin-roadmap/bundled.js +25 -14
  133. package/dist/plugins/fusion-plugin-roadmap/package.json +1 -1
  134. package/dist/plugins/fusion-plugin-todos/bundled.js +25 -14
  135. package/dist/plugins/fusion-plugin-todos/package.json +1 -1
  136. package/dist/plugins/fusion-plugin-whatsapp-chat/bundled.js +25 -14
  137. package/dist/plugins/fusion-plugin-whatsapp-chat/package.json +1 -1
  138. package/package.json +1 -1
  139. package/dist/client/assets/AgentDetailView-jzgy1BAS.css +0 -1
  140. package/dist/client/assets/CommandCenter-ilm-DbtI.css +0 -1
  141. package/dist/client/assets/SettingsModal-B1UCDc_h.js +0 -21
  142. package/dist/client/assets/SettingsModal-C5ic1ls7.css +0 -1
  143. package/dist/client/assets/channel-DfvbdnTK.js +0 -1
  144. package/dist/client/assets/classDiagram-4FO5ZUOK-CkMyClXV.js +0 -1
  145. package/dist/client/assets/classDiagram-v2-Q7XG4LA2-CkMyClXV.js +0 -1
  146. package/dist/client/assets/index-BgE915SE.js +0 -2937
  147. package/dist/client/assets/index-e2yEk7y4.css +0 -1
package/dist/extension.js CHANGED
@@ -1513,6 +1513,199 @@ Be specific: cite the plan section or file path for every finding and explain th
1513
1513
  }
1514
1514
  });
1515
1515
 
1516
+ // ../core/src/workflows/workflow-merge-region.ts
1517
+ function isMergeRegionNode(node) {
1518
+ return MERGE_REGION_ENTRY_NODE_KINDS.has(node.kind) || node.config?.seam === "merge";
1519
+ }
1520
+ function classifyWorkflowNodeMergeRegion(ir2, nodeId) {
1521
+ if (ir2.version !== "v2" || !Array.isArray(ir2.nodes)) return "unknown";
1522
+ const node = ir2.nodes.find((candidate) => candidate.id === nodeId);
1523
+ if (!node) return "unknown";
1524
+ return isMergeRegionNode(node) ? "merge-region" : "outside-merge-region";
1525
+ }
1526
+ var MERGE_REGION_ENTRY_NODE_KINDS;
1527
+ var init_workflow_merge_region = __esm({
1528
+ "../core/src/workflows/workflow-merge-region.ts"() {
1529
+ "use strict";
1530
+ MERGE_REGION_ENTRY_NODE_KINDS = /* @__PURE__ */ new Set([
1531
+ "merge-gate",
1532
+ "merge-attempt",
1533
+ "manual-merge-hold",
1534
+ "retry-backoff",
1535
+ "recovery-router",
1536
+ "branch-group-member-integration",
1537
+ "branch-group-promotion",
1538
+ "pr-create",
1539
+ "pr-respond",
1540
+ "pr-merge"
1541
+ ]);
1542
+ }
1543
+ });
1544
+
1545
+ // ../core/src/workflows/workflow-optional-steps.ts
1546
+ function isWorkflowOptionalGroupEnabled(enabledWorkflowSteps, groupId, defaultOn = false) {
1547
+ return Array.isArray(enabledWorkflowSteps) ? enabledWorkflowSteps.includes(groupId) : defaultOn;
1548
+ }
1549
+ function isOptionalGroupNode(node) {
1550
+ return node.kind === "optional-group";
1551
+ }
1552
+ function computeNodeExecutionRanks(ir2) {
1553
+ const ranks = /* @__PURE__ */ new Map();
1554
+ if (ir2.version !== "v2" || !Array.isArray(ir2.nodes) || !Array.isArray(ir2.edges)) {
1555
+ return ranks;
1556
+ }
1557
+ const outgoing = /* @__PURE__ */ new Map();
1558
+ for (const edge of ir2.edges) {
1559
+ if (!outgoing.has(edge.from)) outgoing.set(edge.from, []);
1560
+ outgoing.get(edge.from)?.push(edge.to);
1561
+ }
1562
+ const queue = [];
1563
+ for (const node of ir2.nodes) {
1564
+ if (node.kind === "start" || node.id === "start") {
1565
+ queue.push({ id: node.id, rank: 0 });
1566
+ }
1567
+ }
1568
+ while (queue.length > 0) {
1569
+ const next = queue.shift();
1570
+ if (!next) continue;
1571
+ const current = ranks.get(next.id);
1572
+ if (current !== void 0 && current <= next.rank) continue;
1573
+ ranks.set(next.id, next.rank);
1574
+ for (const target of outgoing.get(next.id) ?? []) {
1575
+ queue.push({ id: target, rank: next.rank + 1 });
1576
+ }
1577
+ }
1578
+ return ranks;
1579
+ }
1580
+ function resolveWorkflowOptionalSteps(ir2, _pluginTemplates = []) {
1581
+ if (ir2.version !== "v2" || !Array.isArray(ir2.nodes)) return [];
1582
+ const ranks = computeNodeExecutionRanks(ir2);
1583
+ const resolved = [];
1584
+ for (const [nodeIndex, node] of ir2.nodes.entries()) {
1585
+ if (!isOptionalGroupNode(node)) continue;
1586
+ const config3 = node.config ?? {};
1587
+ resolved.push({
1588
+ // Keyed by the group node id (documented above); field name preserved.
1589
+ templateId: node.id,
1590
+ name: typeof config3.name === "string" && config3.name.trim() ? config3.name : node.id,
1591
+ description: "",
1592
+ /*
1593
+ FNXC:WorkflowOptionalSteps 2026-06-29-12:47:
1594
+ Post-merge verification is now a graph-native optional group. Task creation and detail surfaces need the resolver to preserve `config.phase` so post-merge toggles do not look like pre-merge gates.
1595
+ */
1596
+ phase: config3.phase === "post-merge" ? "post-merge" : "pre-merge",
1597
+ defaultOn: config3.defaultOn === true,
1598
+ reportingOnly: config3.reportingOnly === true,
1599
+ /*
1600
+ FNXC:WorkflowDefinitionSteps 2026-06-29-00:41:
1601
+ Definition/task creation surfaces must order optional groups by graph execution position, not raw node-array order. Derived built-ins can insert Plan Review between planning and parse while appending its node object, and operators still need the step list to show Plan Review before execution.
1602
+ */
1603
+ rank: ranks.get(node.id) ?? Number.MAX_SAFE_INTEGER,
1604
+ nodeIndex
1605
+ });
1606
+ }
1607
+ return resolved.sort((a, b3) => a.rank - b3.rank || a.nodeIndex - b3.nodeIndex).map(({ rank: _rank, nodeIndex: _nodeIndex, ...step }) => step);
1608
+ }
1609
+ function resolveDefaultOnOptionalGroupIds(ir2) {
1610
+ return resolveWorkflowOptionalSteps(ir2).filter((step) => step.defaultOn).map((step) => step.templateId);
1611
+ }
1612
+ function resolveAllOptionalGroupIds(ir2) {
1613
+ return resolveWorkflowOptionalSteps(ir2).map((step) => step.templateId);
1614
+ }
1615
+ function isReportingOnlyOptionalGroup(ir2, nodeId) {
1616
+ if (!nodeId) return false;
1617
+ return resolveWorkflowOptionalSteps(ir2).some((step) => step.templateId === nodeId && step.reportingOnly);
1618
+ }
1619
+ var init_workflow_optional_steps = __esm({
1620
+ "../core/src/workflows/workflow-optional-steps.ts"() {
1621
+ "use strict";
1622
+ }
1623
+ });
1624
+
1625
+ // ../core/src/workflows/workflow-fast-lane.ts
1626
+ function isFastExecutionMode(task) {
1627
+ return task.executionMode === "fast";
1628
+ }
1629
+ function configString(node, key) {
1630
+ const value = node.config?.[key];
1631
+ return typeof value === "string" ? value : void 0;
1632
+ }
1633
+ function isTaskStepsForeach(node) {
1634
+ return node.kind === "foreach" && node.config?.source === "task-steps";
1635
+ }
1636
+ function isFastLaneSkippableCustomNode(node, opts = {}) {
1637
+ if (node.config?.summaryTarget === "task" || node.id === "completion-summary") return false;
1638
+ if (opts.optionalGroupId) return false;
1639
+ if (node.config?.seam) return false;
1640
+ return node.kind === "prompt" || node.kind === "script" || node.kind === "gate";
1641
+ }
1642
+ function isFastLaneBypassedTemplateNode(node) {
1643
+ return node.kind === "step-review";
1644
+ }
1645
+ function inactiveRoute() {
1646
+ return {
1647
+ active: false,
1648
+ bypassedNodeIds: /* @__PURE__ */ new Set(),
1649
+ bypassedPreMergeGroups: [],
1650
+ parseStepsNodeIds: /* @__PURE__ */ new Set()
1651
+ };
1652
+ }
1653
+ function resolveFastLaneRoute(ir2, task) {
1654
+ if (!isFastExecutionMode(task)) return inactiveRoute();
1655
+ const foreachNode = ir2.nodes.find(isTaskStepsForeach);
1656
+ const executeNode = ir2.nodes.find((node) => configString(node, "seam") === "execute");
1657
+ const implementationNode = foreachNode ?? executeNode;
1658
+ if (!implementationNode) {
1659
+ return { ...inactiveRoute(), unsupportedReason: "no-implementation-node" };
1660
+ }
1661
+ const implementationKind = foreachNode ? "foreach-steps" : "execute-seam";
1662
+ const bypassedPreMergeGroups = resolveWorkflowOptionalSteps(ir2).filter((step) => step.phase === "pre-merge").map((step) => ({ nodeId: step.templateId, name: step.name }));
1663
+ const bypassedGroupIds = new Set(bypassedPreMergeGroups.map((group) => group.nodeId));
1664
+ const bypassedNodeIds = /* @__PURE__ */ new Set();
1665
+ for (const node of ir2.nodes) {
1666
+ if (isMergeRegionNode(node) || node.id === "completion-summary" || node.config?.summaryTarget === "task") continue;
1667
+ if (configString(node, "seam") === "planning") {
1668
+ bypassedNodeIds.add(node.id);
1669
+ continue;
1670
+ }
1671
+ if (bypassedGroupIds.has(node.id)) {
1672
+ bypassedNodeIds.add(node.id);
1673
+ continue;
1674
+ }
1675
+ const workflowAction = configString(node, "workflowAction");
1676
+ const groupId = configString(node, "forWorkflowStepId");
1677
+ if (workflowAction === "plan-replan" || workflowAction === "plan-review-no-op" || workflowAction === "pre-merge-remediation" || workflowAction === "review-remediation-steps" || groupId !== void 0 && bypassedGroupIds.has(groupId)) {
1678
+ bypassedNodeIds.add(node.id);
1679
+ }
1680
+ }
1681
+ const parseStepsNodeIds = new Set(ir2.nodes.filter((node) => node.kind === "parse-steps").map((node) => node.id));
1682
+ for (const parseNodeId of parseStepsNodeIds) {
1683
+ if (bypassedNodeIds.has(parseNodeId)) {
1684
+ throw new Error(`Fast lane cannot bypass parse-steps node '${parseNodeId}'`);
1685
+ }
1686
+ }
1687
+ return {
1688
+ active: true,
1689
+ bypassedNodeIds,
1690
+ bypassedPreMergeGroups,
1691
+ parseStepsNodeIds,
1692
+ implementationNodeId: implementationNode.id,
1693
+ implementationKind
1694
+ };
1695
+ }
1696
+ var FAST_LANE_STEP_NAME, FAST_LANE_SKIP_VALUE, FAST_MODE_BYPASS_ACTOR, FAST_LANE_STEP_REVIEW_ROUTE_VALUE;
1697
+ var init_workflow_fast_lane = __esm({
1698
+ "../core/src/workflows/workflow-fast-lane.ts"() {
1699
+ "use strict";
1700
+ init_workflow_merge_region();
1701
+ init_workflow_optional_steps();
1702
+ FAST_LANE_STEP_NAME = "Fast implementation";
1703
+ FAST_LANE_SKIP_VALUE = "fast-lane-skip";
1704
+ FAST_MODE_BYPASS_ACTOR = "fast-mode";
1705
+ FAST_LANE_STEP_REVIEW_ROUTE_VALUE = "approve";
1706
+ }
1707
+ });
1708
+
1516
1709
  // ../core/src/workflows/workflow-step-results.ts
1517
1710
  function isWorkflowStepNotRun(result2) {
1518
1711
  return result2.status === "skipped" && typeof result2.notRunReason === "string" && WORKFLOW_STEP_NOT_RUN_REASONS.includes(result2.notRunReason);
@@ -1813,6 +2006,31 @@ var init_workflow_step_results = __esm({
1813
2006
  });
1814
2007
 
1815
2008
  // ../core/src/merge/pre-merge-approval.ts
2009
+ function requiresContentReviewProof(workflowStepId, result2) {
2010
+ return workflowStepId === "code-review" || result2.reviewKind === "code";
2011
+ }
2012
+ function isAuditedOperatorBypass(result2) {
2013
+ if (result2.status !== "skipped") return false;
2014
+ const actor = result2.bypassedBy?.trim();
2015
+ return Boolean(
2016
+ actor && !AUTOMATED_BYPASS_ACTORS.has(actor) && result2.bypassedAt?.trim() && result2.bypassReason?.trim()
2017
+ );
2018
+ }
2019
+ function resolveUnprovenReviewApproval(result2, options) {
2020
+ if ((result2.phase ?? "pre-merge") !== "pre-merge" || !requiresContentReviewProof(result2.workflowStepId, result2) || options.workspace || result2.status !== "passed" || result2.verdict !== "APPROVE" && result2.verdict !== "APPROVE_WITH_NOTES" || result2.reviewInputFingerprint !== void 0 || result2.bypassedBy !== void 0 || result2.remediationArchivedAt != null) {
2021
+ return void 0;
2022
+ }
2023
+ const { verdict: _verdict, ...withoutVerdict } = result2;
2024
+ return {
2025
+ downgraded: {
2026
+ ...withoutVerdict,
2027
+ status: "failed",
2028
+ output: UNPROVEN_REVIEW_APPROVAL_DIAGNOSTIC,
2029
+ notes: UNPROVEN_REVIEW_APPROVAL_DIAGNOSTIC
2030
+ },
2031
+ reason: UNPROVEN_REVIEW_APPROVAL_DIAGNOSTIC
2032
+ };
2033
+ }
1816
2034
  function evaluatePreMergeApprovals(task, options = {}) {
1817
2035
  const required2 = options.requiredPreMergeStepIds;
1818
2036
  if (!required2?.size) return [];
@@ -1823,13 +2041,16 @@ function evaluateStep(workflowStepId, results, task, descriptor) {
1823
2041
  const result2 = results.filter((candidate) => candidate.workflowStepId === workflowStepId).at(-1);
1824
2042
  if (!result2 && descriptor?.kind !== "workspace") return { workflowStepId, state: "missing" };
1825
2043
  if (result2) {
1826
- const requiresExplicitVerdict = workflowStepId === "code-review" || result2.reviewKind === "code";
2044
+ const requiresExplicitVerdict = requiresContentReviewProof(workflowStepId, result2);
1827
2045
  const approvedVerdict = result2.verdict === "APPROVE" || result2.verdict === "APPROVE_WITH_NOTES";
1828
2046
  const isPlanDomain = workflowStepId === PLAN_REVIEW_GROUP_ID || result2.reviewKind === "plan";
1829
2047
  const notRunApproves = isWorkflowStepNotRun(result2) && !requiresExplicitVerdict && !isPlanDomain;
1830
2048
  const approved = result2.status === "passed" && (requiresExplicitVerdict ? approvedVerdict : result2.verdict === void 0 || approvedVerdict) || result2.status === "skipped" && !!result2.bypassedBy || notRunApproves;
1831
2049
  if (!approved || !!result2.remediationArchivedAt) return { workflowStepId, state: "not-approved" };
1832
2050
  if (result2.reviewKind === "plan") return { workflowStepId, state: "approved" };
2051
+ if (isAuditedOperatorBypass(result2) && descriptor?.kind !== "workspace") {
2052
+ return { workflowStepId, state: "approved" };
2053
+ }
1833
2054
  const bindsContent = requiresExplicitVerdict || result2.reviewInputFingerprint !== void 0;
1834
2055
  if (!bindsContent) return { workflowStepId, state: "approved" };
1835
2056
  }
@@ -1860,11 +2081,15 @@ function evaluateStep(workflowStepId, results, task, descriptor) {
1860
2081
  if (stale2.length) return { workflowStepId, state: "stale-content", repositories: stale2 };
1861
2082
  return { workflowStepId, state: "approved" };
1862
2083
  }
2084
+ var AUTOMATED_BYPASS_ACTORS, UNPROVEN_REVIEW_APPROVAL_DIAGNOSTIC;
1863
2085
  var init_pre_merge_approval = __esm({
1864
2086
  "../core/src/merge/pre-merge-approval.ts"() {
1865
2087
  "use strict";
1866
2088
  init_builtin_plan_review_group();
2089
+ init_workflow_fast_lane();
1867
2090
  init_workflow_step_results();
2091
+ AUTOMATED_BYPASS_ACTORS = /* @__PURE__ */ new Set([FAST_MODE_BYPASS_ACTOR]);
2092
+ UNPROVEN_REVIEW_APPROVAL_DIAGNOSTIC = "Content-binding review approval recorded without reviewInputFingerprint; approval invalidated so the gate can run again.";
1868
2093
  }
1869
2094
  });
1870
2095
 
@@ -6426,7 +6651,7 @@ function scoreLatinLocale(text3) {
6426
6651
  return { locale: "en", confidence: "low" };
6427
6652
  }
6428
6653
  const ratio = secondScore === 0 ? Infinity : bestScore / secondScore;
6429
- const confidence = bestScore >= 4 && ratio >= 1.6 ? "high" : bestScore >= 2 && ratio >= 1.25 ? "medium" : "low";
6654
+ const confidence = bestScore >= 4 && ratio >= 1.6 ? "high" : bestScore >= 3 && ratio >= 1.5 ? "medium" : "low";
6430
6655
  return { locale: best[0] === "cs" ? "unknown" : best[0], confidence };
6431
6656
  }
6432
6657
  function detectContentLanguage(text3) {
@@ -6568,11 +6793,16 @@ var init_detect_content_language = __esm({
6568
6793
  "there",
6569
6794
  "should"
6570
6795
  ],
6796
+ /*
6797
+ FNXC:TitleSummaryInputLanguage 2026-09-01-21:25:
6798
+ FN-9241 found that English and technical chat prose can contain `plus`, `par`, and `est`.
6799
+ Omit these collision-prone French tokens so a short English message cannot steer title generation
6800
+ toward French; stronger medium confidence keeps the import translation high-confidence behavior unchanged.
6801
+ */
6571
6802
  fr: [
6572
6803
  "les",
6573
6804
  "des",
6574
6805
  "une",
6575
- "est",
6576
6806
  "dans",
6577
6807
  "pour",
6578
6808
  "que",
@@ -6580,8 +6810,6 @@ var init_detect_content_language = __esm({
6580
6810
  "sur",
6581
6811
  "avec",
6582
6812
  "pas",
6583
- "plus",
6584
- "par",
6585
6813
  "sont",
6586
6814
  "cette",
6587
6815
  "aussi",
@@ -11374,35 +11602,6 @@ var init_workflow_ir_types = __esm({
11374
11602
  }
11375
11603
  });
11376
11604
 
11377
- // ../core/src/workflows/workflow-merge-region.ts
11378
- function isMergeRegionNode(node) {
11379
- return MERGE_REGION_ENTRY_NODE_KINDS.has(node.kind) || node.config?.seam === "merge";
11380
- }
11381
- function classifyWorkflowNodeMergeRegion(ir2, nodeId) {
11382
- if (ir2.version !== "v2" || !Array.isArray(ir2.nodes)) return "unknown";
11383
- const node = ir2.nodes.find((candidate) => candidate.id === nodeId);
11384
- if (!node) return "unknown";
11385
- return isMergeRegionNode(node) ? "merge-region" : "outside-merge-region";
11386
- }
11387
- var MERGE_REGION_ENTRY_NODE_KINDS;
11388
- var init_workflow_merge_region = __esm({
11389
- "../core/src/workflows/workflow-merge-region.ts"() {
11390
- "use strict";
11391
- MERGE_REGION_ENTRY_NODE_KINDS = /* @__PURE__ */ new Set([
11392
- "merge-gate",
11393
- "merge-attempt",
11394
- "manual-merge-hold",
11395
- "retry-backoff",
11396
- "recovery-router",
11397
- "branch-group-member-integration",
11398
- "branch-group-promotion",
11399
- "pr-create",
11400
- "pr-respond",
11401
- "pr-merge"
11402
- ]);
11403
- }
11404
- });
11405
-
11406
11605
  // ../core/src/workflows/workflow-extension-types.ts
11407
11606
  function workflowExtensionRegistryId(pluginId, extensionId) {
11408
11607
  return `plugin:${pluginId}:${extensionId}`;
@@ -12398,6 +12597,16 @@ function validateReviewerAgentOverrides(nodes2) {
12398
12597
  if (Array.isArray(templateNodes)) validateReviewerAgentOverrides(templateNodes);
12399
12598
  }
12400
12599
  }
12600
+ function validateReadonlyMcpServersConfig(nodes2) {
12601
+ for (const node of nodes2) {
12602
+ const value = node.config?.readonlyMcpServers;
12603
+ if (value !== void 0 && (!Array.isArray(value) || value.some((name2) => typeof name2 !== "string" || !name2.trim()))) {
12604
+ throw new WorkflowIrError(`Workflow node '${node.id}' readonlyMcpServers must be an array of non-empty strings when present`);
12605
+ }
12606
+ const templateNodes = node.config?.template?.nodes;
12607
+ if (Array.isArray(templateNodes)) validateReadonlyMcpServersConfig(templateNodes);
12608
+ }
12609
+ }
12401
12610
  function validateThinkingLevelConfig(nodes2) {
12402
12611
  for (const node of nodes2) {
12403
12612
  const value = node.config?.thinkingLevel;
@@ -13012,6 +13221,7 @@ function validateV2(ir2) {
13012
13221
  const topLevelIds = new Set(ir2.nodes.map((n) => n.id));
13013
13222
  validateStepExecutePlacement(ir2.nodes);
13014
13223
  validateThinkingLevelConfig(ir2.nodes);
13224
+ validateReadonlyMcpServersConfig(ir2.nodes);
13015
13225
  validateCredentialInstanceIdConfig(ir2.nodes);
13016
13226
  validateReviewerAgentOverrides(ir2.nodes);
13017
13227
  for (const node of ir2.nodes) {
@@ -29082,169 +29292,6 @@ var init_explicit_duplicate_marker = __esm({
29082
29292
  }
29083
29293
  });
29084
29294
 
29085
- // ../core/src/workflows/workflow-optional-steps.ts
29086
- function isWorkflowOptionalGroupEnabled(enabledWorkflowSteps, groupId, defaultOn = false) {
29087
- return Array.isArray(enabledWorkflowSteps) ? enabledWorkflowSteps.includes(groupId) : defaultOn;
29088
- }
29089
- function isOptionalGroupNode(node) {
29090
- return node.kind === "optional-group";
29091
- }
29092
- function computeNodeExecutionRanks(ir2) {
29093
- const ranks = /* @__PURE__ */ new Map();
29094
- if (ir2.version !== "v2" || !Array.isArray(ir2.nodes) || !Array.isArray(ir2.edges)) {
29095
- return ranks;
29096
- }
29097
- const outgoing = /* @__PURE__ */ new Map();
29098
- for (const edge of ir2.edges) {
29099
- if (!outgoing.has(edge.from)) outgoing.set(edge.from, []);
29100
- outgoing.get(edge.from)?.push(edge.to);
29101
- }
29102
- const queue = [];
29103
- for (const node of ir2.nodes) {
29104
- if (node.kind === "start" || node.id === "start") {
29105
- queue.push({ id: node.id, rank: 0 });
29106
- }
29107
- }
29108
- while (queue.length > 0) {
29109
- const next = queue.shift();
29110
- if (!next) continue;
29111
- const current = ranks.get(next.id);
29112
- if (current !== void 0 && current <= next.rank) continue;
29113
- ranks.set(next.id, next.rank);
29114
- for (const target of outgoing.get(next.id) ?? []) {
29115
- queue.push({ id: target, rank: next.rank + 1 });
29116
- }
29117
- }
29118
- return ranks;
29119
- }
29120
- function resolveWorkflowOptionalSteps(ir2, _pluginTemplates = []) {
29121
- if (ir2.version !== "v2" || !Array.isArray(ir2.nodes)) return [];
29122
- const ranks = computeNodeExecutionRanks(ir2);
29123
- const resolved = [];
29124
- for (const [nodeIndex, node] of ir2.nodes.entries()) {
29125
- if (!isOptionalGroupNode(node)) continue;
29126
- const config3 = node.config ?? {};
29127
- resolved.push({
29128
- // Keyed by the group node id (documented above); field name preserved.
29129
- templateId: node.id,
29130
- name: typeof config3.name === "string" && config3.name.trim() ? config3.name : node.id,
29131
- description: "",
29132
- /*
29133
- FNXC:WorkflowOptionalSteps 2026-06-29-12:47:
29134
- Post-merge verification is now a graph-native optional group. Task creation and detail surfaces need the resolver to preserve `config.phase` so post-merge toggles do not look like pre-merge gates.
29135
- */
29136
- phase: config3.phase === "post-merge" ? "post-merge" : "pre-merge",
29137
- defaultOn: config3.defaultOn === true,
29138
- reportingOnly: config3.reportingOnly === true,
29139
- /*
29140
- FNXC:WorkflowDefinitionSteps 2026-06-29-00:41:
29141
- Definition/task creation surfaces must order optional groups by graph execution position, not raw node-array order. Derived built-ins can insert Plan Review between planning and parse while appending its node object, and operators still need the step list to show Plan Review before execution.
29142
- */
29143
- rank: ranks.get(node.id) ?? Number.MAX_SAFE_INTEGER,
29144
- nodeIndex
29145
- });
29146
- }
29147
- return resolved.sort((a, b3) => a.rank - b3.rank || a.nodeIndex - b3.nodeIndex).map(({ rank: _rank, nodeIndex: _nodeIndex, ...step }) => step);
29148
- }
29149
- function resolveDefaultOnOptionalGroupIds(ir2) {
29150
- return resolveWorkflowOptionalSteps(ir2).filter((step) => step.defaultOn).map((step) => step.templateId);
29151
- }
29152
- function resolveAllOptionalGroupIds(ir2) {
29153
- return resolveWorkflowOptionalSteps(ir2).map((step) => step.templateId);
29154
- }
29155
- function isReportingOnlyOptionalGroup(ir2, nodeId) {
29156
- if (!nodeId) return false;
29157
- return resolveWorkflowOptionalSteps(ir2).some((step) => step.templateId === nodeId && step.reportingOnly);
29158
- }
29159
- var init_workflow_optional_steps = __esm({
29160
- "../core/src/workflows/workflow-optional-steps.ts"() {
29161
- "use strict";
29162
- }
29163
- });
29164
-
29165
- // ../core/src/workflows/workflow-fast-lane.ts
29166
- function isFastExecutionMode(task) {
29167
- return task.executionMode === "fast";
29168
- }
29169
- function configString(node, key) {
29170
- const value = node.config?.[key];
29171
- return typeof value === "string" ? value : void 0;
29172
- }
29173
- function isTaskStepsForeach(node) {
29174
- return node.kind === "foreach" && node.config?.source === "task-steps";
29175
- }
29176
- function isFastLaneSkippableCustomNode(node, opts = {}) {
29177
- if (node.config?.summaryTarget === "task" || node.id === "completion-summary") return false;
29178
- if (opts.optionalGroupId) return false;
29179
- if (node.config?.seam) return false;
29180
- return node.kind === "prompt" || node.kind === "script" || node.kind === "gate";
29181
- }
29182
- function isFastLaneBypassedTemplateNode(node) {
29183
- return node.kind === "step-review";
29184
- }
29185
- function inactiveRoute() {
29186
- return {
29187
- active: false,
29188
- bypassedNodeIds: /* @__PURE__ */ new Set(),
29189
- bypassedPreMergeGroups: [],
29190
- parseStepsNodeIds: /* @__PURE__ */ new Set()
29191
- };
29192
- }
29193
- function resolveFastLaneRoute(ir2, task) {
29194
- if (!isFastExecutionMode(task)) return inactiveRoute();
29195
- const foreachNode = ir2.nodes.find(isTaskStepsForeach);
29196
- const executeNode = ir2.nodes.find((node) => configString(node, "seam") === "execute");
29197
- const implementationNode = foreachNode ?? executeNode;
29198
- if (!implementationNode) {
29199
- return { ...inactiveRoute(), unsupportedReason: "no-implementation-node" };
29200
- }
29201
- const implementationKind = foreachNode ? "foreach-steps" : "execute-seam";
29202
- const bypassedPreMergeGroups = resolveWorkflowOptionalSteps(ir2).filter((step) => step.phase === "pre-merge").map((step) => ({ nodeId: step.templateId, name: step.name }));
29203
- const bypassedGroupIds = new Set(bypassedPreMergeGroups.map((group) => group.nodeId));
29204
- const bypassedNodeIds = /* @__PURE__ */ new Set();
29205
- for (const node of ir2.nodes) {
29206
- if (isMergeRegionNode(node) || node.id === "completion-summary" || node.config?.summaryTarget === "task") continue;
29207
- if (configString(node, "seam") === "planning") {
29208
- bypassedNodeIds.add(node.id);
29209
- continue;
29210
- }
29211
- if (bypassedGroupIds.has(node.id)) {
29212
- bypassedNodeIds.add(node.id);
29213
- continue;
29214
- }
29215
- const workflowAction = configString(node, "workflowAction");
29216
- const groupId = configString(node, "forWorkflowStepId");
29217
- if (workflowAction === "plan-replan" || workflowAction === "plan-review-no-op" || workflowAction === "pre-merge-remediation" || workflowAction === "review-remediation-steps" || groupId !== void 0 && bypassedGroupIds.has(groupId)) {
29218
- bypassedNodeIds.add(node.id);
29219
- }
29220
- }
29221
- const parseStepsNodeIds = new Set(ir2.nodes.filter((node) => node.kind === "parse-steps").map((node) => node.id));
29222
- for (const parseNodeId of parseStepsNodeIds) {
29223
- if (bypassedNodeIds.has(parseNodeId)) {
29224
- throw new Error(`Fast lane cannot bypass parse-steps node '${parseNodeId}'`);
29225
- }
29226
- }
29227
- return {
29228
- active: true,
29229
- bypassedNodeIds,
29230
- bypassedPreMergeGroups,
29231
- parseStepsNodeIds,
29232
- implementationNodeId: implementationNode.id,
29233
- implementationKind
29234
- };
29235
- }
29236
- var FAST_LANE_STEP_NAME, FAST_LANE_SKIP_VALUE, FAST_LANE_STEP_REVIEW_ROUTE_VALUE;
29237
- var init_workflow_fast_lane = __esm({
29238
- "../core/src/workflows/workflow-fast-lane.ts"() {
29239
- "use strict";
29240
- init_workflow_merge_region();
29241
- init_workflow_optional_steps();
29242
- FAST_LANE_STEP_NAME = "Fast implementation";
29243
- FAST_LANE_SKIP_VALUE = "fast-lane-skip";
29244
- FAST_LANE_STEP_REVIEW_ROUTE_VALUE = "approve";
29245
- }
29246
- });
29247
-
29248
29295
  // ../core/src/mesh/mesh-task-replication.ts
29249
29296
  function buildBootstrapPrompt(taskId, title, description) {
29250
29297
  const heading = title ? `${taskId}: ${title}` : taskId;
@@ -66445,10 +66492,10 @@ var init_migration = __esm({
66445
66492
  return basename6(projectPath);
66446
66493
  }
66447
66494
  try {
66448
- const { execFile: execFile46 } = await import("node:child_process");
66449
- const { promisify: promisify101 } = await import("node:util");
66450
- const execFileAsync40 = promisify101(execFile46);
66451
- const { stdout } = await execFileAsync40(
66495
+ const { execFile: execFile47 } = await import("node:child_process");
66496
+ const { promisify: promisify102 } = await import("node:util");
66497
+ const execFileAsync41 = promisify102(execFile47);
66498
+ const { stdout } = await execFileAsync41(
66452
66499
  "git",
66453
66500
  ["remote", "get-url", "origin"],
66454
66501
  { cwd: projectPath, timeout: 1e3 }
@@ -68643,10 +68690,10 @@ var init_central_core = __esm({
68643
68690
  */
68644
68691
  async generateProjectName(projectPath) {
68645
68692
  try {
68646
- const { execFile: execFile46 } = await import("node:child_process");
68647
- const { promisify: promisify101 } = await import("node:util");
68648
- const execFileAsync40 = promisify101(execFile46);
68649
- const { stdout } = await execFileAsync40(
68693
+ const { execFile: execFile47 } = await import("node:child_process");
68694
+ const { promisify: promisify102 } = await import("node:util");
68695
+ const execFileAsync41 = promisify102(execFile47);
68696
+ const { stdout } = await execFileAsync41(
68650
68697
  "git",
68651
68698
  ["remote", "get-url", "origin"],
68652
68699
  { cwd: projectPath, timeout: 5e3 }
@@ -85448,14 +85495,12 @@ async function listWorkflowDefinitionsImpl(store, options) {
85448
85495
  return pluginFiltered;
85449
85496
  }
85450
85497
  async function readAllWorkflowDefinitionsImpl(store) {
85451
- if (store.workflowDefinitionsCache) return store.workflowDefinitionsCache;
85452
85498
  const layer = store.getAsyncLayer();
85453
85499
  if (!layer) {
85454
85500
  throw new Error("workflow definitions: AsyncDataLayer not initialized in backend mode");
85455
85501
  }
85456
85502
  const rows = await listWorkflowRows(layer);
85457
- store.workflowDefinitionsCache = [...BUILTIN_WORKFLOWS, ...rows.map((row) => store.toWorkflowDefinition(row))];
85458
- return store.workflowDefinitionsCache;
85503
+ return [...BUILTIN_WORKFLOWS, ...rows.map((row) => store.toWorkflowDefinition(row))];
85459
85504
  }
85460
85505
  async function getWorkflowDefinitionImpl(store, id) {
85461
85506
  const builtin = getBuiltinWorkflow(id);
@@ -85524,7 +85569,6 @@ function insertWorkflowDefinitionSyncImpl(store, input) {
85524
85569
  if (!isWorkflowDefinitionIdPrimaryKeyCollision(error3)) throw error3;
85525
85570
  continue;
85526
85571
  }
85527
- store.workflowDefinitionsCache = null;
85528
85572
  return definition;
85529
85573
  }
85530
85574
  throw new Error("Unable to allocate a free workflow definition id after repeated id collisions");
@@ -85914,11 +85958,6 @@ function getPluginStoreImpl(store) {
85914
85958
  ...pluginLayer ? { asyncLayer: pluginLayer } : {}
85915
85959
  }
85916
85960
  );
85917
- const clearWorkflowDefinitionCache = () => {
85918
- store.workflowDefinitionsCache = null;
85919
- };
85920
- store.pluginStore.on("plugin:registered", clearWorkflowDefinitionCache);
85921
- store.pluginStore.on("plugin:unregistered", clearWorkflowDefinitionCache);
85922
85961
  }
85923
85962
  return store.pluginStore;
85924
85963
  }
@@ -90290,11 +90329,11 @@ async function searchWithQmd(rootDir, options) {
90290
90329
  const command = "qmd";
90291
90330
  const limit = Math.max(1, Math.min(options.limit ?? 5, 20));
90292
90331
  try {
90293
- const execFileAsync40 = await getDefaultExecFileAsync();
90294
- await ensureQmdProjectMemoryCollection(rootDir, execFileAsync40);
90332
+ const execFileAsync41 = await getDefaultExecFileAsync();
90333
+ await ensureQmdProjectMemoryCollection(rootDir, execFileAsync41);
90295
90334
  scheduleQmdProjectMemoryRefresh(rootDir);
90296
90335
  const args = buildQmdSearchArgs(rootDir, options);
90297
- const { stdout } = await execFileAsync40(command, args, {
90336
+ const { stdout } = await execFileAsync41(command, args, {
90298
90337
  cwd: rootDir,
90299
90338
  timeout: 4e3,
90300
90339
  maxBuffer: 1024 * 1024
@@ -90319,12 +90358,12 @@ async function searchWithQmd(rootDir, options) {
90319
90358
  return [];
90320
90359
  }
90321
90360
  }
90322
- async function ensureQmdProjectMemoryCollection(rootDir, execFileAsync40) {
90361
+ async function ensureQmdProjectMemoryCollection(rootDir, execFileAsync41) {
90323
90362
  const collectionName = qmdMemoryCollectionName(rootDir);
90324
90363
  const memoryDir = memoryWorkspacePath(rootDir);
90325
90364
  await mkdir10(memoryDir, { recursive: true });
90326
90365
  try {
90327
- await execFileAsync40("qmd", buildQmdCollectionAddArgs(rootDir), {
90366
+ await execFileAsync41("qmd", buildQmdCollectionAddArgs(rootDir), {
90328
90367
  cwd: rootDir,
90329
90368
  timeout: 4e3,
90330
90369
  maxBuffer: 512 * 1024
@@ -90422,14 +90461,14 @@ async function refreshQmdProjectMemoryIndex(rootDir, options) {
90422
90461
  }
90423
90462
  }
90424
90463
  const promise = (async () => {
90425
- const execFileAsync40 = options?.execFileAsync ?? await getDefaultExecFileAsync();
90426
- await ensureQmdProjectMemoryCollection(rootDir, execFileAsync40);
90427
- await execFileAsync40("qmd", ["update"], {
90464
+ const execFileAsync41 = options?.execFileAsync ?? await getDefaultExecFileAsync();
90465
+ await ensureQmdProjectMemoryCollection(rootDir, execFileAsync41);
90466
+ await execFileAsync41("qmd", ["update"], {
90428
90467
  cwd: rootDir,
90429
90468
  timeout: 3e4,
90430
90469
  maxBuffer: 1024 * 1024
90431
90470
  });
90432
- await execFileAsync40("qmd", ["embed"], {
90471
+ await execFileAsync41("qmd", ["embed"], {
90433
90472
  cwd: rootDir,
90434
90473
  timeout: 12e4,
90435
90474
  maxBuffer: 1024 * 1024
@@ -90465,12 +90504,12 @@ async function refreshQmdAgentMemoryIndex(rootDir, agentId, options) {
90465
90504
  }
90466
90505
  }
90467
90506
  const promise = (async () => {
90468
- const execFileAsync40 = options?.execFileAsync ?? await getDefaultExecFileAsync();
90507
+ const execFileAsync41 = options?.execFileAsync ?? await getDefaultExecFileAsync();
90469
90508
  const { agentMemoryWorkspacePath: agentMemoryWorkspacePath2 } = await Promise.resolve().then(() => (init_memory_dreams(), memory_dreams_exports));
90470
90509
  const workspacePath = agentMemoryWorkspacePath2(rootDir, agentId);
90471
90510
  await mkdir10(workspacePath, { recursive: true });
90472
90511
  try {
90473
- await execFileAsync40("qmd", ["collection", "add", workspacePath, "--name", qmdAgentMemoryCollectionName(rootDir, agentId), "--mask", "**/*.md"], {
90512
+ await execFileAsync41("qmd", ["collection", "add", workspacePath, "--name", qmdAgentMemoryCollectionName(rootDir, agentId), "--mask", "**/*.md"], {
90474
90513
  cwd: rootDir,
90475
90514
  timeout: 4e3,
90476
90515
  maxBuffer: 512 * 1024
@@ -90483,8 +90522,8 @@ ${stderr}`)) {
90483
90522
  throw err;
90484
90523
  }
90485
90524
  }
90486
- await execFileAsync40("qmd", ["update"], { cwd: rootDir, timeout: 3e4, maxBuffer: 1024 * 1024 });
90487
- await execFileAsync40("qmd", ["embed"], { cwd: rootDir, timeout: 12e4, maxBuffer: 1024 * 1024 });
90525
+ await execFileAsync41("qmd", ["update"], { cwd: rootDir, timeout: 3e4, maxBuffer: 1024 * 1024 });
90526
+ await execFileAsync41("qmd", ["embed"], { cwd: rootDir, timeout: 12e4, maxBuffer: 1024 * 1024 });
90488
90527
  })();
90489
90528
  qmdAgentRefreshState.set(key, { lastStartedAt: now, inFlight: promise });
90490
90529
  try {
@@ -90505,8 +90544,8 @@ function scheduleQmdAgentMemoryRefresh(rootDir, agentId) {
90505
90544
  }
90506
90545
  async function isQmdAvailable() {
90507
90546
  try {
90508
- const execFileAsync40 = await getDefaultExecFileAsync();
90509
- await execFileAsync40("qmd", ["--help"], {
90547
+ const execFileAsync41 = await getDefaultExecFileAsync();
90548
+ await execFileAsync41("qmd", ["--help"], {
90510
90549
  timeout: 3e3,
90511
90550
  maxBuffer: 128 * 1024,
90512
90551
  keepAlive: true
@@ -90517,12 +90556,12 @@ async function isQmdAvailable() {
90517
90556
  }
90518
90557
  }
90519
90558
  async function installQmd(options) {
90520
- const execFileAsync40 = options?.execFileAsync ?? await getDefaultExecFileAsync();
90559
+ const execFileAsync41 = options?.execFileAsync ?? await getDefaultExecFileAsync();
90521
90560
  const [command, ...args] = QMD_INSTALL_COMMAND.split(" ");
90522
90561
  if (!command || args.length === 0) {
90523
90562
  throw new MemoryBackendError("BACKEND_UNAVAILABLE", "qmd install command is not configured", "qmd");
90524
90563
  }
90525
- await execFileAsync40(command, args, {
90564
+ await execFileAsync41(command, args, {
90526
90565
  timeout: 12e4,
90527
90566
  maxBuffer: 1024 * 1024
90528
90567
  });
@@ -94899,7 +94938,7 @@ Your ONLY job is to create a concise title (max 60 characters) that summarizes t
94899
94938
 
94900
94939
  ## Style
94901
94940
  - Clear, descriptive, actionable, professional
94902
- - Write the title in the SAME language as the task description content. For example, a French description requires a French title.
94941
+ - Write the title in the SAME language as the task description content.
94903
94942
  - Maximum 60 characters
94904
94943
  - Focus on the main goal or deliverable of the task`;
94905
94944
  MAX_DESCRIPTION_LENGTH = 2e3;
@@ -100037,7 +100076,6 @@ async function createWorkflowDefinitionImpl(store, input) {
100037
100076
  if (!isWorkflowDefinitionIdPrimaryKeyCollision(error3)) throw error3;
100038
100077
  continue;
100039
100078
  }
100040
- store.workflowDefinitionsCache = null;
100041
100079
  return definition;
100042
100080
  }
100043
100081
  throw new Error("Unable to allocate a free workflow definition id after repeated id collisions");
@@ -103621,7 +103659,6 @@ async function updateWorkflowDefinitionImpl(store, id, updates) {
103621
103659
  eq(project_exports.workflows.id, id),
103622
103660
  projectScopeFor(project_exports.workflows.projectId, layer.projectId)
103623
103661
  ));
103624
- store.workflowDefinitionsCache = null;
103625
103662
  return next;
103626
103663
  });
103627
103664
  if (pendingRehome) {
@@ -103656,7 +103693,6 @@ async function deleteWorkflowDefinitionImpl(store, id) {
103656
103693
  projectScopeFor(project_exports.workflows.projectId, layer.projectId)
103657
103694
  )).returning();
103658
103695
  if (deleted.length === 0) throw new Error(`Workflow '${id}' not found`);
103659
- store.workflowDefinitionsCache = null;
103660
103696
  await layer.db.delete(project_exports.workflowSettings).where(and(
103661
103697
  eq(project_exports.workflowSettings.workflowId, id),
103662
103698
  projectScopeFor(project_exports.workflowSettings.projectId, layer.projectId)
@@ -108250,7 +108286,6 @@ var init_store = __esm({
108250
108286
  taskIdIntegrityReport = { status: "ok", checkedAt: (/* @__PURE__ */ new Date()).toISOString(), anomalies: [] };
108251
108287
  lastTaskIdIntegrityLogSignature = null;
108252
108288
  workflowStepsCache = null;
108253
- workflowDefinitionsCache = null;
108254
108289
  _pluginWorkflowStepTemplates = [];
108255
108290
  globalSettingsStore;
108256
108291
  donePauseBackfillDone = false;
@@ -115880,7 +115915,7 @@ function getPostMergeFinalizeBlocker(task) {
115880
115915
  }
115881
115916
  function planConfirmedMergeChecklistReconciliation(task) {
115882
115917
  return {
115883
- skippedStepIndexes: task.steps.map((step, index2) => step.status === "pending" || step.status === "in-progress" ? index2 : -1).filter((index2) => index2 >= 0),
115918
+ skippedStepIndexes: (task.steps ?? []).map((step, index2) => step.status === "pending" || step.status === "in-progress" ? index2 : -1).filter((index2) => index2 >= 0),
115884
115919
  reconciledWorkflowStepIds: (task.workflowStepResults ?? []).filter((result2) => result2.status === "pending").map((result2) => result2.workflowStepId)
115885
115920
  };
115886
115921
  }
@@ -135930,11 +135965,11 @@ var require_extract_zip = __commonJS({
135930
135965
  var { createWriteStream, promises: fs10 } = __require("fs");
135931
135966
  var getStream = require_get_stream();
135932
135967
  var path13 = __require("path");
135933
- var { promisify: promisify101 } = __require("util");
135968
+ var { promisify: promisify102 } = __require("util");
135934
135969
  var stream = __require("stream");
135935
135970
  var yauzl = require_yauzl();
135936
- var openZip = promisify101(yauzl.open);
135937
- var pipeline = promisify101(stream.pipeline);
135971
+ var openZip = promisify102(yauzl.open);
135972
+ var pipeline = promisify102(stream.pipeline);
135938
135973
  var Extractor = class {
135939
135974
  constructor(zipPath, opts) {
135940
135975
  this.zipPath = zipPath;
@@ -136016,7 +136051,7 @@ var require_extract_zip = __commonJS({
136016
136051
  await fs10.mkdir(destDir, mkdirOptions);
136017
136052
  if (isDir) return;
136018
136053
  debug("opening read stream", dest);
136019
- const readStream = await promisify101(this.zipfile.openReadStream.bind(this.zipfile))(entry);
136054
+ const readStream = await promisify102(this.zipfile.openReadStream.bind(this.zipfile))(entry);
136020
136055
  if (symlink) {
136021
136056
  const link = await getStream(readStream);
136022
136057
  debug("creating symlink", link, dest);
@@ -137455,6 +137490,7 @@ async function updateChatSession(handle2, id, input) {
137455
137490
  if (input.status !== void 0) setValues.status = input.status;
137456
137491
  if (input.modelProvider !== void 0) setValues.modelProvider = input.modelProvider;
137457
137492
  if (input.modelId !== void 0) setValues.modelId = input.modelId;
137493
+ if (input.agentId !== void 0) setValues.agentId = input.agentId;
137458
137494
  if (input.thinkingLevel !== void 0) setValues.thinkingLevel = input.thinkingLevel;
137459
137495
  if (input.memoryFocus !== void 0) setValues.memoryFocus = input.memoryFocus;
137460
137496
  if (input.pinnedAt !== void 0) setValues.pinnedAt = input.pinnedAt;
@@ -138508,6 +138544,7 @@ var init_cli_session_store = __esm({
138508
138544
  });
138509
138545
 
138510
138546
  // ../core/src/secrets/oauth-credential-interop.ts
138547
+ import { createHash as createHash22 } from "node:crypto";
138511
138548
  import { existsSync as existsSync40, readFileSync as readFileSync11 } from "node:fs";
138512
138549
  import { homedir as homedir7 } from "node:os";
138513
138550
  import { join as join55 } from "node:path";
@@ -138567,6 +138604,22 @@ function getLastRefreshFallbackExpiryMs(lastRefresh) {
138567
138604
  }
138568
138605
  return parsed + CODEX_REFRESH_FALLBACK_WINDOW_MS;
138569
138606
  }
138607
+ function isSameStoredCredentialMaterial(left, right) {
138608
+ if (!left || !right || left.type !== right.type) {
138609
+ return false;
138610
+ }
138611
+ return left.key === right.key && left.access === right.access && left.refresh === right.refresh;
138612
+ }
138613
+ function computeStoredCredentialAccountFingerprint(credential) {
138614
+ if (!credential) {
138615
+ return void 0;
138616
+ }
138617
+ const material = typeof credential.refresh === "string" && credential.refresh.length > 0 ? credential.refresh : typeof credential.access === "string" && credential.access.length > 0 ? credential.access : typeof credential.key === "string" && credential.key.length > 0 ? credential.key : void 0;
138618
+ if (!material) {
138619
+ return void 0;
138620
+ }
138621
+ return createHash22("sha256").update(`${credential.type ?? ""}\0${material}`).digest("hex").slice(0, 16);
138622
+ }
138570
138623
  function isStoredAuthCredential(value) {
138571
138624
  if (!value || typeof value !== "object" || Array.isArray(value)) {
138572
138625
  return false;
@@ -139537,7 +139590,7 @@ var init_task_timing = __esm({
139537
139590
  });
139538
139591
 
139539
139592
  // ../core/src/task-recommendation-notice.ts
139540
- import { createHash as createHash22 } from "node:crypto";
139593
+ import { createHash as createHash23 } from "node:crypto";
139541
139594
  function registerTaskRecommendationNoticeMailbox(store, mailbox) {
139542
139595
  mailboxes2.set(store, mailbox);
139543
139596
  return () => {
@@ -139562,7 +139615,7 @@ function buildTaskRecommendationNoticeContent(task, recommendations) {
139562
139615
  }
139563
139616
  function buildTaskRecommendationNoticeIdempotencyKey(taskId, recommendations) {
139564
139617
  const ids = recommendations.map(({ id }) => id).sort().join("\n");
139565
- const digest = createHash22("sha256").update(ids, "utf8").digest("hex").slice(0, 16);
139618
+ const digest = createHash23("sha256").update(ids, "utf8").digest("hex").slice(0, 16);
139566
139619
  return `task-recommendation-notice:${taskId}:${digest}`;
139567
139620
  }
139568
139621
  async function notifyOperatorOfTaskRecommendations(store, task, recommendations, settings) {
@@ -140008,6 +140061,7 @@ __export(src_exports, {
140008
140061
  APPROVAL_REQUEST_STATUSES: () => APPROVAL_REQUEST_STATUSES,
140009
140062
  ARCHIVED_TASK_DOCUMENT_ADDITION_BOUNDARY: () => ARCHIVED_TASK_DOCUMENT_ADDITION_BOUNDARY,
140010
140063
  ARCHIVED_TASK_DOCUMENT_PUBLICATION_REJECTED: () => ARCHIVED_TASK_DOCUMENT_PUBLICATION_REJECTED,
140064
+ AUTOMATED_BYPASS_ACTORS: () => AUTOMATED_BYPASS_ACTORS,
140011
140065
  AUTOMATION_PRESETS: () => AUTOMATION_PRESETS,
140012
140066
  AUTOMATION_SELECTABLE_TOOLS: () => AUTOMATION_SELECTABLE_TOOLS,
140013
140067
  AUTOPILOT_STATES: () => AUTOPILOT_STATES,
@@ -140203,6 +140257,7 @@ __export(src_exports, {
140203
140257
  FAST_LANE_SKIP_VALUE: () => FAST_LANE_SKIP_VALUE,
140204
140258
  FAST_LANE_STEP_NAME: () => FAST_LANE_STEP_NAME,
140205
140259
  FAST_LANE_STEP_REVIEW_ROUTE_VALUE: () => FAST_LANE_STEP_REVIEW_ROUTE_VALUE,
140260
+ FAST_MODE_BYPASS_ACTOR: () => FAST_MODE_BYPASS_ACTOR,
140206
140261
  FEATURE_LOOP_REPAIR_TRANSITIONS: () => FEATURE_LOOP_REPAIR_TRANSITIONS,
140207
140262
  FEATURE_LOOP_STATES: () => FEATURE_LOOP_STATES,
140208
140263
  FEATURE_STATUSES: () => FEATURE_STATUSES,
@@ -140767,6 +140822,7 @@ __export(src_exports, {
140767
140822
  computeRetrySummary: () => computeRetrySummary,
140768
140823
  computeSkillId: () => computeSkillId,
140769
140824
  computeSnapshotChecksum: () => computeSnapshotChecksum,
140825
+ computeStoredCredentialAccountFingerprint: () => computeStoredCredentialAccountFingerprint,
140770
140826
  computeWorkflowIrPin: () => computeWorkflowIrPin,
140771
140827
  configurationTargetKey: () => configurationTargetKey,
140772
140828
  configureApprovalRequestTtls: () => configureApprovalRequestTtls,
@@ -141089,6 +141145,7 @@ __export(src_exports, {
141089
141145
  isApprovalRequestExpired: () => isApprovalRequestExpired,
141090
141146
  isArchivedColumnRole: () => isArchivedColumnRole,
141091
141147
  isArchivedRemediationCarrier: () => isArchivedRemediationCarrier,
141148
+ isAuditedOperatorBypass: () => isAuditedOperatorBypass,
141092
141149
  isBlockingFinding: () => isBlockingFinding,
141093
141150
  isBranchGroupComplete: () => isBranchGroupComplete,
141094
141151
  isBranchGroupMemberLanded: () => isBranchGroupMemberLanded,
@@ -141176,6 +141233,7 @@ __export(src_exports, {
141176
141233
  isReviewColumnRole: () => isReviewColumnRole,
141177
141234
  isRunRecord: () => isRunRecord,
141178
141235
  isRunningAgentTask: () => isRunningAgentTask,
141236
+ isSameStoredCredentialMaterial: () => isSameStoredCredentialMaterial,
141179
141237
  isSanctionedEngineBackwardMove: () => isSanctionedEngineBackwardMove,
141180
141238
  isSandboxExperimentalEnabled: () => isSandboxExperimentalEnabled,
141181
141239
  isSecretAccessPolicy: () => isSecretAccessPolicy,
@@ -141454,6 +141512,7 @@ __export(src_exports, {
141454
141512
  renderTriagePolicyPlaceholders: () => renderTriagePolicyPlaceholders,
141455
141513
  renderValidationCause: () => renderValidationCause,
141456
141514
  renderValidationFailureDescription: () => renderValidationFailureDescription,
141515
+ requiresContentReviewProof: () => requiresContentReviewProof,
141457
141516
  requiresPlannerConfirmation: () => requiresPlannerConfirmation,
141458
141517
  requiresWorktrunkInstallVerification: () => requiresWorktrunkInstallVerification,
141459
141518
  resetGhAvailabilityCache: () => resetGhAvailabilityCache,
@@ -141617,6 +141676,7 @@ __export(src_exports, {
141617
141676
  resolveTitleSummarizerSettingsModel: () => resolveTitleSummarizerSettingsModel,
141618
141677
  resolveToolOutputBudget: () => resolveToolOutputBudget,
141619
141678
  resolveTrailingVerificationStepIndex: () => resolveTrailingVerificationStepIndex,
141679
+ resolveUnprovenReviewApproval: () => resolveUnprovenReviewApproval,
141620
141680
  resolveUpdateAutomationSettings: () => resolveUpdateAutomationSettings,
141621
141681
  resolveUpdateTargetVersion: () => resolveUpdateTargetVersion,
141622
141682
  resolveUpdatesExternallyManaged: () => resolveUpdatesExternallyManaged,
@@ -142251,7 +142311,7 @@ var init_logger3 = __esm({
142251
142311
  });
142252
142312
 
142253
142313
  // ../engine/src/agents/agent-logger.ts
142254
- import { createHash as createHash23 } from "node:crypto";
142314
+ import { createHash as createHash24 } from "node:crypto";
142255
142315
  function truncateToolResultDetail(value) {
142256
142316
  if (value.length <= TOOL_RESULT_DETAIL_LIMIT) return value;
142257
142317
  return `${value.slice(0, TOOL_RESULT_DETAIL_LIMIT)}${TOOL_RESULT_DETAIL_TRUNCATION_NOTICE}`;
@@ -142359,7 +142419,7 @@ function summarizeToolArgs(name2, args) {
142359
142419
  return `${key}=${clipToolArgValue(compactToolArgValue(args[key]), maximum)}`;
142360
142420
  }).join(", ");
142361
142421
  if (rendered.length <= TOOL_ARG_SUMMARY_MAX) return rendered;
142362
- const hash3 = createHash23("sha256").update(fullArgsJson).digest("hex").slice(0, STRUCTURED_ARG_HASH_HEX_LEN);
142422
+ const hash3 = createHash24("sha256").update(fullArgsJson).digest("hex").slice(0, STRUCTURED_ARG_HASH_HEX_LEN);
142363
142423
  const suffix = `\u2026#${hash3}`;
142364
142424
  const keep = Math.max(0, TOOL_ARG_SUMMARY_MAX - suffix.length);
142365
142425
  return `${rendered.slice(0, keep)}${suffix}`;
@@ -144126,6 +144186,14 @@ function resolveAiMergeRootPath(rootDir, settings) {
144126
144186
  function resolveLegacyAiMergeRootPath(rootDir) {
144127
144187
  return join58(rootDir, ".fusion", "ai-merge");
144128
144188
  }
144189
+ function resolveAiMergeSearchRoots(rootDir, settings) {
144190
+ const roots = [
144191
+ resolveAiMergeRootPath(rootDir, settings),
144192
+ resolveLegacyAiMergeRootPath(rootDir),
144193
+ ...settings?.worktreesDir ? [] : [join58(resolveLegacyWorktreesDirLayout(rootDir), AI_MERGE_DIRNAME)]
144194
+ ];
144195
+ return [...new Set(roots)];
144196
+ }
144129
144197
  function resolveWorktreesDir(rootDir, settings, workspaceContext) {
144130
144198
  return resolveWorktreesDirLayout(rootDir, settings, workspaceContext);
144131
144199
  }
@@ -145884,18 +145952,25 @@ function parseWorktreesFromPorcelain(porcelain) {
145884
145952
  if (current.path) rows.push({ path: current.path, branch: current.branch });
145885
145953
  return rows;
145886
145954
  }
145955
+ function isRegenerableIgnoredPorcelainEntry(entry) {
145956
+ const path13 = entry.slice(3).replace(/\/$/, "");
145957
+ if (!path13 || path13.startsWith('"')) return false;
145958
+ const lastSegment = path13.split("/").at(-1);
145959
+ return lastSegment !== void 0 && REGENERABLE_IGNORED_DIR_NAMES.has(lastSegment);
145960
+ }
145887
145961
  function classifyWorktreeRemovalContent(porcelain) {
145888
145962
  const entries = porcelain.split(/\r?\n/).filter((line2) => line2.trim().length > 0);
145889
145963
  if (entries.length === 0) return "clean";
145890
- return entries.every((line2) => line2.startsWith("!!")) ? "ignored-only" : "deliverable";
145964
+ if (entries.some((line2) => !line2.startsWith("!! "))) return "deliverable";
145965
+ return entries.every(isRegenerableIgnoredPorcelainEntry) ? "regenerable-ignored" : "ignored-only";
145891
145966
  }
145892
145967
  async function assertCleanForDefensiveRemoval(worktreePath) {
145893
145968
  if (!existsSync44(worktreePath)) {
145894
- return "clean";
145969
+ return { classification: "clean", entryCount: 0 };
145895
145970
  }
145896
145971
  let stdout;
145897
145972
  try {
145898
- ({ stdout } = await execFileAsync6("git", ["status", "--porcelain=v1", "--ignored", "--untracked-files=normal"], {
145973
+ ({ stdout } = await execFileAsync6("git", ["status", "--porcelain=v1", "--ignored=matching", "--untracked-files=normal"], {
145899
145974
  cwd: worktreePath,
145900
145975
  encoding: "utf-8",
145901
145976
  timeout: 15e3,
@@ -145908,7 +145983,10 @@ async function assertCleanForDefensiveRemoval(worktreePath) {
145908
145983
  if (classification === "deliverable") {
145909
145984
  throw new Error(`preserving ${worktreePath}: uncommitted or ignored content present`);
145910
145985
  }
145911
- return classification;
145986
+ return {
145987
+ classification,
145988
+ entryCount: stdout.split(/\r?\n/).filter((line2) => line2.trim().length > 0).length
145989
+ };
145912
145990
  }
145913
145991
  async function removeWorktree(input) {
145914
145992
  const logger3 = {
@@ -145925,9 +146003,12 @@ async function removeWorktree(input) {
145925
146003
  }
145926
146004
  const requiresCleanWorktree = DEFENSIVE_REMOVAL_REASONS.has(input.reason);
145927
146005
  let contentClassification;
146006
+ let contentEntryCount = 0;
145928
146007
  if (requiresCleanWorktree) {
145929
146008
  try {
145930
- contentClassification = await assertCleanForDefensiveRemoval(input.worktreePath);
146009
+ const contentProbe = await assertCleanForDefensiveRemoval(input.worktreePath);
146010
+ contentClassification = contentProbe.classification;
146011
+ contentEntryCount = contentProbe.entryCount;
145931
146012
  } catch (error3) {
145932
146013
  const classification = error3 instanceof Error && error3.message.includes(": status probe failed (") ? "unverifiable" : "deliverable";
145933
146014
  await input.audit?.git({
@@ -145944,6 +146025,17 @@ async function removeWorktree(input) {
145944
146025
  throw error3;
145945
146026
  }
145946
146027
  if (contentClassification === "ignored-only" && !input.postLandingProof) {
146028
+ await input.audit?.git({
146029
+ type: "worktree:removal-preserved",
146030
+ target: input.worktreePath,
146031
+ metadata: {
146032
+ taskId: input.taskId,
146033
+ reason: input.reason,
146034
+ source: void 0,
146035
+ classification: "ignored-only",
146036
+ hasPostLandingProof: false
146037
+ }
146038
+ }).catch(() => void 0);
145947
146039
  throw new Error(`preserving ${input.worktreePath}: uncommitted or ignored content present`);
145948
146040
  }
145949
146041
  }
@@ -145963,6 +146055,17 @@ async function removeWorktree(input) {
145963
146055
  } catch {
145964
146056
  }
145965
146057
  };
146058
+ const recordRegenerableDiscard = async () => {
146059
+ if (contentClassification !== "regenerable-ignored") return;
146060
+ try {
146061
+ await input.audit?.git({
146062
+ type: "worktree:removal-discarded-regenerable-content",
146063
+ target: input.worktreePath,
146064
+ metadata: { taskId: input.taskId, reason: input.reason, entryCount: contentEntryCount }
146065
+ });
146066
+ } catch {
146067
+ }
146068
+ };
145966
146069
  if (input.expectedOwnerTaskId && input.liveOwnerProbe) {
145967
146070
  const reconciled = reconcileSelfOwnedActiveSessionForRemoval(
145968
146071
  activeSessionRegistry,
@@ -146016,6 +146119,7 @@ async function removeWorktree(input) {
146016
146119
  try {
146017
146120
  await backend.remove(removeInput);
146018
146121
  await recordIgnoredOnlyDiscard();
146122
+ await recordRegenerableDiscard();
146019
146123
  if (input.audit) {
146020
146124
  await input.audit.git({
146021
146125
  type: backend.kind === "worktrunk" ? "worktree:worktrunk-remove" : "worktree:remove",
@@ -146043,6 +146147,7 @@ async function removeWorktree(input) {
146043
146147
  try {
146044
146148
  await native.remove(removeInput);
146045
146149
  await recordIgnoredOnlyDiscard();
146150
+ await recordRegenerableDiscard();
146046
146151
  await input.audit?.git({ type: "worktree:remove", target: input.worktreePath });
146047
146152
  return { removed: true, classification: "removed" };
146048
146153
  } catch (nativeError) {
@@ -146064,7 +146169,7 @@ function resolveWorktreeBackend(settings, deps = {}) {
146064
146169
  }
146065
146170
  return new NativeWorktreeBackend({ logger: deps.logger, settings, audit: deps.audit });
146066
146171
  }
146067
- var execAsync9, execFileAsync6, NATIVE_TIMEOUT_MS, REMOVE_TIMEOUT_MS, MAX_BUFFER3, HARMLESS_MERGE_REMOVE_ERROR_PATTERNS, WORKTRUNK_TIMEOUTS_MS, WORKTREE_BACKEND_MARKER, WorktrunkOperationError, NativeWorktreeBackend, WorktrunkWorktreeBackend, RemovalReason, ALLOWED_FORCE_REASONS, DEFENSIVE_REMOVAL_REASONS, POST_LANDING_PROOF_REASONS, InvalidForceUsageError, InvalidPostLandingProofUsageError, ActiveSessionWorktreeRemovalError;
146172
+ var execAsync9, execFileAsync6, NATIVE_TIMEOUT_MS, REMOVE_TIMEOUT_MS, MAX_BUFFER3, HARMLESS_MERGE_REMOVE_ERROR_PATTERNS, WORKTRUNK_TIMEOUTS_MS, WORKTREE_BACKEND_MARKER, WorktrunkOperationError, NativeWorktreeBackend, WorktrunkWorktreeBackend, RemovalReason, ALLOWED_FORCE_REASONS, DEFENSIVE_REMOVAL_REASONS, POST_LANDING_PROOF_REASONS, InvalidForceUsageError, InvalidPostLandingProofUsageError, ActiveSessionWorktreeRemovalError, REGENERABLE_IGNORED_DIR_NAMES;
146068
146173
  var init_worktree_backend = __esm({
146069
146174
  "../engine/src/worktree/worktree-backend.ts"() {
146070
146175
  "use strict";
@@ -146804,6 +146909,27 @@ ${getErrorStderr(error3) ?? ""}`;
146804
146909
  this.name = "ActiveSessionWorktreeRemovalError";
146805
146910
  }
146806
146911
  };
146912
+ REGENERABLE_IGNORED_DIR_NAMES = /* @__PURE__ */ new Set([
146913
+ "node_modules",
146914
+ "dist",
146915
+ "build",
146916
+ "out",
146917
+ "target",
146918
+ "bin",
146919
+ "obj",
146920
+ "coverage",
146921
+ ".next",
146922
+ ".nuxt",
146923
+ ".turbo",
146924
+ ".svelte-kit",
146925
+ ".parcel-cache",
146926
+ ".pytest_cache",
146927
+ ".mypy_cache",
146928
+ "__pycache__",
146929
+ ".gradle",
146930
+ ".venv",
146931
+ "venv"
146932
+ ]);
146807
146933
  }
146808
146934
  });
146809
146935
 
@@ -147423,7 +147549,7 @@ var init_gating_classifications = __esm({
147423
147549
  });
147424
147550
 
147425
147551
  // ../engine/src/agents/agent-action-gate.ts
147426
- import { createHash as createHash24 } from "node:crypto";
147552
+ import { createHash as createHash25 } from "node:crypto";
147427
147553
  function getExemptTools() {
147428
147554
  if (!_exemptTools) {
147429
147555
  _exemptTools = new Set(DEFAULT_EXEMPT_TOOLS);
@@ -147475,7 +147601,7 @@ function evaluateAgentActionGate(params) {
147475
147601
  if (params.toolName === "bash") {
147476
147602
  const command = extractShellCommand(args);
147477
147603
  const git11 = classifyGitCommand(command);
147478
- resourceId = command ? `cmd:${createHash24("sha256").update(command).digest("hex").slice(0, 16)}` : void 0;
147604
+ resourceId = command ? `cmd:${createHash25("sha256").update(command).digest("hex").slice(0, 16)}` : void 0;
147479
147605
  if (git11?.write) {
147480
147606
  category = "git_write";
147481
147607
  operation = git11.operation;
@@ -149241,6 +149367,15 @@ async function withOAuthRefreshLock(authPath, providerId, refresh) {
149241
149367
  await release3();
149242
149368
  }
149243
149369
  }
149370
+ async function withOAuthInstanceLoginLock(authPath, providerId, operation) {
149371
+ const safeProviderId = providerId.replace(/[^a-zA-Z0-9._-]/g, "_");
149372
+ const release3 = await import_proper_lockfile.default.lock(`${authPath}.${safeProviderId}.instance-login`, AUTH_LOCK_OPTIONS);
149373
+ try {
149374
+ return await operation();
149375
+ } finally {
149376
+ await release3();
149377
+ }
149378
+ }
149244
149379
  function readDefaultInstanceMap(data) {
149245
149380
  const candidate = data.__fusionDefaultInstances;
149246
149381
  if (!candidate || typeof candidate !== "object" || Array.isArray(candidate)) return {};
@@ -149530,7 +149665,7 @@ function createFusionAuthStorage() {
149530
149665
  primary.reload();
149531
149666
  const selectPersistedRefreshCredential = () => {
149532
149667
  const storedCredential = primary.get(storageProvider);
149533
- if (refreshLockProvider !== ANTHROPIC_PROVIDER_ID2) {
149668
+ if (storedCredential?.type === "oauth" || refreshLockProvider !== ANTHROPIC_PROVIDER_ID2) {
149534
149669
  return storedCredential;
149535
149670
  }
149536
149671
  const legacyCredential = primary.get(ANTHROPIC_PROVIDER_ID2);
@@ -149660,18 +149795,16 @@ function createFusionAuthStorage() {
149660
149795
  if (rawKey) return rawKey;
149661
149796
  }
149662
149797
  const subscriptionLoggedOut = loggedOutProviders.has(ANTHROPIC_SUBSCRIPTION_PROVIDER_ID);
149663
- const legacyAnthropicOAuthCredential = rawProviderLoggedOut ? void 0 : selectStoredCredentialByType(ANTHROPIC_PROVIDER_ID2, "oauth");
149798
+ const subscriptionCredential = subscriptionLoggedOut ? void 0 : selectStoredCredential(ANTHROPIC_SUBSCRIPTION_PROVIDER_ID);
149799
+ if (subscriptionCredential?.type === "oauth") {
149800
+ const subscriptionKey = await resolveRefreshableCredentialApiKey(ANTHROPIC_SUBSCRIPTION_PROVIDER_ID, subscriptionCredential);
149801
+ if (subscriptionKey) return subscriptionKey;
149802
+ }
149803
+ const legacyAnthropicOAuthCredential = rawProviderLoggedOut || subscriptionCredential ? void 0 : selectStoredCredentialByType(ANTHROPIC_PROVIDER_ID2, "oauth");
149664
149804
  if (!subscriptionLoggedOut && legacyAnthropicOAuthCredential) {
149665
149805
  const legacyKey = await resolveRefreshableCredentialApiKey(ANTHROPIC_PROVIDER_ID2, legacyAnthropicOAuthCredential);
149666
149806
  if (legacyKey) return legacyKey;
149667
149807
  }
149668
- if (!subscriptionLoggedOut) {
149669
- const subscriptionCredential = selectStoredCredential(ANTHROPIC_SUBSCRIPTION_PROVIDER_ID);
149670
- if (subscriptionCredential?.type === "oauth") {
149671
- const subscriptionKey = await resolveRefreshableCredentialApiKey(ANTHROPIC_SUBSCRIPTION_PROVIDER_ID, subscriptionCredential);
149672
- if (subscriptionKey) return subscriptionKey;
149673
- }
149674
- }
149675
149808
  if (preferSubscription) {
149676
149809
  const rawKey = resolveRawApiKey();
149677
149810
  if (rawKey) return rawKey;
@@ -149848,8 +149981,15 @@ function createFusionAuthStorage() {
149848
149981
  return void 0;
149849
149982
  }
149850
149983
  if (sourceProvider !== ANTHROPIC_SUBSCRIPTION_PROVIDER_ID) {
149851
- await primary.set(ANTHROPIC_SUBSCRIPTION_PROVIDER_ID, subscriptionCredential);
149852
- loggedOutProviders.delete(ANTHROPIC_SUBSCRIPTION_PROVIDER_ID);
149984
+ const legacyKey = await resolveRefreshableCredentialApiKey(ANTHROPIC_PROVIDER_ID2, subscriptionCredential);
149985
+ primary.reload();
149986
+ const storedSubscription = primary.get(ANTHROPIC_SUBSCRIPTION_PROVIDER_ID);
149987
+ const refreshedLegacyCredential = primary.get(ANTHROPIC_PROVIDER_ID2);
149988
+ if (!storedSubscription && refreshedLegacyCredential?.type === "oauth") {
149989
+ await primary.set(ANTHROPIC_SUBSCRIPTION_PROVIDER_ID, refreshedLegacyCredential);
149990
+ loggedOutProviders.delete(ANTHROPIC_SUBSCRIPTION_PROVIDER_ID);
149991
+ }
149992
+ return legacyKey;
149853
149993
  }
149854
149994
  return resolveRefreshableCredentialApiKey(ANTHROPIC_SUBSCRIPTION_PROVIDER_ID, subscriptionCredential);
149855
149995
  }
@@ -149894,6 +150034,9 @@ var init_auth_storage = __esm({
149894
150034
  }
149895
150035
  data = {};
149896
150036
  modelRuntime;
150037
+ async withProviderInstanceLoginLock(providerId, operation) {
150038
+ return withOAuthInstanceLoginLock(this.authPath, providerId, operation);
150039
+ }
149897
150040
  ensureFile() {
149898
150041
  const parent = dirname26(this.authPath);
149899
150042
  if (!existsSync45(parent)) mkdirSync7(parent, { recursive: true, mode: 448 });
@@ -150236,6 +150379,35 @@ var init_model_registry_refresh = __esm({
150236
150379
  });
150237
150380
 
150238
150381
  // ../engine/src/auth/provider-auth.ts
150382
+ async function withProviderInstanceLoginLock(authStorage, providerId, operation) {
150383
+ let locks = instanceLoginLocks.get(authStorage);
150384
+ if (!locks) {
150385
+ locks = /* @__PURE__ */ new Map();
150386
+ instanceLoginLocks.set(authStorage, locks);
150387
+ }
150388
+ const previous = locks.get(providerId);
150389
+ let release3;
150390
+ const gate = new Promise((resolve65) => {
150391
+ release3 = resolve65;
150392
+ });
150393
+ const queued = (previous ?? Promise.resolve()).catch(() => void 0).then(() => gate);
150394
+ locks.set(providerId, queued);
150395
+ await (previous ?? Promise.resolve()).catch(() => void 0);
150396
+ try {
150397
+ if (!authStorage.withProviderInstanceLoginLock) {
150398
+ return await operation();
150399
+ }
150400
+ return await authStorage.withProviderInstanceLoginLock(providerId, async () => {
150401
+ authStorage.reload();
150402
+ return await operation();
150403
+ });
150404
+ } finally {
150405
+ release3();
150406
+ if (locks.get(providerId) === queued) {
150407
+ locks.delete(providerId);
150408
+ }
150409
+ }
150410
+ }
150239
150411
  function toApiKeyStorageProviderId(providerId) {
150240
150412
  return providerId === ANTHROPIC_API_KEY_PROVIDER_ID2 ? ANTHROPIC_STORAGE_PROVIDER_ID : providerId;
150241
150413
  }
@@ -150273,7 +150445,18 @@ function wrapAuthStorageWithApiKeyProviders(authStorage, modelRegistry, readFall
150273
150445
  providerId,
150274
150446
  callbacks
150275
150447
  );
150276
- return;
150448
+ const credential = authStorage.get(providerId);
150449
+ if (credential?.type !== "oauth") {
150450
+ return credential;
150451
+ }
150452
+ const stampedCredential = {
150453
+ ...credential,
150454
+ ...computeStoredCredentialAccountFingerprint(credential) ? {
150455
+ accountFingerprint: computeStoredCredentialAccountFingerprint(credential)
150456
+ } : {}
150457
+ };
150458
+ await authStorage.set(providerId, stampedCredential);
150459
+ return stampedCredential;
150277
150460
  }
150278
150461
  const existingApiKey = mergedAuthStorage.get(ANTHROPIC_STORAGE_PROVIDER_ID);
150279
150462
  await mergedAuthStorage.login(
@@ -150282,19 +150465,29 @@ function wrapAuthStorageWithApiKeyProviders(authStorage, modelRegistry, readFall
150282
150465
  );
150283
150466
  const oauthCredential = authStorage.get(ANTHROPIC_STORAGE_PROVIDER_ID);
150284
150467
  if (oauthCredential?.type === "oauth") {
150285
- await mergedAuthStorage.set(ANTHROPIC_SUBSCRIPTION_STORAGE_PROVIDER_ID, oauthCredential);
150468
+ const stampedCredential = {
150469
+ ...oauthCredential,
150470
+ ...computeStoredCredentialAccountFingerprint(oauthCredential) ? {
150471
+ accountFingerprint: computeStoredCredentialAccountFingerprint(oauthCredential)
150472
+ } : {}
150473
+ };
150474
+ await mergedAuthStorage.set(ANTHROPIC_SUBSCRIPTION_STORAGE_PROVIDER_ID, stampedCredential);
150286
150475
  if (existingApiKey?.type === "api_key") {
150287
150476
  await mergedAuthStorage.set(ANTHROPIC_STORAGE_PROVIDER_ID, existingApiKey);
150288
150477
  } else {
150289
150478
  await authStorage.remove(ANTHROPIC_STORAGE_PROVIDER_ID);
150290
150479
  }
150480
+ return stampedCredential;
150291
150481
  }
150482
+ return void 0;
150292
150483
  };
150293
150484
  return {
150294
150485
  reload: () => mergedAuthStorage.reload(),
150295
150486
  getOAuthProviders: () => mergedAuthStorage.getOAuthProviders().map((provider) => provider.id === ANTHROPIC_STORAGE_PROVIDER_ID ? { id: ANTHROPIC_STORAGE_PROVIDER_ID, name: "Anthropic Subscription" } : { id: provider.id, name: provider.name }),
150296
150487
  hasAuth: (provider) => provider === ANTHROPIC_STORAGE_PROVIDER_ID || provider === ANTHROPIC_SUBSCRIPTION_STORAGE_PROVIDER_ID ? Boolean(getAnthropicSubscriptionCredential()) : mergedAuthStorage.hasAuth(provider),
150297
- login,
150488
+ login: async (providerId, callbacks) => {
150489
+ await login(providerId, callbacks);
150490
+ },
150298
150491
  logout: async (provider) => {
150299
150492
  if (provider !== ANTHROPIC_STORAGE_PROVIDER_ID && provider !== ANTHROPIC_SUBSCRIPTION_STORAGE_PROVIDER_ID) {
150300
150493
  await mergedAuthStorage.logout(provider);
@@ -150378,19 +150571,33 @@ function wrapAuthStorageWithApiKeyProviders(authStorage, modelRegistry, readFall
150378
150571
  },
150379
150572
  loginInstance: async (ref, callbacks, label) => {
150380
150573
  const providerId = ref.providerId === ANTHROPIC_STORAGE_PROVIDER_ID ? ANTHROPIC_SUBSCRIPTION_STORAGE_PROVIDER_ID : ref.providerId;
150381
- const target = { providerId, instanceId: ref.instanceId };
150382
- const previousDefault = mergedAuthStorage.getDefaultInstance(providerId);
150383
- const previousCredential = previousDefault && mergedAuthStorage.getInstance(previousDefault);
150384
- await login(providerId, callbacks);
150385
- const credential = mergedAuthStorage.get(providerId);
150386
- if (!credential) return;
150387
- await mergedAuthStorage.setInstance(target, { ...credential, ...label ? { label } : {} });
150388
- if (previousDefault && previousCredential && previousDefault.instanceId !== target.instanceId) {
150389
- await mergedAuthStorage.setInstance(previousDefault, previousCredential);
150390
- } else if (!previousDefault && target.instanceId !== DEFAULT_PROVIDER_INSTANCE_ID) {
150391
- await mergedAuthStorage.removeInstance({ providerId, instanceId: DEFAULT_PROVIDER_INSTANCE_ID });
150392
- await mergedAuthStorage.setDefaultInstance(target);
150393
- }
150574
+ return withProviderInstanceLoginLock(authStorage, providerId, async () => {
150575
+ const target = { providerId, instanceId: ref.instanceId };
150576
+ const previousDefault = mergedAuthStorage.getDefaultInstance(providerId);
150577
+ const previousCredential = previousDefault && mergedAuthStorage.getInstance(previousDefault);
150578
+ const preLoginInstances = mergedAuthStorage.listInstances(providerId).map((instanceRef) => ({ ref: instanceRef, credential: mergedAuthStorage.getInstance(instanceRef) })).filter((entry) => Boolean(entry.credential));
150579
+ const credential = await login(providerId, callbacks);
150580
+ if (!credential) return;
150581
+ const duplicate = preLoginInstances.find(
150582
+ (entry) => entry.ref.instanceId !== target.instanceId && isSameStoredCredentialMaterial(credential, entry.credential)
150583
+ );
150584
+ if (duplicate) {
150585
+ if (previousDefault && previousCredential) {
150586
+ await mergedAuthStorage.setInstance(previousDefault, previousCredential);
150587
+ }
150588
+ const conflictingAccount = duplicate.credential.label || duplicate.ref.instanceId;
150589
+ throw new Error(
150590
+ `This login authorized the account already stored as ${conflictingAccount}. Sign out of the provider in your browser and retry.`
150591
+ );
150592
+ }
150593
+ await mergedAuthStorage.setInstance(target, { ...credential, ...label ? { label } : {} });
150594
+ if (previousDefault && previousCredential && previousDefault.instanceId !== target.instanceId) {
150595
+ await mergedAuthStorage.setInstance(previousDefault, previousCredential);
150596
+ } else if (!previousDefault && target.instanceId !== DEFAULT_PROVIDER_INSTANCE_ID) {
150597
+ await mergedAuthStorage.removeInstance({ providerId, instanceId: DEFAULT_PROVIDER_INSTANCE_ID });
150598
+ await mergedAuthStorage.setDefaultInstance(target);
150599
+ }
150600
+ });
150394
150601
  },
150395
150602
  logoutInstance: async (ref) => {
150396
150603
  await mergedAuthStorage.removeInstance({
@@ -150634,7 +150841,7 @@ function createReadOnlyAuthFileStorage(authPaths) {
150634
150841
  }
150635
150842
  };
150636
150843
  }
150637
- var ANTHROPIC_API_KEY_PROVIDER_ID2, ANTHROPIC_STORAGE_PROVIDER_ID, ANTHROPIC_SUBSCRIPTION_STORAGE_PROVIDER_ID, BUILT_IN_API_KEY_PROVIDERS, CLI_PROVIDER_IDS;
150844
+ var ANTHROPIC_API_KEY_PROVIDER_ID2, ANTHROPIC_STORAGE_PROVIDER_ID, ANTHROPIC_SUBSCRIPTION_STORAGE_PROVIDER_ID, BUILT_IN_API_KEY_PROVIDERS, CLI_PROVIDER_IDS, instanceLoginLocks;
150638
150845
  var init_provider_auth = __esm({
150639
150846
  "../engine/src/auth/provider-auth.ts"() {
150640
150847
  "use strict";
@@ -150654,6 +150861,7 @@ var init_provider_auth = __esm({
150654
150861
  { id: "zai", name: "Zai" }
150655
150862
  ];
150656
150863
  CLI_PROVIDER_IDS = /* @__PURE__ */ new Set(["pi-claude-cli", "droid-cli"]);
150864
+ instanceLoginLocks = /* @__PURE__ */ new WeakMap();
150657
150865
  }
150658
150866
  });
150659
150867
 
@@ -151039,7 +151247,7 @@ var init_plan_review_continuation = __esm({
151039
151247
  });
151040
151248
 
151041
151249
  // ../engine/src/execution/hold-release.ts
151042
- import { createHash as createHash25 } from "node:crypto";
151250
+ import { createHash as createHash26 } from "node:crypto";
151043
151251
  import { readFile as readFile36 } from "node:fs/promises";
151044
151252
  function findColumn2(ir2, columnId) {
151045
151253
  if (ir2.version !== "v2") return void 0;
@@ -151077,9 +151285,9 @@ async function checkAndRecordUnplannedExecutionBlock(store, task, ir2) {
151077
151285
  promptContent = "";
151078
151286
  }
151079
151287
  }
151080
- const promptMarker = promptContent.length > 0 ? createHash25("sha256").update(promptContent).digest("hex") : "missing";
151288
+ const promptMarker = promptContent.length > 0 ? createHash26("sha256").update(promptContent).digest("hex") : "missing";
151081
151289
  const dependencies = [...task.dependencies ?? []].sort();
151082
- const episode = createHash25("sha256").update(JSON.stringify({
151290
+ const episode = createHash26("sha256").update(JSON.stringify({
151083
151291
  planReviewNode,
151084
151292
  promptMarker,
151085
151293
  dependencies,
@@ -175048,6 +175256,7 @@ async function connectMcpSessionTools(servers, opts = {}) {
175048
175256
  const clients = [];
175049
175257
  const closedClients = /* @__PURE__ */ new WeakSet();
175050
175258
  const usedToolNames = /* @__PURE__ */ new Set();
175259
+ const serverByToolName = /* @__PURE__ */ new Map();
175051
175260
  let disposed = false;
175052
175261
  const closeOnce = async (client) => {
175053
175262
  if (closedClients.has(client)) return;
@@ -175097,7 +175306,9 @@ async function connectMcpSessionTools(servers, opts = {}) {
175097
175306
  opts.logger?.log?.(connectMsg);
175098
175307
  }
175099
175308
  for (const tool of listedTools) {
175100
- tools.push(wrapMcpTool(server.name, tool, client, usedToolNames));
175309
+ const wrappedTool = wrapMcpTool(server.name, tool, client, usedToolNames);
175310
+ tools.push(wrappedTool);
175311
+ serverByToolName.set(wrappedTool.name, server.name);
175101
175312
  }
175102
175313
  break;
175103
175314
  } catch (error3) {
@@ -175124,7 +175335,7 @@ async function connectMcpSessionTools(servers, opts = {}) {
175124
175335
  await closeAll();
175125
175336
  }
175126
175337
  }
175127
- return { tools, connected, skipped, dispose: closeAll };
175338
+ return { tools, connected, skipped, serverByToolName, dispose: closeAll };
175128
175339
  }
175129
175340
  function normalizeMaxAttempts(value) {
175130
175341
  if (value === void 0 || !Number.isFinite(value)) return DEFAULT_MAX_ATTEMPTS;
@@ -178264,8 +178475,10 @@ async function createPiAgentSessionRaw(options) {
178264
178475
  const createSessionWithModel = async (modelOverride) => {
178265
178476
  let mcpToolset;
178266
178477
  const allowReadonlyMcpTools = options.allowMcpToolsInReadonly === true;
178267
- if (forwardedMcpServers.length > 0 && (!isReadonly || allowReadonlyMcpTools)) {
178268
- mcpToolset = await connectMcpSessionTools(forwardedMcpServers, {
178478
+ const readonlyMcpServerAllowlist = options.readonlyMcpServerAllowlist === void 0 ? void 0 : new Set(options.readonlyMcpServerAllowlist.map((name2) => name2.trim()).filter(Boolean));
178479
+ const mcpServersToConnect = isReadonly && allowReadonlyMcpTools && readonlyMcpServerAllowlist !== void 0 ? forwardedMcpServers.filter((server) => readonlyMcpServerAllowlist.has(server.name)) : forwardedMcpServers;
178480
+ if (mcpServersToConnect.length > 0 && (!isReadonly || allowReadonlyMcpTools)) {
178481
+ mcpToolset = await connectMcpSessionTools(mcpServersToConnect, {
178269
178482
  cwd: options.cwd,
178270
178483
  clientFactory: options.mcpClientFactory,
178271
178484
  logger: piLog,
@@ -178276,7 +178489,8 @@ async function createPiAgentSessionRaw(options) {
178276
178489
  piLog.warn(`MCP session continuing with unavailable servers: count=${bootstrapFailures.length}`);
178277
178490
  }
178278
178491
  } else if (forwardedMcpServers.length > 0 && isReadonly) {
178279
- piLog.debug(`readonly session \u2014 MCP servers (${forwardedMcpServers.length}) skipped`);
178492
+ const reason = !allowReadonlyMcpTools ? "no-readonly-opt-in" : "readonly-allowlist-no-match";
178493
+ piLog.log(`readonly session \u2014 MCP servers (${forwardedMcpServers.length}) skipped: reason=${reason}`);
178280
178494
  }
178281
178495
  const mcpReadonlyTools = new Set(mcpToolset?.tools ?? []);
178282
178496
  const candidateCustomTools = [
@@ -178285,7 +178499,9 @@ async function createPiAgentSessionRaw(options) {
178285
178499
  ];
178286
178500
  const readonlyFilteredCustomTools = isReadonly ? filterCustomToolsForReadonly(
178287
178501
  candidateCustomTools,
178288
- allowReadonlyMcpTools ? { allowTool: (tool) => mcpReadonlyTools.has(tool) } : {}
178502
+ allowReadonlyMcpTools ? {
178503
+ allowTool: (tool) => readonlyMcpServerAllowlist === void 0 ? mcpReadonlyTools.has(tool) : readonlyMcpServerAllowlist.has(mcpToolset?.serverByToolName?.get(tool.name) ?? "")
178504
+ } : {}
178289
178505
  ) : { allowed: candidateCustomTools, denied: [] };
178290
178506
  const allowlistFilteredCustomTools = {
178291
178507
  ...readonlyFilteredCustomTools,
@@ -180297,7 +180513,7 @@ var init_retry_burned_logger = __esm({
180297
180513
 
180298
180514
  // ../engine/src/merge/merge-dependency-sync.ts
180299
180515
  import { exec as exec18 } from "node:child_process";
180300
- import { createHash as createHash26 } from "node:crypto";
180516
+ import { createHash as createHash27 } from "node:crypto";
180301
180517
  import { existsSync as existsSync49, readFileSync as readFileSync19, writeFileSync as writeFileSync6 } from "node:fs";
180302
180518
  import { join as join67 } from "node:path";
180303
180519
  import { promisify as promisify23 } from "node:util";
@@ -180330,7 +180546,7 @@ function computeLockfileHash(rootDir) {
180330
180546
  const p2 = join67(rootDir, name2);
180331
180547
  if (existsSync49(p2)) {
180332
180548
  try {
180333
- return createHash26("sha256").update(readFileSync19(p2)).digest("hex");
180549
+ return createHash27("sha256").update(readFileSync19(p2)).digest("hex");
180334
180550
  } catch {
180335
180551
  return null;
180336
180552
  }
@@ -180460,7 +180676,7 @@ var init_merge_dependency_sync = __esm({
180460
180676
  });
180461
180677
 
180462
180678
  // ../engine/src/worktree/worktree-dependency-install.ts
180463
- import { createHash as createHash27 } from "node:crypto";
180679
+ import { createHash as createHash28 } from "node:crypto";
180464
180680
  import { existsSync as existsSync50, readFileSync as readFileSync20, readdirSync as readdirSync8, statSync as statSync11, writeFileSync as writeFileSync7 } from "node:fs";
180465
180681
  import { delimiter as delimiter2, join as join68 } from "node:path";
180466
180682
  function rootEntries(rootDir) {
@@ -180558,7 +180774,7 @@ function isCommandAvailable(binary, env = process.env) {
180558
180774
  );
180559
180775
  }
180560
180776
  function hashParts(parts) {
180561
- const hash3 = createHash27("sha256");
180777
+ const hash3 = createHash28("sha256");
180562
180778
  for (const [name2, value] of [...parts].sort(([left], [right]) => left.localeCompare(right))) {
180563
180779
  hash3.update(name2).update("\0").update(value).update("\0");
180564
180780
  }
@@ -186546,7 +186762,7 @@ var init_mission_feature_sync = __esm({
186546
186762
  // ../engine/src/agent-tools.ts
186547
186763
  import { appendFile as appendFile3, mkdir as mkdir27, readFile as readFile40, readdir as readdir11, realpath, stat as stat11, writeFile as writeFile23 } from "node:fs/promises";
186548
186764
  import { existsSync as existsSync53 } from "node:fs";
186549
- import { createHash as createHash28, randomUUID as randomUUID48 } from "node:crypto";
186765
+ import { createHash as createHash29, randomUUID as randomUUID48 } from "node:crypto";
186550
186766
  import { tmpdir as tmpdir6 } from "node:os";
186551
186767
  import { extname as extname4, isAbsolute as isAbsolute14, join as join71, relative as relative13, resolve as resolve40, sep as sep11 } from "node:path";
186552
186768
  import { Type as Type2 } from "@earendil-works/pi-ai";
@@ -186590,7 +186806,7 @@ async function readAgentMemoryWorkspaceLongTerm(rootDir, agentId) {
186590
186806
  }
186591
186807
  }
186592
186808
  function qmdAgentMemoryCollectionName2(rootDir, agentId) {
186593
- const hash3 = createHash28("sha1").update(`${rootDir}:${agentId}`).digest("hex").slice(0, 12);
186809
+ const hash3 = createHash29("sha1").update(`${rootDir}:${agentId}`).digest("hex").slice(0, 12);
186594
186810
  return `fusion-agent-memory-${sanitizeAgentMemoryId(agentId).toLowerCase()}-${hash3}`;
186595
186811
  }
186596
186812
  function buildQmdAgentMemoryCollectionAddArgs(rootDir, agentId) {
@@ -186719,11 +186935,11 @@ async function refreshAgentMemoryQmdIndex(rootDir, agentMemory) {
186719
186935
  return;
186720
186936
  }
186721
186937
  const promise = (async () => {
186722
- const { execFile: execFile46 } = await import("node:child_process");
186723
- const { promisify: promisify101 } = await import("node:util");
186724
- const execFileAsync40 = promisify101(execFile46);
186938
+ const { execFile: execFile47 } = await import("node:child_process");
186939
+ const { promisify: promisify102 } = await import("node:util");
186940
+ const execFileAsync41 = promisify102(execFile47);
186725
186941
  try {
186726
- await execFileAsync40("qmd", buildQmdAgentMemoryCollectionAddArgs(rootDir, agentMemory.agentId), {
186942
+ await execFileAsync41("qmd", buildQmdAgentMemoryCollectionAddArgs(rootDir, agentMemory.agentId), {
186727
186943
  cwd: rootDir,
186728
186944
  timeout: 4e3,
186729
186945
  maxBuffer: 512 * 1024
@@ -186736,8 +186952,8 @@ ${stderr}`)) {
186736
186952
  throw error3;
186737
186953
  }
186738
186954
  }
186739
- await execFileAsync40("qmd", ["update"], { cwd: rootDir, timeout: 3e4, maxBuffer: 1024 * 1024 });
186740
- await execFileAsync40("qmd", ["embed"], { cwd: rootDir, timeout: 12e4, maxBuffer: 1024 * 1024 });
186955
+ await execFileAsync41("qmd", ["update"], { cwd: rootDir, timeout: 3e4, maxBuffer: 1024 * 1024 });
186956
+ await execFileAsync41("qmd", ["embed"], { cwd: rootDir, timeout: 12e4, maxBuffer: 1024 * 1024 });
186741
186957
  })();
186742
186958
  agentQmdRefreshState.set(key, { lastStartedAt: now, inFlight: promise });
186743
186959
  try {
@@ -186792,10 +187008,10 @@ async function searchAgentMemoryWithQmd(rootDir, agentMemory, query, limit) {
186792
187008
  }
186793
187009
  try {
186794
187010
  await refreshAgentMemoryQmdIndex(rootDir, agentMemory);
186795
- const { execFile: execFile46 } = await import("node:child_process");
186796
- const { promisify: promisify101 } = await import("node:util");
186797
- const execFileAsync40 = promisify101(execFile46);
186798
- const { stdout } = await execFileAsync40("qmd", buildQmdAgentMemorySearchArgs(rootDir, agentMemory.agentId, query, limit), {
187011
+ const { execFile: execFile47 } = await import("node:child_process");
187012
+ const { promisify: promisify102 } = await import("node:util");
187013
+ const execFileAsync41 = promisify102(execFile47);
187014
+ const { stdout } = await execFileAsync41("qmd", buildQmdAgentMemorySearchArgs(rootDir, agentMemory.agentId, query, limit), {
186799
187015
  cwd: rootDir,
186800
187016
  timeout: 4e3,
186801
187017
  maxBuffer: 1024 * 1024
@@ -198085,7 +198301,7 @@ var init_workspace_review_remediation = __esm({
198085
198301
  });
198086
198302
 
198087
198303
  // ../engine/src/worktree/secrets-env-writer.ts
198088
- import { createHash as createHash29 } from "node:crypto";
198304
+ import { createHash as createHash30 } from "node:crypto";
198089
198305
  import { execFile as execFile14 } from "node:child_process";
198090
198306
  import { promises as fs6 } from "node:fs";
198091
198307
  import path3 from "node:path";
@@ -198094,7 +198310,7 @@ function isValidFilename(filename) {
198094
198310
  return !!filename && !filename.includes("/") && !filename.includes("\\") && !filename.includes("..") && !filename.includes("\0") && filename !== FINGERPRINT_FILE;
198095
198311
  }
198096
198312
  function sha2562(content) {
198097
- return createHash29("sha256").update(Buffer.from(content, "utf8")).digest("hex");
198313
+ return createHash30("sha256").update(Buffer.from(content, "utf8")).digest("hex");
198098
198314
  }
198099
198315
  function quote(value) {
198100
198316
  return `"${value.replaceAll("\\", "\\\\").replaceAll('"', '\\"').replaceAll("\n", "\\n").replaceAll("\r", "\\r").replaceAll(" ", "\\t")}"`;
@@ -200653,7 +200869,7 @@ var init_workflow_graph_executor = __esm({
200653
200869
  phase: "pre-merge",
200654
200870
  source: "optional-group",
200655
200871
  status: "skipped",
200656
- bypassedBy: "fast-mode",
200872
+ bypassedBy: FAST_MODE_BYPASS_ACTOR,
200657
200873
  bypassedAt,
200658
200874
  bypassReason: "Fast mode bypasses pre-merge workflow gates",
200659
200875
  bypassedFromStatus: "absent",
@@ -200857,7 +201073,7 @@ var init_workflow_graph_executor = __esm({
200857
201073
  const stepFindings = this.workflowReviewKind(node) && Array.isArray(exitContextPatch?.findings) ? exitContextPatch.findings : void 0;
200858
201074
  const repositoryReviewOutcomes = this.workflowReviewKind(node) && Array.isArray(exitContextPatch?.repositoryReviewOutcomes) ? exitContextPatch.repositoryReviewOutcomes : void 0;
200859
201075
  const repositoryScopeRevision = this.workflowReviewKind(node) && typeof exitContextPatch?.repositoryScopeRevision === "number" ? exitContextPatch.repositoryScopeRevision : void 0;
200860
- const reviewInputFingerprint = this.workflowReviewKind(node) && typeof exitContextPatch?.reviewInputFingerprint === "string" ? exitContextPatch.reviewInputFingerprint : void 0;
201076
+ const reviewInputFingerprint = requiresContentReviewProof(node.id, { reviewKind: this.workflowReviewKind(node) }) && typeof exitContextPatch?.reviewInputFingerprint === "string" ? exitContextPatch.reviewInputFingerprint : void 0;
200861
201077
  const reviewedCommitSha = this.workflowReviewKind(node) && typeof exitContextPatch?.reviewedCommitSha === "string" ? exitContextPatch.reviewedCommitSha : void 0;
200862
201078
  const supersededFindingSourceWorkflowStepId = this.workflowReviewKind(node) && typeof exitContextPatch?.supersededFindingSourceWorkflowStepId === "string" ? exitContextPatch.supersededFindingSourceWorkflowStepId : void 0;
200863
201079
  const supersededFindingIds = supersededFindingSourceWorkflowStepId && this.workflowReviewKind(node) && Array.isArray(exitContextPatch?.supersededFindingIds) ? exitContextPatch.supersededFindingIds.filter((id) => typeof id === "string") : void 0;
@@ -201596,7 +201812,7 @@ ${stepOutput}` : ""}`);
201596
201812
  const findings = this.workflowReviewKind(node) && Array.isArray(contextPatch.findings) ? contextPatch.findings : void 0;
201597
201813
  const repositoryReviewOutcomes = this.workflowReviewKind(node) && Array.isArray(contextPatch.repositoryReviewOutcomes) ? contextPatch.repositoryReviewOutcomes : void 0;
201598
201814
  const repositoryScopeRevision = this.workflowReviewKind(node) && typeof contextPatch.repositoryScopeRevision === "number" ? contextPatch.repositoryScopeRevision : void 0;
201599
- const reviewInputFingerprint = this.workflowReviewKind(node) && typeof contextPatch.reviewInputFingerprint === "string" ? contextPatch.reviewInputFingerprint : void 0;
201815
+ const reviewInputFingerprint = requiresContentReviewProof(node.id, { reviewKind: this.workflowReviewKind(node) }) && typeof contextPatch.reviewInputFingerprint === "string" ? contextPatch.reviewInputFingerprint : void 0;
201600
201816
  const reviewedCommitSha = this.workflowReviewKind(node) && typeof contextPatch.reviewedCommitSha === "string" ? contextPatch.reviewedCommitSha : void 0;
201601
201817
  const supersededFindingSourceWorkflowStepId = this.workflowReviewKind(node) && typeof contextPatch.supersededFindingSourceWorkflowStepId === "string" ? contextPatch.supersededFindingSourceWorkflowStepId : void 0;
201602
201818
  const supersededFindingIds = supersededFindingSourceWorkflowStepId && this.workflowReviewKind(node) && Array.isArray(contextPatch.supersededFindingIds) ? contextPatch.supersededFindingIds.filter((id) => typeof id === "string") : void 0;
@@ -202977,17 +203193,141 @@ var init_agent_binding_pure = __esm({
202977
203193
  }
202978
203194
  });
202979
203195
 
202980
- // ../engine/src/worktree/review-diff-fingerprint.ts
202981
- import { createHash as createHash30 } from "node:crypto";
202982
- import { execFile as execFile15 } from "node:child_process";
203196
+ // ../engine/src/executor/worktree-git-refs.ts
203197
+ import { exec as exec25, execFile as execFile15, execSync as execSync3 } from "node:child_process";
202983
203198
  import { promisify as promisify32 } from "node:util";
203199
+ async function preExecutionWorktreeHasWork(worktreePath) {
203200
+ try {
203201
+ const { stdout: dirty } = await execFileAsync9("git", ["status", "--porcelain"], { cwd: worktreePath, timeout: 3e4 });
203202
+ if (dirty.trim()) return true;
203203
+ const { stdout: ahead } = await execFileAsync9("git", ["log", "--oneline", "@{upstream}..HEAD"], { cwd: worktreePath, timeout: 3e4 }).catch(async () => await execFileAsync9("git", ["log", "--oneline", "-1", "HEAD", "--not", "--remotes", "--branches=main", "--branches=master"], { cwd: worktreePath, timeout: 3e4 }));
203204
+ return Boolean(ahead.trim());
203205
+ } catch {
203206
+ return true;
203207
+ }
203208
+ }
203209
+ async function resolveContaminationBaseRef(worktreePath) {
203210
+ try {
203211
+ const { stdout } = await execAsync22(
203212
+ "git merge-base HEAD main 2>/dev/null || git merge-base HEAD origin/main",
203213
+ { cwd: worktreePath, encoding: "utf-8" }
203214
+ );
203215
+ const ref = stdout.trim();
203216
+ return ref || void 0;
203217
+ } catch (err) {
203218
+ const errorMessage2 = err instanceof Error ? err.message : String(err);
203219
+ executorLog.warn(`Failed merge-base lookup for contamination check in ${worktreePath}: ${errorMessage2}`);
203220
+ return void 0;
203221
+ }
203222
+ }
203223
+ async function resolveDiffBaseRef(worktreePath, baseCommitSha) {
203224
+ if (baseCommitSha) return baseCommitSha;
203225
+ try {
203226
+ const { stdout } = await execAsync22(
203227
+ "git merge-base HEAD origin/main 2>/dev/null || git merge-base HEAD main",
203228
+ { cwd: worktreePath, encoding: "utf-8" }
203229
+ );
203230
+ const ref = stdout.trim();
203231
+ if (ref) return ref;
203232
+ } catch (mergeBaseErr) {
203233
+ const mergeBaseMsg = mergeBaseErr instanceof Error ? mergeBaseErr.message : String(mergeBaseErr);
203234
+ executorLog.warn(`Failed merge-base lookup for diff base in ${worktreePath}, trying HEAD~1 fallback: ${mergeBaseMsg}`);
203235
+ }
203236
+ try {
203237
+ const { stdout } = await execAsync22("git rev-parse HEAD~1", {
203238
+ cwd: worktreePath,
203239
+ encoding: "utf-8"
203240
+ });
203241
+ return stdout.trim() || void 0;
203242
+ } catch {
203243
+ executorLog.debug(`Could not determine base commit for diff in ${worktreePath}`);
203244
+ return void 0;
203245
+ }
203246
+ }
203247
+ async function captureBaseCommitSha(store, task, worktreePath, audit, options = { isResume: false }) {
203248
+ try {
203249
+ if (options.isResume && task.baseCommitSha) {
203250
+ try {
203251
+ execSync3(`git merge-base --is-ancestor ${task.baseCommitSha} HEAD`, {
203252
+ cwd: worktreePath,
203253
+ stdio: "pipe"
203254
+ });
203255
+ executorLog.log(`${task.id}: preserved baseCommitSha ${task.baseCommitSha.slice(0, 7)} (resume)`);
203256
+ await audit.git({
203257
+ type: "commit:create",
203258
+ target: task.baseCommitSha,
203259
+ metadata: { purpose: "base", preserved: true }
203260
+ });
203261
+ return;
203262
+ } catch {
203263
+ }
203264
+ }
203265
+ const baseCommitSha = await resolveCapturedBaseCommitSha(worktreePath, {
203266
+ warn: (msg) => executorLog.warn(`${task.id}: ${msg}`)
203267
+ });
203268
+ if (!baseCommitSha) {
203269
+ throw new Error("could not resolve base commit SHA");
203270
+ }
203271
+ await store.updateTask(task.id, { baseCommitSha });
203272
+ executorLog.debug(`${task.id}: captured baseCommitSha ${baseCommitSha.slice(0, 7)}`);
203273
+ await audit.git({ type: "commit:create", target: baseCommitSha, metadata: { purpose: "base", preserved: false } });
203274
+ } catch (err) {
203275
+ const errorMessage2 = err instanceof Error ? err.message : String(err);
203276
+ executorLog.debug(`Failed to capture baseCommitSha for ${task.id}: ${errorMessage2}`);
203277
+ }
203278
+ }
203279
+ var execAsync22, execFileAsync9;
203280
+ var init_worktree_git_refs = __esm({
203281
+ "../engine/src/executor/worktree-git-refs.ts"() {
203282
+ "use strict";
203283
+ init_base_commit_capture();
203284
+ init_logger3();
203285
+ execAsync22 = promisify32(exec25);
203286
+ execFileAsync9 = promisify32(execFile15);
203287
+ }
203288
+ });
203289
+
203290
+ // ../engine/src/worktree/review-diff-fingerprint.ts
203291
+ import { createHash as createHash31 } from "node:crypto";
203292
+ import { execFile as execFile16 } from "node:child_process";
203293
+ import { promisify as promisify33 } from "node:util";
203294
+ function gitCaptureOptions(cwd) {
203295
+ return {
203296
+ cwd,
203297
+ encoding: "utf8",
203298
+ maxBuffer: REVIEW_DIFF_GIT_MAX_BUFFER_BYTES,
203299
+ timeout: REVIEW_DIFF_GIT_TIMEOUT_MS
203300
+ };
203301
+ }
203302
+ function isGitDiffBufferOverflow(error3) {
203303
+ if (typeof error3 !== "object" || error3 === null) return false;
203304
+ const candidate = error3;
203305
+ return candidate.code === "ERR_CHILD_PROCESS_STDIO_MAXBUFFER" || typeof candidate.message === "string" && candidate.message.includes("maxBuffer");
203306
+ }
203307
+ async function resolveContentReviewInputProof(worktreePath, diffBaseCommitSha) {
203308
+ let baseRef;
203309
+ try {
203310
+ baseRef = await resolveDiffBaseRef(worktreePath, diffBaseCommitSha);
203311
+ } catch {
203312
+ return { kind: "unprovable", reason: "no-diff-base" };
203313
+ }
203314
+ if (!baseRef) return { kind: "unprovable", reason: "no-diff-base" };
203315
+ const probe = await probeReviewDiffFingerprint(worktreePath, baseRef);
203316
+ if (probe.state === "empty") {
203317
+ return { kind: "fingerprint", fingerprint: EMPTY_REVIEW_DIFF_FINGERPRINT };
203318
+ }
203319
+ return probe.state === "fingerprint" ? { kind: "fingerprint", fingerprint: probe.fingerprint } : { kind: "unprovable", reason: probe.reason };
203320
+ }
202984
203321
  async function probeReviewDiffFingerprint(worktreePath, baseRef, targetRef = "HEAD") {
202985
203322
  if (!worktreePath || !baseRef) return { state: "unavailable", reason: "missing-worktree-or-base" };
202986
203323
  try {
202987
- const { stdout } = await execFileAsync9("git", ["diff", "--binary", `${baseRef}..${targetRef}`], { cwd: worktreePath, encoding: "utf8" });
202988
- return stdout ? { state: "fingerprint", fingerprint: createHash30("sha256").update(stdout).digest("hex") } : { state: "empty" };
202989
- } catch {
202990
- return { state: "unavailable", reason: "git-diff-failed" };
203324
+ const { stdout } = await execFileAsync10("git", ["diff", "--binary", `${baseRef}..${targetRef}`], gitCaptureOptions(worktreePath));
203325
+ return stdout ? { state: "fingerprint", fingerprint: createHash31("sha256").update(stdout).digest("hex") } : { state: "empty" };
203326
+ } catch (error3) {
203327
+ return {
203328
+ state: "unavailable",
203329
+ reason: isGitDiffBufferOverflow(error3) ? "git-diff-too-large" : "git-diff-failed"
203330
+ };
202991
203331
  }
202992
203332
  }
202993
203333
  async function probeReviewChangesSinceCommit(worktreePath, reviewedCommitSha) {
@@ -202995,14 +203335,11 @@ async function probeReviewChangesSinceCommit(worktreePath, reviewedCommitSha) {
202995
203335
  return { state: "unavailable", reason: "missing-worktree-or-reviewed-commit" };
202996
203336
  }
202997
203337
  try {
202998
- await execFileAsync9("git", ["cat-file", "-e", `${reviewedCommitSha}^{commit}`], {
202999
- cwd: worktreePath,
203000
- encoding: "utf8"
203001
- });
203338
+ await execFileAsync10("git", ["cat-file", "-e", `${reviewedCommitSha}^{commit}`], gitCaptureOptions(worktreePath));
203002
203339
  const [{ stdout: countOutput }, { stdout: filesOutput }, { stdout: shortstatOutput }] = await Promise.all([
203003
- execFileAsync9("git", ["rev-list", "--count", `${reviewedCommitSha}..HEAD`], { cwd: worktreePath, encoding: "utf8" }),
203004
- execFileAsync9("git", ["diff", "--name-only", "-z", `${reviewedCommitSha}..HEAD`], { cwd: worktreePath, encoding: "utf8" }),
203005
- execFileAsync9("git", ["diff", "--shortstat", `${reviewedCommitSha}..HEAD`], { cwd: worktreePath, encoding: "utf8" })
203340
+ execFileAsync10("git", ["rev-list", "--count", `${reviewedCommitSha}..HEAD`], gitCaptureOptions(worktreePath)),
203341
+ execFileAsync10("git", ["diff", "--name-only", "-z", `${reviewedCommitSha}..HEAD`], gitCaptureOptions(worktreePath)),
203342
+ execFileAsync10("git", ["diff", "--shortstat", `${reviewedCommitSha}..HEAD`], gitCaptureOptions(worktreePath))
203006
203343
  ]);
203007
203344
  const commitCount = Number.parseInt(countOutput.trim(), 10);
203008
203345
  if (!Number.isSafeInteger(commitCount) || commitCount < 0) {
@@ -203031,22 +203368,30 @@ async function computeCodeReviewInputFingerprint(worktreePath, baseRef, targetRe
203031
203368
  if (result2.state === "empty") return EMPTY_REVIEW_DIFF_FINGERPRINT;
203032
203369
  return result2.state === "fingerprint" ? result2.fingerprint : void 0;
203033
203370
  }
203034
- var execFileAsync9, MAX_REVIEW_CHANGED_FILES, EMPTY_REVIEW_DIFF_FINGERPRINT;
203371
+ var execFileAsync10, REVIEW_DIFF_GIT_MAX_BUFFER_BYTES, REVIEW_DIFF_GIT_TIMEOUT_MS, MAX_REVIEW_CHANGED_FILES, EMPTY_REVIEW_DIFF_FINGERPRINT;
203035
203372
  var init_review_diff_fingerprint = __esm({
203036
203373
  "../engine/src/worktree/review-diff-fingerprint.ts"() {
203037
203374
  "use strict";
203038
- execFileAsync9 = promisify32(execFile15);
203375
+ init_worktree_git_refs();
203376
+ execFileAsync10 = promisify33(execFile16);
203377
+ REVIEW_DIFF_GIT_MAX_BUFFER_BYTES = 10 * 1024 * 1024;
203378
+ REVIEW_DIFF_GIT_TIMEOUT_MS = 12e4;
203039
203379
  MAX_REVIEW_CHANGED_FILES = 100;
203040
203380
  EMPTY_REVIEW_DIFF_FINGERPRINT = "empty-review-input:v1";
203041
203381
  }
203042
203382
  });
203043
203383
 
203044
203384
  // ../engine/src/worktree/workspace-review-evidence.ts
203045
- import { execFile as execFile16 } from "node:child_process";
203046
- import { promisify as promisify33 } from "node:util";
203385
+ import { execFile as execFile17 } from "node:child_process";
203386
+ import { promisify as promisify34 } from "node:util";
203047
203387
  import { join as join76 } from "node:path";
203048
203388
  async function git3(args, cwd) {
203049
- const { stdout } = await execFileAsync10("git", args, { cwd, encoding: "utf8" });
203389
+ const { stdout } = await execFileAsync11("git", args, {
203390
+ cwd,
203391
+ encoding: "utf8",
203392
+ maxBuffer: REVIEW_DIFF_GIT_MAX_BUFFER_BYTES,
203393
+ timeout: REVIEW_DIFF_GIT_TIMEOUT_MS
203394
+ });
203050
203395
  return stdout.trim();
203051
203396
  }
203052
203397
  async function captureWorkspaceReviewEvidence(options) {
@@ -203099,13 +203444,60 @@ async function captureWorkspaceReviewEvidence(options) {
203099
203444
  outOfScopeRepositories
203100
203445
  };
203101
203446
  }
203102
- var execFileAsync10;
203447
+ var execFileAsync11;
203103
203448
  var init_workspace_review_evidence = __esm({
203104
203449
  "../engine/src/worktree/workspace-review-evidence.ts"() {
203105
203450
  "use strict";
203106
203451
  init_workspace_base_branch();
203107
203452
  init_review_diff_fingerprint();
203108
- execFileAsync10 = promisify33(execFile16);
203453
+ execFileAsync11 = promisify34(execFile17);
203454
+ }
203455
+ });
203456
+
203457
+ // ../engine/src/worktree/review-inline-fix-recapture.ts
203458
+ import { execFile as execFile18 } from "node:child_process";
203459
+ import { promisify as promisify35 } from "node:util";
203460
+ function classifyReviewInlineFixRecapture(input) {
203461
+ if (input.reviewKind === "plan") return { recapture: false, reason: "plan-domain" };
203462
+ if (input.verdict !== "APPROVE" && input.verdict !== "APPROVE_WITH_NOTES") {
203463
+ return { recapture: false, reason: "not-approval-verdict" };
203464
+ }
203465
+ if (!input.reviewedCommitSha || !input.currentHeadSha || !input.baseRef) {
203466
+ return { recapture: false, reason: "missing-anchor" };
203467
+ }
203468
+ if (input.reviewedCommitSha === input.currentHeadSha) return { recapture: false, reason: "head-unchanged" };
203469
+ if (input.fastForwardAdvance === void 0 || input.baseIsAncestor === void 0) {
203470
+ return { recapture: false, reason: "probe-unavailable" };
203471
+ }
203472
+ if (!input.fastForwardAdvance) return { recapture: false, reason: "history-rewritten" };
203473
+ if (!input.baseIsAncestor) return { recapture: false, reason: "base-not-ancestor" };
203474
+ if (!input.fingerprintProbeAvailable) return { recapture: false, reason: "probe-unavailable" };
203475
+ return { recapture: true, reason: "recaptured" };
203476
+ }
203477
+ async function readHeadSha(worktreePath) {
203478
+ if (!worktreePath) return void 0;
203479
+ try {
203480
+ const { stdout } = await execFileAsync12("git", ["rev-parse", "HEAD"], { cwd: worktreePath, encoding: "utf8" });
203481
+ return stdout.trim() || void 0;
203482
+ } catch {
203483
+ return void 0;
203484
+ }
203485
+ }
203486
+ async function isFastForwardAdvance(worktreePath, fromSha, toSha) {
203487
+ if (!worktreePath || !fromSha || !toSha) return void 0;
203488
+ try {
203489
+ await execFileAsync12("git", ["merge-base", "--is-ancestor", fromSha, toSha], { cwd: worktreePath, encoding: "utf8" });
203490
+ return true;
203491
+ } catch (error3) {
203492
+ const code = error3?.code;
203493
+ return code === 1 ? false : void 0;
203494
+ }
203495
+ }
203496
+ var execFileAsync12;
203497
+ var init_review_inline_fix_recapture = __esm({
203498
+ "../engine/src/worktree/review-inline-fix-recapture.ts"() {
203499
+ "use strict";
203500
+ execFileAsync12 = promisify35(execFile18);
203109
203501
  }
203110
203502
  });
203111
203503
 
@@ -203295,8 +203687,8 @@ var init_workspace_zero_acquire = __esm({
203295
203687
  });
203296
203688
 
203297
203689
  // ../engine/src/execution/branch-attribution.ts
203298
- import { exec as exec25 } from "node:child_process";
203299
- import { promisify as promisify34 } from "node:util";
203690
+ import { exec as exec26 } from "node:child_process";
203691
+ import { promisify as promisify36 } from "node:util";
203300
203692
  function quoteShellArg6(value) {
203301
203693
  return `'${value.replace(/'/g, `'\\''`)}'`;
203302
203694
  }
@@ -203334,7 +203726,7 @@ function taskIdsMatch(a, b3) {
203334
203726
  return a.toUpperCase() === b3.toUpperCase();
203335
203727
  }
203336
203728
  async function collectOwnTaskCommitsForRange(opts) {
203337
- const execImpl = opts.execAsyncImpl ?? execAsync22;
203729
+ const execImpl = opts.execAsyncImpl ?? execAsync23;
203338
203730
  let logOutput;
203339
203731
  try {
203340
203732
  const result2 = await execImpl(
@@ -203375,7 +203767,7 @@ async function collectOwnTaskCommitsForRange(opts) {
203375
203767
  return { ownCommitCount: ownCommitShas.length, ownCommitShas };
203376
203768
  }
203377
203769
  async function filterFilesToOwnTaskCommits(opts) {
203378
- const execImpl = opts.execAsyncImpl ?? execAsync22;
203770
+ const execImpl = opts.execAsyncImpl ?? execAsync23;
203379
203771
  const runGit10 = async (command) => {
203380
203772
  try {
203381
203773
  const { stdout } = await execImpl(command, {
@@ -203441,11 +203833,11 @@ async function filterFilesToOwnTaskCommits(opts) {
203441
203833
  commitAttributions
203442
203834
  };
203443
203835
  }
203444
- var execAsync22, GIT_TIMEOUT_MS5, GIT_MAX_BUFFER2, BranchAttributionError, SilentNoOpAttributionMismatchError;
203836
+ var execAsync23, GIT_TIMEOUT_MS5, GIT_MAX_BUFFER2, BranchAttributionError, SilentNoOpAttributionMismatchError;
203445
203837
  var init_branch_attribution = __esm({
203446
203838
  "../engine/src/execution/branch-attribution.ts"() {
203447
203839
  "use strict";
203448
- execAsync22 = promisify34(exec25);
203840
+ execAsync23 = promisify36(exec26);
203449
203841
  GIT_TIMEOUT_MS5 = 3e4;
203450
203842
  GIT_MAX_BUFFER2 = 10 * 1024 * 1024;
203451
203843
  BranchAttributionError = class extends Error {
@@ -203479,103 +203871,9 @@ var init_branch_attribution = __esm({
203479
203871
  }
203480
203872
  });
203481
203873
 
203482
- // ../engine/src/executor/worktree-git-refs.ts
203483
- import { exec as exec26, execFile as execFile17, execSync as execSync3 } from "node:child_process";
203484
- import { promisify as promisify35 } from "node:util";
203485
- async function preExecutionWorktreeHasWork(worktreePath) {
203486
- try {
203487
- const { stdout: dirty } = await execFileAsync11("git", ["status", "--porcelain"], { cwd: worktreePath, timeout: 3e4 });
203488
- if (dirty.trim()) return true;
203489
- const { stdout: ahead } = await execFileAsync11("git", ["log", "--oneline", "@{upstream}..HEAD"], { cwd: worktreePath, timeout: 3e4 }).catch(async () => await execFileAsync11("git", ["log", "--oneline", "-1", "HEAD", "--not", "--remotes", "--branches=main", "--branches=master"], { cwd: worktreePath, timeout: 3e4 }));
203490
- return Boolean(ahead.trim());
203491
- } catch {
203492
- return true;
203493
- }
203494
- }
203495
- async function resolveContaminationBaseRef(worktreePath) {
203496
- try {
203497
- const { stdout } = await execAsync23(
203498
- "git merge-base HEAD main 2>/dev/null || git merge-base HEAD origin/main",
203499
- { cwd: worktreePath, encoding: "utf-8" }
203500
- );
203501
- const ref = stdout.trim();
203502
- return ref || void 0;
203503
- } catch (err) {
203504
- const errorMessage2 = err instanceof Error ? err.message : String(err);
203505
- executorLog.warn(`Failed merge-base lookup for contamination check in ${worktreePath}: ${errorMessage2}`);
203506
- return void 0;
203507
- }
203508
- }
203509
- async function resolveDiffBaseRef(worktreePath, baseCommitSha) {
203510
- if (baseCommitSha) return baseCommitSha;
203511
- try {
203512
- const { stdout } = await execAsync23(
203513
- "git merge-base HEAD origin/main 2>/dev/null || git merge-base HEAD main",
203514
- { cwd: worktreePath, encoding: "utf-8" }
203515
- );
203516
- const ref = stdout.trim();
203517
- if (ref) return ref;
203518
- } catch (mergeBaseErr) {
203519
- const mergeBaseMsg = mergeBaseErr instanceof Error ? mergeBaseErr.message : String(mergeBaseErr);
203520
- executorLog.warn(`Failed merge-base lookup for diff base in ${worktreePath}, trying HEAD~1 fallback: ${mergeBaseMsg}`);
203521
- }
203522
- try {
203523
- const { stdout } = await execAsync23("git rev-parse HEAD~1", {
203524
- cwd: worktreePath,
203525
- encoding: "utf-8"
203526
- });
203527
- return stdout.trim() || void 0;
203528
- } catch {
203529
- executorLog.debug(`Could not determine base commit for diff in ${worktreePath}`);
203530
- return void 0;
203531
- }
203532
- }
203533
- async function captureBaseCommitSha(store, task, worktreePath, audit, options = { isResume: false }) {
203534
- try {
203535
- if (options.isResume && task.baseCommitSha) {
203536
- try {
203537
- execSync3(`git merge-base --is-ancestor ${task.baseCommitSha} HEAD`, {
203538
- cwd: worktreePath,
203539
- stdio: "pipe"
203540
- });
203541
- executorLog.log(`${task.id}: preserved baseCommitSha ${task.baseCommitSha.slice(0, 7)} (resume)`);
203542
- await audit.git({
203543
- type: "commit:create",
203544
- target: task.baseCommitSha,
203545
- metadata: { purpose: "base", preserved: true }
203546
- });
203547
- return;
203548
- } catch {
203549
- }
203550
- }
203551
- const baseCommitSha = await resolveCapturedBaseCommitSha(worktreePath, {
203552
- warn: (msg) => executorLog.warn(`${task.id}: ${msg}`)
203553
- });
203554
- if (!baseCommitSha) {
203555
- throw new Error("could not resolve base commit SHA");
203556
- }
203557
- await store.updateTask(task.id, { baseCommitSha });
203558
- executorLog.debug(`${task.id}: captured baseCommitSha ${baseCommitSha.slice(0, 7)}`);
203559
- await audit.git({ type: "commit:create", target: baseCommitSha, metadata: { purpose: "base", preserved: false } });
203560
- } catch (err) {
203561
- const errorMessage2 = err instanceof Error ? err.message : String(err);
203562
- executorLog.debug(`Failed to capture baseCommitSha for ${task.id}: ${errorMessage2}`);
203563
- }
203564
- }
203565
- var execAsync23, execFileAsync11;
203566
- var init_worktree_git_refs = __esm({
203567
- "../engine/src/executor/worktree-git-refs.ts"() {
203568
- "use strict";
203569
- init_base_commit_capture();
203570
- init_logger3();
203571
- execAsync23 = promisify35(exec26);
203572
- execFileAsync11 = promisify35(execFile17);
203573
- }
203574
- });
203575
-
203576
203874
  // ../engine/src/executor/worktree-capture-modified-files.ts
203577
203875
  import { exec as exec27 } from "node:child_process";
203578
- import { promisify as promisify36 } from "node:util";
203876
+ import { promisify as promisify37 } from "node:util";
203579
203877
  async function captureModifiedFiles(worktreePath, baseCommitSha, taskId, audit, source = "unspecified") {
203580
203878
  try {
203581
203879
  const baseRef = await resolveDiffBaseRef(worktreePath, baseCommitSha);
@@ -203661,7 +203959,7 @@ var init_worktree_capture_modified_files = __esm({
203661
203959
  init_branch_attribution();
203662
203960
  init_logger3();
203663
203961
  init_worktree_git_refs();
203664
- execAsync24 = promisify36(exec27);
203962
+ execAsync24 = promisify37(exec27);
203665
203963
  }
203666
203964
  });
203667
203965
 
@@ -203826,6 +204124,27 @@ ${result2.review}`);
203826
204124
  summarySections.push(`[${repoRel}] ${result2.verdict}: ${result2.summary}`);
203827
204125
  }
203828
204126
  const aggregateVerdict = reviewedResults.some(({ result: result2 }) => result2.verdict === "RETHINK") ? "RETHINK" : reviewedResults.some(({ result: result2 }) => result2.verdict === "REVISE") ? "REVISE" : reviewedResults.some(({ result: result2 }) => !isApprovalFamilyVerdict(result2.verdict)) ? "UNAVAILABLE" : "APPROVE";
204127
+ let publishedModifiedFiles = modifiedFiles;
204128
+ if (isApprovalFamilyVerdict(aggregateVerdict) && evidence && options.workspaceRootDir) {
204129
+ try {
204130
+ const refreshed = await captureWorkspaceReviewEvidence({ task, workspaceRootDir: options.workspaceRootDir, settings: options.settings ?? {} });
204131
+ if (refreshed.outOfScopeRepositories.size > 0) {
204132
+ return { verdict: "UNAVAILABLE", retryable: false, review: "Workspace Code Review cannot approve changes outside confirmed scope after review.", summary: "Unavailable: changes outside confirmed scope after review", repositoryScopeRevision };
204133
+ }
204134
+ for (const updated of refreshed.repositories) {
204135
+ const original = evidence.repositories.find((candidate) => candidate.repository === updated.repository);
204136
+ const worktreePath = workspaceWorktrees[updated.repository]?.worktreePath;
204137
+ const outcome = repositoryReviewOutcomes.find((candidate) => candidate.repository === updated.repository && candidate.status === "REVIEWED");
204138
+ if (!original || !worktreePath || !updated.fingerprint || !outcome || !repositoryScope.has(updated.repository)) continue;
204139
+ if (await isFastForwardAdvance(worktreePath, original.branch, updated.branch)) {
204140
+ repositoryDiffFingerprints[updated.repository] = updated.fingerprint;
204141
+ outcome.fingerprint = updated.fingerprint;
204142
+ }
204143
+ }
204144
+ publishedModifiedFiles = refreshed.modifiedFiles;
204145
+ } catch {
204146
+ }
204147
+ }
203829
204148
  const blockingRepositories = reviewedResults.filter(({ result: result2 }) => !isApprovalFamilyVerdict(result2.verdict)).map(({ repository }) => repository);
203830
204149
  const blockingSummary = blockingRepositories.length > 0 ? `blocking repositories: ${blockingRepositories.join(", ")}` : `all ${repoKeys.length} modified in-scope repositories approved`;
203831
204150
  const coverageNotice = noVerdictRepositories.length > 0 ? `
@@ -203837,7 +204156,7 @@ Not covered by a verdict: ${noVerdictRepositories.join(", ")}.` : "";
203837
204156
  ${reviewSections.join("\n\n")}`,
203838
204157
  summary: `${aggregateVerdict} \u2014 ${blockingSummary} \u2014 ${summarySections.join(" | ")}`,
203839
204158
  repositoryDiffFingerprints,
203840
- repositoryModifiedFiles: modifiedFiles,
204159
+ repositoryModifiedFiles: publishedModifiedFiles,
203841
204160
  repositoryReviewOutcomes,
203842
204161
  ...findings.length > 0 ? { findings } : {},
203843
204162
  repositoryScopeRevision
@@ -203848,6 +204167,7 @@ var init_workspace_review_per_repo = __esm({
203848
204167
  "../engine/src/executor/workspace-review-per-repo.ts"() {
203849
204168
  "use strict";
203850
204169
  init_workspace_review_evidence();
204170
+ init_review_inline_fix_recapture();
203851
204171
  init_workspace_zero_acquire();
203852
204172
  init_worktree_capture_modified_files();
203853
204173
  hasRepositoryPrefix = (value, repoRel, separator) => value === repoRel || value?.startsWith(`${repoRel}${separator}`) === true;
@@ -205212,6 +205532,8 @@ async function runGraphCustomNode(deps, node, nodeTask, settings, columnBinding,
205212
205532
  }
205213
205533
  const optionalGroupId = typeof graphContext?.[WORKFLOW_OPTIONAL_GROUP_CONTEXT_KEY] === "string" ? graphContext[WORKFLOW_OPTIONAL_GROUP_CONTEXT_KEY] : void 0;
205214
205534
  const declaredReviewKind = cfg.reviewKind === "plan" || cfg.reviewKind === "code" ? cfg.reviewKind : graphContext?.[WORKFLOW_REVIEW_KIND_CONTEXT_KEY] === "plan" || graphContext?.[WORKFLOW_REVIEW_KIND_CONTEXT_KEY] === "code" ? graphContext[WORKFLOW_REVIEW_KIND_CONTEXT_KEY] : void 0;
205535
+ const effectiveStepId = optionalGroupId ?? node.id;
205536
+ const isContentBindingStep = requiresContentReviewProof(effectiveStepId, { reviewKind: declaredReviewKind });
205215
205537
  if (isFastExecutionMode(live) && graphContext?.["workflow:fast-lane-active"] === true && isFastLaneSkippableCustomNode(node, { optionalGroupId })) {
205216
205538
  executorLog.debug(`${live.id}: fast mode \u2014 skipping custom graph node '${node.id}'`);
205217
205539
  await deps.store.logEntry(
@@ -205406,6 +205728,7 @@ ${prompt}`;
205406
205728
  const now = (/* @__PURE__ */ new Date()).toISOString();
205407
205729
  const stepSkillName = executorKind === "skill" && typeof cfg.skillName === "string" && cfg.skillName.trim() ? cfg.skillName.trim() : void 0;
205408
205730
  const stepThinkingLevel = typeof cfg.thinkingLevel === "string" && WORKFLOW_THINKING_LEVEL_SET2.has(cfg.thinkingLevel) ? cfg.thinkingLevel : void 0;
205731
+ const readonlyMcpServers = Array.isArray(cfg.readonlyMcpServers) ? [...new Set(cfg.readonlyMcpServers.filter((name2) => typeof name2 === "string").map((name2) => name2.trim()).filter(Boolean))] : [];
205409
205732
  const step = {
205410
205733
  id: `graph:${node.id}`,
205411
205734
  name: typeof cfg.name === "string" && cfg.name.trim() ? cfg.name : node.id,
@@ -205422,7 +205745,8 @@ ${prompt}`;
205422
205745
  ...stepSkillName ? { skillName: stepSkillName } : {},
205423
205746
  ...cfg.requiresBrowser === true ? { requiresBrowser: true } : {},
205424
205747
  ...modelProvider && modelId ? { modelProvider, modelId } : {},
205425
- ...stepThinkingLevel ? { thinkingLevel: stepThinkingLevel } : {}
205748
+ ...stepThinkingLevel ? { thinkingLevel: stepThinkingLevel } : {},
205749
+ ...readonlyMcpServers.length > 0 ? { readonlyMcpServers } : {}
205426
205750
  };
205427
205751
  if (cfg.summaryTarget === "task") {
205428
205752
  step.summaryTarget = "task";
@@ -205536,12 +205860,36 @@ ${prompt}`;
205536
205860
  outcome = buildWorkspaceReviewOutcome(aggregate, { superseded: reviewSuperseded });
205537
205861
  }
205538
205862
  } else {
205539
- outcome = mode === "script" ? await deps.executeScriptWorkflowStep(live, step, worktreePath, settings, nodeEnv) : await deps.executeWorkflowStep(live, step, worktreePath, settings, nodeEnv, {
205540
- unattended,
205541
- principalAgentId,
205542
- outputLanguage,
205543
- ...nodeSessionBoundary ? { sessionBoundary: nodeSessionBoundary } : {}
205544
- });
205863
+ const dispatchSingularStep = async (reviewInputFingerprint) => {
205864
+ if (mode === "script") {
205865
+ const scriptOutcome = await deps.executeScriptWorkflowStep(live, step, worktreePath, settings, nodeEnv);
205866
+ return reviewInputFingerprint === void 0 ? scriptOutcome : { ...scriptOutcome, reviewInputFingerprint };
205867
+ }
205868
+ return deps.executeWorkflowStep(live, step, worktreePath, settings, nodeEnv, {
205869
+ unattended,
205870
+ principalAgentId,
205871
+ outputLanguage,
205872
+ ...nodeSessionBoundary ? { sessionBoundary: nodeSessionBoundary } : {},
205873
+ ...reviewInputFingerprint !== void 0 ? { reviewInputFingerprint } : {}
205874
+ });
205875
+ };
205876
+ if (isContentBindingStep && !workspaceConfig) {
205877
+ const proof = await resolveContentReviewInputProof(worktreePath, executionTarget.baseCommitSha);
205878
+ if (proof.kind === "unprovable") {
205879
+ const diagnostic = `${step.name} review input is unprovable (${proof.reason}); reviewer dispatch refused.`;
205880
+ await deps.store.logEntry(
205881
+ live.id,
205882
+ `[pre-merge] ${diagnostic}`,
205883
+ void 0,
205884
+ deps.getRunContextFor(live.id)
205885
+ );
205886
+ outcome = { success: false, error: diagnostic, failureValue: "review-input-unprovable" };
205887
+ } else {
205888
+ outcome = await dispatchSingularStep(proof.fingerprint);
205889
+ }
205890
+ } else {
205891
+ outcome = await dispatchSingularStep();
205892
+ }
205545
205893
  }
205546
205894
  if (declaredReviewKind && typeof outcome.output === "string") {
205547
205895
  const rawReviewOutput = outcome.output;
@@ -205628,6 +205976,7 @@ var init_run_graph_custom_node = __esm({
205628
205976
  init_create_authoritative_workflow_seams();
205629
205977
  init_verification_gate();
205630
205978
  init_worktree_dependency_install();
205979
+ init_review_diff_fingerprint();
205631
205980
  WORKFLOW_THINKING_LEVEL_SET2 = new Set(THINKING_LEVELS);
205632
205981
  WORKFLOW_STEP_NOT_RUN_REASON_SET2 = new Set(WORKFLOW_STEP_NOT_RUN_REASONS);
205633
205982
  WORKSPACE_REPO_REVIEW_NO_NOTES_NOTICE = "The repository reviewer recorded no notes for this verdict.";
@@ -207687,7 +208036,7 @@ var init_agent_heartbeat_error_recovery = __esm({
207687
208036
 
207688
208037
  // ../engine/src/agent-heartbeat.ts
207689
208038
  import { Type as Type3 } from "@earendil-works/pi-ai";
207690
- import { createHash as createHash31 } from "node:crypto";
208039
+ import { createHash as createHash32 } from "node:crypto";
207691
208040
  async function resolveHeartbeatReboundColumn(taskStore, taskId) {
207692
208041
  try {
207693
208042
  return resolveReboundTarget(await resolveWorkflowIrForTask(taskStore, taskId)) ?? "todo";
@@ -207842,7 +208191,7 @@ function truncatePrompt(text3, maxChars) {
207842
208191
  ... (truncated, ${text3.length} chars)`;
207843
208192
  }
207844
208193
  function shortContentHash(value) {
207845
- return createHash31("sha256").update(value).digest("hex").slice(0, 8);
208194
+ return createHash32("sha256").update(value).digest("hex").slice(0, 8);
207846
208195
  }
207847
208196
  function buildIdentitySnapshot(args) {
207848
208197
  const { agent, resolvedInstructions, workspaceMemory } = args;
@@ -212275,6 +212624,91 @@ var init_auto_merge_finalization = __esm({
212275
212624
  }
212276
212625
  });
212277
212626
 
212627
+ // ../engine/src/merge/merge-content-capture.ts
212628
+ async function captureMergeContentDescriptor(task, deps) {
212629
+ if (describeMergeContentShape(task) === "workspace") {
212630
+ try {
212631
+ const evidence = await captureWorkspaceReviewEvidence({
212632
+ task,
212633
+ workspaceRootDir: deps.workspaceRootDir,
212634
+ settings: deps.settings
212635
+ });
212636
+ return {
212637
+ kind: "workspace",
212638
+ repositories: {
212639
+ state: "captured",
212640
+ fingerprints: Object.fromEntries(evidence.repositories.filter((repository) => repository.fingerprint).map((repository) => [repository.repository, repository.fingerprint])),
212641
+ inScopeModified: [...evidence.modifiedRepositories].sort()
212642
+ }
212643
+ };
212644
+ } catch {
212645
+ return { kind: "workspace", repositories: { state: "unavailable", reason: "workspace-evidence-capture-failed" } };
212646
+ }
212647
+ }
212648
+ const baseRef = task.worktree ? await resolveDiffBaseRef(task.worktree, task.baseCommitSha) : void 0;
212649
+ const probe = await probeReviewDiffFingerprint(task.worktree, baseRef);
212650
+ return { kind: "singular", diff: probe };
212651
+ }
212652
+ var init_merge_content_capture = __esm({
212653
+ "../engine/src/merge/merge-content-capture.ts"() {
212654
+ "use strict";
212655
+ init_src2();
212656
+ init_worktree_git_refs();
212657
+ init_review_diff_fingerprint();
212658
+ init_workspace_review_evidence();
212659
+ }
212660
+ });
212661
+
212662
+ // ../engine/src/merge/stale-content-review-reroute.ts
212663
+ function isContentReviewNode(node) {
212664
+ if (node.kind !== "optional-group" && node.kind !== "step-review") return false;
212665
+ const name2 = typeof node.config?.name === "string" ? node.config.name : "";
212666
+ return node.id === "code-review" || node.config?.reviewKind === "code" || /code review/i.test(name2);
212667
+ }
212668
+ async function rerouteSingularStaleContentToReview(store, task, options) {
212669
+ const mergeContent = options.mergeContent;
212670
+ if (mergeContent.kind !== "singular" || task.workspaceWorktrees !== void 0) {
212671
+ return { rerouted: false, reason: "not-singular" };
212672
+ }
212673
+ if (task.paused || task.userPaused || task.deletedAt || task.autoMerge === false) {
212674
+ return { rerouted: false, reason: "operator-held" };
212675
+ }
212676
+ if (mergeContent.diff.state !== "fingerprint") return { rerouted: false, reason: "no-progress" };
212677
+ const currentFingerprint = mergeContent.diff.fingerprint;
212678
+ const ir2 = await resolveWorkflowIrForTask(store, task.id);
212679
+ const selection = store.getTaskWorkflowSelectionAsync ? await store.getTaskWorkflowSelectionAsync(task.id) : store.getTaskWorkflowSelection(task.id);
212680
+ const selected = selection?.stepIds ?? [];
212681
+ const nodes2 = ir2.nodes.filter((node2) => {
212682
+ if (!isContentReviewNode(node2) || !options.requiredPreMergeStepIds.has(node2.id)) return false;
212683
+ return node2.config?.defaultOn === true || selected.includes(node2.id);
212684
+ });
212685
+ const node = nodes2.find((candidate) => {
212686
+ const latest = task.workflowStepResults?.filter((result3) => result3.workflowStepId === candidate.id).at(-1);
212687
+ return latest?.reviewInputFingerprint !== currentFingerprint;
212688
+ });
212689
+ if (!node) return { rerouted: false, reason: nodes2.length ? "no-progress" : "no-review-route" };
212690
+ const items = await store.listWorkflowWorkItemsForTask(task.id);
212691
+ const result2 = await store.seedWorkspaceCodeReviewContinuationIfIdle({
212692
+ taskId: task.id,
212693
+ nodeId: node.id,
212694
+ kind: "task",
212695
+ state: "runnable",
212696
+ runId: `${task.id}:stale-content-review-reroute:${node.id}:${items.length}`,
212697
+ stableWorkflowRunId: `${task.id}:${ir2.name}`,
212698
+ continuationSequence: items.length,
212699
+ sourceColumn: task.column,
212700
+ targetColumn: node.column ?? task.column,
212701
+ irHash: computeWorkflowIrPin(ir2, node.id).irHash
212702
+ });
212703
+ return result2.seeded ? { rerouted: true, reason: "seeded", nodeId: node.id, workflowStepId: node.id } : { rerouted: false, reason: "active-continuation", nodeId: node.id, workflowStepId: node.id };
212704
+ }
212705
+ var init_stale_content_review_reroute = __esm({
212706
+ "../engine/src/merge/stale-content-review-reroute.ts"() {
212707
+ "use strict";
212708
+ init_src2();
212709
+ }
212710
+ });
212711
+
212278
212712
  // ../engine/src/healing/auto-recovery.ts
212279
212713
  function actionForMode(mode, failureClass) {
212280
212714
  if (mode === "off") return "pause";
@@ -212431,10 +212865,10 @@ var init_stranded_continuation_reclaim = __esm({
212431
212865
  });
212432
212866
 
212433
212867
  // ../engine/src/merge/workspace-land-predicate.ts
212434
- import { execFile as execFile18 } from "node:child_process";
212435
- import { promisify as promisify37 } from "node:util";
212868
+ import { execFile as execFile19 } from "node:child_process";
212869
+ import { promisify as promisify38 } from "node:util";
212436
212870
  async function git4(args, cwd, opts = {}) {
212437
- const { stdout } = await execFileAsync12("git", args, {
212871
+ const { stdout } = await execFileAsync13("git", args, {
212438
212872
  cwd,
212439
212873
  encoding: "utf-8",
212440
212874
  timeout: opts.timeout ?? 12e4,
@@ -212513,11 +212947,11 @@ async function isRepoLanded(repoRootDir, integrationBranch, landedSha, taskId, b
212513
212947
  await findProvenLandedCommit(repoRootDir, integrationBranch, landedSha, taskId, branch, revertBoundarySha, taskCreatedAt)
212514
212948
  );
212515
212949
  }
212516
- var execFileAsync12, FUSION_TASK_ID_TRAILER_KEY2, TRAILER_SCAN_CLOCK_SKEW_MS, UNBOUNDED_TRAILER_SCAN_WINDOW_MS, UNBOUNDED_TRAILER_SCAN_MAX_COMMITS;
212950
+ var execFileAsync13, FUSION_TASK_ID_TRAILER_KEY2, TRAILER_SCAN_CLOCK_SKEW_MS, UNBOUNDED_TRAILER_SCAN_WINDOW_MS, UNBOUNDED_TRAILER_SCAN_MAX_COMMITS;
212517
212951
  var init_workspace_land_predicate = __esm({
212518
212952
  "../engine/src/merge/workspace-land-predicate.ts"() {
212519
212953
  "use strict";
212520
- execFileAsync12 = promisify37(execFile18);
212954
+ execFileAsync13 = promisify38(execFile19);
212521
212955
  FUSION_TASK_ID_TRAILER_KEY2 = "Fusion-Task-Id";
212522
212956
  TRAILER_SCAN_CLOCK_SKEW_MS = 24 * 60 * 60 * 1e3;
212523
212957
  UNBOUNDED_TRAILER_SCAN_WINDOW_MS = 30 * 24 * 60 * 60 * 1e3;
@@ -212542,7 +212976,7 @@ var init_workspace_land_failure = __esm({
212542
212976
 
212543
212977
  // ../engine/src/merge/already-merged-detector.ts
212544
212978
  import { exec as exec28, execSync as execSync4 } from "node:child_process";
212545
- import { promisify as promisify38 } from "node:util";
212979
+ import { promisify as promisify39 } from "node:util";
212546
212980
  function shellQuote(value) {
212547
212981
  return `'${value.replace(/'/g, "'\\''")}'`;
212548
212982
  }
@@ -212818,7 +213252,7 @@ var init_already_merged_detector = __esm({
212818
213252
  "../engine/src/merge/already-merged-detector.ts"() {
212819
213253
  "use strict";
212820
213254
  init_worktree_names();
212821
- execAsync25 = promisify38(exec28);
213255
+ execAsync25 = promisify39(exec28);
212822
213256
  }
212823
213257
  });
212824
213258
 
@@ -212950,10 +213384,10 @@ var init_review_empty_content_close = __esm({
212950
213384
  });
212951
213385
 
212952
213386
  // ../engine/src/merge/merger-ref-update-advance.ts
212953
- import { execFile as execFile19 } from "node:child_process";
212954
- import { promisify as promisify39 } from "node:util";
213387
+ import { execFile as execFile20 } from "node:child_process";
213388
+ import { promisify as promisify40 } from "node:util";
212955
213389
  async function runGit3(args, cwd) {
212956
- return await execFileAsync13("git", args, {
213390
+ return await execFileAsync14("git", args, {
212957
213391
  cwd,
212958
213392
  encoding: "utf-8",
212959
213393
  timeout: 3e4
@@ -213100,11 +213534,11 @@ async function advanceIntegrationBranchRef(args) {
213100
213534
  };
213101
213535
  }
213102
213536
  }
213103
- var execFileAsync13, testHooks, IntegrationBranchConcurrentAdvanceError;
213537
+ var execFileAsync14, testHooks, IntegrationBranchConcurrentAdvanceError;
213104
213538
  var init_merger_ref_update_advance = __esm({
213105
213539
  "../engine/src/merge/merger-ref-update-advance.ts"() {
213106
213540
  "use strict";
213107
- execFileAsync13 = promisify39(execFile19);
213541
+ execFileAsync14 = promisify40(execFile20);
213108
213542
  testHooks = {
213109
213543
  runGit: runGit3
213110
213544
  };
@@ -213132,7 +213566,7 @@ var init_merger_ref_update_advance = __esm({
213132
213566
 
213133
213567
  // ../engine/src/merge/group-merge-coordinator.ts
213134
213568
  import * as childProcess2 from "node:child_process";
213135
- import { promisify as promisify40 } from "node:util";
213569
+ import { promisify as promisify41 } from "node:util";
213136
213570
  function evaluateBranchGroupCompletion(input) {
213137
213571
  const landedMemberIds = [];
213138
213572
  const pendingMemberIds = [];
@@ -213172,10 +213606,10 @@ function evaluateBranchGroupPromotion(input) {
213172
213606
  }
213173
213607
  async function ensureGroupBranchExists(rootDir, branchName, startPoint) {
213174
213608
  try {
213175
- await execFileAsync14("git", ["show-ref", "--verify", "--quiet", `refs/heads/${branchName}`], { cwd: rootDir });
213609
+ await execFileAsync15("git", ["show-ref", "--verify", "--quiet", `refs/heads/${branchName}`], { cwd: rootDir });
213176
213610
  return;
213177
213611
  } catch {
213178
- await execFileAsync14("git", ["branch", branchName, startPoint], { cwd: rootDir });
213612
+ await execFileAsync15("git", ["branch", branchName, startPoint], { cwd: rootDir });
213179
213613
  }
213180
213614
  }
213181
213615
  function throwIfPromotionAborted(signal) {
@@ -213280,14 +213714,14 @@ async function promoteBranchGroupInner(input) {
213280
213714
  if (!needsPrRepair && !isPrMode) {
213281
213715
  throwIfPromotionAborted(input.signal);
213282
213716
  await ensureGroupBranchExists(input.rootDir, group.branchName, integrationBranch);
213283
- const currentBranch2 = (await execFileAsync14("git", ["rev-parse", "--abbrev-ref", "HEAD"], { cwd: input.rootDir })).stdout.trim();
213717
+ const currentBranch2 = (await execFileAsync15("git", ["rev-parse", "--abbrev-ref", "HEAD"], { cwd: input.rootDir })).stdout.trim();
213284
213718
  try {
213285
213719
  throwIfPromotionAborted(input.signal);
213286
- await execFileAsync14("git", ["checkout", integrationBranch], { cwd: input.rootDir, signal: input.signal });
213720
+ await execFileAsync15("git", ["checkout", integrationBranch], { cwd: input.rootDir, signal: input.signal });
213287
213721
  throwIfPromotionAborted(input.signal);
213288
- await execFileAsync14("git", ["merge", "--no-ff", "--no-edit", group.branchName], { cwd: input.rootDir, signal: input.signal });
213722
+ await execFileAsync15("git", ["merge", "--no-ff", "--no-edit", group.branchName], { cwd: input.rootDir, signal: input.signal });
213289
213723
  } finally {
213290
- await execFileAsync14("git", ["checkout", currentBranch2], { cwd: input.rootDir });
213724
+ await execFileAsync15("git", ["checkout", currentBranch2], { cwd: input.rootDir });
213291
213725
  }
213292
213726
  }
213293
213727
  let prNumber = group.prNumber;
@@ -213411,20 +213845,20 @@ async function resolveBranchGroupMergeRouting(input) {
213411
213845
  )
213412
213846
  };
213413
213847
  }
213414
- var execFileAsync14, promotionLocks;
213848
+ var execFileAsync15, promotionLocks;
213415
213849
  var init_group_merge_coordinator = __esm({
213416
213850
  "../engine/src/merge/group-merge-coordinator.ts"() {
213417
213851
  "use strict";
213418
213852
  init_src2();
213419
213853
  init_integration_branch();
213420
- execFileAsync14 = (file, args, opts) => promisify40(childProcess2.execFile)(file, args, opts);
213854
+ execFileAsync15 = (file, args, opts) => promisify41(childProcess2.execFile)(file, args, opts);
213421
213855
  promotionLocks = /* @__PURE__ */ new Map();
213422
213856
  }
213423
213857
  });
213424
213858
 
213425
213859
  // ../engine/src/merge/merger-bare-subject.ts
213426
213860
  import { exec as exec29 } from "node:child_process";
213427
- import { promisify as promisify41 } from "node:util";
213861
+ import { promisify as promisify42 } from "node:util";
213428
213862
  function quoteArg(value) {
213429
213863
  return `'${value.replace(/'/g, "'\\''")}'`;
213430
213864
  }
@@ -213464,7 +213898,7 @@ var init_merger_bare_subject = __esm({
213464
213898
  "use strict";
213465
213899
  init_src2();
213466
213900
  init_logger3();
213467
- execAsync26 = promisify41(exec29);
213901
+ execAsync26 = promisify42(exec29);
213468
213902
  BARE_MERGE_SUBJECT_RE = /^[a-z]+(?:\([^)]+\))?!?:\s*merge\s+\S+\s*$/i;
213469
213903
  }
213470
213904
  });
@@ -213472,7 +213906,7 @@ var init_merger_bare_subject = __esm({
213472
213906
  // ../engine/src/recovery/foreign-only-contamination.ts
213473
213907
  import { exec as exec30 } from "node:child_process";
213474
213908
  import { existsSync as existsSync62 } from "node:fs";
213475
- import { promisify as promisify42 } from "node:util";
213909
+ import { promisify as promisify43 } from "node:util";
213476
213910
  function quote3(value) {
213477
213911
  return `'${value.replace(/'/g, `'\\''`)}'`;
213478
213912
  }
@@ -213580,7 +214014,7 @@ var init_foreign_only_contamination = __esm({
213580
214014
  init_lifecycle_move();
213581
214015
  init_branch_conflicts();
213582
214016
  init_worktree_pool();
213583
- execAsync27 = promisify42(exec30);
214017
+ execAsync27 = promisify43(exec30);
213584
214018
  GIT_TIMEOUT_MS6 = 3e4;
213585
214019
  GIT_MAX_BUFFER3 = 10 * 1024 * 1024;
213586
214020
  }
@@ -213677,7 +214111,7 @@ var init_surfacing_sweeps = __esm({
213677
214111
  });
213678
214112
 
213679
214113
  // ../engine/src/self-healing-git-evidence.ts
213680
- import { promisify as promisify43 } from "node:util";
214114
+ import { promisify as promisify44 } from "node:util";
213681
214115
  import { exec as exec31 } from "node:child_process";
213682
214116
  function classifyBranchProbeError(error3) {
213683
214117
  if (typeof error3 === "object" && error3 !== null && "code" in error3 && error3.code === 1 && !error3.killed && (error3.signal === null || error3.signal === void 0)) {
@@ -213721,7 +214155,7 @@ var init_self_healing_git_evidence = __esm({
213721
214155
  init_already_merged_detector();
213722
214156
  init_logger3();
213723
214157
  log32 = createLogger2("self-healing");
213724
- execAsync28 = promisify43(exec31);
214158
+ execAsync28 = promisify44(exec31);
213725
214159
  SelfHealingGitEvidence = class {
213726
214160
  async findLandedTaskCommit(task, options) {
213727
214161
  const rebaseBaseSha = task.mergeDetails?.rebaseBaseSha;
@@ -214083,12 +214517,12 @@ var init_self_healing_constants = __esm({
214083
214517
  });
214084
214518
 
214085
214519
  // ../engine/src/healing/self-healing-branch.ts
214086
- import { promisify as promisify44 } from "node:util";
214087
- import { execFile as execFile21 } from "node:child_process";
214520
+ import { promisify as promisify45 } from "node:util";
214521
+ import { execFile as execFile22 } from "node:child_process";
214088
214522
  async function isBranchAheadOfBase(task, rootDir, preferredBaseRef) {
214089
214523
  const branchName = resolveTaskWorkingBranch(task);
214090
214524
  try {
214091
- await execFileAsync15("git", ["rev-parse", "--verify", branchName], {
214525
+ await execFileAsync16("git", ["rev-parse", "--verify", branchName], {
214092
214526
  cwd: rootDir,
214093
214527
  timeout: 3e4
214094
214528
  });
@@ -214098,14 +214532,14 @@ async function isBranchAheadOfBase(task, rootDir, preferredBaseRef) {
214098
214532
  const requestedBaseRef = preferredBaseRef || task.mergeDetails?.mergeTargetBranch || await resolveIntegrationBranch(rootDir, void 0);
214099
214533
  let resolvedBaseRef = requestedBaseRef;
214100
214534
  try {
214101
- await execFileAsync15("git", ["rev-parse", "--verify", requestedBaseRef], {
214535
+ await execFileAsync16("git", ["rev-parse", "--verify", requestedBaseRef], {
214102
214536
  cwd: rootDir,
214103
214537
  timeout: 3e4
214104
214538
  });
214105
214539
  } catch {
214106
214540
  const remoteRef = `origin/${requestedBaseRef}`;
214107
214541
  try {
214108
- await execFileAsync15("git", ["rev-parse", "--verify", remoteRef], {
214542
+ await execFileAsync16("git", ["rev-parse", "--verify", remoteRef], {
214109
214543
  cwd: rootDir,
214110
214544
  timeout: 3e4
214111
214545
  });
@@ -214115,7 +214549,7 @@ async function isBranchAheadOfBase(task, rootDir, preferredBaseRef) {
214115
214549
  }
214116
214550
  }
214117
214551
  try {
214118
- const { stdout } = await execFileAsync15(
214552
+ const { stdout } = await execFileAsync16(
214119
214553
  "git",
214120
214554
  ["rev-list", "--count", `${resolvedBaseRef}..${branchName}`],
214121
214555
  { cwd: rootDir, timeout: 3e4 }
@@ -214133,7 +214567,7 @@ async function isBranchAheadOfBase(task, rootDir, preferredBaseRef) {
214133
214567
  return null;
214134
214568
  }
214135
214569
  }
214136
- var log33, execFileAsync15;
214570
+ var log33, execFileAsync16;
214137
214571
  var init_self_healing_branch = __esm({
214138
214572
  "../engine/src/healing/self-healing-branch.ts"() {
214139
214573
  "use strict";
@@ -214141,7 +214575,7 @@ var init_self_healing_branch = __esm({
214141
214575
  init_integration_branch();
214142
214576
  init_logger3();
214143
214577
  log33 = createLogger2("self-healing");
214144
- execFileAsync15 = promisify44(execFile21);
214578
+ execFileAsync16 = promisify45(execFile22);
214145
214579
  }
214146
214580
  });
214147
214581
 
@@ -214978,7 +215412,7 @@ var init_merger_workspace_test_commands = __esm({
214978
215412
  });
214979
215413
 
214980
215414
  // ../engine/src/merge/merger-file-scope.ts
214981
- import { promisify as promisify45 } from "node:util";
215415
+ import { promisify as promisify46 } from "node:util";
214982
215416
  import { exec as exec32 } from "node:child_process";
214983
215417
  function parseDiffStat(diffStat) {
214984
215418
  const entries = [];
@@ -215190,7 +215624,7 @@ var init_merger_file_scope = __esm({
215190
215624
  init_merger_glob();
215191
215625
  init_logger3();
215192
215626
  init_merge_trait();
215193
- execAsync29 = promisify45(exec32);
215627
+ execAsync29 = promisify46(exec32);
215194
215628
  FileScopeViolationError = class extends Error {
215195
215629
  taskId;
215196
215630
  stagedFiles;
@@ -215250,7 +215684,7 @@ var init_merger_errors = __esm({
215250
215684
  });
215251
215685
 
215252
215686
  // ../engine/src/merge/merger-owned-landed.ts
215253
- import { promisify as promisify46 } from "node:util";
215687
+ import { promisify as promisify47 } from "node:util";
215254
215688
  import * as childProcess3 from "node:child_process";
215255
215689
  function escapeRegexForOwnership(value) {
215256
215690
  return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
@@ -215267,8 +215701,8 @@ function commitOwnedByTask2(taskId, subject, body) {
215267
215701
  async function findOwnedLandedCommitForTask(rootDir, task) {
215268
215702
  const tryHydrate = async (sha) => {
215269
215703
  try {
215270
- await execFileAsync16("git", ["merge-base", "--is-ancestor", sha, "HEAD"], { cwd: rootDir });
215271
- const { stdout } = await execFileAsync16("git", ["log", "-1", "--format=%H%x1f%s%x1f%b", sha], {
215704
+ await execFileAsync17("git", ["merge-base", "--is-ancestor", sha, "HEAD"], { cwd: rootDir });
215705
+ const { stdout } = await execFileAsync17("git", ["log", "-1", "--format=%H%x1f%s%x1f%b", sha], {
215272
215706
  cwd: rootDir,
215273
215707
  encoding: "utf-8"
215274
215708
  });
@@ -215276,7 +215710,7 @@ async function findOwnedLandedCommitForTask(rootDir, task) {
215276
215710
  if (!resolvedSha || !commitOwnedByTask2(task.id, subject, body)) return null;
215277
215711
  const owned = { sha: resolvedSha, subject };
215278
215712
  try {
215279
- const { stdout: statsOut } = await execFileAsync16("git", ["show", "--shortstat", "--format=", resolvedSha], {
215713
+ const { stdout: statsOut } = await execFileAsync17("git", ["show", "--shortstat", "--format=", resolvedSha], {
215280
215714
  cwd: rootDir,
215281
215715
  encoding: "utf-8"
215282
215716
  });
@@ -215299,7 +215733,7 @@ async function findOwnedLandedCommitForTask(rootDir, task) {
215299
215733
  ];
215300
215734
  for (const args of searches) {
215301
215735
  try {
215302
- const { stdout } = await execFileAsync16("git", args, { cwd: rootDir, encoding: "utf-8" });
215736
+ const { stdout } = await execFileAsync17("git", args, { cwd: rootDir, encoding: "utf-8" });
215303
215737
  const first = stdout.trim().split("\n").find(Boolean);
215304
215738
  if (!first) continue;
215305
215739
  const [sha] = first.split("");
@@ -215320,14 +215754,14 @@ async function classifyOwnedLandedEvidence(rootDir, task, opts) {
215320
215754
  const ownedCommit = await findOwnedLandedCommitForTask(rootDir, task);
215321
215755
  if (ownedCommit) {
215322
215756
  try {
215323
- await execFileAsync16("git", ["merge-base", "--is-ancestor", ownedCommit.sha, mergeTargetBranch], { cwd: rootDir });
215757
+ await execFileAsync17("git", ["merge-base", "--is-ancestor", ownedCommit.sha, mergeTargetBranch], { cwd: rootDir });
215324
215758
  return { kind: "owned-commit", commit: ownedCommit };
215325
215759
  } catch {
215326
215760
  }
215327
215761
  }
215328
215762
  let aheadCount = null;
215329
215763
  try {
215330
- const { stdout } = await execFileAsync16("git", ["rev-list", "--count", `${mergeTargetBranch}..${branch}`], {
215764
+ const { stdout } = await execFileAsync17("git", ["rev-list", "--count", `${mergeTargetBranch}..${branch}`], {
215331
215765
  cwd: rootDir,
215332
215766
  encoding: "utf-8"
215333
215767
  });
@@ -215339,7 +215773,7 @@ async function classifyOwnedLandedEvidence(rootDir, task, opts) {
215339
215773
  let baseReachableFromTarget = false;
215340
215774
  if (task.baseCommitSha) {
215341
215775
  try {
215342
- await execFileAsync16("git", ["merge-base", "--is-ancestor", task.baseCommitSha, mergeTargetBranch], { cwd: rootDir });
215776
+ await execFileAsync17("git", ["merge-base", "--is-ancestor", task.baseCommitSha, mergeTargetBranch], { cwd: rootDir });
215343
215777
  baseReachableFromTarget = true;
215344
215778
  } catch {
215345
215779
  baseReachableFromTarget = false;
@@ -215350,7 +215784,7 @@ async function classifyOwnedLandedEvidence(rootDir, task, opts) {
215350
215784
  }
215351
215785
  if (aheadCount !== null && aheadCount > 0) {
215352
215786
  try {
215353
- const { stdout: mergeBaseOut } = await execFileAsync16(
215787
+ const { stdout: mergeBaseOut } = await execFileAsync17(
215354
215788
  "git",
215355
215789
  ["merge-base", mergeTargetBranch, branch],
215356
215790
  { cwd: rootDir, encoding: "utf-8" }
@@ -215359,7 +215793,7 @@ async function classifyOwnedLandedEvidence(rootDir, task, opts) {
215359
215793
  if (mergeBase) {
215360
215794
  let ownDiffEmpty = false;
215361
215795
  try {
215362
- await execFileAsync16(
215796
+ await execFileAsync17(
215363
215797
  "git",
215364
215798
  ["diff", "--quiet", `${mergeBase}..${branch}`],
215365
215799
  { cwd: rootDir }
@@ -215372,7 +215806,7 @@ async function classifyOwnedLandedEvidence(rootDir, task, opts) {
215372
215806
  let mergeBaseReachable = baseReachableFromTarget;
215373
215807
  if (!mergeBaseReachable) {
215374
215808
  try {
215375
- await execFileAsync16(
215809
+ await execFileAsync17(
215376
215810
  "git",
215377
215811
  ["merge-base", "--is-ancestor", mergeBase, mergeTargetBranch],
215378
215812
  { cwd: rootDir }
@@ -215392,7 +215826,7 @@ async function classifyOwnedLandedEvidence(rootDir, task, opts) {
215392
215826
  }
215393
215827
  let branchExists = false;
215394
215828
  try {
215395
- await execFileAsync16("git", ["show-ref", "--verify", "--quiet", `refs/heads/${branch}`], { cwd: rootDir });
215829
+ await execFileAsync17("git", ["show-ref", "--verify", "--quiet", `refs/heads/${branch}`], { cwd: rootDir });
215396
215830
  branchExists = true;
215397
215831
  } catch {
215398
215832
  branchExists = false;
@@ -215410,7 +215844,7 @@ async function classifyOwnedLandedEvidence(rootDir, task, opts) {
215410
215844
  }
215411
215845
  if (task.baseCommitSha && !baseReachableFromTarget) {
215412
215846
  try {
215413
- const { stdout } = await execFileAsync16("git", ["for-each-ref", "--format=%(refname:short)", "refs/heads/fusion"], {
215847
+ const { stdout } = await execFileAsync17("git", ["for-each-ref", "--format=%(refname:short)", "refs/heads/fusion"], {
215414
215848
  cwd: rootDir,
215415
215849
  encoding: "utf-8"
215416
215850
  });
@@ -215418,7 +215852,7 @@ async function classifyOwnedLandedEvidence(rootDir, task, opts) {
215418
215852
  for (const ref of refs) {
215419
215853
  if (ref === branch) continue;
215420
215854
  try {
215421
- await execFileAsync16("git", ["merge-base", "--is-ancestor", task.baseCommitSha, ref], { cwd: rootDir });
215855
+ await execFileAsync17("git", ["merge-base", "--is-ancestor", task.baseCommitSha, ref], { cwd: rootDir });
215422
215856
  return {
215423
215857
  kind: "unproven",
215424
215858
  reason: "foreign-start-point",
@@ -215432,7 +215866,7 @@ async function classifyOwnedLandedEvidence(rootDir, task, opts) {
215432
215866
  }
215433
215867
  if (aheadCount !== null && aheadCount > 0) {
215434
215868
  try {
215435
- const { stdout } = await execFileAsync16("git", ["log", "--format=%s%x1f%b", `${mergeTargetBranch}..${branch}`], {
215869
+ const { stdout } = await execFileAsync17("git", ["log", "--format=%s%x1f%b", `${mergeTargetBranch}..${branch}`], {
215436
215870
  cwd: rootDir,
215437
215871
  encoding: "utf-8"
215438
215872
  });
@@ -215473,20 +215907,20 @@ async function classifyOwnedLandedEvidence(rootDir, task, opts) {
215473
215907
  }
215474
215908
  };
215475
215909
  }
215476
- var execFileAsync16, FUSION_TASK_ID_TRAILER_KEY3;
215910
+ var execFileAsync17, FUSION_TASK_ID_TRAILER_KEY3;
215477
215911
  var init_merger_owned_landed = __esm({
215478
215912
  "../engine/src/merge/merger-owned-landed.ts"() {
215479
215913
  "use strict";
215480
215914
  init_merger_file_scope();
215481
215915
  init_worktree_names();
215482
- execFileAsync16 = (file, args, opts) => promisify46(childProcess3.execFile)(file, args, opts);
215916
+ execFileAsync17 = (file, args, opts) => promisify47(childProcess3.execFile)(file, args, opts);
215483
215917
  FUSION_TASK_ID_TRAILER_KEY3 = "Fusion-Task-Id";
215484
215918
  }
215485
215919
  });
215486
215920
 
215487
215921
  // ../engine/src/merge/merger-conflict-resolution.ts
215488
- import { promisify as promisify47 } from "node:util";
215489
- import { exec as exec33, execFile as execFile23 } from "node:child_process";
215922
+ import { promisify as promisify48 } from "node:util";
215923
+ import { exec as exec33, execFile as execFile24 } from "node:child_process";
215490
215924
  async function getConflictedFiles(cwd) {
215491
215925
  try {
215492
215926
  const { stdout } = await execAsync30("git diff --name-only --diff-filter=U", {
@@ -215502,7 +215936,7 @@ async function getConflictedFiles(cwd) {
215502
215936
  }
215503
215937
  async function isTrivialWhitespaceConflict(filePath, cwd) {
215504
215938
  try {
215505
- const { stdout } = await execFileAsync17(
215939
+ const { stdout } = await execFileAsync18(
215506
215940
  "git",
215507
215941
  ["diff", "-p", "-w", `:2:${filePath}`, `:3:${filePath}`],
215508
215942
  { cwd, encoding: "utf-8" }
@@ -215538,8 +215972,8 @@ async function classifyConflict(filePath, cwd) {
215538
215972
  }
215539
215973
  async function resolveWithOurs(filePath, cwd) {
215540
215974
  try {
215541
- await execFileAsync17("git", ["checkout", "--ours", "--", filePath], { cwd });
215542
- await execFileAsync17("git", ["add", "--", filePath], { cwd });
215975
+ await execFileAsync18("git", ["checkout", "--ours", "--", filePath], { cwd });
215976
+ await execFileAsync18("git", ["add", "--", filePath], { cwd });
215543
215977
  mergerLog.debug(`Auto-resolved ${filePath} using --ours`);
215544
215978
  } catch (error3) {
215545
215979
  throw new Error(`Failed to auto-resolve ${filePath} with ours: ${error3}`);
@@ -215547,8 +215981,8 @@ async function resolveWithOurs(filePath, cwd) {
215547
215981
  }
215548
215982
  async function resolveWithTheirs(filePath, cwd) {
215549
215983
  try {
215550
- await execFileAsync17("git", ["checkout", "--theirs", "--", filePath], { cwd });
215551
- await execFileAsync17("git", ["add", "--", filePath], { cwd });
215984
+ await execFileAsync18("git", ["checkout", "--theirs", "--", filePath], { cwd });
215985
+ await execFileAsync18("git", ["add", "--", filePath], { cwd });
215552
215986
  mergerLog.debug(`Auto-resolved ${filePath} using --theirs`);
215553
215987
  } catch (error3) {
215554
215988
  throw new Error(`Failed to auto-resolve ${filePath} with theirs: ${error3}`);
@@ -215556,8 +215990,8 @@ async function resolveWithTheirs(filePath, cwd) {
215556
215990
  }
215557
215991
  async function resolveTrivialWhitespace(filePath, cwd) {
215558
215992
  try {
215559
- await execFileAsync17("git", ["checkout", "--ours", "--", filePath], { cwd });
215560
- await execFileAsync17("git", ["add", "--", filePath], { cwd });
215993
+ await execFileAsync18("git", ["checkout", "--ours", "--", filePath], { cwd });
215994
+ await execFileAsync18("git", ["add", "--", filePath], { cwd });
215561
215995
  mergerLog.debug(`Auto-resolved ${filePath} (trivial whitespace)`);
215562
215996
  } catch (error3) {
215563
215997
  throw new Error(`Failed to auto-resolve ${filePath} trivial conflict: ${error3}`);
@@ -215603,14 +216037,14 @@ async function resolveConflicts(categories2, rootDir) {
215603
216037
  }
215604
216038
  return remainingComplex;
215605
216039
  }
215606
- var execAsync30, execFileAsync17;
216040
+ var execAsync30, execFileAsync18;
215607
216041
  var init_merger_conflict_resolution = __esm({
215608
216042
  "../engine/src/merge/merger-conflict-resolution.ts"() {
215609
216043
  "use strict";
215610
216044
  init_merger_glob();
215611
216045
  init_logger3();
215612
- execAsync30 = promisify47(exec33);
215613
- execFileAsync17 = (file, args, opts) => promisify47(execFile23)(file, args, opts);
216046
+ execAsync30 = promisify48(exec33);
216047
+ execFileAsync18 = (file, args, opts) => promisify48(execFile24)(file, args, opts);
215614
216048
  }
215615
216049
  });
215616
216050
 
@@ -215734,8 +216168,8 @@ var init_merger_autostash_labels = __esm({
215734
216168
  });
215735
216169
 
215736
216170
  // ../engine/src/merge/merger-squash-audit.ts
215737
- import { execFile as execFile24 } from "node:child_process";
215738
- import { promisify as promisify48 } from "node:util";
216171
+ import { execFile as execFile25 } from "node:child_process";
216172
+ import { promisify as promisify49 } from "node:util";
215739
216173
  async function auditSquashMerge(input) {
215740
216174
  const normalizedLookback = normalizeMergeOverlapLookback(input.lookback);
215741
216175
  if (input.strategy === "rebase") {
@@ -215853,7 +216287,7 @@ async function collectTouchedFileOverlaps(rootDir, touchedFiles, recentMainCommi
215853
216287
  return touchedFileOverlaps;
215854
216288
  }
215855
216289
  async function git5(rootDir, args) {
215856
- const { stdout } = await execFileAsync18("git", args, {
216290
+ const { stdout } = await execFileAsync19("git", args, {
215857
216291
  cwd: rootDir,
215858
216292
  encoding: "utf-8",
215859
216293
  maxBuffer: GIT_OUTPUT_MAX_BUFFER
@@ -215886,19 +216320,19 @@ function normalizeMergeOverlapLookback(value) {
215886
216320
  }
215887
216321
  return Math.trunc(value);
215888
216322
  }
215889
- var execFileAsync18, MERGER_MAIN_OVERLAP_LOOKBACK_COMMITS, GIT_OUTPUT_MAX_BUFFER;
216323
+ var execFileAsync19, MERGER_MAIN_OVERLAP_LOOKBACK_COMMITS, GIT_OUTPUT_MAX_BUFFER;
215890
216324
  var init_merger_squash_audit = __esm({
215891
216325
  "../engine/src/merge/merger-squash-audit.ts"() {
215892
216326
  "use strict";
215893
- execFileAsync18 = promisify48(execFile24);
216327
+ execFileAsync19 = promisify49(execFile25);
215894
216328
  MERGER_MAIN_OVERLAP_LOOKBACK_COMMITS = 30;
215895
216329
  GIT_OUTPUT_MAX_BUFFER = 10 * 1024 * 1024;
215896
216330
  }
215897
216331
  });
215898
216332
 
215899
216333
  // ../engine/src/merge/merger-overlap-guard.ts
215900
- import { execFile as execFile25 } from "node:child_process";
215901
- import { promisify as promisify49 } from "node:util";
216334
+ import { execFile as execFile26 } from "node:child_process";
216335
+ import { promisify as promisify50 } from "node:util";
215902
216336
  async function getBranchTouchedFiles({
215903
216337
  rootDir,
215904
216338
  branch,
@@ -215974,7 +216408,7 @@ async function restoreBranchWinsFiles({
215974
216408
  }
215975
216409
  async function gitLines(rootDir, args, context2) {
215976
216410
  try {
215977
- const { stdout } = await execFileAsync19("git", args, {
216411
+ const { stdout } = await execFileAsync20("git", args, {
215978
216412
  cwd: rootDir,
215979
216413
  encoding: "utf-8",
215980
216414
  maxBuffer: GIT_OUTPUT_MAX_BUFFER2
@@ -215992,7 +216426,7 @@ function normalizeLines3(value) {
215992
216426
  }
215993
216427
  async function gitRun(rootDir, args, context2) {
215994
216428
  try {
215995
- await execFileAsync19("git", args, {
216429
+ await execFileAsync20("git", args, {
215996
216430
  cwd: rootDir,
215997
216431
  encoding: "utf-8",
215998
216432
  maxBuffer: GIT_OUTPUT_MAX_BUFFER2
@@ -216004,7 +216438,7 @@ async function gitRun(rootDir, args, context2) {
216004
216438
  }
216005
216439
  async function gitExitCode2(rootDir, args) {
216006
216440
  try {
216007
- await execFileAsync19("git", args, {
216441
+ await execFileAsync20("git", args, {
216008
216442
  cwd: rootDir,
216009
216443
  encoding: "utf-8",
216010
216444
  maxBuffer: GIT_OUTPUT_MAX_BUFFER2
@@ -216017,20 +216451,20 @@ async function gitExitCode2(rootDir, args) {
216017
216451
  function formatErrorMessage(error3) {
216018
216452
  return error3 instanceof Error ? error3.message : String(error3);
216019
216453
  }
216020
- var execFileAsync19, GIT_OUTPUT_MAX_BUFFER2;
216454
+ var execFileAsync20, GIT_OUTPUT_MAX_BUFFER2;
216021
216455
  var init_merger_overlap_guard = __esm({
216022
216456
  "../engine/src/merge/merger-overlap-guard.ts"() {
216023
216457
  "use strict";
216024
216458
  init_logger3();
216025
216459
  init_merger_squash_audit();
216026
- execFileAsync19 = promisify49(execFile25);
216460
+ execFileAsync20 = promisify50(execFile26);
216027
216461
  GIT_OUTPUT_MAX_BUFFER2 = 10 * 1024 * 1024;
216028
216462
  }
216029
216463
  });
216030
216464
 
216031
216465
  // ../engine/src/merge/merger-auto-prerebase.ts
216032
216466
  import { exec as exec34 } from "node:child_process";
216033
- import { promisify as promisify50 } from "node:util";
216467
+ import { promisify as promisify51 } from "node:util";
216034
216468
  async function probeDivergence(opts) {
216035
216469
  const mainRef = opts.mainRef ?? "HEAD";
216036
216470
  const range = `${opts.baseCommitSha}..${mainRef}`;
@@ -216117,13 +216551,13 @@ var execAsync31;
216117
216551
  var init_merger_auto_prerebase = __esm({
216118
216552
  "../engine/src/merge/merger-auto-prerebase.ts"() {
216119
216553
  "use strict";
216120
- execAsync31 = promisify50(exec34);
216554
+ execAsync31 = promisify51(exec34);
216121
216555
  }
216122
216556
  });
216123
216557
 
216124
216558
  // ../engine/src/execution/branch-autocorrect.ts
216125
216559
  import { exec as exec35 } from "node:child_process";
216126
- import { promisify as promisify51 } from "node:util";
216560
+ import { promisify as promisify52 } from "node:util";
216127
216561
  function quoteShellArg7(value) {
216128
216562
  return `'${value.replace(/'/g, `'\\''`)}'`;
216129
216563
  }
@@ -216192,7 +216626,7 @@ var execAsync32, GIT_TIMEOUT_MS7, GIT_MAX_BUFFER4;
216192
216626
  var init_branch_autocorrect = __esm({
216193
216627
  "../engine/src/execution/branch-autocorrect.ts"() {
216194
216628
  "use strict";
216195
- execAsync32 = promisify51(exec35);
216629
+ execAsync32 = promisify52(exec35);
216196
216630
  GIT_TIMEOUT_MS7 = 1e4;
216197
216631
  GIT_MAX_BUFFER4 = 1024 * 1024;
216198
216632
  }
@@ -216656,9 +217090,9 @@ var init_mesh_lease_manager = __esm({
216656
217090
  });
216657
217091
 
216658
217092
  // ../engine/src/merge/merger-integration-worktree.ts
216659
- import { createHash as createHash32 } from "node:crypto";
216660
- import { exec as exec36, execFile as execFile26 } from "node:child_process";
216661
- import { promisify as promisify52 } from "node:util";
217093
+ import { createHash as createHash33 } from "node:crypto";
217094
+ import { exec as exec36, execFile as execFile27 } from "node:child_process";
217095
+ import { promisify as promisify53 } from "node:util";
216662
217096
  function quoteAutostashArg(value) {
216663
217097
  return `"${value.replace(/(["\\$`])/g, "\\$1")}"`;
216664
217098
  }
@@ -216735,15 +217169,15 @@ async function snapshotDirtyFilesLocal(rootDir) {
216735
217169
  const paths3 = /* @__PURE__ */ new Set();
216736
217170
  try {
216737
217171
  const [unstagedOut, stagedOut, porcelainOut] = await Promise.all([
216738
- execFileAsync20("git", ["diff", "-z", "--name-only"], { cwd: rootDir, encoding: "utf-8" }).then(
217172
+ execFileAsync21("git", ["diff", "-z", "--name-only"], { cwd: rootDir, encoding: "utf-8" }).then(
216739
217173
  (r) => r.stdout,
216740
217174
  () => ""
216741
217175
  ),
216742
- execFileAsync20("git", ["diff", "-z", "--cached", "--name-only"], { cwd: rootDir, encoding: "utf-8" }).then(
217176
+ execFileAsync21("git", ["diff", "-z", "--cached", "--name-only"], { cwd: rootDir, encoding: "utf-8" }).then(
216743
217177
  (r) => r.stdout,
216744
217178
  () => ""
216745
217179
  ),
216746
- execFileAsync20("git", ["status", "-z", "--porcelain"], { cwd: rootDir, encoding: "utf-8" }).then(
217180
+ execFileAsync21("git", ["status", "-z", "--porcelain"], { cwd: rootDir, encoding: "utf-8" }).then(
216747
217181
  (r) => r.stdout,
216748
217182
  () => ""
216749
217183
  )
@@ -216768,18 +217202,18 @@ async function snapshotDirtyFilesLocal(rootDir) {
216768
217202
  async function gitDirtyFingerprintLocal(rootDir) {
216769
217203
  try {
216770
217204
  const [diffOut, statusOut] = await Promise.all([
216771
- execFileAsync20("git", ["diff", "HEAD"], {
217205
+ execFileAsync21("git", ["diff", "HEAD"], {
216772
217206
  cwd: rootDir,
216773
217207
  encoding: "utf-8",
216774
217208
  maxBuffer: 64 * 1024 * 1024
216775
217209
  }).then((r) => r.stdout, () => ""),
216776
- execFileAsync20("git", ["status", "-z", "--porcelain"], { cwd: rootDir, encoding: "utf-8" }).then(
217210
+ execFileAsync21("git", ["status", "-z", "--porcelain"], { cwd: rootDir, encoding: "utf-8" }).then(
216777
217211
  (r) => r.stdout,
216778
217212
  () => ""
216779
217213
  )
216780
217214
  ]);
216781
217215
  if (!diffOut && !statusOut) return "";
216782
- return createHash32("sha256").update(diffOut).update("\0").update(statusOut).digest("hex");
217216
+ return createHash33("sha256").update(diffOut).update("\0").update(statusOut).digest("hex");
216783
217217
  } catch {
216784
217218
  return "";
216785
217219
  }
@@ -216796,7 +217230,7 @@ async function probeIntegrationWorktreeState(input) {
216796
217230
  const dirtyFingerprint = await gitDirtyFingerprintLocal(registeredPath);
216797
217231
  let untrackedCount = 0;
216798
217232
  try {
216799
- const { stdout } = await execFileAsync20("git", ["status", "-z", "--porcelain"], {
217233
+ const { stdout } = await execFileAsync21("git", ["status", "-z", "--porcelain"], {
216800
217234
  cwd: registeredPath,
216801
217235
  encoding: "utf-8"
216802
217236
  });
@@ -217157,7 +217591,7 @@ async function releaseReuseHandoff(input) {
217157
217591
  }
217158
217592
  });
217159
217593
  }
217160
- var execAsync33, execFileAsync20, MERGE_HANDOFF_WORKER_ID, MergeHandoffRefusedError;
217594
+ var execAsync33, execFileAsync21, MERGE_HANDOFF_WORKER_ID, MergeHandoffRefusedError;
217161
217595
  var init_merger_integration_worktree = __esm({
217162
217596
  "../engine/src/merge/merger-integration-worktree.ts"() {
217163
217597
  "use strict";
@@ -217168,8 +217602,8 @@ var init_merger_integration_worktree = __esm({
217168
217602
  init_mesh_lease_manager();
217169
217603
  init_worktree_pool();
217170
217604
  init_worktree_names();
217171
- execAsync33 = promisify52(exec36);
217172
- execFileAsync20 = promisify52(execFile26);
217605
+ execAsync33 = promisify53(exec36);
217606
+ execFileAsync21 = promisify53(execFile27);
217173
217607
  MERGE_HANDOFF_WORKER_ID = "merger-reuse-handoff";
217174
217608
  MergeHandoffRefusedError = class extends Error {
217175
217609
  reason;
@@ -217198,13 +217632,13 @@ var init_push_after_merge_policy = __esm({
217198
217632
  });
217199
217633
 
217200
217634
  // ../engine/src/worktree/worktree-ref-sync.ts
217201
- import { execFile as execFile27, spawn as spawn7 } from "node:child_process";
217202
- import { promisify as promisify53 } from "node:util";
217635
+ import { execFile as execFile28, spawn as spawn7 } from "node:child_process";
217636
+ import { promisify as promisify54 } from "node:util";
217203
217637
  import { copyFileSync, existsSync as existsSync64, mkdirSync as mkdirSync8, mkdtempSync as mkdtempSync2, readFileSync as readFileSync22, rmSync as rmSync5, writeFileSync as writeFileSync8 } from "node:fs";
217204
217638
  import { dirname as dirname39, join as join80 } from "node:path";
217205
217639
  import { tmpdir as tmpdir7 } from "node:os";
217206
217640
  async function runGit5(args, cwd, timeoutMs) {
217207
- const result2 = await execFileAsync21("git", ["-c", "core.quotePath=false", ...args], {
217641
+ const result2 = await execFileAsync22("git", ["-c", "core.quotePath=false", ...args], {
217208
217642
  cwd,
217209
217643
  timeout: timeoutMs,
217210
217644
  maxBuffer: 64 * 1024 * 1024,
@@ -217438,11 +217872,11 @@ async function syncWorktreeToHead(input) {
217438
217872
  }
217439
217873
  }
217440
217874
  }
217441
- var execFileAsync21;
217875
+ var execFileAsync22;
217442
217876
  var init_worktree_ref_sync = __esm({
217443
217877
  "../engine/src/worktree/worktree-ref-sync.ts"() {
217444
217878
  "use strict";
217445
- execFileAsync21 = promisify53(execFile27);
217879
+ execFileAsync22 = promisify54(execFile28);
217446
217880
  }
217447
217881
  });
217448
217882
 
@@ -217450,7 +217884,7 @@ var init_worktree_ref_sync = __esm({
217450
217884
  import { exec as exec37 } from "node:child_process";
217451
217885
  import { readFile as readFile44, writeFile as writeFile26 } from "node:fs/promises";
217452
217886
  import { join as join81 } from "node:path";
217453
- import { promisify as promisify54 } from "node:util";
217887
+ import { promisify as promisify55 } from "node:util";
217454
217888
  function quoteArg4(value) {
217455
217889
  return `'${value.replace(/'/g, `'\\''`)}'`;
217456
217890
  }
@@ -217626,7 +218060,7 @@ var init_merger_scope_auto_widen = __esm({
217626
218060
  "use strict";
217627
218061
  init_src2();
217628
218062
  init_merger();
217629
- execAsync34 = promisify54(exec37);
218063
+ execAsync34 = promisify55(exec37);
217630
218064
  GIT_MAX_BUFFER5 = 10 * 1024 * 1024;
217631
218065
  ScopeAutoWidenPersistError = class extends Error {
217632
218066
  constructor(message) {
@@ -217760,9 +218194,9 @@ __export(merger_exports, {
217760
218194
  });
217761
218195
  import { execSync as execSync6, exec as exec38 } from "node:child_process";
217762
218196
  import * as childProcess4 from "node:child_process";
217763
- import { promisify as promisify55 } from "node:util";
218197
+ import { promisify as promisify56 } from "node:util";
217764
218198
  import { existsSync as existsSync65, readFileSync as readFileSync23, writeFileSync as writeFileSync9, unlinkSync as unlinkSync3, renameSync as renameSync4 } from "node:fs";
217765
- import { createHash as createHash33 } from "node:crypto";
218199
+ import { createHash as createHash34 } from "node:crypto";
217766
218200
  import { join as join82, resolve as resolve48 } from "node:path";
217767
218201
  import { hostname as hostname3 } from "node:os";
217768
218202
  import { Type as Type4 } from "typebox";
@@ -217973,15 +218407,15 @@ async function snapshotDirtyFiles(rootDir) {
217973
218407
  const paths3 = /* @__PURE__ */ new Set();
217974
218408
  try {
217975
218409
  const [unstagedOut, stagedOut, porcelainOut] = await Promise.all([
217976
- execFileAsync22("git", ["diff", "-z", "--name-only"], { cwd: rootDir, encoding: "utf-8" }).then(
218410
+ execFileAsync23("git", ["diff", "-z", "--name-only"], { cwd: rootDir, encoding: "utf-8" }).then(
217977
218411
  (r) => r.stdout,
217978
218412
  () => ""
217979
218413
  ),
217980
- execFileAsync22("git", ["diff", "-z", "--cached", "--name-only"], { cwd: rootDir, encoding: "utf-8" }).then(
218414
+ execFileAsync23("git", ["diff", "-z", "--cached", "--name-only"], { cwd: rootDir, encoding: "utf-8" }).then(
217981
218415
  (r) => r.stdout,
217982
218416
  () => ""
217983
218417
  ),
217984
- execFileAsync22("git", ["status", "-z", "--porcelain"], { cwd: rootDir, encoding: "utf-8" }).then(
218418
+ execFileAsync23("git", ["status", "-z", "--porcelain"], { cwd: rootDir, encoding: "utf-8" }).then(
217985
218419
  (r) => r.stdout,
217986
218420
  () => ""
217987
218421
  )
@@ -218006,18 +218440,18 @@ async function snapshotDirtyFiles(rootDir) {
218006
218440
  async function gitDirtyFingerprint(rootDir) {
218007
218441
  try {
218008
218442
  const [diffOut, statusOut] = await Promise.all([
218009
- execFileAsync22("git", ["diff", "HEAD"], {
218443
+ execFileAsync23("git", ["diff", "HEAD"], {
218010
218444
  cwd: rootDir,
218011
218445
  encoding: "utf-8",
218012
218446
  maxBuffer: 64 * 1024 * 1024
218013
218447
  }).then((r) => r.stdout, () => ""),
218014
- execFileAsync22("git", ["status", "-z", "--porcelain"], { cwd: rootDir, encoding: "utf-8" }).then(
218448
+ execFileAsync23("git", ["status", "-z", "--porcelain"], { cwd: rootDir, encoding: "utf-8" }).then(
218015
218449
  (r) => r.stdout,
218016
218450
  () => ""
218017
218451
  )
218018
218452
  ]);
218019
218453
  if (!diffOut && !statusOut) return "";
218020
- return createHash33("sha256").update(diffOut).update("\0").update(statusOut).digest("hex");
218454
+ return createHash34("sha256").update(diffOut).update("\0").update(statusOut).digest("hex");
218021
218455
  } catch {
218022
218456
  return "";
218023
218457
  }
@@ -219979,7 +220413,7 @@ function splitNulDelimited(output2) {
219979
220413
  return text3.split(NUL).filter((entry) => entry.length > 0);
219980
220414
  }
219981
220415
  async function countStagedPaths(rootDir) {
219982
- const { stdout } = await execFileAsync22("git", ["diff", "--cached", "--name-only", "-z"], {
220416
+ const { stdout } = await execFileAsync23("git", ["diff", "--cached", "--name-only", "-z"], {
219983
220417
  cwd: rootDir,
219984
220418
  encoding: "buffer",
219985
220419
  maxBuffer: 10 * 1024 * 1024
@@ -219988,7 +220422,7 @@ async function countStagedPaths(rootDir) {
219988
220422
  }
219989
220423
  async function filterStagedGitignoredPaths(rootDir, taskId) {
219990
220424
  try {
219991
- const { stdout: stagedOut } = await execFileAsync22("git", ["diff", "--cached", "--name-only", "-z"], {
220425
+ const { stdout: stagedOut } = await execFileAsync23("git", ["diff", "--cached", "--name-only", "-z"], {
219992
220426
  cwd: rootDir,
219993
220427
  encoding: "buffer",
219994
220428
  maxBuffer: 10 * 1024 * 1024
@@ -220002,7 +220436,7 @@ async function filterStagedGitignoredPaths(rootDir, taskId) {
220002
220436
  for (let i = 0; i < stagedPaths.length; i += batchSize) {
220003
220437
  const batch = stagedPaths.slice(i, i + batchSize);
220004
220438
  try {
220005
- const { stdout } = await execFileAsync22("git", ["check-ignore", "--no-index", "--", ...batch], {
220439
+ const { stdout } = await execFileAsync23("git", ["check-ignore", "--no-index", "--", ...batch], {
220006
220440
  cwd: rootDir,
220007
220441
  encoding: "utf-8",
220008
220442
  maxBuffer: 10 * 1024 * 1024
@@ -220025,7 +220459,7 @@ async function filterStagedGitignoredPaths(rootDir, taskId) {
220025
220459
  `${taskId}: refusing to stage gitignored path "${path13}" \u2014 unstaging (agents must not bypass .gitignore via \`git add -f\`)`
220026
220460
  );
220027
220461
  try {
220028
- await execFileAsync22("git", ["reset", "HEAD", "--", path13], {
220462
+ await execFileAsync23("git", ["reset", "HEAD", "--", path13], {
220029
220463
  cwd: rootDir,
220030
220464
  encoding: "utf-8",
220031
220465
  maxBuffer: 10 * 1024 * 1024
@@ -220057,7 +220491,7 @@ async function commitOrAmendMergeWithFixes(rootDir, taskId, branch, commitLog, i
220057
220491
  encoding: "utf-8"
220058
220492
  });
220059
220493
  const unstaged = new Set(unstagedOut.split("\n").map((l) => l.trim()).filter(Boolean));
220060
- const { stdout: porcelainOut } = await execFileAsync22("git", ["status", "-z", "--porcelain"], {
220494
+ const { stdout: porcelainOut } = await execFileAsync23("git", ["status", "-z", "--porcelain"], {
220061
220495
  cwd: rootDir,
220062
220496
  encoding: "utf-8"
220063
220497
  });
@@ -220078,7 +220512,7 @@ async function commitOrAmendMergeWithFixes(rootDir, taskId, branch, commitLog, i
220078
220512
  }
220079
220513
  }
220080
220514
  if (unstagedToStage.length > 0) {
220081
- await execFileAsync22("git", ["add", "--", ...unstagedToStage], { cwd: rootDir });
220515
+ await execFileAsync23("git", ["add", "--", ...unstagedToStage], { cwd: rootDir });
220082
220516
  }
220083
220517
  const untrackedToStage = [];
220084
220518
  for (const p2 of untracked) {
@@ -220091,7 +220525,7 @@ async function commitOrAmendMergeWithFixes(rootDir, taskId, branch, commitLog, i
220091
220525
  }
220092
220526
  }
220093
220527
  if (untrackedToStage.length > 0) {
220094
- await execFileAsync22("git", ["add", "--", ...untrackedToStage], { cwd: rootDir });
220528
+ await execFileAsync23("git", ["add", "--", ...untrackedToStage], { cwd: rootDir });
220095
220529
  }
220096
220530
  const cap = (arr, n = 20) => arr.length <= n ? arr.join(", ") : `${arr.slice(0, n).join(", ")} ... (+${arr.length - n} more)`;
220097
220531
  mergerLog.debug(
@@ -221562,7 +221996,7 @@ function isNonFastForwardPushError(message) {
221562
221996
  }
221563
221997
  async function probeRebaseInProgress(rootDir) {
221564
221998
  const gitPath = async (name2) => {
221565
- const { stdout } = await execFileAsync22("git", ["rev-parse", "--git-path", name2], {
221999
+ const { stdout } = await execFileAsync23("git", ["rev-parse", "--git-path", name2], {
221566
222000
  cwd: rootDir,
221567
222001
  encoding: "utf-8",
221568
222002
  timeout: PUSH_TIMEOUT_MS
@@ -225747,7 +226181,7 @@ async function completeTask(store, taskId, result2) {
225747
226181
  }
225748
226182
  store.emit("task:merged", result2);
225749
226183
  }
225750
- var execAsync35, execFileAsync22, LEGACY_COMPLETE_LANES2, DEPENDENCY_SYNC_TRIGGER_PATTERNS, PULL_REBASE_TIMEOUT_MS, PUSH_TIMEOUT_MS, PUSH_NON_FF_MAX_RETRIES, PUSH_NON_FF_RETRY_BACKOFF_MS, PUSH_TRANSIENT_RETRY_BACKOFF_MS, TRANSIENT_GIT_PUSH_PATTERNS, MERGE_COMMIT_LOG_MAX_CHARS, MERGE_DIFF_STAT_MAX_CHARS, MERGE_USER_COMMENTS_MAX_CHARS, summarizeVerificationOutputLocal, VERIFICATION_EXTRA_ENV, ACTIVE_MERGER_STATUS_FILENAME, __test__, AutostashCreationFailedError, NUL, SquashAuditError;
226184
+ var execAsync35, execFileAsync23, LEGACY_COMPLETE_LANES2, DEPENDENCY_SYNC_TRIGGER_PATTERNS, PULL_REBASE_TIMEOUT_MS, PUSH_TIMEOUT_MS, PUSH_NON_FF_MAX_RETRIES, PUSH_NON_FF_RETRY_BACKOFF_MS, PUSH_TRANSIENT_RETRY_BACKOFF_MS, TRANSIENT_GIT_PUSH_PATTERNS, MERGE_COMMIT_LOG_MAX_CHARS, MERGE_DIFF_STAT_MAX_CHARS, MERGE_USER_COMMENTS_MAX_CHARS, summarizeVerificationOutputLocal, VERIFICATION_EXTRA_ENV, ACTIVE_MERGER_STATUS_FILENAME, __test__, AutostashCreationFailedError, NUL, SquashAuditError;
225751
226185
  var init_merger = __esm({
225752
226186
  "../engine/src/merger.ts"() {
225753
226187
  "use strict";
@@ -225819,8 +226253,8 @@ var init_merger = __esm({
225819
226253
  init_merger_scope_auto_widen();
225820
226254
  init_lifecycle_move();
225821
226255
  init_merger_ref_update_advance();
225822
- execAsync35 = promisify55(exec38);
225823
- execFileAsync22 = (file, args, opts) => promisify55(childProcess4.execFile)(file, args, opts);
226256
+ execAsync35 = promisify56(exec38);
226257
+ execFileAsync23 = (file, args, opts) => promisify56(childProcess4.execFile)(file, args, opts);
225824
226258
  LEGACY_COMPLETE_LANES2 = ["done"];
225825
226259
  DEPENDENCY_SYNC_TRIGGER_PATTERNS = [
225826
226260
  "package.json",
@@ -225924,9 +226358,6 @@ function isDependencySatisfiedWithoutWorkflowMetadata(column) {
225924
226358
  function isLegacyCompleteColumnForWorkspaceTeardown(column) {
225925
226359
  return column === "done";
225926
226360
  }
225927
- function resolveRepoLocalAiMergeRoot(rootDir, settings) {
225928
- return resolveAiMergeRootPath(rootDir, settings);
225929
- }
225930
226361
  async function classifyOwnedLandedEvidenceForSelfHealing(rootDir, task, mergeTargetBranch) {
225931
226362
  const { classifyOwnedLandedEvidence: classifyOwnedLandedEvidence2 } = await Promise.resolve().then(() => (init_merger(), merger_exports));
225932
226363
  return classifyOwnedLandedEvidence2(rootDir, task, { mergeTargetBranch });
@@ -226013,6 +226444,8 @@ var init_self_healing = __esm({
226013
226444
  init_branch_conflicts();
226014
226445
  init_run_audit();
226015
226446
  init_auto_merge_finalization();
226447
+ init_merge_content_capture();
226448
+ init_stale_content_review_reroute();
226016
226449
  init_post_landing_worktree_cleanup();
226017
226450
  init_auto_recovery();
226018
226451
  init_active_session_registry();
@@ -226181,6 +226614,7 @@ var init_self_healing = __esm({
226181
226614
  symbolLockNoActionAudited = false;
226182
226615
  maintenanceTickCounter = 0;
226183
226616
  taskLifecycleRetentionLastPrunedAt = /* @__PURE__ */ new Map();
226617
+ staleContentRerouteAuditKeys = /* @__PURE__ */ new Set();
226184
226618
  githubCheckStateRetentionLastPrunedAt = /* @__PURE__ */ new Map();
226185
226619
  processBootStartedAt = Date.now();
226186
226620
  lastDbCorruptionNotifiedAt = null;
@@ -226896,6 +227330,7 @@ var init_self_healing = __esm({
226896
227330
  // flight" to all of them (the merge gate included), which is the two-hour
226897
227331
  // stall-deadlock ride this sweep exists to prevent.
226898
227332
  { name: "reconcile-orphaned-pending-step-results", fn: () => this.reconcileOrphanedPendingStepResults().then(() => void 0) },
227333
+ { name: "reconcile-unproven-review-approvals", fn: () => this.reconcileUnprovenReviewApprovals().then(() => void 0) },
226899
227334
  /*
226900
227335
  FNXC:WorkspaceContention 2026-08-23-08:00:
226901
227336
  `contention-hold` is owned by an in-memory retry timer. After a process restart that
@@ -227776,6 +228211,7 @@ var init_self_healing = __esm({
227776
228211
  Keep them adjacent and in this order.
227777
228212
  */
227778
228213
  { name: "reconcile-orphaned-pending-step-results", fn: () => this.reconcileOrphanedPendingStepResults() },
228214
+ { name: "reconcile-unproven-review-approvals", fn: () => this.reconcileUnprovenReviewApprovals() },
227779
228215
  { name: "recover-failed-pre-merge-steps", fn: () => this.recoverReviewTasksWithFailedPreMergeSteps() },
227780
228216
  { name: "recover-missing-worktree-review-failures", fn: () => this.recoverMissingWorktreeReviewFailures() },
227781
228217
  { name: "recover-interrupted-merging", fn: () => this.recoverInterruptedMergingTasks() },
@@ -232222,6 +232658,95 @@ var init_self_healing = __esm({
232222
232658
  return 0;
232223
232659
  }
232224
232660
  }
232661
+ /*
232662
+ FNXC:ReviewInputProof 2026-09-01-11:28:
232663
+ Prevention cannot release cards that already persisted a proofless `passed` content review. Repair
232664
+ only the invalid result in place, including when auto-merge is off, so automatic recovery can re-run
232665
+ eligible gates and a human-held card gains the failed result its audited bypass needs. The
232666
+ `needsOperatorBypass` label is therefore truthful rather than aspirational; no lifecycle state moves.
232667
+
232668
+ FNXC:ReviewInputProof 2026-09-01-11:57:
232669
+ Reconciliation must recompute from the lock-held task inside `updateTaskAtomic`; a graph writer can
232670
+ publish a proof-bound approval or another gate after the preliminary read, and a stale array write
232671
+ would discard that valid result. Re-check lane, user pause, workspace ownership, and session liveness
232672
+ in the callback, then log and audit only a repair whose atomic mutation actually applied.
232673
+ */
232674
+ async reconcileUnprovenReviewApprovals() {
232675
+ try {
232676
+ const reviewColumns = await resolveProjectColumnsForRoles(this.store, REVIEW_ROLES);
232677
+ const candidates = /* @__PURE__ */ new Map();
232678
+ for (const column of reviewColumns) {
232679
+ for (const task of await this.store.listTasks({ column, slim: true })) candidates.set(task.id, task);
232680
+ }
232681
+ const settings = await this.store.getSettings().catch(() => void 0);
232682
+ let repairedTasks = 0;
232683
+ const isSessionLive = (taskId) => {
232684
+ const livePaths = activeSessionRegistry.pathsForTask(taskId);
232685
+ return livePaths.some((path13) => activeSessionRegistry.isPathActive(path13)) || executingTaskLock.has(taskId) || this.options.isTaskActive?.(taskId) === true;
232686
+ };
232687
+ for (const candidate of candidates.values()) {
232688
+ if (candidate.userPaused === true || candidate.workspaceWorktrees !== void 0 || isSessionLive(candidate.id)) continue;
232689
+ if (!candidate.workflowStepResults?.some((result2) => resolveUnprovenReviewApproval(result2, { workspace: false }))) continue;
232690
+ const fresh = await this.store.getTask(candidate.id);
232691
+ if (!fresh || !reviewColumns.has(fresh.column) || fresh.userPaused === true || fresh.workspaceWorktrees !== void 0 || isSessionLive(fresh.id)) continue;
232692
+ let repair;
232693
+ try {
232694
+ await this.store.updateTaskAtomic(fresh.id, (live) => {
232695
+ repair = void 0;
232696
+ if (!reviewColumns.has(live.column) || live.userPaused === true || live.workspaceWorktrees !== void 0 || isSessionLive(live.id)) return null;
232697
+ const repairedStepIds = [];
232698
+ let repairReason;
232699
+ const results = (live.workflowStepResults ?? []).map((result2) => {
232700
+ const resolution = resolveUnprovenReviewApproval(result2, { workspace: false });
232701
+ if (!resolution) return result2;
232702
+ repairedStepIds.push(result2.workflowStepId);
232703
+ repairReason ??= resolution.reason;
232704
+ return resolution.downgraded;
232705
+ });
232706
+ if (repairedStepIds.length === 0 || !repairReason) return null;
232707
+ repair = {
232708
+ stepIds: repairedStepIds,
232709
+ reason: repairReason,
232710
+ column: live.column,
232711
+ resultCount: results.length,
232712
+ needsOperatorBypass: settings ? !allowsAutoMergeProcessing(live, settings) : void 0
232713
+ };
232714
+ return { workflowStepResults: results };
232715
+ });
232716
+ if (!repair) continue;
232717
+ repairedTasks += 1;
232718
+ } catch (error3) {
232719
+ log34.warn(`reconcileUnprovenReviewApprovals: failed for ${fresh.id}: ${error3 instanceof Error ? error3.message : String(error3)}`);
232720
+ continue;
232721
+ }
232722
+ await this.store.logEntry(
232723
+ fresh.id,
232724
+ `[pre-merge] Invalid proofless approval repaired for ${repair.stepIds.join(", ")}: ${repair.reason}`
232725
+ ).catch(() => void 0);
232726
+ await emitBoundedRunAudit2(this.store, {
232727
+ taskId: fresh.id,
232728
+ agentId: "self-healing",
232729
+ runId: generateSyntheticRunId("reconcile-unproven-review-approval", fresh.id),
232730
+ domain: "database",
232731
+ mutationType: "task:reconcile-unproven-review-approval",
232732
+ target: fresh.id,
232733
+ metadata: {
232734
+ taskId: fresh.id,
232735
+ column: repair.column,
232736
+ workflowStepId: repair.stepIds[0],
232737
+ repairedCount: repair.stepIds.length,
232738
+ resultCount: repair.resultCount,
232739
+ needsOperatorBypass: repair.needsOperatorBypass
232740
+ }
232741
+ }, { log: log34 });
232742
+ }
232743
+ if (repairedTasks > 0) log34.log(`Repaired unproven review approvals on ${repairedTasks} task(s)`);
232744
+ return repairedTasks;
232745
+ } catch (error3) {
232746
+ log34.error(`reconcileUnprovenReviewApprovals failed: ${error3 instanceof Error ? error3.message : String(error3)}`);
232747
+ return 0;
232748
+ }
232749
+ }
232225
232750
  /**
232226
232751
  * Backward lifecycle move gated on triple proof (FN-5335).
232227
232752
  * When the unproven fallback predicate fails, emits `task:finalize-no-op-review-no-action` and skips lifecycle mutation.
@@ -232575,6 +233100,66 @@ var init_self_healing = __esm({
232575
233100
  }
232576
233101
  return allowsAutoMergeProcessing(task, settings) && resolveEffectiveAutoMerge(task, settings) !== false;
232577
233102
  }
233103
+ /*
233104
+ FNXC:PreMergeApproval 2026-09-01-14:05:
233105
+ Self-healing must not treat a stale content-bound approval as mergeable just because the cheap
233106
+ blocker check lacks a fresh Git descriptor. Capture the same merge content the merge queue uses and
233107
+ seed the graph back at Code Review, so cards like FN-9234 stop looping through doomed merge retries.
233108
+ */
233109
+ async routeStaleSingularApprovalBackToReview(task, reviewColumns, settings) {
233110
+ let mergeGate;
233111
+ try {
233112
+ mergeGate = await resolvePreMergeGateForTask(this.store, task.id, task.enabledWorkflowSteps, task);
233113
+ } catch {
233114
+ return false;
233115
+ }
233116
+ if (mergeGate.provenance === "default" && !mergeGate.selectionAbsent) return false;
233117
+ const mergeContent = await captureMergeContentDescriptor(task, {
233118
+ workspaceRootDir: this.options.rootDir,
233119
+ settings
233120
+ });
233121
+ const blocker = getTaskMergeBlocker(task, {
233122
+ reviewColumns,
233123
+ requiredPreMergeStepIds: mergeGate.requiredPreMergeStepIds,
233124
+ mergeContent
233125
+ });
233126
+ if (blocker !== "task has a pre-merge approval recorded against different content" || mergeContent.kind !== "singular") {
233127
+ return false;
233128
+ }
233129
+ const reroute = await rerouteSingularStaleContentToReview(this.store, task, {
233130
+ requiredPreMergeStepIds: mergeGate.requiredPreMergeStepIds,
233131
+ mergeContent
233132
+ }).catch(() => ({
233133
+ rerouted: false,
233134
+ reason: "no-progress",
233135
+ nodeId: void 0,
233136
+ workflowStepId: void 0
233137
+ }));
233138
+ if (reroute.rerouted) {
233139
+ await this.store.logEntry(task.id, "[pre-merge] Self-healing re-seeded Code Review after stale content evidence blocked merge.");
233140
+ log34.warn(`Stale content approval for ${task.id} re-seeded at ${reroute.nodeId ?? "code-review"}`);
233141
+ }
233142
+ const auditKey = `${task.id}:${reroute.reason}:${reroute.nodeId ?? ""}`;
233143
+ if (!this.staleContentRerouteAuditKeys.has(auditKey)) {
233144
+ this.staleContentRerouteAuditKeys.add(auditKey);
233145
+ await emitBoundedRunAudit2(this.store, {
233146
+ taskId: task.id,
233147
+ agentId: "self-healing",
233148
+ runId: generateSyntheticRunId("self-healing", task.id),
233149
+ domain: "database",
233150
+ mutationType: "task:merge-stale-content-review-rerouted",
233151
+ target: task.id,
233152
+ metadata: {
233153
+ taskId: task.id,
233154
+ nodeId: reroute.nodeId,
233155
+ workflowStepId: reroute.workflowStepId,
233156
+ reason: reroute.reason,
233157
+ source: "self-healing"
233158
+ }
233159
+ });
233160
+ }
233161
+ return true;
233162
+ }
232578
233163
  /**
232579
233164
  * Recover `in-review` tasks that are fully mergeable but never had
232580
233165
  * `mergeTask()` invoked.
@@ -232628,6 +233213,7 @@ var init_self_healing = __esm({
232628
233213
  for (const task of mergeable) {
232629
233214
  const reviewColumns = await ownReviewLanesFor(task);
232630
233215
  if (!reviewColumns.has(task.column)) continue;
233216
+ if (await this.routeStaleSingularApprovalBackToReview(task, reviewColumns, settings)) continue;
232631
233217
  if (getTaskMergeBlocker(task, { reviewColumns }) !== void 0) continue;
232632
233218
  laneQualifiedMergeable.push(task);
232633
233219
  }
@@ -235194,8 +235780,7 @@ This review is not retried until its findings change. Re-run the review after ad
235194
235780
  }
235195
235781
  async listAiMergeWorktreeCandidates(taskId, settings) {
235196
235782
  const roots = Array.from(/* @__PURE__ */ new Set([
235197
- resolveRepoLocalAiMergeRoot(this.options.rootDir, settings),
235198
- resolveLegacyAiMergeRootPath(this.options.rootDir),
235783
+ ...resolveAiMergeSearchRoots(this.options.rootDir, settings),
235199
235784
  tmpdir8()
235200
235785
  ]));
235201
235786
  const testWorkerRoot = process.env.FUSION_TEST_WORKER_ROOT;
@@ -237729,9 +238314,7 @@ This review is not retried until its findings change. Re-run the review after ad
237729
238314
  log34.debug("[self-healing] temp-dir sweep: worktrunk enabled \u2014 AI merge clean-room worktrees use Fusion's dedicated clean-room root, proceeding with native sweep");
237730
238315
  }
237731
238316
  const roots = Array.from(/* @__PURE__ */ new Set([
237732
- resolveRepoLocalAiMergeRoot(this.options.rootDir, settings),
237733
- resolveLegacyAiMergeRootPath(this.options.rootDir),
237734
- ...settings.worktreesDir ? [] : [join83(resolveLegacyWorktreesDirLayout(this.options.rootDir), ".ai-merge")],
238317
+ ...resolveAiMergeSearchRoots(this.options.rootDir, settings),
237735
238318
  tmpdir8()
237736
238319
  ]));
237737
238320
  const auditor = createRunAuditor(this.store, {
@@ -238284,7 +238867,7 @@ import { exec as exec39 } from "node:child_process";
238284
238867
  import { existsSync as existsSync67 } from "node:fs";
238285
238868
  import { readFile as readFile46, writeFile as writeFile27, rm as rm11 } from "node:fs/promises";
238286
238869
  import { join as join84 } from "node:path";
238287
- import { promisify as promisify56 } from "node:util";
238870
+ import { promisify as promisify57 } from "node:util";
238288
238871
  function getPlanningLifecycleLockTransportFailure(task) {
238289
238872
  const candidate = task.customFields?.[PLANNING_LIFECYCLE_LOCK_TRANSPORT_FAILURE_KEY];
238290
238873
  if (!candidate || typeof candidate !== "object" || Array.isArray(candidate)) return null;
@@ -241681,7 +242264,7 @@ ${lines.join("\n")}` }],
241681
242264
  written,
241682
242265
  {
241683
242266
  cwd: this.rootDir,
241684
- exec: promisify56(exec39)
242267
+ exec: promisify57(exec39)
241685
242268
  }
241686
242269
  ),
241687
242270
  new Promise((resolve65) => setTimeout(() => resolve65(null), 15e3))
@@ -241935,7 +242518,7 @@ var init_workflow_gate_activity = __esm({
241935
242518
 
241936
242519
  // ../engine/src/executor/browser-probe.ts
241937
242520
  import { exec as exec40 } from "node:child_process";
241938
- import { promisify as promisify57 } from "node:util";
242521
+ import { promisify as promisify58 } from "node:util";
241939
242522
  function isAgentBrowserNotFoundError(error3) {
241940
242523
  const err = error3;
241941
242524
  const code = typeof err?.code === "string" || typeof err?.code === "number" ? String(err.code) : void 0;
@@ -241991,7 +242574,7 @@ var execAsync36, AGENT_BROWSER_NAVIGATION_SKILL_ID;
241991
242574
  var init_browser_probe = __esm({
241992
242575
  "../engine/src/executor/browser-probe.ts"() {
241993
242576
  "use strict";
241994
- execAsync36 = promisify57(exec40);
242577
+ execAsync36 = promisify58(exec40);
241995
242578
  AGENT_BROWSER_NAVIGATION_SKILL_ID = "agent-browser-navigation";
241996
242579
  }
241997
242580
  });
@@ -242510,7 +243093,7 @@ var init_workflow_failure_scope_guard = __esm({
242510
243093
  // ../engine/src/executor/worktree-registry-helpers.ts
242511
243094
  import { isAbsolute as isAbsolute21, relative as relative20, resolve as resolvePath3 } from "node:path";
242512
243095
  import { exec as exec41 } from "node:child_process";
242513
- import { promisify as promisify58 } from "node:util";
243096
+ import { promisify as promisify59 } from "node:util";
242514
243097
  async function isRegisteredWorktree(rootDir, path13) {
242515
243098
  return isRegisteredGitWorktree(rootDir, path13);
242516
243099
  }
@@ -242554,7 +243137,7 @@ var init_worktree_registry_helpers = __esm({
242554
243137
  "../engine/src/executor/worktree-registry-helpers.ts"() {
242555
243138
  "use strict";
242556
243139
  init_worktree_pool();
242557
- execAsync37 = promisify58(exec41);
243140
+ execAsync37 = promisify59(exec41);
242558
243141
  NonRetryableWorktreeError = class extends Error {
242559
243142
  };
242560
243143
  }
@@ -242783,7 +243366,7 @@ var init_worktree_ownership = __esm({
242783
243366
 
242784
243367
  // ../engine/src/executor/worktree-stale-branch.ts
242785
243368
  import { exec as exec42 } from "node:child_process";
242786
- import { promisify as promisify59 } from "node:util";
243369
+ import { promisify as promisify60 } from "node:util";
242787
243370
  async function cleanupStaleBranch(rootDir, store, branch, taskId) {
242788
243371
  const task = await store.getTask?.(taskId);
242789
243372
  if (!task || !isFusionDeletableBranch(task, branch)) {
@@ -242839,13 +243422,13 @@ var init_worktree_stale_branch = __esm({
242839
243422
  "../engine/src/executor/worktree-stale-branch.ts"() {
242840
243423
  "use strict";
242841
243424
  init_src2();
242842
- execAsync38 = promisify59(exec42);
243425
+ execAsync38 = promisify60(exec42);
242843
243426
  }
242844
243427
  });
242845
243428
 
242846
243429
  // ../engine/src/executor/worktree-squash-import-plan.ts
242847
243430
  import { exec as exec43 } from "node:child_process";
242848
- import { promisify as promisify60 } from "node:util";
243431
+ import { promisify as promisify61 } from "node:util";
242849
243432
  async function planSquashImportFromDep(rootDir, store, _taskId, depTip, originalStartPoint) {
242850
243433
  let settings;
242851
243434
  try {
@@ -242919,7 +243502,7 @@ var init_worktree_squash_import_plan = __esm({
242919
243502
  "../engine/src/executor/worktree-squash-import-plan.ts"() {
242920
243503
  "use strict";
242921
243504
  init_shell_quote();
242922
- execAsync39 = promisify60(exec43);
243505
+ execAsync39 = promisify61(exec43);
242923
243506
  }
242924
243507
  });
242925
243508
 
@@ -243670,7 +244253,7 @@ var init_optional_step_revision = __esm({
243670
244253
 
243671
244254
  // ../engine/src/executor/execute-workflow-step.ts
243672
244255
  import { exec as exec44 } from "node:child_process";
243673
- import { promisify as promisify61 } from "node:util";
244256
+ import { promisify as promisify62 } from "node:util";
243674
244257
  function findReusableReviewResult(task, workflowStepId, reviewInputFingerprint, repositoryScopeRevision) {
243675
244258
  if (!reviewInputFingerprint) return void 0;
243676
244259
  return (task.workflowStepResults ?? []).find(
@@ -243708,6 +244291,8 @@ async function executeWorkflowStep(deps, task, workflowStep, worktreePath, setti
243708
244291
  const unattended = stepOptions?.unattended === true;
243709
244292
  const workflowStepMetadata = workflowStep;
243710
244293
  const optionalGroupId = workflowStepMetadata.optionalGroupId;
244294
+ const effectiveWorkflowStepId = optionalGroupId ?? workflowStep.id.replace(/^graph:/, "");
244295
+ const isContentBindingStep = requiresContentReviewProof(effectiveWorkflowStepId, workflowStepMetadata);
243711
244296
  const isPlanReviewStep = workflowStep.id === "graph:plan-review-step" || workflowStep.name === "Plan Review" || optionalGroupId === PLAN_REVIEW_GROUP_ID;
243712
244297
  const planReviewRevisionKey = optionalStepRevisionKey2(optionalGroupId, workflowStep.name);
243713
244298
  const isReviewTypeWorkflowStep = isPlanReviewStep || workflowStepMetadata.reviewCanFixInline === true || /(?:^|\b)(?:review|verification)(?:\b|$)/i.test(workflowStep.name) || optionalGroupId === "plan-review" || optionalGroupId === "code-review" || optionalGroupId === "browser-verification";
@@ -243718,6 +244303,8 @@ async function executeWorkflowStep(deps, task, workflowStep, worktreePath, setti
243718
244303
  toolMode = "coding";
243719
244304
  }
243720
244305
  const requireExternalIntegrationEvidence = workflowStepMetadata.requireExternalIntegrationEvidence === true;
244306
+ const readonlyMcpServerAllowlist = toolMode === "readonly" ? [...new Set((workflowStepMetadata.readonlyMcpServers ?? []).map((name2) => name2.trim()).filter(Boolean))] : [];
244307
+ const allowReadonlyMcpTools = toolMode === "readonly" && readonlyMcpServerAllowlist.length > 0;
243721
244308
  let workflowReviewSpecArtifact;
243722
244309
  if (isReviewTypeWorkflowStep) {
243723
244310
  try {
@@ -243736,7 +244323,7 @@ async function executeWorkflowStep(deps, task, workflowStep, worktreePath, setti
243736
244323
  const workflowReviewSpecText = typeof workflowReviewSpecArtifact === "string" ? workflowReviewSpecArtifact : "";
243737
244324
  const planReviewSpecText = isPlanReviewStep ? workflowReviewSpecText : "";
243738
244325
  const latestTaskForUserComments = await deps.store.getTask(task.id).catch(() => task);
243739
- const sameGateStepId = workflowStep.id.replace(/^graph:/, "");
244326
+ const sameGateStepId = effectiveWorkflowStepId;
243740
244327
  const planReviewConvergenceContext = isPlanReviewStep ? buildGraphPlanReviewConvergenceContext(latestTaskForUserComments, planReviewRevisionKey) : "";
243741
244328
  const planReviewEnvironmentCapabilities = isPlanReviewStep ? await probeEnvironmentCapabilities({
243742
244329
  extraCommands: [
@@ -243784,21 +244371,44 @@ async function executeWorkflowStep(deps, task, workflowStep, worktreePath, setti
243784
244371
  let diffShortstat;
243785
244372
  let reviewInputFingerprint;
243786
244373
  let reviewedCommitSha;
244374
+ let baseRef;
243787
244375
  try {
243788
- const baseRef = await resolveDiffBaseRef(worktreePath, diffBaseCommitSha);
243789
- if (baseRef) {
243790
- const { stdout } = await execAsync40(`git diff --shortstat ${baseRef}..HEAD`, {
243791
- cwd: worktreePath,
243792
- encoding: "utf-8"
243793
- });
243794
- diffShortstat = stdout.trim() || void 0;
244376
+ baseRef = await resolveDiffBaseRef(worktreePath, diffBaseCommitSha);
244377
+ } catch {
244378
+ }
244379
+ if (isContentBindingStep && task.workspaceWorktrees === void 0) {
244380
+ const suppliedFingerprint = stepOptions?.reviewInputFingerprint?.trim();
244381
+ const proof = suppliedFingerprint ? { kind: "fingerprint", fingerprint: suppliedFingerprint } : await resolveContentReviewInputProof(worktreePath, diffBaseCommitSha);
244382
+ if (proof.kind === "unprovable") {
244383
+ const diagnostic = `${workflowStep.name} review input is unprovable (${proof.reason}); reviewer dispatch refused.`;
244384
+ await deps.store.logEntry(
244385
+ task.id,
244386
+ `[pre-merge] ${diagnostic}`,
244387
+ void 0,
244388
+ deps.getRunContextFor(task.id)
244389
+ );
244390
+ return { success: false, error: diagnostic, failureValue: "review-input-unprovable" };
244391
+ }
244392
+ reviewInputFingerprint = proof.fingerprint;
244393
+ } else if (baseRef) {
244394
+ try {
243795
244395
  if (workflowStepMetadata.reviewKind === "code") {
243796
244396
  reviewInputFingerprint = await computeCodeReviewInputFingerprint(worktreePath, baseRef);
243797
244397
  } else if (isReviewTypeWorkflowStep) {
243798
244398
  reviewInputFingerprint = await computeReviewDiffFingerprint(worktreePath, baseRef);
243799
244399
  }
244400
+ } catch {
244401
+ }
244402
+ }
244403
+ if (baseRef) {
244404
+ try {
244405
+ const { stdout } = await execAsync40(`git diff --shortstat ${baseRef}..HEAD`, {
244406
+ cwd: worktreePath,
244407
+ encoding: "utf-8"
244408
+ });
244409
+ diffShortstat = stdout.trim() || void 0;
244410
+ } catch {
243800
244411
  }
243801
- } catch {
243802
244412
  }
243803
244413
  if (isReviewTypeWorkflowStep) {
243804
244414
  try {
@@ -244108,6 +244718,12 @@ ${workflowStepUserCommentSection}` : ""}${priorFindingsBlock}
244108
244718
  const workflowStepThinkingSource = workflowStep.thinkingLevel ?? (isReviewTypeWorkflowStep ? task.validatorThinkingLevel ?? task.thinkingLevel : task.thinkingLevel);
244109
244719
  const workflowStepThinkingLevel = attemptLabel === "fallback" ? isReviewTypeWorkflowStep ? resolveValidatorFallbackThinkingLevel(workflowStepThinkingSource, settings) : resolveExecutorFallbackThinkingLevel(workflowStepThinkingSource, settings) : isReviewTypeWorkflowStep ? resolveValidatorThinkingLevel(workflowStepThinkingSource, settings) : resolveExecutorThinkingLevel(workflowStepThinkingSource, settings);
244110
244720
  const workflowStepFallbackThinkingLevel = isReviewTypeWorkflowStep ? resolveValidatorFallbackThinkingLevel(workflowStepThinkingSource, settings) : resolveExecutorFallbackThinkingLevel(workflowStepThinkingSource, settings);
244721
+ if (allowReadonlyMcpTools) {
244722
+ await deps.store.logEntry(
244723
+ task.id,
244724
+ `Workflow step '${workflowStep.name}' enabled read-only MCP servers: ${readonlyMcpServerAllowlist.join(", ")}`
244725
+ );
244726
+ }
244111
244727
  const { session } = await createResolvedAgentSession({
244112
244728
  sessionPurpose: "executor",
244113
244729
  taskExecutionSession: true,
@@ -244128,6 +244744,10 @@ ${workflowStepUserCommentSection}` : ""}${priorFindingsBlock}
244128
244744
  settings,
244129
244745
  taskEnv: stepEnv,
244130
244746
  mcpServers: await deps.resolveMcpServers(void 0),
244747
+ ...allowReadonlyMcpTools ? {
244748
+ allowMcpToolsInReadonly: true,
244749
+ readonlyMcpServerAllowlist
244750
+ } : {},
244131
244751
  // FNXC:SessionRouting 2026-06-24-11:20:
244132
244752
  // #1675: propagate task id so workflow-step requests carry the same
244133
244753
  // X-Session-Id/X-Session-Affinity as the primary session.
@@ -244343,6 +244963,60 @@ ${detectedQuestion}
244343
244963
  });
244344
244964
  }
244345
244965
  const revisionRequested = effectiveVerdict === "REVISE";
244966
+ if (isReviewTypeWorkflowStep && !isPlanReviewStep && !revisionRequested) {
244967
+ const priorReviewedCommitSha = reviewedCommitSha;
244968
+ const currentHeadSha = await readHeadSha(worktreePath);
244969
+ const fastForwardAdvance = await isFastForwardAdvance(worktreePath, priorReviewedCommitSha, currentHeadSha);
244970
+ const baseIsAncestor = await isFastForwardAdvance(worktreePath, baseRef, currentHeadSha);
244971
+ const decision = classifyReviewInlineFixRecapture({
244972
+ verdict: effectiveVerdict,
244973
+ reviewKind: workflowStepMetadata.reviewKind,
244974
+ reviewedCommitSha: priorReviewedCommitSha,
244975
+ currentHeadSha,
244976
+ baseRef,
244977
+ fastForwardAdvance,
244978
+ baseIsAncestor,
244979
+ fingerprintProbeAvailable: true
244980
+ });
244981
+ if (decision.recapture) {
244982
+ try {
244983
+ const recapturedFingerprint = workflowStepMetadata.reviewKind === "code" ? await computeCodeReviewInputFingerprint(worktreePath, baseRef) : await computeReviewDiffFingerprint(worktreePath, baseRef);
244984
+ const finalDecision = classifyReviewInlineFixRecapture({
244985
+ verdict: effectiveVerdict,
244986
+ reviewKind: workflowStepMetadata.reviewKind,
244987
+ reviewedCommitSha: priorReviewedCommitSha,
244988
+ currentHeadSha,
244989
+ baseRef,
244990
+ fastForwardAdvance,
244991
+ baseIsAncestor,
244992
+ fingerprintProbeAvailable: recapturedFingerprint !== void 0
244993
+ });
244994
+ if (finalDecision.recapture && currentHeadSha) {
244995
+ reviewInputFingerprint = recapturedFingerprint;
244996
+ reviewedCommitSha = currentHeadSha;
244997
+ const resolvedInReviewFindingCount = parsed.findings?.filter((finding) => finding.resolution === "resolved-in-review").length ?? 0;
244998
+ await deps.store.logEntry(task.id, `[pre-merge] ${workflowStep.name} re-captured its own review identity after fast-forward ${priorReviewedCommitSha?.slice(0, 7)} \u2192 ${currentHeadSha.slice(0, 7)} (${finalDecision.reason})`);
244999
+ const runContext = deps.getRunContextFor(task.id);
245000
+ if (runContext) await emitBoundedRunAudit2(deps.store, {
245001
+ taskId: task.id,
245002
+ agentId: runContext.agentId,
245003
+ runId: runContext.runId,
245004
+ domain: "git",
245005
+ mutationType: "task:review-input-recaptured",
245006
+ target: task.id,
245007
+ metadata: {
245008
+ taskId: task.id,
245009
+ workflowStepId: workflowStep.id,
245010
+ verdict: effectiveVerdict,
245011
+ resolvedInReviewFindingCount,
245012
+ reason: finalDecision.reason
245013
+ }
245014
+ });
245015
+ }
245016
+ } catch {
245017
+ }
245018
+ }
245019
+ }
244346
245020
  if (workflowStep.requiresBrowser === true) {
244347
245021
  await logBrowserVerificationActivity(`[browser-verification] finished browser verification for task ${task.id}: verdict ${effectiveVerdict}`);
244348
245022
  }
@@ -244487,9 +245161,10 @@ var init_execute_workflow_step = __esm({
244487
245161
  init_workflow_step_verdict();
244488
245162
  init_worktree_git_refs();
244489
245163
  init_review_diff_fingerprint();
245164
+ init_review_inline_fix_recapture();
244490
245165
  init_optional_step_revision();
244491
245166
  init_agent_usage_telemetry();
244492
- execAsync40 = promisify61(exec44);
245167
+ execAsync40 = promisify62(exec44);
244493
245168
  WORKFLOW_STEP_NOTES_REPAIR_TIMEOUT_MS = 12e4;
244494
245169
  }
244495
245170
  });
@@ -245123,7 +245798,7 @@ var init_public_reexports = __esm({
245123
245798
 
245124
245799
  // ../engine/src/executor/worktree-create-conflict.ts
245125
245800
  import { exec as exec45 } from "node:child_process";
245126
- import { promisify as promisify62 } from "node:util";
245801
+ import { promisify as promisify63 } from "node:util";
245127
245802
  import { existsSync as existsSync69 } from "node:fs";
245128
245803
  import { rm as rm12 } from "node:fs/promises";
245129
245804
  async function tryCreateWorktree(deps, branch, path13, taskId, startPoint, attemptNumber = 0, recoveryDepth = 0, allowSiblingBranchRename = false, settings = {}) {
@@ -245418,13 +246093,13 @@ var init_worktree_create_conflict = __esm({
245418
246093
  init_logger3();
245419
246094
  init_worktree_conflict_info();
245420
246095
  init_worktree_registry_helpers();
245421
- execAsync41 = promisify62(exec45);
246096
+ execAsync41 = promisify63(exec45);
245422
246097
  }
245423
246098
  });
245424
246099
 
245425
246100
  // ../engine/src/executor/worktree-cleanup-conflicting.ts
245426
246101
  import { exec as exec46 } from "node:child_process";
245427
- import { promisify as promisify63 } from "node:util";
246102
+ import { promisify as promisify64 } from "node:util";
245428
246103
  import { existsSync as existsSync70, lstatSync as lstatSync3, realpathSync as realpathSync11 } from "node:fs";
245429
246104
  import { rm as rm13 } from "node:fs/promises";
245430
246105
  async function cleanupConflictingWorktree(deps, worktreePath, branch, taskId) {
@@ -245543,13 +246218,13 @@ var init_worktree_cleanup_conflicting = __esm({
245543
246218
  init_src2();
245544
246219
  init_worktree_pool();
245545
246220
  init_logger3();
245546
- execAsync42 = promisify63(exec46);
246221
+ execAsync42 = promisify64(exec46);
245547
246222
  }
245548
246223
  });
245549
246224
 
245550
246225
  // ../engine/src/executor/worktree-create-outer.ts
245551
246226
  import { exec as exec47 } from "node:child_process";
245552
- import { promisify as promisify64 } from "node:util";
246227
+ import { promisify as promisify65 } from "node:util";
245553
246228
  import { existsSync as existsSync71 } from "node:fs";
245554
246229
  import { isAbsolute as isAbsolute22 } from "node:path";
245555
246230
  async function resolveWorktreeStartPoint(rootDir, store, startPoint, taskId) {
@@ -245754,13 +246429,13 @@ var init_worktree_create_outer = __esm({
245754
246429
  init_logger3();
245755
246430
  init_shell_quote();
245756
246431
  init_worktree_registry_helpers();
245757
- execAsync43 = promisify64(exec47);
246432
+ execAsync43 = promisify65(exec47);
245758
246433
  }
245759
246434
  });
245760
246435
 
245761
246436
  // ../engine/src/executor/worktree-branch-conflict-handle.ts
245762
246437
  import { exec as exec48 } from "node:child_process";
245763
- import { promisify as promisify65 } from "node:util";
246438
+ import { promisify as promisify66 } from "node:util";
245764
246439
  async function reclaimExistingWorktree(deps, task, livePath, branch, tipSha, count2, settings) {
245765
246440
  const targetPath = preservedWorktreeTargetPathForTask(task.id, livePath, settings, deps.rootDir);
245766
246441
  const normalizedPath = await deps.normalizeReclaimableWorktreePath(livePath, targetPath, task.id, settings);
@@ -245909,7 +246584,7 @@ var init_worktree_branch_conflict_handle = __esm({
245909
246584
  init_worktree_git_refs();
245910
246585
  init_worktree_registry_helpers();
245911
246586
  init_branch_conflict_format();
245912
- execAsync44 = promisify65(exec48);
246587
+ execAsync44 = promisify66(exec48);
245913
246588
  }
245914
246589
  });
245915
246590
 
@@ -246064,10 +246739,10 @@ var init_session_worktree_paths = __esm({
246064
246739
  });
246065
246740
 
246066
246741
  // ../engine/src/execution/external-execution-checkout.ts
246067
- import { execFile as execFile29 } from "node:child_process";
246742
+ import { execFile as execFile30 } from "node:child_process";
246068
246743
  import { existsSync as existsSync72, realpathSync as realpathSync13, statSync as statSync14 } from "node:fs";
246069
246744
  import { isAbsolute as isAbsolute23 } from "node:path";
246070
- import { promisify as promisify66 } from "node:util";
246745
+ import { promisify as promisify67 } from "node:util";
246071
246746
  function readSourceMetadata(task) {
246072
246747
  if (!task || typeof task !== "object") return void 0;
246073
246748
  const sourceMetadata = task.sourceMetadata;
@@ -246086,7 +246761,7 @@ async function inspectExternalGitCheckout(candidate, options = {}) {
246086
246761
  return { valid: false, reason: `checkoutPath is not a directory: ${checkoutPath}` };
246087
246762
  }
246088
246763
  const canonicalCheckout = realpathSync13(checkoutPath);
246089
- const { stdout: topLevelOutput } = await execFileAsync23("git", ["rev-parse", "--show-toplevel"], {
246764
+ const { stdout: topLevelOutput } = await execFileAsync24("git", ["rev-parse", "--show-toplevel"], {
246090
246765
  cwd: canonicalCheckout,
246091
246766
  encoding: "utf-8",
246092
246767
  timeout: 1e4
@@ -246099,7 +246774,7 @@ async function inspectExternalGitCheckout(candidate, options = {}) {
246099
246774
  reason: `checkoutPath must be the Git top-level (observed ${canonicalTopLevel})`
246100
246775
  };
246101
246776
  }
246102
- const { stdout: branchOutput } = await execFileAsync23("git", ["symbolic-ref", "--quiet", "--short", "HEAD"], {
246777
+ const { stdout: branchOutput } = await execFileAsync24("git", ["symbolic-ref", "--quiet", "--short", "HEAD"], {
246103
246778
  cwd: canonicalCheckout,
246104
246779
  encoding: "utf-8",
246105
246780
  timeout: 1e4
@@ -246109,7 +246784,7 @@ async function inspectExternalGitCheckout(candidate, options = {}) {
246109
246784
  return { valid: false, reason: "checkoutPath must have a checked-out branch" };
246110
246785
  }
246111
246786
  if (options.requireClean) {
246112
- const { stdout: statusOutput } = await execFileAsync23("git", ["status", "--porcelain=v1"], {
246787
+ const { stdout: statusOutput } = await execFileAsync24("git", ["status", "--porcelain=v1"], {
246113
246788
  cwd: canonicalCheckout,
246114
246789
  encoding: "utf-8",
246115
246790
  timeout: 1e4
@@ -246157,11 +246832,11 @@ async function resolveExternalExecutionCheckoutRoute(task) {
246157
246832
  }
246158
246833
  return { configured: true, ...inspection };
246159
246834
  }
246160
- var execFileAsync23;
246835
+ var execFileAsync24;
246161
246836
  var init_external_execution_checkout = __esm({
246162
246837
  "../engine/src/execution/external-execution-checkout.ts"() {
246163
246838
  "use strict";
246164
- execFileAsync23 = promisify66(execFile29);
246839
+ execFileAsync24 = promisify67(execFile30);
246165
246840
  }
246166
246841
  });
246167
246842
 
@@ -246185,7 +246860,7 @@ var init_resolve_authoritative_external_execution_route = __esm({
246185
246860
  import { exec as exec49 } from "node:child_process";
246186
246861
  import { existsSync as existsSync73, promises as fs7 } from "node:fs";
246187
246862
  import path4 from "node:path";
246188
- import { promisify as promisify67 } from "node:util";
246863
+ import { promisify as promisify68 } from "node:util";
246189
246864
  function workspaceExecutionAnchor(task) {
246190
246865
  const executionValues = [task.firstExecutionAt, task.executionStartedAt].map((value) => typeof value === "string" ? Date.parse(value) : Number.NaN).filter(Number.isFinite);
246191
246866
  const values2 = executionValues.length ? executionValues : [typeof task.createdAt === "string" ? Date.parse(task.createdAt) : Number.NaN].filter(Number.isFinite);
@@ -246309,14 +246984,14 @@ var init_workspace_main_checkout_guard = __esm({
246309
246984
  init_workspace_paths();
246310
246985
  init_worktree_paths();
246311
246986
  init_workflow_feedback_paths();
246312
- execAsync45 = promisify67(exec49);
246987
+ execAsync45 = promisify68(exec49);
246313
246988
  probeOptions = { encoding: "utf-8", timeout: 1e4, maxBuffer: 1024 * 1024 };
246314
246989
  }
246315
246990
  });
246316
246991
 
246317
246992
  // ../engine/src/executor/worktree-verify-invariants.ts
246318
246993
  import { exec as exec50 } from "node:child_process";
246319
- import { promisify as promisify68 } from "node:util";
246994
+ import { promisify as promisify69 } from "node:util";
246320
246995
  import { existsSync as existsSync74 } from "node:fs";
246321
246996
  async function verifyWorktreeInvariants(deps, task, worktreePathOverride, allowReanchor = true, options) {
246322
246997
  const workspaceConfig = deps.ensureWorkspaceConfig ? await deps.ensureWorkspaceConfig() : deps.workspaceConfig;
@@ -246737,17 +247412,17 @@ var init_worktree_verify_invariants = __esm({
246737
247412
  init_no_commit_eligibility();
246738
247413
  init_resolve_authoritative_external_execution_route();
246739
247414
  init_workspace_main_checkout_guard();
246740
- execAsync46 = promisify68(exec50);
247415
+ execAsync46 = promisify69(exec50);
246741
247416
  }
246742
247417
  });
246743
247418
 
246744
247419
  // ../engine/src/executor/worktree-task-done-scope-leak.ts
246745
- import { execFile as execFile30 } from "node:child_process";
247420
+ import { execFile as execFile31 } from "node:child_process";
246746
247421
  import { access as access10 } from "node:fs/promises";
246747
- import { promisify as promisify69 } from "node:util";
247422
+ import { promisify as promisify70 } from "node:util";
246748
247423
  async function verifyRepositoryCaptureEvidence(worktreePath) {
246749
247424
  await access10(worktreePath);
246750
- const { stdout } = await execFileAsync24("git", ["rev-parse", "--is-inside-work-tree"], {
247425
+ const { stdout } = await execFileAsync25("git", ["rev-parse", "--is-inside-work-tree"], {
246751
247426
  cwd: worktreePath,
246752
247427
  encoding: "utf8"
246753
247428
  });
@@ -246764,7 +247439,7 @@ async function captureRepositoryChangeEvidence(worktreePath, baseCommitSha) {
246764
247439
  } else {
246765
247440
  commands.push(["rev-parse", "--verify", "HEAD"]);
246766
247441
  }
246767
- const results = await Promise.all(commands.map((args) => execFileAsync24("git", args, {
247442
+ const results = await Promise.all(commands.map((args) => execFileAsync25("git", args, {
246768
247443
  cwd: worktreePath,
246769
247444
  encoding: "utf8"
246770
247445
  })));
@@ -246893,7 +247568,7 @@ async function evaluateTaskDoneScopeLeak(deps, task, worktreePath, promptContent
246893
247568
  }
246894
247569
  return { blocked: false };
246895
247570
  }
246896
- var execFileAsync24;
247571
+ var execFileAsync25;
246897
247572
  var init_worktree_task_done_scope_leak = __esm({
246898
247573
  "../engine/src/executor/worktree-task-done-scope-leak.ts"() {
246899
247574
  "use strict";
@@ -246901,7 +247576,7 @@ var init_worktree_task_done_scope_leak = __esm({
246901
247576
  init_logger3();
246902
247577
  init_prompt_derived_eligibility();
246903
247578
  init_workflow_feedback_paths();
246904
- execFileAsync24 = promisify69(execFile30);
247579
+ execFileAsync25 = promisify70(execFile31);
246905
247580
  }
246906
247581
  });
246907
247582
 
@@ -247311,7 +247986,7 @@ var init_task_done_refusal_handler = __esm({
247311
247986
 
247312
247987
  // ../engine/src/executor/dep-abort-cleanup.ts
247313
247988
  import { exec as exec51 } from "node:child_process";
247314
- import { promisify as promisify70 } from "node:util";
247989
+ import { promisify as promisify71 } from "node:util";
247315
247990
  async function handleDepAbortCleanup(deps, taskId, worktreePath) {
247316
247991
  executorLog.log(`${taskId} dependency added \u2014 work discarded, moved to triage for re-planning`);
247317
247992
  const task = await deps.store.getTask(taskId);
@@ -247362,7 +248037,7 @@ var init_dep_abort_cleanup = __esm({
247362
248037
  init_logger3();
247363
248038
  init_external_execution_checkout();
247364
248039
  init_lifecycle_columns();
247365
- execAsync47 = promisify70(exec51);
248040
+ execAsync47 = promisify71(exec51);
247366
248041
  }
247367
248042
  });
247368
248043
 
@@ -247886,7 +248561,7 @@ var init_no_merge_complete_column = __esm({
247886
248561
  // ../engine/src/execution/step-runner.ts
247887
248562
  import { exec as exec52 } from "node:child_process";
247888
248563
  import { existsSync as existsSync75, statSync as statSync15 } from "node:fs";
247889
- import { promisify as promisify71 } from "node:util";
248564
+ import { promisify as promisify72 } from "node:util";
247890
248565
  async function runTaskStep(deps, task, stepIndex, opts = {}) {
247891
248566
  const { store, worktreePath } = deps;
247892
248567
  const gitRevParse = deps.gitRevParse ?? defaultGitRevParse;
@@ -248070,7 +248745,7 @@ var init_step_runner = __esm({
248070
248745
  "../engine/src/execution/step-runner.ts"() {
248071
248746
  "use strict";
248072
248747
  init_logger3();
248073
- execAsync48 = promisify71(exec52);
248748
+ execAsync48 = promisify72(exec52);
248074
248749
  }
248075
248750
  });
248076
248751
 
@@ -250029,7 +250704,7 @@ var init_recover_failed_pre_merge_step = __esm({
250029
250704
 
250030
250705
  // ../engine/src/executor/reconcile-steps-from-git-history.ts
250031
250706
  import { exec as exec53 } from "node:child_process";
250032
- import { promisify as promisify72 } from "node:util";
250707
+ import { promisify as promisify73 } from "node:util";
250033
250708
  async function reconcileStepsFromGitHistory(deps, taskId, detail, worktreePath) {
250034
250709
  const baseCommitSha = detail.baseCommitSha;
250035
250710
  if (!baseCommitSha) return;
@@ -250117,7 +250792,7 @@ var init_reconcile_steps_from_git_history = __esm({
250117
250792
  "use strict";
250118
250793
  init_src2();
250119
250794
  init_logger3();
250120
- execAsync49 = promisify72(exec53);
250795
+ execAsync49 = promisify73(exec53);
250121
250796
  }
250122
250797
  });
250123
250798
 
@@ -252401,7 +253076,14 @@ async function persistWorkflowStepResultWithOutcome(deps, taskId, result2, fence
252401
253076
  scopeSuperseded = true;
252402
253077
  return null;
252403
253078
  }
252404
- const built = buildWorkflowStepResultPatch(current, result2, isPlanReviewResult);
253079
+ const unprovenApproval = resolveUnprovenReviewApproval(result2, {
253080
+ workspace: current.workspaceWorktrees !== void 0
253081
+ });
253082
+ const built = buildWorkflowStepResultPatch(
253083
+ current,
253084
+ unprovenApproval?.downgraded ?? result2,
253085
+ isPlanReviewResult
253086
+ );
252405
253087
  activityResult = built.resultToPersist;
252406
253088
  activityResults = built.results;
252407
253089
  return built.patch;
@@ -252438,21 +253120,30 @@ async function persistWorkflowStepResultWithOutcome(deps, taskId, result2, fence
252438
253120
  if (scopeSuperseded) return { scopeCurrent: false, persisted: false };
252439
253121
  if (fenceRefused) return { scopeCurrent: true, persisted: false };
252440
253122
  const persistedResult = activityResults?.find((entry) => entry.workflowStepId === result2.workflowStepId) ?? activityResult;
252441
- if (isTerminalStepResult(result2)) {
252442
- const passed = result2.status === "passed" || result2.status === "skipped" || result2.verdict === "APPROVE" || result2.verdict === "APPROVE_WITH_NOTES" || result2.verdict === "CLOSE_NO_OP";
253123
+ const approvalDowngraded = result2.status === "passed" && persistedResult.status === "failed" && result2.reviewInputFingerprint === void 0 && persistedResult.verdict === void 0;
253124
+ if (approvalDowngraded) {
253125
+ await deps.store.logEntry(
253126
+ taskId,
253127
+ `[pre-merge] ${result2.workflowStepName} approval invalidated: ${persistedResult.notes ?? persistedResult.output ?? "review input proof missing"}`,
253128
+ void 0,
253129
+ deps.getRunContextFor(taskId)
253130
+ ).catch(() => void 0);
253131
+ }
253132
+ if (isTerminalStepResult(persistedResult)) {
253133
+ const passed = persistedResult.status === "passed" || persistedResult.status === "skipped" || persistedResult.verdict === "APPROVE" || persistedResult.verdict === "APPROVE_WITH_NOTES" || persistedResult.verdict === "CLOSE_NO_OP";
252443
253134
  try {
252444
253135
  await deps.store.recordAgentActivity({
252445
253136
  type: passed ? "workflow:gate-passed" : "workflow:gate-failed",
252446
253137
  attributionClaim: resolveWorkflowGateActivityClaim(
252447
- deps.workflowGateActivityPrincipals?.get(`${taskId}\0${result2.workflowStepId}`) ?? deps.activeWorkflowPrincipals?.get(taskId)?.agentId,
253138
+ deps.workflowGateActivityPrincipals?.get(`${taskId}\0${persistedResult.workflowStepId}`) ?? deps.activeWorkflowPrincipals?.get(taskId)?.agentId,
252448
253139
  live?.assignedAgentId
252449
253140
  ),
252450
253141
  taskId,
252451
- occurredAt: result2.completedAt ?? result2.startedAt ?? (/* @__PURE__ */ new Date()).toISOString(),
252452
- discriminator: `${result2.workflowStepId}:${result2.startedAt ?? result2.completedAt ?? result2.status}`,
252453
- metadata: buildWorkflowGateActivityMetadata(result2, persistedResult)
253142
+ occurredAt: persistedResult.completedAt ?? persistedResult.startedAt ?? (/* @__PURE__ */ new Date()).toISOString(),
253143
+ discriminator: `${persistedResult.workflowStepId}:${persistedResult.startedAt ?? persistedResult.completedAt ?? persistedResult.status}`,
253144
+ metadata: buildWorkflowGateActivityMetadata(persistedResult, persistedResult)
252454
253145
  });
252455
- deps.workflowGateActivityPrincipals?.delete(`${taskId}\0${result2.workflowStepId}`);
253146
+ deps.workflowGateActivityPrincipals?.delete(`${taskId}\0${persistedResult.workflowStepId}`);
252456
253147
  } catch (error3) {
252457
253148
  executorLog.warn(`[agent-activity] ${taskId}: failed to record workflow gate activity: ${error3 instanceof Error ? error3.message : String(error3)}`);
252458
253149
  }
@@ -254463,7 +255154,7 @@ var init_run_spawned_child = __esm({
254463
255154
 
254464
255155
  // ../engine/src/auto-recovery-handlers/file-scope.ts
254465
255156
  import { exec as exec54 } from "node:child_process";
254466
- import { promisify as promisify73 } from "node:util";
255157
+ import { promisify as promisify74 } from "node:util";
254467
255158
  function createFileScopeAutoRecoveryHandler(deps) {
254468
255159
  return new FileScopeAutoRecoveryHandler({
254469
255160
  ...deps,
@@ -254474,7 +255165,7 @@ var execAsync50, FileScopeAutoRecoveryHandler;
254474
255165
  var init_file_scope2 = __esm({
254475
255166
  "../engine/src/auto-recovery-handlers/file-scope.ts"() {
254476
255167
  "use strict";
254477
- execAsync50 = promisify73(exec54);
255168
+ execAsync50 = promisify74(exec54);
254478
255169
  FileScopeAutoRecoveryHandler = class {
254479
255170
  constructor(deps) {
254480
255171
  this.deps = deps;
@@ -254490,7 +255181,7 @@ var init_file_scope2 = __esm({
254490
255181
  // ../engine/src/auto-recovery-handlers/branch-worktree.ts
254491
255182
  import { exec as exec55 } from "node:child_process";
254492
255183
  import { existsSync as existsSync78 } from "node:fs";
254493
- import { promisify as promisify74 } from "node:util";
255184
+ import { promisify as promisify75 } from "node:util";
254494
255185
  var execAsync51, baseLog2, GIT_TIMEOUT_MS8, GIT_MAX_BUFFER6, BranchWorktreeAutoRecoveryHandler;
254495
255186
  var init_branch_worktree = __esm({
254496
255187
  "../engine/src/auto-recovery-handlers/branch-worktree.ts"() {
@@ -254500,7 +255191,7 @@ var init_branch_worktree = __esm({
254500
255191
  init_active_session_registry();
254501
255192
  init_integration_branch();
254502
255193
  init_logger3();
254503
- execAsync51 = promisify74(exec55);
255194
+ execAsync51 = promisify75(exec55);
254504
255195
  baseLog2 = createLogger2("auto-recovery:branch-worktree");
254505
255196
  GIT_TIMEOUT_MS8 = 3e4;
254506
255197
  GIT_MAX_BUFFER6 = 10 * 1024 * 1024;
@@ -255567,7 +256258,7 @@ var init_run_raw_cli_command = __esm({
255567
256258
 
255568
256259
  // ../engine/src/executor/reset-steps-if-work-lost.ts
255569
256260
  import { exec as exec56 } from "node:child_process";
255570
- import { promisify as promisify75 } from "node:util";
256261
+ import { promisify as promisify76 } from "node:util";
255571
256262
  async function resetStepsIfWorkLost(deps, task) {
255572
256263
  const completedSteps = task.steps.filter(
255573
256264
  (s3) => s3.status === "done" || s3.status === "in-progress"
@@ -255608,7 +256299,7 @@ var init_reset_steps_if_work_lost = __esm({
255608
256299
  init_src2();
255609
256300
  init_worktree_names();
255610
256301
  init_logger3();
255611
- execAsync52 = promisify75(exec56);
256302
+ execAsync52 = promisify76(exec56);
255612
256303
  }
255613
256304
  });
255614
256305
 
@@ -255655,7 +256346,7 @@ var init_route_retryable_remediation = __esm({
255655
256346
 
255656
256347
  // ../engine/src/executor/build-foreach-worktree-deps.ts
255657
256348
  import { exec as exec57 } from "node:child_process";
255658
- import { promisify as promisify76 } from "node:util";
256349
+ import { promisify as promisify77 } from "node:util";
255659
256350
  function buildForeachWorktreeDeps(deps, task, runId) {
255660
256351
  const taskId = task.id;
255661
256352
  const instancePaths = /* @__PURE__ */ new Map();
@@ -255849,20 +256540,20 @@ var init_build_foreach_worktree_deps = __esm({
255849
256540
  init_worktree_names();
255850
256541
  init_worktree_paths();
255851
256542
  init_logger3();
255852
- execAsync53 = promisify76(exec57);
256543
+ execAsync53 = promisify77(exec57);
255853
256544
  WORKSPACE_ISOLATION_UNSUPPORTED_MESSAGE = "per-instance worktree isolation is not supported for workspace projects";
255854
256545
  }
255855
256546
  });
255856
256547
 
255857
256548
  // ../engine/src/executor/derive-remediation-steps.ts
255858
- import { createHash as createHash34 } from "node:crypto";
256549
+ import { createHash as createHash35 } from "node:crypto";
255859
256550
  function normalizeVerificationEvidence(output2) {
255860
256551
  if (!output2?.trim()) return "";
255861
256552
  return output2.replace(ansiEscapeSequence, "").replace(/\r\n?/g, "\n").split("\n").map((line2) => line2.trim().replace(isoTimestamp, "<timestamp>").replace(bareTimestamp, "<timestamp>").replace(elapsedDuration, "<duration>").replace(/\s+/g, " ")).filter(Boolean).join("\n");
255862
256553
  }
255863
256554
  function verificationEvidenceDigest(output2) {
255864
256555
  const evidence = normalizeVerificationEvidence(output2);
255865
- return evidence ? createHash34("sha256").update(evidence).digest("hex").slice(0, 16) : void 0;
256556
+ return evidence ? createHash35("sha256").update(evidence).digest("hex").slice(0, 16) : void 0;
255866
256557
  }
255867
256558
  function verificationCandidates(input) {
255868
256559
  const detail = input.verificationCommandLabel ?? "verification command";
@@ -260565,10 +261256,10 @@ var init_pending_review_block = __esm({
260565
261256
  });
260566
261257
 
260567
261258
  // ../engine/src/merge/merger-orphan-rehome.ts
260568
- import { execFile as execFile31 } from "node:child_process";
260569
- import { promisify as promisify77 } from "node:util";
261259
+ import { execFile as execFile32 } from "node:child_process";
261260
+ import { promisify as promisify78 } from "node:util";
260570
261261
  async function runGit6(args, cwd) {
260571
- const { stdout } = await execFileAsync25("git", args, {
261262
+ const { stdout } = await execFileAsync26("git", args, {
260572
261263
  cwd,
260573
261264
  encoding: "utf-8",
260574
261265
  timeout: 3e4
@@ -260577,7 +261268,7 @@ async function runGit6(args, cwd) {
260577
261268
  }
260578
261269
  async function isAncestor3(repoDir, ancestor, descendant) {
260579
261270
  try {
260580
- await execFileAsync25(
261271
+ await execFileAsync26(
260581
261272
  "git",
260582
261273
  ["merge-base", "--is-ancestor", ancestor, descendant],
260583
261274
  { cwd: repoDir, encoding: "utf-8", timeout: 3e4 }
@@ -260672,13 +261363,13 @@ async function rehomeOrphanOntoIntegration(input) {
260672
261363
  newTipSha: advanceResult.newSha
260673
261364
  };
260674
261365
  }
260675
- var execFileAsync25;
261366
+ var execFileAsync26;
260676
261367
  var init_merger_orphan_rehome = __esm({
260677
261368
  "../engine/src/merge/merger-orphan-rehome.ts"() {
260678
261369
  "use strict";
260679
261370
  init_src2();
260680
261371
  init_merger_ref_update_advance();
260681
- execFileAsync25 = promisify77(execFile31);
261372
+ execFileAsync26 = promisify78(execFile32);
260682
261373
  }
260683
261374
  });
260684
261375
 
@@ -260705,7 +261396,7 @@ var init_merge_execution_exclusion = __esm({
260705
261396
 
260706
261397
  // ../engine/src/executor/run-implementation.ts
260707
261398
  import { exec as exec58 } from "node:child_process";
260708
- import { promisify as promisify78 } from "node:util";
261399
+ import { promisify as promisify79 } from "node:util";
260709
261400
  import { existsSync as existsSync81 } from "node:fs";
260710
261401
  import { join as join93 } from "node:path";
260711
261402
  import { SessionManager as SessionManager2 } from "@earendil-works/pi-coding-agent";
@@ -263541,7 +264232,7 @@ var init_run_implementation = __esm({
263541
264232
  init_worktree_acquisition();
263542
264233
  init_worktree_paths();
263543
264234
  init_worktree_pool();
263544
- execAsync54 = promisify78(exec58);
264235
+ execAsync54 = promisify79(exec58);
263545
264236
  MAX_TASK_DONE_SESSION_RETRIES = 3;
263546
264237
  }
263547
264238
  });
@@ -267267,8 +267958,8 @@ var init_worktree_create_binders = __esm({
267267
267958
  });
267268
267959
 
267269
267960
  // ../engine/src/executor/wire-executor-lifecycle.ts
267270
- import { execFile as execFile32 } from "node:child_process";
267271
- import { promisify as promisify79 } from "node:util";
267961
+ import { execFile as execFile33 } from "node:child_process";
267962
+ import { promisify as promisify80 } from "node:util";
267272
267963
  async function releaseWorkspaceAcquireClaims(store, taskId) {
267273
267964
  for (const entry of activeSessionRegistry.entriesByKind("workspace-repo-acquire")) {
267274
267965
  if (entry.taskId === taskId) activeSessionRegistry.unregisterPath(entry.path);
@@ -267316,7 +268007,7 @@ function wireExecutorLifecycle(deps) {
267316
268007
  if (await canonicalizeWorktreePath(entry.worktreePath) === await canonicalizeWorktreePath(entry.repoRootDir)) throw new Error("Refusing to remove workspace repository root");
267317
268008
  activeSessionRegistry.unregisterPath(entry.worktreePath);
267318
268009
  await removeWorktree({ worktreePath: entry.worktreePath, rootDir: entry.repoRootDir, settings: await deps.store.getSettings(), taskId: task.id, reason: RemovalReason.ExecutorDispose, force: true });
267319
- await execFileAsync26("git", ["branch", "-D", entry.branch], { cwd: entry.repoRootDir, timeout: 12e4, maxBuffer: 10 * 1024 * 1024 });
268010
+ await execFileAsync27("git", ["branch", "-D", entry.branch], { cwd: entry.repoRootDir, timeout: 12e4, maxBuffer: 10 * 1024 * 1024 });
267320
268011
  if (task.workspaceWorktrees) for (const repoRel of [entry.repoRel, ...entry.aliasRepoRels]) delete task.workspaceWorktrees[repoRel];
267321
268012
  removed.push(entry.repoRel);
267322
268013
  } catch (error3) {
@@ -267744,7 +268435,7 @@ function wireTaskExecutorLifecycle(host) {
267744
268435
  h.workflowAgentCapacity = new WorkflowAgentCapacity(h.options?.agentStore ?? void 0);
267745
268436
  applyWireExecutorLifecycleDisposers(host, wireExecutorLifecycle(buildWireExecutorLifecycleDeps(host)));
267746
268437
  }
267747
- var execFileAsync26, WIRE_LIFECYCLE_FIELDS, WIRE_LIFECYCLE_METHODS;
268438
+ var execFileAsync27, WIRE_LIFECYCLE_FIELDS, WIRE_LIFECYCLE_METHODS;
267748
268439
  var init_wire_executor_lifecycle = __esm({
267749
268440
  "../engine/src/executor/wire-executor-lifecycle.ts"() {
267750
268441
  "use strict";
@@ -267761,7 +268452,7 @@ var init_wire_executor_lifecycle = __esm({
267761
268452
  init_task_predicates();
267762
268453
  init_facade_methods();
267763
268454
  init_workflow_agent_capacity();
267764
- execFileAsync26 = promisify79(execFile32);
268455
+ execFileAsync27 = promisify80(execFile33);
267765
268456
  WIRE_LIFECYCLE_FIELDS = [
267766
268457
  "activeConfiguredCommandControllers",
267767
268458
  "activeSessions",
@@ -270658,7 +271349,7 @@ import { promises as fs9 } from "node:fs";
270658
271349
  import os7 from "node:os";
270659
271350
  import path5 from "node:path";
270660
271351
  import { exec as exec59 } from "node:child_process";
270661
- import { promisify as promisify80 } from "node:util";
271352
+ import { promisify as promisify81 } from "node:util";
270662
271353
  var execAsync55, verifyLog, GitCheckoutMaterializer;
270663
271354
  var init_mission_verification = __esm({
270664
271355
  "../engine/src/missions/mission-verification.ts"() {
@@ -270668,7 +271359,7 @@ var init_mission_verification = __esm({
270668
271359
  init_sandbox_exec_detect();
270669
271360
  init_verification_utils();
270670
271361
  init_logger3();
270671
- execAsync55 = promisify80(exec59);
271362
+ execAsync55 = promisify81(exec59);
270672
271363
  verifyLog = createLogger2("mission-verify");
270673
271364
  GitCheckoutMaterializer = class {
270674
271365
  async materialize(rootDir, revision) {
@@ -270711,14 +271402,14 @@ ${stdout.trim()}`);
270711
271402
 
270712
271403
  // ../engine/src/missions/mission-execution-loop.ts
270713
271404
  import { EventEmitter as EventEmitter25 } from "node:events";
270714
- import { createHash as createHash35 } from "node:crypto";
271405
+ import { createHash as createHash36 } from "node:crypto";
270715
271406
  import { exec as exec60 } from "node:child_process";
270716
- import { promisify as promisify81 } from "node:util";
271407
+ import { promisify as promisify82 } from "node:util";
270717
271408
  function quoteShellArg8(value) {
270718
271409
  return `'${value.replace(/'/g, `'\\''`)}'`;
270719
271410
  }
270720
271411
  function fingerprintMissionValidationInput(landedSha, provider, modelId, systemPrompt, userPrompt) {
270721
- return createHash35("sha256").update(Buffer.from(JSON.stringify(["mission-validation-input-v1", landedSha, provider, modelId, systemPrompt, userPrompt]), "utf8")).digest("hex");
271412
+ return createHash36("sha256").update(Buffer.from(JSON.stringify(["mission-validation-input-v1", landedSha, provider, modelId, systemPrompt, userPrompt]), "utf8")).digest("hex");
270722
271413
  }
270723
271414
  var execAsync56, loopLog, VALIDATION_TIMEOUT_MS, MAX_VALIDATION_RESPONSE_BYTES, MAX_VALIDATION_JSON_CANDIDATES, VALIDATION_FAILURE_BUDGET_PER_FINGERPRINT, MissionExecutionLoop;
270724
271415
  var init_mission_execution_loop = __esm({
@@ -270734,7 +271425,7 @@ var init_mission_execution_loop = __esm({
270734
271425
  init_fallback_model_observer();
270735
271426
  init_mcp_resolution();
270736
271427
  init_run_audit();
270737
- execAsync56 = promisify81(exec60);
271428
+ execAsync56 = promisify82(exec60);
270738
271429
  loopLog = createLogger2("mission-loop");
270739
271430
  VALIDATION_TIMEOUT_MS = 10 * 60 * 1e3;
270740
271431
  MAX_VALIDATION_RESPONSE_BYTES = 256 * 1024;
@@ -272248,44 +272939,9 @@ ${taskContext}` : ""}`;
272248
272939
  }
272249
272940
  });
272250
272941
 
272251
- // ../engine/src/merge/merge-content-capture.ts
272252
- async function captureMergeContentDescriptor(task, deps) {
272253
- if (describeMergeContentShape(task) === "workspace") {
272254
- try {
272255
- const evidence = await captureWorkspaceReviewEvidence({
272256
- task,
272257
- workspaceRootDir: deps.workspaceRootDir,
272258
- settings: deps.settings
272259
- });
272260
- return {
272261
- kind: "workspace",
272262
- repositories: {
272263
- state: "captured",
272264
- fingerprints: Object.fromEntries(evidence.repositories.filter((repository) => repository.fingerprint).map((repository) => [repository.repository, repository.fingerprint])),
272265
- inScopeModified: [...evidence.modifiedRepositories].sort()
272266
- }
272267
- };
272268
- } catch {
272269
- return { kind: "workspace", repositories: { state: "unavailable", reason: "workspace-evidence-capture-failed" } };
272270
- }
272271
- }
272272
- const baseRef = task.worktree ? await resolveDiffBaseRef(task.worktree, task.baseCommitSha) : void 0;
272273
- const probe = await probeReviewDiffFingerprint(task.worktree, baseRef);
272274
- return { kind: "singular", diff: probe };
272275
- }
272276
- var init_merge_content_capture = __esm({
272277
- "../engine/src/merge/merge-content-capture.ts"() {
272278
- "use strict";
272279
- init_src2();
272280
- init_worktree_git_refs();
272281
- init_review_diff_fingerprint();
272282
- init_workspace_review_evidence();
272283
- }
272284
- });
272285
-
272286
272942
  // ../engine/src/merge/merger-ai-squash-gates.ts
272287
- import { execFile as execFile33 } from "node:child_process";
272288
- import { promisify as promisify82 } from "node:util";
272943
+ import { execFile as execFile34 } from "node:child_process";
272944
+ import { promisify as promisify83 } from "node:util";
272289
272945
  function resolveRepoDeclaredScopeTransform({ repoRel, repoKeys }) {
272290
272946
  return {
272291
272947
  transform(scope) {
@@ -272316,8 +272972,8 @@ async function enforceAiMergeSquashGates(params) {
272316
272972
  });
272317
272973
  } catch (error3) {
272318
272974
  if (!(error3 instanceof FileScopeViolationError)) throw error3;
272319
- await execFileAsync27("git", ["reset", "--hard", params.tipSha], { cwd: params.mergeRoot });
272320
- await execFileAsync27("git", ["clean", "-fd"], { cwd: params.mergeRoot });
272975
+ await execFileAsync28("git", ["reset", "--hard", params.tipSha], { cwd: params.mergeRoot });
272976
+ await execFileAsync28("git", ["clean", "-fd"], { cwd: params.mergeRoot });
272321
272977
  try {
272322
272978
  await params.store.updateTask(params.taskId, { aiMergeReviewReconciliation: null });
272323
272979
  } catch {
@@ -272325,13 +272981,13 @@ async function enforceAiMergeSquashGates(params) {
272325
272981
  throw error3;
272326
272982
  }
272327
272983
  }
272328
- var execFileAsync27;
272984
+ var execFileAsync28;
272329
272985
  var init_merger_ai_squash_gates = __esm({
272330
272986
  "../engine/src/merge/merger-ai-squash-gates.ts"() {
272331
272987
  "use strict";
272332
272988
  init_workspace_paths();
272333
272989
  init_merger_file_scope();
272334
- execFileAsync27 = promisify82(execFile33);
272990
+ execFileAsync28 = promisify83(execFile34);
272335
272991
  }
272336
272992
  });
272337
272993
 
@@ -272831,11 +273487,11 @@ var init_workspace_merge_readiness = __esm({
272831
273487
  });
272832
273488
 
272833
273489
  // ../engine/src/merge/workspace-fence-ref.ts
272834
- import { execFile as execFile34 } from "node:child_process";
272835
- import { createHash as createHash36 } from "node:crypto";
272836
- import { promisify as promisify83 } from "node:util";
273490
+ import { execFile as execFile35 } from "node:child_process";
273491
+ import { createHash as createHash37 } from "node:crypto";
273492
+ import { promisify as promisify84 } from "node:util";
272837
273493
  async function git6(args, cwd) {
272838
- const { stdout } = await execFileAsync28("git", args, {
273494
+ const { stdout } = await execFileAsync29("git", args, {
272839
273495
  cwd,
272840
273496
  encoding: "utf8",
272841
273497
  maxBuffer: 1024 * 1024
@@ -272848,7 +273504,7 @@ async function remoteRefSha(remote, ref, cwd) {
272848
273504
  return /^[0-9a-f]{40,64}$/i.test(sha ?? "") ? sha : void 0;
272849
273505
  }
272850
273506
  function workspaceLandFenceRef(repoRelPath) {
272851
- const slug2 = createHash36("sha256").update(repoRelPath).digest("hex").slice(0, 24);
273507
+ const slug2 = createHash37("sha256").update(repoRelPath).digest("hex").slice(0, 24);
272852
273508
  return `refs/fusion/workspace-lease/${slug2}`;
272853
273509
  }
272854
273510
  function mergeDispatchFenceRef(taskId) {
@@ -272857,7 +273513,7 @@ function mergeDispatchFenceRef(taskId) {
272857
273513
  async function createFenceObject(cwd, handle2) {
272858
273514
  const tree = await git6(["hash-object", "-w", "-t", "tree", "/dev/null"], cwd);
272859
273515
  const message = `workspace lease ${handle2.leaseKey} ${handle2.owner.taskId} ${handle2.owner.nodeId} ${handle2.owner.incarnationId} ${handle2.fenceToken}`;
272860
- const { stdout } = await execFileAsync28("git", [
273516
+ const { stdout } = await execFileAsync29("git", [
272861
273517
  "-c",
272862
273518
  "user.name=Fusion",
272863
273519
  "-c",
@@ -272987,11 +273643,11 @@ async function publishWorkspaceIntegrationRef(input) {
272987
273643
  await pushWithWorkspaceFence({ ...input, expectedTargetSha: observedTargetSha });
272988
273644
  return { republishedFromObservedTip: true, observedTargetSha };
272989
273645
  }
272990
- var execFileAsync28, WorkspaceFenceRefError;
273646
+ var execFileAsync29, WorkspaceFenceRefError;
272991
273647
  var init_workspace_fence_ref = __esm({
272992
273648
  "../engine/src/merge/workspace-fence-ref.ts"() {
272993
273649
  "use strict";
272994
- execFileAsync28 = promisify83(execFile34);
273650
+ execFileAsync29 = promisify84(execFile35);
272995
273651
  WorkspaceFenceRefError = class extends Error {
272996
273652
  constructor(message, kind) {
272997
273653
  super(message);
@@ -273003,10 +273659,10 @@ var init_workspace_fence_ref = __esm({
273003
273659
  });
273004
273660
 
273005
273661
  // ../engine/src/merge/workspace-integration-target.ts
273006
- import { execFile as execFile35 } from "node:child_process";
273007
- import { promisify as promisify84 } from "node:util";
273662
+ import { execFile as execFile36 } from "node:child_process";
273663
+ import { promisify as promisify85 } from "node:util";
273008
273664
  async function git7(args, cwd) {
273009
- const { stdout } = await execFileAsync29("git", args, { cwd, encoding: "utf8" });
273665
+ const { stdout } = await execFileAsync30("git", args, { cwd, encoding: "utf8" });
273010
273666
  return stdout.trim();
273011
273667
  }
273012
273668
  async function resolveWorkspaceIntegrationTarget(input) {
@@ -273046,11 +273702,11 @@ async function resolveWorkspaceIntegrationTarget(input) {
273046
273702
  `Workspace repository ${input.repository} has ambiguous integration remotes: ${remotes.join(", ")}`
273047
273703
  );
273048
273704
  }
273049
- var execFileAsync29, WorkspaceEnvironmentError, WorkspaceIntegrationTargetError;
273705
+ var execFileAsync30, WorkspaceEnvironmentError, WorkspaceIntegrationTargetError;
273050
273706
  var init_workspace_integration_target = __esm({
273051
273707
  "../engine/src/merge/workspace-integration-target.ts"() {
273052
273708
  "use strict";
273053
- execFileAsync29 = promisify84(execFile35);
273709
+ execFileAsync30 = promisify85(execFile36);
273054
273710
  WorkspaceEnvironmentError = class extends Error {
273055
273711
  constructor(repository, resource, action, message) {
273056
273712
  super(message);
@@ -273073,14 +273729,14 @@ var init_workspace_integration_target = __esm({
273073
273729
  });
273074
273730
 
273075
273731
  // ../engine/src/merge/merger-ai-worktree.ts
273076
- import { execFile as execFile36 } from "node:child_process";
273732
+ import { execFile as execFile37 } from "node:child_process";
273077
273733
  import { appendFileSync as appendFileSync2, existsSync as existsSync84, mkdirSync as mkdirSync10, readdirSync as readdirSync12, readFileSync as readFileSync26, realpathSync as realpathSync14, rmSync as rmSync7, statSync as statSync16 } from "node:fs";
273078
273734
  import { rm as rm17 } from "node:fs/promises";
273079
273735
  import { tmpdir as tmpdir11 } from "node:os";
273080
273736
  import { isAbsolute as isAbsolute25, join as join95, relative as relative22 } from "node:path";
273081
- import { promisify as promisify85 } from "node:util";
273737
+ import { promisify as promisify86 } from "node:util";
273082
273738
  async function git8(args, cwd, opts = {}) {
273083
- const { stdout } = await execFileAsync30("git", args, {
273739
+ const { stdout } = await execFileAsync31("git", args, {
273084
273740
  cwd,
273085
273741
  encoding: "utf-8",
273086
273742
  timeout: opts.timeout ?? 12e4,
@@ -273109,14 +273765,7 @@ function ensureAiMergeRootIgnored(projectRootDir, settings) {
273109
273765
  if (!existsSync84(excludePath)) return;
273110
273766
  try {
273111
273767
  const current = readFileSync26(excludePath, "utf-8");
273112
- const legacyAiMergeRoot = resolveLegacyAiMergeRootPath(projectRootDir);
273113
- const legacyRelativeAiMergeRoot = relative22(projectRootDir, legacyAiMergeRoot);
273114
- const entries = [`${legacyRelativeAiMergeRoot.replaceAll("\\", "/")}/`];
273115
- const aiMergeRoot = resolveAiMergeRootPath(projectRootDir, settings);
273116
- const relativeAiMergeRoot = relative22(projectRootDir, aiMergeRoot);
273117
- if (relativeAiMergeRoot && !relativeAiMergeRoot.startsWith("..") && !isAbsolute25(relativeAiMergeRoot)) {
273118
- entries.push(`${relativeAiMergeRoot.replaceAll("\\", "/")}/`);
273119
- }
273768
+ const entries = resolveAiMergeSearchRoots(projectRootDir, settings).map((root) => relative22(projectRootDir, root)).filter((root) => root && !root.startsWith("..") && !isAbsolute25(root)).map((root) => `${root.replaceAll("\\", "/")}/`);
273120
273769
  const missing = entries.filter((entry) => !current.split(/\r?\n/).includes(entry));
273121
273770
  if (missing.length > 0) {
273122
273771
  appendFileSync2(excludePath, `${current.endsWith("\n") ? "" : "\n"}${missing.join("\n")}
@@ -273132,7 +273781,7 @@ function resolveAiMergeRoot(projectRootDir, settings) {
273132
273781
  return root;
273133
273782
  }
273134
273783
  function getAiMergeTempSearchRoots(projectRootDir, settings) {
273135
- const roots = [resolveAiMergeRoot(projectRootDir, settings), resolveLegacyAiMergeRootPath(projectRootDir), tmpdir11()];
273784
+ const roots = [resolveAiMergeRoot(projectRootDir, settings), ...resolveAiMergeSearchRoots(projectRootDir, settings), tmpdir11()];
273136
273785
  const testWorkerRoot = process.env.FUSION_TEST_WORKER_ROOT;
273137
273786
  if (testWorkerRoot) {
273138
273787
  try {
@@ -273185,7 +273834,7 @@ async function pruneExistingAiMergeWorktrees(taskId, projectRootDir, audit, log4
273185
273834
  let alreadyAbsent = false;
273186
273835
  try {
273187
273836
  cleanupAttempted = true;
273188
- await execFileAsync30("git", ["worktree", "remove", "--force", canonicalPath], {
273837
+ await execFileAsync31("git", ["worktree", "remove", "--force", canonicalPath], {
273189
273838
  cwd: projectRootDir,
273190
273839
  timeout: 3e4
273191
273840
  });
@@ -273216,7 +273865,7 @@ async function pruneExistingAiMergeWorktrees(taskId, projectRootDir, audit, log4
273216
273865
  }
273217
273866
  if (cleanupAttempted) {
273218
273867
  try {
273219
- await execFileAsync30("git", ["worktree", "prune"], { cwd: projectRootDir, timeout: 3e4 });
273868
+ await execFileAsync31("git", ["worktree", "prune"], { cwd: projectRootDir, timeout: 3e4 });
273220
273869
  } catch (err) {
273221
273870
  await log42(`AI merge pre-merge prune: git worktree prune failed: ${describeCleanupError(err)}`);
273222
273871
  }
@@ -273282,7 +273931,7 @@ async function cleanupAiMergeWorktree(input) {
273282
273931
  await audit.git({ type: "merge:ai-worktree-cleanup", target: canonicalRoot, metadata: { ...cleanupMetadata, phase: "git-prune", success: false, error: error3, ...code ? { code } : {} } });
273283
273932
  }
273284
273933
  }
273285
- var execFileAsync30;
273934
+ var execFileAsync31;
273286
273935
  var init_merger_ai_worktree = __esm({
273287
273936
  "../engine/src/merge/merger-ai-worktree.ts"() {
273288
273937
  "use strict";
@@ -273290,7 +273939,7 @@ var init_merger_ai_worktree = __esm({
273290
273939
  init_self_healing();
273291
273940
  init_worktree_paths();
273292
273941
  init_worktree_removal_retry();
273293
- execFileAsync30 = promisify85(execFile36);
273942
+ execFileAsync31 = promisify86(execFile37);
273294
273943
  }
273295
273944
  });
273296
273945
 
@@ -273642,8 +274291,8 @@ var init_merger_ai_prompts = __esm({
273642
274291
  });
273643
274292
 
273644
274293
  // ../engine/src/merge/merger-ai.ts
273645
- import { execFile as execFile37 } from "node:child_process";
273646
- import { promisify as promisify86 } from "node:util";
274294
+ import { execFile as execFile38 } from "node:child_process";
274295
+ import { promisify as promisify87 } from "node:util";
273647
274296
  import { realpathSync as realpathSync15, readdirSync as readdirSync13 } from "node:fs";
273648
274297
  import { mkdtemp as mkdtemp5 } from "node:fs/promises";
273649
274298
  import { tmpdir as tmpdir12 } from "node:os";
@@ -273671,7 +274320,7 @@ function writeTransientMergeStatus(store, taskId, signal, status) {
273671
274320
  return fence.write("lifecycle", () => store.updateTask(taskId, { status }).catch(() => void 0));
273672
274321
  }
273673
274322
  async function git9(args, cwd, opts = {}) {
273674
- const { stdout } = await execFileAsync31("git", args, {
274323
+ const { stdout } = await execFileAsync32("git", args, {
273675
274324
  cwd,
273676
274325
  encoding: "utf-8",
273677
274326
  timeout: opts.timeout ?? 12e4,
@@ -273711,7 +274360,7 @@ function boundBlockingReviewReasons(reasons) {
273711
274360
  }
273712
274361
  function listAiMergeWorktreeCandidates(taskId, projectRootDir, settings) {
273713
274362
  const prefix = `fusion-ai-merge-${taskId.toLowerCase()}-`;
273714
- const roots = Array.from(/* @__PURE__ */ new Set([resolveAiMergeRoot(projectRootDir, settings), resolveLegacyAiMergeRootPath(projectRootDir), tmpdir12()]));
274363
+ const roots = Array.from(/* @__PURE__ */ new Set([resolveAiMergeRoot(projectRootDir, settings), ...resolveAiMergeSearchRoots(projectRootDir, settings), tmpdir12()]));
273715
274364
  const testWorkerRoot = process.env.FUSION_TEST_WORKER_ROOT;
273716
274365
  if (testWorkerRoot) {
273717
274366
  try {
@@ -276060,7 +276709,7 @@ async function finalizeTask(store, taskId, result2, audit, log42, rootDir, fence
276060
276709
  function throwIfAborted3(signal, taskId) {
276061
276710
  assertMergeGenerationOwned(signal, taskId);
276062
276711
  }
276063
- var execFileAsync31, aiMergeLog, MAX_CONCURRENT_ADVANCE_RETRIES, MAX_BLOCKING_REVIEW_REASONS, AiMergeBlockedError, AiMergeReviewReconciliationInvalidatedError, LEGACY_COMPLETE_COLUMN, LEGACY_ARCHIVED_COLUMN, LEGACY_TERMINAL_COLUMNS3, LEGACY_REBOUND_COLUMN2, STRONG_LANDED_STRATEGIES, RecordedMergeBranchTipChangedError, WORKSPACE_REPO_LAND_OWNER_KEY, WorkspaceRepoLandBusyError, WorkspaceMergeTechnicalError, WorkspacePartialLandError, WorkspaceFinalizeBlockedError, WorkspaceReviewRequiredError, WorkspaceMergeDispatchSupersededError;
276712
+ var execFileAsync32, aiMergeLog, MAX_CONCURRENT_ADVANCE_RETRIES, MAX_BLOCKING_REVIEW_REASONS, AiMergeBlockedError, AiMergeReviewReconciliationInvalidatedError, LEGACY_COMPLETE_COLUMN, LEGACY_ARCHIVED_COLUMN, LEGACY_TERMINAL_COLUMNS3, LEGACY_REBOUND_COLUMN2, STRONG_LANDED_STRATEGIES, RecordedMergeBranchTipChangedError, WORKSPACE_REPO_LAND_OWNER_KEY, WorkspaceRepoLandBusyError, WorkspaceMergeTechnicalError, WorkspacePartialLandError, WorkspaceFinalizeBlockedError, WorkspaceReviewRequiredError, WorkspaceMergeDispatchSupersededError;
276064
276713
  var init_merger_ai = __esm({
276065
276714
  "../engine/src/merge/merger-ai.ts"() {
276066
276715
  "use strict";
@@ -276110,7 +276759,7 @@ var init_merger_ai = __esm({
276110
276759
  init_merger_ai_prompts();
276111
276760
  init_merger_ai_worktree();
276112
276761
  init_merger_ai_prompts();
276113
- execFileAsync31 = promisify86(execFile37);
276762
+ execFileAsync32 = promisify87(execFile38);
276114
276763
  aiMergeLog = createLogger2("merger-ai");
276115
276764
  MAX_CONCURRENT_ADVANCE_RETRIES = 3;
276116
276765
  MAX_BLOCKING_REVIEW_REASONS = 8;
@@ -276262,7 +276911,7 @@ var init_workspace_merge_dispatch_lease = __esm({
276262
276911
  // ../engine/src/execution/task-revert.ts
276263
276912
  import { exec as exec61 } from "node:child_process";
276264
276913
  import { join as join97 } from "node:path";
276265
- import { promisify as promisify87 } from "node:util";
276914
+ import { promisify as promisify88 } from "node:util";
276266
276915
  function quoteShellArg9(value) {
276267
276916
  return `'${value.replace(/'/g, `'\\''`)}'`;
276268
276917
  }
@@ -277131,7 +277780,7 @@ var init_task_revert = __esm({
277131
277780
  init_branch_attribution();
277132
277781
  init_workspace_base_branch();
277133
277782
  init_run_audit();
277134
- defaultExecAsync2 = promisify87(exec61);
277783
+ defaultExecAsync2 = promisify88(exec61);
277135
277784
  GIT_TIMEOUT_MS9 = 3e4;
277136
277785
  GIT_MAX_BUFFER7 = 10 * 1024 * 1024;
277137
277786
  TaskRevertError = class extends Error {
@@ -277150,10 +277799,10 @@ var init_task_revert = __esm({
277150
277799
  });
277151
277800
 
277152
277801
  // ../engine/src/merge/smart-pull.ts
277153
- import { execFile as execFile38 } from "node:child_process";
277154
- import { promisify as promisify88 } from "node:util";
277802
+ import { execFile as execFile39 } from "node:child_process";
277803
+ import { promisify as promisify89 } from "node:util";
277155
277804
  async function runGit8(args, cwd, timeoutMs) {
277156
- const result2 = await execFileAsync32("git", args, {
277805
+ const result2 = await execFileAsync33("git", args, {
277157
277806
  cwd,
277158
277807
  timeout: timeoutMs,
277159
277808
  maxBuffer: 10 * 1024 * 1024,
@@ -277304,11 +277953,11 @@ async function smartPull(input) {
277304
277953
  return { kind: "stash-pop-conflict", fromSha, toSha, stashSha, stashLabel, conflictedFiles };
277305
277954
  }
277306
277955
  }
277307
- var execFileAsync32;
277956
+ var execFileAsync33;
277308
277957
  var init_smart_pull = __esm({
277309
277958
  "../engine/src/merge/smart-pull.ts"() {
277310
277959
  "use strict";
277311
- execFileAsync32 = promisify88(execFile38);
277960
+ execFileAsync33 = promisify89(execFile39);
277312
277961
  }
277313
277962
  });
277314
277963
 
@@ -278954,7 +279603,7 @@ var init_finalize_types = __esm({
278954
279603
 
278955
279604
  // ../engine/src/experiment/git-ops.ts
278956
279605
  import { exec as exec62 } from "node:child_process";
278957
- import { promisify as promisify89 } from "node:util";
279606
+ import { promisify as promisify90 } from "node:util";
278958
279607
  async function runGit9(cwd, args) {
278959
279608
  const command = `git ${args.join(" ")}`;
278960
279609
  try {
@@ -279061,7 +279710,7 @@ var init_git_ops = __esm({
279061
279710
  "use strict";
279062
279711
  init_git_identity();
279063
279712
  init_finalize_types();
279064
- execAsync57 = promisify89(exec62);
279713
+ execAsync57 = promisify90(exec62);
279065
279714
  GIT_TIMEOUT_MS10 = 3e4;
279066
279715
  GIT_MAX_BUFFER8 = 10 * 1024 * 1024;
279067
279716
  }
@@ -283685,9 +284334,9 @@ var init_routine_scheduler = __esm({
283685
284334
  });
283686
284335
 
283687
284336
  // ../engine/src/healing/stuck-task-detector.ts
283688
- import { createHash as createHash37 } from "node:crypto";
284337
+ import { createHash as createHash38 } from "node:crypto";
283689
284338
  function hashToolDetail(detail) {
283690
- return createHash37("sha256").update(detail).digest("hex").slice(0, TOOL_DETAIL_HASH_HEX_LEN);
284339
+ return createHash38("sha256").update(detail).digest("hex").slice(0, TOOL_DETAIL_HASH_HEX_LEN);
283691
284340
  }
283692
284341
  function buildToolFingerprint(toolName, toolDetail) {
283693
284342
  const name2 = toolName.trim().toLowerCase() || "tool";
@@ -286963,11 +287612,11 @@ var init_adapters = __esm({
286963
287612
 
286964
287613
  // ../engine/src/cli-agent/resume-coordinator.ts
286965
287614
  import { existsSync as existsSync87 } from "node:fs";
286966
- import { execFile as execFile39 } from "node:child_process";
286967
- import { promisify as promisify90 } from "node:util";
287615
+ import { execFile as execFile40 } from "node:child_process";
287616
+ import { promisify as promisify91 } from "node:util";
286968
287617
  async function defaultIsWorktreeDirty(worktreePath) {
286969
287618
  try {
286970
- const { stdout } = await execFileAsync33("git", ["status", "--porcelain"], {
287619
+ const { stdout } = await execFileAsync34("git", ["status", "--porcelain"], {
286971
287620
  cwd: worktreePath
286972
287621
  });
286973
287622
  return stdout.trim().length > 0;
@@ -286975,13 +287624,13 @@ async function defaultIsWorktreeDirty(worktreePath) {
286975
287624
  return false;
286976
287625
  }
286977
287626
  }
286978
- var execFileAsync33, ORPHANED_LIVE_STATES, DEFAULT_MAX_RESUME_ATTEMPTS, DEFAULT_RESUME_BACKOFF_BASE_MS, CliResumeCoordinator;
287627
+ var execFileAsync34, ORPHANED_LIVE_STATES, DEFAULT_MAX_RESUME_ATTEMPTS, DEFAULT_RESUME_BACKOFF_BASE_MS, CliResumeCoordinator;
286979
287628
  var init_resume_coordinator = __esm({
286980
287629
  "../engine/src/cli-agent/resume-coordinator.ts"() {
286981
287630
  "use strict";
286982
287631
  init_session_manager();
286983
287632
  init_state_machine();
286984
- execFileAsync33 = promisify90(execFile39);
287633
+ execFileAsync34 = promisify91(execFile40);
286985
287634
  ORPHANED_LIVE_STATES = /* @__PURE__ */ new Set([
286986
287635
  "starting",
286987
287636
  "ready",
@@ -287268,7 +287917,7 @@ var init_node_dispatch_validation = __esm({
287268
287917
  });
287269
287918
 
287270
287919
  // ../engine/src/runtimes/in-process-runtime.ts
287271
- import { createHash as createHash38 } from "node:crypto";
287920
+ import { createHash as createHash39 } from "node:crypto";
287272
287921
  import { EventEmitter as EventEmitter26 } from "node:events";
287273
287922
  import { setImmediate as setImmediateCb4 } from "node:timers";
287274
287923
  function createDiscoveryPluginLoaderOptions(scopedStore) {
@@ -287533,7 +288182,7 @@ function stableNotificationJson(value) {
287533
288182
  return `{${fields.join(",")}}`;
287534
288183
  }
287535
288184
  function buildCliAgentNotificationDedupeKey(input) {
287536
- const notificationFingerprint = createHash38("sha256").update(stableNotificationJson(input.info.notification ?? null)).digest("hex").slice(0, 16);
288185
+ const notificationFingerprint = createHash39("sha256").update(stableNotificationJson(input.info.notification ?? null)).digest("hex").slice(0, 16);
287537
288186
  const waitingEpoch = input.session?.updatedAt ?? "unknown-waiting-epoch";
287538
288187
  return [
287539
288188
  "cli-agent",
@@ -291614,8 +292263,8 @@ var init_provider_adapters = __esm({
291614
292263
 
291615
292264
  // ../engine/src/remote-access/tunnel-process-manager.ts
291616
292265
  import { EventEmitter as EventEmitter31 } from "node:events";
291617
- import { exec as exec64, execFile as execFile40, spawn as spawn9 } from "node:child_process";
291618
- import { promisify as promisify91 } from "node:util";
292266
+ import { exec as exec64, execFile as execFile41, spawn as spawn9 } from "node:child_process";
292267
+ import { promisify as promisify92 } from "node:util";
291619
292268
  function serveConfigScopes(config3) {
291620
292269
  const foreground = Object.values(config3.Foreground ?? {}).filter(
291621
292270
  (scope) => Boolean(scope)
@@ -291661,7 +292310,7 @@ function toStateError(code, err) {
291661
292310
  at: nowIso2()
291662
292311
  };
291663
292312
  }
291664
- var DEFAULT_MAX_LOG_ENTRIES, DEFAULT_STOP_TIMEOUT_MS2, DEFAULT_RESTART_BASE_DELAY_MS, DEFAULT_RESTART_MAX_DELAY_MS, execFileAsync34, execAsync58, LineBuffer, TunnelProcessManager;
292313
+ var DEFAULT_MAX_LOG_ENTRIES, DEFAULT_STOP_TIMEOUT_MS2, DEFAULT_RESTART_BASE_DELAY_MS, DEFAULT_RESTART_MAX_DELAY_MS, execFileAsync35, execAsync58, LineBuffer, TunnelProcessManager;
291665
292314
  var init_tunnel_process_manager = __esm({
291666
292315
  "../engine/src/remote-access/tunnel-process-manager.ts"() {
291667
292316
  "use strict";
@@ -291672,8 +292321,8 @@ var init_tunnel_process_manager = __esm({
291672
292321
  DEFAULT_STOP_TIMEOUT_MS2 = 5e3;
291673
292322
  DEFAULT_RESTART_BASE_DELAY_MS = 1e3;
291674
292323
  DEFAULT_RESTART_MAX_DELAY_MS = 3e4;
291675
- execFileAsync34 = promisify91(execFile40);
291676
- execAsync58 = promisify91(exec64);
292324
+ execFileAsync35 = promisify92(execFile41);
292325
+ execAsync58 = promisify92(exec64);
291677
292326
  LineBuffer = class {
291678
292327
  pending = "";
291679
292328
  push(chunk) {
@@ -291778,7 +292427,7 @@ var init_tunnel_process_manager = __esm({
291778
292427
  return null;
291779
292428
  }
291780
292429
  try {
291781
- const { stdout } = await execFileAsync34("tailscale", ["status", "--json"], { timeout: 3e3 });
292430
+ const { stdout } = await execFileAsync35("tailscale", ["status", "--json"], { timeout: 3e3 });
291782
292431
  const data = JSON.parse(String(stdout));
291783
292432
  const dnsName = data.Self?.DNSName?.replace(/\.$/, "");
291784
292433
  if (!dnsName) {
@@ -291801,7 +292450,7 @@ var init_tunnel_process_manager = __esm({
291801
292450
  ];
291802
292451
  for (const resetCommand of resetCommands) {
291803
292452
  try {
291804
- await execFileAsync34(resetCommand.command, resetCommand.args, { timeout: 5e3 });
292453
+ await execFileAsync35(resetCommand.command, resetCommand.args, { timeout: 5e3 });
291805
292454
  return;
291806
292455
  } catch {
291807
292456
  }
@@ -291907,7 +292556,7 @@ var init_tunnel_process_manager = __esm({
291907
292556
  async detectActiveFunnel() {
291908
292557
  let stdout = "";
291909
292558
  try {
291910
- const result2 = await execFileAsync34("tailscale", ["serve", "status", "--json"], { timeout: 5e3 });
292559
+ const result2 = await execFileAsync35("tailscale", ["serve", "status", "--json"], { timeout: 5e3 });
291911
292560
  stdout = String(result2.stdout ?? "");
291912
292561
  } catch (error3) {
291913
292562
  const failure = error3;
@@ -292325,8 +292974,8 @@ var init_tunnel_process_manager = __esm({
292325
292974
  });
292326
292975
 
292327
292976
  // ../engine/src/remote-access/remote-tunnel-service.ts
292328
- import { execFile as execFile41 } from "node:child_process";
292329
- import { promisify as promisify92 } from "node:util";
292977
+ import { execFile as execFile42 } from "node:child_process";
292978
+ import { promisify as promisify93 } from "node:util";
292330
292979
  function remoteTunnelScopeKey(input) {
292331
292980
  const projectId2 = input.projectId?.trim();
292332
292981
  if (projectId2) return `project:${projectId2}`;
@@ -292369,14 +293018,14 @@ async function shutdownAllRemoteTunnels() {
292369
293018
  function __resetRemoteTunnelServicesForTests() {
292370
293019
  remoteTunnelServices.clear();
292371
293020
  }
292372
- var execFileAsync35, isRemoteActive, RemoteTunnelService, remoteTunnelServices;
293021
+ var execFileAsync36, isRemoteActive, RemoteTunnelService, remoteTunnelServices;
292373
293022
  var init_remote_tunnel_service = __esm({
292374
293023
  "../engine/src/remote-access/remote-tunnel-service.ts"() {
292375
293024
  "use strict";
292376
293025
  init_logger3();
292377
293026
  init_local_dashboard_port();
292378
293027
  init_tunnel_process_manager();
292379
- execFileAsync35 = promisify92(execFile41);
293028
+ execFileAsync36 = promisify93(execFile42);
292380
293029
  isRemoteActive = (ra) => ra?.activeProvider != null && (ra.providers[ra.activeProvider]?.enabled ?? false);
292381
293030
  RemoteTunnelService = class {
292382
293031
  manager = new TunnelProcessManager();
@@ -292725,7 +293374,7 @@ var init_remote_tunnel_service = __esm({
292725
293374
  async checkTailscaleDaemonReady() {
292726
293375
  let stdout = "";
292727
293376
  try {
292728
- const result2 = await execFileAsync35("tailscale", ["status", "--json"], {
293377
+ const result2 = await execFileAsync36("tailscale", ["status", "--json"], {
292729
293378
  timeout: 5e3,
292730
293379
  maxBuffer: 8 * 1024 * 1024
292731
293380
  });
@@ -292773,7 +293422,7 @@ var init_remote_tunnel_service = __esm({
292773
293422
  async checkExecutableAvailable(command) {
292774
293423
  const checker = process.platform === "win32" ? "where" : "which";
292775
293424
  try {
292776
- await execFileAsync35(checker, [command]);
293425
+ await execFileAsync36(checker, [command]);
292777
293426
  return { available: true };
292778
293427
  } catch {
292779
293428
  return {
@@ -292788,8 +293437,8 @@ var init_remote_tunnel_service = __esm({
292788
293437
  });
292789
293438
 
292790
293439
  // ../engine/src/project-engine.ts
292791
- import { execFile as execFile42 } from "node:child_process";
292792
- import { promisify as promisify93 } from "node:util";
293440
+ import { execFile as execFile43 } from "node:child_process";
293441
+ import { promisify as promisify94 } from "node:util";
292793
293442
  function emitMergeRequestShadowDequeueParityAudit(store, legacyTaskId, shadowTaskId) {
292794
293443
  const agree = shadowTaskId === legacyTaskId;
292795
293444
  void emitBoundedRunAudit2(store, {
@@ -292844,7 +293493,7 @@ async function verifyMergeConfirmedReachability(args) {
292844
293493
  return { reachable: true, skipped: "no-integration-branch" };
292845
293494
  }
292846
293495
  try {
292847
- await execFileAsync36("git", ["cat-file", "-e", `${commitSha}^{commit}`], {
293496
+ await execFileAsync37("git", ["cat-file", "-e", `${commitSha}^{commit}`], {
292848
293497
  cwd,
292849
293498
  timeout: 1e4
292850
293499
  });
@@ -292853,7 +293502,7 @@ async function verifyMergeConfirmedReachability(args) {
292853
293502
  return { reachable: false, reason: "commit-missing", diagnostic };
292854
293503
  }
292855
293504
  try {
292856
- await execFileAsync36(
293505
+ await execFileAsync37(
292857
293506
  "git",
292858
293507
  ["merge-base", "--is-ancestor", commitSha, `refs/heads/${integrationBranch}`],
292859
293508
  { cwd, timeout: 1e4 }
@@ -292869,7 +293518,7 @@ async function verifyMergeConfirmedReachability(args) {
292869
293518
  return { reachable: false, reason: "git-error", diagnostic };
292870
293519
  }
292871
293520
  }
292872
- var WorkspaceMergeDispatchBusyError2, execFileAsync36, MERGE_HANDOFF_GRACE_MS, PR_MERGE_RETRY_BACKOFF_BASE_MS, deterministicMergerModeDeprecationWarnedProjects, ProjectEngine;
293521
+ var WorkspaceMergeDispatchBusyError2, execFileAsync37, MERGE_HANDOFF_GRACE_MS, PR_MERGE_RETRY_BACKOFF_BASE_MS, deterministicMergerModeDeprecationWarnedProjects, ProjectEngine;
292873
293522
  var init_project_engine = __esm({
292874
293523
  "../engine/src/project-engine.ts"() {
292875
293524
  "use strict";
@@ -292902,6 +293551,7 @@ var init_project_engine = __esm({
292902
293551
  init_merger_ai();
292903
293552
  init_group_merge_coordinator();
292904
293553
  init_workspace_review_reroute();
293554
+ init_stale_content_review_reroute();
292905
293555
  init_workspace_integration_target();
292906
293556
  init_concurrency();
292907
293557
  init_merge_reclaim_policy();
@@ -292928,7 +293578,7 @@ var init_project_engine = __esm({
292928
293578
  }
292929
293579
  retryable = true;
292930
293580
  };
292931
- execFileAsync36 = promisify93(execFile42);
293581
+ execFileAsync37 = promisify94(execFile43);
292932
293582
  MERGE_HANDOFF_GRACE_MS = 300;
292933
293583
  PR_MERGE_RETRY_BACKOFF_BASE_MS = 5e3;
292934
293584
  deterministicMergerModeDeprecationWarnedProjects = /* @__PURE__ */ new Set();
@@ -292992,6 +293642,7 @@ var init_project_engine = __esm({
292992
293642
  }
292993
293643
  });
292994
293644
  }
293645
+ staleContentRerouteAuditKeys = /* @__PURE__ */ new Set();
292995
293646
  runtime;
292996
293647
  started = false;
292997
293648
  specDriftReconciler;
@@ -294788,11 +295439,44 @@ ${detail}`);
294788
295439
  workspaceRootDir: this.config.workingDirectory,
294789
295440
  settings
294790
295441
  });
294791
- return getTaskMergeBlocker(task, {
295442
+ const blocker = getTaskMergeBlocker(task, {
294792
295443
  reviewColumns: mergeGate.reviewColumns.size > 0 ? mergeGate.reviewColumns : /* @__PURE__ */ new Set(["in-review"]),
294793
295444
  requiredPreMergeStepIds: mergeGate.requiredPreMergeStepIds,
294794
295445
  mergeContent
294795
295446
  });
295447
+ if (blocker === "task has a pre-merge approval recorded against different content" && mergeContent.kind === "singular") {
295448
+ const reroute = await rerouteSingularStaleContentToReview(store, task, {
295449
+ requiredPreMergeStepIds: mergeGate.requiredPreMergeStepIds,
295450
+ mergeContent
295451
+ }).catch(() => ({
295452
+ rerouted: false,
295453
+ reason: "no-progress",
295454
+ nodeId: void 0,
295455
+ workflowStepId: void 0
295456
+ }));
295457
+ if (reroute.rerouted) {
295458
+ await store.logEntry(task.id, `[pre-merge] Code Review re-entry is owned by the workflow graph after stale content evidence was refused.`);
295459
+ }
295460
+ const auditKey = `${task.id}:${reroute.reason}`;
295461
+ if (!this.staleContentRerouteAuditKeys.has(auditKey)) {
295462
+ this.staleContentRerouteAuditKeys.add(auditKey);
295463
+ await emitBoundedRunAudit2(store, {
295464
+ taskId: task.id,
295465
+ agentId: "merge-gate",
295466
+ runId: `${task.id}:merge-gate`,
295467
+ domain: "database",
295468
+ mutationType: "task:merge-stale-content-review-rerouted",
295469
+ target: task.id,
295470
+ metadata: {
295471
+ taskId: task.id,
295472
+ nodeId: reroute.nodeId,
295473
+ workflowStepId: reroute.workflowStepId,
295474
+ reason: reroute.reason
295475
+ }
295476
+ });
295477
+ }
295478
+ }
295479
+ return blocker;
294796
295480
  }
294797
295481
  /**
294798
295482
  * Remove and return the highest-priority taskId from the merge queue.
@@ -295638,7 +296322,7 @@ ${detail}`);
295638
296322
  }
295639
296323
  const checklist = planConfirmedMergeChecklistReconciliation(task);
295640
296324
  if (checklist.skippedStepIndexes.length > 0 || checklist.reconciledWorkflowStepIds.length > 0) {
295641
- const steps = task.steps.map((step, index2) => checklist.skippedStepIndexes.includes(index2) ? { ...step, status: "skipped" } : step);
296325
+ const steps = (task.steps ?? []).map((step, index2) => checklist.skippedStepIndexes.includes(index2) ? { ...step, status: "skipped" } : step);
295642
296326
  const workflowStepResults = (task.workflowStepResults ?? []).map((result2) => checklist.reconciledWorkflowStepIds.includes(result2.workflowStepId) ? { ...result2, status: "skipped" } : result2);
295643
296327
  await store.updateTask(taskId, { steps, workflowStepResults });
295644
296328
  task.steps = steps;
@@ -297384,9 +298068,9 @@ import { access as access12, unlink as unlink12, writeFile as writeFile31 } from
297384
298068
  import net2 from "node:net";
297385
298069
  import { platform as platform3, tmpdir as tmpdir13 } from "node:os";
297386
298070
  import { join as join100 } from "node:path";
297387
- import { createHash as createHash39 } from "node:crypto";
298071
+ import { createHash as createHash40 } from "node:crypto";
297388
298072
  function projectHash(projectId2) {
297389
- return createHash39("sha1").update(projectId2).digest("hex").slice(0, 16);
298073
+ return createHash40("sha1").update(projectId2).digest("hex").slice(0, 16);
297390
298074
  }
297391
298075
  function computeEngineSocketPath(projectId2) {
297392
298076
  const hash3 = projectHash(projectId2);
@@ -299004,8 +299688,8 @@ var init_remote_access = __esm({
299004
299688
  });
299005
299689
 
299006
299690
  // ../engine/src/healing/archive-worktree-disposer-install.ts
299007
- import { execFile as execFile43 } from "node:child_process";
299008
- import { promisify as promisify94 } from "node:util";
299691
+ import { execFile as execFile44 } from "node:child_process";
299692
+ import { promisify as promisify95 } from "node:util";
299009
299693
  function installBaselineArchiveWorktreeDisposer(store, input) {
299010
299694
  const allowLiveRemoval = input.allowLiveRemoval ?? (() => false);
299011
299695
  const unregisterSingle = getArchiveWorktreeDisposer(store) ? () => {
@@ -299027,7 +299711,7 @@ function installBaselineArchiveWorktreeDisposer(store, input) {
299027
299711
  try {
299028
299712
  if (await canonicalizeWorktreePath(entry.worktreePath) === await canonicalizeWorktreePath(entry.repoRootDir)) throw new Error("Refusing to remove workspace repository root");
299029
299713
  await removeWorktree({ worktreePath: entry.worktreePath, rootDir: entry.repoRootDir, settings: await input.getSettings(), taskId: task.id, reason: RemovalReason.ExecutorDispose, force: true });
299030
- await execFileAsync37("git", ["branch", "-D", entry.branch], { cwd: entry.repoRootDir, timeout: 12e4, maxBuffer: 10 * 1024 * 1024 });
299714
+ await execFileAsync38("git", ["branch", "-D", entry.branch], { cwd: entry.repoRootDir, timeout: 12e4, maxBuffer: 10 * 1024 * 1024 });
299031
299715
  if (task.workspaceWorktrees) for (const repoRel of [entry.repoRel, ...entry.aliasRepoRels]) delete task.workspaceWorktrees[repoRel];
299032
299716
  removed.push(entry.repoRel);
299033
299717
  } catch (error3) {
@@ -299041,13 +299725,13 @@ function installBaselineArchiveWorktreeDisposer(store, input) {
299041
299725
  unregisterSingle();
299042
299726
  };
299043
299727
  }
299044
- var execFileAsync37;
299728
+ var execFileAsync38;
299045
299729
  var init_archive_worktree_disposer_install = __esm({
299046
299730
  "../engine/src/healing/archive-worktree-disposer-install.ts"() {
299047
299731
  "use strict";
299048
299732
  init_src2();
299049
299733
  init_worktree_backend();
299050
- execFileAsync37 = promisify94(execFile43);
299734
+ execFileAsync38 = promisify95(execFile44);
299051
299735
  }
299052
299736
  });
299053
299737
 
@@ -300900,7 +301584,7 @@ import { exec as exec65 } from "node:child_process";
300900
301584
  import { mkdtemp as mkdtemp7, rm as rm18 } from "node:fs/promises";
300901
301585
  import { tmpdir as tmpdir14 } from "node:os";
300902
301586
  import { join as join101 } from "node:path";
300903
- import { promisify as promisify95 } from "node:util";
301587
+ import { promisify as promisify96 } from "node:util";
300904
301588
  function resolveCommentAuthor(input) {
300905
301589
  const login = input.login || "unknown";
300906
301590
  const authorIsBot = Boolean(
@@ -301369,7 +302053,7 @@ var init_github = __esm({
301369
302053
  "use strict";
301370
302054
  init_src2();
301371
302055
  init_issue_image_attachments();
301372
- execAsync59 = promisify95(exec65);
302056
+ execAsync59 = promisify96(exec65);
301373
302057
  DiscussionsDisabledError = class extends Error {
301374
302058
  name = "DiscussionsDisabledError";
301375
302059
  constructor(owner, repo, cause) {
@@ -308726,7 +309410,7 @@ var init_knowledge_index_refresh = __esm({
308726
309410
  });
308727
309411
 
308728
309412
  // ../dashboard/src/pr-metadata-generator.ts
308729
- import { promisify as promisify96 } from "node:util";
309413
+ import { promisify as promisify97 } from "node:util";
308730
309414
  import { exec as execCb } from "node:child_process";
308731
309415
  import { access as access13, readFile as readFile52 } from "node:fs/promises";
308732
309416
  import { join as join102 } from "node:path";
@@ -309018,7 +309702,7 @@ var init_pr_metadata_generator = __esm({
309018
309702
  "use strict";
309019
309703
  init_src2();
309020
309704
  init_src5();
309021
- execAsync60 = promisify96(execCb);
309705
+ execAsync60 = promisify97(execCb);
309022
309706
  PR_METADATA_TIMEOUT_MS = 6e4;
309023
309707
  PR_METADATA_SYSTEM_PROMPT = [
309024
309708
  "You generate GitHub pull request metadata for Fusion tasks.",
@@ -309035,13 +309719,13 @@ var init_pr_metadata_generator = __esm({
309035
309719
  });
309036
309720
 
309037
309721
  // ../dashboard/src/routes/resolve-diff-base.ts
309038
- import { execFile as execFile44 } from "node:child_process";
309039
- import { promisify as promisify97 } from "node:util";
309040
- var execFileAsync38;
309722
+ import { execFile as execFile45 } from "node:child_process";
309723
+ import { promisify as promisify98 } from "node:util";
309724
+ var execFileAsync39;
309041
309725
  var init_resolve_diff_base = __esm({
309042
309726
  "../dashboard/src/routes/resolve-diff-base.ts"() {
309043
309727
  "use strict";
309044
- execFileAsync38 = promisify97(execFile44);
309728
+ execFileAsync39 = promisify98(execFile45);
309045
309729
  }
309046
309730
  });
309047
309731
 
@@ -309082,7 +309766,7 @@ var init_git_worktree_safety = __esm({
309082
309766
 
309083
309767
  // ../dashboard/src/routes/register-git-github.ts
309084
309768
  import { exec as execCb2, spawn as spawn10 } from "node:child_process";
309085
- import { promisify as promisify98 } from "node:util";
309769
+ import { promisify as promisify99 } from "node:util";
309086
309770
  var severityAuditLog30, execAsync61, PR_ROUTE_MAX_BUFFER_BYTES, GITHUB_TRACKING_RECONCILE_INTERVAL_MS;
309087
309771
  var init_register_git_github = __esm({
309088
309772
  "../dashboard/src/routes/register-git-github.ts"() {
@@ -309114,7 +309798,7 @@ var init_register_git_github = __esm({
309114
309798
  init_resolve_diff_base();
309115
309799
  init_git_worktree_safety();
309116
309800
  severityAuditLog30 = createLogger("dashboard-register-git-github");
309117
- execAsync61 = promisify98(execCb2);
309801
+ execAsync61 = promisify99(execCb2);
309118
309802
  PR_ROUTE_MAX_BUFFER_BYTES = 10 * 1024 * 1024;
309119
309803
  GITHUB_TRACKING_RECONCILE_INTERVAL_MS = 15 * 60 * 1e3;
309120
309804
  }
@@ -316051,13 +316735,13 @@ var init_register_agents_projects_nodes = __esm({
316051
316735
  });
316052
316736
 
316053
316737
  // ../dashboard/src/exec-file.ts
316054
- import { execFile as execFile45 } from "node:child_process";
316055
- import { promisify as promisify99 } from "node:util";
316056
- var execFileAsync39;
316738
+ import { execFile as execFile46 } from "node:child_process";
316739
+ import { promisify as promisify100 } from "node:util";
316740
+ var execFileAsync40;
316057
316741
  var init_exec_file = __esm({
316058
316742
  "../dashboard/src/exec-file.ts"() {
316059
316743
  "use strict";
316060
- execFileAsync39 = promisify99(execFile45);
316744
+ execFileAsync40 = promisify100(execFile46);
316061
316745
  }
316062
316746
  });
316063
316747
 
@@ -327335,7 +328019,7 @@ var init_register_fn_binary_routes = __esm({
327335
328019
 
327336
328020
  // ../dashboard/src/update-check.ts
327337
328021
  import { exec as exec66 } from "node:child_process";
327338
- import { promisify as promisify100 } from "node:util";
328022
+ import { promisify as promisify101 } from "node:util";
327339
328023
  var INSTALL_MAX_BUFFER, DAY_MS, execAsync62;
327340
328024
  var init_update_check = __esm({
327341
328025
  "../dashboard/src/update-check.ts"() {
@@ -327343,7 +328027,7 @@ var init_update_check = __esm({
327343
328027
  init_src2();
327344
328028
  INSTALL_MAX_BUFFER = 10 * 1024 * 1024;
327345
328029
  DAY_MS = 24 * 60 * 60 * 1e3;
327346
- execAsync62 = promisify100(exec66);
328030
+ execAsync62 = promisify101(exec66);
327347
328031
  }
327348
328032
  });
327349
328033
 
@@ -327434,7 +328118,7 @@ var init_types5 = __esm({
327434
328118
  });
327435
328119
 
327436
328120
  // ../dashboard/src/stt/model-manager.ts
327437
- import { createHash as createHash40, randomUUID as randomUUID63 } from "node:crypto";
328121
+ import { createHash as createHash41, randomUUID as randomUUID63 } from "node:crypto";
327438
328122
  import { mkdir as mkdir34, open as open5, readFile as readFile56, readdir as readdir14, rename as rename13, rm as rm21, stat as stat15, writeFile as writeFile33 } from "node:fs/promises";
327439
328123
  import { join as join121, relative as relative25, resolve as resolve62, sep as sep20 } from "node:path";
327440
328124
  function parseSafeTarListing(listing) {
@@ -327523,7 +328207,7 @@ function createVoiceModelManager(options = {}) {
327523
328207
  if (!response.ok || !response.body) throw Object.assign(new Error("download failed"), { voiceReason: "network" });
327524
328208
  if (!current(g2, signal)) return state;
327525
328209
  const total = Number(response.headers.get("content-length")) || void 0;
327526
- const hash3 = createHash40("sha256");
328210
+ const hash3 = createHash41("sha256");
327527
328211
  let bytes = 0;
327528
328212
  for await (const chunk of response.body) {
327529
328213
  if (!current(g2, signal)) return state;