@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
package/dist/bin.js CHANGED
@@ -10048,6 +10048,7 @@ function validateForeach(node, topLevelNodeIds, columnIds) {
10048
10048
  );
10049
10049
  }
10050
10050
  for (const inner of templateNodes) {
10051
+ validateReviewKind(inner, "nested");
10051
10052
  if (inner.kind === "foreach" || inner.kind === "loop") {
10052
10053
  throw new WorkflowIrError(
10053
10054
  `foreach node '${node.id}' template may not contain nested loop/foreach ('${inner.id}')`
@@ -10166,6 +10167,7 @@ function validateLoop(node, topLevelNodeIds, columnIds) {
10166
10167
  );
10167
10168
  }
10168
10169
  for (const inner of templateNodes) {
10170
+ validateReviewKind(inner, "nested");
10169
10171
  if (inner.kind === "loop" || inner.kind === "foreach") {
10170
10172
  throw new WorkflowIrError(
10171
10173
  `loop node '${node.id}' template may not contain nested loop/foreach ('${inner.id}')`
@@ -10259,6 +10261,7 @@ function validateOptionalGroup(node, topLevelNodeIds, columnIds) {
10259
10261
  throw new WorkflowIrError(`optional-group node '${node.id}' template has duplicate node ids`);
10260
10262
  }
10261
10263
  for (const inner of templateNodes) {
10264
+ validateReviewKind(inner, "nested");
10262
10265
  if (inner.kind === "loop" || inner.kind === "foreach" || inner.kind === "optional-group") {
10263
10266
  throw new WorkflowIrError(
10264
10267
  `optional-group node '${node.id}' template may not contain nested loop/foreach/optional-group ('${inner.id}')`
@@ -10930,6 +10933,19 @@ function validateColumnRecovery(column) {
10930
10933
  );
10931
10934
  }
10932
10935
  }
10936
+ function validateReviewKind(node, placement) {
10937
+ const value = node.config?.reviewKind;
10938
+ if (value === void 0) return;
10939
+ if (value !== "plan" && value !== "code") {
10940
+ throw new WorkflowIrError(`Workflow node '${node.id}' has invalid reviewKind '${String(value)}'; expected 'plan' or 'code'`);
10941
+ }
10942
+ if (placement === "nested") {
10943
+ throw new WorkflowIrError(`Workflow node '${node.id}' has reviewKind in an unsupported nested template placement`);
10944
+ }
10945
+ if (node.kind !== "prompt" && node.kind !== "gate" && node.kind !== "script" && node.kind !== "optional-group") {
10946
+ throw new WorkflowIrError(`Workflow node '${node.id}' has reviewKind on unsupported node kind '${node.kind}'`);
10947
+ }
10948
+ }
10933
10949
  function validateV2(ir2) {
10934
10950
  validateColumns(ir2);
10935
10951
  for (const [index3, column] of ir2.columns.entries()) {
@@ -10952,6 +10968,7 @@ function validateV2(ir2) {
10952
10968
  }
10953
10969
  const nodesById = new Map(ir2.nodes.map((n) => [n.id, n]));
10954
10970
  for (const node of ir2.nodes) {
10971
+ validateReviewKind(node, "top-level");
10955
10972
  validateExtensionMetadata(`Workflow node '${node.id}'`, node.extensions);
10956
10973
  if (node.column !== void 0 && !columnIds.has(node.column)) {
10957
10974
  throw new WorkflowIrError(
@@ -13415,6 +13432,7 @@ function codeReviewOptionalGroupNode(column, options = {}) {
13415
13432
  column,
13416
13433
  config: {
13417
13434
  name: CODE_REVIEW_NAME,
13435
+ reviewKind: "code",
13418
13436
  // Default-ON: runs for every coding task by default, but operators can toggle it
13419
13437
  // off per task (remove `code-review` from enabledWorkflowSteps).
13420
13438
  defaultOn: options.defaultOn ?? true,
@@ -13560,6 +13578,7 @@ function planReviewOptionalGroupNode(column, options = {}) {
13560
13578
  ...column ? { column } : {},
13561
13579
  config: {
13562
13580
  name: PLAN_REVIEW_NAME,
13581
+ reviewKind: "plan",
13563
13582
  defaultOn: options.defaultOn ?? true,
13564
13583
  /*
13565
13584
  * FNXC:WorkflowRemediation 2026-06-29-12:14:
@@ -127160,6 +127179,36 @@ var init_async_knowledge = __esm({
127160
127179
  });
127161
127180
 
127162
127181
  // ../core/src/workflows/workflow-step-results.ts
127182
+ function normalizeWorkflowReviewFindings(raw) {
127183
+ if (!Array.isArray(raw)) return void 0;
127184
+ const normalized = [];
127185
+ const usedIds = /* @__PURE__ */ new Set();
127186
+ for (const candidate of raw) {
127187
+ if (normalized.length >= MAX_WORKFLOW_REVIEW_FINDINGS || !candidate || typeof candidate !== "object") continue;
127188
+ const value = candidate;
127189
+ const title2 = boundedTrimmedString(value.title, MAX_FINDING_TITLE_LENGTH);
127190
+ const body = boundedTrimmedString(value.body, MAX_FINDING_BODY_LENGTH);
127191
+ if (!title2 || !body) continue;
127192
+ const baseId = boundedTrimmedString(value.id, MAX_FINDING_ID_LENGTH) || `finding-${normalized.length + 1}`;
127193
+ let id = baseId;
127194
+ let suffix = 2;
127195
+ while (usedIds.has(id)) id = `${baseId.slice(0, Math.max(1, MAX_FINDING_ID_LENGTH - String(suffix).length - 1))}-${suffix++}`;
127196
+ usedIds.add(id);
127197
+ const filePath = boundedTrimmedString(value.filePath, MAX_FINDING_PATH_LENGTH);
127198
+ const line2 = typeof value.line === "number" && Number.isFinite(value.line) && value.line > 0 ? Math.floor(value.line) : void 0;
127199
+ const severity = isWorkflowReviewFindingSeverity(value.severity) ? value.severity : void 0;
127200
+ normalized.push({ id, title: title2, body, ...filePath ? { filePath } : {}, ...line2 ? { line: line2 } : {}, ...severity ? { severity } : {} });
127201
+ }
127202
+ return normalized.length > 0 ? normalized : void 0;
127203
+ }
127204
+ function boundedTrimmedString(value, maxLength) {
127205
+ if (typeof value !== "string") return void 0;
127206
+ const trimmed = value.trim();
127207
+ return trimmed && trimmed.length <= maxLength ? trimmed : void 0;
127208
+ }
127209
+ function isWorkflowReviewFindingSeverity(value) {
127210
+ return typeof value === "string" && WORKFLOW_REVIEW_FINDING_SEVERITIES.includes(value);
127211
+ }
127163
127212
  function isTerminalFailure(result2) {
127164
127213
  return TERMINAL_FAILURE_STATUSES.has(result2.status);
127165
127214
  }
@@ -127225,10 +127274,16 @@ function makeReviewLeaseRecord(args) {
127225
127274
  leaseOwner: args.owner
127226
127275
  };
127227
127276
  }
127228
- var MAX_WORKFLOW_STEP_PRIOR_ATTEMPTS, TERMINAL_FAILURE_STATUSES, PLAN_REVIEW_LEASE_STALENESS_MS, TERMINAL_STEP_STATUSES;
127277
+ 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;
127229
127278
  var init_workflow_step_results = __esm({
127230
127279
  "../core/src/workflows/workflow-step-results.ts"() {
127231
127280
  "use strict";
127281
+ WORKFLOW_REVIEW_FINDING_SEVERITIES = ["low", "medium", "high", "critical"];
127282
+ MAX_WORKFLOW_REVIEW_FINDINGS = 20;
127283
+ MAX_FINDING_ID_LENGTH = 128;
127284
+ MAX_FINDING_TITLE_LENGTH = 240;
127285
+ MAX_FINDING_BODY_LENGTH = 4e3;
127286
+ MAX_FINDING_PATH_LENGTH = 1e3;
127232
127287
  MAX_WORKFLOW_STEP_PRIOR_ATTEMPTS = 5;
127233
127288
  TERMINAL_FAILURE_STATUSES = /* @__PURE__ */ new Set([
127234
127289
  "failed",
@@ -127715,6 +127770,7 @@ __export(src_exports, {
127715
127770
  MAX_TITLE_LENGTH: () => MAX_TITLE_LENGTH,
127716
127771
  MAX_TITLE_SUMMARIZE_INPUT_LENGTH: () => MAX_TITLE_SUMMARIZE_INPUT_LENGTH,
127717
127772
  MAX_WORKFLOW_ICON_LENGTH: () => MAX_WORKFLOW_ICON_LENGTH,
127773
+ MAX_WORKFLOW_REVIEW_FINDINGS: () => MAX_WORKFLOW_REVIEW_FINDINGS,
127718
127774
  MAX_WORKFLOW_STEP_PRIOR_ATTEMPTS: () => MAX_WORKFLOW_STEP_PRIOR_ATTEMPTS,
127719
127775
  MEMORY_AUDIT_PATH: () => MEMORY_AUDIT_PATH,
127720
127776
  MEMORY_BACKEND_SETTINGS_KEYS: () => MEMORY_BACKEND_SETTINGS_KEYS,
@@ -127916,6 +127972,7 @@ __export(src_exports, {
127916
127972
  WAKE_DELTA_ASSIGNED_TASKS_CAP: () => WAKE_DELTA_ASSIGNED_TASKS_CAP,
127917
127973
  WEDGE_RENOTIFY_COOLDOWN_MS: () => WEDGE_RENOTIFY_COOLDOWN_MS,
127918
127974
  WORKFLOW_EXTENSION_SCHEMA_VERSION: () => WORKFLOW_EXTENSION_SCHEMA_VERSION,
127975
+ WORKFLOW_REVIEW_FINDING_SEVERITIES: () => WORKFLOW_REVIEW_FINDING_SEVERITIES,
127919
127976
  WORKFLOW_SETTING_REJECTION_CODES: () => WORKFLOW_SETTING_REJECTION_CODES,
127920
127977
  WORKFLOW_SETTING_TYPES: () => WORKFLOW_SETTING_TYPES,
127921
127978
  WORKFLOW_WORK_ITEM_KINDS: () => WORKFLOW_WORK_ITEM_KINDS,
@@ -128443,6 +128500,7 @@ __export(src_exports, {
128443
128500
  isWipColumn: () => isWipColumn,
128444
128501
  isWipColumnRole: () => isWipColumnRole,
128445
128502
  isWorkflowOptionalGroupEnabled: () => isWorkflowOptionalGroupEnabled,
128503
+ isWorkflowReviewFindingSeverity: () => isWorkflowReviewFindingSeverity,
128446
128504
  isWorkspaceTask: () => isWorkspaceTask,
128447
128505
  layoutForIr: () => layoutForIr,
128448
128506
  listActiveAiSessions: () => listActiveAiSessions,
@@ -128516,6 +128574,7 @@ __export(src_exports, {
128516
128574
  normalizeValidationDiagnostics: () => normalizeValidationDiagnostics,
128517
128575
  normalizeWorkflowIcon: () => normalizeWorkflowIcon,
128518
128576
  normalizeWorkflowPromptOverrides: () => normalizeWorkflowPromptOverrides,
128577
+ normalizeWorkflowReviewFindings: () => normalizeWorkflowReviewFindings,
128519
128578
  notifyOperatorOfNonOperatorDelete: () => notifyOperatorOfNonOperatorDelete,
128520
128579
  overseerAdviceSeverityRank: () => overseerAdviceSeverityRank,
128521
128580
  parseAgentManifest: () => parseAgentManifest,
@@ -173916,37 +173975,6 @@ var init_effective_settings = __esm({
173916
173975
  }
173917
173976
  });
173918
173977
 
173919
- // ../engine/src/errors/transient-merge-error-classifier.ts
173920
- function classifyTransientMergeError(error3) {
173921
- if (!error3) return null;
173922
- if (/lease-handoff-failed[^a-z]+target-not-queued/i.test(error3)) {
173923
- return "lease-handoff-target-not-queued";
173924
- }
173925
- if (/\bACP turn failed\b/i.test(error3) || /\bacp rpc code -32(?:603|00[0-3])\b/i.test(error3)) {
173926
- return "ai-provider-turn-failure";
173927
- }
173928
- if (/\bspawn(?:\s+\S+)?\s+ENO(?:TDIR|ENT)\b/i.test(error3)) {
173929
- return "process-spawn-failure";
173930
- }
173931
- if (/\bis not a working tree\b/i.test(error3)) {
173932
- return "process-spawn-failure";
173933
- }
173934
- const sameSha = error3.match(/advanced concurrently \(expected ([0-9a-f]{7,40}),\s+observed ([0-9a-f]{7,40})\)/i);
173935
- if (sameSha && sameSha[1].toLowerCase() === sameSha[2].toLowerCase()) {
173936
- return "spurious-concurrent-advance-same-sha";
173937
- }
173938
- if (isTransientError(error3)) {
173939
- return "network-transport-failure";
173940
- }
173941
- return null;
173942
- }
173943
- var init_transient_merge_error_classifier = __esm({
173944
- "../engine/src/errors/transient-merge-error-classifier.ts"() {
173945
- "use strict";
173946
- init_transient_error_patterns();
173947
- }
173948
- });
173949
-
173950
173978
  // ../engine/src/notification/ntfy-provider.ts
173951
173979
  function resolveParticipantLabel(metadata, kind) {
173952
173980
  const nameKey = kind === "from" ? "fromName" : "toName";
@@ -174420,7 +174448,50 @@ var init_webhook_provider = __esm({
174420
174448
  }
174421
174449
  });
174422
174450
 
174451
+ // ../engine/src/errors/transient-merge-error-classifier.ts
174452
+ function classifyTransientMergeError(error3) {
174453
+ if (!error3) return null;
174454
+ if (/lease-handoff-failed[^a-z]+target-not-queued/i.test(error3)) {
174455
+ return "lease-handoff-target-not-queued";
174456
+ }
174457
+ if (/\bACP turn failed\b/i.test(error3) || /\bacp rpc code -32(?:603|00[0-3])\b/i.test(error3)) {
174458
+ return "ai-provider-turn-failure";
174459
+ }
174460
+ if (/\bspawn(?:\s+\S+)?\s+ENO(?:TDIR|ENT)\b/i.test(error3)) {
174461
+ return "process-spawn-failure";
174462
+ }
174463
+ if (/\bis not a working tree\b/i.test(error3)) {
174464
+ return "process-spawn-failure";
174465
+ }
174466
+ const sameSha = error3.match(/advanced concurrently \(expected ([0-9a-f]{7,40}),\s+observed ([0-9a-f]{7,40})\)/i);
174467
+ if (sameSha && sameSha[1].toLowerCase() === sameSha[2].toLowerCase()) {
174468
+ return "spurious-concurrent-advance-same-sha";
174469
+ }
174470
+ if (isTransientError(error3)) {
174471
+ return "network-transport-failure";
174472
+ }
174473
+ return null;
174474
+ }
174475
+ function hasTransientMergeRecoveryOwner(task) {
174476
+ return classifyTransientMergeError(task.error) !== null && typeof task.mergeTransientRetryCount === "number" && Number.isInteger(task.mergeTransientRetryCount) && task.mergeTransientRetryCount >= 0 && task.mergeTransientRetryCount < MAX_AUTO_MERGE_TRANSIENT_RETRIES;
174477
+ }
174478
+ var MAX_AUTO_MERGE_TRANSIENT_RETRIES;
174479
+ var init_transient_merge_error_classifier = __esm({
174480
+ "../engine/src/errors/transient-merge-error-classifier.ts"() {
174481
+ "use strict";
174482
+ init_transient_error_patterns();
174483
+ MAX_AUTO_MERGE_TRANSIENT_RETRIES = 5;
174484
+ }
174485
+ });
174486
+
174423
174487
  // ../engine/src/notification/task-wedge-notification.ts
174488
+ function describeTaskRecoveryOwner(task) {
174489
+ if (hasTransientMergeRecoveryOwner(task)) return { kind: "transient-merge-retry" };
174490
+ if (typeof task.recoveryRetryCount === "number" && Number.isInteger(task.recoveryRetryCount) && task.recoveryRetryCount >= 0 && typeof task.nextRecoveryAt === "string" && Number.isFinite(Date.parse(task.nextRecoveryAt))) {
174491
+ return { kind: "scheduled-recovery" };
174492
+ }
174493
+ return null;
174494
+ }
174424
174495
  function describeSelfHealingNoActionWedge(task, stage, metadata) {
174425
174496
  const description = SELF_HEALING_NO_ACTIONS[stage];
174426
174497
  if (!description || task.userPaused || task.paused || task.autoMerge === false) return null;
@@ -174478,6 +174549,7 @@ function describeTaskWedge(task) {
174478
174549
  if (/merge.*(?:blocked|verification|gate)|(?:verification|gate).*(?:failed|exhaust)/i.test(error3)) {
174479
174550
  return { reasonKey: "merge-blocked", reason: "Merge verification cannot progress without operator action.", action: "Fix the failing verification, then retry the task." };
174480
174551
  }
174552
+ if (describeTaskRecoveryOwner(task)) return null;
174481
174553
  if (!error3 && (task.mergeRetries ?? 0) < 3) return null;
174482
174554
  return {
174483
174555
  reasonKey: "terminal-failed",
@@ -174489,6 +174561,7 @@ var SELF_HEALING_NO_ACTIONS;
174489
174561
  var init_task_wedge_notification = __esm({
174490
174562
  "../engine/src/notification/task-wedge-notification.ts"() {
174491
174563
  "use strict";
174564
+ init_transient_merge_error_classifier();
174492
174565
  SELF_HEALING_NO_ACTIONS = {
174493
174566
  "reclaim-pr-conflict": {
174494
174567
  reason: "Self-healing could not reclaim a stalled pull-request conflict.",
@@ -174554,7 +174627,6 @@ var init_notification_service = __esm({
174554
174627
  init_src2();
174555
174628
  init_notifier();
174556
174629
  init_logger3();
174557
- init_transient_merge_error_classifier();
174558
174630
  init_ntfy_provider();
174559
174631
  init_webhook_provider();
174560
174632
  init_task_wedge_notification();
@@ -174780,10 +174852,10 @@ var init_notification_service = __esm({
174780
174852
  }
174781
174853
  }
174782
174854
  handleTaskUpdated = (task, meta2) => {
174783
- const transientFailure = task.status === "failed" ? classifyTransientMergeError(task.error) : null;
174784
- const wedge = transientFailure ? null : describeTaskWedge(task);
174855
+ const recoveryOwner = task.status === "failed" ? describeTaskRecoveryOwner(task) : null;
174856
+ const wedge = describeTaskWedge(task);
174785
174857
  if (wedge) this.cancelPendingFailureNotification(task.id, "classified-terminal-wedge");
174786
- if (!transientFailure) void this.enqueueWedgeHandling(task.id, () => this.maybeNotifyTaskWedge(task, wedge));
174858
+ void this.enqueueWedgeHandling(task.id, () => this.maybeNotifyTaskWedge(task, wedge));
174787
174859
  void this.maybeSuppressTransientFailedNotification(task, `status=${task.status ?? "undefined"}`);
174788
174860
  if (task.status === "awaiting-approval") {
174789
174861
  void this.writeAwaitingApprovalMailboxMessage(task);
@@ -174794,17 +174866,12 @@ var init_notification_service = __esm({
174794
174866
  if (!this.notificationsEnabled) {
174795
174867
  return;
174796
174868
  }
174797
- if (task.status === "failed" && !wedge) {
174798
- const transientClass = classifyTransientMergeError(task.error);
174799
- if (transientClass) {
174800
- this.failureNotificationSuppressedCount += 1;
174801
- schedulerLog.debug(
174802
- `[notify] ${task.id} transient merge failure (${transientClass}) \u2014 suppressed notification (self-heal in flight)`
174803
- );
174804
- return;
174805
- }
174869
+ if (task.status === "failed" && recoveryOwner) {
174870
+ this.failureNotificationSuppressedCount += 1;
174871
+ schedulerLog.debug(`[notify] ${task.id} recovery-owned failure \u2014 suppressed notification`);
174872
+ } else if (task.status === "failed" && !wedge) {
174806
174873
  if (this.failureNotificationMode === "all") {
174807
- this.maybeNotify(task.id, "failed", this.createTaskPayload(task, "failed"));
174874
+ void this.maybeNotifyImmediateFailure(task);
174808
174875
  } else {
174809
174876
  this.scheduleFailureNotification(task);
174810
174877
  }
@@ -174891,7 +174958,17 @@ var init_notification_service = __esm({
174891
174958
  await this.enqueueWedgeHandling(task.id, () => this.maybeNotifyTaskWedge(task, descriptor));
174892
174959
  }
174893
174960
  async maybeNotifyTaskWedge(task, suppliedDescriptor) {
174894
- const descriptor = suppliedDescriptor ?? describeTaskWedge(task);
174961
+ const liveTask = this.store.getTask ? await this.store.getTask(task.id) ?? task : task;
174962
+ const recoveryOwner = describeTaskRecoveryOwner(liveTask);
174963
+ if (recoveryOwner) {
174964
+ if (this.activeWedgeReasons.has(task.id) || liveTask.wedgeNotification?.status === "active") {
174965
+ this.activeWedgeReasons.delete(task.id);
174966
+ await this.store.claimTaskWedgeNotificationEpisode?.(task.id, null);
174967
+ }
174968
+ return;
174969
+ }
174970
+ const descriptor = suppliedDescriptor ?? describeTaskWedge(liveTask);
174971
+ task = liveTask;
174895
174972
  let episode;
174896
174973
  if (!descriptor) {
174897
174974
  const isActiveSelfHealingNoAction = task.wedgeNotification?.status === "active" && task.wedgeNotification.reasonKey.startsWith("self-healing-no-action:");
@@ -175258,6 +175335,14 @@ var init_notification_service = __esm({
175258
175335
  this.failureNotificationDelayMs = typeof settings.failureNotificationDelayMs === "number" && settings.failureNotificationDelayMs >= 0 ? settings.failureNotificationDelayMs : this.failedNotificationGraceMs;
175259
175336
  this.failureNotificationMode = settings.failureNotificationMode ?? "sticky-only";
175260
175337
  }
175338
+ async maybeNotifyImmediateFailure(task) {
175339
+ const liveTask = this.store.getTask ? await this.store.getTask(task.id) ?? task : task;
175340
+ if (liveTask.status !== "failed" || describeTaskRecoveryOwner(liveTask) || describeTaskWedge(liveTask)) {
175341
+ this.failureNotificationSuppressedCount += 1;
175342
+ return;
175343
+ }
175344
+ this.maybeNotify(liveTask.id, "failed", this.createTaskPayload(liveTask, "failed"));
175345
+ }
175261
175346
  scheduleFailureNotification(task) {
175262
175347
  if (this.pendingFailureNotifications.has(task.id)) {
175263
175348
  return;
@@ -175314,12 +175399,9 @@ var init_notification_service = __esm({
175314
175399
  schedulerLog.debug(`[notify] ${taskId} no longer failed at dispatch time \u2014 suppressed notification`);
175315
175400
  return;
175316
175401
  }
175317
- const transientClassAtDispatch = classifyTransientMergeError(task.error);
175318
- if (transientClassAtDispatch) {
175402
+ if (describeTaskRecoveryOwner(task)) {
175319
175403
  this.failureNotificationSuppressedCount += 1;
175320
- schedulerLog.debug(
175321
- `[notify] ${taskId} transient merge failure (${transientClassAtDispatch}) at dispatch time \u2014 suppressed notification (self-heal in flight)`
175322
- );
175404
+ schedulerLog.debug(`[notify] ${taskId} recovery-owned failure at dispatch time \u2014 suppressed notification`);
175323
175405
  return;
175324
175406
  }
175325
175407
  if (describeTaskWedge(task)) {
@@ -186974,10 +187056,21 @@ var init_scheduler = __esm({
186974
187056
  const unmetDeps = getUnmetSchedulingDependencies(task, tasks2, schedulingDependencyOptions);
186975
187057
  if (unmetDeps.length > 0) {
186976
187058
  const normalizedUnmetDeps = [...new Set(unmetDeps)].sort();
187059
+ let activeOverlapBlockedBy = null;
187060
+ if (activeScopes.size > 0) {
187061
+ try {
187062
+ const taskScope = await getFilteredFileScope(task.id);
187063
+ if (taskScope.length > 0 && !isCoordinationOnlyTask(task, taskScope)) {
187064
+ activeOverlapBlockedBy = Array.from(activeScopes.entries()).sort(([aId], [bId]) => aId.localeCompare(bId)).find(([, activeScope]) => this.pathsOverlap(taskScope, activeScope))?.[0] ?? null;
187065
+ }
187066
+ } catch (error3) {
187067
+ schedulerLog.warn(`Failed to refresh file-scope overlap blocker for dependency-blocked task ${task.id}`, error3);
187068
+ }
187069
+ }
186977
187070
  await this.transitionQueuedEpisode(task, {
186978
187071
  signature: `dependency:${normalizedUnmetDeps.join(",")}`,
186979
187072
  blockedBy: unmetDeps[0] ?? null,
186980
- overlapBlockedBy: task.overlapBlockedBy ?? null,
187073
+ overlapBlockedBy: activeOverlapBlockedBy,
186981
187074
  action: `queued \u2014 unmet dependencies: ${unmetDeps.join(", ")}`
186982
187075
  });
186983
187076
  this.options.onBlocked?.(task, unmetDeps);
@@ -218779,7 +218872,7 @@ function recoverPassedPlanReviewFromLatestLog(task) {
218779
218872
  completedAt: latest.timestamp
218780
218873
  };
218781
218874
  }
218782
- var PLAN_REVIEW_PROVIDER_FAILURE_HOLD_VALUE, PLAN_REVIEW_LEASE_HELD_VALUE, SESSION_CONTENTION_HOLD_VALUE, WORKFLOW_INTERRUPTED_NODE_ID_CONTEXT_KEY, WORKFLOW_INTERRUPTED_NODE_ABORT_KIND_CONTEXT_KEY, WORKFLOW_OPTIONAL_GROUP_CONTEXT_KEY, WORKFLOW_NODE_ENGINE_PAUSE_ABORT_KIND, WORKFLOW_DRIFT_PARK_CONTEXT_KEY, WorkflowGraphSuspended, MERGE_REGION_KINDS, WorkflowGraphExecutor;
218875
+ var PLAN_REVIEW_PROVIDER_FAILURE_HOLD_VALUE, PLAN_REVIEW_LEASE_HELD_VALUE, SESSION_CONTENTION_HOLD_VALUE, WORKFLOW_INTERRUPTED_NODE_ID_CONTEXT_KEY, WORKFLOW_INTERRUPTED_NODE_ABORT_KIND_CONTEXT_KEY, WORKFLOW_OPTIONAL_GROUP_CONTEXT_KEY, WORKFLOW_REVIEW_KIND_CONTEXT_KEY, WORKFLOW_NODE_ENGINE_PAUSE_ABORT_KIND, WORKFLOW_DRIFT_PARK_CONTEXT_KEY, WorkflowGraphSuspended, MERGE_REGION_KINDS, WorkflowGraphExecutor;
218783
218876
  var init_workflow_graph_executor = __esm({
218784
218877
  "../engine/src/workflows/workflow-graph-executor.ts"() {
218785
218878
  "use strict";
@@ -218799,6 +218892,7 @@ var init_workflow_graph_executor = __esm({
218799
218892
  WORKFLOW_INTERRUPTED_NODE_ID_CONTEXT_KEY = "workflow:interruptedNodeId";
218800
218893
  WORKFLOW_INTERRUPTED_NODE_ABORT_KIND_CONTEXT_KEY = "workflow:interruptedNodeAbortKind";
218801
218894
  WORKFLOW_OPTIONAL_GROUP_CONTEXT_KEY = "workflow:optionalGroupActive";
218895
+ WORKFLOW_REVIEW_KIND_CONTEXT_KEY = "workflow:reviewKind";
218802
218896
  WORKFLOW_NODE_ENGINE_PAUSE_ABORT_KIND = "engine-pause";
218803
218897
  WORKFLOW_DRIFT_PARK_CONTEXT_KEY = "workflow:driftPark";
218804
218898
  WorkflowGraphSuspended = class extends Error {
@@ -219047,6 +219141,7 @@ var init_workflow_graph_executor = __esm({
219047
219141
  phase: stepPhase,
219048
219142
  status: "pending",
219049
219143
  source: "optional-group",
219144
+ ...this.workflowReviewKind(node) ? { reviewKind: this.workflowReviewKind(node) } : {},
219050
219145
  startedAt: stepStartedAt,
219051
219146
  // U3/KTD-4: stamp the lease owner so a concurrent/crashed re-entry
219052
219147
  // adopts this pending gate instead of dispatching a second reviewer.
@@ -219066,7 +219161,8 @@ var init_workflow_graph_executor = __esm({
219066
219161
  runTemplateNode: (tNode, sig, contextOverride) => {
219067
219162
  const optionalGroupContext = {
219068
219163
  ...contextOverride ?? context,
219069
- [WORKFLOW_OPTIONAL_GROUP_CONTEXT_KEY]: node.id
219164
+ [WORKFLOW_OPTIONAL_GROUP_CONTEXT_KEY]: node.id,
219165
+ ...this.workflowReviewKind(node) ? { [WORKFLOW_REVIEW_KIND_CONTEXT_KEY]: this.workflowReviewKind(node) } : {}
219070
219166
  };
219071
219167
  return this.executeNodeWithRetries(tNode, task, settings, optionalGroupContext, ir2, sig, false);
219072
219168
  },
@@ -219084,6 +219180,7 @@ var init_workflow_graph_executor = __esm({
219084
219180
  const exitContextPatch = exitResult?.contextPatch;
219085
219181
  let stepOutput = typeof exitContextPatch?.output === "string" ? exitContextPatch.output : void 0;
219086
219182
  const stepNotes = typeof exitContextPatch?.notes === "string" ? exitContextPatch.notes : void 0;
219183
+ const stepFindings = this.workflowReviewKind(node) && Array.isArray(exitContextPatch?.findings) ? exitContextPatch.findings : void 0;
219087
219184
  if (stepStatus === "failed" && !verdict && stepOutput === void 0 && stepNotes === void 0) {
219088
219185
  const lastVisited = groupResult.visitedNodeIds[groupResult.visitedNodeIds.length - 1];
219089
219186
  const templateNodeId = lastVisited?.includes("::") ? lastVisited.slice(lastVisited.indexOf("::") + 2) : void 0;
@@ -219101,9 +219198,11 @@ var init_workflow_graph_executor = __esm({
219101
219198
  phase: stepPhase,
219102
219199
  source: "optional-group",
219103
219200
  status: stepStatus,
219201
+ ...this.workflowReviewKind(node) ? { reviewKind: this.workflowReviewKind(node) } : {},
219104
219202
  ...verdict ? { verdict } : {},
219105
219203
  ...stepOutput !== void 0 ? { output: stepOutput } : {},
219106
219204
  ...stepNotes !== void 0 ? { notes: stepNotes } : {},
219205
+ ...stepFindings?.length ? { findings: stepFindings } : {},
219107
219206
  startedAt: stepStartedAt,
219108
219207
  completedAt: (/* @__PURE__ */ new Date()).toISOString()
219109
219208
  });
@@ -219563,9 +219662,15 @@ var init_workflow_graph_executor = __esm({
219563
219662
  }
219564
219663
  return failureResult;
219565
219664
  }
219665
+ /** FNXC:WorkflowReviewKind 2026-08-05-02:31: Persist only the declared closed
219666
+ * marker; execution never derives review semantics from a label, verdict, or gate mode. */
219667
+ workflowReviewKind(node) {
219668
+ return node.config?.reviewKind === "plan" || node.config?.reviewKind === "code" ? node.config.reviewKind : void 0;
219669
+ }
219566
219670
  shouldRecordNodeProgress(node) {
219567
219671
  const skillName = typeof node.config?.skillName === "string" ? node.config.skillName.trim() : "";
219568
- return skillName.length > 0 && (node.kind === "prompt" || node.kind === "gate");
219672
+ const markedReview = this.workflowReviewKind(node) !== void 0;
219673
+ return markedReview && (node.kind === "prompt" || node.kind === "gate" || node.kind === "script") || skillName.length > 0 && (node.kind === "prompt" || node.kind === "gate");
219569
219674
  }
219570
219675
  workflowNodeProgressName(node) {
219571
219676
  const configuredName = typeof node.config?.name === "string" ? node.config.name.trim() : "";
@@ -219579,6 +219684,7 @@ var init_workflow_graph_executor = __esm({
219579
219684
  phase: node.config?.phase === "post-merge" ? "post-merge" : "pre-merge",
219580
219685
  source: "node",
219581
219686
  status: "pending",
219687
+ ...this.workflowReviewKind(node) ? { reviewKind: this.workflowReviewKind(node) } : {},
219582
219688
  startedAt
219583
219689
  };
219584
219690
  await this.recordOptionalGroupStepResult(taskId, result2);
@@ -219589,6 +219695,7 @@ var init_workflow_graph_executor = __esm({
219589
219695
  const contextPatch = nodeResult.contextPatch ?? {};
219590
219696
  let output = typeof contextPatch.output === "string" ? contextPatch.output : void 0;
219591
219697
  const notes = typeof contextPatch.notes === "string" ? contextPatch.notes : void 0;
219698
+ const findings = this.workflowReviewKind(node) && Array.isArray(contextPatch.findings) ? contextPatch.findings : void 0;
219592
219699
  if (status === "failed" && output === void 0 && notes === void 0) {
219593
219700
  output = this.synthesizeNonVerdictFailureOutput({
219594
219701
  stepLabel: this.workflowNodeProgressName(node),
@@ -219603,8 +219710,10 @@ var init_workflow_graph_executor = __esm({
219603
219710
  phase: started?.phase ?? (node.config?.phase === "post-merge" ? "post-merge" : "pre-merge"),
219604
219711
  source: "node",
219605
219712
  status,
219713
+ ...this.workflowReviewKind(node) ? { reviewKind: this.workflowReviewKind(node) } : {},
219606
219714
  ...output !== void 0 ? { output } : {},
219607
219715
  ...notes !== void 0 ? { notes } : {},
219716
+ ...findings?.length ? { findings } : {},
219608
219717
  startedAt: started?.startedAt ?? (/* @__PURE__ */ new Date()).toISOString(),
219609
219718
  completedAt: (/* @__PURE__ */ new Date()).toISOString()
219610
219719
  });
@@ -224885,9 +224994,11 @@ function parseWorkflowStepVerdict(rawOutput) {
224885
224994
  verdict = "REVISE";
224886
224995
  }
224887
224996
  if (!verdict) continue;
224997
+ const findings = normalizeWorkflowReviewFindings(parsed.findings);
224888
224998
  return {
224889
224999
  verdict,
224890
- notes: typeof parsed.notes === "string" ? parsed.notes : ""
225000
+ notes: typeof parsed.notes === "string" ? parsed.notes : "",
225001
+ ...findings ? { findings } : {}
224891
225002
  };
224892
225003
  } catch {
224893
225004
  }
@@ -224919,7 +225030,8 @@ function parseWorkflowStepOutput(rawOutput, options = {}) {
224919
225030
  return {
224920
225031
  output: parsed.notes || "",
224921
225032
  verdict: parsed.verdict,
224922
- notes: parsed.notes
225033
+ notes: parsed.notes,
225034
+ ...parsed.findings ? { findings: parsed.findings } : {}
224923
225035
  };
224924
225036
  }
224925
225037
  const inferred = inferWorkflowStepVerdictFromProse(trimmed);
@@ -231180,6 +231292,7 @@ ${info.feedback}`, revisionKey),
231180
231292
  return this.runCliAgentNode(node, await this.store.getTask(live.id), cfg);
231181
231293
  }
231182
231294
  const optionalGroupId = typeof graphContext?.[WORKFLOW_OPTIONAL_GROUP_CONTEXT_KEY] === "string" ? graphContext[WORKFLOW_OPTIONAL_GROUP_CONTEXT_KEY] : void 0;
231295
+ 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;
231183
231296
  const isCompletionSummaryNode2 = cfg.summaryTarget === "task" || node.id === "completion-summary";
231184
231297
  if (live.executionMode === "fast" && !isCompletionSummaryNode2 && !optionalGroupId && !cfg.seam && (node.kind === "prompt" || node.kind === "script" || node.kind === "gate")) {
231185
231298
  executorLog.debug(`${live.id}: fast mode \u2014 skipping custom graph node '${node.id}'`);
@@ -231328,6 +231441,9 @@ ${prompt}`;
231328
231441
  if (optionalGroupId) {
231329
231442
  step.optionalGroupId = optionalGroupId;
231330
231443
  }
231444
+ if (declaredReviewKind) {
231445
+ step.reviewKind = declaredReviewKind;
231446
+ }
231331
231447
  if (cfg.reviewCanFixInline === true) {
231332
231448
  step.reviewCanFixInline = true;
231333
231449
  }
@@ -231340,7 +231456,11 @@ ${prompt}`;
231340
231456
  executorLog.debug(`${live.id}: graph node '${node.id}' runtime env injected (${injected.pathEntryCount} PATH entries, ${injected.injectedKeyCount} env keys)`);
231341
231457
  }
231342
231458
  const unattended = this.graphUnattendedRuns.has(live.id);
231343
- const outcome = mode === "script" ? await this.executeScriptWorkflowStep(live, step, worktreePath, settings, nodeEnv) : await this.executeWorkflowStep(live, step, worktreePath, settings, nodeEnv, { unattended });
231459
+ let outcome = mode === "script" ? await this.executeScriptWorkflowStep(live, step, worktreePath, settings, nodeEnv) : await this.executeWorkflowStep(live, step, worktreePath, settings, nodeEnv, { unattended });
231460
+ if (declaredReviewKind && typeof outcome.output === "string") {
231461
+ const parsedReviewOutput = parseWorkflowStepOutput(outcome.output, { requireVerdict: false });
231462
+ if (parsedReviewOutput.findings?.length) outcome = { ...outcome, findings: parsedReviewOutput.findings };
231463
+ }
231344
231464
  const awaitQuestion = parseAwaitInputSentinel(outcome.output);
231345
231465
  if (awaitQuestion) {
231346
231466
  await this.store.logEntry(
@@ -231363,6 +231483,8 @@ ${prompt}`;
231363
231483
  const contextPatch = {};
231364
231484
  if (typeof stepOutput === "string") contextPatch.output = stepOutput;
231365
231485
  if (typeof stepNotes === "string" && stepNotes) contextPatch.notes = stepNotes;
231486
+ const stepFindings = outcome.findings;
231487
+ if (stepFindings?.length) contextPatch.findings = stepFindings;
231366
231488
  if (cfg.summaryTarget === "task" && typeof stepOutput === "string" && stepOutput.trim()) {
231367
231489
  contextPatch.summary = stepOutput.trim();
231368
231490
  }
@@ -237865,13 +237987,14 @@ CRITICAL SCOPING RULES \u2014 read before doing anything else:
237865
237987
  const isSkillStep = typeof workflowStep.skillName === "string" && workflowStep.skillName.trim().length > 0;
237866
237988
  const isSummaryProjectionStep = workflowStep.summaryTarget === "task";
237867
237989
  const requireVerdict = !isSummaryProjectionStep && (workflowStep.gateMode === "gate" || !isSkillStep);
237990
+ const reviewFindingsContract = workflowStepMetadata.reviewKind === "plan" || workflowStepMetadata.reviewKind === "code";
237868
237991
  const verdictBlock = requireVerdict ? `
237869
237992
 
237870
237993
  ## Feedback Format
237871
237994
 
237872
237995
  When your review is complete, your final line MUST be a single JSON object (no markdown fences):
237873
237996
 
237874
- {"verdict":"APPROVE|APPROVE_WITH_NOTES|REVISE","notes":"..."}
237997
+ ${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":"..."}'}
237875
237998
 
237876
237999
  Rules:
237877
238000
  - Output exactly one trailing JSON object and stop.
@@ -238181,7 +238304,8 @@ ${detectedQuestion}
238181
238304
  revisionRequested,
238182
238305
  output: parsed.output,
238183
238306
  verdict: parsed.verdict,
238184
- notes: parsed.notes
238307
+ notes: parsed.notes,
238308
+ ...parsed.findings ? { findings: parsed.findings } : {}
238185
238309
  };
238186
238310
  }
238187
238311
  if (parsed.malformed) {
@@ -262283,7 +262407,7 @@ var init_project_engine = __esm({
262283
262407
  *
262284
262408
  * Readable (not private) so tests derive the cap from this single source of truth rather
262285
262409
  * than hardcoding it — the FN-8004 bump broke two suites that had baked in the old `3`. */
262286
- static MAX_AUTO_MERGE_TRANSIENT_RETRIES = 5;
262410
+ static MAX_AUTO_MERGE_TRANSIENT_RETRIES = MAX_AUTO_MERGE_TRANSIENT_RETRIES;
262287
262411
  static MERGE_REQUEST_RETRY_EXHAUSTED_AGE_MS = 30 * 60 * 1e3;
262288
262412
  /** Cap on outer in-review→in-progress bounces caused by deterministic
262289
262413
  * verification failures during auto-merge. After this many failed merges
@@ -292714,10 +292838,32 @@ function parseTaskReviewVerdict(value) {
292714
292838
  return void 0;
292715
292839
  }
292716
292840
  }
292717
- function isCurrentWorkflowReviewResult(result2) {
292718
- return CURRENT_WORKFLOW_REVIEW_STEP_IDS.has(result2.workflowStepId) && CURRENT_WORKFLOW_REVIEW_STATUSES.has(result2.status) && result2.verdict !== void 0 && result2.supersededAt === void 0 && result2.bypassedBy === void 0 && result2.bypassedAt === void 0 && result2.bypassReason === void 0 && result2.bypassedFromStatus === void 0 && result2.bypassedFromVerdict === void 0;
292841
+ function hasCurrentWorkflowReviewIdentity(result2) {
292842
+ return result2.supersededAt === void 0 && result2.supersededReason === void 0 && result2.bypassedBy === void 0 && result2.bypassedAt === void 0 && result2.bypassReason === void 0 && result2.bypassedFromStatus === void 0 && result2.bypassedFromVerdict === void 0;
292843
+ }
292844
+ function isCurrentMarkedWorkflowReviewResult(result2) {
292845
+ return CURRENT_WORKFLOW_REVIEW_STATUSES.has(result2.status) && hasCurrentWorkflowReviewIdentity(result2) && Boolean(result2.output?.trim() || result2.notes?.trim()) && (result2.reviewKind === "plan" || result2.reviewKind === "code") && (result2.source === "node" || result2.source === "optional-group");
292846
+ }
292847
+ async function getDeclaredTopLevelReviewResultSources(task, store) {
292848
+ const resolved = await resolveWorkflowIrForTaskWithProvenance(store, task.id);
292849
+ if (resolved.source !== "selection") return /* @__PURE__ */ new Map();
292850
+ return new Map(resolved.ir.nodes.flatMap((node) => {
292851
+ if (node.kind === "optional-group") return [[node.id, "optional-group"]];
292852
+ if (node.kind === "prompt" || node.kind === "gate" || node.kind === "script") return [[node.id, "node"]];
292853
+ return [];
292854
+ }));
292855
+ }
292856
+ function isCurrentLegacyWorkflowReviewResult(result2) {
292857
+ return CURRENT_WORKFLOW_REVIEW_STEP_IDS.has(result2.workflowStepId) && result2.reviewKind === void 0 && LEGACY_WORKFLOW_REVIEW_STATUSES.has(result2.status) && result2.verdict !== void 0 && hasCurrentWorkflowReviewIdentity(result2);
292858
+ }
292859
+ function getWorkflowReviewKind(result2, declaredTopLevelReviewResultSources) {
292860
+ if (isCurrentMarkedWorkflowReviewResult(result2) && declaredTopLevelReviewResultSources.get(result2.workflowStepId) === result2.source) return result2.reviewKind;
292861
+ if (isCurrentLegacyWorkflowReviewResult(result2)) {
292862
+ return result2.workflowStepId === "plan-review" ? "plan" : "code";
292863
+ }
292864
+ return void 0;
292719
292865
  }
292720
- function buildWorkflowReviewItemId(task, result2) {
292866
+ function buildWorkflowReviewItemId(task, result2, findingId) {
292721
292867
  const identity = JSON.stringify({
292722
292868
  taskId: task.id,
292723
292869
  workflowStepId: result2.workflowStepId,
@@ -292725,22 +292871,44 @@ function buildWorkflowReviewItemId(task, result2) {
292725
292871
  phase: result2.phase,
292726
292872
  status: result2.status,
292727
292873
  verdict: result2.verdict,
292874
+ reviewKind: result2.reviewKind,
292728
292875
  completedAt: result2.completedAt,
292729
292876
  startedAt: result2.startedAt,
292730
292877
  output: result2.output,
292731
- notes: result2.notes
292878
+ notes: result2.notes,
292879
+ findingId
292732
292880
  });
292733
292881
  return `workflow-review-${createHash29("sha256").update(identity).digest("hex").slice(0, 24)}`;
292734
292882
  }
292735
- function buildWorkflowReviewItems(task) {
292736
- return (task.workflowStepResults ?? []).filter(isCurrentWorkflowReviewResult).map((result2) => {
292737
- const reviewType = result2.workflowStepId === "plan-review" ? "plan" : "code";
292738
- const body = result2.output?.trim() || result2.notes?.trim() || "No written feedback was provided by this review step.";
292883
+ async function buildWorkflowReviewItems(task, store) {
292884
+ const declaredTopLevelReviewResultSources = await getDeclaredTopLevelReviewResultSources(task, store);
292885
+ return (task.workflowStepResults ?? []).flatMap((result2) => {
292886
+ const reviewType = getWorkflowReviewKind(result2, declaredTopLevelReviewResultSources);
292887
+ if (!reviewType) return [];
292739
292888
  const timestamp2 = result2.completedAt ?? result2.startedAt ?? task.updatedAt ?? task.createdAt;
292740
- return {
292889
+ if (result2.findings?.length) {
292890
+ return result2.findings.map((finding) => ({
292891
+ itemId: buildWorkflowReviewItemId(task, result2, finding.id),
292892
+ sourceMode: "reviewer-agent",
292893
+ title: finding.title,
292894
+ body: finding.body,
292895
+ author: "reviewer-agent",
292896
+ createdAt: timestamp2,
292897
+ updatedAt: timestamp2,
292898
+ ...finding.filePath ? { filePath: finding.filePath } : {},
292899
+ ...finding.line ? { line: finding.line } : {},
292900
+ ...finding.severity ? { severity: finding.severity } : {},
292901
+ reviewState: result2.verdict,
292902
+ verdict: result2.verdict,
292903
+ reviewType,
292904
+ progressStatus: null
292905
+ }));
292906
+ }
292907
+ const body = result2.output?.trim() || result2.notes?.trim() || "No written feedback was provided by this review step.";
292908
+ return [{
292741
292909
  itemId: buildWorkflowReviewItemId(task, result2),
292742
292910
  sourceMode: "reviewer-agent",
292743
- title: `${result2.workflowStepName || result2.workflowStepId} ${result2.verdict}`,
292911
+ title: `${result2.workflowStepName || result2.workflowStepId} ${result2.verdict ?? result2.status}`,
292744
292912
  body,
292745
292913
  author: "reviewer-agent",
292746
292914
  createdAt: timestamp2,
@@ -292749,7 +292917,7 @@ function buildWorkflowReviewItems(task) {
292749
292917
  verdict: result2.verdict,
292750
292918
  reviewType,
292751
292919
  progressStatus: null
292752
- };
292920
+ }];
292753
292921
  }).sort((a, b3) => Date.parse(b3.createdAt ?? "") - Date.parse(a.createdAt ?? "") || a.itemId.localeCompare(b3.itemId));
292754
292922
  }
292755
292923
  function buildDirectReviewSummary(items) {
@@ -292757,7 +292925,7 @@ function buildDirectReviewSummary(items) {
292757
292925
  return latest ? { summary: latest.title, verdict: latest.verdict } : null;
292758
292926
  }
292759
292927
  async function buildDirectTaskReviewData(task, store) {
292760
- const structuredItems = buildWorkflowReviewItems(task);
292928
+ const structuredItems = await buildWorkflowReviewItems(task, store);
292761
292929
  if (structuredItems.length > 0) {
292762
292930
  return {
292763
292931
  mode: "reviewer-agent",
@@ -294821,6 +294989,17 @@ See \`GET /api/tasks/${task.id}/diff\` for the landed diff being reverted.
294821
294989
  rethrowTaskApiError(err, req.params.id, "Failed to read task verification status");
294822
294990
  }
294823
294991
  });
294992
+ router.get("/tasks/:id/prompt", async (req, res) => {
294993
+ try {
294994
+ const { store: scopedStore } = await getProjectContext2(req);
294995
+ const task = await scopedStore.getTask(req.params.id);
294996
+ res.json(task.prompt === void 0 ? { id: task.id } : { id: task.id, prompt: task.prompt });
294997
+ } catch (err) {
294998
+ if (err instanceof ApiError) throw err;
294999
+ if (isTaskLookupMiss(err)) throw notFound(`Task ${req.params.id} not found`);
295000
+ rethrowAsApiError8(err, "Internal server error");
295001
+ }
295002
+ });
294824
295003
  router.get("/tasks/:id", async (req, res) => {
294825
295004
  try {
294826
295005
  const { store: scopedStore, engine } = await getProjectContext2(req);
@@ -296466,6 +296645,7 @@ See \`GET /api/tasks/${task.id}/diff\` for the landed diff being reverted.
296466
296645
  updatedAt: item.updatedAt ?? void 0,
296467
296646
  path: item.filePath,
296468
296647
  line: item.line,
296648
+ severity: item.severity,
296469
296649
  threadId: item.threadId,
296470
296650
  htmlUrl: item.url,
296471
296651
  state: item.reviewState ?? void 0,
@@ -296508,6 +296688,7 @@ See \`GET /api/tasks/${task.id}/diff\` for the landed diff being reverted.
296508
296688
  author: item.author.login,
296509
296689
  filePath: item.path,
296510
296690
  lineNumber: item.line,
296691
+ severity: item.severity,
296511
296692
  threadId: item.threadId,
296512
296693
  url: item.htmlUrl
296513
296694
  };
@@ -296543,6 +296724,7 @@ See \`GET /api/tasks/${task.id}/diff\` for the landed diff being reverted.
296543
296724
  authorLogin: item.author,
296544
296725
  filePath: item.filePath,
296545
296726
  lineNumber: item.lineNumber,
296727
+ severity: item.severity,
296546
296728
  threadId: item.threadId,
296547
296729
  url: item.url
296548
296730
  },
@@ -296835,7 +297017,7 @@ See \`GET /api/tasks/${task.id}/diff\` for the landed diff being reverted.
296835
297017
  }
296836
297018
  });
296837
297019
  }
296838
- var severityAuditLog33, AWAITING_PLANNING_ENRICH_LIMIT, LEGACY_WIP_LANES3, LEGACY_ARCHIVE_LANES5, REVIEW_BLOCK_RE, REVIEW_VERDICT_RE, REVIEW_STEP_RE, DUPLICATE_STOPWORDS, ARTIFACT_TYPES, ADDRESS_PR_FEEDBACK_PROMPT, __fingerprintCreateLocksForTests, RESET_TASK_FIELDS, RESET_DRIFT_CORRECTION_FIELDS, CURRENT_WORKFLOW_REVIEW_STEP_IDS, CURRENT_WORKFLOW_REVIEW_STATUSES;
297020
+ var severityAuditLog33, AWAITING_PLANNING_ENRICH_LIMIT, LEGACY_WIP_LANES3, LEGACY_ARCHIVE_LANES5, REVIEW_BLOCK_RE, REVIEW_VERDICT_RE, REVIEW_STEP_RE, DUPLICATE_STOPWORDS, ARTIFACT_TYPES, ADDRESS_PR_FEEDBACK_PROMPT, __fingerprintCreateLocksForTests, RESET_TASK_FIELDS, RESET_DRIFT_CORRECTION_FIELDS, CURRENT_WORKFLOW_REVIEW_STEP_IDS, CURRENT_WORKFLOW_REVIEW_STATUSES, LEGACY_WORKFLOW_REVIEW_STATUSES;
296839
297021
  var init_register_task_workflow_routes = __esm({
296840
297022
  "../dashboard/src/routes/register-task-workflow-routes.ts"() {
296841
297023
  "use strict";
@@ -296898,7 +297080,8 @@ var init_register_task_workflow_routes = __esm({
296898
297080
  sessionFile: null
296899
297081
  };
296900
297082
  CURRENT_WORKFLOW_REVIEW_STEP_IDS = /* @__PURE__ */ new Set(["code-review", "plan-review"]);
296901
- CURRENT_WORKFLOW_REVIEW_STATUSES = /* @__PURE__ */ new Set(["passed", "failed", "advisory_failure"]);
297083
+ CURRENT_WORKFLOW_REVIEW_STATUSES = /* @__PURE__ */ new Set(["passed", "failed"]);
297084
+ LEGACY_WORKFLOW_REVIEW_STATUSES = /* @__PURE__ */ new Set(["passed", "failed", "advisory_failure"]);
296902
297085
  }
296903
297086
  });
296904
297087