@runfusion/fusion 0.77.0-beta.5 → 0.77.0-beta.6
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 +433 -102
- package/dist/child-process-worker.js +352 -81
- package/dist/client/.vite/manifest.json +261 -261
- package/dist/client/assets/{AgentDetailView-CSK_Rnlk.js → AgentDetailView-oyY72uSo.js} +1 -1
- package/dist/client/assets/{AgentPermissionPolicyEditor-kG44Adcm.js → AgentPermissionPolicyEditor-C3Qo9p1M.js} +1 -1
- package/dist/client/assets/{AgentsView-ClKwKWnU.js → AgentsView-M2MhTIGW.js} +3 -3
- package/dist/client/assets/{ChatView-DoOqgkPX.js → ChatView-zKrUSqiQ.js} +1 -1
- package/dist/client/assets/{CommandCenter-CJxvPbgn.js → CommandCenter-CpcAt6jt.js} +27 -27
- package/dist/client/assets/{DevServerView-C4nyLONX.js → DevServerView-CjTneoG7.js} +1 -1
- package/dist/client/assets/{DirectoryPicker-BIvXWIB5.js → DirectoryPicker-BY6V5HLV.js} +1 -1
- package/dist/client/assets/{DocumentsView-DH_aZysM.js → DocumentsView-RDK56-qg.js} +1 -1
- package/dist/client/assets/{EvalsView-klRK-GJQ.js → EvalsView-DKK4ceUW.js} +1 -1
- package/dist/client/assets/{ExperimentalAgentOnboardingModal-CgBmukIm.js → ExperimentalAgentOnboardingModal-DiBPZVwC.js} +1 -1
- package/dist/client/assets/{GoalsView-ysh3X2x6.js → GoalsView-DPj59B3l.js} +1 -1
- package/dist/client/assets/{InsightsView-BbMjCu7J.js → InsightsView-QLJNkKJp.js} +1 -1
- package/dist/client/assets/{MemoryView-B3IgA8Bx.js → MemoryView-CDCS-1ig.js} +1 -1
- package/dist/client/assets/{PiExtensionsManager-CZlIw7C3.js → PiExtensionsManager-DktXXrmF.js} +1 -1
- package/dist/client/assets/{PluginManager-vy0YZ2Nx.js → PluginManager-CQ3iEFMq.js} +1 -1
- package/dist/client/assets/{PullRequestView-DhhsJV8I.js → PullRequestView-CkObT6e3.js} +1 -1
- package/dist/client/assets/{ReportModal-CQMn4i1y.js → ReportModal-B5LTdDgi.js} +1 -1
- package/dist/client/assets/{ResearchView-CVwfs4l3.js → ResearchView-CryFrV8l.js} +1 -1
- package/dist/client/assets/{SecretsView-CrJfHle9.js → SecretsView-9YcNW_fo.js} +1 -1
- package/dist/client/assets/{SessionTerminal-BRI8W1-G.js → SessionTerminal-BWsA9puD.js} +1 -1
- package/dist/client/assets/{SettingsModal-h1A_aFqD.js → SettingsModal-CL6aiXiI.js} +4 -4
- package/dist/client/assets/{SettingsModal-B6U5Umru.js → SettingsModal-llkoaJev.js} +1 -1
- package/dist/client/assets/{SettingsTextareaRow-O7-u9uMC.js → SettingsTextareaRow-B_Legu-A.js} +1 -1
- package/dist/client/assets/{SetupWizardModal-nXfJp8cA.js → SetupWizardModal-DLA4UAGp.js} +2 -2
- package/dist/client/assets/{SkillsView-CoWAjvWU.js → SkillsView-CSeauwIw.js} +1 -1
- package/dist/client/assets/{WorkflowNodeEditor-BFMM3RF_.js → WorkflowNodeEditor-CGgMiiWy.js} +1 -1
- package/dist/client/assets/{agent-import-generation-BqDmtTur.js → agent-import-generation-CttglSmZ.js} +1 -1
- package/dist/client/assets/{agentsOrgChartLayout-C4cLqlV6.js → agentsOrgChartLayout-f-9mwRbD.js} +1 -1
- package/dist/client/assets/{architectureDiagram-3BPJPVTR-CdxcwdRe.js → architectureDiagram-3BPJPVTR-DA_Hm_di.js} +1 -1
- package/dist/client/assets/{blockDiagram-GPEHLZMM-CfQrRfHR.js → blockDiagram-GPEHLZMM-JF7K8CcV.js} +1 -1
- package/dist/client/assets/{c4Diagram-AAUBKEIU-Cx7-2SOm.js → c4Diagram-AAUBKEIU-DN-wklCP.js} +1 -1
- package/dist/client/assets/channel-CwFfFSI9.js +1 -0
- package/dist/client/assets/{chunk-2J33WTMH-D8jvgP3M.js → chunk-2J33WTMH-ChtjU8Cu.js} +1 -1
- package/dist/client/assets/{chunk-4BX2VUAB-B5tBx_s_.js → chunk-4BX2VUAB-dJL0n3nn.js} +1 -1
- package/dist/client/assets/{chunk-55IACEB6-C-F-HQ5Z.js → chunk-55IACEB6-Bfg-QB9A.js} +1 -1
- package/dist/client/assets/{chunk-727SXJPM-BP8Ln-8C.js → chunk-727SXJPM-BHPPwYUS.js} +1 -1
- package/dist/client/assets/{chunk-AQP2D5EJ-DMnTmRmn.js → chunk-AQP2D5EJ-DIIpPUQN.js} +1 -1
- package/dist/client/assets/{chunk-FMBD7UC4-D_YdfnNc.js → chunk-FMBD7UC4-D230R32b.js} +1 -1
- package/dist/client/assets/{chunk-ND2GUHAM-CULXyuXq.js → chunk-ND2GUHAM-0Lhp5CRv.js} +1 -1
- package/dist/client/assets/{chunk-QZHKN3VN-CT1BWEJL.js → chunk-QZHKN3VN-BxbHBfKJ.js} +1 -1
- package/dist/client/assets/classDiagram-4FO5ZUOK-C8dZsxxX.js +1 -0
- package/dist/client/assets/classDiagram-v2-Q7XG4LA2-C8dZsxxX.js +1 -0
- package/dist/client/assets/{cose-bilkent-S5V4N54A-M-3DXa6e.js → cose-bilkent-S5V4N54A-3vEwhfYz.js} +1 -1
- package/dist/client/assets/{dagre-BM42HDAG-Bsiqvroz.js → dagre-BM42HDAG-Br44UsdY.js} +1 -1
- package/dist/client/assets/{dashboard-view-Bo_gFjdO.js → dashboard-view-B0OM3KYC.js} +1 -1
- package/dist/client/assets/{dashboard-view-CnFFG3cW.js → dashboard-view-BldxxYfz.js} +1 -1
- package/dist/client/assets/{dashboard-view-DAHn20ph.js → dashboard-view-CcPcELPx.js} +1 -1
- package/dist/client/assets/{dashboard-view-BEBh_Ei5.js → dashboard-view-_EglxMDl.js} +1 -1
- package/dist/client/assets/{diagram-2AECGRRQ-CZiHoXo5.js → diagram-2AECGRRQ-CLNxbHUP.js} +1 -1
- package/dist/client/assets/{diagram-5GNKFQAL-BoJVz4Or.js → diagram-5GNKFQAL-D3cNAeQH.js} +1 -1
- package/dist/client/assets/{diagram-KO2AKTUF-DivGQuAL.js → diagram-KO2AKTUF-DkX7h6to.js} +1 -1
- package/dist/client/assets/{diagram-LMA3HP47-DkMK7OJp.js → diagram-LMA3HP47-6iQIiYUr.js} +1 -1
- package/dist/client/assets/{diagram-OG6HWLK6-DFFvRcpW.js → diagram-OG6HWLK6-Caje3229.js} +1 -1
- package/dist/client/assets/{erDiagram-TEJ5UH35--217vrom.js → erDiagram-TEJ5UH35-BkU-1HB1.js} +1 -1
- package/dist/client/assets/{flowDiagram-I6XJVG4X-CQgWO3xf.js → flowDiagram-I6XJVG4X-BRx3V6RE.js} +1 -1
- package/dist/client/assets/{folder-open-DR6cRcNR.js → folder-open-BaSOBfk2.js} +1 -1
- package/dist/client/assets/{ganttDiagram-6RSMTGT7-BjDMUrS3.js → ganttDiagram-6RSMTGT7-D9f1kH3t.js} +1 -1
- package/dist/client/assets/{gitGraphDiagram-PVQCEYII-gKUcDnln.js → gitGraphDiagram-PVQCEYII-CQwmZcRd.js} +1 -1
- package/dist/client/assets/{index-oxnX59eO.js → index-B8PcG8AJ.js} +298 -298
- package/dist/client/assets/{infoDiagram-5YYISTIA-BA91T8S5.js → infoDiagram-5YYISTIA-DYWBkGjG.js} +1 -1
- package/dist/client/assets/{ishikawaDiagram-YF4QCWOH-0BY5xe-c.js → ishikawaDiagram-YF4QCWOH-Df0Nzvh4.js} +1 -1
- package/dist/client/assets/{journeyDiagram-JHISSGLW-B_OwenbQ.js → journeyDiagram-JHISSGLW-bSvrZSyZ.js} +1 -1
- package/dist/client/assets/{kanban-definition-UN3LZRKU-GLabUTKp.js → kanban-definition-UN3LZRKU-yHJ101M8.js} +1 -1
- package/dist/client/assets/{mermaid.core-BXo4rxYI.js → mermaid.core-Bc_0VLJw.js} +4 -4
- package/dist/client/assets/{mindmap-definition-RKZ34NQL-DZl6LPoE.js → mindmap-definition-RKZ34NQL-DWRnMguM.js} +1 -1
- package/dist/client/assets/{pieDiagram-4H26LBE5-DtHq-4py.js → pieDiagram-4H26LBE5-Ds-DEOqr.js} +1 -1
- package/dist/client/assets/{puzzle-OVm-I1L4.js → puzzle-CdYF81IT.js} +1 -1
- package/dist/client/assets/{quadrantDiagram-W4KKPZXB-BdjXhd5d.js → quadrantDiagram-W4KKPZXB-Bz-j2fkf.js} +1 -1
- package/dist/client/assets/{requirementDiagram-4Y6WPE33-BoFVjELN.js → requirementDiagram-4Y6WPE33-BaQ_PaZ-.js} +1 -1
- package/dist/client/assets/{sankeyDiagram-5OEKKPKP-B0qyJh-P.js → sankeyDiagram-5OEKKPKP-pUCz8V4-.js} +1 -1
- package/dist/client/assets/{sequenceDiagram-3UESZ5HK-ByY62Eqp.js → sequenceDiagram-3UESZ5HK-CFwUMspe.js} +1 -1
- package/dist/client/assets/{shield-alert-CDy69fl7.js → shield-alert-4qeU3_Rm.js} +1 -1
- package/dist/client/assets/{standing-instructions-template-BsnnRlwm.js → standing-instructions-template-Crocrob7.js} +1 -1
- package/dist/client/assets/{stateDiagram-AJRCARHV-CwJ-_Phi.js → stateDiagram-AJRCARHV-CHZwo-vd.js} +1 -1
- package/dist/client/assets/{stateDiagram-v2-BHNVJYJU-Cfvhq8mV.js → stateDiagram-v2-BHNVJYJU-BTvKaIZR.js} +1 -1
- package/dist/client/assets/{timeline-definition-PNZ67QCA-Co8VfGN9.js → timeline-definition-PNZ67QCA-Cn3XIoz2.js} +1 -1
- package/dist/client/assets/{upload-CkqDbEeE.js → upload--GqImz6h.js} +1 -1
- package/dist/client/assets/{useDiscoveredSkillsCache-BHUasRiZ.js → useDiscoveredSkillsCache-BEHgTFai.js} +1 -1
- package/dist/client/assets/{users-B8DkpbXD.js → users-B0h1q4x6.js} +1 -1
- package/dist/client/assets/{vennDiagram-CIIHVFJN-XSJteQrL.js → vennDiagram-CIIHVFJN-CS0FNVU_.js} +1 -1
- package/dist/client/assets/{wardley-L42UT6IY-Btc0FRfF.js → wardley-L42UT6IY-DdYkfF5d.js} +1 -1
- package/dist/client/assets/{wardleyDiagram-YWT4CUSO--IcDNmrf.js → wardleyDiagram-YWT4CUSO-B_rFmPCh.js} +1 -1
- package/dist/client/assets/{xychartDiagram-2RQKCTM6-CZW93G6X.js → xychartDiagram-2RQKCTM6-CCoaCgmc.js} +1 -1
- package/dist/client/index.html +1 -1
- package/dist/client/version.json +1 -1
- package/dist/droid-cli/package.json +1 -1
- package/dist/extension.js +381 -87
- package/dist/pi-claude-cli/package.json +1 -1
- package/dist/plugin-sdk/index.d.ts +9 -0
- package/dist/plugins/fusion-plugin-acp-runtime/package.json +1 -1
- package/dist/plugins/fusion-plugin-claude-runtime/package.json +1 -1
- package/dist/plugins/fusion-plugin-cli-printing-press/package.json +1 -1
- package/dist/plugins/fusion-plugin-compound-engineering/package.json +1 -1
- package/dist/plugins/fusion-plugin-cursor-runtime/package.json +1 -1
- package/dist/plugins/fusion-plugin-dependency-graph/package.json +1 -1
- package/dist/plugins/fusion-plugin-droid-runtime/package.json +1 -1
- package/dist/plugins/fusion-plugin-grok-runtime/package.json +1 -1
- package/dist/plugins/fusion-plugin-hermes-runtime/package.json +1 -1
- package/dist/plugins/fusion-plugin-linear-import/package.json +1 -1
- package/dist/plugins/fusion-plugin-omp-runtime/package.json +1 -1
- package/dist/plugins/fusion-plugin-openclaw-runtime/package.json +1 -1
- package/dist/plugins/fusion-plugin-paperclip-runtime/package.json +1 -1
- package/dist/plugins/fusion-plugin-quality/package.json +1 -1
- package/dist/plugins/fusion-plugin-reports/package.json +1 -1
- package/dist/plugins/fusion-plugin-roadmap/package.json +1 -1
- package/dist/plugins/fusion-plugin-todos/package.json +1 -1
- package/dist/plugins/fusion-plugin-whatsapp-chat/package.json +1 -1
- package/package.json +1 -1
- package/dist/client/assets/channel-B4WN1jC_.js +0 -1
- package/dist/client/assets/classDiagram-4FO5ZUOK-CKE8RSOv.js +0 -1
- package/dist/client/assets/classDiagram-v2-Q7XG4LA2-CKE8RSOv.js +0 -1
package/dist/extension.js
CHANGED
|
@@ -152,10 +152,16 @@ function resolveEntryPointBranchAssignment(input) {
|
|
|
152
152
|
};
|
|
153
153
|
}
|
|
154
154
|
}
|
|
155
|
-
var isValidTaskBranchName;
|
|
155
|
+
var BranchWriteProvenanceError, isValidTaskBranchName;
|
|
156
156
|
var init_branch_assignment = __esm({
|
|
157
157
|
"../core/src/branch/branch-assignment.ts"() {
|
|
158
158
|
"use strict";
|
|
159
|
+
BranchWriteProvenanceError = class extends Error {
|
|
160
|
+
constructor() {
|
|
161
|
+
super("branchWriteOrigin is required when branch is provided");
|
|
162
|
+
this.name = "BranchWriteProvenanceError";
|
|
163
|
+
}
|
|
164
|
+
};
|
|
159
165
|
isValidTaskBranchName = isValidBranchGroupBranchName;
|
|
160
166
|
}
|
|
161
167
|
});
|
|
@@ -4533,8 +4539,8 @@ var init_research_settings = __esm({
|
|
|
4533
4539
|
function resolveResearchFindingId(finding) {
|
|
4534
4540
|
const explicitId = typeof finding.id === "string" ? finding.id.trim() : "";
|
|
4535
4541
|
if (explicitId) return explicitId;
|
|
4536
|
-
const
|
|
4537
|
-
const canonical = [
|
|
4542
|
+
const normalize10 = (value) => value.trim().replace(/\s+/g, " ").toLowerCase();
|
|
4543
|
+
const canonical = [normalize10(finding.heading ?? ""), normalize10(finding.content ?? ""), ...[...finding.sources ?? []].map(normalize10).filter(Boolean).sort()].join("\n");
|
|
4538
4544
|
let hash3 = 2166136261;
|
|
4539
4545
|
for (let index2 = 0; index2 < canonical.length; index2 += 1) {
|
|
4540
4546
|
hash3 ^= canonical.charCodeAt(index2);
|
|
@@ -35988,17 +35994,17 @@ var init_agent_store = __esm({
|
|
|
35988
35994
|
* @returns Matching agent when unambiguous; otherwise null
|
|
35989
35995
|
*/
|
|
35990
35996
|
async resolveAgent(shortname) {
|
|
35991
|
-
const
|
|
35997
|
+
const normalize10 = (value) => value.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
|
|
35992
35998
|
const all = await this.listAgents();
|
|
35993
35999
|
const exact = all.find((agent) => agent.id === shortname);
|
|
35994
36000
|
if (exact) {
|
|
35995
36001
|
return exact;
|
|
35996
36002
|
}
|
|
35997
|
-
const normalizedTarget =
|
|
36003
|
+
const normalizedTarget = normalize10(shortname);
|
|
35998
36004
|
if (!normalizedTarget) {
|
|
35999
36005
|
return null;
|
|
36000
36006
|
}
|
|
36001
|
-
const matches = all.filter((agent) =>
|
|
36007
|
+
const matches = all.filter((agent) => normalize10(agent.name) === normalizedTarget);
|
|
36002
36008
|
return matches.length === 1 ? matches[0] : null;
|
|
36003
36009
|
}
|
|
36004
36010
|
// ─────────────────────────────────────────────────────────────────────────
|
|
@@ -45240,11 +45246,11 @@ function withWindowsNativeBinPath(environment, nativeRoot, platform4 = process.p
|
|
|
45240
45246
|
if (platform4 !== "win32") return environment;
|
|
45241
45247
|
const pathKey = Object.keys(environment).find((key) => key.toLowerCase() === "path") ?? "PATH";
|
|
45242
45248
|
const bin = nativeRoot.replace(/[\\/]+$/, "") + "\\bin";
|
|
45243
|
-
const
|
|
45249
|
+
const normalize10 = (value) => value.replace(/\//g, "\\").toLowerCase();
|
|
45244
45250
|
const inherited = environment[pathKey] ?? "";
|
|
45245
45251
|
const segments = inherited.split(";").filter(Boolean);
|
|
45246
|
-
const normalizedBin =
|
|
45247
|
-
const deduplicated = segments.filter((segment) =>
|
|
45252
|
+
const normalizedBin = normalize10(bin);
|
|
45253
|
+
const deduplicated = segments.filter((segment) => normalize10(segment) !== normalizedBin);
|
|
45248
45254
|
return { ...environment, [pathKey]: [bin, ...deduplicated].join(";") };
|
|
45249
45255
|
}
|
|
45250
45256
|
function readPostgresPid(dataDir) {
|
|
@@ -67193,14 +67199,14 @@ function resolveGlobalDir(dir) {
|
|
|
67193
67199
|
const isFusionDirInsideRepo = basename8(dir) === ".fusion" && existsSync14(join19(dirname11(dir), ".git"));
|
|
67194
67200
|
if (isFusionDirInsideRepo) {
|
|
67195
67201
|
const homeGlobalDir = resolveGlobalDirForHome(getHomeDir2());
|
|
67196
|
-
const
|
|
67202
|
+
const normalize10 = (p2) => {
|
|
67197
67203
|
try {
|
|
67198
67204
|
return realpathSync3.native(p2);
|
|
67199
67205
|
} catch {
|
|
67200
67206
|
return resolve13(p2);
|
|
67201
67207
|
}
|
|
67202
67208
|
};
|
|
67203
|
-
if (
|
|
67209
|
+
if (normalize10(dir) !== normalize10(homeGlobalDir)) {
|
|
67204
67210
|
throw new Error(
|
|
67205
67211
|
`resolveGlobalDir(): refusing project-local '.fusion' directory '${dir}' for the central/global store. This would create a stray per-project central database seeded with default global settings and silently reset them. Pass the resolved global dir (or omit the argument so it defaults to ~/.fusion); see TaskStore.getGlobalSettingsDir(). If this really is your intended global dir (e.g. a version-controlled dotfiles repo), set FUSION_ALLOW_PROJECT_LOCAL_GLOBAL_DIR=true to override.`
|
|
67206
67212
|
);
|
|
@@ -87927,15 +87933,15 @@ async function appendRecall(layer, input) {
|
|
|
87927
87933
|
async function mergeRecallGraphNodeIds(layer, recordId, addGraphNodeIds) {
|
|
87928
87934
|
const pid = projectId(layer);
|
|
87929
87935
|
const table = project_exports.memoryRecallRecords;
|
|
87930
|
-
const
|
|
87936
|
+
const normalize10 = (ids) => [...new Set(ids.map((id) => id.trim()).filter(Boolean))].sort();
|
|
87931
87937
|
return layer.transactionImmediate(async (tx) => {
|
|
87932
87938
|
await tx.execute(sql`SELECT pg_advisory_xact_lock(hashtext(${recallGraphNodeIdsLockKey(pid, recordId)}))`);
|
|
87933
87939
|
const rows = await tx.select().from(table).where(and(eq(table.projectId, pid), eq(table.id, recordId))).limit(1);
|
|
87934
87940
|
await recallGraphNodeIdsTestHooks?.afterRowRead?.();
|
|
87935
87941
|
const row = rows[0];
|
|
87936
87942
|
if (!row) return { status: "missing" };
|
|
87937
|
-
const stored =
|
|
87938
|
-
const next =
|
|
87943
|
+
const stored = normalize10(row.graphNodeIds);
|
|
87944
|
+
const next = normalize10([...stored, ...addGraphNodeIds]);
|
|
87939
87945
|
if (stored.length === next.length && stored.every((value, index2) => value === next[index2])) return { status: "unchanged" };
|
|
87940
87946
|
await tx.update(table).set({ graphNodeIds: next, updatedAt: (/* @__PURE__ */ new Date()).toISOString() }).where(and(eq(table.projectId, pid), eq(table.id, recordId)));
|
|
87941
87947
|
return { status: "updated" };
|
|
@@ -92338,7 +92344,7 @@ import { randomUUID as randomUUID31 } from "node:crypto";
|
|
|
92338
92344
|
function normalizeCreateBranchProvenance(input) {
|
|
92339
92345
|
if (input.branch === void 0) return input;
|
|
92340
92346
|
if (input.branchWriteOrigin !== "operator" && input.branchWriteOrigin !== "engine") {
|
|
92341
|
-
throw new
|
|
92347
|
+
throw new BranchWriteProvenanceError();
|
|
92342
92348
|
}
|
|
92343
92349
|
validateTaskBranchName(input.branch);
|
|
92344
92350
|
if (input.branchWriteOrigin === "operator") {
|
|
@@ -94555,7 +94561,7 @@ async function updateTaskRepositoryScopeImpl(store, id, requestedScope) {
|
|
|
94555
94561
|
...replacement,
|
|
94556
94562
|
repositories: nextRepositories,
|
|
94557
94563
|
revision: Math.max((current.repositoryScope?.revision ?? 0) + 1, replacement.revision ?? 0),
|
|
94558
|
-
...scopeChanged ? { reviewEvidence: void 0 } : {}
|
|
94564
|
+
...scopeChanged ? { reviewEvidence: void 0, reviewRemediation: void 0 } : {}
|
|
94559
94565
|
};
|
|
94560
94566
|
const [updatedRow] = await tx.update(project_exports.tasks).set({
|
|
94561
94567
|
repositoryScope: normalized2 ?? null,
|
|
@@ -94571,6 +94577,35 @@ async function updateTaskRepositoryScopeImpl(store, id, requestedScope) {
|
|
|
94571
94577
|
return outcome;
|
|
94572
94578
|
}));
|
|
94573
94579
|
}
|
|
94580
|
+
async function updateWorkspaceReviewStateImpl(store, id, expectedScopeRevision, reviewRemediation) {
|
|
94581
|
+
return store.withTaskLock(id, async () => {
|
|
94582
|
+
const layer = store.asyncLayer;
|
|
94583
|
+
const outcome = await layer.transactionImmediate(async (tx) => {
|
|
94584
|
+
await acquireTaskAdvisoryXactLock(tx, layer.projectId, id);
|
|
94585
|
+
const row = await readTaskRowInTransaction(tx, id, { includeDeleted: true }, layer.projectId);
|
|
94586
|
+
if (!row) throw new TaskNotFoundError(id);
|
|
94587
|
+
if (row.deletedAt) throw new TaskDeletedError(id, row.deletedAt);
|
|
94588
|
+
const current = store.rowToTask(store.pgRowToTaskRow(row));
|
|
94589
|
+
if (current.repositoryScope?.revision !== expectedScopeRevision) return { task: current, updated: false };
|
|
94590
|
+
const repositoryScope = {
|
|
94591
|
+
...current.repositoryScope,
|
|
94592
|
+
...reviewRemediation ? { reviewRemediation } : { reviewRemediation: void 0 }
|
|
94593
|
+
};
|
|
94594
|
+
const [updatedRow] = await tx.update(project_exports.tasks).set({
|
|
94595
|
+
repositoryScope,
|
|
94596
|
+
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
94597
|
+
}).where(and(eq(project_exports.tasks.id, id), taskProjectScope(layer))).returning();
|
|
94598
|
+
if (!updatedRow) throw new TaskNotFoundError(id);
|
|
94599
|
+
return { task: store.rowToTask(store.pgRowToTaskRow(updatedRow)), updated: true };
|
|
94600
|
+
});
|
|
94601
|
+
if (outcome.updated) {
|
|
94602
|
+
await store.writeTaskJsonFile(store.taskDir(id), outcome.task);
|
|
94603
|
+
if (store.isWatching) store.taskCache.set(id, { ...outcome.task });
|
|
94604
|
+
store.emitTaskLifecycleEventSafely("task:updated", [outcome.task]);
|
|
94605
|
+
}
|
|
94606
|
+
return outcome;
|
|
94607
|
+
});
|
|
94608
|
+
}
|
|
94574
94609
|
async function resolveTaskWedgeNotificationEpisodeImpl(store, id, episodeId) {
|
|
94575
94610
|
const layer = store.asyncLayer;
|
|
94576
94611
|
const row = await resolveActiveTaskWedgeEpisodeRow(layer, id, episodeId, (/* @__PURE__ */ new Date()).toISOString());
|
|
@@ -103374,7 +103409,7 @@ async function updateTaskUnlockedImpl(store, id, updates, runContext) {
|
|
|
103374
103409
|
if (updates.recommendations !== void 0) assertValidRecommendations(updates.recommendations);
|
|
103375
103410
|
if (updates.branch !== void 0) {
|
|
103376
103411
|
if (updates.branchWriteOrigin !== "operator" && updates.branchWriteOrigin !== "engine") {
|
|
103377
|
-
throw new
|
|
103412
|
+
throw new BranchWriteProvenanceError();
|
|
103378
103413
|
}
|
|
103379
103414
|
if (updates.branch !== null) validateTaskBranchName(updates.branch);
|
|
103380
103415
|
}
|
|
@@ -103463,7 +103498,7 @@ async function updateTaskUnlockedImpl(store, id, updates, runContext) {
|
|
|
103463
103498
|
task.repositoryScope = void 0;
|
|
103464
103499
|
} else if (updates.repositoryScope !== void 0) {
|
|
103465
103500
|
const scopeRevisionChanged = task.repositoryScope?.revision !== updates.repositoryScope.revision;
|
|
103466
|
-
task.repositoryScope = scopeRevisionChanged ? { ...updates.repositoryScope, reviewEvidence: void 0 } : updates.repositoryScope;
|
|
103501
|
+
task.repositoryScope = scopeRevisionChanged ? { ...updates.repositoryScope, reviewEvidence: void 0, reviewRemediation: void 0 } : updates.repositoryScope;
|
|
103467
103502
|
if (scopeRevisionChanged) {
|
|
103468
103503
|
task.workflowStepResults = invalidateSupersededRepositoryScopeReviews(
|
|
103469
103504
|
task.workflowStepResults,
|
|
@@ -105963,6 +105998,9 @@ var init_store = __esm({
|
|
|
105963
105998
|
async mutateTaskRepositoryScope(id, mutation) {
|
|
105964
105999
|
return updateTaskRepositoryScopeImpl(this, id, mutation);
|
|
105965
106000
|
}
|
|
106001
|
+
async updateWorkspaceReviewState(id, expectedScopeRevision, reviewRemediation) {
|
|
106002
|
+
return updateWorkspaceReviewStateImpl(this, id, expectedScopeRevision, reviewRemediation);
|
|
106003
|
+
}
|
|
105966
106004
|
async resolveTaskWedgeNotificationEpisode(id, episodeId) {
|
|
105967
106005
|
return resolveTaskWedgeNotificationEpisodeImpl(this, id, episodeId);
|
|
105968
106006
|
}
|
|
@@ -135970,6 +136008,7 @@ __export(src_exports, {
|
|
|
135970
136008
|
BUNDLED_PLUGIN_IDS: () => BUNDLED_PLUGIN_IDS,
|
|
135971
136009
|
BackupManager: () => BackupManager,
|
|
135972
136010
|
BackwardCompat: () => BackwardCompat,
|
|
136011
|
+
BranchWriteProvenanceError: () => BranchWriteProvenanceError,
|
|
135973
136012
|
CENTRAL_BACKUP_SCHEMAS: () => CENTRAL_BACKUP_SCHEMAS,
|
|
135974
136013
|
CLAUDE_SONNET_5_MODEL_ID: () => CLAUDE_SONNET_5_MODEL_ID,
|
|
135975
136014
|
CLEAN_COMPLETION_MARKERS: () => CLEAN_COMPLETION_MARKERS,
|
|
@@ -157868,8 +157907,8 @@ var require_resolve = __commonJS({
|
|
|
157868
157907
|
}
|
|
157869
157908
|
return count2;
|
|
157870
157909
|
}
|
|
157871
|
-
function getFullPath(resolver, id = "",
|
|
157872
|
-
if (
|
|
157910
|
+
function getFullPath(resolver, id = "", normalize10) {
|
|
157911
|
+
if (normalize10 !== false)
|
|
157873
157912
|
id = normalizeId2(id);
|
|
157874
157913
|
const p2 = resolver.parse(id);
|
|
157875
157914
|
return _getFullPath(resolver, p2);
|
|
@@ -159209,7 +159248,7 @@ var require_fast_uri = __commonJS({
|
|
|
159209
159248
|
"use strict";
|
|
159210
159249
|
var { normalizeIPv6, removeDotSegments, recomposeAuthority, normalizeComponentEncoding, isIPv4, nonSimpleDomain } = require_utils();
|
|
159211
159250
|
var { SCHEMES, getSchemeHandler } = require_schemes();
|
|
159212
|
-
function
|
|
159251
|
+
function normalize10(uri, options) {
|
|
159213
159252
|
if (typeof uri === "string") {
|
|
159214
159253
|
uri = /** @type {T} */
|
|
159215
159254
|
serialize(parse4(uri, options), options);
|
|
@@ -159445,7 +159484,7 @@ var require_fast_uri = __commonJS({
|
|
|
159445
159484
|
}
|
|
159446
159485
|
var fastUri = {
|
|
159447
159486
|
SCHEMES,
|
|
159448
|
-
normalize:
|
|
159487
|
+
normalize: normalize10,
|
|
159449
159488
|
resolve: resolve63,
|
|
159450
159489
|
resolveComponent,
|
|
159451
159490
|
equal,
|
|
@@ -174866,6 +174905,39 @@ var init_worktrunk_failure_handler = __esm({
|
|
|
174866
174905
|
}
|
|
174867
174906
|
});
|
|
174868
174907
|
|
|
174908
|
+
// ../engine/src/executor/workspace-review-remediation.ts
|
|
174909
|
+
function normalize8(value) {
|
|
174910
|
+
return (value ?? "").replace(/\s+/g, " ").trim().toLowerCase();
|
|
174911
|
+
}
|
|
174912
|
+
function resolveWorkspaceReviewRemediationRepository(task, declaredRepositories) {
|
|
174913
|
+
const scope = task.repositoryScope;
|
|
174914
|
+
const remediation = scope?.reviewRemediation;
|
|
174915
|
+
if (!remediation) return void 0;
|
|
174916
|
+
if (scope?.state !== "confirmed" || scope.revision !== remediation.scopeRevision) {
|
|
174917
|
+
throw new Error(`Workspace Code Review remediation target is stale for ${task.id}`);
|
|
174918
|
+
}
|
|
174919
|
+
if (!declaredRepositories.includes(remediation.repository)) {
|
|
174920
|
+
throw new Error(`Workspace Code Review remediation repository ${remediation.repository} is not declared for ${task.id}`);
|
|
174921
|
+
}
|
|
174922
|
+
return remediation.repository;
|
|
174923
|
+
}
|
|
174924
|
+
function deriveWorkspaceReviewRemediation(result2) {
|
|
174925
|
+
if (typeof result2.repositoryScopeRevision !== "number") return void 0;
|
|
174926
|
+
const blocking = (result2.repositoryReviewOutcomes ?? []).filter((outcome) => outcome.status === "REVIEWED" && (outcome.verdict === "REVISE" || outcome.verdict === "RETHINK")).sort((left, right) => left.repository.localeCompare(right.repository))[0];
|
|
174927
|
+
if (!blocking) return void 0;
|
|
174928
|
+
const findings = (blocking.findings ?? []).map((finding) => `${finding.id}:${normalize8(finding.title)}:${normalize8(finding.body)}`).sort().join("|");
|
|
174929
|
+
return {
|
|
174930
|
+
scopeRevision: result2.repositoryScopeRevision,
|
|
174931
|
+
repository: blocking.repository,
|
|
174932
|
+
inputSignature: `${result2.workflowStepId}\0${blocking.repository}\0${blocking.fingerprint ?? ""}\0${blocking.verdict}\0${findings}`
|
|
174933
|
+
};
|
|
174934
|
+
}
|
|
174935
|
+
var init_workspace_review_remediation = __esm({
|
|
174936
|
+
"../engine/src/executor/workspace-review-remediation.ts"() {
|
|
174937
|
+
"use strict";
|
|
174938
|
+
}
|
|
174939
|
+
});
|
|
174940
|
+
|
|
174869
174941
|
// ../engine/src/worktree/worktree-copy-files.ts
|
|
174870
174942
|
import { constants as fsConstants3 } from "node:fs";
|
|
174871
174943
|
import { access as access7, copyFile as copyFile2, lstat as lstat2, mkdir as mkdir26 } from "node:fs/promises";
|
|
@@ -175252,14 +175324,15 @@ function invalidateWorkspaceConfigCache(owner) {
|
|
|
175252
175324
|
workspaceConfigEpochs.set(owner, (workspaceConfigEpochs.get(owner) ?? 0) + 1);
|
|
175253
175325
|
}
|
|
175254
175326
|
async function normalizeWorkspaceTaskRouting(store, taskId) {
|
|
175255
|
-
const
|
|
175256
|
-
if (typeof
|
|
175327
|
+
const normalize10 = store.normalizeWorkspaceTaskWorktreeMetadata;
|
|
175328
|
+
if (typeof normalize10 === "function") return normalize10.call(store, taskId);
|
|
175257
175329
|
let task = await store.getTask(taskId);
|
|
175258
175330
|
if (task.worktree || task.branch || task.executionStartBranch || task.baseCommitSha) {
|
|
175259
175331
|
if (typeof store.updateTask !== "function") return task;
|
|
175260
175332
|
await store.updateTask(taskId, {
|
|
175261
175333
|
worktree: null,
|
|
175262
175334
|
branch: null,
|
|
175335
|
+
branchWriteOrigin: "engine",
|
|
175263
175336
|
executionStartBranch: null,
|
|
175264
175337
|
baseCommitSha: null,
|
|
175265
175338
|
sessionFile: null
|
|
@@ -175461,11 +175534,15 @@ async function ensureWorkspaceGroupOwnership(workspaceContext, settings) {
|
|
|
175461
175534
|
async function acquireTaskWorktree(opts) {
|
|
175462
175535
|
const { task, rootDir, store, settings, pool, logger: logger3, audit, runContext, createWorktree: createWorktree2, runConfiguredCommand: runConfiguredCommand2, runInitCommand, taskEnv, secretsStore, workspaceContext } = opts;
|
|
175463
175536
|
const persistWorktreeAssignment = async (patch) => {
|
|
175537
|
+
const provenancePatch = patch.branch === void 0 ? patch : {
|
|
175538
|
+
...patch,
|
|
175539
|
+
branchWriteOrigin: patch.branchWriteOrigin ?? (classifyTaskBranchOrigin(task, patch.branch ?? void 0) === "operator-supplied" ? "operator" : "engine")
|
|
175540
|
+
};
|
|
175464
175541
|
if (!opts.suppressSingularWorktreePersist) {
|
|
175465
|
-
await store.updateTask(task.id,
|
|
175542
|
+
await store.updateTask(task.id, provenancePatch);
|
|
175466
175543
|
return;
|
|
175467
175544
|
}
|
|
175468
|
-
const { worktree: _worktree, branch: _branch, ...nonSingularPatch } =
|
|
175545
|
+
const { worktree: _worktree, branch: _branch, branchWriteOrigin: _origin, ...nonSingularPatch } = provenancePatch;
|
|
175469
175546
|
if (Object.keys(nonSingularPatch).length > 0) {
|
|
175470
175547
|
await store.updateTask(task.id, nonSingularPatch);
|
|
175471
175548
|
}
|
|
@@ -175702,7 +175779,22 @@ async function acquireTaskWorktree(opts) {
|
|
|
175702
175779
|
}
|
|
175703
175780
|
worktreePath = created2.path;
|
|
175704
175781
|
branch = created2.branch;
|
|
175705
|
-
|
|
175782
|
+
try {
|
|
175783
|
+
await persistWorktreeAssignment({ worktree: created2.path, branch: created2.branch });
|
|
175784
|
+
} catch (error3) {
|
|
175785
|
+
try {
|
|
175786
|
+
await removeWorktree({
|
|
175787
|
+
worktreePath: created2.path,
|
|
175788
|
+
rootDir,
|
|
175789
|
+
settings,
|
|
175790
|
+
taskId: task.id,
|
|
175791
|
+
reason: RemovalReason.ExecutorDispose
|
|
175792
|
+
});
|
|
175793
|
+
} catch (cleanupError) {
|
|
175794
|
+
logger3?.warn(`${task.id}: failed to compensate unpersisted worktree ${created2.path}: ${formatError(cleanupError)}`);
|
|
175795
|
+
}
|
|
175796
|
+
throw error3;
|
|
175797
|
+
}
|
|
175706
175798
|
await audit?.git({ type: "worktree:create", target: created2.path, metadata: { branch: created2.branch, source: logOrigin === "return-guard" ? "acquire-return-guard" : void 0 } });
|
|
175707
175799
|
await audit?.git({ type: "branch:create", target: created2.branch });
|
|
175708
175800
|
if (created2.branch !== branchName) {
|
|
@@ -175944,6 +176036,17 @@ async function acquireTaskWorktree(opts) {
|
|
|
175944
176036
|
if (pinned) {
|
|
175945
176037
|
return acquirePinnedWorktree();
|
|
175946
176038
|
}
|
|
176039
|
+
if (!workspaceContext && !task.worktree && !(pool && settings.recycleWorktrees)) {
|
|
176040
|
+
const registeredMatches = (await getRegisteredWorktreeBranches(rootDir)).filter((entry) => entry.branch === branchName);
|
|
176041
|
+
if (registeredMatches.length === 1) {
|
|
176042
|
+
const registeredPath = registeredMatches[0].worktreePath;
|
|
176043
|
+
const classification = await classifyTaskWorktree(rootDir, registeredPath);
|
|
176044
|
+
if (classification.ok) {
|
|
176045
|
+
await persistWorktreeAssignment({ worktree: registeredPath, branch: branchName });
|
|
176046
|
+
return reuseWarmWorktree(registeredPath, branchName, "existing");
|
|
176047
|
+
}
|
|
176048
|
+
}
|
|
176049
|
+
}
|
|
175947
176050
|
if (task.worktree && isResume) {
|
|
175948
176051
|
if (logger3?.debug) logger3.debug(`Reusing existing worktree: ${worktreePath}`);
|
|
175949
176052
|
const cleanup = await removeDesktopBuildArtifacts(worktreePath, logger3);
|
|
@@ -175979,6 +176082,7 @@ async function acquireTaskWorktree(opts) {
|
|
|
175979
176082
|
}
|
|
175980
176083
|
}
|
|
175981
176084
|
if (pooled) {
|
|
176085
|
+
let poolAssignmentPersistenceStarted = false;
|
|
175982
176086
|
try {
|
|
175983
176087
|
const preparedRaw = await pool.prepareForTask(pooled, branchName, freshStartPoint, {
|
|
175984
176088
|
allowSiblingBranchRename,
|
|
@@ -176036,7 +176140,9 @@ async function acquireTaskWorktree(opts) {
|
|
|
176036
176140
|
});
|
|
176037
176141
|
acquiredFromPool = true;
|
|
176038
176142
|
logger3?.log(`Acquired worktree from pool: ${worktreePath}`);
|
|
176143
|
+
poolAssignmentPersistenceStarted = true;
|
|
176039
176144
|
await persistWorktreeAssignment({ worktree: worktreePath, branch });
|
|
176145
|
+
poolAssignmentPersistenceStarted = false;
|
|
176040
176146
|
await audit?.git({ type: "worktree:reuse", target: worktreePath, metadata: { branch, reclaimed: prepared.reclaimed } });
|
|
176041
176147
|
if (prepared.reclaimed) {
|
|
176042
176148
|
await store.logEntry(task.id, `Acquired reclaimed worktree from pool: ${worktreePath} (${prepared.strandedCommitCount ?? 0} commits preserved)`, void 0, runContext);
|
|
@@ -176090,6 +176196,10 @@ async function acquireTaskWorktree(opts) {
|
|
|
176090
176196
|
});
|
|
176091
176197
|
}
|
|
176092
176198
|
} catch (poolErr) {
|
|
176199
|
+
if (poolAssignmentPersistenceStarted) {
|
|
176200
|
+
pool.release(pooled, task.id);
|
|
176201
|
+
throw poolErr;
|
|
176202
|
+
}
|
|
176093
176203
|
if (poolErr instanceof WorktreeBaseRefreshError) {
|
|
176094
176204
|
await persistWorktreeAssignment({ worktree: null, branch: null, branchWriteOrigin: "engine", sessionFile: null });
|
|
176095
176205
|
pool.release(pooled, task.id);
|
|
@@ -176401,7 +176511,11 @@ async function acquireWorkspaceTaskWorktrees(opts) {
|
|
|
176401
176511
|
current = await opts.store.getTask(opts.task.id);
|
|
176402
176512
|
}
|
|
176403
176513
|
const entries = current.workspaceWorktrees ?? {};
|
|
176404
|
-
const
|
|
176514
|
+
const remediationRepository = resolveWorkspaceReviewRemediationRepository(current, repoRelPaths);
|
|
176515
|
+
if (opts.remediationRepository !== remediationRepository) {
|
|
176516
|
+
throw new Error(`Workspace Code Review remediation target changed during acquisition for ${opts.task.id}`);
|
|
176517
|
+
}
|
|
176518
|
+
const coordinatorWorktreePath = remediationRepository ? entries[remediationRepository]?.worktreePath : repoRelPaths.map((repoRelPath) => entries[repoRelPath]?.worktreePath).find((path13) => typeof path13 === "string" && path13.length > 0);
|
|
176405
176519
|
if (!coordinatorWorktreePath) {
|
|
176406
176520
|
throw new Error(`Workspace task ${opts.task.id} did not acquire a declared repository worktree`);
|
|
176407
176521
|
}
|
|
@@ -176422,6 +176536,7 @@ var init_worktree_acquisition = __esm({
|
|
|
176422
176536
|
init_worktree_backend();
|
|
176423
176537
|
init_worktrunk_installer();
|
|
176424
176538
|
init_worktrunk_failure_handler();
|
|
176539
|
+
init_workspace_review_remediation();
|
|
176425
176540
|
init_secrets_env_writer();
|
|
176426
176541
|
init_worktree_desktop_artifacts();
|
|
176427
176542
|
init_worktree_hooks();
|
|
@@ -191255,7 +191370,7 @@ var init_agent_memory_index = __esm({
|
|
|
191255
191370
|
// ../engine/src/agents/agent-instructions.ts
|
|
191256
191371
|
import { readFile as readFile42, writeFile as writeFile28, mkdir as mkdir29, access as access9 } from "node:fs/promises";
|
|
191257
191372
|
import { constants as fsConstants4 } from "node:fs";
|
|
191258
|
-
import { isAbsolute as isAbsolute16, resolve as resolve40, relative as relative15, normalize as
|
|
191373
|
+
import { isAbsolute as isAbsolute16, resolve as resolve40, relative as relative15, normalize as normalize9, sep as sep12, dirname as dirname31 } from "node:path";
|
|
191259
191374
|
function trimAndClamp(value, maxLength, label, agentId) {
|
|
191260
191375
|
const trimmed = value.trim();
|
|
191261
191376
|
if (!trimmed) {
|
|
@@ -191289,7 +191404,7 @@ function resolveValidatedMarkdownPath(rawPath, rootDir, agentId, fieldLabel) {
|
|
|
191289
191404
|
log26.warn(`${fieldLabel} must be project-relative for agent ${agentId}: ${trimmed}`);
|
|
191290
191405
|
return null;
|
|
191291
191406
|
}
|
|
191292
|
-
const normalized2 =
|
|
191407
|
+
const normalized2 = normalize9(trimmed);
|
|
191293
191408
|
if (isPathTraversal2(normalized2)) {
|
|
191294
191409
|
log26.warn(`${fieldLabel} traversal is not allowed for agent ${agentId}: ${trimmed}`);
|
|
191295
191410
|
return null;
|
|
@@ -200052,8 +200167,8 @@ async function autoRecoverWorktreeSessionStartFailure(store, task, opts) {
|
|
|
200052
200167
|
}
|
|
200053
200168
|
const workspaceTask = configuredWorkspace || isWorkspaceTask(task);
|
|
200054
200169
|
if (workspaceTask) {
|
|
200055
|
-
const
|
|
200056
|
-
if (typeof
|
|
200170
|
+
const normalize10 = store.normalizeWorkspaceTaskWorktreeMetadata;
|
|
200171
|
+
if (typeof normalize10 === "function") task = await normalize10.call(store, task.id);
|
|
200057
200172
|
}
|
|
200058
200173
|
const resetRetryBudget = opts.resetRetryBudgetOnStaleMetadataClear === true;
|
|
200059
200174
|
const staleMetadataClearRecoveryRetryCount = opts.staleMetadataClearRecoveryRetryCount;
|
|
@@ -200108,7 +200223,13 @@ async function autoRecoverWorktreeSessionStartFailure(store, task, opts) {
|
|
|
200108
200223
|
worktreeSessionRetryCount: nextCount,
|
|
200109
200224
|
...nextStaleMetadataClearRecoveryCount === void 0 ? {} : { recoveryRetryCount: nextStaleMetadataClearRecoveryCount },
|
|
200110
200225
|
worktree: clearWorktreeMetadata ? null : staleWorktree,
|
|
200111
|
-
|
|
200226
|
+
/*
|
|
200227
|
+
* FNXC:BranchNaming 2026-08-21-09:36:
|
|
200228
|
+
* Session recovery often changes only worktree/session metadata. Do not reassert an unchanged
|
|
200229
|
+
* branch with engine provenance: that would erase a matching operator override and later make
|
|
200230
|
+
* its branch eligible for engine cleanup. A real canonical clear remains an explicit engine write.
|
|
200231
|
+
*/
|
|
200232
|
+
...(task.branch ?? null) === nextBranch ? {} : { branch: nextBranch, branchWriteOrigin: "engine" },
|
|
200112
200233
|
sessionFile: null
|
|
200113
200234
|
});
|
|
200114
200235
|
await opts.auditor?.database({
|
|
@@ -209837,7 +209958,8 @@ async function tryEarlyEmptyOwnDiffFinalize(input) {
|
|
|
209837
209958
|
try {
|
|
209838
209959
|
await store.updateTask(taskId, {
|
|
209839
209960
|
worktree: worktreeRemoved ? null : task.worktree,
|
|
209840
|
-
branch: branchDeleted ? null : task.branch
|
|
209961
|
+
branch: branchDeleted ? null : task.branch,
|
|
209962
|
+
...branchDeleted ? { branchWriteOrigin: "engine" } : {}
|
|
209841
209963
|
});
|
|
209842
209964
|
if (worktreeRemoved) task.worktree = void 0;
|
|
209843
209965
|
if (branchDeleted) task.branch = void 0;
|
|
@@ -210197,7 +210319,7 @@ async function aiMergeTask(store, rootDir, taskId, options = {}) {
|
|
|
210197
210319
|
rootDir,
|
|
210198
210320
|
integrationBranch: mergeTarget.branch
|
|
210199
210321
|
});
|
|
210200
|
-
await store.updateTask(taskId, { worktree: reusableMatch.path, branch: reusableMatch.branch });
|
|
210322
|
+
await store.updateTask(taskId, { worktree: reusableMatch.path, branch: reusableMatch.branch, branchWriteOrigin: "engine" });
|
|
210201
210323
|
await emitReuseHandoffAuditEvent(
|
|
210202
210324
|
"merge:reuse-fallback-reused-existing-registration",
|
|
210203
210325
|
{
|
|
@@ -217757,7 +217879,7 @@ var init_self_healing = __esm({
|
|
|
217757
217879
|
} catch {
|
|
217758
217880
|
}
|
|
217759
217881
|
}
|
|
217760
|
-
const patch = preservedWorktree ? { branch: selected.branch } : { branch: selected.branch, worktree: null };
|
|
217882
|
+
const patch = preservedWorktree ? { branch: selected.branch, branchWriteOrigin: "engine" } : { branch: selected.branch, branchWriteOrigin: "engine", worktree: null };
|
|
217761
217883
|
if (!task.baseCommitSha) {
|
|
217762
217884
|
const derivedBaseCommit = (await execAsync18(
|
|
217763
217885
|
`git merge-base ${shellQuote2(integrationBase)} ${shellQuote2(selected.branch)}`,
|
|
@@ -221434,6 +221556,12 @@ var init_self_healing = __esm({
|
|
|
221434
221556
|
await this.emitWorkspacePartialLandNoAction(task, "merge-pending", liveness.livePaths);
|
|
221435
221557
|
continue;
|
|
221436
221558
|
}
|
|
221559
|
+
const latestTask = await this.store.getTask(task.id).catch(() => null);
|
|
221560
|
+
const blockerTask = latestTask?.status === "failed" && latestTask.error?.startsWith("Workspace partial land:") ? { ...latestTask, status: null, error: void 0 } : latestTask;
|
|
221561
|
+
if (!blockerTask || getTaskMergeBlocker(blockerTask, { skipColumnIdentityCheck: true }) !== void 0) {
|
|
221562
|
+
await this.emitWorkspacePartialLandNoAction(task, "merge-blocked", []);
|
|
221563
|
+
continue;
|
|
221564
|
+
}
|
|
221437
221565
|
const workspaceWorktrees = task.workspaceWorktrees ?? {};
|
|
221438
221566
|
const explicitScope = task.repositoryScope?.state === "confirmed" ? task.repositoryScope.repositories : void 0;
|
|
221439
221567
|
if (!explicitScope) {
|
|
@@ -221441,8 +221569,12 @@ var init_self_healing = __esm({
|
|
|
221441
221569
|
continue;
|
|
221442
221570
|
}
|
|
221443
221571
|
const repoKeys = Object.keys(workspaceWorktrees).filter(
|
|
221444
|
-
(repoRel) => explicitScope.includes(repoRel) && (task.modifiedFiles ?? []).some((file) => file.startsWith(`${repoRel}/`))
|
|
221572
|
+
(repoRel) => explicitScope.includes(repoRel) && ((task.modifiedFiles ?? []).some((file) => file.startsWith(`${repoRel}/`)) || Boolean(workspaceWorktrees[repoRel]?.landedSha))
|
|
221445
221573
|
);
|
|
221574
|
+
if (repoKeys.length === 0) {
|
|
221575
|
+
await this.emitWorkspacePartialLandNoAction(task, "empty-obligations", []);
|
|
221576
|
+
continue;
|
|
221577
|
+
}
|
|
221446
221578
|
const landedRepos = [];
|
|
221447
221579
|
const unlandedRepos = [];
|
|
221448
221580
|
const unrecoverableRepos = [];
|
|
@@ -221499,13 +221631,12 @@ var init_self_healing = __esm({
|
|
|
221499
221631
|
phase: "reconcile-workspace-partial-land"
|
|
221500
221632
|
});
|
|
221501
221633
|
if (evidenceUnavailableRepos.length > 0) {
|
|
221502
|
-
const defers = (
|
|
221503
|
-
this.
|
|
221634
|
+
const defers = (task.mergeTransientRetryCount ?? 0) + 1;
|
|
221635
|
+
await this.store.updateTask(task.id, { mergeTransientRetryCount: defers });
|
|
221504
221636
|
if (defers >= MAX_STARVATION_DROPS) {
|
|
221505
221637
|
const error3 = `Workspace partial-land evidence unavailable: branch state could not be read after ${MAX_STARVATION_DROPS} sweeps for sub-repo(s) ${evidenceUnavailableRepos.join(", ")} \u2014 manual intervention required.`;
|
|
221506
221638
|
await this.store.updateTask(task.id, { status: "failed", error: error3 });
|
|
221507
221639
|
await this.store.logEntry(task.id, error3);
|
|
221508
|
-
this.workspacePartialLandEvidenceDefers.delete(task.id);
|
|
221509
221640
|
await auditor.database({
|
|
221510
221641
|
type: "task:reconcile-workspace-partial-land",
|
|
221511
221642
|
target: task.id,
|
|
@@ -221518,7 +221649,6 @@ var init_self_healing = __esm({
|
|
|
221518
221649
|
}
|
|
221519
221650
|
continue;
|
|
221520
221651
|
}
|
|
221521
|
-
this.workspacePartialLandEvidenceDefers.delete(task.id);
|
|
221522
221652
|
if (unrecoverableRepos.length > 0) {
|
|
221523
221653
|
const missingBranches = unrecoverableRepos.map((repoRel) => workspaceWorktrees[repoRel]?.branch ?? resolveTaskWorkingBranch(task)).join(", ");
|
|
221524
221654
|
const error3 = `Workspace partial-land unrecoverable: sub-repo(s) ${unrecoverableRepos.join(", ")} have no branch (${missingBranches}) and no landedSha \u2014 manual intervention required.`;
|
|
@@ -221546,7 +221676,7 @@ var init_self_healing = __esm({
|
|
|
221546
221676
|
landedRepos,
|
|
221547
221677
|
unlandedRepos: [],
|
|
221548
221678
|
reason: "all-landed-not-finalized",
|
|
221549
|
-
successLog: "
|
|
221679
|
+
successLog: "Workspace merge recovery scheduled: all sub-repositories have proven landing evidence; awaiting finalize-once result"
|
|
221550
221680
|
});
|
|
221551
221681
|
recovered++;
|
|
221552
221682
|
continue;
|
|
@@ -221555,7 +221685,7 @@ var init_self_healing = __esm({
|
|
|
221555
221685
|
landedRepos,
|
|
221556
221686
|
unlandedRepos,
|
|
221557
221687
|
reason: landedRepos.length > 0 ? "partial-land" : "zero-land",
|
|
221558
|
-
successLog: `
|
|
221688
|
+
successLog: `Workspace merge recovery scheduled: ${landedRepos.length} landed, ${unlandedRepos.length} pending`
|
|
221559
221689
|
});
|
|
221560
221690
|
recovered++;
|
|
221561
221691
|
} catch (err) {
|
|
@@ -221620,7 +221750,8 @@ var init_self_healing = __esm({
|
|
|
221620
221750
|
}).catch(() => void 0);
|
|
221621
221751
|
return false;
|
|
221622
221752
|
}
|
|
221623
|
-
const drops = (
|
|
221753
|
+
const drops = (task.mergeTransientRetryCount ?? 0) + 1;
|
|
221754
|
+
await this.store.updateTask(task.id, { mergeTransientRetryCount: drops });
|
|
221624
221755
|
this.workspacePartialLandDrops.set(task.id, drops);
|
|
221625
221756
|
log34.warn(`reconcileWorkspacePartialLands: enqueue dropped for ${task.id} (${drops}/${MAX_STARVATION_DROPS}); merge queue rejected re-enqueue`);
|
|
221626
221757
|
if (drops >= MAX_STARVATION_DROPS) {
|
|
@@ -231216,7 +231347,7 @@ function recoverPassedPlanReviewFromLatestLog(task) {
|
|
|
231216
231347
|
completedAt: latest.timestamp
|
|
231217
231348
|
};
|
|
231218
231349
|
}
|
|
231219
|
-
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;
|
|
231350
|
+
var PLAN_REVIEW_PROVIDER_FAILURE_HOLD_VALUE, BRANCH_WRITE_PROVENANCE_FAILURE_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;
|
|
231220
231351
|
var init_workflow_graph_executor = __esm({
|
|
231221
231352
|
"../engine/src/workflows/workflow-graph-executor.ts"() {
|
|
231222
231353
|
"use strict";
|
|
@@ -231229,8 +231360,10 @@ var init_workflow_graph_executor = __esm({
|
|
|
231229
231360
|
init_workflow_graph_foreach();
|
|
231230
231361
|
init_workflow_graph_loop();
|
|
231231
231362
|
init_workflow_node_execution_needs();
|
|
231363
|
+
init_src2();
|
|
231232
231364
|
init_worktree_acquisition();
|
|
231233
231365
|
PLAN_REVIEW_PROVIDER_FAILURE_HOLD_VALUE = "plan-review-provider-failure-hold";
|
|
231366
|
+
BRANCH_WRITE_PROVENANCE_FAILURE_VALUE = "branch-write-provenance-failure";
|
|
231234
231367
|
PLAN_REVIEW_LEASE_HELD_VALUE = "plan-review-lease-held";
|
|
231235
231368
|
SESSION_CONTENTION_HOLD_VALUE = "session-contention-hold";
|
|
231236
231369
|
WORKFLOW_INTERRUPTED_NODE_ID_CONTEXT_KEY = "workflow:interruptedNodeId";
|
|
@@ -232147,7 +232280,7 @@ var init_workflow_graph_executor = __esm({
|
|
|
232147
232280
|
const failureResult = {
|
|
232148
232281
|
outcome: "failure",
|
|
232149
232282
|
// FNXC:SessionContention 2026-07-25-21:30: contention is a retryable hold, not an exception.
|
|
232150
|
-
value: isSessionContentionError(lastErrorText) ? SESSION_CONTENTION_HOLD_VALUE : "exception",
|
|
232283
|
+
value: lastError instanceof BranchWriteProvenanceError ? BRANCH_WRITE_PROVENANCE_FAILURE_VALUE : isSessionContentionError(lastErrorText) ? SESSION_CONTENTION_HOLD_VALUE : "exception",
|
|
232151
232284
|
contextPatch: {
|
|
232152
232285
|
[`node:${node.id}:error`]: lastErrorText
|
|
232153
232286
|
}
|
|
@@ -236235,6 +236368,7 @@ async function handleBranchConflict(deps, task, error3) {
|
|
|
236235
236368
|
status: "failed",
|
|
236236
236369
|
error: conflictMessage,
|
|
236237
236370
|
branch: error3.branchName,
|
|
236371
|
+
branchWriteOrigin: "engine",
|
|
236238
236372
|
worktree: error3.conflictingWorktreePath,
|
|
236239
236373
|
paused: true,
|
|
236240
236374
|
pausedReason: "branch-conflict-unrecoverable"
|
|
@@ -236290,8 +236424,8 @@ async function recoverMissingWorktreeSessionStartFailure(deps, task, worktreePat
|
|
|
236290
236424
|
}
|
|
236291
236425
|
let recoveryTask = task;
|
|
236292
236426
|
if (workspaceTask) {
|
|
236293
|
-
const
|
|
236294
|
-
if (typeof
|
|
236427
|
+
const normalize10 = deps.store.normalizeWorkspaceTaskWorktreeMetadata;
|
|
236428
|
+
if (typeof normalize10 === "function") recoveryTask = await normalize10.call(deps.store, task.id);
|
|
236295
236429
|
}
|
|
236296
236430
|
if (!workspaceTask && isInsideWorktreesDir(deps.rootDir, staleWorktreePath)) {
|
|
236297
236431
|
try {
|
|
@@ -236709,8 +236843,21 @@ async function detectWorkspaceMainCheckoutWork(deps, task, repos, declaredScope)
|
|
|
236709
236843
|
const { stdout } = await execAsync41("git log -n 200 --format=%H%x1f%ct%x1f%B%x1e HEAD", { ...probeOptions, cwd: checkout });
|
|
236710
236844
|
const attributed = new RegExp(`(?:${task.id.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}|Fusion-Task-Id:\\s*${task.id.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")})`, "i");
|
|
236711
236845
|
for (const commit of parseCommits(stdout)) {
|
|
236712
|
-
const
|
|
236713
|
-
|
|
236846
|
+
const entry = workspaceWorktrees[repo];
|
|
236847
|
+
const recordedLanding = entry?.landedSha === commit.sha || task.mergeDetails?.workspaceLandedShas?.[repo] === commit.sha || task.mergeDetails?.commitSha === commit.sha;
|
|
236848
|
+
let reachableFromBaseline = false;
|
|
236849
|
+
if (entry?.baseCommitSha) {
|
|
236850
|
+
try {
|
|
236851
|
+
await execAsync41(`git merge-base --is-ancestor ${commit.sha} ${entry.baseCommitSha}`, { ...probeOptions, cwd: checkout });
|
|
236852
|
+
reachableFromBaseline = true;
|
|
236853
|
+
} catch {
|
|
236854
|
+
}
|
|
236855
|
+
}
|
|
236856
|
+
if (attributed.test(commit.body) && !recordedLanding && !reachableFromBaseline && anchor !== null && commit.committedAt >= anchor) {
|
|
236857
|
+
violations.push({ repo, files: [], commits: [commit.sha], evidence: "task-attributed-commit" });
|
|
236858
|
+
} else if (!recordedLanding && !reachableFromBaseline && anchor !== null && commit.committedAt >= anchor) {
|
|
236859
|
+
warnings.push({ repo, files: [], commits: [commit.sha], reason: "pre-existing-dirt" });
|
|
236860
|
+
}
|
|
236714
236861
|
}
|
|
236715
236862
|
} catch {
|
|
236716
236863
|
warnings.push({ repo, files: [], commits: [], reason: "commit-scan-unavailable" });
|
|
@@ -236790,7 +236937,7 @@ async function verifyWorktreeInvariants(deps, task, worktreePathOverride, allowR
|
|
|
236790
236937
|
reason: "main_checkout_edit",
|
|
236791
236938
|
repo: firstMainCheckoutViolation.repo,
|
|
236792
236939
|
observed: `${firstMainCheckoutViolation.evidence}: ${observed}`,
|
|
236793
|
-
expected: `move task work into the acquired fusion/${task.id} worktree for ${firstMainCheckoutViolation.repo} (acquire it first if needed), restore its main checkout, then
|
|
236940
|
+
expected: `move task work into the acquired fusion/${task.id} worktree for ${firstMainCheckoutViolation.repo} (acquire it first if needed), restore its main checkout, then ask an operator to retry fn_task_done`
|
|
236794
236941
|
};
|
|
236795
236942
|
}
|
|
236796
236943
|
const zeroAcquire = classifyWorkspaceZeroAcquire(task, {
|
|
@@ -238273,7 +238420,7 @@ var init_workflow_step_failure_injection = __esm({
|
|
|
238273
238420
|
});
|
|
238274
238421
|
|
|
238275
238422
|
// ../engine/src/executor/send-task-back-for-fix.ts
|
|
238276
|
-
async function sendTaskBackForFix(deps, task, worktreePath, failureFeedback, stepName, reason, preserveResumeState = true, mergeVerificationFailure = false, retryPresentation, findings) {
|
|
238423
|
+
async function sendTaskBackForFix(deps, task, worktreePath, failureFeedback, stepName, reason, preserveResumeState = true, mergeVerificationFailure = false, retryPresentation, findings, persistWorktreePath) {
|
|
238277
238424
|
const taskId = task.id;
|
|
238278
238425
|
deps.clearCompletedTaskWatchdog(taskId);
|
|
238279
238426
|
const { task: authoritativeRemediationTask, route: externalExecutionRoute } = await resolveAuthoritativeExternalExecutionRoute(deps.store, task);
|
|
@@ -238316,7 +238463,7 @@ Please fix the issues so the verification can pass on the next attempt.`,
|
|
|
238316
238463
|
remediationWorktreePath,
|
|
238317
238464
|
`${taskId}: sent back to in-progress for remediation`,
|
|
238318
238465
|
preserveResumeState,
|
|
238319
|
-
!externalExecutionRoute.configured
|
|
238466
|
+
persistWorktreePath ?? !externalExecutionRoute.configured
|
|
238320
238467
|
);
|
|
238321
238468
|
}
|
|
238322
238469
|
var init_send_task_back_for_fix = __esm({
|
|
@@ -243412,6 +243559,7 @@ async function ensureGraphCustomNodeWorktree(deps, task, settings, nodeId, refre
|
|
|
243412
243559
|
deps.getRunContextFor(task.id)
|
|
243413
243560
|
);
|
|
243414
243561
|
if (workspaceConfig) {
|
|
243562
|
+
const remediationRepository = resolveWorkspaceReviewRemediationRepository(task, workspaceConfig.repos);
|
|
243415
243563
|
const workspace = await acquireWorkspaceTaskWorktrees({
|
|
243416
243564
|
workspaceConfig,
|
|
243417
243565
|
workspaceRootDir: deps.rootDir,
|
|
@@ -243436,7 +243584,8 @@ async function ensureGraphCustomNodeWorktree(deps, task, settings, nodeId, refre
|
|
|
243436
243584
|
return result2;
|
|
243437
243585
|
}),
|
|
243438
243586
|
taskEnv: process.env,
|
|
243439
|
-
addActiveWorktree: deps.addActiveWorktree
|
|
243587
|
+
addActiveWorktree: deps.addActiveWorktree,
|
|
243588
|
+
remediationRepository
|
|
243440
243589
|
});
|
|
243441
243590
|
deps.onStart?.(workspace.task, workspace.coordinatorWorktreePath);
|
|
243442
243591
|
executorLog.debug(`${task.id}: workflow node '${nodeId}' using workspace coordinator ${workspace.coordinatorWorktreePath}`);
|
|
@@ -243500,6 +243649,7 @@ var init_ensure_graph_custom_node_worktree = __esm({
|
|
|
243500
243649
|
init_worktree_git_refs();
|
|
243501
243650
|
init_task_predicates();
|
|
243502
243651
|
init_workspace_config_resolver();
|
|
243652
|
+
init_workspace_review_remediation();
|
|
243503
243653
|
}
|
|
243504
243654
|
});
|
|
243505
243655
|
|
|
@@ -244088,7 +244238,22 @@ ${info.feedback}`,
|
|
|
244088
244238
|
return false;
|
|
244089
244239
|
}
|
|
244090
244240
|
const revisionKey = optionalStepRevisionKey2(info.nodeId, info.stepName);
|
|
244091
|
-
|
|
244241
|
+
const reviewResult = (liveTask.workflowStepResults ?? []).find(
|
|
244242
|
+
(result2) => (result2.workflowStepId === info.nodeId || result2.workflowStepName === info.stepName) && result2.verdict === "REVISE"
|
|
244243
|
+
);
|
|
244244
|
+
const derivedRemediation = reviewResult ? deriveWorkspaceReviewRemediation(reviewResult) : void 0;
|
|
244245
|
+
const remediation = derivedRemediation && liveTask.workspaceWorktrees ? derivedRemediation : void 0;
|
|
244246
|
+
const priorRemediation = liveTask.repositoryScope?.reviewRemediation;
|
|
244247
|
+
const hasDurableRepeatedWorkspaceReview = remediation !== void 0 && priorRemediation?.scopeRevision === remediation.scopeRevision && priorRemediation.repository === remediation.repository && priorRemediation.inputSignature === remediation.inputSignature;
|
|
244248
|
+
const updateWorkspaceReviewState = deps.store.updateWorkspaceReviewState;
|
|
244249
|
+
if (remediation && !hasDurableRepeatedWorkspaceReview && updateWorkspaceReviewState) {
|
|
244250
|
+
const persisted = await updateWorkspaceReviewState.call(deps.store, taskId, remediation.scopeRevision, remediation);
|
|
244251
|
+
if (!persisted.updated) {
|
|
244252
|
+
executorLog.warn(`${taskId}: workspace Code Review remediation was superseded by a repository scope change.`);
|
|
244253
|
+
return false;
|
|
244254
|
+
}
|
|
244255
|
+
}
|
|
244256
|
+
if (hasDurableRepeatedWorkspaceReview || (!remediation || !updateWorkspaceReviewState) && hasRepeatedUnchangedCodeReview(liveTask, info)) {
|
|
244092
244257
|
const runContext = deps.getRunContextFor(taskId);
|
|
244093
244258
|
await deps.store.logEntry(
|
|
244094
244259
|
taskId,
|
|
@@ -244136,9 +244301,18 @@ Feedback:
|
|
|
244136
244301
|
${info.feedback}`, revisionKey),
|
|
244137
244302
|
deps.getRunContextFor(taskId)
|
|
244138
244303
|
);
|
|
244139
|
-
|
|
244304
|
+
const remediationWorktreePath = remediation ? liveTask.workspaceWorktrees?.[remediation.repository]?.worktreePath : liveTask.worktree;
|
|
244305
|
+
if (remediation && !remediationWorktreePath) {
|
|
244306
|
+
await deps.store.updateTask(taskId, {
|
|
244307
|
+
status: "awaiting-approval",
|
|
244308
|
+
awaitingApprovalReason: "code-review-non-convergence",
|
|
244309
|
+
error: `Workspace Code Review remediation target ${remediation.repository} has no acquired worktree.`
|
|
244310
|
+
});
|
|
244311
|
+
return false;
|
|
244312
|
+
}
|
|
244313
|
+
const sendArgs = [
|
|
244140
244314
|
liveTask,
|
|
244141
|
-
|
|
244315
|
+
remediationWorktreePath ?? "",
|
|
244142
244316
|
info.feedback,
|
|
244143
244317
|
info.stepName,
|
|
244144
244318
|
`Pre-merge optional workflow step "${info.stepName}" requested revision`,
|
|
@@ -244146,7 +244320,12 @@ ${info.feedback}`, revisionKey),
|
|
|
244146
244320
|
false,
|
|
244147
244321
|
{ attempt: nextCount, max: budget.unbounded ? void 0 : budget.max },
|
|
244148
244322
|
info.findings
|
|
244149
|
-
|
|
244323
|
+
];
|
|
244324
|
+
if (remediation) {
|
|
244325
|
+
await deps.sendTaskBackForFix(...sendArgs, false);
|
|
244326
|
+
} else {
|
|
244327
|
+
await deps.sendTaskBackForFix(...sendArgs);
|
|
244328
|
+
}
|
|
244150
244329
|
return true;
|
|
244151
244330
|
}
|
|
244152
244331
|
var init_request_pre_merge_optional_step_fix = __esm({
|
|
@@ -244161,6 +244340,7 @@ var init_request_pre_merge_optional_step_fix = __esm({
|
|
|
244161
244340
|
init_plan_review_feedback_history();
|
|
244162
244341
|
init_logger3();
|
|
244163
244342
|
init_emit_bounded_run_audit3();
|
|
244343
|
+
init_workspace_review_remediation();
|
|
244164
244344
|
}
|
|
244165
244345
|
});
|
|
244166
244346
|
|
|
@@ -245049,6 +245229,14 @@ function createTaskDoneTool(deps, taskId, worktreePath, promptContent, codeRevie
|
|
|
245049
245229
|
const refusalMessage = `fn_task_done refused: ${invariantCheck.reason} \u2014 observed=${invariantCheck.observed}, expected=${invariantCheck.expected}`;
|
|
245050
245230
|
await store.logEntry(taskId, refusalMessage, void 0, deps.getRunContextFor(task.id));
|
|
245051
245231
|
executorLog.error(`${taskId}: fn_task_done refused (${invariantCheck.reason}) \u2014 observed=${invariantCheck.observed}, expected=${invariantCheck.expected}`);
|
|
245232
|
+
if (invariantCheck.reason === "main_checkout_edit") {
|
|
245233
|
+
await store.updateTask(taskId, { status: "failed", error: refusalMessage });
|
|
245234
|
+
await store.logEntry(taskId, `${refusalMessage} \u2014 operator cleanup and Retry are required; executor retry budget preserved`, void 0, deps.getRunContextFor(task.id));
|
|
245235
|
+
return {
|
|
245236
|
+
content: [{ type: "text", text: refusalMessage }],
|
|
245237
|
+
details: { error: refusalMessage }
|
|
245238
|
+
};
|
|
245239
|
+
}
|
|
245052
245240
|
const priorRequeues = task.taskDoneRetryCount ?? 0;
|
|
245053
245241
|
const nextRequeueCount = priorRequeues + 1;
|
|
245054
245242
|
if (priorRequeues < MAX_TASK_DONE_REQUEUE_RETRIES) {
|
|
@@ -246881,7 +247069,9 @@ Repository scope (task-level; review this plan once): ${detail.repositoryScope?.
|
|
|
246881
247069
|
return {
|
|
246882
247070
|
repositoryScope: {
|
|
246883
247071
|
...currentScope,
|
|
246884
|
-
reviewEvidence: Object.fromEntries(Object.entries(review.repositoryDiffFingerprints).map(([repo, fingerprint]) => [repo, { fingerprint, approvedAt }]))
|
|
247072
|
+
reviewEvidence: Object.fromEntries(Object.entries(review.repositoryDiffFingerprints).map(([repo, fingerprint]) => [repo, { fingerprint, approvedAt }])),
|
|
247073
|
+
// FNXC:WorkspaceFinalization 2026-08-21-09:50: An APPROVE for this fenced scope ends its REVISE episode; retaining its target would misroute later acquisition.
|
|
247074
|
+
...currentScope.reviewRemediation?.scopeRevision === review.repositoryScopeRevision ? { reviewRemediation: void 0 } : {}
|
|
246885
247075
|
}
|
|
246886
247076
|
};
|
|
246887
247077
|
});
|
|
@@ -248286,7 +248476,8 @@ async function runGraphCustomNode(deps, node, nodeTask, settings, columnBinding,
|
|
|
248286
248476
|
executionTarget = await deps.ensureGraphCustomNodeWorktree(acquisitionTask, settings, node.id);
|
|
248287
248477
|
}
|
|
248288
248478
|
}
|
|
248289
|
-
const
|
|
248479
|
+
const remediationRepository = workspaceConfig ? resolveWorkspaceReviewRemediationRepository(executionTarget, workspaceConfig.repos) : void 0;
|
|
248480
|
+
const workspaceCoordinator = workspaceConfig ? remediationRepository ? executionTarget.workspaceWorktrees?.[remediationRepository]?.worktreePath : Object.keys(executionTarget.workspaceWorktrees ?? {}).filter((repoRelPath) => workspaceConfig.repos.includes(repoRelPath)).sort().map((repoRelPath) => executionTarget.workspaceWorktrees?.[repoRelPath]?.worktreePath).find((path13) => typeof path13 === "string" && path13.length > 0) : void 0;
|
|
248290
248481
|
if (!executionTarget.worktree && !workspaceCoordinator) {
|
|
248291
248482
|
return { outcome: "failure", value: "no-worktree-for-write-node" };
|
|
248292
248483
|
}
|
|
@@ -248446,7 +248637,9 @@ ${prompt}`;
|
|
|
248446
248637
|
return {
|
|
248447
248638
|
repositoryScope: {
|
|
248448
248639
|
...currentScope,
|
|
248449
|
-
reviewEvidence: Object.fromEntries(Object.entries(aggregate.repositoryDiffFingerprints).map(([repo, fingerprint]) => [repo, { fingerprint, approvedAt }]))
|
|
248640
|
+
reviewEvidence: Object.fromEntries(Object.entries(aggregate.repositoryDiffFingerprints).map(([repo, fingerprint]) => [repo, { fingerprint, approvedAt }])),
|
|
248641
|
+
// FNXC:WorkspaceFinalization 2026-08-21-09:50: A current-scope APPROVE clears the durable REVISE coordinator before graph completion can schedule another run.
|
|
248642
|
+
...currentScope.reviewRemediation?.scopeRevision === aggregate.repositoryScopeRevision ? { reviewRemediation: void 0 } : {}
|
|
248450
248643
|
}
|
|
248451
248644
|
};
|
|
248452
248645
|
});
|
|
@@ -248544,6 +248737,7 @@ var init_run_graph_custom_node = __esm({
|
|
|
248544
248737
|
"use strict";
|
|
248545
248738
|
init_src2();
|
|
248546
248739
|
init_logger3();
|
|
248740
|
+
init_workspace_review_remediation();
|
|
248547
248741
|
init_workflow_graph_executor();
|
|
248548
248742
|
init_workflow_node_execution_needs();
|
|
248549
248743
|
init_workflow_step_verdict();
|
|
@@ -248599,6 +248793,13 @@ async function handleGraphFailure(deps, task, result2) {
|
|
|
248599
248793
|
await deps.persistTokenUsage(task.id);
|
|
248600
248794
|
return;
|
|
248601
248795
|
}
|
|
248796
|
+
if (graphFailureValue(result2) === BRANCH_WRITE_PROVENANCE_FAILURE_VALUE) {
|
|
248797
|
+
const diagnostic = graphFailureErrorTexts(result2).find((message2) => message2.includes("branchWriteOrigin is required when branch is provided")) ?? "branchWriteOrigin is required when branch is provided";
|
|
248798
|
+
await deps.store.logEntry(task.id, diagnostic, void 0, deps.getRunContextFor(task.id));
|
|
248799
|
+
await deps.store.updateTask(task.id, { status: "failed", error: diagnostic }, deps.getRunContextFor(task.id));
|
|
248800
|
+
await deps.persistTokenUsage(task.id);
|
|
248801
|
+
return;
|
|
248802
|
+
}
|
|
248602
248803
|
if (isSessionContentionGraphFailure(result2)) {
|
|
248603
248804
|
await deps.holdForSessionContention(task, live, result2);
|
|
248604
248805
|
await deps.persistTokenUsage(task.id);
|
|
@@ -251336,6 +251537,7 @@ async function runImplementation(deps, task, graphCompletion, reportImplementati
|
|
|
251336
251537
|
deps.registerConfiguredCommandController(task.id, taskCommandAbortController);
|
|
251337
251538
|
let workspaceCoordinatorWorktree;
|
|
251338
251539
|
if (hasWorkspaceRepos && deps.workspaceConfig) {
|
|
251540
|
+
const remediationRepository = resolveWorkspaceReviewRemediationRepository(task, deps.workspaceConfig.repos);
|
|
251339
251541
|
const workspace = await acquireWorkspaceTaskWorktrees({
|
|
251340
251542
|
workspaceConfig: deps.workspaceConfig,
|
|
251341
251543
|
workspaceRootDir: deps.rootDir,
|
|
@@ -251360,7 +251562,8 @@ async function runImplementation(deps, task, graphCompletion, reportImplementati
|
|
|
251360
251562
|
return result2;
|
|
251361
251563
|
}),
|
|
251362
251564
|
taskEnv,
|
|
251363
|
-
addActiveWorktree: deps.addActiveWorktree
|
|
251565
|
+
addActiveWorktree: deps.addActiveWorktree,
|
|
251566
|
+
remediationRepository
|
|
251364
251567
|
});
|
|
251365
251568
|
task = workspace.task;
|
|
251366
251569
|
workspaceCoordinatorWorktree = workspace.coordinatorWorktreePath;
|
|
@@ -253920,6 +254123,7 @@ var init_run_implementation = __esm({
|
|
|
253920
254123
|
init_run_audit();
|
|
253921
254124
|
init_emit_bounded_run_audit3();
|
|
253922
254125
|
init_worktree_acquisition();
|
|
254126
|
+
init_workspace_review_remediation();
|
|
253923
254127
|
init_worktree_paths();
|
|
253924
254128
|
init_worktree_pool();
|
|
253925
254129
|
execAsync52 = promisify75(exec56);
|
|
@@ -262827,6 +263031,40 @@ var init_overseer_noop_finalize_veto = __esm({
|
|
|
262827
263031
|
}
|
|
262828
263032
|
});
|
|
262829
263033
|
|
|
263034
|
+
// ../engine/src/merge/workspace-merge-readiness.ts
|
|
263035
|
+
function resolveWorkspaceMergeReadiness(task, freshModifiedRepositories, netZeroBranchRepositories) {
|
|
263036
|
+
const scope = task.repositoryScope;
|
|
263037
|
+
if (scope?.state !== "confirmed") {
|
|
263038
|
+
return { kind: "blocked", reason: `Workspace repository scope is unresolved for ${task.id}; operator confirmation is required before landing` };
|
|
263039
|
+
}
|
|
263040
|
+
const entries = task.workspaceWorktrees ?? {};
|
|
263041
|
+
const declaredRepositories = scope.repositories.map((repository) => repository.trim());
|
|
263042
|
+
if (declaredRepositories.some((repository) => !repository) || new Set(declaredRepositories).size !== declaredRepositories.length) {
|
|
263043
|
+
return { kind: "blocked", reason: `Workspace repository scope has duplicate or invalid declarations for ${task.id}; operator repair is required` };
|
|
263044
|
+
}
|
|
263045
|
+
const worktreePaths = Object.values(entries).map((entry) => entry.worktreePath?.trim()).filter((path13) => Boolean(path13));
|
|
263046
|
+
if (new Set(worktreePaths).size !== worktreePaths.length) {
|
|
263047
|
+
return { kind: "blocked", reason: `Workspace merge has duplicate worktree paths for ${task.id}; operator repair is required` };
|
|
263048
|
+
}
|
|
263049
|
+
const repositories = declaredRepositories.filter((repo) => Boolean(entries[repo])).filter((repo) => freshModifiedRepositories.has(repo) || netZeroBranchRepositories.has(repo) || Boolean(entries[repo]?.landedSha)).sort();
|
|
263050
|
+
const preservedFiles = [...new Set(task.modifiedFiles ?? [])].sort();
|
|
263051
|
+
if (repositories.length > 0) return { kind: "ready", repositories, preservedFiles };
|
|
263052
|
+
if (task.noCommitsExpected === true && scope.repositories.length === 0) {
|
|
263053
|
+
return { kind: "no-op", repositories: [], preservedFiles: [] };
|
|
263054
|
+
}
|
|
263055
|
+
const declared = [...declaredRepositories].sort();
|
|
263056
|
+
const knownEntries = declared.filter((repo) => entries[repo] !== void 0);
|
|
263057
|
+
if (knownEntries.length === 0) {
|
|
263058
|
+
return { kind: "blocked", reason: `Workspace merge has no acquired repositories matching confirmed scope for ${task.id}; operator repair is required` };
|
|
263059
|
+
}
|
|
263060
|
+
return { kind: "blocked", reason: `Workspace merge has no evidenced landing obligations for ${task.id}; operator repair is required` };
|
|
263061
|
+
}
|
|
263062
|
+
var init_workspace_merge_readiness = __esm({
|
|
263063
|
+
"../engine/src/merge/workspace-merge-readiness.ts"() {
|
|
263064
|
+
"use strict";
|
|
263065
|
+
}
|
|
263066
|
+
});
|
|
263067
|
+
|
|
262830
263068
|
// ../engine/src/merge/workspace-fence-ref.ts
|
|
262831
263069
|
import { execFile as execFile32 } from "node:child_process";
|
|
262832
263070
|
import { createHash as createHash33 } from "node:crypto";
|
|
@@ -264591,8 +264829,21 @@ async function landWorkspaceTask(store, task, workspaceRootDir, options = {}, de
|
|
|
264591
264829
|
const taskTitle = task.title?.trim() ? task.title.split("\n")[0] : void 0;
|
|
264592
264830
|
const liveMergeBoundaryTask = await store.getTask(taskId).catch(() => void 0);
|
|
264593
264831
|
const mergeBoundaryTask = liveMergeBoundaryTask?.workspaceWorktrees ? liveMergeBoundaryTask : task;
|
|
264832
|
+
const reviewColumns = /* @__PURE__ */ new Set(["in-review"]);
|
|
264833
|
+
const workflowIr = await resolveWorkflowIrForTask(store, taskId).catch(() => void 0);
|
|
264834
|
+
if (workflowIr) for (const column of resolveReviewColumns(workflowIr)) reviewColumns.add(column);
|
|
264835
|
+
const completeColumns = /* @__PURE__ */ new Set(["done"]);
|
|
264836
|
+
if (workflowIr) for (const column of resolveTerminalColumns(workflowIr)) completeColumns.add(column);
|
|
264837
|
+
if (!completeColumns.has(mergeBoundaryTask.column)) {
|
|
264838
|
+
const mergeBlocker = getTaskMergeBlocker(mergeBoundaryTask, {
|
|
264839
|
+
manual: options.manual === true,
|
|
264840
|
+
reviewColumns
|
|
264841
|
+
});
|
|
264842
|
+
if (mergeBlocker) throw new WorkspaceFinalizeBlockedError(taskId, mergeBlocker);
|
|
264843
|
+
}
|
|
264594
264844
|
const confirmedScope = mergeBoundaryTask.repositoryScope?.state === "confirmed" ? new Set(mergeBoundaryTask.repositoryScope.repositories) : void 0;
|
|
264595
264845
|
const mergeBoundaryModifiedFiles = [];
|
|
264846
|
+
const mergeBoundaryModifiedRepositories = /* @__PURE__ */ new Set();
|
|
264596
264847
|
const mergeBoundaryFingerprints = {};
|
|
264597
264848
|
const modifiedOutOfScopeRepositories = /* @__PURE__ */ new Set();
|
|
264598
264849
|
const netZeroBranchRepositories = /* @__PURE__ */ new Set();
|
|
@@ -264609,6 +264860,7 @@ async function landWorkspaceTask(store, task, workspaceRootDir, options = {}, de
|
|
|
264609
264860
|
if (files.length > 0 && !confirmedScope?.has(repoRel)) {
|
|
264610
264861
|
modifiedOutOfScopeRepositories.add(repoRel);
|
|
264611
264862
|
} else if (confirmedScope?.has(repoRel)) {
|
|
264863
|
+
if (files.length > 0) mergeBoundaryModifiedRepositories.add(repoRel);
|
|
264612
264864
|
mergeBoundaryModifiedFiles.push(...files.map((file) => `${repoRel}/${file}`));
|
|
264613
264865
|
if (files.length === 0) {
|
|
264614
264866
|
const { stdout: aheadCount } = await execFileAsync28("git", ["rev-list", "--count", `HEAD..${entry.branch}`], { cwd: entry.worktreePath, encoding: "utf8" });
|
|
@@ -264624,19 +264876,26 @@ async function landWorkspaceTask(store, task, workspaceRootDir, options = {}, de
|
|
|
264624
264876
|
}
|
|
264625
264877
|
const normalizedMergeBoundaryFiles = [...new Set(mergeBoundaryModifiedFiles)].sort();
|
|
264626
264878
|
const persistedReviewFiles = [...new Set(mergeBoundaryTask.modifiedFiles ?? [])].sort();
|
|
264627
|
-
const approvedReviewEvidence = mergeBoundaryTask.repositoryScope?.reviewEvidence
|
|
264628
|
-
const
|
|
264629
|
-
|
|
264879
|
+
const approvedReviewEvidence = mergeBoundaryTask.repositoryScope?.reviewEvidence;
|
|
264880
|
+
const requiresRepositoryReviewEvidence = approvedReviewEvidence !== void 0 || (mergeBoundaryTask.enabledWorkflowSteps ?? []).some((step) => /review/i.test(step));
|
|
264881
|
+
const missingOrStaleReviewApproval = requiresRepositoryReviewEvidence && Object.entries(mergeBoundaryFingerprints).some(([repoRel, fingerprint]) => approvedReviewEvidence?.[repoRel]?.fingerprint !== fingerprint);
|
|
264882
|
+
if (requiresRepositoryReviewEvidence && normalizedMergeBoundaryFiles.some((file) => !persistedReviewFiles.includes(file)) || missingOrStaleReviewApproval) {
|
|
264630
264883
|
throw new Error(`Workspace merge evidence changed after review for ${taskId}; return the task to Code Review before landing`);
|
|
264631
264884
|
}
|
|
264632
|
-
|
|
264633
|
-
|
|
264634
|
-
|
|
264635
|
-
|
|
264636
|
-
|
|
264637
|
-
|
|
264885
|
+
const readiness = resolveWorkspaceMergeReadiness(
|
|
264886
|
+
mergeBoundaryTask,
|
|
264887
|
+
mergeBoundaryModifiedRepositories,
|
|
264888
|
+
netZeroBranchRepositories
|
|
264889
|
+
);
|
|
264890
|
+
if (readiness.kind === "blocked") throw new Error(readiness.reason);
|
|
264891
|
+
if (readiness.kind === "no-op") {
|
|
264892
|
+
throw new Error(`Workspace task ${taskId} has explicit no-commits policy and cannot enter repository landing`);
|
|
264638
264893
|
}
|
|
264639
|
-
const
|
|
264894
|
+
const retainedModifiedFiles = [.../* @__PURE__ */ new Set([...normalizedMergeBoundaryFiles, ...readiness.preservedFiles])].sort();
|
|
264895
|
+
await store.updateTask(taskId, { modifiedFiles: retainedModifiedFiles });
|
|
264896
|
+
task = { ...mergeBoundaryTask, modifiedFiles: retainedModifiedFiles };
|
|
264897
|
+
const workspaceWorktrees = task.workspaceWorktrees ?? {};
|
|
264898
|
+
const repoKeys = readiness.repositories;
|
|
264640
264899
|
const repos = [];
|
|
264641
264900
|
let allLanded = true;
|
|
264642
264901
|
await setStatus("merging");
|
|
@@ -264665,7 +264924,7 @@ async function landWorkspaceTask(store, task, workspaceRootDir, options = {}, de
|
|
|
264665
264924
|
};
|
|
264666
264925
|
} catch (error3) {
|
|
264667
264926
|
if (error3 instanceof WorkspaceFenceRefError) {
|
|
264668
|
-
throw new
|
|
264927
|
+
throw new WorkspaceMergeTechnicalError("dispatch-fence-publication", `Workspace merge dispatch fence publication failed for ${taskId}: ${error3.message}`);
|
|
264669
264928
|
}
|
|
264670
264929
|
throw error3;
|
|
264671
264930
|
}
|
|
@@ -264754,7 +265013,7 @@ async function landWorkspaceTask(store, task, workspaceRootDir, options = {}, de
|
|
|
264754
265013
|
} catch (error3) {
|
|
264755
265014
|
if (durableLandLease) await store.releaseWorkspaceLease(durableLandLease).catch(() => void 0);
|
|
264756
265015
|
if (error3 instanceof WorkspaceRepoLandBusyError) throw error3;
|
|
264757
|
-
throw new
|
|
265016
|
+
throw new WorkspaceMergeTechnicalError("durable-lease", `Workspace repository lease unavailable for ${repoRel}: ${getErrorMessage4(error3)}`);
|
|
264758
265017
|
}
|
|
264759
265018
|
try {
|
|
264760
265019
|
activeSessionRegistry.registerPath(repoRootDir, {
|
|
@@ -264791,7 +265050,7 @@ async function landWorkspaceTask(store, task, workspaceRootDir, options = {}, de
|
|
|
264791
265050
|
renewalTimer?.unref?.();
|
|
264792
265051
|
const landSignal = options.signal ? AbortSignal.any([options.signal, leaseAbort.signal]) : leaseAbort.signal;
|
|
264793
265052
|
const assertLeaseLive = () => {
|
|
264794
|
-
if (leaseLost || leaseAbort.signal.aborted) throw new
|
|
265053
|
+
if (leaseLost || leaseAbort.signal.aborted) throw new WorkspaceMergeTechnicalError("durable-lease", `Workspace repository lease renewal was lost for ${repoRel}`);
|
|
264795
265054
|
};
|
|
264796
265055
|
try {
|
|
264797
265056
|
const landResult = await landOneRepo(repoRootDir, entry.branch, integrationBranch, {
|
|
@@ -264876,11 +265135,11 @@ async function landWorkspaceTask(store, task, workspaceRootDir, options = {}, de
|
|
|
264876
265135
|
repos.push({ repo: repoRel, repoRootDir, integrationBranch, branch: entry.branch, status: "empty" });
|
|
264877
265136
|
}
|
|
264878
265137
|
} catch (err) {
|
|
264879
|
-
if (leaseLost) throw new
|
|
265138
|
+
if (leaseLost) throw new WorkspaceMergeTechnicalError("durable-lease", `Workspace repository lease renewal was lost for ${repoRel}`);
|
|
264880
265139
|
if (isMergeAbortedError(err)) throw err;
|
|
264881
265140
|
if (err instanceof WorkspacePartialLandError) throw err;
|
|
264882
265141
|
if (err instanceof WorkspaceFenceRefError) {
|
|
264883
|
-
throw new
|
|
265142
|
+
throw new WorkspaceMergeTechnicalError("repository-fence-publication", `Workspace repository fence publication failed for ${repoRel}: ${err.message}`);
|
|
264884
265143
|
}
|
|
264885
265144
|
const message = getErrorMessage4(err);
|
|
264886
265145
|
await log42(`AI merge (workspace): sub-repo ${repoRel} land failed: ${message}`);
|
|
@@ -265439,7 +265698,7 @@ async function finalizeTask(store, taskId, result2, audit, log42, rootDir, fence
|
|
|
265439
265698
|
function throwIfAborted2(signal, taskId) {
|
|
265440
265699
|
assertMergeGenerationOwned(signal, taskId);
|
|
265441
265700
|
}
|
|
265442
|
-
var execFileAsync28, aiMergeLog, MAX_CONCURRENT_ADVANCE_RETRIES, MAX_BLOCKING_REVIEW_REASONS, AiMergeBlockedError, AiMergeReviewReconciliationInvalidatedError, LEGACY_COMPLETE_COLUMN, LEGACY_ARCHIVED_COLUMN, LEGACY_TERMINAL_COLUMNS3, LEGACY_REBOUND_COLUMN2, STRONG_LANDED_STRATEGIES, WORKSPACE_REPO_LAND_OWNER_KEY, WorkspaceRepoLandBusyError, WorkspacePartialLandError, WorkspaceFinalizeBlockedError, WorkspaceMergeDispatchSupersededError;
|
|
265701
|
+
var execFileAsync28, aiMergeLog, MAX_CONCURRENT_ADVANCE_RETRIES, MAX_BLOCKING_REVIEW_REASONS, AiMergeBlockedError, AiMergeReviewReconciliationInvalidatedError, LEGACY_COMPLETE_COLUMN, LEGACY_ARCHIVED_COLUMN, LEGACY_TERMINAL_COLUMNS3, LEGACY_REBOUND_COLUMN2, STRONG_LANDED_STRATEGIES, WORKSPACE_REPO_LAND_OWNER_KEY, WorkspaceRepoLandBusyError, WorkspaceMergeTechnicalError, WorkspacePartialLandError, WorkspaceFinalizeBlockedError, WorkspaceMergeDispatchSupersededError;
|
|
265443
265702
|
var init_merger_ai = __esm({
|
|
265444
265703
|
"../engine/src/merge/merger-ai.ts"() {
|
|
265445
265704
|
"use strict";
|
|
@@ -265471,6 +265730,7 @@ var init_merger_ai = __esm({
|
|
|
265471
265730
|
init_active_session_registry();
|
|
265472
265731
|
init_mcp_resolution();
|
|
265473
265732
|
init_workspace_land_predicate();
|
|
265733
|
+
init_workspace_merge_readiness();
|
|
265474
265734
|
init_workspace_land_failure();
|
|
265475
265735
|
init_workspace_fence_ref();
|
|
265476
265736
|
init_auto_merge_finalization();
|
|
@@ -265516,6 +265776,14 @@ var init_merger_ai = __esm({
|
|
|
265516
265776
|
}
|
|
265517
265777
|
retryable = true;
|
|
265518
265778
|
};
|
|
265779
|
+
WorkspaceMergeTechnicalError = class extends Error {
|
|
265780
|
+
constructor(kind, message) {
|
|
265781
|
+
super(message);
|
|
265782
|
+
this.kind = kind;
|
|
265783
|
+
this.name = "WorkspaceMergeTechnicalError";
|
|
265784
|
+
}
|
|
265785
|
+
retryable = true;
|
|
265786
|
+
};
|
|
265519
265787
|
WorkspacePartialLandError = class extends Error {
|
|
265520
265788
|
constructor(landedCount, failedRepos, message) {
|
|
265521
265789
|
super(message);
|
|
@@ -284709,6 +284977,15 @@ ${detail}`);
|
|
|
284709
284977
|
}
|
|
284710
284978
|
continue;
|
|
284711
284979
|
}
|
|
284980
|
+
if (err instanceof WorkspaceMergeTechnicalError) {
|
|
284981
|
+
await store.logEntry(taskId, `Workspace merge technical failure (${err.kind}): ${err.message}`, "WorkspaceMergeTechnicalFailure").catch(() => void 0);
|
|
284982
|
+
if (hasManualResolver) {
|
|
284983
|
+
this.rejectMergeResolvers(taskId, err);
|
|
284984
|
+
} else if (!await this.maybeRetryTransientMerge(store, taskId, await store.getTask(taskId).catch(() => null), err.message, true)) {
|
|
284985
|
+
await store.updateTask(taskId, { status: "failed", error: err.message }).catch(() => void 0);
|
|
284986
|
+
}
|
|
284987
|
+
continue;
|
|
284988
|
+
}
|
|
284712
284989
|
const isWorkspaceBusyError = err instanceof WorkspaceRepoLandBusyError || err instanceof WorkspaceMergeDispatchBusyError || err instanceof WorkspaceMergeDispatchSupersededError;
|
|
284713
284990
|
if (isWorkspaceBusyError && hasManualResolver) {
|
|
284714
284991
|
await store.logEntry(taskId, `Workspace sub-repo land busy (contention): ${errorMsg}`, "WorkspaceRepoLandBusy").catch(() => void 0);
|
|
@@ -284716,18 +284993,18 @@ ${detail}`);
|
|
|
284716
284993
|
continue;
|
|
284717
284994
|
}
|
|
284718
284995
|
if (isWorkspaceBusyError && !hasManualResolver) {
|
|
284719
|
-
const
|
|
284996
|
+
const liveTask = await store.getTask(taskId).catch(() => null);
|
|
284997
|
+
const busyCount = liveTask?.mergeTransientRetryCount ?? 0;
|
|
284720
284998
|
await store.logEntry(taskId, `Workspace sub-repo land busy (contention): ${errorMsg}`, "WorkspaceRepoLandBusy").catch(() => void 0);
|
|
284721
284999
|
if (busyCount < _ProjectEngine.WORKSPACE_BUSY_MAX_REENQUEUES) {
|
|
284722
|
-
|
|
285000
|
+
const nextCount = busyCount + 1;
|
|
284723
285001
|
const delayMs = Math.min(5e3 * Math.pow(2, busyCount), 6e4);
|
|
284724
|
-
await store.updateTask(taskId, { status: null }).catch(() => void 0);
|
|
285002
|
+
await store.updateTask(taskId, { status: null, mergeTransientRetryCount: nextCount }).catch(() => void 0);
|
|
284725
285003
|
runtimeLog.log(
|
|
284726
|
-
`Workspace land busy re-enqueue ${
|
|
285004
|
+
`Workspace land busy re-enqueue ${nextCount}/${_ProjectEngine.WORKSPACE_BUSY_MAX_REENQUEUES} for ${taskId} in ${delayMs / 1e3}s (durable transient retry)`
|
|
284727
285005
|
);
|
|
284728
285006
|
this.scheduleWorkspaceBusyReenqueue(taskId, delayMs);
|
|
284729
285007
|
} else {
|
|
284730
|
-
this.workspaceBusyReenqueues.delete(taskId);
|
|
284731
285008
|
await store.updateTask(taskId, { status: "failed", error: errorMsg }).catch(() => void 0);
|
|
284732
285009
|
runtimeLog.error(
|
|
284733
285010
|
`Auto-merge: ${taskId} workspace land busy ${_ProjectEngine.WORKSPACE_BUSY_MAX_REENQUEUES} times \u2014 parked as failed (sustained sub-repo lease contention)`
|
|
@@ -286056,7 +286333,7 @@ var init_engine_singleton_lock = __esm({
|
|
|
286056
286333
|
import { realpathSync as realpathSync16 } from "node:fs";
|
|
286057
286334
|
import { resolve as pathResolve } from "node:path";
|
|
286058
286335
|
function sameProjectRoot(a, b3) {
|
|
286059
|
-
const
|
|
286336
|
+
const normalize10 = (p2) => {
|
|
286060
286337
|
const resolved = pathResolve(p2);
|
|
286061
286338
|
try {
|
|
286062
286339
|
return realpathSync16(resolved);
|
|
@@ -286064,7 +286341,7 @@ function sameProjectRoot(a, b3) {
|
|
|
286064
286341
|
return resolved;
|
|
286065
286342
|
}
|
|
286066
286343
|
};
|
|
286067
|
-
return
|
|
286344
|
+
return normalize10(a) === normalize10(b3);
|
|
286068
286345
|
}
|
|
286069
286346
|
var DEFAULT_RECONCILIATION_INTERVAL_MS, ProjectEngineManager;
|
|
286070
286347
|
var init_project_engine_manager = __esm({
|
|
@@ -296872,7 +297149,7 @@ var init_gitlab = __esm({
|
|
|
296872
297149
|
if (response.status === 204) return void 0;
|
|
296873
297150
|
return await response.json();
|
|
296874
297151
|
}
|
|
296875
|
-
async listPaginated(path13, options,
|
|
297152
|
+
async listPaginated(path13, options, normalize10) {
|
|
296876
297153
|
const limit = clampLimit(options.limit);
|
|
296877
297154
|
const out = [];
|
|
296878
297155
|
let page = 1;
|
|
@@ -296883,7 +297160,7 @@ var init_gitlab = __esm({
|
|
|
296883
297160
|
appendListParams(params, options);
|
|
296884
297161
|
const rows = await this.request(`${path13}?${params.toString()}`);
|
|
296885
297162
|
if (!Array.isArray(rows) || rows.length === 0) break;
|
|
296886
|
-
out.push(...rows.map(
|
|
297163
|
+
out.push(...rows.map(normalize10));
|
|
296887
297164
|
if (rows.length < Number(params.get("per_page"))) break;
|
|
296888
297165
|
page += 1;
|
|
296889
297166
|
}
|
|
@@ -315714,6 +315991,22 @@ var init_update_install_coordinator = __esm({
|
|
|
315714
315991
|
pendingVersion;
|
|
315715
315992
|
pendingResult;
|
|
315716
315993
|
restartRequested = false;
|
|
315994
|
+
/**
|
|
315995
|
+
* FNXC:PendingUpdateInstall 2026-08-21-05:58:
|
|
315996
|
+
* A completed install belongs to the still-running old process, not a mounted
|
|
315997
|
+
* Settings dialog. Expose its target until process replacement so every route
|
|
315998
|
+
* can reject a second install and every dashboard remount can offer restart.
|
|
315999
|
+
*/
|
|
316000
|
+
getPendingInstall() {
|
|
316001
|
+
if (!this.pendingVersion || !this.pendingResult) return void 0;
|
|
316002
|
+
return {
|
|
316003
|
+
...this.pendingResult,
|
|
316004
|
+
latestVersion: this.pendingVersion,
|
|
316005
|
+
restartAttempted: this.restartRequested,
|
|
316006
|
+
restartScheduled: this.restartRequested,
|
|
316007
|
+
priorPid: this.restartRequested ? process.pid : void 0
|
|
316008
|
+
};
|
|
316009
|
+
}
|
|
315717
316010
|
async install(targetVersion, operation) {
|
|
315718
316011
|
if (this.pendingVersion && this.pendingResult) return this.pendingResult;
|
|
315719
316012
|
if (!this.inFlight) {
|
|
@@ -316932,6 +317225,7 @@ var init_server2 = __esm({
|
|
|
316932
317225
|
init_api_error();
|
|
316933
317226
|
init_project_store_resolver();
|
|
316934
317227
|
init_chat_project_services();
|
|
317228
|
+
init_file_service();
|
|
316935
317229
|
init_terminal_viewport();
|
|
316936
317230
|
init_terminal_service();
|
|
316937
317231
|
init_terminal();
|