@runfusion/fusion 0.75.1-beta.0 → 0.75.1-beta.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (132) hide show
  1. package/dist/bin.js +259 -76
  2. package/dist/child-process-worker.js +185 -61
  3. package/dist/client/.vite/manifest.json +248 -248
  4. package/dist/client/assets/{AgentDetailView-swTdiyeE.js → AgentDetailView-CE_7nvDT.js} +1 -1
  5. package/dist/client/assets/{AgentPermissionPolicyEditor-Dq-KuDaA.js → AgentPermissionPolicyEditor-DYr3dk5X.js} +1 -1
  6. package/dist/client/assets/{AgentsView-D1GEHyH0.js → AgentsView-DZoYoiaD.js} +3 -3
  7. package/dist/client/assets/{ChatView-D1czGvvT.js → ChatView-BE9zMzYZ.js} +1 -1
  8. package/dist/client/assets/{CommandCenter-DWZ7OTOB.js → CommandCenter-DnLsp4_S.js} +1 -1
  9. package/dist/client/assets/{DevServerView-CAcUKOXd.js → DevServerView-CI-3j-lH.js} +1 -1
  10. package/dist/client/assets/{DirectoryPicker-DVUl5jOk.js → DirectoryPicker-D1ADu0Av.js} +1 -1
  11. package/dist/client/assets/{DocumentsView-CnexOxtr.js → DocumentsView-CF8y1gob.js} +1 -1
  12. package/dist/client/assets/{EvalsView-rTupzcDf.js → EvalsView-C9vLiM2_.js} +1 -1
  13. package/dist/client/assets/{ExperimentalAgentOnboardingModal-CXE6Bj3V.js → ExperimentalAgentOnboardingModal-CX_2Hjh0.js} +1 -1
  14. package/dist/client/assets/{GoalsView-CjHwnnIi.js → GoalsView-DJIA3k8q.js} +1 -1
  15. package/dist/client/assets/{InsightsView-C3XocYP-.js → InsightsView--TCeutLO.js} +1 -1
  16. package/dist/client/assets/{MemoryView-CsNHcx_d.js → MemoryView-Btp4m7pK.js} +1 -1
  17. package/dist/client/assets/{PiExtensionsManager-BPTPoZBI.js → PiExtensionsManager-DXdfh-Uf.js} +1 -1
  18. package/dist/client/assets/{PluginManager-CXadn8JE.js → PluginManager-BDFelFY6.js} +1 -1
  19. package/dist/client/assets/{PullRequestView-CC03CtZJ.js → PullRequestView-TSNKGSAg.js} +1 -1
  20. package/dist/client/assets/{ReportModal-Ci0na1eI.js → ReportModal-DhXMNHDL.js} +1 -1
  21. package/dist/client/assets/{ResearchView-DGWyAo8p.js → ResearchView-0w30HikQ.js} +1 -1
  22. package/dist/client/assets/{SecretsView-Cv2IuqFO.js → SecretsView-DgMu7279.js} +1 -1
  23. package/dist/client/assets/{SessionTerminal-B22RlQeo.js → SessionTerminal-CF6_uy2u.js} +1 -1
  24. package/dist/client/assets/{SettingsModal-BkgBQZ19.js → SettingsModal-BMAPX_7v.js} +1 -1
  25. package/dist/client/assets/{SettingsModal-CPRpRK71.js → SettingsModal-Dckls6LL.js} +3 -3
  26. package/dist/client/assets/{SettingsTextareaRow-0LXwMUQR.js → SettingsTextareaRow-LqXxpJl5.js} +1 -1
  27. package/dist/client/assets/{SetupWizardModal-CyW3AlRO.js → SetupWizardModal-D1ZvGOcK.js} +2 -2
  28. package/dist/client/assets/{SkillsView-3oqB1ubq.js → SkillsView-CfO5B7SO.js} +1 -1
  29. package/dist/client/assets/{WorkflowNodeEditor-CfUK_xs1.js → WorkflowNodeEditor-CjdK9fFS.js} +3 -3
  30. package/dist/client/assets/{agent-import-generation-C7-Cmo3M.js → agent-import-generation-DqTzyLOZ.js} +1 -1
  31. package/dist/client/assets/{architectureDiagram-3BPJPVTR-BQ7ZN1r_.js → architectureDiagram-3BPJPVTR-CHkolL_b.js} +1 -1
  32. package/dist/client/assets/{blockDiagram-GPEHLZMM-Debw1Xka.js → blockDiagram-GPEHLZMM-BFRYQi8H.js} +1 -1
  33. package/dist/client/assets/{c4Diagram-AAUBKEIU-CiU212s3.js → c4Diagram-AAUBKEIU-D9mMEYbR.js} +1 -1
  34. package/dist/client/assets/channel-CLdamWm5.js +1 -0
  35. package/dist/client/assets/{chunk-2J33WTMH-BUJQ86zb.js → chunk-2J33WTMH-CCv6QaQn.js} +1 -1
  36. package/dist/client/assets/{chunk-4BX2VUAB-C6GdEm1z.js → chunk-4BX2VUAB-Bfe22y5T.js} +1 -1
  37. package/dist/client/assets/{chunk-55IACEB6-CImvrxQL.js → chunk-55IACEB6-CwicrA9V.js} +1 -1
  38. package/dist/client/assets/{chunk-727SXJPM-Dt-A3pkM.js → chunk-727SXJPM-DlIpTC03.js} +1 -1
  39. package/dist/client/assets/{chunk-AQP2D5EJ-DDj4oXTx.js → chunk-AQP2D5EJ-CRX4mFVq.js} +1 -1
  40. package/dist/client/assets/{chunk-FMBD7UC4-BD4r1ukC.js → chunk-FMBD7UC4-DzG-bovU.js} +1 -1
  41. package/dist/client/assets/{chunk-ND2GUHAM-DFSteefi.js → chunk-ND2GUHAM-D5F2jtpM.js} +1 -1
  42. package/dist/client/assets/{chunk-QZHKN3VN-t2zF8tjB.js → chunk-QZHKN3VN-BmSaCwG6.js} +1 -1
  43. package/dist/client/assets/classDiagram-4FO5ZUOK-BYWknNX0.js +1 -0
  44. package/dist/client/assets/classDiagram-v2-Q7XG4LA2-BYWknNX0.js +1 -0
  45. package/dist/client/assets/{cose-bilkent-S5V4N54A-tkvDkXQA.js → cose-bilkent-S5V4N54A-BbKTljui.js} +1 -1
  46. package/dist/client/assets/{dagre-BM42HDAG-DD2rW7wo.js → dagre-BM42HDAG-u0hwXb_T.js} +1 -1
  47. package/dist/client/assets/{dashboard-view-B1vduc8x.js → dashboard-view-BfYZZ9Ms.js} +1 -1
  48. package/dist/client/assets/{dashboard-view-DruCruAc.js → dashboard-view-BjPZG2Fv.js} +1 -1
  49. package/dist/client/assets/{dashboard-view-CciuDS5A.js → dashboard-view-faKSXA4_.js} +1 -1
  50. package/dist/client/assets/{dashboard-view-C7jJ1lA4.js → dashboard-view-oCjWDGbW.js} +1 -1
  51. package/dist/client/assets/{diagram-2AECGRRQ-DASuxGNc.js → diagram-2AECGRRQ-Q7towbBl.js} +1 -1
  52. package/dist/client/assets/{diagram-5GNKFQAL-f9WtiIUk.js → diagram-5GNKFQAL-UAwBmAxb.js} +1 -1
  53. package/dist/client/assets/{diagram-KO2AKTUF-QivICYCd.js → diagram-KO2AKTUF-BL1n0pkf.js} +1 -1
  54. package/dist/client/assets/{diagram-LMA3HP47-CMwcDFHR.js → diagram-LMA3HP47-O-v_IZUJ.js} +1 -1
  55. package/dist/client/assets/{diagram-OG6HWLK6-C-vPMeHZ.js → diagram-OG6HWLK6-CAYSE2Mz.js} +1 -1
  56. package/dist/client/assets/{erDiagram-TEJ5UH35-CxGtk9vj.js → erDiagram-TEJ5UH35-Cr_DgCyp.js} +1 -1
  57. package/dist/client/assets/{flowDiagram-I6XJVG4X-qBZRZoV1.js → flowDiagram-I6XJVG4X-TdLzHKPW.js} +1 -1
  58. package/dist/client/assets/{folder-open-Dysi9Zzg.js → folder-open-BoddkTkv.js} +1 -1
  59. package/dist/client/assets/{ganttDiagram-6RSMTGT7-DAaXroiu.js → ganttDiagram-6RSMTGT7-CO1ErtJY.js} +1 -1
  60. package/dist/client/assets/{gitGraphDiagram-PVQCEYII-DNUNQot_.js → gitGraphDiagram-PVQCEYII-C8_Kx0Yh.js} +1 -1
  61. package/dist/client/assets/index-C7KY0q8Q.js +2767 -0
  62. package/dist/client/assets/{infoDiagram-5YYISTIA-BSJmWLwh.js → infoDiagram-5YYISTIA-Dd6Zo7Dh.js} +1 -1
  63. package/dist/client/assets/{ishikawaDiagram-YF4QCWOH-AyVKvoDq.js → ishikawaDiagram-YF4QCWOH-BE9JCLb5.js} +1 -1
  64. package/dist/client/assets/{journeyDiagram-JHISSGLW-972txzzd.js → journeyDiagram-JHISSGLW-B6RIZGDq.js} +1 -1
  65. package/dist/client/assets/{kanban-definition-UN3LZRKU-1xiNvIqK.js → kanban-definition-UN3LZRKU-CPXff-h-.js} +1 -1
  66. package/dist/client/assets/{mermaid.core-oeDA8jOm.js → mermaid.core-CbGj7iEw.js} +4 -4
  67. package/dist/client/assets/{mindmap-definition-RKZ34NQL-DcktmI8s.js → mindmap-definition-RKZ34NQL-DF5LZmsH.js} +1 -1
  68. package/dist/client/assets/{pieDiagram-4H26LBE5-ekBwLt-O.js → pieDiagram-4H26LBE5-Dpy0Xm9T.js} +1 -1
  69. package/dist/client/assets/{puzzle-I6pr6ZrW.js → puzzle-BZa4U8-N.js} +1 -1
  70. package/dist/client/assets/{quadrantDiagram-W4KKPZXB-D_wjTsvn.js → quadrantDiagram-W4KKPZXB-BTLAQFpz.js} +1 -1
  71. package/dist/client/assets/{requirementDiagram-4Y6WPE33-CKGHyiJ1.js → requirementDiagram-4Y6WPE33-DSUV9ySs.js} +1 -1
  72. package/dist/client/assets/{sankeyDiagram-5OEKKPKP-CtiZFNk5.js → sankeyDiagram-5OEKKPKP-Fmfn0XDL.js} +1 -1
  73. package/dist/client/assets/{sequenceDiagram-3UESZ5HK-B6OMrfGk.js → sequenceDiagram-3UESZ5HK-_343I_b4.js} +1 -1
  74. package/dist/client/assets/{shield-alert-CVMmviMG.js → shield-alert-CdJ55UvI.js} +1 -1
  75. package/dist/client/assets/{standing-instructions-template-Cg1a3tjW.js → standing-instructions-template-r03dZ5Z0.js} +1 -1
  76. package/dist/client/assets/{stateDiagram-AJRCARHV-CyTY2M_7.js → stateDiagram-AJRCARHV-C9EbWegm.js} +1 -1
  77. package/dist/client/assets/{stateDiagram-v2-BHNVJYJU-NWPCCiqC.js → stateDiagram-v2-BHNVJYJU-Bmh2GRh6.js} +1 -1
  78. package/dist/client/assets/{timeline-definition-PNZ67QCA-DU4TVYpw.js → timeline-definition-PNZ67QCA-q86S9wyl.js} +1 -1
  79. package/dist/client/assets/{upload-VCzeCiFH.js → upload-DS9-qtCF.js} +1 -1
  80. package/dist/client/assets/{users-DB3knZtm.js → users-DP5Sngeg.js} +1 -1
  81. package/dist/client/assets/{vennDiagram-CIIHVFJN-DX0Ppll6.js → vennDiagram-CIIHVFJN-BFq8uIt3.js} +1 -1
  82. package/dist/client/assets/{wardley-L42UT6IY-CBoWxpDv.js → wardley-L42UT6IY-BL_sLGYI.js} +1 -1
  83. package/dist/client/assets/{wardleyDiagram-YWT4CUSO-B_eVqdhC.js → wardleyDiagram-YWT4CUSO-BEKJJ9Vi.js} +1 -1
  84. package/dist/client/assets/{xychartDiagram-2RQKCTM6-DeZzEX0f.js → xychartDiagram-2RQKCTM6-DsvFnlxK.js} +1 -1
  85. package/dist/client/index.html +1 -1
  86. package/dist/client/version.json +1 -1
  87. package/dist/droid-cli/package.json +1 -1
  88. package/dist/extension.js +186 -62
  89. package/dist/pi-claude-cli/package.json +1 -1
  90. package/dist/plugin-sdk/index.d.ts +157 -140
  91. package/dist/plugin-sdk/index.js +19 -0
  92. package/dist/plugins/fusion-plugin-acp-runtime/bundled.js +19 -0
  93. package/dist/plugins/fusion-plugin-acp-runtime/package.json +1 -1
  94. package/dist/plugins/fusion-plugin-claude-runtime/bundled.js +19 -0
  95. package/dist/plugins/fusion-plugin-claude-runtime/package.json +1 -1
  96. package/dist/plugins/fusion-plugin-cli-printing-press/bundled.js +19 -0
  97. package/dist/plugins/fusion-plugin-cli-printing-press/package.json +1 -1
  98. package/dist/plugins/fusion-plugin-compound-engineering/bundled.js +19 -0
  99. package/dist/plugins/fusion-plugin-compound-engineering/package.json +1 -1
  100. package/dist/plugins/fusion-plugin-cursor-runtime/bundled.js +19 -0
  101. package/dist/plugins/fusion-plugin-cursor-runtime/package.json +1 -1
  102. package/dist/plugins/fusion-plugin-dependency-graph/bundled.js +19 -0
  103. package/dist/plugins/fusion-plugin-dependency-graph/package.json +1 -1
  104. package/dist/plugins/fusion-plugin-droid-runtime/bundled.js +19 -0
  105. package/dist/plugins/fusion-plugin-droid-runtime/package.json +1 -1
  106. package/dist/plugins/fusion-plugin-grok-runtime/bundled.js +19 -0
  107. package/dist/plugins/fusion-plugin-grok-runtime/package.json +1 -1
  108. package/dist/plugins/fusion-plugin-hermes-runtime/bundled.js +19 -0
  109. package/dist/plugins/fusion-plugin-hermes-runtime/package.json +1 -1
  110. package/dist/plugins/fusion-plugin-linear-import/bundled.js +19 -0
  111. package/dist/plugins/fusion-plugin-linear-import/package.json +1 -1
  112. package/dist/plugins/fusion-plugin-omp-runtime/bundled.js +19 -0
  113. package/dist/plugins/fusion-plugin-omp-runtime/package.json +1 -1
  114. package/dist/plugins/fusion-plugin-openclaw-runtime/bundled.js +19 -0
  115. package/dist/plugins/fusion-plugin-openclaw-runtime/package.json +1 -1
  116. package/dist/plugins/fusion-plugin-paperclip-runtime/bundled.js +19 -0
  117. package/dist/plugins/fusion-plugin-paperclip-runtime/package.json +1 -1
  118. package/dist/plugins/fusion-plugin-quality/bundled.js +19 -0
  119. package/dist/plugins/fusion-plugin-quality/package.json +1 -1
  120. package/dist/plugins/fusion-plugin-reports/bundled.js +19 -0
  121. package/dist/plugins/fusion-plugin-reports/package.json +1 -1
  122. package/dist/plugins/fusion-plugin-roadmap/bundled.js +19 -0
  123. package/dist/plugins/fusion-plugin-roadmap/package.json +1 -1
  124. package/dist/plugins/fusion-plugin-todos/bundled.js +19 -0
  125. package/dist/plugins/fusion-plugin-todos/package.json +1 -1
  126. package/dist/plugins/fusion-plugin-whatsapp-chat/bundled.js +19 -0
  127. package/dist/plugins/fusion-plugin-whatsapp-chat/package.json +1 -1
  128. package/package.json +1 -1
  129. package/dist/client/assets/channel-D-O4mRXx.js +0 -1
  130. package/dist/client/assets/classDiagram-4FO5ZUOK-BhHdSetA.js +0 -1
  131. package/dist/client/assets/classDiagram-v2-Q7XG4LA2-BhHdSetA.js +0 -1
  132. package/dist/client/assets/index-D0QgE5F5.js +0 -2767
