@runfusion/fusion 0.75.1-beta.0 → 0.75.1-beta.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/bin.js +523 -212
- package/dist/child-process-worker.js +323 -99
- package/dist/client/.vite/manifest.json +249 -249
- package/dist/client/assets/{AgentDetailView-swTdiyeE.js → AgentDetailView-ltOG8f0-.js} +1 -1
- package/dist/client/assets/{AgentPermissionPolicyEditor-Dq-KuDaA.js → AgentPermissionPolicyEditor-CC_rrbbG.js} +1 -1
- package/dist/client/assets/{AgentsView-D1GEHyH0.js → AgentsView-DWUdi27N.js} +3 -3
- package/dist/client/assets/{ChatView-D1czGvvT.js → ChatView-mFKfoegS.js} +1 -1
- package/dist/client/assets/{CommandCenter-DWZ7OTOB.js → CommandCenter-C2wvDq_N.js} +1 -1
- package/dist/client/assets/{DevServerView-CAcUKOXd.js → DevServerView-0MWEJM7o.js} +1 -1
- package/dist/client/assets/{DirectoryPicker-DVUl5jOk.js → DirectoryPicker-BjMPhCdh.js} +1 -1
- package/dist/client/assets/{DocumentsView-CnexOxtr.js → DocumentsView-ckH3rjmd.js} +1 -1
- package/dist/client/assets/{EvalsView-rTupzcDf.js → EvalsView-NyA644VF.js} +1 -1
- package/dist/client/assets/{ExperimentalAgentOnboardingModal-CXE6Bj3V.js → ExperimentalAgentOnboardingModal-D0ZfbBRN.js} +1 -1
- package/dist/client/assets/{GoalsView-CjHwnnIi.js → GoalsView-Ctt5P_yo.js} +1 -1
- package/dist/client/assets/{InsightsView-C3XocYP-.js → InsightsView-NDp0bdNB.js} +1 -1
- package/dist/client/assets/{MemoryView-CsNHcx_d.js → MemoryView-BKbA4Bm4.js} +1 -1
- package/dist/client/assets/{PiExtensionsManager-BPTPoZBI.js → PiExtensionsManager-V3AFWMHm.js} +1 -1
- package/dist/client/assets/{PluginManager-CXadn8JE.js → PluginManager-D88G_TZh.js} +1 -1
- package/dist/client/assets/{PullRequestView-CC03CtZJ.js → PullRequestView-DQQmFFYC.js} +1 -1
- package/dist/client/assets/{ReportModal-Ci0na1eI.js → ReportModal-Clh1S8RF.js} +1 -1
- package/dist/client/assets/{ResearchView-DGWyAo8p.js → ResearchView-kxvq4zp5.js} +1 -1
- package/dist/client/assets/SecretsView-Dm0Vk8SA.js +1 -0
- package/dist/client/assets/{SessionTerminal-B22RlQeo.js → SessionTerminal-DJO4JTEX.js} +1 -1
- package/dist/client/assets/{SettingsModal-BkgBQZ19.js → SettingsModal-BN5mL_ym.js} +1 -1
- package/dist/client/assets/SettingsModal-CTf5voEW.js +21 -0
- package/dist/client/assets/{SettingsTextareaRow-0LXwMUQR.js → SettingsTextareaRow-CvKszqO-.js} +1 -1
- package/dist/client/assets/{SetupWizardModal-CyW3AlRO.js → SetupWizardModal-D5hVZh1G.js} +2 -2
- package/dist/client/assets/{SkillsView-3oqB1ubq.js → SkillsView-DQVz21w2.js} +1 -1
- package/dist/client/assets/{WorkflowNodeEditor-CfUK_xs1.js → WorkflowNodeEditor-CpKIi6Tb.js} +3 -3
- package/dist/client/assets/{agent-import-generation-C7-Cmo3M.js → agent-import-generation-DGUNF3nd.js} +1 -1
- package/dist/client/assets/{architectureDiagram-3BPJPVTR-BQ7ZN1r_.js → architectureDiagram-3BPJPVTR-CV83wubP.js} +1 -1
- package/dist/client/assets/{blockDiagram-GPEHLZMM-Debw1Xka.js → blockDiagram-GPEHLZMM-CNQsA3dD.js} +1 -1
- package/dist/client/assets/{c4Diagram-AAUBKEIU-CiU212s3.js → c4Diagram-AAUBKEIU-CXe7ii5H.js} +1 -1
- package/dist/client/assets/channel-DoX862zl.js +1 -0
- package/dist/client/assets/{chunk-2J33WTMH-BUJQ86zb.js → chunk-2J33WTMH-BS6W9bWv.js} +1 -1
- package/dist/client/assets/{chunk-4BX2VUAB-C6GdEm1z.js → chunk-4BX2VUAB-DbehRhdW.js} +1 -1
- package/dist/client/assets/{chunk-55IACEB6-CImvrxQL.js → chunk-55IACEB6-DMI5SR42.js} +1 -1
- package/dist/client/assets/{chunk-727SXJPM-Dt-A3pkM.js → chunk-727SXJPM-D1d-GBVj.js} +1 -1
- package/dist/client/assets/{chunk-AQP2D5EJ-DDj4oXTx.js → chunk-AQP2D5EJ-BzO6K3Uf.js} +1 -1
- package/dist/client/assets/{chunk-FMBD7UC4-BD4r1ukC.js → chunk-FMBD7UC4-BVRLV3lK.js} +1 -1
- package/dist/client/assets/{chunk-ND2GUHAM-DFSteefi.js → chunk-ND2GUHAM-DdWByQfn.js} +1 -1
- package/dist/client/assets/{chunk-QZHKN3VN-t2zF8tjB.js → chunk-QZHKN3VN-1ia12cRV.js} +1 -1
- package/dist/client/assets/classDiagram-4FO5ZUOK-mb2IeAGN.js +1 -0
- package/dist/client/assets/classDiagram-v2-Q7XG4LA2-mb2IeAGN.js +1 -0
- package/dist/client/assets/{cose-bilkent-S5V4N54A-tkvDkXQA.js → cose-bilkent-S5V4N54A-BPDT41HR.js} +1 -1
- package/dist/client/assets/{dagre-BM42HDAG-DD2rW7wo.js → dagre-BM42HDAG-Cktn26EH.js} +1 -1
- package/dist/client/assets/{dashboard-view-B1vduc8x.js → dashboard-view-8o0EUcRX.js} +1 -1
- package/dist/client/assets/{dashboard-view-CciuDS5A.js → dashboard-view-CkCerlVJ.js} +1 -1
- package/dist/client/assets/{dashboard-view-DruCruAc.js → dashboard-view-D_HC4lnr.js} +1 -1
- package/dist/client/assets/{dashboard-view-C7jJ1lA4.js → dashboard-view-zd8GxC49.js} +1 -1
- package/dist/client/assets/{diagram-2AECGRRQ-DASuxGNc.js → diagram-2AECGRRQ-But4Lvf-.js} +1 -1
- package/dist/client/assets/{diagram-5GNKFQAL-f9WtiIUk.js → diagram-5GNKFQAL-2bpQAGFc.js} +1 -1
- package/dist/client/assets/{diagram-KO2AKTUF-QivICYCd.js → diagram-KO2AKTUF-Ckn9Qd1u.js} +1 -1
- package/dist/client/assets/{diagram-LMA3HP47-CMwcDFHR.js → diagram-LMA3HP47-Bjn8h84N.js} +1 -1
- package/dist/client/assets/{diagram-OG6HWLK6-C-vPMeHZ.js → diagram-OG6HWLK6-BI6541hx.js} +1 -1
- package/dist/client/assets/{erDiagram-TEJ5UH35-CxGtk9vj.js → erDiagram-TEJ5UH35-BqMiwHiM.js} +1 -1
- package/dist/client/assets/{flowDiagram-I6XJVG4X-qBZRZoV1.js → flowDiagram-I6XJVG4X-Q_SAB10M.js} +1 -1
- package/dist/client/assets/{folder-open-Dysi9Zzg.js → folder-open-CHiAJbTN.js} +1 -1
- package/dist/client/assets/{ganttDiagram-6RSMTGT7-DAaXroiu.js → ganttDiagram-6RSMTGT7-CqWFSfPF.js} +1 -1
- package/dist/client/assets/{gitGraphDiagram-PVQCEYII-DNUNQot_.js → gitGraphDiagram-PVQCEYII-B1o4uEjO.js} +1 -1
- package/dist/client/assets/index-Bdw5eNKV.css +1 -0
- package/dist/client/assets/index-kwRywd9M.js +2767 -0
- package/dist/client/assets/{infoDiagram-5YYISTIA-BSJmWLwh.js → infoDiagram-5YYISTIA-BzaMa0c2.js} +1 -1
- package/dist/client/assets/{ishikawaDiagram-YF4QCWOH-AyVKvoDq.js → ishikawaDiagram-YF4QCWOH-I38yge00.js} +1 -1
- package/dist/client/assets/{journeyDiagram-JHISSGLW-972txzzd.js → journeyDiagram-JHISSGLW-WVQSgx4P.js} +1 -1
- package/dist/client/assets/{kanban-definition-UN3LZRKU-1xiNvIqK.js → kanban-definition-UN3LZRKU-BWtPQ3AH.js} +1 -1
- package/dist/client/assets/{mermaid.core-oeDA8jOm.js → mermaid.core-D1hnBtbi.js} +4 -4
- package/dist/client/assets/{mindmap-definition-RKZ34NQL-DcktmI8s.js → mindmap-definition-RKZ34NQL-DqiSLkW7.js} +1 -1
- package/dist/client/assets/{pieDiagram-4H26LBE5-ekBwLt-O.js → pieDiagram-4H26LBE5-CcgwbdIN.js} +1 -1
- package/dist/client/assets/{puzzle-I6pr6ZrW.js → puzzle-BhXUaTQl.js} +1 -1
- package/dist/client/assets/{quadrantDiagram-W4KKPZXB-D_wjTsvn.js → quadrantDiagram-W4KKPZXB-MsqQQfDN.js} +1 -1
- package/dist/client/assets/{requirementDiagram-4Y6WPE33-CKGHyiJ1.js → requirementDiagram-4Y6WPE33-CILGjMMA.js} +1 -1
- package/dist/client/assets/{sankeyDiagram-5OEKKPKP-CtiZFNk5.js → sankeyDiagram-5OEKKPKP-c14fWbtR.js} +1 -1
- package/dist/client/assets/{sequenceDiagram-3UESZ5HK-B6OMrfGk.js → sequenceDiagram-3UESZ5HK-BD5bxMom.js} +1 -1
- package/dist/client/assets/{shield-alert-CVMmviMG.js → shield-alert-C9W2WywS.js} +1 -1
- package/dist/client/assets/{standing-instructions-template-Cg1a3tjW.js → standing-instructions-template-CJdbDSyg.js} +1 -1
- package/dist/client/assets/{stateDiagram-AJRCARHV-CyTY2M_7.js → stateDiagram-AJRCARHV-CZ5YqeOn.js} +1 -1
- package/dist/client/assets/{stateDiagram-v2-BHNVJYJU-NWPCCiqC.js → stateDiagram-v2-BHNVJYJU-mdMN-PNX.js} +1 -1
- package/dist/client/assets/{timeline-definition-PNZ67QCA-DU4TVYpw.js → timeline-definition-PNZ67QCA-DHKay9uj.js} +1 -1
- package/dist/client/assets/{upload-VCzeCiFH.js → upload-DxEwowRU.js} +1 -1
- package/dist/client/assets/{users-DB3knZtm.js → users-mNFgW_Zd.js} +1 -1
- package/dist/client/assets/{vennDiagram-CIIHVFJN-DX0Ppll6.js → vennDiagram-CIIHVFJN-X29K9hWM.js} +1 -1
- package/dist/client/assets/{wardley-L42UT6IY-CBoWxpDv.js → wardley-L42UT6IY-yEoTrfmN.js} +1 -1
- package/dist/client/assets/{wardleyDiagram-YWT4CUSO-B_eVqdhC.js → wardleyDiagram-YWT4CUSO-ny4k3kG0.js} +1 -1
- package/dist/client/assets/{xychartDiagram-2RQKCTM6-DeZzEX0f.js → xychartDiagram-2RQKCTM6-DC8ODtRJ.js} +1 -1
- package/dist/client/index.html +2 -2
- package/dist/client/version.json +1 -1
- package/dist/droid-cli/package.json +1 -1
- package/dist/extension.js +411 -155
- package/dist/pi-claude-cli/package.json +1 -1
- package/dist/plugin-sdk/index.d.ts +159 -141
- package/dist/plugin-sdk/index.js +19 -0
- package/dist/plugins/fusion-plugin-acp-runtime/bundled.js +19 -0
- package/dist/plugins/fusion-plugin-acp-runtime/package.json +1 -1
- package/dist/plugins/fusion-plugin-claude-runtime/bundled.js +19 -0
- package/dist/plugins/fusion-plugin-claude-runtime/package.json +1 -1
- package/dist/plugins/fusion-plugin-cli-printing-press/bundled.js +19 -0
- package/dist/plugins/fusion-plugin-cli-printing-press/package.json +1 -1
- package/dist/plugins/fusion-plugin-compound-engineering/bundled.js +19 -0
- package/dist/plugins/fusion-plugin-compound-engineering/package.json +1 -1
- package/dist/plugins/fusion-plugin-cursor-runtime/bundled.js +19 -0
- package/dist/plugins/fusion-plugin-cursor-runtime/package.json +1 -1
- package/dist/plugins/fusion-plugin-dependency-graph/bundled.js +19 -0
- package/dist/plugins/fusion-plugin-dependency-graph/package.json +1 -1
- package/dist/plugins/fusion-plugin-droid-runtime/bundled.js +19 -0
- package/dist/plugins/fusion-plugin-droid-runtime/package.json +1 -1
- package/dist/plugins/fusion-plugin-grok-runtime/bundled.js +19 -0
- package/dist/plugins/fusion-plugin-grok-runtime/package.json +1 -1
- package/dist/plugins/fusion-plugin-hermes-runtime/bundled.js +19 -0
- package/dist/plugins/fusion-plugin-hermes-runtime/package.json +1 -1
- package/dist/plugins/fusion-plugin-linear-import/bundled.js +19 -0
- package/dist/plugins/fusion-plugin-linear-import/package.json +1 -1
- package/dist/plugins/fusion-plugin-omp-runtime/bundled.js +19 -0
- package/dist/plugins/fusion-plugin-omp-runtime/package.json +1 -1
- package/dist/plugins/fusion-plugin-openclaw-runtime/bundled.js +19 -0
- package/dist/plugins/fusion-plugin-openclaw-runtime/package.json +1 -1
- package/dist/plugins/fusion-plugin-paperclip-runtime/bundled.js +19 -0
- package/dist/plugins/fusion-plugin-paperclip-runtime/package.json +1 -1
- package/dist/plugins/fusion-plugin-quality/bundled.js +19 -0
- package/dist/plugins/fusion-plugin-quality/package.json +1 -1
- package/dist/plugins/fusion-plugin-reports/bundled.js +19 -0
- package/dist/plugins/fusion-plugin-reports/package.json +1 -1
- package/dist/plugins/fusion-plugin-roadmap/bundled.js +19 -0
- package/dist/plugins/fusion-plugin-roadmap/package.json +1 -1
- package/dist/plugins/fusion-plugin-todos/bundled.js +19 -0
- package/dist/plugins/fusion-plugin-todos/package.json +1 -1
- package/dist/plugins/fusion-plugin-whatsapp-chat/bundled.js +19 -0
- package/dist/plugins/fusion-plugin-whatsapp-chat/package.json +1 -1
- package/package.json +1 -1
- package/dist/client/assets/SecretsView-Cv2IuqFO.js +0 -1
- package/dist/client/assets/SettingsModal-CPRpRK71.js +0 -21
- package/dist/client/assets/channel-D-O4mRXx.js +0 -1
- package/dist/client/assets/classDiagram-4FO5ZUOK-BhHdSetA.js +0 -1
- package/dist/client/assets/classDiagram-v2-Q7XG4LA2-BhHdSetA.js +0 -1
- package/dist/client/assets/index-D0QgE5F5.js +0 -2767
- package/dist/client/assets/index-DHk_s6rA.css +0 -1
|
@@ -1181,6 +1181,9 @@ function isFusionSiblingBranch(branch) {
|
|
|
1181
1181
|
function resolveEffectiveAutoMerge(task, settings) {
|
|
1182
1182
|
return task.autoMerge ?? settings.autoMerge;
|
|
1183
1183
|
}
|
|
1184
|
+
function hasUserAutoMergeHold(task) {
|
|
1185
|
+
return task.autoMerge === false && task.autoMergeProvenance === "user";
|
|
1186
|
+
}
|
|
1184
1187
|
function allowsAutoMergeProcessing(task, settings) {
|
|
1185
1188
|
return (settings.autoMerge !== false || task.autoMerge === true) && !taskHasManualOpenPullRequest(task);
|
|
1186
1189
|
}
|
|
@@ -10046,6 +10049,7 @@ function validateForeach(node, topLevelNodeIds, columnIds) {
|
|
|
10046
10049
|
);
|
|
10047
10050
|
}
|
|
10048
10051
|
for (const inner of templateNodes) {
|
|
10052
|
+
validateReviewKind(inner, "nested");
|
|
10049
10053
|
if (inner.kind === "foreach" || inner.kind === "loop") {
|
|
10050
10054
|
throw new WorkflowIrError(
|
|
10051
10055
|
`foreach node '${node.id}' template may not contain nested loop/foreach ('${inner.id}')`
|
|
@@ -10164,6 +10168,7 @@ function validateLoop(node, topLevelNodeIds, columnIds) {
|
|
|
10164
10168
|
);
|
|
10165
10169
|
}
|
|
10166
10170
|
for (const inner of templateNodes) {
|
|
10171
|
+
validateReviewKind(inner, "nested");
|
|
10167
10172
|
if (inner.kind === "loop" || inner.kind === "foreach") {
|
|
10168
10173
|
throw new WorkflowIrError(
|
|
10169
10174
|
`loop node '${node.id}' template may not contain nested loop/foreach ('${inner.id}')`
|
|
@@ -10257,6 +10262,7 @@ function validateOptionalGroup(node, topLevelNodeIds, columnIds) {
|
|
|
10257
10262
|
throw new WorkflowIrError(`optional-group node '${node.id}' template has duplicate node ids`);
|
|
10258
10263
|
}
|
|
10259
10264
|
for (const inner of templateNodes) {
|
|
10265
|
+
validateReviewKind(inner, "nested");
|
|
10260
10266
|
if (inner.kind === "loop" || inner.kind === "foreach" || inner.kind === "optional-group") {
|
|
10261
10267
|
throw new WorkflowIrError(
|
|
10262
10268
|
`optional-group node '${node.id}' template may not contain nested loop/foreach/optional-group ('${inner.id}')`
|
|
@@ -10928,6 +10934,19 @@ function validateColumnRecovery(column) {
|
|
|
10928
10934
|
);
|
|
10929
10935
|
}
|
|
10930
10936
|
}
|
|
10937
|
+
function validateReviewKind(node, placement) {
|
|
10938
|
+
const value = node.config?.reviewKind;
|
|
10939
|
+
if (value === void 0) return;
|
|
10940
|
+
if (value !== "plan" && value !== "code") {
|
|
10941
|
+
throw new WorkflowIrError(`Workflow node '${node.id}' has invalid reviewKind '${String(value)}'; expected 'plan' or 'code'`);
|
|
10942
|
+
}
|
|
10943
|
+
if (placement === "nested") {
|
|
10944
|
+
throw new WorkflowIrError(`Workflow node '${node.id}' has reviewKind in an unsupported nested template placement`);
|
|
10945
|
+
}
|
|
10946
|
+
if (node.kind !== "prompt" && node.kind !== "gate" && node.kind !== "script" && node.kind !== "optional-group") {
|
|
10947
|
+
throw new WorkflowIrError(`Workflow node '${node.id}' has reviewKind on unsupported node kind '${node.kind}'`);
|
|
10948
|
+
}
|
|
10949
|
+
}
|
|
10931
10950
|
function validateV2(ir2) {
|
|
10932
10951
|
validateColumns(ir2);
|
|
10933
10952
|
for (const [index2, column] of ir2.columns.entries()) {
|
|
@@ -10950,6 +10969,7 @@ function validateV2(ir2) {
|
|
|
10950
10969
|
}
|
|
10951
10970
|
const nodesById = new Map(ir2.nodes.map((n) => [n.id, n]));
|
|
10952
10971
|
for (const node of ir2.nodes) {
|
|
10972
|
+
validateReviewKind(node, "top-level");
|
|
10953
10973
|
validateExtensionMetadata(`Workflow node '${node.id}'`, node.extensions);
|
|
10954
10974
|
if (node.column !== void 0 && !columnIds.has(node.column)) {
|
|
10955
10975
|
throw new WorkflowIrError(
|
|
@@ -13413,6 +13433,7 @@ function codeReviewOptionalGroupNode(column, options = {}) {
|
|
|
13413
13433
|
column,
|
|
13414
13434
|
config: {
|
|
13415
13435
|
name: CODE_REVIEW_NAME,
|
|
13436
|
+
reviewKind: "code",
|
|
13416
13437
|
// Default-ON: runs for every coding task by default, but operators can toggle it
|
|
13417
13438
|
// off per task (remove `code-review` from enabledWorkflowSteps).
|
|
13418
13439
|
defaultOn: options.defaultOn ?? true,
|
|
@@ -13558,6 +13579,7 @@ function planReviewOptionalGroupNode(column, options = {}) {
|
|
|
13558
13579
|
...column ? { column } : {},
|
|
13559
13580
|
config: {
|
|
13560
13581
|
name: PLAN_REVIEW_NAME,
|
|
13582
|
+
reviewKind: "plan",
|
|
13561
13583
|
defaultOn: options.defaultOn ?? true,
|
|
13562
13584
|
/*
|
|
13563
13585
|
* FNXC:WorkflowRemediation 2026-06-29-12:14:
|
|
@@ -63402,7 +63424,7 @@ function rowToTask(row) {
|
|
|
63402
63424
|
executionStartBranch: row.executionStartBranch || void 0,
|
|
63403
63425
|
branch: row.branch || void 0,
|
|
63404
63426
|
autoMerge: row.autoMerge === null ? void 0 : row.autoMerge === 1,
|
|
63405
|
-
autoMergeProvenance: row.autoMergeProvenance === "user" || row.autoMergeProvenance === "legacy-stamp" ? row.autoMergeProvenance : void 0,
|
|
63427
|
+
autoMergeProvenance: row.autoMergeProvenance === "user" || row.autoMergeProvenance === "mission" || row.autoMergeProvenance === "legacy-stamp" ? row.autoMergeProvenance : void 0,
|
|
63406
63428
|
baseCommitSha: row.baseCommitSha || void 0,
|
|
63407
63429
|
scopeOverride: row.scopeOverride ? true : void 0,
|
|
63408
63430
|
scopeOverrideReason: row.scopeOverrideReason || void 0,
|
|
@@ -72918,10 +72940,10 @@ ${feature.acceptanceCriteria}`);
|
|
|
72918
72940
|
Apply the selected Missions header workflow atomically during TaskStore.createTask so newly triaged features land in the intended workflow lane. Duplicate-guard reuses skip this create path, preserving existing duplicate tasks without workflow mutation.
|
|
72919
72941
|
*/
|
|
72920
72942
|
/*
|
|
72921
|
-
FNXC:MissionAutoMerge 2026-
|
|
72922
|
-
An autoMerge:false mission stamps each newly triaged task so its shared branch produces one PR instead of per-task auto-merges. Duplicate reuse intentionally bypasses this create-only override.
|
|
72943
|
+
FNXC:MissionAutoMerge 2026-08-05-22:50:
|
|
72944
|
+
An autoMerge:false mission stamps each newly triaged task as mission policy so its shared branch produces one PR instead of per-task auto-merges. Duplicate reuse intentionally bypasses this create-only override; policy must not impersonate an operator manual-hold choice.
|
|
72923
72945
|
*/
|
|
72924
|
-
...mission?.autoMerge === false ? { autoMerge: false } : {},
|
|
72946
|
+
...mission?.autoMerge === false ? { autoMerge: false, autoMergeProvenance: "mission" } : {},
|
|
72925
72947
|
// FNXC:MissionTaskPrefix 2026-07-26-12:00: thread the mission's optional taskPrefix into TaskCreateInput for distributed id minting.
|
|
72926
72948
|
...mission?.taskPrefix ? { taskPrefix: mission.taskPrefix } : {},
|
|
72927
72949
|
...branchOptions?.workflowId !== void 0 ? { workflowId: branchOptions.workflowId } : {}
|
|
@@ -76099,10 +76121,10 @@ ${feature.acceptanceCriteria}`);
|
|
|
76099
76121
|
}
|
|
76100
76122
|
} : {},
|
|
76101
76123
|
/*
|
|
76102
|
-
FNXC:MissionAutoMerge 2026-
|
|
76103
|
-
An autoMerge:false mission stamps each newly triaged task so its shared branch produces one PR instead of per-task auto-merges. Duplicate reuse intentionally bypasses this create-only override.
|
|
76124
|
+
FNXC:MissionAutoMerge 2026-08-05-22:50:
|
|
76125
|
+
An autoMerge:false mission stamps each newly triaged task as mission policy so its shared branch produces one PR instead of per-task auto-merges. Duplicate reuse intentionally bypasses this create-only override; policy must not impersonate an operator manual-hold choice.
|
|
76104
76126
|
*/
|
|
76105
|
-
...mission?.autoMerge === false ? { autoMerge: false } : {},
|
|
76127
|
+
...mission?.autoMerge === false ? { autoMerge: false, autoMergeProvenance: "mission" } : {},
|
|
76106
76128
|
// FNXC:MissionTaskPrefix 2026-07-26-12:00: thread the mission's optional taskPrefix into TaskCreateInput so the distributed allocator mints ERR-N (etc.) instead of the project prefix.
|
|
76107
76129
|
...mission?.taskPrefix ? { taskPrefix: mission.taskPrefix } : {},
|
|
76108
76130
|
...branchOptions?.workflowId !== void 0 ? { workflowId: branchOptions.workflowId } : {}
|
|
@@ -86453,7 +86475,9 @@ async function _createTaskInternalBackendImpl(store, input, title, resolvedWorkf
|
|
|
86453
86475
|
sourceMetadata: withTaskBranchContextInSourceMetadata(input.source?.sourceMetadata, input.branchContext),
|
|
86454
86476
|
branchContext: input.branchContext,
|
|
86455
86477
|
autoMerge: input.autoMerge,
|
|
86456
|
-
|
|
86478
|
+
// FNXC:SharedBranchMemberHold 2026-08-05-22:50: trusted mission creation
|
|
86479
|
+
// preserves policy provenance; operator/API create requests retain user provenance.
|
|
86480
|
+
autoMergeProvenance: input.autoMerge === void 0 ? void 0 : input.autoMergeProvenance ?? "user",
|
|
86457
86481
|
// FNXC:CodingIdeasWorkflow 2026-07-05-19:45: land the task in its
|
|
86458
86482
|
// workflow's manual intake column (e.g. Coding (Ideas) → "ideas") when
|
|
86459
86483
|
// no explicit column is given (main FN-7591 parity).
|
|
@@ -127158,6 +127182,36 @@ var init_async_knowledge = __esm({
|
|
|
127158
127182
|
});
|
|
127159
127183
|
|
|
127160
127184
|
// ../core/src/workflows/workflow-step-results.ts
|
|
127185
|
+
function normalizeWorkflowReviewFindings(raw) {
|
|
127186
|
+
if (!Array.isArray(raw)) return void 0;
|
|
127187
|
+
const normalized = [];
|
|
127188
|
+
const usedIds = /* @__PURE__ */ new Set();
|
|
127189
|
+
for (const candidate of raw) {
|
|
127190
|
+
if (normalized.length >= MAX_WORKFLOW_REVIEW_FINDINGS || !candidate || typeof candidate !== "object") continue;
|
|
127191
|
+
const value = candidate;
|
|
127192
|
+
const title = boundedTrimmedString(value.title, MAX_FINDING_TITLE_LENGTH);
|
|
127193
|
+
const body = boundedTrimmedString(value.body, MAX_FINDING_BODY_LENGTH);
|
|
127194
|
+
if (!title || !body) continue;
|
|
127195
|
+
const baseId = boundedTrimmedString(value.id, MAX_FINDING_ID_LENGTH) || `finding-${normalized.length + 1}`;
|
|
127196
|
+
let id = baseId;
|
|
127197
|
+
let suffix = 2;
|
|
127198
|
+
while (usedIds.has(id)) id = `${baseId.slice(0, Math.max(1, MAX_FINDING_ID_LENGTH - String(suffix).length - 1))}-${suffix++}`;
|
|
127199
|
+
usedIds.add(id);
|
|
127200
|
+
const filePath = boundedTrimmedString(value.filePath, MAX_FINDING_PATH_LENGTH);
|
|
127201
|
+
const line2 = typeof value.line === "number" && Number.isFinite(value.line) && value.line > 0 ? Math.floor(value.line) : void 0;
|
|
127202
|
+
const severity = isWorkflowReviewFindingSeverity(value.severity) ? value.severity : void 0;
|
|
127203
|
+
normalized.push({ id, title, body, ...filePath ? { filePath } : {}, ...line2 ? { line: line2 } : {}, ...severity ? { severity } : {} });
|
|
127204
|
+
}
|
|
127205
|
+
return normalized.length > 0 ? normalized : void 0;
|
|
127206
|
+
}
|
|
127207
|
+
function boundedTrimmedString(value, maxLength) {
|
|
127208
|
+
if (typeof value !== "string") return void 0;
|
|
127209
|
+
const trimmed = value.trim();
|
|
127210
|
+
return trimmed && trimmed.length <= maxLength ? trimmed : void 0;
|
|
127211
|
+
}
|
|
127212
|
+
function isWorkflowReviewFindingSeverity(value) {
|
|
127213
|
+
return typeof value === "string" && WORKFLOW_REVIEW_FINDING_SEVERITIES.includes(value);
|
|
127214
|
+
}
|
|
127161
127215
|
function isTerminalFailure(result) {
|
|
127162
127216
|
return TERMINAL_FAILURE_STATUSES.has(result.status);
|
|
127163
127217
|
}
|
|
@@ -127223,10 +127277,16 @@ function makeReviewLeaseRecord(args) {
|
|
|
127223
127277
|
leaseOwner: args.owner
|
|
127224
127278
|
};
|
|
127225
127279
|
}
|
|
127226
|
-
var MAX_WORKFLOW_STEP_PRIOR_ATTEMPTS, TERMINAL_FAILURE_STATUSES, PLAN_REVIEW_LEASE_STALENESS_MS, TERMINAL_STEP_STATUSES;
|
|
127280
|
+
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
127281
|
var init_workflow_step_results = __esm({
|
|
127228
127282
|
"../core/src/workflows/workflow-step-results.ts"() {
|
|
127229
127283
|
"use strict";
|
|
127284
|
+
WORKFLOW_REVIEW_FINDING_SEVERITIES = ["low", "medium", "high", "critical"];
|
|
127285
|
+
MAX_WORKFLOW_REVIEW_FINDINGS = 20;
|
|
127286
|
+
MAX_FINDING_ID_LENGTH = 128;
|
|
127287
|
+
MAX_FINDING_TITLE_LENGTH = 240;
|
|
127288
|
+
MAX_FINDING_BODY_LENGTH = 4e3;
|
|
127289
|
+
MAX_FINDING_PATH_LENGTH = 1e3;
|
|
127230
127290
|
MAX_WORKFLOW_STEP_PRIOR_ATTEMPTS = 5;
|
|
127231
127291
|
TERMINAL_FAILURE_STATUSES = /* @__PURE__ */ new Set([
|
|
127232
127292
|
"failed",
|
|
@@ -127295,6 +127355,7 @@ var init_task_timing = __esm({
|
|
|
127295
127355
|
});
|
|
127296
127356
|
|
|
127297
127357
|
// ../core/src/session-identity-registry.ts
|
|
127358
|
+
import { AsyncLocalStorage } from "node:async_hooks";
|
|
127298
127359
|
import { resolve as resolve21 } from "node:path";
|
|
127299
127360
|
import { realpathSync as realpathSync5 } from "node:fs";
|
|
127300
127361
|
function getRegistry() {
|
|
@@ -127306,6 +127367,16 @@ function getRegistry() {
|
|
|
127306
127367
|
}
|
|
127307
127368
|
return registry3;
|
|
127308
127369
|
}
|
|
127370
|
+
function getActiveInvocationStorage() {
|
|
127371
|
+
const holder = globalThis;
|
|
127372
|
+
const existing = holder[ACTIVE_INVOCATION_KEY];
|
|
127373
|
+
if (existing instanceof AsyncLocalStorage) {
|
|
127374
|
+
return existing;
|
|
127375
|
+
}
|
|
127376
|
+
const storage = new AsyncLocalStorage();
|
|
127377
|
+
holder[ACTIVE_INVOCATION_KEY] = storage;
|
|
127378
|
+
return storage;
|
|
127379
|
+
}
|
|
127309
127380
|
function canonicalizeCwd(cwd) {
|
|
127310
127381
|
const resolved = resolve21(cwd);
|
|
127311
127382
|
try {
|
|
@@ -127333,8 +127404,13 @@ function registerFusionSessionIdentity(cwd, identity) {
|
|
|
127333
127404
|
};
|
|
127334
127405
|
}
|
|
127335
127406
|
function resolveFusionSessionPrincipal(cwd) {
|
|
127407
|
+
const canonicalCwd = canonicalizeCwd(cwd);
|
|
127408
|
+
const activeInvocation = getActiveInvocationStorage().getStore();
|
|
127409
|
+
if (activeInvocation?.cwdKeys.has(canonicalCwd)) {
|
|
127410
|
+
return { kind: "agent", identity: activeInvocation.identity };
|
|
127411
|
+
}
|
|
127336
127412
|
const registry3 = getRegistry();
|
|
127337
|
-
const list = registry3.get(
|
|
127413
|
+
const list = registry3.get(canonicalCwd);
|
|
127338
127414
|
if (!list || list.length === 0) {
|
|
127339
127415
|
return { kind: "operator" };
|
|
127340
127416
|
}
|
|
@@ -127343,14 +127419,24 @@ function resolveFusionSessionPrincipal(cwd) {
|
|
|
127343
127419
|
}
|
|
127344
127420
|
return { kind: "ambiguous", identities: [...list] };
|
|
127345
127421
|
}
|
|
127422
|
+
function runWithFusionSessionIdentity(cwdKeys, identity, callback) {
|
|
127423
|
+
const invocation = {
|
|
127424
|
+
identity: { ...identity, registeredAt: Date.now() },
|
|
127425
|
+
cwdKeys: new Set(cwdKeys.map(canonicalizeCwd))
|
|
127426
|
+
};
|
|
127427
|
+
return getActiveInvocationStorage().run(invocation, callback);
|
|
127428
|
+
}
|
|
127346
127429
|
function __clearFusionSessionIdentityRegistryForTests() {
|
|
127347
|
-
|
|
127430
|
+
const holder = globalThis;
|
|
127431
|
+
holder[REGISTRY_KEY] = /* @__PURE__ */ new Map();
|
|
127432
|
+
holder[ACTIVE_INVOCATION_KEY] = new AsyncLocalStorage();
|
|
127348
127433
|
}
|
|
127349
|
-
var REGISTRY_KEY;
|
|
127434
|
+
var REGISTRY_KEY, ACTIVE_INVOCATION_KEY;
|
|
127350
127435
|
var init_session_identity_registry = __esm({
|
|
127351
127436
|
"../core/src/session-identity-registry.ts"() {
|
|
127352
127437
|
"use strict";
|
|
127353
127438
|
REGISTRY_KEY = "__FUSION_SESSION_IDENTITY_REGISTRY_V1__";
|
|
127439
|
+
ACTIVE_INVOCATION_KEY = "__FUSION_SESSION_IDENTITY_ACTIVE_INVOCATION_V1__";
|
|
127354
127440
|
}
|
|
127355
127441
|
});
|
|
127356
127442
|
|
|
@@ -127713,6 +127799,7 @@ __export(src_exports, {
|
|
|
127713
127799
|
MAX_TITLE_LENGTH: () => MAX_TITLE_LENGTH,
|
|
127714
127800
|
MAX_TITLE_SUMMARIZE_INPUT_LENGTH: () => MAX_TITLE_SUMMARIZE_INPUT_LENGTH,
|
|
127715
127801
|
MAX_WORKFLOW_ICON_LENGTH: () => MAX_WORKFLOW_ICON_LENGTH,
|
|
127802
|
+
MAX_WORKFLOW_REVIEW_FINDINGS: () => MAX_WORKFLOW_REVIEW_FINDINGS,
|
|
127716
127803
|
MAX_WORKFLOW_STEP_PRIOR_ATTEMPTS: () => MAX_WORKFLOW_STEP_PRIOR_ATTEMPTS,
|
|
127717
127804
|
MEMORY_AUDIT_PATH: () => MEMORY_AUDIT_PATH,
|
|
127718
127805
|
MEMORY_BACKEND_SETTINGS_KEYS: () => MEMORY_BACKEND_SETTINGS_KEYS,
|
|
@@ -127914,6 +128001,7 @@ __export(src_exports, {
|
|
|
127914
128001
|
WAKE_DELTA_ASSIGNED_TASKS_CAP: () => WAKE_DELTA_ASSIGNED_TASKS_CAP,
|
|
127915
128002
|
WEDGE_RENOTIFY_COOLDOWN_MS: () => WEDGE_RENOTIFY_COOLDOWN_MS,
|
|
127916
128003
|
WORKFLOW_EXTENSION_SCHEMA_VERSION: () => WORKFLOW_EXTENSION_SCHEMA_VERSION,
|
|
128004
|
+
WORKFLOW_REVIEW_FINDING_SEVERITIES: () => WORKFLOW_REVIEW_FINDING_SEVERITIES,
|
|
127917
128005
|
WORKFLOW_SETTING_REJECTION_CODES: () => WORKFLOW_SETTING_REJECTION_CODES,
|
|
127918
128006
|
WORKFLOW_SETTING_TYPES: () => WORKFLOW_SETTING_TYPES,
|
|
127919
128007
|
WORKFLOW_WORK_ITEM_KINDS: () => WORKFLOW_WORK_ITEM_KINDS,
|
|
@@ -128319,6 +128407,7 @@ __export(src_exports, {
|
|
|
128319
128407
|
hasRoleOverrides: () => hasRoleOverrides,
|
|
128320
128408
|
hasSyncPassphraseConfigured: () => hasSyncPassphraseConfigured,
|
|
128321
128409
|
hasTitleIdDrift: () => hasTitleIdDrift,
|
|
128410
|
+
hasUserAutoMergeHold: () => hasUserAutoMergeHold,
|
|
128322
128411
|
hashWorkflowIr: () => hashWorkflowIr,
|
|
128323
128412
|
healSchemaDrift: () => healSchemaDrift,
|
|
128324
128413
|
importMcpServersJson: () => importMcpServersJson,
|
|
@@ -128441,6 +128530,7 @@ __export(src_exports, {
|
|
|
128441
128530
|
isWipColumn: () => isWipColumn,
|
|
128442
128531
|
isWipColumnRole: () => isWipColumnRole,
|
|
128443
128532
|
isWorkflowOptionalGroupEnabled: () => isWorkflowOptionalGroupEnabled,
|
|
128533
|
+
isWorkflowReviewFindingSeverity: () => isWorkflowReviewFindingSeverity,
|
|
128444
128534
|
isWorkspaceTask: () => isWorkspaceTask,
|
|
128445
128535
|
layoutForIr: () => layoutForIr,
|
|
128446
128536
|
listActiveAiSessions: () => listActiveAiSessions,
|
|
@@ -128514,6 +128604,7 @@ __export(src_exports, {
|
|
|
128514
128604
|
normalizeValidationDiagnostics: () => normalizeValidationDiagnostics,
|
|
128515
128605
|
normalizeWorkflowIcon: () => normalizeWorkflowIcon,
|
|
128516
128606
|
normalizeWorkflowPromptOverrides: () => normalizeWorkflowPromptOverrides,
|
|
128607
|
+
normalizeWorkflowReviewFindings: () => normalizeWorkflowReviewFindings,
|
|
128517
128608
|
notifyOperatorOfNonOperatorDelete: () => notifyOperatorOfNonOperatorDelete,
|
|
128518
128609
|
overseerAdviceSeverityRank: () => overseerAdviceSeverityRank,
|
|
128519
128610
|
parseAgentManifest: () => parseAgentManifest,
|
|
@@ -128777,6 +128868,7 @@ __export(src_exports, {
|
|
|
128777
128868
|
runMemoryBackupCommand: () => runMemoryBackupCommand,
|
|
128778
128869
|
runReconciliationAbort: () => runReconciliationAbort,
|
|
128779
128870
|
runScheduledEvalBatch: () => runScheduledEvalBatch,
|
|
128871
|
+
runWithFusionSessionIdentity: () => runWithFusionSessionIdentity,
|
|
128780
128872
|
sanitizeBranchSegment: () => sanitizeBranchSegment,
|
|
128781
128873
|
sanitizeCliAgentSettings: () => sanitizeCliAgentSettings,
|
|
128782
128874
|
sanitizeCliAgentsSettings: () => sanitizeCliAgentsSettings,
|
|
@@ -158175,6 +158267,7 @@ import { existsSync as existsSync49, readFileSync as readFileSync16, realpathSyn
|
|
|
158175
158267
|
import { exec as exec11, execFile as execFile9 } from "node:child_process";
|
|
158176
158268
|
import { promisify as promisify15 } from "node:util";
|
|
158177
158269
|
import { createRequire as createRequire5 } from "node:module";
|
|
158270
|
+
import { randomUUID as randomUUID42 } from "node:crypto";
|
|
158178
158271
|
import { basename as basename16, dirname as dirname24, join as join59, relative as relative8, isAbsolute as isAbsolute10, resolve as resolve30 } from "node:path";
|
|
158179
158272
|
import {
|
|
158180
158273
|
createAgentSession,
|
|
@@ -159879,7 +159972,8 @@ async function createFnAgent2(options) {
|
|
|
159879
159972
|
});
|
|
159880
159973
|
};
|
|
159881
159974
|
const sessionIdentity = (() => {
|
|
159882
|
-
const
|
|
159975
|
+
const namedAgentId = options.actionGateContext?.agentId ?? options.permanentAgentGating?.requester?.actorId;
|
|
159976
|
+
const principalAgentId = namedAgentId ?? `engine-session-${randomUUID42()}`;
|
|
159883
159977
|
const principalAgentName = options.actionGateContext?.agentName ?? options.permanentAgentGating?.requester?.actorName;
|
|
159884
159978
|
return {
|
|
159885
159979
|
agentId: principalAgentId,
|
|
@@ -159891,6 +159985,14 @@ async function createFnAgent2(options) {
|
|
|
159891
159985
|
const sessionIdentityKeys = [...new Set([options.cwd, resolvedProjectRoot].filter((key) => Boolean(key)))];
|
|
159892
159986
|
const attachSessionIdentity = (session) => {
|
|
159893
159987
|
const identityDisposers = sessionIdentityKeys.map((key) => registerFusionSessionIdentity(key, sessionIdentity));
|
|
159988
|
+
const sessionInvocations = session;
|
|
159989
|
+
const wrapInvocation = (methodName) => {
|
|
159990
|
+
const original = sessionInvocations[methodName];
|
|
159991
|
+
if (typeof original !== "function") return;
|
|
159992
|
+
sessionInvocations[methodName] = (...args) => runWithFusionSessionIdentity(sessionIdentityKeys, sessionIdentity, () => original.apply(session, args));
|
|
159993
|
+
};
|
|
159994
|
+
wrapInvocation("prompt");
|
|
159995
|
+
wrapInvocation("promptWithFallback");
|
|
159894
159996
|
const disposeBeforeIdentity = typeof session.dispose === "function" ? session.dispose.bind(session) : () => void 0;
|
|
159895
159997
|
session.dispose = async () => {
|
|
159896
159998
|
for (const disposeIdentity of identityDisposers) {
|
|
@@ -162686,10 +162788,11 @@ function createMergeAttemptHandler(deps) {
|
|
|
162686
162788
|
);
|
|
162687
162789
|
};
|
|
162688
162790
|
}
|
|
162689
|
-
function createMergeGateHandler() {
|
|
162791
|
+
function createMergeGateHandler(deps = {}) {
|
|
162690
162792
|
return async (_node, ctx) => {
|
|
162691
162793
|
const settingsAutoMerge = ctx.settings?.autoMerge;
|
|
162692
|
-
const
|
|
162794
|
+
const settings = { autoMerge: settingsAutoMerge ?? true };
|
|
162795
|
+
const autoMerge = !hasUserAutoMergeHold(ctx.task) && (await deps.isLiveSharedBranchMember?.(ctx.task, settings) === true || ctx.task.autoMerge !== false && settings.autoMerge !== false);
|
|
162693
162796
|
return {
|
|
162694
162797
|
outcome: "success",
|
|
162695
162798
|
value: autoMerge ? "auto-on" : "auto-off"
|
|
@@ -162699,6 +162802,7 @@ function createMergeGateHandler() {
|
|
|
162699
162802
|
var init_merge_runner = __esm({
|
|
162700
162803
|
"../engine/src/workflow-node-runners/merge-runner.ts"() {
|
|
162701
162804
|
"use strict";
|
|
162805
|
+
init_src2();
|
|
162702
162806
|
init_workflow_merge_nodes();
|
|
162703
162807
|
}
|
|
162704
162808
|
});
|
|
@@ -163042,7 +163146,7 @@ function createDefaultNodeHandlers(seams, runCustomNode, deps) {
|
|
|
163042
163146
|
"parse-steps": parseSteps,
|
|
163043
163147
|
code: createCodeNodeHandler(deps?.runCode),
|
|
163044
163148
|
notify: createNotifyHandler(deps?.notifyDispatch),
|
|
163045
|
-
"merge-gate": createMergeGateHandler(),
|
|
163149
|
+
"merge-gate": createMergeGateHandler({ isLiveSharedBranchMember: deps?.isLiveSharedBranchMember }),
|
|
163046
163150
|
"merge-attempt": createMergeAttemptHandler({
|
|
163047
163151
|
primitives: deps?.primitives,
|
|
163048
163152
|
seams,
|
|
@@ -164660,37 +164764,6 @@ var init_code_node_runner = __esm({
|
|
|
164660
164764
|
}
|
|
164661
164765
|
});
|
|
164662
164766
|
|
|
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
164767
|
// ../engine/src/notification/ntfy-provider.ts
|
|
164695
164768
|
function resolveParticipantLabel(metadata, kind) {
|
|
164696
164769
|
const nameKey = kind === "from" ? "fromName" : "toName";
|
|
@@ -165164,7 +165237,50 @@ var init_webhook_provider = __esm({
|
|
|
165164
165237
|
}
|
|
165165
165238
|
});
|
|
165166
165239
|
|
|
165240
|
+
// ../engine/src/errors/transient-merge-error-classifier.ts
|
|
165241
|
+
function classifyTransientMergeError(error2) {
|
|
165242
|
+
if (!error2) return null;
|
|
165243
|
+
if (/lease-handoff-failed[^a-z]+target-not-queued/i.test(error2)) {
|
|
165244
|
+
return "lease-handoff-target-not-queued";
|
|
165245
|
+
}
|
|
165246
|
+
if (/\bACP turn failed\b/i.test(error2) || /\bacp rpc code -32(?:603|00[0-3])\b/i.test(error2)) {
|
|
165247
|
+
return "ai-provider-turn-failure";
|
|
165248
|
+
}
|
|
165249
|
+
if (/\bspawn(?:\s+\S+)?\s+ENO(?:TDIR|ENT)\b/i.test(error2)) {
|
|
165250
|
+
return "process-spawn-failure";
|
|
165251
|
+
}
|
|
165252
|
+
if (/\bis not a working tree\b/i.test(error2)) {
|
|
165253
|
+
return "process-spawn-failure";
|
|
165254
|
+
}
|
|
165255
|
+
const sameSha = error2.match(/advanced concurrently \(expected ([0-9a-f]{7,40}),\s+observed ([0-9a-f]{7,40})\)/i);
|
|
165256
|
+
if (sameSha && sameSha[1].toLowerCase() === sameSha[2].toLowerCase()) {
|
|
165257
|
+
return "spurious-concurrent-advance-same-sha";
|
|
165258
|
+
}
|
|
165259
|
+
if (isTransientError(error2)) {
|
|
165260
|
+
return "network-transport-failure";
|
|
165261
|
+
}
|
|
165262
|
+
return null;
|
|
165263
|
+
}
|
|
165264
|
+
function hasTransientMergeRecoveryOwner(task) {
|
|
165265
|
+
return classifyTransientMergeError(task.error) !== null && typeof task.mergeTransientRetryCount === "number" && Number.isInteger(task.mergeTransientRetryCount) && task.mergeTransientRetryCount >= 0 && task.mergeTransientRetryCount < MAX_AUTO_MERGE_TRANSIENT_RETRIES;
|
|
165266
|
+
}
|
|
165267
|
+
var MAX_AUTO_MERGE_TRANSIENT_RETRIES;
|
|
165268
|
+
var init_transient_merge_error_classifier = __esm({
|
|
165269
|
+
"../engine/src/errors/transient-merge-error-classifier.ts"() {
|
|
165270
|
+
"use strict";
|
|
165271
|
+
init_transient_error_patterns();
|
|
165272
|
+
MAX_AUTO_MERGE_TRANSIENT_RETRIES = 5;
|
|
165273
|
+
}
|
|
165274
|
+
});
|
|
165275
|
+
|
|
165167
165276
|
// ../engine/src/notification/task-wedge-notification.ts
|
|
165277
|
+
function describeTaskRecoveryOwner(task) {
|
|
165278
|
+
if (hasTransientMergeRecoveryOwner(task)) return { kind: "transient-merge-retry" };
|
|
165279
|
+
if (typeof task.recoveryRetryCount === "number" && Number.isInteger(task.recoveryRetryCount) && task.recoveryRetryCount >= 0 && typeof task.nextRecoveryAt === "string" && Number.isFinite(Date.parse(task.nextRecoveryAt))) {
|
|
165280
|
+
return { kind: "scheduled-recovery" };
|
|
165281
|
+
}
|
|
165282
|
+
return null;
|
|
165283
|
+
}
|
|
165168
165284
|
function describeSelfHealingNoActionWedge(task, stage, metadata) {
|
|
165169
165285
|
const description = SELF_HEALING_NO_ACTIONS[stage];
|
|
165170
165286
|
if (!description || task.userPaused || task.paused || task.autoMerge === false) return null;
|
|
@@ -165222,6 +165338,7 @@ function describeTaskWedge(task) {
|
|
|
165222
165338
|
if (/merge.*(?:blocked|verification|gate)|(?:verification|gate).*(?:failed|exhaust)/i.test(error2)) {
|
|
165223
165339
|
return { reasonKey: "merge-blocked", reason: "Merge verification cannot progress without operator action.", action: "Fix the failing verification, then retry the task." };
|
|
165224
165340
|
}
|
|
165341
|
+
if (describeTaskRecoveryOwner(task)) return null;
|
|
165225
165342
|
if (!error2 && (task.mergeRetries ?? 0) < 3) return null;
|
|
165226
165343
|
return {
|
|
165227
165344
|
reasonKey: "terminal-failed",
|
|
@@ -165233,6 +165350,7 @@ var SELF_HEALING_NO_ACTIONS;
|
|
|
165233
165350
|
var init_task_wedge_notification = __esm({
|
|
165234
165351
|
"../engine/src/notification/task-wedge-notification.ts"() {
|
|
165235
165352
|
"use strict";
|
|
165353
|
+
init_transient_merge_error_classifier();
|
|
165236
165354
|
SELF_HEALING_NO_ACTIONS = {
|
|
165237
165355
|
"reclaim-pr-conflict": {
|
|
165238
165356
|
reason: "Self-healing could not reclaim a stalled pull-request conflict.",
|
|
@@ -165298,7 +165416,6 @@ var init_notification_service = __esm({
|
|
|
165298
165416
|
init_src2();
|
|
165299
165417
|
init_notifier();
|
|
165300
165418
|
init_logger();
|
|
165301
|
-
init_transient_merge_error_classifier();
|
|
165302
165419
|
init_ntfy_provider();
|
|
165303
165420
|
init_webhook_provider();
|
|
165304
165421
|
init_task_wedge_notification();
|
|
@@ -165524,10 +165641,10 @@ var init_notification_service = __esm({
|
|
|
165524
165641
|
}
|
|
165525
165642
|
}
|
|
165526
165643
|
handleTaskUpdated = (task, meta2) => {
|
|
165527
|
-
const
|
|
165528
|
-
const wedge =
|
|
165644
|
+
const recoveryOwner = task.status === "failed" ? describeTaskRecoveryOwner(task) : null;
|
|
165645
|
+
const wedge = describeTaskWedge(task);
|
|
165529
165646
|
if (wedge) this.cancelPendingFailureNotification(task.id, "classified-terminal-wedge");
|
|
165530
|
-
|
|
165647
|
+
void this.enqueueWedgeHandling(task.id, () => this.maybeNotifyTaskWedge(task, wedge));
|
|
165531
165648
|
void this.maybeSuppressTransientFailedNotification(task, `status=${task.status ?? "undefined"}`);
|
|
165532
165649
|
if (task.status === "awaiting-approval") {
|
|
165533
165650
|
void this.writeAwaitingApprovalMailboxMessage(task);
|
|
@@ -165538,17 +165655,12 @@ var init_notification_service = __esm({
|
|
|
165538
165655
|
if (!this.notificationsEnabled) {
|
|
165539
165656
|
return;
|
|
165540
165657
|
}
|
|
165541
|
-
if (task.status === "failed" &&
|
|
165542
|
-
|
|
165543
|
-
|
|
165544
|
-
|
|
165545
|
-
schedulerLog.debug(
|
|
165546
|
-
`[notify] ${task.id} transient merge failure (${transientClass}) \u2014 suppressed notification (self-heal in flight)`
|
|
165547
|
-
);
|
|
165548
|
-
return;
|
|
165549
|
-
}
|
|
165658
|
+
if (task.status === "failed" && recoveryOwner) {
|
|
165659
|
+
this.failureNotificationSuppressedCount += 1;
|
|
165660
|
+
schedulerLog.debug(`[notify] ${task.id} recovery-owned failure \u2014 suppressed notification`);
|
|
165661
|
+
} else if (task.status === "failed" && !wedge) {
|
|
165550
165662
|
if (this.failureNotificationMode === "all") {
|
|
165551
|
-
|
|
165663
|
+
void this.maybeNotifyImmediateFailure(task);
|
|
165552
165664
|
} else {
|
|
165553
165665
|
this.scheduleFailureNotification(task);
|
|
165554
165666
|
}
|
|
@@ -165635,7 +165747,17 @@ var init_notification_service = __esm({
|
|
|
165635
165747
|
await this.enqueueWedgeHandling(task.id, () => this.maybeNotifyTaskWedge(task, descriptor));
|
|
165636
165748
|
}
|
|
165637
165749
|
async maybeNotifyTaskWedge(task, suppliedDescriptor) {
|
|
165638
|
-
const
|
|
165750
|
+
const liveTask = this.store.getTask ? await this.store.getTask(task.id) ?? task : task;
|
|
165751
|
+
const recoveryOwner = describeTaskRecoveryOwner(liveTask);
|
|
165752
|
+
if (recoveryOwner) {
|
|
165753
|
+
if (this.activeWedgeReasons.has(task.id) || liveTask.wedgeNotification?.status === "active") {
|
|
165754
|
+
this.activeWedgeReasons.delete(task.id);
|
|
165755
|
+
await this.store.claimTaskWedgeNotificationEpisode?.(task.id, null);
|
|
165756
|
+
}
|
|
165757
|
+
return;
|
|
165758
|
+
}
|
|
165759
|
+
const descriptor = suppliedDescriptor ?? describeTaskWedge(liveTask);
|
|
165760
|
+
task = liveTask;
|
|
165639
165761
|
let episode;
|
|
165640
165762
|
if (!descriptor) {
|
|
165641
165763
|
const isActiveSelfHealingNoAction = task.wedgeNotification?.status === "active" && task.wedgeNotification.reasonKey.startsWith("self-healing-no-action:");
|
|
@@ -166002,6 +166124,14 @@ var init_notification_service = __esm({
|
|
|
166002
166124
|
this.failureNotificationDelayMs = typeof settings.failureNotificationDelayMs === "number" && settings.failureNotificationDelayMs >= 0 ? settings.failureNotificationDelayMs : this.failedNotificationGraceMs;
|
|
166003
166125
|
this.failureNotificationMode = settings.failureNotificationMode ?? "sticky-only";
|
|
166004
166126
|
}
|
|
166127
|
+
async maybeNotifyImmediateFailure(task) {
|
|
166128
|
+
const liveTask = this.store.getTask ? await this.store.getTask(task.id) ?? task : task;
|
|
166129
|
+
if (liveTask.status !== "failed" || describeTaskRecoveryOwner(liveTask) || describeTaskWedge(liveTask)) {
|
|
166130
|
+
this.failureNotificationSuppressedCount += 1;
|
|
166131
|
+
return;
|
|
166132
|
+
}
|
|
166133
|
+
this.maybeNotify(liveTask.id, "failed", this.createTaskPayload(liveTask, "failed"));
|
|
166134
|
+
}
|
|
166005
166135
|
scheduleFailureNotification(task) {
|
|
166006
166136
|
if (this.pendingFailureNotifications.has(task.id)) {
|
|
166007
166137
|
return;
|
|
@@ -166058,12 +166188,9 @@ var init_notification_service = __esm({
|
|
|
166058
166188
|
schedulerLog.debug(`[notify] ${taskId} no longer failed at dispatch time \u2014 suppressed notification`);
|
|
166059
166189
|
return;
|
|
166060
166190
|
}
|
|
166061
|
-
|
|
166062
|
-
if (transientClassAtDispatch) {
|
|
166191
|
+
if (describeTaskRecoveryOwner(task)) {
|
|
166063
166192
|
this.failureNotificationSuppressedCount += 1;
|
|
166064
|
-
schedulerLog.debug(
|
|
166065
|
-
`[notify] ${taskId} transient merge failure (${transientClassAtDispatch}) at dispatch time \u2014 suppressed notification (self-heal in flight)`
|
|
166066
|
-
);
|
|
166193
|
+
schedulerLog.debug(`[notify] ${taskId} recovery-owned failure at dispatch time \u2014 suppressed notification`);
|
|
166067
166194
|
return;
|
|
166068
166195
|
}
|
|
166069
166196
|
if (describeTaskWedge(task)) {
|
|
@@ -169630,7 +169757,7 @@ var init_goal_anchoring_audit = __esm({
|
|
|
169630
169757
|
// ../engine/src/agent-tools.ts
|
|
169631
169758
|
import { appendFile as appendFile3, mkdir as mkdir26, readFile as readFile34, readdir as readdir9, realpath, stat as stat9, writeFile as writeFile24 } from "node:fs/promises";
|
|
169632
169759
|
import { existsSync as existsSync52 } from "node:fs";
|
|
169633
|
-
import { createHash as createHash19, randomUUID as
|
|
169760
|
+
import { createHash as createHash19, randomUUID as randomUUID43 } from "node:crypto";
|
|
169634
169761
|
import { tmpdir as tmpdir6 } from "node:os";
|
|
169635
169762
|
import { extname as extname3, isAbsolute as isAbsolute13, join as join64, relative as relative11, resolve as resolve33, sep as sep9 } from "node:path";
|
|
169636
169763
|
import { Type as Type2 } from "@earendil-works/pi-ai";
|
|
@@ -170276,7 +170403,7 @@ function createTaskCreateTool(store, provenance, options) {
|
|
|
170276
170403
|
toType: "user",
|
|
170277
170404
|
type: "agent-to-user",
|
|
170278
170405
|
content: `Task proposal awaiting validation: ${title}`,
|
|
170279
|
-
metadata: { kind: "task-proposal", proposalStatus: "pending", proposalIdempotencyKey:
|
|
170406
|
+
metadata: { kind: "task-proposal", proposalStatus: "pending", proposalIdempotencyKey: randomUUID43(), taskId: options.sourceTaskId, proposedTask: { title, description: params.description, priority: params.priority, workflowId: params.workflow_id, dependencies: params.dependencies } }
|
|
170280
170407
|
});
|
|
170281
170408
|
return { content: [{ type: "text", text: "Task proposal submitted to the operator for validation; no task was created." }], details: { proposed: true } };
|
|
170282
170409
|
}
|
|
@@ -192043,10 +192170,21 @@ var Scheduler = class {
|
|
|
192043
192170
|
const unmetDeps = getUnmetSchedulingDependencies(task, tasks2, schedulingDependencyOptions);
|
|
192044
192171
|
if (unmetDeps.length > 0) {
|
|
192045
192172
|
const normalizedUnmetDeps = [...new Set(unmetDeps)].sort();
|
|
192173
|
+
let activeOverlapBlockedBy = null;
|
|
192174
|
+
if (activeScopes.size > 0) {
|
|
192175
|
+
try {
|
|
192176
|
+
const taskScope = await getFilteredFileScope(task.id);
|
|
192177
|
+
if (taskScope.length > 0 && !isCoordinationOnlyTask(task, taskScope)) {
|
|
192178
|
+
activeOverlapBlockedBy = Array.from(activeScopes.entries()).sort(([aId], [bId]) => aId.localeCompare(bId)).find(([, activeScope]) => this.pathsOverlap(taskScope, activeScope))?.[0] ?? null;
|
|
192179
|
+
}
|
|
192180
|
+
} catch (error2) {
|
|
192181
|
+
schedulerLog.warn(`Failed to refresh file-scope overlap blocker for dependency-blocked task ${task.id}`, error2);
|
|
192182
|
+
}
|
|
192183
|
+
}
|
|
192046
192184
|
await this.transitionQueuedEpisode(task, {
|
|
192047
192185
|
signature: `dependency:${normalizedUnmetDeps.join(",")}`,
|
|
192048
192186
|
blockedBy: unmetDeps[0] ?? null,
|
|
192049
|
-
overlapBlockedBy:
|
|
192187
|
+
overlapBlockedBy: activeOverlapBlockedBy,
|
|
192050
192188
|
action: `queued \u2014 unmet dependencies: ${unmetDeps.join(", ")}`
|
|
192051
192189
|
});
|
|
192052
192190
|
this.options.onBlocked?.(task, unmetDeps);
|
|
@@ -194569,6 +194707,7 @@ var SESSION_CONTENTION_HOLD_VALUE = "session-contention-hold";
|
|
|
194569
194707
|
var WORKFLOW_INTERRUPTED_NODE_ID_CONTEXT_KEY = "workflow:interruptedNodeId";
|
|
194570
194708
|
var WORKFLOW_INTERRUPTED_NODE_ABORT_KIND_CONTEXT_KEY = "workflow:interruptedNodeAbortKind";
|
|
194571
194709
|
var WORKFLOW_OPTIONAL_GROUP_CONTEXT_KEY = "workflow:optionalGroupActive";
|
|
194710
|
+
var WORKFLOW_REVIEW_KIND_CONTEXT_KEY = "workflow:reviewKind";
|
|
194572
194711
|
var WORKFLOW_NODE_ENGINE_PAUSE_ABORT_KIND = "engine-pause";
|
|
194573
194712
|
var WORKFLOW_DRIFT_PARK_CONTEXT_KEY = "workflow:driftPark";
|
|
194574
194713
|
var WorkflowGraphSuspended = class extends Error {
|
|
@@ -194672,7 +194811,8 @@ var WorkflowGraphExecutor = class {
|
|
|
194672
194811
|
parseSteps: deps.parseStepsDeps,
|
|
194673
194812
|
runCode: deps.runCode,
|
|
194674
194813
|
notifyDispatch: deps.notifyDispatch,
|
|
194675
|
-
prNodes: deps.prNodes
|
|
194814
|
+
prNodes: deps.prNodes,
|
|
194815
|
+
isLiveSharedBranchMember: deps.isLiveSharedBranchMember
|
|
194676
194816
|
}),
|
|
194677
194817
|
...deps.runnerRegistry?.toHandlers() ?? {},
|
|
194678
194818
|
...deps.handlers ?? {}
|
|
@@ -194893,6 +195033,7 @@ var WorkflowGraphExecutor = class {
|
|
|
194893
195033
|
phase: stepPhase,
|
|
194894
195034
|
status: "pending",
|
|
194895
195035
|
source: "optional-group",
|
|
195036
|
+
...this.workflowReviewKind(node) ? { reviewKind: this.workflowReviewKind(node) } : {},
|
|
194896
195037
|
startedAt: stepStartedAt,
|
|
194897
195038
|
// U3/KTD-4: stamp the lease owner so a concurrent/crashed re-entry
|
|
194898
195039
|
// adopts this pending gate instead of dispatching a second reviewer.
|
|
@@ -194912,7 +195053,8 @@ var WorkflowGraphExecutor = class {
|
|
|
194912
195053
|
runTemplateNode: (tNode, sig, contextOverride) => {
|
|
194913
195054
|
const optionalGroupContext = {
|
|
194914
195055
|
...contextOverride ?? context,
|
|
194915
|
-
[WORKFLOW_OPTIONAL_GROUP_CONTEXT_KEY]: node.id
|
|
195056
|
+
[WORKFLOW_OPTIONAL_GROUP_CONTEXT_KEY]: node.id,
|
|
195057
|
+
...this.workflowReviewKind(node) ? { [WORKFLOW_REVIEW_KIND_CONTEXT_KEY]: this.workflowReviewKind(node) } : {}
|
|
194916
195058
|
};
|
|
194917
195059
|
return this.executeNodeWithRetries(tNode, task, settings, optionalGroupContext, ir2, sig, false);
|
|
194918
195060
|
},
|
|
@@ -194930,6 +195072,7 @@ var WorkflowGraphExecutor = class {
|
|
|
194930
195072
|
const exitContextPatch = exitResult?.contextPatch;
|
|
194931
195073
|
let stepOutput = typeof exitContextPatch?.output === "string" ? exitContextPatch.output : void 0;
|
|
194932
195074
|
const stepNotes = typeof exitContextPatch?.notes === "string" ? exitContextPatch.notes : void 0;
|
|
195075
|
+
const stepFindings = this.workflowReviewKind(node) && Array.isArray(exitContextPatch?.findings) ? exitContextPatch.findings : void 0;
|
|
194933
195076
|
if (stepStatus === "failed" && !verdict && stepOutput === void 0 && stepNotes === void 0) {
|
|
194934
195077
|
const lastVisited = groupResult.visitedNodeIds[groupResult.visitedNodeIds.length - 1];
|
|
194935
195078
|
const templateNodeId = lastVisited?.includes("::") ? lastVisited.slice(lastVisited.indexOf("::") + 2) : void 0;
|
|
@@ -194947,9 +195090,11 @@ var WorkflowGraphExecutor = class {
|
|
|
194947
195090
|
phase: stepPhase,
|
|
194948
195091
|
source: "optional-group",
|
|
194949
195092
|
status: stepStatus,
|
|
195093
|
+
...this.workflowReviewKind(node) ? { reviewKind: this.workflowReviewKind(node) } : {},
|
|
194950
195094
|
...verdict ? { verdict } : {},
|
|
194951
195095
|
...stepOutput !== void 0 ? { output: stepOutput } : {},
|
|
194952
195096
|
...stepNotes !== void 0 ? { notes: stepNotes } : {},
|
|
195097
|
+
...stepFindings?.length ? { findings: stepFindings } : {},
|
|
194953
195098
|
startedAt: stepStartedAt,
|
|
194954
195099
|
completedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
194955
195100
|
});
|
|
@@ -195409,9 +195554,15 @@ var WorkflowGraphExecutor = class {
|
|
|
195409
195554
|
}
|
|
195410
195555
|
return failureResult;
|
|
195411
195556
|
}
|
|
195557
|
+
/** FNXC:WorkflowReviewKind 2026-08-05-02:31: Persist only the declared closed
|
|
195558
|
+
* marker; execution never derives review semantics from a label, verdict, or gate mode. */
|
|
195559
|
+
workflowReviewKind(node) {
|
|
195560
|
+
return node.config?.reviewKind === "plan" || node.config?.reviewKind === "code" ? node.config.reviewKind : void 0;
|
|
195561
|
+
}
|
|
195412
195562
|
shouldRecordNodeProgress(node) {
|
|
195413
195563
|
const skillName = typeof node.config?.skillName === "string" ? node.config.skillName.trim() : "";
|
|
195414
|
-
|
|
195564
|
+
const markedReview = this.workflowReviewKind(node) !== void 0;
|
|
195565
|
+
return markedReview && (node.kind === "prompt" || node.kind === "gate" || node.kind === "script") || skillName.length > 0 && (node.kind === "prompt" || node.kind === "gate");
|
|
195415
195566
|
}
|
|
195416
195567
|
workflowNodeProgressName(node) {
|
|
195417
195568
|
const configuredName = typeof node.config?.name === "string" ? node.config.name.trim() : "";
|
|
@@ -195425,6 +195576,7 @@ var WorkflowGraphExecutor = class {
|
|
|
195425
195576
|
phase: node.config?.phase === "post-merge" ? "post-merge" : "pre-merge",
|
|
195426
195577
|
source: "node",
|
|
195427
195578
|
status: "pending",
|
|
195579
|
+
...this.workflowReviewKind(node) ? { reviewKind: this.workflowReviewKind(node) } : {},
|
|
195428
195580
|
startedAt
|
|
195429
195581
|
};
|
|
195430
195582
|
await this.recordOptionalGroupStepResult(taskId, result);
|
|
@@ -195435,6 +195587,7 @@ var WorkflowGraphExecutor = class {
|
|
|
195435
195587
|
const contextPatch = nodeResult.contextPatch ?? {};
|
|
195436
195588
|
let output = typeof contextPatch.output === "string" ? contextPatch.output : void 0;
|
|
195437
195589
|
const notes = typeof contextPatch.notes === "string" ? contextPatch.notes : void 0;
|
|
195590
|
+
const findings = this.workflowReviewKind(node) && Array.isArray(contextPatch.findings) ? contextPatch.findings : void 0;
|
|
195438
195591
|
if (status === "failed" && output === void 0 && notes === void 0) {
|
|
195439
195592
|
output = this.synthesizeNonVerdictFailureOutput({
|
|
195440
195593
|
stepLabel: this.workflowNodeProgressName(node),
|
|
@@ -195449,8 +195602,10 @@ var WorkflowGraphExecutor = class {
|
|
|
195449
195602
|
phase: started?.phase ?? (node.config?.phase === "post-merge" ? "post-merge" : "pre-merge"),
|
|
195450
195603
|
source: "node",
|
|
195451
195604
|
status,
|
|
195605
|
+
...this.workflowReviewKind(node) ? { reviewKind: this.workflowReviewKind(node) } : {},
|
|
195452
195606
|
...output !== void 0 ? { output } : {},
|
|
195453
195607
|
...notes !== void 0 ? { notes } : {},
|
|
195608
|
+
...findings?.length ? { findings } : {},
|
|
195454
195609
|
startedAt: started?.startedAt ?? (/* @__PURE__ */ new Date()).toISOString(),
|
|
195455
195610
|
completedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
195456
195611
|
});
|
|
@@ -195900,6 +196055,10 @@ var WorkflowGraphTaskRunner = class {
|
|
|
195900
196055
|
runCode: this.deps.runCode,
|
|
195901
196056
|
notifyDispatch: this.deps.notifyDispatch,
|
|
195902
196057
|
prNodes: this.deps.prNodes,
|
|
196058
|
+
// FNXC:SharedBranchMemberHold 2026-08-06-00:24: carry the executor's
|
|
196059
|
+
// live group resolver into the graph merge gate; omitting it turns
|
|
196060
|
+
// mission-policy members into manual holds before integration.
|
|
196061
|
+
isLiveSharedBranchMember: this.deps.isLiveSharedBranchMember,
|
|
195903
196062
|
/*
|
|
195904
196063
|
FNXC:WorkflowResume 2026-06-29-08:49:
|
|
195905
196064
|
Production graph runs must fetch live task steps during foreach replay. The runner is the workflow boundary that has store access, so it supplies the fresh projection seam instead of making executor self-healing guess after a stale step node fails.
|
|
@@ -207306,16 +207465,29 @@ var SelfHealingManager = class _SelfHealingManager extends SelfHealingGitEvidenc
|
|
|
207306
207465
|
activeWork: await this.resolveActiveWorkColumnsFor(taskId, cache)
|
|
207307
207466
|
};
|
|
207308
207467
|
}
|
|
207309
|
-
|
|
207468
|
+
/*
|
|
207469
|
+
FNXC:SharedBranchMemberHold 2026-08-05-23:35:
|
|
207470
|
+
Pause-abort recovery must resolve the same live intermediate-group predicate as
|
|
207471
|
+
merge admission. Shared metadata alone is stale after group closure or a
|
|
207472
|
+
default-branch collision, so it cannot suppress that task's standalone hold.
|
|
207473
|
+
*/
|
|
207474
|
+
async isLiveSharedMemberIntegration(task, settings) {
|
|
207475
|
+
const groupId = task.branchContext?.groupId?.trim();
|
|
207476
|
+
const group = groupId ? await this.store.getBranchGroup?.(groupId) : null;
|
|
207477
|
+
const defaultBranch = await resolveIntegrationBranch(this.options.rootDir, settings);
|
|
207478
|
+
return isLiveSharedBranchGroupMemberIntegration(task, group, defaultBranch);
|
|
207479
|
+
}
|
|
207480
|
+
async classifyPausedAbortWorkflowRecovery(task, settings, isExecuting, columns) {
|
|
207310
207481
|
const isPausedAbortPark = task.status === "failed" && typeof task.error === "string" && task.error.includes(PAUSE_ABORT_PARK_OPERATOR_MARKER) && task.error.includes(PAUSE_ABORT_PARK_ERROR_MARKER);
|
|
207311
207482
|
if (!isPausedAbortPark) return { kind: "no-action", reason: "not-pause-abort" };
|
|
207312
207483
|
if (task.paused || task.userPaused || isExecuting) return { kind: "no-action", reason: "unsafe-or-not-routable" };
|
|
207313
207484
|
const errorText = typeof task.error === "string" ? task.error.toLowerCase() : "";
|
|
207314
207485
|
const isTerminalMergePark = errorText.includes("conflict") || errorText.includes("contamination") || errorText.includes("foreign") || errorText.includes("retry-exhausted") || errorText.includes("retries exhausted") || errorText.includes("max retries");
|
|
207315
207486
|
const completedSteps = task.steps.length > 0 && task.steps.every((step) => step.status === "done" || step.status === "skipped");
|
|
207316
|
-
const
|
|
207317
|
-
const
|
|
207318
|
-
const
|
|
207487
|
+
const liveSharedBranchMember = await this.isLiveSharedMemberIntegration(task, settings);
|
|
207488
|
+
const autoMergeProcessing = allowsAutoMergeProcessing(task, settings) || liveSharedBranchMember;
|
|
207489
|
+
const hasReviewProgress = columns.review.has(task.column) && autoMergeProcessing && task.mergeDetails?.mergeConfirmed !== true && !isTerminalMergePark && completedSteps;
|
|
207490
|
+
const hasManualMergeHoldProgress = columns.review.has(task.column) && (hasUserAutoMergeHold(task) || !allowsAutoMergeProcessing(task, settings) || resolveEffectiveAutoMerge(task, settings) === false) && (!liveSharedBranchMember || hasUserAutoMergeHold(task)) && task.mergeDetails?.mergeConfirmed !== true && !isTerminalMergePark && completedSteps;
|
|
207319
207491
|
if (hasManualMergeHoldProgress) {
|
|
207320
207492
|
return { kind: "work-item-resume", reason: "pause-abort-manual-merge-hold" };
|
|
207321
207493
|
}
|
|
@@ -212983,8 +213155,23 @@ var SelfHealingManager = class _SelfHealingManager extends SelfHealingGitEvidenc
|
|
|
212983
213155
|
return own.length > 0 ? new Set(own) : /* @__PURE__ */ new Set(["in-review"]);
|
|
212984
213156
|
};
|
|
212985
213157
|
const executingIds = this.options.getExecutingTaskIds?.() ?? /* @__PURE__ */ new Set();
|
|
213158
|
+
const canRecoverMergeableReviewTask = async (task) => {
|
|
213159
|
+
if (hasUserAutoMergeHold(task)) return false;
|
|
213160
|
+
const groupId = task.branchContext?.groupId?.trim();
|
|
213161
|
+
const branchGroup = groupId ? await this.store.getBranchGroup(groupId) : null;
|
|
213162
|
+
const projectDefaultBranch = await resolveIntegrationBranch(this.options.rootDir, settings);
|
|
213163
|
+
if (isLiveSharedBranchGroupMemberIntegration(task, branchGroup, projectDefaultBranch)) {
|
|
213164
|
+
return true;
|
|
213165
|
+
}
|
|
213166
|
+
return allowsAutoMergeProcessing(task, settings) && resolveEffectiveAutoMerge(task, settings) !== false;
|
|
213167
|
+
};
|
|
213168
|
+
const mergeAdmission = await Promise.all(tasks2.map(async (task) => [
|
|
213169
|
+
task.id,
|
|
213170
|
+
await canRecoverMergeableReviewTask(task)
|
|
213171
|
+
]));
|
|
213172
|
+
const mergeAdmissionByTaskId = new Map(mergeAdmission);
|
|
212986
213173
|
const mergeable = tasks2.filter(
|
|
212987
|
-
(t) =>
|
|
213174
|
+
(t) => mergeAdmissionByTaskId.get(t.id) === true && !t.paused && !executingIds.has(t.id) && t.status !== "failed" && // Exclude transient merge statuses. Active merges should be left alone;
|
|
212988
213175
|
// stale ones are handled by recoverStaleMergingStatus().
|
|
212989
213176
|
t.status !== "merging" && t.status !== "merging-pr" && // FNXC:Workspace 2026-06-22-09:30 (Phase D U1, KTD1 — admit workspace tasks):
|
|
212990
213177
|
// A workspace task has task.worktree===null (its worktrees live per-repo in
|
|
@@ -216031,7 +216218,7 @@ var SelfHealingManager = class _SelfHealingManager extends SelfHealingGitEvidenc
|
|
|
216031
216218
|
for (const t of tasks2) {
|
|
216032
216219
|
if (!this.isPauseAbortParkCandidate(t)) continue;
|
|
216033
216220
|
const columns = await this.resolvePauseAbortColumnsFor(t.id, columnCache);
|
|
216034
|
-
if (this.classifyPausedAbortWorkflowRecovery(t, settings, executingIds.has(t.id), columns).kind !== "no-action") {
|
|
216221
|
+
if ((await this.classifyPausedAbortWorkflowRecovery(t, settings, executingIds.has(t.id), columns)).kind !== "no-action") {
|
|
216035
216222
|
parked.push(t);
|
|
216036
216223
|
}
|
|
216037
216224
|
}
|
|
@@ -216048,7 +216235,7 @@ var SelfHealingManager = class _SelfHealingManager extends SelfHealingGitEvidenc
|
|
|
216048
216235
|
continue;
|
|
216049
216236
|
}
|
|
216050
216237
|
const freshColumns = await this.resolvePauseAbortColumnsFor(fresh.id, columnCache);
|
|
216051
|
-
const route = this.classifyPausedAbortWorkflowRecovery(fresh, settings, latestExecutingIds.has(fresh.id), freshColumns);
|
|
216238
|
+
const route = await this.classifyPausedAbortWorkflowRecovery(fresh, settings, latestExecutingIds.has(fresh.id), freshColumns);
|
|
216052
216239
|
if (route.kind === "no-action") {
|
|
216053
216240
|
continue;
|
|
216054
216241
|
}
|
|
@@ -219935,9 +220122,11 @@ function parseWorkflowStepVerdict(rawOutput) {
|
|
|
219935
220122
|
verdict = "REVISE";
|
|
219936
220123
|
}
|
|
219937
220124
|
if (!verdict) continue;
|
|
220125
|
+
const findings = normalizeWorkflowReviewFindings(parsed.findings);
|
|
219938
220126
|
return {
|
|
219939
220127
|
verdict,
|
|
219940
|
-
notes: typeof parsed.notes === "string" ? parsed.notes : ""
|
|
220128
|
+
notes: typeof parsed.notes === "string" ? parsed.notes : "",
|
|
220129
|
+
...findings ? { findings } : {}
|
|
219941
220130
|
};
|
|
219942
220131
|
} catch {
|
|
219943
220132
|
}
|
|
@@ -219969,7 +220158,8 @@ function parseWorkflowStepOutput(rawOutput, options = {}) {
|
|
|
219969
220158
|
return {
|
|
219970
220159
|
output: parsed.notes || "",
|
|
219971
220160
|
verdict: parsed.verdict,
|
|
219972
|
-
notes: parsed.notes
|
|
220161
|
+
notes: parsed.notes,
|
|
220162
|
+
...parsed.findings ? { findings: parsed.findings } : {}
|
|
219973
220163
|
};
|
|
219974
220164
|
}
|
|
219975
220165
|
const inferred = inferWorkflowStepVerdictFromProse(trimmed);
|
|
@@ -222770,6 +222960,7 @@ ${feedback}`,
|
|
|
222770
222960
|
if (info.phase !== "pre-merge") return false;
|
|
222771
222961
|
if (info.status !== "advisory_failure" && info.status !== "failed") return false;
|
|
222772
222962
|
const liveTask = await this.store.getTask(taskId).catch(() => fallbackTask);
|
|
222963
|
+
if (hasUserAutoMergeHold(liveTask)) return false;
|
|
222773
222964
|
const missingArtifactKeys = parseRequiredArtifactMissingValue(info.failureValue);
|
|
222774
222965
|
if (missingArtifactKeys) {
|
|
222775
222966
|
await this.recoverMissingRequiredArtifacts(liveTask, missingArtifactKeys, {
|
|
@@ -223026,6 +223217,7 @@ ${info.feedback}`, revisionKey),
|
|
|
223026
223217
|
*/
|
|
223027
223218
|
async recoverFailedPreMergeWorkflowStep(task) {
|
|
223028
223219
|
try {
|
|
223220
|
+
if (hasUserAutoMergeHold(task)) return false;
|
|
223029
223221
|
const failed = (task.workflowStepResults ?? []).filter((r) => (r.phase || "pre-merge") === "pre-merge" && r.status === "failed").sort((a, b3) => {
|
|
223030
223222
|
const aTs = Date.parse(a.completedAt || a.startedAt || "");
|
|
223031
223223
|
const bTs = Date.parse(b3.completedAt || b3.startedAt || "");
|
|
@@ -223552,6 +223744,7 @@ ${info.feedback}`, revisionKey),
|
|
|
223552
223744
|
getTask: (taskId) => this.store.getTask(taskId)
|
|
223553
223745
|
},
|
|
223554
223746
|
runId: resolvedRunId,
|
|
223747
|
+
isLiveSharedBranchMember: (nodeTask) => this.isLiveSharedBranchGroupMember(nodeTask),
|
|
223555
223748
|
primitives: this.createAuthoritativeWorkflowPrimitives(settings),
|
|
223556
223749
|
seams: this.createAuthoritativeWorkflowSeams(settings),
|
|
223557
223750
|
prepareNodeExecution: (node, nodeTask, requirement) => this.prepareGraphNodeExecution(node, nodeTask, settings, requirement),
|
|
@@ -225794,6 +225987,7 @@ ${info.feedback}`, revisionKey),
|
|
|
225794
225987
|
return this.runCliAgentNode(node, await this.store.getTask(live.id), cfg);
|
|
225795
225988
|
}
|
|
225796
225989
|
const optionalGroupId = typeof graphContext?.[WORKFLOW_OPTIONAL_GROUP_CONTEXT_KEY] === "string" ? graphContext[WORKFLOW_OPTIONAL_GROUP_CONTEXT_KEY] : void 0;
|
|
225990
|
+
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
225991
|
const isCompletionSummaryNode2 = cfg.summaryTarget === "task" || node.id === "completion-summary";
|
|
225798
225992
|
if (live.executionMode === "fast" && !isCompletionSummaryNode2 && !optionalGroupId && !cfg.seam && (node.kind === "prompt" || node.kind === "script" || node.kind === "gate")) {
|
|
225799
225993
|
executorLog.debug(`${live.id}: fast mode \u2014 skipping custom graph node '${node.id}'`);
|
|
@@ -225942,6 +226136,9 @@ ${prompt}`;
|
|
|
225942
226136
|
if (optionalGroupId) {
|
|
225943
226137
|
step.optionalGroupId = optionalGroupId;
|
|
225944
226138
|
}
|
|
226139
|
+
if (declaredReviewKind) {
|
|
226140
|
+
step.reviewKind = declaredReviewKind;
|
|
226141
|
+
}
|
|
225945
226142
|
if (cfg.reviewCanFixInline === true) {
|
|
225946
226143
|
step.reviewCanFixInline = true;
|
|
225947
226144
|
}
|
|
@@ -225954,7 +226151,11 @@ ${prompt}`;
|
|
|
225954
226151
|
executorLog.debug(`${live.id}: graph node '${node.id}' runtime env injected (${injected.pathEntryCount} PATH entries, ${injected.injectedKeyCount} env keys)`);
|
|
225955
226152
|
}
|
|
225956
226153
|
const unattended = this.graphUnattendedRuns.has(live.id);
|
|
225957
|
-
|
|
226154
|
+
let outcome = mode === "script" ? await this.executeScriptWorkflowStep(live, step, worktreePath, settings, nodeEnv) : await this.executeWorkflowStep(live, step, worktreePath, settings, nodeEnv, { unattended });
|
|
226155
|
+
if (declaredReviewKind && typeof outcome.output === "string") {
|
|
226156
|
+
const parsedReviewOutput = parseWorkflowStepOutput(outcome.output, { requireVerdict: false });
|
|
226157
|
+
if (parsedReviewOutput.findings?.length) outcome = { ...outcome, findings: parsedReviewOutput.findings };
|
|
226158
|
+
}
|
|
225958
226159
|
const awaitQuestion = parseAwaitInputSentinel(outcome.output);
|
|
225959
226160
|
if (awaitQuestion) {
|
|
225960
226161
|
await this.store.logEntry(
|
|
@@ -225977,6 +226178,8 @@ ${prompt}`;
|
|
|
225977
226178
|
const contextPatch = {};
|
|
225978
226179
|
if (typeof stepOutput === "string") contextPatch.output = stepOutput;
|
|
225979
226180
|
if (typeof stepNotes === "string" && stepNotes) contextPatch.notes = stepNotes;
|
|
226181
|
+
const stepFindings = outcome.findings;
|
|
226182
|
+
if (stepFindings?.length) contextPatch.findings = stepFindings;
|
|
225980
226183
|
if (cfg.summaryTarget === "task" && typeof stepOutput === "string" && stepOutput.trim()) {
|
|
225981
226184
|
contextPatch.summary = stepOutput.trim();
|
|
225982
226185
|
}
|
|
@@ -226483,6 +226686,7 @@ Graph value: ${failureValue}` : ""}`, budget.key),
|
|
|
226483
226686
|
} catch {
|
|
226484
226687
|
return false;
|
|
226485
226688
|
}
|
|
226689
|
+
if (hasUserAutoMergeHold(live)) return false;
|
|
226486
226690
|
const sharedBranchMember = await this.isLiveSharedBranchGroupMember(live);
|
|
226487
226691
|
if (!sharedBranchMember && !allowsAutoMergeProcessing(live, settings)) return false;
|
|
226488
226692
|
if (!sharedBranchMember && resolveEffectiveAutoMerge(live, settings) === false) return false;
|
|
@@ -226544,8 +226748,8 @@ Graph value: ${failureValue}` : ""}`, budget.key),
|
|
|
226544
226748
|
} catch {
|
|
226545
226749
|
return false;
|
|
226546
226750
|
}
|
|
226547
|
-
if (await this.isLiveSharedBranchGroupMember(live)) return false;
|
|
226548
|
-
return !allowsAutoMergeProcessing(live, settings) || resolveEffectiveAutoMerge(live, settings) === false;
|
|
226751
|
+
if (await this.isLiveSharedBranchGroupMember(live) && !hasUserAutoMergeHold(live)) return false;
|
|
226752
|
+
return hasUserAutoMergeHold(live) || !allowsAutoMergeProcessing(live, settings) || resolveEffectiveAutoMerge(live, settings) === false;
|
|
226549
226753
|
}
|
|
226550
226754
|
async handleStaleInReviewPlanPauseAbortReplay(live, result, abortProvenance, pausedAborted, userCanceled, resumeLanesMemo) {
|
|
226551
226755
|
if (!pausedAborted) return false;
|
|
@@ -226709,9 +226913,9 @@ Graph value: ${failureValue}` : ""}`, budget.key),
|
|
|
226709
226913
|
if (settings.globalPause === true) return false;
|
|
226710
226914
|
}
|
|
226711
226915
|
if (live.column === resumeLanes.review) {
|
|
226712
|
-
if (live.autoMerge === false) return false;
|
|
226713
226916
|
if (!settings) return false;
|
|
226714
226917
|
const sharedBranchMember = await this.isLiveSharedBranchGroupMember(live);
|
|
226918
|
+
if (hasUserAutoMergeHold(live) || live.autoMerge === false && !sharedBranchMember) return false;
|
|
226715
226919
|
if (!sharedBranchMember && !allowsAutoMergeProcessing(live, settings)) return false;
|
|
226716
226920
|
if (live.mergeDetails?.mergeConfirmed === true) return false;
|
|
226717
226921
|
}
|
|
@@ -232479,13 +232683,14 @@ CRITICAL SCOPING RULES \u2014 read before doing anything else:
|
|
|
232479
232683
|
const isSkillStep = typeof workflowStep.skillName === "string" && workflowStep.skillName.trim().length > 0;
|
|
232480
232684
|
const isSummaryProjectionStep = workflowStep.summaryTarget === "task";
|
|
232481
232685
|
const requireVerdict = !isSummaryProjectionStep && (workflowStep.gateMode === "gate" || !isSkillStep);
|
|
232686
|
+
const reviewFindingsContract = workflowStepMetadata.reviewKind === "plan" || workflowStepMetadata.reviewKind === "code";
|
|
232482
232687
|
const verdictBlock = requireVerdict ? `
|
|
232483
232688
|
|
|
232484
232689
|
## Feedback Format
|
|
232485
232690
|
|
|
232486
232691
|
When your review is complete, your final line MUST be a single JSON object (no markdown fences):
|
|
232487
232692
|
|
|
232488
|
-
{"verdict":"APPROVE|APPROVE_WITH_NOTES|REVISE","notes":"..."}
|
|
232693
|
+
${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
232694
|
|
|
232490
232695
|
Rules:
|
|
232491
232696
|
- Output exactly one trailing JSON object and stop.
|
|
@@ -232795,7 +233000,8 @@ ${detectedQuestion}
|
|
|
232795
233000
|
revisionRequested,
|
|
232796
233001
|
output: parsed.output,
|
|
232797
233002
|
verdict: parsed.verdict,
|
|
232798
|
-
notes: parsed.notes
|
|
233003
|
+
notes: parsed.notes,
|
|
233004
|
+
...parsed.findings ? { findings: parsed.findings } : {}
|
|
232799
233005
|
};
|
|
232800
233006
|
}
|
|
232801
233007
|
if (parsed.malformed) {
|
|
@@ -247766,6 +247972,7 @@ var InProcessRuntime = class extends EventEmitter27 {
|
|
|
247766
247972
|
}
|
|
247767
247973
|
}
|
|
247768
247974
|
const prNodeGithubOps = this.config.prNodeGithubOps;
|
|
247975
|
+
const secretsStore = await this.taskStore.getSecretsStore();
|
|
247769
247976
|
const executorOptions = {
|
|
247770
247977
|
/*
|
|
247771
247978
|
FNXC:PlanReviewLease 2026-07-26-21:12:
|
|
@@ -247781,6 +247988,7 @@ var InProcessRuntime = class extends EventEmitter27 {
|
|
|
247781
247988
|
cliAgentRuntime: this.cliAgentRuntime?.bundle,
|
|
247782
247989
|
pluginRunner: this.pluginRunner,
|
|
247783
247990
|
messageStore: this.messageStore,
|
|
247991
|
+
secretsStore,
|
|
247784
247992
|
missionStore,
|
|
247785
247993
|
reflectionService,
|
|
247786
247994
|
// PR-entity nodes (U3): assemble the handler deps from the CLI-injected
|
|
@@ -247882,6 +248090,7 @@ var InProcessRuntime = class extends EventEmitter27 {
|
|
|
247882
248090
|
reflectionService,
|
|
247883
248091
|
selfImproveService,
|
|
247884
248092
|
credentialRotator: this.credentialRotator,
|
|
248093
|
+
secretsStore,
|
|
247885
248094
|
snapshotManager: autoClaimSnapshotManager,
|
|
247886
248095
|
onMissed: (agentId, reason) => {
|
|
247887
248096
|
runtimeLog.warn(`Agent ${agentId} missed heartbeat: ${reason}`);
|
|
@@ -254921,7 +255130,21 @@ var ProjectEngine = class _ProjectEngine {
|
|
|
254921
255130
|
Cleared on the first non-busy outcome (success path resets it).
|
|
254922
255131
|
*/
|
|
254923
255132
|
workspaceBusyReenqueues = /* @__PURE__ */ new Map();
|
|
255133
|
+
workspaceBusyReenqueueTimers = /* @__PURE__ */ new Set();
|
|
254924
255134
|
static WORKSPACE_BUSY_MAX_REENQUEUES = 10;
|
|
255135
|
+
/*
|
|
255136
|
+
FNXC:WorkspaceMergeDispatch 2026-08-05-23:56:
|
|
255137
|
+
Workspace lease-contention retries are engine-owned lifecycle work, not detached callbacks.
|
|
255138
|
+
Track only these busy re-enqueue timers so stop() can cancel them and tests can measure the
|
|
255139
|
+
capped workspace ladder without confusing it with merge-body or maintenance timers.
|
|
255140
|
+
*/
|
|
255141
|
+
scheduleWorkspaceBusyReenqueue(taskId, delayMs) {
|
|
255142
|
+
const timer2 = setTimeout(() => {
|
|
255143
|
+
this.workspaceBusyReenqueueTimers.delete(timer2);
|
|
255144
|
+
if (!this.shuttingDown) this.internalEnqueueMerge(taskId);
|
|
255145
|
+
}, delayMs);
|
|
255146
|
+
this.workspaceBusyReenqueueTimers.add(timer2);
|
|
255147
|
+
}
|
|
254925
255148
|
/**
|
|
254926
255149
|
* Pending manual merge resolvers — keyed by taskId.
|
|
254927
255150
|
* When `onMerge` is called, the task is enqueued like auto-merge but a
|
|
@@ -254982,7 +255205,7 @@ var ProjectEngine = class _ProjectEngine {
|
|
|
254982
255205
|
*
|
|
254983
255206
|
* Readable (not private) so tests derive the cap from this single source of truth rather
|
|
254984
255207
|
* than hardcoding it — the FN-8004 bump broke two suites that had baked in the old `3`. */
|
|
254985
|
-
static MAX_AUTO_MERGE_TRANSIENT_RETRIES =
|
|
255208
|
+
static MAX_AUTO_MERGE_TRANSIENT_RETRIES = MAX_AUTO_MERGE_TRANSIENT_RETRIES;
|
|
254986
255209
|
static MERGE_REQUEST_RETRY_EXHAUSTED_AGE_MS = 30 * 60 * 1e3;
|
|
254987
255210
|
/** Cap on outer in-review→in-progress bounces caused by deterministic
|
|
254988
255211
|
* verification failures during auto-merge. After this many failed merges
|
|
@@ -255456,6 +255679,10 @@ ${detail}`);
|
|
|
255456
255679
|
clearInterval(this.mergeActiveReconcileTimer);
|
|
255457
255680
|
this.mergeActiveReconcileTimer = null;
|
|
255458
255681
|
}
|
|
255682
|
+
for (const timer2 of this.workspaceBusyReenqueueTimers) {
|
|
255683
|
+
clearTimeout(timer2);
|
|
255684
|
+
}
|
|
255685
|
+
this.workspaceBusyReenqueueTimers.clear();
|
|
255459
255686
|
for (const [taskId, deferred] of this.capacityDeferredMerges) {
|
|
255460
255687
|
clearTimeout(deferred.timer);
|
|
255461
255688
|
for (const resolver of deferred.resolvers) {
|
|
@@ -256755,16 +256982,15 @@ ${detail}`);
|
|
|
256755
256982
|
* older low-priority task would start before a later urgent one.
|
|
256756
256983
|
*/
|
|
256757
256984
|
async allowInReviewMergeProcessing(task, settings, store = this.runtime.getTaskStore()) {
|
|
256758
|
-
if (
|
|
256759
|
-
return true;
|
|
256760
|
-
}
|
|
256985
|
+
if (hasUserAutoMergeHold(task)) return false;
|
|
256761
256986
|
const groupId = task.branchContext?.groupId?.trim();
|
|
256762
256987
|
const branchGroup = groupId ? await store.getBranchGroup?.(groupId) : null;
|
|
256763
|
-
if (!branchGroup || branchGroup.status !== "open" || !branchGroup.branchName.trim()) {
|
|
256764
|
-
return false;
|
|
256765
|
-
}
|
|
256766
256988
|
const projectDefaultBranch = await resolveIntegrationBranch(this.config.workingDirectory, settings);
|
|
256767
|
-
|
|
256989
|
+
if (isLiveSharedBranchGroupMemberIntegration(task, branchGroup, projectDefaultBranch)) {
|
|
256990
|
+
return true;
|
|
256991
|
+
}
|
|
256992
|
+
if (task.autoMerge === false && groupId) return false;
|
|
256993
|
+
return allowsAutoMergeProcessing(task, settings);
|
|
256768
256994
|
}
|
|
256769
256995
|
async emitLegacyAutoMergeStampAdvisory(store) {
|
|
256770
256996
|
if (this.legacyAutoMergeStampAdvisoryEmitted) {
|
|
@@ -257717,9 +257943,7 @@ ${detail}`);
|
|
|
257717
257943
|
runtimeLog.log(
|
|
257718
257944
|
`Workspace land busy re-enqueue ${busyCount + 1}/${_ProjectEngine.WORKSPACE_BUSY_MAX_REENQUEUES} for ${taskId} in ${delayMs / 1e3}s (no mergeRetry consumed \u2014 pure lease contention)`
|
|
257719
257945
|
);
|
|
257720
|
-
|
|
257721
|
-
if (!this.shuttingDown) this.internalEnqueueMerge(taskId);
|
|
257722
|
-
}, delayMs);
|
|
257946
|
+
this.scheduleWorkspaceBusyReenqueue(taskId, delayMs);
|
|
257723
257947
|
} else {
|
|
257724
257948
|
this.workspaceBusyReenqueues.delete(taskId);
|
|
257725
257949
|
await store.updateTask(taskId, { status: "failed", error: errorMsg }).catch(() => void 0);
|