@@ -10046,6 +10046,7 @@ function validateForeach(node, topLevelNodeIds, columnIds) {
10046
10046
  );
10047
10047
  }
10048
10048
  for (const inner of templateNodes) {
10049
+ validateReviewKind(inner, "nested");
10049
10050
  if (inner.kind === "foreach" || inner.kind === "loop") {
10050
10051
  throw new WorkflowIrError(
10051
10052
  `foreach node '${node.id}' template may not contain nested loop/foreach ('${inner.id}')`
@@ -10164,6 +10165,7 @@ function validateLoop(node, topLevelNodeIds, columnIds) {
10164
10165
  );
10165
10166
  }
10166
10167
  for (const inner of templateNodes) {
10168
+ validateReviewKind(inner, "nested");
10167
10169
  if (inner.kind === "loop" || inner.kind === "foreach") {
10168
10170
  throw new WorkflowIrError(
10169
10171
  `loop node '${node.id}' template may not contain nested loop/foreach ('${inner.id}')`
@@ -10257,6 +10259,7 @@ function validateOptionalGroup(node, topLevelNodeIds, columnIds) {
10257
10259
  throw new WorkflowIrError(`optional-group node '${node.id}' template has duplicate node ids`);
10258
10260
  }
10259
10261
  for (const inner of templateNodes) {
10262
+ validateReviewKind(inner, "nested");
10260
10263
  if (inner.kind === "loop" || inner.kind === "foreach" || inner.kind === "optional-group") {
10261
10264
  throw new WorkflowIrError(
10262
10265
  `optional-group node '${node.id}' template may not contain nested loop/foreach/optional-group ('${inner.id}')`
@@ -10928,6 +10931,19 @@ function validateColumnRecovery(column) {
10928
10931
  );
10929
10932
  }
10930
10933
  }
10934
+ function validateReviewKind(node, placement) {
10935
+ const value = node.config?.reviewKind;
10936
+ if (value === void 0) return;
10937
+ if (value !== "plan" && value !== "code") {
10938
+ throw new WorkflowIrError(`Workflow node '${node.id}' has invalid reviewKind '${String(value)}'; expected 'plan' or 'code'`);
10939
+ }
10940
+ if (placement === "nested") {
10941
+ throw new WorkflowIrError(`Workflow node '${node.id}' has reviewKind in an unsupported nested template placement`);
10942
+ }
10943
+ if (node.kind !== "prompt" && node.kind !== "gate" && node.kind !== "script" && node.kind !== "optional-group") {
10944
+ throw new WorkflowIrError(`Workflow node '${node.id}' has reviewKind on unsupported node kind '${node.kind}'`);
10945
+ }
10946
+ }
10931
10947
  function validateV2(ir2) {
10932
10948
  validateColumns(ir2);
10933
10949
  for (const [index2, column] of ir2.columns.entries()) {
@@ -10950,6 +10966,7 @@ function validateV2(ir2) {
10950
10966
  }
10951
10967
  const nodesById = new Map(ir2.nodes.map((n) => [n.id, n]));
10952
10968
  for (const node of ir2.nodes) {
10969
+ validateReviewKind(node, "top-level");
10953
10970
  validateExtensionMetadata(`Workflow node '${node.id}'`, node.extensions);
10954
10971
  if (node.column !== void 0 && !columnIds.has(node.column)) {
10955
10972
  throw new WorkflowIrError(
@@ -13413,6 +13430,7 @@ function codeReviewOptionalGroupNode(column, options = {}) {
13413
13430
  column,
13414
13431
  config: {
13415
13432
  name: CODE_REVIEW_NAME,
13433
+ reviewKind: "code",
13416
13434
  // Default-ON: runs for every coding task by default, but operators can toggle it
13417
13435
  // off per task (remove `code-review` from enabledWorkflowSteps).
13418
13436
  defaultOn: options.defaultOn ?? true,
@@ -13558,6 +13576,7 @@ function planReviewOptionalGroupNode(column, options = {}) {
13558
13576
  ...column ? { column } : {},
13559
13577
  config: {
13560
13578
  name: PLAN_REVIEW_NAME,
13579
+ reviewKind: "plan",
13561
13580
  defaultOn: options.defaultOn ?? true,
13562
13581
  /*
13563
13582
  * FNXC:WorkflowRemediation 2026-06-29-12:14:
@@ -127158,6 +127177,36 @@ var init_async_knowledge = __esm({
127158
127177
  });
127159
127178
 
127160
127179
  // ../core/src/workflows/workflow-step-results.ts
127180
+ function normalizeWorkflowReviewFindings(raw) {
127181
+ if (!Array.isArray(raw)) return void 0;
127182
+ const normalized = [];
127183
+ const usedIds = /* @__PURE__ */ new Set();
127184
+ for (const candidate of raw) {
127185
+ if (normalized.length >= MAX_WORKFLOW_REVIEW_FINDINGS || !candidate || typeof candidate !== "object") continue;
127186
+ const value = candidate;
127187
+ const title = boundedTrimmedString(value.title, MAX_FINDING_TITLE_LENGTH);
127188
+ const body = boundedTrimmedString(value.body, MAX_FINDING_BODY_LENGTH);
127189
+ if (!title || !body) continue;
127190
+ const baseId = boundedTrimmedString(value.id, MAX_FINDING_ID_LENGTH) || `finding-${normalized.length + 1}`;
127191
+ let id = baseId;
127192
+ let suffix = 2;
127193
+ while (usedIds.has(id)) id = `${baseId.slice(0, Math.max(1, MAX_FINDING_ID_LENGTH - String(suffix).length - 1))}-${suffix++}`;
127194
+ usedIds.add(id);
127195
+ const filePath = boundedTrimmedString(value.filePath, MAX_FINDING_PATH_LENGTH);
127196
+ const line2 = typeof value.line === "number" && Number.isFinite(value.line) && value.line > 0 ? Math.floor(value.line) : void 0;
127197
+ const severity = isWorkflowReviewFindingSeverity(value.severity) ? value.severity : void 0;
127198
+ normalized.push({ id, title, body, ...filePath ? { filePath } : {}, ...line2 ? { line: line2 } : {}, ...severity ? { severity } : {} });
127199
+ }
127200
+ return normalized.length > 0 ? normalized : void 0;
127201
+ }
127202
+ function boundedTrimmedString(value, maxLength) {
127203
+ if (typeof value !== "string") return void 0;
127204
+ const trimmed = value.trim();
127205
+ return trimmed && trimmed.length <= maxLength ? trimmed : void 0;
127206
+ }
127207
+ function isWorkflowReviewFindingSeverity(value) {
127208
+ return typeof value === "string" && WORKFLOW_REVIEW_FINDING_SEVERITIES.includes(value);
127209
+ }
127161
127210
  function isTerminalFailure(result) {
127162
127211
  return TERMINAL_FAILURE_STATUSES.has(result.status);
127163
127212
  }
@@ -127223,10 +127272,16 @@ function makeReviewLeaseRecord(args) {
127223
127272
  leaseOwner: args.owner
127224
127273
  };
127225
127274
  }
127226
- var MAX_WORKFLOW_STEP_PRIOR_ATTEMPTS, TERMINAL_FAILURE_STATUSES, PLAN_REVIEW_LEASE_STALENESS_MS, TERMINAL_STEP_STATUSES;
127275
+ var WORKFLOW_REVIEW_FINDING_SEVERITIES, MAX_WORKFLOW_REVIEW_FINDINGS, MAX_FINDING_ID_LENGTH, MAX_FINDING_TITLE_LENGTH, MAX_FINDING_BODY_LENGTH, MAX_FINDING_PATH_LENGTH, MAX_WORKFLOW_STEP_PRIOR_ATTEMPTS, TERMINAL_FAILURE_STATUSES, PLAN_REVIEW_LEASE_STALENESS_MS, TERMINAL_STEP_STATUSES;
127227
127276
  var init_workflow_step_results = __esm({
127228
127277
  "../core/src/workflows/workflow-step-results.ts"() {
127229
127278
  "use strict";
127279
+ WORKFLOW_REVIEW_FINDING_SEVERITIES = ["low", "medium", "high", "critical"];
127280
+ MAX_WORKFLOW_REVIEW_FINDINGS = 20;
127281
+ MAX_FINDING_ID_LENGTH = 128;
127282
+ MAX_FINDING_TITLE_LENGTH = 240;
127283
+ MAX_FINDING_BODY_LENGTH = 4e3;
127284
+ MAX_FINDING_PATH_LENGTH = 1e3;
127230
127285
  MAX_WORKFLOW_STEP_PRIOR_ATTEMPTS = 5;
127231
127286
  TERMINAL_FAILURE_STATUSES = /* @__PURE__ */ new Set([
127232
127287
  "failed",
@@ -127713,6 +127768,7 @@ __export(src_exports, {
127713
127768
  MAX_TITLE_LENGTH: () => MAX_TITLE_LENGTH,
127714
127769
  MAX_TITLE_SUMMARIZE_INPUT_LENGTH: () => MAX_TITLE_SUMMARIZE_INPUT_LENGTH,
127715
127770
  MAX_WORKFLOW_ICON_LENGTH: () => MAX_WORKFLOW_ICON_LENGTH,
127771
+ MAX_WORKFLOW_REVIEW_FINDINGS: () => MAX_WORKFLOW_REVIEW_FINDINGS,
127716
127772
  MAX_WORKFLOW_STEP_PRIOR_ATTEMPTS: () => MAX_WORKFLOW_STEP_PRIOR_ATTEMPTS,
127717
127773
  MEMORY_AUDIT_PATH: () => MEMORY_AUDIT_PATH,
127718
127774
  MEMORY_BACKEND_SETTINGS_KEYS: () => MEMORY_BACKEND_SETTINGS_KEYS,
@@ -127914,6 +127970,7 @@ __export(src_exports, {
127914
127970
  WAKE_DELTA_ASSIGNED_TASKS_CAP: () => WAKE_DELTA_ASSIGNED_TASKS_CAP,
127915
127971
  WEDGE_RENOTIFY_COOLDOWN_MS: () => WEDGE_RENOTIFY_COOLDOWN_MS,
127916
127972
  WORKFLOW_EXTENSION_SCHEMA_VERSION: () => WORKFLOW_EXTENSION_SCHEMA_VERSION,
127973
+ WORKFLOW_REVIEW_FINDING_SEVERITIES: () => WORKFLOW_REVIEW_FINDING_SEVERITIES,
127917
127974
  WORKFLOW_SETTING_REJECTION_CODES: () => WORKFLOW_SETTING_REJECTION_CODES,
127918
127975
  WORKFLOW_SETTING_TYPES: () => WORKFLOW_SETTING_TYPES,
127919
127976
  WORKFLOW_WORK_ITEM_KINDS: () => WORKFLOW_WORK_ITEM_KINDS,
@@ -128441,6 +128498,7 @@ __export(src_exports, {
128441
128498
  isWipColumn: () => isWipColumn,
128442
128499
  isWipColumnRole: () => isWipColumnRole,
128443
128500
  isWorkflowOptionalGroupEnabled: () => isWorkflowOptionalGroupEnabled,
128501
+ isWorkflowReviewFindingSeverity: () => isWorkflowReviewFindingSeverity,
128444
128502
  isWorkspaceTask: () => isWorkspaceTask,
128445
128503
  layoutForIr: () => layoutForIr,
128446
128504
  listActiveAiSessions: () => listActiveAiSessions,
@@ -128514,6 +128572,7 @@ __export(src_exports, {
128514
128572
  normalizeValidationDiagnostics: () => normalizeValidationDiagnostics,
128515
128573
  normalizeWorkflowIcon: () => normalizeWorkflowIcon,
128516
128574
  normalizeWorkflowPromptOverrides: () => normalizeWorkflowPromptOverrides,
128575
+ normalizeWorkflowReviewFindings: () => normalizeWorkflowReviewFindings,
128517
128576
  notifyOperatorOfNonOperatorDelete: () => notifyOperatorOfNonOperatorDelete,
128518
128577
  overseerAdviceSeverityRank: () => overseerAdviceSeverityRank,
128519
128578
  parseAgentManifest: () => parseAgentManifest,
@@ -164660,37 +164719,6 @@ var init_code_node_runner = __esm({
164660
164719
  }
164661
164720
  });
164662
164721
 
164663
- // ../engine/src/errors/transient-merge-error-classifier.ts
164664
- function classifyTransientMergeError(error2) {
164665
- if (!error2) return null;
164666
- if (/lease-handoff-failed[^a-z]+target-not-queued/i.test(error2)) {
164667
- return "lease-handoff-target-not-queued";
164668
- }
164669
- if (/\bACP turn failed\b/i.test(error2) || /\bacp rpc code -32(?:603|00[0-3])\b/i.test(error2)) {
164670
- return "ai-provider-turn-failure";
164671
- }
164672
- if (/\bspawn(?:\s+\S+)?\s+ENO(?:TDIR|ENT)\b/i.test(error2)) {
164673
- return "process-spawn-failure";
164674
- }
164675
- if (/\bis not a working tree\b/i.test(error2)) {
164676
- return "process-spawn-failure";
164677
- }
164678
- const sameSha = error2.match(/advanced concurrently \(expected ([0-9a-f]{7,40}),\s+observed ([0-9a-f]{7,40})\)/i);
164679
- if (sameSha && sameSha[1].toLowerCase() === sameSha[2].toLowerCase()) {
164680
- return "spurious-concurrent-advance-same-sha";
164681
- }
164682
- if (isTransientError(error2)) {
164683
- return "network-transport-failure";
164684
- }
164685
- return null;
164686
- }
164687
- var init_transient_merge_error_classifier = __esm({
164688
- "../engine/src/errors/transient-merge-error-classifier.ts"() {
164689
- "use strict";
164690
- init_transient_error_patterns();
164691
- }
164692
- });
164693
-
164694
164722
  // ../engine/src/notification/ntfy-provider.ts
164695
164723
  function resolveParticipantLabel(metadata, kind) {
164696
164724
  const nameKey = kind === "from" ? "fromName" : "toName";
@@ -165164,7 +165192,50 @@ var init_webhook_provider = __esm({
165164
165192
  }
165165
165193
  });
165166
165194
 
165195
+ // ../engine/src/errors/transient-merge-error-classifier.ts
165196
+ function classifyTransientMergeError(error2) {
165197
+ if (!error2) return null;
165198
+ if (/lease-handoff-failed[^a-z]+target-not-queued/i.test(error2)) {
165199
+ return "lease-handoff-target-not-queued";
165200
+ }
165201
+ if (/\bACP turn failed\b/i.test(error2) || /\bacp rpc code -32(?:603|00[0-3])\b/i.test(error2)) {
165202
+ return "ai-provider-turn-failure";
165203
+ }
165204
+ if (/\bspawn(?:\s+\S+)?\s+ENO(?:TDIR|ENT)\b/i.test(error2)) {
165205
+ return "process-spawn-failure";
165206
+ }
165207
+ if (/\bis not a working tree\b/i.test(error2)) {
165208
+ return "process-spawn-failure";
165209
+ }
165210
+ const sameSha = error2.match(/advanced concurrently \(expected ([0-9a-f]{7,40}),\s+observed ([0-9a-f]{7,40})\)/i);
165211
+ if (sameSha && sameSha[1].toLowerCase() === sameSha[2].toLowerCase()) {
165212
+ return "spurious-concurrent-advance-same-sha";
165213
+ }
165214
+ if (isTransientError(error2)) {
165215
+ return "network-transport-failure";
165216
+ }
165217
+ return null;
165218
+ }
165219
+ function hasTransientMergeRecoveryOwner(task) {
165220
+ return classifyTransientMergeError(task.error) !== null && typeof task.mergeTransientRetryCount === "number" && Number.isInteger(task.mergeTransientRetryCount) && task.mergeTransientRetryCount >= 0 && task.mergeTransientRetryCount < MAX_AUTO_MERGE_TRANSIENT_RETRIES;
165221
+ }
165222
+ var MAX_AUTO_MERGE_TRANSIENT_RETRIES;
165223
+ var init_transient_merge_error_classifier = __esm({
165224
+ "../engine/src/errors/transient-merge-error-classifier.ts"() {
165225
+ "use strict";
165226
+ init_transient_error_patterns();
165227
+ MAX_AUTO_MERGE_TRANSIENT_RETRIES = 5;
165228
+ }
165229
+ });
165230
+
165167
165231
  // ../engine/src/notification/task-wedge-notification.ts
165232
+ function describeTaskRecoveryOwner(task) {
165233
+ if (hasTransientMergeRecoveryOwner(task)) return { kind: "transient-merge-retry" };
165234
+ if (typeof task.recoveryRetryCount === "number" && Number.isInteger(task.recoveryRetryCount) && task.recoveryRetryCount >= 0 && typeof task.nextRecoveryAt === "string" && Number.isFinite(Date.parse(task.nextRecoveryAt))) {
165235
+ return { kind: "scheduled-recovery" };
165236
+ }
165237
+ return null;
165238
+ }
165168
165239
  function describeSelfHealingNoActionWedge(task, stage, metadata) {
165169
165240
  const description = SELF_HEALING_NO_ACTIONS[stage];
165170
165241
  if (!description || task.userPaused || task.paused || task.autoMerge === false) return null;
@@ -165222,6 +165293,7 @@ function describeTaskWedge(task) {
165222
165293
  if (/merge.*(?:blocked|verification|gate)|(?:verification|gate).*(?:failed|exhaust)/i.test(error2)) {
165223
165294
  return { reasonKey: "merge-blocked", reason: "Merge verification cannot progress without operator action.", action: "Fix the failing verification, then retry the task." };
165224
165295
  }
165296
+ if (describeTaskRecoveryOwner(task)) return null;
165225
165297
  if (!error2 && (task.mergeRetries ?? 0) < 3) return null;
165226
165298
  return {
165227
165299
  reasonKey: "terminal-failed",
@@ -165233,6 +165305,7 @@ var SELF_HEALING_NO_ACTIONS;
165233
165305
  var init_task_wedge_notification = __esm({
165234
165306
  "../engine/src/notification/task-wedge-notification.ts"() {
165235
165307
  "use strict";
165308
+ init_transient_merge_error_classifier();
165236
165309
  SELF_HEALING_NO_ACTIONS = {
165237
165310
  "reclaim-pr-conflict": {
165238
165311
  reason: "Self-healing could not reclaim a stalled pull-request conflict.",
@@ -165298,7 +165371,6 @@ var init_notification_service = __esm({
165298
165371
  init_src2();
165299
165372
  init_notifier();
165300
165373
  init_logger();
165301
- init_transient_merge_error_classifier();
165302
165374
  init_ntfy_provider();
165303
165375
  init_webhook_provider();
165304
165376
  init_task_wedge_notification();
@@ -165524,10 +165596,10 @@ var init_notification_service = __esm({
165524
165596
  }
165525
165597
  }
165526
165598
  handleTaskUpdated = (task, meta2) => {
165527
- const transientFailure = task.status === "failed" ? classifyTransientMergeError(task.error) : null;
165528
- const wedge = transientFailure ? null : describeTaskWedge(task);
165599
+ const recoveryOwner = task.status === "failed" ? describeTaskRecoveryOwner(task) : null;
165600
+ const wedge = describeTaskWedge(task);
165529
165601
  if (wedge) this.cancelPendingFailureNotification(task.id, "classified-terminal-wedge");
165530
- if (!transientFailure) void this.enqueueWedgeHandling(task.id, () => this.maybeNotifyTaskWedge(task, wedge));
165602
+ void this.enqueueWedgeHandling(task.id, () => this.maybeNotifyTaskWedge(task, wedge));
165531
165603
  void this.maybeSuppressTransientFailedNotification(task, `status=${task.status ?? "undefined"}`);
165532
165604
  if (task.status === "awaiting-approval") {
165533
165605
  void this.writeAwaitingApprovalMailboxMessage(task);
@@ -165538,17 +165610,12 @@ var init_notification_service = __esm({
165538
165610
  if (!this.notificationsEnabled) {
165539
165611
  return;
165540
165612
  }
165541
- if (task.status === "failed" && !wedge) {
165542
- const transientClass = classifyTransientMergeError(task.error);
165543
- if (transientClass) {
165544
- this.failureNotificationSuppressedCount += 1;
165545
- schedulerLog.debug(
165546
- `[notify] ${task.id} transient merge failure (${transientClass}) \u2014 suppressed notification (self-heal in flight)`
165547
- );
165548
- return;
165549
- }
165613
+ if (task.status === "failed" && recoveryOwner) {
165614
+ this.failureNotificationSuppressedCount += 1;
165615
+ schedulerLog.debug(`[notify] ${task.id} recovery-owned failure \u2014 suppressed notification`);
165616
+ } else if (task.status === "failed" && !wedge) {
165550
165617
  if (this.failureNotificationMode === "all") {
165551
- this.maybeNotify(task.id, "failed", this.createTaskPayload(task, "failed"));
165618
+ void this.maybeNotifyImmediateFailure(task);
165552
165619
  } else {
165553
165620
  this.scheduleFailureNotification(task);
165554
165621
  }
@@ -165635,7 +165702,17 @@ var init_notification_service = __esm({
165635
165702
  await this.enqueueWedgeHandling(task.id, () => this.maybeNotifyTaskWedge(task, descriptor));
165636
165703
  }
165637
165704
  async maybeNotifyTaskWedge(task, suppliedDescriptor) {
165638
- const descriptor = suppliedDescriptor ?? describeTaskWedge(task);
165705
+ const liveTask = this.store.getTask ? await this.store.getTask(task.id) ?? task : task;
165706
+ const recoveryOwner = describeTaskRecoveryOwner(liveTask);
165707
+ if (recoveryOwner) {
165708
+ if (this.activeWedgeReasons.has(task.id) || liveTask.wedgeNotification?.status === "active") {
165709
+ this.activeWedgeReasons.delete(task.id);
165710
+ await this.store.claimTaskWedgeNotificationEpisode?.(task.id, null);
165711
+ }
165712
+ return;
165713
+ }
165714
+ const descriptor = suppliedDescriptor ?? describeTaskWedge(liveTask);
165715
+ task = liveTask;
165639
165716
  let episode;
165640
165717
  if (!descriptor) {
165641
165718
  const isActiveSelfHealingNoAction = task.wedgeNotification?.status === "active" && task.wedgeNotification.reasonKey.startsWith("self-healing-no-action:");
@@ -166002,6 +166079,14 @@ var init_notification_service = __esm({
166002
166079
  this.failureNotificationDelayMs = typeof settings.failureNotificationDelayMs === "number" && settings.failureNotificationDelayMs >= 0 ? settings.failureNotificationDelayMs : this.failedNotificationGraceMs;
166003
166080
  this.failureNotificationMode = settings.failureNotificationMode ?? "sticky-only";
166004
166081
  }
166082
+ async maybeNotifyImmediateFailure(task) {
166083
+ const liveTask = this.store.getTask ? await this.store.getTask(task.id) ?? task : task;
166084
+ if (liveTask.status !== "failed" || describeTaskRecoveryOwner(liveTask) || describeTaskWedge(liveTask)) {
166085
+ this.failureNotificationSuppressedCount += 1;
166086
+ return;
166087
+ }
166088
+ this.maybeNotify(liveTask.id, "failed", this.createTaskPayload(liveTask, "failed"));
166089
+ }
166005
166090
  scheduleFailureNotification(task) {
166006
166091
  if (this.pendingFailureNotifications.has(task.id)) {
166007
166092
  return;
@@ -166058,12 +166143,9 @@ var init_notification_service = __esm({
166058
166143
  schedulerLog.debug(`[notify] ${taskId} no longer failed at dispatch time \u2014 suppressed notification`);
166059
166144
  return;
166060
166145
  }
166061
- const transientClassAtDispatch = classifyTransientMergeError(task.error);
166062
- if (transientClassAtDispatch) {
166146
+ if (describeTaskRecoveryOwner(task)) {
166063
166147
  this.failureNotificationSuppressedCount += 1;
166064
- schedulerLog.debug(
166065
- `[notify] ${taskId} transient merge failure (${transientClassAtDispatch}) at dispatch time \u2014 suppressed notification (self-heal in flight)`
166066
- );
166148
+ schedulerLog.debug(`[notify] ${taskId} recovery-owned failure at dispatch time \u2014 suppressed notification`);
166067
166149
  return;
166068
166150
  }
166069
166151
  if (describeTaskWedge(task)) {
@@ -192043,10 +192125,21 @@ var Scheduler = class {
192043
192125
  const unmetDeps = getUnmetSchedulingDependencies(task, tasks2, schedulingDependencyOptions);
192044
192126
  if (unmetDeps.length > 0) {
192045
192127
  const normalizedUnmetDeps = [...new Set(unmetDeps)].sort();
192128
+ let activeOverlapBlockedBy = null;
192129
+ if (activeScopes.size > 0) {
192130
+ try {
192131
+ const taskScope = await getFilteredFileScope(task.id);
192132
+ if (taskScope.length > 0 && !isCoordinationOnlyTask(task, taskScope)) {
192133
+ activeOverlapBlockedBy = Array.from(activeScopes.entries()).sort(([aId], [bId]) => aId.localeCompare(bId)).find(([, activeScope]) => this.pathsOverlap(taskScope, activeScope))?.[0] ?? null;
192134
+ }
192135
+ } catch (error2) {
192136
+ schedulerLog.warn(`Failed to refresh file-scope overlap blocker for dependency-blocked task ${task.id}`, error2);
192137
+ }
192138
+ }
192046
192139
  await this.transitionQueuedEpisode(task, {
192047
192140
  signature: `dependency:${normalizedUnmetDeps.join(",")}`,
192048
192141
  blockedBy: unmetDeps[0] ?? null,
192049
- overlapBlockedBy: task.overlapBlockedBy ?? null,
192142
+ overlapBlockedBy: activeOverlapBlockedBy,
192050
192143
  action: `queued \u2014 unmet dependencies: ${unmetDeps.join(", ")}`
192051
192144
  });
192052
192145
  this.options.onBlocked?.(task, unmetDeps);
@@ -194569,6 +194662,7 @@ var SESSION_CONTENTION_HOLD_VALUE = "session-contention-hold";
194569
194662
  var WORKFLOW_INTERRUPTED_NODE_ID_CONTEXT_KEY = "workflow:interruptedNodeId";
194570
194663
  var WORKFLOW_INTERRUPTED_NODE_ABORT_KIND_CONTEXT_KEY = "workflow:interruptedNodeAbortKind";
194571
194664
  var WORKFLOW_OPTIONAL_GROUP_CONTEXT_KEY = "workflow:optionalGroupActive";
194665
+ var WORKFLOW_REVIEW_KIND_CONTEXT_KEY = "workflow:reviewKind";
194572
194666
  var WORKFLOW_NODE_ENGINE_PAUSE_ABORT_KIND = "engine-pause";
194573
194667
  var WORKFLOW_DRIFT_PARK_CONTEXT_KEY = "workflow:driftPark";
194574
194668
  var WorkflowGraphSuspended = class extends Error {
@@ -194893,6 +194987,7 @@ var WorkflowGraphExecutor = class {
194893
194987
  phase: stepPhase,
194894
194988
  status: "pending",
194895
194989
  source: "optional-group",
194990
+ ...this.workflowReviewKind(node) ? { reviewKind: this.workflowReviewKind(node) } : {},
194896
194991
  startedAt: stepStartedAt,
194897
194992
  // U3/KTD-4: stamp the lease owner so a concurrent/crashed re-entry
194898
194993
  // adopts this pending gate instead of dispatching a second reviewer.
@@ -194912,7 +195007,8 @@ var WorkflowGraphExecutor = class {
194912
195007
  runTemplateNode: (tNode, sig, contextOverride) => {
194913
195008
  const optionalGroupContext = {
194914
195009
  ...contextOverride ?? context,
194915
- [WORKFLOW_OPTIONAL_GROUP_CONTEXT_KEY]: node.id
195010
+ [WORKFLOW_OPTIONAL_GROUP_CONTEXT_KEY]: node.id,
195011
+ ...this.workflowReviewKind(node) ? { [WORKFLOW_REVIEW_KIND_CONTEXT_KEY]: this.workflowReviewKind(node) } : {}
194916
195012
  };
194917
195013
  return this.executeNodeWithRetries(tNode, task, settings, optionalGroupContext, ir2, sig, false);
194918
195014
  },
@@ -194930,6 +195026,7 @@ var WorkflowGraphExecutor = class {
194930
195026
  const exitContextPatch = exitResult?.contextPatch;
194931
195027
  let stepOutput = typeof exitContextPatch?.output === "string" ? exitContextPatch.output : void 0;
194932
195028
  const stepNotes = typeof exitContextPatch?.notes === "string" ? exitContextPatch.notes : void 0;
195029
+ const stepFindings = this.workflowReviewKind(node) && Array.isArray(exitContextPatch?.findings) ? exitContextPatch.findings : void 0;
194933
195030
  if (stepStatus === "failed" && !verdict && stepOutput === void 0 && stepNotes === void 0) {
194934
195031
  const lastVisited = groupResult.visitedNodeIds[groupResult.visitedNodeIds.length - 1];
194935
195032
  const templateNodeId = lastVisited?.includes("::") ? lastVisited.slice(lastVisited.indexOf("::") + 2) : void 0;
@@ -194947,9 +195044,11 @@ var WorkflowGraphExecutor = class {
194947
195044
  phase: stepPhase,
194948
195045
  source: "optional-group",
194949
195046
  status: stepStatus,
195047
+ ...this.workflowReviewKind(node) ? { reviewKind: this.workflowReviewKind(node) } : {},
194950
195048
  ...verdict ? { verdict } : {},
194951
195049
  ...stepOutput !== void 0 ? { output: stepOutput } : {},
194952
195050
  ...stepNotes !== void 0 ? { notes: stepNotes } : {},
195051
+ ...stepFindings?.length ? { findings: stepFindings } : {},
194953
195052
  startedAt: stepStartedAt,
194954
195053
  completedAt: (/* @__PURE__ */ new Date()).toISOString()
194955
195054
  });
@@ -195409,9 +195508,15 @@ var WorkflowGraphExecutor = class {
195409
195508
  }
195410
195509
  return failureResult;
195411
195510
  }
195511
+ /** FNXC:WorkflowReviewKind 2026-08-05-02:31: Persist only the declared closed
195512
+ * marker; execution never derives review semantics from a label, verdict, or gate mode. */
195513
+ workflowReviewKind(node) {
195514
+ return node.config?.reviewKind === "plan" || node.config?.reviewKind === "code" ? node.config.reviewKind : void 0;
195515
+ }
195412
195516
  shouldRecordNodeProgress(node) {
195413
195517
  const skillName = typeof node.config?.skillName === "string" ? node.config.skillName.trim() : "";
195414
- return skillName.length > 0 && (node.kind === "prompt" || node.kind === "gate");
195518
+ const markedReview = this.workflowReviewKind(node) !== void 0;
195519
+ return markedReview && (node.kind === "prompt" || node.kind === "gate" || node.kind === "script") || skillName.length > 0 && (node.kind === "prompt" || node.kind === "gate");
195415
195520
  }
195416
195521
  workflowNodeProgressName(node) {
195417
195522
  const configuredName = typeof node.config?.name === "string" ? node.config.name.trim() : "";
@@ -195425,6 +195530,7 @@ var WorkflowGraphExecutor = class {
195425
195530
  phase: node.config?.phase === "post-merge" ? "post-merge" : "pre-merge",
195426
195531
  source: "node",
195427
195532
  status: "pending",
195533
+ ...this.workflowReviewKind(node) ? { reviewKind: this.workflowReviewKind(node) } : {},
195428
195534
  startedAt
195429
195535
  };
195430
195536
  await this.recordOptionalGroupStepResult(taskId, result);
@@ -195435,6 +195541,7 @@ var WorkflowGraphExecutor = class {
195435
195541
  const contextPatch = nodeResult.contextPatch ?? {};
195436
195542
  let output = typeof contextPatch.output === "string" ? contextPatch.output : void 0;
195437
195543
  const notes = typeof contextPatch.notes === "string" ? contextPatch.notes : void 0;
195544
+ const findings = this.workflowReviewKind(node) && Array.isArray(contextPatch.findings) ? contextPatch.findings : void 0;
195438
195545
  if (status === "failed" && output === void 0 && notes === void 0) {
195439
195546
  output = this.synthesizeNonVerdictFailureOutput({
195440
195547
  stepLabel: this.workflowNodeProgressName(node),
@@ -195449,8 +195556,10 @@ var WorkflowGraphExecutor = class {
195449
195556
  phase: started?.phase ?? (node.config?.phase === "post-merge" ? "post-merge" : "pre-merge"),
195450
195557
  source: "node",
195451
195558
  status,
195559
+ ...this.workflowReviewKind(node) ? { reviewKind: this.workflowReviewKind(node) } : {},
195452
195560
  ...output !== void 0 ? { output } : {},
195453
195561
  ...notes !== void 0 ? { notes } : {},
195562
+ ...findings?.length ? { findings } : {},
195454
195563
  startedAt: started?.startedAt ?? (/* @__PURE__ */ new Date()).toISOString(),
195455
195564
  completedAt: (/* @__PURE__ */ new Date()).toISOString()
195456
195565
  });
@@ -219935,9 +220044,11 @@ function parseWorkflowStepVerdict(rawOutput) {
219935
220044
  verdict = "REVISE";
219936
220045
  }
219937
220046
  if (!verdict) continue;
220047
+ const findings = normalizeWorkflowReviewFindings(parsed.findings);
219938
220048
  return {
219939
220049
  verdict,
219940
- notes: typeof parsed.notes === "string" ? parsed.notes : ""
220050
+ notes: typeof parsed.notes === "string" ? parsed.notes : "",
220051
+ ...findings ? { findings } : {}
219941
220052
  };
219942
220053
  } catch {
219943
220054
  }
@@ -219969,7 +220080,8 @@ function parseWorkflowStepOutput(rawOutput, options = {}) {
219969
220080
  return {
219970
220081
  output: parsed.notes || "",
219971
220082
  verdict: parsed.verdict,
219972
- notes: parsed.notes
220083
+ notes: parsed.notes,
220084
+ ...parsed.findings ? { findings: parsed.findings } : {}
219973
220085
  };
219974
220086
  }
219975
220087
  const inferred = inferWorkflowStepVerdictFromProse(trimmed);
@@ -225794,6 +225906,7 @@ ${info.feedback}`, revisionKey),
225794
225906
  return this.runCliAgentNode(node, await this.store.getTask(live.id), cfg);
225795
225907
  }
225796
225908
  const optionalGroupId = typeof graphContext?.[WORKFLOW_OPTIONAL_GROUP_CONTEXT_KEY] === "string" ? graphContext[WORKFLOW_OPTIONAL_GROUP_CONTEXT_KEY] : void 0;
225909
+ 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;
225797
225910
  const isCompletionSummaryNode2 = cfg.summaryTarget === "task" || node.id === "completion-summary";
225798
225911
  if (live.executionMode === "fast" && !isCompletionSummaryNode2 && !optionalGroupId && !cfg.seam && (node.kind === "prompt" || node.kind === "script" || node.kind === "gate")) {
225799
225912
  executorLog.debug(`${live.id}: fast mode \u2014 skipping custom graph node '${node.id}'`);
@@ -225942,6 +226055,9 @@ ${prompt}`;
225942
226055
  if (optionalGroupId) {
225943
226056
  step.optionalGroupId = optionalGroupId;
225944
226057
  }
226058
+ if (declaredReviewKind) {
226059
+ step.reviewKind = declaredReviewKind;
226060
+ }
225945
226061
  if (cfg.reviewCanFixInline === true) {
225946
226062
  step.reviewCanFixInline = true;
225947
226063
  }
@@ -225954,7 +226070,11 @@ ${prompt}`;
225954
226070
  executorLog.debug(`${live.id}: graph node '${node.id}' runtime env injected (${injected.pathEntryCount} PATH entries, ${injected.injectedKeyCount} env keys)`);
225955
226071
  }
225956
226072
  const unattended = this.graphUnattendedRuns.has(live.id);
225957
- const outcome = mode === "script" ? await this.executeScriptWorkflowStep(live, step, worktreePath, settings, nodeEnv) : await this.executeWorkflowStep(live, step, worktreePath, settings, nodeEnv, { unattended });
226073
+ let outcome = mode === "script" ? await this.executeScriptWorkflowStep(live, step, worktreePath, settings, nodeEnv) : await this.executeWorkflowStep(live, step, worktreePath, settings, nodeEnv, { unattended });
226074
+ if (declaredReviewKind && typeof outcome.output === "string") {
226075
+ const parsedReviewOutput = parseWorkflowStepOutput(outcome.output, { requireVerdict: false });
226076
+ if (parsedReviewOutput.findings?.length) outcome = { ...outcome, findings: parsedReviewOutput.findings };
226077
+ }
225958
226078
  const awaitQuestion = parseAwaitInputSentinel(outcome.output);
225959
226079
  if (awaitQuestion) {
225960
226080
  await this.store.logEntry(
@@ -225977,6 +226097,8 @@ ${prompt}`;
225977
226097
  const contextPatch = {};
225978
226098
  if (typeof stepOutput === "string") contextPatch.output = stepOutput;
225979
226099
  if (typeof stepNotes === "string" && stepNotes) contextPatch.notes = stepNotes;
226100
+ const stepFindings = outcome.findings;
226101
+ if (stepFindings?.length) contextPatch.findings = stepFindings;
225980
226102
  if (cfg.summaryTarget === "task" && typeof stepOutput === "string" && stepOutput.trim()) {
225981
226103
  contextPatch.summary = stepOutput.trim();
225982
226104
  }
@@ -232479,13 +232601,14 @@ CRITICAL SCOPING RULES \u2014 read before doing anything else:
232479
232601
  const isSkillStep = typeof workflowStep.skillName === "string" && workflowStep.skillName.trim().length > 0;
232480
232602
  const isSummaryProjectionStep = workflowStep.summaryTarget === "task";
232481
232603
  const requireVerdict = !isSummaryProjectionStep && (workflowStep.gateMode === "gate" || !isSkillStep);
232604
+ const reviewFindingsContract = workflowStepMetadata.reviewKind === "plan" || workflowStepMetadata.reviewKind === "code";
232482
232605
  const verdictBlock = requireVerdict ? `
232483
232606
 
232484
232607
  ## Feedback Format
232485
232608
 
232486
232609
  When your review is complete, your final line MUST be a single JSON object (no markdown fences):
232487
232610
 
232488
- {"verdict":"APPROVE|APPROVE_WITH_NOTES|REVISE","notes":"..."}
232611
+ ${reviewFindingsContract ? '{"verdict":"APPROVE|APPROVE_WITH_NOTES|REVISE","notes":"...","findings":[{"id":"stable-id","title":"concise issue","body":"actionable detail","filePath":"optional/path","line":1,"severity":"low|medium|high|critical"}]}' : '{"verdict":"APPROVE|APPROVE_WITH_NOTES|REVISE","notes":"..."}'}
232489
232612
 
232490
232613
  Rules:
232491
232614
  - Output exactly one trailing JSON object and stop.
@@ -232795,7 +232918,8 @@ ${detectedQuestion}
232795
232918
  revisionRequested,
232796
232919
  output: parsed.output,
232797
232920
  verdict: parsed.verdict,
232798
- notes: parsed.notes
232921
+ notes: parsed.notes,
232922
+ ...parsed.findings ? { findings: parsed.findings } : {}
232799
232923
  };
232800
232924
  }
232801
232925
  if (parsed.malformed) {
@@ -254982,7 +255106,7 @@ var ProjectEngine = class _ProjectEngine {
254982
255106
  *
254983
255107
  * Readable (not private) so tests derive the cap from this single source of truth rather
254984
255108
  * than hardcoding it — the FN-8004 bump broke two suites that had baked in the old `3`. */
254985
- static MAX_AUTO_MERGE_TRANSIENT_RETRIES = 5;
255109
+ static MAX_AUTO_MERGE_TRANSIENT_RETRIES = MAX_AUTO_MERGE_TRANSIENT_RETRIES;
254986
255110
  static MERGE_REQUEST_RETRY_EXHAUSTED_AGE_MS = 30 * 60 * 1e3;
254987
255111
  /** Cap on outer in-review→in-progress bounces caused by deterministic
254988
255112
  * verification failures during auto-merge. After this many failed merges